@shopify/cli-kit 4.9.5 → 5.0.1

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 (127) hide show
  1. package/dist/cli/api/graphql/admin/generated/metafield_definitions_by_owner_type.d.ts +5 -0
  2. package/dist/cli/api/graphql/admin/generated/metafield_definitions_by_owner_type.js +22 -0
  3. package/dist/cli/api/graphql/admin/generated/metafield_definitions_by_owner_type.js.map +1 -1
  4. package/dist/private/node/api/graphql.d.ts +2 -1
  5. package/dist/private/node/api/graphql.js +12 -1
  6. package/dist/private/node/api/graphql.js.map +1 -1
  7. package/dist/private/node/api.d.ts +2 -0
  8. package/dist/private/node/api.js +49 -22
  9. package/dist/private/node/api.js.map +1 -1
  10. package/dist/private/node/command-json-schema.js +1 -0
  11. package/dist/private/node/command-json-schema.js.map +1 -1
  12. package/dist/private/node/constants.d.ts +1 -0
  13. package/dist/private/node/constants.js +1 -0
  14. package/dist/private/node/constants.js.map +1 -1
  15. package/dist/private/node/dev-instance-output.d.ts +47 -0
  16. package/dist/private/node/dev-instance-output.js +143 -0
  17. package/dist/private/node/dev-instance-output.js.map +1 -0
  18. package/dist/private/node/json-error.d.ts +1 -15
  19. package/dist/private/node/json-error.js +4 -98
  20. package/dist/private/node/json-error.js.map +1 -1
  21. package/dist/private/node/output-context.d.ts +8 -0
  22. package/dist/private/node/output-context.js +10 -0
  23. package/dist/private/node/output-context.js.map +1 -0
  24. package/dist/private/node/output.test.js +4 -4
  25. package/dist/private/node/output.test.js.map +1 -1
  26. package/dist/private/node/session/automation-token.d.ts +50 -0
  27. package/dist/private/node/session/automation-token.js +77 -0
  28. package/dist/private/node/session/automation-token.js.map +1 -0
  29. package/dist/private/node/session/exchange.d.ts +5 -1
  30. package/dist/private/node/session/exchange.js +97 -7
  31. package/dist/private/node/session/exchange.js.map +1 -1
  32. package/dist/private/node/session/permanent-store-domain.d.ts +21 -0
  33. package/dist/private/node/session/permanent-store-domain.js +72 -0
  34. package/dist/private/node/session/permanent-store-domain.js.map +1 -0
  35. package/dist/private/node/session.d.ts +3 -2
  36. package/dist/private/node/session.js +12 -3
  37. package/dist/private/node/session.js.map +1 -1
  38. package/dist/private/node/themes/trust-challenge.d.ts +31 -0
  39. package/dist/private/node/themes/trust-challenge.js +143 -0
  40. package/dist/private/node/themes/trust-challenge.js.map +1 -0
  41. package/dist/private/node/ui/alert.js +1 -1
  42. package/dist/private/node/ui/alert.js.map +1 -1
  43. package/dist/private/node/ui/components/ConcurrentOutput.d.ts +2 -7
  44. package/dist/private/node/ui/components/ConcurrentOutput.js +4 -8
  45. package/dist/private/node/ui/components/ConcurrentOutput.js.map +1 -1
  46. package/dist/private/node/ui/components/LoadingBar.js +9 -15
  47. package/dist/private/node/ui/components/LoadingBar.js.map +1 -1
  48. package/dist/private/node/ui/components/LoadingBar.test.js +45 -107
  49. package/dist/private/node/ui/components/LoadingBar.test.js.map +1 -1
  50. package/dist/private/node/ui/components/LoadingIndicator.d.ts +7 -0
  51. package/dist/private/node/ui/components/LoadingIndicator.js +22 -0
  52. package/dist/private/node/ui/components/LoadingIndicator.js.map +1 -0
  53. package/dist/private/node/ui/components/Table/Table.d.ts +8 -0
  54. package/dist/private/node/ui/components/Table/Table.js +18 -2
  55. package/dist/private/node/ui/components/Table/Table.js.map +1 -1
  56. package/dist/public/common/version.d.ts +1 -1
  57. package/dist/public/common/version.js +1 -1
  58. package/dist/public/common/version.js.map +1 -1
  59. package/dist/public/node/api/admin.js +13 -0
  60. package/dist/public/node/api/admin.js.map +1 -1
  61. package/dist/public/node/api/graphql.js +38 -7
  62. package/dist/public/node/api/graphql.js.map +1 -1
  63. package/dist/public/node/archiver.js +13 -6
  64. package/dist/public/node/archiver.js.map +1 -1
  65. package/dist/public/node/base-command.d.ts +6 -0
  66. package/dist/public/node/base-command.js +97 -16
  67. package/dist/public/node/base-command.js.map +1 -1
  68. package/dist/public/node/dev-instance.d.ts +138 -0
  69. package/dist/public/node/dev-instance.js +365 -0
  70. package/dist/public/node/dev-instance.js.map +1 -0
  71. package/dist/public/node/environment.d.ts +6 -3
  72. package/dist/public/node/environment.js +11 -4
  73. package/dist/public/node/environment.js.map +1 -1
  74. package/dist/public/node/error/index.js +2 -1
  75. package/dist/public/node/error/index.js.map +1 -1
  76. package/dist/public/node/error/schema.d.ts +24 -24
  77. package/dist/public/node/error/serialization.d.ts +8 -0
  78. package/dist/public/node/error/serialization.js +103 -0
  79. package/dist/public/node/error/serialization.js.map +1 -0
  80. package/dist/public/node/graphiql/server.js +18 -2
  81. package/dist/public/node/graphiql/server.js.map +1 -1
  82. package/dist/public/node/is-global.js +2 -1
  83. package/dist/public/node/is-global.js.map +1 -1
  84. package/dist/public/node/notifications-system.d.ts +6 -6
  85. package/dist/public/node/path.d.ts +19 -3
  86. package/dist/public/node/path.js +21 -3
  87. package/dist/public/node/path.js.map +1 -1
  88. package/dist/public/node/session.d.ts +38 -3
  89. package/dist/public/node/session.js +56 -9
  90. package/dist/public/node/session.js.map +1 -1
  91. package/dist/public/node/system.d.ts +4 -0
  92. package/dist/public/node/system.js +39 -9
  93. package/dist/public/node/system.js.map +1 -1
  94. package/dist/public/node/themes/api.d.ts +9 -0
  95. package/dist/public/node/themes/api.js +50 -22
  96. package/dist/public/node/themes/api.js.map +1 -1
  97. package/dist/public/node/themes/theme-manager.d.ts +8 -1
  98. package/dist/public/node/themes/theme-manager.js +1 -1
  99. package/dist/public/node/themes/theme-manager.js.map +1 -1
  100. package/dist/public/node/tree-kill.js +12 -2
  101. package/dist/public/node/tree-kill.js.map +1 -1
  102. package/dist/public/node/ui/components.d.ts +1 -0
  103. package/dist/public/node/ui/components.js +1 -0
  104. package/dist/public/node/ui/components.js.map +1 -1
  105. package/dist/public/node/ui.d.ts +24 -4
  106. package/dist/public/node/ui.js +54 -21
  107. package/dist/public/node/ui.js.map +1 -1
  108. package/dist/public/node/upgrade/output.d.ts +17 -0
  109. package/dist/public/node/upgrade/output.js +51 -0
  110. package/dist/public/node/upgrade/output.js.map +1 -0
  111. package/dist/public/node/upgrade/result.d.ts +8 -0
  112. package/dist/public/node/upgrade/result.js +21 -0
  113. package/dist/public/node/upgrade/result.js.map +1 -0
  114. package/dist/public/node/upgrade/types.d.ts +61 -0
  115. package/dist/public/node/upgrade/types.js +46 -0
  116. package/dist/public/node/upgrade/types.js.map +1 -0
  117. package/dist/public/node/upgrade.d.ts +8 -0
  118. package/dist/public/node/upgrade.js +57 -22
  119. package/dist/public/node/upgrade.js.map +1 -1
  120. package/dist/tsconfig.tsbuildinfo +1 -1
  121. package/package.json +3 -3
  122. package/dist/private/node/ui/components/TextAnimation.d.ts +0 -10
  123. package/dist/private/node/ui/components/TextAnimation.js +0 -56
  124. package/dist/private/node/ui/components/TextAnimation.js.map +0 -1
  125. package/dist/private/node/ui/components/TextAnimation.test.d.ts +0 -1
  126. package/dist/private/node/ui/components/TextAnimation.test.js +0 -73
  127. package/dist/private/node/ui/components/TextAnimation.test.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"is-global.js","sourceRoot":"","sources":["../../../src/public/node/is-global.ts"],"names":[],"mappings":"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","sourcesContent":["import {cwd, dirname, isSubpath, joinPath, sniffForPath} from './path.js'\nimport {isUnitTest} from './context/local.js'\nimport {findPathUpSync, globSync} from './fs.js'\nimport {realpathSync} from 'fs'\nimport type {PackageManager} from './node-package-manager.js'\n\nlet _isGlobal: boolean | undefined\n\n/**\n * Returns true if the current process is running in a global context.\n *\n * @param argv - The arguments passed to the process.\n * @returns `true` if the current process is running in a global context.\n */\nexport function currentProcessIsGlobal(argv = process.argv): boolean {\n // If we are running tests, we need to disable the cache\n try {\n if (_isGlobal !== undefined && !isUnitTest()) return _isGlobal\n\n // Path where the current project is (app/hydrogen)\n const path = sniffForPath() ?? cwd()\n\n const projectDir = getProjectDir(path)\n if (!projectDir) {\n return true\n }\n\n // From node docs: \"The second element [of the array] will be the path to the JavaScript file being executed\"\n const binDir = argv[1] ?? ''\n if (!binDir) {\n return true\n }\n\n // If binDir lives inside projectDir, we are running a local CLI.\n // Use isSubpath (pathe.relative under the hood) instead of a raw\n // string startsWith: projectDir flows through normalizePath and is\n // forward-slash on every platform, while argv[1] is OS-native, so on\n // Windows it arrives backslash-separated and a naive startsWith would\n // misclassify a local install as global.\n const isLocal = isSubpath(projectDir.trim(), binDir)\n\n _isGlobal = !isLocal\n return _isGlobal\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n return false\n }\n}\n\n/**\n * Installs the global Shopify CLI, using the provided package manager.\n *\n * @param packageManager - The package manager to use.\n */\nexport async function installGlobalShopifyCLI(packageManager: PackageManager): Promise<void> {\n const {outputInfo} = await import('./output.js')\n const {exec} = await import('./system.js')\n const args =\n packageManager === 'yarn' ? ['global', 'add', '@shopify/cli@latest'] : ['install', '-g', '@shopify/cli@latest']\n outputInfo(`Running ${packageManager} ${args.join(' ')}...`)\n await exec(packageManager, args, {stdio: 'inherit'})\n}\n\nexport interface InstallGlobalCLIPromptResult {\n install: boolean\n alreadyInstalled: boolean\n}\n/**\n * Prompts the user to install the global CLI.\n *\n * @returns `true` if the user has installed the global CLI.\n */\nexport async function installGlobalCLIPrompt(): Promise<InstallGlobalCLIPromptResult> {\n const {terminalSupportsPrompting} = await import('./system.js')\n if (!terminalSupportsPrompting()) return {install: false, alreadyInstalled: false}\n const {globalCLIVersion} = await import('./version.js')\n if (await globalCLIVersion()) {\n return {install: false, alreadyInstalled: true}\n }\n const {renderSelectPrompt} = await import('./ui.js')\n const result = await renderSelectPrompt({\n message: 'We recommend installing Shopify CLI globally in your system. Would you like to install it now?',\n choices: [\n {value: 'yes', label: 'Yes'},\n {value: 'no', label: 'No, just for this project'},\n ],\n })\n\n return {install: result === 'yes', alreadyInstalled: false}\n}\n\n/**\n * Infers the package manager used by the global CLI.\n *\n * @param argv - The arguments passed to the process.\n * @param env - The environment variables of the process.\n * @returns The package manager used by the global CLI.\n */\nexport function inferPackageManagerForGlobalCLI(argv = process.argv, env = process.env): PackageManager {\n if (!currentProcessIsGlobal(argv)) return 'unknown'\n\n // Check for Homebrew first (most reliable via environment variable)\n if (env.SHOPIFY_HOMEBREW_FORMULA) {\n return 'homebrew'\n }\n\n const processArgv = argv[1] ?? ''\n const symlinkPath = processArgv.toLowerCase()\n\n // Resolve symlinks to get the real path of the binary.\n let realPath = symlinkPath\n try {\n realPath = realpathSync(processArgv).toLowerCase()\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n // fall back to using the original path for detection\n }\n\n // Inspect both the (unresolved) symlink path and the resolved real path. Some\n // package managers — notably bun (`~/.bun/bin/<name>`) — install global binaries\n // as symlinks pointing into a generic `node_modules` directory whose real path\n // no longer contains the package manager name. The original symlink under the\n // PM's bin dir is the most reliable signal in that case.\n const matches = (needle: string) => realPath.includes(needle) || symlinkPath.includes(needle)\n\n if (matches('yarn')) return 'yarn'\n if (matches('pnpm')) return 'pnpm'\n if (matches('bun')) return 'bun'\n\n // Check for Homebrew via Cellar path (resolved symlink)\n if (realPath.includes('/cellar/')) return 'homebrew'\n\n return 'npm'\n}\n\n/**\n * Returns the project directory for the given path.\n *\n * @param directory - The path to search upward from.\n * @returns The project root directory, or undefined if not found.\n */\nexport function getProjectDir(directory: string): string | undefined {\n const configFiles = ['shopify.app{,.*}.toml', 'hydrogen.config.js', 'hydrogen.config.ts']\n const existsConfigFile = (directory: string) => {\n const configPaths = globSync(configFiles.map((file) => joinPath(directory, file)))\n return configPaths.length > 0 ? configPaths[0] : undefined\n }\n try {\n const configFile = findPathUpSync(existsConfigFile, {\n cwd: directory,\n type: 'file',\n })\n if (configFile) return dirname(configFile)\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n return undefined\n }\n}\n"]}
1
+ {"version":3,"file":"is-global.js","sourceRoot":"","sources":["../../../src/public/node/is-global.ts"],"names":[],"mappings":"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","sourcesContent":["import {cwd, dirname, isSubpath, joinPath, sniffForPath} from './path.js'\nimport {isUnitTest} from './context/local.js'\nimport {findPathUpSync, globSync} from './fs.js'\nimport {realpathSync} from 'fs'\nimport type {PackageManager} from './node-package-manager.js'\n\nlet _isGlobal: boolean | undefined\n\n/**\n * Returns true if the current process is running in a global context.\n *\n * @param argv - The arguments passed to the process.\n * @returns `true` if the current process is running in a global context.\n */\nexport function currentProcessIsGlobal(argv = process.argv): boolean {\n // If we are running tests, we need to disable the cache\n try {\n if (_isGlobal !== undefined && !isUnitTest()) return _isGlobal\n\n // Path where the current project is (app/hydrogen)\n const path = sniffForPath() ?? cwd()\n\n const projectDir = getProjectDir(path)\n if (!projectDir) {\n return true\n }\n\n // From node docs: \"The second element [of the array] will be the path to the JavaScript file being executed\"\n const binDir = argv[1] ?? ''\n if (!binDir) {\n return true\n }\n\n // If binDir lives inside projectDir, we are running a local CLI.\n // Use isSubpath (pathe.relative under the hood) instead of a raw\n // string startsWith: projectDir flows through normalizePath and is\n // forward-slash on every platform, while argv[1] is OS-native, so on\n // Windows it arrives backslash-separated and a naive startsWith would\n // misclassify a local install as global.\n const isLocal = isSubpath(projectDir.trim(), binDir)\n\n _isGlobal = !isLocal\n return _isGlobal\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n return false\n }\n}\n\n/**\n * Installs the global Shopify CLI, using the provided package manager.\n *\n * @param packageManager - The package manager to use.\n */\nexport async function installGlobalShopifyCLI(packageManager: PackageManager): Promise<void> {\n const {outputInfo} = await import('./output.js')\n const {exec} = await import('./system.js')\n const {jsonOutputEnabled} = await import('./environment.js')\n const args =\n packageManager === 'yarn' ? ['global', 'add', '@shopify/cli@latest'] : ['install', '-g', '@shopify/cli@latest']\n outputInfo(`Running ${packageManager} ${args.join(' ')}...`)\n await exec(packageManager, args, jsonOutputEnabled() ? {} : {stdio: 'inherit'})\n}\n\nexport interface InstallGlobalCLIPromptResult {\n install: boolean\n alreadyInstalled: boolean\n}\n/**\n * Prompts the user to install the global CLI.\n *\n * @returns `true` if the user has installed the global CLI.\n */\nexport async function installGlobalCLIPrompt(): Promise<InstallGlobalCLIPromptResult> {\n const {terminalSupportsPrompting} = await import('./system.js')\n if (!terminalSupportsPrompting()) return {install: false, alreadyInstalled: false}\n const {globalCLIVersion} = await import('./version.js')\n if (await globalCLIVersion()) {\n return {install: false, alreadyInstalled: true}\n }\n const {renderSelectPrompt} = await import('./ui.js')\n const result = await renderSelectPrompt({\n message: 'We recommend installing Shopify CLI globally in your system. Would you like to install it now?',\n choices: [\n {value: 'yes', label: 'Yes'},\n {value: 'no', label: 'No, just for this project'},\n ],\n })\n\n return {install: result === 'yes', alreadyInstalled: false}\n}\n\n/**\n * Infers the package manager used by the global CLI.\n *\n * @param argv - The arguments passed to the process.\n * @param env - The environment variables of the process.\n * @returns The package manager used by the global CLI.\n */\nexport function inferPackageManagerForGlobalCLI(argv = process.argv, env = process.env): PackageManager {\n if (!currentProcessIsGlobal(argv)) return 'unknown'\n\n // Check for Homebrew first (most reliable via environment variable)\n if (env.SHOPIFY_HOMEBREW_FORMULA) {\n return 'homebrew'\n }\n\n const processArgv = argv[1] ?? ''\n const symlinkPath = processArgv.toLowerCase()\n\n // Resolve symlinks to get the real path of the binary.\n let realPath = symlinkPath\n try {\n realPath = realpathSync(processArgv).toLowerCase()\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n // fall back to using the original path for detection\n }\n\n // Inspect both the (unresolved) symlink path and the resolved real path. Some\n // package managers — notably bun (`~/.bun/bin/<name>`) — install global binaries\n // as symlinks pointing into a generic `node_modules` directory whose real path\n // no longer contains the package manager name. The original symlink under the\n // PM's bin dir is the most reliable signal in that case.\n const matches = (needle: string) => realPath.includes(needle) || symlinkPath.includes(needle)\n\n if (matches('yarn')) return 'yarn'\n if (matches('pnpm')) return 'pnpm'\n if (matches('bun')) return 'bun'\n\n // Check for Homebrew via Cellar path (resolved symlink)\n if (realPath.includes('/cellar/')) return 'homebrew'\n\n return 'npm'\n}\n\n/**\n * Returns the project directory for the given path.\n *\n * @param directory - The path to search upward from.\n * @returns The project root directory, or undefined if not found.\n */\nexport function getProjectDir(directory: string): string | undefined {\n const configFiles = ['shopify.app{,.*}.toml', 'hydrogen.config.js', 'hydrogen.config.ts']\n const existsConfigFile = (directory: string) => {\n const configPaths = globSync(configFiles.map((file) => joinPath(directory, file)))\n return configPaths.length > 0 ? configPaths[0] : undefined\n }\n try {\n const configFile = findPathUpSync(existsConfigFile, {\n cwd: directory,\n type: 'file',\n })\n if (configFile) return dirname(configFile)\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n return undefined\n }\n}\n"]}
@@ -26,7 +26,7 @@ declare const NotificationSchema: zod.ZodObject<{
26
26
  id: string;
27
27
  type: "info" | "error" | "warning";
28
28
  message: string;
29
- frequency: "always" | "once" | "once_a_day" | "once_a_week";
29
+ frequency: "once" | "always" | "once_a_day" | "once_a_week";
30
30
  ownerChannel: string;
31
31
  title?: string | undefined;
32
32
  cta?: {
@@ -43,7 +43,7 @@ declare const NotificationSchema: zod.ZodObject<{
43
43
  id: string;
44
44
  type: "info" | "error" | "warning";
45
45
  message: string;
46
- frequency: "always" | "once" | "once_a_day" | "once_a_week";
46
+ frequency: "once" | "always" | "once_a_day" | "once_a_week";
47
47
  ownerChannel: string;
48
48
  title?: string | undefined;
49
49
  cta?: {
@@ -86,7 +86,7 @@ declare const NotificationsSchema: zod.ZodObject<{
86
86
  id: string;
87
87
  type: "info" | "error" | "warning";
88
88
  message: string;
89
- frequency: "always" | "once" | "once_a_day" | "once_a_week";
89
+ frequency: "once" | "always" | "once_a_day" | "once_a_week";
90
90
  ownerChannel: string;
91
91
  title?: string | undefined;
92
92
  cta?: {
@@ -103,7 +103,7 @@ declare const NotificationsSchema: zod.ZodObject<{
103
103
  id: string;
104
104
  type: "info" | "error" | "warning";
105
105
  message: string;
106
- frequency: "always" | "once" | "once_a_day" | "once_a_week";
106
+ frequency: "once" | "always" | "once_a_day" | "once_a_week";
107
107
  ownerChannel: string;
108
108
  title?: string | undefined;
109
109
  cta?: {
@@ -122,7 +122,7 @@ declare const NotificationsSchema: zod.ZodObject<{
122
122
  id: string;
123
123
  type: "info" | "error" | "warning";
124
124
  message: string;
125
- frequency: "always" | "once" | "once_a_day" | "once_a_week";
125
+ frequency: "once" | "always" | "once_a_day" | "once_a_week";
126
126
  ownerChannel: string;
127
127
  title?: string | undefined;
128
128
  cta?: {
@@ -141,7 +141,7 @@ declare const NotificationsSchema: zod.ZodObject<{
141
141
  id: string;
142
142
  type: "info" | "error" | "warning";
143
143
  message: string;
144
- frequency: "always" | "once" | "once_a_day" | "once_a_week";
144
+ frequency: "once" | "always" | "once_a_day" | "once_a_week";
145
145
  ownerChannel: string;
146
146
  title?: string | undefined;
147
147
  cta?: {
@@ -1,4 +1,7 @@
1
1
  import type { URL } from 'url';
2
+ interface IsSubpathOptions {
3
+ native?: boolean;
4
+ }
2
5
  /**
3
6
  * Joins a list of paths together.
4
7
  *
@@ -90,13 +93,25 @@ export declare function commonParentDirectory(first: string, second: string): st
90
93
  */
91
94
  export declare function relativizePath(path: string, dir?: string): string;
92
95
  /**
93
- * Given 2 paths, it returns whether the second path is a subpath of the first path.
96
+ * Given 2 paths, it returns whether the second path is inside or equal to the first path.
97
+ *
98
+ * By default, paths are compared with pathe, which treats `\` as a separator
99
+ * on every platform. This behaviour is kept for backward compatibility.
100
+ *
101
+ * When `native` is true, paths are compared with the current OS's native path
102
+ * semantics, so literal backslashes on POSIX are preserved as filename
103
+ * characters. Use `native: true` for security or containment checks on
104
+ * resolved filesystem paths.
105
+ *
106
+ * Symbolic links are not resolved; use fileRealPath from the fs module first
107
+ * when checking physical containment.
94
108
  *
95
109
  * @param mainPath - The main path.
96
110
  * @param subpath - The subpath.
97
- * @returns Whether the subpath is a subpath of the main path.
111
+ * @param options - Comparison options. Set `native` to true to use the OS's native path semantics. Defaults to false.
112
+ * @returns Whether the subpath is inside or equal to the main path.
98
113
  */
99
- export declare function isSubpath(mainPath: string, subpath: string): boolean;
114
+ export declare function isSubpath(mainPath: string, subpath: string, options?: IsSubpathOptions): boolean;
100
115
  /**
101
116
  * Given a module's import.meta.url it returns the directory containing the module.
102
117
  *
@@ -139,3 +154,4 @@ export declare function sniffForJson(argv?: string[]): boolean;
139
154
  * @returns The sanitized path (may be an empty string if all segments were traversal).
140
155
  */
141
156
  export declare function sanitizeRelativePath(input: string, warn: (msg: string) => void): string;
157
+ export {};
@@ -1,5 +1,7 @@
1
1
  import { relative, dirname as patheDirname, join, normalize, resolve, basename as basenamePathe, extname as extnamePathe, parse, isAbsolute, } from 'pathe';
2
2
  import { fileURLToPath } from 'url';
3
+ // eslint-disable-next-line no-restricted-imports -- Native containment must preserve literal POSIX backslashes.
4
+ import { relative as nativeRelative, isAbsolute as nativeIsAbsolute, sep as nativeSeparator } from 'node:path';
3
5
  /**
4
6
  * Joins a list of paths together.
5
7
  *
@@ -121,13 +123,29 @@ export function relativizePath(path, dir = cwd()) {
121
123
  }
122
124
  }
123
125
  /**
124
- * Given 2 paths, it returns whether the second path is a subpath of the first path.
126
+ * Given 2 paths, it returns whether the second path is inside or equal to the first path.
127
+ *
128
+ * By default, paths are compared with pathe, which treats `\` as a separator
129
+ * on every platform. This behaviour is kept for backward compatibility.
130
+ *
131
+ * When `native` is true, paths are compared with the current OS's native path
132
+ * semantics, so literal backslashes on POSIX are preserved as filename
133
+ * characters. Use `native: true` for security or containment checks on
134
+ * resolved filesystem paths.
135
+ *
136
+ * Symbolic links are not resolved; use fileRealPath from the fs module first
137
+ * when checking physical containment.
125
138
  *
126
139
  * @param mainPath - The main path.
127
140
  * @param subpath - The subpath.
128
- * @returns Whether the subpath is a subpath of the main path.
141
+ * @param options - Comparison options. Set `native` to true to use the OS's native path semantics. Defaults to false.
142
+ * @returns Whether the subpath is inside or equal to the main path.
129
143
  */
130
- export function isSubpath(mainPath, subpath) {
144
+ export function isSubpath(mainPath, subpath, options = { native: false }) {
145
+ if (options.native) {
146
+ const relativePath = nativeRelative(mainPath, subpath);
147
+ return relativePath !== '..' && !relativePath.startsWith(`..${nativeSeparator}`) && !nativeIsAbsolute(relativePath);
148
+ }
131
149
  const relativePath = relative(mainPath, subpath);
132
150
  return !relativePath.startsWith('..') && !isAbsolutePath(relativePath);
133
151
  }
@@ -1 +1 @@
1
- {"version":3,"file":"path.js","sourceRoot":"","sources":["../../../src/public/node/path.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n relative,\n dirname as patheDirname,\n join,\n normalize,\n resolve,\n basename as basenamePathe,\n extname as extnamePathe,\n parse,\n isAbsolute,\n} from 'pathe'\nimport {fileURLToPath} from 'url'\n// eslint-disable-next-line n/prefer-global/url\nimport type {URL} from 'url'\n\n/**\n * Joins a list of paths together.\n *\n * @param paths - Paths to join.\n * @returns Joined path.\n */\nexport function joinPath(...paths: string[]): string {\n return join(...paths)\n}\n\n/**\n * Normalizes a path.\n *\n * @param path - Path to normalize.\n * @returns Normalized path.\n */\nexport function normalizePath(path: string): string {\n return normalize(path)\n}\n\n/**\n * Resolves a list of paths together.\n *\n * @param paths - Paths to resolve.\n * @returns Resolved path.\n */\nexport function resolvePath(...paths: string[]): string {\n return resolve(...paths)\n}\n\n/**\n * Returns the relative path from one path to another.\n *\n * @param from - Path to resolve from.\n * @param to - Path to resolve to.\n * @returns Relative path.\n */\nexport function relativePath(from: string, to: string): string {\n return relative(from, to)\n}\n\n/**\n * Returns whether the path is absolute.\n *\n * @param path - Path to check.\n * @returns Whether the path is absolute.\n */\nexport function isAbsolutePath(path: string): boolean {\n return isAbsolute(path)\n}\n\n/**\n * Returns the directory name of a path.\n *\n * @param path - Path to get the directory name of.\n * @returns Directory name.\n */\nexport function dirname(path: string): string {\n return patheDirname(path)\n}\n\n/**\n * Returns the base name of a path.\n *\n * @param path - Path to get the base name of.\n * @param ext - Optional extension to remove from the result.\n * @returns Base name.\n */\nexport function basename(path: string, ext?: string): string {\n return basenamePathe(path, ext)\n}\n\n/**\n * Returns the extension of the path.\n *\n * @param path - Path to get the extension of.\n * @returns Extension.\n */\nexport function extname(path: string): string {\n return extnamePathe(path)\n}\n\n/**\n * Parses a path into its components (root, dir, base, ext, name).\n *\n * @param path - Path to parse.\n * @returns Parsed path object.\n */\nexport function parsePath(path: string): {root: string; dir: string; base: string; ext: string; name: string} {\n return parse(path)\n}\n\n/**\n * Returns the longest common parent directory of two absolute paths.\n *\n * @param first - First absolute path.\n * @param second - Second absolute path.\n * @returns The common parent directory, or '/' if they share only the root.\n */\nexport function commonParentDirectory(first: string, second: string): string {\n const firstParts = first.split(/\\/+|\\\\+/)\n const secondParts = second.split(/\\/+|\\\\+/)\n let i = 0\n while (i < firstParts.length && i < secondParts.length && firstParts[i] === secondParts[i]) {\n i++\n }\n return i > 1 ? firstParts.slice(0, i).join('/') : '/'\n}\n\n/**\n * Given an absolute filesystem path, it makes it relative to\n * the current working directory. This is useful when logging paths\n * to allow the users to click on the file and let the OS open it\n * in the editor of choice.\n *\n * @param path - Path to relativize.\n * @param dir - Current working directory.\n * @returns Relativized path.\n */\nexport function relativizePath(path: string, dir: string = cwd()): string {\n const result = commonParentDirectory(path, dir)\n const relativePath = relative(dir, path)\n const relativeComponents = relativePath.split('/').filter((component) => component === '..').length\n if (result === '/' || relativePath === '' || relativeComponents > 2) {\n return path\n } else {\n return relativePath\n }\n}\n\n/**\n * Given 2 paths, it returns whether the second path is a subpath of the first path.\n *\n * @param mainPath - The main path.\n * @param subpath - The subpath.\n * @returns Whether the subpath is a subpath of the main path.\n */\nexport function isSubpath(mainPath: string, subpath: string): boolean {\n const relativePath = relative(mainPath, subpath)\n return !relativePath.startsWith('..') && !isAbsolutePath(relativePath)\n}\n\n/**\n * Given a module's import.meta.url it returns the directory containing the module.\n *\n * @param moduleURL - The value of import.meta.url in the context of the caller module.\n * @returns The path to the directory containing the caller module.\n */\nexport function moduleDirectory(moduleURL: string | URL): string {\n return dirname(fileURLToPath(moduleURL))\n}\n\n/**\n * When running a script using `npm run`, something interesting happens. If the current\n * folder does not have a `package.json` or a `node_modules` folder, npm will traverse\n * the directory tree upwards until it finds one. Then it will run the script and set\n * `process.cwd()` to that folder, while the actual path is stored in the INIT_CWD\n * environment variable (see here: https://docs.npmjs.com/cli/v9/commands/npm-run-script#description).\n *\n * @returns The path to the current working directory.\n */\nexport function cwd(): string {\n // eslint-disable-next-line @shopify/cli/no-process-cwd\n return normalize(process.env.INIT_CWD || process.cwd()) // eslint-disable-line @typescript-eslint/prefer-nullish-coalescing -- empty env var should fall through\n}\n\n/**\n * Tries to get the value of the `--path` argument, if provided.\n *\n * @param argv - The arguments to search for the `--path` argument.\n * @returns The value of the `--path` argument, if provided.\n */\nexport function sniffForPath(argv = process.argv): string | undefined {\n const pathFlagIndex = argv.indexOf('--path')\n if (pathFlagIndex === -1) {\n const pathArg = argv.find((arg) => arg.startsWith('--path='))\n return pathArg?.split('=')[1]\n }\n const pathFlag = argv[pathFlagIndex + 1]\n if (!pathFlag || pathFlag.startsWith('-')) return\n return pathFlag\n}\n\n/**\n * Returns whether the `--json` or `-j` flags are present in the arguments.\n *\n * @param argv - The arguments to search for the `--json` and `-j` flags.\n * @returns Whether the `--json` or `-j` flag is present in the arguments.\n */\nexport function sniffForJson(argv = process.argv): boolean {\n const passthroughIndex = argv.indexOf('--')\n const commandArguments = passthroughIndex === -1 ? argv : argv.slice(0, passthroughIndex)\n return commandArguments.includes('--json') || commandArguments.includes('-j')\n}\n\n/**\n * Removes any `..` traversal segments from a relative path and calls `warn`\n * if any were stripped. Normal `..` that cancel out within the path (e.g.\n * `foo/../bar` → `bar`) are collapsed but never allowed to escape the root.\n * Both `/` and `\\` are treated as separators for cross-platform safety.\n *\n * @param input - The relative path to sanitize.\n * @param warn - Called with a human-readable warning when traversal segments are removed.\n * @returns The sanitized path (may be an empty string if all segments were traversal).\n */\nexport function sanitizeRelativePath(input: string, warn: (msg: string) => void): string {\n const segments = input.replace(/\\\\/g, '/').split('/')\n const stack: string[] = []\n let stripped = false\n for (const seg of segments) {\n if (seg === '..') {\n stripped = true\n stack.pop()\n } else if (seg !== '.') {\n stack.push(seg)\n }\n }\n const result = stack.join('/')\n if (stripped) {\n warn(`Warning: path '${input}' contains '..' traversal — sanitized to '${result || '.'}'\\n`)\n }\n return result\n}\n"]}
1
+ {"version":3,"file":"path.js","sourceRoot":"","sources":["../../../src/public/node/path.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n relative,\n dirname as patheDirname,\n join,\n normalize,\n resolve,\n basename as basenamePathe,\n extname as extnamePathe,\n parse,\n isAbsolute,\n} from 'pathe'\nimport {fileURLToPath} from 'url'\n// eslint-disable-next-line no-restricted-imports -- Native containment must preserve literal POSIX backslashes.\nimport {relative as nativeRelative, isAbsolute as nativeIsAbsolute, sep as nativeSeparator} from 'node:path'\n// eslint-disable-next-line n/prefer-global/url\nimport type {URL} from 'url'\n\ninterface IsSubpathOptions {\n native?: boolean\n}\n\n/**\n * Joins a list of paths together.\n *\n * @param paths - Paths to join.\n * @returns Joined path.\n */\nexport function joinPath(...paths: string[]): string {\n return join(...paths)\n}\n\n/**\n * Normalizes a path.\n *\n * @param path - Path to normalize.\n * @returns Normalized path.\n */\nexport function normalizePath(path: string): string {\n return normalize(path)\n}\n\n/**\n * Resolves a list of paths together.\n *\n * @param paths - Paths to resolve.\n * @returns Resolved path.\n */\nexport function resolvePath(...paths: string[]): string {\n return resolve(...paths)\n}\n\n/**\n * Returns the relative path from one path to another.\n *\n * @param from - Path to resolve from.\n * @param to - Path to resolve to.\n * @returns Relative path.\n */\nexport function relativePath(from: string, to: string): string {\n return relative(from, to)\n}\n\n/**\n * Returns whether the path is absolute.\n *\n * @param path - Path to check.\n * @returns Whether the path is absolute.\n */\nexport function isAbsolutePath(path: string): boolean {\n return isAbsolute(path)\n}\n\n/**\n * Returns the directory name of a path.\n *\n * @param path - Path to get the directory name of.\n * @returns Directory name.\n */\nexport function dirname(path: string): string {\n return patheDirname(path)\n}\n\n/**\n * Returns the base name of a path.\n *\n * @param path - Path to get the base name of.\n * @param ext - Optional extension to remove from the result.\n * @returns Base name.\n */\nexport function basename(path: string, ext?: string): string {\n return basenamePathe(path, ext)\n}\n\n/**\n * Returns the extension of the path.\n *\n * @param path - Path to get the extension of.\n * @returns Extension.\n */\nexport function extname(path: string): string {\n return extnamePathe(path)\n}\n\n/**\n * Parses a path into its components (root, dir, base, ext, name).\n *\n * @param path - Path to parse.\n * @returns Parsed path object.\n */\nexport function parsePath(path: string): {root: string; dir: string; base: string; ext: string; name: string} {\n return parse(path)\n}\n\n/**\n * Returns the longest common parent directory of two absolute paths.\n *\n * @param first - First absolute path.\n * @param second - Second absolute path.\n * @returns The common parent directory, or '/' if they share only the root.\n */\nexport function commonParentDirectory(first: string, second: string): string {\n const firstParts = first.split(/\\/+|\\\\+/)\n const secondParts = second.split(/\\/+|\\\\+/)\n let i = 0\n while (i < firstParts.length && i < secondParts.length && firstParts[i] === secondParts[i]) {\n i++\n }\n return i > 1 ? firstParts.slice(0, i).join('/') : '/'\n}\n\n/**\n * Given an absolute filesystem path, it makes it relative to\n * the current working directory. This is useful when logging paths\n * to allow the users to click on the file and let the OS open it\n * in the editor of choice.\n *\n * @param path - Path to relativize.\n * @param dir - Current working directory.\n * @returns Relativized path.\n */\nexport function relativizePath(path: string, dir: string = cwd()): string {\n const result = commonParentDirectory(path, dir)\n const relativePath = relative(dir, path)\n const relativeComponents = relativePath.split('/').filter((component) => component === '..').length\n if (result === '/' || relativePath === '' || relativeComponents > 2) {\n return path\n } else {\n return relativePath\n }\n}\n\n/**\n * Given 2 paths, it returns whether the second path is inside or equal to the first path.\n *\n * By default, paths are compared with pathe, which treats `\\` as a separator\n * on every platform. This behaviour is kept for backward compatibility.\n *\n * When `native` is true, paths are compared with the current OS's native path\n * semantics, so literal backslashes on POSIX are preserved as filename\n * characters. Use `native: true` for security or containment checks on\n * resolved filesystem paths.\n *\n * Symbolic links are not resolved; use fileRealPath from the fs module first\n * when checking physical containment.\n *\n * @param mainPath - The main path.\n * @param subpath - The subpath.\n * @param options - Comparison options. Set `native` to true to use the OS's native path semantics. Defaults to false.\n * @returns Whether the subpath is inside or equal to the main path.\n */\nexport function isSubpath(mainPath: string, subpath: string, options: IsSubpathOptions = {native: false}): boolean {\n if (options.native) {\n const relativePath = nativeRelative(mainPath, subpath)\n return relativePath !== '..' && !relativePath.startsWith(`..${nativeSeparator}`) && !nativeIsAbsolute(relativePath)\n }\n const relativePath = relative(mainPath, subpath)\n return !relativePath.startsWith('..') && !isAbsolutePath(relativePath)\n}\n\n/**\n * Given a module's import.meta.url it returns the directory containing the module.\n *\n * @param moduleURL - The value of import.meta.url in the context of the caller module.\n * @returns The path to the directory containing the caller module.\n */\nexport function moduleDirectory(moduleURL: string | URL): string {\n return dirname(fileURLToPath(moduleURL))\n}\n\n/**\n * When running a script using `npm run`, something interesting happens. If the current\n * folder does not have a `package.json` or a `node_modules` folder, npm will traverse\n * the directory tree upwards until it finds one. Then it will run the script and set\n * `process.cwd()` to that folder, while the actual path is stored in the INIT_CWD\n * environment variable (see here: https://docs.npmjs.com/cli/v9/commands/npm-run-script#description).\n *\n * @returns The path to the current working directory.\n */\nexport function cwd(): string {\n // eslint-disable-next-line @shopify/cli/no-process-cwd\n return normalize(process.env.INIT_CWD || process.cwd()) // eslint-disable-line @typescript-eslint/prefer-nullish-coalescing -- empty env var should fall through\n}\n\n/**\n * Tries to get the value of the `--path` argument, if provided.\n *\n * @param argv - The arguments to search for the `--path` argument.\n * @returns The value of the `--path` argument, if provided.\n */\nexport function sniffForPath(argv = process.argv): string | undefined {\n const pathFlagIndex = argv.indexOf('--path')\n if (pathFlagIndex === -1) {\n const pathArg = argv.find((arg) => arg.startsWith('--path='))\n return pathArg?.split('=')[1]\n }\n const pathFlag = argv[pathFlagIndex + 1]\n if (!pathFlag || pathFlag.startsWith('-')) return\n return pathFlag\n}\n\n/**\n * Returns whether the `--json` or `-j` flags are present in the arguments.\n *\n * @param argv - The arguments to search for the `--json` and `-j` flags.\n * @returns Whether the `--json` or `-j` flag is present in the arguments.\n */\nexport function sniffForJson(argv = process.argv): boolean {\n const passthroughIndex = argv.indexOf('--')\n const commandArguments = passthroughIndex === -1 ? argv : argv.slice(0, passthroughIndex)\n return commandArguments.includes('--json') || commandArguments.includes('-j')\n}\n\n/**\n * Removes any `..` traversal segments from a relative path and calls `warn`\n * if any were stripped. Normal `..` that cancel out within the path (e.g.\n * `foo/../bar` → `bar`) are collapsed but never allowed to escape the root.\n * Both `/` and `\\` are treated as separators for cross-platform safety.\n *\n * @param input - The relative path to sanitize.\n * @param warn - Called with a human-readable warning when traversal segments are removed.\n * @returns The sanitized path (may be an empty string if all segments were traversal).\n */\nexport function sanitizeRelativePath(input: string, warn: (msg: string) => void): string {\n const segments = input.replace(/\\\\/g, '/').split('/')\n const stack: string[] = []\n let stripped = false\n for (const seg of segments) {\n if (seg === '..') {\n stripped = true\n stack.pop()\n } else if (seg !== '.') {\n stack.push(seg)\n }\n }\n const result = stack.join('/')\n if (stripped) {\n warn(`Warning: path '${input}' contains '..' traversal — sanitized to '${result || '.'}'\\n`)\n }\n return result\n}\n"]}
@@ -121,6 +121,10 @@ export declare function ensureAuthenticatedAdmin(store: string, scopes?: AdminAP
121
121
  * If a password is provided, that token will be used against Theme Access API.
122
122
  * Otherwise, it will ensure that the user is authenticated with the Admin API.
123
123
  *
124
+ * Theme Access passwords only work with the store's permanent `.myshopify.com` domain, so when the password is a
125
+ * Theme Access password the returned session uses the permanent domain of `store`, even if `store` is another of the
126
+ * store's domains.
127
+ *
124
128
  * @param store - Store fqdn to request auth for.
125
129
  * @param password - Password generated from Theme Access app.
126
130
  * @param scopes - Optional array of extra scopes to authenticate with.
@@ -128,14 +132,44 @@ export declare function ensureAuthenticatedAdmin(store: string, scopes?: AdminAP
128
132
  * @returns The access token and store.
129
133
  */
130
134
  export declare function ensureAuthenticatedThemes(store: string, password: string | undefined, scopes?: AdminAPIScope[], options?: EnsureAuthenticatedAdditionalOptions): Promise<AdminSession>;
135
+ /**
136
+ * Options for `ensureAuthenticatedBusinessPlatform`.
137
+ */
138
+ export interface EnsureAuthenticatedBusinessPlatformOptions extends EnsureAuthenticatedAdditionalOptions {
139
+ /**
140
+ * Authenticate with the automation token set in the environment, when there is one, instead of the
141
+ * logged-in user. Only commands that support automation tokens opt in; other callers, such as
142
+ * Hydrogen's login, keep using the user's session.
143
+ */
144
+ allowAutomationToken?: boolean;
145
+ }
131
146
  /**
132
147
  * Ensure that we have a valid session to access the Business Platform API.
133
148
  *
134
- * @param scopes - Optional array of extra scopes to authenticate with.
149
+ * @param scopes - Optional array of extra scopes to authenticate with. Ignored when an automation token is used.
135
150
  * @param options - Optional extra options to use.
136
151
  * @returns The access token for the Business Platform API.
137
152
  */
138
- export declare function ensureAuthenticatedBusinessPlatform(scopes?: BusinessPlatformScope[], options?: EnsureAuthenticatedAdditionalOptions): Promise<string>;
153
+ export declare function ensureAuthenticatedBusinessPlatform(scopes?: BusinessPlatformScope[], options?: EnsureAuthenticatedBusinessPlatformOptions): Promise<string>;
154
+ /**
155
+ * Whether SHOPIFY_SERVICE_ACCOUNT_TOKEN is set, which makes it the automation token the CLI authenticates with.
156
+ *
157
+ * True even when it's empty or set together with another automation token variable. Commands use this to skip logins
158
+ * saved on this machine, and authentication then reports any problem with the variables.
159
+ *
160
+ * @returns True when SHOPIFY_SERVICE_ACCOUNT_TOKEN is set.
161
+ */
162
+ export declare function isServiceAccountTokenSet(): boolean;
163
+ /**
164
+ * Fails when SHOPIFY_SERVICE_ACCOUNT_TOKEN is set, for commands that can't run with that token.
165
+ *
166
+ * Service account tokens can't log in as a user or call a store's Admin API, so commands that need either
167
+ * refuse to run instead of quietly using the person's own login. `ensureAuthenticated` runs this check before any
168
+ * login. Commands that run on a login saved by `shopify store auth` call it before loading that login.
169
+ *
170
+ * @throws AbortError when SHOPIFY_SERVICE_ACCOUNT_TOKEN is set.
171
+ */
172
+ export declare function ensureNoServiceAccountToken(): void;
139
173
  /**
140
174
  * Logout from Shopify.
141
175
  *
@@ -147,7 +181,8 @@ export declare function logout(): Promise<void>;
147
181
  *
148
182
  * See `ensureAuthenticatedAdmin` for access on behalf of a user.
149
183
  *
150
- * @param storeFqdn - Store fqdn to request auth for.
184
+ * @param storeFqdn - Store handle or domain, normalized with `normalizeStoreFqdn`. Unsupported URL components are
185
+ * rejected before the client secret is sent.
151
186
  * @param clientId - Client ID of the app.
152
187
  * @param clientSecret - Client secret of the app.
153
188
  * @returns The access token for the Admin API.
@@ -2,11 +2,15 @@ import { shopifyFetch } from './http.js';
2
2
  import { nonRandomUUID } from './crypto.js';
3
3
  import { getAppAutomationToken } from './environment.js';
4
4
  import { AbortError, BugError } from './error.js';
5
+ import { normalizeStoreFqdn } from './context/fqdn.js';
5
6
  import { outputContent, outputToken, outputDebug } from './output.js';
6
7
  import * as sessionStore from '../../private/node/session/store.js';
8
+ import { automationTokenVariable } from '../../private/node/session/automation-token.js';
9
+ import { environmentVariables } from '../../private/node/constants.js';
7
10
  import { exchangeCustomPartnerToken, exchangeAppAutomationTokenForAppManagementAccessToken, exchangeAppAutomationTokenForBusinessPlatformAccessToken, } from '../../private/node/session/exchange.js';
8
11
  import { ensureAuthenticated, setCommandSessionId, setLastSeenAuthMethod, setLastSeenUserIdAfterAuth, } from '../../private/node/session.js';
9
12
  import { isThemeAccessSession } from '../../private/node/api/rest.js';
13
+ import { resolvePermanentStoreFqdn } from '../../private/node/session/permanent-store-domain.js';
10
14
  /**
11
15
  * Records the user ID that should be attached to command analytics for this process.
12
16
  *
@@ -178,6 +182,10 @@ ${outputToken.json(scopes)}
178
182
  * If a password is provided, that token will be used against Theme Access API.
179
183
  * Otherwise, it will ensure that the user is authenticated with the Admin API.
180
184
  *
185
+ * Theme Access passwords only work with the store's permanent `.myshopify.com` domain, so when the password is a
186
+ * Theme Access password the returned session uses the permanent domain of `store`, even if `store` is another of the
187
+ * store's domains.
188
+ *
181
189
  * @param store - Store fqdn to request auth for.
182
190
  * @param password - Password generated from Theme Access app.
183
191
  * @param scopes - Optional array of extra scopes to authenticate with.
@@ -190,9 +198,11 @@ ${outputToken.json(scopes)}
190
198
  `);
191
199
  if (password) {
192
200
  const session = { token: password, storeFqdn: store };
193
- const authMethod = isThemeAccessSession(session) ? 'theme_access_token' : 'custom_app_token';
194
- setLastSeenAuthMethod(authMethod);
201
+ const themeAccess = isThemeAccessSession(session);
202
+ setLastSeenAuthMethod(themeAccess ? 'theme_access_token' : 'custom_app_token');
195
203
  setLastSeenUserIdAfterAuth(nonRandomUUID(password));
204
+ if (themeAccess)
205
+ session.storeFqdn = await resolvePermanentStoreFqdn(store);
196
206
  return session;
197
207
  }
198
208
  return ensureAuthenticatedAdmin(store, scopes, options);
@@ -200,7 +210,7 @@ ${outputToken.json(scopes)}
200
210
  /**
201
211
  * Ensure that we have a valid session to access the Business Platform API.
202
212
  *
203
- * @param scopes - Optional array of extra scopes to authenticate with.
213
+ * @param scopes - Optional array of extra scopes to authenticate with. Ignored when an automation token is used.
204
214
  * @param options - Optional extra options to use.
205
215
  * @returns The access token for the Business Platform API.
206
216
  */
@@ -208,12 +218,47 @@ export async function ensureAuthenticatedBusinessPlatform(scopes = [], options =
208
218
  outputDebug(outputContent `Ensuring that the user is authenticated with the Business Platform API with the following scopes:
209
219
  ${outputToken.json(scopes)}
210
220
  `);
211
- const tokens = await ensureAuthenticated({ businessPlatformApi: { scopes } }, process.env, options);
221
+ const { allowAutomationToken = false, ...authenticationOptions } = options;
222
+ const automationToken = allowAutomationToken ? getAppAutomationToken() : undefined;
223
+ if (automationToken) {
224
+ // Request no explicit scopes, so Identity issues every Business Platform scope the token holds. The organizations
225
+ // API admits a CLI token only with a scope such as organization.store-management, and naming that scope would
226
+ // fail the exchange for app and Partner tokens, which don't hold it.
227
+ const result = await exchangeAppAutomationTokenForBusinessPlatformAccessToken(automationToken, []);
228
+ return result.accessToken;
229
+ }
230
+ const tokens = await ensureAuthenticated({ businessPlatformApi: { scopes } }, process.env, authenticationOptions);
212
231
  if (!tokens.businessPlatform) {
213
232
  throw new BugError('No business-platform token found after ensuring authenticated');
214
233
  }
215
234
  return tokens.businessPlatform;
216
235
  }
236
+ /**
237
+ * Whether SHOPIFY_SERVICE_ACCOUNT_TOKEN is set, which makes it the automation token the CLI authenticates with.
238
+ *
239
+ * True even when it's empty or set together with another automation token variable. Commands use this to skip logins
240
+ * saved on this machine, and authentication then reports any problem with the variables.
241
+ *
242
+ * @returns True when SHOPIFY_SERVICE_ACCOUNT_TOKEN is set.
243
+ */
244
+ export function isServiceAccountTokenSet() {
245
+ return automationTokenVariable() === environmentVariables.serviceAccountToken;
246
+ }
247
+ /**
248
+ * Fails when SHOPIFY_SERVICE_ACCOUNT_TOKEN is set, for commands that can't run with that token.
249
+ *
250
+ * Service account tokens can't log in as a user or call a store's Admin API, so commands that need either
251
+ * refuse to run instead of quietly using the person's own login. `ensureAuthenticated` runs this check before any
252
+ * login. Commands that run on a login saved by `shopify store auth` call it before loading that login.
253
+ *
254
+ * @throws AbortError when SHOPIFY_SERVICE_ACCOUNT_TOKEN is set.
255
+ */
256
+ export function ensureNoServiceAccountToken() {
257
+ if (!isServiceAccountTokenSet())
258
+ return;
259
+ const variable = environmentVariables.serviceAccountToken;
260
+ throw new AbortError(`This command can't use ${variable}.`, `Unset ${variable} to run it with your Shopify account.`);
261
+ }
217
262
  /**
218
263
  * Logout from Shopify.
219
264
  *
@@ -227,18 +272,20 @@ export function logout() {
227
272
  *
228
273
  * See `ensureAuthenticatedAdmin` for access on behalf of a user.
229
274
  *
230
- * @param storeFqdn - Store fqdn to request auth for.
275
+ * @param storeFqdn - Store handle or domain, normalized with `normalizeStoreFqdn`. Unsupported URL components are
276
+ * rejected before the client secret is sent.
231
277
  * @param clientId - Client ID of the app.
232
278
  * @param clientSecret - Client secret of the app.
233
279
  * @returns The access token for the Admin API.
234
280
  */
235
281
  export async function ensureAuthenticatedAdminAsApp(storeFqdn, clientId, clientSecret) {
282
+ const normalizedStoreFqdn = normalizeStoreFqdn(storeFqdn);
236
283
  const bodyData = {
237
284
  client_id: clientId,
238
285
  client_secret: clientSecret,
239
286
  grant_type: 'client_credentials',
240
287
  };
241
- const tokenResponse = await shopifyFetch(`https://${storeFqdn}/admin/oauth/access_token`, {
288
+ const tokenResponse = await shopifyFetch(`https://${normalizedStoreFqdn}/admin/oauth/access_token`, {
242
289
  method: 'POST',
243
290
  headers: {
244
291
  'Content-Type': 'application/json',
@@ -248,13 +295,13 @@ export async function ensureAuthenticatedAdminAsApp(storeFqdn, clientId, clientS
248
295
  const body = await tokenResponse.text();
249
296
  if (tokenResponse.status === 400) {
250
297
  if (body.includes('app_not_installed')) {
251
- throw new AbortError(outputContent `App is not installed on ${outputToken.green(storeFqdn)}. Try running ${outputToken.genericShellCommand(`shopify app dev`)} to connect your app to the shop.`);
298
+ throw new AbortError(outputContent `App is not installed on ${outputToken.green(normalizedStoreFqdn)}. Try running ${outputToken.genericShellCommand(`shopify app dev`)} to connect your app to the shop.`);
252
299
  }
253
- throw new AbortError(`Failed to get access token for app ${clientId} on store ${storeFqdn}: ${tokenResponse.statusText}`);
300
+ throw new AbortError(`Failed to get access token for app ${clientId} on store ${normalizedStoreFqdn}: ${tokenResponse.statusText}`);
254
301
  }
255
302
  try {
256
303
  const tokenJson = JSON.parse(body);
257
- return { token: tokenJson.access_token, storeFqdn };
304
+ return { token: tokenJson.access_token, storeFqdn: normalizedStoreFqdn };
258
305
  }
259
306
  catch (error) {
260
307
  if (error instanceof SyntaxError) {
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/public/node/session.ts"],"names":[],"mappings":"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","sourcesContent":["import {shopifyFetch} from './http.js'\nimport {nonRandomUUID} from './crypto.js'\nimport {getAppAutomationToken} from './environment.js'\nimport {AbortError, BugError} from './error.js'\nimport {outputContent, outputToken, outputDebug} from './output.js'\nimport * as sessionStore from '../../private/node/session/store.js'\nimport {\n exchangeCustomPartnerToken,\n exchangeAppAutomationTokenForAppManagementAccessToken,\n exchangeAppAutomationTokenForBusinessPlatformAccessToken,\n} from '../../private/node/session/exchange.js'\nimport {\n AdminAPIScope,\n AppManagementAPIScope,\n BusinessPlatformScope,\n EnsureAuthenticatedAdditionalOptions,\n PartnersAPIScope,\n StorefrontRendererScope,\n ensureAuthenticated,\n setCommandSessionId,\n setLastSeenAuthMethod,\n setLastSeenUserIdAfterAuth,\n} from '../../private/node/session.js'\nimport {isThemeAccessSession} from '../../private/node/api/rest.js'\n\n/**\n * Session Object to access the Admin API, includes the token and the store FQDN.\n */\nexport interface AdminSession {\n token: string\n storeFqdn: string\n}\n\n/**\n * Session Object for Partners API and App Management API access.\n */\nexport interface Session {\n token: string\n businessPlatformToken: string\n accountInfo: AccountInfo\n userId: string\n}\n\nexport type AccountInfo = UserAccountInfo | ServiceAccountInfo | UnknownAccountInfo\n\n/**\n * Records the user ID that should be attached to command analytics for this process.\n *\n * @param userId - User identifier to report on the command analytics event.\n */\nexport function setLastSeenUserId(userId: string): void {\n setLastSeenUserIdAfterAuth(userId)\n}\n\n/**\n * Finds a stored Shopify account session by alias without changing the current session.\n *\n * @param alias - The account alias to find.\n * @returns The matching session ID, or undefined if no session matches.\n */\nexport async function findSessionIdByAlias(alias: string): Promise<string | undefined> {\n return sessionStore.findSessionByAlias(alias)\n}\n\n/**\n * Selects a stored Shopify account session by alias for the current command process.\n *\n * @param alias - The account alias to select. Passing undefined clears the command selection.\n */\nexport async function setCurrentSessionAlias(alias?: string): Promise<void> {\n if (!alias) {\n setCommandSessionId(undefined)\n return\n }\n\n const sessionId = await findSessionIdByAlias(alias)\n if (!sessionId) {\n throw new AbortError(\n outputContent`No authenticated account found for alias ${outputToken.yellow(alias)}.`,\n outputContent`Run ${outputToken.genericShellCommand(`shopify auth login`)} first.`,\n )\n }\n setCommandSessionId(sessionId)\n}\n\ninterface UserAccountInfo {\n type: 'UserAccount'\n email: string\n}\n\ninterface ServiceAccountInfo {\n type: 'ServiceAccount'\n orgName: string\n}\n\ninterface UnknownAccountInfo {\n type: 'UnknownAccount'\n}\n\n/**\n * Type guard to check if an account is a UserAccount.\n *\n * @param account - The account to check.\n * @returns True if the account is a UserAccount.\n */\nexport function isUserAccount(account: AccountInfo): account is UserAccountInfo {\n return account.type === 'UserAccount'\n}\n\n/**\n * Type guard to check if an account is a ServiceAccount.\n *\n * @param account - The account to check.\n * @returns True if the account is a ServiceAccount.\n */\nexport function isServiceAccount(account: AccountInfo): account is ServiceAccountInfo {\n return account.type === 'ServiceAccount'\n}\n\n/**\n * Ensure that we have a valid session with no particular scopes.\n *\n * @param env - Optional environment variables to use.\n * @param options - Optional extra options to use.\n * @returns The user ID.\n */\nexport async function ensureAuthenticatedUser(\n env = process.env,\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<{userId: string}> {\n outputDebug(outputContent`Ensuring that the user is authenticated with no particular scopes`)\n const tokens = await ensureAuthenticated({}, env, options)\n return {userId: tokens.userId}\n}\n\n/**\n * Ensure that we have a valid session to access the Partners API.\n * If SHOPIFY_CLI_PARTNERS_TOKEN exists, that token will be used to obtain a valid Partners Token\n * If SHOPIFY_CLI_PARTNERS_TOKEN exists, scopes will be ignored.\n *\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param env - Optional environment variables to use.\n * @param options - Optional extra options to use.\n * @returns The access token for the Partners API.\n */\nexport async function ensureAuthenticatedPartners(\n scopes: PartnersAPIScope[] = [],\n env = process.env,\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<{token: string; userId: string}> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Partners API with the following scopes:\n${outputToken.json(scopes)}\n`)\n const envToken = getAppAutomationToken()\n if (envToken) {\n const result = await exchangeCustomPartnerToken(envToken)\n return {token: result.accessToken, userId: result.userId}\n }\n const tokens = await ensureAuthenticated({partnersApi: {scopes}}, env, options)\n if (!tokens.partners) {\n throw new BugError('No partners token found after ensuring authenticated')\n }\n return {token: tokens.partners, userId: tokens.userId}\n}\n\n/**\n * Ensure that we have a valid session to access the App Management API.\n *\n * @param options - Optional extra options to use.\n * @param appManagementScopes - Optional array of extra scopes to authenticate with.\n * @param businessPlatformScopes - Optional array of extra scopes to authenticate with.\n * @param env - Optional environment variables to use.\n * @returns The access token for the App Management API.\n */\nexport async function ensureAuthenticatedAppManagementAndBusinessPlatform(\n options: EnsureAuthenticatedAdditionalOptions = {},\n appManagementScopes: AppManagementAPIScope[] = [],\n businessPlatformScopes: BusinessPlatformScope[] = [],\n env = process.env,\n): Promise<{appManagementToken: string; userId: string; businessPlatformToken: string}> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the App Management API with the following scopes:\n${outputToken.json(appManagementScopes)}\n`)\n\n const envToken = getAppAutomationToken()\n if (envToken) {\n const appManagmentToken = await exchangeAppAutomationTokenForAppManagementAccessToken(envToken)\n const businessPlatformToken = await exchangeAppAutomationTokenForBusinessPlatformAccessToken(envToken)\n\n return {\n appManagementToken: appManagmentToken.accessToken,\n userId: appManagmentToken.userId,\n businessPlatformToken: businessPlatformToken.accessToken,\n }\n }\n\n const tokens = await ensureAuthenticated(\n {appManagementApi: {scopes: appManagementScopes}, businessPlatformApi: {scopes: businessPlatformScopes}},\n env,\n options,\n )\n if (!tokens.appManagement || !tokens.businessPlatform) {\n throw new BugError('No App Management or Business Platform token found after ensuring authenticated')\n }\n\n return {\n appManagementToken: tokens.appManagement,\n userId: tokens.userId,\n businessPlatformToken: tokens.businessPlatform,\n }\n}\n\n/**\n * Ensure that we have a valid session to access the Storefront API.\n *\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param password - Optional password to use.\n * @param options - Optional extra options to use.\n * @returns The access token for the Storefront API.\n */\nexport async function ensureAuthenticatedStorefront(\n scopes: StorefrontRendererScope[] = [],\n password: string | undefined = undefined,\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<string> {\n if (password) {\n const session = {token: password, storeFqdn: ''}\n const authMethod = isThemeAccessSession(session) ? 'theme_access_token' : 'custom_app_token'\n setLastSeenAuthMethod(authMethod)\n setLastSeenUserIdAfterAuth(nonRandomUUID(password))\n return password\n }\n\n outputDebug(outputContent`Ensuring that the user is authenticated with the Storefront API with the following scopes:\n${outputToken.json(scopes)}\n`)\n const tokens = await ensureAuthenticated({storefrontRendererApi: {scopes}}, process.env, options)\n if (!tokens.storefront) {\n throw new BugError('No storefront token found after ensuring authenticated')\n }\n return tokens.storefront\n}\n\n/**\n * Ensure that we have a valid Admin session for the given store.\n *\n * @param store - Store fqdn to request auth for.\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param options - Optional extra options to use.\n * @returns The access token for the Admin API.\n */\nexport async function ensureAuthenticatedAdmin(\n store: string,\n scopes: AdminAPIScope[] = [],\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<AdminSession> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Admin API with the following scopes for the store ${outputToken.raw(\n store,\n )}:\n${outputToken.json(scopes)}\n`)\n const tokens = await ensureAuthenticated({adminApi: {scopes, storeFqdn: store}}, process.env, {\n ...options,\n })\n if (!tokens.admin) {\n throw new BugError('No admin token found after ensuring authenticated')\n }\n return tokens.admin\n}\n\n/**\n * Ensure that we have a valid session to access the Theme API.\n * If a password is provided, that token will be used against Theme Access API.\n * Otherwise, it will ensure that the user is authenticated with the Admin API.\n *\n * @param store - Store fqdn to request auth for.\n * @param password - Password generated from Theme Access app.\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param options - Optional extra options to use.\n * @returns The access token and store.\n */\nexport async function ensureAuthenticatedThemes(\n store: string,\n password: string | undefined,\n scopes: AdminAPIScope[] = [],\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<AdminSession> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Theme API with the following scopes:\n${outputToken.json(scopes)}\n`)\n if (password) {\n const session = {token: password, storeFqdn: store}\n const authMethod = isThemeAccessSession(session) ? 'theme_access_token' : 'custom_app_token'\n setLastSeenAuthMethod(authMethod)\n setLastSeenUserIdAfterAuth(nonRandomUUID(password))\n return session\n }\n return ensureAuthenticatedAdmin(store, scopes, options)\n}\n\n/**\n * Ensure that we have a valid session to access the Business Platform API.\n *\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param options - Optional extra options to use.\n * @returns The access token for the Business Platform API.\n */\nexport async function ensureAuthenticatedBusinessPlatform(\n scopes: BusinessPlatformScope[] = [],\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<string> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Business Platform API with the following scopes:\n${outputToken.json(scopes)}\n`)\n const tokens = await ensureAuthenticated({businessPlatformApi: {scopes}}, process.env, options)\n if (!tokens.businessPlatform) {\n throw new BugError('No business-platform token found after ensuring authenticated')\n }\n return tokens.businessPlatform\n}\n\n/**\n * Logout from Shopify.\n *\n * @returns A promise that resolves when the logout is complete.\n */\nexport function logout(): Promise<void> {\n return sessionStore.remove()\n}\n\n/**\n * Ensure that we have a valid Admin session for the given store, with access on behalf of the app.\n *\n * See `ensureAuthenticatedAdmin` for access on behalf of a user.\n *\n * @param storeFqdn - Store fqdn to request auth for.\n * @param clientId - Client ID of the app.\n * @param clientSecret - Client secret of the app.\n * @returns The access token for the Admin API.\n */\nexport async function ensureAuthenticatedAdminAsApp(\n storeFqdn: string,\n clientId: string,\n clientSecret: string,\n): Promise<AdminSession> {\n const bodyData = {\n client_id: clientId,\n client_secret: clientSecret,\n grant_type: 'client_credentials',\n }\n const tokenResponse = await shopifyFetch(\n `https://${storeFqdn}/admin/oauth/access_token`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(bodyData),\n },\n 'slow-request',\n )\n\n const body = await tokenResponse.text()\n\n if (tokenResponse.status === 400) {\n if (body.includes('app_not_installed')) {\n throw new AbortError(\n outputContent`App is not installed on ${outputToken.green(\n storeFqdn,\n )}. Try running ${outputToken.genericShellCommand(`shopify app dev`)} to connect your app to the shop.`,\n )\n }\n throw new AbortError(\n `Failed to get access token for app ${clientId} on store ${storeFqdn}: ${tokenResponse.statusText}`,\n )\n }\n try {\n const tokenJson = JSON.parse(body) as {access_token: string}\n return {token: tokenJson.access_token, storeFqdn}\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new AbortError(\n `Received invalid response from admin authentication service (HTTP ${tokenResponse.status}).`,\n 'The response could not be parsed as JSON. The service may be temporarily unavailable. Please try again.',\n )\n }\n throw error\n }\n}\n"]}
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/public/node/session.ts"],"names":[],"mappings":"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","sourcesContent":["import {shopifyFetch} from './http.js'\nimport {nonRandomUUID} from './crypto.js'\nimport {getAppAutomationToken} from './environment.js'\nimport {AbortError, BugError} from './error.js'\nimport {normalizeStoreFqdn} from './context/fqdn.js'\nimport {outputContent, outputToken, outputDebug} from './output.js'\nimport * as sessionStore from '../../private/node/session/store.js'\nimport {automationTokenVariable} from '../../private/node/session/automation-token.js'\nimport {environmentVariables} from '../../private/node/constants.js'\nimport {\n exchangeCustomPartnerToken,\n exchangeAppAutomationTokenForAppManagementAccessToken,\n exchangeAppAutomationTokenForBusinessPlatformAccessToken,\n} from '../../private/node/session/exchange.js'\nimport {\n AdminAPIScope,\n AppManagementAPIScope,\n BusinessPlatformScope,\n EnsureAuthenticatedAdditionalOptions,\n PartnersAPIScope,\n StorefrontRendererScope,\n ensureAuthenticated,\n setCommandSessionId,\n setLastSeenAuthMethod,\n setLastSeenUserIdAfterAuth,\n} from '../../private/node/session.js'\nimport {isThemeAccessSession} from '../../private/node/api/rest.js'\nimport {resolvePermanentStoreFqdn} from '../../private/node/session/permanent-store-domain.js'\n\n/**\n * Session Object to access the Admin API, includes the token and the store FQDN.\n */\nexport interface AdminSession {\n token: string\n storeFqdn: string\n}\n\n/**\n * Session Object for Partners API and App Management API access.\n */\nexport interface Session {\n token: string\n businessPlatformToken: string\n accountInfo: AccountInfo\n userId: string\n}\n\nexport type AccountInfo = UserAccountInfo | ServiceAccountInfo | UnknownAccountInfo\n\n/**\n * Records the user ID that should be attached to command analytics for this process.\n *\n * @param userId - User identifier to report on the command analytics event.\n */\nexport function setLastSeenUserId(userId: string): void {\n setLastSeenUserIdAfterAuth(userId)\n}\n\n/**\n * Finds a stored Shopify account session by alias without changing the current session.\n *\n * @param alias - The account alias to find.\n * @returns The matching session ID, or undefined if no session matches.\n */\nexport async function findSessionIdByAlias(alias: string): Promise<string | undefined> {\n return sessionStore.findSessionByAlias(alias)\n}\n\n/**\n * Selects a stored Shopify account session by alias for the current command process.\n *\n * @param alias - The account alias to select. Passing undefined clears the command selection.\n */\nexport async function setCurrentSessionAlias(alias?: string): Promise<void> {\n if (!alias) {\n setCommandSessionId(undefined)\n return\n }\n\n const sessionId = await findSessionIdByAlias(alias)\n if (!sessionId) {\n throw new AbortError(\n outputContent`No authenticated account found for alias ${outputToken.yellow(alias)}.`,\n outputContent`Run ${outputToken.genericShellCommand(`shopify auth login`)} first.`,\n )\n }\n setCommandSessionId(sessionId)\n}\n\ninterface UserAccountInfo {\n type: 'UserAccount'\n email: string\n}\n\ninterface ServiceAccountInfo {\n type: 'ServiceAccount'\n orgName: string\n}\n\ninterface UnknownAccountInfo {\n type: 'UnknownAccount'\n}\n\n/**\n * Type guard to check if an account is a UserAccount.\n *\n * @param account - The account to check.\n * @returns True if the account is a UserAccount.\n */\nexport function isUserAccount(account: AccountInfo): account is UserAccountInfo {\n return account.type === 'UserAccount'\n}\n\n/**\n * Type guard to check if an account is a ServiceAccount.\n *\n * @param account - The account to check.\n * @returns True if the account is a ServiceAccount.\n */\nexport function isServiceAccount(account: AccountInfo): account is ServiceAccountInfo {\n return account.type === 'ServiceAccount'\n}\n\n/**\n * Ensure that we have a valid session with no particular scopes.\n *\n * @param env - Optional environment variables to use.\n * @param options - Optional extra options to use.\n * @returns The user ID.\n */\nexport async function ensureAuthenticatedUser(\n env = process.env,\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<{userId: string}> {\n outputDebug(outputContent`Ensuring that the user is authenticated with no particular scopes`)\n const tokens = await ensureAuthenticated({}, env, options)\n return {userId: tokens.userId}\n}\n\n/**\n * Ensure that we have a valid session to access the Partners API.\n * If SHOPIFY_CLI_PARTNERS_TOKEN exists, that token will be used to obtain a valid Partners Token\n * If SHOPIFY_CLI_PARTNERS_TOKEN exists, scopes will be ignored.\n *\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param env - Optional environment variables to use.\n * @param options - Optional extra options to use.\n * @returns The access token for the Partners API.\n */\nexport async function ensureAuthenticatedPartners(\n scopes: PartnersAPIScope[] = [],\n env = process.env,\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<{token: string; userId: string}> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Partners API with the following scopes:\n${outputToken.json(scopes)}\n`)\n const envToken = getAppAutomationToken()\n if (envToken) {\n const result = await exchangeCustomPartnerToken(envToken)\n return {token: result.accessToken, userId: result.userId}\n }\n const tokens = await ensureAuthenticated({partnersApi: {scopes}}, env, options)\n if (!tokens.partners) {\n throw new BugError('No partners token found after ensuring authenticated')\n }\n return {token: tokens.partners, userId: tokens.userId}\n}\n\n/**\n * Ensure that we have a valid session to access the App Management API.\n *\n * @param options - Optional extra options to use.\n * @param appManagementScopes - Optional array of extra scopes to authenticate with.\n * @param businessPlatformScopes - Optional array of extra scopes to authenticate with.\n * @param env - Optional environment variables to use.\n * @returns The access token for the App Management API.\n */\nexport async function ensureAuthenticatedAppManagementAndBusinessPlatform(\n options: EnsureAuthenticatedAdditionalOptions = {},\n appManagementScopes: AppManagementAPIScope[] = [],\n businessPlatformScopes: BusinessPlatformScope[] = [],\n env = process.env,\n): Promise<{appManagementToken: string; userId: string; businessPlatformToken: string}> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the App Management API with the following scopes:\n${outputToken.json(appManagementScopes)}\n`)\n\n const envToken = getAppAutomationToken()\n if (envToken) {\n const appManagmentToken = await exchangeAppAutomationTokenForAppManagementAccessToken(envToken)\n const businessPlatformToken = await exchangeAppAutomationTokenForBusinessPlatformAccessToken(envToken)\n\n return {\n appManagementToken: appManagmentToken.accessToken,\n userId: appManagmentToken.userId,\n businessPlatformToken: businessPlatformToken.accessToken,\n }\n }\n\n const tokens = await ensureAuthenticated(\n {appManagementApi: {scopes: appManagementScopes}, businessPlatformApi: {scopes: businessPlatformScopes}},\n env,\n options,\n )\n if (!tokens.appManagement || !tokens.businessPlatform) {\n throw new BugError('No App Management or Business Platform token found after ensuring authenticated')\n }\n\n return {\n appManagementToken: tokens.appManagement,\n userId: tokens.userId,\n businessPlatformToken: tokens.businessPlatform,\n }\n}\n\n/**\n * Ensure that we have a valid session to access the Storefront API.\n *\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param password - Optional password to use.\n * @param options - Optional extra options to use.\n * @returns The access token for the Storefront API.\n */\nexport async function ensureAuthenticatedStorefront(\n scopes: StorefrontRendererScope[] = [],\n password: string | undefined = undefined,\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<string> {\n if (password) {\n const session = {token: password, storeFqdn: ''}\n const authMethod = isThemeAccessSession(session) ? 'theme_access_token' : 'custom_app_token'\n setLastSeenAuthMethod(authMethod)\n setLastSeenUserIdAfterAuth(nonRandomUUID(password))\n return password\n }\n\n outputDebug(outputContent`Ensuring that the user is authenticated with the Storefront API with the following scopes:\n${outputToken.json(scopes)}\n`)\n const tokens = await ensureAuthenticated({storefrontRendererApi: {scopes}}, process.env, options)\n if (!tokens.storefront) {\n throw new BugError('No storefront token found after ensuring authenticated')\n }\n return tokens.storefront\n}\n\n/**\n * Ensure that we have a valid Admin session for the given store.\n *\n * @param store - Store fqdn to request auth for.\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param options - Optional extra options to use.\n * @returns The access token for the Admin API.\n */\nexport async function ensureAuthenticatedAdmin(\n store: string,\n scopes: AdminAPIScope[] = [],\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<AdminSession> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Admin API with the following scopes for the store ${outputToken.raw(\n store,\n )}:\n${outputToken.json(scopes)}\n`)\n const tokens = await ensureAuthenticated({adminApi: {scopes, storeFqdn: store}}, process.env, {\n ...options,\n })\n if (!tokens.admin) {\n throw new BugError('No admin token found after ensuring authenticated')\n }\n return tokens.admin\n}\n\n/**\n * Ensure that we have a valid session to access the Theme API.\n * If a password is provided, that token will be used against Theme Access API.\n * Otherwise, it will ensure that the user is authenticated with the Admin API.\n *\n * Theme Access passwords only work with the store's permanent `.myshopify.com` domain, so when the password is a\n * Theme Access password the returned session uses the permanent domain of `store`, even if `store` is another of the\n * store's domains.\n *\n * @param store - Store fqdn to request auth for.\n * @param password - Password generated from Theme Access app.\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param options - Optional extra options to use.\n * @returns The access token and store.\n */\nexport async function ensureAuthenticatedThemes(\n store: string,\n password: string | undefined,\n scopes: AdminAPIScope[] = [],\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<AdminSession> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Theme API with the following scopes:\n${outputToken.json(scopes)}\n`)\n if (password) {\n const session = {token: password, storeFqdn: store}\n const themeAccess = isThemeAccessSession(session)\n setLastSeenAuthMethod(themeAccess ? 'theme_access_token' : 'custom_app_token')\n setLastSeenUserIdAfterAuth(nonRandomUUID(password))\n if (themeAccess) session.storeFqdn = await resolvePermanentStoreFqdn(store)\n return session\n }\n return ensureAuthenticatedAdmin(store, scopes, options)\n}\n\n/**\n * Options for `ensureAuthenticatedBusinessPlatform`.\n */\nexport interface EnsureAuthenticatedBusinessPlatformOptions extends EnsureAuthenticatedAdditionalOptions {\n /**\n * Authenticate with the automation token set in the environment, when there is one, instead of the\n * logged-in user. Only commands that support automation tokens opt in; other callers, such as\n * Hydrogen's login, keep using the user's session.\n */\n allowAutomationToken?: boolean\n}\n\n/**\n * Ensure that we have a valid session to access the Business Platform API.\n *\n * @param scopes - Optional array of extra scopes to authenticate with. Ignored when an automation token is used.\n * @param options - Optional extra options to use.\n * @returns The access token for the Business Platform API.\n */\nexport async function ensureAuthenticatedBusinessPlatform(\n scopes: BusinessPlatformScope[] = [],\n options: EnsureAuthenticatedBusinessPlatformOptions = {},\n): Promise<string> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Business Platform API with the following scopes:\n${outputToken.json(scopes)}\n`)\n const {allowAutomationToken = false, ...authenticationOptions} = options\n const automationToken = allowAutomationToken ? getAppAutomationToken() : undefined\n if (automationToken) {\n // Request no explicit scopes, so Identity issues every Business Platform scope the token holds. The organizations\n // API admits a CLI token only with a scope such as organization.store-management, and naming that scope would\n // fail the exchange for app and Partner tokens, which don't hold it.\n const result = await exchangeAppAutomationTokenForBusinessPlatformAccessToken(automationToken, [])\n return result.accessToken\n }\n\n const tokens = await ensureAuthenticated({businessPlatformApi: {scopes}}, process.env, authenticationOptions)\n if (!tokens.businessPlatform) {\n throw new BugError('No business-platform token found after ensuring authenticated')\n }\n return tokens.businessPlatform\n}\n\n/**\n * Whether SHOPIFY_SERVICE_ACCOUNT_TOKEN is set, which makes it the automation token the CLI authenticates with.\n *\n * True even when it's empty or set together with another automation token variable. Commands use this to skip logins\n * saved on this machine, and authentication then reports any problem with the variables.\n *\n * @returns True when SHOPIFY_SERVICE_ACCOUNT_TOKEN is set.\n */\nexport function isServiceAccountTokenSet(): boolean {\n return automationTokenVariable() === environmentVariables.serviceAccountToken\n}\n\n/**\n * Fails when SHOPIFY_SERVICE_ACCOUNT_TOKEN is set, for commands that can't run with that token.\n *\n * Service account tokens can't log in as a user or call a store's Admin API, so commands that need either\n * refuse to run instead of quietly using the person's own login. `ensureAuthenticated` runs this check before any\n * login. Commands that run on a login saved by `shopify store auth` call it before loading that login.\n *\n * @throws AbortError when SHOPIFY_SERVICE_ACCOUNT_TOKEN is set.\n */\nexport function ensureNoServiceAccountToken(): void {\n if (!isServiceAccountTokenSet()) return\n const variable = environmentVariables.serviceAccountToken\n\n throw new AbortError(`This command can't use ${variable}.`, `Unset ${variable} to run it with your Shopify account.`)\n}\n\n/**\n * Logout from Shopify.\n *\n * @returns A promise that resolves when the logout is complete.\n */\nexport function logout(): Promise<void> {\n return sessionStore.remove()\n}\n\n/**\n * Ensure that we have a valid Admin session for the given store, with access on behalf of the app.\n *\n * See `ensureAuthenticatedAdmin` for access on behalf of a user.\n *\n * @param storeFqdn - Store handle or domain, normalized with `normalizeStoreFqdn`. Unsupported URL components are\n * rejected before the client secret is sent.\n * @param clientId - Client ID of the app.\n * @param clientSecret - Client secret of the app.\n * @returns The access token for the Admin API.\n */\nexport async function ensureAuthenticatedAdminAsApp(\n storeFqdn: string,\n clientId: string,\n clientSecret: string,\n): Promise<AdminSession> {\n const normalizedStoreFqdn = normalizeStoreFqdn(storeFqdn)\n const bodyData = {\n client_id: clientId,\n client_secret: clientSecret,\n grant_type: 'client_credentials',\n }\n const tokenResponse = await shopifyFetch(\n `https://${normalizedStoreFqdn}/admin/oauth/access_token`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(bodyData),\n },\n 'slow-request',\n )\n\n const body = await tokenResponse.text()\n\n if (tokenResponse.status === 400) {\n if (body.includes('app_not_installed')) {\n throw new AbortError(\n outputContent`App is not installed on ${outputToken.green(\n normalizedStoreFqdn,\n )}. Try running ${outputToken.genericShellCommand(`shopify app dev`)} to connect your app to the shop.`,\n )\n }\n throw new AbortError(\n `Failed to get access token for app ${clientId} on store ${normalizedStoreFqdn}: ${tokenResponse.statusText}`,\n )\n }\n try {\n const tokenJson = JSON.parse(body) as {access_token: string}\n return {token: tokenJson.access_token, storeFqdn: normalizedStoreFqdn}\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new AbortError(\n `Received invalid response from admin authentication service (HTTP ${tokenResponse.status}).`,\n 'The response could not be parsed as JSON. The service may be temporarily unavailable. Please try again.',\n )\n }\n throw error\n }\n}\n"]}
@@ -8,6 +8,8 @@ export interface ExecOptions {
8
8
  stderr?: Writable | 'inherit';
9
9
  stdio?: 'inherit';
10
10
  input?: string;
11
+ /** Whether to remove the final newline from captured stdout/stderr (default: true). */
12
+ stripFinalNewline?: boolean;
11
13
  signal?: AbortSignal;
12
14
  externalErrorHandler?: (error: unknown) => Promise<void>;
13
15
  background?: boolean;
@@ -42,6 +44,7 @@ export interface CaptureOutputResult {
42
44
  /**
43
45
  * Runs a command asynchronously and returns stdout, stderr, and exit code.
44
46
  * Unlike captureOutput, this function does NOT throw on non-zero exit codes.
47
+ * Startup failures and signal termination still throw an ExternalError.
45
48
  *
46
49
  * @param command - Command to be executed.
47
50
  * @param args - Arguments to pass to the command.
@@ -61,6 +64,7 @@ export declare function captureOutputWithExitCode(command: string, args: string[
61
64
  * Runs a command string asynchronously and returns stdout, stderr, and exit code.
62
65
  * Parses the command string into command and arguments (handles quoted strings).
63
66
  * Unlike captureOutput, this function does NOT throw on non-zero exit codes.
67
+ * Startup failures and signal termination still throw an ExternalError.
64
68
  *
65
69
  * @param command - Full command string to be executed (e.g., 'ls -la "my folder"').
66
70
  * @param options - Optional settings for how to run the command.