@shopify/cli-kit 3.76.1 → 3.77.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/private/node/api.d.ts +27 -4
  2. package/dist/private/node/api.js +64 -8
  3. package/dist/private/node/api.js.map +1 -1
  4. package/dist/private/node/constants.d.ts +2 -0
  5. package/dist/private/node/constants.js +2 -0
  6. package/dist/private/node/constants.js.map +1 -1
  7. package/dist/private/node/session/device-authorization.js +3 -3
  8. package/dist/private/node/session/device-authorization.js.map +1 -1
  9. package/dist/private/node/session/schema.d.ts +40 -40
  10. package/dist/private/node/sleep-with-backoff.d.ts +16 -0
  11. package/dist/private/node/sleep-with-backoff.js +64 -0
  12. package/dist/private/node/sleep-with-backoff.js.map +1 -0
  13. package/dist/public/common/version.d.ts +1 -1
  14. package/dist/public/common/version.js +1 -1
  15. package/dist/public/common/version.js.map +1 -1
  16. package/dist/public/node/api/admin.js +2 -2
  17. package/dist/public/node/api/admin.js.map +1 -1
  18. package/dist/public/node/api/graphql.js +6 -1
  19. package/dist/public/node/api/graphql.js.map +1 -1
  20. package/dist/public/node/context/fqdn.js +1 -1
  21. package/dist/public/node/context/fqdn.js.map +1 -1
  22. package/dist/public/node/environment.d.ts +20 -0
  23. package/dist/public/node/environment.js +29 -0
  24. package/dist/public/node/environment.js.map +1 -1
  25. package/dist/public/node/github.d.ts +1 -0
  26. package/dist/public/node/github.js +31 -2
  27. package/dist/public/node/github.js.map +1 -1
  28. package/dist/public/node/hooks/prerun.js +1 -2
  29. package/dist/public/node/hooks/prerun.js.map +1 -1
  30. package/dist/public/node/http.d.ts +48 -3
  31. package/dist/public/node/http.js +134 -24
  32. package/dist/public/node/http.js.map +1 -1
  33. package/dist/public/node/monorail.js +1 -1
  34. package/dist/public/node/monorail.js.map +1 -1
  35. package/dist/public/node/notifications-system.js +26 -11
  36. package/dist/public/node/notifications-system.js.map +1 -1
  37. package/dist/public/node/system.d.ts +6 -0
  38. package/dist/public/node/system.js +9 -2
  39. package/dist/public/node/system.js.map +1 -1
  40. package/dist/public/node/testing/test-with-temp-dir.d.ts +9 -0
  41. package/dist/public/node/testing/test-with-temp-dir.js +14 -0
  42. package/dist/public/node/testing/test-with-temp-dir.js.map +1 -0
  43. package/dist/public/node/themes/types.d.ts +3 -2
  44. package/dist/public/node/themes/types.js.map +1 -1
  45. package/dist/public/node/vendor/dev_server/dev-server-2016.d.ts +9 -0
  46. package/dist/public/node/vendor/dev_server/dev-server-2016.js +38 -0
  47. package/dist/public/node/vendor/dev_server/dev-server-2016.js.map +1 -0
  48. package/dist/public/node/vendor/dev_server/dev-server-2024.d.ts +9 -0
  49. package/dist/public/node/vendor/dev_server/dev-server-2024.js +68 -0
  50. package/dist/public/node/vendor/dev_server/dev-server-2024.js.map +1 -0
  51. package/dist/public/node/vendor/dev_server/dev-server-spin.d.ts +5 -0
  52. package/dist/public/node/vendor/dev_server/dev-server-spin.js +28 -0
  53. package/dist/public/node/vendor/dev_server/dev-server-spin.js.map +1 -0
  54. package/dist/public/node/vendor/dev_server/dev-server.d.ts +15 -0
  55. package/dist/public/node/vendor/dev_server/dev-server.js +59 -0
  56. package/dist/public/node/vendor/dev_server/dev-server.js.map +1 -0
  57. package/dist/public/node/vendor/dev_server/env.d.ts +2 -0
  58. package/dist/public/node/vendor/dev_server/env.js +7 -0
  59. package/dist/public/node/vendor/dev_server/env.js.map +1 -0
  60. package/dist/public/node/vendor/dev_server/index.d.ts +2 -0
  61. package/dist/public/node/vendor/dev_server/index.js +3 -0
  62. package/dist/public/node/vendor/dev_server/index.js.map +1 -0
  63. package/dist/public/node/vendor/dev_server/network/host.d.ts +2 -0
  64. package/dist/public/node/vendor/dev_server/network/host.js +45 -0
  65. package/dist/public/node/vendor/dev_server/network/host.js.map +1 -0
  66. package/dist/public/node/vendor/dev_server/network/index.d.ts +9 -0
  67. package/dist/public/node/vendor/dev_server/network/index.js +36 -0
  68. package/dist/public/node/vendor/dev_server/network/index.js.map +1 -0
  69. package/dist/public/node/vendor/dev_server/types.d.ts +11 -0
  70. package/dist/public/node/vendor/dev_server/types.js +2 -0
  71. package/dist/public/node/vendor/dev_server/types.js.map +1 -0
  72. package/dist/tsconfig.tsbuildinfo +1 -1
  73. package/package.json +3 -1
  74. package/dist/public/node/vendor/dev_server/DevServer.d.ts +0 -19
  75. package/dist/public/node/vendor/dev_server/DevServer.js +0 -170
  76. package/dist/public/node/vendor/dev_server/DevServer.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"monorail.js","sourceRoot":"","sources":["../../../src/public/node/monorail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,EAAC,MAAM,WAAW,CAAA;AAE/B,OAAO,EAAC,WAAW,EAAE,aAAa,EAAE,WAAW,EAAC,MAAM,6BAA6B,CAAA;AAKnF,MAAM,GAAG,GAAG,iDAAiD,CAAA;AAI7D,mFAAmF;AACnF,MAAM,CAAC,MAAM,sBAAsB,GAAG,uBAAuB,CAAA;AAgK7D,MAAM,qBAAqB,GAAG,IAAI,GAAG,EAAU,CAAA;AAE/C;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,QAAmB,EACnB,UAA8B,EAC9B,aAAoC;IAEpC,qHAAqH;IACrH,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,CAAA;IACtC,IAAI,WAAW,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;QACnD,IAAI,qBAAqB,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;YAC3C,OAAO,EAAC,IAAI,EAAE,IAAI,EAAC,CAAA;QACrB,CAAC;QACD,qBAAqB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IACxC,CAAC;IAED,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,CAAA;QACxC,MAAM,OAAO,GAAG,EAAC,GAAG,UAAU,EAAE,GAAG,aAAa,EAAC,CAAA;QACjD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAC,CAAC,CAAA;QAC3D,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,CAAC,CAAA;QAEzC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAC,CAAC,CAAA;QAElE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,WAAW,CAAC,aAAa,CAAA,yBAAyB,WAAW,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAA;YAC/F,OAAO,EAAC,IAAI,EAAE,IAAI,EAAC,CAAA;QACrB,CAAC;aAAM,CAAC;YACN,WAAW,CAAC,qCAAqC,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;YACvE,OAAO,EAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,UAAU,EAAC,CAAA;QACtD,CAAC;QACD,qDAAqD;IACvD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,GAAG,kCAAkC,CAAA;QAChD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;QAChD,CAAC;QACD,WAAW,CAAC,OAAO,CAAC,CAAA;QACpB,OAAO,EAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAC,CAAA;IACjC,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,eAAe,CAAmB,OAAU;IACnD,MAAM,MAAM,GAAG,EAAC,GAAG,OAAO,EAAC,CAAA;IAC3B,IAAI,SAAS,IAAI,MAAM,EAAE,CAAC;QACxB,MAAM,CAAC,OAAO,GAAG,MAAM,CAAA;IACzB,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED,MAAM,YAAY,GAAG,CAAC,WAAmB,EAAE,EAAE;IAC3C,OAAO;QACL,cAAc,EAAE,iCAAiC;QACjD,qCAAqC,EAAE,WAAW,CAAC,QAAQ,EAAE;QAC7D,kCAAkC,EAAE,WAAW,CAAC,QAAQ,EAAE;KAC3D,CAAA;AACH,CAAC,CAAA","sourcesContent":["import {fetch} from './http.js'\nimport {JsonMap} from '../../private/common/json.js'\nimport {outputDebug, outputContent, outputToken} from '../../public/node/output.js'\nimport {DeepRequired} from '../common/ts/deep-required.js'\n\nexport {DeepRequired}\n\nconst url = 'https://monorail-edge.shopifysvc.com/v1/produce'\n\ntype Optional<T> = T | null\n\n// This is the topic name of the main event we log to Monorail, the command tracker\nexport const MONORAIL_COMMAND_TOPIC = 'app_cli3_command/1.16'\n\nexport interface Schemas {\n [MONORAIL_COMMAND_TOPIC]: {\n sensitive: {\n args: string\n error_message?: Optional<string>\n app_name?: Optional<string>\n metadata?: Optional<string>\n store_fqdn?: Optional<string>\n cmd_all_environment_flags?: Optional<string>\n\n // Dev related commands\n cmd_dev_tunnel_custom?: Optional<string>\n\n // Environment\n env_plugin_installed_all?: Optional<string>\n }\n public: {\n business_platform_id?: Optional<number>\n partner_id?: Optional<number>\n command: string\n project_type?: Optional<string>\n time_start: number\n time_end: number\n total_time: number\n success: boolean\n api_key?: Optional<string>\n cli_version: string\n uname: string\n ruby_version: string\n node_version: string\n is_employee: boolean\n store_fqdn_hash?: Optional<string>\n user_id: string\n\n // Any and all commands\n cmd_all_alias_used?: Optional<string>\n cmd_all_launcher?: Optional<string>\n cmd_all_path_override?: Optional<boolean>\n cmd_all_path_override_hash?: Optional<string>\n cmd_all_plugin?: Optional<string>\n cmd_all_topic?: Optional<string>\n cmd_all_verbose?: Optional<boolean>\n cmd_all_exit?: Optional<string>\n cmd_all_force?: Optional<boolean>\n cmd_all_last_graphql_request_id?: Optional<string>\n\n cmd_all_timing_network_ms?: Optional<number>\n cmd_all_timing_prompts_ms?: Optional<number>\n cmd_all_timing_active_ms?: Optional<number>\n\n // Any extension related command\n cmd_extensions_binary_from_source?: Optional<boolean>\n\n // Scaffolding related commands\n cmd_scaffold_required_auth?: Optional<boolean>\n cmd_scaffold_template_custom?: Optional<boolean>\n cmd_scaffold_template_flavor?: Optional<string>\n cmd_scaffold_type?: Optional<string>\n cmd_scaffold_type_category?: Optional<string>\n cmd_scaffold_type_gated?: Optional<boolean>\n cmd_scaffold_type_owner?: Optional<string>\n cmd_scaffold_used_prompts_for_type?: Optional<boolean>\n\n // Used in several but not all commands\n cmd_app_dependency_installation_skipped?: Optional<boolean>\n cmd_app_reset_used?: Optional<boolean>\n cmd_app_linked_config_used?: Optional<boolean>\n cmd_app_linked_config_name?: Optional<string>\n cmd_app_linked_config_git_tracked?: Optional<boolean>\n cmd_app_all_configs_any?: Optional<boolean>\n cmd_app_all_configs_clients?: Optional<string>\n cmd_app_linked_config_source?: Optional<string>\n cmd_app_linked_config_uses_cli_managed_urls?: Optional<boolean>\n cmd_app_warning_api_key_deprecation_displayed?: Optional<boolean>\n cmd_app_deployment_mode?: Optional<string>\n\n // Dev related commands\n cmd_dev_tunnel_type?: Optional<string>\n cmd_dev_tunnel_custom_hash?: Optional<string>\n cmd_dev_urls_updated?: Optional<boolean>\n cmd_dev_preview_url_opened?: Optional<boolean>\n cmd_dev_graphiql_opened?: Optional<boolean>\n cmd_dev_dev_preview_toggle_used?: Optional<boolean>\n\n // Create-app related commands\n cmd_create_app_template?: Optional<string>\n cmd_create_app_template_url?: Optional<string>\n\n // Deploy related commands\n cmd_deploy_flag_message_used?: Optional<boolean>\n cmd_deploy_flag_version_used?: Optional<boolean>\n cmd_deploy_flag_source_url_used?: Optional<boolean>\n cmd_deploy_confirm_new_registrations?: Optional<number>\n cmd_deploy_confirm_updated_registrations?: Optional<number>\n cmd_deploy_confirm_removed_registrations?: Optional<number>\n cmd_deploy_confirm_cancelled?: Optional<boolean>\n cmd_deploy_confirm_time_to_complete_ms?: Optional<number>\n cmd_deploy_prompt_upgrade_to_unified_displayed?: Optional<boolean>\n cmd_deploy_prompt_upgrade_to_unified_response?: Optional<string>\n cmd_deploy_confirm_include_config_used?: Optional<boolean>\n cmd_deploy_include_config_used?: Optional<boolean>\n cmd_deploy_config_modules_breakdown?: Optional<string>\n cmd_deploy_config_modules_updated?: Optional<string>\n cmd_deploy_config_modules_added?: Optional<string>\n cmd_deploy_config_modules_deleted?: Optional<string>\n\n // Release related commands\n cmd_release_confirm_cancelled?: Optional<boolean>\n\n // App setup\n app_extensions_any?: Optional<boolean>\n app_extensions_breakdown?: Optional<string>\n app_extensions_count?: Optional<number>\n app_extensions_custom_layout?: Optional<boolean>\n app_extensions_function_any?: Optional<boolean>\n app_extensions_function_count?: Optional<number>\n app_extensions_function_custom_layout?: Optional<boolean>\n app_extensions_theme_any?: Optional<boolean>\n app_extensions_theme_count?: Optional<number>\n app_extensions_theme_custom_layout?: Optional<boolean>\n app_extensions_ui_any?: Optional<boolean>\n app_extensions_ui_count?: Optional<number>\n app_extensions_ui_custom_layout?: Optional<boolean>\n app_name_hash?: Optional<string>\n app_path_hash?: Optional<string>\n app_scopes?: Optional<string>\n app_web_backend_any?: Optional<boolean>\n app_web_backend_count?: Optional<number>\n app_web_custom_layout?: Optional<boolean>\n app_web_framework?: Optional<string>\n app_web_frontend_any?: Optional<boolean>\n app_web_frontend_count?: Optional<number>\n\n // Environment\n env_ci?: Optional<boolean>\n env_ci_platform?: Optional<string>\n env_device_id?: Optional<string>\n env_package_manager?: Optional<string>\n env_package_manager_workspaces?: Optional<boolean>\n env_plugin_installed_any_custom?: Optional<boolean>\n env_plugin_installed_shopify?: Optional<string>\n env_shell?: Optional<string>\n env_web_ide?: Optional<string>\n env_cloud?: Optional<string>\n env_is_global?: Optional<boolean>\n env_auth_method?: Optional<string>\n }\n }\n [schemaId: string]: {sensitive: JsonMap; public: JsonMap}\n}\n\n// In reality, we're normally most interested in just this from Schemas, so export it for ease of use.\n// The monorail schema itself has lots of optional values as it must be backwards-compatible. For our schema we want mandatory values instead.\nexport type MonorailEventPublic = DeepRequired<Schemas[typeof MONORAIL_COMMAND_TOPIC]['public']>\nexport type MonorailEventSensitive = Schemas[typeof MONORAIL_COMMAND_TOPIC]['sensitive']\n\ntype MonorailResult = {type: 'ok'} | {type: 'error'; message: string}\n\nconst publishedCommandNames = new Set<string>()\n\n/**\n * Publishes an event to Monorail.\n *\n * @param schemaId - The schema ID of the event to publish.\n * @param publicData - The public data to publish.\n * @param sensitiveData - The sensitive data to publish.\n * @returns A result indicating whether the event was successfully published.\n */\nexport async function publishMonorailEvent<TSchemaId extends keyof Schemas, TPayload extends Schemas[TSchemaId]>(\n schemaId: TSchemaId,\n publicData: TPayload['public'],\n sensitiveData: TPayload['sensitive'],\n): Promise<MonorailResult> {\n // If a command has already been logged, never re-log it. This is to prevent duplication caused by unexpected errors.\n const commandName = publicData.command\n if (commandName && typeof commandName === 'string') {\n if (publishedCommandNames.has(commandName)) {\n return {type: 'ok'}\n }\n publishedCommandNames.add(commandName)\n }\n\n try {\n const currentTime = new Date().getTime()\n const payload = {...publicData, ...sensitiveData}\n const body = JSON.stringify({schema_id: schemaId, payload})\n const headers = buildHeaders(currentTime)\n\n const response = await fetch(url, {method: 'POST', body, headers})\n\n if (response.status === 200) {\n outputDebug(outputContent`Analytics event sent: ${outputToken.json(sanitizePayload(payload))}`)\n return {type: 'ok'}\n } else {\n outputDebug(`Failed to report usage analytics: ${response.statusText}`)\n return {type: 'error', message: response.statusText}\n }\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n let message = 'Failed to report usage analytics'\n if (error instanceof Error) {\n message = message.concat(`: ${error.message}`)\n }\n outputDebug(message)\n return {type: 'error', message}\n }\n}\n\n/**\n * Sanitizies the api_key from the payload and returns a new hash.\n *\n * @param payload - The public and sensitive data.\n * @returns A copy of the payload with the api_key sanitized.\n */\nfunction sanitizePayload<T extends object>(payload: T): T {\n const result = {...payload}\n if ('api_key' in result) {\n result.api_key = '****'\n }\n\n return result\n}\n\nconst buildHeaders = (currentTime: number) => {\n return {\n 'Content-Type': 'application/json; charset=utf-8',\n 'X-Monorail-Edge-Event-Created-At-Ms': currentTime.toString(),\n 'X-Monorail-Edge-Event-Sent-At-Ms': currentTime.toString(),\n }\n}\n"]}
1
+ {"version":3,"file":"monorail.js","sourceRoot":"","sources":["../../../src/public/node/monorail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,EAAC,MAAM,WAAW,CAAA;AAE/B,OAAO,EAAC,WAAW,EAAE,aAAa,EAAE,WAAW,EAAC,MAAM,6BAA6B,CAAA;AAKnF,MAAM,GAAG,GAAG,iDAAiD,CAAA;AAI7D,mFAAmF;AACnF,MAAM,CAAC,MAAM,sBAAsB,GAAG,uBAAuB,CAAA;AAgK7D,MAAM,qBAAqB,GAAG,IAAI,GAAG,EAAU,CAAA;AAE/C;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,QAAmB,EACnB,UAA8B,EAC9B,aAAoC;IAEpC,qHAAqH;IACrH,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,CAAA;IACtC,IAAI,WAAW,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;QACnD,IAAI,qBAAqB,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;YAC3C,OAAO,EAAC,IAAI,EAAE,IAAI,EAAC,CAAA;QACrB,CAAC;QACD,qBAAqB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IACxC,CAAC;IAED,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,CAAA;QACxC,MAAM,OAAO,GAAG,EAAC,GAAG,UAAU,EAAE,GAAG,aAAa,EAAC,CAAA;QACjD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAC,CAAC,CAAA;QAC3D,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,CAAC,CAAA;QAEzC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAC,EAAE,cAAc,CAAC,CAAA;QAElF,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,WAAW,CAAC,aAAa,CAAA,yBAAyB,WAAW,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAA;YAC/F,OAAO,EAAC,IAAI,EAAE,IAAI,EAAC,CAAA;QACrB,CAAC;aAAM,CAAC;YACN,WAAW,CAAC,qCAAqC,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAA;YACvE,OAAO,EAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,UAAU,EAAC,CAAA;QACtD,CAAC;QACD,qDAAqD;IACvD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,GAAG,kCAAkC,CAAA;QAChD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;QAChD,CAAC;QACD,WAAW,CAAC,OAAO,CAAC,CAAA;QACpB,OAAO,EAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAC,CAAA;IACjC,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,eAAe,CAAmB,OAAU;IACnD,MAAM,MAAM,GAAG,EAAC,GAAG,OAAO,EAAC,CAAA;IAC3B,IAAI,SAAS,IAAI,MAAM,EAAE,CAAC;QACxB,MAAM,CAAC,OAAO,GAAG,MAAM,CAAA;IACzB,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED,MAAM,YAAY,GAAG,CAAC,WAAmB,EAAE,EAAE;IAC3C,OAAO;QACL,cAAc,EAAE,iCAAiC;QACjD,qCAAqC,EAAE,WAAW,CAAC,QAAQ,EAAE;QAC7D,kCAAkC,EAAE,WAAW,CAAC,QAAQ,EAAE;KAC3D,CAAA;AACH,CAAC,CAAA","sourcesContent":["import {fetch} from './http.js'\nimport {JsonMap} from '../../private/common/json.js'\nimport {outputDebug, outputContent, outputToken} from '../../public/node/output.js'\nimport {DeepRequired} from '../common/ts/deep-required.js'\n\nexport {DeepRequired}\n\nconst url = 'https://monorail-edge.shopifysvc.com/v1/produce'\n\ntype Optional<T> = T | null\n\n// This is the topic name of the main event we log to Monorail, the command tracker\nexport const MONORAIL_COMMAND_TOPIC = 'app_cli3_command/1.16'\n\nexport interface Schemas {\n [MONORAIL_COMMAND_TOPIC]: {\n sensitive: {\n args: string\n error_message?: Optional<string>\n app_name?: Optional<string>\n metadata?: Optional<string>\n store_fqdn?: Optional<string>\n cmd_all_environment_flags?: Optional<string>\n\n // Dev related commands\n cmd_dev_tunnel_custom?: Optional<string>\n\n // Environment\n env_plugin_installed_all?: Optional<string>\n }\n public: {\n business_platform_id?: Optional<number>\n partner_id?: Optional<number>\n command: string\n project_type?: Optional<string>\n time_start: number\n time_end: number\n total_time: number\n success: boolean\n api_key?: Optional<string>\n cli_version: string\n uname: string\n ruby_version: string\n node_version: string\n is_employee: boolean\n store_fqdn_hash?: Optional<string>\n user_id: string\n\n // Any and all commands\n cmd_all_alias_used?: Optional<string>\n cmd_all_launcher?: Optional<string>\n cmd_all_path_override?: Optional<boolean>\n cmd_all_path_override_hash?: Optional<string>\n cmd_all_plugin?: Optional<string>\n cmd_all_topic?: Optional<string>\n cmd_all_verbose?: Optional<boolean>\n cmd_all_exit?: Optional<string>\n cmd_all_force?: Optional<boolean>\n cmd_all_last_graphql_request_id?: Optional<string>\n\n cmd_all_timing_network_ms?: Optional<number>\n cmd_all_timing_prompts_ms?: Optional<number>\n cmd_all_timing_active_ms?: Optional<number>\n\n // Any extension related command\n cmd_extensions_binary_from_source?: Optional<boolean>\n\n // Scaffolding related commands\n cmd_scaffold_required_auth?: Optional<boolean>\n cmd_scaffold_template_custom?: Optional<boolean>\n cmd_scaffold_template_flavor?: Optional<string>\n cmd_scaffold_type?: Optional<string>\n cmd_scaffold_type_category?: Optional<string>\n cmd_scaffold_type_gated?: Optional<boolean>\n cmd_scaffold_type_owner?: Optional<string>\n cmd_scaffold_used_prompts_for_type?: Optional<boolean>\n\n // Used in several but not all commands\n cmd_app_dependency_installation_skipped?: Optional<boolean>\n cmd_app_reset_used?: Optional<boolean>\n cmd_app_linked_config_used?: Optional<boolean>\n cmd_app_linked_config_name?: Optional<string>\n cmd_app_linked_config_git_tracked?: Optional<boolean>\n cmd_app_all_configs_any?: Optional<boolean>\n cmd_app_all_configs_clients?: Optional<string>\n cmd_app_linked_config_source?: Optional<string>\n cmd_app_linked_config_uses_cli_managed_urls?: Optional<boolean>\n cmd_app_warning_api_key_deprecation_displayed?: Optional<boolean>\n cmd_app_deployment_mode?: Optional<string>\n\n // Dev related commands\n cmd_dev_tunnel_type?: Optional<string>\n cmd_dev_tunnel_custom_hash?: Optional<string>\n cmd_dev_urls_updated?: Optional<boolean>\n cmd_dev_preview_url_opened?: Optional<boolean>\n cmd_dev_graphiql_opened?: Optional<boolean>\n cmd_dev_dev_preview_toggle_used?: Optional<boolean>\n\n // Create-app related commands\n cmd_create_app_template?: Optional<string>\n cmd_create_app_template_url?: Optional<string>\n\n // Deploy related commands\n cmd_deploy_flag_message_used?: Optional<boolean>\n cmd_deploy_flag_version_used?: Optional<boolean>\n cmd_deploy_flag_source_url_used?: Optional<boolean>\n cmd_deploy_confirm_new_registrations?: Optional<number>\n cmd_deploy_confirm_updated_registrations?: Optional<number>\n cmd_deploy_confirm_removed_registrations?: Optional<number>\n cmd_deploy_confirm_cancelled?: Optional<boolean>\n cmd_deploy_confirm_time_to_complete_ms?: Optional<number>\n cmd_deploy_prompt_upgrade_to_unified_displayed?: Optional<boolean>\n cmd_deploy_prompt_upgrade_to_unified_response?: Optional<string>\n cmd_deploy_confirm_include_config_used?: Optional<boolean>\n cmd_deploy_include_config_used?: Optional<boolean>\n cmd_deploy_config_modules_breakdown?: Optional<string>\n cmd_deploy_config_modules_updated?: Optional<string>\n cmd_deploy_config_modules_added?: Optional<string>\n cmd_deploy_config_modules_deleted?: Optional<string>\n\n // Release related commands\n cmd_release_confirm_cancelled?: Optional<boolean>\n\n // App setup\n app_extensions_any?: Optional<boolean>\n app_extensions_breakdown?: Optional<string>\n app_extensions_count?: Optional<number>\n app_extensions_custom_layout?: Optional<boolean>\n app_extensions_function_any?: Optional<boolean>\n app_extensions_function_count?: Optional<number>\n app_extensions_function_custom_layout?: Optional<boolean>\n app_extensions_theme_any?: Optional<boolean>\n app_extensions_theme_count?: Optional<number>\n app_extensions_theme_custom_layout?: Optional<boolean>\n app_extensions_ui_any?: Optional<boolean>\n app_extensions_ui_count?: Optional<number>\n app_extensions_ui_custom_layout?: Optional<boolean>\n app_name_hash?: Optional<string>\n app_path_hash?: Optional<string>\n app_scopes?: Optional<string>\n app_web_backend_any?: Optional<boolean>\n app_web_backend_count?: Optional<number>\n app_web_custom_layout?: Optional<boolean>\n app_web_framework?: Optional<string>\n app_web_frontend_any?: Optional<boolean>\n app_web_frontend_count?: Optional<number>\n\n // Environment\n env_ci?: Optional<boolean>\n env_ci_platform?: Optional<string>\n env_device_id?: Optional<string>\n env_package_manager?: Optional<string>\n env_package_manager_workspaces?: Optional<boolean>\n env_plugin_installed_any_custom?: Optional<boolean>\n env_plugin_installed_shopify?: Optional<string>\n env_shell?: Optional<string>\n env_web_ide?: Optional<string>\n env_cloud?: Optional<string>\n env_is_global?: Optional<boolean>\n env_auth_method?: Optional<string>\n }\n }\n [schemaId: string]: {sensitive: JsonMap; public: JsonMap}\n}\n\n// In reality, we're normally most interested in just this from Schemas, so export it for ease of use.\n// The monorail schema itself has lots of optional values as it must be backwards-compatible. For our schema we want mandatory values instead.\nexport type MonorailEventPublic = DeepRequired<Schemas[typeof MONORAIL_COMMAND_TOPIC]['public']>\nexport type MonorailEventSensitive = Schemas[typeof MONORAIL_COMMAND_TOPIC]['sensitive']\n\ntype MonorailResult = {type: 'ok'} | {type: 'error'; message: string}\n\nconst publishedCommandNames = new Set<string>()\n\n/**\n * Publishes an event to Monorail.\n *\n * @param schemaId - The schema ID of the event to publish.\n * @param publicData - The public data to publish.\n * @param sensitiveData - The sensitive data to publish.\n * @returns A result indicating whether the event was successfully published.\n */\nexport async function publishMonorailEvent<TSchemaId extends keyof Schemas, TPayload extends Schemas[TSchemaId]>(\n schemaId: TSchemaId,\n publicData: TPayload['public'],\n sensitiveData: TPayload['sensitive'],\n): Promise<MonorailResult> {\n // If a command has already been logged, never re-log it. This is to prevent duplication caused by unexpected errors.\n const commandName = publicData.command\n if (commandName && typeof commandName === 'string') {\n if (publishedCommandNames.has(commandName)) {\n return {type: 'ok'}\n }\n publishedCommandNames.add(commandName)\n }\n\n try {\n const currentTime = new Date().getTime()\n const payload = {...publicData, ...sensitiveData}\n const body = JSON.stringify({schema_id: schemaId, payload})\n const headers = buildHeaders(currentTime)\n\n const response = await fetch(url, {method: 'POST', body, headers}, 'non-blocking')\n\n if (response.status === 200) {\n outputDebug(outputContent`Analytics event sent: ${outputToken.json(sanitizePayload(payload))}`)\n return {type: 'ok'}\n } else {\n outputDebug(`Failed to report usage analytics: ${response.statusText}`)\n return {type: 'error', message: response.statusText}\n }\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n let message = 'Failed to report usage analytics'\n if (error instanceof Error) {\n message = message.concat(`: ${error.message}`)\n }\n outputDebug(message)\n return {type: 'error', message}\n }\n}\n\n/**\n * Sanitizies the api_key from the payload and returns a new hash.\n *\n * @param payload - The public and sensitive data.\n * @returns A copy of the payload with the api_key sanitized.\n */\nfunction sanitizePayload<T extends object>(payload: T): T {\n const result = {...payload}\n if ('api_key' in result) {\n result.api_key = '****'\n }\n\n return result\n}\n\nconst buildHeaders = (currentTime: number) => {\n return {\n 'Content-Type': 'application/json; charset=utf-8',\n 'X-Monorail-Edge-Event-Created-At-Ms': currentTime.toString(),\n 'X-Monorail-Edge-Event-Sent-At-Ms': currentTime.toString(),\n }\n}\n"]}
@@ -12,6 +12,15 @@ import { cacheRetrieve, cacheStore } from '../../private/node/conf-store.js';
12
12
  import { fetch } from '@shopify/cli-kit/node/http';
13
13
  const URL = 'https://cdn.shopify.com/static/cli/notifications.json';
14
14
  const EMPTY_CACHE_MESSAGE = 'Cache is empty';
15
+ const COMMANDS_TO_SKIP = [
16
+ 'notifications:list',
17
+ 'notifications:generate',
18
+ 'init',
19
+ 'app:init',
20
+ 'theme:init',
21
+ 'hydrogen:init',
22
+ 'cache:clear',
23
+ ];
15
24
  function url() {
16
25
  return process.env.SHOPIFY_CLI_NOTIFICATIONS_URL ?? URL;
17
26
  }
@@ -45,29 +54,31 @@ const NotificationsSchema = zod.object({ notifications: zod.array(NotificationSc
45
54
  */
46
55
  export async function showNotificationsIfNeeded(currentSurfaces, environment = process.env) {
47
56
  try {
48
- if (skipNotifications(environment) || jsonOutputEnabled(environment))
57
+ const commandId = getCurrentCommandId();
58
+ if (skipNotifications(commandId, environment) || jsonOutputEnabled(environment))
49
59
  return;
50
60
  const notifications = await getNotifications();
51
- const commandId = getCurrentCommandId();
52
61
  const notificationsToShow = filterNotifications(notifications.notifications, commandId, currentSurfaces);
53
62
  outputDebug(`Notifications to show: ${notificationsToShow.length}`);
54
63
  await renderNotifications(notificationsToShow);
55
64
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
65
  }
57
66
  catch (error) {
67
+ if (error.message === EMPTY_CACHE_MESSAGE) {
68
+ outputDebug('Notifications to show: 0 (Cache is empty)');
69
+ return;
70
+ }
58
71
  if (error.message === 'abort')
59
72
  throw new AbortSilentError();
60
- const errorMessage = `Error retrieving notifications: ${error.message}`;
73
+ const errorMessage = `Error showing notifications: ${error.message}`;
61
74
  outputDebug(errorMessage);
62
- if (error.message === EMPTY_CACHE_MESSAGE)
63
- return;
64
75
  // This is very prone to becoming a circular dependency, so we import it dynamically
65
76
  const { sendErrorToBugsnag } = await import('./error-handler.js');
66
77
  await sendErrorToBugsnag(errorMessage, 'unexpected_error');
67
78
  }
68
79
  }
69
- function skipNotifications(environment = process.env) {
70
- return isTruthy(environment.CI) || isTruthy(environment.SHOPIFY_UNIT_TEST);
80
+ function skipNotifications(currentCommand, environment = process.env) {
81
+ return (isTruthy(environment.CI) || isTruthy(environment.SHOPIFY_UNIT_TEST) || COMMANDS_TO_SKIP.includes(currentCommand));
71
82
  }
72
83
  /**
73
84
  * Renders the first 2 notifications to the user.
@@ -117,8 +128,12 @@ export async function getNotifications() {
117
128
  * @returns A string with the notifications.
118
129
  */
119
130
  export async function fetchNotifications() {
120
- outputDebug(`Fetching notifications...`);
121
- const response = await fetch(url(), { signal: AbortSignal.timeout(3 * 1000) });
131
+ outputDebug(`Fetching notifications...`);
132
+ const response = await fetch(url(), undefined, {
133
+ useNetworkLevelRetry: false,
134
+ useAbortSignal: true,
135
+ timeoutMs: 3 * 1000,
136
+ });
122
137
  if (response.status !== 200)
123
138
  throw new Error(`Failed to fetch notifications: ${response.statusText}`);
124
139
  const rawNotifications = await response.text();
@@ -145,10 +160,10 @@ async function cacheNotifications(notifications) {
145
160
  * @param environment - Process environment variables.
146
161
  */
147
162
  export function fetchNotificationsInBackground(currentCommand, argv = process.argv, environment = process.env) {
148
- if (skipNotifications(environment))
163
+ if (skipNotifications(currentCommand, environment))
149
164
  return;
150
165
  let command = 'shopify';
151
- const args = ['notifications', 'list'];
166
+ const args = ['notifications', 'list', '--ignore-errors'];
152
167
  // Run the Shopify command the same way as the current execution when it's not the global installation
153
168
  if (argv[0] && argv[0] !== 'shopify') {
154
169
  command = argv[0];
@@ -1 +1 @@
1
- {"version":3,"file":"notifications-system.js","sourceRoot":"","sources":["../../../src/public/node/notifications-system.ts"],"names":[],"mappings":"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","sourcesContent":["import {versionSatisfies} from './node-package-manager.js'\nimport {renderError, renderInfo, renderWarning} from './ui.js'\nimport {getCurrentCommandId} from './global-context.js'\nimport {outputDebug} from './output.js'\nimport {zod} from './schema.js'\nimport {AbortSilentError} from './error.js'\nimport {isTruthy} from './context/utilities.js'\nimport {exec} from './system.js'\nimport {jsonOutputEnabled} from './environment.js'\nimport {CLI_KIT_VERSION} from '../common/version.js'\nimport {NotificationKey, NotificationsKey, cacheRetrieve, cacheStore} from '../../private/node/conf-store.js'\nimport {fetch} from '@shopify/cli-kit/node/http'\n\nconst URL = 'https://cdn.shopify.com/static/cli/notifications.json'\nconst EMPTY_CACHE_MESSAGE = 'Cache is empty'\n\nfunction url(): string {\n return process.env.SHOPIFY_CLI_NOTIFICATIONS_URL ?? URL\n}\n\nconst NotificationSchema = zod.object({\n id: zod.string(),\n message: zod.string(),\n type: zod.enum(['info', 'warning', 'error']),\n frequency: zod.enum(['always', 'once', 'once_a_day', 'once_a_week']),\n ownerChannel: zod.string(),\n cta: zod\n .object({\n label: zod.string(),\n url: zod.string().url(),\n })\n .optional(),\n title: zod.string().optional(),\n minVersion: zod.string().optional(),\n maxVersion: zod.string().optional(),\n minDate: zod.string().optional(),\n maxDate: zod.string().optional(),\n commands: zod.array(zod.string()).optional(),\n surface: zod.string().optional(),\n})\nexport type Notification = zod.infer<typeof NotificationSchema>\n\nconst NotificationsSchema = zod.object({notifications: zod.array(NotificationSchema)})\nexport type Notifications = zod.infer<typeof NotificationsSchema>\n\n/**\n * Shows notifications to the user if they meet the criteria specified in the notifications.json file.\n *\n * @param currentSurfaces - The surfaces present in the current project (usually for app extensions).\n * @param environment - Process environment variables.\n * @returns - A promise that resolves when the notifications have been shown.\n */\nexport async function showNotificationsIfNeeded(\n currentSurfaces?: string[],\n environment: NodeJS.ProcessEnv = process.env,\n): Promise<void> {\n try {\n if (skipNotifications(environment) || jsonOutputEnabled(environment)) return\n\n const notifications = await getNotifications()\n const commandId = getCurrentCommandId()\n const notificationsToShow = filterNotifications(notifications.notifications, commandId, currentSurfaces)\n outputDebug(`Notifications to show: ${notificationsToShow.length}`)\n await renderNotifications(notificationsToShow)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n if (error.message === 'abort') throw new AbortSilentError()\n const errorMessage = `Error retrieving notifications: ${error.message}`\n outputDebug(errorMessage)\n if (error.message === EMPTY_CACHE_MESSAGE) return\n // This is very prone to becoming a circular dependency, so we import it dynamically\n const {sendErrorToBugsnag} = await import('./error-handler.js')\n await sendErrorToBugsnag(errorMessage, 'unexpected_error')\n }\n}\n\nfunction skipNotifications(environment: NodeJS.ProcessEnv = process.env): boolean {\n return isTruthy(environment.CI) || isTruthy(environment.SHOPIFY_UNIT_TEST)\n}\n\n/**\n * Renders the first 2 notifications to the user.\n *\n * @param notifications - The notifications to render.\n */\nasync function renderNotifications(notifications: Notification[]) {\n notifications.slice(0, 2).forEach((notification) => {\n const content = {\n headline: notification.title,\n body: notification.message.replaceAll('\\\\n', '\\n'),\n link: notification.cta,\n }\n switch (notification.type) {\n case 'info': {\n renderInfo(content)\n break\n }\n case 'warning': {\n renderWarning(content)\n break\n }\n case 'error': {\n renderError(content)\n throw new Error('abort')\n }\n }\n cacheStore(`notification-${notification.id}`, new Date().getTime().toString())\n })\n}\n\n/**\n * Get notifications list from cache, that is updated in the background from bin/fetch-notifications.json.\n *\n * @returns A Notifications object.\n */\nexport async function getNotifications(): Promise<Notifications> {\n const cacheKey: NotificationsKey = `notifications-${url()}`\n const rawNotifications = cacheRetrieve(cacheKey)?.value as unknown as string\n if (!rawNotifications) throw new Error(EMPTY_CACHE_MESSAGE)\n const notifications: object = JSON.parse(rawNotifications)\n return NotificationsSchema.parse(notifications)\n}\n\n/**\n * Fetch notifications from the CDN and chache them.\n *\n * @returns A string with the notifications.\n */\nexport async function fetchNotifications(): Promise<Notifications> {\n outputDebug(`Fetching notifications...`)\n const response = await fetch(url(), {signal: AbortSignal.timeout(3 * 1000)})\n if (response.status !== 200) throw new Error(`Failed to fetch notifications: ${response.statusText}`)\n const rawNotifications = await response.text()\n const notifications: object = JSON.parse(rawNotifications)\n const result = NotificationsSchema.parse(notifications)\n await cacheNotifications(rawNotifications)\n return result\n}\n\n/**\n * Store the notifications in the cache.\n *\n * @param notifications - String with the notifications to cache.\n * @returns A Notifications object.\n */\nasync function cacheNotifications(notifications: string): Promise<void> {\n cacheStore(`notifications-${url()}`, notifications)\n outputDebug(`Notifications from ${url()} stored in the cache`)\n}\n\n/**\n * Fetch notifications in background as a detached process.\n *\n * @param currentCommand - The current Shopify command being run.\n * @param argv - The arguments passed to the current process.\n * @param environment - Process environment variables.\n */\nexport function fetchNotificationsInBackground(\n currentCommand: string,\n argv = process.argv,\n environment: NodeJS.ProcessEnv = process.env,\n): void {\n if (skipNotifications(environment)) return\n\n let command = 'shopify'\n const args = ['notifications', 'list']\n // Run the Shopify command the same way as the current execution when it's not the global installation\n if (argv[0] && argv[0] !== 'shopify') {\n command = argv[0]\n const indexValue = currentCommand.split(':')[0] ?? ''\n const index = argv.indexOf(indexValue)\n if (index > 0) args.unshift(...argv.slice(1, index))\n }\n // eslint-disable-next-line no-void\n void exec(command, args, {background: true, env: {...process.env, SHOPIFY_CLI_NO_ANALYTICS: '1'}})\n}\n\n/**\n * Filters notifications based on the version of the CLI.\n *\n * @param notifications - The notifications to filter.\n * @param commandId - The command ID to filter by.\n * @param currentSurfaces - The surfaces present in the current project (usually for app extensions).\n * @param today - The current date.\n * @param currentVersion - The current version of the CLI.\n * @returns - The filtered notifications.\n */\nexport function filterNotifications(\n notifications: Notification[],\n commandId: string,\n currentSurfaces?: string[],\n today: Date = new Date(new Date().setUTCHours(0, 0, 0, 0)),\n currentVersion: string = CLI_KIT_VERSION,\n): Notification[] {\n return notifications\n .filter((notification) => filterByVersion(notification, currentVersion))\n .filter((notifications) => filterByDate(notifications, today))\n .filter((notification) => filterByCommand(notification, commandId))\n .filter((notification) => filterBySurface(notification, commandId, currentSurfaces))\n .filter((notification) => filterByFrequency(notification))\n}\n\n/**\n * Filters notifications based on the version of the CLI.\n *\n * @param notification - The notification to filter.\n * @param currentVersion - The current version of the CLI.\n */\nfunction filterByVersion(notification: Notification, currentVersion: string) {\n const minVersion = !notification.minVersion || versionSatisfies(currentVersion, `>=${notification.minVersion}`)\n const maxVersion = !notification.maxVersion || versionSatisfies(currentVersion, `<=${notification.maxVersion}`)\n return minVersion && maxVersion\n}\n\n/**\n * Filters notifications based on the date.\n *\n * @param notification - The notification to filter.\n * @param today - The current date.\n */\nfunction filterByDate(notification: Notification, today: Date) {\n const minDate = !notification.minDate || new Date(notification.minDate) <= today\n const maxDate = !notification.maxDate || new Date(notification.maxDate) >= today\n return minDate && maxDate\n}\n\n/**\n * Filters notifications based on the command ID.\n *\n * @param notification - The notification to filter.\n * @param commandId - The command ID to filter by.\n * @returns - A boolean indicating whether the notification should be shown.\n */\nfunction filterByCommand(notification: Notification, commandId: string) {\n if (commandId === '') return true\n return !notification.commands || notification.commands.includes(commandId)\n}\n\n/**\n * Filters notifications based on the surface.\n *\n * @param notification - The notification to filter.\n * @param commandId - The command id.\n * @param surfacesFromContext - The surfaces present in the current project (usually for app extensions).\n * @returns - A boolean indicating whether the notification should be shown.\n */\nfunction filterBySurface(notification: Notification, commandId: string, surfacesFromContext?: string[]) {\n const surfaceFromCommand = commandId.split(':')[0] ?? 'all'\n const notificationSurface = notification.surface ?? 'all'\n\n if (surfacesFromContext) return surfacesFromContext.includes(notificationSurface)\n\n return notificationSurface === surfaceFromCommand || notificationSurface === 'all'\n}\n\n/**\n * Filters notifications based on the frequency.\n *\n * @param notification - The notification to filter.\n * @returns - A boolean indicating whether the notification should be shown.\n */\nfunction filterByFrequency(notification: Notification): boolean {\n if (!notification.frequency) return true\n const cacheKey: NotificationKey = `notification-${notification.id}`\n const lastShown = cacheRetrieve(cacheKey)?.value as unknown as string\n if (!lastShown) return true\n\n switch (notification.frequency) {\n case 'always': {\n return true\n }\n case 'once': {\n return false\n }\n case 'once_a_day': {\n return new Date().getTime() - Number(lastShown) > 24 * 3600 * 1000\n }\n case 'once_a_week': {\n return new Date().getTime() - Number(lastShown) > 7 * 24 * 3600 * 1000\n }\n }\n}\n\n/**\n * Returns a string with the filters from a notification, one by line.\n *\n * @param notification - The notification to get the filters from.\n * @returns A string with human-readable filters from the notification.\n */\nexport function stringifyFilters(notification: Notification): string {\n const filters = []\n if (notification.minDate) filters.push(`from ${notification.minDate}`)\n if (notification.maxDate) filters.push(`to ${notification.maxDate}`)\n if (notification.minVersion) filters.push(`from v${notification.minVersion}`)\n if (notification.maxVersion) filters.push(`to v${notification.maxVersion}`)\n if (notification.frequency === 'once') filters.push('show only once')\n if (notification.frequency === 'once_a_day') filters.push('show once a day')\n if (notification.frequency === 'once_a_week') filters.push('show once a week')\n if (notification.surface) filters.push(`surface = ${notification.surface}`)\n if (notification.commands) filters.push(`commands = ${notification.commands.join(', ')}`)\n return filters.join('\\n')\n}\n"]}
1
+ {"version":3,"file":"notifications-system.js","sourceRoot":"","sources":["../../../src/public/node/notifications-system.ts"],"names":[],"mappings":"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","sourcesContent":["import {versionSatisfies} from './node-package-manager.js'\nimport {renderError, renderInfo, renderWarning} from './ui.js'\nimport {getCurrentCommandId} from './global-context.js'\nimport {outputDebug} from './output.js'\nimport {zod} from './schema.js'\nimport {AbortSilentError} from './error.js'\nimport {isTruthy} from './context/utilities.js'\nimport {exec} from './system.js'\nimport {jsonOutputEnabled} from './environment.js'\nimport {CLI_KIT_VERSION} from '../common/version.js'\nimport {NotificationKey, NotificationsKey, cacheRetrieve, cacheStore} from '../../private/node/conf-store.js'\nimport {fetch} from '@shopify/cli-kit/node/http'\n\nconst URL = 'https://cdn.shopify.com/static/cli/notifications.json'\nconst EMPTY_CACHE_MESSAGE = 'Cache is empty'\nconst COMMANDS_TO_SKIP = [\n 'notifications:list',\n 'notifications:generate',\n 'init',\n 'app:init',\n 'theme:init',\n 'hydrogen:init',\n 'cache:clear',\n]\n\nfunction url(): string {\n return process.env.SHOPIFY_CLI_NOTIFICATIONS_URL ?? URL\n}\n\nconst NotificationSchema = zod.object({\n id: zod.string(),\n message: zod.string(),\n type: zod.enum(['info', 'warning', 'error']),\n frequency: zod.enum(['always', 'once', 'once_a_day', 'once_a_week']),\n ownerChannel: zod.string(),\n cta: zod\n .object({\n label: zod.string(),\n url: zod.string().url(),\n })\n .optional(),\n title: zod.string().optional(),\n minVersion: zod.string().optional(),\n maxVersion: zod.string().optional(),\n minDate: zod.string().optional(),\n maxDate: zod.string().optional(),\n commands: zod.array(zod.string()).optional(),\n surface: zod.string().optional(),\n})\nexport type Notification = zod.infer<typeof NotificationSchema>\n\nconst NotificationsSchema = zod.object({notifications: zod.array(NotificationSchema)})\nexport type Notifications = zod.infer<typeof NotificationsSchema>\n\n/**\n * Shows notifications to the user if they meet the criteria specified in the notifications.json file.\n *\n * @param currentSurfaces - The surfaces present in the current project (usually for app extensions).\n * @param environment - Process environment variables.\n * @returns - A promise that resolves when the notifications have been shown.\n */\nexport async function showNotificationsIfNeeded(\n currentSurfaces?: string[],\n environment: NodeJS.ProcessEnv = process.env,\n): Promise<void> {\n try {\n const commandId = getCurrentCommandId()\n if (skipNotifications(commandId, environment) || jsonOutputEnabled(environment)) return\n\n const notifications = await getNotifications()\n const notificationsToShow = filterNotifications(notifications.notifications, commandId, currentSurfaces)\n outputDebug(`Notifications to show: ${notificationsToShow.length}`)\n await renderNotifications(notificationsToShow)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n if (error.message === EMPTY_CACHE_MESSAGE) {\n outputDebug('Notifications to show: 0 (Cache is empty)')\n return\n }\n if (error.message === 'abort') throw new AbortSilentError()\n const errorMessage = `Error showing notifications: ${error.message}`\n outputDebug(errorMessage)\n // This is very prone to becoming a circular dependency, so we import it dynamically\n const {sendErrorToBugsnag} = await import('./error-handler.js')\n await sendErrorToBugsnag(errorMessage, 'unexpected_error')\n }\n}\n\nfunction skipNotifications(currentCommand: string, environment: NodeJS.ProcessEnv = process.env): boolean {\n return (\n isTruthy(environment.CI) || isTruthy(environment.SHOPIFY_UNIT_TEST) || COMMANDS_TO_SKIP.includes(currentCommand)\n )\n}\n\n/**\n * Renders the first 2 notifications to the user.\n *\n * @param notifications - The notifications to render.\n */\nasync function renderNotifications(notifications: Notification[]) {\n notifications.slice(0, 2).forEach((notification) => {\n const content = {\n headline: notification.title,\n body: notification.message.replaceAll('\\\\n', '\\n'),\n link: notification.cta,\n }\n switch (notification.type) {\n case 'info': {\n renderInfo(content)\n break\n }\n case 'warning': {\n renderWarning(content)\n break\n }\n case 'error': {\n renderError(content)\n throw new Error('abort')\n }\n }\n cacheStore(`notification-${notification.id}`, new Date().getTime().toString())\n })\n}\n\n/**\n * Get notifications list from cache, that is updated in the background from bin/fetch-notifications.json.\n *\n * @returns A Notifications object.\n */\nexport async function getNotifications(): Promise<Notifications> {\n const cacheKey: NotificationsKey = `notifications-${url()}`\n const rawNotifications = cacheRetrieve(cacheKey)?.value as unknown as string\n if (!rawNotifications) throw new Error(EMPTY_CACHE_MESSAGE)\n const notifications: object = JSON.parse(rawNotifications)\n return NotificationsSchema.parse(notifications)\n}\n\n/**\n * Fetch notifications from the CDN and chache them.\n *\n * @returns A string with the notifications.\n */\nexport async function fetchNotifications(): Promise<Notifications> {\n outputDebug(`Fetching notifications...`)\n const response = await fetch(url(), undefined, {\n useNetworkLevelRetry: false,\n useAbortSignal: true,\n timeoutMs: 3 * 1000,\n })\n if (response.status !== 200) throw new Error(`Failed to fetch notifications: ${response.statusText}`)\n const rawNotifications = await response.text()\n const notifications: object = JSON.parse(rawNotifications)\n const result = NotificationsSchema.parse(notifications)\n await cacheNotifications(rawNotifications)\n return result\n}\n\n/**\n * Store the notifications in the cache.\n *\n * @param notifications - String with the notifications to cache.\n * @returns A Notifications object.\n */\nasync function cacheNotifications(notifications: string): Promise<void> {\n cacheStore(`notifications-${url()}`, notifications)\n outputDebug(`Notifications from ${url()} stored in the cache`)\n}\n\n/**\n * Fetch notifications in background as a detached process.\n *\n * @param currentCommand - The current Shopify command being run.\n * @param argv - The arguments passed to the current process.\n * @param environment - Process environment variables.\n */\nexport function fetchNotificationsInBackground(\n currentCommand: string,\n argv = process.argv,\n environment: NodeJS.ProcessEnv = process.env,\n): void {\n if (skipNotifications(currentCommand, environment)) return\n\n let command = 'shopify'\n const args = ['notifications', 'list', '--ignore-errors']\n // Run the Shopify command the same way as the current execution when it's not the global installation\n if (argv[0] && argv[0] !== 'shopify') {\n command = argv[0]\n const indexValue = currentCommand.split(':')[0] ?? ''\n const index = argv.indexOf(indexValue)\n if (index > 0) args.unshift(...argv.slice(1, index))\n }\n // eslint-disable-next-line no-void\n void exec(command, args, {background: true, env: {...process.env, SHOPIFY_CLI_NO_ANALYTICS: '1'}})\n}\n\n/**\n * Filters notifications based on the version of the CLI.\n *\n * @param notifications - The notifications to filter.\n * @param commandId - The command ID to filter by.\n * @param currentSurfaces - The surfaces present in the current project (usually for app extensions).\n * @param today - The current date.\n * @param currentVersion - The current version of the CLI.\n * @returns - The filtered notifications.\n */\nexport function filterNotifications(\n notifications: Notification[],\n commandId: string,\n currentSurfaces?: string[],\n today: Date = new Date(new Date().setUTCHours(0, 0, 0, 0)),\n currentVersion: string = CLI_KIT_VERSION,\n): Notification[] {\n return notifications\n .filter((notification) => filterByVersion(notification, currentVersion))\n .filter((notifications) => filterByDate(notifications, today))\n .filter((notification) => filterByCommand(notification, commandId))\n .filter((notification) => filterBySurface(notification, commandId, currentSurfaces))\n .filter((notification) => filterByFrequency(notification))\n}\n\n/**\n * Filters notifications based on the version of the CLI.\n *\n * @param notification - The notification to filter.\n * @param currentVersion - The current version of the CLI.\n */\nfunction filterByVersion(notification: Notification, currentVersion: string) {\n const minVersion = !notification.minVersion || versionSatisfies(currentVersion, `>=${notification.minVersion}`)\n const maxVersion = !notification.maxVersion || versionSatisfies(currentVersion, `<=${notification.maxVersion}`)\n return minVersion && maxVersion\n}\n\n/**\n * Filters notifications based on the date.\n *\n * @param notification - The notification to filter.\n * @param today - The current date.\n */\nfunction filterByDate(notification: Notification, today: Date) {\n const minDate = !notification.minDate || new Date(notification.minDate) <= today\n const maxDate = !notification.maxDate || new Date(notification.maxDate) >= today\n return minDate && maxDate\n}\n\n/**\n * Filters notifications based on the command ID.\n *\n * @param notification - The notification to filter.\n * @param commandId - The command ID to filter by.\n * @returns - A boolean indicating whether the notification should be shown.\n */\nfunction filterByCommand(notification: Notification, commandId: string) {\n if (commandId === '') return true\n return !notification.commands || notification.commands.includes(commandId)\n}\n\n/**\n * Filters notifications based on the surface.\n *\n * @param notification - The notification to filter.\n * @param commandId - The command id.\n * @param surfacesFromContext - The surfaces present in the current project (usually for app extensions).\n * @returns - A boolean indicating whether the notification should be shown.\n */\nfunction filterBySurface(notification: Notification, commandId: string, surfacesFromContext?: string[]) {\n const surfaceFromCommand = commandId.split(':')[0] ?? 'all'\n const notificationSurface = notification.surface ?? 'all'\n\n if (surfacesFromContext) return surfacesFromContext.includes(notificationSurface)\n\n return notificationSurface === surfaceFromCommand || notificationSurface === 'all'\n}\n\n/**\n * Filters notifications based on the frequency.\n *\n * @param notification - The notification to filter.\n * @returns - A boolean indicating whether the notification should be shown.\n */\nfunction filterByFrequency(notification: Notification): boolean {\n if (!notification.frequency) return true\n const cacheKey: NotificationKey = `notification-${notification.id}`\n const lastShown = cacheRetrieve(cacheKey)?.value as unknown as string\n if (!lastShown) return true\n\n switch (notification.frequency) {\n case 'always': {\n return true\n }\n case 'once': {\n return false\n }\n case 'once_a_day': {\n return new Date().getTime() - Number(lastShown) > 24 * 3600 * 1000\n }\n case 'once_a_week': {\n return new Date().getTime() - Number(lastShown) > 7 * 24 * 3600 * 1000\n }\n }\n}\n\n/**\n * Returns a string with the filters from a notification, one by line.\n *\n * @param notification - The notification to get the filters from.\n * @returns A string with human-readable filters from the notification.\n */\nexport function stringifyFilters(notification: Notification): string {\n const filters = []\n if (notification.minDate) filters.push(`from ${notification.minDate}`)\n if (notification.maxDate) filters.push(`to ${notification.maxDate}`)\n if (notification.minVersion) filters.push(`from v${notification.minVersion}`)\n if (notification.maxVersion) filters.push(`to v${notification.maxVersion}`)\n if (notification.frequency === 'once') filters.push('show only once')\n if (notification.frequency === 'once_a_day') filters.push('show once a day')\n if (notification.frequency === 'once_a_week') filters.push('show once a week')\n if (notification.surface) filters.push(`surface = ${notification.surface}`)\n if (notification.commands) filters.push(`commands = ${notification.commands.join(', ')}`)\n return filters.join('\\n')\n}\n"]}
@@ -51,3 +51,9 @@ export declare function sleep(seconds: number): Promise<void>;
51
51
  * @returns True if the standard input and output streams support prompting.
52
52
  */
53
53
  export declare function terminalSupportsPrompting(): boolean;
54
+ /**
55
+ * Check if the current environment is a CI environment.
56
+ *
57
+ * @returns True if the current environment is a CI environment.
58
+ */
59
+ export declare function isCI(): boolean;
@@ -117,8 +117,7 @@ function buildExec(command, args, options) {
117
117
  detached: options?.background,
118
118
  cleanup: !options?.background,
119
119
  });
120
- outputDebug(`
121
- Running system process${options?.background ? ' in background' : ''}:
120
+ outputDebug(`Running system process${options?.background ? ' in background' : ''}:
122
121
  · Command: ${command} ${args.join(' ')}
123
122
  · Working directory: ${executionCwd}
124
123
  `);
@@ -159,4 +158,12 @@ export function terminalSupportsPrompting() {
159
158
  }
160
159
  return Boolean(process.stdin.isTTY && process.stdout.isTTY);
161
160
  }
161
+ /**
162
+ * Check if the current environment is a CI environment.
163
+ *
164
+ * @returns True if the current environment is a CI environment.
165
+ */
166
+ export function isCI() {
167
+ return isTruthy(process.env.CI);
168
+ }
162
169
  //# sourceMappingURL=system.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"system.js","sourceRoot":"","sources":["../../../src/public/node/system.ts"],"names":[],"mappings":"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","sourcesContent":["import {AbortSignal} from './abort.js'\nimport {AbortError, ExternalError} from './error.js'\nimport {cwd, dirname} from './path.js'\nimport {treeKill} from './tree-kill.js'\nimport {isTruthy} from './context/utilities.js'\nimport {renderWarning} from './ui.js'\nimport {platformAndArch} from './os.js'\nimport {shouldDisplayColors, outputDebug} from '../../public/node/output.js'\nimport {execa, ExecaChildProcess} from 'execa'\nimport which from 'which'\nimport {delimiter} from 'pathe'\nimport type {Writable, Readable} from 'stream'\n\nexport interface ExecOptions {\n cwd?: string\n env?: {[key: string]: string | undefined}\n stdin?: Readable | 'inherit'\n stdout?: Writable | 'inherit'\n stderr?: Writable | 'inherit'\n stdio?: 'inherit'\n input?: string\n signal?: AbortSignal\n // Custom handler if process exits with a non-zero code\n externalErrorHandler?: (error: unknown) => Promise<void>\n // Ignored on Windows\n background?: boolean\n}\n\n/**\n * Opens a URL in the user's default browser.\n *\n * @param url - URL to open.\n * @returns A promise that resolves true if the URL was opened successfully, false otherwise.\n */\nexport async function openURL(url: string): Promise<boolean> {\n const externalOpen = await import('open')\n try {\n await externalOpen.default(url)\n return true\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n return false\n }\n}\n\n/**\n * Runs a command asynchronously, aggregates the stdout data, and returns it.\n *\n * @param command - Command to be executed.\n * @param args - Arguments to pass to the command.\n * @param options - Optional settings for how to run the command.\n * @returns A promise that resolves with the aggregatted stdout of the command.\n */\nexport async function captureOutput(command: string, args: string[], options?: ExecOptions): Promise<string> {\n const result = await buildExec(command, args, options)\n return result.stdout\n}\n\n/**\n * Runs a command asynchronously.\n *\n * @param command - Command to be executed.\n * @param args - Arguments to pass to the command.\n * @param options - Optional settings for how to run the command.\n */\nexport async function exec(command: string, args: string[], options?: ExecOptions): Promise<void> {\n if (options) {\n // Windows opens a new console window when running a command in the background, so we disable it.\n const runningOnWindows = platformAndArch().platform === 'windows'\n options.background = runningOnWindows ? false : options.background\n }\n\n const commandProcess = buildExec(command, args, options)\n\n if (options?.background) {\n commandProcess.unref()\n }\n\n if (options?.stderr && options.stderr !== 'inherit') {\n commandProcess.stderr?.pipe(options.stderr, {end: false})\n }\n if (options?.stdout && options.stdout !== 'inherit') {\n commandProcess.stdout?.pipe(options.stdout, {end: false})\n }\n let aborted = false\n options?.signal?.addEventListener('abort', () => {\n const pid = commandProcess.pid\n if (pid) {\n outputDebug(`Killing process ${pid}: ${command} ${args.join(' ')}`)\n aborted = true\n treeKill(pid, 'SIGTERM')\n }\n })\n try {\n await commandProcess\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (processError: any) {\n // Windows will throw an error whenever the process is killed, no matter the reason.\n // The aborted flag tell use that we killed it, so we can ignore the error.\n if (aborted) return\n if (options?.externalErrorHandler) {\n await options.externalErrorHandler(processError)\n } else {\n const abortError = new ExternalError(processError.message, command, args)\n abortError.stack = processError.stack\n throw abortError\n }\n }\n}\n\n/**\n * Runs a command asynchronously.\n *\n * @param command - Command to be executed.\n * @param args - Arguments to pass to the command.\n * @param options - Optional settings for how to run the command.\n * @returns A promise for a result with stdout and stderr properties.\n */\nfunction buildExec(command: string, args: string[], options?: ExecOptions): ExecaChildProcess {\n const env = options?.env ?? process.env\n if (shouldDisplayColors()) {\n env.FORCE_COLOR = '1'\n }\n const executionCwd = options?.cwd ?? cwd()\n checkCommandSafety(command, {cwd: executionCwd})\n const commandProcess = execa(command, args, {\n env,\n cwd: executionCwd,\n input: options?.input,\n stdio: options?.background ? 'ignore' : options?.stdio,\n stdin: options?.stdin,\n stdout: options?.stdout === 'inherit' ? 'inherit' : undefined,\n stderr: options?.stderr === 'inherit' ? 'inherit' : undefined,\n // Setting this to false makes it possible to kill the main process\n // and all its sub-processes with Ctrl+C on Windows\n windowsHide: false,\n detached: options?.background,\n cleanup: !options?.background,\n })\n outputDebug(`\nRunning system process${options?.background ? ' in background' : ''}:\n · Command: ${command} ${args.join(' ')}\n · Working directory: ${executionCwd}\n`)\n return commandProcess\n}\n\nfunction checkCommandSafety(command: string, _options: {cwd: string}): void {\n const pathIncludingLocal = `${_options.cwd}${delimiter}${process.env.PATH}`\n const commandPath = which.sync(command, {\n nothrow: true,\n path: pathIncludingLocal,\n })\n if (commandPath && dirname(commandPath) === _options.cwd) {\n const headline = ['Skipped run of unsecure binary', {command}, 'found in the current directory.']\n const body = 'Please remove that file or review your current PATH.'\n renderWarning({headline, body})\n throw new AbortError(headline, body)\n }\n}\n\n/**\n * Waits for a given number of seconds.\n *\n * @param seconds - Number of seconds to wait.\n * @returns A Promise resolving after the number of seconds.\n */\nexport async function sleep(seconds: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, 1000 * seconds)\n })\n}\n\n/**\n * Check if the standard input and output streams support prompting.\n *\n * @returns True if the standard input and output streams support prompting.\n */\nexport function terminalSupportsPrompting(): boolean {\n if (isTruthy(process.env.CI)) {\n return false\n }\n return Boolean(process.stdin.isTTY && process.stdout.isTTY)\n}\n"]}
1
+ {"version":3,"file":"system.js","sourceRoot":"","sources":["../../../src/public/node/system.ts"],"names":[],"mappings":"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","sourcesContent":["import {AbortSignal} from './abort.js'\nimport {AbortError, ExternalError} from './error.js'\nimport {cwd, dirname} from './path.js'\nimport {treeKill} from './tree-kill.js'\nimport {isTruthy} from './context/utilities.js'\nimport {renderWarning} from './ui.js'\nimport {platformAndArch} from './os.js'\nimport {shouldDisplayColors, outputDebug} from '../../public/node/output.js'\nimport {execa, ExecaChildProcess} from 'execa'\nimport which from 'which'\nimport {delimiter} from 'pathe'\nimport type {Writable, Readable} from 'stream'\n\nexport interface ExecOptions {\n cwd?: string\n env?: {[key: string]: string | undefined}\n stdin?: Readable | 'inherit'\n stdout?: Writable | 'inherit'\n stderr?: Writable | 'inherit'\n stdio?: 'inherit'\n input?: string\n signal?: AbortSignal\n // Custom handler if process exits with a non-zero code\n externalErrorHandler?: (error: unknown) => Promise<void>\n // Ignored on Windows\n background?: boolean\n}\n\n/**\n * Opens a URL in the user's default browser.\n *\n * @param url - URL to open.\n * @returns A promise that resolves true if the URL was opened successfully, false otherwise.\n */\nexport async function openURL(url: string): Promise<boolean> {\n const externalOpen = await import('open')\n try {\n await externalOpen.default(url)\n return true\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n return false\n }\n}\n\n/**\n * Runs a command asynchronously, aggregates the stdout data, and returns it.\n *\n * @param command - Command to be executed.\n * @param args - Arguments to pass to the command.\n * @param options - Optional settings for how to run the command.\n * @returns A promise that resolves with the aggregatted stdout of the command.\n */\nexport async function captureOutput(command: string, args: string[], options?: ExecOptions): Promise<string> {\n const result = await buildExec(command, args, options)\n return result.stdout\n}\n\n/**\n * Runs a command asynchronously.\n *\n * @param command - Command to be executed.\n * @param args - Arguments to pass to the command.\n * @param options - Optional settings for how to run the command.\n */\nexport async function exec(command: string, args: string[], options?: ExecOptions): Promise<void> {\n if (options) {\n // Windows opens a new console window when running a command in the background, so we disable it.\n const runningOnWindows = platformAndArch().platform === 'windows'\n options.background = runningOnWindows ? false : options.background\n }\n\n const commandProcess = buildExec(command, args, options)\n\n if (options?.background) {\n commandProcess.unref()\n }\n\n if (options?.stderr && options.stderr !== 'inherit') {\n commandProcess.stderr?.pipe(options.stderr, {end: false})\n }\n if (options?.stdout && options.stdout !== 'inherit') {\n commandProcess.stdout?.pipe(options.stdout, {end: false})\n }\n let aborted = false\n options?.signal?.addEventListener('abort', () => {\n const pid = commandProcess.pid\n if (pid) {\n outputDebug(`Killing process ${pid}: ${command} ${args.join(' ')}`)\n aborted = true\n treeKill(pid, 'SIGTERM')\n }\n })\n try {\n await commandProcess\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (processError: any) {\n // Windows will throw an error whenever the process is killed, no matter the reason.\n // The aborted flag tell use that we killed it, so we can ignore the error.\n if (aborted) return\n if (options?.externalErrorHandler) {\n await options.externalErrorHandler(processError)\n } else {\n const abortError = new ExternalError(processError.message, command, args)\n abortError.stack = processError.stack\n throw abortError\n }\n }\n}\n\n/**\n * Runs a command asynchronously.\n *\n * @param command - Command to be executed.\n * @param args - Arguments to pass to the command.\n * @param options - Optional settings for how to run the command.\n * @returns A promise for a result with stdout and stderr properties.\n */\nfunction buildExec(command: string, args: string[], options?: ExecOptions): ExecaChildProcess {\n const env = options?.env ?? process.env\n if (shouldDisplayColors()) {\n env.FORCE_COLOR = '1'\n }\n const executionCwd = options?.cwd ?? cwd()\n checkCommandSafety(command, {cwd: executionCwd})\n const commandProcess = execa(command, args, {\n env,\n cwd: executionCwd,\n input: options?.input,\n stdio: options?.background ? 'ignore' : options?.stdio,\n stdin: options?.stdin,\n stdout: options?.stdout === 'inherit' ? 'inherit' : undefined,\n stderr: options?.stderr === 'inherit' ? 'inherit' : undefined,\n // Setting this to false makes it possible to kill the main process\n // and all its sub-processes with Ctrl+C on Windows\n windowsHide: false,\n detached: options?.background,\n cleanup: !options?.background,\n })\n outputDebug(`Running system process${options?.background ? ' in background' : ''}:\n · Command: ${command} ${args.join(' ')}\n · Working directory: ${executionCwd}\n`)\n return commandProcess\n}\n\nfunction checkCommandSafety(command: string, _options: {cwd: string}): void {\n const pathIncludingLocal = `${_options.cwd}${delimiter}${process.env.PATH}`\n const commandPath = which.sync(command, {\n nothrow: true,\n path: pathIncludingLocal,\n })\n if (commandPath && dirname(commandPath) === _options.cwd) {\n const headline = ['Skipped run of unsecure binary', {command}, 'found in the current directory.']\n const body = 'Please remove that file or review your current PATH.'\n renderWarning({headline, body})\n throw new AbortError(headline, body)\n }\n}\n\n/**\n * Waits for a given number of seconds.\n *\n * @param seconds - Number of seconds to wait.\n * @returns A Promise resolving after the number of seconds.\n */\nexport async function sleep(seconds: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, 1000 * seconds)\n })\n}\n\n/**\n * Check if the standard input and output streams support prompting.\n *\n * @returns True if the standard input and output streams support prompting.\n */\nexport function terminalSupportsPrompting(): boolean {\n if (isTruthy(process.env.CI)) {\n return false\n }\n return Boolean(process.stdin.isTTY && process.stdout.isTTY)\n}\n\n/**\n * Check if the current environment is a CI environment.\n *\n * @returns True if the current environment is a CI environment.\n */\nexport function isCI(): boolean {\n return isTruthy(process.env.CI)\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import { TestAPI } from 'vitest';
2
+ interface TempDirFixture {
3
+ tempDir: string;
4
+ }
5
+ /**
6
+ * Vitest fixture providing the test with a temporary directory to work in.
7
+ */
8
+ export declare const testWithTempDir: TestAPI<TempDirFixture>;
9
+ export {};
@@ -0,0 +1,14 @@
1
+ import { inTemporaryDirectory } from '../fs.js';
2
+ import { test } from 'vitest';
3
+ /**
4
+ * Vitest fixture providing the test with a temporary directory to work in.
5
+ */
6
+ export const testWithTempDir = test.extend({
7
+ // eslint-disable-next-line no-empty-pattern
8
+ tempDir: async ({}, use) => {
9
+ await inTemporaryDirectory(async (tempDir) => {
10
+ await use(tempDir);
11
+ });
12
+ },
13
+ });
14
+ //# sourceMappingURL=test-with-temp-dir.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-with-temp-dir.js","sourceRoot":"","sources":["../../../../src/public/node/testing/test-with-temp-dir.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,oBAAoB,EAAC,MAAM,UAAU,CAAA;AAC7C,OAAO,EAAC,IAAI,EAAU,MAAM,QAAQ,CAAA;AAMpC;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAA4B,IAAI,CAAC,MAAM,CAAiB;IAClF,4CAA4C;IAC5C,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE;QACzB,MAAM,oBAAoB,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;YAC3C,MAAM,GAAG,CAAC,OAAO,CAAC,CAAA;QACpB,CAAC,CAAC,CAAA;IACJ,CAAC;CACF,CAAC,CAAA","sourcesContent":["import {inTemporaryDirectory} from '../fs.js'\nimport {test, TestAPI} from 'vitest'\n\ninterface TempDirFixture {\n tempDir: string\n}\n\n/**\n * Vitest fixture providing the test with a temporary directory to work in.\n */\nexport const testWithTempDir: TestAPI<TempDirFixture> = test.extend<TempDirFixture>({\n // eslint-disable-next-line no-empty-pattern\n tempDir: async ({}, use) => {\n await inTemporaryDirectory(async (tempDir) => {\n await use(tempDir)\n })\n },\n})\n"]}
@@ -4,18 +4,19 @@ import { AdminSession } from '../session.js';
4
4
  */
5
5
  export type Key = string;
6
6
  export type ThemeFSEventName = 'add' | 'change' | 'unlink';
7
+ type OnSync = (onSuccess: () => void, onError?: () => void) => void;
7
8
  type ThemeFSEvent = {
8
9
  type: 'unlink';
9
10
  payload: {
10
11
  fileKey: Key;
11
- onSync?: (fn: () => void) => void;
12
+ onSync: OnSync;
12
13
  };
13
14
  } | {
14
15
  type: 'add' | 'change';
15
16
  payload: {
16
17
  fileKey: Key;
17
18
  onContent: (fn: (content: string) => void) => void;
18
- onSync: (fn: () => void) => void;
19
+ onSync: OnSync;
19
20
  };
20
21
  };
21
22
  export type ThemeFSEventPayload<T extends ThemeFSEventName = 'add'> = (ThemeFSEvent & {
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/public/node/themes/types.ts"],"names":[],"mappings":"AA4NA,MAAM,CAAN,IAAY,SAGX;AAHD,WAAY,SAAS;IACnB,8BAAiB,CAAA;IACjB,8BAAiB,CAAA;AACnB,CAAC,EAHW,SAAS,KAAT,SAAS,QAGpB","sourcesContent":["import {AdminSession} from '../session.js'\n\n/**\n * {@link Key} represents the unique identifier of a file in a theme.\n */\nexport type Key = string\n\nexport type ThemeFSEventName = 'add' | 'change' | 'unlink'\n\ntype ThemeFSEvent =\n | {\n type: 'unlink'\n payload: {fileKey: Key; onSync?: (fn: () => void) => void}\n }\n | {\n type: 'add' | 'change'\n payload: {\n fileKey: Key\n onContent: (fn: (content: string) => void) => void\n onSync: (fn: () => void) => void\n }\n }\n\nexport type ThemeFSEventPayload<T extends ThemeFSEventName = 'add'> = (ThemeFSEvent & {type: T})['payload']\n\nexport interface ThemeFileSystemOptions {\n filters?: {ignore?: string[]; only?: string[]}\n notify?: string\n noDelete?: boolean\n}\n\n/**\n * Represents a theme on the file system.\n */\nexport interface VirtualFileSystem {\n /**\n * The root path of the theme.\n */\n root: string\n\n /**\n * Local files.\n */\n files: Map<Key, ThemeAsset>\n\n /**\n * File keys that have been modified in memory and are not uploaded yet.\n */\n unsyncedFileKeys: Set<Key>\n\n /**\n * Promise that resolves when all the initial files are found.\n */\n ready: () => Promise<void>\n\n /**\n * Removes a file from the local disk and updates the file system\n *\n * @param fileKey - The key of the file to remove\n */\n delete: (fileKey: Key) => Promise<void>\n\n /**\n * Writes a file to the local disk and updates the file system\n *\n * @param asset - The ThemeAsset representing the file to write\n */\n write: (asset: ThemeAsset) => Promise<void>\n\n /**\n * Reads a file from the local disk and updates the file system\n * Returns a ThemeAsset representing the file that was read\n * Returns undefined if the file does not exist\n *\n * @param fileKey - The key of the file to read\n */\n read: (fileKey: Key) => Promise<string | Buffer | undefined>\n\n /**\n * Add callbacks to run after certain events are fired.\n */\n addEventListener: <T extends ThemeFSEventName>(eventName: T, cb: (params: ThemeFSEventPayload<T>) => void) => void\n}\n\n/**\n * Represents a theme on the file system.\n */\nexport interface ThemeFileSystem extends VirtualFileSystem {\n /**\n * Starts a file watcher for the theme directory.\n *\n * @param themeId - The ID of the theme being watched.\n * @param adminSession - The admin session for API communication.\n * @returns A Promise that resolves to an FSWatcher instance.\n */\n startWatcher: (themeId: string, adminSession: AdminSession) => Promise<void>\n\n /**\n * Applies filters to ignore files from .shopifyignore file, --ignore and --only flags.\n */\n applyIgnoreFilters: <T extends {key: string}>(files: T[]) => T[]\n\n /**\n * Stores upload errors returned when uploading files via the Asset API\n */\n uploadErrors: Map<Key, string[]>\n}\n\n/**\n * Represents a theme on the file system.\n */\nexport interface ThemeExtensionFileSystem extends VirtualFileSystem {\n /**\n * Starts a file watcher for the theme extension directory.\n */\n startWatcher: () => Promise<void>\n}\n\n/**\n * Represents a theme.\n */\nexport interface Theme {\n /**\n * The remote ID of the theme.\n */\n id: number\n\n /**\n * The remote name of the theme.\n */\n name: string\n\n /**\n * A boolean determining whether or not the theme was created at runtime.\n */\n createdAtRuntime: boolean\n\n /**\n * A boolean determining if the theme is processing at the theme library.\n */\n processing: boolean\n\n /**\n * The remote role of the theme.\n */\n role: string\n\n /**\n * A public URL where Shopify can access the theme code.\n */\n src?: string\n}\n\n/**\n * Represents the remote checksum for a file in a theme.\n */\nexport interface Checksum {\n /**\n * Identifier of the theme file.\n */\n key: Key\n\n /**\n * Represents the checksum value of the theme file.\n */\n checksum: string\n}\n\n/**\n * Represents a theme or theme extension asset.\n */\nexport interface ThemeAsset extends Checksum {\n /**\n * A base64-encoded image.\n */\n attachment?: string\n\n /**\n * The text content of the asset, such as the HTML and Liquid markup of a template file.\n */\n value?: string\n\n /**\n * File stats at time of last modification. For attachments, this is the size of the base64 string.\n */\n stats?: {mtime: number; size: number}\n}\n\n/**\n * Represents a single result for an upload or delete operation on a single file\n * Each result includes the unique identifier for the file, the type of the operation,\n * the success status of the operation, any errors that occurred, and the asset value of the file.\n */\nexport interface Result {\n /**\n * The unique identifier for the file being uploaded.\n */\n key: string\n\n /**\n * The operation associated with the result.\n */\n operation: Operation\n\n /**\n * Indicates whether the upload operation for this file was successful.\n */\n success: boolean\n\n /**\n * Error message that was generated during the upload operation for this file.\n */\n errors?: {asset?: string[]}\n\n /**\n * The asset that was uploaded as part of the upload operation for this file.\n */\n asset?: Omit<ThemeAsset, 'stats'>\n}\n\nexport enum Operation {\n Delete = 'DELETE',\n Upload = 'UPLOAD',\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/public/node/themes/types.ts"],"names":[],"mappings":"AA8NA,MAAM,CAAN,IAAY,SAGX;AAHD,WAAY,SAAS;IACnB,8BAAiB,CAAA;IACjB,8BAAiB,CAAA;AACnB,CAAC,EAHW,SAAS,KAAT,SAAS,QAGpB","sourcesContent":["import {AdminSession} from '../session.js'\n\n/**\n * {@link Key} represents the unique identifier of a file in a theme.\n */\nexport type Key = string\n\nexport type ThemeFSEventName = 'add' | 'change' | 'unlink'\n\ntype OnSync = (onSuccess: () => void, onError?: () => void) => void\n\ntype ThemeFSEvent =\n | {\n type: 'unlink'\n payload: {fileKey: Key; onSync: OnSync}\n }\n | {\n type: 'add' | 'change'\n payload: {\n fileKey: Key\n onContent: (fn: (content: string) => void) => void\n onSync: OnSync\n }\n }\n\nexport type ThemeFSEventPayload<T extends ThemeFSEventName = 'add'> = (ThemeFSEvent & {type: T})['payload']\n\nexport interface ThemeFileSystemOptions {\n filters?: {ignore?: string[]; only?: string[]}\n notify?: string\n noDelete?: boolean\n}\n\n/**\n * Represents a theme on the file system.\n */\nexport interface VirtualFileSystem {\n /**\n * The root path of the theme.\n */\n root: string\n\n /**\n * Local files.\n */\n files: Map<Key, ThemeAsset>\n\n /**\n * File keys that have been modified in memory and are not uploaded yet.\n */\n unsyncedFileKeys: Set<Key>\n\n /**\n * Promise that resolves when all the initial files are found.\n */\n ready: () => Promise<void>\n\n /**\n * Removes a file from the local disk and updates the file system\n *\n * @param fileKey - The key of the file to remove\n */\n delete: (fileKey: Key) => Promise<void>\n\n /**\n * Writes a file to the local disk and updates the file system\n *\n * @param asset - The ThemeAsset representing the file to write\n */\n write: (asset: ThemeAsset) => Promise<void>\n\n /**\n * Reads a file from the local disk and updates the file system\n * Returns a ThemeAsset representing the file that was read\n * Returns undefined if the file does not exist\n *\n * @param fileKey - The key of the file to read\n */\n read: (fileKey: Key) => Promise<string | Buffer | undefined>\n\n /**\n * Add callbacks to run after certain events are fired.\n */\n addEventListener: <T extends ThemeFSEventName>(eventName: T, cb: (params: ThemeFSEventPayload<T>) => void) => void\n}\n\n/**\n * Represents a theme on the file system.\n */\nexport interface ThemeFileSystem extends VirtualFileSystem {\n /**\n * Starts a file watcher for the theme directory.\n *\n * @param themeId - The ID of the theme being watched.\n * @param adminSession - The admin session for API communication.\n * @returns A Promise that resolves to an FSWatcher instance.\n */\n startWatcher: (themeId: string, adminSession: AdminSession) => Promise<void>\n\n /**\n * Applies filters to ignore files from .shopifyignore file, --ignore and --only flags.\n */\n applyIgnoreFilters: <T extends {key: string}>(files: T[]) => T[]\n\n /**\n * Stores upload errors returned when uploading files via the Asset API\n */\n uploadErrors: Map<Key, string[]>\n}\n\n/**\n * Represents a theme on the file system.\n */\nexport interface ThemeExtensionFileSystem extends VirtualFileSystem {\n /**\n * Starts a file watcher for the theme extension directory.\n */\n startWatcher: () => Promise<void>\n}\n\n/**\n * Represents a theme.\n */\nexport interface Theme {\n /**\n * The remote ID of the theme.\n */\n id: number\n\n /**\n * The remote name of the theme.\n */\n name: string\n\n /**\n * A boolean determining whether or not the theme was created at runtime.\n */\n createdAtRuntime: boolean\n\n /**\n * A boolean determining if the theme is processing at the theme library.\n */\n processing: boolean\n\n /**\n * The remote role of the theme.\n */\n role: string\n\n /**\n * A public URL where Shopify can access the theme code.\n */\n src?: string\n}\n\n/**\n * Represents the remote checksum for a file in a theme.\n */\nexport interface Checksum {\n /**\n * Identifier of the theme file.\n */\n key: Key\n\n /**\n * Represents the checksum value of the theme file.\n */\n checksum: string\n}\n\n/**\n * Represents a theme or theme extension asset.\n */\nexport interface ThemeAsset extends Checksum {\n /**\n * A base64-encoded image.\n */\n attachment?: string\n\n /**\n * The text content of the asset, such as the HTML and Liquid markup of a template file.\n */\n value?: string\n\n /**\n * File stats at time of last modification. For attachments, this is the size of the base64 string.\n */\n stats?: {mtime: number; size: number}\n}\n\n/**\n * Represents a single result for an upload or delete operation on a single file\n * Each result includes the unique identifier for the file, the type of the operation,\n * the success status of the operation, any errors that occurred, and the asset value of the file.\n */\nexport interface Result {\n /**\n * The unique identifier for the file being uploaded.\n */\n key: string\n\n /**\n * The operation associated with the result.\n */\n operation: Operation\n\n /**\n * Indicates whether the upload operation for this file was successful.\n */\n success: boolean\n\n /**\n * Error message that was generated during the upload operation for this file.\n */\n errors?: {asset?: string[]}\n\n /**\n * The asset that was uploaded as part of the upload operation for this file.\n */\n asset?: Omit<ThemeAsset, 'stats'>\n}\n\nexport enum Operation {\n Delete = 'DELETE',\n Upload = 'UPLOAD',\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import { HostOptions } from './types.js';
2
+ export declare function createServer(projectName: string): {
3
+ host: (options?: HostOptions) => string;
4
+ url: (options?: HostOptions) => string;
5
+ };
6
+ declare function assertRunning2016(projectName: string): void;
7
+ declare let assertRunningOverride: typeof assertRunning2016 | undefined;
8
+ export declare function setAssertRunning(override: typeof assertRunningOverride): void;
9
+ export {};
@@ -0,0 +1,38 @@
1
+ import fs from 'fs';
2
+ import * as os from 'node:os';
3
+ import { assertConnectable } from './network/index.js';
4
+ import { assertCompatibleEnvironment } from './env.js';
5
+ export function createServer(projectName) {
6
+ return {
7
+ host: (options = {}) => host(projectName, options),
8
+ url: (options = {}) => url(projectName, options),
9
+ };
10
+ }
11
+ function host(projectName, options = {}) {
12
+ assertCompatibleEnvironment();
13
+ (assertRunningOverride || assertRunning2016)(projectName);
14
+ const prefix = options.nonstandardHostPrefix || projectName;
15
+ return `${prefix}.myshopify.io`;
16
+ }
17
+ function url(projectName, options = {}) {
18
+ return `https://${host(projectName, options)}`;
19
+ }
20
+ function assertRunning2016(projectName) {
21
+ const [addr, port] = getAddrPort(projectName);
22
+ assertConnectable({ projectName, addr, port });
23
+ }
24
+ function getAddrPort(name) {
25
+ try {
26
+ const portContent = fs.readFileSync(`${os.homedir()}/.local/run/services/${name}/server/port`, 'utf-8');
27
+ return ['localhost', parseInt(portContent, 10)];
28
+ }
29
+ catch (error) {
30
+ throw new Error(`DevServer for '${name}' is not running: \`dev up ${name}\` to start it.`);
31
+ }
32
+ }
33
+ // Allow overrides for more concise test setup. Meh.
34
+ let assertRunningOverride;
35
+ export function setAssertRunning(override) {
36
+ assertRunningOverride = override;
37
+ }
38
+ //# sourceMappingURL=dev-server-2016.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-server-2016.js","sourceRoot":"","sources":["../../../../../src/public/node/vendor/dev_server/dev-server-2016.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAA;AACnB,OAAO,KAAK,EAAE,MAAM,SAAS,CAAA;AAE7B,OAAO,EAAC,iBAAiB,EAAC,MAAM,oBAAoB,CAAA;AACpD,OAAO,EAAC,2BAA2B,EAAC,MAAM,UAAU,CAAA;AAGpD,MAAM,UAAU,YAAY,CAAC,WAAmB;IAC9C,OAAO;QACL,IAAI,EAAE,CAAC,UAAuB,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC;QAC/D,GAAG,EAAE,CAAC,UAAuB,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC;KAC9D,CAAA;AACH,CAAC;AAED,SAAS,IAAI,CAAC,WAAmB,EAAE,UAAuB,EAAE;IAC1D,2BAA2B,EAAE,CAC5B;IAAA,CAAC,qBAAqB,IAAI,iBAAiB,CAAC,CAAC,WAAW,CAAC,CAAA;IAE1D,MAAM,MAAM,GAAG,OAAO,CAAC,qBAAqB,IAAI,WAAW,CAAA;IAE3D,OAAO,GAAG,MAAM,eAAe,CAAA;AACjC,CAAC;AAED,SAAS,GAAG,CAAC,WAAmB,EAAE,UAAuB,EAAE;IACzD,OAAO,WAAW,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,EAAE,CAAA;AAChD,CAAC;AAED,SAAS,iBAAiB,CAAC,WAAmB;IAC5C,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,WAAW,CAAC,WAAW,CAAC,CAAA;IAC7C,iBAAiB,CAAC,EAAC,WAAW,EAAE,IAAI,EAAE,IAAI,EAAC,CAAC,CAAA;AAC9C,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,wBAAwB,IAAI,cAAc,EAAE,OAAO,CAAC,CAAA;QACvG,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAA;IACjD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,8BAA8B,IAAI,iBAAiB,CAAC,CAAA;IAC5F,CAAC;AACH,CAAC;AAED,oDAAoD;AACpD,IAAI,qBAA2D,CAAA;AAE/D,MAAM,UAAU,gBAAgB,CAAC,QAAsC;IACrE,qBAAqB,GAAG,QAAQ,CAAA;AAClC,CAAC","sourcesContent":["import fs from 'fs'\nimport * as os from 'node:os'\n\nimport {assertConnectable} from './network/index.js'\nimport {assertCompatibleEnvironment} from './env.js'\nimport {HostOptions} from './types.js'\n\nexport function createServer(projectName: string) {\n return {\n host: (options: HostOptions = {}) => host(projectName, options),\n url: (options: HostOptions = {}) => url(projectName, options),\n }\n}\n\nfunction host(projectName: string, options: HostOptions = {}): string {\n assertCompatibleEnvironment()\n ;(assertRunningOverride || assertRunning2016)(projectName)\n\n const prefix = options.nonstandardHostPrefix || projectName\n\n return `${prefix}.myshopify.io`\n}\n\nfunction url(projectName: string, options: HostOptions = {}): string {\n return `https://${host(projectName, options)}`\n}\n\nfunction assertRunning2016(projectName: string): void {\n const [addr, port] = getAddrPort(projectName)\n assertConnectable({projectName, addr, port})\n}\n\nfunction getAddrPort(name: string): [string, number] {\n try {\n const portContent = fs.readFileSync(`${os.homedir()}/.local/run/services/${name}/server/port`, 'utf-8')\n return ['localhost', parseInt(portContent, 10)]\n } catch (error) {\n throw new Error(`DevServer for '${name}' is not running: \\`dev up ${name}\\` to start it.`)\n }\n}\n\n// Allow overrides for more concise test setup. Meh.\nlet assertRunningOverride: typeof assertRunning2016 | undefined\n\nexport function setAssertRunning(override: typeof assertRunningOverride) {\n assertRunningOverride = override\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import type { HostOptions } from './types.js';
2
+ export declare function createServer(projectName: string): {
3
+ host: (options?: HostOptions) => string;
4
+ url: (options?: HostOptions) => string;
5
+ };
6
+ declare function assertRunning2024(projectName: string): void;
7
+ declare let assertRunningOverride: typeof assertRunning2024 | undefined;
8
+ export declare function setAssertRunning(override: typeof assertRunningOverride): void;
9
+ export {};
@@ -0,0 +1,68 @@
1
+ import fs from 'node:fs';
2
+ import * as ni from 'network-interfaces';
3
+ import { assertConnectable, getIpFromHosts } from './network/index.js';
4
+ import { assertCompatibleEnvironment } from './env.js';
5
+ const NON_SHOP_PREFIXES = ['app', 'dev', 'shopify'];
6
+ const BACKEND_PORT = 8080;
7
+ export function createServer(projectName) {
8
+ return {
9
+ host: (options = {}) => host(projectName, options),
10
+ url: (options = {}) => url(projectName, options),
11
+ };
12
+ }
13
+ function host(projectName, options = {}) {
14
+ assertCompatibleEnvironment();
15
+ (assertRunningOverride || assertRunning2024)(projectName);
16
+ const prefix = (options.nonstandardHostPrefix || projectName).replace(/_/g, '-');
17
+ if (projectName === 'shopify') {
18
+ if (prefix.endsWith('-dev-api')) {
19
+ const shopName = prefix.replace('-dev-api', '');
20
+ return `${shopName}.dev-api.shop.dev`;
21
+ }
22
+ if (!NON_SHOP_PREFIXES.includes(prefix)) {
23
+ return `${prefix}.my.shop.dev`;
24
+ }
25
+ }
26
+ return `${prefix}.shop.dev`;
27
+ }
28
+ function url(projectName, options = {}) {
29
+ return `https://${host(projectName, options)}`;
30
+ }
31
+ function assertRunning2024(projectName) {
32
+ assertConnectable({
33
+ projectName,
34
+ addr: getBackendIp(projectName),
35
+ port: BACKEND_PORT,
36
+ });
37
+ }
38
+ function getBackendIp(projectName) {
39
+ try {
40
+ const backendIp = resolveBackendHost(projectName);
41
+ ni.fromIp(backendIp, { internal: true, ipVersion: 4 });
42
+ return backendIp;
43
+ }
44
+ catch (error) {
45
+ throw new Error(`DevServer for '${projectName}' is not running: \`dev up ${projectName}\` to start it.`);
46
+ }
47
+ }
48
+ function resolveBackendHost(name) {
49
+ let host;
50
+ try {
51
+ host = fs.readlinkSync(`/opt/nginx/etc/manifest/${name}/current`);
52
+ }
53
+ catch (error) {
54
+ host = `${name}.root.shopify.dev.internal`;
55
+ }
56
+ try {
57
+ return getIpFromHosts(host);
58
+ }
59
+ catch {
60
+ return host;
61
+ }
62
+ }
63
+ // Allow overrides for more concise test setup. Meh.
64
+ let assertRunningOverride;
65
+ export function setAssertRunning(override) {
66
+ assertRunningOverride = override;
67
+ }
68
+ //# sourceMappingURL=dev-server-2024.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-server-2024.js","sourceRoot":"","sources":["../../../../../src/public/node/vendor/dev_server/dev-server-2024.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'node:fs'\n\nimport * as ni from 'network-interfaces'\n\nimport type {HostOptions} from './types.js'\nimport {assertConnectable, getIpFromHosts} from './network/index.js'\nimport {assertCompatibleEnvironment} from './env.js'\n\nconst NON_SHOP_PREFIXES = ['app', 'dev', 'shopify']\nconst BACKEND_PORT = 8080\n\nexport function createServer(projectName: string) {\n return {\n host: (options: HostOptions = {}) => host(projectName, options),\n url: (options: HostOptions = {}) => url(projectName, options),\n }\n}\n\nfunction host(projectName: string, options: HostOptions = {}): string {\n assertCompatibleEnvironment()\n ;(assertRunningOverride || assertRunning2024)(projectName)\n\n const prefix = (options.nonstandardHostPrefix || projectName).replace(/_/g, '-')\n\n if (projectName === 'shopify') {\n if (prefix.endsWith('-dev-api')) {\n const shopName = prefix.replace('-dev-api', '')\n return `${shopName}.dev-api.shop.dev`\n }\n if (!NON_SHOP_PREFIXES.includes(prefix)) {\n return `${prefix}.my.shop.dev`\n }\n }\n return `${prefix}.shop.dev`\n}\n\nfunction url(projectName: string, options: HostOptions = {}): string {\n return `https://${host(projectName, options)}`\n}\n\nfunction assertRunning2024(projectName: string): void {\n assertConnectable({\n projectName,\n addr: getBackendIp(projectName),\n port: BACKEND_PORT,\n })\n}\n\nfunction getBackendIp(projectName: string): string {\n try {\n const backendIp = resolveBackendHost(projectName)\n ni.fromIp(backendIp, {internal: true, ipVersion: 4})\n\n return backendIp\n } catch (error) {\n throw new Error(`DevServer for '${projectName}' is not running: \\`dev up ${projectName}\\` to start it.`)\n }\n}\n\nfunction resolveBackendHost(name: string): string {\n let host: string\n try {\n host = fs.readlinkSync(`/opt/nginx/etc/manifest/${name}/current`)\n } catch (error) {\n host = `${name}.root.shopify.dev.internal`\n }\n\n try {\n return getIpFromHosts(host)\n } catch {\n return host\n }\n}\n\n// Allow overrides for more concise test setup. Meh.\nlet assertRunningOverride: typeof assertRunning2024 | undefined\n\nexport function setAssertRunning(override: typeof assertRunningOverride) {\n assertRunningOverride = override\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import type { HostOptions } from './types.js';
2
+ export declare function createServer(projectName: string): {
3
+ host: (options?: HostOptions) => string;
4
+ url: (options?: HostOptions) => string;
5
+ };
@@ -0,0 +1,28 @@
1
+ import fs from 'node:fs';
2
+ import { assertCompatibleEnvironment } from './env.js';
3
+ export function createServer(projectName) {
4
+ return {
5
+ host: (options = {}) => host(projectName, options),
6
+ url: (options = {}) => url(projectName, options),
7
+ };
8
+ }
9
+ function host(projectName, { nonstandardHostPrefix } = {}) {
10
+ assertCompatibleEnvironment();
11
+ const services = fs.readdirSync('/run/ports2').filter((file) => file.endsWith(`--${projectName}`));
12
+ if (services.length === 0) {
13
+ throw new Error(`DevServer for '${projectName}' not present in this spin environment`);
14
+ }
15
+ // Spin mostly doesn't do alternative hostname prefixing for core.
16
+ if (projectName === 'shopify') {
17
+ const prefix = nonstandardHostPrefix?.replace(/[-_]dev[-_]api$/, '');
18
+ return `${prefix}.${projectName}.${process.env.SPIN_FQDN}`;
19
+ }
20
+ const match = new RegExp(`^(.+)${projectName}$`).exec(services[0]);
21
+ const organization = match ? match[1] : '';
22
+ const spinPrefix = organization === 'shopify--' ? '' : `${organization}`;
23
+ return `${spinPrefix}${projectName}.${process.env.SPIN_FQDN}`;
24
+ }
25
+ function url(projectName, options = {}) {
26
+ return `https://${host(projectName, options)}`;
27
+ }
28
+ //# sourceMappingURL=dev-server-spin.js.map