@shopify/cli-kit 3.48.0 → 3.48.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/private/node/api/graphql.js +8 -1
  2. package/dist/private/node/api/graphql.js.map +1 -1
  3. package/dist/private/node/api/headers.js +1 -1
  4. package/dist/private/node/api/headers.js.map +1 -1
  5. package/dist/private/node/api/urls.d.ts +6 -0
  6. package/dist/private/node/api/urls.js +16 -0
  7. package/dist/private/node/api/urls.js.map +1 -0
  8. package/dist/private/node/api.js +2 -1
  9. package/dist/private/node/api.js.map +1 -1
  10. package/dist/private/node/ui/components/ConcurrentOutput.d.ts +17 -6
  11. package/dist/private/node/ui/components/ConcurrentOutput.js +50 -12
  12. package/dist/private/node/ui/components/ConcurrentOutput.js.map +1 -1
  13. package/dist/private/node/ui/components/ConcurrentOutput.test.js +17 -9
  14. package/dist/private/node/ui/components/ConcurrentOutput.test.js.map +1 -1
  15. package/dist/private/node/ui.d.ts +1 -1
  16. package/dist/private/node/ui.js +3 -3
  17. package/dist/private/node/ui.js.map +1 -1
  18. package/dist/public/common/version.d.ts +1 -1
  19. package/dist/public/common/version.js +1 -1
  20. package/dist/public/common/version.js.map +1 -1
  21. package/dist/public/node/api/partners.js +11 -2
  22. package/dist/public/node/api/partners.js.map +1 -1
  23. package/dist/public/node/base-command.d.ts +1 -0
  24. package/dist/public/node/base-command.js +3 -0
  25. package/dist/public/node/base-command.js.map +1 -1
  26. package/dist/public/node/hooks/postrun.js +23 -0
  27. package/dist/public/node/hooks/postrun.js.map +1 -1
  28. package/dist/public/node/http.js +5 -2
  29. package/dist/public/node/http.js.map +1 -1
  30. package/dist/public/node/node-package-manager.d.ts +2 -0
  31. package/dist/public/node/node-package-manager.js +5 -2
  32. package/dist/public/node/node-package-manager.js.map +1 -1
  33. package/dist/public/node/system.js +1 -1
  34. package/dist/public/node/system.js.map +1 -1
  35. package/dist/public/node/tree-kill.d.ts +6 -0
  36. package/dist/public/node/tree-kill.js +12 -0
  37. package/dist/public/node/tree-kill.js.map +1 -0
  38. package/dist/public/node/ui.d.ts +4 -1
  39. package/dist/public/node/ui.js +6 -4
  40. package/dist/public/node/ui.js.map +1 -1
  41. package/dist/tsconfig.tsbuildinfo +1 -1
  42. package/package.json +3 -2
  43. package/dist/private/node/tree-kill.d.ts +0 -1
  44. package/dist/private/node/tree-kill.js +0 -7
  45. package/dist/private/node/tree-kill.js.map +0 -1
@@ -5,11 +5,18 @@ import { ClientError } from 'graphql-request';
5
5
  export function debugLogRequestInfo(api, query, variables, headers = {}) {
6
6
  outputDebug(outputContent `Sending ${outputToken.json(api)} GraphQL request:
7
7
  ${outputToken.raw(query.toString().trim())}
8
- ${variables ? `\nWith variables:\n${JSON.stringify(variables, null, 2)}\n` : ''}
8
+ ${variables ? `\nWith variables:\n${sanitizeVariables(variables)}\n` : ''}
9
9
  With request headers:
10
10
  ${sanitizedHeadersOutput(headers)}
11
11
  `);
12
12
  }
13
+ function sanitizeVariables(variables) {
14
+ const result = { ...variables };
15
+ if ('apiKey' in result) {
16
+ result.apiKey = '*****';
17
+ }
18
+ return JSON.stringify(result, null, 2);
19
+ }
13
20
  export function errorHandler(api) {
14
21
  return (error) => {
15
22
  if (error instanceof ClientError) {
@@ -1 +1 @@
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,gBAAgB,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAC,MAAM,gCAAgC,CAAA;AACxG,OAAO,EAAC,UAAU,EAAC,MAAM,+BAA+B,CAAA;AACxD,OAAO,EAAC,WAAW,EAA6B,MAAM,iBAAiB,CAAA;AAEvE,MAAM,UAAU,mBAAmB,CACjC,GAAW,EACX,KAAsB,EACtB,SAAqB,EACrB,UAAmC,EAAE;IAErC,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,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;;EAE7E,sBAAsB,CAAC,OAAO,CAAC;CAChC,CAAC,CAAA;AACF,CAAC;AAED,MAAM,UAAU,YAAY,CAAI,GAAW;IACzC,OAAO,CAAC,KAAc,EAAE,EAAE;QACxB,IAAI,KAAK,YAAY,WAAW,EAAE;YAChC,MAAM,YAAY,GAAG,gBAAgB,CAAC,aAAa,CAAA;QACjD,WAAW,CAAC,GAAG,CACnB,GAAG,CACJ,8DAA8D,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE;;IAEvF,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;OACpC,CAAC,CAAA;YACF,IAAI,WAAkB,CAAA;YACtB,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE;gBAC/B,WAAW,GAAG,IAAI,kBAAkB,CAAC,YAAY,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;aACjG;iBAAM;gBACL,WAAW,GAAG,IAAI,UAAU,CAAC,YAAY,CAAC,CAAA;aAC3C;YACD,WAAW,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAA;YAC/B,OAAO,WAAW,CAAA;SACnB;aAAM;YACL,OAAO,KAAK,CAAA;SACb;IACH,CAAC,CAAA;AACH,CAAC","sourcesContent":["import {GraphQLClientError, sanitizedHeadersOutput} from './headers.js'\nimport {stringifyMessage, outputContent, outputToken, outputDebug} from '../../../public/node/output.js'\nimport {AbortError} from '../../../public/node/error.js'\nimport {ClientError, RequestDocument, Variables} from 'graphql-request'\n\nexport function debugLogRequestInfo<T>(\n api: string,\n query: RequestDocument,\n variables?: Variables,\n headers: {[key: string]: string} = {},\n) {\n outputDebug(outputContent`Sending ${outputToken.json(api)} GraphQL request:\n ${outputToken.raw(query.toString().trim())}\n${variables ? `\\nWith variables:\\n${JSON.stringify(variables, null, 2)}\\n` : ''}\nWith request headers:\n${sanitizedHeadersOutput(headers)}\n`)\n}\n\nexport function errorHandler<T>(api: string): (error: unknown) => Error | unknown {\n return (error: unknown) => {\n if (error instanceof ClientError) {\n const errorMessage = stringifyMessage(outputContent`\n The ${outputToken.raw(\n api,\n )} GraphQL API responded unsuccessfully with the HTTP status ${`${error.response.status}`} and errors:\n\n ${outputToken.json(error.response.errors)}\n `)\n let mappedError: Error\n if (error.response.status < 500) {\n mappedError = new GraphQLClientError(errorMessage, error.response.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"]}
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,gBAAgB,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAC,MAAM,gCAAgC,CAAA;AACxG,OAAO,EAAC,UAAU,EAAC,MAAM,+BAA+B,CAAA;AACxD,OAAO,EAAC,WAAW,EAA6B,MAAM,iBAAiB,CAAA;AAEvE,MAAM,UAAU,mBAAmB,CACjC,GAAW,EACX,KAAsB,EACtB,SAAqB,EACrB,UAAmC,EAAE;IAErC,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;CAChC,CAAC,CAAA;AACF,CAAC;AAED,SAAS,iBAAiB,CAAC,SAAoB;IAC7C,MAAM,MAAM,GAAc,EAAC,GAAG,SAAS,EAAC,CAAA;IACxC,IAAI,QAAQ,IAAI,MAAM,EAAE;QACtB,MAAM,CAAC,MAAM,GAAG,OAAO,CAAA;KACxB;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;AACxC,CAAC;AAED,MAAM,UAAU,YAAY,CAAI,GAAW;IACzC,OAAO,CAAC,KAAc,EAAE,EAAE;QACxB,IAAI,KAAK,YAAY,WAAW,EAAE;YAChC,MAAM,YAAY,GAAG,gBAAgB,CAAC,aAAa,CAAA;QACjD,WAAW,CAAC,GAAG,CACnB,GAAG,CACJ,8DAA8D,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE;;IAEvF,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;OACpC,CAAC,CAAA;YACF,IAAI,WAAkB,CAAA;YACtB,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE;gBAC/B,WAAW,GAAG,IAAI,kBAAkB,CAAC,YAAY,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;aACjG;iBAAM;gBACL,WAAW,GAAG,IAAI,UAAU,CAAC,YAAY,CAAC,CAAA;aAC3C;YACD,WAAW,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAA;YAC/B,OAAO,WAAW,CAAA;SACnB;aAAM;YACL,OAAO,KAAK,CAAA;SACb;IACH,CAAC,CAAA;AACH,CAAC","sourcesContent":["import {GraphQLClientError, sanitizedHeadersOutput} from './headers.js'\nimport {stringifyMessage, outputContent, outputToken, outputDebug} from '../../../public/node/output.js'\nimport {AbortError} from '../../../public/node/error.js'\nimport {ClientError, RequestDocument, Variables} from 'graphql-request'\n\nexport function debugLogRequestInfo<T>(\n api: string,\n query: RequestDocument,\n variables?: Variables,\n headers: {[key: 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`)\n}\n\nfunction sanitizeVariables(variables: Variables): string {\n const result: Variables = {...variables}\n if ('apiKey' in result) {\n result.apiKey = '*****'\n }\n return JSON.stringify(result, null, 2)\n}\n\nexport function errorHandler<T>(api: string): (error: unknown) => Error | unknown {\n return (error: unknown) => {\n if (error instanceof ClientError) {\n const errorMessage = stringifyMessage(outputContent`\n The ${outputToken.raw(\n api,\n )} GraphQL API responded unsuccessfully with the HTTP status ${`${error.response.status}`} and errors:\n\n ${outputToken.json(error.response.errors)}\n `)\n let mappedError: Error\n if (error.response.status < 500) {\n mappedError = new GraphQLClientError(errorMessage, error.response.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"]}
@@ -23,7 +23,7 @@ export class GraphQLClientError extends RequestClientError {
23
23
  */
24
24
  export function sanitizedHeadersOutput(headers) {
25
25
  const sanitized = {};
26
- const keywords = ['token', 'authorization'];
26
+ const keywords = ['token', 'authorization', 'subject_token'];
27
27
  Object.keys(headers).forEach((header) => {
28
28
  if (keywords.find((keyword) => header.toLocaleLowerCase().includes(keyword)) === undefined) {
29
29
  sanitized[header] = headers[header];
@@ -1 +1 @@
1
- {"version":3,"file":"headers.js","sourceRoot":"","sources":["../../../../src/private/node/api/headers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,mCAAmC,CAAA;AACjE,OAAO,EAAC,aAAa,EAAC,MAAM,uCAAuC,CAAA;AACnE,OAAO,EAAC,WAAW,EAAE,kBAAkB,EAAC,MAAM,uBAAuB,CAAA;AACrE,OAAO,EAAC,eAAe,EAAC,MAAM,+BAA+B,CAAA;AAC7D,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,MAAM,OAAO,kBAAmB,SAAQ,eAAe;IAErD,YAAmB,OAAe,EAAE,UAAkB;QACpD,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;IAC9B,CAAC;CACF;AACD,MAAM,OAAO,kBAAmB,SAAQ,kBAAkB;IAIxD,8DAA8D;IAC9D,YAAmB,OAAe,EAAE,UAAkB,EAAE,MAAc;QACpE,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;QAC1B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAgC;IACrE,MAAM,SAAS,GAA4B,EAAE,CAAA;IAC7C,MAAM,QAAQ,GAAG,CAAC,OAAO,EAAE,eAAe,CAAC,CAAA;IAC3C,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;QACtC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,SAAS,EAAE;YAC1F,SAAS,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAE,CAAA;SACrC;IACH,CAAC,CAAC,CAAA;IACF,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;SAC1B,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QACd,OAAO,MAAM,MAAM,KAAK,SAAS,CAAC,MAAM,CAAC,EAAE,CAAA;IAC7C,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAA;AACf,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,MAAM,SAAS,GAAG,kBAAkB,eAAe,EAAE,CAAA;IAErD,MAAM,OAAO,GAA+B;QAC1C,YAAY,EAAE,SAAS;QACvB,YAAY,EAAE,YAAY;QAC1B,0DAA0D;QAC1D,oBAAoB,EAAE,OAAO,CAAC,QAAQ;QACtC,cAAc,EAAE,kBAAkB;QAClC,GAAG,CAAC,aAAa,EAAE,IAAI,EAAC,wBAAwB,EAAE,GAAG,EAAC,CAAC;KACxD,CAAA;IACD,IAAI,KAAK,EAAE;QACT,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,KAAK,EAAE,CAAA;QACxE,wCAAwC;QACxC,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,CAAA;QACrC,OAAO,CAAC,wBAAwB,CAAC,GAAG,UAAU,CAAA;KAC/C;IAED,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC;QACrB,kBAAkB,EAAE,MAAM,gCAAgC,EAAE;QAC5D,SAAS,EAAE,IAAI;KAChB,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,KAAK,UAAU,gCAAgC;IAC7C,OAAO,CAAC,MAAM,kBAAkB,EAAE,CAAC,KAAK,WAAW,CAAC,IAAI,CAAA;AAC1D,CAAC","sourcesContent":["import {CLI_KIT_VERSION} from '../../../public/common/version.js'\nimport {firstPartyDev} from '../../../public/node/context/local.js'\nimport {Environment, serviceEnvironment} from '../context/service.js'\nimport {ExtendableError} from '../../../public/node/error.js'\nimport https from 'https'\n\nexport class RequestClientError extends ExtendableError {\n statusCode: number\n public constructor(message: string, statusCode: number) {\n super(message)\n this.statusCode = statusCode\n }\n}\nexport class GraphQLClientError extends RequestClientError {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n errors?: any[]\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n public constructor(message: string, statusCode: number, errors?: any[]) {\n super(message, statusCode)\n this.errors = errors\n }\n}\n\n/**\n * Removes the sensitive data from the headers and outputs them as a string.\n * @param headers - HTTP headers.\n * @returns A sanitized version of the headers as a string.\n */\nexport function sanitizedHeadersOutput(headers: {[key: string]: string}): string {\n const sanitized: {[key: string]: string} = {}\n const keywords = ['token', 'authorization']\n Object.keys(headers).forEach((header) => {\n if (keywords.find((keyword) => header.toLocaleLowerCase().includes(keyword)) === undefined) {\n sanitized[header] = headers[header]!\n }\n })\n return Object.keys(sanitized)\n .map((header) => {\n return ` - ${header}: ${sanitized[header]}`\n })\n .join('\\n')\n}\n\nexport function buildHeaders(token?: string): {[key: string]: string} {\n const userAgent = `Shopify CLI; v=${CLI_KIT_VERSION}`\n\n const headers: {[header: string]: string} = {\n 'User-Agent': userAgent,\n 'Keep-Alive': 'timeout=30',\n // 'Sec-CH-UA': secCHUA, This header requires the Git sha.\n 'Sec-CH-UA-PLATFORM': process.platform,\n 'Content-Type': 'application/json',\n ...(firstPartyDev() && {'X-Shopify-Cli-Employee': '1'}),\n }\n if (token) {\n const authString = token.startsWith('shpat') ? token : `Bearer ${token}`\n // eslint-disable-next-line dot-notation\n headers['authorization'] = authString\n headers['X-Shopify-Access-Token'] = authString\n }\n\n return headers\n}\n\n/**\n * This utility function returns the https.Agent to use for a given service. The agent\n * includes the right configuration based on the service's environment. For example,\n * if the service is running in a Spin environment, the attribute \"rejectUnauthorized\" is\n * set to false\n */\nexport async function httpsAgent(): Promise<https.Agent> {\n return new https.Agent({\n rejectUnauthorized: await shouldRejectUnauthorizedRequests(),\n keepAlive: true,\n })\n}\n\n/**\n * Spin stores the CA certificate in the keychain and it should be used when sending HTTP\n * requests to Spin instances. However, Node doesn't read certificates from the Keychain\n * by default, which leads to Shopifolks running into issues that they workaround by setting the\n * NODE_TLS_REJECT_UNAUTHORIZED=0 environment variable, which applies to all the HTTP\n * requests sent from the CLI (context: https://github.com/nodejs/node/issues/39657)\n * This utility function allows controlling the behavior in a per-service level by returning\n * the value of for the \"rejectUnauthorized\" attribute that's used in the https agent.\n *\n * @returns A promise that resolves with a boolean indicating whether\n * unauthorized requests should be rejected or not.\n */\nasync function shouldRejectUnauthorizedRequests(): Promise<boolean> {\n return (await serviceEnvironment()) !== Environment.Spin\n}\n"]}
1
+ {"version":3,"file":"headers.js","sourceRoot":"","sources":["../../../../src/private/node/api/headers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,mCAAmC,CAAA;AACjE,OAAO,EAAC,aAAa,EAAC,MAAM,uCAAuC,CAAA;AACnE,OAAO,EAAC,WAAW,EAAE,kBAAkB,EAAC,MAAM,uBAAuB,CAAA;AACrE,OAAO,EAAC,eAAe,EAAC,MAAM,+BAA+B,CAAA;AAC7D,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,MAAM,OAAO,kBAAmB,SAAQ,eAAe;IAErD,YAAmB,OAAe,EAAE,UAAkB;QACpD,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;IAC9B,CAAC;CACF;AACD,MAAM,OAAO,kBAAmB,SAAQ,kBAAkB;IAIxD,8DAA8D;IAC9D,YAAmB,OAAe,EAAE,UAAkB,EAAE,MAAc;QACpE,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;QAC1B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAgC;IACrE,MAAM,SAAS,GAA4B,EAAE,CAAA;IAC7C,MAAM,QAAQ,GAAG,CAAC,OAAO,EAAE,eAAe,EAAE,eAAe,CAAC,CAAA;IAC5D,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;QACtC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,SAAS,EAAE;YAC1F,SAAS,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAE,CAAA;SACrC;IACH,CAAC,CAAC,CAAA;IACF,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;SAC1B,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QACd,OAAO,MAAM,MAAM,KAAK,SAAS,CAAC,MAAM,CAAC,EAAE,CAAA;IAC7C,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAA;AACf,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,MAAM,SAAS,GAAG,kBAAkB,eAAe,EAAE,CAAA;IAErD,MAAM,OAAO,GAA+B;QAC1C,YAAY,EAAE,SAAS;QACvB,YAAY,EAAE,YAAY;QAC1B,0DAA0D;QAC1D,oBAAoB,EAAE,OAAO,CAAC,QAAQ;QACtC,cAAc,EAAE,kBAAkB;QAClC,GAAG,CAAC,aAAa,EAAE,IAAI,EAAC,wBAAwB,EAAE,GAAG,EAAC,CAAC;KACxD,CAAA;IACD,IAAI,KAAK,EAAE;QACT,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,KAAK,EAAE,CAAA;QACxE,wCAAwC;QACxC,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,CAAA;QACrC,OAAO,CAAC,wBAAwB,CAAC,GAAG,UAAU,CAAA;KAC/C;IAED,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC;QACrB,kBAAkB,EAAE,MAAM,gCAAgC,EAAE;QAC5D,SAAS,EAAE,IAAI;KAChB,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,KAAK,UAAU,gCAAgC;IAC7C,OAAO,CAAC,MAAM,kBAAkB,EAAE,CAAC,KAAK,WAAW,CAAC,IAAI,CAAA;AAC1D,CAAC","sourcesContent":["import {CLI_KIT_VERSION} from '../../../public/common/version.js'\nimport {firstPartyDev} from '../../../public/node/context/local.js'\nimport {Environment, serviceEnvironment} from '../context/service.js'\nimport {ExtendableError} from '../../../public/node/error.js'\nimport https from 'https'\n\nexport class RequestClientError extends ExtendableError {\n statusCode: number\n public constructor(message: string, statusCode: number) {\n super(message)\n this.statusCode = statusCode\n }\n}\nexport class GraphQLClientError extends RequestClientError {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n errors?: any[]\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n public constructor(message: string, statusCode: number, errors?: any[]) {\n super(message, statusCode)\n this.errors = errors\n }\n}\n\n/**\n * Removes the sensitive data from the headers and outputs them as a string.\n * @param headers - HTTP headers.\n * @returns A sanitized version of the headers as a string.\n */\nexport function sanitizedHeadersOutput(headers: {[key: string]: string}): string {\n const sanitized: {[key: string]: string} = {}\n const keywords = ['token', 'authorization', 'subject_token']\n Object.keys(headers).forEach((header) => {\n if (keywords.find((keyword) => header.toLocaleLowerCase().includes(keyword)) === undefined) {\n sanitized[header] = headers[header]!\n }\n })\n return Object.keys(sanitized)\n .map((header) => {\n return ` - ${header}: ${sanitized[header]}`\n })\n .join('\\n')\n}\n\nexport function buildHeaders(token?: string): {[key: string]: string} {\n const userAgent = `Shopify CLI; v=${CLI_KIT_VERSION}`\n\n const headers: {[header: string]: string} = {\n 'User-Agent': userAgent,\n 'Keep-Alive': 'timeout=30',\n // 'Sec-CH-UA': secCHUA, This header requires the Git sha.\n 'Sec-CH-UA-PLATFORM': process.platform,\n 'Content-Type': 'application/json',\n ...(firstPartyDev() && {'X-Shopify-Cli-Employee': '1'}),\n }\n if (token) {\n const authString = token.startsWith('shpat') ? token : `Bearer ${token}`\n // eslint-disable-next-line dot-notation\n headers['authorization'] = authString\n headers['X-Shopify-Access-Token'] = authString\n }\n\n return headers\n}\n\n/**\n * This utility function returns the https.Agent to use for a given service. The agent\n * includes the right configuration based on the service's environment. For example,\n * if the service is running in a Spin environment, the attribute \"rejectUnauthorized\" is\n * set to false\n */\nexport async function httpsAgent(): Promise<https.Agent> {\n return new https.Agent({\n rejectUnauthorized: await shouldRejectUnauthorizedRequests(),\n keepAlive: true,\n })\n}\n\n/**\n * Spin stores the CA certificate in the keychain and it should be used when sending HTTP\n * requests to Spin instances. However, Node doesn't read certificates from the Keychain\n * by default, which leads to Shopifolks running into issues that they workaround by setting the\n * NODE_TLS_REJECT_UNAUTHORIZED=0 environment variable, which applies to all the HTTP\n * requests sent from the CLI (context: https://github.com/nodejs/node/issues/39657)\n * This utility function allows controlling the behavior in a per-service level by returning\n * the value of for the \"rejectUnauthorized\" attribute that's used in the https agent.\n *\n * @returns A promise that resolves with a boolean indicating whether\n * unauthorized requests should be rejected or not.\n */\nasync function shouldRejectUnauthorizedRequests(): Promise<boolean> {\n return (await serviceEnvironment()) !== Environment.Spin\n}\n"]}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Removes the sensitive data from the url and outputs them as a string.
3
+ * @param url - HTTP headers.
4
+ * @returns A sanitized version of the url as a string.
5
+ */
6
+ export declare function sanitizeURL(url: string): string;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Removes the sensitive data from the url and outputs them as a string.
3
+ * @param url - HTTP headers.
4
+ * @returns A sanitized version of the url as a string.
5
+ */
6
+ export function sanitizeURL(url) {
7
+ const parsedUrl = new URL(url);
8
+ if (parsedUrl.searchParams.has('subject_token')) {
9
+ parsedUrl.searchParams.set('subject_token', '****');
10
+ }
11
+ if (parsedUrl.searchParams.has('token')) {
12
+ parsedUrl.searchParams.set('token', '****');
13
+ }
14
+ return parsedUrl.toString();
15
+ }
16
+ //# sourceMappingURL=urls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"urls.js","sourceRoot":"","sources":["../../../../src/private/node/api/urls.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;IAC9B,IAAI,SAAS,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE;QAC/C,SAAS,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,CAAA;KACpD;IACD,IAAI,SAAS,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE;QACvC,SAAS,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;KAC5C;IACD,OAAO,SAAS,CAAC,QAAQ,EAAE,CAAA;AAC7B,CAAC","sourcesContent":["/**\n * Removes the sensitive data from the url and outputs them as a string.\n * @param url - HTTP headers.\n * @returns A sanitized version of the url as a string.\n */\nexport function sanitizeURL(url: string): string {\n const parsedUrl = new URL(url)\n if (parsedUrl.searchParams.has('subject_token')) {\n parsedUrl.searchParams.set('subject_token', '****')\n }\n if (parsedUrl.searchParams.has('token')) {\n parsedUrl.searchParams.set('token', '****')\n }\n return parsedUrl.toString()\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import { sanitizedHeadersOutput } from './api/headers.js';
2
+ import { sanitizeURL } from './api/urls.js';
2
3
  import { outputDebug } from '@shopify/cli-kit/node/output';
3
4
  import { ClientError } from 'graphql-request';
4
5
  import { performance } from 'perf_hooks';
@@ -34,7 +35,7 @@ export async function debugLogResponseInfo({ request, url }, errorHandler) {
34
35
  }
35
36
  finally {
36
37
  const t1 = performance.now();
37
- outputDebug(`Request to ${url} completed in ${Math.round(t1 - t0)} ms
38
+ outputDebug(`Request to ${sanitizeURL(url)} completed in ${Math.round(t1 - t0)} ms
38
39
  With response headers:
39
40
  ${sanitizedHeadersOutput(responseHeaders)}
40
41
  `);
@@ -1 +1 @@
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,8BAA8B,CAAA;AAExD,OAAO,EAAC,WAAW,EAAC,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAC,WAAW,EAAC,MAAM,YAAY,CAAA;AAItC,MAAM,CAAC,MAAM,OAAO,GAAU,CAAC,OAAO,EAAE,qBAAqB,EAAE,UAAU,EAAE,mBAAmB,CAAC,CAAA;AAO/F,MAAM,0BAA0B,GAAG,IAAI,GAAG,CAAC,CAAC,eAAe,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC,CAAA;AAErG,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,EAAC,OAAO,EAAE,GAAG,EAAoB,EACjC,YAAkD;IAElD,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,EAAE,CAAA;IAC5B,MAAM,eAAe,GAA4B,EAAE,CAAA;IACnD,IAAI,QAAQ,GAAM,EAAO,CAAA;IACzB,IAAI;QACF,QAAQ,GAAG,MAAM,OAAO,CAAA;QACxB,8DAA8D;QAC9D,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAU,EAAE,GAAQ,EAAE,EAAE;YAChD,IAAI,0BAA0B,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,eAAe,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;QACvE,CAAC,CAAC,CAAA;KACH;IAAC,OAAO,GAAG,EAAE;QACZ,IAAI,GAAG,YAAY,WAAW,EAAE;YAC9B,IAAI,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE;gBACzB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,OAAqC,EAAE;oBAC9E,IAAI,0BAA0B,CAAC,GAAG,CAAC,GAAG,CAAC;wBAAE,eAAe,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;iBACtE;aACF;SACF;QACD,IAAI,YAAY,EAAE;YAChB,MAAM,YAAY,CAAC,GAAG,CAAC,CAAA;SACxB;aAAM;YACL,MAAM,GAAG,CAAA;SACV;KACF;YAAS;QACR,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,EAAE,CAAA;QAC5B,WAAW,CAAC,cAAc,GAAG,iBAAiB,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC;;EAEnE,sBAAsB,CAAC,eAAe,CAAC;KACpC,CAAC,CAAA;KACH;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC","sourcesContent":["import {sanitizedHeadersOutput} from './api/headers.js'\nimport {outputDebug} from '@shopify/cli-kit/node/output'\nimport {Headers} from 'form-data'\nimport {ClientError} from 'graphql-request'\nimport {performance} from 'perf_hooks'\n\nexport type API = 'admin' | 'storefront-renderer' | 'partners' | 'business-platform'\n\nexport const allAPIs: API[] = ['admin', 'storefront-renderer', 'partners', 'business-platform']\n\ninterface RequestOptions<T> {\n request: Promise<T>\n url: string\n}\n\nconst interestingResponseHeaders = new Set(['cache-control', 'content-type', 'etag', 'x-request-id'])\n\nexport async function debugLogResponseInfo<T extends {headers: Headers; status: number}>(\n {request, url}: RequestOptions<T>,\n errorHandler?: (error: unknown) => Error | unknown,\n): Promise<T> {\n const t0 = performance.now()\n const responseHeaders: {[key: string]: string} = {}\n let response: T = {} as T\n try {\n response = await request\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n response.headers.forEach((value: any, key: any) => {\n if (interestingResponseHeaders.has(key)) responseHeaders[key] = value\n })\n } catch (err) {\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 (interestingResponseHeaders.has(key)) responseHeaders[key] = value\n }\n }\n }\n if (errorHandler) {\n throw errorHandler(err)\n } else {\n throw err\n }\n } finally {\n const t1 = performance.now()\n outputDebug(`Request to ${url} completed in ${Math.round(t1 - t0)} ms\nWith response headers:\n${sanitizedHeadersOutput(responseHeaders)}\n `)\n }\n return response\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,WAAW,EAAC,MAAM,8BAA8B,CAAA;AAExD,OAAO,EAAC,WAAW,EAAC,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAC,WAAW,EAAC,MAAM,YAAY,CAAA;AAItC,MAAM,CAAC,MAAM,OAAO,GAAU,CAAC,OAAO,EAAE,qBAAqB,EAAE,UAAU,EAAE,mBAAmB,CAAC,CAAA;AAO/F,MAAM,0BAA0B,GAAG,IAAI,GAAG,CAAC,CAAC,eAAe,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC,CAAA;AAErG,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,EAAC,OAAO,EAAE,GAAG,EAAoB,EACjC,YAAkD;IAElD,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,EAAE,CAAA;IAC5B,MAAM,eAAe,GAA4B,EAAE,CAAA;IACnD,IAAI,QAAQ,GAAM,EAAO,CAAA;IACzB,IAAI;QACF,QAAQ,GAAG,MAAM,OAAO,CAAA;QACxB,8DAA8D;QAC9D,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAU,EAAE,GAAQ,EAAE,EAAE;YAChD,IAAI,0BAA0B,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,eAAe,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;QACvE,CAAC,CAAC,CAAA;KACH;IAAC,OAAO,GAAG,EAAE;QACZ,IAAI,GAAG,YAAY,WAAW,EAAE;YAC9B,IAAI,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE;gBACzB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,OAAqC,EAAE;oBAC9E,IAAI,0BAA0B,CAAC,GAAG,CAAC,GAAG,CAAC;wBAAE,eAAe,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;iBACtE;aACF;SACF;QACD,IAAI,YAAY,EAAE;YAChB,MAAM,YAAY,CAAC,GAAG,CAAC,CAAA;SACxB;aAAM;YACL,MAAM,GAAG,CAAA;SACV;KACF;YAAS;QACR,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,EAAE,CAAA;QAC5B,WAAW,CAAC,cAAc,WAAW,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC;;EAEhF,sBAAsB,CAAC,eAAe,CAAC;KACpC,CAAC,CAAA;KACH;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC","sourcesContent":["import {sanitizedHeadersOutput} from './api/headers.js'\nimport {sanitizeURL} from './api/urls.js'\nimport {outputDebug} from '@shopify/cli-kit/node/output'\nimport {Headers} from 'form-data'\nimport {ClientError} from 'graphql-request'\nimport {performance} from 'perf_hooks'\n\nexport type API = 'admin' | 'storefront-renderer' | 'partners' | 'business-platform'\n\nexport const allAPIs: API[] = ['admin', 'storefront-renderer', 'partners', 'business-platform']\n\ninterface RequestOptions<T> {\n request: Promise<T>\n url: string\n}\n\nconst interestingResponseHeaders = new Set(['cache-control', 'content-type', 'etag', 'x-request-id'])\n\nexport async function debugLogResponseInfo<T extends {headers: Headers; status: number}>(\n {request, url}: RequestOptions<T>,\n errorHandler?: (error: unknown) => Error | unknown,\n): Promise<T> {\n const t0 = performance.now()\n const responseHeaders: {[key: string]: string} = {}\n let response: T = {} as T\n try {\n response = await request\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n response.headers.forEach((value: any, key: any) => {\n if (interestingResponseHeaders.has(key)) responseHeaders[key] = value\n })\n } catch (err) {\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 (interestingResponseHeaders.has(key)) responseHeaders[key] = value\n }\n }\n }\n if (errorHandler) {\n throw errorHandler(err)\n } else {\n throw err\n }\n } finally {\n const t1 = performance.now()\n outputDebug(`Request to ${sanitizeURL(url)} completed in ${Math.round(t1 - t0)} ms\nWith response headers:\n${sanitizedHeadersOutput(responseHeaders)}\n `)\n }\n return response\n}\n"]}
@@ -1,23 +1,34 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  import { OutputProcess } from '../../../../public/node/output.js';
3
- import { AbortSignal } from '../../../../public/node/abort.js';
3
+ import { AbortController } from '../../../../public/node/abort.js';
4
4
  import { FunctionComponent } from 'react';
5
5
  import { Key } from 'ink';
6
6
  import { Writable } from 'stream';
7
7
  export type WritableStream = (process: OutputProcess, index: number) => Writable;
8
+ export interface Footer {
9
+ shortcuts: Shortcut[];
10
+ subTitle?: string;
11
+ }
12
+ export interface FooterContext {
13
+ footer?: Footer;
14
+ updateShortcut: (prevShortcut: Shortcut, newShortcut: Shortcut) => void;
15
+ updateSubTitle: (subTitle: string) => void;
16
+ }
8
17
  interface Shortcut {
9
18
  key: string;
10
19
  action: string;
20
+ syncer?: (footerContext: FooterContext, abortController: AbortController) => void;
21
+ state?: {
22
+ [key: string]: unknown;
23
+ };
11
24
  }
12
25
  export interface ConcurrentOutputProps {
13
26
  processes: OutputProcess[];
14
- abortSignal: AbortSignal;
27
+ abortController: AbortController;
15
28
  showTimestamps?: boolean;
16
29
  onInput?: (input: string, key: Key, exit: () => void) => void;
17
- footer?: {
18
- shortcuts: Shortcut[];
19
- subTitle?: string;
20
- };
30
+ onInputAsync?: (input: string, key: Key, exit: () => void, footerContext: FooterContext) => Promise<void>;
31
+ footer?: Footer;
21
32
  keepRunningAfterProcessesResolve?: boolean;
22
33
  }
23
34
  /**
@@ -1,6 +1,6 @@
1
+ import { treeKill } from '../../../../public/node/tree-kill.js';
1
2
  import { handleCtrlC } from '../../ui.js';
2
3
  import { addOrUpdateConcurrentUIEventOutput } from '../../demo-recorder.js';
3
- import { treeKill } from '../../tree-kill.js';
4
4
  import useAbortSignal from '../hooks/use-abort-signal.js';
5
5
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
6
6
  import { Box, Static, Text, useInput, useStdin, useApp } from 'ink';
@@ -60,12 +60,13 @@ function currentTime() {
60
60
  *
61
61
  * ```
62
62
  */
63
- const ConcurrentOutput = ({ processes, abortSignal, showTimestamps = true, onInput, footer, keepRunningAfterProcessesResolve, }) => {
63
+ const ConcurrentOutput = ({ processes, abortController, showTimestamps = true, onInput, onInputAsync, footer, keepRunningAfterProcessesResolve, }) => {
64
64
  const [processOutput, setProcessOutput] = useState([]);
65
65
  const { exit: unmountInk } = useApp();
66
66
  const prefixColumnSize = Math.max(...processes.map((process) => process.prefix.length));
67
67
  const { isRawModeSupported } = useStdin();
68
68
  const [state, setState] = useState(ConcurrentOutputState.Running);
69
+ const [footerContent, setFooterContent] = useState(footer);
69
70
  const concurrentColors = useMemo(() => ['yellow', 'cyan', 'magenta', 'green', 'blue'], []);
70
71
  const lineColor = useCallback((index) => {
71
72
  const colorIndex = index < concurrentColors.length ? index : index % concurrentColors.length;
@@ -88,19 +89,56 @@ const ConcurrentOutput = ({ processes, abortSignal, showTimestamps = true, onInp
88
89
  },
89
90
  });
90
91
  }, [footer, lineColor]);
91
- const { isAborted } = useAbortSignal(abortSignal);
92
+ const { isAborted } = useAbortSignal(abortController.signal);
92
93
  const useShortcuts = isRawModeSupported && state === ConcurrentOutputState.Running && !isAborted;
94
+ const updateShortcut = (prevShortcut, newShortcut) => {
95
+ if (!footerContent)
96
+ return;
97
+ const newFooterContent = { ...footerContent };
98
+ newFooterContent.shortcuts.map((short) => {
99
+ if (short.key === prevShortcut.key) {
100
+ short.action = newShortcut.action;
101
+ short.key = newShortcut.key;
102
+ short.state = newShortcut.state;
103
+ }
104
+ });
105
+ setFooterContent(newFooterContent);
106
+ };
107
+ const updateSubTitle = (subTitle) => {
108
+ if (!footerContent)
109
+ return;
110
+ setFooterContent({ ...footerContent, subTitle });
111
+ };
112
+ const runShortcutSyncs = () => {
113
+ footerContent?.shortcuts?.forEach((shortcut) => {
114
+ if (shortcut.syncer)
115
+ shortcut.syncer({ footer: footerContent, updateShortcut, updateSubTitle }, abortController);
116
+ });
117
+ };
93
118
  useInput((input, key) => {
94
- handleCtrlC(input, key);
95
- onInput(input, key, () => treeKill('SIGINT'));
96
- }, { isActive: typeof onInput !== 'undefined' && useShortcuts });
119
+ const exit = abortController ? () => abortController.abort() : () => treeKill('SIGINT');
120
+ handleCtrlC(input, key, exit);
121
+ const triggerOnInput = async () => {
122
+ if (onInput) {
123
+ onInput(input, key, exit);
124
+ }
125
+ else if (onInputAsync) {
126
+ await onInputAsync(input, key, exit, { footer: footerContent, updateShortcut, updateSubTitle });
127
+ }
128
+ };
129
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
130
+ triggerOnInput();
131
+ }, { isActive: (typeof onInput !== 'undefined' || typeof onInputAsync !== 'undefined') && useShortcuts });
132
+ useEffect(() => {
133
+ runShortcutSyncs();
134
+ }, []);
97
135
  useEffect(() => {
98
136
  ;
99
137
  (() => {
100
138
  return Promise.all(processes.map(async (process, index) => {
101
139
  const stdout = writableStream(process, index);
102
140
  const stderr = writableStream(process, index);
103
- await process.action(stdout, stderr, abortSignal);
141
+ await process.action(stdout, stderr, abortController.signal);
104
142
  }));
105
143
  })()
106
144
  .then(() => {
@@ -113,7 +151,7 @@ const ConcurrentOutput = ({ processes, abortSignal, showTimestamps = true, onInp
113
151
  setState(ConcurrentOutputState.Stopped);
114
152
  unmountInk(error);
115
153
  });
116
- }, [abortSignal, processes, writableStream, unmountInk, keepRunningAfterProcessesResolve]);
154
+ }, [abortController, processes, writableStream, unmountInk, keepRunningAfterProcessesResolve]);
117
155
  const { lineVertical } = figures;
118
156
  return (React.createElement(React.Fragment, null,
119
157
  React.createElement(Static, { items: processOutput }, (chunk, index) => {
@@ -133,8 +171,8 @@ const ConcurrentOutput = ({ processes, abortSignal, showTimestamps = true, onInp
133
171
  " ",
134
172
  line)))))));
135
173
  }),
136
- footer ? (React.createElement(Box, { marginY: 1, flexDirection: "column", flexGrow: 1 },
137
- useShortcuts ? (React.createElement(Box, { flexDirection: "column" }, footer.shortcuts.map((shortcut, index) => (React.createElement(Text, { key: index },
174
+ footerContent && !isAborted ? (React.createElement(Box, { marginY: 1, paddingTop: 1, flexDirection: "column", flexGrow: 1, borderStyle: "single", borderBottom: false, borderLeft: false, borderRight: false, borderTop: true },
175
+ useShortcuts ? (React.createElement(Box, { flexDirection: "column" }, footerContent.shortcuts.map((shortcut, index) => (React.createElement(Text, { key: index },
138
176
  figures.pointerSmall,
139
177
  " Press ",
140
178
  React.createElement(Text, { bold: true }, shortcut.key),
@@ -142,8 +180,8 @@ const ConcurrentOutput = ({ processes, abortSignal, showTimestamps = true, onInp
142
180
  figures.lineVertical,
143
181
  " ",
144
182
  shortcut.action))))) : null,
145
- footer.subTitle ? (React.createElement(Box, { marginTop: useShortcuts ? 1 : 0 },
146
- React.createElement(Text, null, footer.subTitle))) : null)) : null));
183
+ footerContent.subTitle ? (React.createElement(Box, { marginTop: useShortcuts ? 1 : 0 },
184
+ React.createElement(Text, null, footerContent.subTitle))) : null)) : null));
147
185
  };
148
186
  export { ConcurrentOutput };
149
187
  //# 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 {OutputProcess} from '../../../../public/node/output.js'\nimport {AbortSignal} from '../../../../public/node/abort.js'\nimport {handleCtrlC} from '../../ui.js'\nimport {addOrUpdateConcurrentUIEventOutput} from '../../demo-recorder.js'\nimport {treeKill} from '../../tree-kill.js'\nimport useAbortSignal from '../hooks/use-abort-signal.js'\nimport React, {FunctionComponent, useCallback, useEffect, useMemo, useState} from 'react'\nimport {Box, Key, Static, Text, useInput, TextProps, useStdin, useApp} from 'ink'\nimport stripAnsi from 'strip-ansi'\nimport figures from 'figures'\nimport {Writable} from 'stream'\n\nexport type WritableStream = (process: OutputProcess, index: number) => Writable\n\ninterface Shortcut {\n key: string\n action: string\n}\nexport interface ConcurrentOutputProps {\n processes: OutputProcess[]\n abortSignal: AbortSignal\n showTimestamps?: boolean\n onInput?: (input: string, key: Key, exit: () => void) => void\n footer?: {\n shortcuts: Shortcut[]\n subTitle?: string\n }\n // If set, the component is not automatically unmounted once the processes have all finished\n keepRunningAfterProcessesResolve?: boolean\n}\ninterface Chunk {\n color: TextProps['color']\n prefix: string\n lines: string[]\n}\n\nenum ConcurrentOutputState {\n Running = 'running',\n Stopped = 'stopped',\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\n const hours = addLeadingZero(currentDateTime.getHours())\n const minutes = addLeadingZero(currentDateTime.getMinutes())\n const seconds = addLeadingZero(currentDateTime.getSeconds())\n\n return `${hours}:${minutes}:${seconds}`\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 abortSignal,\n showTimestamps = true,\n onInput,\n footer,\n keepRunningAfterProcessesResolve,\n}) => {\n const [processOutput, setProcessOutput] = useState<Chunk[]>([])\n const {exit: unmountInk} = useApp()\n const prefixColumnSize = Math.max(...processes.map((process) => process.prefix.length))\n const {isRawModeSupported} = useStdin()\n const [state, setState] = useState<ConcurrentOutputState>(ConcurrentOutputState.Running)\n const concurrentColors: TextProps['color'][] = useMemo(() => ['yellow', 'cyan', 'magenta', 'green', 'blue'], [])\n const lineColor = useCallback(\n (index: number) => {\n const colorIndex = index < concurrentColors.length ? index : index % concurrentColors.length\n return concurrentColors[colorIndex]!\n },\n [concurrentColors],\n )\n\n const writableStream = useCallback(\n (process: OutputProcess, index: number) => {\n return new Writable({\n write(chunk, _encoding, next) {\n const lines = stripAnsi(chunk.toString('utf8').replace(/(\\n)$/, '')).split(/\\n/)\n addOrUpdateConcurrentUIEventOutput({prefix: process.prefix, index, output: lines.join('\\n')}, {footer})\n\n setProcessOutput((previousProcessOutput) => [\n ...previousProcessOutput,\n {\n color: lineColor(index),\n prefix: process.prefix,\n lines,\n },\n ])\n\n next()\n },\n })\n },\n [footer, lineColor],\n )\n\n const {isAborted} = useAbortSignal(abortSignal)\n const useShortcuts = isRawModeSupported && state === ConcurrentOutputState.Running && !isAborted\n\n useInput(\n (input, key) => {\n handleCtrlC(input, key)\n\n onInput!(input, key, () => treeKill('SIGINT'))\n },\n {isActive: typeof onInput !== 'undefined' && useShortcuts},\n )\n\n useEffect(() => {\n ;(() => {\n return Promise.all(\n processes.map(async (process, index) => {\n const stdout = writableStream(process, index)\n const stderr = writableStream(process, index)\n\n await process.action(stdout, stderr, abortSignal)\n }),\n )\n })()\n .then(() => {\n if (!keepRunningAfterProcessesResolve) {\n setState(ConcurrentOutputState.Stopped)\n unmountInk()\n }\n })\n .catch((error) => {\n setState(ConcurrentOutputState.Stopped)\n unmountInk(error)\n })\n }, [abortSignal, processes, writableStream, unmountInk, keepRunningAfterProcessesResolve])\n\n const {lineVertical} = figures\n\n return (\n <>\n <Static items={processOutput}>\n {(chunk, index) => {\n const prefixBuffer = ' '.repeat(prefixColumnSize - chunk.prefix.length)\n return (\n <Box flexDirection=\"column\" key={index}>\n {chunk.lines.map((line, index) => (\n <Box key={index} flexDirection=\"row\">\n <Text color={chunk.color}>\n {showTimestamps ? (\n <Text>\n {currentTime()} {lineVertical}{' '}\n </Text>\n ) : null}\n\n <Text>\n {chunk.prefix}\n {prefixBuffer} {lineVertical} {line}\n </Text>\n </Text>\n </Box>\n ))}\n </Box>\n )\n }}\n </Static>\n {footer ? (\n <Box marginY={1} flexDirection=\"column\" flexGrow={1}>\n {useShortcuts ? (\n <Box flexDirection=\"column\">\n {footer.shortcuts.map((shortcut, index) => (\n <Text key={index}>\n {figures.pointerSmall} Press <Text bold>{shortcut.key}</Text> {figures.lineVertical} {shortcut.action}\n </Text>\n ))}\n </Box>\n ) : null}\n {footer.subTitle ? (\n <Box marginTop={useShortcuts ? 1 : 0}>\n <Text>{footer.subTitle}</Text>\n </Box>\n ) : null}\n </Box>\n ) : null}\n </>\n )\n}\n\nexport {ConcurrentOutput}\n"]}
1
+ {"version":3,"file":"ConcurrentOutput.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/ConcurrentOutput.tsx"],"names":[],"mappings":"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","sourcesContent":["import {OutputProcess} from '../../../../public/node/output.js'\nimport {AbortController} from '../../../../public/node/abort.js'\nimport {treeKill} from '../../../../public/node/tree-kill.js'\nimport {handleCtrlC} from '../../ui.js'\nimport {addOrUpdateConcurrentUIEventOutput} from '../../demo-recorder.js'\nimport useAbortSignal from '../hooks/use-abort-signal.js'\nimport React, {FunctionComponent, useCallback, useEffect, useMemo, useState} from 'react'\nimport {Box, Key, Static, Text, useInput, TextProps, useStdin, useApp} from 'ink'\nimport stripAnsi from 'strip-ansi'\nimport figures from 'figures'\nimport {Writable} from 'stream'\n\nexport type WritableStream = (process: OutputProcess, index: number) => Writable\n\nexport interface Footer {\n shortcuts: Shortcut[]\n subTitle?: string\n}\n\nexport interface FooterContext {\n footer?: Footer\n updateShortcut: (prevShortcut: Shortcut, newShortcut: Shortcut) => void\n updateSubTitle: (subTitle: string) => void\n}\n\ninterface Shortcut {\n key: string\n action: string\n syncer?: (footerContext: FooterContext, abortController: AbortController) => void\n state?: {\n [key: string]: unknown\n }\n}\nexport interface ConcurrentOutputProps {\n processes: OutputProcess[]\n abortController: AbortController\n showTimestamps?: boolean\n onInput?: (input: string, key: Key, exit: () => void) => void\n onInputAsync?: (input: string, key: Key, exit: () => void, footerContext: FooterContext) => Promise<void>\n footer?: Footer\n // If set, the component is not automatically unmounted once the processes have all finished\n keepRunningAfterProcessesResolve?: boolean\n}\ninterface Chunk {\n color: TextProps['color']\n prefix: string\n lines: string[]\n}\nenum ConcurrentOutputState {\n Running = 'running',\n Stopped = 'stopped',\n}\nfunction addLeadingZero(number: number) {\n if (number < 10) {\n return `0${number}`\n } else {\n return number.toString()\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 * 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 abortController,\n showTimestamps = true,\n onInput,\n onInputAsync,\n footer,\n keepRunningAfterProcessesResolve,\n}) => {\n const [processOutput, setProcessOutput] = useState<Chunk[]>([])\n const {exit: unmountInk} = useApp()\n const prefixColumnSize = Math.max(...processes.map((process) => process.prefix.length))\n const {isRawModeSupported} = useStdin()\n const [state, setState] = useState<ConcurrentOutputState>(ConcurrentOutputState.Running)\n const [footerContent, setFooterContent] = useState<Footer | undefined>(footer)\n const concurrentColors: TextProps['color'][] = useMemo(() => ['yellow', 'cyan', 'magenta', 'green', 'blue'], [])\n const lineColor = useCallback(\n (index: number) => {\n const colorIndex = index < concurrentColors.length ? index : index % concurrentColors.length\n return concurrentColors[colorIndex]!\n },\n [concurrentColors],\n )\n const writableStream = useCallback(\n (process: OutputProcess, index: number) => {\n return new Writable({\n write(chunk, _encoding, next) {\n const lines = stripAnsi(chunk.toString('utf8').replace(/(\\n)$/, '')).split(/\\n/)\n addOrUpdateConcurrentUIEventOutput({prefix: process.prefix, index, output: lines.join('\\n')}, {footer})\n setProcessOutput((previousProcessOutput) => [\n ...previousProcessOutput,\n {\n color: lineColor(index),\n prefix: process.prefix,\n lines,\n },\n ])\n next()\n },\n })\n },\n [footer, lineColor],\n )\n const {isAborted} = useAbortSignal(abortController.signal)\n const useShortcuts = isRawModeSupported && state === ConcurrentOutputState.Running && !isAborted\n\n const updateShortcut = (prevShortcut: Shortcut, newShortcut: Shortcut) => {\n if (!footerContent) return\n const newFooterContent = {...footerContent}\n\n newFooterContent.shortcuts.map((short) => {\n if (short.key === prevShortcut.key) {\n short.action = newShortcut.action\n short.key = newShortcut.key\n short.state = newShortcut.state\n }\n })\n setFooterContent(newFooterContent)\n }\n\n const updateSubTitle = (subTitle: string) => {\n if (!footerContent) return\n\n setFooterContent({...footerContent, subTitle})\n }\n\n const runShortcutSyncs = () => {\n footerContent?.shortcuts?.forEach((shortcut) => {\n if (shortcut.syncer) shortcut.syncer({footer: footerContent, updateShortcut, updateSubTitle}, abortController)\n })\n }\n\n useInput(\n (input, key) => {\n const exit = abortController ? () => abortController.abort() : () => treeKill('SIGINT')\n\n handleCtrlC(input, key, exit)\n\n const triggerOnInput = async () => {\n if (onInput) {\n onInput(input, key, exit)\n } else if (onInputAsync) {\n await onInputAsync(input, key, exit, {footer: footerContent, updateShortcut, updateSubTitle})\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n triggerOnInput()\n },\n {isActive: (typeof onInput !== 'undefined' || typeof onInputAsync !== 'undefined') && useShortcuts},\n )\n\n useEffect(() => {\n runShortcutSyncs()\n }, [])\n\n useEffect(() => {\n ;(() => {\n return Promise.all(\n processes.map(async (process, index) => {\n const stdout = writableStream(process, index)\n const stderr = writableStream(process, index)\n await process.action(stdout, stderr, abortController.signal)\n }),\n )\n })()\n .then(() => {\n if (!keepRunningAfterProcessesResolve) {\n setState(ConcurrentOutputState.Stopped)\n unmountInk()\n }\n })\n .catch((error) => {\n setState(ConcurrentOutputState.Stopped)\n unmountInk(error)\n })\n }, [abortController, processes, writableStream, unmountInk, keepRunningAfterProcessesResolve])\n const {lineVertical} = figures\n return (\n <>\n <Static items={processOutput}>\n {(chunk, index) => {\n const prefixBuffer = ' '.repeat(prefixColumnSize - chunk.prefix.length)\n return (\n <Box flexDirection=\"column\" key={index}>\n {chunk.lines.map((line, index) => (\n <Box key={index} flexDirection=\"row\">\n <Text color={chunk.color}>\n {showTimestamps ? (\n <Text>\n {currentTime()} {lineVertical}{' '}\n </Text>\n ) : null}\n <Text>\n {chunk.prefix}\n {prefixBuffer} {lineVertical} {line}\n </Text>\n </Text>\n </Box>\n ))}\n </Box>\n )\n }}\n </Static>\n {footerContent && !isAborted ? (\n <Box\n marginY={1}\n paddingTop={1}\n flexDirection=\"column\"\n flexGrow={1}\n borderStyle=\"single\"\n borderBottom={false}\n borderLeft={false}\n borderRight={false}\n borderTop\n >\n {useShortcuts ? (\n <Box flexDirection=\"column\">\n {footerContent.shortcuts.map((shortcut, index) => (\n <Text key={index}>\n {figures.pointerSmall} Press <Text bold>{shortcut.key}</Text> {figures.lineVertical} {shortcut.action}\n </Text>\n ))}\n </Box>\n ) : null}\n {footerContent.subTitle ? (\n <Box marginTop={useShortcuts ? 1 : 0}>\n <Text>{footerContent.subTitle}</Text>\n </Box>\n ) : null}\n </Box>\n ) : null}\n </>\n )\n}\nexport {ConcurrentOutput}\n"]}
@@ -37,7 +37,7 @@ describe('ConcurrentOutput', () => {
37
37
  },
38
38
  };
39
39
  // When
40
- const renderInstance = render(React.createElement(ConcurrentOutput, { processes: [backendProcess, frontendProcess], abortSignal: new AbortController().signal, footer: {
40
+ const renderInstance = render(React.createElement(ConcurrentOutput, { processes: [backendProcess, frontendProcess], abortController: new AbortController(), footer: {
41
41
  shortcuts: [
42
42
  {
43
43
  key: 'p',
@@ -60,6 +60,8 @@ describe('ConcurrentOutput', () => {
60
60
  00:00:00 │ frontend │ second frontend message
61
61
  00:00:00 │ frontend │ third frontend message
62
62
 
63
+ ────────────────────────────────────────────────────────────────────────────────────────────────────
64
+
63
65
  › Press p │ preview in your browser
64
66
  › Press q │ quit
65
67
 
@@ -99,7 +101,7 @@ describe('ConcurrentOutput', () => {
99
101
  },
100
102
  };
101
103
  // When
102
- const renderInstance = render(React.createElement(ConcurrentOutput, { processes: [backendProcess, frontendProcess], abortSignal: new AbortController().signal, footer: {
104
+ const renderInstance = render(React.createElement(ConcurrentOutput, { processes: [backendProcess, frontendProcess], abortController: new AbortController(), footer: {
103
105
  shortcuts: [
104
106
  {
105
107
  key: 'p',
@@ -122,6 +124,8 @@ describe('ConcurrentOutput', () => {
122
124
  00:00:00 │ frontend │ second frontend message
123
125
  00:00:00 │ frontend │ third frontend message
124
126
 
127
+ ────────────────────────────────────────────────────────────────────────────────────────────────────
128
+
125
129
  Preview URL: https://shopify.com
126
130
  "
127
131
  `);
@@ -135,7 +139,7 @@ describe('ConcurrentOutput', () => {
135
139
  },
136
140
  };
137
141
  const onInput = vi.fn();
138
- const renderInstance = render(React.createElement(ConcurrentOutput, { processes: [neverEndingProcess], onInput: (input, key) => onInput(input, key), abortSignal: new AbortController().signal }));
142
+ const renderInstance = render(React.createElement(ConcurrentOutput, { processes: [neverEndingProcess], onInput: (input, key) => onInput(input, key), abortController: new AbortController() }));
139
143
  await waitForInputsToBeReady();
140
144
  renderInstance.stdin.write('a');
141
145
  expect(onInput).toHaveBeenCalledTimes(1);
@@ -155,7 +159,7 @@ describe('ConcurrentOutput', () => {
155
159
  },
156
160
  };
157
161
  // When
158
- const renderInstance = render(React.createElement(ConcurrentOutput, { processes: [backendProcess], abortSignal: abortController.signal, footer: {
162
+ const renderInstance = render(React.createElement(ConcurrentOutput, { processes: [backendProcess], abortController: abortController, footer: {
159
163
  shortcuts: [
160
164
  {
161
165
  key: 'p',
@@ -174,8 +178,6 @@ describe('ConcurrentOutput', () => {
174
178
  "00:00:00 │ backend │ first backend message
175
179
  00:00:00 │ backend │ second backend message
176
180
  00:00:00 │ backend │ third backend message
177
-
178
- Preview URL: https://shopify.com
179
181
  "
180
182
  `);
181
183
  await expect(promise).resolves.toEqual(undefined);
@@ -192,7 +194,7 @@ describe('ConcurrentOutput', () => {
192
194
  },
193
195
  };
194
196
  // When
195
- const renderInstance = render(React.createElement(ConcurrentOutput, { processes: [backendProcess], abortSignal: new AbortController().signal, footer: {
197
+ const renderInstance = render(React.createElement(ConcurrentOutput, { processes: [backendProcess], abortController: new AbortController(), footer: {
196
198
  shortcuts: [
197
199
  {
198
200
  key: 'p',
@@ -211,6 +213,8 @@ describe('ConcurrentOutput', () => {
211
213
  00:00:00 │ backend │ second backend message
212
214
  00:00:00 │ backend │ third backend message
213
215
 
216
+ ────────────────────────────────────────────────────────────────────────────────────────────────────
217
+
214
218
  Preview URL: https://shopify.com
215
219
  "
216
220
  `);
@@ -226,7 +230,7 @@ describe('ConcurrentOutput', () => {
226
230
  },
227
231
  };
228
232
  // When
229
- const renderInstance = render(React.createElement(ConcurrentOutput, { processes: [backendProcess], abortSignal: new AbortController().signal, footer: {
233
+ const renderInstance = render(React.createElement(ConcurrentOutput, { processes: [backendProcess], abortController: new AbortController(), footer: {
230
234
  shortcuts: [
231
235
  {
232
236
  key: 'p',
@@ -245,6 +249,8 @@ describe('ConcurrentOutput', () => {
245
249
  00:00:00 │ backend │ second backend message
246
250
  00:00:00 │ backend │ third backend message
247
251
 
252
+ ────────────────────────────────────────────────────────────────────────────────────────────────────
253
+
248
254
  Preview URL: https://shopify.com
249
255
  "
250
256
  `);
@@ -261,7 +267,7 @@ describe('ConcurrentOutput', () => {
261
267
  },
262
268
  };
263
269
  // When
264
- const renderInstance = render(React.createElement(ConcurrentOutput, { processes: [backendProcess], abortSignal: new AbortController().signal, footer: {
270
+ const renderInstance = render(React.createElement(ConcurrentOutput, { processes: [backendProcess], abortController: new AbortController(), footer: {
265
271
  shortcuts: [
266
272
  {
267
273
  key: 'p',
@@ -280,6 +286,8 @@ describe('ConcurrentOutput', () => {
280
286
  00:00:00 │ backend │ second backend message
281
287
  00:00:00 │ backend │ third backend message
282
288
 
289
+ ────────────────────────────────────────────────────────────────────────────────────────────────────
290
+
283
291
  › Press p │ preview in your browser
284
292
  › Press q │ quit
285
293
 
@@ -1 +1 @@
1
- {"version":3,"file":"ConcurrentOutput.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/ConcurrentOutput.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import {ConcurrentOutput} from './ConcurrentOutput.js'\nimport {getLastFrameAfterUnmount, waitForInputsToBeReady, render, Stdin} from '../../testing/ui.js'\nimport {AbortController, AbortSignal} from '../../../../public/node/abort.js'\nimport {unstyled} from '../../../../public/node/output.js'\nimport React from 'react'\nimport {describe, expect, test, vi} from 'vitest'\nimport {Writable} from 'stream'\n\ndescribe('ConcurrentOutput', () => {\n test('renders a stream of concurrent outputs from sub-processes', async () => {\n // Given\n let backendPromiseResolve: () => void\n let frontendPromiseResolve: () => void\n\n const backendPromise = new Promise<void>(function (resolve, _reject) {\n backendPromiseResolve = resolve\n })\n\n const frontendPromise = new Promise<void>(function (resolve, _reject) {\n frontendPromiseResolve = resolve\n })\n\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n\n backendPromiseResolve()\n },\n }\n\n const frontendProcess = {\n prefix: 'frontend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n await backendPromise\n\n stdout.write('first frontend message')\n stdout.write('second frontend message')\n stdout.write('third frontend message')\n\n frontendPromiseResolve()\n\n // await promise that never resolves\n await new Promise(() => {})\n },\n }\n // When\n\n const renderInstance = render(\n <ConcurrentOutput\n processes={[backendProcess, frontendProcess]}\n abortSignal={new AbortController().signal}\n footer={{\n shortcuts: [\n {\n key: 'p',\n action: 'preview in your browser',\n },\n {\n key: 'q',\n action: 'quit',\n },\n ],\n subTitle: `Preview URL: https://shopify.com`,\n }}\n />,\n )\n\n await frontendPromise\n\n // Then\n expect(unstyled(renderInstance.lastFrame()!.replace(/\\d/g, '0'))).toMatchInlineSnapshot(`\n \"00:00:00 │ backend │ first backend message\n 00:00:00 │ backend │ second backend message\n 00:00:00 │ backend │ third backend message\n 00:00:00 │ frontend │ first frontend message\n 00:00:00 │ frontend │ second frontend message\n 00:00:00 │ frontend │ third frontend message\n\n › Press p │ preview in your browser\n › Press q │ quit\n\n Preview URL: https://shopify.com\n \"\n `)\n })\n\n test(\"doesn't render shortcuts if the stdin is not a TTY\", async () => {\n // Given\n let backendPromiseResolve: () => void\n let frontendPromiseResolve: () => void\n\n const backendPromise = new Promise<void>(function (resolve, _reject) {\n backendPromiseResolve = resolve\n })\n\n const frontendPromise = new Promise<void>(function (resolve, _reject) {\n frontendPromiseResolve = resolve\n })\n\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n\n backendPromiseResolve()\n },\n }\n\n const frontendProcess = {\n prefix: 'frontend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n await backendPromise\n\n stdout.write('first frontend message')\n stdout.write('second frontend message')\n stdout.write('third frontend message')\n\n frontendPromiseResolve()\n\n // await promise that never resolves\n await new Promise(() => {})\n },\n }\n // When\n\n const renderInstance = render(\n <ConcurrentOutput\n processes={[backendProcess, frontendProcess]}\n abortSignal={new AbortController().signal}\n footer={{\n shortcuts: [\n {\n key: 'p',\n action: 'preview in your browser',\n },\n {\n key: 'q',\n action: 'quit',\n },\n ],\n subTitle: `Preview URL: https://shopify.com`,\n }}\n />,\n {stdin: new Stdin({isTTY: false})},\n )\n\n await frontendPromise\n\n // Then\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!.replace(/\\d/g, '0'))).toMatchInlineSnapshot(`\n \"00:00:00 │ backend │ first backend message\n 00:00:00 │ backend │ second backend message\n 00:00:00 │ backend │ third backend message\n 00:00:00 │ frontend │ first frontend message\n 00:00:00 │ frontend │ second frontend message\n 00:00:00 │ frontend │ third frontend message\n\n Preview URL: https://shopify.com\n \"\n `)\n })\n\n test('accepts a onInput function that fires when a key is pressed', async () => {\n const neverEndingPromise = new Promise<void>(function (_resolve, _reject) {})\n\n const neverEndingProcess = {\n prefix: 'never-ending-process',\n action: async () => {\n await neverEndingPromise\n },\n }\n\n const onInput = vi.fn()\n\n const renderInstance = render(\n <ConcurrentOutput\n processes={[neverEndingProcess]}\n onInput={(input, key) => onInput(input, key)}\n abortSignal={new AbortController().signal}\n />,\n )\n\n await waitForInputsToBeReady()\n renderInstance.stdin.write('a')\n expect(onInput).toHaveBeenCalledTimes(1)\n expect(onInput.mock.calls[0]![0]).toBe('a')\n })\n\n test('abortController can be used to exit from outside and should preserve static output', async () => {\n // Given\n const abortController = new AbortController()\n\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n\n // await promise that never resolves\n await new Promise(() => {})\n },\n }\n\n // When\n\n const renderInstance = render(\n <ConcurrentOutput\n processes={[backendProcess]}\n abortSignal={abortController.signal}\n footer={{\n shortcuts: [\n {\n key: 'p',\n action: 'preview in your browser',\n },\n {\n key: 'q',\n action: 'quit',\n },\n ],\n subTitle: `Preview URL: https://shopify.com`,\n }}\n />,\n )\n\n const promise = renderInstance.waitUntilExit()\n\n abortController.abort()\n\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!).replace(/\\d/g, '0')).toMatchInlineSnapshot(`\n \"00:00:00 │ backend │ first backend message\n 00:00:00 │ backend │ second backend message\n 00:00:00 │ backend │ third backend message\n\n Preview URL: https://shopify.com\n \"\n `)\n\n await expect(promise).resolves.toEqual(undefined)\n })\n\n test('rejects with the error thrown inside one of the processes', async () => {\n // Given\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n\n throw new Error('something went wrong')\n },\n }\n\n // When\n\n const renderInstance = render(\n <ConcurrentOutput\n processes={[backendProcess]}\n abortSignal={new AbortController().signal}\n footer={{\n shortcuts: [\n {\n key: 'p',\n action: 'preview in your browser',\n },\n {\n key: 'q',\n action: 'quit',\n },\n ],\n subTitle: `Preview URL: https://shopify.com`,\n }}\n />,\n )\n\n await expect(renderInstance.waitUntilExit()).rejects.toThrowError('something went wrong')\n\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!).replace(/\\d/g, '0')).toMatchInlineSnapshot(`\n \"00:00:00 │ backend │ first backend message\n 00:00:00 │ backend │ second backend message\n 00:00:00 │ backend │ third backend message\n\n Preview URL: https://shopify.com\n \"\n `)\n })\n\n test(\"doesn't render the shortcuts when the processes resolve\", async () => {\n // Given\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n },\n }\n\n // When\n\n const renderInstance = render(\n <ConcurrentOutput\n processes={[backendProcess]}\n abortSignal={new AbortController().signal}\n footer={{\n shortcuts: [\n {\n key: 'p',\n action: 'preview in your browser',\n },\n {\n key: 'q',\n action: 'quit',\n },\n ],\n subTitle: `Preview URL: https://shopify.com`,\n }}\n />,\n )\n\n await renderInstance.waitUntilExit()\n\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!).replace(/\\d/g, '0')).toMatchInlineSnapshot(`\n \"00:00:00 │ backend │ first backend message\n 00:00:00 │ backend │ second backend message\n 00:00:00 │ backend │ third backend message\n\n Preview URL: https://shopify.com\n \"\n `)\n })\n\n test('renders the shortcuts and accepts inputs when the processes resolve and keepRunningAfterProcessesResolve is true', async () => {\n const onInput = vi.fn()\n // Given\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n },\n }\n\n // When\n const renderInstance = render(\n <ConcurrentOutput\n processes={[backendProcess]}\n abortSignal={new AbortController().signal}\n footer={{\n shortcuts: [\n {\n key: 'p',\n action: 'preview in your browser',\n },\n {\n key: 'q',\n action: 'quit',\n },\n ],\n subTitle: `Preview URL: https://shopify.com`,\n }}\n onInput={onInput}\n keepRunningAfterProcessesResolve\n />,\n )\n\n await new Promise((resolve) => setTimeout(resolve, 1000))\n\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!).replace(/\\d/g, '0')).toMatchInlineSnapshot(`\n \"00:00:00 │ backend │ first backend message\n 00:00:00 │ backend │ second backend message\n 00:00:00 │ backend │ third backend message\n\n › Press p │ preview in your browser\n › Press q │ quit\n\n Preview URL: https://shopify.com\n \"\n `)\n\n renderInstance.stdin.write('a')\n expect(onInput).toHaveBeenCalledTimes(1)\n expect(onInput.mock.calls[0]![0]).toBe('a')\n })\n})\n"]}
1
+ {"version":3,"file":"ConcurrentOutput.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/ConcurrentOutput.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import {ConcurrentOutput} from './ConcurrentOutput.js'\nimport {getLastFrameAfterUnmount, waitForInputsToBeReady, render, Stdin} from '../../testing/ui.js'\nimport {AbortController, AbortSignal} from '../../../../public/node/abort.js'\nimport {unstyled} from '../../../../public/node/output.js'\nimport React from 'react'\nimport {describe, expect, test, vi} from 'vitest'\nimport {Writable} from 'stream'\n\ndescribe('ConcurrentOutput', () => {\n test('renders a stream of concurrent outputs from sub-processes', async () => {\n // Given\n let backendPromiseResolve: () => void\n let frontendPromiseResolve: () => void\n\n const backendPromise = new Promise<void>(function (resolve, _reject) {\n backendPromiseResolve = resolve\n })\n\n const frontendPromise = new Promise<void>(function (resolve, _reject) {\n frontendPromiseResolve = resolve\n })\n\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n\n backendPromiseResolve()\n },\n }\n\n const frontendProcess = {\n prefix: 'frontend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n await backendPromise\n\n stdout.write('first frontend message')\n stdout.write('second frontend message')\n stdout.write('third frontend message')\n\n frontendPromiseResolve()\n\n // await promise that never resolves\n await new Promise(() => {})\n },\n }\n // When\n\n const renderInstance = render(\n <ConcurrentOutput\n processes={[backendProcess, frontendProcess]}\n abortController={new AbortController()}\n footer={{\n shortcuts: [\n {\n key: 'p',\n action: 'preview in your browser',\n },\n {\n key: 'q',\n action: 'quit',\n },\n ],\n subTitle: `Preview URL: https://shopify.com`,\n }}\n />,\n )\n\n await frontendPromise\n\n // Then\n expect(unstyled(renderInstance.lastFrame()!.replace(/\\d/g, '0'))).toMatchInlineSnapshot(`\n \"00:00:00 │ backend │ first backend message\n 00:00:00 │ backend │ second backend message\n 00:00:00 │ backend │ third backend message\n 00:00:00 │ frontend │ first frontend message\n 00:00:00 │ frontend │ second frontend message\n 00:00:00 │ frontend │ third frontend message\n\n ────────────────────────────────────────────────────────────────────────────────────────────────────\n\n › Press p │ preview in your browser\n › Press q │ quit\n\n Preview URL: https://shopify.com\n \"\n `)\n })\n\n test(\"doesn't render shortcuts if the stdin is not a TTY\", async () => {\n // Given\n let backendPromiseResolve: () => void\n let frontendPromiseResolve: () => void\n\n const backendPromise = new Promise<void>(function (resolve, _reject) {\n backendPromiseResolve = resolve\n })\n\n const frontendPromise = new Promise<void>(function (resolve, _reject) {\n frontendPromiseResolve = resolve\n })\n\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n\n backendPromiseResolve()\n },\n }\n\n const frontendProcess = {\n prefix: 'frontend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n await backendPromise\n\n stdout.write('first frontend message')\n stdout.write('second frontend message')\n stdout.write('third frontend message')\n\n frontendPromiseResolve()\n\n // await promise that never resolves\n await new Promise(() => {})\n },\n }\n // When\n\n const renderInstance = render(\n <ConcurrentOutput\n processes={[backendProcess, frontendProcess]}\n abortController={new AbortController()}\n footer={{\n shortcuts: [\n {\n key: 'p',\n action: 'preview in your browser',\n },\n {\n key: 'q',\n action: 'quit',\n },\n ],\n subTitle: `Preview URL: https://shopify.com`,\n }}\n />,\n {stdin: new Stdin({isTTY: false})},\n )\n\n await frontendPromise\n\n // Then\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!.replace(/\\d/g, '0'))).toMatchInlineSnapshot(`\n \"00:00:00 │ backend │ first backend message\n 00:00:00 │ backend │ second backend message\n 00:00:00 │ backend │ third backend message\n 00:00:00 │ frontend │ first frontend message\n 00:00:00 │ frontend │ second frontend message\n 00:00:00 │ frontend │ third frontend message\n\n ────────────────────────────────────────────────────────────────────────────────────────────────────\n\n Preview URL: https://shopify.com\n \"\n `)\n })\n\n test('accepts a onInput function that fires when a key is pressed', async () => {\n const neverEndingPromise = new Promise<void>(function (_resolve, _reject) {})\n\n const neverEndingProcess = {\n prefix: 'never-ending-process',\n action: async () => {\n await neverEndingPromise\n },\n }\n\n const onInput = vi.fn()\n\n const renderInstance = render(\n <ConcurrentOutput\n processes={[neverEndingProcess]}\n onInput={(input, key) => onInput(input, key)}\n abortController={new AbortController()}\n />,\n )\n\n await waitForInputsToBeReady()\n renderInstance.stdin.write('a')\n expect(onInput).toHaveBeenCalledTimes(1)\n expect(onInput.mock.calls[0]![0]).toBe('a')\n })\n\n test('abortController can be used to exit from outside and should preserve static output', async () => {\n // Given\n const abortController = new AbortController()\n\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n\n // await promise that never resolves\n await new Promise(() => {})\n },\n }\n\n // When\n\n const renderInstance = render(\n <ConcurrentOutput\n processes={[backendProcess]}\n abortController={abortController}\n footer={{\n shortcuts: [\n {\n key: 'p',\n action: 'preview in your browser',\n },\n {\n key: 'q',\n action: 'quit',\n },\n ],\n subTitle: `Preview URL: https://shopify.com`,\n }}\n />,\n )\n\n const promise = renderInstance.waitUntilExit()\n\n abortController.abort()\n\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!).replace(/\\d/g, '0')).toMatchInlineSnapshot(`\n \"00:00:00 │ backend │ first backend message\n 00:00:00 │ backend │ second backend message\n 00:00:00 │ backend │ third backend message\n \"\n `)\n\n await expect(promise).resolves.toEqual(undefined)\n })\n\n test('rejects with the error thrown inside one of the processes', async () => {\n // Given\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n\n throw new Error('something went wrong')\n },\n }\n\n // When\n\n const renderInstance = render(\n <ConcurrentOutput\n processes={[backendProcess]}\n abortController={new AbortController()}\n footer={{\n shortcuts: [\n {\n key: 'p',\n action: 'preview in your browser',\n },\n {\n key: 'q',\n action: 'quit',\n },\n ],\n subTitle: `Preview URL: https://shopify.com`,\n }}\n />,\n )\n\n await expect(renderInstance.waitUntilExit()).rejects.toThrowError('something went wrong')\n\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!).replace(/\\d/g, '0')).toMatchInlineSnapshot(`\n \"00:00:00 │ backend │ first backend message\n 00:00:00 │ backend │ second backend message\n 00:00:00 │ backend │ third backend message\n\n ────────────────────────────────────────────────────────────────────────────────────────────────────\n\n Preview URL: https://shopify.com\n \"\n `)\n })\n\n test(\"doesn't render the shortcuts when the processes resolve\", async () => {\n // Given\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n },\n }\n\n // When\n\n const renderInstance = render(\n <ConcurrentOutput\n processes={[backendProcess]}\n abortController={new AbortController()}\n footer={{\n shortcuts: [\n {\n key: 'p',\n action: 'preview in your browser',\n },\n {\n key: 'q',\n action: 'quit',\n },\n ],\n subTitle: `Preview URL: https://shopify.com`,\n }}\n />,\n )\n\n await renderInstance.waitUntilExit()\n\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!).replace(/\\d/g, '0')).toMatchInlineSnapshot(`\n \"00:00:00 │ backend │ first backend message\n 00:00:00 │ backend │ second backend message\n 00:00:00 │ backend │ third backend message\n\n ────────────────────────────────────────────────────────────────────────────────────────────────────\n\n Preview URL: https://shopify.com\n \"\n `)\n })\n\n test('renders the shortcuts and accepts inputs when the processes resolve and keepRunningAfterProcessesResolve is true', async () => {\n const onInput = vi.fn()\n // Given\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n },\n }\n\n // When\n const renderInstance = render(\n <ConcurrentOutput\n processes={[backendProcess]}\n abortController={new AbortController()}\n footer={{\n shortcuts: [\n {\n key: 'p',\n action: 'preview in your browser',\n },\n {\n key: 'q',\n action: 'quit',\n },\n ],\n subTitle: `Preview URL: https://shopify.com`,\n }}\n onInput={onInput}\n keepRunningAfterProcessesResolve\n />,\n )\n\n await new Promise((resolve) => setTimeout(resolve, 1000))\n\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!).replace(/\\d/g, '0')).toMatchInlineSnapshot(`\n \"00:00:00 │ backend │ first backend message\n 00:00:00 │ backend │ second backend message\n 00:00:00 │ backend │ third backend message\n\n ────────────────────────────────────────────────────────────────────────────────────────────────────\n\n › Press p │ preview in your browser\n › Press q │ quit\n\n Preview URL: https://shopify.com\n \"\n `)\n\n renderInstance.stdin.write('a')\n expect(onInput).toHaveBeenCalledTimes(1)\n expect(onInput.mock.calls[0]![0]).toBe('a')\n })\n})\n"]}
@@ -22,5 +22,5 @@ export declare class Stdout extends EventEmitter {
22
22
  write: (frame: string) => void;
23
23
  lastFrame: () => string | undefined;
24
24
  }
25
- export declare function handleCtrlC(input: string, key: Key): void;
25
+ export declare function handleCtrlC(input: string, key: Key, exit?: () => void): void;
26
26
  export {};