@commercetools/cli-topic-integration-layer 0.0.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +64 -0
  3. package/dist/commands/integration-layer/config/get.d.ts +9 -0
  4. package/dist/commands/integration-layer/config/get.js +22 -0
  5. package/dist/commands/integration-layer/config/get.js.map +1 -0
  6. package/dist/commands/integration-layer/config/list.d.ts +6 -0
  7. package/dist/commands/integration-layer/config/list.js +20 -0
  8. package/dist/commands/integration-layer/config/list.js.map +1 -0
  9. package/dist/commands/integration-layer/config/set.d.ts +13 -0
  10. package/dist/commands/integration-layer/config/set.js +29 -0
  11. package/dist/commands/integration-layer/config/set.js.map +1 -0
  12. package/dist/commands/integration-layer/config/unset.d.ts +9 -0
  13. package/dist/commands/integration-layer/config/unset.js +18 -0
  14. package/dist/commands/integration-layer/config/unset.js.map +1 -0
  15. package/dist/commands/integration-layer/extension/build.d.ts +12 -0
  16. package/dist/commands/integration-layer/extension/build.js +40 -0
  17. package/dist/commands/integration-layer/extension/build.js.map +1 -0
  18. package/dist/commands/integration-layer/extension/delete.d.ts +9 -0
  19. package/dist/commands/integration-layer/extension/delete.js +39 -0
  20. package/dist/commands/integration-layer/extension/delete.js.map +1 -0
  21. package/dist/commands/integration-layer/extension/invoke.d.ts +13 -0
  22. package/dist/commands/integration-layer/extension/invoke.js +89 -0
  23. package/dist/commands/integration-layer/extension/invoke.js.map +1 -0
  24. package/dist/commands/integration-layer/extension/push.d.ts +28 -0
  25. package/dist/commands/integration-layer/extension/push.js +217 -0
  26. package/dist/commands/integration-layer/extension/push.js.map +1 -0
  27. package/dist/commands/integration-layer/extension/serve.d.ts +37 -0
  28. package/dist/commands/integration-layer/extension/serve.js +528 -0
  29. package/dist/commands/integration-layer/extension/serve.js.map +1 -0
  30. package/dist/commands/integration-layer/extension/status.d.ts +6 -0
  31. package/dist/commands/integration-layer/extension/status.js +28 -0
  32. package/dist/commands/integration-layer/extension/status.js.map +1 -0
  33. package/dist/commands/integration-layer/extension/validate.d.ts +15 -0
  34. package/dist/commands/integration-layer/extension/validate.js +98 -0
  35. package/dist/commands/integration-layer/extension/validate.js.map +1 -0
  36. package/dist/commands/integration-layer/hello.d.ts +6 -0
  37. package/dist/commands/integration-layer/hello.js +10 -0
  38. package/dist/commands/integration-layer/hello.js.map +1 -0
  39. package/dist/commands/integration-layer/init.d.ts +13 -0
  40. package/dist/commands/integration-layer/init.js +279 -0
  41. package/dist/commands/integration-layer/init.js.map +1 -0
  42. package/dist/commands/integration-layer/schema/fetch.d.ts +9 -0
  43. package/dist/commands/integration-layer/schema/fetch.js +27 -0
  44. package/dist/commands/integration-layer/schema/fetch.js.map +1 -0
  45. package/dist/index.d.ts +1 -0
  46. package/dist/index.js +5 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/lib/awaitBundleState.d.ts +33 -0
  49. package/dist/lib/awaitBundleState.js +77 -0
  50. package/dist/lib/awaitBundleState.js.map +1 -0
  51. package/dist/lib/base.d.ts +50 -0
  52. package/dist/lib/base.js +85 -0
  53. package/dist/lib/base.js.map +1 -0
  54. package/dist/lib/ilClient.d.ts +93 -0
  55. package/dist/lib/ilClient.js +120 -0
  56. package/dist/lib/ilClient.js.map +1 -0
  57. package/dist/lib/sourceRevision.d.ts +39 -0
  58. package/dist/lib/sourceRevision.js +95 -0
  59. package/dist/lib/sourceRevision.js.map +1 -0
  60. package/dist/lib/tooling/apiExtension.d.ts +31 -0
  61. package/dist/lib/tooling/apiExtension.js +39 -0
  62. package/dist/lib/tooling/apiExtension.js.map +1 -0
  63. package/dist/lib/tooling/build.d.ts +18 -0
  64. package/dist/lib/tooling/build.js +54 -0
  65. package/dist/lib/tooling/build.js.map +1 -0
  66. package/dist/lib/tooling/compose.d.ts +28 -0
  67. package/dist/lib/tooling/compose.js +48 -0
  68. package/dist/lib/tooling/compose.js.map +1 -0
  69. package/dist/lib/tooling/extensions.d.ts +77 -0
  70. package/dist/lib/tooling/extensions.js +209 -0
  71. package/dist/lib/tooling/extensions.js.map +1 -0
  72. package/dist/lib/tooling/gateway.d.ts +21 -0
  73. package/dist/lib/tooling/gateway.js +65 -0
  74. package/dist/lib/tooling/gateway.js.map +1 -0
  75. package/dist/lib/tooling/loadBundle.d.ts +10 -0
  76. package/dist/lib/tooling/loadBundle.js +27 -0
  77. package/dist/lib/tooling/loadBundle.js.map +1 -0
  78. package/dist/lib/tooling/staticAnalysis.d.ts +11 -0
  79. package/dist/lib/tooling/staticAnalysis.js +167 -0
  80. package/dist/lib/tooling/staticAnalysis.js.map +1 -0
  81. package/dist/lib/tooling/validateBundle.d.ts +19 -0
  82. package/dist/lib/tooling/validateBundle.js +144 -0
  83. package/dist/lib/tooling/validateBundle.js.map +1 -0
  84. package/package.json +47 -5
@@ -0,0 +1,50 @@
1
+ import { AuthCommand, CtpAuthorizedClient } from "@commercetools/cli-plugin-auth";
2
+ /** The resolved, ready-to-use integration-layer context: where to call and as whom. */
3
+ export interface IlContext {
4
+ /** The integration-layer edge base URL, trailing slashes trimmed. */
5
+ baseUrl: string;
6
+ projectKey: string;
7
+ /** The logged-in principal's commercetools access token (the `manage_project` bearer). */
8
+ token: string;
9
+ }
10
+ /** The subset of parsed flags {@link IntegrationLayerCommand.resolveIlContext} reads. */
11
+ export interface IlFlagValues {
12
+ "integration-layer-url"?: string;
13
+ "project-key"?: string;
14
+ }
15
+ /**
16
+ * Derive the integration-layer EXTENSIONS edge base URL from the authenticated
17
+ * principal's commercetools region. The extensions edge — the connector-backend pod
18
+ * that serves the CLI's `/<project>/subgraph` + `/<project>/extension/*` routes —
19
+ * follows the same host convention as the commercetools API itself
20
+ * (`<svc>.<region>.commercetools.com`, cf. the auth client's
21
+ * `auth.<region>.commercetools.com`), so a login in region `eu-central-1.aws`
22
+ * resolves to `https://extensions.integration-layer.eu-central-1.aws.commercetools.com`
23
+ * (the production host). `--integration-layer-url` / `INTEGRATION_LAYER_URL` stays
24
+ * the explicit override — e.g. to point at a local edge or a staging edge on the
25
+ * escemo/stage zone, which don't follow the production convention. Returns undefined
26
+ * only when the region is absent, so resolveIlContext then fails loudly.
27
+ */
28
+ export declare function edgeUrlForRegion(region: string): string | undefined;
29
+ export declare abstract class IntegrationLayerCommand extends AuthCommand {
30
+ /**
31
+ * Whether the command needs a logged-in principal. True for every command that
32
+ * calls the integration layer; a command that only conditionally reaches it (e.g.
33
+ * `serve` in standalone mode) sets this false and relies on {@link resolveIlContext}
34
+ * to fail loudly if the network path is actually taken.
35
+ */
36
+ protected authorized: boolean;
37
+ static baseFlags: {
38
+ "integration-layer-url": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
39
+ "project-key": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
40
+ };
41
+ protected init(): Promise<void>;
42
+ /** The logged-in commercetools principal, or an `AuthorizationError` pointing at `auth login`. */
43
+ protected requirePrincipal(): CtpAuthorizedClient;
44
+ /**
45
+ * Resolve the integration-layer base URL + project key from the login context and
46
+ * take the principal's access token as the bearer. Fails loudly (no defensive
47
+ * defaults) when a required value is absent.
48
+ */
49
+ protected resolveIlContext(flags: IlFlagValues): Promise<IlContext>;
50
+ }
@@ -0,0 +1,85 @@
1
+ // Base class for the topic's commands that reach the integration layer's
2
+ // `manage_project` boundary. It extends the host CLI's `AuthCommand`
3
+ // (`@commercetools/cli-plugin-auth`), so the bearer is the token
4
+ // `commercetools auth login` already persisted to `~/.commercetools/credentials`
5
+ // (loaded into the security context by the auth plugin's prerun hook) — no separate
6
+ // credential handling, exactly as the `connect` topic does.
7
+ //
8
+ // `@commercetools/cli-plugin-auth` is a PEER dependency: at runtime the plugin uses
9
+ // the HOST CLI's copy, so the static auth manager/repository the host wires up are
10
+ // the same singletons this class reads (a bundled second copy would see an empty
11
+ // security context and reject every logged-in caller).
12
+ import { Flags } from "@oclif/core";
13
+ import { AuthCommand, AuthorizationError, CtpAuthorizedClient, } from "@commercetools/cli-plugin-auth";
14
+ /**
15
+ * Derive the integration-layer EXTENSIONS edge base URL from the authenticated
16
+ * principal's commercetools region. The extensions edge — the connector-backend pod
17
+ * that serves the CLI's `/<project>/subgraph` + `/<project>/extension/*` routes —
18
+ * follows the same host convention as the commercetools API itself
19
+ * (`<svc>.<region>.commercetools.com`, cf. the auth client's
20
+ * `auth.<region>.commercetools.com`), so a login in region `eu-central-1.aws`
21
+ * resolves to `https://extensions.integration-layer.eu-central-1.aws.commercetools.com`
22
+ * (the production host). `--integration-layer-url` / `INTEGRATION_LAYER_URL` stays
23
+ * the explicit override — e.g. to point at a local edge or a staging edge on the
24
+ * escemo/stage zone, which don't follow the production convention. Returns undefined
25
+ * only when the region is absent, so resolveIlContext then fails loudly.
26
+ */
27
+ export function edgeUrlForRegion(region) {
28
+ const trimmed = region.trim();
29
+ if (!trimmed)
30
+ return undefined;
31
+ return `https://extensions.integration-layer.${trimmed}.commercetools.com`;
32
+ }
33
+ export class IntegrationLayerCommand extends AuthCommand {
34
+ /**
35
+ * Whether the command needs a logged-in principal. True for every command that
36
+ * calls the integration layer; a command that only conditionally reaches it (e.g.
37
+ * `serve` in standalone mode) sets this false and relies on {@link resolveIlContext}
38
+ * to fail loudly if the network path is actually taken.
39
+ */
40
+ authorized = true;
41
+ static baseFlags = {
42
+ "integration-layer-url": Flags.string({
43
+ description: "integration-layer extensions edge base URL (also settable via INTEGRATION_LAYER_URL); overrides the URL derived from your login region",
44
+ env: "INTEGRATION_LAYER_URL",
45
+ helpGroup: "INTEGRATION LAYER",
46
+ }),
47
+ "project-key": Flags.string({
48
+ description: "override the logged-in project key",
49
+ helpGroup: "INTEGRATION LAYER",
50
+ }),
51
+ };
52
+ async init() {
53
+ await super.init();
54
+ if (this.authorized)
55
+ this.requirePrincipal();
56
+ }
57
+ /** The logged-in commercetools principal, or an `AuthorizationError` pointing at `auth login`. */
58
+ requirePrincipal() {
59
+ const principal = this.getAuthentication()?.getPrincipal();
60
+ if (principal instanceof CtpAuthorizedClient)
61
+ return principal;
62
+ throw new AuthorizationError("not logged in — run `commercetools auth login` first");
63
+ }
64
+ /**
65
+ * Resolve the integration-layer base URL + project key from the login context and
66
+ * take the principal's access token as the bearer. Fails loudly (no defensive
67
+ * defaults) when a required value is absent.
68
+ */
69
+ async resolveIlContext(flags) {
70
+ const principal = this.requirePrincipal();
71
+ const token = principal.getAccessToken().getTokenValue();
72
+ const projectKey = flags["project-key"] ?? principal.getProjectKey();
73
+ if (!projectKey) {
74
+ throw new Error("no project key — log in with `commercetools auth login --project-key <key>` or pass --project-key");
75
+ }
76
+ const baseUrl = flags["integration-layer-url"] ?? edgeUrlForRegion(principal.getRegion());
77
+ if (!baseUrl) {
78
+ throw new Error("could not resolve the integration layer URL: pass --integration-layer-url or set " +
79
+ "INTEGRATION_LAYER_URL (the extensions edge base, e.g. " +
80
+ "https://extensions.integration-layer.eu-central-1.aws.commercetools.com)");
81
+ }
82
+ return { baseUrl: baseUrl.replace(/\/+$/, ""), projectKey, token };
83
+ }
84
+ }
85
+ //# sourceMappingURL=base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/lib/base.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,qEAAqE;AACrE,iEAAiE;AACjE,iFAAiF;AACjF,oFAAoF;AACpF,4DAA4D;AAC5D,EAAE;AACF,oFAAoF;AACpF,mFAAmF;AACnF,iFAAiF;AACjF,uDAAuD;AAEvD,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,gCAAgC,CAAC;AAiBxC;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,OAAO,wCAAwC,OAAO,oBAAoB,CAAC;AAC7E,CAAC;AAED,MAAM,OAAgB,uBAAwB,SAAQ,WAAW;IAC/D;;;;;OAKG;IACO,UAAU,GAAG,IAAI,CAAC;IAE5B,MAAM,CAAU,SAAS,GAAG;QAC1B,uBAAuB,EAAE,KAAK,CAAC,MAAM,CAAC;YACpC,WAAW,EACT,wIAAwI;YAC1I,GAAG,EAAE,uBAAuB;YAC5B,SAAS,EAAE,mBAAmB;SAC/B,CAAC;QACF,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC;YAC1B,WAAW,EAAE,oCAAoC;YACjD,SAAS,EAAE,mBAAmB;SAC/B,CAAC;KACH,CAAC;IAEiB,KAAK,CAAC,IAAI;QAC3B,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,IAAI,IAAI,CAAC,UAAU;YAAE,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC/C,CAAC;IAED,kGAAkG;IACxF,gBAAgB;QACxB,MAAM,SAAS,GAAG,IAAI,CAAC,iBAAiB,EAAE,EAAE,YAAY,EAAE,CAAC;QAC3D,IAAI,SAAS,YAAY,mBAAmB;YAAE,OAAO,SAAS,CAAC;QAC/D,MAAM,IAAI,kBAAkB,CAAC,sDAAsD,CAAC,CAAC;IACvF,CAAC;IAED;;;;OAIG;IACO,KAAK,CAAC,gBAAgB,CAAC,KAAmB;QAClD,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,SAAS,CAAC,cAAc,EAAE,CAAC,aAAa,EAAE,CAAC;QAEzD,MAAM,UAAU,GAAG,KAAK,CAAC,aAAa,CAAC,IAAI,SAAS,CAAC,aAAa,EAAE,CAAC;QACrE,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CACb,mGAAmG,CACpG,CAAC;QACJ,CAAC;QAED,MAAM,OAAO,GAAG,KAAK,CAAC,uBAAuB,CAAC,IAAI,gBAAgB,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC;QAC1F,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACb,mFAAmF;gBACjF,wDAAwD;gBACxD,0EAA0E,CAC7E,CAAC;QACJ,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IACrE,CAAC"}
@@ -0,0 +1,93 @@
1
+ /** A single {key,value,secret} entry of a project's extension config (secret values redacted on read). */
2
+ export interface ConfigEntry {
3
+ key: string;
4
+ value: string | null;
5
+ secret: boolean;
6
+ }
7
+ /**
8
+ * Lifecycle state of a stored bundle version.
9
+ *
10
+ * pending — stored and being handed out, but nothing has confirmed it loads.
11
+ * running — an extension loaded it and published its schema. Once a version
12
+ * reaches this it is never demoted.
13
+ * failed — it could not be loaded (`reason` says why). The integration layer
14
+ * stops handing it out and the version beneath it takes over.
15
+ */
16
+ export type ExtensionState = "pending" | "running" | "failed";
17
+ /** The stored bundle's metadata (integration layer `ExtensionMeta`). */
18
+ export interface ExtensionMeta {
19
+ projectKey: string;
20
+ length: number;
21
+ uploadedAt: number;
22
+ version: number;
23
+ updatedBy?: string;
24
+ filename?: string;
25
+ /**
26
+ * The integrator's own version-control revision the stored bundle was built from,
27
+ * as reported on upload. Absent for a bundle uploaded without one (e.g. by hand in
28
+ * the Merchant Center).
29
+ */
30
+ sourceRevision?: string;
31
+ /**
32
+ * Lifecycle state of THIS version. Absent when talking to an integration layer
33
+ * that predates bundle state — callers must treat `undefined` as "this deployment
34
+ * can't tell me", not as a failure.
35
+ */
36
+ state?: ExtensionState;
37
+ /** Why this version failed to load; only ever set alongside state "failed". */
38
+ reason?: string;
39
+ /**
40
+ * The version actually in use, which is NOT always `version`: when the newest
41
+ * upload failed to load, the integration layer serves the one beneath it and this
42
+ * reports that older one. `null` when nothing is in use. Returned by the meta read
43
+ * only, not by an upload's response.
44
+ *
45
+ * It carries the source revision as well as the number, because the number is the
46
+ * integration layer's own counter — the commit is what identifies the build to
47
+ * whoever pushed it.
48
+ */
49
+ served?: {
50
+ version: number;
51
+ sourceRevision?: string;
52
+ } | null;
53
+ }
54
+ export interface BreakingChange {
55
+ type: string;
56
+ description: string;
57
+ }
58
+ export interface RemoteValidationResult {
59
+ valid: boolean;
60
+ composes: boolean;
61
+ compositionErrors: string[];
62
+ breakingChanges: BreakingChange[];
63
+ /** False when no published baseline was available, so the breaking check was skipped. */
64
+ comparedToPublished: boolean;
65
+ }
66
+ /** GET the project's raw core-subgraph SDL — the compose input (`GET …/subgraph`). */
67
+ export declare function fetchSubgraphSdl(baseUrl: string, projectKey: string, token: string): Promise<string>;
68
+ /** POST a candidate SDL to the composition + breaking-change gate (`POST …/extension/validate`). */
69
+ export declare function remoteValidate(baseUrl: string, projectKey: string, token: string, typeDefs: string): Promise<RemoteValidationResult>;
70
+ /**
71
+ * Upload the built bundle, replacing the project's stored one (`PUT …/extension/bundle`).
72
+ *
73
+ * `sourceRevision` (optional) records which of the integrator's revisions this build
74
+ * came from; the integration layer stores it against the revision, shows it in the
75
+ * Merchant Center, and hands it to the connector.
76
+ */
77
+ export declare function pushBundle(baseUrl: string, projectKey: string, token: string, bundle: string, filename: string, sourceRevision?: string): Promise<ExtensionMeta>;
78
+ /** The stored bundle's metadata, or null if none is stored (`GET …/extension/bundle/meta`). */
79
+ export declare function fetchExtensionMeta(baseUrl: string, projectKey: string, token: string): Promise<ExtensionMeta | null>;
80
+ /** Remove the project's extension subgraph from Hive (`DELETE …/extension/subgraph`). */
81
+ export declare function deleteExtensionSubgraph(baseUrl: string, projectKey: string, token: string): Promise<void>;
82
+ /** List the project's extension config entries, secret values redacted (`GET …/extension/config`). */
83
+ export declare function listConfig(baseUrl: string, projectKey: string, token: string): Promise<ConfigEntry[]>;
84
+ /**
85
+ * Upsert/delete config entries and return the resulting (redacted) list
86
+ * (`PATCH …/extension/config`). An entry with `value: null` deletes that key. PATCH
87
+ * (not PUT) so a single set/unset touches one key without clobbering the rest.
88
+ */
89
+ export declare function patchConfig(baseUrl: string, projectKey: string, token: string, entries: Array<{
90
+ key: string;
91
+ value: string | null;
92
+ secret?: boolean;
93
+ }>): Promise<ConfigEntry[]>;
@@ -0,0 +1,120 @@
1
+ // Low-level HTTP client for the integration layer's per-project manage-surface
2
+ // routes. Every call is parameterised by (baseUrl, projectKey, token) — the command
3
+ // layer resolves those (`IntegrationLayerCommand.resolveIlContext`, whose token is the
4
+ // `commercetools auth login` principal's) and this module just speaks the wire
5
+ // protocol. The route bodies these mirror live in the integration
6
+ // layer's `routes/manage.ts`; the request/response shapes here are extracted from the
7
+ // `ee-ext` tooling's `remoteValidate.ts` / `push.ts` (which read them from env) and
8
+ // the remaining manage routes (config, status, delete, subgraph).
9
+ function apiRoot(baseUrl, projectKey) {
10
+ return `${baseUrl.replace(/\/+$/, "")}/${encodeURIComponent(projectKey)}`;
11
+ }
12
+ function bearer(token) {
13
+ return { Authorization: `Bearer ${token}` };
14
+ }
15
+ /** GET the project's raw core-subgraph SDL — the compose input (`GET …/subgraph`). */
16
+ export async function fetchSubgraphSdl(baseUrl, projectKey, token) {
17
+ const url = `${apiRoot(baseUrl, projectKey)}/subgraph`;
18
+ const res = await fetch(url, { headers: { accept: "text/plain", ...bearer(token) } });
19
+ const text = await res.text();
20
+ if (!res.ok) {
21
+ throw new Error(`could not fetch core-subgraph SDL (${res.status}) from ${url}: ${text}`);
22
+ }
23
+ return text;
24
+ }
25
+ /** POST a candidate SDL to the composition + breaking-change gate (`POST …/extension/validate`). */
26
+ export async function remoteValidate(baseUrl, projectKey, token, typeDefs) {
27
+ const url = `${apiRoot(baseUrl, projectKey)}/extension/validate`;
28
+ const res = await fetch(url, {
29
+ method: "POST",
30
+ headers: { "Content-Type": "application/json", ...bearer(token) },
31
+ body: JSON.stringify({ typeDefs }),
32
+ });
33
+ const text = await res.text();
34
+ if (!res.ok) {
35
+ throw new Error(`schema validation request failed (${res.status}): ${text}`);
36
+ }
37
+ return JSON.parse(text);
38
+ }
39
+ /**
40
+ * Upload the built bundle, replacing the project's stored one (`PUT …/extension/bundle`).
41
+ *
42
+ * `sourceRevision` (optional) records which of the integrator's revisions this build
43
+ * came from; the integration layer stores it against the revision, shows it in the
44
+ * Merchant Center, and hands it to the connector.
45
+ */
46
+ export async function pushBundle(baseUrl, projectKey, token, bundle, filename, sourceRevision) {
47
+ const url = `${apiRoot(baseUrl, projectKey)}/extension/bundle`;
48
+ const res = await fetch(url, {
49
+ method: "PUT",
50
+ headers: {
51
+ "Content-Type": "text/javascript",
52
+ // The route requires the original filename (percent-encoded; it carries the
53
+ // module's type). The bundle is plain CommonJS, so name it `.cjs`.
54
+ "x-extension-filename": encodeURIComponent(filename),
55
+ // Percent-encoded for the same reason as the filename: keep an arbitrary value
56
+ // header-safe. Omitted entirely when there's no revision to report — the route
57
+ // treats a missing header as "no revision", not an error.
58
+ ...(sourceRevision
59
+ ? { "x-extension-source-revision": encodeURIComponent(sourceRevision) }
60
+ : {}),
61
+ ...bearer(token),
62
+ },
63
+ body: bundle,
64
+ });
65
+ const text = await res.text();
66
+ if (!res.ok) {
67
+ throw new Error(`PUT extension/bundle failed (${res.status}): ${text}`);
68
+ }
69
+ return JSON.parse(text);
70
+ }
71
+ /** The stored bundle's metadata, or null if none is stored (`GET …/extension/bundle/meta`). */
72
+ export async function fetchExtensionMeta(baseUrl, projectKey, token) {
73
+ const url = `${apiRoot(baseUrl, projectKey)}/extension/bundle/meta`;
74
+ const res = await fetch(url, { headers: { accept: "application/json", ...bearer(token) } });
75
+ if (res.status === 404)
76
+ return null;
77
+ const text = await res.text();
78
+ if (!res.ok) {
79
+ throw new Error(`GET extension/bundle/meta failed (${res.status}): ${text}`);
80
+ }
81
+ return JSON.parse(text);
82
+ }
83
+ /** Remove the project's extension subgraph from Hive (`DELETE …/extension/subgraph`). */
84
+ export async function deleteExtensionSubgraph(baseUrl, projectKey, token) {
85
+ const url = `${apiRoot(baseUrl, projectKey)}/extension/subgraph`;
86
+ const res = await fetch(url, { method: "DELETE", headers: { ...bearer(token) } });
87
+ if (!res.ok && res.status !== 204) {
88
+ const text = await res.text();
89
+ throw new Error(`DELETE extension/subgraph failed (${res.status}): ${text}`);
90
+ }
91
+ }
92
+ /** List the project's extension config entries, secret values redacted (`GET …/extension/config`). */
93
+ export async function listConfig(baseUrl, projectKey, token) {
94
+ const url = `${apiRoot(baseUrl, projectKey)}/extension/config`;
95
+ const res = await fetch(url, { headers: { accept: "application/json", ...bearer(token) } });
96
+ const text = await res.text();
97
+ if (!res.ok) {
98
+ throw new Error(`GET extension/config failed (${res.status}): ${text}`);
99
+ }
100
+ return JSON.parse(text);
101
+ }
102
+ /**
103
+ * Upsert/delete config entries and return the resulting (redacted) list
104
+ * (`PATCH …/extension/config`). An entry with `value: null` deletes that key. PATCH
105
+ * (not PUT) so a single set/unset touches one key without clobbering the rest.
106
+ */
107
+ export async function patchConfig(baseUrl, projectKey, token, entries) {
108
+ const url = `${apiRoot(baseUrl, projectKey)}/extension/config`;
109
+ const res = await fetch(url, {
110
+ method: "PATCH",
111
+ headers: { "Content-Type": "application/json", ...bearer(token) },
112
+ body: JSON.stringify(entries),
113
+ });
114
+ const text = await res.text();
115
+ if (!res.ok) {
116
+ throw new Error(`PATCH extension/config failed (${res.status}): ${text}`);
117
+ }
118
+ return JSON.parse(text);
119
+ }
120
+ //# sourceMappingURL=ilClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ilClient.js","sourceRoot":"","sources":["../../src/lib/ilClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Longest revision the integration layer stores — it truncates past this. Applied
3
+ * here too so what the CLI prints is what actually gets stored, rather than a value
4
+ * the server silently shortens.
5
+ */
6
+ export declare const MAX_SOURCE_REVISION_LENGTH = 200;
7
+ /**
8
+ * Reduce a revision to printable ASCII, length-capped — matching the integration
9
+ * layer's own rule, so the CLI never reports a value different from the stored one.
10
+ * (The server sanitizes regardless; it doesn't trust its callers. This is about
11
+ * telling the user the truth.)
12
+ */
13
+ export declare function sanitizeSourceRevision(value: string | undefined): string | undefined;
14
+ /**
15
+ * The git revision describing `cwd`'s working copy, or null when it isn't a git
16
+ * checkout.
17
+ *
18
+ * `git describe --tags --always` gives the most human-meaningful identifier
19
+ * available, in decreasing order of usefulness: an exact tag (`v1.4.2`), a tag plus
20
+ * distance and commit (`v1.4.2-3-g1a2b3c4d`), or — with no tags at all — the bare
21
+ * commit (`1a2b3c4d`). That single command therefore covers "commit hash or tag"
22
+ * without the caller choosing.
23
+ *
24
+ * A `-dirty` suffix is appended from `git status --porcelain -- .` rather than
25
+ * `describe --dirty`, because `--dirty` ignores UNTRACKED files — and an untracked
26
+ * source file that the entry point imports is bundled into the artifact, so a build
27
+ * containing it is genuinely not the tagged commit. Scoped to `-- .` so it reflects
28
+ * the directory being pushed, not unrelated edits elsewhere in a monorepo.
29
+ */
30
+ export declare function detectGitRevision(cwd: string): Promise<string | null>;
31
+ /**
32
+ * The source revision to record for this push: the explicit value if the caller gave
33
+ * one, else whatever the working copy says, else nothing.
34
+ *
35
+ * Nothing is a legitimate outcome — a bundle built outside version control has no
36
+ * revision, and inventing one ("unknown", a timestamp) would put a value in the
37
+ * Merchant Center and in every connector log line that means nothing.
38
+ */
39
+ export declare function resolveSourceRevision(explicit: string | undefined, cwd: string): Promise<string | undefined>;
@@ -0,0 +1,95 @@
1
+ // Where the bundle being pushed came from — the integrator's OWN version-control
2
+ // revision, recorded against the stored bundle by the integration layer.
3
+ //
4
+ // The point is provenance: with this, the Merchant Center shows which revision a
5
+ // project is running, the connector stamps it on every log line, and
6
+ // `{ _extensionBundle { sourceRevision } }` answers it over GraphQL. So the default
7
+ // has to be zero-effort — `extension push` reads the working copy and reports what
8
+ // it finds, with no flag.
9
+ //
10
+ // GIT is auto-detected because that's what the examples and (nearly) every
11
+ // integrator use. Any other VCS — hg, svn, Perforce, a CI build id — is supported
12
+ // through `--source-revision` / `EXTENSION_SOURCE_REVISION` rather than a detector
13
+ // per system: the integration layer treats the value as an OPAQUE string, so
14
+ // whatever identifies a revision to the integrator is the right thing to send.
15
+ import { execFile } from "node:child_process";
16
+ import { promisify } from "node:util";
17
+ const exec = promisify(execFile);
18
+ /**
19
+ * Longest revision the integration layer stores — it truncates past this. Applied
20
+ * here too so what the CLI prints is what actually gets stored, rather than a value
21
+ * the server silently shortens.
22
+ */
23
+ export const MAX_SOURCE_REVISION_LENGTH = 200;
24
+ /**
25
+ * Reduce a revision to printable ASCII, length-capped — matching the integration
26
+ * layer's own rule, so the CLI never reports a value different from the stored one.
27
+ * (The server sanitizes regardless; it doesn't trust its callers. This is about
28
+ * telling the user the truth.)
29
+ */
30
+ export function sanitizeSourceRevision(value) {
31
+ if (!value)
32
+ return undefined;
33
+ const cleaned = value
34
+ .replace(/[^\x20-\x7e]/g, "")
35
+ .trim()
36
+ .slice(0, MAX_SOURCE_REVISION_LENGTH);
37
+ return cleaned || undefined;
38
+ }
39
+ // Run a git command in `cwd`, or return null if git isn't available, this isn't a
40
+ // repository, or the command has nothing to say. Provenance is a nice-to-have on a
41
+ // push: never fail the push over it.
42
+ async function git(args, cwd) {
43
+ try {
44
+ const { stdout } = await exec("git", args, { cwd, windowsHide: true });
45
+ const trimmed = stdout.trim();
46
+ return trimmed || null;
47
+ }
48
+ catch {
49
+ return null;
50
+ }
51
+ }
52
+ /**
53
+ * The git revision describing `cwd`'s working copy, or null when it isn't a git
54
+ * checkout.
55
+ *
56
+ * `git describe --tags --always` gives the most human-meaningful identifier
57
+ * available, in decreasing order of usefulness: an exact tag (`v1.4.2`), a tag plus
58
+ * distance and commit (`v1.4.2-3-g1a2b3c4d`), or — with no tags at all — the bare
59
+ * commit (`1a2b3c4d`). That single command therefore covers "commit hash or tag"
60
+ * without the caller choosing.
61
+ *
62
+ * A `-dirty` suffix is appended from `git status --porcelain -- .` rather than
63
+ * `describe --dirty`, because `--dirty` ignores UNTRACKED files — and an untracked
64
+ * source file that the entry point imports is bundled into the artifact, so a build
65
+ * containing it is genuinely not the tagged commit. Scoped to `-- .` so it reflects
66
+ * the directory being pushed, not unrelated edits elsewhere in a monorepo.
67
+ */
68
+ export async function detectGitRevision(cwd) {
69
+ // Cheapest possible "is this a git checkout?" probe — everything below is
70
+ // pointless outside one, and this keeps a non-git working copy silent rather than
71
+ // noisy.
72
+ if (!(await git(["rev-parse", "--git-dir"], cwd)))
73
+ return null;
74
+ const described = await git(["describe", "--tags", "--always", "--abbrev=12"], cwd);
75
+ // A repository with no commits yet describes to nothing — there is no revision to
76
+ // report, so report none.
77
+ if (!described)
78
+ return null;
79
+ const dirt = await git(["status", "--porcelain", "--", "."], cwd);
80
+ return dirt ? `${described}-dirty` : described;
81
+ }
82
+ /**
83
+ * The source revision to record for this push: the explicit value if the caller gave
84
+ * one, else whatever the working copy says, else nothing.
85
+ *
86
+ * Nothing is a legitimate outcome — a bundle built outside version control has no
87
+ * revision, and inventing one ("unknown", a timestamp) would put a value in the
88
+ * Merchant Center and in every connector log line that means nothing.
89
+ */
90
+ export async function resolveSourceRevision(explicit, cwd) {
91
+ if (explicit !== undefined)
92
+ return sanitizeSourceRevision(explicit);
93
+ return sanitizeSourceRevision((await detectGitRevision(cwd)) ?? undefined);
94
+ }
95
+ //# sourceMappingURL=sourceRevision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sourceRevision.js","sourceRoot":"","sources":["../../src/lib/sourceRevision.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,yEAAyE;AACzE,EAAE;AACF,iFAAiF;AACjF,qEAAqE;AACrE,oFAAoF;AACpF,mFAAmF;AACnF,0BAA0B;AAC1B,EAAE;AACF,2EAA2E;AAC3E,kFAAkF;AAClF,mFAAmF;AACnF,6EAA6E;AAC7E,+EAA+E;AAE/E,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAEjC;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAG,CAAC;AAE9C;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAyB;IAC9D,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,OAAO,GAAG,KAAK;SAClB,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC;SAC5B,IAAI,EAAE;SACN,KAAK,CAAC,CAAC,EAAE,0BAA0B,CAAC,CAAC;IACxC,OAAO,OAAO,IAAI,SAAS,CAAC;AAC9B,CAAC;AAED,kFAAkF;AAClF,mFAAmF;AACnF,qCAAqC;AACrC,KAAK,UAAU,GAAG,CAAC,IAAc,EAAE,GAAW;IAC5C,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QACvE,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;QAC9B,OAAO,OAAO,IAAI,IAAI,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,GAAW;IACjD,0EAA0E;IAC1E,kFAAkF;IAClF,SAAS;IACT,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,GAAG,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAE/D,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,CAAC,EAAE,GAAG,CAAC,CAAC;IACpF,kFAAkF;IAClF,0BAA0B;IAC1B,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAE5B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IAClE,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,SAAS,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;AACjD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,QAA4B,EAC5B,GAAW;IAEX,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,sBAAsB,CAAC,QAAQ,CAAC,CAAC;IACpE,OAAO,sBAAsB,CAAC,CAAC,MAAM,iBAAiB,CAAC,GAAG,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC;AAC7E,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { ExtensionAction, ExtensionInput } from '@commercetools/platform-sdk';
2
+ export type { ExtensionInput };
3
+ export type ApiExtensionAction = Extract<ExtensionAction, 'Create' | 'Update'>;
4
+ export interface ExtensionContext {
5
+ now(): number;
6
+ config: Readonly<Record<string, string>>;
7
+ }
8
+ export type ApiExtensionInput = ExtensionInput;
9
+ export interface ApiExtensionError {
10
+ code: string;
11
+ message: string;
12
+ }
13
+ export type ApiExtensionResult = void | {
14
+ actions?: unknown[];
15
+ errors?: ApiExtensionError[];
16
+ };
17
+ export interface ApiExtensionDefinition {
18
+ key: string;
19
+ resourceTypeId: string;
20
+ actions: ApiExtensionAction[];
21
+ condition?: string;
22
+ handler: (input: ApiExtensionInput, ctx: ExtensionContext) => ApiExtensionResult | Promise<ApiExtensionResult>;
23
+ }
24
+ /** Identity helper — declare an API-Extension handler, type-checked against the SDK payload. */
25
+ export declare function defineApiExtension(definition: ApiExtensionDefinition): ApiExtensionDefinition;
26
+ /** Approve the write unchanged. */
27
+ export declare function approve(): ApiExtensionResult;
28
+ /** Block the write with a validation error (commercetools returns it to the caller). */
29
+ export declare function block(code: string, message: string): ApiExtensionResult;
30
+ /** Apply commercetools update actions to the triggering resource. */
31
+ export declare function update(actions: unknown[]): ApiExtensionResult;
@@ -0,0 +1,39 @@
1
+ // Authoring API for commercetools API Extensions.
2
+ //
3
+ // Copied verbatim from commercetools/integration-layer-extension-examples
4
+ // (packages/tooling/src/apiExtension.ts). An API Extension is the commercetools
5
+ // platform feature: a synchronous HTTP callback commercetools makes before
6
+ // persisting a cart/order/… create or update, which can approve, modify, or BLOCK
7
+ // the write. This is a DIFFERENT thing from a GraphQL schema extension (typeDefs +
8
+ // resolvers), which adds fields to the graph. A bundle may export `apiExtensions`,
9
+ // `typeDefs`/`resolvers`, or both.
10
+ //
11
+ // A handler is a plain function `(input, ctx) => result`. Use `defineApiExtension`
12
+ // for type checking and the `approve` / `block` / `update` helpers to return intent
13
+ // instead of hand-rolling the wire shape. The runtime (the sandbox) maps the result
14
+ // to commercetools' response contract.
15
+ //
16
+ // The RECEIVED payload is the commercetools SDK's own `ExtensionInput` — you don't
17
+ // define the resource types. Its `resource` is the discriminated `Reference` union,
18
+ // so narrowing on `input.resource.typeId === 'cart'` gives you `resource.obj` typed
19
+ // as the real `Cart` (and so on). These are type-only imports (erased by the
20
+ // bundler), so they add nothing to the pushed bundle. The SDK does NOT model the
21
+ // RESPONSE envelope (approve/block/actions is an HTTP contract, not a resource), so
22
+ // `ApiExtensionResult` / `ApiExtensionError` below stay local.
23
+ /** Identity helper — declare an API-Extension handler, type-checked against the SDK payload. */
24
+ export function defineApiExtension(definition) {
25
+ return definition;
26
+ }
27
+ /** Approve the write unchanged. */
28
+ export function approve() {
29
+ return {};
30
+ }
31
+ /** Block the write with a validation error (commercetools returns it to the caller). */
32
+ export function block(code, message) {
33
+ return { errors: [{ code, message }] };
34
+ }
35
+ /** Apply commercetools update actions to the triggering resource. */
36
+ export function update(actions) {
37
+ return { actions };
38
+ }
39
+ //# sourceMappingURL=apiExtension.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiExtension.js","sourceRoot":"","sources":["../../../src/lib/tooling/apiExtension.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,EAAE;AACF,0EAA0E;AAC1E,gFAAgF;AAChF,2EAA2E;AAC3E,kFAAkF;AAClF,mFAAmF;AACnF,mFAAmF;AACnF,mCAAmC;AACnC,EAAE;AACF,mFAAmF;AACnF,oFAAoF;AACpF,oFAAoF;AACpF,uCAAuC;AACvC,EAAE;AACF,mFAAmF;AACnF,oFAAoF;AACpF,oFAAoF;AACpF,6EAA6E;AAC7E,iFAAiF;AACjF,oFAAoF;AACpF,+DAA+D;AA+C/D,gGAAgG;AAChG,MAAM,UAAU,kBAAkB,CAAC,UAAkC;IACnE,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,OAAO;IACrB,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,KAAK,CAAC,IAAY,EAAE,OAAe;IACjD,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;AACzC,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,MAAM,CAAC,OAAkB;IACvC,OAAO,EAAE,OAAO,EAAE,CAAC;AACrB,CAAC"}
@@ -0,0 +1,18 @@
1
+ /** Peer modules the runtime owns — bundled never, resolved at load time. */
2
+ export declare const HOST_PROVIDED_EXTERNALS: string[];
3
+ /** The extension source of the example the tool is invoked inside (cwd-relative). */
4
+ export declare function defaultEntry(): string;
5
+ /** The bundle output for the example the tool is invoked inside (cwd-relative). */
6
+ export declare function defaultOutfile(): string;
7
+ export interface BuildResult {
8
+ /** Absolute path to the bundled artifact. */
9
+ outfile: string;
10
+ /**
11
+ * The author's own source files that went into the bundle (entry + local
12
+ * imports), absolute paths. npm packages are excluded — these feed the static
13
+ * analyzer, which only checks code the author wrote.
14
+ */
15
+ sourceFiles: string[];
16
+ }
17
+ /** Bundle `entry` into a single self-contained CJS file at `outfile`. */
18
+ export declare function buildBundle(entry?: string, outfile?: string): Promise<BuildResult>;