@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.
- package/dist/private/node/api/graphql.js +0 -4
- package/dist/private/node/api/graphql.js.map +1 -1
- package/dist/private/node/api.d.ts +0 -2
- package/dist/private/node/api.js +22 -49
- package/dist/private/node/api.js.map +1 -1
- package/dist/private/node/command-json-schema.js +0 -1
- package/dist/private/node/command-json-schema.js.map +1 -1
- package/dist/private/node/output.test.js +4 -4
- package/dist/private/node/output.test.js.map +1 -1
- package/dist/private/node/ui/components/ConcurrentOutput.d.ts +7 -2
- package/dist/private/node/ui/components/ConcurrentOutput.js +8 -4
- package/dist/private/node/ui/components/ConcurrentOutput.js.map +1 -1
- package/dist/private/node/ui/components/LoadingBar.js +15 -9
- package/dist/private/node/ui/components/LoadingBar.js.map +1 -1
- package/dist/private/node/ui/components/LoadingBar.test.js +107 -45
- package/dist/private/node/ui/components/LoadingBar.test.js.map +1 -1
- package/dist/private/node/ui/components/TextAnimation.d.ts +10 -0
- package/dist/private/node/ui/components/TextAnimation.js +56 -0
- package/dist/private/node/ui/components/TextAnimation.js.map +1 -0
- package/dist/private/node/ui/components/TextAnimation.test.d.ts +1 -0
- package/dist/private/node/ui/components/TextAnimation.test.js +73 -0
- package/dist/private/node/ui/components/TextAnimation.test.js.map +1 -0
- package/dist/public/common/version.d.ts +1 -1
- package/dist/public/common/version.js +1 -1
- package/dist/public/common/version.js.map +1 -1
- package/dist/public/node/api/graphql.js +4 -29
- package/dist/public/node/api/graphql.js.map +1 -1
- package/dist/public/node/tree-kill.js +2 -12
- package/dist/public/node/tree-kill.js.map +1 -1
- package/dist/public/node/ui/components.d.ts +0 -1
- package/dist/public/node/ui/components.js +0 -1
- package/dist/public/node/ui/components.js.map +1 -1
- package/dist/public/node/ui.d.ts +2 -2
- package/dist/public/node/ui.js +2 -2
- package/dist/public/node/ui.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/dist/private/node/dev-instance-output.d.ts +0 -47
- package/dist/private/node/dev-instance-output.js +0 -143
- package/dist/private/node/dev-instance-output.js.map +0 -1
- package/dist/private/node/output-context.d.ts +0 -8
- package/dist/private/node/output-context.js +0 -10
- package/dist/private/node/output-context.js.map +0 -1
- package/dist/private/node/ui/components/LoadingIndicator.d.ts +0 -7
- package/dist/private/node/ui/components/LoadingIndicator.js +0 -22
- package/dist/private/node/ui/components/LoadingIndicator.js.map +0 -1
- package/dist/public/node/dev-instance.d.ts +0 -138
- package/dist/public/node/dev-instance.js +0 -365
- 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":"AAAA,OAAO,EAAC,kBAAkB,EAAE,sBAAsB,EAAC,MAAM,cAAc,CAAA;AACvE,OAAO,EAAC,WAAW,EAAC,MAAM,WAAW,CAAA;AACrC,OAAO,EAAC,gBAAgB,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAC,MAAM,gCAAgC,CAAA;AACxG,OAAO,EAAC,UAAU,EAAC,MAAM,+BAA+B,CAAA;AAExD,OAAO,EAAC,WAAW,EAAY,MAAM,iBAAiB,CAAA;AAEtD,MAAM,UAAU,mBAAmB,CACjC,GAAW,EACX,KAAa,EACb,GAAW,EACX,SAAqB,EACrB,UAAkC,EAAE;IAEpC,WAAW,CAAC,aAAa,CAAA,WAAW,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC;IACvD,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;EAC1C,SAAS,CAAC,CAAC,CAAC,sBAAsB,iBAAiB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;;EAEvE,sBAAsB,CAAC,OAAO,CAAC;KAC5B,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;AACxB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,SAAoB;IACpD,MAAM,MAAM,GAAc,EAAC,GAAG,SAAS,EAAC,CAAA;IACxC,MAAM,aAAa,GAAG,CAAC,QAAQ,EAAE,mBAAmB,EAAE,cAAc,EAAE,eAAe,CAAC,CAAA;IAEtF,MAAM,eAAe,GAAG,qBAAqB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;IAEpE,OAAO,IAAI,CAAC,SAAS,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;AACjD,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAc,EAAE,aAAuB;IACpE,oBAAoB;IACpB,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;YAChC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBAClD,MAAM,SAAS,GAAG,qBAAqB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;gBAC9D,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;YACxC,CAAC;YACD,qDAAqD;QACvD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,OAAO,KAAK,CAAA;IACd,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,qBAAqB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC,CAAA;IACxE,CAAC;IAED,MAAM,MAAM,GAAc,EAAE,CAAA;IAE5B,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,IAAI,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC3D,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,CAAA;YACrB,SAAQ;QACV,CAAC;QAED,MAAM,CAAC,GAAG,CAAC,GAAG,qBAAqB,CAAC,GAAG,EAAE,aAAa,CAAC,CAAA;IACzD,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;;;GAQG;AACH,8DAA8D;AAC9D,MAAM,UAAU,2BAA2B,CAAC,MAAyB;IACnE,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAA;IAEpD,MAAM,QAAQ,GAAa,EAAE,CAAA;IAE7B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,2DAA2D;QAC3D,MAAM,SAAS,GAAG,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,CAAA;QACvD,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrD,MAAM,WAAW,GAAa,EAAE,CAAA;YAChC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;gBACjC,MAAM,eAAe,GAAG,uBAAuB,CAAC,QAAQ,CAAC,CAAA;gBACzD,IAAI,eAAe,EAAE,CAAC;oBACpB,WAAW,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;gBACnC,CAAC;qBAAM,IAAI,OAAO,QAAQ,EAAE,OAAO,KAAK,QAAQ,EAAE,CAAC;oBACjD,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;gBACpC,CAAC;YACH,CAAC;YACD,+EAA+E;YAC/E,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3B,QAAQ,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAA;YAC/B,CAAC;iBAAM,IAAI,OAAO,KAAK,EAAE,OAAO,KAAK,QAAQ,EAAE,CAAC;gBAC9C,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;YAC9B,CAAC;QACH,CAAC;QACD,uCAAuC;aAClC,IAAI,OAAO,KAAK,EAAE,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC5C,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAC9B,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;AAC9D,CAAC;AAED;;;GAGG;AACH,8DAA8D;AAC9D,SAAS,uBAAuB,CAAC,QAAa;IAC5C,IAAI,QAAQ,EAAE,QAAQ,KAAK,eAAe,EAAE,CAAC;QAC3C,OAAO,wHAAwH,CAAA;IACjI,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,CAAC,KAAc,EAAE,SAAkB,EAAE,EAAE;QAC5C,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;YACjC,MAAM,EAAC,MAAM,EAAC,GAAG,KAAK,CAAC,QAAQ,CAAA;YAE/B,MAAM,iBAAiB,GAAG,2BAA2B,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;YAE5E,IAAI,YAAoB,CAAA;YACxB,IAAI,iBAAiB,IAAI,MAAM,GAAG,GAAG,EAAE,CAAC;gBACtC,YAAY,GAAG,iBAAiB,CAAA;YAClC,CAAC;iBAAM,IAAI,iBAAiB,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;gBAC9C,YAAY,GAAG,OAAO,GAAG,2CAA2C,MAAM,KAAK,iBAAiB,EAAE,CAAA;YACpG,CAAC;iBAAM,CAAC;gBACN,YAAY,GAAG,gBAAgB,CAAC,aAAa,CAAA;MAC/C,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,6CAChB,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,oBAAoB,MAAM,MAClD;;EAEN,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;SAChC,CAAC,CAAA;YACJ,CAAC;YACD,IAAI,SAAS,EAAE,CAAC;gBACd,YAAY,IAAI,mBAAmB,SAAS,EAAE,CAAA;YAChD,CAAC;YACD,IAAI,WAAkB,CAAA;YACtB,IAAI,MAAM,GAAG,GAAG,EAAE,CAAC;gBACjB,WAAW,GAAG,IAAI,kBAAkB,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;YACnF,CAAC;iBAAM,CAAC;gBACN,WAAW,GAAG,IAAI,UAAU,CAAC,YAAY,CAAC,CAAA;YAC5C,CAAC;YACD,WAAW,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAA;YAC/B,OAAO,WAAW,CAAA;QACpB,CAAC;aAAM,CAAC;YACN,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC,CAAA;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,IAAI,KAAK,YAAY,WAAW;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;IAC1G,IAAI,KAAK,YAAY,kBAAkB;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;IAC/F,OAAO,EAAE,CAAA;AACX,CAAC","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.
|
package/dist/private/node/api.js
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import { sanitizedHeadersOutput } from './api/headers.js';
|
|
2
2
|
import { sanitizeURL } from './api/urls.js';
|
|
3
|
-
import {
|
|
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
|
|
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
|
-
//
|
|
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.
|
|
217
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
353
|
-
|
|
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(
|
|
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":"AAAA,OAAO,EAAC,sBAAsB,EAAC,MAAM,kBAAkB,CAAA;AACvD,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAA;AACzC,OAAO,EAAC,gBAAgB,EAAC,MAAM,oCAAoC,CAAA;AACnE,OAAO,EAAC,qBAAqB,EAAC,MAAM,yBAAyB,CAAA;AAC7D,OAAO,EAAC,WAAW,EAAC,MAAM,6BAA6B,CAAA;AACvD,OAAO,EAAC,WAAW,EAAC,MAAM,gCAAgC,CAAA;AAG1D,OAAO,EAAC,WAAW,EAAC,MAAM,iBAAiB,CAAA;AAE3C,OAAO,EAAC,WAAW,EAAC,MAAM,YAAY,CAAA;AAItC,MAAM,CAAC,MAAM,OAAO,GAAU,CAAC,OAAO,EAAE,qBAAqB,EAAE,UAAU,EAAE,mBAAmB,EAAE,gBAAgB,CAAC,CAAA;AAEjH,MAAM,sBAAsB,GAAG,IAAI,CAAA;AACnC,MAAM,mBAAmB,GAAG,EAAE,CAAA;AAkB9B,MAAM,0BAA0B,GAAG,IAAI,GAAG,CAAC;IACzC,eAAe;IACf,cAAc;IACd,MAAM;IACN,cAAc;IACd,eAAe;IACf,aAAa;CACd,CAAC,CAAA;AAEF,SAAS,2BAA2B,CAAC,MAAc;IACjD,OAAO,0BAA0B,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;AAC/C,CAAC;AA8BD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACpD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,MAAM,sBAAsB,GAAG;YAC7B,gBAAgB;YAChB,YAAY;YACZ,cAAc;YACd,WAAW;YACX,aAAa;YACb,6BAA6B;YAC7B,WAAW;YACX,cAAc;YACd,WAAW;YACX,OAAO;YACP,2BAA2B;YAC3B,SAAS;YACT,iBAAiB;YACjB,aAAa;SACd,CAAA;QACD,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAA;QAChD,MAAM,UAAU,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAA;QACrG,MAAM,aAAa,GAAG,oCAAoC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;QAC7E,OAAO,UAAU,IAAI,aAAa,CAAA;IACpC,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,gDAAgD;IAChD,IAAI,uBAAuB,CAAC,KAAK,CAAC,EAAE,CAAC;QACnC,OAAO,IAAI,CAAA;IACb,CAAC;IAED,uEAAuE;IACvE,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,MAAM,6BAA6B,GAAG,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,CAAA;QACvF,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAA;QAChD,OAAO,6BAA6B,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAA;IAClG,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,+BAA+B,GAAG,CAAC,4BAA4B,EAAE,uBAAuB,CAAU,CAAA;AAExG;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAC3C,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY;QAAE,OAAO,IAAI,CAAA;IAC5C,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAA;IAChD,OAAO,+BAA+B,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAA;AAC5F,CAAC;AAED,KAAK,UAAU,+BAA+B,CAC5C,cAAiC;IAEjC,IAAI,CAAC,cAAc,CAAC,oBAAoB,EAAE,CAAC;QACzC,OAAO,cAAc,CAAC,OAAO,EAAE,CAAA;IACjC,CAAC;IAED,IAAI,aAAsB,CAAA;IAE1B,IAAI,KAAK,EAAE,MAAM,QAAQ,IAAI,qBAAqB,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,CAAC;QAClF,IAAI,CAAC;YACH,OAAO,MAAM,cAAc,CAAC,OAAO,EAAE,CAAA;QACvC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,aAAa,GAAG,GAAG,CAAA;YACnB,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,EAAE,CAAC;gBAClC,MAAM,GAAG,CAAA;YACX,CAAC;YAED,yBAAyB;YACzB,IAAI,cAAc,CAAC,oBAAoB,EAAE,CAAC;gBACxC,WAAW,CAAC,cAAc,CAAC,GAAG,EAAE,iBAAkB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAA;YAC5E,CAAC;YAED,WAAW,CAAC,uBAAuB,cAAc,CAAC,GAAG,yBAAyB,GAAG,EAAE,CAAC,CAAA;QACtF,CAAC;IACH,CAAC;IACD,MAAM,aAAa,CAAA;AACrB,CAAC;AAED,KAAK,UAAU,kBAAkB,CAC/B,cAAiC;IAEjC,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,EAAE,CAAA;IAC5B,IAAI,QAAQ,GAAG,CAAC,CAAA;IAChB,MAAM,eAAe,GAA2B,EAAE,CAAA;IAClD,MAAM,YAAY,GAAG,WAAW,CAAC,cAAc,CAAC,GAAG,CAAC,CAAA;IACpD,IAAI,QAAQ,GAAM,EAAO,CAAA;IACzB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,+BAA+B,CAAC,cAAc,CAAC,CAAA;QAChE,8DAA8D;QAC9D,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAU,EAAE,GAAQ,EAAE,EAAE;YAChD,IAAI,2BAA2B,CAAC,GAAG,CAAC;gBAAE,eAAe,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;QACpE,CAAC,CAAC,CAAA;QACF,qDAAqD;IACvD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,EAAE,CAAA;QAC5B,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,CAAA;QAE9B,IAAI,GAAG,YAAY,WAAW,EAAE,CAAC;YAC/B,IAAI,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACzB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,OAAqC,EAAE,CAAC;oBAC9E,IAAI,2BAA2B,CAAC,GAAG,CAAC;wBAAE,eAAe,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;gBACpE,CAAC;YACH,CAAC;YACD,MAAM,gBAAgB,GAAG,sBAAsB,CAAC,eAAe,CAAC,CAAA;YAEhE,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrB,IAAI,OAA2B,CAAA;gBAE/B,IAAI,CAAC;oBACH,OAAO,GAAG,eAAe,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,aAAa,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;oBAC1G,qDAAqD;gBACvD,CAAC;gBAAC,MAAM,CAAC;oBACP,iDAAiD;gBACnD,CAAC;gBACD,OAAO;oBACL,MAAM,EAAE,WAAW;oBACnB,WAAW,EAAE,GAAG;oBAChB,QAAQ;oBACR,gBAAgB;oBAChB,YAAY;oBACZ,SAAS,EAAE,eAAe,CAAC,cAAc,CAAC;oBAC1C,OAAO;iBACR,CAAA;YACH,CAAC;iBAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACvC,OAAO;oBACL,MAAM,EAAE,cAAc;oBACtB,WAAW,EAAE,GAAG;oBAChB,QAAQ;oBACR,gBAAgB;oBAChB,YAAY;oBACZ,SAAS,EAAE,eAAe,CAAC,cAAc,CAAC;oBAC1C,OAAO,EAAE,GAAG;iBACb,CAAA;YACH,CAAC;YAED,OAAO;gBACL,MAAM,EAAE,cAAc;gBACtB,WAAW,EAAE,GAAG;gBAChB,QAAQ;gBACR,gBAAgB;gBAChB,YAAY;gBACZ,SAAS,EAAE,eAAe,CAAC,cAAc,CAAC;aAC3C,CAAA;QACH,CAAC;QACD,OAAO;YACL,MAAM,EAAE,eAAe;YACvB,KAAK,EAAE,GAAG;YACV,QAAQ;YACR,gBAAgB,EAAE,sBAAsB,CAAC,eAAe,CAAC;YACzD,YAAY;YACZ,SAAS,EAAE,eAAe,CAAC,cAAc,CAAC;SAC3C,CAAA;IACH,CAAC;IACD,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,EAAE,CAAA;IAC5B,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,CAAA;IAC9B,OAAO;QACL,MAAM,EAAE,IAAI;QACZ,QAAQ;QACR,QAAQ;QACR,gBAAgB,EAAE,sBAAsB,CAAC,eAAe,CAAC;QACzD,YAAY;QACZ,SAAS,EAAE,eAAe,CAAC,cAAc,CAAC;KAC3C,CAAA;AACH,CAAC;AAED,0EAA0E;AAC1E,uEAAuE;AACvE,wEAAwE;AACxE,mCAAmC;AACnC,SAAS,WAAW,CAAC,KAAkB;IACrC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAClC,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,gBAAgB,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;AAChD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,cAAiC,EACjC,YAAyE;IAEzE,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,cAAc,CAAC,CAAA;IAEvD,WAAW,CAAC,cAAc,MAAM,CAAC,YAAY,iBAAiB,MAAM,CAAC,QAAQ;;EAE7E,MAAM,CAAC,gBAAgB;KACpB,CAAC,CAAA;IAEJ,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC;QACtB,KAAK,IAAI,CAAC,CAAC,CAAC;YACV,OAAO,MAAM,CAAC,QAAQ,CAAA;QACxB,CAAC;QACD,KAAK,cAAc,CAAC,CAAC,CAAC;YACpB,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;YAC1D,CAAC;iBAAM,CAAC;gBACN,MAAM,MAAM,CAAC,WAAW,CAAA;YAC1B,CAAC;QACH,CAAC;QACD,KAAK,eAAe,CAAC,CAAC,CAAC;YACrB,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,YAAY,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;YACpD,CAAC;iBAAM,CAAC;gBACN,MAAM,MAAM,CAAC,KAAK,CAAA;YACpB,CAAC;QACH,CAAC;QACD,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;YAC1D,CAAC;iBAAM,CAAC;gBACN,MAAM,MAAM,CAAC,WAAW,CAAA;YAC1B,CAAC;QACH,CAAC;QACD,KAAK,cAAc,CAAC,CAAC,CAAC;YACpB,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;YAC1D,CAAC;iBAAM,CAAC;gBACN,MAAM,MAAM,CAAC,WAAW,CAAA;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,cAAiC,EACjC,YAAyE,EACzE,eAII;IACF,aAAa,EAAE,UAAU;CAC1B;IAED,IAAI,WAAW,GAAG,CAAC,CAAA;IACnB,MAAM,cAAc,GAAG,YAAY,CAAC,cAAc,IAAI,mBAAmB,CAAA;IAEzE,IAAI,MAAM,GAAG,MAAM,kBAAkB,CAAC,cAAc,CAAC,CAAA;IAErD,WAAW,CAAC,cAAc,MAAM,CAAC,YAAY,iBAAiB,MAAM,CAAC,QAAQ;;EAE7E,MAAM,CAAC,gBAAgB;KACpB,CAAC,CAAA;IAEJ,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;YAC3B,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;gBACpB,WAAW,CAAC,cAAc,MAAM,CAAC,YAAY,oBAAoB,WAAW,UAAU,CAAC,CAAA;YACzF,CAAC;YACD,OAAO,MAAM,CAAC,QAAQ,CAAA;QACxB,CAAC;aAAM,IAAI,MAAM,CAAC,MAAM,KAAK,cAAc,EAAE,CAAC;YAC5C,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;YAC1D,CAAC;iBAAM,CAAC;gBACN,MAAM,MAAM,CAAC,WAAW,CAAA;YAC1B,CAAC;QACH,CAAC;aAAM,IAAI,MAAM,CAAC,MAAM,KAAK,eAAe,EAAE,CAAC;YAC7C,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,YAAY,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;YACpD,CAAC;iBAAM,CAAC;gBACN,MAAM,MAAM,CAAC,KAAK,CAAA;YACpB,CAAC;QACH,CAAC;aAAM,IAAI,MAAM,CAAC,MAAM,KAAK,cAAc,EAAE,CAAC;YAC5C,MAAM,MAAM,CAAC,WAAW,CAAA;QAC1B,CAAC;QAED,IAAI,cAAc,IAAI,WAAW,EAAE,CAAC;YAClC,WAAW,CAAC,GAAG,cAAc,qCAAqC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAA;YACxF,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;YAC1D,CAAC;iBAAM,CAAC;gBACN,MAAM,MAAM,CAAC,WAAW,CAAA;YAC1B,CAAC;QACH,CAAC;QACD,WAAW,IAAI,CAAC,CAAA;QAEhB,yBAAyB;QACzB,IAAI,cAAc,CAAC,oBAAoB,EAAE,CAAC;YACxC,WAAW,CAAC,cAAc,CAAC,GAAG,EAAE,cAAc,WAAW,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAA;QAChF,CAAC;QAED,uGAAuG;QACvG,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,IAAI,YAAY,CAAC,cAAc,IAAI,sBAAsB,CAAA;QAC5F,WAAW,CAAC,6BAA6B,WAAW,OAAO,MAAM,CAAC,YAAY,OAAO,YAAY,KAAK,CAAC,CAAA;QAEvG,4CAA4C;QAC5C,MAAM,GAAG,MAAM,IAAI,OAAO,CAAqB,CAAC,OAAO,EAAE,EAAE;YACzD,YAAY,CAAC,aAAa,CAAC,GAAG,EAAE;gBAC9B,OAAO,CAAC,kBAAkB,CAAC,cAAc,CAAC,CAAC,CAAA;YAC7C,CAAC,EAAE,YAAY,CAAC,CAAA;QAClB,CAAC,CAAC,CAAA;IACJ,CAAC;AACH,CAAC","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,
|
|
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(
|
|
42
|
+
expect(writes.join('')).toContain('▀');
|
|
43
43
|
const beforeLog = writes.length;
|
|
44
44
|
log('Prepared an item');
|
|
45
|
-
// Clear
|
|
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(
|
|
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(
|
|
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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
|
|
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,
|
|
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 =
|
|
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 =
|
|
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,
|
|
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"]}
|