@commercetools/cli-topic-integration-layer 0.2.1 → 0.3.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/README.md +1 -0
- package/dist/commands/integration-layer/explore.d.ts +20 -0
- package/dist/commands/integration-layer/explore.js +209 -0
- package/dist/commands/integration-layer/explore.js.map +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/invoke.d.ts +1 -0
- package/dist/commands/integration-layer/extension/invoke.js +9 -4
- package/dist/commands/integration-layer/extension/invoke.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 +30 -0
- package/dist/commands/integration-layer/extension/serve.js +244 -6
- 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 +228 -77
- 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/base.d.ts +35 -0
- package/dist/lib/base.js +99 -13
- package/dist/lib/base.js.map +1 -1
- package/dist/lib/ilClient.d.ts +62 -2
- package/dist/lib/ilClient.js +42 -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/exploreSchema.d.ts +15 -0
- package/dist/lib/tooling/exploreSchema.js +77 -0
- package/dist/lib/tooling/exploreSchema.js.map +1 -0
- package/dist/lib/tooling/explorerPage.d.ts +1 -0
- package/dist/lib/tooling/explorerPage.js +96 -0
- package/dist/lib/tooling/explorerPage.js.map +1 -0
- package/dist/lib/tooling/explorerServer.d.ts +27 -0
- package/dist/lib/tooling/explorerServer.js +134 -0
- package/dist/lib/tooling/explorerServer.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/dist/lib/tooling/introspectionRequest.d.ts +13 -0
- package/dist/lib/tooling/introspectionRequest.js +47 -0
- package/dist/lib/tooling/introspectionRequest.js.map +1 -0
- package/dist/lib/tooling/session.d.ts +36 -0
- package/dist/lib/tooling/session.js +99 -0
- package/dist/lib/tooling/session.js.map +1 -0
- package/package.json +10 -12
package/dist/lib/base.js
CHANGED
|
@@ -1,16 +1,62 @@
|
|
|
1
1
|
// Base class for the topic's commands that reach the integration layer's
|
|
2
|
-
// `manage_project` boundary.
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// (loaded into the security context by the auth plugin's prerun hook) — no separate
|
|
6
|
-
// credential handling, exactly as the `connect` topic does.
|
|
2
|
+
// `manage_project` boundary. The bearer is the token `commercetools auth login`
|
|
3
|
+
// already persisted to `~/.commercetools/credentials`; we read it back from disk
|
|
4
|
+
// here rather than from the auth plugin's in-memory security context — see below.
|
|
7
5
|
//
|
|
8
|
-
// `@commercetools/cli-plugin-auth`
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
6
|
+
// `@commercetools/cli-plugin-auth` holds the security context in a MODULE-LEVEL
|
|
7
|
+
// STATIC (`SecurityContextHolder.globalContext`), so there is one context per loaded
|
|
8
|
+
// copy of that module. When this topic is bundled into the host CLI it resolves up
|
|
9
|
+
// into the host's single copy and the static the host fills is the one we read. But
|
|
10
|
+
// when the topic is installed via `oclif plugins install`, it lands in the oclif data
|
|
11
|
+
// dir's own node_modules tree, npm auto-installs the declared `cli-plugin-auth` there,
|
|
12
|
+
// and Node loads that as a SECOND, distinct module instance. `auth login`'s prerun
|
|
13
|
+
// hook (running in the host) fills the HOST copy's static; a read via the inherited
|
|
14
|
+
// `getAuthentication()` would hit the DATA-DIR copy's empty static and reject every
|
|
15
|
+
// logged-in caller ("not logged in"). This is not hypothetical — it is the exact
|
|
16
|
+
// failure mode that a CI symlink-dedupe workaround previously papered over.
|
|
17
|
+
//
|
|
18
|
+
// The cure is to stop depending on cross-copy static identity. `auth login` serializes
|
|
19
|
+
// the full principal (token, region, project key, scope, expiry) to
|
|
20
|
+
// `~/.commercetools/credentials` via the host CLI's `FileBasedAuthenticationRepository`.
|
|
21
|
+
// We reconstruct it from that same file, from THIS module's own copy of the repository
|
|
22
|
+
// — no network, no auth manager, no shared static — so it no longer matters whether one
|
|
23
|
+
// or two copies of `cli-plugin-auth` are loaded. Accordingly `cli-plugin-auth` is a
|
|
24
|
+
// regular `dependency`, not a peer: a second copy is now harmless. The path, auth id
|
|
25
|
+
// (`"client-credentials"`), and serialized shape mirror the host CLI's
|
|
26
|
+
// `providers.ts`/`init.ts` wiring exactly.
|
|
27
|
+
import os from "node:os";
|
|
28
|
+
import path from "node:path";
|
|
12
29
|
import { Flags } from "@oclif/core";
|
|
13
|
-
import { AuthCommand, AuthorizationError, CtpAuthorizedClient, } from "@commercetools/cli-plugin-auth";
|
|
30
|
+
import { AuthCommand, AuthorizationError, CtpAuthorizedClient, CtpClientAuthenticationToken, FileBasedAuthenticationRepository, } from "@commercetools/cli-plugin-auth";
|
|
31
|
+
/**
|
|
32
|
+
* Where `commercetools auth login` persists the serialized principal, and the auth id
|
|
33
|
+
* it files the client-credentials token under. These mirror the host CLI's
|
|
34
|
+
* `providers.ts` (`@commercetools/cli`) — the on-disk credentials contract — verbatim.
|
|
35
|
+
*/
|
|
36
|
+
const CREDENTIALS_FILE = path.join(os.homedir(), ".commercetools", "credentials");
|
|
37
|
+
const CLIENT_CREDENTIALS_AUTH_ID = "client-credentials";
|
|
38
|
+
/**
|
|
39
|
+
* A `FileBasedAuthenticationRepository` over the shared credentials file, wired to
|
|
40
|
+
* deserialize the client-credentials principal. Constructed from THIS module's copy of
|
|
41
|
+
* `cli-plugin-auth`, so `loadAuthentication()` reconstructs the principal regardless of
|
|
42
|
+
* how many copies of the auth plugin are loaded (see the header note).
|
|
43
|
+
*/
|
|
44
|
+
function persistedAuthenticationRepository(credentialsFile) {
|
|
45
|
+
const repository = new FileBasedAuthenticationRepository(credentialsFile);
|
|
46
|
+
repository.addSerializable(CLIENT_CREDENTIALS_AUTH_ID, CtpClientAuthenticationToken, CtpClientAuthenticationToken.Serializable);
|
|
47
|
+
return repository;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Reconstruct the logged-in principal from the persisted credentials file. Returns
|
|
51
|
+
* undefined when no valid client-credentials login is on disk (absent file, or a
|
|
52
|
+
* different auth kind), so unauthorized commands still run. No network. Exported for
|
|
53
|
+
* tests; commands go through {@link IntegrationLayerCommand.requirePrincipal}.
|
|
54
|
+
*/
|
|
55
|
+
export async function loadPersistedPrincipal(credentialsFile = CREDENTIALS_FILE) {
|
|
56
|
+
const authentication = await persistedAuthenticationRepository(credentialsFile).loadAuthentication();
|
|
57
|
+
const principal = authentication?.getPrincipal();
|
|
58
|
+
return principal instanceof CtpAuthorizedClient ? principal : undefined;
|
|
59
|
+
}
|
|
14
60
|
/**
|
|
15
61
|
* Derive the integration-layer EXTENSIONS edge base URL from the authenticated
|
|
16
62
|
* principal's commercetools region. The extensions edge — the connector-backend pod
|
|
@@ -30,6 +76,31 @@ export function edgeUrlForRegion(region) {
|
|
|
30
76
|
return undefined;
|
|
31
77
|
return `https://extensions.integration-layer.${trimmed}.commercetools.com`;
|
|
32
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* The GRAPHQL edge (the integration-router) for a region — where operations are
|
|
81
|
+
* actually served, at `/{project}/graphql`. A distinct host from the extensions
|
|
82
|
+
* edge above: the extensions edge is the connector backend (manage_project routes),
|
|
83
|
+
* the graphql edge is the router. `--graphql-url` / `IL_GRAPHQL_URL` overrides it
|
|
84
|
+
* for staging zones, which don't follow the production convention.
|
|
85
|
+
*/
|
|
86
|
+
export function graphqlEdgeUrlForRegion(region) {
|
|
87
|
+
const trimmed = region.trim();
|
|
88
|
+
if (!trimmed)
|
|
89
|
+
return undefined;
|
|
90
|
+
return `https://graphql.integration-layer.${trimmed}.commercetools.com`;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The IDENTITY edge (the storefront pod) for a region — where sessions are minted,
|
|
94
|
+
* at `POST /{project}/session`. Again a distinct host from both of the above; the
|
|
95
|
+
* four-pod split put shopper identity, shopper GraphQL, and the machine surface on
|
|
96
|
+
* separate ingresses. `--auth-url` / `IL_AUTH_URL` overrides it.
|
|
97
|
+
*/
|
|
98
|
+
export function authEdgeUrlForRegion(region) {
|
|
99
|
+
const trimmed = region.trim();
|
|
100
|
+
if (!trimmed)
|
|
101
|
+
return undefined;
|
|
102
|
+
return `https://auth.integration-layer.${trimmed}.commercetools.com`;
|
|
103
|
+
}
|
|
33
104
|
export class IntegrationLayerCommand extends AuthCommand {
|
|
34
105
|
/**
|
|
35
106
|
* Whether the command needs a logged-in principal. True for every command that
|
|
@@ -38,6 +109,12 @@ export class IntegrationLayerCommand extends AuthCommand {
|
|
|
38
109
|
* to fail loudly if the network path is actually taken.
|
|
39
110
|
*/
|
|
40
111
|
authorized = true;
|
|
112
|
+
/**
|
|
113
|
+
* The principal loaded from `~/.commercetools/credentials` in {@link init}, or
|
|
114
|
+
* undefined when no valid login is persisted. Resolved once per command run so the
|
|
115
|
+
* synchronous {@link requirePrincipal} call sites (here and in `explore`) are unchanged.
|
|
116
|
+
*/
|
|
117
|
+
persistedPrincipal;
|
|
41
118
|
static baseFlags = {
|
|
42
119
|
"integration-layer-url": Flags.string({
|
|
43
120
|
description: "integration-layer extensions edge base URL (also settable via INTEGRATION_LAYER_URL); overrides the URL derived from your login region",
|
|
@@ -51,14 +128,23 @@ export class IntegrationLayerCommand extends AuthCommand {
|
|
|
51
128
|
};
|
|
52
129
|
async init() {
|
|
53
130
|
await super.init();
|
|
131
|
+
this.persistedPrincipal = await this.loadPersistedPrincipal();
|
|
54
132
|
if (this.authorized)
|
|
55
133
|
this.requirePrincipal();
|
|
56
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Read the persisted login from `~/.commercetools/credentials`. Returns undefined when
|
|
137
|
+
* no login is on disk (so unauthorized commands like standalone `serve` still run) and
|
|
138
|
+
* only fails loudly when the file is present but malformed. No network — the token was
|
|
139
|
+
* already minted by `auth login` and serialized whole.
|
|
140
|
+
*/
|
|
141
|
+
async loadPersistedPrincipal() {
|
|
142
|
+
return loadPersistedPrincipal();
|
|
143
|
+
}
|
|
57
144
|
/** The logged-in commercetools principal, or an `AuthorizationError` pointing at `auth login`. */
|
|
58
145
|
requirePrincipal() {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return principal;
|
|
146
|
+
if (this.persistedPrincipal)
|
|
147
|
+
return this.persistedPrincipal;
|
|
62
148
|
throw new AuthorizationError("not logged in — run `commercetools auth login` first");
|
|
63
149
|
}
|
|
64
150
|
/**
|
package/dist/lib/base.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/lib/base.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/lib/base.ts"],"names":[],"mappings":"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"}
|
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;
|
|
@@ -27,10 +65,32 @@ export interface RemoteValidationResult {
|
|
|
27
65
|
}
|
|
28
66
|
/** GET the project's raw core-subgraph SDL — the compose input (`GET …/subgraph`). */
|
|
29
67
|
export declare function fetchSubgraphSdl(baseUrl: string, projectKey: string, token: string): Promise<string>;
|
|
68
|
+
/**
|
|
69
|
+
* GET the project's DEPLOYED composed graph as a PUBLIC API SCHEMA — what the router
|
|
70
|
+
* actually plans and serves (core subgraph + the project's published extension),
|
|
71
|
+
* read from Hive by the integration layer and reduced there (`GET …/schema/api`).
|
|
72
|
+
*
|
|
73
|
+
* The integration layer strips the federation machinery before sending, so this is
|
|
74
|
+
* `buildSchema`-able as-is; there is no supergraph to reduce client-side. It is the
|
|
75
|
+
* byte-identical response the Merchant Center console's explorer reads under its
|
|
76
|
+
* own operator JWT — one artifact, two guards.
|
|
77
|
+
*
|
|
78
|
+
* This is the authenticated replacement for introspecting the public edge: the
|
|
79
|
+
* router runs `introspection: false`, so the composed graph is read here, over the
|
|
80
|
+
* same `manage_project` boundary as `/subgraph`. 404 means the project has never
|
|
81
|
+
* published a composable version.
|
|
82
|
+
*/
|
|
83
|
+
export declare function fetchDeployedApiSchemaSdl(baseUrl: string, projectKey: string, token: string): Promise<string>;
|
|
30
84
|
/** POST a candidate SDL to the composition + breaking-change gate (`POST …/extension/validate`). */
|
|
31
85
|
export declare function remoteValidate(baseUrl: string, projectKey: string, token: string, typeDefs: string): Promise<RemoteValidationResult>;
|
|
32
|
-
/**
|
|
33
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Upload the built bundle, replacing the project's stored one (`PUT …/extension/bundle`).
|
|
88
|
+
*
|
|
89
|
+
* `sourceRevision` (optional) records which of the integrator's revisions this build
|
|
90
|
+
* came from; the integration layer stores it against the revision, shows it in the
|
|
91
|
+
* Merchant Center, and hands it to the connector.
|
|
92
|
+
*/
|
|
93
|
+
export declare function pushBundle(baseUrl: string, projectKey: string, token: string, bundle: string, filename: string, sourceRevision?: string): Promise<ExtensionMeta>;
|
|
34
94
|
/** The stored bundle's metadata, or null if none is stored (`GET …/extension/bundle/meta`). */
|
|
35
95
|
export declare function fetchExtensionMeta(baseUrl: string, projectKey: string, token: string): Promise<ExtensionMeta | null>;
|
|
36
96
|
/** Remove the project's extension subgraph from Hive (`DELETE …/extension/subgraph`). */
|
package/dist/lib/ilClient.js
CHANGED
|
@@ -22,6 +22,34 @@ export async function fetchSubgraphSdl(baseUrl, projectKey, token) {
|
|
|
22
22
|
}
|
|
23
23
|
return text;
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* GET the project's DEPLOYED composed graph as a PUBLIC API SCHEMA — what the router
|
|
27
|
+
* actually plans and serves (core subgraph + the project's published extension),
|
|
28
|
+
* read from Hive by the integration layer and reduced there (`GET …/schema/api`).
|
|
29
|
+
*
|
|
30
|
+
* The integration layer strips the federation machinery before sending, so this is
|
|
31
|
+
* `buildSchema`-able as-is; there is no supergraph to reduce client-side. It is the
|
|
32
|
+
* byte-identical response the Merchant Center console's explorer reads under its
|
|
33
|
+
* own operator JWT — one artifact, two guards.
|
|
34
|
+
*
|
|
35
|
+
* This is the authenticated replacement for introspecting the public edge: the
|
|
36
|
+
* router runs `introspection: false`, so the composed graph is read here, over the
|
|
37
|
+
* same `manage_project` boundary as `/subgraph`. 404 means the project has never
|
|
38
|
+
* published a composable version.
|
|
39
|
+
*/
|
|
40
|
+
export async function fetchDeployedApiSchemaSdl(baseUrl, projectKey, token) {
|
|
41
|
+
const url = `${apiRoot(baseUrl, projectKey)}/schema/api`;
|
|
42
|
+
const res = await fetch(url, { headers: { accept: "text/plain", ...bearer(token) } });
|
|
43
|
+
const text = await res.text();
|
|
44
|
+
if (res.status === 404) {
|
|
45
|
+
throw new Error(`project '${projectKey}' has no composed schema published yet — publish one from the ` +
|
|
46
|
+
`Merchant Center console (Schema → Refresh schema), or drop --deployed to compose locally`);
|
|
47
|
+
}
|
|
48
|
+
if (!res.ok) {
|
|
49
|
+
throw new Error(`could not fetch the deployed composed schema (${res.status}) from ${url}: ${text}`);
|
|
50
|
+
}
|
|
51
|
+
return text;
|
|
52
|
+
}
|
|
25
53
|
/** POST a candidate SDL to the composition + breaking-change gate (`POST …/extension/validate`). */
|
|
26
54
|
export async function remoteValidate(baseUrl, projectKey, token, typeDefs) {
|
|
27
55
|
const url = `${apiRoot(baseUrl, projectKey)}/extension/validate`;
|
|
@@ -36,8 +64,14 @@ export async function remoteValidate(baseUrl, projectKey, token, typeDefs) {
|
|
|
36
64
|
}
|
|
37
65
|
return JSON.parse(text);
|
|
38
66
|
}
|
|
39
|
-
/**
|
|
40
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Upload the built bundle, replacing the project's stored one (`PUT …/extension/bundle`).
|
|
69
|
+
*
|
|
70
|
+
* `sourceRevision` (optional) records which of the integrator's revisions this build
|
|
71
|
+
* came from; the integration layer stores it against the revision, shows it in the
|
|
72
|
+
* Merchant Center, and hands it to the connector.
|
|
73
|
+
*/
|
|
74
|
+
export async function pushBundle(baseUrl, projectKey, token, bundle, filename, sourceRevision) {
|
|
41
75
|
const url = `${apiRoot(baseUrl, projectKey)}/extension/bundle`;
|
|
42
76
|
const res = await fetch(url, {
|
|
43
77
|
method: "PUT",
|
|
@@ -46,6 +80,12 @@ export async function pushBundle(baseUrl, projectKey, token, bundle, filename) {
|
|
|
46
80
|
// The route requires the original filename (percent-encoded; it carries the
|
|
47
81
|
// module's type). The bundle is plain CommonJS, so name it `.cjs`.
|
|
48
82
|
"x-extension-filename": encodeURIComponent(filename),
|
|
83
|
+
// Percent-encoded for the same reason as the filename: keep an arbitrary value
|
|
84
|
+
// header-safe. Omitted entirely when there's no revision to report — the route
|
|
85
|
+
// treats a missing header as "no revision", not an error.
|
|
86
|
+
...(sourceRevision
|
|
87
|
+
? { "x-extension-source-revision": encodeURIComponent(sourceRevision) }
|
|
88
|
+
: {}),
|
|
49
89
|
...bearer(token),
|
|
50
90
|
},
|
|
51
91
|
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,15 @@
|
|
|
1
|
+
import { type GraphQLSchema } from "graphql";
|
|
2
|
+
/** A resolved explorer schema plus how it was obtained (for the startup banner). */
|
|
3
|
+
export interface ExplorerSchema {
|
|
4
|
+
schema: GraphQLSchema;
|
|
5
|
+
sdl: string;
|
|
6
|
+
/** e.g. `local compose (core + your extension)` or `deployed (Hive)`. */
|
|
7
|
+
describe: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The local-composition path: the project's core subgraph, plus `extensionSdl` when
|
|
11
|
+
* the caller found a local extension to build. Throws with the composition errors
|
|
12
|
+
* when the two don't compose — the same failure `extension validate` reports, and a
|
|
13
|
+
* real answer rather than a half-rendered explorer.
|
|
14
|
+
*/
|
|
15
|
+
export declare function composeLocalExplorerSchema(coreSdl: string, extensionSdl: string | undefined): ExplorerSchema;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// The schema the local explorer renders — resolved one of two ways.
|
|
2
|
+
//
|
|
3
|
+
// default compose locally: the project's core-subgraph SDL (GET /subgraph)
|
|
4
|
+
// plus, when the working directory holds one, your LOCAL extension.
|
|
5
|
+
// Fast inner loop: edit the extension, restart, see your fields.
|
|
6
|
+
//
|
|
7
|
+
// --deployed the project's DEPLOYED composed graph (GET /schema/api), read from
|
|
8
|
+
// Hive by the integration layer and reduced THERE to a public API
|
|
9
|
+
// schema — exactly what the router plans and serves, including
|
|
10
|
+
// whichever extension is actually deployed. The command just
|
|
11
|
+
// buildSchema()s it, so nothing in this module handles that path.
|
|
12
|
+
//
|
|
13
|
+
// Only the LOCAL path composes, which is why this module exists at all: a supergraph
|
|
14
|
+
// can't be composed on the server for a working-tree extension that was never pushed.
|
|
15
|
+
// Both paths end at a client-facing `GraphQLSchema`, which the explorer serves to
|
|
16
|
+
// GraphiQL (answering introspection locally). Execution is proxied to the real edge
|
|
17
|
+
// either way — this schema drives the docs pane, autocomplete, and validation only.
|
|
18
|
+
import { printSchema } from "graphql";
|
|
19
|
+
import { composeServices } from "@apollo/composition";
|
|
20
|
+
import { parse } from "graphql";
|
|
21
|
+
import { composeWithIntegrationLayer, INTEGRATION_LAYER_SERVICE, } from "./compose.js";
|
|
22
|
+
/** Placeholder URLs baked into a locally-composed supergraph. The explorer never
|
|
23
|
+
* routes through them (it proxies whole operations to the edge), so they are
|
|
24
|
+
* cosmetic — but composition requires each subgraph to declare one. */
|
|
25
|
+
const LOCAL_IL_URL = "http://integration-layer.local/graphql";
|
|
26
|
+
const LOCAL_EXTENSION_URL = "http://extension.local/graphql";
|
|
27
|
+
/**
|
|
28
|
+
* Compose the core subgraph ALONE into an API schema — the local path when the
|
|
29
|
+
* working directory has no extension. `composeWithIntegrationLayer` takes two
|
|
30
|
+
* subgraphs, so the single-subgraph case goes straight to `composeServices`.
|
|
31
|
+
*/
|
|
32
|
+
function composeCoreOnly(coreSdl) {
|
|
33
|
+
let services;
|
|
34
|
+
try {
|
|
35
|
+
services = [
|
|
36
|
+
{ name: INTEGRATION_LAYER_SERVICE, typeDefs: parse(coreSdl), url: LOCAL_IL_URL },
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
return { ok: false, errors: [`core-subgraph SDL is not valid GraphQL: ${err.message}`] };
|
|
41
|
+
}
|
|
42
|
+
const result = composeServices(services);
|
|
43
|
+
if (result.errors)
|
|
44
|
+
return { ok: false, errors: result.errors.map((e) => e.message) };
|
|
45
|
+
const apiSchema = result.schema.toAPISchema().toGraphQLJSSchema();
|
|
46
|
+
return {
|
|
47
|
+
ok: true,
|
|
48
|
+
apiSchema,
|
|
49
|
+
apiSdl: printSchema(apiSchema),
|
|
50
|
+
supergraphSdl: result.supergraphSdl,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The local-composition path: the project's core subgraph, plus `extensionSdl` when
|
|
55
|
+
* the caller found a local extension to build. Throws with the composition errors
|
|
56
|
+
* when the two don't compose — the same failure `extension validate` reports, and a
|
|
57
|
+
* real answer rather than a half-rendered explorer.
|
|
58
|
+
*/
|
|
59
|
+
export function composeLocalExplorerSchema(coreSdl, extensionSdl) {
|
|
60
|
+
const result = extensionSdl
|
|
61
|
+
? composeWithIntegrationLayer(coreSdl, extensionSdl, {
|
|
62
|
+
integrationLayerUrl: LOCAL_IL_URL,
|
|
63
|
+
extensionUrl: LOCAL_EXTENSION_URL,
|
|
64
|
+
})
|
|
65
|
+
: composeCoreOnly(coreSdl);
|
|
66
|
+
if (!result.ok) {
|
|
67
|
+
throw new Error(`could not compose the schema locally:\n${result.errors.map((e) => ` - ${e}`).join("\n")}`);
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
schema: result.apiSchema,
|
|
71
|
+
sdl: result.apiSdl,
|
|
72
|
+
describe: extensionSdl
|
|
73
|
+
? "local compose (core subgraph + your local extension)"
|
|
74
|
+
: "local compose (core subgraph only — no extension in this directory)",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=exploreSchema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exploreSchema.js","sourceRoot":"","sources":["../../../src/lib/tooling/exploreSchema.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,EAAE;AACF,kFAAkF;AAClF,mFAAmF;AACnF,gFAAgF;AAChF,EAAE;AACF,oFAAoF;AACpF,iFAAiF;AACjF,8EAA8E;AAC9E,4EAA4E;AAC5E,iFAAiF;AACjF,EAAE;AACF,qFAAqF;AACrF,sFAAsF;AACtF,kFAAkF;AAClF,oFAAoF;AACpF,oFAAoF;AAEpF,OAAO,EAAE,WAAW,EAAsB,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,GAE1B,MAAM,cAAc,CAAC;AAEtB;;wEAEwE;AACxE,MAAM,YAAY,GAAG,wCAAwC,CAAC;AAC9D,MAAM,mBAAmB,GAAG,gCAAgC,CAAC;AAU7D;;;;GAIG;AACH,SAAS,eAAe,CAAC,OAAe;IACtC,IAAI,QAAQ,CAAC;IACb,IAAI,CAAC;QACH,QAAQ,GAAG;YACT,EAAE,IAAI,EAAE,yBAAyB,EAAE,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,YAAY,EAAE;SACjF,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,2CAA4C,GAAa,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;IACtG,CAAC;IACD,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,MAAM,CAAC,MAAM;QAAE,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;IACrF,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;AAED;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CACxC,OAAe,EACf,YAAgC;IAEhC,MAAM,MAAM,GAAG,YAAY;QACzB,CAAC,CAAC,2BAA2B,CAAC,OAAO,EAAE,YAAY,EAAE;YACjD,mBAAmB,EAAE,YAAY;YACjC,YAAY,EAAE,mBAAmB;SAClC,CAAC;QACJ,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IAE7B,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,0CAA0C,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC5F,CAAC;IACJ,CAAC;IACD,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,SAAS;QACxB,GAAG,EAAE,MAAM,CAAC,MAAM;QAClB,QAAQ,EAAE,YAAY;YACpB,CAAC,CAAC,sDAAsD;YACxD,CAAC,CAAC,qEAAqE;KAC1E,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const EXPLORER_HTML: string;
|