@sanity/cli 8.24.0 → 8.25.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 (78) hide show
  1. package/README.md +88 -87
  2. package/bin/run.js +7 -1
  3. package/dist/actions/dev/devAction.js +48 -44
  4. package/dist/actions/dev/devAction.js.map +1 -1
  5. package/dist/actions/init/initHelpers.js +1 -1
  6. package/dist/actions/init/initHelpers.js.map +1 -1
  7. package/dist/actions/init/initStudio.js +1 -1
  8. package/dist/actions/init/initStudio.js.map +1 -1
  9. package/dist/actions/init/project/getOrCreateDataset.js +2 -7
  10. package/dist/actions/init/project/getOrCreateDataset.js.map +1 -1
  11. package/dist/commands/api.js +1 -1
  12. package/dist/commands/api.js.map +1 -1
  13. package/dist/commands/assets/upload.js +1 -1
  14. package/dist/commands/assets/upload.js.map +1 -1
  15. package/dist/commands/cors/add.js +1 -1
  16. package/dist/commands/cors/add.js.map +1 -1
  17. package/dist/commands/cors/delete.js +1 -1
  18. package/dist/commands/cors/delete.js.map +1 -1
  19. package/dist/commands/cors/list.js +1 -1
  20. package/dist/commands/cors/list.js.map +1 -1
  21. package/dist/commands/datasets/alias/create.js +1 -1
  22. package/dist/commands/datasets/alias/create.js.map +1 -1
  23. package/dist/commands/datasets/import.js +0 -8
  24. package/dist/commands/datasets/import.js.map +1 -1
  25. package/dist/commands/datasets/list.js +1 -1
  26. package/dist/commands/datasets/list.js.map +1 -1
  27. package/dist/commands/documents/create.js +1 -1
  28. package/dist/commands/documents/create.js.map +1 -1
  29. package/dist/commands/documents/delete.js +1 -1
  30. package/dist/commands/documents/delete.js.map +1 -1
  31. package/dist/commands/documents/get.js +1 -1
  32. package/dist/commands/documents/get.js.map +1 -1
  33. package/dist/commands/documents/query.js +1 -9
  34. package/dist/commands/documents/query.js.map +1 -1
  35. package/dist/commands/documents/validate.js +2 -2
  36. package/dist/commands/documents/validate.js.map +1 -1
  37. package/dist/commands/graphql/list.js +1 -1
  38. package/dist/commands/graphql/list.js.map +1 -1
  39. package/dist/commands/graphql/undeploy.js +1 -11
  40. package/dist/commands/graphql/undeploy.js.map +1 -1
  41. package/dist/commands/hooks/attempt.js +1 -1
  42. package/dist/commands/hooks/attempt.js.map +1 -1
  43. package/dist/commands/hooks/create.js +1 -1
  44. package/dist/commands/hooks/create.js.map +1 -1
  45. package/dist/commands/hooks/delete.js +1 -1
  46. package/dist/commands/hooks/delete.js.map +1 -1
  47. package/dist/commands/hooks/list.js +1 -1
  48. package/dist/commands/hooks/list.js.map +1 -1
  49. package/dist/commands/hooks/logs.js +1 -1
  50. package/dist/commands/hooks/logs.js.map +1 -1
  51. package/dist/commands/init.js +3 -5
  52. package/dist/commands/init.js.map +1 -1
  53. package/dist/commands/migrations/run.js +5 -5
  54. package/dist/commands/migrations/run.js.map +1 -1
  55. package/dist/commands/projects/unclaimed.js +6 -5
  56. package/dist/commands/projects/unclaimed.js.map +1 -1
  57. package/dist/commands/tokens/create.js +4 -4
  58. package/dist/commands/tokens/create.js.map +1 -1
  59. package/dist/commands/tokens/delete.js +2 -2
  60. package/dist/commands/tokens/delete.js.map +1 -1
  61. package/dist/commands/tokens/list.js +2 -2
  62. package/dist/commands/tokens/list.js.map +1 -1
  63. package/dist/commands/users/invite.js +1 -1
  64. package/dist/commands/users/invite.js.map +1 -1
  65. package/dist/commands/users/list.js +1 -1
  66. package/dist/commands/users/list.js.map +1 -1
  67. package/dist/exports/invokeSanityCli/index.js +1 -1
  68. package/dist/exports/invokeSanityCli/index.js.map +1 -1
  69. package/dist/hooks/prerun/injectEnvVariables.js +1 -1
  70. package/dist/hooks/prerun/injectEnvVariables.js.map +1 -1
  71. package/dist/util/sharedFlags.js +8 -4
  72. package/dist/util/sharedFlags.js.map +1 -1
  73. package/dist/util/warnOnNonProductionEnvironment.js +4 -3
  74. package/dist/util/warnOnNonProductionEnvironment.js.map +1 -1
  75. package/oclif.manifest.json +1917 -1753
  76. package/package.json +5 -5
  77. package/dist/util/getSanityEnv.js +0 -8
  78. package/dist/util/getSanityEnv.js.map +0 -1
package/bin/run.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import {execute, settings} from '@oclif/core'
2
+ /* eslint-disable n/no-unsupported-features/node-builtins -- Supported by the Node version guard. */
3
+ import module from 'node:module'
3
4
 
4
5
  var err = '\u001B[31m\u001B[1mERROR:\u001B[22m\u001B[39m '
5
6
  var nodeVersionParts = process.version.replace(/^v/i, '').split('.').map(Number)
@@ -21,6 +22,11 @@ if (!isSupportedNodeVersion(majorVersion, minorVersion)) {
21
22
  process.exit(1)
22
23
  }
23
24
 
25
+ module.enableCompileCache()
26
+ setTimeout(() => module.flushCompileCache(), 10 * 1000).unref()
27
+
28
+ const {execute, settings} = module.createRequire(import.meta.url)('@oclif/core')
29
+
24
30
  settings.enableAutoTranspile = false
25
31
 
26
32
  await execute({dir: import.meta.url})
@@ -1,7 +1,9 @@
1
1
  import { SANITY_CACHE_DIR } from '@sanity/cli-build/_internal/build';
2
2
  import { extractCoreAppManifest } from '@sanity/cli-build/_internal/manifest';
3
3
  import { isWorkbenchApp, isWorkbenchConfig } from '@sanity/workbench-cli';
4
+ import { startWorkbenchDev, watchForWorkbenchOptIn } from '@sanity/workbench-cli/dev';
4
5
  import { checkForDeprecatedAppId } from '../../util/appId.js';
6
+ import { determineIsApp } from '../../util/determineIsApp.js';
5
7
  import { getSharedServerConfig } from '../../util/getSharedServerConfig.js';
6
8
  import { resolveReactStrictMode } from '../../util/resolveReactStrictMode.js';
7
9
  import { extractStudioManifest } from '../manifest/extractStudioManifest.js';
@@ -10,10 +12,34 @@ import { startStudioDevServer } from './servers/startStudioDevServer.js';
10
12
  const noop = async ()=>{};
11
13
  /**
12
14
  * Entry point for `sanity dev`. A plain studio/app starts a single dev server, as
13
- * before workbench existed. A workbench app (via `defineApplication`) delegates to
15
+ * before workbench existed, and switches to the workbench if `sanity.cli.*` opts
16
+ * in while it runs. A workbench app (via `defineApplication`) delegates to
14
17
  * `@sanity/workbench-cli`, injecting the CLI-domain pieces (app server, manifest
15
- * extraction, app id) and loading the package lazily so plain projects never do.
18
+ * extraction, app id).
16
19
  */ export async function devAction(options) {
20
+ const { cliConfig, output, workDir } = options;
21
+ // A workbench app (an app to render) or a workbench config (config-only, like
22
+ // the media library, which needs the workbench to render it) both delegate.
23
+ if (isWorkbenchApp(cliConfig?.app) || isWorkbenchConfig(cliConfig?.app)) {
24
+ return startWorkbench(options);
25
+ }
26
+ // Plain non-workbench studio/app: one dev server announcing its own URL.
27
+ const result = await (options.isApp ? startAppDevServer : startStudioDevServer)(options);
28
+ if (!result.started) return {
29
+ close: noop
30
+ };
31
+ return watchForWorkbenchOptIn({
32
+ closePlainServer: result.close,
33
+ output,
34
+ startWorkbench: (freshConfig)=>startWorkbench({
35
+ ...options,
36
+ cliConfig: freshConfig,
37
+ isApp: determineIsApp(freshConfig)
38
+ }),
39
+ workDir
40
+ });
41
+ }
42
+ function startWorkbench(options) {
17
43
  const { cliConfig, flags, isApp, output, workDir } = options;
18
44
  const { httpHost, httpPort } = getSharedServerConfig({
19
45
  cliConfig,
@@ -23,50 +49,28 @@ const noop = async ()=>{};
23
49
  },
24
50
  workDir
25
51
  });
26
- // The app/studio server, parameterized per call; `announceUrl` is false when
27
- // the workbench announces the URL on its behalf.
28
- const startAppServer = (params)=>(isApp ? startAppDevServer : startStudioDevServer)({
29
- ...options,
30
- announceUrl: params.announceUrl,
31
- cliConfig: params.cliConfig,
32
- httpPort: params.httpPort
33
- });
34
- // A workbench app (an app to render) or a workbench config (config-only, like
35
- // the media library, which needs the workbench to render it) both delegate.
36
- if (isWorkbenchApp(cliConfig?.app) || isWorkbenchConfig(cliConfig?.app)) {
37
- // Lazy so a non-workbench `sanity dev` never loads the package. `doImport`
38
- // is path-based and doesn't apply to a bare specifier.
39
- // eslint-disable-next-line no-restricted-syntax
40
- const { startWorkbenchDev } = await import('@sanity/workbench-cli/dev');
41
- return startWorkbenchDev({
42
- cacheDir: `${SANITY_CACHE_DIR}/vite`,
43
- checkForDeprecatedAppId: ()=>checkForDeprecatedAppId({
44
- cliConfig,
45
- output
46
- }),
47
- cliConfig,
48
- extractManifest: isApp ? ({ workDir: wd })=>extractCoreAppManifest({
49
- workDir: wd
50
- }) : (params)=>extractStudioManifest(params),
51
- httpHost,
52
- httpPort,
53
- isApp,
54
- output,
55
- // Runtime template needs a concrete boolean; collapse an unset config to off.
56
- reactStrictMode: resolveReactStrictMode(cliConfig) ?? false,
57
- startAppServer,
58
- workDir
59
- });
60
- }
61
- // Plain non-workbench studio/app: one dev server announcing its own URL.
62
- const result = await startAppServer({
63
- announceUrl: true,
52
+ return startWorkbenchDev({
53
+ cacheDir: `${SANITY_CACHE_DIR}/vite`,
54
+ checkForDeprecatedAppId: ()=>checkForDeprecatedAppId({
55
+ cliConfig,
56
+ output
57
+ }),
64
58
  cliConfig,
65
- httpPort
59
+ extractManifest: isApp ? ({ workDir: wd })=>extractCoreAppManifest({
60
+ workDir: wd
61
+ }) : (params)=>extractStudioManifest(params),
62
+ httpHost,
63
+ httpPort,
64
+ isApp,
65
+ output,
66
+ // Runtime template needs a concrete boolean; collapse an unset config to off.
67
+ reactStrictMode: resolveReactStrictMode(cliConfig) ?? false,
68
+ startAppServer: (params)=>(isApp ? startAppDevServer : startStudioDevServer)({
69
+ ...options,
70
+ ...params
71
+ }),
72
+ workDir
66
73
  });
67
- return {
68
- close: result.started ? result.close : noop
69
- };
70
74
  }
71
75
 
72
76
  //# sourceMappingURL=devAction.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/dev/devAction.ts"],"sourcesContent":["import {SANITY_CACHE_DIR} from '@sanity/cli-build/_internal/build'\nimport {extractCoreAppManifest} from '@sanity/cli-build/_internal/manifest'\nimport {type CliConfig} from '@sanity/cli-core'\nimport {isWorkbenchApp, isWorkbenchConfig} from '@sanity/workbench-cli'\n\nimport {checkForDeprecatedAppId} from '../../util/appId.js'\nimport {getSharedServerConfig} from '../../util/getSharedServerConfig.js'\nimport {resolveReactStrictMode} from '../../util/resolveReactStrictMode.js'\nimport {extractStudioManifest} from '../manifest/extractStudioManifest.js'\nimport {startAppDevServer} from './servers/startAppDevServer.js'\nimport {startStudioDevServer} from './servers/startStudioDevServer.js'\nimport {type DevActionOptions} from './types.js'\n\nconst noop = async () => {}\n\n/**\n * Entry point for `sanity dev`. A plain studio/app starts a single dev server, as\n * before workbench existed. A workbench app (via `defineApplication`) delegates to\n * `@sanity/workbench-cli`, injecting the CLI-domain pieces (app server, manifest\n * extraction, app id) and loading the package lazily so plain projects never do.\n */\nexport async function devAction(options: DevActionOptions): Promise<{close: () => Promise<void>}> {\n const {cliConfig, flags, isApp, output, workDir} = options\n\n const {httpHost, httpPort} = getSharedServerConfig({\n cliConfig,\n flags: {host: flags.host, port: flags.port},\n workDir,\n })\n\n // The app/studio server, parameterized per call; `announceUrl` is false when\n // the workbench announces the URL on its behalf.\n const startAppServer = (params: {announceUrl: boolean; cliConfig: CliConfig; httpPort: number}) =>\n (isApp ? startAppDevServer : startStudioDevServer)({\n ...options,\n announceUrl: params.announceUrl,\n cliConfig: params.cliConfig,\n httpPort: params.httpPort,\n })\n\n // A workbench app (an app to render) or a workbench config (config-only, like\n // the media library, which needs the workbench to render it) both delegate.\n if (isWorkbenchApp(cliConfig?.app) || isWorkbenchConfig(cliConfig?.app)) {\n // Lazy so a non-workbench `sanity dev` never loads the package. `doImport`\n // is path-based and doesn't apply to a bare specifier.\n // eslint-disable-next-line no-restricted-syntax\n const {startWorkbenchDev} = await import('@sanity/workbench-cli/dev')\n return startWorkbenchDev({\n cacheDir: `${SANITY_CACHE_DIR}/vite`,\n checkForDeprecatedAppId: () => checkForDeprecatedAppId({cliConfig, output}),\n cliConfig,\n extractManifest: isApp\n ? ({workDir: wd}) => extractCoreAppManifest({workDir: wd})\n : (params) => extractStudioManifest(params),\n httpHost,\n httpPort,\n isApp,\n output,\n // Runtime template needs a concrete boolean; collapse an unset config to off.\n reactStrictMode: resolveReactStrictMode(cliConfig) ?? false,\n startAppServer,\n workDir,\n })\n }\n\n // Plain non-workbench studio/app: one dev server announcing its own URL.\n const result = await startAppServer({announceUrl: true, cliConfig, httpPort})\n return {close: result.started ? result.close : noop}\n}\n"],"names":["SANITY_CACHE_DIR","extractCoreAppManifest","isWorkbenchApp","isWorkbenchConfig","checkForDeprecatedAppId","getSharedServerConfig","resolveReactStrictMode","extractStudioManifest","startAppDevServer","startStudioDevServer","noop","devAction","options","cliConfig","flags","isApp","output","workDir","httpHost","httpPort","host","port","startAppServer","params","announceUrl","app","startWorkbenchDev","cacheDir","extractManifest","wd","reactStrictMode","result","close","started"],"mappings":"AAAA,SAAQA,gBAAgB,QAAO,oCAAmC;AAClE,SAAQC,sBAAsB,QAAO,uCAAsC;AAE3E,SAAQC,cAAc,EAAEC,iBAAiB,QAAO,wBAAuB;AAEvE,SAAQC,uBAAuB,QAAO,sBAAqB;AAC3D,SAAQC,qBAAqB,QAAO,sCAAqC;AACzE,SAAQC,sBAAsB,QAAO,uCAAsC;AAC3E,SAAQC,qBAAqB,QAAO,uCAAsC;AAC1E,SAAQC,iBAAiB,QAAO,iCAAgC;AAChE,SAAQC,oBAAoB,QAAO,oCAAmC;AAGtE,MAAMC,OAAO,WAAa;AAE1B;;;;;CAKC,GACD,OAAO,eAAeC,UAAUC,OAAyB;IACvD,MAAM,EAACC,SAAS,EAAEC,KAAK,EAAEC,KAAK,EAAEC,MAAM,EAAEC,OAAO,EAAC,GAAGL;IAEnD,MAAM,EAACM,QAAQ,EAAEC,QAAQ,EAAC,GAAGd,sBAAsB;QACjDQ;QACAC,OAAO;YAACM,MAAMN,MAAMM,IAAI;YAAEC,MAAMP,MAAMO,IAAI;QAAA;QAC1CJ;IACF;IAEA,6EAA6E;IAC7E,iDAAiD;IACjD,MAAMK,iBAAiB,CAACC,SACtB,AAACR,CAAAA,QAAQP,oBAAoBC,oBAAmB,EAAG;YACjD,GAAGG,OAAO;YACVY,aAAaD,OAAOC,WAAW;YAC/BX,WAAWU,OAAOV,SAAS;YAC3BM,UAAUI,OAAOJ,QAAQ;QAC3B;IAEF,8EAA8E;IAC9E,4EAA4E;IAC5E,IAAIjB,eAAeW,WAAWY,QAAQtB,kBAAkBU,WAAWY,MAAM;QACvE,2EAA2E;QAC3E,uDAAuD;QACvD,gDAAgD;QAChD,MAAM,EAACC,iBAAiB,EAAC,GAAG,MAAM,MAAM,CAAC;QACzC,OAAOA,kBAAkB;YACvBC,UAAU,GAAG3B,iBAAiB,KAAK,CAAC;YACpCI,yBAAyB,IAAMA,wBAAwB;oBAACS;oBAAWG;gBAAM;YACzEH;YACAe,iBAAiBb,QACb,CAAC,EAACE,SAASY,EAAE,EAAC,GAAK5B,uBAAuB;oBAACgB,SAASY;gBAAE,KACtD,CAACN,SAAWhB,sBAAsBgB;YACtCL;YACAC;YACAJ;YACAC;YACA,8EAA8E;YAC9Ec,iBAAiBxB,uBAAuBO,cAAc;YACtDS;YACAL;QACF;IACF;IAEA,yEAAyE;IACzE,MAAMc,SAAS,MAAMT,eAAe;QAACE,aAAa;QAAMX;QAAWM;IAAQ;IAC3E,OAAO;QAACa,OAAOD,OAAOE,OAAO,GAAGF,OAAOC,KAAK,GAAGtB;IAAI;AACrD"}
1
+ {"version":3,"sources":["../../../src/actions/dev/devAction.ts"],"sourcesContent":["import {SANITY_CACHE_DIR} from '@sanity/cli-build/_internal/build'\nimport {extractCoreAppManifest} from '@sanity/cli-build/_internal/manifest'\nimport {isWorkbenchApp, isWorkbenchConfig} from '@sanity/workbench-cli'\nimport {startWorkbenchDev, watchForWorkbenchOptIn} from '@sanity/workbench-cli/dev'\n\nimport {checkForDeprecatedAppId} from '../../util/appId.js'\nimport {determineIsApp} from '../../util/determineIsApp.js'\nimport {getSharedServerConfig} from '../../util/getSharedServerConfig.js'\nimport {resolveReactStrictMode} from '../../util/resolveReactStrictMode.js'\nimport {extractStudioManifest} from '../manifest/extractStudioManifest.js'\nimport {startAppDevServer} from './servers/startAppDevServer.js'\nimport {startStudioDevServer} from './servers/startStudioDevServer.js'\nimport {type DevActionOptions} from './types.js'\n\nconst noop = async () => {}\n\n/**\n * Entry point for `sanity dev`. A plain studio/app starts a single dev server, as\n * before workbench existed, and switches to the workbench if `sanity.cli.*` opts\n * in while it runs. A workbench app (via `defineApplication`) delegates to\n * `@sanity/workbench-cli`, injecting the CLI-domain pieces (app server, manifest\n * extraction, app id).\n */\nexport async function devAction(options: DevActionOptions): Promise<{close: () => Promise<void>}> {\n const {cliConfig, output, workDir} = options\n\n // A workbench app (an app to render) or a workbench config (config-only, like\n // the media library, which needs the workbench to render it) both delegate.\n if (isWorkbenchApp(cliConfig?.app) || isWorkbenchConfig(cliConfig?.app)) {\n return startWorkbench(options)\n }\n\n // Plain non-workbench studio/app: one dev server announcing its own URL.\n const result = await (options.isApp ? startAppDevServer : startStudioDevServer)(options)\n if (!result.started) return {close: noop}\n\n return watchForWorkbenchOptIn({\n closePlainServer: result.close,\n output,\n startWorkbench: (freshConfig) =>\n startWorkbench({...options, cliConfig: freshConfig, isApp: determineIsApp(freshConfig)}),\n workDir,\n })\n}\n\nfunction startWorkbench(options: DevActionOptions): Promise<{close: () => Promise<void>}> {\n const {cliConfig, flags, isApp, output, workDir} = options\n const {httpHost, httpPort} = getSharedServerConfig({\n cliConfig,\n flags: {host: flags.host, port: flags.port},\n workDir,\n })\n\n return startWorkbenchDev({\n cacheDir: `${SANITY_CACHE_DIR}/vite`,\n checkForDeprecatedAppId: () => checkForDeprecatedAppId({cliConfig, output}),\n cliConfig,\n extractManifest: isApp\n ? ({workDir: wd}) => extractCoreAppManifest({workDir: wd})\n : (params) => extractStudioManifest(params),\n httpHost,\n httpPort,\n isApp,\n output,\n // Runtime template needs a concrete boolean; collapse an unset config to off.\n reactStrictMode: resolveReactStrictMode(cliConfig) ?? false,\n startAppServer: (params) =>\n (isApp ? startAppDevServer : startStudioDevServer)({...options, ...params}),\n workDir,\n })\n}\n"],"names":["SANITY_CACHE_DIR","extractCoreAppManifest","isWorkbenchApp","isWorkbenchConfig","startWorkbenchDev","watchForWorkbenchOptIn","checkForDeprecatedAppId","determineIsApp","getSharedServerConfig","resolveReactStrictMode","extractStudioManifest","startAppDevServer","startStudioDevServer","noop","devAction","options","cliConfig","output","workDir","app","startWorkbench","result","isApp","started","close","closePlainServer","freshConfig","flags","httpHost","httpPort","host","port","cacheDir","extractManifest","wd","params","reactStrictMode","startAppServer"],"mappings":"AAAA,SAAQA,gBAAgB,QAAO,oCAAmC;AAClE,SAAQC,sBAAsB,QAAO,uCAAsC;AAC3E,SAAQC,cAAc,EAAEC,iBAAiB,QAAO,wBAAuB;AACvE,SAAQC,iBAAiB,EAAEC,sBAAsB,QAAO,4BAA2B;AAEnF,SAAQC,uBAAuB,QAAO,sBAAqB;AAC3D,SAAQC,cAAc,QAAO,+BAA8B;AAC3D,SAAQC,qBAAqB,QAAO,sCAAqC;AACzE,SAAQC,sBAAsB,QAAO,uCAAsC;AAC3E,SAAQC,qBAAqB,QAAO,uCAAsC;AAC1E,SAAQC,iBAAiB,QAAO,iCAAgC;AAChE,SAAQC,oBAAoB,QAAO,oCAAmC;AAGtE,MAAMC,OAAO,WAAa;AAE1B;;;;;;CAMC,GACD,OAAO,eAAeC,UAAUC,OAAyB;IACvD,MAAM,EAACC,SAAS,EAAEC,MAAM,EAAEC,OAAO,EAAC,GAAGH;IAErC,8EAA8E;IAC9E,4EAA4E;IAC5E,IAAIb,eAAec,WAAWG,QAAQhB,kBAAkBa,WAAWG,MAAM;QACvE,OAAOC,eAAeL;IACxB;IAEA,yEAAyE;IACzE,MAAMM,SAAS,MAAM,AAACN,CAAAA,QAAQO,KAAK,GAAGX,oBAAoBC,oBAAmB,EAAGG;IAChF,IAAI,CAACM,OAAOE,OAAO,EAAE,OAAO;QAACC,OAAOX;IAAI;IAExC,OAAOR,uBAAuB;QAC5BoB,kBAAkBJ,OAAOG,KAAK;QAC9BP;QACAG,gBAAgB,CAACM,cACfN,eAAe;gBAAC,GAAGL,OAAO;gBAAEC,WAAWU;gBAAaJ,OAAOf,eAAemB;YAAY;QACxFR;IACF;AACF;AAEA,SAASE,eAAeL,OAAyB;IAC/C,MAAM,EAACC,SAAS,EAAEW,KAAK,EAAEL,KAAK,EAAEL,MAAM,EAAEC,OAAO,EAAC,GAAGH;IACnD,MAAM,EAACa,QAAQ,EAAEC,QAAQ,EAAC,GAAGrB,sBAAsB;QACjDQ;QACAW,OAAO;YAACG,MAAMH,MAAMG,IAAI;YAAEC,MAAMJ,MAAMI,IAAI;QAAA;QAC1Cb;IACF;IAEA,OAAOd,kBAAkB;QACvB4B,UAAU,GAAGhC,iBAAiB,KAAK,CAAC;QACpCM,yBAAyB,IAAMA,wBAAwB;gBAACU;gBAAWC;YAAM;QACzED;QACAiB,iBAAiBX,QACb,CAAC,EAACJ,SAASgB,EAAE,EAAC,GAAKjC,uBAAuB;gBAACiB,SAASgB;YAAE,KACtD,CAACC,SAAWzB,sBAAsByB;QACtCP;QACAC;QACAP;QACAL;QACA,8EAA8E;QAC9EmB,iBAAiB3B,uBAAuBO,cAAc;QACtDqB,gBAAgB,CAACF,SACf,AAACb,CAAAA,QAAQX,oBAAoBC,oBAAmB,EAAG;gBAAC,GAAGG,OAAO;gBAAE,GAAGoB,MAAM;YAAA;QAC3EjB;IACF;AACF"}
@@ -1,4 +1,4 @@
1
- import { getSanityEnv } from '../../util/getSanityEnv.js';
1
+ import { getSanityEnv } from '@sanity/cli-core/util';
2
2
  import { createOrAppendEnvVars } from './env/createOrAppendEnvVars.js';
3
3
  import { fetchPostInitPrompt } from './fetchPostInitPrompt.js';
4
4
  export function shouldPrompt(unattended, flagValue) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/init/initHelpers.ts"],"sourcesContent":["import {getSanityEnv} from '../../util/getSanityEnv.js'\nimport {type EditorName} from '../mcp/editorConfigs.js'\nimport {createOrAppendEnvVars} from './env/createOrAppendEnvVars.js'\nimport {fetchPostInitPrompt} from './fetchPostInitPrompt.js'\nimport {type InitContext} from './types.js'\n\nexport function shouldPrompt(unattended: boolean, flagValue: unknown): boolean {\n return !unattended && flagValue === undefined\n}\n\nexport function flagOrDefault(flagValue: boolean | undefined, defaultValue: boolean): boolean {\n return typeof flagValue === 'boolean' ? flagValue : defaultValue\n}\n\nexport async function getPostInitMCPPrompt(editorsNames: EditorName[]): Promise<string> {\n return fetchPostInitPrompt(new Intl.ListFormat('en').format(editorsNames))\n}\n\nexport async function writeStagingEnvIfNeeded(\n output: InitContext['output'],\n outputPath: string,\n): Promise<void> {\n const sanityEnv = getSanityEnv()\n if (sanityEnv === 'production') return\n\n await createOrAppendEnvVars({\n envVars: {INTERNAL_ENV: sanityEnv},\n filename: '.env',\n framework: null,\n log: false,\n output,\n outputPath,\n })\n}\n"],"names":["getSanityEnv","createOrAppendEnvVars","fetchPostInitPrompt","shouldPrompt","unattended","flagValue","undefined","flagOrDefault","defaultValue","getPostInitMCPPrompt","editorsNames","Intl","ListFormat","format","writeStagingEnvIfNeeded","output","outputPath","sanityEnv","envVars","INTERNAL_ENV","filename","framework","log"],"mappings":"AAAA,SAAQA,YAAY,QAAO,6BAA4B;AAEvD,SAAQC,qBAAqB,QAAO,iCAAgC;AACpE,SAAQC,mBAAmB,QAAO,2BAA0B;AAG5D,OAAO,SAASC,aAAaC,UAAmB,EAAEC,SAAkB;IAClE,OAAO,CAACD,cAAcC,cAAcC;AACtC;AAEA,OAAO,SAASC,cAAcF,SAA8B,EAAEG,YAAqB;IACjF,OAAO,OAAOH,cAAc,YAAYA,YAAYG;AACtD;AAEA,OAAO,eAAeC,qBAAqBC,YAA0B;IACnE,OAAOR,oBAAoB,IAAIS,KAAKC,UAAU,CAAC,MAAMC,MAAM,CAACH;AAC9D;AAEA,OAAO,eAAeI,wBACpBC,MAA6B,EAC7BC,UAAkB;IAElB,MAAMC,YAAYjB;IAClB,IAAIiB,cAAc,cAAc;IAEhC,MAAMhB,sBAAsB;QAC1BiB,SAAS;YAACC,cAAcF;QAAS;QACjCG,UAAU;QACVC,WAAW;QACXC,KAAK;QACLP;QACAC;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/init/initHelpers.ts"],"sourcesContent":["import {getSanityEnv} from '@sanity/cli-core/util'\n\nimport {type EditorName} from '../mcp/editorConfigs.js'\nimport {createOrAppendEnvVars} from './env/createOrAppendEnvVars.js'\nimport {fetchPostInitPrompt} from './fetchPostInitPrompt.js'\nimport {type InitContext} from './types.js'\n\nexport function shouldPrompt(unattended: boolean, flagValue: unknown): boolean {\n return !unattended && flagValue === undefined\n}\n\nexport function flagOrDefault(flagValue: boolean | undefined, defaultValue: boolean): boolean {\n return typeof flagValue === 'boolean' ? flagValue : defaultValue\n}\n\nexport async function getPostInitMCPPrompt(editorsNames: EditorName[]): Promise<string> {\n return fetchPostInitPrompt(new Intl.ListFormat('en').format(editorsNames))\n}\n\nexport async function writeStagingEnvIfNeeded(\n output: InitContext['output'],\n outputPath: string,\n): Promise<void> {\n const sanityEnv = getSanityEnv()\n if (sanityEnv === 'production') return\n\n await createOrAppendEnvVars({\n envVars: {INTERNAL_ENV: sanityEnv},\n filename: '.env',\n framework: null,\n log: false,\n output,\n outputPath,\n })\n}\n"],"names":["getSanityEnv","createOrAppendEnvVars","fetchPostInitPrompt","shouldPrompt","unattended","flagValue","undefined","flagOrDefault","defaultValue","getPostInitMCPPrompt","editorsNames","Intl","ListFormat","format","writeStagingEnvIfNeeded","output","outputPath","sanityEnv","envVars","INTERNAL_ENV","filename","framework","log"],"mappings":"AAAA,SAAQA,YAAY,QAAO,wBAAuB;AAGlD,SAAQC,qBAAqB,QAAO,iCAAgC;AACpE,SAAQC,mBAAmB,QAAO,2BAA0B;AAG5D,OAAO,SAASC,aAAaC,UAAmB,EAAEC,SAAkB;IAClE,OAAO,CAACD,cAAcC,cAAcC;AACtC;AAEA,OAAO,SAASC,cAAcF,SAA8B,EAAEG,YAAqB;IACjF,OAAO,OAAOH,cAAc,YAAYA,YAAYG;AACtD;AAEA,OAAO,eAAeC,qBAAqBC,YAA0B;IACnE,OAAOR,oBAAoB,IAAIS,KAAKC,UAAU,CAAC,MAAMC,MAAM,CAACH;AAC9D;AAEA,OAAO,eAAeI,wBACpBC,MAA6B,EAC7BC,UAAkB;IAElB,MAAMC,YAAYjB;IAClB,IAAIiB,cAAc,cAAc;IAEhC,MAAMhB,sBAAsB;QAC1BiB,SAAS;YAACC,cAAcF;QAAS;QACjCG,UAAU;QACVC,WAAW;QACXC,KAAK;QACLP;QACAC;IACF;AACF"}
@@ -64,7 +64,7 @@ export async function initStudio({ datasetName, defaults, displayName, isFirstPr
64
64
  const { ImportDatasetCommand } = await import('../../commands/datasets/import.js');
65
65
  await ImportDatasetCommand.run([
66
66
  resolvedTemplate.datasetUrl,
67
- '--project-id',
67
+ '--project',
68
68
  projectId,
69
69
  '--dataset',
70
70
  datasetName,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/init/initStudio.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {getCliToken, type Output, subdebug, type TelemetryUserProperties} from '@sanity/cli-core'\nimport {confirm} from '@sanity/cli-core/ux'\nimport {type TelemetryTrace} from '@sanity/telemetry'\n\nimport {updateProjectInitializedAt} from '../../services/projects.js'\nimport {type InitStepResult} from '../../telemetry/init.telemetry.js'\nimport {getInstallCommand} from '../../util/packageManager/installPackages.js'\nimport {type PackageManager} from '../../util/packageManager/packageManagerChoice.js'\nimport {type EditorName} from '../mcp/editorConfigs.js'\nimport {InitError} from './initError.js'\nimport {getPostInitMCPPrompt} from './initHelpers.js'\nimport {type RepoInfo} from './remoteTemplate.js'\nimport {scaffoldAndInstall, selectTemplate} from './scaffoldTemplate.js'\nimport {type InitOptions} from './types.js'\n\nconst debug = subdebug('init')\n\nasync function promptForDatasetImport(message?: string): Promise<boolean> {\n return confirm({\n default: true,\n message: message || 'This template includes a sample dataset, would you like to use it?',\n })\n}\n\nexport async function initStudio({\n datasetName,\n defaults,\n displayName,\n isFirstProject,\n mcpConfigured,\n options,\n organizationId,\n output,\n outputPath,\n preclaim = false,\n projectId,\n remoteTemplateInfo,\n sluggedName,\n trace,\n workbench,\n workDir,\n}: {\n datasetName: string\n defaults: {projectName: string}\n displayName: string\n isFirstProject: boolean\n mcpConfigured: EditorName[]\n options: InitOptions\n organizationId: string | undefined\n output: Output\n outputPath: string\n preclaim?: boolean\n projectId: string\n remoteTemplateInfo: RepoInfo | undefined\n sluggedName: string\n trace: TelemetryTrace<TelemetryUserProperties, InitStepResult>\n workbench: boolean\n workDir: string\n}): Promise<void> {\n const {importDataset, install, unattended} = options\n const {\n template: resolvedTemplate,\n templateName,\n useTypeScript,\n } = await selectTemplate({\n options,\n remoteTemplateInfo,\n trace,\n })\n\n if (!remoteTemplateInfo && !resolvedTemplate) {\n throw new InitError(`Template \"${templateName}\" not found`, 1)\n }\n\n // If the template has a sample dataset, prompt the user whether or not we should import it\n const shouldImport =\n resolvedTemplate?.datasetUrl &&\n (importDataset ??\n (!unattended && (await promptForDatasetImport(resolvedTemplate.importPrompt))))\n\n trace.log({\n selectedOption: shouldImport ? 'yes' : 'no',\n step: 'importTemplateDataset',\n })\n\n if (!preclaim) {\n try {\n await updateProjectInitializedAt(projectId)\n } catch (err) {\n // Non-critical update\n debug('Failed to update cliInitializedAt metadata', err)\n }\n }\n\n const {pkgManager} = await scaffoldAndInstall({\n datasetName,\n defaults,\n displayName,\n options,\n organizationId,\n output,\n outputPath,\n projectId,\n remoteTemplateInfo,\n sluggedName,\n templateName,\n trace,\n useTypeScript,\n workbench,\n workDir,\n })\n\n // Prompt for dataset import (if a dataset is defined)\n if (shouldImport && resolvedTemplate?.datasetUrl) {\n const token = await getCliToken()\n if (!token) {\n throw new InitError('Authentication required to import dataset', 1)\n }\n // eslint-disable-next-line no-restricted-syntax\n const {ImportDatasetCommand} = await import('../../commands/datasets/import.js')\n await ImportDatasetCommand.run(\n [\n resolvedTemplate.datasetUrl,\n '--project-id',\n projectId,\n '--dataset',\n datasetName,\n '--token',\n token,\n '--missing',\n ],\n {\n root: outputPath,\n },\n )\n\n output.log('')\n output.log('If you want to delete the imported data, use')\n output.log(` ${styleText('cyan', `npx sanity dataset delete ${datasetName}`)}`)\n output.log('and create a new clean dataset with')\n output.log(` ${styleText('cyan', `npx sanity dataset create <name>`)}\\n`)\n }\n\n // `sanity new` owns the combined Studio/frontend outro.\n if (preclaim) return\n\n const devCommandMap: Record<PackageManager, string> = {\n bun: 'bun dev',\n manual: 'npm run dev',\n npm: 'npm run dev',\n pnpm: 'pnpm dev',\n yarn: 'yarn dev',\n }\n const devCommand = devCommandMap[pkgManager]\n\n const isCurrentDir = outputPath === workDir\n const goToProjectDir = `\\n(${styleText('cyan', `cd ${outputPath}`)} to navigate to your new project directory)`\n\n //output for Studios here\n output.log(`\\u2705 ${styleText(['green', 'bold'], 'Success!')} Your Studio has been created.`)\n if (!isCurrentDir) output.log(goToProjectDir)\n const startupCommands = install\n ? styleText('cyan', devCommand)\n : `${styleText('cyan', getInstallCommand(pkgManager))}, then ${styleText('cyan', devCommand)}`\n output.log(\n `\\nGet started by running ${startupCommands} to launch your Studio's development server`,\n )\n if (mcpConfigured && mcpConfigured.length > 0) {\n const message = await getPostInitMCPPrompt(mcpConfigured)\n output.log(`\\n${message}`)\n output.log(`\\nLearn more: ${styleText('cyan', 'https://mcp.sanity.io')}`)\n output.log(\n `\\nHave feedback? Tell us in the community: ${styleText('cyan', 'https://www.sanity.io/community/join')}`,\n )\n }\n output.log('\\n')\n output.log(`Other helpful commands:`)\n output.log(`npx sanity docs browse to open the documentation in a browser`)\n output.log(`npx sanity manage to open the project settings in a browser`)\n output.log(`npx sanity help to explore the CLI manual`)\n\n if (isFirstProject) {\n trace.log({selectedOption: 'yes', step: 'sendCommunityInvite'})\n\n const DISCORD_INVITE_LINK = 'https://www.sanity.io/community/join'\n\n output.log(`\\nJoin the Sanity community: ${styleText('cyan', DISCORD_INVITE_LINK)}`)\n output.log('We look forward to seeing you there!\\n')\n }\n}\n"],"names":["styleText","getCliToken","subdebug","confirm","updateProjectInitializedAt","getInstallCommand","InitError","getPostInitMCPPrompt","scaffoldAndInstall","selectTemplate","debug","promptForDatasetImport","message","default","initStudio","datasetName","defaults","displayName","isFirstProject","mcpConfigured","options","organizationId","output","outputPath","preclaim","projectId","remoteTemplateInfo","sluggedName","trace","workbench","workDir","importDataset","install","unattended","template","resolvedTemplate","templateName","useTypeScript","shouldImport","datasetUrl","importPrompt","log","selectedOption","step","err","pkgManager","token","ImportDatasetCommand","run","root","devCommandMap","bun","manual","npm","pnpm","yarn","devCommand","isCurrentDir","goToProjectDir","startupCommands","length","DISCORD_INVITE_LINK"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/init/initStudio.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {getCliToken, type Output, subdebug, type TelemetryUserProperties} from '@sanity/cli-core'\nimport {confirm} from '@sanity/cli-core/ux'\nimport {type TelemetryTrace} from '@sanity/telemetry'\n\nimport {updateProjectInitializedAt} from '../../services/projects.js'\nimport {type InitStepResult} from '../../telemetry/init.telemetry.js'\nimport {getInstallCommand} from '../../util/packageManager/installPackages.js'\nimport {type PackageManager} from '../../util/packageManager/packageManagerChoice.js'\nimport {type EditorName} from '../mcp/editorConfigs.js'\nimport {InitError} from './initError.js'\nimport {getPostInitMCPPrompt} from './initHelpers.js'\nimport {type RepoInfo} from './remoteTemplate.js'\nimport {scaffoldAndInstall, selectTemplate} from './scaffoldTemplate.js'\nimport {type InitOptions} from './types.js'\n\nconst debug = subdebug('init')\n\nasync function promptForDatasetImport(message?: string): Promise<boolean> {\n return confirm({\n default: true,\n message: message || 'This template includes a sample dataset, would you like to use it?',\n })\n}\n\nexport async function initStudio({\n datasetName,\n defaults,\n displayName,\n isFirstProject,\n mcpConfigured,\n options,\n organizationId,\n output,\n outputPath,\n preclaim = false,\n projectId,\n remoteTemplateInfo,\n sluggedName,\n trace,\n workbench,\n workDir,\n}: {\n datasetName: string\n defaults: {projectName: string}\n displayName: string\n isFirstProject: boolean\n mcpConfigured: EditorName[]\n options: InitOptions\n organizationId: string | undefined\n output: Output\n outputPath: string\n preclaim?: boolean\n projectId: string\n remoteTemplateInfo: RepoInfo | undefined\n sluggedName: string\n trace: TelemetryTrace<TelemetryUserProperties, InitStepResult>\n workbench: boolean\n workDir: string\n}): Promise<void> {\n const {importDataset, install, unattended} = options\n const {\n template: resolvedTemplate,\n templateName,\n useTypeScript,\n } = await selectTemplate({\n options,\n remoteTemplateInfo,\n trace,\n })\n\n if (!remoteTemplateInfo && !resolvedTemplate) {\n throw new InitError(`Template \"${templateName}\" not found`, 1)\n }\n\n // If the template has a sample dataset, prompt the user whether or not we should import it\n const shouldImport =\n resolvedTemplate?.datasetUrl &&\n (importDataset ??\n (!unattended && (await promptForDatasetImport(resolvedTemplate.importPrompt))))\n\n trace.log({\n selectedOption: shouldImport ? 'yes' : 'no',\n step: 'importTemplateDataset',\n })\n\n if (!preclaim) {\n try {\n await updateProjectInitializedAt(projectId)\n } catch (err) {\n // Non-critical update\n debug('Failed to update cliInitializedAt metadata', err)\n }\n }\n\n const {pkgManager} = await scaffoldAndInstall({\n datasetName,\n defaults,\n displayName,\n options,\n organizationId,\n output,\n outputPath,\n projectId,\n remoteTemplateInfo,\n sluggedName,\n templateName,\n trace,\n useTypeScript,\n workbench,\n workDir,\n })\n\n // Prompt for dataset import (if a dataset is defined)\n if (shouldImport && resolvedTemplate?.datasetUrl) {\n const token = await getCliToken()\n if (!token) {\n throw new InitError('Authentication required to import dataset', 1)\n }\n // eslint-disable-next-line no-restricted-syntax\n const {ImportDatasetCommand} = await import('../../commands/datasets/import.js')\n await ImportDatasetCommand.run(\n [\n resolvedTemplate.datasetUrl,\n '--project',\n projectId,\n '--dataset',\n datasetName,\n '--token',\n token,\n '--missing',\n ],\n {\n root: outputPath,\n },\n )\n\n output.log('')\n output.log('If you want to delete the imported data, use')\n output.log(` ${styleText('cyan', `npx sanity dataset delete ${datasetName}`)}`)\n output.log('and create a new clean dataset with')\n output.log(` ${styleText('cyan', `npx sanity dataset create <name>`)}\\n`)\n }\n\n // `sanity new` owns the combined Studio/frontend outro.\n if (preclaim) return\n\n const devCommandMap: Record<PackageManager, string> = {\n bun: 'bun dev',\n manual: 'npm run dev',\n npm: 'npm run dev',\n pnpm: 'pnpm dev',\n yarn: 'yarn dev',\n }\n const devCommand = devCommandMap[pkgManager]\n\n const isCurrentDir = outputPath === workDir\n const goToProjectDir = `\\n(${styleText('cyan', `cd ${outputPath}`)} to navigate to your new project directory)`\n\n //output for Studios here\n output.log(`\\u2705 ${styleText(['green', 'bold'], 'Success!')} Your Studio has been created.`)\n if (!isCurrentDir) output.log(goToProjectDir)\n const startupCommands = install\n ? styleText('cyan', devCommand)\n : `${styleText('cyan', getInstallCommand(pkgManager))}, then ${styleText('cyan', devCommand)}`\n output.log(\n `\\nGet started by running ${startupCommands} to launch your Studio's development server`,\n )\n if (mcpConfigured && mcpConfigured.length > 0) {\n const message = await getPostInitMCPPrompt(mcpConfigured)\n output.log(`\\n${message}`)\n output.log(`\\nLearn more: ${styleText('cyan', 'https://mcp.sanity.io')}`)\n output.log(\n `\\nHave feedback? Tell us in the community: ${styleText('cyan', 'https://www.sanity.io/community/join')}`,\n )\n }\n output.log('\\n')\n output.log(`Other helpful commands:`)\n output.log(`npx sanity docs browse to open the documentation in a browser`)\n output.log(`npx sanity manage to open the project settings in a browser`)\n output.log(`npx sanity help to explore the CLI manual`)\n\n if (isFirstProject) {\n trace.log({selectedOption: 'yes', step: 'sendCommunityInvite'})\n\n const DISCORD_INVITE_LINK = 'https://www.sanity.io/community/join'\n\n output.log(`\\nJoin the Sanity community: ${styleText('cyan', DISCORD_INVITE_LINK)}`)\n output.log('We look forward to seeing you there!\\n')\n }\n}\n"],"names":["styleText","getCliToken","subdebug","confirm","updateProjectInitializedAt","getInstallCommand","InitError","getPostInitMCPPrompt","scaffoldAndInstall","selectTemplate","debug","promptForDatasetImport","message","default","initStudio","datasetName","defaults","displayName","isFirstProject","mcpConfigured","options","organizationId","output","outputPath","preclaim","projectId","remoteTemplateInfo","sluggedName","trace","workbench","workDir","importDataset","install","unattended","template","resolvedTemplate","templateName","useTypeScript","shouldImport","datasetUrl","importPrompt","log","selectedOption","step","err","pkgManager","token","ImportDatasetCommand","run","root","devCommandMap","bun","manual","npm","pnpm","yarn","devCommand","isCurrentDir","goToProjectDir","startupCommands","length","DISCORD_INVITE_LINK"],"mappings":"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"}
@@ -10,12 +10,6 @@ const debug = subdebug('init');
10
10
  export async function getOrCreateDataset(opts) {
11
11
  const { dataset, visibility } = opts;
12
12
  let { defaultConfig } = opts;
13
- if (dataset && opts.unattended) {
14
- return {
15
- datasetName: dataset,
16
- userAction: 'none'
17
- };
18
- }
19
13
  const [datasets, projectFeatures] = await Promise.all([
20
14
  listDatasets(opts.projectId),
21
15
  getProjectFeatures(opts.projectId)
@@ -28,6 +22,7 @@ export async function getOrCreateDataset(opts) {
28
22
  await createDataset({
29
23
  datasetName: dataset,
30
24
  forcePublic: defaultConfig,
25
+ isUnattended: opts.unattended,
31
26
  output: opts.output,
32
27
  projectFeatures,
33
28
  projectId: opts.projectId,
@@ -36,7 +31,7 @@ export async function getOrCreateDataset(opts) {
36
31
  }
37
32
  return {
38
33
  datasetName: dataset,
39
- userAction: 'none'
34
+ userAction: existing ? 'none' : 'create'
40
35
  };
41
36
  }
42
37
  if (opts.unattended) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/actions/init/project/getOrCreateDataset.ts"],"sourcesContent":["import {subdebug} from '@sanity/cli-core'\nimport {search, Separator} from '@sanity/cli-core/ux'\n\nimport {promptForDatasetName} from '../../../prompts/promptForDatasetName.js'\nimport {promptForDefaultConfig} from '../../../prompts/promptForDefaultConfig.js'\nimport {listDatasets} from '../../../services/datasets.js'\nimport {getProjectFeatures} from '../../../services/getProjectFeatures.js'\nimport {searchChoices, searchTheme} from '../../../util/searchChoices.js'\nimport {createDataset} from '../../dataset/create.js'\nimport {type InitContext} from '../types.js'\n\nconst debug = subdebug('init')\n\nexport async function getOrCreateDataset(opts: {\n dataset?: string\n defaultConfig: boolean | undefined\n displayName: string\n output: InitContext['output']\n projectId: string\n showDefaultConfigPrompt: boolean\n unattended: boolean\n visibility?: string\n}): Promise<{\n datasetName: string\n userAction: 'create' | 'none' | 'select'\n}> {\n const {dataset, visibility} = opts\n let {defaultConfig} = opts\n\n if (dataset && opts.unattended) {\n return {datasetName: dataset, userAction: 'none'}\n }\n\n const [datasets, projectFeatures] = await Promise.all([\n listDatasets(opts.projectId),\n getProjectFeatures(opts.projectId),\n ])\n\n if (dataset) {\n debug('User has specified dataset through a flag (%s)', dataset)\n const existing = datasets.find((ds) => ds.name === dataset)\n if (!existing) {\n debug('Specified dataset not found, creating it')\n await createDataset({\n datasetName: dataset,\n forcePublic: defaultConfig,\n output: opts.output,\n projectFeatures,\n projectId: opts.projectId,\n visibility,\n })\n }\n\n return {datasetName: dataset, userAction: 'none'}\n }\n\n if (opts.unattended) {\n debug('Unattended mode without --dataset, defaulting to \"production\" dataset')\n const datasetName = 'production'\n const existing = datasets.find((ds) => ds.name === datasetName)\n if (!existing) {\n await createDataset({\n datasetName,\n forcePublic: visibility === undefined,\n isUnattended: true,\n output: opts.output,\n projectFeatures,\n projectId: opts.projectId,\n visibility,\n })\n }\n return {datasetName, userAction: existing ? 'none' : 'create'}\n }\n\n if (datasets.length === 0) {\n debug('No datasets found for project, prompting for name')\n if (opts.showDefaultConfigPrompt) {\n defaultConfig = await promptForDefaultConfig()\n }\n const name = defaultConfig\n ? 'production'\n : await promptForDatasetName({\n message: 'Name of your first dataset:',\n })\n await createDataset({\n datasetName: name,\n forcePublic: defaultConfig,\n output: opts.output,\n projectFeatures,\n projectId: opts.projectId,\n visibility,\n })\n return {datasetName: name, userAction: 'create'}\n }\n\n debug(`User has ${datasets.length} dataset(s) already, showing list of choices`)\n const datasetChoices = datasets.map((dataset) => ({name: dataset.name, value: dataset.name}))\n\n const selected = await search({\n message: 'Select dataset to use',\n source: searchChoices([\n {name: 'Create new dataset', value: 'new'},\n new Separator(),\n ...datasetChoices,\n ]),\n theme: searchTheme,\n })\n\n if (selected === 'new') {\n const existingDatasetNames = datasets.map((ds) => ds.name)\n debug('User wants to create a new dataset, prompting for name')\n if (opts.showDefaultConfigPrompt && !existingDatasetNames.includes('production')) {\n defaultConfig = await promptForDefaultConfig()\n }\n\n const newDatasetName = defaultConfig\n ? 'production'\n : await promptForDatasetName(\n {\n message: 'Dataset name:',\n },\n existingDatasetNames,\n )\n await createDataset({\n datasetName: newDatasetName,\n forcePublic: defaultConfig,\n output: opts.output,\n projectFeatures,\n projectId: opts.projectId,\n visibility,\n })\n return {datasetName: newDatasetName, userAction: 'create'}\n }\n\n debug(`Returning selected dataset (${selected})`)\n return {datasetName: selected, userAction: 'select'}\n}\n"],"names":["subdebug","search","Separator","promptForDatasetName","promptForDefaultConfig","listDatasets","getProjectFeatures","searchChoices","searchTheme","createDataset","debug","getOrCreateDataset","opts","dataset","visibility","defaultConfig","unattended","datasetName","userAction","datasets","projectFeatures","Promise","all","projectId","existing","find","ds","name","forcePublic","output","undefined","isUnattended","length","showDefaultConfigPrompt","message","datasetChoices","map","value","selected","source","theme","existingDatasetNames","includes","newDatasetName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/actions/init/project/getOrCreateDataset.ts"],"sourcesContent":["import {subdebug} from '@sanity/cli-core'\nimport {search, Separator} from '@sanity/cli-core/ux'\n\nimport {promptForDatasetName} from '../../../prompts/promptForDatasetName.js'\nimport {promptForDefaultConfig} from '../../../prompts/promptForDefaultConfig.js'\nimport {listDatasets} from '../../../services/datasets.js'\nimport {getProjectFeatures} from '../../../services/getProjectFeatures.js'\nimport {searchChoices, searchTheme} from '../../../util/searchChoices.js'\nimport {createDataset} from '../../dataset/create.js'\nimport {type InitContext} from '../types.js'\n\nconst debug = subdebug('init')\n\nexport async function getOrCreateDataset(opts: {\n dataset?: string\n defaultConfig: boolean | undefined\n displayName: string\n output: InitContext['output']\n projectId: string\n showDefaultConfigPrompt: boolean\n unattended: boolean\n visibility?: string\n}): Promise<{\n datasetName: string\n userAction: 'create' | 'none' | 'select'\n}> {\n const {dataset, visibility} = opts\n let {defaultConfig} = opts\n\n const [datasets, projectFeatures] = await Promise.all([\n listDatasets(opts.projectId),\n getProjectFeatures(opts.projectId),\n ])\n\n if (dataset) {\n debug('User has specified dataset through a flag (%s)', dataset)\n const existing = datasets.find((ds) => ds.name === dataset)\n if (!existing) {\n debug('Specified dataset not found, creating it')\n await createDataset({\n datasetName: dataset,\n forcePublic: defaultConfig,\n isUnattended: opts.unattended,\n output: opts.output,\n projectFeatures,\n projectId: opts.projectId,\n visibility,\n })\n }\n\n return {datasetName: dataset, userAction: existing ? 'none' : 'create'}\n }\n\n if (opts.unattended) {\n debug('Unattended mode without --dataset, defaulting to \"production\" dataset')\n const datasetName = 'production'\n const existing = datasets.find((ds) => ds.name === datasetName)\n if (!existing) {\n await createDataset({\n datasetName,\n forcePublic: visibility === undefined,\n isUnattended: true,\n output: opts.output,\n projectFeatures,\n projectId: opts.projectId,\n visibility,\n })\n }\n return {datasetName, userAction: existing ? 'none' : 'create'}\n }\n\n if (datasets.length === 0) {\n debug('No datasets found for project, prompting for name')\n if (opts.showDefaultConfigPrompt) {\n defaultConfig = await promptForDefaultConfig()\n }\n const name = defaultConfig\n ? 'production'\n : await promptForDatasetName({\n message: 'Name of your first dataset:',\n })\n await createDataset({\n datasetName: name,\n forcePublic: defaultConfig,\n output: opts.output,\n projectFeatures,\n projectId: opts.projectId,\n visibility,\n })\n return {datasetName: name, userAction: 'create'}\n }\n\n debug(`User has ${datasets.length} dataset(s) already, showing list of choices`)\n const datasetChoices = datasets.map((dataset) => ({name: dataset.name, value: dataset.name}))\n\n const selected = await search({\n message: 'Select dataset to use',\n source: searchChoices([\n {name: 'Create new dataset', value: 'new'},\n new Separator(),\n ...datasetChoices,\n ]),\n theme: searchTheme,\n })\n\n if (selected === 'new') {\n const existingDatasetNames = datasets.map((ds) => ds.name)\n debug('User wants to create a new dataset, prompting for name')\n if (opts.showDefaultConfigPrompt && !existingDatasetNames.includes('production')) {\n defaultConfig = await promptForDefaultConfig()\n }\n\n const newDatasetName = defaultConfig\n ? 'production'\n : await promptForDatasetName(\n {\n message: 'Dataset name:',\n },\n existingDatasetNames,\n )\n await createDataset({\n datasetName: newDatasetName,\n forcePublic: defaultConfig,\n output: opts.output,\n projectFeatures,\n projectId: opts.projectId,\n visibility,\n })\n return {datasetName: newDatasetName, userAction: 'create'}\n }\n\n debug(`Returning selected dataset (${selected})`)\n return {datasetName: selected, userAction: 'select'}\n}\n"],"names":["subdebug","search","Separator","promptForDatasetName","promptForDefaultConfig","listDatasets","getProjectFeatures","searchChoices","searchTheme","createDataset","debug","getOrCreateDataset","opts","dataset","visibility","defaultConfig","datasets","projectFeatures","Promise","all","projectId","existing","find","ds","name","datasetName","forcePublic","isUnattended","unattended","output","userAction","undefined","length","showDefaultConfigPrompt","message","datasetChoices","map","value","selected","source","theme","existingDatasetNames","includes","newDatasetName"],"mappings":"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"}
@@ -261,7 +261,7 @@ specific token instead - for example in CI or when the CLI is not logged in
261
261
  return resolveEndpoint({
262
262
  ...baseOptions,
263
263
  forceHost,
264
- projectId: flags['project-id'] ?? cliConfig.api?.projectId
264
+ projectId: flags.project ?? cliConfig.api?.projectId
265
265
  });
266
266
  } catch (error) {
267
267
  if (!(error instanceof ProjectIdRequiredError)) throw error;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/api.ts"],"sourcesContent":["import {readFileSync} from 'node:fs'\nimport {readFile} from 'node:fs/promises'\nimport {buffer} from 'node:stream/consumers'\nimport {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {colorizeJson, exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\n\nimport {ApiUsageError, ProjectIdRequiredError} from '../actions/api/errors.js'\nimport {fieldsToQuery, type FieldValue, parseFields} from '../actions/api/parseFields.js'\nimport {type ResolvedEndpoint, resolveEndpoint} from '../actions/api/resolveEndpoint.js'\nimport {apiRoutes} from '../generated/apiRoutes.js'\nimport {promptForProject} from '../prompts/promptForProject.js'\nimport {type ApiResponse, performApiRequest} from '../services/api.js'\nimport {getDatasetFlag, getProjectIdFlag} from '../util/sharedFlags.js'\n\nconst apiDebug = subdebug('api')\n\nconst METHOD_RE = /^[a-z]+$/i\n\nconst BODYLESS_METHODS = new Set(['GET', 'HEAD'])\n\nexport class ApiCommand extends SanityCommand<typeof ApiCommand> {\n static override args = {\n endpoint: Args.string({\n description:\n 'API path (eg \"projects\" or \"data/query/{dataset}\"), optionally with placeholders, or a full https://*.api.sanity.io URL',\n required: true,\n }),\n }\n\n static override description = `Make an authenticated HTTP request to a Sanity API\n\nThe endpoint argument is an API path as documented in the published OpenAPI\nspecifications - list them with \"sanity openapi list\" and inspect one with\n\"sanity openapi get <slug>\". Paths can be copied verbatim from the specs:\n{projectId} and {dataset} placeholders are filled in from flags or the CLI\nconfiguration, and the API host (api.sanity.io or <projectId>.api.sanity.io)\nis chosen based on the specs' routing information.\n\nThe default request method is GET, or POST when fields or --input are\nprovided. For GET/HEAD requests, fields are sent as query parameters;\notherwise they are combined into a JSON request body sent with\n\"Content-Type: application/json\". Raw --input bodies are sent without a\ndefault Content-Type - provide one with -H when the API requires it. The\nresponse body is written to stdout.\n\nRequests are authenticated with the token from \"sanity login\". To use a\nspecific token instead - for example in CI or when the CLI is not logged in\n- pass --token or set the SANITY_AUTH_TOKEN environment variable. Pass\n--anonymous to send no token at all.`\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> users/me',\n description: 'Get the current user',\n },\n {\n command: '<%= config.bin %> <%= command.id %> projects/{projectId}',\n description: 'Get the current project (placeholder filled from CLI config)',\n },\n {\n command: `<%= config.bin %> <%= command.id %> 'data/query/{dataset}' -f query='*[_type == \"movie\"][0..2]'`,\n description: 'Run a GROQ query against the project host',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> projects/{projectId} -X PATCH -F displayName=\"My project\"',\n description: 'Send a JSON body built from typed fields',\n },\n {\n command: `echo '{\"mutations\": []}' | <%= config.bin %> <%= command.id %> 'data/mutate/{dataset}' --input - -H 'Content-Type: application/json'`,\n description: 'Send a raw request body from stdin',\n },\n {\n command: '<%= config.bin %> <%= command.id %> jobs/123 --include --api-version v2025-02-19',\n description: 'Include the response status and headers, pinning the API version',\n },\n {\n command: 'SANITY_AUTH_TOKEN=<token> <%= config.bin %> <%= command.id %> users/me',\n description: 'Authenticate with a specific token instead of the logged-in session',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID for {projectId} placeholders and project-hosted APIs',\n semantics: 'override',\n }),\n ...getDatasetFlag({\n description: 'Dataset for {dataset} placeholders',\n semantics: 'override',\n }),\n anonymous: Flags.boolean({\n default: false,\n description: 'Send the request without an authorization token',\n }),\n 'api-version': Flags.string({\n description:\n 'API version to use (eg v2025-02-19). Defaults to a version embedded in the endpoint path, or the version from the matching OpenAPI spec',\n helpValue: '<version>',\n }),\n field: Flags.string({\n char: 'F',\n description:\n 'Add a typed parameter (key=value): true/false/null and numbers are converted, @file reads the value from a file, @- from stdin',\n helpValue: '<key=value>',\n multiple: true,\n }),\n global: Flags.boolean({\n description: 'Force the request to the global API host (api.sanity.io)',\n exclusive: ['project-hosted'],\n }),\n header: Flags.string({\n char: 'H',\n description: 'Add an HTTP request header (key: value)',\n helpValue: '<key:value>',\n multiple: true,\n }),\n include: Flags.boolean({\n char: 'i',\n default: false,\n description: 'Include the HTTP response status and headers in the output',\n }),\n input: Flags.string({\n description:\n 'Read the raw request body from a file (use \"-\" for stdin). Sent without a default Content-Type - provide one with -H when the API requires it',\n exclusive: ['field', 'raw-field'],\n helpValue: '<file>',\n }),\n method: Flags.string({\n char: 'X',\n description: 'HTTP method to use (default GET, or POST when fields or --input are provided)',\n helpValue: '<method>',\n }),\n pretty: Flags.boolean({\n default: false,\n description: 'Colorize JSON output',\n }),\n 'project-hosted': Flags.boolean({\n description: 'Force the request to the project API host (<projectId>.api.sanity.io)',\n exclusive: ['global'],\n }),\n 'raw-field': Flags.string({\n char: 'f',\n description: 'Add a string parameter (key=value)',\n helpValue: '<key=value>',\n multiple: true,\n }),\n token: Flags.string({\n char: 't',\n description: 'API token to authenticate with, instead of the logged-in user token',\n exclusive: ['anonymous'],\n helpValue: '<token>',\n }),\n }\n\n /**\n * Read the raw body for `--input`, or the content referenced by a `@-`\n * field value. Returns raw bytes so binary bodies survive unmodified.\n * Extracted so tests can substitute stdin.\n */\n protected readStdin(): Promise<Buffer> {\n return buffer(process.stdin)\n }\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(ApiCommand)\n\n try {\n await this.performRequest(args.endpoint, flags)\n } catch (error) {\n if (error instanceof ApiUsageError) {\n this.error(error.message, {exit: exitCodes.USAGE_ERROR})\n }\n throw error\n }\n }\n\n private async performRequest(\n endpoint: string,\n flags: (typeof ApiCommand.prototype)['flags'],\n ): Promise<void> {\n const stdin = await this.resolveStdin(flags)\n\n const fields = parseFields({\n fields: flags.field,\n rawFields: flags['raw-field'],\n readFile: (path) => readFileSync(path, 'utf8'),\n stdin: stdin?.toString('utf8'),\n })\n const hasFields = Object.keys(fields).length > 0\n\n const method = this.resolveMethod(flags.method, hasFields || flags.input !== undefined)\n const useQueryFields = BODYLESS_METHODS.has(method)\n\n const body = await this.resolveBody(flags, fields, hasFields, useQueryFields, stdin)\n const query = useQueryFields && hasFields ? fieldsToQuery(fields) : {}\n\n const resolved = await this.resolveTarget(endpoint, flags)\n apiDebug('Resolved endpoint', resolved)\n\n const response = await performApiRequest({\n ...(body === undefined ? {} : {body}),\n headers: parseHeaders(flags.header ?? []),\n method,\n query,\n resolved,\n token: flags.token,\n unauthenticated: flags.anonymous,\n })\n\n this.printResponse(response, flags)\n\n if (response.statusCode >= 400) {\n const statusLine = `HTTP ${response.statusCode}${\n response.statusMessage ? ` ${response.statusMessage}` : ''\n }`\n const loginHint =\n response.statusCode === 401 && !flags.anonymous\n ? `. You may need to login again with ${styleText('cyan', 'sanity login')}`\n : ''\n this.error(`${statusLine}${loginHint}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n private printResponse(response: ApiResponse, flags: {include: boolean; pretty: boolean}): void {\n if (flags.include) {\n this.log(\n `HTTP ${response.statusCode}${response.statusMessage ? ` ${response.statusMessage}` : ''}`,\n )\n for (const [key, value] of Object.entries(response.headers)) {\n this.log(`${key}: ${value}`)\n }\n this.log('')\n }\n\n if (!response.jsonBody) {\n if (response.rawBody !== '') this.log(response.rawBody)\n return\n }\n\n this.log(flags.pretty ? colorizeJson(response.body) : JSON.stringify(response.body, null, 2))\n }\n\n private async resolveBody(\n flags: {input?: string},\n fields: Record<string, FieldValue>,\n hasFields: boolean,\n useQueryFields: boolean,\n stdin: Buffer | undefined,\n ): Promise<Buffer | unknown | undefined> {\n if (flags.input !== undefined) {\n if (flags.input === '-') return stdin\n try {\n // Read raw bytes - `--input` bodies may be binary (eg asset uploads)\n return await readFile(flags.input)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new ApiUsageError(`Failed to read --input file \"${flags.input}\": ${message}`)\n }\n }\n\n return hasFields && !useQueryFields ? fields : undefined\n }\n\n private resolveMethod(methodFlag: string | undefined, hasBody: boolean): string {\n if (methodFlag === undefined) {\n return hasBody ? 'POST' : 'GET'\n }\n if (!METHOD_RE.test(methodFlag)) {\n throw new ApiUsageError(`Invalid HTTP method \"${methodFlag}\"`)\n }\n return methodFlag.toUpperCase()\n }\n\n private async resolveStdin(flags: {\n field?: string[]\n input?: string\n }): Promise<Buffer | undefined> {\n const needsStdin =\n flags.input === '-' || (flags.field ?? []).some((field) => field.endsWith('=@-'))\n return needsStdin ? this.readStdin() : undefined\n }\n\n private async resolveTarget(\n endpoint: string,\n flags: {\n 'api-version'?: string\n dataset?: string\n global?: boolean\n 'project-hosted'?: boolean\n 'project-id'?: string\n },\n ): Promise<ResolvedEndpoint> {\n const cliConfig = await this.tryGetCliConfig()\n\n const baseOptions = {\n apiVersion: flags['api-version'],\n dataset: flags.dataset ?? cliConfig.api?.dataset,\n endpoint,\n forceHost: flags.global ? ('global' as const) : undefined,\n routes: apiRoutes,\n }\n const forceHost = flags['project-hosted'] ? ('project' as const) : baseOptions.forceHost\n\n try {\n return resolveEndpoint({\n ...baseOptions,\n forceHost,\n projectId: flags['project-id'] ?? cliConfig.api?.projectId,\n })\n } catch (error) {\n if (!(error instanceof ProjectIdRequiredError)) throw error\n\n // The request needs a project ID that flags/config didn't provide -\n // resolve one interactively (or fail with actionable suggestions).\n const projectId = await this.getProjectId({fallback: () => promptForProject({})})\n return resolveEndpoint({...baseOptions, forceHost, projectId})\n }\n }\n}\n\nfunction parseHeaders(headerFlags: string[]): Record<string, string> {\n // Null-prototype for the same reason as the field containers in parseFields\n const headers: Record<string, string> = Object.create(null)\n\n for (const header of headerFlags) {\n const separatorIndex = header.indexOf(':')\n if (separatorIndex < 1) {\n throw new ApiUsageError(`Invalid --header \"${header}\": expected \"key: value\" format`)\n }\n headers[header.slice(0, separatorIndex).trim()] = header.slice(separatorIndex + 1).trim()\n }\n\n return headers\n}\n"],"names":["readFileSync","readFile","buffer","styleText","Args","Flags","colorizeJson","exitCodes","SanityCommand","subdebug","ApiUsageError","ProjectIdRequiredError","fieldsToQuery","parseFields","resolveEndpoint","apiRoutes","promptForProject","performApiRequest","getDatasetFlag","getProjectIdFlag","apiDebug","METHOD_RE","BODYLESS_METHODS","Set","ApiCommand","args","endpoint","string","description","required","examples","command","flags","semantics","anonymous","boolean","default","helpValue","field","char","multiple","global","exclusive","header","include","input","method","pretty","token","readStdin","process","stdin","run","parse","performRequest","error","message","exit","USAGE_ERROR","resolveStdin","fields","rawFields","path","toString","hasFields","Object","keys","length","resolveMethod","undefined","useQueryFields","has","body","resolveBody","query","resolved","resolveTarget","response","headers","parseHeaders","unauthenticated","printResponse","statusCode","statusLine","statusMessage","loginHint","RUNTIME_ERROR","log","key","value","entries","jsonBody","rawBody","JSON","stringify","Error","String","methodFlag","hasBody","test","toUpperCase","needsStdin","some","endsWith","cliConfig","tryGetCliConfig","baseOptions","apiVersion","dataset","api","forceHost","routes","projectId","getProjectId","fallback","headerFlags","create","separatorIndex","indexOf","slice","trim"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/commands/api.ts"],"sourcesContent":["import {readFileSync} from 'node:fs'\nimport {readFile} from 'node:fs/promises'\nimport {buffer} from 'node:stream/consumers'\nimport {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {colorizeJson, exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\n\nimport {ApiUsageError, ProjectIdRequiredError} from '../actions/api/errors.js'\nimport {fieldsToQuery, type FieldValue, parseFields} from '../actions/api/parseFields.js'\nimport {type ResolvedEndpoint, resolveEndpoint} from '../actions/api/resolveEndpoint.js'\nimport {apiRoutes} from '../generated/apiRoutes.js'\nimport {promptForProject} from '../prompts/promptForProject.js'\nimport {type ApiResponse, performApiRequest} from '../services/api.js'\nimport {getDatasetFlag, getProjectIdFlag} from '../util/sharedFlags.js'\n\nconst apiDebug = subdebug('api')\n\nconst METHOD_RE = /^[a-z]+$/i\n\nconst BODYLESS_METHODS = new Set(['GET', 'HEAD'])\n\nexport class ApiCommand extends SanityCommand<typeof ApiCommand> {\n static override args = {\n endpoint: Args.string({\n description:\n 'API path (eg \"projects\" or \"data/query/{dataset}\"), optionally with placeholders, or a full https://*.api.sanity.io URL',\n required: true,\n }),\n }\n\n static override description = `Make an authenticated HTTP request to a Sanity API\n\nThe endpoint argument is an API path as documented in the published OpenAPI\nspecifications - list them with \"sanity openapi list\" and inspect one with\n\"sanity openapi get <slug>\". Paths can be copied verbatim from the specs:\n{projectId} and {dataset} placeholders are filled in from flags or the CLI\nconfiguration, and the API host (api.sanity.io or <projectId>.api.sanity.io)\nis chosen based on the specs' routing information.\n\nThe default request method is GET, or POST when fields or --input are\nprovided. For GET/HEAD requests, fields are sent as query parameters;\notherwise they are combined into a JSON request body sent with\n\"Content-Type: application/json\". Raw --input bodies are sent without a\ndefault Content-Type - provide one with -H when the API requires it. The\nresponse body is written to stdout.\n\nRequests are authenticated with the token from \"sanity login\". To use a\nspecific token instead - for example in CI or when the CLI is not logged in\n- pass --token or set the SANITY_AUTH_TOKEN environment variable. Pass\n--anonymous to send no token at all.`\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> users/me',\n description: 'Get the current user',\n },\n {\n command: '<%= config.bin %> <%= command.id %> projects/{projectId}',\n description: 'Get the current project (placeholder filled from CLI config)',\n },\n {\n command: `<%= config.bin %> <%= command.id %> 'data/query/{dataset}' -f query='*[_type == \"movie\"][0..2]'`,\n description: 'Run a GROQ query against the project host',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> projects/{projectId} -X PATCH -F displayName=\"My project\"',\n description: 'Send a JSON body built from typed fields',\n },\n {\n command: `echo '{\"mutations\": []}' | <%= config.bin %> <%= command.id %> 'data/mutate/{dataset}' --input - -H 'Content-Type: application/json'`,\n description: 'Send a raw request body from stdin',\n },\n {\n command: '<%= config.bin %> <%= command.id %> jobs/123 --include --api-version v2025-02-19',\n description: 'Include the response status and headers, pinning the API version',\n },\n {\n command: 'SANITY_AUTH_TOKEN=<token> <%= config.bin %> <%= command.id %> users/me',\n description: 'Authenticate with a specific token instead of the logged-in session',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID for {projectId} placeholders and project-hosted APIs',\n semantics: 'override',\n }),\n ...getDatasetFlag({\n description: 'Dataset for {dataset} placeholders',\n semantics: 'override',\n }),\n anonymous: Flags.boolean({\n default: false,\n description: 'Send the request without an authorization token',\n }),\n 'api-version': Flags.string({\n description:\n 'API version to use (eg v2025-02-19). Defaults to a version embedded in the endpoint path, or the version from the matching OpenAPI spec',\n helpValue: '<version>',\n }),\n field: Flags.string({\n char: 'F',\n description:\n 'Add a typed parameter (key=value): true/false/null and numbers are converted, @file reads the value from a file, @- from stdin',\n helpValue: '<key=value>',\n multiple: true,\n }),\n global: Flags.boolean({\n description: 'Force the request to the global API host (api.sanity.io)',\n exclusive: ['project-hosted'],\n }),\n header: Flags.string({\n char: 'H',\n description: 'Add an HTTP request header (key: value)',\n helpValue: '<key:value>',\n multiple: true,\n }),\n include: Flags.boolean({\n char: 'i',\n default: false,\n description: 'Include the HTTP response status and headers in the output',\n }),\n input: Flags.string({\n description:\n 'Read the raw request body from a file (use \"-\" for stdin). Sent without a default Content-Type - provide one with -H when the API requires it',\n exclusive: ['field', 'raw-field'],\n helpValue: '<file>',\n }),\n method: Flags.string({\n char: 'X',\n description: 'HTTP method to use (default GET, or POST when fields or --input are provided)',\n helpValue: '<method>',\n }),\n pretty: Flags.boolean({\n default: false,\n description: 'Colorize JSON output',\n }),\n 'project-hosted': Flags.boolean({\n description: 'Force the request to the project API host (<projectId>.api.sanity.io)',\n exclusive: ['global'],\n }),\n 'raw-field': Flags.string({\n char: 'f',\n description: 'Add a string parameter (key=value)',\n helpValue: '<key=value>',\n multiple: true,\n }),\n token: Flags.string({\n char: 't',\n description: 'API token to authenticate with, instead of the logged-in user token',\n exclusive: ['anonymous'],\n helpValue: '<token>',\n }),\n }\n\n /**\n * Read the raw body for `--input`, or the content referenced by a `@-`\n * field value. Returns raw bytes so binary bodies survive unmodified.\n * Extracted so tests can substitute stdin.\n */\n protected readStdin(): Promise<Buffer> {\n return buffer(process.stdin)\n }\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(ApiCommand)\n\n try {\n await this.performRequest(args.endpoint, flags)\n } catch (error) {\n if (error instanceof ApiUsageError) {\n this.error(error.message, {exit: exitCodes.USAGE_ERROR})\n }\n throw error\n }\n }\n\n private async performRequest(\n endpoint: string,\n flags: (typeof ApiCommand.prototype)['flags'],\n ): Promise<void> {\n const stdin = await this.resolveStdin(flags)\n\n const fields = parseFields({\n fields: flags.field,\n rawFields: flags['raw-field'],\n readFile: (path) => readFileSync(path, 'utf8'),\n stdin: stdin?.toString('utf8'),\n })\n const hasFields = Object.keys(fields).length > 0\n\n const method = this.resolveMethod(flags.method, hasFields || flags.input !== undefined)\n const useQueryFields = BODYLESS_METHODS.has(method)\n\n const body = await this.resolveBody(flags, fields, hasFields, useQueryFields, stdin)\n const query = useQueryFields && hasFields ? fieldsToQuery(fields) : {}\n\n const resolved = await this.resolveTarget(endpoint, flags)\n apiDebug('Resolved endpoint', resolved)\n\n const response = await performApiRequest({\n ...(body === undefined ? {} : {body}),\n headers: parseHeaders(flags.header ?? []),\n method,\n query,\n resolved,\n token: flags.token,\n unauthenticated: flags.anonymous,\n })\n\n this.printResponse(response, flags)\n\n if (response.statusCode >= 400) {\n const statusLine = `HTTP ${response.statusCode}${\n response.statusMessage ? ` ${response.statusMessage}` : ''\n }`\n const loginHint =\n response.statusCode === 401 && !flags.anonymous\n ? `. You may need to login again with ${styleText('cyan', 'sanity login')}`\n : ''\n this.error(`${statusLine}${loginHint}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n private printResponse(response: ApiResponse, flags: {include: boolean; pretty: boolean}): void {\n if (flags.include) {\n this.log(\n `HTTP ${response.statusCode}${response.statusMessage ? ` ${response.statusMessage}` : ''}`,\n )\n for (const [key, value] of Object.entries(response.headers)) {\n this.log(`${key}: ${value}`)\n }\n this.log('')\n }\n\n if (!response.jsonBody) {\n if (response.rawBody !== '') this.log(response.rawBody)\n return\n }\n\n this.log(flags.pretty ? colorizeJson(response.body) : JSON.stringify(response.body, null, 2))\n }\n\n private async resolveBody(\n flags: {input?: string},\n fields: Record<string, FieldValue>,\n hasFields: boolean,\n useQueryFields: boolean,\n stdin: Buffer | undefined,\n ): Promise<Buffer | unknown | undefined> {\n if (flags.input !== undefined) {\n if (flags.input === '-') return stdin\n try {\n // Read raw bytes - `--input` bodies may be binary (eg asset uploads)\n return await readFile(flags.input)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new ApiUsageError(`Failed to read --input file \"${flags.input}\": ${message}`)\n }\n }\n\n return hasFields && !useQueryFields ? fields : undefined\n }\n\n private resolveMethod(methodFlag: string | undefined, hasBody: boolean): string {\n if (methodFlag === undefined) {\n return hasBody ? 'POST' : 'GET'\n }\n if (!METHOD_RE.test(methodFlag)) {\n throw new ApiUsageError(`Invalid HTTP method \"${methodFlag}\"`)\n }\n return methodFlag.toUpperCase()\n }\n\n private async resolveStdin(flags: {\n field?: string[]\n input?: string\n }): Promise<Buffer | undefined> {\n const needsStdin =\n flags.input === '-' || (flags.field ?? []).some((field) => field.endsWith('=@-'))\n return needsStdin ? this.readStdin() : undefined\n }\n\n private async resolveTarget(\n endpoint: string,\n flags: {\n 'api-version'?: string\n dataset?: string\n global?: boolean\n project?: string\n 'project-hosted'?: boolean\n },\n ): Promise<ResolvedEndpoint> {\n const cliConfig = await this.tryGetCliConfig()\n\n const baseOptions = {\n apiVersion: flags['api-version'],\n dataset: flags.dataset ?? cliConfig.api?.dataset,\n endpoint,\n forceHost: flags.global ? ('global' as const) : undefined,\n routes: apiRoutes,\n }\n const forceHost = flags['project-hosted'] ? ('project' as const) : baseOptions.forceHost\n\n try {\n return resolveEndpoint({\n ...baseOptions,\n forceHost,\n projectId: flags.project ?? cliConfig.api?.projectId,\n })\n } catch (error) {\n if (!(error instanceof ProjectIdRequiredError)) throw error\n\n // The request needs a project ID that flags/config didn't provide -\n // resolve one interactively (or fail with actionable suggestions).\n const projectId = await this.getProjectId({fallback: () => promptForProject({})})\n return resolveEndpoint({...baseOptions, forceHost, projectId})\n }\n }\n}\n\nfunction parseHeaders(headerFlags: string[]): Record<string, string> {\n // Null-prototype for the same reason as the field containers in parseFields\n const headers: Record<string, string> = Object.create(null)\n\n for (const header of headerFlags) {\n const separatorIndex = header.indexOf(':')\n if (separatorIndex < 1) {\n throw new ApiUsageError(`Invalid --header \"${header}\": expected \"key: value\" format`)\n }\n headers[header.slice(0, separatorIndex).trim()] = header.slice(separatorIndex + 1).trim()\n }\n\n return headers\n}\n"],"names":["readFileSync","readFile","buffer","styleText","Args","Flags","colorizeJson","exitCodes","SanityCommand","subdebug","ApiUsageError","ProjectIdRequiredError","fieldsToQuery","parseFields","resolveEndpoint","apiRoutes","promptForProject","performApiRequest","getDatasetFlag","getProjectIdFlag","apiDebug","METHOD_RE","BODYLESS_METHODS","Set","ApiCommand","args","endpoint","string","description","required","examples","command","flags","semantics","anonymous","boolean","default","helpValue","field","char","multiple","global","exclusive","header","include","input","method","pretty","token","readStdin","process","stdin","run","parse","performRequest","error","message","exit","USAGE_ERROR","resolveStdin","fields","rawFields","path","toString","hasFields","Object","keys","length","resolveMethod","undefined","useQueryFields","has","body","resolveBody","query","resolved","resolveTarget","response","headers","parseHeaders","unauthenticated","printResponse","statusCode","statusLine","statusMessage","loginHint","RUNTIME_ERROR","log","key","value","entries","jsonBody","rawBody","JSON","stringify","Error","String","methodFlag","hasBody","test","toUpperCase","needsStdin","some","endsWith","cliConfig","tryGetCliConfig","baseOptions","apiVersion","dataset","api","forceHost","routes","projectId","project","getProjectId","fallback","headerFlags","create","separatorIndex","indexOf","slice","trim"],"mappings":"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"}
@@ -60,7 +60,7 @@ export class UploadAssetCommand extends SanityCommand {
60
60
  description: 'Upload an image using the configured project'
61
61
  },
62
62
  {
63
- command: '<%= config.bin %> <%= command.id %> --file ./brief.pdf --type file --content-type application/pdf --project-id abc123 --dataset production',
63
+ command: '<%= config.bin %> <%= command.id %> --file ./brief.pdf --type file --content-type application/pdf --project abc123 --dataset production',
64
64
  description: 'Upload a file with explicit project, dataset, and MIME type'
65
65
  },
66
66
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/assets/upload.ts"],"sourcesContent":["import {basename, resolve} from 'node:path'\n\nimport {Flags} from '@oclif/core'\nimport {type FlagInput} from '@oclif/core/interfaces'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\n\nimport {AssetFileError} from '../../actions/assets/assetFileError.js'\nimport {ingestAssetFromUrlWithProgress} from '../../actions/assets/ingestAssetFromUrlWithProgress.js'\nimport {uploadAssetWithProgress} from '../../actions/assets/uploadAssetWithProgress.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {type AssetType} from '../../services/assets.js'\nimport {getAssetFilenameError, getIngestUrlError} from '../../util/assetSourceValidation.js'\nimport {getAssetUploadErrorMessage} from '../../util/assetUploadErrors.js'\nimport {getDatasetFlag, getProjectIdFlag} from '../../util/sharedFlags.js'\nimport {defineCommandTelemetry} from '../../util/telemetry/commandTelemetry.js'\n\nconst uploadAssetDebug = subdebug('assets:upload')\n\nconst flags = {\n ...getProjectIdFlag({\n description: 'Project ID to upload the asset to',\n semantics: 'override',\n }),\n ...getDatasetFlag({description: 'Dataset to upload the asset to', semantics: 'override'}),\n 'content-type': Flags.string({\n description:\n 'MIME type of the asset, such as image/png or application/pdf. Only applies to --file',\n helpValue: '<mime-type>',\n }),\n file: Flags.string({\n description: 'Path to the local file to upload',\n exactlyOne: ['file', 'from-url'],\n helpValue: '<path>',\n }),\n filename: Flags.string({\n description:\n 'Original filename stored on the asset document. Defaults to the local filename when using --file',\n helpValue: '<filename>',\n }),\n 'from-url': Flags.string({\n description:\n 'https URL for Sanity to fetch the asset from, instead of uploading a local file. Must be reachable from the public internet without authentication',\n exactlyOne: ['file', 'from-url'],\n helpValue: '<url>',\n }),\n type: Flags.custom<AssetType>({\n default: 'image',\n description: 'Asset type to create',\n options: ['image', 'file'],\n })(),\n} satisfies FlagInput\n\nexport class UploadAssetCommand extends SanityCommand<typeof UploadAssetCommand> {\n static override description =\n 'Upload one image or file to a Sanity dataset, from a local path or a URL, and print the asset document as JSON'\n\n static override examples = [\n {\n command:\n '<%= config.bin %> <%= command.id %> --file ./hero.png --type image --dataset production',\n description: 'Upload an image using the configured project',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> --file ./brief.pdf --type file --content-type application/pdf --project-id abc123 --dataset production',\n description: 'Upload a file with explicit project, dataset, and MIME type',\n },\n {\n command:\n \"<%= config.bin %> <%= command.id %> --from-url 'https://example.com/hero.png' --type image --dataset production\",\n description: 'Have Sanity fetch an image from a public URL',\n },\n ]\n\n static override flags = flags\n\n static override hiddenAliases: string[] = ['asset:upload']\n\n static telemetry = defineCommandTelemetry(flags, {\n redact: ['file', 'filename', 'from-url'],\n })\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(UploadAssetCommand)\n const sourceUrl = flags['from-url']\n\n if (sourceUrl && flags['content-type']) {\n this.error(\n 'Asset upload failed: --content-type cannot be combined with --from-url. Sanity derives the MIME type automatically.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n const sourceUrlError = sourceUrl === undefined ? undefined : getIngestUrlError(sourceUrl)\n if (sourceUrlError) {\n this.error(`Asset upload failed: ${sourceUrlError}`, {exit: exitCodes.USAGE_ERROR})\n }\n\n const filenameError =\n flags.filename === undefined ? undefined : getAssetFilenameError(flags.filename)\n if (filenameError) {\n this.error(`Asset upload failed: ${filenameError}`, {exit: exitCodes.USAGE_ERROR})\n }\n\n const cliConfig = await this.tryGetCliConfig()\n const projectId = await this.getProjectId({fallback: () => promptForProject({})})\n const dataset = flags.dataset ?? cliConfig.api?.dataset\n if (!dataset) {\n this.error(\n 'Asset upload failed: Dataset is required. Pass --dataset <name> or configure a dataset in sanity.cli.ts.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n const isInteractive = this.resolveIsInteractive()\n\n try {\n let asset\n if (sourceUrl === undefined) {\n // `exactlyOne` on the flags guarantees a source, but not to the compiler.\n if (flags.file === undefined) {\n this.error('Asset upload failed: Pass either --file <path> or --from-url <url>.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n const filePath = resolve(flags.file)\n asset = await uploadAssetWithProgress({\n assetType: flags.type,\n contentType: flags['content-type'],\n dataset,\n filename: flags.filename ?? basename(filePath),\n filePath,\n isInteractive,\n logToStderr: (message) => this.logToStderr(message),\n projectId,\n })\n } else {\n asset = await ingestAssetFromUrlWithProgress({\n assetType: flags.type,\n dataset,\n filename: flags.filename,\n isInteractive,\n logToStderr: (message) => this.logToStderr(message),\n projectId,\n url: sourceUrl,\n })\n }\n const fieldType = flags.type === 'image' ? 'image' : 'file'\n\n this.log(\n JSON.stringify(\n {\n asset: {\n _id: asset._id,\n _type: asset._type,\n extension: asset.extension,\n mimeType: asset.mimeType,\n originalFilename: asset.originalFilename,\n size: asset.size,\n url: asset.url,\n },\n reference: {\n _type: fieldType,\n asset: {_ref: asset._id, _type: 'reference'},\n },\n },\n null,\n 2,\n ),\n )\n } catch (error) {\n if (error instanceof Error && error.message === 'SIGINT') throw error\n if (error instanceof AssetFileError) {\n if (error.reason === 'not-file') {\n this.error('Asset upload failed: --file must point to a file, not a directory.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n this.error(\n 'Asset upload failed: Cannot read the local file. Check that --file points to a readable file, then retry.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n uploadAssetDebug('Asset upload failed', error)\n this.error(\n getAssetUploadErrorMessage(error, {\n fromUrl: sourceUrl !== undefined,\n target: 'dataset',\n }),\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n }\n}\n"],"names":["basename","resolve","Flags","exitCodes","SanityCommand","subdebug","AssetFileError","ingestAssetFromUrlWithProgress","uploadAssetWithProgress","promptForProject","getAssetFilenameError","getIngestUrlError","getAssetUploadErrorMessage","getDatasetFlag","getProjectIdFlag","defineCommandTelemetry","uploadAssetDebug","flags","description","semantics","string","helpValue","file","exactlyOne","filename","type","custom","default","options","UploadAssetCommand","examples","command","hiddenAliases","telemetry","redact","run","parse","sourceUrl","error","exit","USAGE_ERROR","sourceUrlError","undefined","filenameError","cliConfig","tryGetCliConfig","projectId","getProjectId","fallback","dataset","api","isInteractive","resolveIsInteractive","asset","filePath","assetType","contentType","logToStderr","message","url","fieldType","log","JSON","stringify","_id","_type","extension","mimeType","originalFilename","size","reference","_ref","Error","reason","fromUrl","target","RUNTIME_ERROR"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/commands/assets/upload.ts"],"sourcesContent":["import {basename, resolve} from 'node:path'\n\nimport {Flags} from '@oclif/core'\nimport {type FlagInput} from '@oclif/core/interfaces'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\n\nimport {AssetFileError} from '../../actions/assets/assetFileError.js'\nimport {ingestAssetFromUrlWithProgress} from '../../actions/assets/ingestAssetFromUrlWithProgress.js'\nimport {uploadAssetWithProgress} from '../../actions/assets/uploadAssetWithProgress.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {type AssetType} from '../../services/assets.js'\nimport {getAssetFilenameError, getIngestUrlError} from '../../util/assetSourceValidation.js'\nimport {getAssetUploadErrorMessage} from '../../util/assetUploadErrors.js'\nimport {getDatasetFlag, getProjectIdFlag} from '../../util/sharedFlags.js'\nimport {defineCommandTelemetry} from '../../util/telemetry/commandTelemetry.js'\n\nconst uploadAssetDebug = subdebug('assets:upload')\n\nconst flags = {\n ...getProjectIdFlag({\n description: 'Project ID to upload the asset to',\n semantics: 'override',\n }),\n ...getDatasetFlag({description: 'Dataset to upload the asset to', semantics: 'override'}),\n 'content-type': Flags.string({\n description:\n 'MIME type of the asset, such as image/png or application/pdf. Only applies to --file',\n helpValue: '<mime-type>',\n }),\n file: Flags.string({\n description: 'Path to the local file to upload',\n exactlyOne: ['file', 'from-url'],\n helpValue: '<path>',\n }),\n filename: Flags.string({\n description:\n 'Original filename stored on the asset document. Defaults to the local filename when using --file',\n helpValue: '<filename>',\n }),\n 'from-url': Flags.string({\n description:\n 'https URL for Sanity to fetch the asset from, instead of uploading a local file. Must be reachable from the public internet without authentication',\n exactlyOne: ['file', 'from-url'],\n helpValue: '<url>',\n }),\n type: Flags.custom<AssetType>({\n default: 'image',\n description: 'Asset type to create',\n options: ['image', 'file'],\n })(),\n} satisfies FlagInput\n\nexport class UploadAssetCommand extends SanityCommand<typeof UploadAssetCommand> {\n static override description =\n 'Upload one image or file to a Sanity dataset, from a local path or a URL, and print the asset document as JSON'\n\n static override examples = [\n {\n command:\n '<%= config.bin %> <%= command.id %> --file ./hero.png --type image --dataset production',\n description: 'Upload an image using the configured project',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> --file ./brief.pdf --type file --content-type application/pdf --project abc123 --dataset production',\n description: 'Upload a file with explicit project, dataset, and MIME type',\n },\n {\n command:\n \"<%= config.bin %> <%= command.id %> --from-url 'https://example.com/hero.png' --type image --dataset production\",\n description: 'Have Sanity fetch an image from a public URL',\n },\n ]\n\n static override flags = flags\n\n static override hiddenAliases: string[] = ['asset:upload']\n\n static telemetry = defineCommandTelemetry(flags, {\n redact: ['file', 'filename', 'from-url'],\n })\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(UploadAssetCommand)\n const sourceUrl = flags['from-url']\n\n if (sourceUrl && flags['content-type']) {\n this.error(\n 'Asset upload failed: --content-type cannot be combined with --from-url. Sanity derives the MIME type automatically.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n const sourceUrlError = sourceUrl === undefined ? undefined : getIngestUrlError(sourceUrl)\n if (sourceUrlError) {\n this.error(`Asset upload failed: ${sourceUrlError}`, {exit: exitCodes.USAGE_ERROR})\n }\n\n const filenameError =\n flags.filename === undefined ? undefined : getAssetFilenameError(flags.filename)\n if (filenameError) {\n this.error(`Asset upload failed: ${filenameError}`, {exit: exitCodes.USAGE_ERROR})\n }\n\n const cliConfig = await this.tryGetCliConfig()\n const projectId = await this.getProjectId({fallback: () => promptForProject({})})\n const dataset = flags.dataset ?? cliConfig.api?.dataset\n if (!dataset) {\n this.error(\n 'Asset upload failed: Dataset is required. Pass --dataset <name> or configure a dataset in sanity.cli.ts.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n const isInteractive = this.resolveIsInteractive()\n\n try {\n let asset\n if (sourceUrl === undefined) {\n // `exactlyOne` on the flags guarantees a source, but not to the compiler.\n if (flags.file === undefined) {\n this.error('Asset upload failed: Pass either --file <path> or --from-url <url>.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n const filePath = resolve(flags.file)\n asset = await uploadAssetWithProgress({\n assetType: flags.type,\n contentType: flags['content-type'],\n dataset,\n filename: flags.filename ?? basename(filePath),\n filePath,\n isInteractive,\n logToStderr: (message) => this.logToStderr(message),\n projectId,\n })\n } else {\n asset = await ingestAssetFromUrlWithProgress({\n assetType: flags.type,\n dataset,\n filename: flags.filename,\n isInteractive,\n logToStderr: (message) => this.logToStderr(message),\n projectId,\n url: sourceUrl,\n })\n }\n const fieldType = flags.type === 'image' ? 'image' : 'file'\n\n this.log(\n JSON.stringify(\n {\n asset: {\n _id: asset._id,\n _type: asset._type,\n extension: asset.extension,\n mimeType: asset.mimeType,\n originalFilename: asset.originalFilename,\n size: asset.size,\n url: asset.url,\n },\n reference: {\n _type: fieldType,\n asset: {_ref: asset._id, _type: 'reference'},\n },\n },\n null,\n 2,\n ),\n )\n } catch (error) {\n if (error instanceof Error && error.message === 'SIGINT') throw error\n if (error instanceof AssetFileError) {\n if (error.reason === 'not-file') {\n this.error('Asset upload failed: --file must point to a file, not a directory.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n this.error(\n 'Asset upload failed: Cannot read the local file. Check that --file points to a readable file, then retry.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n uploadAssetDebug('Asset upload failed', error)\n this.error(\n getAssetUploadErrorMessage(error, {\n fromUrl: sourceUrl !== undefined,\n target: 'dataset',\n }),\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n }\n}\n"],"names":["basename","resolve","Flags","exitCodes","SanityCommand","subdebug","AssetFileError","ingestAssetFromUrlWithProgress","uploadAssetWithProgress","promptForProject","getAssetFilenameError","getIngestUrlError","getAssetUploadErrorMessage","getDatasetFlag","getProjectIdFlag","defineCommandTelemetry","uploadAssetDebug","flags","description","semantics","string","helpValue","file","exactlyOne","filename","type","custom","default","options","UploadAssetCommand","examples","command","hiddenAliases","telemetry","redact","run","parse","sourceUrl","error","exit","USAGE_ERROR","sourceUrlError","undefined","filenameError","cliConfig","tryGetCliConfig","projectId","getProjectId","fallback","dataset","api","isInteractive","resolveIsInteractive","asset","filePath","assetType","contentType","logToStderr","message","url","fieldType","log","JSON","stringify","_id","_type","extension","mimeType","originalFilename","size","reference","_ref","Error","reason","fromUrl","target","RUNTIME_ERROR"],"mappings":"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"}
@@ -33,7 +33,7 @@ export class Add extends SanityCommand {
33
33
  description: 'Add a production origin with credentials allowed'
34
34
  },
35
35
  {
36
- command: '<%= config.bin %> <%= command.id %> https://myapp.com --project-id abc123',
36
+ command: '<%= config.bin %> <%= command.id %> https://myapp.com --project abc123',
37
37
  description: 'Add a CORS origin for a specific project'
38
38
  }
39
39
  ];