@commercetools/cli-topic-integration-layer 0.2.1 → 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.
- package/README.md +1 -0
- package/dist/commands/integration-layer/extension/build.d.ts +2 -0
- package/dist/commands/integration-layer/extension/build.js +24 -3
- package/dist/commands/integration-layer/extension/build.js.map +1 -1
- package/dist/commands/integration-layer/extension/push.d.ts +15 -0
- package/dist/commands/integration-layer/extension/push.js +137 -3
- package/dist/commands/integration-layer/extension/push.js.map +1 -1
- package/dist/commands/integration-layer/extension/serve.d.ts +19 -0
- package/dist/commands/integration-layer/extension/serve.js +211 -3
- package/dist/commands/integration-layer/extension/serve.js.map +1 -1
- package/dist/commands/integration-layer/extension/status.js +4 -0
- package/dist/commands/integration-layer/extension/status.js.map +1 -1
- package/dist/commands/integration-layer/extension/validate.d.ts +2 -0
- package/dist/commands/integration-layer/extension/validate.js +24 -2
- package/dist/commands/integration-layer/extension/validate.js.map +1 -1
- package/dist/commands/integration-layer/init.js +219 -75
- package/dist/commands/integration-layer/init.js.map +1 -1
- package/dist/lib/awaitBundleState.d.ts +33 -0
- package/dist/lib/awaitBundleState.js +77 -0
- package/dist/lib/awaitBundleState.js.map +1 -0
- package/dist/lib/ilClient.d.ts +46 -2
- package/dist/lib/ilClient.js +14 -2
- package/dist/lib/ilClient.js.map +1 -1
- package/dist/lib/sourceRevision.d.ts +39 -0
- package/dist/lib/sourceRevision.js +95 -0
- package/dist/lib/sourceRevision.js.map +1 -0
- package/dist/lib/tooling/extensions.d.ts +77 -0
- package/dist/lib/tooling/extensions.js +209 -0
- package/dist/lib/tooling/extensions.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"awaitBundleState.js","sourceRoot":"","sources":["../../src/lib/awaitBundleState.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,EAAE;AACF,mFAAmF;AACnF,+EAA+E;AAC/E,gFAAgF;AAChF,mFAAmF;AACnF,oFAAoF;AACpF,EAAE;AACF,iFAAiF;AACjF,kEAAkE;AAClE,EAAE;AACF,4CAA4C;AAC5C,EAAE;AACF,oFAAoF;AACpF,qFAAqF;AACrF,gFAAgF;AAChF,+EAA+E;AAC/E,kFAAkF;AAClF,qFAAqF;AACrF,2CAA2C;AAC3C,kFAAkF;AAClF,iEAAiE;AAEjE,OAAO,EAAE,kBAAkB,EAAsB,MAAM,eAAe,CAAC;AAoBvE,MAAM,YAAY,GAAG,CAAC,EAAU,EAAiB,EAAE,CACjD,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,OAAe,EACf,UAAkB,EAClB,KAAa,EACb,OAAe,EACf,OAAqB;IAErB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,YAAY,CAAC;IAC5C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACpC,MAAM,QAAQ,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC;IAE3C,SAAS,CAAC;QACR,IAAI,IAA0B,CAAC;QAC/B,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,kBAAkB,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;QAC9D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,8CAA+C,GAAa,CAAC,OAAO,EAAE;aAC/E,CAAC;QACJ,CAAC;QAED,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,4DAA4D;YAC5D,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,+CAA+C,EAAE,CAAC;QACtF,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC7B,uEAAuE;YACvE,OAAO;gBACL,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,sDAAsD;gBAC9D,IAAI;aACL,CAAC;QACJ,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;YAC7B,4EAA4E;YAC5E,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;QACtC,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QAC/D,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ;YAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAE7D,iBAAiB;QACjB,IAAI,GAAG,EAAE,IAAI,QAAQ,EAAE,CAAC;YACtB,OAAO;gBACL,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,2CAA2C,OAAO,WAAW,IAAI,CAAC,KAAK,CAC7E,OAAO,CAAC,SAAS,GAAG,IAAI,CACzB,GAAG;gBACJ,IAAI;aACL,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAClC,CAAC;AACH,CAAC"}
|
package/dist/lib/ilClient.d.ts
CHANGED
|
@@ -4,6 +4,16 @@ export interface ConfigEntry {
|
|
|
4
4
|
value: string | null;
|
|
5
5
|
secret: boolean;
|
|
6
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";
|
|
7
17
|
/** The stored bundle's metadata (integration layer `ExtensionMeta`). */
|
|
8
18
|
export interface ExtensionMeta {
|
|
9
19
|
projectKey: string;
|
|
@@ -12,6 +22,34 @@ export interface ExtensionMeta {
|
|
|
12
22
|
version: number;
|
|
13
23
|
updatedBy?: string;
|
|
14
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;
|
|
15
53
|
}
|
|
16
54
|
export interface BreakingChange {
|
|
17
55
|
type: string;
|
|
@@ -29,8 +67,14 @@ export interface RemoteValidationResult {
|
|
|
29
67
|
export declare function fetchSubgraphSdl(baseUrl: string, projectKey: string, token: string): Promise<string>;
|
|
30
68
|
/** POST a candidate SDL to the composition + breaking-change gate (`POST …/extension/validate`). */
|
|
31
69
|
export declare function remoteValidate(baseUrl: string, projectKey: string, token: string, typeDefs: string): Promise<RemoteValidationResult>;
|
|
32
|
-
/**
|
|
33
|
-
|
|
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>;
|
|
34
78
|
/** The stored bundle's metadata, or null if none is stored (`GET …/extension/bundle/meta`). */
|
|
35
79
|
export declare function fetchExtensionMeta(baseUrl: string, projectKey: string, token: string): Promise<ExtensionMeta | null>;
|
|
36
80
|
/** Remove the project's extension subgraph from Hive (`DELETE …/extension/subgraph`). */
|
package/dist/lib/ilClient.js
CHANGED
|
@@ -36,8 +36,14 @@ export async function remoteValidate(baseUrl, projectKey, token, typeDefs) {
|
|
|
36
36
|
}
|
|
37
37
|
return JSON.parse(text);
|
|
38
38
|
}
|
|
39
|
-
/**
|
|
40
|
-
|
|
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) {
|
|
41
47
|
const url = `${apiRoot(baseUrl, projectKey)}/extension/bundle`;
|
|
42
48
|
const res = await fetch(url, {
|
|
43
49
|
method: "PUT",
|
|
@@ -46,6 +52,12 @@ export async function pushBundle(baseUrl, projectKey, token, bundle, filename) {
|
|
|
46
52
|
// The route requires the original filename (percent-encoded; it carries the
|
|
47
53
|
// module's type). The bundle is plain CommonJS, so name it `.cjs`.
|
|
48
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
|
+
: {}),
|
|
49
61
|
...bearer(token),
|
|
50
62
|
},
|
|
51
63
|
body: bundle,
|
package/dist/lib/ilClient.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ilClient.js","sourceRoot":"","sources":["../../src/lib/ilClient.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,oFAAoF;AACpF,uFAAuF;AACvF,+EAA+E;AAC/E,kEAAkE;AAClE,sFAAsF;AACtF,oFAAoF;AACpF,kEAAkE;
|
|
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,77 @@
|
|
|
1
|
+
import { type GraphQLSchema } from "graphql";
|
|
2
|
+
/** An extension package discovered under the workspace's extensions/ directory. */
|
|
3
|
+
export interface DiscoveredExtension {
|
|
4
|
+
/** The package/directory name (used only for messages — the combined subgraph has one name). */
|
|
5
|
+
name: string;
|
|
6
|
+
/** The extension source entry (`<dir>/src/extension.ts`). */
|
|
7
|
+
entry: string;
|
|
8
|
+
/** The esbuild bundle output for a standalone build (`<dir>/dist/extension.js`). */
|
|
9
|
+
outfile: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Discover every extension package under `<root>/<dirName>` that has a
|
|
13
|
+
* `src/extension.ts`. Sorted by name for stable, deterministic merge output.
|
|
14
|
+
*/
|
|
15
|
+
export declare function discoverExtensions(root: string, dirName: string): Promise<DiscoveredExtension[]>;
|
|
16
|
+
/** One extension's contribution to the combined subgraph. */
|
|
17
|
+
export interface ExtensionModule {
|
|
18
|
+
name: string;
|
|
19
|
+
typeDefs: string;
|
|
20
|
+
resolvers: object;
|
|
21
|
+
}
|
|
22
|
+
/** The single subgraph all extensions merge into. */
|
|
23
|
+
export interface MergedSubgraph {
|
|
24
|
+
/** Executable subgraph schema (typeDefs + wired resolvers) — for serving. */
|
|
25
|
+
schema: GraphQLSchema;
|
|
26
|
+
/** Federation SDL of the merged subgraph — for composing with the core / publishing. */
|
|
27
|
+
sdl: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Merge several extension modules into ONE federation subgraph — the single bundle a
|
|
31
|
+
* project deploys. Each extension's SDL is a self-contained subgraph (its own
|
|
32
|
+
* `type Query`, its own `@link` header), so we can't just concatenate them
|
|
33
|
+
* (`buildSubgraphSchema` would see two `type Query`). `mergeTypeDefs` folds same-named
|
|
34
|
+
* types into one — so two extensions can each contribute fields to `Query` — and dedupes
|
|
35
|
+
* the repeated `@link`.
|
|
36
|
+
*
|
|
37
|
+
* First we reject two extensions declaring the SAME field (even identically): merged into
|
|
38
|
+
* one bundle their resolvers would silently shadow each other, so it's an authoring error
|
|
39
|
+
* we name loudly rather than paper over. The merged SDL is printed with
|
|
40
|
+
* `printSubgraphSchema`, so it round-trips as the exact federation SDL the IL validates /
|
|
41
|
+
* publishes for the deployed bundle.
|
|
42
|
+
*/
|
|
43
|
+
export declare function mergeExtensionSubgraph(modules: ExtensionModule[]): MergedSubgraph;
|
|
44
|
+
/** Result of a combined build — the same shape {@link buildBundle} returns. */
|
|
45
|
+
export interface CombinedBuildResult {
|
|
46
|
+
/** Absolute path to the single combined bundle. */
|
|
47
|
+
outfile: string;
|
|
48
|
+
/** The authors' source files that went into it (for static analysis; excludes the generated entry). */
|
|
49
|
+
sourceFiles: string[];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Build ONE combined bundle from every discovered extension — the artifact `push`
|
|
53
|
+
* uploads. Each extension is bundled + loaded to read its `typeDefs` (merged into one
|
|
54
|
+
* SDL, so a clash fails here on the author's machine) and to be re-imported by a
|
|
55
|
+
* generated entry that, at load time, merges the resolvers (per type, then per field),
|
|
56
|
+
* concatenates `apiExtensions`, and merges `hooks`. That entry is then bundled into the
|
|
57
|
+
* final self-contained artifact — a faithful stand-in for the single deployed bundle.
|
|
58
|
+
*/
|
|
59
|
+
export declare function buildCombinedBundle(extensions: DiscoveredExtension[], outfile: string): Promise<CombinedBuildResult>;
|
|
60
|
+
/** What {@link bundleForFlags} produced, plus a label for the command's log line. */
|
|
61
|
+
export interface BundleForFlags extends CombinedBuildResult {
|
|
62
|
+
/** Human description of what was bundled (the entry, or "N extension(s) (…)"). */
|
|
63
|
+
describe: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The single build step shared by `build`/`validate`/`push`: bundle either the one
|
|
67
|
+
* extension in the cwd (default) or — with `--all` — every extension under
|
|
68
|
+
* `<extensionsDir>/*` merged into the one combined bundle a project deploys. Throws a
|
|
69
|
+
* clean message when `--all` finds nothing to build.
|
|
70
|
+
*/
|
|
71
|
+
export declare function bundleForFlags(opts: {
|
|
72
|
+
all: boolean;
|
|
73
|
+
extensionsDir: string;
|
|
74
|
+
entry: string;
|
|
75
|
+
out: string;
|
|
76
|
+
cwd?: string;
|
|
77
|
+
}): Promise<BundleForFlags>;
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// Monorepo helpers for the "one bundle per project" model. In production a project
|
|
2
|
+
// has a SINGLE extension bundle = one federation subgraph (all of a repo's extensions
|
|
3
|
+
// merged), which the router then composes with the integration-layer core subgraph.
|
|
4
|
+
// So a monorepo of `extensions/*` packages must:
|
|
5
|
+
// - MERGE into one subgraph for local `serve --all` (mergeExtensionSubgraph), and
|
|
6
|
+
// - BUILD into one bundle for `build/validate/push --all` (buildCombinedBundle),
|
|
7
|
+
// never one-subgraph-per-extension (that's not the deployed shape).
|
|
8
|
+
import { readdir, readFile, stat, writeFile, mkdtemp } from "node:fs/promises";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { join, resolve } from "node:path";
|
|
11
|
+
import process from "node:process";
|
|
12
|
+
import { buildSubgraphSchema, printSubgraphSchema } from "@apollo/subgraph";
|
|
13
|
+
import { mergeTypeDefs } from "@graphql-tools/merge";
|
|
14
|
+
import { Kind, parse } from "graphql";
|
|
15
|
+
import { buildBundle } from "./build.js";
|
|
16
|
+
import { loadBundleSource } from "./loadBundle.js";
|
|
17
|
+
/**
|
|
18
|
+
* Discover every extension package under `<root>/<dirName>` that has a
|
|
19
|
+
* `src/extension.ts`. Sorted by name for stable, deterministic merge output.
|
|
20
|
+
*/
|
|
21
|
+
export async function discoverExtensions(root, dirName) {
|
|
22
|
+
const base = join(root, dirName);
|
|
23
|
+
const dirents = await readdir(base, { withFileTypes: true }).catch(() => []);
|
|
24
|
+
const found = [];
|
|
25
|
+
for (const dirent of dirents) {
|
|
26
|
+
if (!dirent.isDirectory())
|
|
27
|
+
continue;
|
|
28
|
+
const entry = join(base, dirent.name, "src", "extension.ts");
|
|
29
|
+
const hasEntry = await stat(entry)
|
|
30
|
+
.then((s) => s.isFile())
|
|
31
|
+
.catch(() => false);
|
|
32
|
+
if (!hasEntry)
|
|
33
|
+
continue;
|
|
34
|
+
found.push({
|
|
35
|
+
name: dirent.name,
|
|
36
|
+
entry,
|
|
37
|
+
outfile: join(base, dirent.name, "dist", "extension.js"),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return found.sort((a, b) => a.name.localeCompare(b.name));
|
|
41
|
+
}
|
|
42
|
+
/** Collect `Type.field` keys an SDL declares on object/interface types (incl. extensions). */
|
|
43
|
+
function declaredFields(sdl) {
|
|
44
|
+
const out = [];
|
|
45
|
+
for (const def of parse(sdl).definitions) {
|
|
46
|
+
if (def.kind === Kind.OBJECT_TYPE_DEFINITION ||
|
|
47
|
+
def.kind === Kind.OBJECT_TYPE_EXTENSION ||
|
|
48
|
+
def.kind === Kind.INTERFACE_TYPE_DEFINITION ||
|
|
49
|
+
def.kind === Kind.INTERFACE_TYPE_EXTENSION) {
|
|
50
|
+
for (const f of def.fields ?? [])
|
|
51
|
+
out.push({ type: def.name.value, field: f.name.value });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Merge several extension modules into ONE federation subgraph — the single bundle a
|
|
58
|
+
* project deploys. Each extension's SDL is a self-contained subgraph (its own
|
|
59
|
+
* `type Query`, its own `@link` header), so we can't just concatenate them
|
|
60
|
+
* (`buildSubgraphSchema` would see two `type Query`). `mergeTypeDefs` folds same-named
|
|
61
|
+
* types into one — so two extensions can each contribute fields to `Query` — and dedupes
|
|
62
|
+
* the repeated `@link`.
|
|
63
|
+
*
|
|
64
|
+
* First we reject two extensions declaring the SAME field (even identically): merged into
|
|
65
|
+
* one bundle their resolvers would silently shadow each other, so it's an authoring error
|
|
66
|
+
* we name loudly rather than paper over. The merged SDL is printed with
|
|
67
|
+
* `printSubgraphSchema`, so it round-trips as the exact federation SDL the IL validates /
|
|
68
|
+
* publishes for the deployed bundle.
|
|
69
|
+
*/
|
|
70
|
+
export function mergeExtensionSubgraph(modules) {
|
|
71
|
+
if (modules.length === 0) {
|
|
72
|
+
throw new Error("no extension exports a GraphQL subgraph (typeDefs) to merge");
|
|
73
|
+
}
|
|
74
|
+
const resolvers = {};
|
|
75
|
+
const sdls = [];
|
|
76
|
+
const owner = new Map(); // "Type.field" -> first extension that declared it
|
|
77
|
+
for (const m of modules) {
|
|
78
|
+
if (typeof m.typeDefs !== "string" || m.typeDefs.trim() === "") {
|
|
79
|
+
throw new Error(`extension '${m.name}' must export a non-empty \`typeDefs\` string`);
|
|
80
|
+
}
|
|
81
|
+
if (m.resolvers === null || typeof m.resolvers !== "object") {
|
|
82
|
+
throw new Error(`extension '${m.name}' with \`typeDefs\` must also export a \`resolvers\` object`);
|
|
83
|
+
}
|
|
84
|
+
let fields;
|
|
85
|
+
try {
|
|
86
|
+
fields = declaredFields(m.typeDefs);
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
throw new Error(`extension '${m.name}' has invalid \`typeDefs\`: ${err.message}`);
|
|
90
|
+
}
|
|
91
|
+
for (const { type, field } of fields) {
|
|
92
|
+
const key = `${type}.${field}`;
|
|
93
|
+
const prev = owner.get(key);
|
|
94
|
+
if (prev !== undefined && prev !== m.name) {
|
|
95
|
+
throw new Error(`extensions '${prev}' and '${m.name}' both declare \`${key}\` — a project ships one bundle, so each field must be owned by a single extension`);
|
|
96
|
+
}
|
|
97
|
+
owner.set(key, m.name);
|
|
98
|
+
}
|
|
99
|
+
sdls.push(m.typeDefs);
|
|
100
|
+
// Merge resolvers per type then per field — the same shape the combined bundle uses.
|
|
101
|
+
for (const [type, fieldResolvers] of Object.entries(m.resolvers)) {
|
|
102
|
+
resolvers[type] = Object.assign(resolvers[type] ?? {}, fieldResolvers);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
let schema;
|
|
106
|
+
try {
|
|
107
|
+
// `mergeTypeDefs` folds the per-extension subgraphs into one document. `@apollo/subgraph`
|
|
108
|
+
// types `resolvers` as its internal `GraphQLResolverMap`; cast through the param type.
|
|
109
|
+
const typeDefs = mergeTypeDefs(sdls);
|
|
110
|
+
schema = buildSubgraphSchema({ typeDefs, resolvers });
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
throw new Error(`extensions do not merge into a single subgraph: ${err.message}`);
|
|
114
|
+
}
|
|
115
|
+
return { schema, sdl: printSubgraphSchema(schema) };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Build ONE combined bundle from every discovered extension — the artifact `push`
|
|
119
|
+
* uploads. Each extension is bundled + loaded to read its `typeDefs` (merged into one
|
|
120
|
+
* SDL, so a clash fails here on the author's machine) and to be re-imported by a
|
|
121
|
+
* generated entry that, at load time, merges the resolvers (per type, then per field),
|
|
122
|
+
* concatenates `apiExtensions`, and merges `hooks`. That entry is then bundled into the
|
|
123
|
+
* final self-contained artifact — a faithful stand-in for the single deployed bundle.
|
|
124
|
+
*/
|
|
125
|
+
export async function buildCombinedBundle(extensions, outfile) {
|
|
126
|
+
if (extensions.length === 0) {
|
|
127
|
+
throw new Error("no extensions to build");
|
|
128
|
+
}
|
|
129
|
+
// 1. Bundle + load each extension to read its exported subgraph (typeDefs/resolvers).
|
|
130
|
+
const tmp = await mkdtemp(join(tmpdir(), "il-combine-"));
|
|
131
|
+
const loaded = [];
|
|
132
|
+
for (const ext of extensions) {
|
|
133
|
+
const out = join(tmp, `${ext.name}.js`);
|
|
134
|
+
await buildBundle(ext.entry, out);
|
|
135
|
+
loaded.push({ name: ext.name, module: loadBundleSource(await readFile(out, "utf8")) });
|
|
136
|
+
}
|
|
137
|
+
// 2. Merge the subgraph-bearing extensions into one SDL (conflict detection). An
|
|
138
|
+
// extension may be API-extensions-only (no typeDefs) — those contribute no SDL.
|
|
139
|
+
const withSubgraph = loaded.filter((l) => typeof l.module.typeDefs === "string" && l.module.typeDefs.trim() !== "");
|
|
140
|
+
const mergedSdl = withSubgraph.length > 0
|
|
141
|
+
? mergeExtensionSubgraph(withSubgraph.map((l) => ({
|
|
142
|
+
name: l.name,
|
|
143
|
+
typeDefs: l.module.typeDefs,
|
|
144
|
+
resolvers: l.module.resolvers ?? {},
|
|
145
|
+
}))).sdl
|
|
146
|
+
: undefined;
|
|
147
|
+
// 3. Generate the combining entry: re-import each extension, merge at load time.
|
|
148
|
+
const imports = extensions
|
|
149
|
+
.map((e, i) => `import * as e${i} from ${JSON.stringify(e.entry)};`)
|
|
150
|
+
.join("\n");
|
|
151
|
+
const modulesArray = `[${extensions.map((_, i) => `e${i}`).join(", ")}]`;
|
|
152
|
+
const entrySource = `${imports}
|
|
153
|
+
|
|
154
|
+
const __modules = ${modulesArray};
|
|
155
|
+
|
|
156
|
+
${mergedSdl !== undefined ? `export const typeDefs = ${JSON.stringify(mergedSdl)};` : ""}
|
|
157
|
+
|
|
158
|
+
const __mergeResolvers = (mods) => {
|
|
159
|
+
const out = {};
|
|
160
|
+
for (const m of mods) {
|
|
161
|
+
const r = m && m.resolvers;
|
|
162
|
+
if (!r || typeof r !== "object") continue;
|
|
163
|
+
for (const type of Object.keys(r)) {
|
|
164
|
+
out[type] = Object.assign(out[type] ?? {}, r[type]);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return out;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export const resolvers = __mergeResolvers(__modules);
|
|
171
|
+
export const apiExtensions = __modules.flatMap((m) =>
|
|
172
|
+
m && Array.isArray(m.apiExtensions) ? m.apiExtensions : [],
|
|
173
|
+
);
|
|
174
|
+
export const hooks = Object.assign(
|
|
175
|
+
{},
|
|
176
|
+
...__modules.map((m) => (m && m.hooks && typeof m.hooks === "object" ? m.hooks : {})),
|
|
177
|
+
);
|
|
178
|
+
`;
|
|
179
|
+
const entryPath = join(tmp, "combined-entry.ts");
|
|
180
|
+
await writeFile(entryPath, entrySource, "utf8");
|
|
181
|
+
// 4. Bundle the generated entry into the single artifact. Its imported author sources
|
|
182
|
+
// feed static analysis; the generated entry itself is trusted, so drop it.
|
|
183
|
+
const { sourceFiles } = await buildBundle(entryPath, outfile);
|
|
184
|
+
const generated = resolve(entryPath);
|
|
185
|
+
return { outfile, sourceFiles: sourceFiles.filter((f) => resolve(f) !== generated) };
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* The single build step shared by `build`/`validate`/`push`: bundle either the one
|
|
189
|
+
* extension in the cwd (default) or — with `--all` — every extension under
|
|
190
|
+
* `<extensionsDir>/*` merged into the one combined bundle a project deploys. Throws a
|
|
191
|
+
* clean message when `--all` finds nothing to build.
|
|
192
|
+
*/
|
|
193
|
+
export async function bundleForFlags(opts) {
|
|
194
|
+
if (!opts.all) {
|
|
195
|
+
const { outfile, sourceFiles } = await buildBundle(opts.entry, opts.out);
|
|
196
|
+
return { outfile, sourceFiles, describe: opts.entry };
|
|
197
|
+
}
|
|
198
|
+
const extensions = await discoverExtensions(opts.cwd ?? process.cwd(), opts.extensionsDir);
|
|
199
|
+
if (extensions.length === 0) {
|
|
200
|
+
throw new Error(`no extensions found under ./${opts.extensionsDir}/*/src/extension.ts — run --all from the monorepo root`);
|
|
201
|
+
}
|
|
202
|
+
const { outfile, sourceFiles } = await buildCombinedBundle(extensions, opts.out);
|
|
203
|
+
return {
|
|
204
|
+
outfile,
|
|
205
|
+
sourceFiles,
|
|
206
|
+
describe: `${extensions.length} extension(s) (${extensions.map((e) => e.name).join(", ")})`,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=extensions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extensions.js","sourceRoot":"","sources":["../../../src/lib/tooling/extensions.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,sFAAsF;AACtF,oFAAoF;AACpF,iDAAiD;AACjD,oFAAoF;AACpF,mFAAmF;AACnF,oEAAoE;AAEpE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC/E,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAsB,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAwB,MAAM,iBAAiB,CAAC;AAYzE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,IAAY,EACZ,OAAe;IAEf,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACjC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAC7E,MAAM,KAAK,GAA0B,EAAE,CAAC;IACxC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE;YAAE,SAAS;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC;QAC7D,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC;aAC/B,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;aACvB,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;QACtB,IAAI,CAAC,QAAQ;YAAE,SAAS;QACxB,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,KAAK;YACL,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,CAAC;SACzD,CAAC,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5D,CAAC;AAiBD,8FAA8F;AAC9F,SAAS,cAAc,CAAC,GAAW;IACjC,MAAM,GAAG,GAAsC,EAAE,CAAC;IAClD,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;QACzC,IACE,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,sBAAsB;YACxC,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,qBAAqB;YACvC,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,yBAAyB;YAC3C,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,wBAAwB,EAC1C,CAAC;YACD,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE;gBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAC5F,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAA0B;IAC/D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;IACjF,CAAC;IACD,MAAM,SAAS,GAA4C,EAAE,CAAC;IAC9D,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC,CAAC,mDAAmD;IAC5F,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAC/D,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC,IAAI,+CAA+C,CAAC,CAAC;QACvF,CAAC;QACD,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI,IAAI,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC5D,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC,IAAI,6DAA6D,CAAC,CAAC;QACrG,CAAC;QACD,IAAI,MAAM,CAAC;QACX,IAAI,CAAC;YACH,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC,IAAI,+BAAgC,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC/F,CAAC;QACD,KAAK,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,MAAM,EAAE,CAAC;YACrC,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,KAAK,EAAE,CAAC;YAC/B,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC5B,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC1C,MAAM,IAAI,KAAK,CACb,eAAe,IAAI,UAAU,CAAC,CAAC,IAAI,oBAAoB,GAAG,oFAAoF,CAC/I,CAAC;YACJ,CAAC;YACD,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACtB,qFAAqF;QACrF,KAAK,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,SAAoC,CAAC,EAAE,CAAC;YAC5F,SAAS,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,cAAyC,CAAC,CAAC;QACpG,CAAC;IACH,CAAC;IAED,IAAI,MAAqB,CAAC;IAC1B,IAAI,CAAC;QACH,0FAA0F;QAC1F,uFAAuF;QACvF,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,GAAG,mBAAmB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAE9C,CAAC,CAAC;IACR,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,mDAAoD,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IAC/F,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,mBAAmB,CAAC,MAAM,CAAC,EAAE,CAAC;AACtD,CAAC;AAUD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,UAAiC,EACjC,OAAe;IAEf,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;IAED,sFAAsF;IACtF,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC;IACzD,MAAM,MAAM,GAAgD,EAAE,CAAC;IAC/D,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC;QACxC,MAAM,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAClC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,gBAAgB,CAAC,MAAM,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC;IACzF,CAAC;IAED,iFAAiF;IACjF,mFAAmF;IACnF,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAChC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,QAAQ,IAAK,CAAC,CAAC,MAAM,CAAC,QAAmB,CAAC,IAAI,EAAE,KAAK,EAAE,CAC5F,CAAC;IACF,MAAM,SAAS,GACb,YAAY,CAAC,MAAM,GAAG,CAAC;QACrB,CAAC,CAAC,sBAAsB,CACpB,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACvB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,QAAkB;YACrC,SAAS,EAAG,CAAC,CAAC,MAAM,CAAC,SAAgC,IAAI,EAAE;SAC5D,CAAC,CAAC,CACJ,CAAC,GAAG;QACP,CAAC,CAAC,SAAS,CAAC;IAEhB,iFAAiF;IACjF,MAAM,OAAO,GAAG,UAAU;SACvB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC;SACnE,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,MAAM,YAAY,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IACzE,MAAM,WAAW,GAAG,GAAG,OAAO;;oBAEZ,YAAY;;EAE9B,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,2BAA2B,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;;;;;;;;CAsBvF,CAAC;IACA,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC;IACjD,MAAM,SAAS,CAAC,SAAS,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;IAEhD,sFAAsF;IACtF,8EAA8E;IAC9E,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC9D,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACrC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,EAAE,CAAC;AACvF,CAAC;AAQD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAMpC;IACC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;QACd,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QACzE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;IACxD,CAAC;IACD,MAAM,UAAU,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;IAC3F,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CACb,+BAA+B,IAAI,CAAC,aAAa,wDAAwD,CAC1G,CAAC;IACJ,CAAC;IACD,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,mBAAmB,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACjF,OAAO;QACL,OAAO;QACP,WAAW;QACX,QAAQ,EAAE,GAAG,UAAU,CAAC,MAAM,kBAAkB,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;KAC5F,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commercetools/cli-topic-integration-layer",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "commercetools CLI plugin: integration-layer topic",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"@apollo/gateway": "^2.14.1",
|
|
26
26
|
"@apollo/subgraph": "^2.14.1",
|
|
27
27
|
"@envelop/apollo-federation": "^8.1.1",
|
|
28
|
+
"@graphql-tools/merge": "^9.2.2",
|
|
28
29
|
"@oclif/core": "4.2.10",
|
|
29
30
|
"esbuild": "^0.28.0",
|
|
30
31
|
"graphql": "^16.9.0",
|