From c026d3b74ea81b11554392ded5ef140fa5e5b35f Mon Sep 17 00:00:00 2001 From: PIERLUIGI VITI Date: Thu, 1 Oct 2026 18:03:46 +0200 Subject: [PATCH 1/5] feat(core): the Core API version of the requests (--api-version) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SDK 8 can put an API version in the request path (/api//…). clApi.apiVersion reads it from the --api-version flag, then CL_CLI_API_VERSION; without one the requests stay unversioned, which is what the production API serves today (versioned paths answer 404). clCommand.apiVersionFlag is the shared flag definition. Co-Authored-By: Claude Opus 5.5 --- packages/core/src/api.ts | 12 +++++++++++- packages/core/src/command.ts | 10 +++++++++- packages/core/test/unit/api.test.ts | 24 ++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/packages/core/src/api.ts b/packages/core/src/api.ts index 4ec471094..37bebda5d 100644 --- a/packages/core/src/api.ts +++ b/packages/core/src/api.ts @@ -25,6 +25,16 @@ const extractDomain = (baseUrl: string): string | undefined => { } +/** + * Core API version of the requests: the --api-version flag, then CL_CLI_API_VERSION. + * SDK 8 puts a version in the path (/api//…), which the production API + * doesn't serve yet: without one the requests stay unversioned (/api/…). + */ +const apiVersion = (flags?: Record): string | undefined => { + return flags?.['api-version'] || process.env.CL_CLI_API_VERSION || undefined +} + + /** Decode API execution mode */ const execMode = (liveFlag: string | boolean | undefined): ApiMode => { return ((liveFlag === true) || (liveFlag === 'live')) ? 'live' : 'test' @@ -38,7 +48,7 @@ const humanizeResource = (type: string, singular?: boolean): string => { } -export { baseURL, execMode, extractDomain, humanizeResource } +export { apiVersion, baseURL, execMode, extractDomain, humanizeResource } const CACHEABLE_RESOURCES = [ diff --git a/packages/core/src/command.ts b/packages/core/src/command.ts index 3248d6ecf..bd99e242b 100644 --- a/packages/core/src/command.ts +++ b/packages/core/src/command.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -import type { Command, Interfaces } from "@oclif/core" +import { type Command, Flags, type Interfaces } from "@oclif/core" type FlagInput = Interfaces.FlagInput @@ -16,6 +16,14 @@ export const commandFlags = (flags: T, exclude?: Array Flags.string({ + description: 'the Core API version of the requests (e.g. 2026-05), unversioned by default', + env: 'CL_CLI_API_VERSION', + helpGroup: 'API', +}) + + export const allFlags = (command: Command.Class): FlagInput => { return { ...command.flags, ...command.baseFlags } } diff --git a/packages/core/test/unit/api.test.ts b/packages/core/test/unit/api.test.ts index cbf273977..8b3244436 100644 --- a/packages/core/test/unit/api.test.ts +++ b/packages/core/test/unit/api.test.ts @@ -4,6 +4,7 @@ import { join } from 'node:path' import { api, coreApi, useMockedApi } from '@commercelayer/cli-test-utils' import { expect } from 'chai' import { + apiVersion, baseURL, execMode, extractDomain, @@ -47,6 +48,29 @@ describe('api', () => { expect(liveEnvironment('test')).to.equal(false) }) + describe('apiVersion', () => { + let env: string | undefined + beforeEach(() => { + env = process.env.CL_CLI_API_VERSION + delete process.env.CL_CLI_API_VERSION + }) + afterEach(() => { + if (env === undefined) delete process.env.CL_CLI_API_VERSION + else process.env.CL_CLI_API_VERSION = env + }) + + it('is unversioned by default', () => { + expect(apiVersion()).to.equal(undefined) + expect(apiVersion({})).to.equal(undefined) + }) + + it('takes the flag, then the environment', () => { + process.env.CL_CLI_API_VERSION = '2017-08' + expect(apiVersion()).to.equal('2017-08') + expect(apiVersion({ 'api-version': '2026-05' })).to.equal('2026-05') + }) + }) + it('humanizes resource types', () => { expect(humanizeResource('price_lists')).to.equal('price lists') expect(humanizeResource('price_lists', true)).to.equal('price list') From 8008678bbf6da27e189475f0541dc612d4d379dd Mon Sep 17 00:00:00 2001 From: PIERLUIGI VITI Date: Thu, 1 Oct 2026 18:03:46 +0200 Subject: [PATCH 2/5] feat!: move to the SDK 8 and provisioning-sdk 3 previews The previews come from pkg.pr.new (commercelayer-sdk 5259929, built from the staging1 schemas): @commercelayer/sdk 8.0.0-beta.13 and @commercelayer/provisioning-sdk 3.0.0-beta.1. Not for release: the catalog points at preview URLs until SDK 8 is on npm. - The Core API clients pass apiVersion (clApi.apiVersion), and the commands that call the Core API get the --api-version flag; requests stay unversioned by default. The resources Node snippet uses the named CommerceLayer import (the default export is deprecated) and adds apiVersion when set. - provisioning: the CommonJS declarations of the provisioning-sdk preview end with export=, which hides its named exports from CommonJS code. src/sdk.ts re-exports them typed with the ESM declarations (the runtime exports are fine); the cli patch does the same for CommerceLayerProvisioningStatic. To remove once the SDK fixes its declarations. - Tests: resources:schema prints the API version (2026-05) instead of a semver. - resources: the resource list is regenerated from the SDK (staging1 schema, so it includes resources not yet in production). Co-Authored-By: Claude Opus 5.5 --- .../cli/src/commands/applications/login.ts | 3 +- packages/cli/src/patches/command.ts | 5 +- plugins/checkout/src/base.ts | 5 +- plugins/cleanups/src/base.ts | 5 +- plugins/exports/src/base.ts | 6 +- plugins/imports/src/base.ts | 5 +- plugins/links/src/base.ts | 5 +- plugins/microstore/src/base.ts | 5 +- plugins/orders/src/base.ts | 3 +- plugins/orders/src/init.ts | 4 +- plugins/provisioning/src/base.ts | 6 +- .../src/commands/provisioning/create.ts | 8 +-- .../src/commands/provisioning/delete.ts | 2 +- .../src/commands/provisioning/exec.ts | 2 +- .../src/commands/provisioning/list.ts | 10 ++-- .../src/commands/provisioning/relationship.ts | 10 ++-- .../src/commands/provisioning/retrieve.ts | 8 +-- .../src/commands/provisioning/update.ts | 8 +-- plugins/provisioning/src/lang/curl.ts | 2 +- plugins/provisioning/src/lang/index.ts | 2 +- plugins/provisioning/src/lang/node.ts | 2 +- plugins/provisioning/src/lang/request.ts | 2 +- plugins/provisioning/src/lang/ruby.ts | 2 +- plugins/provisioning/src/sdk.ts | 23 ++++++++ .../provisioning/src/util/resources/index.ts | 2 +- plugins/resources/src/base.ts | 7 ++- .../resources/src/commands/resources/count.ts | 3 +- plugins/resources/src/lang/node.ts | 9 +-- .../resources/src/util/resources/available.ts | 53 ++++++++++++------ .../test/commands/resources/schema.test.ts | 2 +- plugins/seeder/src/base.ts | 4 +- plugins/seeder/src/commands/seeder/clean.ts | 3 +- plugins/seeder/src/commands/seeder/seed.ts | 3 +- plugins/seeder/src/fix.ts | 3 + plugins/tags/src/base.ts | 6 +- plugins/token/src/token.ts | 4 +- plugins/triggers/src/base.ts | 3 +- plugins/triggers/src/exec.ts | 3 +- plugins/webhooks/src/base.ts | 5 +- pnpm-lock.yaml | 56 ++++++++++--------- pnpm-workspace.yaml | 4 +- 41 files changed, 195 insertions(+), 108 deletions(-) create mode 100644 plugins/provisioning/src/sdk.ts diff --git a/packages/cli/src/commands/applications/login.ts b/packages/cli/src/commands/applications/login.ts index 0263e3287..d7e10417e 100644 --- a/packages/cli/src/commands/applications/login.ts +++ b/packages/cli/src/commands/applications/login.ts @@ -2,6 +2,7 @@ import { inspect } from 'node:util' import type { ApiMode, AppAuth, AppInfo, AuthScope } from '@commercelayer/cli-core' import { clApi, clApplication, clColor, clCommand, clConfig, clToken } from '@commercelayer/cli-core' import clprovisioning from '@commercelayer/provisioning-sdk' +import type { ApiVersion } from '@commercelayer/sdk' import commercelayer, { type Application, CommerceLayerStatic, type Organization } from '@commercelayer/sdk' import type { Interfaces } from '@oclif/core' import { Command, type Config, Errors, Flags } from '@oclif/core' @@ -187,7 +188,7 @@ const getApplicationInfo = async (auth: AppAuth, accessToken: string): Promise { error(cl.organization.type()) }) // Application info diff --git a/packages/cli/src/patches/command.ts b/packages/cli/src/patches/command.ts index 032bbbd65..44519c4ae 100644 --- a/packages/cli/src/patches/command.ts +++ b/packages/cli/src/patches/command.ts @@ -1,6 +1,9 @@ import { clText } from "@commercelayer/cli-core" -import { CommerceLayerProvisioningStatic } from "@commercelayer/provisioning-sdk" + +// The CommonJS declarations of the provisioning-sdk preview hide its named exports +// behind export=: the value comes from require(), typed with the ESM declarations +const { CommerceLayerProvisioningStatic } = require('@commercelayer/provisioning-sdk') as typeof import('@commercelayer/provisioning-sdk', { with: { 'resolution-mode': 'import' } }) const PROVISIONING = true diff --git a/plugins/checkout/src/base.ts b/plugins/checkout/src/base.ts index 3c0e55840..c48215dde 100644 --- a/plugins/checkout/src/base.ts +++ b/plugins/checkout/src/base.ts @@ -1,4 +1,5 @@ -import { clColor, clOutput, clToken, clUpdate, clUtil } from '@commercelayer/cli-core' +import { clApi, clColor, clCommand, clOutput, clToken, clUpdate, clUtil } from '@commercelayer/cli-core' +import type { ApiVersion } from '@commercelayer/sdk' import commercelayer, { type CommerceLayerClient, CommerceLayerStatic } from '@commercelayer/sdk' import type { Interfaces } from '@oclif/core' import { Args, Command, Flags } from '@oclif/core' @@ -29,6 +30,7 @@ export default abstract class extends Command { dependsOn: ['organization'], env: 'CL_CLI_DOMAIN' }), + 'api-version': clCommand.apiVersionFlag(), accessToken: Flags.string({ char: 'a', description: 'custom access token to use instead of the one used for login', @@ -81,6 +83,7 @@ export default abstract class extends Command { const userAgent = clUtil.userAgent(this.config) return commercelayer({ + apiVersion: clApi.apiVersion(flags) as ApiVersion, organization, domain, accessToken, diff --git a/plugins/cleanups/src/base.ts b/plugins/cleanups/src/base.ts index 3553f273c..c12772b59 100644 --- a/plugins/cleanups/src/base.ts +++ b/plugins/cleanups/src/base.ts @@ -1,5 +1,6 @@ -import { clColor, clFilter, clOutput, clToken, clUpdate, clUtil, type KeyValRel, type KeyValString } from '@commercelayer/cli-core' +import { clApi, clColor, clCommand, clFilter, clOutput, clToken, clUpdate, clUtil, type KeyValRel, type KeyValString } from '@commercelayer/cli-core' import * as cliux from '@commercelayer/cli-ux' +import type { ApiVersion } from '@commercelayer/sdk' import commercelayer, { type CommerceLayerClient, CommerceLayerStatic } from '@commercelayer/sdk' import type { Interfaces } from '@oclif/core' import { Args, Command, Flags } from '@oclif/core' @@ -27,6 +28,7 @@ export default abstract class extends Command { dependsOn: ['organization'], env: 'CL_CLI_DOMAIN', }), + 'api-version': clCommand.apiVersionFlag(), accessToken: Flags.string({ hidden: true, required: true, @@ -120,6 +122,7 @@ export default abstract class extends Command { const userAgent = clUtil.userAgent(this.config) return commercelayer({ + apiVersion: clApi.apiVersion(flags) as ApiVersion, organization, domain, accessToken, diff --git a/plugins/exports/src/base.ts b/plugins/exports/src/base.ts index 83c5dd7a0..b84623a55 100644 --- a/plugins/exports/src/base.ts +++ b/plugins/exports/src/base.ts @@ -3,9 +3,9 @@ import { rename } from 'node:fs/promises' import { join } from 'node:path' import { gunzipSync, type InputType } from 'node:zlib' import type { ApiMode, KeyValRel, KeyValString } from '@commercelayer/cli-core' -import { clApi, clColor, clFilter, clOutput, clToken, clUpdate, clUtil } from '@commercelayer/cli-core' +import { clApi, clColor, clCommand, clFilter, clOutput, clToken, clUpdate, clUtil } from '@commercelayer/cli-core' import * as cliux from '@commercelayer/cli-ux' -import type { CommerceLayerClient, Export, ResourceTypeLock } from '@commercelayer/sdk' +import type { ApiVersion, CommerceLayerClient, Export, ResourceTypeLock } from '@commercelayer/sdk' import commercelayer, { CommerceLayerStatic } from '@commercelayer/sdk' import type { Interfaces } from '@oclif/core' import { Args, Command, Flags } from '@oclif/core' @@ -63,6 +63,7 @@ export default abstract class BaseCommand extends Command { dependsOn: ['organization'], env: 'CL_CLI_DOMAIN' }), + 'api-version': clCommand.apiVersionFlag(), accessToken: Flags.string({ hidden: true, required: true, @@ -116,6 +117,7 @@ export default abstract class BaseCommand extends Command { this.environment = clToken.getTokenEnvironment(accessToken) this.cl = commercelayer({ + apiVersion: clApi.apiVersion(flags) as ApiVersion, organization, domain, accessToken, diff --git a/plugins/imports/src/base.ts b/plugins/imports/src/base.ts index 63f24b049..3b117cb28 100644 --- a/plugins/imports/src/base.ts +++ b/plugins/imports/src/base.ts @@ -1,5 +1,6 @@ -import { type ApiMode, clColor, clOutput, clToken, clUpdate, clUtil } from '@commercelayer/cli-core' +import { type ApiMode, clApi, clColor, clCommand, clOutput, clToken, clUpdate, clUtil } from '@commercelayer/cli-core' import * as cliux from '@commercelayer/cli-ux' +import type { ApiVersion } from '@commercelayer/sdk' import commercelayer, { type CommerceLayerClient, CommerceLayerStatic } from '@commercelayer/sdk' import type { Interfaces } from '@oclif/core' import { Args, Command, Flags } from '@oclif/core' @@ -29,6 +30,7 @@ export default abstract class extends Command { dependsOn: ['organization'], env: 'CL_CLI_DOMAIN', }), + 'api-version': clCommand.apiVersionFlag(), accessToken: Flags.string({ hidden: true, required: true, @@ -108,6 +110,7 @@ export default abstract class extends Command { this.environment = clToken.getTokenEnvironment(accessToken) return commercelayer({ + apiVersion: clApi.apiVersion(flags) as ApiVersion, organization, domain, accessToken, diff --git a/plugins/links/src/base.ts b/plugins/links/src/base.ts index 5c2739fd4..df622d257 100644 --- a/plugins/links/src/base.ts +++ b/plugins/links/src/base.ts @@ -1,4 +1,5 @@ -import { clColor, clConfig, clOutput, clText, clUpdate, clUtil } from '@commercelayer/cli-core' +import { clApi, clColor, clCommand, clConfig, clOutput, clText, clUpdate, clUtil } from '@commercelayer/cli-core' +import type { ApiVersion } from '@commercelayer/sdk' import commercelayer, { type CommerceLayerClient, CommerceLayerStatic, type LinkCreate } from '@commercelayer/sdk' import type { Interfaces } from '@oclif/core' import { Args, Command, ux as cliux, Flags } from '@oclif/core' @@ -29,6 +30,7 @@ export abstract class BaseCommand extends Command { dependsOn: ['organization'], env: 'CL_CLI_DOMAIN' }), + 'api-version': clCommand.apiVersionFlag(), accessToken: Flags.string({ char: 'a', description: 'custom access token to use instead of the one used for login', @@ -75,6 +77,7 @@ export abstract class BaseCommand extends Command { const userAgent = clUtil.userAgent(this.config) return this.cl = commercelayer({ + apiVersion: clApi.apiVersion(flags) as ApiVersion, organization, domain, accessToken, diff --git a/plugins/microstore/src/base.ts b/plugins/microstore/src/base.ts index 69ef9e42d..e94e202e1 100644 --- a/plugins/microstore/src/base.ts +++ b/plugins/microstore/src/base.ts @@ -1,4 +1,5 @@ -import { clColor, clOutput, clToken, clUpdate, clUtil } from '@commercelayer/cli-core' +import { clApi, clColor, clCommand, clOutput, clToken, clUpdate, clUtil } from '@commercelayer/cli-core' +import type { ApiVersion } from '@commercelayer/sdk' import commercelayer, { type CommerceLayerClient, CommerceLayerStatic } from '@commercelayer/sdk' import type { Interfaces } from '@oclif/core' import { Command, Flags } from '@oclif/core' @@ -29,6 +30,7 @@ export default abstract class extends Command { dependsOn: ['organization'], env: 'CL_CLI_DOMAIN' }), + 'api-version': clCommand.apiVersionFlag(), accessToken: Flags.string({ char: 'a', description: 'custom access token to use instead of the one used for login', @@ -108,6 +110,7 @@ export default abstract class extends Command { const userAgent = clUtil.userAgent(this.config) return commercelayer({ + apiVersion: clApi.apiVersion(flags) as ApiVersion, organization, domain, accessToken, diff --git a/plugins/orders/src/base.ts b/plugins/orders/src/base.ts index 4551b8192..f86fa6e6c 100644 --- a/plugins/orders/src/base.ts +++ b/plugins/orders/src/base.ts @@ -1,4 +1,4 @@ -import { clColor, clOutput, clUpdate } from '@commercelayer/cli-core' +import { clColor, clCommand, clOutput, clUpdate } from '@commercelayer/cli-core' import { CommerceLayerStatic, type Order } from '@commercelayer/sdk' import type { Interfaces } from '@oclif/core' import { Args, Command, Flags } from '@oclif/core' @@ -28,6 +28,7 @@ export default abstract class extends Command { dependsOn: ['organization'], env: 'CL_CLI_DOMAIN' }), + 'api-version': clCommand.apiVersionFlag(), accessToken: Flags.string({ hidden: true, required: true, diff --git a/plugins/orders/src/init.ts b/plugins/orders/src/init.ts index 2697b2ee8..d0d0864df 100644 --- a/plugins/orders/src/init.ts +++ b/plugins/orders/src/init.ts @@ -1,4 +1,5 @@ -import { clUtil } from '@commercelayer/cli-core' +import { clApi, clUtil } from '@commercelayer/cli-core' +import type { ApiVersion } from '@commercelayer/sdk' import commercelayer, { type CommerceLayerClient } from '@commercelayer/sdk' import type { Interfaces } from '@oclif/core' @@ -14,6 +15,7 @@ export const commercelayerInit = (flags: any, config?: Config): CommerceLayerCli const userAgent = config? clUtil.userAgent(config) : undefined return commercelayer({ + apiVersion: clApi.apiVersion(flags) as ApiVersion, organization, domain, accessToken, diff --git a/plugins/provisioning/src/base.ts b/plugins/provisioning/src/base.ts index 1d2db23fd..ea275344c 100644 --- a/plugins/provisioning/src/base.ts +++ b/plugins/provisioning/src/base.ts @@ -17,13 +17,13 @@ import { clUpdate, clUtil, } from "@commercelayer/cli-core" +import type { Interfaces } from "@oclif/core" +import { Args, Command, ux as cliux, Flags } from "@oclif/core" import commercelayer, { type CommerceLayerProvisioningClient, CommerceLayerProvisioningStatic, type QueryParams, -} from "@commercelayer/provisioning-sdk" -import type { Interfaces } from "@oclif/core" -import { Args, Command, ux as cliux, Flags } from "@oclif/core" +} from "./sdk" type CommandError = Interfaces.CommandError diff --git a/plugins/provisioning/src/commands/provisioning/create.ts b/plugins/provisioning/src/commands/provisioning/create.ts index 35a49690c..c0cc4f5b2 100644 --- a/plugins/provisioning/src/commands/provisioning/create.ts +++ b/plugins/provisioning/src/commands/provisioning/create.ts @@ -1,12 +1,12 @@ import { clApi, clColor } from "@commercelayer/cli-core"; -import type { - CommerceLayerProvisioningClient, - QueryParamsRetrieve, -} from "@commercelayer/provisioning-sdk"; import Command, { Flags /* , FLAG_LOAD_PARAMS, FLAG_SAVE_PARAMS */, } from "../../base"; import { addRequestReader, isRequestInterrupted } from "../../lang"; +import type { + CommerceLayerProvisioningClient, + QueryParamsRetrieve, +} from "../../sdk"; // import { mergeCommandParams } from '../../commands' diff --git a/plugins/provisioning/src/commands/provisioning/delete.ts b/plugins/provisioning/src/commands/provisioning/delete.ts index 13578749e..f02d3b936 100644 --- a/plugins/provisioning/src/commands/provisioning/delete.ts +++ b/plugins/provisioning/src/commands/provisioning/delete.ts @@ -1,7 +1,7 @@ import { clColor, clCommand } from "@commercelayer/cli-core"; -import type { CommerceLayerProvisioningClient } from "@commercelayer/provisioning-sdk"; import Command, { Args } from "../../base"; import { addRequestReader, isRequestInterrupted } from "../../lang"; +import type { CommerceLayerProvisioningClient } from "../../sdk"; const OPERATION = "delete"; diff --git a/plugins/provisioning/src/commands/provisioning/exec.ts b/plugins/provisioning/src/commands/provisioning/exec.ts index 9dc459b9c..7672a6216 100644 --- a/plugins/provisioning/src/commands/provisioning/exec.ts +++ b/plugins/provisioning/src/commands/provisioning/exec.ts @@ -1,6 +1,6 @@ import { clColor } from "@commercelayer/cli-core"; -import type { CommerceLayerProvisioningClient } from "@commercelayer/provisioning-sdk"; import Command, { Args, BaseCommand, Flags } from "../../base"; +import type { CommerceLayerProvisioningClient } from "../../sdk"; export default class ProvisioningExec extends BaseCommand { static description = "execute an action on a resource"; diff --git a/plugins/provisioning/src/commands/provisioning/list.ts b/plugins/provisioning/src/commands/provisioning/list.ts index 61613f3c1..37bbbc7f8 100644 --- a/plugins/provisioning/src/commands/provisioning/list.ts +++ b/plugins/provisioning/src/commands/provisioning/list.ts @@ -1,15 +1,15 @@ // import { mergeCommandParams } from '../../commands' import { clColor } from "@commercelayer/cli-core"; -import type { - CommerceLayerProvisioningClient, - QueryPageSize, - QueryParamsList, -} from "@commercelayer/provisioning-sdk"; import Command, { cliux, Flags /* FLAG_LOAD_PARAMS, FLAG_SAVE_PARAMS, */, } from "../../base"; import { addRequestReader, isRequestInterrupted } from "../../lang"; +import type { + CommerceLayerProvisioningClient, + QueryPageSize, + QueryParamsList, +} from "../../sdk"; const OPERATION = "list"; diff --git a/plugins/provisioning/src/commands/provisioning/relationship.ts b/plugins/provisioning/src/commands/provisioning/relationship.ts index ebad3b67c..298bdf28f 100644 --- a/plugins/provisioning/src/commands/provisioning/relationship.ts +++ b/plugins/provisioning/src/commands/provisioning/relationship.ts @@ -1,14 +1,14 @@ import { clColor, clText } from "@commercelayer/cli-core"; -import type { - CommerceLayerProvisioningClient, - QueryPageSize, - QueryParamsList, -} from "@commercelayer/provisioning-sdk"; import Command, { Args, cliux /*, FLAG_LOAD_PARAMS, FLAG_SAVE_PARAMS */, } from "../../base"; import { addRequestReader, isRequestInterrupted } from "../../lang"; +import type { + CommerceLayerProvisioningClient, + QueryPageSize, + QueryParamsList, +} from "../../sdk"; // import { mergeCommandParams } from '../../commands' import ResourcesList from "./list"; diff --git a/plugins/provisioning/src/commands/provisioning/retrieve.ts b/plugins/provisioning/src/commands/provisioning/retrieve.ts index 37d8c79f8..1c2f612fc 100644 --- a/plugins/provisioning/src/commands/provisioning/retrieve.ts +++ b/plugins/provisioning/src/commands/provisioning/retrieve.ts @@ -1,12 +1,12 @@ -import type { - CommerceLayerProvisioningClient, - QueryParamsRetrieve, -} from "@commercelayer/provisioning-sdk"; import Command, { Args, /*, FLAG_LOAD_PARAMS, FLAG_SAVE_PARAM */ Flags, } from "../../base"; import { addRequestReader, isRequestInterrupted } from "../../lang"; +import type { + CommerceLayerProvisioningClient, + QueryParamsRetrieve, +} from "../../sdk"; // import { mergeCommandParams } from '../../commands' diff --git a/plugins/provisioning/src/commands/provisioning/update.ts b/plugins/provisioning/src/commands/provisioning/update.ts index 81b3e052d..679db8166 100644 --- a/plugins/provisioning/src/commands/provisioning/update.ts +++ b/plugins/provisioning/src/commands/provisioning/update.ts @@ -1,13 +1,13 @@ import { clApi, clColor } from "@commercelayer/cli-core"; -import type { - CommerceLayerProvisioningClient, - QueryParamsRetrieve, -} from "@commercelayer/provisioning-sdk"; import Command, { Args, /* , FLAG_LOAD_PARAMS, FLAG_SAVE_PARAMS */ Flags, } from "../../base"; import { addRequestReader, isRequestInterrupted } from "../../lang"; +import type { + CommerceLayerProvisioningClient, + QueryParamsRetrieve, +} from "../../sdk"; const OPERATION = "update"; diff --git a/plugins/provisioning/src/lang/curl.ts b/plugins/provisioning/src/lang/curl.ts index 79aad61a8..b2d8d217d 100644 --- a/plugins/provisioning/src/lang/curl.ts +++ b/plugins/provisioning/src/lang/curl.ts @@ -1,4 +1,4 @@ -import type { QueryParams } from "@commercelayer/provisioning-sdk"; +import type { QueryParams } from "../sdk"; import { getFullUrl } from "."; import { getHeaders, getMethod, type RequestData } from "./request"; diff --git a/plugins/provisioning/src/lang/index.ts b/plugins/provisioning/src/lang/index.ts index a20676127..50e2daf19 100644 --- a/plugins/provisioning/src/lang/index.ts +++ b/plugins/provisioning/src/lang/index.ts @@ -1,5 +1,5 @@ -import type { QueryParams } from "@commercelayer/provisioning-sdk"; import inquirer from "inquirer"; +import type { QueryParams } from "../sdk"; import { buildCurl } from "./curl"; import { buildTypescript } from "./node"; import type { RequestData } from "./request"; diff --git a/plugins/provisioning/src/lang/node.ts b/plugins/provisioning/src/lang/node.ts index 74a45f9b8..e0b32132b 100644 --- a/plugins/provisioning/src/lang/node.ts +++ b/plugins/provisioning/src/lang/node.ts @@ -1,5 +1,5 @@ import { clOutput } from "@commercelayer/cli-core"; -import type { QueryParams } from "@commercelayer/provisioning-sdk"; +import type { QueryParams } from "../sdk"; import { getOperation } from "."; import type { RequestData } from "./request"; diff --git a/plugins/provisioning/src/lang/request.ts b/plugins/provisioning/src/lang/request.ts index c949def36..f26f90aa1 100644 --- a/plugins/provisioning/src/lang/request.ts +++ b/plugins/provisioning/src/lang/request.ts @@ -3,7 +3,7 @@ import { type CommerceLayerProvisioningClient, CommerceLayerProvisioningStatic, type RequestObj, -} from "@commercelayer/provisioning-sdk"; +} from "../sdk"; type RequestData = { baseUrl: string; diff --git a/plugins/provisioning/src/lang/ruby.ts b/plugins/provisioning/src/lang/ruby.ts index fe6f9728f..3d7f9bcd0 100644 --- a/plugins/provisioning/src/lang/ruby.ts +++ b/plugins/provisioning/src/lang/ruby.ts @@ -1,4 +1,4 @@ -import type { QueryParams } from "@commercelayer/provisioning-sdk"; +import type { QueryParams } from "../sdk"; import type { RequestData } from "./request"; const buildRuby = (_request: RequestData, _params?: QueryParams): string => { diff --git a/plugins/provisioning/src/sdk.ts b/plugins/provisioning/src/sdk.ts new file mode 100644 index 000000000..7f2be7416 --- /dev/null +++ b/plugins/provisioning/src/sdk.ts @@ -0,0 +1,23 @@ +/** + * @commercelayer/provisioning-sdk, typed through its ESM declarations. + * + * Workaround: the CommonJS declarations of the SDK preview (lib/index.d.cts) + * end with `export =`, which hides their named exports (CommerceLayerProvisioningStatic, + * QueryParams, …) from CommonJS code like this plugin. The runtime exports are fine. + * Remove this module, and import from '@commercelayer/provisioning-sdk' again, + * once the SDK fixes its CommonJS declarations. + */ +type Sdk = typeof import('@commercelayer/provisioning-sdk', { with: { 'resolution-mode': 'import' } }) + +const sdk = require('@commercelayer/provisioning-sdk') as Sdk + +export const { CommerceLayerProvisioningStatic } = sdk +export default sdk.CommerceLayerProvisioning + +export type CommerceLayerProvisioningClient = import('@commercelayer/provisioning-sdk', { with: { 'resolution-mode': 'import' } }).CommerceLayerProvisioningClient +export type QueryPageSize = import('@commercelayer/provisioning-sdk', { with: { 'resolution-mode': 'import' } }).QueryPageSize +export type QueryParams = import('@commercelayer/provisioning-sdk', { with: { 'resolution-mode': 'import' } }).QueryParams +export type QueryParamsList = import('@commercelayer/provisioning-sdk', { with: { 'resolution-mode': 'import' } }).QueryParamsList +export type QueryParamsRetrieve = import('@commercelayer/provisioning-sdk', { with: { 'resolution-mode': 'import' } }).QueryParamsRetrieve +export type RequestObj = import('@commercelayer/provisioning-sdk', { with: { 'resolution-mode': 'import' } }).RequestObj +export type ResourceTypeLock = import('@commercelayer/provisioning-sdk', { with: { 'resolution-mode': 'import' } }).ResourceTypeLock diff --git a/plugins/provisioning/src/util/resources/index.ts b/plugins/provisioning/src/util/resources/index.ts index 18f3ef970..4ff64f386 100644 --- a/plugins/provisioning/src/util/resources/index.ts +++ b/plugins/provisioning/src/util/resources/index.ts @@ -1,4 +1,4 @@ -import type { ResourceTypeLock } from "@commercelayer/provisioning-sdk"; +import type { ResourceTypeLock } from "../../sdk"; import RESOURCES from "./available"; interface ApiResource { diff --git a/plugins/resources/src/base.ts b/plugins/resources/src/base.ts index 97a1a0834..d08e00060 100644 --- a/plugins/resources/src/base.ts +++ b/plugins/resources/src/base.ts @@ -1,8 +1,8 @@ import { existsSync } from 'node:fs' import type { KeyVal, KeyValArray, KeyValObj, KeyValRel, KeyValSort, KeyValString, ResAttributes } from '@commercelayer/cli-core' -import { clColor, clCommand, clConfig, clFilter, clText, clToken, clUpdate, clUtil } from '@commercelayer/cli-core' +import { clApi, clColor, clCommand, clConfig, clFilter, clText, clToken, clUpdate, clUtil } from '@commercelayer/cli-core' import * as cliux from '@commercelayer/cli-ux' -import type { CommerceLayerClient, QueryParams, QueryParamsRetrieve, ResourceId, ResourceType, ResourceTypeLock } from '@commercelayer/sdk' +import type { ApiVersion, CommerceLayerClient, QueryParams, QueryParamsRetrieve, ResourceId, ResourceType, ResourceTypeLock } from '@commercelayer/sdk' import commercelayer, { CommerceLayerStatic } from '@commercelayer/sdk' import type { Interfaces } from '@oclif/core' import { Args, Command, type Config, Flags } from '@oclif/core' @@ -41,6 +41,7 @@ export abstract class BaseCommand extends Command { dependsOn: ['organization'], env: 'CL_CLI_DOMAIN' }), + 'api-version': clCommand.apiVersionFlag(), accessToken: Flags.string({ hidden: true, required: true, @@ -57,7 +58,7 @@ export abstract class BaseCommand extends Command { const userAgent = clUtil.userAgent(this.config) - const cl = commercelayer({ organization, domain, accessToken, userAgent, ...options }) + const cl = commercelayer({ apiVersion: clApi.apiVersion(flags) as ApiVersion, organization, domain, accessToken, userAgent, ...options }) if ('cl' in this) this.cl = cl diff --git a/plugins/resources/src/commands/resources/count.ts b/plugins/resources/src/commands/resources/count.ts index 267ee1d4a..6f7fb9424 100644 --- a/plugins/resources/src/commands/resources/count.ts +++ b/plugins/resources/src/commands/resources/count.ts @@ -1,4 +1,5 @@ import { clApi, clColor, clFilter, clUtil, type KeyValString } from '@commercelayer/cli-core' +import type { ApiVersion } from '@commercelayer/sdk' import { CommerceLayer, type CommerceLayerClient, type ListResponse, type Resource } from '@commercelayer/sdk' import { Args, BaseCommand, cliux, Flags } from '../../base' @@ -41,7 +42,7 @@ export default class ResourcesCount extends BaseCommand { const accessToken = flags.accessToken - const cl = CommerceLayer({ organization, domain, accessToken, userAgent: clUtil.userAgent(this.config), timeout: 20_000 }) + const cl = CommerceLayer({ apiVersion: clApi.apiVersion(flags) as ApiVersion, organization, domain, accessToken, userAgent: clUtil.userAgent(this.config), timeout: 20_000 }) const resSdk: any = cl[resource.api as keyof CommerceLayerClient] this.checkOperation(resSdk) diff --git a/plugins/resources/src/lang/node.ts b/plugins/resources/src/lang/node.ts index 99ce6aa68..6c32706c1 100644 --- a/plugins/resources/src/lang/node.ts +++ b/plugins/resources/src/lang/node.ts @@ -1,4 +1,4 @@ -import { clOutput } from '@commercelayer/cli-core' +import { clApi, clOutput } from '@commercelayer/cli-core' import type { QueryParams } from '@commercelayer/sdk' import { getOperation, type RequestData } from './request' @@ -8,15 +8,16 @@ const buildTypescript = (request: RequestData, params?: QueryParams, flags?: any const hasParams = params && (Object.keys(params).length > 0) const operation = getOperation(request) const qpSuffix = (operation.name === 'list') ? 'List' : 'Retrieve' - const paramsImport = hasParams ? `, { QueryParams${qpSuffix} }` : '' + const paramsImport = hasParams ? `, type QueryParams${qpSuffix}` : '' - let ts = `import commercelayer${paramsImport} from '@commercelayer/sdk'` + let ts = `import { CommerceLayer${paramsImport} } from '@commercelayer/sdk'` ts += `\n\nconst organization = '${flags.organization}'` ts += `\nconst accessToken = '${flags.accessToken}'` if (flags.domain) ts += `\nconst domain = '${flags.domain}'` - ts += `\n\nconst cl = commercelayer({ organization, accessToken${flags.domain ? ', domain' : ''} })` + const apiVersion = clApi.apiVersion(flags) + ts += `\n\nconst cl = CommerceLayer({ organization, accessToken${apiVersion ? `, apiVersion: '${apiVersion}'` : ''}${flags.domain ? ', domain' : ''} })` if (hasParams) ts += `\n\nconst params: QueryParams${qpSuffix} = ${clOutput.printObject(params, { color: false })}` diff --git a/plugins/resources/src/util/resources/available.ts b/plugins/resources/src/util/resources/available.ts index f64ba8692..d75b5a925 100644 --- a/plugins/resources/src/util/resources/available.ts +++ b/plugins/resources/src/util/resources/available.ts @@ -10,6 +10,7 @@ const RESOURCES = [ { name: 'avalara_account', type: 'avalara_accounts', api: 'avalara_accounts', model: 'AvalaraAccount' }, { name: 'axerve_gateway', type: 'axerve_gateways', api: 'axerve_gateways', model: 'AxerveGateway' }, { name: 'axerve_payment', type: 'axerve_payments', api: 'axerve_payments', model: 'AxervePayment' }, + { name: 'billing_info_validation_rule', type: 'billing_info_validation_rules', api: 'billing_info_validation_rules', model: 'BillingInfoValidationRule' }, { name: 'bing_geocoder', type: 'bing_geocoders', api: 'bing_geocoders', model: 'BingGeocoder' }, { name: 'braintree_gateway', type: 'braintree_gateways', api: 'braintree_gateways', model: 'BraintreeGateway' }, { name: 'braintree_payment', type: 'braintree_payments', api: 'braintree_payments', model: 'BraintreePayment' }, @@ -20,23 +21,23 @@ const RESOURCES = [ { name: 'checkout_com_gateway', type: 'checkout_com_gateways', api: 'checkout_com_gateways', model: 'CheckoutComGateway' }, { name: 'checkout_com_payment', type: 'checkout_com_payments', api: 'checkout_com_payments', model: 'CheckoutComPayment' }, { name: 'cleanup', type: 'cleanups', api: 'cleanups', model: 'Cleanup' }, + { name: 'coupon', type: 'coupons', api: 'coupons', model: 'Coupon' }, { name: 'coupon_codes_promotion_rule', type: 'coupon_codes_promotion_rules', api: 'coupon_codes_promotion_rules', model: 'CouponCodesPromotionRule' }, { name: 'coupon_recipient', type: 'coupon_recipients', api: 'coupon_recipients', model: 'CouponRecipient' }, - { name: 'coupon', type: 'coupons', api: 'coupons', model: 'Coupon' }, { name: 'custom_promotion_rule', type: 'custom_promotion_rules', api: 'custom_promotion_rules', model: 'CustomPromotionRule' }, + { name: 'customer', type: 'customers', api: 'customers', model: 'Customer' }, { name: 'customer_address', type: 'customer_addresses', api: 'customer_addresses', model: 'CustomerAddress' }, { name: 'customer_group', type: 'customer_groups', api: 'customer_groups', model: 'CustomerGroup' }, { name: 'customer_password_reset', type: 'customer_password_resets', api: 'customer_password_resets', model: 'CustomerPasswordReset' }, { name: 'customer_payment_source', type: 'customer_payment_sources', api: 'customer_payment_sources', model: 'CustomerPaymentSource' }, { name: 'customer_subscription', type: 'customer_subscriptions', api: 'customer_subscriptions', model: 'CustomerSubscription' }, - { name: 'customer', type: 'customers', api: 'customers', model: 'Customer' }, { name: 'delivery_lead_time', type: 'delivery_lead_times', api: 'delivery_lead_times', model: 'DeliveryLeadTime' }, - { name: 'discount_engine_item', type: 'discount_engine_items', api: 'discount_engine_items', model: 'DiscountEngineItem' }, { name: 'discount_engine', type: 'discount_engines', api: 'discount_engines', model: 'DiscountEngine' }, + { name: 'discount_engine_item', type: 'discount_engine_items', api: 'discount_engine_items', model: 'DiscountEngineItem' }, { name: 'easypost_pickup', type: 'easypost_pickups', api: 'easypost_pickups', model: 'EasypostPickup' }, + { name: 'event', type: 'events', api: 'events', model: 'Event' }, { name: 'event_callback', type: 'event_callbacks', api: 'event_callbacks', model: 'EventCallback' }, { name: 'event_store', type: 'event_stores', api: 'event_stores', model: 'EventStore' }, - { name: 'event', type: 'events', api: 'events', model: 'Event' }, { name: 'export', type: 'exports', api: 'exports', model: 'Export' }, { name: 'external_gateway', type: 'external_gateways', api: 'external_gateways', model: 'ExternalGateway' }, { name: 'external_payment', type: 'external_payments', api: 'external_payments', model: 'ExternalPayment' }, @@ -48,8 +49,8 @@ const RESOURCES = [ { name: 'free_gift_promotion', type: 'free_gift_promotions', api: 'free_gift_promotions', model: 'FreeGiftPromotion' }, { name: 'free_shipping_promotion', type: 'free_shipping_promotions', api: 'free_shipping_promotions', model: 'FreeShippingPromotion' }, { name: 'geocoder', type: 'geocoders', api: 'geocoders', model: 'Geocoder' }, - { name: 'gift_card_recipient', type: 'gift_card_recipients', api: 'gift_card_recipients', model: 'GiftCardRecipient' }, { name: 'gift_card', type: 'gift_cards', api: 'gift_cards', model: 'GiftCard' }, + { name: 'gift_card_recipient', type: 'gift_card_recipients', api: 'gift_card_recipients', model: 'GiftCardRecipient' }, { name: 'google_geocoder', type: 'google_geocoders', api: 'google_geocoders', model: 'GoogleGeocoder' }, { name: 'import', type: 'imports', api: 'imports', model: 'Import' }, { name: 'in_stock_subscription', type: 'in_stock_subscriptions', api: 'in_stock_subscriptions', model: 'InStockSubscription' }, @@ -58,58 +59,78 @@ const RESOURCES = [ { name: 'inventory_stock_location', type: 'inventory_stock_locations', api: 'inventory_stock_locations', model: 'InventoryStockLocation' }, { name: 'klarna_gateway', type: 'klarna_gateways', api: 'klarna_gateways', model: 'KlarnaGateway' }, { name: 'klarna_payment', type: 'klarna_payments', api: 'klarna_payments', model: 'KlarnaPayment' }, - { name: 'line_item_option', type: 'line_item_options', api: 'line_item_options', model: 'LineItemOption' }, { name: 'line_item', type: 'line_items', api: 'line_items', model: 'LineItem' }, + { name: 'line_item_option', type: 'line_item_options', api: 'line_item_options', model: 'LineItemOption' }, { name: 'link', type: 'links', api: 'links', model: 'Link' }, { name: 'manual_gateway', type: 'manual_gateways', api: 'manual_gateways', model: 'ManualGateway' }, { name: 'manual_tax_calculator', type: 'manual_tax_calculators', api: 'manual_tax_calculators', model: 'ManualTaxCalculator' }, { name: 'market', type: 'markets', api: 'markets', model: 'Market' }, { name: 'merchant', type: 'merchants', api: 'merchants', model: 'Merchant' }, { name: 'notification', type: 'notifications', api: 'notifications', model: 'Notification' }, + { name: 'order', type: 'orders', api: 'orders', model: 'Order' }, { name: 'order_amount_promotion_rule', type: 'order_amount_promotion_rules', api: 'order_amount_promotion_rules', model: 'OrderAmountPromotionRule' }, { name: 'order_copy', type: 'order_copies', api: 'order_copies', model: 'OrderCopy' }, { name: 'order_factory', type: 'order_factories', api: 'order_factories', model: 'OrderFactory' }, - { name: 'order_subscription_item', type: 'order_subscription_items', api: 'order_subscription_items', model: 'OrderSubscriptionItem' }, { name: 'order_subscription', type: 'order_subscriptions', api: 'order_subscriptions', model: 'OrderSubscription' }, - { name: 'order', type: 'orders', api: 'orders', model: 'Order' }, + { name: 'order_subscription_item', type: 'order_subscription_items', api: 'order_subscription_items', model: 'OrderSubscriptionItem' }, + { name: 'order_validation_rule', type: 'order_validation_rules', api: 'order_validation_rules', model: 'OrderValidationRule' }, { name: 'organization', type: 'organizations', api: 'organization', model: 'Organization', singleton: true }, { name: 'package', type: 'packages', api: 'packages', model: 'Package' }, - { name: 'parcel_line_item', type: 'parcel_line_items', api: 'parcel_line_items', model: 'ParcelLineItem' }, { name: 'parcel', type: 'parcels', api: 'parcels', model: 'Parcel' }, + { name: 'parcel_line_item', type: 'parcel_line_items', api: 'parcel_line_items', model: 'ParcelLineItem' }, + { name: 'payment_authorization', type: 'payment_authorizations', api: 'payment_authorizations', model: 'PaymentAuthorization' }, + { name: 'payment_capture', type: 'payment_captures', api: 'payment_captures', model: 'PaymentCapture' }, { name: 'payment_gateway', type: 'payment_gateways', api: 'payment_gateways', model: 'PaymentGateway' }, + { name: 'payment_link', type: 'payment_links', api: 'payment_links', model: 'PaymentLink' }, { name: 'payment_method', type: 'payment_methods', api: 'payment_methods', model: 'PaymentMethod' }, { name: 'payment_option', type: 'payment_options', api: 'payment_options', model: 'PaymentOption' }, + { name: 'payment_refund', type: 'payment_refunds', api: 'payment_refunds', model: 'PaymentRefund' }, + { name: 'payment_rule', type: 'payment_rules', api: 'payment_rules', model: 'PaymentRule' }, + { name: 'payment_session', type: 'payment_sessions', api: 'payment_sessions', model: 'PaymentSession' }, + { name: 'payment_setting', type: 'payment_settings', api: 'payment_settings', model: 'PaymentSetting' }, + { name: 'payment_setting_adyen', type: 'payment_setting_adyens', api: 'payment_setting_adyens', model: 'PaymentSettingAdyen' }, + { name: 'payment_setting_braintree', type: 'payment_setting_braintrees', api: 'payment_setting_braintrees', model: 'PaymentSettingBraintree' }, + { name: 'payment_setting_checkout_com', type: 'payment_setting_checkout_coms', api: 'payment_setting_checkout_coms', model: 'PaymentSettingCheckoutCom' }, + { name: 'payment_setting_external', type: 'payment_setting_externals', api: 'payment_setting_externals', model: 'PaymentSettingExternal' }, + { name: 'payment_setting_gift_card', type: 'payment_setting_gift_cards', api: 'payment_setting_gift_cards', model: 'PaymentSettingGiftCard' }, + { name: 'payment_setting_manual', type: 'payment_setting_manuals', api: 'payment_setting_manuals', model: 'PaymentSettingManual' }, + { name: 'payment_setting_paypal', type: 'payment_setting_paypals', api: 'payment_setting_paypals', model: 'PaymentSettingPaypal' }, + { name: 'payment_setting_stripe', type: 'payment_setting_stripes', api: 'payment_setting_stripes', model: 'PaymentSettingStripe' }, + { name: 'payment_transaction', type: 'payment_transactions', api: 'payment_transactions', model: 'PaymentTransaction' }, + { name: 'payment_void', type: 'payment_voids', api: 'payment_voids', model: 'PaymentVoid' }, + { name: 'payment_wallet', type: 'payment_wallets', api: 'payment_wallets', model: 'PaymentWallet' }, { name: 'paypal_gateway', type: 'paypal_gateways', api: 'paypal_gateways', model: 'PaypalGateway' }, { name: 'paypal_payment', type: 'paypal_payments', api: 'paypal_payments', model: 'PaypalPayment' }, { name: 'percentage_discount_promotion', type: 'percentage_discount_promotions', api: 'percentage_discount_promotions', model: 'PercentageDiscountPromotion' }, { name: 'pickup', type: 'pickups', api: 'pickups', model: 'Pickup' }, + { name: 'price', type: 'prices', api: 'prices', model: 'Price' }, { name: 'price_frequency_tier', type: 'price_frequency_tiers', api: 'price_frequency_tiers', model: 'PriceFrequencyTier' }, - { name: 'price_list_scheduler', type: 'price_list_schedulers', api: 'price_list_schedulers', model: 'PriceListScheduler' }, { name: 'price_list', type: 'price_lists', api: 'price_lists', model: 'PriceList' }, + { name: 'price_list_scheduler', type: 'price_list_schedulers', api: 'price_list_schedulers', model: 'PriceListScheduler' }, { name: 'price_tier', type: 'price_tiers', api: 'price_tiers', model: 'PriceTier' }, { name: 'price_volume_tier', type: 'price_volume_tiers', api: 'price_volume_tiers', model: 'PriceVolumeTier' }, - { name: 'price', type: 'prices', api: 'prices', model: 'Price' }, - { name: 'promotion_rule', type: 'promotion_rules', api: 'promotion_rules', model: 'PromotionRule' }, { name: 'promotion', type: 'promotions', api: 'promotions', model: 'Promotion' }, + { name: 'promotion_rule', type: 'promotion_rules', api: 'promotion_rules', model: 'PromotionRule' }, { name: 'recurring_order_copy', type: 'recurring_order_copies', api: 'recurring_order_copies', model: 'RecurringOrderCopy' }, { name: 'refund', type: 'refunds', api: 'refunds', model: 'Refund' }, { name: 'reserved_stock', type: 'reserved_stocks', api: 'reserved_stocks', model: 'ReservedStock' }, { name: 'resource_error', type: 'resource_errors', api: 'resource_errors', model: 'ResourceError' }, - { name: 'return_line_item', type: 'return_line_items', api: 'return_line_items', model: 'ReturnLineItem' }, { name: 'return', type: 'returns', api: 'returns', model: 'Return' }, + { name: 'return_line_item', type: 'return_line_items', api: 'return_line_items', model: 'ReturnLineItem' }, { name: 'satispay_gateway', type: 'satispay_gateways', api: 'satispay_gateways', model: 'SatispayGateway' }, { name: 'satispay_payment', type: 'satispay_payments', api: 'satispay_payments', model: 'SatispayPayment' }, { name: 'shipment', type: 'shipments', api: 'shipments', model: 'Shipment' }, + { name: 'shipment_line_item', type: 'shipment_line_items', api: 'shipment_line_items', model: 'ShipmentLineItem' }, { name: 'shipping_category', type: 'shipping_categories', api: 'shipping_categories', model: 'ShippingCategory' }, - { name: 'shipping_method_tier', type: 'shipping_method_tiers', api: 'shipping_method_tiers', model: 'ShippingMethodTier' }, { name: 'shipping_method', type: 'shipping_methods', api: 'shipping_methods', model: 'ShippingMethod' }, + { name: 'shipping_method_tier', type: 'shipping_method_tiers', api: 'shipping_method_tiers', model: 'ShippingMethodTier' }, { name: 'shipping_weight_tier', type: 'shipping_weight_tiers', api: 'shipping_weight_tiers', model: 'ShippingWeightTier' }, { name: 'shipping_zone', type: 'shipping_zones', api: 'shipping_zones', model: 'ShippingZone' }, + { name: 'sku', type: 'skus', api: 'skus', model: 'Sku' }, + { name: 'sku_list', type: 'sku_lists', api: 'sku_lists', model: 'SkuList' }, { name: 'sku_list_item', type: 'sku_list_items', api: 'sku_list_items', model: 'SkuListItem' }, { name: 'sku_list_promotion_rule', type: 'sku_list_promotion_rules', api: 'sku_list_promotion_rules', model: 'SkuListPromotionRule' }, - { name: 'sku_list', type: 'sku_lists', api: 'sku_lists', model: 'SkuList' }, { name: 'sku_option', type: 'sku_options', api: 'sku_options', model: 'SkuOption' }, - { name: 'sku', type: 'skus', api: 'skus', model: 'Sku' }, { name: 'stock_item', type: 'stock_items', api: 'stock_items', model: 'StockItem' }, { name: 'stock_line_item', type: 'stock_line_items', api: 'stock_line_items', model: 'StockLineItem' }, { name: 'stock_location', type: 'stock_locations', api: 'stock_locations', model: 'StockLocation' }, diff --git a/plugins/resources/test/commands/resources/schema.test.ts b/plugins/resources/test/commands/resources/schema.test.ts index 5e5f9f784..c60bddaf3 100644 --- a/plugins/resources/test/commands/resources/schema.test.ts +++ b/plugins/resources/test/commands/resources/schema.test.ts @@ -5,6 +5,6 @@ describe('resources:schema', () => { it('prints the schema version of the SDK', async () => { const ctx = await runCommand(['resources:schema']) if (ctx.error) throw ctx.error - expect(ctx.stdout).to.match(/Current schema version: \d+\.\d+\.\d+/) + expect(ctx.stdout).to.match(/Current schema version: (\d+\.\d+\.\d+|\d{4}-\d{2})/) }) }) diff --git a/plugins/seeder/src/base.ts b/plugins/seeder/src/base.ts index 2c9d61bab..07db2ab1b 100644 --- a/plugins/seeder/src/base.ts +++ b/plugins/seeder/src/base.ts @@ -1,6 +1,6 @@ import { type ApiMode, clApi, clColor, clToken, clUpdate, clUtil, type Method } from '@commercelayer/cli-core' import * as cliux from '@commercelayer/cli-ux' -import type { CommerceLayerClient, ListResponse, QueryParamsList, Resource, ResourceId } from '@commercelayer/sdk' +import type { ApiVersion, CommerceLayerClient, ListResponse, QueryParamsList, Resource, ResourceId } from '@commercelayer/sdk' import commercelayer, { CommerceLayerStatic } from '@commercelayer/sdk' import type { Interfaces } from '@oclif/core' import { Command, type Errors, Flags } from '@oclif/core' @@ -82,7 +82,7 @@ export default abstract class extends Command { const accessToken: string = flags.accessToken const userAgent = clUtil.userAgent(this.config) - this.cl = commercelayer({ organization, domain, accessToken, userAgent }) + this.cl = commercelayer({ apiVersion: clApi.apiVersion(flags) as ApiVersion, organization, domain, accessToken, userAgent }) this.environment = clToken.getTokenEnvironment(accessToken) diff --git a/plugins/seeder/src/commands/seeder/clean.ts b/plugins/seeder/src/commands/seeder/clean.ts index 05eb328ff..ee6427fdb 100644 --- a/plugins/seeder/src/commands/seeder/clean.ts +++ b/plugins/seeder/src/commands/seeder/clean.ts @@ -1,6 +1,6 @@ /* eslint-disable no-await-in-loop */ -import { clApi, clColor, clText } from '@commercelayer/cli-core' +import { clApi, clColor, clCommand, clText } from '@commercelayer/cli-core' import { type CommerceLayerClient, CommerceLayerStatic, type ResourceId } from '@commercelayer/sdk' import Listr from 'listr' import Command, { Flags } from '../../base' @@ -28,6 +28,7 @@ export default class SeederClean extends Command { dependsOn: ['organization'], env: 'CL_CLI_DOMAIN', }), + 'api-version': clCommand.apiVersionFlag(), accessToken: Flags.string({ hidden: true, required: true, diff --git a/plugins/seeder/src/commands/seeder/seed.ts b/plugins/seeder/src/commands/seeder/seed.ts index 699b24341..91e959c52 100644 --- a/plugins/seeder/src/commands/seeder/seed.ts +++ b/plugins/seeder/src/commands/seeder/seed.ts @@ -1,6 +1,6 @@ /* eslint-disable no-await-in-loop */ -import { clApi, clColor, clText, clToken, clUtil } from '@commercelayer/cli-core' +import { clApi, clColor, clCommand, clText, clToken, clUtil } from '@commercelayer/cli-core' import type { CommerceLayerClient, ResourceCreate, ResourceId, ResourceUpdate } from '@commercelayer/sdk' import Listr from 'listr' import Command, { Flags } from '../../base' @@ -38,6 +38,7 @@ export default class SeederSeed extends Command { dependsOn: ['organization'], env: 'CL_CLI_DOMAIN', }), + 'api-version': clCommand.apiVersionFlag(), accessToken: Flags.string({ hidden: true, required: true, diff --git a/plugins/seeder/src/fix.ts b/plugins/seeder/src/fix.ts index 33451ef5e..07727f5b5 100644 --- a/plugins/seeder/src/fix.ts +++ b/plugins/seeder/src/fix.ts @@ -1,3 +1,5 @@ +import { clApi } from '@commercelayer/cli-core' +import type { ApiVersion } from '@commercelayer/sdk' /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-misused-promises */ /* eslint-disable prefer-regex-literals */ @@ -129,6 +131,7 @@ async function fix(): Promise { const cl = commercelayer({ + apiVersion: clApi.apiVersion() as ApiVersion, organization: process.env.CL_CLI_ORGANIZATION || 'cli-test-org', accessToken: process.env.CL_CLI_ACCESS_TOKEN || '', }) diff --git a/plugins/tags/src/base.ts b/plugins/tags/src/base.ts index 2a54db187..7add61053 100644 --- a/plugins/tags/src/base.ts +++ b/plugins/tags/src/base.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/unbound-method */ -import { clColor, clConfig, clOutput, clToken, clUpdate, clUtil } from '@commercelayer/cli-core' +import { clApi, clColor, clCommand, clConfig, clOutput, clToken, clUpdate, clUtil } from '@commercelayer/cli-core' import * as cliux from '@commercelayer/cli-ux' -import type { CommerceLayerClient, ListResponse, Tag, TaggableResource, TaggableResourceType } from '@commercelayer/sdk' +import type { ApiVersion, CommerceLayerClient, ListResponse, Tag, TaggableResource, TaggableResourceType } from '@commercelayer/sdk' import commercelayer, { CommerceLayerStatic } from '@commercelayer/sdk' import type { Interfaces } from '@oclif/core' import { Args, Command, Flags } from '@oclif/core' @@ -31,6 +31,7 @@ export default abstract class BaseCommand extends Command { dependsOn: ['organization'], env: 'CL_CLI_DOMAIN', }), + 'api-version': clCommand.apiVersionFlag(), accessToken: Flags.string({ hidden: true, required: true, @@ -91,6 +92,7 @@ export default abstract class BaseCommand extends Command { const userAgent = clUtil.userAgent(this.config) this.cl = commercelayer({ + apiVersion: clApi.apiVersion(flags) as ApiVersion, organization, domain, accessToken, diff --git a/plugins/token/src/token.ts b/plugins/token/src/token.ts index 88aa18c0c..ab519cdca 100644 --- a/plugins/token/src/token.ts +++ b/plugins/token/src/token.ts @@ -1,5 +1,6 @@ import type { AccessToken, AccessTokenInfo, AppAuth, CustomToken } from '@commercelayer/cli-core' -import { clConfig, clToken } from '@commercelayer/cli-core' +import { clApi, clConfig, clToken } from '@commercelayer/cli-core' +import type { ApiVersion } from '@commercelayer/sdk' import commercelayer from '@commercelayer/sdk' @@ -36,6 +37,7 @@ const testAccessToken = async (token: CustomToken | string, flags: any): Promise const accessToken = (typeof token === 'string') ? token : token.accessToken const cl = commercelayer({ + apiVersion: clApi.apiVersion(flags) as ApiVersion, organization, domain, accessToken, diff --git a/plugins/triggers/src/base.ts b/plugins/triggers/src/base.ts index 5b96034c5..4ccf8b69e 100644 --- a/plugins/triggers/src/base.ts +++ b/plugins/triggers/src/base.ts @@ -1,4 +1,4 @@ -import { clApi, clColor, clOutput, clUpdate } from '@commercelayer/cli-core' +import { clApi, clColor, clCommand, clOutput, clUpdate } from '@commercelayer/cli-core' import { CommerceLayerStatic, type Resource } from '@commercelayer/sdk' import type { Interfaces } from '@oclif/core' import { Args, Command, type Errors, Flags } from '@oclif/core' @@ -28,6 +28,7 @@ export default abstract class extends Command { dependsOn: ['organization'], env: 'CL_CLI_DOMAIN', }), + 'api-version': clCommand.apiVersionFlag(), accessToken: Flags.string({ hidden: true, required: true, diff --git a/plugins/triggers/src/exec.ts b/plugins/triggers/src/exec.ts index 313687d49..9b60f4c84 100644 --- a/plugins/triggers/src/exec.ts +++ b/plugins/triggers/src/exec.ts @@ -1,5 +1,5 @@ import { clApi, clColor, clText, clUtil } from '@commercelayer/cli-core' -import type { CommerceLayerClient, QueryParamsRetrieve, Resource } from '@commercelayer/sdk' +import type { ApiVersion, CommerceLayerClient, QueryParamsRetrieve, Resource } from '@commercelayer/sdk' import commercelayer, { CommerceLayerStatic } from '@commercelayer/sdk' import type { Interfaces } from '@oclif/core' import { Errors } from '@oclif/core' @@ -18,6 +18,7 @@ const commercelayerInit = (flags: any, config?: Config): CommerceLayerClient => const userAgent = config? clUtil.userAgent(config) : undefined return commercelayer({ + apiVersion: clApi.apiVersion(flags) as ApiVersion, organization, domain, accessToken, diff --git a/plugins/webhooks/src/base.ts b/plugins/webhooks/src/base.ts index b8f2915db..30cd1f50c 100644 --- a/plugins/webhooks/src/base.ts +++ b/plugins/webhooks/src/base.ts @@ -1,5 +1,6 @@ -import { clColor, clOutput, clUpdate, clUtil } from '@commercelayer/cli-core' +import { clApi, clColor, clCommand, clOutput, clUpdate, clUtil } from '@commercelayer/cli-core' import * as cliux from '@commercelayer/cli-ux' +import type { ApiVersion } from '@commercelayer/sdk' import commercelayer, { type CommerceLayerClient, CommerceLayerStatic } from '@commercelayer/sdk' import type { Interfaces } from '@oclif/core' import { Args, Command, Flags } from '@oclif/core' @@ -27,6 +28,7 @@ export abstract class BaseCommand extends Command { dependsOn: ['organization'], env: 'CL_CLI_DOMAIN' }), + 'api-version': clCommand.apiVersionFlag(), accessToken: Flags.string({ hidden: true, required: true, @@ -72,6 +74,7 @@ export abstract class BaseCommand extends Command { const userAgent = clUtil.userAgent(this.config) return commercelayer({ + apiVersion: clApi.apiVersion(flags) as ApiVersion, organization, domain, accessToken, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f038d4336..071aca0b4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -168,11 +168,11 @@ catalogs: specifier: ^8.0.1 version: 8.0.1 '@commercelayer/provisioning-sdk': - specifier: ^2.10.2 - version: 2.10.2 + specifier: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/provisioning-sdk@5259929 + version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/provisioning-sdk@5259929 '@commercelayer/sdk': - specifier: ^6.58.0 - version: 6.58.0 + specifier: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 + version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 '@oclif/core': specifier: ^5.1.2 version: 5.1.2 @@ -285,10 +285,10 @@ importers: version: link:../ux '@commercelayer/provisioning-sdk': specifier: 'catalog:' - version: 2.10.2 + version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/provisioning-sdk@5259929 '@commercelayer/sdk': specifier: 'catalog:' - version: 6.58.0 + version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -636,7 +636,7 @@ importers: version: link:../../packages/core '@commercelayer/sdk': specifier: 'catalog:' - version: 6.58.0 + version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -700,7 +700,7 @@ importers: version: link:../../packages/ux '@commercelayer/sdk': specifier: 'catalog:' - version: 6.58.0 + version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -779,7 +779,7 @@ importers: version: link:../../packages/ux '@commercelayer/sdk': specifier: 'catalog:' - version: 6.58.0 + version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -858,7 +858,7 @@ importers: version: link:../../packages/ux '@commercelayer/sdk': specifier: 'catalog:' - version: 6.58.0 + version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -931,7 +931,7 @@ importers: version: link:../../packages/core '@commercelayer/sdk': specifier: 'catalog:' - version: 6.58.0 + version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1004,7 +1004,7 @@ importers: version: link:../../packages/ux '@commercelayer/sdk': specifier: 'catalog:' - version: 6.58.0 + version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1068,7 +1068,7 @@ importers: version: link:../../packages/core '@commercelayer/sdk': specifier: 'catalog:' - version: 6.58.0 + version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1132,7 +1132,7 @@ importers: version: link:../../packages/ux '@commercelayer/sdk': specifier: 'catalog:' - version: 6.58.0 + version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1199,7 +1199,7 @@ importers: version: link:../../packages/ux '@commercelayer/provisioning-sdk': specifier: 'catalog:' - version: 2.10.2 + version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/provisioning-sdk@5259929 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1269,7 +1269,7 @@ importers: version: link:../../packages/ux '@commercelayer/sdk': specifier: 'catalog:' - version: 6.58.0 + version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1348,7 +1348,7 @@ importers: version: link:../../packages/ux '@commercelayer/sdk': specifier: 'catalog:' - version: 6.58.0 + version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1415,7 +1415,7 @@ importers: version: link:../../packages/ux '@commercelayer/sdk': specifier: 'catalog:' - version: 6.58.0 + version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1485,7 +1485,7 @@ importers: version: 8.0.1 '@commercelayer/sdk': specifier: 'catalog:' - version: 6.58.0 + version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1546,7 +1546,7 @@ importers: version: link:../../packages/core '@commercelayer/sdk': specifier: 'catalog:' - version: 6.58.0 + version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1613,7 +1613,7 @@ importers: version: link:../../packages/ux '@commercelayer/sdk': specifier: 'catalog:' - version: 6.58.0 + version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1886,12 +1886,14 @@ packages: resolution: {integrity: sha512-5lt18iBdvvSsjIgLXvfkdEWGyW/f92ulTFu8y8MzkUSdBQS5wjXyHRexpZeDd4/UTn9jBp4znjqPiHOPi9g+FQ==} engines: {node: '>=20.0.0'} - '@commercelayer/provisioning-sdk@2.10.2': - resolution: {integrity: sha512-5uqqH5CXXAQPuMrz+IGbF2HcQaKzgMzF7z4ye/mdo9Ygp8uMMNYH5jnaPB4QvYBVOMSi8r20egF3B9sFvqILLQ==} + '@commercelayer/provisioning-sdk@https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/provisioning-sdk@5259929': + resolution: {integrity: sha512-hlp61OGrVVuTi3n0HdRIw6f5O8B6uoTiJ3ub2W6Z83gjGrxB0LR84ApddJf6Xiz/EWJDn9RDBw5KmPURPuRTmw==, tarball: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/provisioning-sdk@5259929} + version: 3.0.0-beta.1 engines: {node: '>=20'} - '@commercelayer/sdk@6.58.0': - resolution: {integrity: sha512-AoTMeIXim3/JNbuffvMtE4XY0eGn7qVOxdTFpVvuQrz5LVSimJYEFL/VMzqMap4vukHsZdYq4p5X+ZzXXP/Jbg==} + '@commercelayer/sdk@https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929': + resolution: {integrity: sha512-KRF6CGX7KfDUTmNR14n8IJIdxQoEndGFgmOGOHPohvFxadvnexhTgsA2DCNh8JrPdnHHCbGZx/upLlrqLlKm0Q==, tarball: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929} + version: 8.0.0-beta.13 engines: {node: '>=20'} '@cspotcode/source-map-support@0.8.1': @@ -5239,9 +5241,9 @@ snapshots: '@commercelayer/js-auth@8.0.1': {} - '@commercelayer/provisioning-sdk@2.10.2': {} + '@commercelayer/provisioning-sdk@https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/provisioning-sdk@5259929': {} - '@commercelayer/sdk@6.58.0': {} + '@commercelayer/sdk@https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929': {} '@cspotcode/source-map-support@0.8.1': dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 517a41a25..d3643f67b 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,8 +6,8 @@ packages: # `"": "catalog:"` and the version lives only here. catalog: '@commercelayer/js-auth': ^8.0.1 - '@commercelayer/provisioning-sdk': ^2.10.2 - '@commercelayer/sdk': ^6.58.0 + '@commercelayer/provisioning-sdk': https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/provisioning-sdk@5259929 + '@commercelayer/sdk': https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 '@oclif/core': ^5.1.2 '@oclif/plugin-help': ^7.0.2 '@oclif/test': ^5.0.1 From 27d0a50a19c1559ac3797fc67aead41918722c93 Mon Sep 17 00:00:00 2001 From: PIERLUIGI VITI Date: Fri, 2 Oct 2026 12:47:30 +0200 Subject: [PATCH 3/5] feat(cli): --api-version works like --domain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Core API version is an operating parameter, as the domain is: the domain says where the requests go, and more API versions can answer there, so the version can be given, or a default applies. - The --api-version flag of the commands is hidden, set by CL_CLI_API_VERSION (as --domain by CL_CLI_DOMAIN). - applications:login and applications:add take --api-version and save it with the application, as the domain; the prerun hook adds it to the commands' flags, and applications:scope keeps it. - The default is clConfig.api.default_api_version (as default_domain), unversioned for now: production doesn't serve /api//… yet. A version equal to the default isn't saved. - applications --extra shows the API VERSION column. Co-Authored-By: Claude Opus 5.5 --- packages/cli/src/commands/applications/add.ts | 2 + .../cli/src/commands/applications/index.ts | 1 + .../cli/src/commands/applications/login.ts | 9 ++- .../cli/src/commands/applications/scope.ts | 1 + packages/cli/src/config.ts | 3 + packages/cli/src/hooks/prerun/login.ts | 1 + .../hooks/prerun/application-flags.test.ts | 58 +++++++++++++++++++ packages/core/src/api.ts | 9 +-- packages/core/src/application.ts | 2 + packages/core/src/command.ts | 8 ++- packages/core/src/config.ts | 4 ++ packages/core/test/unit/api.test.ts | 14 +++++ 12 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 packages/cli/test/hooks/prerun/application-flags.test.ts diff --git a/packages/cli/src/commands/applications/add.ts b/packages/cli/src/commands/applications/add.ts index a896ed673..75a55048b 100644 --- a/packages/cli/src/commands/applications/add.ts +++ b/packages/cli/src/commands/applications/add.ts @@ -57,12 +57,14 @@ export default class ApplicationsAdd extends Command { clientSecret: flags.clientSecret, slug: flags.organization, domain: flags.domain, + apiVersion: flags['api-version'], scope, email: flags.email, password: flags.password } if (config.domain === configParam(ConfigParams.defaultDomain)) config.domain = undefined + if (!config.apiVersion || (config.apiVersion === configParam(ConfigParams.defaultApiVersion))) config.apiVersion = undefined try { diff --git a/packages/cli/src/commands/applications/index.ts b/packages/cli/src/commands/applications/index.ts index e1c5ae660..3b61bafd2 100644 --- a/packages/cli/src/commands/applications/index.ts +++ b/packages/cli/src/commands/applications/index.ts @@ -85,6 +85,7 @@ const extraColumns = (flags: any): any => { extra.id = { header: 'ID', get: (row: { id: any }) => clColor.dim(row.id || '') } extra.appkey = { header: 'APPKEY', get: (row: { key: any }) => clColor.dim(row.key || '') } extra.domain = { header: 'DOMAIN', get: (row: { domain: any }) => clColor.dim(row.domain || '') } + extra.apiVersion = { header: 'API VERSION', get: (row: { apiVersion: any }) => clColor.dim(row.apiVersion || '') } } return extra } diff --git a/packages/cli/src/commands/applications/login.ts b/packages/cli/src/commands/applications/login.ts index d7e10417e..e3bba42b9 100644 --- a/packages/cli/src/commands/applications/login.ts +++ b/packages/cli/src/commands/applications/login.ts @@ -39,6 +39,11 @@ export default class ApplicationsLogin extends Command { required: false, hidden: true }), + 'api-version': Flags.string({ + description: 'Core API version (default: the CLI default), saved with the application', + required: false, + hidden: true + }), clientId: Flags.string({ name: 'clientId', char: 'i', @@ -112,12 +117,14 @@ export default class ApplicationsLogin extends Command { clientSecret: flags.clientSecret, slug: flags.organization, domain: flags.domain, + apiVersion: flags['api-version'], scope, email: flags.email, password: flags.password } if (config.domain === configParam(ConfigParams.defaultDomain)) config.domain = undefined + if (!config.apiVersion || (config.apiVersion === configParam(ConfigParams.defaultApiVersion))) config.apiVersion = undefined try { @@ -188,7 +195,7 @@ const getApplicationInfo = async (auth: AppAuth, accessToken: string): Promise { error(cl.organization.type()) }) // Application info diff --git a/packages/cli/src/commands/applications/scope.ts b/packages/cli/src/commands/applications/scope.ts index 62ee8494e..6df943aea 100644 --- a/packages/cli/src/commands/applications/scope.ts +++ b/packages/cli/src/commands/applications/scope.ts @@ -59,6 +59,7 @@ export default class ApplicationsScope extends Command { const argv: string[] = [] if (current.slug) argv.push('-o', current.slug) if (current.domain) argv.push('-d', current.domain) + if (current.apiVersion) argv.push('--api-version', current.apiVersion) let appInfo: AppInfo diff --git a/packages/cli/src/config.ts b/packages/cli/src/config.ts index 54304c0d1..2a2d64e8d 100644 --- a/packages/cli/src/config.ts +++ b/packages/cli/src/config.ts @@ -139,6 +139,7 @@ const currentApplication = (app?: AppInfo): AppInfo | undefined => { name: app.name, slug: app.slug, domain: app.domain, + apiVersion: app.apiVersion, kind: app.kind, mode: app.mode, organization: app.organization, @@ -191,6 +192,7 @@ enum ConfigParams { applicationTypeCheck = 'applicationTypeCheck', scopeCheck = 'scopeCheck', defaultDomain = 'defaultDomain', + defaultApiVersion = 'defaultApiVersion', test = 'test' } @@ -203,6 +205,7 @@ const defaultConfig: any = { test: 'defaultTestValue', commandRetention: 30, // days of retention defaultDomain: clConfig.api.default_domain, + defaultApiVersion: clConfig.api.default_api_version, applicationTypeCheck: clConfig.cli.applications, scopeCheck: clConfig.application.login_scopes } diff --git a/packages/cli/src/hooks/prerun/login.ts b/packages/cli/src/hooks/prerun/login.ts index 8654f6c3f..52d43583c 100644 --- a/packages/cli/src/hooks/prerun/login.ts +++ b/packages/cli/src/hooks/prerun/login.ts @@ -81,6 +81,7 @@ const hook: Hook<'prerun'> = async function (opts) { // Add to command line args application info read from config file if (_flags.organization && configData.slug) opts.argv.splice(ffIdx, 0, '--organization=' + configData.slug) if (_flags.domain && configData.domain) opts.argv.splice(ffIdx, 0, '--domain=' + configData.domain) + if (_flags['api-version'] && configData.apiVersion) opts.argv.splice(ffIdx, 0, '--api-version=' + configData.apiVersion) // If command requires clientId and clientSecret (or scope) add them to the command line arguments if (_flags.clientId && configData.clientId) opts.argv.splice(ffIdx, 0, '--clientId=' + configData.clientId) diff --git a/packages/cli/test/hooks/prerun/application-flags.test.ts b/packages/cli/test/hooks/prerun/application-flags.test.ts new file mode 100644 index 000000000..37dad6dfb --- /dev/null +++ b/packages/cli/test/hooks/prerun/application-flags.test.ts @@ -0,0 +1,58 @@ +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { expect } from 'chai' +import hook from '../../../src/hooks/prerun/login' + +/** + * The application settings saved at login reach the commands as flags: the + * domain and the Core API version are added to the command line, unless the + * command has no such flag. Runs on a temporary config folder, with the + * application passed by --appkey: the saved current application is not used. + */ +describe('prerun hook: saved application flags', () => { + let configDir: string + const key = 'testAppKey' + + const save = (app: Record) => { + const apps = join(configDir, 'applications') + mkdirSync(apps, { recursive: true }) + writeFileSync(join(apps, `${key}.config.json`), JSON.stringify({ key, mode: 'test', kind: 'integration', slug: 'cli-test-org', ...app })) + writeFileSync(join(apps, `${key}.token.json`), JSON.stringify({ accessToken: 'token', expires: new Date(Date.now() + 3_600_000).toISOString() })) + } + + const run = async (flags: string[]): Promise => { + const config = { configDir } as any + const Command = { id: 'resources:list', flags: Object.fromEntries(flags.map((f) => [f, {}])), baseFlags: {} } as any + const argv = ['skus', `--appkey=${key}`] + await hook.call({ config, error: (msg: string) => { throw new Error(msg) }, log: () => {} } as any, { Command, argv, config } as any) + return argv + } + + beforeEach(() => { + configDir = mkdtempSync(join(tmpdir(), 'cl-cli-prerun-')) + }) + afterEach(() => rmSync(configDir, { recursive: true, force: true })) + + it('adds the saved domain and API version', async () => { + save({ domain: 'commercelayer.co', apiVersion: '2026-05' }) + const argv = await run(['organization', 'domain', 'api-version', 'accessToken']) + expect(argv).to.include('--organization=cli-test-org') + expect(argv).to.include('--domain=commercelayer.co') + expect(argv).to.include('--api-version=2026-05') + expect(argv).to.include('--accessToken=token') + }) + + it('adds no API version when none is saved, so the default or CL_CLI_API_VERSION apply', async () => { + save({}) + const argv = await run(['organization', 'domain', 'api-version', 'accessToken']) + expect(argv.some((a) => a.startsWith('--api-version'))).to.equal(false) + expect(argv.some((a) => a.startsWith('--domain'))).to.equal(false) + }) + + it('adds no API version to a command without the flag', async () => { + save({ apiVersion: '2026-05' }) + const argv = await run(['organization', 'accessToken']) + expect(argv.some((a) => a.startsWith('--api-version'))).to.equal(false) + }) +}) diff --git a/packages/core/src/api.ts b/packages/core/src/api.ts index 60d3b93be..d965d7e06 100644 --- a/packages/core/src/api.ts +++ b/packages/core/src/api.ts @@ -26,12 +26,13 @@ const extractDomain = (baseUrl: string): string | undefined => { /** - * Core API version of the requests: the --api-version flag, then CL_CLI_API_VERSION. - * SDK 8 puts a version in the path (/api//…), which the production API - * doesn't serve yet: without one the requests stay unversioned (/api/…). + * Core API version of the requests, as the domain is for their URL: the + * --api-version flag (saved with the application at login, or set by + * CL_CLI_API_VERSION), otherwise the default version (config.api.default_api_version). + * Undefined means unversioned requests (/api/…). */ const apiVersion = (flags?: Record): string | undefined => { - return flags?.['api-version'] || process.env.CL_CLI_API_VERSION || undefined + return flags?.['api-version'] || process.env.CL_CLI_API_VERSION || config.api.default_api_version || undefined } diff --git a/packages/core/src/application.ts b/packages/core/src/application.ts index db9101022..76b62ab6a 100644 --- a/packages/core/src/application.ts +++ b/packages/core/src/application.ts @@ -15,6 +15,8 @@ interface AppKey { interface AppAuth { slug?: string domain?: string + /** Core API version of the requests (see clApi.apiVersion), unversioned when missing */ + apiVersion?: string clientId: string clientSecret?: string scope?: AuthScope diff --git a/packages/core/src/command.ts b/packages/core/src/command.ts index bd99e242b..10a3ae92d 100644 --- a/packages/core/src/command.ts +++ b/packages/core/src/command.ts @@ -16,11 +16,15 @@ export const commandFlags = (flags: T, exclude?: Array, app: Partial, user: any if (provisioning) { - const clp = clprovisioning({ domain: auth.domain, accessToken }) + // Unversioned requests (/api/…): provisioning-sdk 3 types require 2026-05, the SDK omits the segment when undefined + const clp = clprovisioning({ apiVersion: undefined as unknown as Parameters[0]['apiVersion'], domain: auth.domain, accessToken }) // User info const usr = await clp.user.retrieve().catch(() => { error(clp.user.type()) }) if (usr) user = { name: `${usr.first_name}${(usr.first_name && usr.last_name)? ' ' : ''}${usr.last_name}`, email: usr.email } diff --git a/plugins/provisioning/src/base.ts b/plugins/provisioning/src/base.ts index ea275344c..bb38e9690 100644 --- a/plugins/provisioning/src/base.ts +++ b/plugins/provisioning/src/base.ts @@ -20,6 +20,7 @@ import { import type { Interfaces } from "@oclif/core" import { Args, Command, ux as cliux, Flags } from "@oclif/core" import commercelayer, { + API_VERSION, type CommerceLayerProvisioningClient, CommerceLayerProvisioningStatic, type QueryParams, @@ -142,7 +143,7 @@ export abstract class BaseCommand extends Command { const accessToken = flags.accessToken const userAgent = clUtil.userAgent(this.config) - const cl = commercelayer({ domain, accessToken, userAgent, ...options }) + const cl = commercelayer({ apiVersion: API_VERSION, domain, accessToken, userAgent, ...options }) if ("cl" in this) this.cl = cl diff --git a/plugins/provisioning/src/lang/node.ts b/plugins/provisioning/src/lang/node.ts index e0b32132b..a6b1be857 100644 --- a/plugins/provisioning/src/lang/node.ts +++ b/plugins/provisioning/src/lang/node.ts @@ -18,7 +18,7 @@ const buildTypescript = ( ts += `\nconst accessToken = '${flags.accessToken}'`; if (flags.domain) ts += `\nconst domain = '${flags.domain}'`; - ts += `\n\nconst cl = commercelayer({ accessToken${flags.domain ? ", domain" : ""} })`; + ts += `\n\nconst cl = commercelayer({ apiVersion: '2026-05', accessToken${flags.domain ? ", domain" : ""} })`; if (hasParams) ts += `\n\nconst params: QueryParams${qpSuffix} = ${clOutput.printObject(params, { color: false })}`; diff --git a/plugins/provisioning/src/sdk.ts b/plugins/provisioning/src/sdk.ts index 7f2be7416..79b08632e 100644 --- a/plugins/provisioning/src/sdk.ts +++ b/plugins/provisioning/src/sdk.ts @@ -1,7 +1,7 @@ /** * @commercelayer/provisioning-sdk, typed through its ESM declarations. * - * Workaround: the CommonJS declarations of the SDK preview (lib/index.d.cts) + * Workaround: the CommonJS declarations of the SDK (lib/index.d.cts, still in 3.0.0) * end with `export =`, which hides their named exports (CommerceLayerProvisioningStatic, * QueryParams, …) from CommonJS code like this plugin. The runtime exports are fine. * Remove this module, and import from '@commercelayer/provisioning-sdk' again, @@ -21,3 +21,12 @@ export type QueryParamsList = import('@commercelayer/provisioning-sdk', { with: export type QueryParamsRetrieve = import('@commercelayer/provisioning-sdk', { with: { 'resolution-mode': 'import' } }).QueryParamsRetrieve export type RequestObj = import('@commercelayer/provisioning-sdk', { with: { 'resolution-mode': 'import' } }).RequestObj export type ResourceTypeLock = import('@commercelayer/provisioning-sdk', { with: { 'resolution-mode': 'import' } }).ResourceTypeLock +export type ApiVersion = import('@commercelayer/provisioning-sdk', { with: { 'resolution-mode': 'import' } }).ApiVersion + +/** + * The Provisioning API version of the requests. The SDK types require one + * (2026-05, the only version of provisioning-sdk 3), but the requests stay + * unversioned (/api/…), as the CLI has always made them: the SDK omits the + * path segment when the version is undefined. + */ +export const API_VERSION = undefined as unknown as ApiVersion diff --git a/plugins/resources/test/commands/resources/all.test.ts b/plugins/resources/test/commands/resources/all.test.ts index 80ae529cc..6940051b6 100644 --- a/plugins/resources/test/commands/resources/all.test.ts +++ b/plugins/resources/test/commands/resources/all.test.ts @@ -27,10 +27,10 @@ describe('resources:all', () => { const scope = api() .get('/api/skus') .query((q) => q['page[number]'] === '1') - .reply(200, { data: page(1, 25), meta: { record_count: 10_001, page_count: 1 } }) + .reply(200, { data: page(1, 25), meta: { record_count: 10_001, page_count: 1, record_count_estimated: true } }) .get('/api/skus') .query((q) => q['page[number]'] === '2') - .reply(200, { data: page(2, 3), meta: { record_count: 10_001, page_count: 1 } }) + .reply(200, { data: page(2, 3), meta: { record_count: 10_001, page_count: 1, record_count_estimated: true } }) const ctx = await runCommand(['resources:all', 'skus', ...AUTH, '-x', join(dir, 'estimated.json'), '-j', '--blind']) if (ctx.error) throw ctx.error expect(scope.isDone(), 'second page requested').to.equal(true) diff --git a/plugins/resources/test/commands/resources/count.test.ts b/plugins/resources/test/commands/resources/count.test.ts index 92c913ca6..c85a4a682 100644 --- a/plugins/resources/test/commands/resources/count.test.ts +++ b/plugins/resources/test/commands/resources/count.test.ts @@ -19,12 +19,22 @@ describe('resources:count', () => { api() .get('/api/skus') .query(true) - .reply(200, { ...list([resource('skus', 'sKu1')]), meta: { record_count: 16_998, page_count: 1700 } }) + .reply(200, { ...list([resource('skus', 'sKu1')]), meta: { record_count: 16_998, page_count: 1700, record_count_estimated: true } }) const ctx = await runCommand(['resources:count', 'skus', ...AUTH]) if (ctx.error) throw ctx.error expect(ctx.stdout + ctx.stderr).to.contain('≈16,998').and.to.contain('estimated above 10,000') }) + it('trusts the API when it says a count above 10,000 is exact', async () => { + api() + .get('/api/skus') + .query(true) + .reply(200, { ...list([resource('skus', 'sKu1')]), meta: { record_count: 16_998, page_count: 1700, record_count_estimated: false } }) + const ctx = await runCommand(['resources:count', 'skus', ...AUTH]) + if (ctx.error) throw ctx.error + expect(ctx.stdout + ctx.stderr).to.contain('16,998').and.not.to.contain('≈') + }) + it('reports zero as a count, not as an error', async () => { api().get('/api/orders').query(true).reply(200, list([])) const ctx = await runCommand(['resources:count', 'orders', ...AUTH, '-w', 'status_eq=unicorn']) diff --git a/plugins/resources/test/commands/resources/list.test.ts b/plugins/resources/test/commands/resources/list.test.ts index 84b9ae124..37b13bb91 100644 --- a/plugins/resources/test/commands/resources/list.test.ts +++ b/plugins/resources/test/commands/resources/list.test.ts @@ -17,7 +17,7 @@ describe('resources:list', () => { api() .get('/api/customers') .query(true) - .reply(200, { ...list([resource('customers', 'cUs1', { email: 'jane@example.com' })]), meta: { record_count: 12_345, page_count: 1235 } }) + .reply(200, { ...list([resource('customers', 'cUs1', { email: 'jane@example.com' })]), meta: { record_count: 12_345, page_count: 1235, record_count_estimated: true } }) const ctx = await runCommand(['resources:list', 'customers', ...AUTH]) if (ctx.error) throw ctx.error expect(ctx.stdout).to.match(/Records: .*1.* of ≈12,345 \| Page: .*1.* of ≈1,235/) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8c6af10b4..83c6cdcf8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -168,11 +168,11 @@ catalogs: specifier: ^8.0.1 version: 8.0.1 '@commercelayer/provisioning-sdk': - specifier: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/provisioning-sdk@5259929 - version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/provisioning-sdk@5259929 + specifier: ^3.0.0 + version: 3.0.0 '@commercelayer/sdk': - specifier: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 - version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 + specifier: ^8.0.0 + version: 8.0.0 '@oclif/core': specifier: ^5.1.2 version: 5.1.2 @@ -297,10 +297,10 @@ importers: version: link:../ux '@commercelayer/provisioning-sdk': specifier: 'catalog:' - version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/provisioning-sdk@5259929 + version: 3.0.0 '@commercelayer/sdk': specifier: 'catalog:' - version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 + version: 8.0.0 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -697,7 +697,7 @@ importers: version: link:../../packages/core '@commercelayer/sdk': specifier: 'catalog:' - version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 + version: 8.0.0 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -761,7 +761,7 @@ importers: version: link:../../packages/ux '@commercelayer/sdk': specifier: 'catalog:' - version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 + version: 8.0.0 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -840,7 +840,7 @@ importers: version: link:../../packages/ux '@commercelayer/sdk': specifier: 'catalog:' - version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 + version: 8.0.0 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -919,7 +919,7 @@ importers: version: link:../../packages/ux '@commercelayer/sdk': specifier: 'catalog:' - version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 + version: 8.0.0 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -992,7 +992,7 @@ importers: version: link:../../packages/core '@commercelayer/sdk': specifier: 'catalog:' - version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 + version: 8.0.0 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1065,7 +1065,7 @@ importers: version: link:../../packages/ux '@commercelayer/sdk': specifier: 'catalog:' - version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 + version: 8.0.0 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1129,7 +1129,7 @@ importers: version: link:../../packages/core '@commercelayer/sdk': specifier: 'catalog:' - version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 + version: 8.0.0 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1193,7 +1193,7 @@ importers: version: link:../../packages/ux '@commercelayer/sdk': specifier: 'catalog:' - version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 + version: 8.0.0 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1260,7 +1260,7 @@ importers: version: link:../../packages/ux '@commercelayer/provisioning-sdk': specifier: 'catalog:' - version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/provisioning-sdk@5259929 + version: 3.0.0 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1330,7 +1330,7 @@ importers: version: link:../../packages/ux '@commercelayer/sdk': specifier: 'catalog:' - version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 + version: 8.0.0 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1409,7 +1409,7 @@ importers: version: link:../../packages/ux '@commercelayer/sdk': specifier: 'catalog:' - version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 + version: 8.0.0 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1476,7 +1476,7 @@ importers: version: link:../../packages/ux '@commercelayer/sdk': specifier: 'catalog:' - version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 + version: 8.0.0 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1546,7 +1546,7 @@ importers: version: 8.0.1 '@commercelayer/sdk': specifier: 'catalog:' - version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 + version: 8.0.0 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1607,7 +1607,7 @@ importers: version: link:../../packages/core '@commercelayer/sdk': specifier: 'catalog:' - version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 + version: 8.0.0 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1674,7 +1674,7 @@ importers: version: link:../../packages/ux '@commercelayer/sdk': specifier: 'catalog:' - version: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 + version: 8.0.0 '@oclif/core': specifier: 'catalog:' version: 5.1.2 @@ -1947,14 +1947,12 @@ packages: resolution: {integrity: sha512-5lt18iBdvvSsjIgLXvfkdEWGyW/f92ulTFu8y8MzkUSdBQS5wjXyHRexpZeDd4/UTn9jBp4znjqPiHOPi9g+FQ==} engines: {node: '>=20.0.0'} - '@commercelayer/provisioning-sdk@https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/provisioning-sdk@5259929': - resolution: {integrity: sha512-hlp61OGrVVuTi3n0HdRIw6f5O8B6uoTiJ3ub2W6Z83gjGrxB0LR84ApddJf6Xiz/EWJDn9RDBw5KmPURPuRTmw==, tarball: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/provisioning-sdk@5259929} - version: 3.0.0-beta.1 + '@commercelayer/provisioning-sdk@3.0.0': + resolution: {integrity: sha512-lNzQDBOXka/Os5ei1i+F1qqaCEC3Zt7yI4UOuPdmvZSgAk7XcKnFYfTJ7RyPbIMJeIFqOXp2qf4Yd1bIXqSqNA==} engines: {node: '>=20'} - '@commercelayer/sdk@https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929': - resolution: {integrity: sha512-KRF6CGX7KfDUTmNR14n8IJIdxQoEndGFgmOGOHPohvFxadvnexhTgsA2DCNh8JrPdnHHCbGZx/upLlrqLlKm0Q==, tarball: https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929} - version: 8.0.0-beta.13 + '@commercelayer/sdk@8.0.0': + resolution: {integrity: sha512-cdlMwi3wvK79hs9LTlE8nLsEx7JM1JNPJmtOCDRNzjG/sUiezfO96qDbQmP1Qd2PtUtapcAEqUgDmj59rtGlVw==} engines: {node: '>=20'} '@commitlint/cli@21.2.3': @@ -5621,9 +5619,9 @@ snapshots: '@commercelayer/js-auth@8.0.1': {} - '@commercelayer/provisioning-sdk@https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/provisioning-sdk@5259929': {} + '@commercelayer/provisioning-sdk@3.0.0': {} - '@commercelayer/sdk@https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929': {} + '@commercelayer/sdk@8.0.0': {} '@commitlint/cli@21.2.3(@types/node@26.6.3)(conventional-commits-filter@6.0.1)(conventional-commits-parser@7.1.2)(typescript@6.0.3)': dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index d3643f67b..8a1353059 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,8 +6,8 @@ packages: # `"": "catalog:"` and the version lives only here. catalog: '@commercelayer/js-auth': ^8.0.1 - '@commercelayer/provisioning-sdk': https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/provisioning-sdk@5259929 - '@commercelayer/sdk': https://pkg.pr.new/commercelayer/commercelayer-sdk/@commercelayer/sdk@5259929 + '@commercelayer/provisioning-sdk': ^3.0.0 + '@commercelayer/sdk': ^8.0.0 '@oclif/core': ^5.1.2 '@oclif/plugin-help': ^7.0.2 '@oclif/test': ^5.0.1 From 68ee2eaf7cb2c6d90595d98ec5872485afb4ff5f Mon Sep 17 00:00:00 2001 From: PIERLUIGI VITI Date: Tue, 6 Oct 2026 17:39:18 +0200 Subject: [PATCH 5/5] feat: the Core API version defaults to 2026-05 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The production API now serves the versioned paths (/api//…), so the requests of the commands go to the 2026-05 API unless --api-version, the version saved with the application or CL_CLI_API_VERSION says otherwise (clConfig.api.default_api_version). The Provisioning API requests stay unversioned. The API mocks of the tests (test-utils api()) match the versioned paths, whatever the version; versionedApi() checks the version itself, as the new resources tests do for the default, --api-version and CL_CLI_API_VERSION. The released SDKs are excluded from pnpm's default minimumReleaseAge (one day), to adopt them right away. Co-Authored-By: Claude Opus 5.5 --- packages/core/src/config.ts | 5 +-- packages/core/test/unit/api.test.ts | 6 +-- packages/test-utils/src/index.ts | 13 +++++- .../commands/resources/api-version.test.ts | 40 +++++++++++++++++++ plugins/resources/test/helpers.ts | 2 +- pnpm-workspace.yaml | 6 +++ 6 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 plugins/resources/test/commands/resources/api-version.test.ts diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index f4a751636..14ee131ef 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -111,7 +111,7 @@ const LINK_RESOURCE_TYPES: readonly string[] = [ type ApiConfig = { default_domain: string - /** Core API version used when none is given ('' = unversioned requests, /api/…) */ + /** Core API version used when none is given ('' would make the requests unversioned, /api/…) */ default_api_version: string default_app_domain: string default_stg_domain: string @@ -246,8 +246,7 @@ const RATE_LIMIT = { const config: Config = { api: { // CORE default_domain: 'commercelayer.io', - // Unversioned for now: the production API doesn't serve the versioned paths (/api//…) yet - default_api_version: '', + default_api_version: '2026-05', default_app_domain: 'commercelayer.app', default_stg_domain: 'commercelayer.co', token_expiration_mins: 60 * 4, // 4 hours (14400 secs) diff --git a/packages/core/test/unit/api.test.ts b/packages/core/test/unit/api.test.ts index 153621093..3cf1e687e 100644 --- a/packages/core/test/unit/api.test.ts +++ b/packages/core/test/unit/api.test.ts @@ -62,9 +62,9 @@ describe('api', () => { else process.env.CL_CLI_API_VERSION = env }) - it('is unversioned by default', () => { - expect(apiVersion()).to.equal(undefined) - expect(apiVersion({})).to.equal(undefined) + it('defaults to 2026-05', () => { + expect(apiVersion()).to.equal('2026-05') + expect(apiVersion({})).to.equal('2026-05') }) it('takes the flag, then the environment', () => { diff --git a/packages/test-utils/src/index.ts b/packages/test-utils/src/index.ts index 42d7c6a25..0ac44f46c 100644 --- a/packages/test-utils/src/index.ts +++ b/packages/test-utils/src/index.ts @@ -31,7 +31,18 @@ export const useMockedApi = (): void => { } /** A nock scope on the Core API of the test organization */ -export const api = (base: string = coreApi()): nock.Scope => nock(base) +/** The version segment of the Core API paths (/api/2026-05/…) */ +const API_VERSION_SEGMENT = /^\/api\/\d{4}-\d{2}\// + +/** + * A mock of the API. The requests carry the Core API version in their path + * (/api/2026-05/orders): the mocks are written for /api/orders and match + * whatever version the CLI uses (versionedApi checks the version). + */ +export const api = (base: string = coreApi()): nock.Scope => nock(base).filteringPath((path) => path.replace(API_VERSION_SEGMENT, '/api/')) + +/** A mock of the API that matches the exact request path, version included */ +export const versionedApi = (base: string = coreApi()): nock.Scope => nock(base) /** An unsigned JWT: the CLI commands only decode access tokens */ export const jwt = (payload: object, header: object = { alg: 'HS512', typ: 'JWT' }): string => diff --git a/plugins/resources/test/commands/resources/api-version.test.ts b/plugins/resources/test/commands/resources/api-version.test.ts new file mode 100644 index 000000000..d3d8c6ab0 --- /dev/null +++ b/plugins/resources/test/commands/resources/api-version.test.ts @@ -0,0 +1,40 @@ +import { runCommand } from '@oclif/test' +import { expect } from 'chai' +import { AUTH, list, resource, useMockedApi, versionedApi } from '../../helpers' + +/** The Core API version in the request path: 2026-05 by default, --api-version or CL_CLI_API_VERSION otherwise */ +describe('Core API version', () => { + useMockedApi() + + let env: string | undefined + beforeEach(() => { + env = process.env.CL_CLI_API_VERSION + delete process.env.CL_CLI_API_VERSION + }) + afterEach(() => { + if (env === undefined) delete process.env.CL_CLI_API_VERSION + else process.env.CL_CLI_API_VERSION = env + }) + + it('calls the 2026-05 API by default', async () => { + const scope = versionedApi().get('/api/2026-05/skus').query(true).reply(200, list([resource('skus', 'sKu1')])) + const ctx = await runCommand(['resources:list', 'skus', ...AUTH]) + if (ctx.error) throw ctx.error + expect(scope.isDone()).to.equal(true) + }) + + it('calls the version of --api-version', async () => { + const scope = versionedApi().get('/api/2017-08/skus').query(true).reply(200, list([resource('skus', 'sKu1')])) + const ctx = await runCommand(['resources:list', 'skus', ...AUTH, '--api-version', '2017-08']) + if (ctx.error) throw ctx.error + expect(scope.isDone()).to.equal(true) + }) + + it('calls the version of CL_CLI_API_VERSION', async () => { + process.env.CL_CLI_API_VERSION = '2017-08' + const scope = versionedApi().get('/api/2017-08/skus').query(true).reply(200, list([resource('skus', 'sKu1')])) + const ctx = await runCommand(['resources:list', 'skus', ...AUTH]) + if (ctx.error) throw ctx.error + expect(scope.isDone()).to.equal(true) + }) +}) diff --git a/plugins/resources/test/helpers.ts b/plugins/resources/test/helpers.ts index cfbaad6c4..c71c24f9d 100644 --- a/plugins/resources/test/helpers.ts +++ b/plugins/resources/test/helpers.ts @@ -1,7 +1,7 @@ /** Test helpers: the shared ones from @commercelayer/cli-test-utils, plus this plugin's own. */ import { accessToken, ORG } from '@commercelayer/cli-test-utils' -export { api, apiError, list, notFound, ORG, resource, single, useMockedApi } from '@commercelayer/cli-test-utils' +export { api, apiError, list, notFound, ORG, resource, single, useMockedApi, versionedApi } from '@commercelayer/cli-test-utils' /** Organization and token flags every API command needs */ export const AUTH = ['-o', ORG, '--accessToken', accessToken()] diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 8a1353059..0a8275124 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -53,3 +53,9 @@ overrides: uuid@<11.1.1: ^11.1.1 # GHSA-g7r4-m6w7-qqqr (low) - tsup pins esbuild ^0.27, fixed in 0.28.1 esbuild@<0.28.1: ^0.28.1 + +# The SDKs released on 2026-10-06, within pnpm's default minimumReleaseAge +# (1 day) when adopted: first-party packages, installed right away. +minimumReleaseAgeExclude: + - '@commercelayer/provisioning-sdk@3.0.0' + - '@commercelayer/sdk@8.0.0'