@shopify/cli-kit 0.0.0-snapshot-20261027084255 → 0.0.0-snapshot-20261027112754

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 (49) hide show
  1. package/dist/private/node/api/graphql.js +0 -4
  2. package/dist/private/node/api/graphql.js.map +1 -1
  3. package/dist/private/node/api.d.ts +0 -2
  4. package/dist/private/node/api.js +22 -49
  5. package/dist/private/node/api.js.map +1 -1
  6. package/dist/private/node/command-json-schema.js +0 -1
  7. package/dist/private/node/command-json-schema.js.map +1 -1
  8. package/dist/private/node/output.test.js +4 -4
  9. package/dist/private/node/output.test.js.map +1 -1
  10. package/dist/private/node/ui/components/ConcurrentOutput.d.ts +7 -2
  11. package/dist/private/node/ui/components/ConcurrentOutput.js +8 -4
  12. package/dist/private/node/ui/components/ConcurrentOutput.js.map +1 -1
  13. package/dist/private/node/ui/components/LoadingBar.js +15 -9
  14. package/dist/private/node/ui/components/LoadingBar.js.map +1 -1
  15. package/dist/private/node/ui/components/LoadingBar.test.js +107 -45
  16. package/dist/private/node/ui/components/LoadingBar.test.js.map +1 -1
  17. package/dist/private/node/ui/components/TextAnimation.d.ts +10 -0
  18. package/dist/private/node/ui/components/TextAnimation.js +56 -0
  19. package/dist/private/node/ui/components/TextAnimation.js.map +1 -0
  20. package/dist/private/node/ui/components/TextAnimation.test.d.ts +1 -0
  21. package/dist/private/node/ui/components/TextAnimation.test.js +73 -0
  22. package/dist/private/node/ui/components/TextAnimation.test.js.map +1 -0
  23. package/dist/public/common/version.d.ts +1 -1
  24. package/dist/public/common/version.js +1 -1
  25. package/dist/public/common/version.js.map +1 -1
  26. package/dist/public/node/api/graphql.js +4 -29
  27. package/dist/public/node/api/graphql.js.map +1 -1
  28. package/dist/public/node/tree-kill.js +2 -12
  29. package/dist/public/node/tree-kill.js.map +1 -1
  30. package/dist/public/node/ui/components.d.ts +0 -1
  31. package/dist/public/node/ui/components.js +0 -1
  32. package/dist/public/node/ui/components.js.map +1 -1
  33. package/dist/public/node/ui.d.ts +2 -2
  34. package/dist/public/node/ui.js +2 -2
  35. package/dist/public/node/ui.js.map +1 -1
  36. package/dist/tsconfig.tsbuildinfo +1 -1
  37. package/package.json +3 -3
  38. package/dist/private/node/dev-instance-output.d.ts +0 -47
  39. package/dist/private/node/dev-instance-output.js +0 -143
  40. package/dist/private/node/dev-instance-output.js.map +0 -1
  41. package/dist/private/node/output-context.d.ts +0 -8
  42. package/dist/private/node/output-context.js +0 -10
  43. package/dist/private/node/output-context.js.map +0 -1
  44. package/dist/private/node/ui/components/LoadingIndicator.d.ts +0 -7
  45. package/dist/private/node/ui/components/LoadingIndicator.js +0 -22
  46. package/dist/private/node/ui/components/LoadingIndicator.js.map +0 -1
  47. package/dist/public/node/dev-instance.d.ts +0 -138
  48. package/dist/public/node/dev-instance.js +0 -365
  49. package/dist/public/node/dev-instance.js.map +0 -1
@@ -114,10 +114,6 @@ export function errorHandler(api) {
114
114
  else if (extractedMessages && status >= 500) {
115
115
  errorMessage = `The ${api} GraphQL API responded with HTTP status ${status}: ${extractedMessages}`;
116
116
  }
117
- else if (error.response.errors === undefined) {
118
- // A 401 from an API gateway, for example, has no GraphQL body to quote.
119
- errorMessage = `The ${api} GraphQL API responded unsuccessfully with the HTTP status ${status}.`;
120
- }
121
117
  else {
122
118
  errorMessage = stringifyMessage(outputContent `
123
119
  The ${outputToken.raw(api)} GraphQL API responded unsuccessfully with${status === 200 ? '' : ` the HTTP status ${status} and`} errors:
@@ -1 +1 @@
1
- {"version":3,"file":"graphql.js","sourceRoot":"","sources":["../../../../src/private/node/api/graphql.ts"],"names":[],"mappings":"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","sourcesContent":["import {GraphQLClientError, sanitizedHeadersOutput} from './headers.js'\nimport {sanitizeURL} from './urls.js'\nimport {stringifyMessage, outputContent, outputToken, outputDebug} from '../../../public/node/output.js'\nimport {AbortError} from '../../../public/node/error.js'\n\nimport {ClientError, Variables} from 'graphql-request'\n\nexport function debugLogRequestInfo(\n api: string,\n query: string,\n url: string,\n variables?: Variables,\n headers: Record<string, string> = {},\n) {\n outputDebug(outputContent`Sending ${outputToken.json(api)} GraphQL request:\n ${outputToken.raw(query.toString().trim())}\n${variables ? `\\nWith variables:\\n${sanitizeVariables(variables)}\\n` : ''}\nWith request headers:\n${sanitizedHeadersOutput(headers)}\\n\nto ${sanitizeURL(url)}`)\n}\n\nexport function sanitizeVariables(variables: Variables): string {\n const result: Variables = {...variables}\n const sensitiveKeys = ['apiKey', 'serialized_script', 'sharedSecret', 'shared_secret']\n\n const sanitizedResult = sanitizeDeepVariables(result, sensitiveKeys)\n\n return JSON.stringify(sanitizedResult, null, 2)\n}\n\nfunction sanitizeDeepVariables(value: unknown, sensitiveKeys: string[]): unknown {\n // Checking for JSON\n if (typeof value === 'string') {\n try {\n const parsed = JSON.parse(value)\n if (typeof parsed === 'object' && parsed !== null) {\n const sanitized = sanitizeDeepVariables(parsed, sensitiveKeys)\n return JSON.stringify(sanitized, null)\n }\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n return value\n }\n }\n\n if (typeof value !== 'object' || value === null) {\n return value\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => sanitizeDeepVariables(item, sensitiveKeys))\n }\n\n const result: Variables = {}\n\n for (const [key, val] of Object.entries(value)) {\n if (sensitiveKeys.includes(key) && typeof val === 'string') {\n result[key] = '*****'\n continue\n }\n\n result[key] = sanitizeDeepVariables(val, sensitiveKeys)\n }\n\n return result\n}\n\n/**\n * Extracts human-readable error messages from a GraphQL errors array.\n *\n * Some APIs (e.g. App Management) return structured errors nested inside\n * `extensions.app_errors.errors[].message`. When those are present, we extract\n * them so the CLI displays a clean message instead of a raw JSON dump.\n * Falls back to each error's top-level `message` field, and ultimately to\n * the full JSON representation if no messages can be extracted.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function extractGraphQLErrorMessages(errors: any[] | undefined): string | undefined {\n if (!errors || errors.length === 0) return undefined\n\n const messages: string[] = []\n\n for (const error of errors) {\n // Check for nested app_errors (App Management API pattern)\n const appErrors = error?.extensions?.app_errors?.errors\n if (Array.isArray(appErrors) && appErrors.length > 0) {\n const appMessages: string[] = []\n for (const appError of appErrors) {\n const friendlyMessage = friendlyAppErrorMessage(appError)\n if (friendlyMessage) {\n appMessages.push(friendlyMessage)\n } else if (typeof appError?.message === 'string') {\n appMessages.push(appError.message)\n }\n }\n // Fall back to top-level error message if no app_error messages were extracted\n if (appMessages.length > 0) {\n messages.push(...appMessages)\n } else if (typeof error?.message === 'string') {\n messages.push(error.message)\n }\n }\n // Fall back to top-level error message\n else if (typeof error?.message === 'string') {\n messages.push(error.message)\n }\n }\n\n return messages.length > 0 ? messages.join('\\n') : undefined\n}\n\n/**\n * Maps known app_errors categories to user-friendly messages.\n * Returns undefined if the error doesn't match a known pattern.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction friendlyAppErrorMessage(appError: any): string | undefined {\n if (appError?.category === 'access_denied') {\n return \"You don't have the necessary permissions to perform this action. Check that you're using the correct account or token.\"\n }\n return undefined\n}\n\nexport function errorHandler(api: string): (error: unknown, requestId?: string) => unknown {\n return (error: unknown, requestId?: string) => {\n if (error instanceof ClientError) {\n const {status} = error.response\n\n const extractedMessages = extractGraphQLErrorMessages(error.response.errors)\n\n let errorMessage: string\n if (extractedMessages && status < 500) {\n errorMessage = extractedMessages\n } else if (extractedMessages && status >= 500) {\n errorMessage = `The ${api} GraphQL API responded with HTTP status ${status}: ${extractedMessages}`\n } else if (error.response.errors === undefined) {\n // A 401 from an API gateway, for example, has no GraphQL body to quote.\n errorMessage = `The ${api} GraphQL API responded unsuccessfully with the HTTP status ${status}.`\n } else {\n errorMessage = stringifyMessage(outputContent`\nThe ${outputToken.raw(api)} GraphQL API responded unsuccessfully with${\n status === 200 ? '' : ` the HTTP status ${status} and`\n } errors:\n\n${outputToken.json(error.response.errors)}\n `)\n }\n if (requestId) {\n errorMessage += `\\n\\nRequest ID: ${requestId}`\n }\n let mappedError: Error\n if (status < 500) {\n mappedError = new GraphQLClientError(errorMessage, status, error.response.errors)\n } else {\n mappedError = new AbortError(errorMessage)\n }\n mappedError.stack = error.stack\n return mappedError\n } else {\n return error\n }\n }\n}\n\nexport function extractGraphQLErrors(error: unknown): NonNullable<ClientError['response']['errors']> {\n if (error instanceof ClientError) return Array.isArray(error.response.errors) ? error.response.errors : []\n if (error instanceof GraphQLClientError) return Array.isArray(error.errors) ? error.errors : []\n return []\n}\n"]}
1
+ {"version":3,"file":"graphql.js","sourceRoot":"","sources":["../../../../src/private/node/api/graphql.ts"],"names":[],"mappings":"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","sourcesContent":["import {GraphQLClientError, sanitizedHeadersOutput} from './headers.js'\nimport {sanitizeURL} from './urls.js'\nimport {stringifyMessage, outputContent, outputToken, outputDebug} from '../../../public/node/output.js'\nimport {AbortError} from '../../../public/node/error.js'\n\nimport {ClientError, Variables} from 'graphql-request'\n\nexport function debugLogRequestInfo(\n api: string,\n query: string,\n url: string,\n variables?: Variables,\n headers: Record<string, string> = {},\n) {\n outputDebug(outputContent`Sending ${outputToken.json(api)} GraphQL request:\n ${outputToken.raw(query.toString().trim())}\n${variables ? `\\nWith variables:\\n${sanitizeVariables(variables)}\\n` : ''}\nWith request headers:\n${sanitizedHeadersOutput(headers)}\\n\nto ${sanitizeURL(url)}`)\n}\n\nexport function sanitizeVariables(variables: Variables): string {\n const result: Variables = {...variables}\n const sensitiveKeys = ['apiKey', 'serialized_script', 'sharedSecret', 'shared_secret']\n\n const sanitizedResult = sanitizeDeepVariables(result, sensitiveKeys)\n\n return JSON.stringify(sanitizedResult, null, 2)\n}\n\nfunction sanitizeDeepVariables(value: unknown, sensitiveKeys: string[]): unknown {\n // Checking for JSON\n if (typeof value === 'string') {\n try {\n const parsed = JSON.parse(value)\n if (typeof parsed === 'object' && parsed !== null) {\n const sanitized = sanitizeDeepVariables(parsed, sensitiveKeys)\n return JSON.stringify(sanitized, null)\n }\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n return value\n }\n }\n\n if (typeof value !== 'object' || value === null) {\n return value\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => sanitizeDeepVariables(item, sensitiveKeys))\n }\n\n const result: Variables = {}\n\n for (const [key, val] of Object.entries(value)) {\n if (sensitiveKeys.includes(key) && typeof val === 'string') {\n result[key] = '*****'\n continue\n }\n\n result[key] = sanitizeDeepVariables(val, sensitiveKeys)\n }\n\n return result\n}\n\n/**\n * Extracts human-readable error messages from a GraphQL errors array.\n *\n * Some APIs (e.g. App Management) return structured errors nested inside\n * `extensions.app_errors.errors[].message`. When those are present, we extract\n * them so the CLI displays a clean message instead of a raw JSON dump.\n * Falls back to each error's top-level `message` field, and ultimately to\n * the full JSON representation if no messages can be extracted.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function extractGraphQLErrorMessages(errors: any[] | undefined): string | undefined {\n if (!errors || errors.length === 0) return undefined\n\n const messages: string[] = []\n\n for (const error of errors) {\n // Check for nested app_errors (App Management API pattern)\n const appErrors = error?.extensions?.app_errors?.errors\n if (Array.isArray(appErrors) && appErrors.length > 0) {\n const appMessages: string[] = []\n for (const appError of appErrors) {\n const friendlyMessage = friendlyAppErrorMessage(appError)\n if (friendlyMessage) {\n appMessages.push(friendlyMessage)\n } else if (typeof appError?.message === 'string') {\n appMessages.push(appError.message)\n }\n }\n // Fall back to top-level error message if no app_error messages were extracted\n if (appMessages.length > 0) {\n messages.push(...appMessages)\n } else if (typeof error?.message === 'string') {\n messages.push(error.message)\n }\n }\n // Fall back to top-level error message\n else if (typeof error?.message === 'string') {\n messages.push(error.message)\n }\n }\n\n return messages.length > 0 ? messages.join('\\n') : undefined\n}\n\n/**\n * Maps known app_errors categories to user-friendly messages.\n * Returns undefined if the error doesn't match a known pattern.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction friendlyAppErrorMessage(appError: any): string | undefined {\n if (appError?.category === 'access_denied') {\n return \"You don't have the necessary permissions to perform this action. Check that you're using the correct account or token.\"\n }\n return undefined\n}\n\nexport function errorHandler(api: string): (error: unknown, requestId?: string) => unknown {\n return (error: unknown, requestId?: string) => {\n if (error instanceof ClientError) {\n const {status} = error.response\n\n const extractedMessages = extractGraphQLErrorMessages(error.response.errors)\n\n let errorMessage: string\n if (extractedMessages && status < 500) {\n errorMessage = extractedMessages\n } else if (extractedMessages && status >= 500) {\n errorMessage = `The ${api} GraphQL API responded with HTTP status ${status}: ${extractedMessages}`\n } else {\n errorMessage = stringifyMessage(outputContent`\nThe ${outputToken.raw(api)} GraphQL API responded unsuccessfully with${\n status === 200 ? '' : ` the HTTP status ${status} and`\n } errors:\n\n${outputToken.json(error.response.errors)}\n `)\n }\n if (requestId) {\n errorMessage += `\\n\\nRequest ID: ${requestId}`\n }\n let mappedError: Error\n if (status < 500) {\n mappedError = new GraphQLClientError(errorMessage, status, error.response.errors)\n } else {\n mappedError = new AbortError(errorMessage)\n }\n mappedError.stack = error.stack\n return mappedError\n } else {\n return error\n }\n }\n}\n\nexport function extractGraphQLErrors(error: unknown): NonNullable<ClientError['response']['errors']> {\n if (error instanceof ClientError) return Array.isArray(error.response.errors) ? error.response.errors : []\n if (error instanceof GraphQLClientError) return Array.isArray(error.errors) ? error.errors : []\n return []\n}\n"]}
@@ -60,8 +60,6 @@ export declare function simpleRequestWithDebugLog<T extends {
60
60
  * Makes a HTTP request to some API, retrying if response headers indicate a retryable error.
61
61
  *
62
62
  * If a request fails with a 429, the retry-after header determines a delay before an automatic retry is performed.
63
- * Without a valid header, rate-limit retries use exponential backoff with jitter, capped at 8 seconds per delay.
64
- * No new rate-limit retry starts after 30 seconds. A server-requested delay beyond that budget is not shortened.
65
63
  *
66
64
  * If unauthorizedHandler is provided, then it will be called in the case of a 401 and a retry performed. This allows
67
65
  * for a token refresh for instance.
@@ -1,17 +1,14 @@
1
1
  import { sanitizedHeadersOutput } from './api/headers.js';
2
2
  import { sanitizeURL } from './api/urls.js';
3
- import { graphQLErrorCodes, isRateLimitCode } from './analytics/graphql-error-codes.js';
3
+ import { hasRateLimitCode } from './analytics/graphql-error-codes.js';
4
4
  import { sleepWithBackoffUntil } from './sleep-with-backoff.js';
5
5
  import { outputDebug } from '../../public/node/output.js';
6
6
  import { recordRetry } from '../../public/node/analytics.js';
7
7
  import { ClientError } from 'graphql-request';
8
- import { getOperationAST, GraphQLError, parse } from 'graphql';
9
8
  import { performance } from 'perf_hooks';
10
9
  export const allAPIs = ['admin', 'storefront-renderer', 'partners', 'business-platform', 'app-management'];
11
10
  const DEFAULT_RETRY_DELAY_MS = 1000;
12
11
  const DEFAULT_RETRY_LIMIT = 10;
13
- const MAX_THROTTLE_RETRY_DELAY_MS = 8000;
14
- const MAX_THROTTLE_RETRY_TIME_MS = 30_000;
15
12
  const interestingResponseHeaders = new Set([
16
13
  'cache-control',
17
14
  'content-type',
@@ -161,6 +158,14 @@ async function makeVerboseRequest(requestOptions) {
161
158
  }
162
159
  const sanitizedHeaders = sanitizedHeadersOutput(responseHeaders);
163
160
  if (isThrottled(err)) {
161
+ let delayMs;
162
+ try {
163
+ delayMs = responseHeaders['retry-after'] ? Number.parseInt(responseHeaders['retry-after'], 10) : undefined;
164
+ // eslint-disable-next-line no-catch-all/no-catch-all
165
+ }
166
+ catch {
167
+ // ignore errors in extracting retry-after header
168
+ }
164
169
  return {
165
170
  status: 'can-retry',
166
171
  clientError: err,
@@ -168,7 +173,7 @@ async function makeVerboseRequest(requestOptions) {
168
173
  sanitizedHeaders,
169
174
  sanitizedUrl,
170
175
  requestId: responseHeaders['x-request-id'],
171
- delayMs: retryAfterDelayMs(responseHeaders['retry-after']),
176
+ delayMs,
172
177
  };
173
178
  }
174
179
  else if (err.response.status === 401) {
@@ -211,43 +216,15 @@ async function makeVerboseRequest(requestOptions) {
211
216
  requestId: responseHeaders['x-request-id'],
212
217
  };
213
218
  }
214
- // Partial mutation data can include a successful write. Read queries are safe to repeat.
219
+ // Shopify GraphQL APIs signal rate limiting with `extensions.code` set to
220
+ // `THROTTLED` (often on a 200 response) or `429` — the same codes that
221
+ // crash-report suppression and analytics grouping already treat as rate
222
+ // limiting via this shared helper.
215
223
  function isThrottled(error) {
216
- if (error.response.data != null && !isReadOnlyQuery(error.request.query))
217
- return false;
218
- const httpRateLimited = error.response.status === 429;
219
- const errors = error.response.errors;
220
- if (!Array.isArray(errors) || errors.length === 0)
221
- return httpRateLimited;
222
- return errors.every((entry) => {
223
- const codes = graphQLErrorCodes([entry]);
224
- return (codes.length > 0 || httpRateLimited) && codes.every(isRateLimitCode);
225
- });
226
- }
227
- function isReadOnlyQuery(query) {
228
- if (typeof query !== 'string')
229
- return false;
230
- try {
231
- return getOperationAST(parse(query))?.operation === 'query';
232
- }
233
- catch (error) {
234
- if (error instanceof GraphQLError)
235
- return false;
236
- throw error;
237
- }
238
- }
239
- function retryAfterDelayMs(retryAfter) {
240
- const value = retryAfter?.trim();
241
- if (!value)
242
- return undefined;
243
- const seconds = Number(value);
244
- if (!Number.isNaN(seconds)) {
245
- return Number.isFinite(seconds) && seconds >= 0 ? seconds * 1000 : undefined;
224
+ if (error.response.status === 429) {
225
+ return true;
246
226
  }
247
- // The asctime HTTP-date format omits a time zone but still uses UTC.
248
- const isAsctimeDate = /^[A-Za-z]{3} [A-Za-z]{3} (?: \d|\d{2}) \d{2}:\d{2}:\d{2} \d{4}$/.test(value);
249
- const retryAt = Date.parse(isAsctimeDate ? `${value} GMT` : value);
250
- return Number.isNaN(retryAt) ? undefined : Math.max(0, retryAt - Date.now());
227
+ return hasRateLimitCode(error.response.errors);
251
228
  }
252
229
  export async function simpleRequestWithDebugLog(requestOptions, errorHandler) {
253
230
  const result = await makeVerboseRequest(requestOptions);
@@ -297,8 +274,6 @@ ${result.sanitizedHeaders}
297
274
  * Makes a HTTP request to some API, retrying if response headers indicate a retryable error.
298
275
  *
299
276
  * If a request fails with a 429, the retry-after header determines a delay before an automatic retry is performed.
300
- * Without a valid header, rate-limit retries use exponential backoff with jitter, capped at 8 seconds per delay.
301
- * No new rate-limit retry starts after 30 seconds. A server-requested delay beyond that budget is not shortened.
302
277
  *
303
278
  * If unauthorizedHandler is provided, then it will be called in the case of a 401 and a retry performed. This allows
304
279
  * for a token refresh for instance.
@@ -318,7 +293,6 @@ export async function retryAwareRequest(requestOptions, errorHandler, retryOptio
318
293
  let retriesUsed = 0;
319
294
  const limitRetriesTo = retryOptions.limitRetriesTo ?? DEFAULT_RETRY_LIMIT;
320
295
  let result = await makeVerboseRequest(requestOptions);
321
- const retryDeadline = performance.now() + MAX_THROTTLE_RETRY_TIME_MS;
322
296
  outputDebug(`Request to ${result.sanitizedUrl} completed in ${result.duration} ms
323
297
  With response headers:
324
298
  ${result.sanitizedHeaders}
@@ -349,10 +323,8 @@ ${result.sanitizedHeaders}
349
323
  else if (result.status === 'unauthorized') {
350
324
  throw result.clientError;
351
325
  }
352
- const maximumDelayMs = Math.min((retryOptions.defaultDelayMs ?? DEFAULT_RETRY_DELAY_MS) * 2 ** retriesUsed, MAX_THROTTLE_RETRY_DELAY_MS);
353
- const retryDelayMs = result.delayMs ?? Math.floor(maximumDelayMs * (0.5 + Math.random() / 2));
354
- if (limitRetriesTo <= retriesUsed || retryDelayMs >= retryDeadline - performance.now()) {
355
- outputDebug(`Rate-limit retry budget exhausted after ${retriesUsed} retries for request to ${result.sanitizedUrl}`);
326
+ if (limitRetriesTo <= retriesUsed) {
327
+ outputDebug(`${limitRetriesTo} retries exhausted for request to ${result.sanitizedUrl}`);
356
328
  if (errorHandler) {
357
329
  throw errorHandler(result.clientError, result.requestId);
358
330
  }
@@ -365,12 +337,13 @@ ${result.sanitizedHeaders}
365
337
  if (requestOptions.recordCommandRetries) {
366
338
  recordRetry(requestOptions.url, `http-retry-${retriesUsed}:${result.status}:`);
367
339
  }
340
+ // prefer to wait based on a header if given; the caller's preference if not; and a default if neither.
341
+ const retryDelayMs = result.delayMs ?? retryOptions.defaultDelayMs ?? DEFAULT_RETRY_DELAY_MS;
368
342
  outputDebug(`Scheduling retry request #${retriesUsed} to ${result.sanitizedUrl} in ${retryDelayMs} ms`);
369
- const previousResult = result;
370
343
  // eslint-disable-next-line no-await-in-loop
371
344
  result = await new Promise((resolve) => {
372
345
  retryOptions.scheduleDelay(() => {
373
- resolve(performance.now() < retryDeadline ? makeVerboseRequest(requestOptions) : previousResult);
346
+ resolve(makeVerboseRequest(requestOptions));
374
347
  }, retryDelayMs);
375
348
  });
376
349
  }
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/private/node/api.ts"],"names":[],"mappings":"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","sourcesContent":["import {sanitizedHeadersOutput} from './api/headers.js'\nimport {sanitizeURL} from './api/urls.js'\nimport {graphQLErrorCodes, isRateLimitCode} from './analytics/graphql-error-codes.js'\nimport {sleepWithBackoffUntil} from './sleep-with-backoff.js'\nimport {outputDebug} from '../../public/node/output.js'\nimport {recordRetry} from '../../public/node/analytics.js'\n\nimport {Headers} from 'form-data'\nimport {ClientError} from 'graphql-request'\nimport {getOperationAST, GraphQLError, parse} from 'graphql'\n\nimport {performance} from 'perf_hooks'\n\nexport type API = 'admin' | 'storefront-renderer' | 'partners' | 'business-platform' | 'app-management'\n\nexport const allAPIs: API[] = ['admin', 'storefront-renderer', 'partners', 'business-platform', 'app-management']\n\nconst DEFAULT_RETRY_DELAY_MS = 1000\nconst DEFAULT_RETRY_LIMIT = 10\nconst MAX_THROTTLE_RETRY_DELAY_MS = 8000\nconst MAX_THROTTLE_RETRY_TIME_MS = 30_000\n\nexport type NetworkRetryBehaviour =\n | {\n useNetworkLevelRetry: true\n maxRetryTimeMs: number\n recordCommandRetries?: boolean\n }\n | {\n useNetworkLevelRetry: false\n recordCommandRetries?: boolean\n }\n\ntype RequestOptions<T> = {\n request: () => Promise<T>\n url: string\n} & NetworkRetryBehaviour\n\nconst interestingResponseHeaders = new Set([\n 'cache-control',\n 'content-type',\n 'etag',\n 'x-request-id',\n 'server-timing',\n 'retry-after',\n])\n\nfunction responseHeaderIsInteresting(header: string): boolean {\n return interestingResponseHeaders.has(header)\n}\n\ninterface CommonResponse {\n duration: number\n sanitizedHeaders: string\n sanitizedUrl: string\n requestId?: string\n}\n\ntype OkResponse<T> = CommonResponse & {status: 'ok'; response: T}\ntype ClientErrorResponse = CommonResponse & {status: 'client-error'; clientError: ClientError}\ntype UnknownErrorResponse = CommonResponse & {status: 'unknown-error'; error: unknown}\ntype CanRetryErrorResponse = CommonResponse & {\n status: 'can-retry'\n clientError: ClientError\n delayMs: number | undefined\n}\ntype UnauthorizedErrorResponse = CommonResponse & {\n status: 'unauthorized'\n clientError: ClientError\n delayMs: number | undefined\n}\n\ntype VerboseResponse<T> =\n | OkResponse<T>\n | ClientErrorResponse\n | UnknownErrorResponse\n | CanRetryErrorResponse\n | UnauthorizedErrorResponse\n\n/**\n * Checks if an error is a transient network error that is likely to recover with retries.\n *\n * Use this function for retry logic. Use isNetworkError for error classification.\n *\n * Examples of transient errors (worth retrying):\n * - Connection timeouts, resets, and aborts\n * - DNS failures (enotfound, getaddrinfo, eai_again) - can be temporary\n * - Socket disconnects and hang ups\n * - Premature connection closes\n */\nexport function isTransientNetworkError(error: unknown): boolean {\n if (error instanceof Error) {\n const transientErrorMessages = [\n 'socket hang up',\n 'econnreset',\n 'econnaborted',\n 'enotfound',\n 'enetunreach',\n 'network socket disconnected',\n 'etimedout',\n 'econnrefused',\n 'eai_again',\n 'epipe',\n 'the operation was aborted',\n 'timeout',\n 'premature close',\n 'getaddrinfo',\n ]\n const errorMessage = error.message.toLowerCase()\n const anyMatches = transientErrorMessages.some((issueMessage) => errorMessage.includes(issueMessage))\n const missingReason = /^request to .* failed, reason:\\s*$/.test(errorMessage)\n return anyMatches || missingReason\n }\n return false\n}\n\n/**\n * Checks if an error is any kind of network-related error (connection issues, timeouts, DNS failures,\n * TLS/certificate errors, etc.) rather than an application logic error.\n *\n * These errors should be reported as user-facing errors (AbortError) rather than bugs (BugError),\n * regardless of whether they are transient or permanent.\n *\n * Examples include:\n * - Transient: connection timeouts, socket hang ups, temporary DNS failures\n * - Permanent: certificate validation failures, misconfigured SSL\n */\nexport function isNetworkError(error: unknown): boolean {\n // First check if it's a transient network error\n if (isTransientNetworkError(error)) {\n return true\n }\n\n // Then check for permanent network errors (SSL/TLS/certificate issues)\n if (error instanceof Error) {\n const permanentNetworkErrorMessages = ['certificate', 'cert', 'tls', 'ssl', 'altnames']\n const errorMessage = error.message.toLowerCase()\n return permanentNetworkErrorMessages.some((issueMessage) => errorMessage.includes(issueMessage))\n }\n\n return false\n}\n\n/**\n * Lower-cased substrings that mean a request was aborted rather than failing on its own.\n * `node-fetch` says 'The user aborted a request.', undici says 'This operation was aborted', and\n * cli-kit's own request timeout says 'The operation was aborted'.\n */\nconst ABORTED_FETCH_MESSAGE_FRAGMENTS = ['the user aborted a request', 'operation was aborted'] as const\n\n/**\n * Checks if an error is an aborted request: a user cancelling the command, the host process\n * cancelling it, or one of the CLI's own request timeouts firing.\n *\n * Not used by the retry logic, because a user-cancelled request must not be retried.\n * `isTransientNetworkError` separately matches the CLI's own timeout message, so timeouts do\n * still retry.\n *\n * The `name` check matches the `AbortError` shape that fetch throws, not cli-kit's own\n * `AbortError`, which leaves `name` as 'Error'.\n *\n * @param error - Error to be checked.\n * @returns A boolean indicating if the request was aborted.\n */\nexport function isAbortedFetchError(error: unknown): boolean {\n if (!(error instanceof Error)) return false\n if (error.name === 'AbortError') return true\n const errorMessage = error.message.toLowerCase()\n return ABORTED_FETCH_MESSAGE_FRAGMENTS.some((fragment) => errorMessage.includes(fragment))\n}\n\nasync function runRequestWithNetworkLevelRetry<T extends {headers: Headers; status: number}>(\n requestOptions: RequestOptions<T>,\n): Promise<T> {\n if (!requestOptions.useNetworkLevelRetry) {\n return requestOptions.request()\n }\n\n let lastSeenError: unknown\n\n for await (const _delayMs of sleepWithBackoffUntil(requestOptions.maxRetryTimeMs)) {\n try {\n return await requestOptions.request()\n } catch (err) {\n lastSeenError = err\n if (!isTransientNetworkError(err)) {\n throw err\n }\n\n // Record command retries\n if (requestOptions.recordCommandRetries) {\n recordRetry(requestOptions.url, `network-retry:${(err as Error).message}`)\n }\n\n outputDebug(`Retrying request to ${requestOptions.url} due to network error ${err}`)\n }\n }\n throw lastSeenError\n}\n\nasync function makeVerboseRequest<T extends {headers: Headers; status: number}>(\n requestOptions: RequestOptions<T>,\n): Promise<VerboseResponse<T>> {\n const t0 = performance.now()\n let duration = 0\n const responseHeaders: Record<string, string> = {}\n const sanitizedUrl = sanitizeURL(requestOptions.url)\n let response: T = {} as T\n try {\n response = await runRequestWithNetworkLevelRetry(requestOptions)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n response.headers.forEach((value: any, key: any) => {\n if (responseHeaderIsInteresting(key)) responseHeaders[key] = value\n })\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (err) {\n const t1 = performance.now()\n duration = Math.round(t1 - t0)\n\n if (err instanceof ClientError) {\n if (err.response.headers) {\n for (const [key, value] of err.response.headers as Iterable<[string, string]>) {\n if (responseHeaderIsInteresting(key)) responseHeaders[key] = value\n }\n }\n const sanitizedHeaders = sanitizedHeadersOutput(responseHeaders)\n\n if (isThrottled(err)) {\n return {\n status: 'can-retry',\n clientError: err,\n duration,\n sanitizedHeaders,\n sanitizedUrl,\n requestId: responseHeaders['x-request-id'],\n delayMs: retryAfterDelayMs(responseHeaders['retry-after']),\n }\n } else if (err.response.status === 401) {\n return {\n status: 'unauthorized',\n clientError: err,\n duration,\n sanitizedHeaders,\n sanitizedUrl,\n requestId: responseHeaders['x-request-id'],\n delayMs: 500,\n }\n }\n\n return {\n status: 'client-error',\n clientError: err,\n duration,\n sanitizedHeaders,\n sanitizedUrl,\n requestId: responseHeaders['x-request-id'],\n }\n }\n return {\n status: 'unknown-error',\n error: err,\n duration,\n sanitizedHeaders: sanitizedHeadersOutput(responseHeaders),\n sanitizedUrl,\n requestId: responseHeaders['x-request-id'],\n }\n }\n const t1 = performance.now()\n duration = Math.round(t1 - t0)\n return {\n status: 'ok',\n response,\n duration,\n sanitizedHeaders: sanitizedHeadersOutput(responseHeaders),\n sanitizedUrl,\n requestId: responseHeaders['x-request-id'],\n }\n}\n\n// Partial mutation data can include a successful write. Read queries are safe to repeat.\nfunction isThrottled(error: ClientError): boolean {\n if (error.response.data != null && !isReadOnlyQuery(error.request.query)) return false\n const httpRateLimited = error.response.status === 429\n const errors = error.response.errors\n if (!Array.isArray(errors) || errors.length === 0) return httpRateLimited\n return errors.every((entry) => {\n const codes = graphQLErrorCodes([entry])\n return (codes.length > 0 || httpRateLimited) && codes.every(isRateLimitCode)\n })\n}\n\nfunction isReadOnlyQuery(query: string | string[]): boolean {\n if (typeof query !== 'string') return false\n try {\n return getOperationAST(parse(query))?.operation === 'query'\n } catch (error) {\n if (error instanceof GraphQLError) return false\n throw error\n }\n}\n\nfunction retryAfterDelayMs(retryAfter: string | undefined): number | undefined {\n const value = retryAfter?.trim()\n if (!value) return undefined\n\n const seconds = Number(value)\n if (!Number.isNaN(seconds)) {\n return Number.isFinite(seconds) && seconds >= 0 ? seconds * 1000 : undefined\n }\n\n // The asctime HTTP-date format omits a time zone but still uses UTC.\n const isAsctimeDate = /^[A-Za-z]{3} [A-Za-z]{3} (?: \\d|\\d{2}) \\d{2}:\\d{2}:\\d{2} \\d{4}$/.test(value)\n const retryAt = Date.parse(isAsctimeDate ? `${value} GMT` : value)\n return Number.isNaN(retryAt) ? undefined : Math.max(0, retryAt - Date.now())\n}\n\nexport async function simpleRequestWithDebugLog<T extends {headers: Headers; status: number}>(\n requestOptions: RequestOptions<T>,\n errorHandler?: (error: unknown, requestId: string | undefined) => unknown,\n): Promise<T> {\n const result = await makeVerboseRequest(requestOptions)\n\n outputDebug(`Request to ${result.sanitizedUrl} completed in ${result.duration} ms\nWith response headers:\n${result.sanitizedHeaders}\n `)\n\n switch (result.status) {\n case 'ok': {\n return result.response\n }\n case 'client-error': {\n if (errorHandler) {\n throw errorHandler(result.clientError, result.requestId)\n } else {\n throw result.clientError\n }\n }\n case 'unknown-error': {\n if (errorHandler) {\n throw errorHandler(result.error, result.requestId)\n } else {\n throw result.error\n }\n }\n case 'can-retry': {\n if (errorHandler) {\n throw errorHandler(result.clientError, result.requestId)\n } else {\n throw result.clientError\n }\n }\n case 'unauthorized': {\n if (errorHandler) {\n throw errorHandler(result.clientError, result.requestId)\n } else {\n throw result.clientError\n }\n }\n }\n}\n\n/**\n * Makes a HTTP request to some API, retrying if response headers indicate a retryable error.\n *\n * If a request fails with a 429, the retry-after header determines a delay before an automatic retry is performed.\n * Without a valid header, rate-limit retries use exponential backoff with jitter, capped at 8 seconds per delay.\n * No new rate-limit retry starts after 30 seconds. A server-requested delay beyond that budget is not shortened.\n *\n * If unauthorizedHandler is provided, then it will be called in the case of a 401 and a retry performed. This allows\n * for a token refresh for instance.\n *\n * If there's a network error, e.g. DNS fails to resolve, then API calls are automatically retried.\n *\n * @param request - A function that returns a promise of the response\n * @param url - The URL to request\n * @param errorHandler - A function that handles errors\n * @param unauthorizedHandler - A function that handles unauthorized errors\n * @param retryOptions - Options for the retry\n * @returns The response from the request\n */\nexport async function retryAwareRequest<T extends {headers: Headers; status: number}>(\n requestOptions: RequestOptions<T>,\n errorHandler?: (error: unknown, requestId: string | undefined) => unknown,\n retryOptions: {\n limitRetriesTo?: number\n defaultDelayMs?: number\n scheduleDelay: (fn: () => void, delay: number) => void\n } = {\n scheduleDelay: setTimeout,\n },\n): Promise<T> {\n let retriesUsed = 0\n const limitRetriesTo = retryOptions.limitRetriesTo ?? DEFAULT_RETRY_LIMIT\n\n let result = await makeVerboseRequest(requestOptions)\n const retryDeadline = performance.now() + MAX_THROTTLE_RETRY_TIME_MS\n\n outputDebug(`Request to ${result.sanitizedUrl} completed in ${result.duration} ms\nWith response headers:\n${result.sanitizedHeaders}\n `)\n\n while (true) {\n if (result.status === 'ok') {\n if (retriesUsed > 0) {\n outputDebug(`Request to ${result.sanitizedUrl} succeeded after ${retriesUsed} retries`)\n }\n return result.response\n } else if (result.status === 'client-error') {\n if (errorHandler) {\n throw errorHandler(result.clientError, result.requestId)\n } else {\n throw result.clientError\n }\n } else if (result.status === 'unknown-error') {\n if (errorHandler) {\n throw errorHandler(result.error, result.requestId)\n } else {\n throw result.error\n }\n } else if (result.status === 'unauthorized') {\n throw result.clientError\n }\n\n const maximumDelayMs = Math.min(\n (retryOptions.defaultDelayMs ?? DEFAULT_RETRY_DELAY_MS) * 2 ** retriesUsed,\n MAX_THROTTLE_RETRY_DELAY_MS,\n )\n const retryDelayMs = result.delayMs ?? Math.floor(maximumDelayMs * (0.5 + Math.random() / 2))\n if (limitRetriesTo <= retriesUsed || retryDelayMs >= retryDeadline - performance.now()) {\n outputDebug(\n `Rate-limit retry budget exhausted after ${retriesUsed} retries for request to ${result.sanitizedUrl}`,\n )\n if (errorHandler) {\n throw errorHandler(result.clientError, result.requestId)\n } else {\n throw result.clientError\n }\n }\n retriesUsed += 1\n\n // Record command retries\n if (requestOptions.recordCommandRetries) {\n recordRetry(requestOptions.url, `http-retry-${retriesUsed}:${result.status}:`)\n }\n\n outputDebug(`Scheduling retry request #${retriesUsed} to ${result.sanitizedUrl} in ${retryDelayMs} ms`)\n\n const previousResult = result\n // eslint-disable-next-line no-await-in-loop\n result = await new Promise<VerboseResponse<T>>((resolve) => {\n retryOptions.scheduleDelay(() => {\n resolve(performance.now() < retryDeadline ? makeVerboseRequest(requestOptions) : previousResult)\n }, retryDelayMs)\n })\n }\n}\n"]}
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/private/node/api.ts"],"names":[],"mappings":"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","sourcesContent":["import {sanitizedHeadersOutput} from './api/headers.js'\nimport {sanitizeURL} from './api/urls.js'\nimport {hasRateLimitCode} from './analytics/graphql-error-codes.js'\nimport {sleepWithBackoffUntil} from './sleep-with-backoff.js'\nimport {outputDebug} from '../../public/node/output.js'\nimport {recordRetry} from '../../public/node/analytics.js'\n\nimport {Headers} from 'form-data'\nimport {ClientError} from 'graphql-request'\n\nimport {performance} from 'perf_hooks'\n\nexport type API = 'admin' | 'storefront-renderer' | 'partners' | 'business-platform' | 'app-management'\n\nexport const allAPIs: API[] = ['admin', 'storefront-renderer', 'partners', 'business-platform', 'app-management']\n\nconst DEFAULT_RETRY_DELAY_MS = 1000\nconst DEFAULT_RETRY_LIMIT = 10\n\nexport type NetworkRetryBehaviour =\n | {\n useNetworkLevelRetry: true\n maxRetryTimeMs: number\n recordCommandRetries?: boolean\n }\n | {\n useNetworkLevelRetry: false\n recordCommandRetries?: boolean\n }\n\ntype RequestOptions<T> = {\n request: () => Promise<T>\n url: string\n} & NetworkRetryBehaviour\n\nconst interestingResponseHeaders = new Set([\n 'cache-control',\n 'content-type',\n 'etag',\n 'x-request-id',\n 'server-timing',\n 'retry-after',\n])\n\nfunction responseHeaderIsInteresting(header: string): boolean {\n return interestingResponseHeaders.has(header)\n}\n\ninterface CommonResponse {\n duration: number\n sanitizedHeaders: string\n sanitizedUrl: string\n requestId?: string\n}\n\ntype OkResponse<T> = CommonResponse & {status: 'ok'; response: T}\ntype ClientErrorResponse = CommonResponse & {status: 'client-error'; clientError: ClientError}\ntype UnknownErrorResponse = CommonResponse & {status: 'unknown-error'; error: unknown}\ntype CanRetryErrorResponse = CommonResponse & {\n status: 'can-retry'\n clientError: ClientError\n delayMs: number | undefined\n}\ntype UnauthorizedErrorResponse = CommonResponse & {\n status: 'unauthorized'\n clientError: ClientError\n delayMs: number | undefined\n}\n\ntype VerboseResponse<T> =\n | OkResponse<T>\n | ClientErrorResponse\n | UnknownErrorResponse\n | CanRetryErrorResponse\n | UnauthorizedErrorResponse\n\n/**\n * Checks if an error is a transient network error that is likely to recover with retries.\n *\n * Use this function for retry logic. Use isNetworkError for error classification.\n *\n * Examples of transient errors (worth retrying):\n * - Connection timeouts, resets, and aborts\n * - DNS failures (enotfound, getaddrinfo, eai_again) - can be temporary\n * - Socket disconnects and hang ups\n * - Premature connection closes\n */\nexport function isTransientNetworkError(error: unknown): boolean {\n if (error instanceof Error) {\n const transientErrorMessages = [\n 'socket hang up',\n 'econnreset',\n 'econnaborted',\n 'enotfound',\n 'enetunreach',\n 'network socket disconnected',\n 'etimedout',\n 'econnrefused',\n 'eai_again',\n 'epipe',\n 'the operation was aborted',\n 'timeout',\n 'premature close',\n 'getaddrinfo',\n ]\n const errorMessage = error.message.toLowerCase()\n const anyMatches = transientErrorMessages.some((issueMessage) => errorMessage.includes(issueMessage))\n const missingReason = /^request to .* failed, reason:\\s*$/.test(errorMessage)\n return anyMatches || missingReason\n }\n return false\n}\n\n/**\n * Checks if an error is any kind of network-related error (connection issues, timeouts, DNS failures,\n * TLS/certificate errors, etc.) rather than an application logic error.\n *\n * These errors should be reported as user-facing errors (AbortError) rather than bugs (BugError),\n * regardless of whether they are transient or permanent.\n *\n * Examples include:\n * - Transient: connection timeouts, socket hang ups, temporary DNS failures\n * - Permanent: certificate validation failures, misconfigured SSL\n */\nexport function isNetworkError(error: unknown): boolean {\n // First check if it's a transient network error\n if (isTransientNetworkError(error)) {\n return true\n }\n\n // Then check for permanent network errors (SSL/TLS/certificate issues)\n if (error instanceof Error) {\n const permanentNetworkErrorMessages = ['certificate', 'cert', 'tls', 'ssl', 'altnames']\n const errorMessage = error.message.toLowerCase()\n return permanentNetworkErrorMessages.some((issueMessage) => errorMessage.includes(issueMessage))\n }\n\n return false\n}\n\n/**\n * Lower-cased substrings that mean a request was aborted rather than failing on its own.\n * `node-fetch` says 'The user aborted a request.', undici says 'This operation was aborted', and\n * cli-kit's own request timeout says 'The operation was aborted'.\n */\nconst ABORTED_FETCH_MESSAGE_FRAGMENTS = ['the user aborted a request', 'operation was aborted'] as const\n\n/**\n * Checks if an error is an aborted request: a user cancelling the command, the host process\n * cancelling it, or one of the CLI's own request timeouts firing.\n *\n * Not used by the retry logic, because a user-cancelled request must not be retried.\n * `isTransientNetworkError` separately matches the CLI's own timeout message, so timeouts do\n * still retry.\n *\n * The `name` check matches the `AbortError` shape that fetch throws, not cli-kit's own\n * `AbortError`, which leaves `name` as 'Error'.\n *\n * @param error - Error to be checked.\n * @returns A boolean indicating if the request was aborted.\n */\nexport function isAbortedFetchError(error: unknown): boolean {\n if (!(error instanceof Error)) return false\n if (error.name === 'AbortError') return true\n const errorMessage = error.message.toLowerCase()\n return ABORTED_FETCH_MESSAGE_FRAGMENTS.some((fragment) => errorMessage.includes(fragment))\n}\n\nasync function runRequestWithNetworkLevelRetry<T extends {headers: Headers; status: number}>(\n requestOptions: RequestOptions<T>,\n): Promise<T> {\n if (!requestOptions.useNetworkLevelRetry) {\n return requestOptions.request()\n }\n\n let lastSeenError: unknown\n\n for await (const _delayMs of sleepWithBackoffUntil(requestOptions.maxRetryTimeMs)) {\n try {\n return await requestOptions.request()\n } catch (err) {\n lastSeenError = err\n if (!isTransientNetworkError(err)) {\n throw err\n }\n\n // Record command retries\n if (requestOptions.recordCommandRetries) {\n recordRetry(requestOptions.url, `network-retry:${(err as Error).message}`)\n }\n\n outputDebug(`Retrying request to ${requestOptions.url} due to network error ${err}`)\n }\n }\n throw lastSeenError\n}\n\nasync function makeVerboseRequest<T extends {headers: Headers; status: number}>(\n requestOptions: RequestOptions<T>,\n): Promise<VerboseResponse<T>> {\n const t0 = performance.now()\n let duration = 0\n const responseHeaders: Record<string, string> = {}\n const sanitizedUrl = sanitizeURL(requestOptions.url)\n let response: T = {} as T\n try {\n response = await runRequestWithNetworkLevelRetry(requestOptions)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n response.headers.forEach((value: any, key: any) => {\n if (responseHeaderIsInteresting(key)) responseHeaders[key] = value\n })\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (err) {\n const t1 = performance.now()\n duration = Math.round(t1 - t0)\n\n if (err instanceof ClientError) {\n if (err.response.headers) {\n for (const [key, value] of err.response.headers as Iterable<[string, string]>) {\n if (responseHeaderIsInteresting(key)) responseHeaders[key] = value\n }\n }\n const sanitizedHeaders = sanitizedHeadersOutput(responseHeaders)\n\n if (isThrottled(err)) {\n let delayMs: number | undefined\n\n try {\n delayMs = responseHeaders['retry-after'] ? Number.parseInt(responseHeaders['retry-after'], 10) : undefined\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch {\n // ignore errors in extracting retry-after header\n }\n return {\n status: 'can-retry',\n clientError: err,\n duration,\n sanitizedHeaders,\n sanitizedUrl,\n requestId: responseHeaders['x-request-id'],\n delayMs,\n }\n } else if (err.response.status === 401) {\n return {\n status: 'unauthorized',\n clientError: err,\n duration,\n sanitizedHeaders,\n sanitizedUrl,\n requestId: responseHeaders['x-request-id'],\n delayMs: 500,\n }\n }\n\n return {\n status: 'client-error',\n clientError: err,\n duration,\n sanitizedHeaders,\n sanitizedUrl,\n requestId: responseHeaders['x-request-id'],\n }\n }\n return {\n status: 'unknown-error',\n error: err,\n duration,\n sanitizedHeaders: sanitizedHeadersOutput(responseHeaders),\n sanitizedUrl,\n requestId: responseHeaders['x-request-id'],\n }\n }\n const t1 = performance.now()\n duration = Math.round(t1 - t0)\n return {\n status: 'ok',\n response,\n duration,\n sanitizedHeaders: sanitizedHeadersOutput(responseHeaders),\n sanitizedUrl,\n requestId: responseHeaders['x-request-id'],\n }\n}\n\n// Shopify GraphQL APIs signal rate limiting with `extensions.code` set to\n// `THROTTLED` (often on a 200 response) or `429` — the same codes that\n// crash-report suppression and analytics grouping already treat as rate\n// limiting via this shared helper.\nfunction isThrottled(error: ClientError): boolean {\n if (error.response.status === 429) {\n return true\n }\n return hasRateLimitCode(error.response.errors)\n}\n\nexport async function simpleRequestWithDebugLog<T extends {headers: Headers; status: number}>(\n requestOptions: RequestOptions<T>,\n errorHandler?: (error: unknown, requestId: string | undefined) => unknown,\n): Promise<T> {\n const result = await makeVerboseRequest(requestOptions)\n\n outputDebug(`Request to ${result.sanitizedUrl} completed in ${result.duration} ms\nWith response headers:\n${result.sanitizedHeaders}\n `)\n\n switch (result.status) {\n case 'ok': {\n return result.response\n }\n case 'client-error': {\n if (errorHandler) {\n throw errorHandler(result.clientError, result.requestId)\n } else {\n throw result.clientError\n }\n }\n case 'unknown-error': {\n if (errorHandler) {\n throw errorHandler(result.error, result.requestId)\n } else {\n throw result.error\n }\n }\n case 'can-retry': {\n if (errorHandler) {\n throw errorHandler(result.clientError, result.requestId)\n } else {\n throw result.clientError\n }\n }\n case 'unauthorized': {\n if (errorHandler) {\n throw errorHandler(result.clientError, result.requestId)\n } else {\n throw result.clientError\n }\n }\n }\n}\n\n/**\n * Makes a HTTP request to some API, retrying if response headers indicate a retryable error.\n *\n * If a request fails with a 429, the retry-after header determines a delay before an automatic retry is performed.\n *\n * If unauthorizedHandler is provided, then it will be called in the case of a 401 and a retry performed. This allows\n * for a token refresh for instance.\n *\n * If there's a network error, e.g. DNS fails to resolve, then API calls are automatically retried.\n *\n * @param request - A function that returns a promise of the response\n * @param url - The URL to request\n * @param errorHandler - A function that handles errors\n * @param unauthorizedHandler - A function that handles unauthorized errors\n * @param retryOptions - Options for the retry\n * @returns The response from the request\n */\nexport async function retryAwareRequest<T extends {headers: Headers; status: number}>(\n requestOptions: RequestOptions<T>,\n errorHandler?: (error: unknown, requestId: string | undefined) => unknown,\n retryOptions: {\n limitRetriesTo?: number\n defaultDelayMs?: number\n scheduleDelay: (fn: () => void, delay: number) => void\n } = {\n scheduleDelay: setTimeout,\n },\n): Promise<T> {\n let retriesUsed = 0\n const limitRetriesTo = retryOptions.limitRetriesTo ?? DEFAULT_RETRY_LIMIT\n\n let result = await makeVerboseRequest(requestOptions)\n\n outputDebug(`Request to ${result.sanitizedUrl} completed in ${result.duration} ms\nWith response headers:\n${result.sanitizedHeaders}\n `)\n\n while (true) {\n if (result.status === 'ok') {\n if (retriesUsed > 0) {\n outputDebug(`Request to ${result.sanitizedUrl} succeeded after ${retriesUsed} retries`)\n }\n return result.response\n } else if (result.status === 'client-error') {\n if (errorHandler) {\n throw errorHandler(result.clientError, result.requestId)\n } else {\n throw result.clientError\n }\n } else if (result.status === 'unknown-error') {\n if (errorHandler) {\n throw errorHandler(result.error, result.requestId)\n } else {\n throw result.error\n }\n } else if (result.status === 'unauthorized') {\n throw result.clientError\n }\n\n if (limitRetriesTo <= retriesUsed) {\n outputDebug(`${limitRetriesTo} retries exhausted for request to ${result.sanitizedUrl}`)\n if (errorHandler) {\n throw errorHandler(result.clientError, result.requestId)\n } else {\n throw result.clientError\n }\n }\n retriesUsed += 1\n\n // Record command retries\n if (requestOptions.recordCommandRetries) {\n recordRetry(requestOptions.url, `http-retry-${retriesUsed}:${result.status}:`)\n }\n\n // prefer to wait based on a header if given; the caller's preference if not; and a default if neither.\n const retryDelayMs = result.delayMs ?? retryOptions.defaultDelayMs ?? DEFAULT_RETRY_DELAY_MS\n outputDebug(`Scheduling retry request #${retriesUsed} to ${result.sanitizedUrl} in ${retryDelayMs} ms`)\n\n // eslint-disable-next-line no-await-in-loop\n result = await new Promise<VerboseResponse<T>>((resolve) => {\n retryOptions.scheduleDelay(() => {\n resolve(makeVerboseRequest(requestOptions))\n }, retryDelayMs)\n })\n }\n}\n"]}
@@ -27,7 +27,6 @@ export async function printCommandJsonSchema(config, argv, lazyCommandLoader) {
27
27
  name: 'CommandOutput',
28
28
  schema: zod.union([outputSchema.schema, jsonErrorOutputSchema.schema, commandEventOutputSchema.schema]),
29
29
  definitions: {
30
- ...outputSchema.definitions,
31
30
  Result: outputSchema.schema,
32
31
  Error: jsonErrorOutputSchema.schema,
33
32
  Event: commandEventOutputSchema.schema,
@@ -1 +1 @@
1
- {"version":3,"file":"command-json-schema.js","sourceRoot":"","sources":["../../../src/private/node/command-json-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,4BAA4B,CAAA;AACrD,OAAO,EAAC,wBAAwB,EAAC,MAAM,qCAAqC,CAAA;AAC5E,OAAO,EAAC,qBAAqB,EAAC,MAAM,mCAAmC,CAAA;AACvE,OAAO,EAAC,sBAAsB,EAAwB,MAAM,yCAAyC,CAAA;AACrG,OAAO,EAAC,WAAW,EAAE,YAAY,EAAC,MAAM,6BAA6B,CAAA;AACrE,OAAO,EAAC,GAAG,EAAC,MAAM,6BAA6B,CAAA;AAC/C,OAAO,EAAC,aAAa,EAAC,MAAM,kBAAkB,CAAA;AAI9C;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAc,EACd,IAAc,EACd,iBAAqC;IAErC,MAAM,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACxC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,sDAAsD,CAAC,CAAA;IAE3G,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;IACtC,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,UAAU,CAAC,YAAY,EAAE,cAAc,CAAC,CAAA;IAEhE,MAAM,YAAY,GAAG,CAAC,MAAM,iBAAiB,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC,CAAA;IACtF,MAAM,YAAY,GAAI,YAA4E,CAAC,gBAAgB,CAAA;IACnH,IAAI,CAAC,YAAY;QAAE,MAAM,IAAI,UAAU,CAAC,oDAAoD,CAAC,CAAA;IAE7F,MAAM,aAAa,GAAG,sBAAsB,CAAC;QAC3C,IAAI,EAAE,eAAe;QACrB,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,qBAAqB,CAAC,MAAM,EAAE,wBAAwB,CAAC,MAAM,CAAC,CAAC;QACvG,WAAW,EAAE;YACX,GAAG,YAAY,CAAC,WAAW;YAC3B,MAAM,EAAE,YAAY,CAAC,MAAM;YAC3B,KAAK,EAAE,qBAAqB,CAAC,MAAM;YACnC,KAAK,EAAE,wBAAwB,CAAC,MAAM;SACvC;KACF,CAAC,CAAA;IACF,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;IAC/D,MAAM,WAAW,EAAE,CAAA;AACrB,CAAC","sourcesContent":["import {AbortError} from '../../public/node/error.js'\nimport {commandEventOutputSchema} from '../../public/node/command-events.js'\nimport {jsonErrorOutputSchema} from '../../public/node/error/schema.js'\nimport {defineJsonOutputSchema, type JsonOutputSchema} from '../../public/node/json-output-schema.js'\nimport {flushStdout, outputResult} from '../../public/node/output.js'\nimport {zod} from '../../public/node/schema.js'\nimport {normalizeArgv} from '@oclif/core/help'\nimport type {LazyCommandLoader} from '../../public/node/custom-oclif-loader.js'\nimport type {Config} from '@oclif/core'\n\n/**\n * Loads a command's schema without running its lifecycle or Oclif hooks.\n *\n * @param config - The loaded CLI configuration.\n * @param argv - The CLI arguments, including the command name.\n * @param lazyCommandLoader - The optional loader for bundled commands.\n */\nexport async function printCommandJsonSchema(\n config: Config,\n argv: string[],\n lazyCommandLoader?: LazyCommandLoader,\n): Promise<void> {\n const [id] = normalizeArgv(config, argv)\n if (!id || id.startsWith('-')) throw new AbortError('Specify a command to inspect its JSON output schema.')\n\n const command = config.findCommand(id)\n if (!command) throw new AbortError(`Command \"${id}\" not found.`)\n\n const commandClass = (await lazyCommandLoader?.(command.id)) ?? (await command.load())\n const outputSchema = (commandClass as typeof commandClass & {jsonOutputSchema?: JsonOutputSchema}).jsonOutputSchema\n if (!outputSchema) throw new AbortError('This command does not define a JSON output schema.')\n\n const commandSchema = defineJsonOutputSchema({\n name: 'CommandOutput',\n schema: zod.union([outputSchema.schema, jsonErrorOutputSchema.schema, commandEventOutputSchema.schema]),\n definitions: {\n ...outputSchema.definitions,\n Result: outputSchema.schema,\n Error: jsonErrorOutputSchema.schema,\n Event: commandEventOutputSchema.schema,\n },\n })\n outputResult(JSON.stringify(commandSchema.jsonSchema, null, 2))\n await flushStdout()\n}\n"]}
1
+ {"version":3,"file":"command-json-schema.js","sourceRoot":"","sources":["../../../src/private/node/command-json-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,4BAA4B,CAAA;AACrD,OAAO,EAAC,wBAAwB,EAAC,MAAM,qCAAqC,CAAA;AAC5E,OAAO,EAAC,qBAAqB,EAAC,MAAM,mCAAmC,CAAA;AACvE,OAAO,EAAC,sBAAsB,EAAwB,MAAM,yCAAyC,CAAA;AACrG,OAAO,EAAC,WAAW,EAAE,YAAY,EAAC,MAAM,6BAA6B,CAAA;AACrE,OAAO,EAAC,GAAG,EAAC,MAAM,6BAA6B,CAAA;AAC/C,OAAO,EAAC,aAAa,EAAC,MAAM,kBAAkB,CAAA;AAI9C;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAc,EACd,IAAc,EACd,iBAAqC;IAErC,MAAM,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACxC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,sDAAsD,CAAC,CAAA;IAE3G,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;IACtC,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,UAAU,CAAC,YAAY,EAAE,cAAc,CAAC,CAAA;IAEhE,MAAM,YAAY,GAAG,CAAC,MAAM,iBAAiB,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC,CAAA;IACtF,MAAM,YAAY,GAAI,YAA4E,CAAC,gBAAgB,CAAA;IACnH,IAAI,CAAC,YAAY;QAAE,MAAM,IAAI,UAAU,CAAC,oDAAoD,CAAC,CAAA;IAE7F,MAAM,aAAa,GAAG,sBAAsB,CAAC;QAC3C,IAAI,EAAE,eAAe;QACrB,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,qBAAqB,CAAC,MAAM,EAAE,wBAAwB,CAAC,MAAM,CAAC,CAAC;QACvG,WAAW,EAAE;YACX,MAAM,EAAE,YAAY,CAAC,MAAM;YAC3B,KAAK,EAAE,qBAAqB,CAAC,MAAM;YACnC,KAAK,EAAE,wBAAwB,CAAC,MAAM;SACvC;KACF,CAAC,CAAA;IACF,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;IAC/D,MAAM,WAAW,EAAE,CAAA;AACrB,CAAC","sourcesContent":["import {AbortError} from '../../public/node/error.js'\nimport {commandEventOutputSchema} from '../../public/node/command-events.js'\nimport {jsonErrorOutputSchema} from '../../public/node/error/schema.js'\nimport {defineJsonOutputSchema, type JsonOutputSchema} from '../../public/node/json-output-schema.js'\nimport {flushStdout, outputResult} from '../../public/node/output.js'\nimport {zod} from '../../public/node/schema.js'\nimport {normalizeArgv} from '@oclif/core/help'\nimport type {LazyCommandLoader} from '../../public/node/custom-oclif-loader.js'\nimport type {Config} from '@oclif/core'\n\n/**\n * Loads a command's schema without running its lifecycle or Oclif hooks.\n *\n * @param config - The loaded CLI configuration.\n * @param argv - The CLI arguments, including the command name.\n * @param lazyCommandLoader - The optional loader for bundled commands.\n */\nexport async function printCommandJsonSchema(\n config: Config,\n argv: string[],\n lazyCommandLoader?: LazyCommandLoader,\n): Promise<void> {\n const [id] = normalizeArgv(config, argv)\n if (!id || id.startsWith('-')) throw new AbortError('Specify a command to inspect its JSON output schema.')\n\n const command = config.findCommand(id)\n if (!command) throw new AbortError(`Command \"${id}\" not found.`)\n\n const commandClass = (await lazyCommandLoader?.(command.id)) ?? (await command.load())\n const outputSchema = (commandClass as typeof commandClass & {jsonOutputSchema?: JsonOutputSchema}).jsonOutputSchema\n if (!outputSchema) throw new AbortError('This command does not define a JSON output schema.')\n\n const commandSchema = defineJsonOutputSchema({\n name: 'CommandOutput',\n schema: zod.union([outputSchema.schema, jsonErrorOutputSchema.schema, commandEventOutputSchema.schema]),\n definitions: {\n Result: outputSchema.schema,\n Error: jsonErrorOutputSchema.schema,\n Event: commandEventOutputSchema.schema,\n },\n })\n outputResult(JSON.stringify(commandSchema.jsonSchema, null, 2))\n await flushStdout()\n}\n"]}
@@ -39,14 +39,14 @@ describe('logging during an Ink task', () => {
39
39
  });
40
40
  try {
41
41
  await vi.waitFor(() => expect(writes.join('')).toContain('Preparing the result'));
42
- expect(unstyled(writes.join(''))).toContain('S>');
42
+ expect(writes.join('')).toContain('▀');
43
43
  const beforeLog = writes.length;
44
44
  log('Prepared an item');
45
- // Clear the UI line and its trailing newline before writing the log.
45
+ // Clear both UI lines and their trailing newline before writing the log.
46
46
  const logWrites = writes.slice(beforeLog);
47
47
  const messageIndex = logWrites.findIndex((write) => write.includes('Prepared an item'));
48
48
  expect(messageIndex).toBeGreaterThan(0);
49
- expect(logWrites[messageIndex - 1]).toBe(ansiEscapes.eraseLines(2));
49
+ expect(logWrites[messageIndex - 1]).toBe(ansiEscapes.eraseLines(3));
50
50
  expect(unstyled(logWrites[messageIndex])).toBe('Prepared an item\n');
51
51
  expect(logWrites[messageIndex + 1]).toContain('Preparing the result');
52
52
  outputResult('{"items":1}');
@@ -56,7 +56,7 @@ describe('logging during an Ink task', () => {
56
56
  finishTask();
57
57
  await instance.waitUntilExit();
58
58
  const cleanup = writes.slice(afterLog).join('');
59
- expect(cleanup).toContain(ansiEscapes.eraseLines(2));
59
+ expect(cleanup).toContain(ansiEscapes.eraseLines(3));
60
60
  expect(unstyled(cleanup).trim()).toBe('');
61
61
  }
62
62
  finally {
@@ -1 +1 @@
1
- {"version":3,"file":"output.test.js","sourceRoot":"","sources":["../../../src/private/node/output.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import {InkLifecycleRoot} from './ui.js'\nimport {SingleTask} from './ui/components/SingleTask.js'\nimport {outputInfo, outputWarn, outputResult, TokenizedString, unstyled} from '../../public/node/output.js'\nimport React from 'react'\nimport {render} from 'ink'\nimport ansiEscapes from 'ansi-escapes'\nimport {describe, expect, test, vi} from 'vitest'\nimport {PassThrough} from 'stream'\n// Vitest's console omits the constructor used by Ink's console patch.\n// eslint-disable-next-line n/prefer-global/console\nimport {Console} from 'console'\n\n// Ink detects CI when imported; exercise terminal rendering on CI runners too.\nvi.hoisted(() => {\n vi.stubEnv('CI', 'false')\n vi.stubEnv('CONTINUOUS_INTEGRATION', 'false')\n})\nvi.mock('../../public/node/context/local.js', async (importOriginal) => ({\n ...(await importOriginal<typeof import('../../public/node/context/local.js')>()),\n isUnitTest: () => false,\n}))\n\ndescribe('logging during an Ink task', () => {\n test.each([outputInfo, outputWarn])('clears and redraws the task around %o', async (log) => {\n vi.stubGlobal('console', {...console, Console})\n const writes: string[] = []\n const terminal = Object.assign(new PassThrough(), {isTTY: true, columns: 80, rows: 24})\n terminal.on('data', (data: Buffer) => writes.push(data.toString()))\n let finishTask!: () => void\n const taskResult = new Promise<void>((resolve) => {\n finishTask = resolve\n })\n const stdoutWrite = vi.spyOn(process.stdout, 'write').mockReturnValue(true)\n const stderrWrite = vi.spyOn(process.stderr, 'write').mockReturnValue(true)\n const instance = render(\n <InkLifecycleRoot>\n <SingleTask title={new TokenizedString('Preparing the result')} task={() => taskResult} />\n </InkLifecycleRoot>,\n {\n stdout: terminal as unknown as NodeJS.WriteStream,\n stderr: terminal as unknown as NodeJS.WriteStream,\n patchConsole: true,\n exitOnCtrlC: false,\n },\n )\n\n try {\n await vi.waitFor(() => expect(writes.join('')).toContain('Preparing the result'))\n expect(unstyled(writes.join(''))).toContain('S>')\n const beforeLog = writes.length\n\n log('Prepared an item')\n\n // Clear the UI line and its trailing newline before writing the log.\n const logWrites = writes.slice(beforeLog)\n const messageIndex = logWrites.findIndex((write) => write.includes('Prepared an item'))\n expect(messageIndex).toBeGreaterThan(0)\n expect(logWrites[messageIndex - 1]).toBe(ansiEscapes.eraseLines(2))\n expect(unstyled(logWrites[messageIndex]!)).toBe('Prepared an item\\n')\n expect(logWrites[messageIndex + 1]).toContain('Preparing the result')\n\n outputResult('{\"items\":1}')\n expect(stdoutWrite).toHaveBeenCalledWith('{\"items\":1}\\n')\n expect(stderrWrite).not.toHaveBeenCalled()\n\n const afterLog = writes.length\n finishTask()\n await instance.waitUntilExit()\n\n const cleanup = writes.slice(afterLog).join('')\n expect(cleanup).toContain(ansiEscapes.eraseLines(2))\n expect(unstyled(cleanup).trim()).toBe('')\n } finally {\n finishTask()\n instance.unmount()\n instance.cleanup()\n stdoutWrite.mockRestore()\n stderrWrite.mockRestore()\n vi.unstubAllGlobals()\n vi.unstubAllEnvs()\n terminal.destroy()\n }\n })\n})\n"]}
1
+ {"version":3,"file":"output.test.js","sourceRoot":"","sources":["../../../src/private/node/output.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import {InkLifecycleRoot} from './ui.js'\nimport {SingleTask} from './ui/components/SingleTask.js'\nimport {outputInfo, outputWarn, outputResult, TokenizedString, unstyled} from '../../public/node/output.js'\nimport React from 'react'\nimport {render} from 'ink'\nimport ansiEscapes from 'ansi-escapes'\nimport {describe, expect, test, vi} from 'vitest'\nimport {PassThrough} from 'stream'\n// Vitest's console omits the constructor used by Ink's console patch.\n// eslint-disable-next-line n/prefer-global/console\nimport {Console} from 'console'\n\n// Ink detects CI when imported; exercise terminal rendering on CI runners too.\nvi.hoisted(() => {\n vi.stubEnv('CI', 'false')\n vi.stubEnv('CONTINUOUS_INTEGRATION', 'false')\n})\nvi.mock('../../public/node/context/local.js', async (importOriginal) => ({\n ...(await importOriginal<typeof import('../../public/node/context/local.js')>()),\n isUnitTest: () => false,\n}))\n\ndescribe('logging during an Ink task', () => {\n test.each([outputInfo, outputWarn])('clears and redraws the task around %o', async (log) => {\n vi.stubGlobal('console', {...console, Console})\n const writes: string[] = []\n const terminal = Object.assign(new PassThrough(), {isTTY: true, columns: 80, rows: 24})\n terminal.on('data', (data: Buffer) => writes.push(data.toString()))\n let finishTask!: () => void\n const taskResult = new Promise<void>((resolve) => {\n finishTask = resolve\n })\n const stdoutWrite = vi.spyOn(process.stdout, 'write').mockReturnValue(true)\n const stderrWrite = vi.spyOn(process.stderr, 'write').mockReturnValue(true)\n const instance = render(\n <InkLifecycleRoot>\n <SingleTask title={new TokenizedString('Preparing the result')} task={() => taskResult} />\n </InkLifecycleRoot>,\n {\n stdout: terminal as unknown as NodeJS.WriteStream,\n stderr: terminal as unknown as NodeJS.WriteStream,\n patchConsole: true,\n exitOnCtrlC: false,\n },\n )\n\n try {\n await vi.waitFor(() => expect(writes.join('')).toContain('Preparing the result'))\n expect(writes.join('')).toContain('▀')\n const beforeLog = writes.length\n\n log('Prepared an item')\n\n // Clear both UI lines and their trailing newline before writing the log.\n const logWrites = writes.slice(beforeLog)\n const messageIndex = logWrites.findIndex((write) => write.includes('Prepared an item'))\n expect(messageIndex).toBeGreaterThan(0)\n expect(logWrites[messageIndex - 1]).toBe(ansiEscapes.eraseLines(3))\n expect(unstyled(logWrites[messageIndex]!)).toBe('Prepared an item\\n')\n expect(logWrites[messageIndex + 1]).toContain('Preparing the result')\n\n outputResult('{\"items\":1}')\n expect(stdoutWrite).toHaveBeenCalledWith('{\"items\":1}\\n')\n expect(stderrWrite).not.toHaveBeenCalled()\n\n const afterLog = writes.length\n finishTask()\n await instance.waitUntilExit()\n\n const cleanup = writes.slice(afterLog).join('')\n expect(cleanup).toContain(ansiEscapes.eraseLines(3))\n expect(unstyled(cleanup).trim()).toBe('')\n } finally {\n finishTask()\n instance.unmount()\n instance.cleanup()\n stdoutWrite.mockRestore()\n stderrWrite.mockRestore()\n vi.unstubAllGlobals()\n vi.unstubAllEnvs()\n terminal.destroy()\n }\n })\n})\n"]}
@@ -1,4 +1,3 @@
1
- import { ConcurrentOutputContext, getConcurrentOutputContext, useConcurrentOutputContext } from '../../output-context.js';
2
1
  import { type OutputProcess } from '../../../../public/node/output.js';
3
2
  import { AbortSignal } from '../../../../public/node/abort.js';
4
3
  import { FunctionComponent } from 'react';
@@ -14,6 +13,12 @@ export interface ConcurrentOutputProps {
14
13
  keepRunningAfterProcessesResolve?: boolean;
15
14
  useAlternativeColorPalette?: boolean;
16
15
  }
16
+ interface ConcurrentOutputContext {
17
+ outputPrefix?: string;
18
+ /** Controls ANSI stripping for terminal output. JSON diagnostics are always unstyled. */
19
+ stripAnsi?: boolean;
20
+ }
21
+ declare function useConcurrentOutputContext<T>(context: ConcurrentOutputContext, callback: () => T): T;
17
22
  /** Runs finite processes concurrently and routes their output through the shared diagnostic context. */
18
23
  export declare function runConcurrentProcessesForJson({ processes, abortSignal, }: Pick<ConcurrentOutputProps, 'processes' | 'abortSignal'>): Promise<void>;
19
24
  /**
@@ -50,4 +55,4 @@ export declare function runConcurrentProcessesForJson({ processes, abortSignal,
50
55
  * ```
51
56
  */
52
57
  declare const ConcurrentOutput: FunctionComponent<ConcurrentOutputProps>;
53
- export { ConcurrentOutput, ConcurrentOutputContext, getConcurrentOutputContext, useConcurrentOutputContext };
58
+ export { ConcurrentOutput, ConcurrentOutputContext, useConcurrentOutputContext };
@@ -1,4 +1,3 @@
1
- import { getConcurrentOutputContext, useConcurrentOutputContext } from '../../output-context.js';
2
1
  import { outputInfo } from '../../../../public/node/output.js';
3
2
  import { useComplete } from '../../ui.js';
4
3
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
@@ -6,6 +5,7 @@ import { Box, Static, Text } from 'ink';
6
5
  import figures from 'figures';
7
6
  import stripAnsi from 'strip-ansi';
8
7
  import { Writable } from 'stream';
8
+ import { AsyncLocalStorage } from 'node:async_hooks';
9
9
  import { StringDecoder } from 'node:string_decoder';
10
10
  function addLeadingZero(number) {
11
11
  if (number < 10) {
@@ -22,6 +22,10 @@ function currentTime() {
22
22
  const seconds = addLeadingZero(currentDateTime.getSeconds());
23
23
  return `${hours}:${minutes}:${seconds}`;
24
24
  }
25
+ const outputContextStore = new AsyncLocalStorage();
26
+ function useConcurrentOutputContext(context, callback) {
27
+ return outputContextStore.run(context, callback);
28
+ }
25
29
  /** Runs finite processes concurrently and routes their output through the shared diagnostic context. */
26
30
  export async function runConcurrentProcessesForJson({ processes, abortSignal, }) {
27
31
  await Promise.all(processes.map(async (process) => {
@@ -41,7 +45,7 @@ export async function runConcurrentProcessesForJson({ processes, abortSignal, })
41
45
  next();
42
46
  return;
43
47
  }
44
- const currentPrefix = getConcurrentOutputContext()?.outputPrefix ?? process.prefix;
48
+ const currentPrefix = outputContextStore.getStore()?.outputPrefix ?? process.prefix;
45
49
  if (!hasPendingLine)
46
50
  prefix = currentPrefix;
47
51
  const lines = (pending + decoder.write(chunk)).split(/\r?\n/);
@@ -137,7 +141,7 @@ const ConcurrentOutput = ({ processes, prefixColumnSize, abortSignal, showTimest
137
141
  const writableStream = useCallback((process, prefixes) => {
138
142
  return new Writable({
139
143
  write(chunk, _encoding, next) {
140
- const context = getConcurrentOutputContext();
144
+ const context = outputContextStore.getStore();
141
145
  const prefix = context?.outputPrefix ?? process.prefix;
142
146
  const shouldStripAnsi = context?.stripAnsi ?? true;
143
147
  const log = chunk.toString('utf8').replace(/(\n)$/, '');
@@ -207,5 +211,5 @@ const ConcurrentOutput = ({ processes, prefixColumnSize, abortSignal, showTimest
207
211
  line)))))));
208
212
  }));
209
213
  };
210
- export { ConcurrentOutput, getConcurrentOutputContext, useConcurrentOutputContext };
214
+ export { ConcurrentOutput, useConcurrentOutputContext };
211
215
  //# sourceMappingURL=ConcurrentOutput.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ConcurrentOutput.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/ConcurrentOutput.tsx"],"names":[],"mappings":"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","sourcesContent":["import {ConcurrentOutputContext, getConcurrentOutputContext, useConcurrentOutputContext} from '../../output-context.js'\nimport {outputInfo, type OutputProcess} from '../../../../public/node/output.js'\nimport {AbortSignal} from '../../../../public/node/abort.js'\nimport {useComplete} from '../../ui.js'\nimport React, {FunctionComponent, useCallback, useEffect, useMemo, useState} from 'react'\nimport {Box, Static, Text, TextProps} from 'ink'\nimport figures from 'figures'\nimport stripAnsi from 'strip-ansi'\n\nimport {Writable} from 'stream'\nimport {StringDecoder} from 'node:string_decoder'\n\nexport interface ConcurrentOutputProps {\n processes: OutputProcess[]\n prefixColumnSize?: number\n abortSignal: AbortSignal\n showTimestamps?: boolean\n /**\n * Keeps terminal UI running after all processes finish. Defaults to false.\n * In JSON mode, false uses finite progress/diagnostic events; true retains streaming terminal UI.\n */\n keepRunningAfterProcessesResolve?: boolean\n useAlternativeColorPalette?: boolean\n}\n\ninterface Chunk {\n color: TextProps['color']\n prefix: string\n lines: string[]\n}\n\nfunction addLeadingZero(number: number) {\n if (number < 10) {\n return `0${number}`\n } else {\n return number.toString()\n }\n}\n\nfunction currentTime() {\n const currentDateTime = new Date()\n const hours = addLeadingZero(currentDateTime.getHours())\n const minutes = addLeadingZero(currentDateTime.getMinutes())\n const seconds = addLeadingZero(currentDateTime.getSeconds())\n return `${hours}:${minutes}:${seconds}`\n}\n\n/** Runs finite processes concurrently and routes their output through the shared diagnostic context. */\nexport async function runConcurrentProcessesForJson({\n processes,\n abortSignal,\n}: Pick<ConcurrentOutputProps, 'processes' | 'abortSignal'>): Promise<void> {\n await Promise.all(\n processes.map(async (process) => {\n const createStream = () => {\n const decoder = new StringDecoder('utf8')\n let pending = ''\n let hasPendingLine = false\n let prefix = process.prefix\n const emitLine = (line: string) => {\n const message = stripAnsi(line)\n if (message.trim().length > 0) outputInfo(`${prefix}: ${message}`)\n }\n const stream = new Writable({\n write(chunk, _encoding, next) {\n if (chunk.length === 0) {\n next()\n return\n }\n const currentPrefix = getConcurrentOutputContext()?.outputPrefix ?? process.prefix\n if (!hasPendingLine) prefix = currentPrefix\n const lines = (pending + decoder.write(chunk)).split(/\\r?\\n/)\n pending = lines.pop() ?? ''\n for (const line of lines) {\n emitLine(line)\n prefix = currentPrefix\n }\n hasPendingLine = chunk[chunk.length - 1] !== 10\n next()\n },\n })\n return {\n stream,\n flush: () => {\n emitLine((pending + decoder.end()).replace(/\\r$/, ''))\n pending = ''\n hasPendingLine = false\n },\n }\n }\n const stdout = createStream()\n const stderr = createStream()\n try {\n await process.action(stdout.stream, stderr.stream, abortSignal)\n } finally {\n stdout.flush()\n stderr.flush()\n }\n }),\n )\n}\n\n/**\n * Renders output from concurrent processes to the terminal.\n * Output will be divided in a three column layout\n * with the left column containing the timestamp,\n * the right column containing the output,\n * and the middle column containing the process prefix.\n * Every process will be rendered with a different color, up to 4 colors.\n *\n * For example running `shopify app dev`:\n *\n * ```shell\n * 2022-10-10 13:11:03 | backend | npm\n * 2022-10-10 13:11:03 | backend | WARN ignoring workspace config at ...\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | backend | > shopify-app-template-node@0.1.0 dev\n * 2022-10-10 13:11:03 | backend | > cross-env NODE_ENV=development nodemon backend/index.js --watch ./backend\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | frontend |\n * 2022-10-10 13:11:03 | frontend | > starter-react-frontend-app@0.1.0 dev\n * 2022-10-10 13:11:03 | frontend | > cross-env NODE_ENV=development node vite-server.js\n * 2022-10-10 13:11:03 | frontend |\n * 2022-10-10 13:11:03 | frontend |\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | backend | [nodemon] to restart at any time, enter `rs`\n * 2022-10-10 13:11:03 | backend | [nodemon] watching path(s): backend/\n * 2022-10-10 13:11:03 | backend | [nodemon] watching extensions: js,mjs,json\n * 2022-10-10 13:11:03 | backend | [nodemon] starting `node backend/index.js`\n * 2022-10-10 13:11:03 | backend |\n *\n * ```\n */\nconst ConcurrentOutput: FunctionComponent<ConcurrentOutputProps> = ({\n processes,\n prefixColumnSize,\n abortSignal,\n showTimestamps = true,\n keepRunningAfterProcessesResolve = false,\n useAlternativeColorPalette = false,\n}) => {\n const [processOutput, setProcessOutput] = useState<Chunk[]>([])\n const [completionResult, setCompletionResult] = useState<{error?: Error} | null>(null)\n const complete = useComplete()\n const concurrentColors: TextProps['color'][] = useMemo(\n () =>\n useAlternativeColorPalette\n ? ['#b994c3', '#e69e19', '#d17a73', 'cyan', 'magenta', 'blue']\n : ['yellow', 'cyan', 'magenta', 'green', 'blue'],\n [useAlternativeColorPalette],\n )\n\n const calculatedPrefixColumnSize = useMemo(() => {\n const maxColumnSize = 25\n\n // If the prefixColumnSize is not provided, we calculate it based on the longest process prefix\n const columnSize =\n prefixColumnSize ??\n processes.reduce((maxPrefixLength, process) => Math.max(maxPrefixLength, process.prefix.length), 0)\n\n // Apply overall limit to the prefix column size\n return Math.min(columnSize, maxColumnSize)\n }, [processes, prefixColumnSize])\n\n const addPrefix = (prefix: string, prefixes: string[]) => {\n const index = prefixes.indexOf(prefix)\n if (index !== -1) {\n return index\n }\n prefixes.push(prefix)\n return prefixes.length - 1\n }\n\n const lineColor = useCallback(\n (index: number) => {\n const colorIndex = index % concurrentColors.length\n return concurrentColors[colorIndex]\n },\n [concurrentColors],\n )\n\n const writableStream = useCallback(\n (process: OutputProcess, prefixes: string[]) => {\n return new Writable({\n write(chunk, _encoding, next) {\n const context = getConcurrentOutputContext()\n const prefix = context?.outputPrefix ?? process.prefix\n const shouldStripAnsi = context?.stripAnsi ?? true\n const log = chunk.toString('utf8').replace(/(\\n)$/, '')\n\n const index = addPrefix(prefix, prefixes)\n\n const lines = shouldStripAnsi ? stripAnsi(log).split(/\\n/) : log.split(/\\n/)\n setProcessOutput((previousProcessOutput) => [\n ...previousProcessOutput,\n {\n color: lineColor(index),\n prefix,\n lines,\n },\n ])\n next()\n },\n })\n },\n [lineColor],\n )\n\n const formatPrefix = (prefix: string) => {\n // Truncate prefix if needed\n if (prefix.length > calculatedPrefixColumnSize) {\n return prefix.substring(0, calculatedPrefixColumnSize)\n }\n\n return `${' '.repeat(calculatedPrefixColumnSize - prefix.length)}${prefix}`\n }\n\n useEffect(() => {\n const runProcesses = async () => {\n const prefixes: string[] = []\n\n try {\n await Promise.all(\n processes.map(async (process) => {\n const stdout = writableStream(process, prefixes)\n const stderr = writableStream(process, prefixes)\n await process.action(stdout, stderr, abortSignal)\n }),\n )\n if (!keepRunningAfterProcessesResolve) {\n setCompletionResult({})\n }\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error: unknown) {\n if (!keepRunningAfterProcessesResolve) {\n setCompletionResult({error: error as Error})\n }\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n runProcesses()\n }, [abortSignal, processes, writableStream, keepRunningAfterProcessesResolve])\n\n useEffect(() => {\n if (completionResult !== null) {\n complete(completionResult.error)\n }\n }, [completionResult, complete])\n\n const {lineVertical} = figures\n\n return (\n <Static items={processOutput}>\n {(chunk, index) => {\n return (\n <Box flexDirection=\"column\" key={index}>\n {chunk.lines.map((line, index) => (\n <Box key={index} flexDirection=\"row\">\n <Text>\n {showTimestamps ? (\n <Text>\n {currentTime()} {lineVertical}{' '}\n </Text>\n ) : null}\n <Text color={chunk.color}>{formatPrefix(chunk.prefix)}</Text>\n <Text>\n {' '}\n {lineVertical} {line}\n </Text>\n </Text>\n </Box>\n ))}\n </Box>\n )\n }}\n </Static>\n )\n}\nexport {ConcurrentOutput, ConcurrentOutputContext, getConcurrentOutputContext, useConcurrentOutputContext}\n"]}
1
+ {"version":3,"file":"ConcurrentOutput.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/ConcurrentOutput.tsx"],"names":[],"mappings":"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","sourcesContent":["import {outputInfo, type OutputProcess} from '../../../../public/node/output.js'\nimport {AbortSignal} from '../../../../public/node/abort.js'\nimport {useComplete} from '../../ui.js'\nimport React, {FunctionComponent, useCallback, useEffect, useMemo, useState} from 'react'\nimport {Box, Static, Text, TextProps} from 'ink'\nimport figures from 'figures'\nimport stripAnsi from 'strip-ansi'\n\nimport {Writable} from 'stream'\nimport {AsyncLocalStorage} from 'node:async_hooks'\nimport {StringDecoder} from 'node:string_decoder'\n\nexport interface ConcurrentOutputProps {\n processes: OutputProcess[]\n prefixColumnSize?: number\n abortSignal: AbortSignal\n showTimestamps?: boolean\n /**\n * Keeps terminal UI running after all processes finish. Defaults to false.\n * In JSON mode, false uses finite progress/diagnostic events; true retains streaming terminal UI.\n */\n keepRunningAfterProcessesResolve?: boolean\n useAlternativeColorPalette?: boolean\n}\n\ninterface Chunk {\n color: TextProps['color']\n prefix: string\n lines: string[]\n}\n\nfunction addLeadingZero(number: number) {\n if (number < 10) {\n return `0${number}`\n } else {\n return number.toString()\n }\n}\n\nfunction currentTime() {\n const currentDateTime = new Date()\n const hours = addLeadingZero(currentDateTime.getHours())\n const minutes = addLeadingZero(currentDateTime.getMinutes())\n const seconds = addLeadingZero(currentDateTime.getSeconds())\n return `${hours}:${minutes}:${seconds}`\n}\n\ninterface ConcurrentOutputContext {\n outputPrefix?: string\n /** Controls ANSI stripping for terminal output. JSON diagnostics are always unstyled. */\n stripAnsi?: boolean\n}\n\nconst outputContextStore = new AsyncLocalStorage<ConcurrentOutputContext>()\n\nfunction useConcurrentOutputContext<T>(context: ConcurrentOutputContext, callback: () => T): T {\n return outputContextStore.run(context, callback)\n}\n\n/** Runs finite processes concurrently and routes their output through the shared diagnostic context. */\nexport async function runConcurrentProcessesForJson({\n processes,\n abortSignal,\n}: Pick<ConcurrentOutputProps, 'processes' | 'abortSignal'>): Promise<void> {\n await Promise.all(\n processes.map(async (process) => {\n const createStream = () => {\n const decoder = new StringDecoder('utf8')\n let pending = ''\n let hasPendingLine = false\n let prefix = process.prefix\n const emitLine = (line: string) => {\n const message = stripAnsi(line)\n if (message.trim().length > 0) outputInfo(`${prefix}: ${message}`)\n }\n const stream = new Writable({\n write(chunk, _encoding, next) {\n if (chunk.length === 0) {\n next()\n return\n }\n const currentPrefix = outputContextStore.getStore()?.outputPrefix ?? process.prefix\n if (!hasPendingLine) prefix = currentPrefix\n const lines = (pending + decoder.write(chunk)).split(/\\r?\\n/)\n pending = lines.pop() ?? ''\n for (const line of lines) {\n emitLine(line)\n prefix = currentPrefix\n }\n hasPendingLine = chunk[chunk.length - 1] !== 10\n next()\n },\n })\n return {\n stream,\n flush: () => {\n emitLine((pending + decoder.end()).replace(/\\r$/, ''))\n pending = ''\n hasPendingLine = false\n },\n }\n }\n const stdout = createStream()\n const stderr = createStream()\n try {\n await process.action(stdout.stream, stderr.stream, abortSignal)\n } finally {\n stdout.flush()\n stderr.flush()\n }\n }),\n )\n}\n\n/**\n * Renders output from concurrent processes to the terminal.\n * Output will be divided in a three column layout\n * with the left column containing the timestamp,\n * the right column containing the output,\n * and the middle column containing the process prefix.\n * Every process will be rendered with a different color, up to 4 colors.\n *\n * For example running `shopify app dev`:\n *\n * ```shell\n * 2022-10-10 13:11:03 | backend | npm\n * 2022-10-10 13:11:03 | backend | WARN ignoring workspace config at ...\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | backend | > shopify-app-template-node@0.1.0 dev\n * 2022-10-10 13:11:03 | backend | > cross-env NODE_ENV=development nodemon backend/index.js --watch ./backend\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | frontend |\n * 2022-10-10 13:11:03 | frontend | > starter-react-frontend-app@0.1.0 dev\n * 2022-10-10 13:11:03 | frontend | > cross-env NODE_ENV=development node vite-server.js\n * 2022-10-10 13:11:03 | frontend |\n * 2022-10-10 13:11:03 | frontend |\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | backend | [nodemon] to restart at any time, enter `rs`\n * 2022-10-10 13:11:03 | backend | [nodemon] watching path(s): backend/\n * 2022-10-10 13:11:03 | backend | [nodemon] watching extensions: js,mjs,json\n * 2022-10-10 13:11:03 | backend | [nodemon] starting `node backend/index.js`\n * 2022-10-10 13:11:03 | backend |\n *\n * ```\n */\nconst ConcurrentOutput: FunctionComponent<ConcurrentOutputProps> = ({\n processes,\n prefixColumnSize,\n abortSignal,\n showTimestamps = true,\n keepRunningAfterProcessesResolve = false,\n useAlternativeColorPalette = false,\n}) => {\n const [processOutput, setProcessOutput] = useState<Chunk[]>([])\n const [completionResult, setCompletionResult] = useState<{error?: Error} | null>(null)\n const complete = useComplete()\n const concurrentColors: TextProps['color'][] = useMemo(\n () =>\n useAlternativeColorPalette\n ? ['#b994c3', '#e69e19', '#d17a73', 'cyan', 'magenta', 'blue']\n : ['yellow', 'cyan', 'magenta', 'green', 'blue'],\n [useAlternativeColorPalette],\n )\n\n const calculatedPrefixColumnSize = useMemo(() => {\n const maxColumnSize = 25\n\n // If the prefixColumnSize is not provided, we calculate it based on the longest process prefix\n const columnSize =\n prefixColumnSize ??\n processes.reduce((maxPrefixLength, process) => Math.max(maxPrefixLength, process.prefix.length), 0)\n\n // Apply overall limit to the prefix column size\n return Math.min(columnSize, maxColumnSize)\n }, [processes, prefixColumnSize])\n\n const addPrefix = (prefix: string, prefixes: string[]) => {\n const index = prefixes.indexOf(prefix)\n if (index !== -1) {\n return index\n }\n prefixes.push(prefix)\n return prefixes.length - 1\n }\n\n const lineColor = useCallback(\n (index: number) => {\n const colorIndex = index % concurrentColors.length\n return concurrentColors[colorIndex]\n },\n [concurrentColors],\n )\n\n const writableStream = useCallback(\n (process: OutputProcess, prefixes: string[]) => {\n return new Writable({\n write(chunk, _encoding, next) {\n const context = outputContextStore.getStore()\n const prefix = context?.outputPrefix ?? process.prefix\n const shouldStripAnsi = context?.stripAnsi ?? true\n const log = chunk.toString('utf8').replace(/(\\n)$/, '')\n\n const index = addPrefix(prefix, prefixes)\n\n const lines = shouldStripAnsi ? stripAnsi(log).split(/\\n/) : log.split(/\\n/)\n setProcessOutput((previousProcessOutput) => [\n ...previousProcessOutput,\n {\n color: lineColor(index),\n prefix,\n lines,\n },\n ])\n next()\n },\n })\n },\n [lineColor],\n )\n\n const formatPrefix = (prefix: string) => {\n // Truncate prefix if needed\n if (prefix.length > calculatedPrefixColumnSize) {\n return prefix.substring(0, calculatedPrefixColumnSize)\n }\n\n return `${' '.repeat(calculatedPrefixColumnSize - prefix.length)}${prefix}`\n }\n\n useEffect(() => {\n const runProcesses = async () => {\n const prefixes: string[] = []\n\n try {\n await Promise.all(\n processes.map(async (process) => {\n const stdout = writableStream(process, prefixes)\n const stderr = writableStream(process, prefixes)\n await process.action(stdout, stderr, abortSignal)\n }),\n )\n if (!keepRunningAfterProcessesResolve) {\n setCompletionResult({})\n }\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error: unknown) {\n if (!keepRunningAfterProcessesResolve) {\n setCompletionResult({error: error as Error})\n }\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n runProcesses()\n }, [abortSignal, processes, writableStream, keepRunningAfterProcessesResolve])\n\n useEffect(() => {\n if (completionResult !== null) {\n complete(completionResult.error)\n }\n }, [completionResult, complete])\n\n const {lineVertical} = figures\n\n return (\n <Static items={processOutput}>\n {(chunk, index) => {\n return (\n <Box flexDirection=\"column\" key={index}>\n {chunk.lines.map((line, index) => (\n <Box key={index} flexDirection=\"row\">\n <Text>\n {showTimestamps ? (\n <Text>\n {currentTime()} {lineVertical}{' '}\n </Text>\n ) : null}\n <Text color={chunk.color}>{formatPrefix(chunk.prefix)}</Text>\n <Text>\n {' '}\n {lineVertical} {line}\n </Text>\n </Text>\n </Box>\n ))}\n </Box>\n )\n }}\n </Static>\n )\n}\nexport {ConcurrentOutput, ConcurrentOutputContext, useConcurrentOutputContext}\n"]}