@shopify/cli-kit 4.2.0 → 4.3.0

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.
@@ -19,7 +19,16 @@ export async function fetch() {
19
19
  if (!content) {
20
20
  return undefined;
21
21
  }
22
- const contentJson = JSON.parse(content);
22
+ let contentJson;
23
+ try {
24
+ contentJson = JSON.parse(content);
25
+ }
26
+ catch (error) {
27
+ if (!(error instanceof SyntaxError))
28
+ throw error;
29
+ await remove();
30
+ return undefined;
31
+ }
23
32
  const parsedSessions = await SessionsSchema.safeParseAsync(contentJson);
24
33
  if (parsedSessions.success) {
25
34
  return parsedSessions.data;
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../../../../src/private/node/session/store.ts"],"names":[],"mappings":"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","sourcesContent":["import {SessionsSchema} from './schema.js'\nimport {getSessions, removeCurrentSessionId, removeSessions, setSessions} from '../conf-store.js'\nimport {identityFqdn} from '../../../public/node/context/fqdn.js'\nimport type {Session, Sessions} from './schema.js'\n\n/**\n * Serializes the session as a JSON and stores it in the system.\n * @param session - the session to store.\n */\nexport async function store(sessions: Sessions) {\n const jsonSessions = JSON.stringify(sessions)\n setSessions(jsonSessions)\n}\n\n/**\n * Fetches the sessions from the local storage and returns it.\n * If the format of the object is invalid, the method will discard it.\n * @returns Returns a promise that resolves with the sessions object if it exists and is valid.\n */\nexport async function fetch(): Promise<Sessions | undefined> {\n const content = getSessions()\n\n if (!content) {\n return undefined\n }\n const contentJson = JSON.parse(content)\n const parsedSessions = await SessionsSchema.safeParseAsync(contentJson)\n if (parsedSessions.success) {\n return parsedSessions.data\n } else {\n await remove()\n return undefined\n }\n}\n\n/**\n * Removes a session from the system.\n */\nexport async function remove() {\n removeSessions()\n removeCurrentSessionId()\n}\n\n/**\n * Gets the session alias for a given user ID.\n *\n * @param userId - The user ID of the session to get the alias for.\n * @returns The alias for the session if it exists, otherwise undefined.\n */\nexport async function getSessionAlias(userId: string): Promise<string | undefined> {\n const sessions = await fetch()\n if (!sessions) return undefined\n\n const fqdn = await identityFqdn()\n if (!sessions[fqdn] || !sessions[fqdn][userId]) return undefined\n return sessions[fqdn][userId].identity.alias\n}\n\n/**\n * Sets the alias for a given user's session and persists it.\n *\n * @param userId - The user ID of the session to update.\n * @param alias - The new alias to set.\n */\nexport async function setSessionAlias(userId: string, alias: string): Promise<void> {\n const sessions = await fetch()\n if (!sessions) return\n\n const fqdn = await identityFqdn()\n const session: Session | undefined = sessions[fqdn]?.[userId]\n if (!session) return\n\n session.identity.alias = alias\n await store(sessions)\n}\n\n/**\n * Finds a session by its alias.\n *\n * @param alias - The alias to search for\n * @returns The user ID if found, otherwise undefined\n */\nexport async function findSessionByAlias(alias: string): Promise<string | undefined> {\n const sessions = await fetch()\n if (!sessions) return undefined\n\n const fqdn = await identityFqdn()\n const fqdnSessions = sessions[fqdn]\n if (!fqdnSessions) return undefined\n\n for (const [userId, session] of Object.entries(fqdnSessions)) {\n if (session.identity.alias === alias || userId === alias) {\n return userId\n }\n }\n\n return undefined\n}\n"]}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../../../src/private/node/session/store.ts"],"names":[],"mappings":"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","sourcesContent":["import {SessionsSchema} from './schema.js'\nimport {getSessions, removeCurrentSessionId, removeSessions, setSessions} from '../conf-store.js'\nimport {identityFqdn} from '../../../public/node/context/fqdn.js'\nimport type {Session, Sessions} from './schema.js'\n\n/**\n * Serializes the session as a JSON and stores it in the system.\n * @param session - the session to store.\n */\nexport async function store(sessions: Sessions) {\n const jsonSessions = JSON.stringify(sessions)\n setSessions(jsonSessions)\n}\n\n/**\n * Fetches the sessions from the local storage and returns it.\n * If the format of the object is invalid, the method will discard it.\n * @returns Returns a promise that resolves with the sessions object if it exists and is valid.\n */\nexport async function fetch(): Promise<Sessions | undefined> {\n const content = getSessions()\n\n if (!content) {\n return undefined\n }\n\n let contentJson: unknown\n try {\n contentJson = JSON.parse(content)\n } catch (error) {\n if (!(error instanceof SyntaxError)) throw error\n await remove()\n return undefined\n }\n\n const parsedSessions = await SessionsSchema.safeParseAsync(contentJson)\n if (parsedSessions.success) {\n return parsedSessions.data\n } else {\n await remove()\n return undefined\n }\n}\n\n/**\n * Removes a session from the system.\n */\nexport async function remove() {\n removeSessions()\n removeCurrentSessionId()\n}\n\n/**\n * Gets the session alias for a given user ID.\n *\n * @param userId - The user ID of the session to get the alias for.\n * @returns The alias for the session if it exists, otherwise undefined.\n */\nexport async function getSessionAlias(userId: string): Promise<string | undefined> {\n const sessions = await fetch()\n if (!sessions) return undefined\n\n const fqdn = await identityFqdn()\n if (!sessions[fqdn] || !sessions[fqdn][userId]) return undefined\n return sessions[fqdn][userId].identity.alias\n}\n\n/**\n * Sets the alias for a given user's session and persists it.\n *\n * @param userId - The user ID of the session to update.\n * @param alias - The new alias to set.\n */\nexport async function setSessionAlias(userId: string, alias: string): Promise<void> {\n const sessions = await fetch()\n if (!sessions) return\n\n const fqdn = await identityFqdn()\n const session: Session | undefined = sessions[fqdn]?.[userId]\n if (!session) return\n\n session.identity.alias = alias\n await store(sessions)\n}\n\n/**\n * Finds a session by its alias.\n *\n * @param alias - The alias to search for\n * @returns The user ID if found, otherwise undefined\n */\nexport async function findSessionByAlias(alias: string): Promise<string | undefined> {\n const sessions = await fetch()\n if (!sessions) return undefined\n\n const fqdn = await identityFqdn()\n const fqdnSessions = sessions[fqdn]\n if (!fqdnSessions) return undefined\n\n for (const [userId, session] of Object.entries(fqdnSessions)) {\n if (session.identity.alias === alias || userId === alias) {\n return userId\n }\n }\n\n return undefined\n}\n"]}
@@ -42,10 +42,19 @@ export function extractHost(value) {
42
42
  if (!value)
43
43
  return undefined;
44
44
  const lowered = value.toLowerCase();
45
- const parsed = safeParseURL(lowered);
46
- if (parsed)
47
- return parsed.hostname;
48
- return lowered.replace(/^https?:\/\//, '').split('/')[0];
45
+ // A bare `host:port` (e.g. `my-shop.shop.dev:9292`) parses as an opaque URL whose hostname is
46
+ // empty (the host is read as the scheme), so try parsing with an explicit scheme as well and only
47
+ // accept a non-empty hostname.
48
+ for (const candidate of [lowered, `https://${lowered}`]) {
49
+ const hostname = safeParseURL(candidate)?.hostname;
50
+ if (hostname)
51
+ return hostname;
52
+ }
53
+ // Never return an empty string: callers using `extractHost(value) ?? value` must keep the input.
54
+ const fallback = lowered.replace(/^https?:\/\//, '').split('/')[0];
55
+ if (fallback)
56
+ return fallback;
57
+ return undefined;
49
58
  }
50
59
  /**
51
60
  * Extracts the subdomain handle from a `*.myshopify.com` URL or host.
@@ -1 +1 @@
1
- {"version":3,"file":"url.js","sourceRoot":"","sources":["../../../src/public/common/url.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,IAAI,CAAC;QACH,OAAO,OAAO,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;IAC9B,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,IAAI,KAAK,YAAY,SAAS;YAAE,OAAO,KAAK,CAAA;QAC5C,MAAM,KAAK,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;QACnB,qDAAqD;IACvD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,KAAgC;IAC1D,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAA;IAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,EAAE,CAAA;IACnC,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;IACpC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAA;IAClC,OAAO,OAAO,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;AAC1D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAgC;IACrE,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,CAAA;IAC/B,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAA;IAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAA;IACrD,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;AACrC,CAAC","sourcesContent":["/**\n * Check if the format of a URL is valid or not.\n *\n * @param url - URL to be checked.\n * @returns True if the URL is valid, false otherwise.\n * @throws An error if URL's constructor throws an error other than `TypeError`.\n */\nexport function isValidURL(url: string): boolean {\n try {\n return Boolean(new URL(url))\n } catch (error: unknown) {\n if (error instanceof TypeError) return false\n throw error\n }\n}\n\n/**\n * Safely parse a string into a URL.\n *\n * @param url - The string to parse into a URL.\n * @returns A URL object if the parsing is successful, undefined otherwise.\n */\nexport function safeParseURL(url: string): URL | undefined {\n try {\n return new URL(url)\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n return undefined\n }\n}\n\n/**\n * Extracts the lowercased hostname from a URL-shaped string. Tolerates\n * bare hosts (without a scheme) and inputs that come back from APIs as\n * either `https://shop.myshopify.com` or `shop.myshopify.com`.\n *\n * @param value - A URL or bare host string, possibly null/undefined.\n * @returns The lowercased hostname, or undefined when the input is empty.\n */\nexport function extractHost(value: string | null | undefined): string | undefined {\n if (!value) return undefined\n const lowered = value.toLowerCase()\n const parsed = safeParseURL(lowered)\n if (parsed) return parsed.hostname\n return lowered.replace(/^https?:\\/\\//, '').split('/')[0]\n}\n\n/**\n * Extracts the subdomain handle from a `*.myshopify.com` URL or host.\n *\n * @param value - A URL or host string, possibly null/undefined.\n * @returns The myshopify subdomain handle, or undefined when the input isn't a `*.myshopify.com` URL.\n */\nexport function extractMyshopifyHandle(value: string | null | undefined): string | undefined {\n const host = extractHost(value)\n if (!host) return undefined\n const match = host.match(/^([^.]+)\\.myshopify\\.com$/)\n return match ? match[1] : undefined\n}\n"]}
1
+ {"version":3,"file":"url.js","sourceRoot":"","sources":["../../../src/public/common/url.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,IAAI,CAAC;QACH,OAAO,OAAO,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;IAC9B,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,IAAI,KAAK,YAAY,SAAS;YAAE,OAAO,KAAK,CAAA;QAC5C,MAAM,KAAK,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;QACnB,qDAAqD;IACvD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,KAAgC;IAC1D,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAA;IAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,EAAE,CAAA;IACnC,8FAA8F;IAC9F,kGAAkG;IAClG,+BAA+B;IAC/B,KAAK,MAAM,SAAS,IAAI,CAAC,OAAO,EAAE,WAAW,OAAO,EAAE,CAAC,EAAE,CAAC;QACxD,MAAM,QAAQ,GAAG,YAAY,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAA;QAClD,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAA;IAC/B,CAAC;IACD,iGAAiG;IACjG,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;IAClE,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAA;IAC7B,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAgC;IACrE,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,CAAA;IAC/B,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAA;IAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAA;IACrD,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;AACrC,CAAC","sourcesContent":["/**\n * Check if the format of a URL is valid or not.\n *\n * @param url - URL to be checked.\n * @returns True if the URL is valid, false otherwise.\n * @throws An error if URL's constructor throws an error other than `TypeError`.\n */\nexport function isValidURL(url: string): boolean {\n try {\n return Boolean(new URL(url))\n } catch (error: unknown) {\n if (error instanceof TypeError) return false\n throw error\n }\n}\n\n/**\n * Safely parse a string into a URL.\n *\n * @param url - The string to parse into a URL.\n * @returns A URL object if the parsing is successful, undefined otherwise.\n */\nexport function safeParseURL(url: string): URL | undefined {\n try {\n return new URL(url)\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n return undefined\n }\n}\n\n/**\n * Extracts the lowercased hostname from a URL-shaped string. Tolerates\n * bare hosts (without a scheme) and inputs that come back from APIs as\n * either `https://shop.myshopify.com` or `shop.myshopify.com`.\n *\n * @param value - A URL or bare host string, possibly null/undefined.\n * @returns The lowercased hostname, or undefined when the input is empty.\n */\nexport function extractHost(value: string | null | undefined): string | undefined {\n if (!value) return undefined\n const lowered = value.toLowerCase()\n // A bare `host:port` (e.g. `my-shop.shop.dev:9292`) parses as an opaque URL whose hostname is\n // empty (the host is read as the scheme), so try parsing with an explicit scheme as well and only\n // accept a non-empty hostname.\n for (const candidate of [lowered, `https://${lowered}`]) {\n const hostname = safeParseURL(candidate)?.hostname\n if (hostname) return hostname\n }\n // Never return an empty string: callers using `extractHost(value) ?? value` must keep the input.\n const fallback = lowered.replace(/^https?:\\/\\//, '').split('/')[0]\n if (fallback) return fallback\n return undefined\n}\n\n/**\n * Extracts the subdomain handle from a `*.myshopify.com` URL or host.\n *\n * @param value - A URL or host string, possibly null/undefined.\n * @returns The myshopify subdomain handle, or undefined when the input isn't a `*.myshopify.com` URL.\n */\nexport function extractMyshopifyHandle(value: string | null | undefined): string | undefined {\n const host = extractHost(value)\n if (!host) return undefined\n const match = host.match(/^([^.]+)\\.myshopify\\.com$/)\n return match ? match[1] : undefined\n}\n"]}
@@ -1 +1 @@
1
- export declare const CLI_KIT_VERSION = "4.2.0";
1
+ export declare const CLI_KIT_VERSION = "4.3.0";
@@ -1,2 +1,2 @@
1
- export const CLI_KIT_VERSION = '4.2.0';
1
+ export const CLI_KIT_VERSION = '4.3.0';
2
2
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/public/common/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAA","sourcesContent":["export const CLI_KIT_VERSION = '4.2.0'\n"]}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/public/common/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAA","sourcesContent":["export const CLI_KIT_VERSION = '4.3.0'\n"]}
@@ -128,7 +128,7 @@ export function normalizeStoreFqdn(store) {
128
128
  return `${storeFqdn}.myshopify.com`;
129
129
  }
130
130
  };
131
- const containDomain = (storeFqdn) => storeFqdn.endsWith('.myshopify.com') || storeFqdn.endsWith('shopify.io') || storeFqdn.endsWith('.shop.dev');
131
+ const containDomain = (storeFqdn) => storeFqdn.endsWith('.myshopify.com') || storeFqdn.endsWith('.myshopify.io') || storeFqdn.endsWith('.shop.dev');
132
132
  return containDomain(storeFqdn) ? storeFqdn : addDomain(storeFqdn);
133
133
  }
134
134
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"fqdn.js","sourceRoot":"","sources":["../../../../src/public/node/context/fqdn.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/switch-exhaustiveness-check -- all switches branch on Environment.Local vs default (production) */\nimport {AbortError} from '../error.js'\nimport {serviceEnvironment} from '../../../private/node/context/service.js'\nimport {DevServer, DevServerCore} from '../vendor/dev_server/index.js'\n\nexport const NotProvidedStoreFQDNError = new AbortError(\n \"Couldn't obtain the Shopify FQDN because the store FQDN was not provided.\",\n)\n\n/**\n * It returns the Partners' API service we should interact with.\n *\n * @returns Fully-qualified domain of the partners service we should interact with.\n */\nexport async function partnersFqdn(): Promise<string> {\n const environment = serviceEnvironment()\n const productionFqdn = 'partners.shopify.com'\n switch (environment) {\n case 'local':\n return new DevServer('partners').host()\n default:\n return productionFqdn\n }\n}\n\n/**\n * It returns the Admin service we should interact with.\n *\n * @returns Fully-qualified domain of the Admin service we should interact with.\n */\nexport async function adminFqdn(): Promise<string> {\n const environment = serviceEnvironment()\n const productionFqdn = 'admin.shopify.com'\n switch (environment) {\n case 'local':\n return new DevServerCore().host('admin')\n default:\n return productionFqdn\n }\n}\n\n/**\n * It returns the App Management API service we should interact with.\n *\n * @returns Fully-qualified domain of the App Management service we should interact with.\n */\nexport async function appManagementFqdn(): Promise<string> {\n const environment = serviceEnvironment()\n const productionFqdn = 'app.shopify.com'\n switch (environment) {\n case 'local':\n return new DevServerCore().host('app')\n default:\n return productionFqdn\n }\n}\n\n/**\n * It returns the App Dev API service we should interact with.\n *\n * @param storeFqdn - The store FQDN.\n * @returns Fully-qualified domain of the App Dev service we should interact with.\n */\nexport async function appDevFqdn(storeFqdn: string): Promise<string> {\n const environment = serviceEnvironment()\n switch (environment) {\n case 'local':\n return new DevServerCore().host('app')\n default:\n return storeFqdn\n }\n}\n/**\n * It returns the Developer Dashboard domain we should interact with.\n *\n * @returns Fully-qualified domain of the Developer Dashboard we should interact with.\n */\nexport async function developerDashboardFqdn(): Promise<string> {\n const environment = serviceEnvironment()\n const productionFqdn = 'dev.shopify.com'\n switch (environment) {\n case 'local':\n return new DevServerCore().host('dev')\n default:\n return productionFqdn\n }\n}\n\n/**\n * It returns the BusinessPlatform' API service we should interact with.\n *\n * @returns Fully-qualified domain of the partners service we should interact with.\n */\nexport async function businessPlatformFqdn(): Promise<string> {\n const environment = serviceEnvironment()\n const productionFqdn = 'destinations.shopifysvc.com'\n switch (environment) {\n case 'local':\n return new DevServer('business-platform').host()\n default:\n return productionFqdn\n }\n}\n\n/**\n * It returns the Identity service we should interact with.\n *\n * @returns Fully-qualified domain of the Identity service we should interact with.\n */\nexport async function identityFqdn(): Promise<string> {\n const environment = serviceEnvironment()\n const productionFqdn = 'accounts.shopify.com'\n switch (environment) {\n case 'local':\n return new DevServer('identity').host()\n default:\n return productionFqdn\n }\n}\n\n/**\n * Normalize the store name to be used in the CLI.\n * It will add the .myshopify.com domain if it's not present.\n *\n * @param store - Store name.\n * @returns Normalized store name.\n */\nexport function normalizeStoreFqdn(store: string): string {\n const storeFqdn = store\n .replace(/^https?:\\/\\//, '')\n .replace(/\\/$/, '')\n .replace(/\\/admin$/, '')\n const addDomain = (storeFqdn: string) => {\n switch (serviceEnvironment()) {\n case 'local':\n return new DevServerCore().host(storeFqdn)\n default:\n return `${storeFqdn}.myshopify.com`\n }\n }\n const containDomain = (storeFqdn: string) =>\n storeFqdn.endsWith('.myshopify.com') || storeFqdn.endsWith('shopify.io') || storeFqdn.endsWith('.shop.dev')\n return containDomain(storeFqdn) ? storeFqdn : addDomain(storeFqdn)\n}\n\n/**\n * Convert a store FQDN to the admin URL pattern for local development.\n * In local mode, transforms \\{store\\}.my.shop.dev to admin.shop.dev/store/\\{store\\}.\n *\n * @param storeFqdn - Normalized store FQDN.\n * @returns Store admin URL base (without protocol or path).\n */\nexport function storeAdminUrl(storeFqdn: string): string {\n if (serviceEnvironment() === 'local' && storeFqdn.endsWith('.my.shop.dev')) {\n const storeName = storeFqdn.replace('.my.shop.dev', '')\n return `admin.shop.dev/store/${storeName}`\n }\n return storeFqdn\n}\n"]}
1
+ {"version":3,"file":"fqdn.js","sourceRoot":"","sources":["../../../../src/public/node/context/fqdn.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/switch-exhaustiveness-check -- all switches branch on Environment.Local vs default (production) */\nimport {AbortError} from '../error.js'\nimport {serviceEnvironment} from '../../../private/node/context/service.js'\nimport {DevServer, DevServerCore} from '../vendor/dev_server/index.js'\n\nexport const NotProvidedStoreFQDNError = new AbortError(\n \"Couldn't obtain the Shopify FQDN because the store FQDN was not provided.\",\n)\n\n/**\n * It returns the Partners' API service we should interact with.\n *\n * @returns Fully-qualified domain of the partners service we should interact with.\n */\nexport async function partnersFqdn(): Promise<string> {\n const environment = serviceEnvironment()\n const productionFqdn = 'partners.shopify.com'\n switch (environment) {\n case 'local':\n return new DevServer('partners').host()\n default:\n return productionFqdn\n }\n}\n\n/**\n * It returns the Admin service we should interact with.\n *\n * @returns Fully-qualified domain of the Admin service we should interact with.\n */\nexport async function adminFqdn(): Promise<string> {\n const environment = serviceEnvironment()\n const productionFqdn = 'admin.shopify.com'\n switch (environment) {\n case 'local':\n return new DevServerCore().host('admin')\n default:\n return productionFqdn\n }\n}\n\n/**\n * It returns the App Management API service we should interact with.\n *\n * @returns Fully-qualified domain of the App Management service we should interact with.\n */\nexport async function appManagementFqdn(): Promise<string> {\n const environment = serviceEnvironment()\n const productionFqdn = 'app.shopify.com'\n switch (environment) {\n case 'local':\n return new DevServerCore().host('app')\n default:\n return productionFqdn\n }\n}\n\n/**\n * It returns the App Dev API service we should interact with.\n *\n * @param storeFqdn - The store FQDN.\n * @returns Fully-qualified domain of the App Dev service we should interact with.\n */\nexport async function appDevFqdn(storeFqdn: string): Promise<string> {\n const environment = serviceEnvironment()\n switch (environment) {\n case 'local':\n return new DevServerCore().host('app')\n default:\n return storeFqdn\n }\n}\n/**\n * It returns the Developer Dashboard domain we should interact with.\n *\n * @returns Fully-qualified domain of the Developer Dashboard we should interact with.\n */\nexport async function developerDashboardFqdn(): Promise<string> {\n const environment = serviceEnvironment()\n const productionFqdn = 'dev.shopify.com'\n switch (environment) {\n case 'local':\n return new DevServerCore().host('dev')\n default:\n return productionFqdn\n }\n}\n\n/**\n * It returns the BusinessPlatform' API service we should interact with.\n *\n * @returns Fully-qualified domain of the partners service we should interact with.\n */\nexport async function businessPlatformFqdn(): Promise<string> {\n const environment = serviceEnvironment()\n const productionFqdn = 'destinations.shopifysvc.com'\n switch (environment) {\n case 'local':\n return new DevServer('business-platform').host()\n default:\n return productionFqdn\n }\n}\n\n/**\n * It returns the Identity service we should interact with.\n *\n * @returns Fully-qualified domain of the Identity service we should interact with.\n */\nexport async function identityFqdn(): Promise<string> {\n const environment = serviceEnvironment()\n const productionFqdn = 'accounts.shopify.com'\n switch (environment) {\n case 'local':\n return new DevServer('identity').host()\n default:\n return productionFqdn\n }\n}\n\n/**\n * Normalize the store name to be used in the CLI.\n * It will add the .myshopify.com domain if it's not present.\n *\n * @param store - Store name.\n * @returns Normalized store name.\n */\nexport function normalizeStoreFqdn(store: string): string {\n const storeFqdn = store\n .replace(/^https?:\\/\\//, '')\n .replace(/\\/$/, '')\n .replace(/\\/admin$/, '')\n const addDomain = (storeFqdn: string) => {\n switch (serviceEnvironment()) {\n case 'local':\n return new DevServerCore().host(storeFqdn)\n default:\n return `${storeFqdn}.myshopify.com`\n }\n }\n const containDomain = (storeFqdn: string) =>\n storeFqdn.endsWith('.myshopify.com') || storeFqdn.endsWith('.myshopify.io') || storeFqdn.endsWith('.shop.dev')\n return containDomain(storeFqdn) ? storeFqdn : addDomain(storeFqdn)\n}\n\n/**\n * Convert a store FQDN to the admin URL pattern for local development.\n * In local mode, transforms \\{store\\}.my.shop.dev to admin.shop.dev/store/\\{store\\}.\n *\n * @param storeFqdn - Normalized store FQDN.\n * @returns Store admin URL base (without protocol or path).\n */\nexport function storeAdminUrl(storeFqdn: string): string {\n if (serviceEnvironment() === 'local' && storeFqdn.endsWith('.my.shop.dev')) {\n const storeName = storeFqdn.replace('.my.shop.dev', '')\n return `admin.shop.dev/store/${storeName}`\n }\n return storeFqdn\n}\n"]}
@@ -75,6 +75,22 @@ export function jsonSchemaValidate(subject, schema, handleInvalidAdditionalPrope
75
75
  rawErrors: undefined,
76
76
  };
77
77
  }
78
+ /**
79
+ * Get a more precise type name for JSON Schema error messages.
80
+ *
81
+ * @param value - The value to get the type of.
82
+ * @returns A string representing the type (e.g., 'array', 'null', 'object').
83
+ */
84
+ function getJsonSchemaValueType(value) {
85
+ if (Array.isArray(value))
86
+ return 'array';
87
+ if (value === null)
88
+ return 'null';
89
+ return typeof value;
90
+ }
91
+ function getJsonSchemaErrorValue(subject, path) {
92
+ return path.length === 0 ? subject : getPathValue(subject, path);
93
+ }
78
94
  /**
79
95
  * Converts errors from Ajv into a zod-like format.
80
96
  *
@@ -97,15 +113,15 @@ function convertJsonSchemaErrors(rawErrors, subject, schema) {
97
113
  const expectedType = Array.isArray(error.params.type)
98
114
  ? error.params.type.join(', ')
99
115
  : error.params.type;
100
- const actualType = getPathValue(subject, path.join('.'));
101
- return { path, message: `Expected ${expectedType}, received ${typeof actualType}` };
116
+ const actualType = getJsonSchemaErrorValue(subject, path);
117
+ return { path, message: `Expected ${expectedType}, received ${getJsonSchemaValueType(actualType)}` };
102
118
  }
103
119
  if (error.keyword === 'anyOf' || error.keyword === 'oneOf') {
104
120
  return { path, message: 'Invalid input' };
105
121
  }
106
122
  if (error.params.allowedValues) {
107
123
  const allowedValues = error.params.allowedValues;
108
- const actualValue = getPathValue(subject, path.join('.'));
124
+ const actualValue = getJsonSchemaErrorValue(subject, path);
109
125
  return {
110
126
  path,
111
127
  message: `Invalid enum value. Expected ${allowedValues
@@ -116,7 +132,7 @@ function convertJsonSchemaErrors(rawErrors, subject, schema) {
116
132
  if (error.params.comparison) {
117
133
  const comparison = error.params.comparison;
118
134
  const limit = error.params.limit;
119
- const actualValue = getPathValue(subject, path.join('.'));
135
+ const actualValue = getJsonSchemaErrorValue(subject, path);
120
136
  let comparisonText = comparison;
121
137
  switch (comparison) {
122
138
  case '<=':
@@ -190,7 +206,7 @@ function simplifyUnionErrors(rawErrors, subject, schema) {
190
206
  const dottedSchemaPath = unionError.schemaPath.replace('#/', '').replace(/\//g, '.');
191
207
  const unionSchemas = getPathValue(schema, dottedSchemaPath);
192
208
  // and the slice of the subject that caused the issue
193
- const subjectValue = getPathValue(subject, unionError.instancePath.split('/').slice(1).join('.'));
209
+ const subjectValue = getJsonSchemaErrorValue(subject, unionError.instancePath.split('/').slice(1));
194
210
  if (unionSchemas !== undefined && subjectValue !== undefined) {
195
211
  // we know that none of the union schemas are correct, but for each of them we can measure how wrong they are
196
212
  const correctValuesAndErrors = unionSchemas
@@ -203,7 +219,7 @@ function simplifyUnionErrors(rawErrors, subject, schema) {
203
219
  const candidatesObjectProperties = Object.keys(candidateSchemaFromUnion.properties);
204
220
  score = candidatesObjectProperties.reduce((acc, propertyName) => {
205
221
  const subSchema = candidateSchemaFromUnion.properties[propertyName];
206
- const subjectValueSlice = getPathValue(subjectValue, propertyName);
222
+ const subjectValueSlice = getJsonSchemaErrorValue(subjectValue, [propertyName]);
207
223
  const subValidator = createAjvValidator('fail', subSchema);
208
224
  if (subValidator(subjectValueSlice)) {
209
225
  return acc + 1;
@@ -1 +1 @@
1
- {"version":3,"file":"json-schema.js","sourceRoot":"","sources":["../../../src/public/node/json-schema.ts"],"names":[],"mappings":"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","sourcesContent":["import {randomUUID} from './crypto.js'\nimport {ParseConfigurationResult} from './schema.js'\nimport {getPathValue} from '../common/object.js'\n\nimport {capitalize} from '../common/string.js'\nimport {Ajv, ErrorObject, SchemaObject, ValidateFunction} from 'ajv'\nimport $RefParser from '@apidevtools/json-schema-ref-parser'\nimport cloneDeep from 'lodash/cloneDeep.js'\n\nexport type HandleInvalidAdditionalProperties = 'strip' | 'fail'\n\ntype AjvError = ErrorObject<string, Record<string, unknown>>\n\n/**\n * Normalises a JSON Schema by standardising it's internal implementation.\n *\n * We prefer to not use $ref elements in our schemas, so we inline them; it's easier then to process errors.\n *\n * @param schema - The JSON schema (as a string) to normalise.\n * @returns The normalised JSON schema.\n */\nexport async function normaliseJsonSchema(schema: string): Promise<SchemaObject> {\n // we want to modify the schema, removing any $ref elements and inlining with their source\n const parsedSchema = JSON.parse(schema)\n await $RefParser.dereference(parsedSchema, {resolve: {external: false}})\n return parsedSchema\n}\n\nfunction createAjvValidator(\n handleInvalidAdditionalProperties: HandleInvalidAdditionalProperties,\n schema: SchemaObject,\n) {\n // allowUnionTypes: Allows types like `type: [\"string\", \"number\"]`\n if (handleInvalidAdditionalProperties === 'strip') {\n // we need to let additional properties through, so that we can strip them later\n schema.additionalProperties = true\n }\n\n const ajv = new Ajv({allowUnionTypes: true, allErrors: true, verbose: true})\n ajv.addKeyword('x-taplo')\n\n const validator = ajv.compile(schema)\n\n return validator\n}\n\nconst validatorsCache = new Map<string, ValidateFunction>()\n\n/**\n * Given a subject object and a JSON schema contract, validate the subject against the contract.\n *\n * Errors are returned in a zod-like format, and processed to better handle unions.\n *\n * @param subject - The object to validate.\n * @param schema - The JSON schema to validate against.\n * @param handleInvalidAdditionalProperties - Whether to strip or fail on invalid additional properties.\n * @param identifier - The identifier of the schema being validated, used to cache the validator.\n * @returns The result of the validation. If the state is 'error', the errors will be in a zod-like format.\n */\nexport function jsonSchemaValidate(\n subject: object,\n schema: SchemaObject,\n handleInvalidAdditionalProperties: HandleInvalidAdditionalProperties,\n identifier?: string,\n): ParseConfigurationResult<unknown> & {rawErrors?: AjvError[]} {\n const subjectToModify = cloneDeep(subject)\n\n const cacheKey = identifier ?? randomUUID()\n\n const validator = validatorsCache.get(cacheKey) ?? createAjvValidator(handleInvalidAdditionalProperties, schema)\n validatorsCache.set(cacheKey, validator)\n\n validator(subjectToModify)\n\n // Errors from the contract are post-processed to be more zod-like and to deal with unions better\n let jsonSchemaErrors\n if (validator.errors && validator.errors.length > 0) {\n jsonSchemaErrors = convertJsonSchemaErrors(validator.errors, subjectToModify, schema)\n return {\n state: 'error',\n data: undefined,\n errors: jsonSchemaErrors,\n rawErrors: validator.errors,\n }\n }\n\n if (handleInvalidAdditionalProperties === 'strip') {\n const topLevelSchemaProperties = Object.keys(schema.properties ?? {})\n // strip any properties that are not in the top level schema from subjectToModify\n Object.keys(subjectToModify).forEach((key) => {\n if (!topLevelSchemaProperties.includes(key)) {\n // this isn't actually dynamic, because key came from Object.keys\n\n delete subjectToModify[key as keyof typeof subjectToModify]\n }\n })\n }\n\n return {\n state: 'ok',\n data: subjectToModify,\n errors: undefined,\n rawErrors: undefined,\n }\n}\n\n/**\n * Converts errors from Ajv into a zod-like format.\n *\n * @param rawErrors - JSON Schema errors taken directly from Ajv.\n * @param subject - The object being validated.\n * @param schema - The JSON schema to validated against.\n * @returns The errors in a zod-like format.\n */\nfunction convertJsonSchemaErrors(rawErrors: AjvError[], subject: object, schema: SchemaObject) {\n // This reduces the number of errors by simplifying errors coming from different branches of a union\n const errors = simplifyUnionErrors(rawErrors, subject, schema)\n\n // Now we can remap errors to be more zod-like\n return errors.map((error) => {\n const path: string[] = error.instancePath.split('/').slice(1)\n if (error.params.missingProperty) {\n const missingProperty = error.params.missingProperty as string\n return {path: [...path, missingProperty], message: 'Required'}\n }\n\n if (error.params.type) {\n const expectedType = Array.isArray(error.params.type)\n ? error.params.type.join(', ')\n : (error.params.type as string)\n const actualType = getPathValue(subject, path.join('.'))\n return {path, message: `Expected ${expectedType}, received ${typeof actualType}`}\n }\n\n if (error.keyword === 'anyOf' || error.keyword === 'oneOf') {\n return {path, message: 'Invalid input'}\n }\n\n if (error.params.allowedValues) {\n const allowedValues = error.params.allowedValues as string[]\n const actualValue = getPathValue(subject, path.join('.'))\n return {\n path,\n message: `Invalid enum value. Expected ${allowedValues\n .map((value) => JSON.stringify(value))\n .join(' | ')}, received ${JSON.stringify(actualValue)}`.replace(/\"/g, \"'\"),\n }\n }\n\n if (error.params.comparison) {\n const comparison = error.params.comparison as string\n const limit = error.params.limit\n const actualValue = getPathValue(subject, path.join('.'))\n\n let comparisonText = comparison\n switch (comparison) {\n case '<=':\n comparisonText = 'less than or equal to'\n break\n case '<':\n comparisonText = 'less than'\n break\n case '>=':\n comparisonText = 'greater than or equal to'\n break\n case '>':\n comparisonText = 'greater than'\n break\n }\n\n return {\n path,\n message: capitalize(`${typeof actualValue} must be ${comparisonText} ${limit}`),\n }\n }\n\n if (error.params.additionalProperty) {\n const supportedProperties = Object.keys(error.parentSchema?.properties ?? {})\n\n // if a property was already set, remove it from here\n const alreadySetProperties = Object.keys(error.data ?? {})\n const remainingProperties = supportedProperties.filter((property) => !alreadySetProperties.includes(property))\n\n if (remainingProperties.length > 0) {\n return {\n path: [...path, error.params.additionalProperty as string],\n message: `No additional properties allowed. You can set ${remainingProperties.sort().join(', ')}.`,\n }\n }\n }\n\n return {\n path,\n message: error.message,\n }\n })\n}\n\n/**\n * If a JSON schema specifies a union (anyOf, oneOf), and the subject doesn't meet any of the 'candidates' for the\n * union, then the error list received ends up being quite long: you get an error for the union property itself, and\n * then additional errors for each of the candidate branches.\n *\n * This function simplifies the error collection. By default it strips anything other than the union error itself.\n *\n * In some cases, it can be possible to identify what the intended branch of the union was -- for instance, maybe there\n * is a discriminating field like `type` that is unique between the branches. We inspect each candidate branch and if\n * one branch is less wrong than the others -- e.g. It had a valid `type`, but problems elsewhere -- then we keep the\n * errors for that branch.\n *\n * This is complex but in practise gives much more actionable errors.\n *\n * @param rawErrors - JSON Schema errors taken directly from Ajv.\n * @param subject - The object being validated.\n * @param schema - The JSON schema to validated against.\n * @returns A simplified list of errors.\n */\nfunction simplifyUnionErrors(rawErrors: AjvError[], subject: object, schema: SchemaObject): AjvError[] {\n let errors = rawErrors\n\n const resolvedUnionErrors = new Set()\n while (true) {\n const unionError = errors.filter(\n (error) =>\n (error.keyword === 'oneOf' || error.keyword === 'anyOf') && !resolvedUnionErrors.has(error.instancePath),\n )[0]\n if (unionError === undefined) {\n break\n }\n // split errors into those sharing an instance path and those not\n const unrelatedErrors = errors.filter((error) => !error.instancePath.startsWith(unionError.instancePath))\n\n // we start by assuming only the union error itself is useful, and not the errors from the candidate schemas\n let simplifiedUnionRelatedErrors: AjvError[] = [unionError]\n\n // get the schema list from where the union issue occured\n const dottedSchemaPath = unionError.schemaPath.replace('#/', '').replace(/\\//g, '.')\n const unionSchemas = getPathValue<SchemaObject[]>(schema, dottedSchemaPath)\n // and the slice of the subject that caused the issue\n const subjectValue = getPathValue(subject, unionError.instancePath.split('/').slice(1).join('.'))\n\n if (unionSchemas !== undefined && subjectValue !== undefined) {\n // we know that none of the union schemas are correct, but for each of them we can measure how wrong they are\n const correctValuesAndErrors = unionSchemas\n .map((candidateSchemaFromUnion: SchemaObject) => {\n const candidateSchemaValidator = createAjvValidator('fail', candidateSchemaFromUnion)\n candidateSchemaValidator(subjectValue)\n\n let score = 0\n if (candidateSchemaFromUnion.type === 'object') {\n // provided the schema is an object, we can measure how many properties are good\n const candidatesObjectProperties = Object.keys(candidateSchemaFromUnion.properties)\n score = candidatesObjectProperties.reduce((acc, propertyName) => {\n const subSchema = candidateSchemaFromUnion.properties[propertyName] as SchemaObject\n const subjectValueSlice = getPathValue(subjectValue, propertyName)\n\n const subValidator = createAjvValidator('fail', subSchema)\n if (subValidator(subjectValueSlice)) {\n return acc + 1\n }\n return acc\n }, score)\n }\n\n return [score, candidateSchemaValidator.errors!] as const\n })\n .sort(([scoreA], [scoreB]) => scoreA - scoreB)\n\n if (correctValuesAndErrors.length >= 2) {\n const [bestScore, bestErrors] = correctValuesAndErrors[correctValuesAndErrors.length - 1]!\n const [penultimateScore] = correctValuesAndErrors[correctValuesAndErrors.length - 2]!\n\n if (bestScore !== penultimateScore) {\n // If there's a winner, show the errors for the best schema as they'll likely be actionable.\n // We got these through a nested schema, so we need to adjust the instance path\n simplifiedUnionRelatedErrors = [\n unionError,\n ...bestErrors.map((bestError) => ({\n ...bestError,\n instancePath: unionError.instancePath + bestError.instancePath,\n })),\n ]\n }\n }\n }\n errors = [...unrelatedErrors, ...simplifiedUnionRelatedErrors]\n\n resolvedUnionErrors.add(unionError.instancePath)\n }\n return errors\n}\n"]}
1
+ {"version":3,"file":"json-schema.js","sourceRoot":"","sources":["../../../src/public/node/json-schema.ts"],"names":[],"mappings":"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","sourcesContent":["import {randomUUID} from './crypto.js'\nimport {ParseConfigurationResult} from './schema.js'\nimport {getPathValue} from '../common/object.js'\n\nimport {capitalize} from '../common/string.js'\nimport {Ajv, ErrorObject, SchemaObject, ValidateFunction} from 'ajv'\nimport $RefParser from '@apidevtools/json-schema-ref-parser'\nimport cloneDeep from 'lodash/cloneDeep.js'\n\nexport type HandleInvalidAdditionalProperties = 'strip' | 'fail'\n\ntype AjvError = ErrorObject<string, Record<string, unknown>>\n\n/**\n * Normalises a JSON Schema by standardising it's internal implementation.\n *\n * We prefer to not use $ref elements in our schemas, so we inline them; it's easier then to process errors.\n *\n * @param schema - The JSON schema (as a string) to normalise.\n * @returns The normalised JSON schema.\n */\nexport async function normaliseJsonSchema(schema: string): Promise<SchemaObject> {\n // we want to modify the schema, removing any $ref elements and inlining with their source\n const parsedSchema = JSON.parse(schema)\n await $RefParser.dereference(parsedSchema, {resolve: {external: false}})\n return parsedSchema\n}\n\nfunction createAjvValidator(\n handleInvalidAdditionalProperties: HandleInvalidAdditionalProperties,\n schema: SchemaObject,\n) {\n // allowUnionTypes: Allows types like `type: [\"string\", \"number\"]`\n if (handleInvalidAdditionalProperties === 'strip') {\n // we need to let additional properties through, so that we can strip them later\n schema.additionalProperties = true\n }\n\n const ajv = new Ajv({allowUnionTypes: true, allErrors: true, verbose: true})\n ajv.addKeyword('x-taplo')\n\n const validator = ajv.compile(schema)\n\n return validator\n}\n\nconst validatorsCache = new Map<string, ValidateFunction>()\n\n/**\n * Given a subject object and a JSON schema contract, validate the subject against the contract.\n *\n * Errors are returned in a zod-like format, and processed to better handle unions.\n *\n * @param subject - The object to validate.\n * @param schema - The JSON schema to validate against.\n * @param handleInvalidAdditionalProperties - Whether to strip or fail on invalid additional properties.\n * @param identifier - The identifier of the schema being validated, used to cache the validator.\n * @returns The result of the validation. If the state is 'error', the errors will be in a zod-like format.\n */\nexport function jsonSchemaValidate(\n subject: object,\n schema: SchemaObject,\n handleInvalidAdditionalProperties: HandleInvalidAdditionalProperties,\n identifier?: string,\n): ParseConfigurationResult<unknown> & {rawErrors?: AjvError[]} {\n const subjectToModify = cloneDeep(subject)\n\n const cacheKey = identifier ?? randomUUID()\n\n const validator = validatorsCache.get(cacheKey) ?? createAjvValidator(handleInvalidAdditionalProperties, schema)\n validatorsCache.set(cacheKey, validator)\n\n validator(subjectToModify)\n\n // Errors from the contract are post-processed to be more zod-like and to deal with unions better\n let jsonSchemaErrors\n if (validator.errors && validator.errors.length > 0) {\n jsonSchemaErrors = convertJsonSchemaErrors(validator.errors, subjectToModify, schema)\n return {\n state: 'error',\n data: undefined,\n errors: jsonSchemaErrors,\n rawErrors: validator.errors,\n }\n }\n\n if (handleInvalidAdditionalProperties === 'strip') {\n const topLevelSchemaProperties = Object.keys(schema.properties ?? {})\n // strip any properties that are not in the top level schema from subjectToModify\n Object.keys(subjectToModify).forEach((key) => {\n if (!topLevelSchemaProperties.includes(key)) {\n // this isn't actually dynamic, because key came from Object.keys\n\n delete subjectToModify[key as keyof typeof subjectToModify]\n }\n })\n }\n\n return {\n state: 'ok',\n data: subjectToModify,\n errors: undefined,\n rawErrors: undefined,\n }\n}\n\n/**\n * Get a more precise type name for JSON Schema error messages.\n *\n * @param value - The value to get the type of.\n * @returns A string representing the type (e.g., 'array', 'null', 'object').\n */\nfunction getJsonSchemaValueType(value: unknown): string {\n if (Array.isArray(value)) return 'array'\n if (value === null) return 'null'\n return typeof value\n}\n\nfunction getJsonSchemaErrorValue(subject: object, path: string[]): unknown {\n return path.length === 0 ? subject : getPathValue(subject, path)\n}\n\n/**\n * Converts errors from Ajv into a zod-like format.\n *\n * @param rawErrors - JSON Schema errors taken directly from Ajv.\n * @param subject - The object being validated.\n * @param schema - The JSON schema to validated against.\n * @returns The errors in a zod-like format.\n */\nfunction convertJsonSchemaErrors(rawErrors: AjvError[], subject: object, schema: SchemaObject) {\n // This reduces the number of errors by simplifying errors coming from different branches of a union\n const errors = simplifyUnionErrors(rawErrors, subject, schema)\n\n // Now we can remap errors to be more zod-like\n return errors.map((error) => {\n const path: string[] = error.instancePath.split('/').slice(1)\n if (error.params.missingProperty) {\n const missingProperty = error.params.missingProperty as string\n return {path: [...path, missingProperty], message: 'Required'}\n }\n\n if (error.params.type) {\n const expectedType = Array.isArray(error.params.type)\n ? error.params.type.join(', ')\n : (error.params.type as string)\n const actualType = getJsonSchemaErrorValue(subject, path)\n return {path, message: `Expected ${expectedType}, received ${getJsonSchemaValueType(actualType)}`}\n }\n\n if (error.keyword === 'anyOf' || error.keyword === 'oneOf') {\n return {path, message: 'Invalid input'}\n }\n\n if (error.params.allowedValues) {\n const allowedValues = error.params.allowedValues as string[]\n const actualValue = getJsonSchemaErrorValue(subject, path)\n return {\n path,\n message: `Invalid enum value. Expected ${allowedValues\n .map((value) => JSON.stringify(value))\n .join(' | ')}, received ${JSON.stringify(actualValue)}`.replace(/\"/g, \"'\"),\n }\n }\n\n if (error.params.comparison) {\n const comparison = error.params.comparison as string\n const limit = error.params.limit\n const actualValue = getJsonSchemaErrorValue(subject, path)\n\n let comparisonText = comparison\n switch (comparison) {\n case '<=':\n comparisonText = 'less than or equal to'\n break\n case '<':\n comparisonText = 'less than'\n break\n case '>=':\n comparisonText = 'greater than or equal to'\n break\n case '>':\n comparisonText = 'greater than'\n break\n }\n\n return {\n path,\n message: capitalize(`${typeof actualValue} must be ${comparisonText} ${limit}`),\n }\n }\n\n if (error.params.additionalProperty) {\n const supportedProperties = Object.keys(error.parentSchema?.properties ?? {})\n\n // if a property was already set, remove it from here\n const alreadySetProperties = Object.keys(error.data ?? {})\n const remainingProperties = supportedProperties.filter((property) => !alreadySetProperties.includes(property))\n\n if (remainingProperties.length > 0) {\n return {\n path: [...path, error.params.additionalProperty as string],\n message: `No additional properties allowed. You can set ${remainingProperties.sort().join(', ')}.`,\n }\n }\n }\n\n return {\n path,\n message: error.message,\n }\n })\n}\n\n/**\n * If a JSON schema specifies a union (anyOf, oneOf), and the subject doesn't meet any of the 'candidates' for the\n * union, then the error list received ends up being quite long: you get an error for the union property itself, and\n * then additional errors for each of the candidate branches.\n *\n * This function simplifies the error collection. By default it strips anything other than the union error itself.\n *\n * In some cases, it can be possible to identify what the intended branch of the union was -- for instance, maybe there\n * is a discriminating field like `type` that is unique between the branches. We inspect each candidate branch and if\n * one branch is less wrong than the others -- e.g. It had a valid `type`, but problems elsewhere -- then we keep the\n * errors for that branch.\n *\n * This is complex but in practise gives much more actionable errors.\n *\n * @param rawErrors - JSON Schema errors taken directly from Ajv.\n * @param subject - The object being validated.\n * @param schema - The JSON schema to validated against.\n * @returns A simplified list of errors.\n */\nfunction simplifyUnionErrors(rawErrors: AjvError[], subject: object, schema: SchemaObject): AjvError[] {\n let errors = rawErrors\n\n const resolvedUnionErrors = new Set()\n while (true) {\n const unionError = errors.filter(\n (error) =>\n (error.keyword === 'oneOf' || error.keyword === 'anyOf') && !resolvedUnionErrors.has(error.instancePath),\n )[0]\n if (unionError === undefined) {\n break\n }\n // split errors into those sharing an instance path and those not\n const unrelatedErrors = errors.filter((error) => !error.instancePath.startsWith(unionError.instancePath))\n\n // we start by assuming only the union error itself is useful, and not the errors from the candidate schemas\n let simplifiedUnionRelatedErrors: AjvError[] = [unionError]\n\n // get the schema list from where the union issue occured\n const dottedSchemaPath = unionError.schemaPath.replace('#/', '').replace(/\\//g, '.')\n const unionSchemas = getPathValue<SchemaObject[]>(schema, dottedSchemaPath)\n // and the slice of the subject that caused the issue\n const subjectValue = getJsonSchemaErrorValue(subject, unionError.instancePath.split('/').slice(1))\n\n if (unionSchemas !== undefined && subjectValue !== undefined) {\n // we know that none of the union schemas are correct, but for each of them we can measure how wrong they are\n const correctValuesAndErrors = unionSchemas\n .map((candidateSchemaFromUnion: SchemaObject) => {\n const candidateSchemaValidator = createAjvValidator('fail', candidateSchemaFromUnion)\n candidateSchemaValidator(subjectValue)\n\n let score = 0\n if (candidateSchemaFromUnion.type === 'object') {\n // provided the schema is an object, we can measure how many properties are good\n const candidatesObjectProperties = Object.keys(candidateSchemaFromUnion.properties)\n score = candidatesObjectProperties.reduce((acc, propertyName) => {\n const subSchema = candidateSchemaFromUnion.properties[propertyName] as SchemaObject\n const subjectValueSlice = getJsonSchemaErrorValue(subjectValue as object, [propertyName])\n\n const subValidator = createAjvValidator('fail', subSchema)\n if (subValidator(subjectValueSlice)) {\n return acc + 1\n }\n return acc\n }, score)\n }\n\n return [score, candidateSchemaValidator.errors!] as const\n })\n .sort(([scoreA], [scoreB]) => scoreA - scoreB)\n\n if (correctValuesAndErrors.length >= 2) {\n const [bestScore, bestErrors] = correctValuesAndErrors[correctValuesAndErrors.length - 1]!\n const [penultimateScore] = correctValuesAndErrors[correctValuesAndErrors.length - 2]!\n\n if (bestScore !== penultimateScore) {\n // If there's a winner, show the errors for the best schema as they'll likely be actionable.\n // We got these through a nested schema, so we need to adjust the instance path\n simplifiedUnionRelatedErrors = [\n unionError,\n ...bestErrors.map((bestError) => ({\n ...bestError,\n instancePath: unionError.instancePath + bestError.instancePath,\n })),\n ]\n }\n }\n }\n errors = [...unrelatedErrors, ...simplifiedUnionRelatedErrors]\n\n resolvedUnionErrors.add(unionError.instancePath)\n }\n return errors\n}\n"]}
@@ -2,7 +2,7 @@ import { JsonMap } from '../../private/common/json.js';
2
2
  import { DeepRequired } from '../common/ts/deep-required.js';
3
3
  export { DeepRequired };
4
4
  type Optional<T> = T | null;
5
- export declare const MONORAIL_COMMAND_TOPIC = "app_cli3_command/1.26";
5
+ export declare const MONORAIL_COMMAND_TOPIC = "app_cli3_command/1.28";
6
6
  export interface Schemas {
7
7
  [MONORAIL_COMMAND_TOPIC]: {
8
8
  sensitive: {
@@ -19,6 +19,7 @@ export interface Schemas {
19
19
  public: {
20
20
  business_platform_id?: Optional<number>;
21
21
  partner_id?: Optional<number>;
22
+ store_id?: Optional<number>;
22
23
  command: string;
23
24
  project_type?: Optional<string>;
24
25
  time_start: number;
@@ -2,7 +2,7 @@ import { fetch } from './http.js';
2
2
  import { outputDebug, outputContent, outputToken } from './output.js';
3
3
  const url = 'https://monorail-edge.shopifysvc.com/v1/produce';
4
4
  // This is the topic name of the main event we log to Monorail, the command tracker
5
- export const MONORAIL_COMMAND_TOPIC = 'app_cli3_command/1.26';
5
+ export const MONORAIL_COMMAND_TOPIC = 'app_cli3_command/1.28';
6
6
  const publishedCommandNames = new Set();
7
7
  /**
8
8
  * Publishes an event to Monorail.
@@ -1 +1 @@
1
- {"version":3,"file":"monorail.js","sourceRoot":"","sources":["../../../src/public/node/monorail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,EAAC,MAAM,WAAW,CAAA;AAC/B,OAAO,EAAC,WAAW,EAAE,aAAa,EAAE,WAAW,EAAC,MAAM,aAAa,CAAA;AAMnE,MAAM,GAAG,GAAG,iDAAiD,CAAA;AAI7D,mFAAmF;AACnF,MAAM,CAAC,MAAM,sBAAsB,GAAG,uBAAuB,CAAA;AAoL7D,MAAM,qBAAqB,GAAG,IAAI,GAAG,EAAU,CAAA;AAE/C;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,QAAmB,EACnB,UAA8B,EAC9B,aAAoC;IAEpC,qHAAqH;IACrH,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,CAAA;IACtC,IAAI,WAAW,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;QACnD,IAAI,qBAAqB,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;YAC3C,OAAO,EAAC,IAAI,EAAE,IAAI,EAAC,CAAA;QACrB,CAAC;QACD,qBAAqB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IACxC,CAAC;IAED,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,CAAA;QACxC,MAAM,OAAO,GAAG,EAAC,GAAG,UAAU,EAAE,GAAG,aAAa,EAAC,CAAA;QACjD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAC,CAAC,CAAA;QAC3D,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,CAAC,CAAA;QAEzC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAC,EAAE,cAAc,CAAC,CAAA;QAElF,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,WAAW,CAAC,aAAa,CAAA,yBAAyB,WAAW,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAA;YAC/F,OAAO,EAAC,IAAI,EAAE,IAAI,EAAC,CAAA;QACrB,CAAC;aAAM,CAAC;YACN,WAAW,CAAC,qCAAqC,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;YACvE,OAAO,EAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,UAAU,EAAC,CAAA;QACtD,CAAC;QACD,qDAAqD;IACvD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,GAAG,kCAAkC,CAAA;QAChD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;QAChD,CAAC;QACD,WAAW,CAAC,OAAO,CAAC,CAAA;QACpB,OAAO,EAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAC,CAAA;IACjC,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,eAAe,CAAmB,OAAU;IACnD,MAAM,MAAM,GAAG,EAAC,GAAG,OAAO,EAAC,CAAA;IAC3B,IAAI,SAAS,IAAI,MAAM,EAAE,CAAC;QACxB,MAAM,CAAC,OAAO,GAAG,MAAM,CAAA;IACzB,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED,MAAM,YAAY,GAAG,CAAC,WAAmB,EAAE,EAAE;IAC3C,OAAO;QACL,cAAc,EAAE,iCAAiC;QACjD,qCAAqC,EAAE,WAAW,CAAC,QAAQ,EAAE;QAC7D,kCAAkC,EAAE,WAAW,CAAC,QAAQ,EAAE;KAC3D,CAAA;AACH,CAAC,CAAA","sourcesContent":["import {fetch} from './http.js'\nimport {outputDebug, outputContent, outputToken} from './output.js'\nimport {JsonMap} from '../../private/common/json.js'\nimport {DeepRequired} from '../common/ts/deep-required.js'\n\nexport {DeepRequired}\n\nconst url = 'https://monorail-edge.shopifysvc.com/v1/produce'\n\ntype Optional<T> = T | null\n\n// This is the topic name of the main event we log to Monorail, the command tracker\nexport const MONORAIL_COMMAND_TOPIC = 'app_cli3_command/1.26'\n\nexport interface Schemas {\n [MONORAIL_COMMAND_TOPIC]: {\n sensitive: {\n args: string\n error_message?: Optional<string>\n app_name?: Optional<string>\n metadata?: Optional<string>\n store_fqdn?: Optional<string>\n cmd_all_environment_flags?: Optional<string>\n\n // Dev related commands\n cmd_dev_tunnel_custom?: Optional<string>\n\n // Environment\n env_plugin_installed_all?: Optional<string>\n env_shopify_variables?: Optional<string>\n }\n public: {\n business_platform_id?: Optional<number>\n partner_id?: Optional<number>\n command: string\n project_type?: Optional<string>\n time_start: number\n time_end: number\n total_time: number\n success: boolean\n api_key?: Optional<string>\n cli_version: string\n uname: string\n ruby_version: string\n node_version: string\n is_employee: boolean\n store_fqdn_hash?: Optional<string>\n store_fqdn_validated?: Optional<boolean>\n store_domain?: Optional<string>\n user_id: string\n\n // Any and all commands\n cmd_all_alias_used?: Optional<string>\n cmd_all_launcher?: Optional<string>\n cmd_all_path_override?: Optional<boolean>\n cmd_all_path_override_hash?: Optional<string>\n cmd_all_plugin?: Optional<string>\n cmd_all_topic?: Optional<string>\n cmd_all_verbose?: Optional<boolean>\n cmd_all_exit?: Optional<string>\n cmd_all_force?: Optional<boolean>\n cmd_all_last_graphql_request_id?: Optional<string>\n\n cmd_all_timing_network_ms?: Optional<number>\n cmd_all_timing_prompts_ms?: Optional<number>\n cmd_all_timing_active_ms?: Optional<number>\n\n // Auto-upgrade\n env_auto_upgrade_enabled?: Optional<boolean>\n env_auto_upgrade_accepted?: Optional<boolean>\n env_auto_upgrade_skipped_reason?: Optional<string>\n env_auto_upgrade_success?: Optional<boolean>\n\n // Any extension related command\n cmd_extensions_binary_from_source?: Optional<boolean>\n\n // Scaffolding related commands\n cmd_scaffold_required_auth?: Optional<boolean>\n cmd_scaffold_template_custom?: Optional<boolean>\n cmd_scaffold_template_flavor?: Optional<string>\n cmd_scaffold_type?: Optional<string>\n cmd_scaffold_type_category?: Optional<string>\n cmd_scaffold_type_gated?: Optional<boolean>\n cmd_scaffold_type_owner?: Optional<string>\n cmd_scaffold_used_prompts_for_type?: Optional<boolean>\n\n // Used in several but not all commands\n cmd_app_dependency_installation_skipped?: Optional<boolean>\n cmd_app_reset_used?: Optional<boolean>\n cmd_app_linked_config_used?: Optional<boolean>\n cmd_app_linked_config_name?: Optional<string>\n cmd_app_linked_config_git_tracked?: Optional<boolean>\n cmd_app_all_configs_any?: Optional<boolean>\n cmd_app_all_configs_clients?: Optional<string>\n cmd_app_linked_config_source?: Optional<string>\n cmd_app_linked_config_uses_cli_managed_urls?: Optional<boolean>\n cmd_app_warning_api_key_deprecation_displayed?: Optional<boolean>\n cmd_app_deployment_mode?: Optional<string>\n cmd_app_validate_json?: Optional<boolean>\n cmd_app_validate_valid?: Optional<boolean>\n cmd_app_validate_issue_count?: Optional<number>\n cmd_app_validate_file_count?: Optional<number>\n\n // Dev related commands\n cmd_dev_tunnel_type?: Optional<string>\n cmd_dev_tunnel_custom_hash?: Optional<string>\n cmd_dev_urls_updated?: Optional<boolean>\n cmd_dev_preview_url_opened?: Optional<boolean>\n cmd_dev_graphiql_opened?: Optional<boolean>\n cmd_dev_dev_preview_toggle_used?: Optional<boolean>\n\n // Create-app related commands\n cmd_create_app_template?: Optional<string>\n cmd_create_app_template_url?: Optional<string>\n\n // Deploy related commands\n cmd_deploy_flag_message_used?: Optional<boolean>\n cmd_deploy_flag_version_used?: Optional<boolean>\n cmd_deploy_flag_source_url_used?: Optional<boolean>\n cmd_deploy_confirm_new_registrations?: Optional<number>\n cmd_deploy_confirm_updated_registrations?: Optional<number>\n cmd_deploy_confirm_removed_registrations?: Optional<number>\n cmd_deploy_confirm_cancelled?: Optional<boolean>\n cmd_deploy_confirm_time_to_complete_ms?: Optional<number>\n cmd_deploy_prompt_upgrade_to_unified_displayed?: Optional<boolean>\n cmd_deploy_prompt_upgrade_to_unified_response?: Optional<string>\n cmd_deploy_confirm_include_config_used?: Optional<boolean>\n cmd_deploy_include_config_used?: Optional<boolean>\n cmd_deploy_config_modules_breakdown?: Optional<string>\n cmd_deploy_config_modules_updated?: Optional<string>\n cmd_deploy_config_modules_added?: Optional<string>\n cmd_deploy_config_modules_deleted?: Optional<string>\n\n // Release related commands\n cmd_release_confirm_cancelled?: Optional<boolean>\n\n // App setup\n app_extensions_any?: Optional<boolean>\n app_extensions_breakdown?: Optional<string>\n app_extensions_count?: Optional<number>\n app_extensions_custom_layout?: Optional<boolean>\n app_extensions_function_any?: Optional<boolean>\n app_extensions_function_count?: Optional<number>\n app_extensions_function_custom_layout?: Optional<boolean>\n app_extensions_theme_any?: Optional<boolean>\n app_extensions_theme_count?: Optional<number>\n app_extensions_theme_custom_layout?: Optional<boolean>\n app_extensions_ui_any?: Optional<boolean>\n app_extensions_ui_count?: Optional<number>\n app_extensions_ui_custom_layout?: Optional<boolean>\n app_name_hash?: Optional<string>\n app_path_hash?: Optional<string>\n app_scopes?: Optional<string>\n app_web_backend_any?: Optional<boolean>\n app_web_backend_count?: Optional<number>\n app_web_custom_layout?: Optional<boolean>\n app_web_framework?: Optional<string>\n app_web_frontend_any?: Optional<boolean>\n app_web_frontend_count?: Optional<number>\n\n // Theme related commands\n cmd_theme_timings?: Optional<string>\n cmd_theme_errors?: Optional<string>\n cmd_theme_retries?: Optional<string>\n cmd_theme_events?: Optional<string>\n\n // Environment\n env_ci?: Optional<boolean>\n env_ci_platform?: Optional<string>\n env_device_id?: Optional<string>\n env_package_manager?: Optional<string>\n env_install_package_manager?: Optional<string>\n env_package_manager_workspaces?: Optional<boolean>\n env_plugin_installed_any_custom?: Optional<boolean>\n env_plugin_installed_shopify?: Optional<string>\n env_shell?: Optional<string>\n env_web_ide?: Optional<string>\n env_cloud?: Optional<string>\n env_is_global?: Optional<boolean>\n env_auth_method?: Optional<string>\n }\n }\n [schemaId: string]: {sensitive: JsonMap; public: JsonMap}\n}\n\n// In reality, we're normally most interested in just this from Schemas, so export it for ease of use.\n// The monorail schema itself has lots of optional values as it must be backwards-compatible. For our schema we want mandatory values instead.\nexport type MonorailEventPublic = DeepRequired<Schemas[typeof MONORAIL_COMMAND_TOPIC]['public']>\nexport type MonorailEventSensitive = Schemas[typeof MONORAIL_COMMAND_TOPIC]['sensitive']\n\ntype MonorailResult = {type: 'ok'} | {type: 'error'; message: string}\n\nconst publishedCommandNames = new Set<string>()\n\n/**\n * Publishes an event to Monorail.\n *\n * @param schemaId - The schema ID of the event to publish.\n * @param publicData - The public data to publish.\n * @param sensitiveData - The sensitive data to publish.\n * @returns A result indicating whether the event was successfully published.\n */\nexport async function publishMonorailEvent<TSchemaId extends keyof Schemas, TPayload extends Schemas[TSchemaId]>(\n schemaId: TSchemaId,\n publicData: TPayload['public'],\n sensitiveData: TPayload['sensitive'],\n): Promise<MonorailResult> {\n // If a command has already been logged, never re-log it. This is to prevent duplication caused by unexpected errors.\n const commandName = publicData.command\n if (commandName && typeof commandName === 'string') {\n if (publishedCommandNames.has(commandName)) {\n return {type: 'ok'}\n }\n publishedCommandNames.add(commandName)\n }\n\n try {\n const currentTime = new Date().getTime()\n const payload = {...publicData, ...sensitiveData}\n const body = JSON.stringify({schema_id: schemaId, payload})\n const headers = buildHeaders(currentTime)\n\n const response = await fetch(url, {method: 'POST', body, headers}, 'non-blocking')\n\n if (response.status === 200) {\n outputDebug(outputContent`Analytics event sent: ${outputToken.json(sanitizePayload(payload))}`)\n return {type: 'ok'}\n } else {\n outputDebug(`Failed to report usage analytics: ${response.statusText}`)\n return {type: 'error', message: response.statusText}\n }\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n let message = 'Failed to report usage analytics'\n if (error instanceof Error) {\n message = message.concat(`: ${error.message}`)\n }\n outputDebug(message)\n return {type: 'error', message}\n }\n}\n\n/**\n * Sanitizies the api_key from the payload and returns a new hash.\n *\n * @param payload - The public and sensitive data.\n * @returns A copy of the payload with the api_key sanitized.\n */\nfunction sanitizePayload<T extends object>(payload: T): T {\n const result = {...payload}\n if ('api_key' in result) {\n result.api_key = '****'\n }\n\n return result\n}\n\nconst buildHeaders = (currentTime: number) => {\n return {\n 'Content-Type': 'application/json; charset=utf-8',\n 'X-Monorail-Edge-Event-Created-At-Ms': currentTime.toString(),\n 'X-Monorail-Edge-Event-Sent-At-Ms': currentTime.toString(),\n }\n}\n"]}
1
+ {"version":3,"file":"monorail.js","sourceRoot":"","sources":["../../../src/public/node/monorail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,EAAC,MAAM,WAAW,CAAA;AAC/B,OAAO,EAAC,WAAW,EAAE,aAAa,EAAE,WAAW,EAAC,MAAM,aAAa,CAAA;AAMnE,MAAM,GAAG,GAAG,iDAAiD,CAAA;AAI7D,mFAAmF;AACnF,MAAM,CAAC,MAAM,sBAAsB,GAAG,uBAAuB,CAAA;AAqL7D,MAAM,qBAAqB,GAAG,IAAI,GAAG,EAAU,CAAA;AAE/C;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,QAAmB,EACnB,UAA8B,EAC9B,aAAoC;IAEpC,qHAAqH;IACrH,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,CAAA;IACtC,IAAI,WAAW,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;QACnD,IAAI,qBAAqB,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;YAC3C,OAAO,EAAC,IAAI,EAAE,IAAI,EAAC,CAAA;QACrB,CAAC;QACD,qBAAqB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IACxC,CAAC;IAED,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,CAAA;QACxC,MAAM,OAAO,GAAG,EAAC,GAAG,UAAU,EAAE,GAAG,aAAa,EAAC,CAAA;QACjD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAC,CAAC,CAAA;QAC3D,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,CAAC,CAAA;QAEzC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAC,EAAE,cAAc,CAAC,CAAA;QAElF,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,WAAW,CAAC,aAAa,CAAA,yBAAyB,WAAW,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAA;YAC/F,OAAO,EAAC,IAAI,EAAE,IAAI,EAAC,CAAA;QACrB,CAAC;aAAM,CAAC;YACN,WAAW,CAAC,qCAAqC,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;YACvE,OAAO,EAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,UAAU,EAAC,CAAA;QACtD,CAAC;QACD,qDAAqD;IACvD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,GAAG,kCAAkC,CAAA;QAChD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;QAChD,CAAC;QACD,WAAW,CAAC,OAAO,CAAC,CAAA;QACpB,OAAO,EAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAC,CAAA;IACjC,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,eAAe,CAAmB,OAAU;IACnD,MAAM,MAAM,GAAG,EAAC,GAAG,OAAO,EAAC,CAAA;IAC3B,IAAI,SAAS,IAAI,MAAM,EAAE,CAAC;QACxB,MAAM,CAAC,OAAO,GAAG,MAAM,CAAA;IACzB,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED,MAAM,YAAY,GAAG,CAAC,WAAmB,EAAE,EAAE;IAC3C,OAAO;QACL,cAAc,EAAE,iCAAiC;QACjD,qCAAqC,EAAE,WAAW,CAAC,QAAQ,EAAE;QAC7D,kCAAkC,EAAE,WAAW,CAAC,QAAQ,EAAE;KAC3D,CAAA;AACH,CAAC,CAAA","sourcesContent":["import {fetch} from './http.js'\nimport {outputDebug, outputContent, outputToken} from './output.js'\nimport {JsonMap} from '../../private/common/json.js'\nimport {DeepRequired} from '../common/ts/deep-required.js'\n\nexport {DeepRequired}\n\nconst url = 'https://monorail-edge.shopifysvc.com/v1/produce'\n\ntype Optional<T> = T | null\n\n// This is the topic name of the main event we log to Monorail, the command tracker\nexport const MONORAIL_COMMAND_TOPIC = 'app_cli3_command/1.28'\n\nexport interface Schemas {\n [MONORAIL_COMMAND_TOPIC]: {\n sensitive: {\n args: string\n error_message?: Optional<string>\n app_name?: Optional<string>\n metadata?: Optional<string>\n store_fqdn?: Optional<string>\n cmd_all_environment_flags?: Optional<string>\n\n // Dev related commands\n cmd_dev_tunnel_custom?: Optional<string>\n\n // Environment\n env_plugin_installed_all?: Optional<string>\n env_shopify_variables?: Optional<string>\n }\n public: {\n business_platform_id?: Optional<number>\n partner_id?: Optional<number>\n store_id?: Optional<number>\n command: string\n project_type?: Optional<string>\n time_start: number\n time_end: number\n total_time: number\n success: boolean\n api_key?: Optional<string>\n cli_version: string\n uname: string\n ruby_version: string\n node_version: string\n is_employee: boolean\n store_fqdn_hash?: Optional<string>\n store_fqdn_validated?: Optional<boolean>\n store_domain?: Optional<string>\n user_id: string\n\n // Any and all commands\n cmd_all_alias_used?: Optional<string>\n cmd_all_launcher?: Optional<string>\n cmd_all_path_override?: Optional<boolean>\n cmd_all_path_override_hash?: Optional<string>\n cmd_all_plugin?: Optional<string>\n cmd_all_topic?: Optional<string>\n cmd_all_verbose?: Optional<boolean>\n cmd_all_exit?: Optional<string>\n cmd_all_force?: Optional<boolean>\n cmd_all_last_graphql_request_id?: Optional<string>\n\n cmd_all_timing_network_ms?: Optional<number>\n cmd_all_timing_prompts_ms?: Optional<number>\n cmd_all_timing_active_ms?: Optional<number>\n\n // Auto-upgrade\n env_auto_upgrade_enabled?: Optional<boolean>\n env_auto_upgrade_accepted?: Optional<boolean>\n env_auto_upgrade_skipped_reason?: Optional<string>\n env_auto_upgrade_success?: Optional<boolean>\n\n // Any extension related command\n cmd_extensions_binary_from_source?: Optional<boolean>\n\n // Scaffolding related commands\n cmd_scaffold_required_auth?: Optional<boolean>\n cmd_scaffold_template_custom?: Optional<boolean>\n cmd_scaffold_template_flavor?: Optional<string>\n cmd_scaffold_type?: Optional<string>\n cmd_scaffold_type_category?: Optional<string>\n cmd_scaffold_type_gated?: Optional<boolean>\n cmd_scaffold_type_owner?: Optional<string>\n cmd_scaffold_used_prompts_for_type?: Optional<boolean>\n\n // Used in several but not all commands\n cmd_app_dependency_installation_skipped?: Optional<boolean>\n cmd_app_reset_used?: Optional<boolean>\n cmd_app_linked_config_used?: Optional<boolean>\n cmd_app_linked_config_name?: Optional<string>\n cmd_app_linked_config_git_tracked?: Optional<boolean>\n cmd_app_all_configs_any?: Optional<boolean>\n cmd_app_all_configs_clients?: Optional<string>\n cmd_app_linked_config_source?: Optional<string>\n cmd_app_linked_config_uses_cli_managed_urls?: Optional<boolean>\n cmd_app_warning_api_key_deprecation_displayed?: Optional<boolean>\n cmd_app_deployment_mode?: Optional<string>\n cmd_app_validate_json?: Optional<boolean>\n cmd_app_validate_valid?: Optional<boolean>\n cmd_app_validate_issue_count?: Optional<number>\n cmd_app_validate_file_count?: Optional<number>\n\n // Dev related commands\n cmd_dev_tunnel_type?: Optional<string>\n cmd_dev_tunnel_custom_hash?: Optional<string>\n cmd_dev_urls_updated?: Optional<boolean>\n cmd_dev_preview_url_opened?: Optional<boolean>\n cmd_dev_graphiql_opened?: Optional<boolean>\n cmd_dev_dev_preview_toggle_used?: Optional<boolean>\n\n // Create-app related commands\n cmd_create_app_template?: Optional<string>\n cmd_create_app_template_url?: Optional<string>\n\n // Deploy related commands\n cmd_deploy_flag_message_used?: Optional<boolean>\n cmd_deploy_flag_version_used?: Optional<boolean>\n cmd_deploy_flag_source_url_used?: Optional<boolean>\n cmd_deploy_confirm_new_registrations?: Optional<number>\n cmd_deploy_confirm_updated_registrations?: Optional<number>\n cmd_deploy_confirm_removed_registrations?: Optional<number>\n cmd_deploy_confirm_cancelled?: Optional<boolean>\n cmd_deploy_confirm_time_to_complete_ms?: Optional<number>\n cmd_deploy_prompt_upgrade_to_unified_displayed?: Optional<boolean>\n cmd_deploy_prompt_upgrade_to_unified_response?: Optional<string>\n cmd_deploy_confirm_include_config_used?: Optional<boolean>\n cmd_deploy_include_config_used?: Optional<boolean>\n cmd_deploy_config_modules_breakdown?: Optional<string>\n cmd_deploy_config_modules_updated?: Optional<string>\n cmd_deploy_config_modules_added?: Optional<string>\n cmd_deploy_config_modules_deleted?: Optional<string>\n\n // Release related commands\n cmd_release_confirm_cancelled?: Optional<boolean>\n\n // App setup\n app_extensions_any?: Optional<boolean>\n app_extensions_breakdown?: Optional<string>\n app_extensions_count?: Optional<number>\n app_extensions_custom_layout?: Optional<boolean>\n app_extensions_function_any?: Optional<boolean>\n app_extensions_function_count?: Optional<number>\n app_extensions_function_custom_layout?: Optional<boolean>\n app_extensions_theme_any?: Optional<boolean>\n app_extensions_theme_count?: Optional<number>\n app_extensions_theme_custom_layout?: Optional<boolean>\n app_extensions_ui_any?: Optional<boolean>\n app_extensions_ui_count?: Optional<number>\n app_extensions_ui_custom_layout?: Optional<boolean>\n app_name_hash?: Optional<string>\n app_path_hash?: Optional<string>\n app_scopes?: Optional<string>\n app_web_backend_any?: Optional<boolean>\n app_web_backend_count?: Optional<number>\n app_web_custom_layout?: Optional<boolean>\n app_web_framework?: Optional<string>\n app_web_frontend_any?: Optional<boolean>\n app_web_frontend_count?: Optional<number>\n\n // Theme related commands\n cmd_theme_timings?: Optional<string>\n cmd_theme_errors?: Optional<string>\n cmd_theme_retries?: Optional<string>\n cmd_theme_events?: Optional<string>\n\n // Environment\n env_ci?: Optional<boolean>\n env_ci_platform?: Optional<string>\n env_device_id?: Optional<string>\n env_package_manager?: Optional<string>\n env_install_package_manager?: Optional<string>\n env_package_manager_workspaces?: Optional<boolean>\n env_plugin_installed_any_custom?: Optional<boolean>\n env_plugin_installed_shopify?: Optional<string>\n env_shell?: Optional<string>\n env_web_ide?: Optional<string>\n env_cloud?: Optional<string>\n env_is_global?: Optional<boolean>\n env_auth_method?: Optional<string>\n }\n }\n [schemaId: string]: {sensitive: JsonMap; public: JsonMap}\n}\n\n// In reality, we're normally most interested in just this from Schemas, so export it for ease of use.\n// The monorail schema itself has lots of optional values as it must be backwards-compatible. For our schema we want mandatory values instead.\nexport type MonorailEventPublic = DeepRequired<Schemas[typeof MONORAIL_COMMAND_TOPIC]['public']>\nexport type MonorailEventSensitive = Schemas[typeof MONORAIL_COMMAND_TOPIC]['sensitive']\n\ntype MonorailResult = {type: 'ok'} | {type: 'error'; message: string}\n\nconst publishedCommandNames = new Set<string>()\n\n/**\n * Publishes an event to Monorail.\n *\n * @param schemaId - The schema ID of the event to publish.\n * @param publicData - The public data to publish.\n * @param sensitiveData - The sensitive data to publish.\n * @returns A result indicating whether the event was successfully published.\n */\nexport async function publishMonorailEvent<TSchemaId extends keyof Schemas, TPayload extends Schemas[TSchemaId]>(\n schemaId: TSchemaId,\n publicData: TPayload['public'],\n sensitiveData: TPayload['sensitive'],\n): Promise<MonorailResult> {\n // If a command has already been logged, never re-log it. This is to prevent duplication caused by unexpected errors.\n const commandName = publicData.command\n if (commandName && typeof commandName === 'string') {\n if (publishedCommandNames.has(commandName)) {\n return {type: 'ok'}\n }\n publishedCommandNames.add(commandName)\n }\n\n try {\n const currentTime = new Date().getTime()\n const payload = {...publicData, ...sensitiveData}\n const body = JSON.stringify({schema_id: schemaId, payload})\n const headers = buildHeaders(currentTime)\n\n const response = await fetch(url, {method: 'POST', body, headers}, 'non-blocking')\n\n if (response.status === 200) {\n outputDebug(outputContent`Analytics event sent: ${outputToken.json(sanitizePayload(payload))}`)\n return {type: 'ok'}\n } else {\n outputDebug(`Failed to report usage analytics: ${response.statusText}`)\n return {type: 'error', message: response.statusText}\n }\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n let message = 'Failed to report usage analytics'\n if (error instanceof Error) {\n message = message.concat(`: ${error.message}`)\n }\n outputDebug(message)\n return {type: 'error', message}\n }\n}\n\n/**\n * Sanitizies the api_key from the payload and returns a new hash.\n *\n * @param payload - The public and sensitive data.\n * @returns A copy of the payload with the api_key sanitized.\n */\nfunction sanitizePayload<T extends object>(payload: T): T {\n const result = {...payload}\n if ('api_key' in result) {\n result.api_key = '****'\n }\n\n return result\n}\n\nconst buildHeaders = (currentTime: number) => {\n return {\n 'Content-Type': 'application/json; charset=utf-8',\n 'X-Monorail-Edge-Event-Created-At-Ms': currentTime.toString(),\n 'X-Monorail-Edge-Event-Sent-At-Ms': currentTime.toString(),\n }\n}\n"]}
@@ -119,9 +119,10 @@ export declare function ensureAuthenticatedThemes(store: string, password: strin
119
119
  * Ensure that we have a valid session to access the Business Platform API.
120
120
  *
121
121
  * @param scopes - Optional array of extra scopes to authenticate with.
122
+ * @param options - Optional extra options to use.
122
123
  * @returns The access token for the Business Platform API.
123
124
  */
124
- export declare function ensureAuthenticatedBusinessPlatform(scopes?: BusinessPlatformScope[]): Promise<string>;
125
+ export declare function ensureAuthenticatedBusinessPlatform(scopes?: BusinessPlatformScope[], options?: EnsureAuthenticatedAdditionalOptions): Promise<string>;
125
126
  /**
126
127
  * Logout from Shopify.
127
128
  *
@@ -176,13 +176,14 @@ ${outputToken.json(scopes)}
176
176
  * Ensure that we have a valid session to access the Business Platform API.
177
177
  *
178
178
  * @param scopes - Optional array of extra scopes to authenticate with.
179
+ * @param options - Optional extra options to use.
179
180
  * @returns The access token for the Business Platform API.
180
181
  */
181
- export async function ensureAuthenticatedBusinessPlatform(scopes = []) {
182
+ export async function ensureAuthenticatedBusinessPlatform(scopes = [], options = {}) {
182
183
  outputDebug(outputContent `Ensuring that the user is authenticated with the Business Platform API with the following scopes:
183
184
  ${outputToken.json(scopes)}
184
185
  `);
185
- const tokens = await ensureAuthenticated({ businessPlatformApi: { scopes } }, process.env);
186
+ const tokens = await ensureAuthenticated({ businessPlatformApi: { scopes } }, process.env, options);
186
187
  if (!tokens.businessPlatform) {
187
188
  throw new BugError('No business-platform token found after ensuring authenticated');
188
189
  }
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/public/node/session.ts"],"names":[],"mappings":"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","sourcesContent":["import {shopifyFetch} from './http.js'\nimport {nonRandomUUID} from './crypto.js'\nimport {getAppAutomationToken} from './environment.js'\nimport {AbortError, BugError} from './error.js'\nimport {outputContent, outputToken, outputDebug} from './output.js'\nimport * as sessionStore from '../../private/node/session/store.js'\nimport {\n exchangeCustomPartnerToken,\n exchangeAppAutomationTokenForAppManagementAccessToken,\n exchangeAppAutomationTokenForBusinessPlatformAccessToken,\n} from '../../private/node/session/exchange.js'\nimport {\n AdminAPIScope,\n AppManagementAPIScope,\n BusinessPlatformScope,\n EnsureAuthenticatedAdditionalOptions,\n PartnersAPIScope,\n StorefrontRendererScope,\n ensureAuthenticated,\n setLastSeenAuthMethod,\n setLastSeenUserIdAfterAuth,\n} from '../../private/node/session.js'\nimport {isThemeAccessSession} from '../../private/node/api/rest.js'\n\n/**\n * Session Object to access the Admin API, includes the token and the store FQDN.\n */\nexport interface AdminSession {\n token: string\n storeFqdn: string\n}\n\n/**\n * Session Object for Partners API and App Management API access.\n */\nexport interface Session {\n token: string\n businessPlatformToken: string\n accountInfo: AccountInfo\n userId: string\n}\n\nexport type AccountInfo = UserAccountInfo | ServiceAccountInfo | UnknownAccountInfo\n\n/**\n * Records the user ID that should be attached to command analytics for this process.\n *\n * @param userId - User identifier to report on the command analytics event.\n */\nexport function setLastSeenUserId(userId: string): void {\n setLastSeenUserIdAfterAuth(userId)\n}\n\ninterface UserAccountInfo {\n type: 'UserAccount'\n email: string\n}\n\ninterface ServiceAccountInfo {\n type: 'ServiceAccount'\n orgName: string\n}\n\ninterface UnknownAccountInfo {\n type: 'UnknownAccount'\n}\n\n/**\n * Type guard to check if an account is a UserAccount.\n *\n * @param account - The account to check.\n * @returns True if the account is a UserAccount.\n */\nexport function isUserAccount(account: AccountInfo): account is UserAccountInfo {\n return account.type === 'UserAccount'\n}\n\n/**\n * Type guard to check if an account is a ServiceAccount.\n *\n * @param account - The account to check.\n * @returns True if the account is a ServiceAccount.\n */\nexport function isServiceAccount(account: AccountInfo): account is ServiceAccountInfo {\n return account.type === 'ServiceAccount'\n}\n\n/**\n * Ensure that we have a valid session with no particular scopes.\n *\n * @param env - Optional environment variables to use.\n * @param options - Optional extra options to use.\n * @returns The user ID.\n */\nexport async function ensureAuthenticatedUser(\n env = process.env,\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<{userId: string}> {\n outputDebug(outputContent`Ensuring that the user is authenticated with no particular scopes`)\n const tokens = await ensureAuthenticated({}, env, options)\n return {userId: tokens.userId}\n}\n\n/**\n * Ensure that we have a valid session to access the Partners API.\n * If SHOPIFY_CLI_PARTNERS_TOKEN exists, that token will be used to obtain a valid Partners Token\n * If SHOPIFY_CLI_PARTNERS_TOKEN exists, scopes will be ignored.\n *\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param env - Optional environment variables to use.\n * @param options - Optional extra options to use.\n * @returns The access token for the Partners API.\n */\nexport async function ensureAuthenticatedPartners(\n scopes: PartnersAPIScope[] = [],\n env = process.env,\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<{token: string; userId: string}> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Partners API with the following scopes:\n${outputToken.json(scopes)}\n`)\n const envToken = getAppAutomationToken()\n if (envToken) {\n const result = await exchangeCustomPartnerToken(envToken)\n return {token: result.accessToken, userId: result.userId}\n }\n const tokens = await ensureAuthenticated({partnersApi: {scopes}}, env, options)\n if (!tokens.partners) {\n throw new BugError('No partners token found after ensuring authenticated')\n }\n return {token: tokens.partners, userId: tokens.userId}\n}\n\n/**\n * Ensure that we have a valid session to access the App Management API.\n *\n * @param options - Optional extra options to use.\n * @param appManagementScopes - Optional array of extra scopes to authenticate with.\n * @param businessPlatformScopes - Optional array of extra scopes to authenticate with.\n * @param env - Optional environment variables to use.\n * @returns The access token for the App Management API.\n */\nexport async function ensureAuthenticatedAppManagementAndBusinessPlatform(\n options: EnsureAuthenticatedAdditionalOptions = {},\n appManagementScopes: AppManagementAPIScope[] = [],\n businessPlatformScopes: BusinessPlatformScope[] = [],\n env = process.env,\n): Promise<{appManagementToken: string; userId: string; businessPlatformToken: string}> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the App Management API with the following scopes:\n${outputToken.json(appManagementScopes)}\n`)\n\n const envToken = getAppAutomationToken()\n if (envToken) {\n const appManagmentToken = await exchangeAppAutomationTokenForAppManagementAccessToken(envToken)\n const businessPlatformToken = await exchangeAppAutomationTokenForBusinessPlatformAccessToken(envToken)\n\n return {\n appManagementToken: appManagmentToken.accessToken,\n userId: appManagmentToken.userId,\n businessPlatformToken: businessPlatformToken.accessToken,\n }\n }\n\n const tokens = await ensureAuthenticated(\n {appManagementApi: {scopes: appManagementScopes}, businessPlatformApi: {scopes: businessPlatformScopes}},\n env,\n options,\n )\n if (!tokens.appManagement || !tokens.businessPlatform) {\n throw new BugError('No App Management or Business Platform token found after ensuring authenticated')\n }\n\n return {\n appManagementToken: tokens.appManagement,\n userId: tokens.userId,\n businessPlatformToken: tokens.businessPlatform,\n }\n}\n\n/**\n * Ensure that we have a valid session to access the Storefront API.\n *\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param password - Optional password to use.\n * @param options - Optional extra options to use.\n * @returns The access token for the Storefront API.\n */\nexport async function ensureAuthenticatedStorefront(\n scopes: StorefrontRendererScope[] = [],\n password: string | undefined = undefined,\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<string> {\n if (password) {\n const session = {token: password, storeFqdn: ''}\n const authMethod = isThemeAccessSession(session) ? 'theme_access_token' : 'custom_app_token'\n setLastSeenAuthMethod(authMethod)\n setLastSeenUserIdAfterAuth(nonRandomUUID(password))\n return password\n }\n\n outputDebug(outputContent`Ensuring that the user is authenticated with the Storefront API with the following scopes:\n${outputToken.json(scopes)}\n`)\n const tokens = await ensureAuthenticated({storefrontRendererApi: {scopes}}, process.env, options)\n if (!tokens.storefront) {\n throw new BugError('No storefront token found after ensuring authenticated')\n }\n return tokens.storefront\n}\n\n/**\n * Ensure that we have a valid Admin session for the given store.\n *\n * @param store - Store fqdn to request auth for.\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param options - Optional extra options to use.\n * @returns The access token for the Admin API.\n */\nexport async function ensureAuthenticatedAdmin(\n store: string,\n scopes: AdminAPIScope[] = [],\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<AdminSession> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Admin API with the following scopes for the store ${outputToken.raw(\n store,\n )}:\n${outputToken.json(scopes)}\n`)\n const tokens = await ensureAuthenticated({adminApi: {scopes, storeFqdn: store}}, process.env, {\n ...options,\n })\n if (!tokens.admin) {\n throw new BugError('No admin token found after ensuring authenticated')\n }\n return tokens.admin\n}\n\n/**\n * Ensure that we have a valid session to access the Theme API.\n * If a password is provided, that token will be used against Theme Access API.\n * Otherwise, it will ensure that the user is authenticated with the Admin API.\n *\n * @param store - Store fqdn to request auth for.\n * @param password - Password generated from Theme Access app.\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param options - Optional extra options to use.\n * @returns The access token and store.\n */\nexport async function ensureAuthenticatedThemes(\n store: string,\n password: string | undefined,\n scopes: AdminAPIScope[] = [],\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<AdminSession> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Theme API with the following scopes:\n${outputToken.json(scopes)}\n`)\n if (password) {\n const session = {token: password, storeFqdn: store}\n const authMethod = isThemeAccessSession(session) ? 'theme_access_token' : 'custom_app_token'\n setLastSeenAuthMethod(authMethod)\n setLastSeenUserIdAfterAuth(nonRandomUUID(password))\n return session\n }\n return ensureAuthenticatedAdmin(store, scopes, options)\n}\n\n/**\n * Ensure that we have a valid session to access the Business Platform API.\n *\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @returns The access token for the Business Platform API.\n */\nexport async function ensureAuthenticatedBusinessPlatform(scopes: BusinessPlatformScope[] = []): Promise<string> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Business Platform API with the following scopes:\n${outputToken.json(scopes)}\n`)\n const tokens = await ensureAuthenticated({businessPlatformApi: {scopes}}, process.env)\n if (!tokens.businessPlatform) {\n throw new BugError('No business-platform token found after ensuring authenticated')\n }\n return tokens.businessPlatform\n}\n\n/**\n * Logout from Shopify.\n *\n * @returns A promise that resolves when the logout is complete.\n */\nexport function logout(): Promise<void> {\n return sessionStore.remove()\n}\n\n/**\n * Ensure that we have a valid Admin session for the given store, with access on behalf of the app.\n *\n * See `ensureAuthenticatedAdmin` for access on behalf of a user.\n *\n * @param storeFqdn - Store fqdn to request auth for.\n * @param clientId - Client ID of the app.\n * @param clientSecret - Client secret of the app.\n * @returns The access token for the Admin API.\n */\nexport async function ensureAuthenticatedAdminAsApp(\n storeFqdn: string,\n clientId: string,\n clientSecret: string,\n): Promise<AdminSession> {\n const bodyData = {\n client_id: clientId,\n client_secret: clientSecret,\n grant_type: 'client_credentials',\n }\n const tokenResponse = await shopifyFetch(\n `https://${storeFqdn}/admin/oauth/access_token`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(bodyData),\n },\n 'slow-request',\n )\n\n const body = await tokenResponse.text()\n\n if (tokenResponse.status === 400) {\n if (body.includes('app_not_installed')) {\n throw new AbortError(\n outputContent`App is not installed on ${outputToken.green(\n storeFqdn,\n )}. Try running ${outputToken.genericShellCommand(`shopify app dev`)} to connect your app to the shop.`,\n )\n }\n throw new AbortError(\n `Failed to get access token for app ${clientId} on store ${storeFqdn}: ${tokenResponse.statusText}`,\n )\n }\n try {\n const tokenJson = JSON.parse(body) as {access_token: string}\n return {token: tokenJson.access_token, storeFqdn}\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new AbortError(\n `Received invalid response from admin authentication service (HTTP ${tokenResponse.status}).`,\n 'The response could not be parsed as JSON. The service may be temporarily unavailable. Please try again.',\n )\n }\n throw error\n }\n}\n"]}
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/public/node/session.ts"],"names":[],"mappings":"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","sourcesContent":["import {shopifyFetch} from './http.js'\nimport {nonRandomUUID} from './crypto.js'\nimport {getAppAutomationToken} from './environment.js'\nimport {AbortError, BugError} from './error.js'\nimport {outputContent, outputToken, outputDebug} from './output.js'\nimport * as sessionStore from '../../private/node/session/store.js'\nimport {\n exchangeCustomPartnerToken,\n exchangeAppAutomationTokenForAppManagementAccessToken,\n exchangeAppAutomationTokenForBusinessPlatformAccessToken,\n} from '../../private/node/session/exchange.js'\nimport {\n AdminAPIScope,\n AppManagementAPIScope,\n BusinessPlatformScope,\n EnsureAuthenticatedAdditionalOptions,\n PartnersAPIScope,\n StorefrontRendererScope,\n ensureAuthenticated,\n setLastSeenAuthMethod,\n setLastSeenUserIdAfterAuth,\n} from '../../private/node/session.js'\nimport {isThemeAccessSession} from '../../private/node/api/rest.js'\n\n/**\n * Session Object to access the Admin API, includes the token and the store FQDN.\n */\nexport interface AdminSession {\n token: string\n storeFqdn: string\n}\n\n/**\n * Session Object for Partners API and App Management API access.\n */\nexport interface Session {\n token: string\n businessPlatformToken: string\n accountInfo: AccountInfo\n userId: string\n}\n\nexport type AccountInfo = UserAccountInfo | ServiceAccountInfo | UnknownAccountInfo\n\n/**\n * Records the user ID that should be attached to command analytics for this process.\n *\n * @param userId - User identifier to report on the command analytics event.\n */\nexport function setLastSeenUserId(userId: string): void {\n setLastSeenUserIdAfterAuth(userId)\n}\n\ninterface UserAccountInfo {\n type: 'UserAccount'\n email: string\n}\n\ninterface ServiceAccountInfo {\n type: 'ServiceAccount'\n orgName: string\n}\n\ninterface UnknownAccountInfo {\n type: 'UnknownAccount'\n}\n\n/**\n * Type guard to check if an account is a UserAccount.\n *\n * @param account - The account to check.\n * @returns True if the account is a UserAccount.\n */\nexport function isUserAccount(account: AccountInfo): account is UserAccountInfo {\n return account.type === 'UserAccount'\n}\n\n/**\n * Type guard to check if an account is a ServiceAccount.\n *\n * @param account - The account to check.\n * @returns True if the account is a ServiceAccount.\n */\nexport function isServiceAccount(account: AccountInfo): account is ServiceAccountInfo {\n return account.type === 'ServiceAccount'\n}\n\n/**\n * Ensure that we have a valid session with no particular scopes.\n *\n * @param env - Optional environment variables to use.\n * @param options - Optional extra options to use.\n * @returns The user ID.\n */\nexport async function ensureAuthenticatedUser(\n env = process.env,\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<{userId: string}> {\n outputDebug(outputContent`Ensuring that the user is authenticated with no particular scopes`)\n const tokens = await ensureAuthenticated({}, env, options)\n return {userId: tokens.userId}\n}\n\n/**\n * Ensure that we have a valid session to access the Partners API.\n * If SHOPIFY_CLI_PARTNERS_TOKEN exists, that token will be used to obtain a valid Partners Token\n * If SHOPIFY_CLI_PARTNERS_TOKEN exists, scopes will be ignored.\n *\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param env - Optional environment variables to use.\n * @param options - Optional extra options to use.\n * @returns The access token for the Partners API.\n */\nexport async function ensureAuthenticatedPartners(\n scopes: PartnersAPIScope[] = [],\n env = process.env,\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<{token: string; userId: string}> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Partners API with the following scopes:\n${outputToken.json(scopes)}\n`)\n const envToken = getAppAutomationToken()\n if (envToken) {\n const result = await exchangeCustomPartnerToken(envToken)\n return {token: result.accessToken, userId: result.userId}\n }\n const tokens = await ensureAuthenticated({partnersApi: {scopes}}, env, options)\n if (!tokens.partners) {\n throw new BugError('No partners token found after ensuring authenticated')\n }\n return {token: tokens.partners, userId: tokens.userId}\n}\n\n/**\n * Ensure that we have a valid session to access the App Management API.\n *\n * @param options - Optional extra options to use.\n * @param appManagementScopes - Optional array of extra scopes to authenticate with.\n * @param businessPlatformScopes - Optional array of extra scopes to authenticate with.\n * @param env - Optional environment variables to use.\n * @returns The access token for the App Management API.\n */\nexport async function ensureAuthenticatedAppManagementAndBusinessPlatform(\n options: EnsureAuthenticatedAdditionalOptions = {},\n appManagementScopes: AppManagementAPIScope[] = [],\n businessPlatformScopes: BusinessPlatformScope[] = [],\n env = process.env,\n): Promise<{appManagementToken: string; userId: string; businessPlatformToken: string}> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the App Management API with the following scopes:\n${outputToken.json(appManagementScopes)}\n`)\n\n const envToken = getAppAutomationToken()\n if (envToken) {\n const appManagmentToken = await exchangeAppAutomationTokenForAppManagementAccessToken(envToken)\n const businessPlatformToken = await exchangeAppAutomationTokenForBusinessPlatformAccessToken(envToken)\n\n return {\n appManagementToken: appManagmentToken.accessToken,\n userId: appManagmentToken.userId,\n businessPlatformToken: businessPlatformToken.accessToken,\n }\n }\n\n const tokens = await ensureAuthenticated(\n {appManagementApi: {scopes: appManagementScopes}, businessPlatformApi: {scopes: businessPlatformScopes}},\n env,\n options,\n )\n if (!tokens.appManagement || !tokens.businessPlatform) {\n throw new BugError('No App Management or Business Platform token found after ensuring authenticated')\n }\n\n return {\n appManagementToken: tokens.appManagement,\n userId: tokens.userId,\n businessPlatformToken: tokens.businessPlatform,\n }\n}\n\n/**\n * Ensure that we have a valid session to access the Storefront API.\n *\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param password - Optional password to use.\n * @param options - Optional extra options to use.\n * @returns The access token for the Storefront API.\n */\nexport async function ensureAuthenticatedStorefront(\n scopes: StorefrontRendererScope[] = [],\n password: string | undefined = undefined,\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<string> {\n if (password) {\n const session = {token: password, storeFqdn: ''}\n const authMethod = isThemeAccessSession(session) ? 'theme_access_token' : 'custom_app_token'\n setLastSeenAuthMethod(authMethod)\n setLastSeenUserIdAfterAuth(nonRandomUUID(password))\n return password\n }\n\n outputDebug(outputContent`Ensuring that the user is authenticated with the Storefront API with the following scopes:\n${outputToken.json(scopes)}\n`)\n const tokens = await ensureAuthenticated({storefrontRendererApi: {scopes}}, process.env, options)\n if (!tokens.storefront) {\n throw new BugError('No storefront token found after ensuring authenticated')\n }\n return tokens.storefront\n}\n\n/**\n * Ensure that we have a valid Admin session for the given store.\n *\n * @param store - Store fqdn to request auth for.\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param options - Optional extra options to use.\n * @returns The access token for the Admin API.\n */\nexport async function ensureAuthenticatedAdmin(\n store: string,\n scopes: AdminAPIScope[] = [],\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<AdminSession> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Admin API with the following scopes for the store ${outputToken.raw(\n store,\n )}:\n${outputToken.json(scopes)}\n`)\n const tokens = await ensureAuthenticated({adminApi: {scopes, storeFqdn: store}}, process.env, {\n ...options,\n })\n if (!tokens.admin) {\n throw new BugError('No admin token found after ensuring authenticated')\n }\n return tokens.admin\n}\n\n/**\n * Ensure that we have a valid session to access the Theme API.\n * If a password is provided, that token will be used against Theme Access API.\n * Otherwise, it will ensure that the user is authenticated with the Admin API.\n *\n * @param store - Store fqdn to request auth for.\n * @param password - Password generated from Theme Access app.\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param options - Optional extra options to use.\n * @returns The access token and store.\n */\nexport async function ensureAuthenticatedThemes(\n store: string,\n password: string | undefined,\n scopes: AdminAPIScope[] = [],\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<AdminSession> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Theme API with the following scopes:\n${outputToken.json(scopes)}\n`)\n if (password) {\n const session = {token: password, storeFqdn: store}\n const authMethod = isThemeAccessSession(session) ? 'theme_access_token' : 'custom_app_token'\n setLastSeenAuthMethod(authMethod)\n setLastSeenUserIdAfterAuth(nonRandomUUID(password))\n return session\n }\n return ensureAuthenticatedAdmin(store, scopes, options)\n}\n\n/**\n * Ensure that we have a valid session to access the Business Platform API.\n *\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param options - Optional extra options to use.\n * @returns The access token for the Business Platform API.\n */\nexport async function ensureAuthenticatedBusinessPlatform(\n scopes: BusinessPlatformScope[] = [],\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<string> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Business Platform API with the following scopes:\n${outputToken.json(scopes)}\n`)\n const tokens = await ensureAuthenticated({businessPlatformApi: {scopes}}, process.env, options)\n if (!tokens.businessPlatform) {\n throw new BugError('No business-platform token found after ensuring authenticated')\n }\n return tokens.businessPlatform\n}\n\n/**\n * Logout from Shopify.\n *\n * @returns A promise that resolves when the logout is complete.\n */\nexport function logout(): Promise<void> {\n return sessionStore.remove()\n}\n\n/**\n * Ensure that we have a valid Admin session for the given store, with access on behalf of the app.\n *\n * See `ensureAuthenticatedAdmin` for access on behalf of a user.\n *\n * @param storeFqdn - Store fqdn to request auth for.\n * @param clientId - Client ID of the app.\n * @param clientSecret - Client secret of the app.\n * @returns The access token for the Admin API.\n */\nexport async function ensureAuthenticatedAdminAsApp(\n storeFqdn: string,\n clientId: string,\n clientSecret: string,\n): Promise<AdminSession> {\n const bodyData = {\n client_id: clientId,\n client_secret: clientSecret,\n grant_type: 'client_credentials',\n }\n const tokenResponse = await shopifyFetch(\n `https://${storeFqdn}/admin/oauth/access_token`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(bodyData),\n },\n 'slow-request',\n )\n\n const body = await tokenResponse.text()\n\n if (tokenResponse.status === 400) {\n if (body.includes('app_not_installed')) {\n throw new AbortError(\n outputContent`App is not installed on ${outputToken.green(\n storeFqdn,\n )}. Try running ${outputToken.genericShellCommand(`shopify app dev`)} to connect your app to the shop.`,\n )\n }\n throw new AbortError(\n `Failed to get access token for app ${clientId} on store ${storeFqdn}: ${tokenResponse.statusText}`,\n )\n }\n try {\n const tokenJson = JSON.parse(body) as {access_token: string}\n return {token: tokenJson.access_token, storeFqdn}\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new AbortError(\n `Received invalid response from admin authentication service (HTTP ${tokenResponse.status}).`,\n 'The response could not be parsed as JSON. The service may be temporarily unavailable. Please try again.',\n )\n }\n throw error\n }\n}\n"]}