@shopify/cli-kit 4.3.0 → 4.5.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.
Files changed (84) hide show
  1. package/dist/cli/api/graphql/bulk-operations/generated/bulk-operation-cancel.d.ts +28 -0
  2. package/dist/cli/api/graphql/bulk-operations/generated/bulk-operation-cancel.js +74 -0
  3. package/dist/cli/api/graphql/bulk-operations/generated/bulk-operation-cancel.js.map +1 -0
  4. package/dist/cli/api/graphql/bulk-operations/generated/bulk-operation-run-mutation.d.ts +28 -0
  5. package/dist/cli/api/graphql/bulk-operations/generated/bulk-operation-run-mutation.js +92 -0
  6. package/dist/cli/api/graphql/bulk-operations/generated/bulk-operation-run-mutation.js.map +1 -0
  7. package/dist/cli/api/graphql/bulk-operations/generated/bulk-operation-run-query.d.ts +26 -0
  8. package/dist/cli/api/graphql/bulk-operations/generated/bulk-operation-run-query.js +72 -0
  9. package/dist/cli/api/graphql/bulk-operations/generated/bulk-operation-run-query.js.map +1 -0
  10. package/dist/cli/api/graphql/bulk-operations/generated/get-bulk-operation-by-id.d.ts +19 -0
  11. package/dist/cli/api/graphql/bulk-operations/generated/get-bulk-operation-by-id.js +49 -0
  12. package/dist/cli/api/graphql/bulk-operations/generated/get-bulk-operation-by-id.js.map +1 -0
  13. package/dist/cli/api/graphql/bulk-operations/generated/list-bulk-operations.d.ts +22 -0
  14. package/dist/cli/api/graphql/bulk-operations/generated/list-bulk-operations.js +78 -0
  15. package/dist/cli/api/graphql/bulk-operations/generated/list-bulk-operations.js.map +1 -0
  16. package/dist/cli/api/graphql/bulk-operations/generated/staged-uploads-create.d.ts +22 -0
  17. package/dist/cli/api/graphql/bulk-operations/generated/staged-uploads-create.js +82 -0
  18. package/dist/cli/api/graphql/bulk-operations/generated/staged-uploads-create.js.map +1 -0
  19. package/dist/private/node/api/headers.js +1 -1
  20. package/dist/private/node/api/headers.js.map +1 -1
  21. package/dist/private/node/ui/components/Alert.test.js +5 -5
  22. package/dist/private/node/ui/components/Alert.test.js.map +1 -1
  23. package/dist/private/node/ui/components/Banner.js +9 -1
  24. package/dist/private/node/ui/components/Banner.js.map +1 -1
  25. package/dist/public/common/version.d.ts +1 -1
  26. package/dist/public/common/version.js +1 -1
  27. package/dist/public/common/version.js.map +1 -1
  28. package/dist/public/node/api/admin.d.ts +2 -1
  29. package/dist/public/node/api/admin.js +4 -3
  30. package/dist/public/node/api/admin.js.map +1 -1
  31. package/dist/public/node/api/bulk-operations/cancel.d.ts +15 -0
  32. package/dist/public/node/api/bulk-operations/cancel.js +20 -0
  33. package/dist/public/node/api/bulk-operations/cancel.js.map +1 -0
  34. package/dist/public/node/api/bulk-operations/constants.d.ts +5 -0
  35. package/dist/public/node/api/bulk-operations/constants.js +6 -0
  36. package/dist/public/node/api/bulk-operations/constants.js.map +1 -0
  37. package/dist/public/node/api/bulk-operations/download-bulk-operation-results.d.ts +17 -0
  38. package/dist/public/node/api/bulk-operations/download-bulk-operation-results.js +48 -0
  39. package/dist/public/node/api/bulk-operations/download-bulk-operation-results.js.map +1 -0
  40. package/dist/public/node/api/bulk-operations/fetch.d.ts +31 -0
  41. package/dist/public/node/api/bulk-operations/fetch.js +42 -0
  42. package/dist/public/node/api/bulk-operations/fetch.js.map +1 -0
  43. package/dist/public/node/api/bulk-operations/format-bulk-operation-status.d.ts +45 -0
  44. package/dist/public/node/api/bulk-operations/format-bulk-operation-status.js +91 -0
  45. package/dist/public/node/api/bulk-operations/format-bulk-operation-status.js.map +1 -0
  46. package/dist/public/node/api/bulk-operations/helpers.d.ts +51 -0
  47. package/dist/public/node/api/bulk-operations/helpers.js +110 -0
  48. package/dist/public/node/api/bulk-operations/helpers.js.map +1 -0
  49. package/dist/public/node/api/bulk-operations/query.d.ts +22 -0
  50. package/dist/public/node/api/bulk-operations/query.js +40 -0
  51. package/dist/public/node/api/bulk-operations/query.js.map +1 -0
  52. package/dist/public/node/api/bulk-operations/run-mutation.d.ts +16 -0
  53. package/dist/public/node/api/bulk-operations/run-mutation.js +27 -0
  54. package/dist/public/node/api/bulk-operations/run-mutation.js.map +1 -0
  55. package/dist/public/node/api/bulk-operations/run-query.d.ts +15 -0
  56. package/dist/public/node/api/bulk-operations/run-query.js +19 -0
  57. package/dist/public/node/api/bulk-operations/run-query.js.map +1 -0
  58. package/dist/public/node/api/bulk-operations/stage-file.d.ts +13 -0
  59. package/dist/public/node/api/bulk-operations/stage-file.js +89 -0
  60. package/dist/public/node/api/bulk-operations/stage-file.js.map +1 -0
  61. package/dist/public/node/api/bulk-operations/watch-bulk-operation.d.ts +26 -0
  62. package/dist/public/node/api/bulk-operations/watch-bulk-operation.js +131 -0
  63. package/dist/public/node/api/bulk-operations/watch-bulk-operation.js.map +1 -0
  64. package/dist/public/node/api/bulk-operations.d.ts +18 -0
  65. package/dist/public/node/api/bulk-operations.js +19 -0
  66. package/dist/public/node/api/bulk-operations.js.map +1 -0
  67. package/dist/public/node/constants.d.ts +1 -0
  68. package/dist/public/node/constants.js +2 -0
  69. package/dist/public/node/constants.js.map +1 -0
  70. package/dist/public/node/context/fqdn.js +45 -4
  71. package/dist/public/node/context/fqdn.js.map +1 -1
  72. package/dist/public/node/graphiql/session.d.ts +50 -0
  73. package/dist/public/node/graphiql/session.js +92 -0
  74. package/dist/public/node/graphiql/session.js.map +1 -0
  75. package/dist/public/node/graphiql/templates/graphiql.js +1 -4
  76. package/dist/public/node/graphiql/templates/graphiql.js.map +1 -1
  77. package/dist/public/node/store-auth-session.d.ts +113 -0
  78. package/dist/public/node/store-auth-session.js +257 -0
  79. package/dist/public/node/store-auth-session.js.map +1 -0
  80. package/dist/public/node/ui.d.ts +2 -6
  81. package/dist/public/node/ui.js +2 -6
  82. package/dist/public/node/ui.js.map +1 -1
  83. package/dist/tsconfig.tsbuildinfo +1 -1
  84. package/package.json +3 -3
@@ -0,0 +1,89 @@
1
+ import { StagedUploadsCreate, } from '../../../../cli/api/graphql/bulk-operations/generated/staged-uploads-create.js';
2
+ import { adminRequestDoc } from '../admin.js';
3
+ import { fetch } from '../../http.js';
4
+ import { AbortError } from '../../error.js';
5
+ import { outputContent } from '../../output.js';
6
+ import { renderSingleTask } from '../../ui.js';
7
+ /**
8
+ * Uploads bulk mutation variables to a staged upload target and returns the staged upload key.
9
+ *
10
+ * @param options - The admin session and JSONL variables.
11
+ * @returns The staged upload key used as `stagedUploadPath` for the bulk mutation.
12
+ */
13
+ export async function stageFile(options) {
14
+ const { adminSession, variablesJsonl } = options;
15
+ const buffer = Buffer.from(variablesJsonl ?? '', 'utf-8');
16
+ const filename = 'bulk-variables.jsonl';
17
+ const size = buffer.length;
18
+ const response = await requestStagedUpload(adminSession, filename, size);
19
+ const target = validateStagedUploadResponse(response);
20
+ await uploadFileToStagedUrl(buffer, target.url, target.parameters, filename);
21
+ return target.stagedUploadKey;
22
+ }
23
+ async function requestStagedUpload(adminSession, filename, size) {
24
+ return adminRequestDoc({
25
+ query: StagedUploadsCreate,
26
+ session: adminSession,
27
+ variables: {
28
+ input: [
29
+ {
30
+ filename,
31
+ fileSize: size.toString(),
32
+ httpMethod: 'POST',
33
+ mimeType: 'text/jsonl',
34
+ resource: 'BULK_MUTATION_VARIABLES',
35
+ },
36
+ ],
37
+ },
38
+ });
39
+ }
40
+ function validateStagedUploadResponse(response) {
41
+ if (!response.stagedUploadsCreate) {
42
+ throw new AbortError('No response received from stagedUploadsCreate mutation');
43
+ }
44
+ if (response.stagedUploadsCreate.userErrors.length > 0) {
45
+ const errors = response.stagedUploadsCreate.userErrors
46
+ .map((error) => error.message)
47
+ .join(', ');
48
+ throw new AbortError(`Failed to create staged upload: ${errors}`);
49
+ }
50
+ const target = response.stagedUploadsCreate.stagedTargets?.[0];
51
+ if (!target) {
52
+ throw new AbortError('No staged upload target returned from Shopify');
53
+ }
54
+ if (!target.url || !target.resourceUrl) {
55
+ throw new AbortError('Invalid staged upload target: missing required URLs');
56
+ }
57
+ const stagedUploadKey = target.parameters.find((param) => param.name === 'key')?.value;
58
+ if (!stagedUploadKey) {
59
+ throw new AbortError('No key parameter found in staged upload target');
60
+ }
61
+ return {
62
+ url: target.url,
63
+ resourceUrl: target.resourceUrl,
64
+ parameters: target.parameters,
65
+ stagedUploadKey,
66
+ };
67
+ }
68
+ async function uploadFileToStagedUrl(fileContents, uploadUrl, parameters, filename) {
69
+ const form = new FormData();
70
+ for (const param of parameters) {
71
+ form.append(param.name, param.value);
72
+ }
73
+ form.append('file', new Blob([new Uint8Array(fileContents)], { type: 'text/jsonl' }), filename);
74
+ const uploadResponse = await renderSingleTask({
75
+ title: outputContent `Uploading bulk operation variables`,
76
+ task: async () => {
77
+ return fetch(uploadUrl, {
78
+ method: 'POST',
79
+ body: form,
80
+ }, 'slow-request');
81
+ },
82
+ renderOptions: { stdout: process.stderr },
83
+ });
84
+ if (!uploadResponse.ok) {
85
+ const errorText = await uploadResponse.text();
86
+ throw new AbortError(`Failed to upload file to staged URL: ${uploadResponse.statusText}\n${errorText}`);
87
+ }
88
+ }
89
+ //# sourceMappingURL=stage-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stage-file.js","sourceRoot":"","sources":["../../../../../src/public/node/api/bulk-operations/stage-file.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n StagedUploadsCreate,\n StagedUploadsCreateMutation,\n StagedUploadsCreateMutationVariables,\n} from '../../../../cli/api/graphql/bulk-operations/generated/staged-uploads-create.js'\nimport {adminRequestDoc} from '../admin.js'\nimport {AdminSession} from '../../session.js'\nimport {fetch} from '../../http.js'\nimport {AbortError} from '../../error.js'\nimport {outputContent} from '../../output.js'\nimport {renderSingleTask} from '../../ui.js'\n\ninterface StageFileOptions {\n adminSession: AdminSession\n variablesJsonl?: string\n}\n\n/**\n * Uploads bulk mutation variables to a staged upload target and returns the staged upload key.\n *\n * @param options - The admin session and JSONL variables.\n * @returns The staged upload key used as `stagedUploadPath` for the bulk mutation.\n */\nexport async function stageFile(options: StageFileOptions): Promise<string> {\n const {adminSession, variablesJsonl} = options\n\n const buffer = Buffer.from(variablesJsonl ?? '', 'utf-8')\n const filename = 'bulk-variables.jsonl'\n const size = buffer.length\n\n const response = await requestStagedUpload(adminSession, filename, size)\n const target = validateStagedUploadResponse(response)\n\n await uploadFileToStagedUrl(buffer, target.url, target.parameters, filename)\n\n return target.stagedUploadKey\n}\n\nasync function requestStagedUpload(\n adminSession: AdminSession,\n filename: string,\n size: number,\n): Promise<StagedUploadsCreateMutation> {\n return adminRequestDoc<StagedUploadsCreateMutation, StagedUploadsCreateMutationVariables>({\n query: StagedUploadsCreate,\n session: adminSession,\n variables: {\n input: [\n {\n filename,\n fileSize: size.toString(),\n httpMethod: 'POST',\n mimeType: 'text/jsonl',\n resource: 'BULK_MUTATION_VARIABLES',\n },\n ],\n },\n })\n}\n\nfunction validateStagedUploadResponse(response: StagedUploadsCreateMutation): {\n url: string\n resourceUrl: string\n parameters: {name: string; value: string}[]\n stagedUploadKey: string\n} {\n if (!response.stagedUploadsCreate) {\n throw new AbortError('No response received from stagedUploadsCreate mutation')\n }\n\n if (response.stagedUploadsCreate.userErrors.length > 0) {\n const errors = response.stagedUploadsCreate.userErrors\n .map((error: {field?: string[] | null; message: string}) => error.message)\n .join(', ')\n throw new AbortError(`Failed to create staged upload: ${errors}`)\n }\n\n const target = response.stagedUploadsCreate.stagedTargets?.[0]\n if (!target) {\n throw new AbortError('No staged upload target returned from Shopify')\n }\n\n if (!target.url || !target.resourceUrl) {\n throw new AbortError('Invalid staged upload target: missing required URLs')\n }\n\n const stagedUploadKey = target.parameters.find((param) => param.name === 'key')?.value\n if (!stagedUploadKey) {\n throw new AbortError('No key parameter found in staged upload target')\n }\n\n return {\n url: target.url,\n resourceUrl: target.resourceUrl,\n parameters: target.parameters,\n stagedUploadKey,\n }\n}\n\nasync function uploadFileToStagedUrl(\n fileContents: Buffer,\n uploadUrl: string,\n parameters: {name: string; value: string}[],\n filename: string,\n): Promise<void> {\n const form = new FormData()\n\n for (const param of parameters) {\n form.append(param.name, param.value)\n }\n\n form.append('file', new Blob([new Uint8Array(fileContents)], {type: 'text/jsonl'}), filename)\n\n const uploadResponse = await renderSingleTask({\n title: outputContent`Uploading bulk operation variables`,\n task: async () => {\n return fetch(\n uploadUrl,\n {\n method: 'POST',\n body: form,\n },\n 'slow-request',\n )\n },\n renderOptions: {stdout: process.stderr},\n })\n\n if (!uploadResponse.ok) {\n const errorText = await uploadResponse.text()\n throw new AbortError(`Failed to upload file to staged URL: ${uploadResponse.statusText}\\n${errorText}`)\n }\n}\n"]}
@@ -0,0 +1,26 @@
1
+ import { GetBulkOperationByIdQuery } from '../../../../cli/api/graphql/bulk-operations/generated/get-bulk-operation-by-id.js';
2
+ import { AdminSession } from '../../session.js';
3
+ import { AbortSignal } from '../../abort.js';
4
+ export declare const QUICK_WATCH_TIMEOUT_MS = 3000;
5
+ export declare const QUICK_WATCH_POLL_INTERVAL_MS = 300;
6
+ export type BulkOperation = NonNullable<GetBulkOperationByIdQuery['bulkOperation']>;
7
+ /**
8
+ * Polls a bulk operation briefly to surface its initial state, returning quickly so the caller can
9
+ * keep working while the operation runs in the background.
10
+ *
11
+ * @param adminSession - The admin session.
12
+ * @param operationId - The bulk operation ID to poll.
13
+ * @returns The latest known operation state.
14
+ */
15
+ export declare function shortBulkOperationPoll(adminSession: AdminSession, operationId: string): Promise<BulkOperation>;
16
+ /**
17
+ * Polls a bulk operation until it reaches a terminal state or is aborted, updating the rendered
18
+ * status as it progresses.
19
+ *
20
+ * @param adminSession - The admin session.
21
+ * @param operationId - The bulk operation ID to watch.
22
+ * @param abortSignal - Signal used to stop watching early.
23
+ * @param onAbort - Callback invoked when the user aborts.
24
+ * @returns The latest known operation state.
25
+ */
26
+ export declare function watchBulkOperation(adminSession: AdminSession, operationId: string, abortSignal: AbortSignal, onAbort: () => void): Promise<BulkOperation>;
@@ -0,0 +1,131 @@
1
+ import { formatBulkOperationStatus } from './format-bulk-operation-status.js';
2
+ import { BULK_OPERATIONS_MIN_API_VERSION } from './constants.js';
3
+ import { GetBulkOperationById, } from '../../../../cli/api/graphql/bulk-operations/generated/get-bulk-operation-by-id.js';
4
+ import { adminRequestDoc } from '../admin.js';
5
+ import { sleep } from '../../system.js';
6
+ import { outputContent } from '../../output.js';
7
+ import { renderSingleTask } from '../../ui.js';
8
+ const TERMINAL_STATUSES = ['COMPLETED', 'FAILED', 'CANCELED', 'EXPIRED'];
9
+ const INITIAL_POLL_INTERVAL_SECONDS = 1;
10
+ const REGULAR_POLL_INTERVAL_SECONDS = 5;
11
+ const INITIAL_POLL_COUNT = 10;
12
+ export const QUICK_WATCH_TIMEOUT_MS = 3000;
13
+ export const QUICK_WATCH_POLL_INTERVAL_MS = 300;
14
+ /**
15
+ * Polls a bulk operation briefly to surface its initial state, returning quickly so the caller can
16
+ * keep working while the operation runs in the background.
17
+ *
18
+ * @param adminSession - The admin session.
19
+ * @param operationId - The bulk operation ID to poll.
20
+ * @returns The latest known operation state.
21
+ */
22
+ export async function shortBulkOperationPoll(adminSession, operationId) {
23
+ return renderSingleTask({
24
+ title: outputContent `Starting bulk operation`,
25
+ task: async () => {
26
+ const startTime = Date.now();
27
+ const poller = pollBulkOperation({
28
+ adminSession,
29
+ operationId,
30
+ pollIntervalSeconds: QUICK_WATCH_POLL_INTERVAL_MS / 1000,
31
+ useAdaptivePolling: false,
32
+ });
33
+ let latestOperationState;
34
+ do {
35
+ // eslint-disable-next-line no-await-in-loop
36
+ const { value, done } = await poller.next();
37
+ latestOperationState = value;
38
+ if (done)
39
+ return latestOperationState;
40
+ } while (Date.now() - startTime < QUICK_WATCH_TIMEOUT_MS);
41
+ return latestOperationState;
42
+ },
43
+ renderOptions: { stdout: process.stderr },
44
+ });
45
+ }
46
+ /**
47
+ * Polls a bulk operation until it reaches a terminal state or is aborted, updating the rendered
48
+ * status as it progresses.
49
+ *
50
+ * @param adminSession - The admin session.
51
+ * @param operationId - The bulk operation ID to watch.
52
+ * @param abortSignal - Signal used to stop watching early.
53
+ * @param onAbort - Callback invoked when the user aborts.
54
+ * @returns The latest known operation state.
55
+ */
56
+ export async function watchBulkOperation(adminSession, operationId, abortSignal, onAbort) {
57
+ return renderSingleTask({
58
+ title: outputContent `Polling bulk operation`,
59
+ task: async (updateStatus) => {
60
+ const poller = pollBulkOperation({
61
+ adminSession,
62
+ operationId,
63
+ pollIntervalSeconds: REGULAR_POLL_INTERVAL_SECONDS,
64
+ initialPollIntervalSeconds: INITIAL_POLL_INTERVAL_SECONDS,
65
+ initialPollCount: INITIAL_POLL_COUNT,
66
+ useAdaptivePolling: true,
67
+ abortSignal,
68
+ });
69
+ while (true) {
70
+ // eslint-disable-next-line no-await-in-loop
71
+ const { value: latestOperationState, done } = await poller.next();
72
+ if (done) {
73
+ return latestOperationState;
74
+ }
75
+ else {
76
+ updateStatus(formatBulkOperationStatus(latestOperationState));
77
+ }
78
+ }
79
+ },
80
+ onAbort,
81
+ renderOptions: { stdout: process.stderr },
82
+ });
83
+ }
84
+ async function* pollBulkOperation({ adminSession, operationId, pollIntervalSeconds, useAdaptivePolling = false, initialPollIntervalSeconds = INITIAL_POLL_INTERVAL_SECONDS, initialPollCount = INITIAL_POLL_COUNT, abortSignal, }) {
85
+ let pollCount = 0;
86
+ while (true) {
87
+ // eslint-disable-next-line no-await-in-loop
88
+ const response = await fetchBulkOperation(adminSession, operationId);
89
+ if (!response.bulkOperation) {
90
+ throw new Error('bulk operation not found');
91
+ }
92
+ const latestOperationState = response.bulkOperation;
93
+ if (TERMINAL_STATUSES.includes(latestOperationState.status) || abortSignal?.aborted) {
94
+ return latestOperationState;
95
+ }
96
+ else {
97
+ yield latestOperationState;
98
+ }
99
+ pollCount++;
100
+ let pollInterval = pollIntervalSeconds;
101
+ if (useAdaptivePolling && pollCount <= initialPollCount) {
102
+ pollInterval = initialPollIntervalSeconds;
103
+ }
104
+ if (abortSignal) {
105
+ // Remove the abort listener after each race so the common case (sleep wins) doesn't leak a
106
+ // listener per poll, which would otherwise trigger MaxListenersExceededWarning on long watches.
107
+ let removeAbortListener;
108
+ const abortPromise = new Promise((resolve) => {
109
+ const handler = () => resolve();
110
+ abortSignal.addEventListener('abort', handler, { once: true });
111
+ removeAbortListener = () => abortSignal.removeEventListener('abort', handler);
112
+ });
113
+ // eslint-disable-next-line no-await-in-loop
114
+ await Promise.race([sleep(pollInterval), abortPromise]);
115
+ removeAbortListener?.();
116
+ }
117
+ else {
118
+ // eslint-disable-next-line no-await-in-loop
119
+ await sleep(pollInterval);
120
+ }
121
+ }
122
+ }
123
+ async function fetchBulkOperation(adminSession, operationId) {
124
+ return adminRequestDoc({
125
+ query: GetBulkOperationById,
126
+ session: adminSession,
127
+ variables: { id: operationId },
128
+ version: BULK_OPERATIONS_MIN_API_VERSION,
129
+ });
130
+ }
131
+ //# sourceMappingURL=watch-bulk-operation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch-bulk-operation.js","sourceRoot":"","sources":["../../../../../src/public/node/api/bulk-operations/watch-bulk-operation.ts"],"names":[],"mappings":"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","sourcesContent":["import {formatBulkOperationStatus} from './format-bulk-operation-status.js'\nimport {BULK_OPERATIONS_MIN_API_VERSION} from './constants.js'\nimport {\n GetBulkOperationById,\n GetBulkOperationByIdQuery,\n} from '../../../../cli/api/graphql/bulk-operations/generated/get-bulk-operation-by-id.js'\nimport {adminRequestDoc} from '../admin.js'\nimport {sleep} from '../../system.js'\nimport {AdminSession} from '../../session.js'\nimport {outputContent} from '../../output.js'\nimport {renderSingleTask} from '../../ui.js'\nimport {AbortSignal} from '../../abort.js'\n\nconst TERMINAL_STATUSES = ['COMPLETED', 'FAILED', 'CANCELED', 'EXPIRED']\nconst INITIAL_POLL_INTERVAL_SECONDS = 1\nconst REGULAR_POLL_INTERVAL_SECONDS = 5\nconst INITIAL_POLL_COUNT = 10\n\nexport const QUICK_WATCH_TIMEOUT_MS = 3000\nexport const QUICK_WATCH_POLL_INTERVAL_MS = 300\n\nexport type BulkOperation = NonNullable<GetBulkOperationByIdQuery['bulkOperation']>\n\n/**\n * Polls a bulk operation briefly to surface its initial state, returning quickly so the caller can\n * keep working while the operation runs in the background.\n *\n * @param adminSession - The admin session.\n * @param operationId - The bulk operation ID to poll.\n * @returns The latest known operation state.\n */\nexport async function shortBulkOperationPoll(adminSession: AdminSession, operationId: string): Promise<BulkOperation> {\n return renderSingleTask<BulkOperation>({\n title: outputContent`Starting bulk operation`,\n task: async () => {\n const startTime = Date.now()\n const poller = pollBulkOperation({\n adminSession,\n operationId,\n pollIntervalSeconds: QUICK_WATCH_POLL_INTERVAL_MS / 1000,\n useAdaptivePolling: false,\n })\n\n let latestOperationState: BulkOperation | undefined\n\n do {\n // eslint-disable-next-line no-await-in-loop\n const {value, done} = await poller.next()\n latestOperationState = value\n if (done) return latestOperationState\n } while (Date.now() - startTime < QUICK_WATCH_TIMEOUT_MS)\n\n return latestOperationState\n },\n renderOptions: {stdout: process.stderr},\n })\n}\n\n/**\n * Polls a bulk operation until it reaches a terminal state or is aborted, updating the rendered\n * status as it progresses.\n *\n * @param adminSession - The admin session.\n * @param operationId - The bulk operation ID to watch.\n * @param abortSignal - Signal used to stop watching early.\n * @param onAbort - Callback invoked when the user aborts.\n * @returns The latest known operation state.\n */\nexport async function watchBulkOperation(\n adminSession: AdminSession,\n operationId: string,\n abortSignal: AbortSignal,\n onAbort: () => void,\n): Promise<BulkOperation> {\n return renderSingleTask<BulkOperation>({\n title: outputContent`Polling bulk operation`,\n task: async (updateStatus) => {\n const poller = pollBulkOperation({\n adminSession,\n operationId,\n pollIntervalSeconds: REGULAR_POLL_INTERVAL_SECONDS,\n initialPollIntervalSeconds: INITIAL_POLL_INTERVAL_SECONDS,\n initialPollCount: INITIAL_POLL_COUNT,\n useAdaptivePolling: true,\n abortSignal,\n })\n\n while (true) {\n // eslint-disable-next-line no-await-in-loop\n const {value: latestOperationState, done} = await poller.next()\n if (done) {\n return latestOperationState\n } else {\n updateStatus(formatBulkOperationStatus(latestOperationState))\n }\n }\n },\n onAbort,\n renderOptions: {stdout: process.stderr},\n })\n}\n\ninterface PollBulkOperationOptions {\n adminSession: AdminSession\n operationId: string\n pollIntervalSeconds: number\n /** When true, polls faster initially then slows to pollIntervalSeconds. */\n useAdaptivePolling?: boolean\n /** Poll interval in seconds for initial fast polls (default: 1). */\n initialPollIntervalSeconds?: number\n /** Number of fast polls before switching to regular interval (default: 10). */\n initialPollCount?: number\n abortSignal?: AbortSignal\n}\n\nasync function* pollBulkOperation({\n adminSession,\n operationId,\n pollIntervalSeconds,\n useAdaptivePolling = false,\n initialPollIntervalSeconds = INITIAL_POLL_INTERVAL_SECONDS,\n initialPollCount = INITIAL_POLL_COUNT,\n abortSignal,\n}: PollBulkOperationOptions): AsyncGenerator<BulkOperation, BulkOperation> {\n let pollCount = 0\n\n while (true) {\n // eslint-disable-next-line no-await-in-loop\n const response = await fetchBulkOperation(adminSession, operationId)\n\n if (!response.bulkOperation) {\n throw new Error('bulk operation not found')\n }\n\n const latestOperationState = response.bulkOperation\n\n if (TERMINAL_STATUSES.includes(latestOperationState.status) || abortSignal?.aborted) {\n return latestOperationState\n } else {\n yield latestOperationState\n }\n\n pollCount++\n let pollInterval = pollIntervalSeconds\n if (useAdaptivePolling && pollCount <= initialPollCount) {\n pollInterval = initialPollIntervalSeconds\n }\n\n if (abortSignal) {\n // Remove the abort listener after each race so the common case (sleep wins) doesn't leak a\n // listener per poll, which would otherwise trigger MaxListenersExceededWarning on long watches.\n let removeAbortListener: (() => void) | undefined\n const abortPromise = new Promise<void>((resolve) => {\n const handler = () => resolve()\n abortSignal.addEventListener('abort', handler, {once: true})\n removeAbortListener = () => abortSignal.removeEventListener('abort', handler)\n })\n // eslint-disable-next-line no-await-in-loop\n await Promise.race([sleep(pollInterval), abortPromise])\n removeAbortListener?.()\n } else {\n // eslint-disable-next-line no-await-in-loop\n await sleep(pollInterval)\n }\n }\n}\n\nasync function fetchBulkOperation(adminSession: AdminSession, operationId: string): Promise<GetBulkOperationByIdQuery> {\n return adminRequestDoc<GetBulkOperationByIdQuery, {id: string}>({\n query: GetBulkOperationById,\n session: adminSession,\n variables: {id: operationId},\n version: BULK_OPERATIONS_MIN_API_VERSION,\n })\n}\n"]}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Shared primitives for running Admin API bulk operations.
3
+ *
4
+ * These are auth-agnostic: callers provide an `AdminSession` (however they obtained it —
5
+ * app client credentials, a stored store session, etc.) and these helpers handle starting,
6
+ * watching, fetching, cancelling, downloading, and formatting bulk operations.
7
+ */
8
+ export { BULK_OPERATIONS_MIN_API_VERSION } from './bulk-operations/constants.js';
9
+ export { normalizeBulkOperationId, extractBulkOperationId, isMutation, validateSingleOperation, resolveApiVersion, } from './bulk-operations/helpers.js';
10
+ export { resolveBulkOperationQuery } from './bulk-operations/query.js';
11
+ export { runBulkOperationQuery } from './bulk-operations/run-query.js';
12
+ export { runBulkOperationMutation } from './bulk-operations/run-mutation.js';
13
+ export { stageFile } from './bulk-operations/stage-file.js';
14
+ export { fetchBulkOperationById, fetchRecentBulkOperations } from './bulk-operations/fetch.js';
15
+ export { cancelBulkOperationRequest } from './bulk-operations/cancel.js';
16
+ export { watchBulkOperation, shortBulkOperationPoll, QUICK_WATCH_TIMEOUT_MS, QUICK_WATCH_POLL_INTERVAL_MS, type BulkOperation, } from './bulk-operations/watch-bulk-operation.js';
17
+ export { downloadBulkOperationResults, resultsContainUserErrors, } from './bulk-operations/download-bulk-operation-results.js';
18
+ export { formatBulkOperationStatus, renderBulkOperationUserErrors, formatBulkOperationCancellationResult, type BulkOperationCancellationResult, } from './bulk-operations/format-bulk-operation-status.js';
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Shared primitives for running Admin API bulk operations.
3
+ *
4
+ * These are auth-agnostic: callers provide an `AdminSession` (however they obtained it —
5
+ * app client credentials, a stored store session, etc.) and these helpers handle starting,
6
+ * watching, fetching, cancelling, downloading, and formatting bulk operations.
7
+ */
8
+ export { BULK_OPERATIONS_MIN_API_VERSION } from './bulk-operations/constants.js';
9
+ export { normalizeBulkOperationId, extractBulkOperationId, isMutation, validateSingleOperation, resolveApiVersion, } from './bulk-operations/helpers.js';
10
+ export { resolveBulkOperationQuery } from './bulk-operations/query.js';
11
+ export { runBulkOperationQuery } from './bulk-operations/run-query.js';
12
+ export { runBulkOperationMutation } from './bulk-operations/run-mutation.js';
13
+ export { stageFile } from './bulk-operations/stage-file.js';
14
+ export { fetchBulkOperationById, fetchRecentBulkOperations } from './bulk-operations/fetch.js';
15
+ export { cancelBulkOperationRequest } from './bulk-operations/cancel.js';
16
+ export { watchBulkOperation, shortBulkOperationPoll, QUICK_WATCH_TIMEOUT_MS, QUICK_WATCH_POLL_INTERVAL_MS, } from './bulk-operations/watch-bulk-operation.js';
17
+ export { downloadBulkOperationResults, resultsContainUserErrors, } from './bulk-operations/download-bulk-operation-results.js';
18
+ export { formatBulkOperationStatus, renderBulkOperationUserErrors, formatBulkOperationCancellationResult, } from './bulk-operations/format-bulk-operation-status.js';
19
+ //# sourceMappingURL=bulk-operations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bulk-operations.js","sourceRoot":"","sources":["../../../../src/public/node/api/bulk-operations.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAC,+BAA+B,EAAC,MAAM,gCAAgC,CAAA;AAC9E,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,UAAU,EACV,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAAC,yBAAyB,EAAC,MAAM,4BAA4B,CAAA;AACpE,OAAO,EAAC,qBAAqB,EAAC,MAAM,gCAAgC,CAAA;AACpE,OAAO,EAAC,wBAAwB,EAAC,MAAM,mCAAmC,CAAA;AAC1E,OAAO,EAAC,SAAS,EAAC,MAAM,iCAAiC,CAAA;AACzD,OAAO,EAAC,sBAAsB,EAAE,yBAAyB,EAAC,MAAM,4BAA4B,CAAA;AAC5F,OAAO,EAAC,0BAA0B,EAAC,MAAM,6BAA6B,CAAA;AACtE,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,4BAA4B,GAE7B,MAAM,2CAA2C,CAAA;AAClD,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,GACzB,MAAM,sDAAsD,CAAA;AAC7D,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,qCAAqC,GAEtC,MAAM,mDAAmD,CAAA","sourcesContent":["/**\n * Shared primitives for running Admin API bulk operations.\n *\n * These are auth-agnostic: callers provide an `AdminSession` (however they obtained it —\n * app client credentials, a stored store session, etc.) and these helpers handle starting,\n * watching, fetching, cancelling, downloading, and formatting bulk operations.\n */\nexport {BULK_OPERATIONS_MIN_API_VERSION} from './bulk-operations/constants.js'\nexport {\n normalizeBulkOperationId,\n extractBulkOperationId,\n isMutation,\n validateSingleOperation,\n resolveApiVersion,\n} from './bulk-operations/helpers.js'\nexport {resolveBulkOperationQuery} from './bulk-operations/query.js'\nexport {runBulkOperationQuery} from './bulk-operations/run-query.js'\nexport {runBulkOperationMutation} from './bulk-operations/run-mutation.js'\nexport {stageFile} from './bulk-operations/stage-file.js'\nexport {fetchBulkOperationById, fetchRecentBulkOperations} from './bulk-operations/fetch.js'\nexport {cancelBulkOperationRequest} from './bulk-operations/cancel.js'\nexport {\n watchBulkOperation,\n shortBulkOperationPoll,\n QUICK_WATCH_TIMEOUT_MS,\n QUICK_WATCH_POLL_INTERVAL_MS,\n type BulkOperation,\n} from './bulk-operations/watch-bulk-operation.js'\nexport {\n downloadBulkOperationResults,\n resultsContainUserErrors,\n} from './bulk-operations/download-bulk-operation-results.js'\nexport {\n formatBulkOperationStatus,\n renderBulkOperationUserErrors,\n formatBulkOperationCancellationResult,\n type BulkOperationCancellationResult,\n} from './bulk-operations/format-bulk-operation-status.js'\n"]}
@@ -0,0 +1 @@
1
+ export declare const STORE_AUTH_APP_CLIENT_ID = "7e9cb568cfd431c538f36d1ad3f2b4f6";
@@ -0,0 +1,2 @@
1
+ export const STORE_AUTH_APP_CLIENT_ID = '7e9cb568cfd431c538f36d1ad3f2b4f6';
2
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/public/node/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,wBAAwB,GAAG,kCAAkC,CAAA","sourcesContent":["export const STORE_AUTH_APP_CLIENT_ID = '7e9cb568cfd431c538f36d1ad3f2b4f6'\n"]}
@@ -116,10 +116,8 @@ export async function identityFqdn() {
116
116
  * @returns Normalized store name.
117
117
  */
118
118
  export function normalizeStoreFqdn(store) {
119
- const storeFqdn = store
120
- .replace(/^https?:\/\//, '')
121
- .replace(/\/$/, '')
122
- .replace(/\/admin$/, '');
119
+ const storeFqdn = parseStoreFqdn(store);
120
+ assertValidStoreFqdn(storeFqdn, store);
123
121
  const addDomain = (storeFqdn) => {
124
122
  switch (serviceEnvironment()) {
125
123
  case 'local':
@@ -131,6 +129,49 @@ export function normalizeStoreFqdn(store) {
131
129
  const containDomain = (storeFqdn) => storeFqdn.endsWith('.myshopify.com') || storeFqdn.endsWith('.myshopify.io') || storeFqdn.endsWith('.shop.dev');
132
130
  return containDomain(storeFqdn) ? storeFqdn : addDomain(storeFqdn);
133
131
  }
132
+ const invalidStoreFqdnTryMessage = 'Provide a store handle or Shopify store domain, for example "example" or "example.myshopify.com". ' +
133
+ 'Store URLs may only include the optional /admin path.';
134
+ function parseStoreFqdn(store) {
135
+ const trimmedStore = store.trim();
136
+ if (trimmedStore.startsWith('/')) {
137
+ throw new AbortError(`Invalid store value: ${store}`, invalidStoreFqdnTryMessage);
138
+ }
139
+ const storeUrl = parseSupportedStoreUrl(/^https?:\/\//i.test(trimmedStore) ? trimmedStore : `https://${trimmedStore}`, store);
140
+ return storeUrl.hostname;
141
+ }
142
+ function parseSupportedStoreUrl(store, originalStore) {
143
+ let storeUrl;
144
+ try {
145
+ storeUrl = new URL(store);
146
+ }
147
+ catch {
148
+ throw new AbortError(`Invalid store value: ${originalStore}`, invalidStoreFqdnTryMessage);
149
+ }
150
+ const supportedPath = storeUrl.pathname === '/' || storeUrl.pathname === '/admin' || storeUrl.pathname === '/admin/';
151
+ if (!storeUrl.hostname ||
152
+ storeUrl.username ||
153
+ storeUrl.password ||
154
+ storeUrl.port ||
155
+ storeUrl.search ||
156
+ storeUrl.hash ||
157
+ !supportedPath) {
158
+ throw new AbortError(`Invalid store value: ${originalStore}`, invalidStoreFqdnTryMessage);
159
+ }
160
+ return storeUrl;
161
+ }
162
+ function assertValidStoreFqdn(storeFqdn, store) {
163
+ if (storeFqdn.length === 0 || storeFqdn.length > 253) {
164
+ throw new AbortError(`Invalid store value: ${store}`, invalidStoreFqdnTryMessage);
165
+ }
166
+ const domainLabels = storeFqdn.split('.');
167
+ const validStoreDomainLabel = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;
168
+ const validDomain = domainLabels.every((label) => {
169
+ return label.length > 0 && label.length <= 63 && validStoreDomainLabel.test(label);
170
+ });
171
+ if (!validDomain) {
172
+ throw new AbortError(`Invalid store value: ${store}`, invalidStoreFqdnTryMessage);
173
+ }
174
+ }
134
175
  /**
135
176
  * Convert a store FQDN to the admin URL pattern for local development.
136
177
  * In local mode, transforms \{store\}.my.shop.dev to admin.shop.dev/store/\{store\}.
@@ -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('.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"]}
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 = parseStoreFqdn(store)\n\n assertValidStoreFqdn(storeFqdn, store)\n\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\nconst invalidStoreFqdnTryMessage =\n 'Provide a store handle or Shopify store domain, for example \"example\" or \"example.myshopify.com\". ' +\n 'Store URLs may only include the optional /admin path.'\n\nfunction parseStoreFqdn(store: string): string {\n const trimmedStore = store.trim()\n\n if (trimmedStore.startsWith('/')) {\n throw new AbortError(`Invalid store value: ${store}`, invalidStoreFqdnTryMessage)\n }\n\n const storeUrl = parseSupportedStoreUrl(\n /^https?:\\/\\//i.test(trimmedStore) ? trimmedStore : `https://${trimmedStore}`,\n store,\n )\n return storeUrl.hostname\n}\n\nfunction parseSupportedStoreUrl(store: string, originalStore: string): URL {\n let storeUrl: URL\n try {\n storeUrl = new URL(store)\n } catch {\n throw new AbortError(`Invalid store value: ${originalStore}`, invalidStoreFqdnTryMessage)\n }\n\n const supportedPath = storeUrl.pathname === '/' || storeUrl.pathname === '/admin' || storeUrl.pathname === '/admin/'\n if (\n !storeUrl.hostname ||\n storeUrl.username ||\n storeUrl.password ||\n storeUrl.port ||\n storeUrl.search ||\n storeUrl.hash ||\n !supportedPath\n ) {\n throw new AbortError(`Invalid store value: ${originalStore}`, invalidStoreFqdnTryMessage)\n }\n\n return storeUrl\n}\n\nfunction assertValidStoreFqdn(storeFqdn: string, store: string) {\n if (storeFqdn.length === 0 || storeFqdn.length > 253) {\n throw new AbortError(`Invalid store value: ${store}`, invalidStoreFqdnTryMessage)\n }\n\n const domainLabels = storeFqdn.split('.')\n const validStoreDomainLabel = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i\n const validDomain = domainLabels.every((label) => {\n return label.length > 0 && label.length <= 63 && validStoreDomainLabel.test(label)\n })\n\n if (!validDomain) {\n throw new AbortError(`Invalid store value: ${store}`, invalidStoreFqdnTryMessage)\n }\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"]}
@@ -0,0 +1,50 @@
1
+ import { GraphiQLAppContext, TokenProvider } from './server.js';
2
+ import { AbortSignal } from '../abort.js';
3
+ import { OutputMessage } from '../output.js';
4
+ import { Writable } from 'stream';
5
+ export interface BuildGraphiQLUrlOptions {
6
+ port: number;
7
+ key: string;
8
+ query?: string;
9
+ variables?: string;
10
+ apiVersion?: string;
11
+ }
12
+ export interface RunGraphiQLSessionOptions {
13
+ storeFqdn: string;
14
+ tokenProvider: TokenProvider;
15
+ key: string;
16
+ port?: number;
17
+ query?: string;
18
+ variables?: string;
19
+ apiVersion?: string;
20
+ appContext?: GraphiQLAppContext;
21
+ protectMutations?: boolean;
22
+ additionalReadyMessages?: OutputMessage[];
23
+ abortSignal?: AbortSignal;
24
+ stdout?: Writable;
25
+ }
26
+ /**
27
+ * Generates a random key suitable for authenticating a single local GraphiQL session.
28
+ *
29
+ * @returns A 64-character hex string.
30
+ */
31
+ export declare function generateRandomGraphiQLKey(): string;
32
+ /**
33
+ * Builds the browser URL for a local GraphiQL session.
34
+ *
35
+ * @param options - The local port, key, and optional query string fields to prefill.
36
+ * @returns The fully-qualified local GraphiQL URL.
37
+ */
38
+ export declare function buildGraphiQLUrl(options: BuildGraphiQLUrlOptions): string;
39
+ /**
40
+ * Runs a local GraphiQL session until the user interrupts it or the supplied abort signal fires.
41
+ *
42
+ * @param options - The server, authentication, URL prefill, and output options for the session.
43
+ */
44
+ export declare function runGraphiQLSession(options: RunGraphiQLSessionOptions): Promise<void>;
45
+ /**
46
+ * Resolves when the process receives SIGINT or when the supplied abort signal fires.
47
+ *
48
+ * @param externalSignal - Optional signal used by tests or callers to stop the session.
49
+ */
50
+ export declare function waitForGraphiQLAbort(externalSignal?: AbortSignal): Promise<void>;
@@ -0,0 +1,92 @@
1
+ import { setupGraphiQLServer } from './server.js';
2
+ import { AbortController } from '../abort.js';
3
+ import { outputContent, outputInfo, outputToken, outputWarn } from '../output.js';
4
+ import { openURL } from '../system.js';
5
+ import { getAvailableTCPPort } from '../tcp.js';
6
+ import { randomBytes } from 'crypto';
7
+ /**
8
+ * Generates a random key suitable for authenticating a single local GraphiQL session.
9
+ *
10
+ * @returns A 64-character hex string.
11
+ */
12
+ export function generateRandomGraphiQLKey() {
13
+ return randomBytes(32).toString('hex');
14
+ }
15
+ /**
16
+ * Builds the browser URL for a local GraphiQL session.
17
+ *
18
+ * @param options - The local port, key, and optional query string fields to prefill.
19
+ * @returns The fully-qualified local GraphiQL URL.
20
+ */
21
+ export function buildGraphiQLUrl(options) {
22
+ const url = new URL(`http://localhost:${options.port}/graphiql`);
23
+ url.searchParams.set('key', options.key);
24
+ if (options.query)
25
+ url.searchParams.set('query', options.query);
26
+ if (options.variables)
27
+ url.searchParams.set('variables', options.variables);
28
+ if (options.apiVersion)
29
+ url.searchParams.set('api_version', options.apiVersion);
30
+ return url.toString();
31
+ }
32
+ /**
33
+ * Runs a local GraphiQL session until the user interrupts it or the supplied abort signal fires.
34
+ *
35
+ * @param options - The server, authentication, URL prefill, and output options for the session.
36
+ */
37
+ export async function runGraphiQLSession(options) {
38
+ const port = await getAvailableTCPPort(options.port);
39
+ const server = setupGraphiQLServer({
40
+ stdout: options.stdout ?? process.stdout,
41
+ port,
42
+ storeFqdn: options.storeFqdn,
43
+ tokenProvider: options.tokenProvider,
44
+ key: options.key,
45
+ appContext: options.appContext,
46
+ protectMutations: options.protectMutations,
47
+ });
48
+ const url = buildGraphiQLUrl({
49
+ port,
50
+ key: options.key,
51
+ query: options.query,
52
+ variables: options.variables,
53
+ apiVersion: options.apiVersion,
54
+ });
55
+ try {
56
+ outputInfo(outputContent `GraphiQL is running at ${outputToken.link(url)}`);
57
+ options.additionalReadyMessages?.forEach((message) => outputInfo(message));
58
+ outputInfo('Press Ctrl+C to stop.');
59
+ const opened = await openURL(url);
60
+ if (!opened) {
61
+ outputWarn('Browser did not open automatically. Open the URL above manually.');
62
+ }
63
+ await waitForGraphiQLAbort(options.abortSignal);
64
+ }
65
+ finally {
66
+ server.close();
67
+ }
68
+ }
69
+ /**
70
+ * Resolves when the process receives SIGINT or when the supplied abort signal fires.
71
+ *
72
+ * @param externalSignal - Optional signal used by tests or callers to stop the session.
73
+ */
74
+ export async function waitForGraphiQLAbort(externalSignal) {
75
+ const controller = new AbortController();
76
+ const onSigint = () => controller.abort();
77
+ process.once('SIGINT', onSigint);
78
+ try {
79
+ await new Promise((resolve) => {
80
+ if (controller.signal.aborted || externalSignal?.aborted) {
81
+ resolve();
82
+ return;
83
+ }
84
+ controller.signal.addEventListener('abort', () => resolve(), { once: true });
85
+ externalSignal?.addEventListener('abort', () => controller.abort(), { once: true });
86
+ });
87
+ }
88
+ finally {
89
+ process.removeListener('SIGINT', onSigint);
90
+ }
91
+ }
92
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../../src/public/node/graphiql/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,mBAAmB,EAAgB,MAAM,aAAa,CAAA;AAClF,OAAO,EAAC,eAAe,EAAc,MAAM,aAAa,CAAA;AACxD,OAAO,EAAgB,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAC,MAAM,cAAc,CAAA;AAC9F,OAAO,EAAC,OAAO,EAAC,MAAM,cAAc,CAAA;AACpC,OAAO,EAAC,mBAAmB,EAAC,MAAM,WAAW,CAAA;AAC7C,OAAO,EAAC,WAAW,EAAC,MAAM,QAAQ,CAAA;AA0BlC;;;;GAIG;AACH,MAAM,UAAU,yBAAyB;IACvC,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;AACxC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAgC;IAC/D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,oBAAoB,OAAO,CAAC,IAAI,WAAW,CAAC,CAAA;IAChE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAA;IACxC,IAAI,OAAO,CAAC,KAAK;QAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;IAC/D,IAAI,OAAO,CAAC,SAAS;QAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;IAC3E,IAAI,OAAO,CAAC,UAAU;QAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,aAAa,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;IAC/E,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAA;AACvB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OAAkC;IACzE,MAAM,IAAI,GAAG,MAAM,mBAAmB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IACpD,MAAM,MAAM,GAAG,mBAAmB,CAAC;QACjC,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM;QACxC,IAAI;QACJ,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;KAC3C,CAAC,CAAA;IAEF,MAAM,GAAG,GAAG,gBAAgB,CAAC;QAC3B,IAAI;QACJ,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,UAAU,EAAE,OAAO,CAAC,UAAU;KAC/B,CAAC,CAAA;IAEF,IAAI,CAAC;QACH,UAAU,CAAC,aAAa,CAAA,0BAA0B,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QAC1E,OAAO,CAAC,uBAAuB,EAAE,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAA;QAC1E,UAAU,CAAC,uBAAuB,CAAC,CAAA;QAEnC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAA;QACjC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,UAAU,CAAC,kEAAkE,CAAC,CAAA;QAChF,CAAC;QAED,MAAM,oBAAoB,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;IACjD,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,KAAK,EAAE,CAAA;IAChB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,cAA4B;IACrE,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IAExC,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,CAAA;IACzC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IAEhC,IAAI,CAAC;QACH,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAClC,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,IAAI,cAAc,EAAE,OAAO,EAAE,CAAC;gBACzD,OAAO,EAAE,CAAA;gBACT,OAAM;YACR,CAAC;YACD,UAAU,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,EAAC,IAAI,EAAE,IAAI,EAAC,CAAC,CAAA;YAC1E,cAAc,EAAE,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,EAAC,IAAI,EAAE,IAAI,EAAC,CAAC,CAAA;QACnF,CAAC,CAAC,CAAA;IACJ,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IAC5C,CAAC;AACH,CAAC","sourcesContent":["import {GraphiQLAppContext, setupGraphiQLServer, TokenProvider} from './server.js'\nimport {AbortController, AbortSignal} from '../abort.js'\nimport {OutputMessage, outputContent, outputInfo, outputToken, outputWarn} from '../output.js'\nimport {openURL} from '../system.js'\nimport {getAvailableTCPPort} from '../tcp.js'\nimport {randomBytes} from 'crypto'\nimport {Writable} from 'stream'\n\nexport interface BuildGraphiQLUrlOptions {\n port: number\n key: string\n query?: string\n variables?: string\n apiVersion?: string\n}\n\nexport interface RunGraphiQLSessionOptions {\n storeFqdn: string\n tokenProvider: TokenProvider\n key: string\n port?: number\n query?: string\n variables?: string\n apiVersion?: string\n appContext?: GraphiQLAppContext\n protectMutations?: boolean\n additionalReadyMessages?: OutputMessage[]\n abortSignal?: AbortSignal\n stdout?: Writable\n}\n\n/**\n * Generates a random key suitable for authenticating a single local GraphiQL session.\n *\n * @returns A 64-character hex string.\n */\nexport function generateRandomGraphiQLKey(): string {\n return randomBytes(32).toString('hex')\n}\n\n/**\n * Builds the browser URL for a local GraphiQL session.\n *\n * @param options - The local port, key, and optional query string fields to prefill.\n * @returns The fully-qualified local GraphiQL URL.\n */\nexport function buildGraphiQLUrl(options: BuildGraphiQLUrlOptions): string {\n const url = new URL(`http://localhost:${options.port}/graphiql`)\n url.searchParams.set('key', options.key)\n if (options.query) url.searchParams.set('query', options.query)\n if (options.variables) url.searchParams.set('variables', options.variables)\n if (options.apiVersion) url.searchParams.set('api_version', options.apiVersion)\n return url.toString()\n}\n\n/**\n * Runs a local GraphiQL session until the user interrupts it or the supplied abort signal fires.\n *\n * @param options - The server, authentication, URL prefill, and output options for the session.\n */\nexport async function runGraphiQLSession(options: RunGraphiQLSessionOptions): Promise<void> {\n const port = await getAvailableTCPPort(options.port)\n const server = setupGraphiQLServer({\n stdout: options.stdout ?? process.stdout,\n port,\n storeFqdn: options.storeFqdn,\n tokenProvider: options.tokenProvider,\n key: options.key,\n appContext: options.appContext,\n protectMutations: options.protectMutations,\n })\n\n const url = buildGraphiQLUrl({\n port,\n key: options.key,\n query: options.query,\n variables: options.variables,\n apiVersion: options.apiVersion,\n })\n\n try {\n outputInfo(outputContent`GraphiQL is running at ${outputToken.link(url)}`)\n options.additionalReadyMessages?.forEach((message) => outputInfo(message))\n outputInfo('Press Ctrl+C to stop.')\n\n const opened = await openURL(url)\n if (!opened) {\n outputWarn('Browser did not open automatically. Open the URL above manually.')\n }\n\n await waitForGraphiQLAbort(options.abortSignal)\n } finally {\n server.close()\n }\n}\n\n/**\n * Resolves when the process receives SIGINT or when the supplied abort signal fires.\n *\n * @param externalSignal - Optional signal used by tests or callers to stop the session.\n */\nexport async function waitForGraphiQLAbort(externalSignal?: AbortSignal): Promise<void> {\n const controller = new AbortController()\n\n const onSigint = () => controller.abort()\n process.once('SIGINT', onSigint)\n\n try {\n await new Promise<void>((resolve) => {\n if (controller.signal.aborted || externalSignal?.aborted) {\n resolve()\n return\n }\n controller.signal.addEventListener('abort', () => resolve(), {once: true})\n externalSignal?.addEventListener('abort', () => controller.abort(), {once: true})\n })\n } finally {\n process.removeListener('SIGINT', onSigint)\n }\n}\n"]}
@@ -184,10 +184,7 @@ export function graphiqlTemplate({ apiVersion, apiVersions, appName, appUrl, key
184
184
  React.createElement(Text, { as: "span", tone: "subdued" }, scopesNoteText({ hasAppContext, protectMutations }))))),
185
185
  React.createElement("div", { id: "top-error-bar" },
186
186
  React.createElement(Banner, { tone: "critical", onDismiss: () => { }, icon: DisabledIcon },
187
- React.createElement("p", null,
188
- "The server has been stopped. Restart ",
189
- React.createElement("code", null, "dev"),
190
- " from the CLI."))))))))}
187
+ React.createElement("p", null, "The server has been stopped. Restart it from the CLI."))))))))}
191
188
  <div id="graphiql-explorer">Loading...</div>
192
189
  </div>
193
190
  <script