@commercetools/cli-topic-integration-layer 0.0.0 → 0.2.1
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.
- package/LICENSE +21 -0
- package/README.md +63 -0
- package/dist/commands/integration-layer/config/get.d.ts +9 -0
- package/dist/commands/integration-layer/config/get.js +22 -0
- package/dist/commands/integration-layer/config/get.js.map +1 -0
- package/dist/commands/integration-layer/config/list.d.ts +6 -0
- package/dist/commands/integration-layer/config/list.js +20 -0
- package/dist/commands/integration-layer/config/list.js.map +1 -0
- package/dist/commands/integration-layer/config/set.d.ts +13 -0
- package/dist/commands/integration-layer/config/set.js +29 -0
- package/dist/commands/integration-layer/config/set.js.map +1 -0
- package/dist/commands/integration-layer/config/unset.d.ts +9 -0
- package/dist/commands/integration-layer/config/unset.js +18 -0
- package/dist/commands/integration-layer/config/unset.js.map +1 -0
- package/dist/commands/integration-layer/extension/build.d.ts +10 -0
- package/dist/commands/integration-layer/extension/build.js +19 -0
- package/dist/commands/integration-layer/extension/build.js.map +1 -0
- package/dist/commands/integration-layer/extension/delete.d.ts +9 -0
- package/dist/commands/integration-layer/extension/delete.js +39 -0
- package/dist/commands/integration-layer/extension/delete.js.map +1 -0
- package/dist/commands/integration-layer/extension/invoke.d.ts +13 -0
- package/dist/commands/integration-layer/extension/invoke.js +89 -0
- package/dist/commands/integration-layer/extension/invoke.js.map +1 -0
- package/dist/commands/integration-layer/extension/push.d.ts +13 -0
- package/dist/commands/integration-layer/extension/push.js +83 -0
- package/dist/commands/integration-layer/extension/push.js.map +1 -0
- package/dist/commands/integration-layer/extension/serve.d.ts +18 -0
- package/dist/commands/integration-layer/extension/serve.js +320 -0
- package/dist/commands/integration-layer/extension/serve.js.map +1 -0
- package/dist/commands/integration-layer/extension/status.d.ts +6 -0
- package/dist/commands/integration-layer/extension/status.js +24 -0
- package/dist/commands/integration-layer/extension/status.js.map +1 -0
- package/dist/commands/integration-layer/extension/validate.d.ts +13 -0
- package/dist/commands/integration-layer/extension/validate.js +76 -0
- package/dist/commands/integration-layer/extension/validate.js.map +1 -0
- package/dist/commands/integration-layer/hello.d.ts +6 -0
- package/dist/commands/integration-layer/hello.js +10 -0
- package/dist/commands/integration-layer/hello.js.map +1 -0
- package/dist/commands/integration-layer/init.d.ts +13 -0
- package/dist/commands/integration-layer/init.js +135 -0
- package/dist/commands/integration-layer/init.js.map +1 -0
- package/dist/commands/integration-layer/schema/fetch.d.ts +9 -0
- package/dist/commands/integration-layer/schema/fetch.js +27 -0
- package/dist/commands/integration-layer/schema/fetch.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/base.d.ts +50 -0
- package/dist/lib/base.js +85 -0
- package/dist/lib/base.js.map +1 -0
- package/dist/lib/ilClient.d.ts +49 -0
- package/dist/lib/ilClient.js +108 -0
- package/dist/lib/ilClient.js.map +1 -0
- package/dist/lib/tooling/apiExtension.d.ts +31 -0
- package/dist/lib/tooling/apiExtension.js +39 -0
- package/dist/lib/tooling/apiExtension.js.map +1 -0
- package/dist/lib/tooling/build.d.ts +18 -0
- package/dist/lib/tooling/build.js +54 -0
- package/dist/lib/tooling/build.js.map +1 -0
- package/dist/lib/tooling/compose.d.ts +28 -0
- package/dist/lib/tooling/compose.js +48 -0
- package/dist/lib/tooling/compose.js.map +1 -0
- package/dist/lib/tooling/gateway.d.ts +21 -0
- package/dist/lib/tooling/gateway.js +65 -0
- package/dist/lib/tooling/gateway.js.map +1 -0
- package/dist/lib/tooling/loadBundle.d.ts +10 -0
- package/dist/lib/tooling/loadBundle.js +27 -0
- package/dist/lib/tooling/loadBundle.js.map +1 -0
- package/dist/lib/tooling/staticAnalysis.d.ts +11 -0
- package/dist/lib/tooling/staticAnalysis.js +167 -0
- package/dist/lib/tooling/staticAnalysis.js.map +1 -0
- package/dist/lib/tooling/validateBundle.d.ts +19 -0
- package/dist/lib/tooling/validateBundle.js +144 -0
- package/dist/lib/tooling/validateBundle.js.map +1 -0
- package/package.json +46 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/commands/integration-layer/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEnD,oFAAoF;AACpF,sFAAsF;AACtF,8EAA8E;AAC9E,MAAM,cAAc,GAA2B;IAC7C,kBAAkB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmCrB;IACC,cAAc,EAAE;;;;;;;;;;;;;;;;CAgBjB;IACC,eAAe,EAAE;;;;;;;;;;;CAWlB;IACC,cAAc,EAAE;;;;CAIjB;IACC,YAAY,EAAE;;;CAGf;IACC,WAAW,EAAE;;;;;;;;;;;CAWd;CACA,CAAC;AAEF,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,OAAO;IACvC,MAAM,CAAU,WAAW,GAAG,iEAAiE,CAAC;IAEhG,MAAM,CAAU,QAAQ,GAAG;QACzB,uDAAuD;QACvD,wEAAwE;KACzE,CAAC;IAEF,MAAM,CAAU,IAAI,GAAG;QACrB,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,4BAA4B,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;KACtF,CAAC;IAEF,MAAM,CAAU,KAAK,GAAG;QACtB,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC;YACrB,WAAW,EAAE,qBAAqB;YAClC,OAAO,EAAE,CAAC,OAAO,CAAC;YAClB,OAAO,EAAE,OAAO;SACjB,CAAC;QACF,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC;YACnB,IAAI,EAAE,GAAG;YACT,WAAW,EAAE,qCAAqC;YAClD,OAAO,EAAE,KAAK;SACf,CAAC;KACH,CAAC;IAEF,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC;QAE3B,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAc,CAAC,CAAC;QAChE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACxC,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,0DAA0D,CAAC,CAAC;QAC1F,CAAC;QAED,KAAK,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;YACjE,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAClC,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACrD,MAAM,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC5C,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,mBAAmB,KAAK,CAAC,QAAQ,kBAAkB,GAAG,EAAE,CAAC,CAAC;QACnE,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACxB,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC;QACxB,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QAC1B,IAAI,CAAC,GAAG,CAAC,mDAAmD,CAAC,CAAC;IAChE,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { IntegrationLayerCommand } from "../../../lib/base.js";
|
|
2
|
+
export default class SchemaFetch extends IntegrationLayerCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
out: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
};
|
|
8
|
+
run(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
2
|
+
import { Flags } from "@oclif/core";
|
|
3
|
+
import { fetchSubgraphSdl } from "../../../lib/ilClient.js";
|
|
4
|
+
import { IntegrationLayerCommand } from "../../../lib/base.js";
|
|
5
|
+
export default class SchemaFetch extends IntegrationLayerCommand {
|
|
6
|
+
static description = "Print (or write) the project's core-subgraph SDL — the input extensions compose against";
|
|
7
|
+
static examples = [
|
|
8
|
+
"<%= config.bin %> integration-layer schema fetch",
|
|
9
|
+
"<%= config.bin %> integration-layer schema fetch --out subgraph.graphql",
|
|
10
|
+
];
|
|
11
|
+
static flags = {
|
|
12
|
+
out: Flags.string({ description: "write the SDL to this file instead of stdout" }),
|
|
13
|
+
};
|
|
14
|
+
async run() {
|
|
15
|
+
const { flags } = await this.parse(SchemaFetch);
|
|
16
|
+
const { baseUrl, projectKey, token } = await this.resolveIlContext(flags);
|
|
17
|
+
const sdl = await fetchSubgraphSdl(baseUrl, projectKey, token);
|
|
18
|
+
if (flags.out) {
|
|
19
|
+
await writeFile(flags.out, sdl, "utf8");
|
|
20
|
+
this.log(`✓ wrote ${projectKey} core-subgraph SDL → ${flags.out}`);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
this.log(sdl);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../../../src/commands/integration-layer/schema/fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,uBAAuB;IAC9D,MAAM,CAAU,WAAW,GACzB,yFAAyF,CAAC;IAE5F,MAAM,CAAU,QAAQ,GAAG;QACzB,kDAAkD;QAClD,yEAAyE;KAC1E,CAAC;IAEF,MAAM,CAAU,KAAK,GAAG;QACtB,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,8CAA8C,EAAE,CAAC;KACnF,CAAC;IAEF,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAChD,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAC1E,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;QAC/D,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;YACd,MAAM,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;YACxC,IAAI,CAAC,GAAG,CAAC,WAAW,UAAU,wBAAwB,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QACrE,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChB,CAAC;IACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { defineApiExtension, approve, block, update, type ApiExtensionAction, type ApiExtensionDefinition, type ApiExtensionError, type ApiExtensionInput, type ApiExtensionResult, type ExtensionContext, type ExtensionInput, } from "./lib/tooling/apiExtension.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// oclif discovers commands from the `oclif.commands` directory (see package.json).
|
|
2
|
+
// The package `main` re-exports the extension authoring API so an author can import
|
|
3
|
+
// the helpers/types when writing `src/extension.ts`.
|
|
4
|
+
export { defineApiExtension, approve, block, update, } from "./lib/tooling/apiExtension.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,oFAAoF;AACpF,qDAAqD;AACrD,OAAO,EACL,kBAAkB,EAClB,OAAO,EACP,KAAK,EACL,MAAM,GAQP,MAAM,+BAA+B,CAAC"}
|
|
@@ -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
|
+
}
|
package/dist/lib/base.js
ADDED
|
@@ -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,49 @@
|
|
|
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
|
+
/** The stored bundle's metadata (integration layer `ExtensionMeta`). */
|
|
8
|
+
export interface ExtensionMeta {
|
|
9
|
+
projectKey: string;
|
|
10
|
+
length: number;
|
|
11
|
+
uploadedAt: number;
|
|
12
|
+
version: number;
|
|
13
|
+
updatedBy?: string;
|
|
14
|
+
filename?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface BreakingChange {
|
|
17
|
+
type: string;
|
|
18
|
+
description: string;
|
|
19
|
+
}
|
|
20
|
+
export interface RemoteValidationResult {
|
|
21
|
+
valid: boolean;
|
|
22
|
+
composes: boolean;
|
|
23
|
+
compositionErrors: string[];
|
|
24
|
+
breakingChanges: BreakingChange[];
|
|
25
|
+
/** False when no published baseline was available, so the breaking check was skipped. */
|
|
26
|
+
comparedToPublished: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** GET the project's raw core-subgraph SDL — the compose input (`GET …/subgraph`). */
|
|
29
|
+
export declare function fetchSubgraphSdl(baseUrl: string, projectKey: string, token: string): Promise<string>;
|
|
30
|
+
/** POST a candidate SDL to the composition + breaking-change gate (`POST …/extension/validate`). */
|
|
31
|
+
export declare function remoteValidate(baseUrl: string, projectKey: string, token: string, typeDefs: string): Promise<RemoteValidationResult>;
|
|
32
|
+
/** Upload the built bundle, replacing the project's stored one (`PUT …/extension/bundle`). */
|
|
33
|
+
export declare function pushBundle(baseUrl: string, projectKey: string, token: string, bundle: string, filename: string): Promise<ExtensionMeta>;
|
|
34
|
+
/** The stored bundle's metadata, or null if none is stored (`GET …/extension/bundle/meta`). */
|
|
35
|
+
export declare function fetchExtensionMeta(baseUrl: string, projectKey: string, token: string): Promise<ExtensionMeta | null>;
|
|
36
|
+
/** Remove the project's extension subgraph from Hive (`DELETE …/extension/subgraph`). */
|
|
37
|
+
export declare function deleteExtensionSubgraph(baseUrl: string, projectKey: string, token: string): Promise<void>;
|
|
38
|
+
/** List the project's extension config entries, secret values redacted (`GET …/extension/config`). */
|
|
39
|
+
export declare function listConfig(baseUrl: string, projectKey: string, token: string): Promise<ConfigEntry[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Upsert/delete config entries and return the resulting (redacted) list
|
|
42
|
+
* (`PATCH …/extension/config`). An entry with `value: null` deletes that key. PATCH
|
|
43
|
+
* (not PUT) so a single set/unset touches one key without clobbering the rest.
|
|
44
|
+
*/
|
|
45
|
+
export declare function patchConfig(baseUrl: string, projectKey: string, token: string, entries: Array<{
|
|
46
|
+
key: string;
|
|
47
|
+
value: string | null;
|
|
48
|
+
secret?: boolean;
|
|
49
|
+
}>): Promise<ConfigEntry[]>;
|
|
@@ -0,0 +1,108 @@
|
|
|
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
|
+
/** Upload the built bundle, replacing the project's stored one (`PUT …/extension/bundle`). */
|
|
40
|
+
export async function pushBundle(baseUrl, projectKey, token, bundle, filename) {
|
|
41
|
+
const url = `${apiRoot(baseUrl, projectKey)}/extension/bundle`;
|
|
42
|
+
const res = await fetch(url, {
|
|
43
|
+
method: "PUT",
|
|
44
|
+
headers: {
|
|
45
|
+
"Content-Type": "text/javascript",
|
|
46
|
+
// The route requires the original filename (percent-encoded; it carries the
|
|
47
|
+
// module's type). The bundle is plain CommonJS, so name it `.cjs`.
|
|
48
|
+
"x-extension-filename": encodeURIComponent(filename),
|
|
49
|
+
...bearer(token),
|
|
50
|
+
},
|
|
51
|
+
body: bundle,
|
|
52
|
+
});
|
|
53
|
+
const text = await res.text();
|
|
54
|
+
if (!res.ok) {
|
|
55
|
+
throw new Error(`PUT extension/bundle failed (${res.status}): ${text}`);
|
|
56
|
+
}
|
|
57
|
+
return JSON.parse(text);
|
|
58
|
+
}
|
|
59
|
+
/** The stored bundle's metadata, or null if none is stored (`GET …/extension/bundle/meta`). */
|
|
60
|
+
export async function fetchExtensionMeta(baseUrl, projectKey, token) {
|
|
61
|
+
const url = `${apiRoot(baseUrl, projectKey)}/extension/bundle/meta`;
|
|
62
|
+
const res = await fetch(url, { headers: { accept: "application/json", ...bearer(token) } });
|
|
63
|
+
if (res.status === 404)
|
|
64
|
+
return null;
|
|
65
|
+
const text = await res.text();
|
|
66
|
+
if (!res.ok) {
|
|
67
|
+
throw new Error(`GET extension/bundle/meta failed (${res.status}): ${text}`);
|
|
68
|
+
}
|
|
69
|
+
return JSON.parse(text);
|
|
70
|
+
}
|
|
71
|
+
/** Remove the project's extension subgraph from Hive (`DELETE …/extension/subgraph`). */
|
|
72
|
+
export async function deleteExtensionSubgraph(baseUrl, projectKey, token) {
|
|
73
|
+
const url = `${apiRoot(baseUrl, projectKey)}/extension/subgraph`;
|
|
74
|
+
const res = await fetch(url, { method: "DELETE", headers: { ...bearer(token) } });
|
|
75
|
+
if (!res.ok && res.status !== 204) {
|
|
76
|
+
const text = await res.text();
|
|
77
|
+
throw new Error(`DELETE extension/subgraph failed (${res.status}): ${text}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** List the project's extension config entries, secret values redacted (`GET …/extension/config`). */
|
|
81
|
+
export async function listConfig(baseUrl, projectKey, token) {
|
|
82
|
+
const url = `${apiRoot(baseUrl, projectKey)}/extension/config`;
|
|
83
|
+
const res = await fetch(url, { headers: { accept: "application/json", ...bearer(token) } });
|
|
84
|
+
const text = await res.text();
|
|
85
|
+
if (!res.ok) {
|
|
86
|
+
throw new Error(`GET extension/config failed (${res.status}): ${text}`);
|
|
87
|
+
}
|
|
88
|
+
return JSON.parse(text);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Upsert/delete config entries and return the resulting (redacted) list
|
|
92
|
+
* (`PATCH …/extension/config`). An entry with `value: null` deletes that key. PATCH
|
|
93
|
+
* (not PUT) so a single set/unset touches one key without clobbering the rest.
|
|
94
|
+
*/
|
|
95
|
+
export async function patchConfig(baseUrl, projectKey, token, entries) {
|
|
96
|
+
const url = `${apiRoot(baseUrl, projectKey)}/extension/config`;
|
|
97
|
+
const res = await fetch(url, {
|
|
98
|
+
method: "PATCH",
|
|
99
|
+
headers: { "Content-Type": "application/json", ...bearer(token) },
|
|
100
|
+
body: JSON.stringify(entries),
|
|
101
|
+
});
|
|
102
|
+
const text = await res.text();
|
|
103
|
+
if (!res.ok) {
|
|
104
|
+
throw new Error(`PATCH extension/config failed (${res.status}): ${text}`);
|
|
105
|
+
}
|
|
106
|
+
return JSON.parse(text);
|
|
107
|
+
}
|
|
108
|
+
//# 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,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>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Bundle an extension source into a single self-contained CommonJS artifact.
|
|
2
|
+
//
|
|
3
|
+
// Copied from commercetools/integration-layer-extension-examples
|
|
4
|
+
// (packages/tooling/src/build.ts) — a LOW-LEVEL function reused verbatim; the
|
|
5
|
+
// argv/env-driven `buildCommand` wrapper is NOT copied (this plugin's oclif command
|
|
6
|
+
// is the wrapper). esbuild bundles local helper modules inline. The output is
|
|
7
|
+
// CommonJS so the runtime can evaluate it as a script and read `module.exports`.
|
|
8
|
+
// `platform: "neutral"` keeps Node built-ins out (an import of one fails the build,
|
|
9
|
+
// not at load time), and `conditions: ["worker"]` steers a bundled SDK to its
|
|
10
|
+
// fetch-based build (the runtime provides a global `fetch`, not Node `http`/`https`).
|
|
11
|
+
// `graphql` stays EXTERNAL — the runtime supplies its single instance; a second
|
|
12
|
+
// inlined copy would break its `instanceof` checks.
|
|
13
|
+
import { build } from "esbuild";
|
|
14
|
+
import { join, resolve } from "node:path";
|
|
15
|
+
import process from "node:process";
|
|
16
|
+
/** Peer modules the runtime owns — bundled never, resolved at load time. */
|
|
17
|
+
export const HOST_PROVIDED_EXTERNALS = ["graphql"];
|
|
18
|
+
/** The extension source of the example the tool is invoked inside (cwd-relative). */
|
|
19
|
+
export function defaultEntry() {
|
|
20
|
+
return join(process.cwd(), "src", "extension.ts");
|
|
21
|
+
}
|
|
22
|
+
/** The bundle output for the example the tool is invoked inside (cwd-relative). */
|
|
23
|
+
export function defaultOutfile() {
|
|
24
|
+
return join(process.cwd(), "dist", "extension.js");
|
|
25
|
+
}
|
|
26
|
+
/** Bundle `entry` into a single self-contained CJS file at `outfile`. */
|
|
27
|
+
export async function buildBundle(entry = defaultEntry(), outfile = defaultOutfile()) {
|
|
28
|
+
const result = await build({
|
|
29
|
+
entryPoints: [entry],
|
|
30
|
+
outfile,
|
|
31
|
+
bundle: true,
|
|
32
|
+
format: "cjs",
|
|
33
|
+
platform: "neutral",
|
|
34
|
+
target: "node22",
|
|
35
|
+
conditions: ["worker"],
|
|
36
|
+
// `platform: "neutral"` leaves `mainFields` empty, so a package that ships only
|
|
37
|
+
// classic `main`/`module` fields (no `exports`) — e.g. `@commercetools/ts-client`
|
|
38
|
+
// / `@commercetools/platform-sdk`, which an extension reaches for to read CT from
|
|
39
|
+
// a resolver — is otherwise unresolvable ("Could not resolve"). Preferring
|
|
40
|
+
// `module` (the fetch-based ESM build, no Node built-ins) then `main` fixes that;
|
|
41
|
+
// packages that DO declare `exports` (e.g. algoliasearch) resolve through it and
|
|
42
|
+
// ignore this. (ILEE's build.ts lacks this line — its examples don't hit the case;
|
|
43
|
+
// keep the two in sync when it's added there.)
|
|
44
|
+
mainFields: ["module", "main"],
|
|
45
|
+
external: HOST_PROVIDED_EXTERNALS,
|
|
46
|
+
metafile: true,
|
|
47
|
+
logLevel: "silent",
|
|
48
|
+
});
|
|
49
|
+
const sourceFiles = Object.keys(result.metafile.inputs)
|
|
50
|
+
.filter((input) => !input.includes("node_modules"))
|
|
51
|
+
.map((input) => resolve(process.cwd(), input));
|
|
52
|
+
return { outfile, sourceFiles };
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../../src/lib/tooling/build.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,EAAE;AACF,iEAAiE;AACjE,8EAA8E;AAC9E,oFAAoF;AACpF,8EAA8E;AAC9E,iFAAiF;AACjF,oFAAoF;AACpF,8EAA8E;AAC9E,sFAAsF;AACtF,gFAAgF;AAChF,oDAAoD;AAEpD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,OAAO,MAAM,cAAc,CAAC;AAEnC,4EAA4E;AAC5E,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,SAAS,CAAC,CAAC;AAEnD,qFAAqF;AACrF,MAAM,UAAU,YAAY;IAC1B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC;AACpD,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,cAAc;IAC5B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC;AACrD,CAAC;AAaD,yEAAyE;AACzE,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,QAAgB,YAAY,EAAE,EAC9B,UAAkB,cAAc,EAAE;IAElC,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC;QACzB,WAAW,EAAE,CAAC,KAAK,CAAC;QACpB,OAAO;QACP,MAAM,EAAE,IAAI;QACZ,MAAM,EAAE,KAAK;QACb,QAAQ,EAAE,SAAS;QACnB,MAAM,EAAE,QAAQ;QAChB,UAAU,EAAE,CAAC,QAAQ,CAAC;QACtB,gFAAgF;QAChF,kFAAkF;QAClF,kFAAkF;QAClF,2EAA2E;QAC3E,kFAAkF;QAClF,iFAAiF;QACjF,mFAAmF;QACnF,+CAA+C;QAC/C,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC9B,QAAQ,EAAE,uBAAuB;QACjC,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,QAAQ;KACnB,CAAC,CAAC;IACH,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;SACpD,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;SAClD,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;IACjD,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type GraphQLSchema } from "graphql";
|
|
2
|
+
/** The subgraph names the integration layer composes under — matched here so the
|
|
3
|
+
* local composition mirrors the deployed one. `integration-layer` is the name an
|
|
4
|
+
* extension's `@override(from: …)` references. */
|
|
5
|
+
export declare const INTEGRATION_LAYER_SERVICE = "integration-layer";
|
|
6
|
+
export declare const EXTENSION_SERVICE = "extensions";
|
|
7
|
+
export type ComposeResult = {
|
|
8
|
+
ok: true;
|
|
9
|
+
/** The client-facing API schema (federation directives stripped), browsable. */
|
|
10
|
+
apiSchema: GraphQLSchema;
|
|
11
|
+
/** Printed API-schema SDL — "what the full schema looks like" to a consumer. */
|
|
12
|
+
apiSdl: string;
|
|
13
|
+
/** The raw composed supergraph SDL the router plans against. */
|
|
14
|
+
supergraphSdl: string;
|
|
15
|
+
} | {
|
|
16
|
+
ok: false;
|
|
17
|
+
errors: string[];
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Compose the integration layer + the extension into a supergraph and derive the
|
|
21
|
+
* client-facing API schema. Never throws — a composition failure comes back in
|
|
22
|
+
* `errors` so the dev server can surface it and keep serving. The URLs are baked into
|
|
23
|
+
* the supergraph so `--gateway` routes correctly; for `--compose` they're cosmetic.
|
|
24
|
+
*/
|
|
25
|
+
export declare function composeWithIntegrationLayer(integrationLayerSdl: string, extensionSdl: string, urls: {
|
|
26
|
+
integrationLayerUrl: string;
|
|
27
|
+
extensionUrl: string;
|
|
28
|
+
}): ComposeResult;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Compose the local extension subgraph with the project's live integration-layer
|
|
2
|
+
// subgraph — the same two-subgraph composition the integration layer runs on publish.
|
|
3
|
+
//
|
|
4
|
+
// The LOW-LEVEL `composeWithIntegrationLayer` is copied verbatim from
|
|
5
|
+
// commercetools/integration-layer-extension-examples (packages/tooling/src/compose.ts).
|
|
6
|
+
// The env/token-minting `fetchIntegrationLayerSubgraphSdl` wrapper is NOT copied —
|
|
7
|
+
// fetching the project's subgraph SDL is a plain authenticated HTTP call, so it lives
|
|
8
|
+
// in `../ilClient.ts` parameterised by (baseUrl, projectKey, token), and the command
|
|
9
|
+
// layer supplies the token.
|
|
10
|
+
import { parse, printSchema } from "graphql";
|
|
11
|
+
import { composeServices } from "@apollo/composition";
|
|
12
|
+
/** The subgraph names the integration layer composes under — matched here so the
|
|
13
|
+
* local composition mirrors the deployed one. `integration-layer` is the name an
|
|
14
|
+
* extension's `@override(from: …)` references. */
|
|
15
|
+
export const INTEGRATION_LAYER_SERVICE = "integration-layer";
|
|
16
|
+
export const EXTENSION_SERVICE = "extensions";
|
|
17
|
+
/**
|
|
18
|
+
* Compose the integration layer + the extension into a supergraph and derive the
|
|
19
|
+
* client-facing API schema. Never throws — a composition failure comes back in
|
|
20
|
+
* `errors` so the dev server can surface it and keep serving. The URLs are baked into
|
|
21
|
+
* the supergraph so `--gateway` routes correctly; for `--compose` they're cosmetic.
|
|
22
|
+
*/
|
|
23
|
+
export function composeWithIntegrationLayer(integrationLayerSdl, extensionSdl, urls) {
|
|
24
|
+
let services;
|
|
25
|
+
try {
|
|
26
|
+
services = [
|
|
27
|
+
{ name: INTEGRATION_LAYER_SERVICE, typeDefs: parse(integrationLayerSdl), url: urls.integrationLayerUrl },
|
|
28
|
+
{ name: EXTENSION_SERVICE, typeDefs: parse(extensionSdl), url: urls.extensionUrl },
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
return { ok: false, errors: [`SDL is not valid GraphQL: ${err.message}`] };
|
|
33
|
+
}
|
|
34
|
+
const result = composeServices(services);
|
|
35
|
+
// `errors` is the discriminant of the composition-failure variant; its presence
|
|
36
|
+
// narrows `result` to the success variant (with `schema` + `supergraphSdl`).
|
|
37
|
+
if (result.errors) {
|
|
38
|
+
return { ok: false, errors: result.errors.map((e) => e.message) };
|
|
39
|
+
}
|
|
40
|
+
const apiSchema = result.schema.toAPISchema().toGraphQLJSSchema();
|
|
41
|
+
return {
|
|
42
|
+
ok: true,
|
|
43
|
+
apiSchema,
|
|
44
|
+
apiSdl: printSchema(apiSchema),
|
|
45
|
+
supergraphSdl: result.supergraphSdl,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=compose.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compose.js","sourceRoot":"","sources":["../../../src/lib/tooling/compose.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,sFAAsF;AACtF,EAAE;AACF,sEAAsE;AACtE,wFAAwF;AACxF,mFAAmF;AACnF,sFAAsF;AACtF,qFAAqF;AACrF,4BAA4B;AAE5B,OAAO,EAAE,KAAK,EAAE,WAAW,EAAsB,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD;;mDAEmD;AACnD,MAAM,CAAC,MAAM,yBAAyB,GAAG,mBAAmB,CAAC;AAC7D,MAAM,CAAC,MAAM,iBAAiB,GAAG,YAAY,CAAC;AAc9C;;;;;GAKG;AACH,MAAM,UAAU,2BAA2B,CACzC,mBAA2B,EAC3B,YAAoB,EACpB,IAA2D;IAE3D,IAAI,QAAQ,CAAC;IACb,IAAI,CAAC;QACH,QAAQ,GAAG;YACT,EAAE,IAAI,EAAE,yBAAyB,EAAE,QAAQ,EAAE,KAAK,CAAC,mBAAmB,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,mBAAmB,EAAE;YACxG,EAAE,IAAI,EAAE,iBAAiB,EAAE,QAAQ,EAAE,KAAK,CAAC,YAAY,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,YAAY,EAAE;SACnF,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,6BAA8B,GAAa,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;IACxF,CAAC;IAED,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IACzC,gFAAgF;IAChF,6EAA6E;IAC7E,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACpE,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,iBAAiB,EAAE,CAAC;IAClE,OAAO;QACL,EAAE,EAAE,IAAI;QACR,SAAS;QACT,MAAM,EAAE,WAAW,CAAC,SAAS,CAAC;QAC9B,aAAa,EAAE,MAAM,CAAC,aAAa;KACpC,CAAC;AACJ,CAAC"}
|