@sanity/cli 7.15.0 → 7.16.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 (138) hide show
  1. package/README.md +88 -1
  2. package/dist/actions/build/shouldAutoUpdate.js +28 -5
  3. package/dist/actions/build/shouldAutoUpdate.js.map +1 -1
  4. package/dist/actions/deploy/deployApp.js +4 -8
  5. package/dist/actions/deploy/deployApp.js.map +1 -1
  6. package/dist/actions/deploy/deployChecks.js +49 -31
  7. package/dist/actions/deploy/deployChecks.js.map +1 -1
  8. package/dist/actions/deploy/deployRunner.js +1 -1
  9. package/dist/actions/deploy/deployRunner.js.map +1 -1
  10. package/dist/actions/deploy/deployStudio.js +12 -10
  11. package/dist/actions/deploy/deployStudio.js.map +1 -1
  12. package/dist/actions/deploy/deploymentPlan.js +2 -2
  13. package/dist/actions/deploy/deploymentPlan.js.map +1 -1
  14. package/dist/actions/deploy/resolveDeployTarget.js +13 -13
  15. package/dist/actions/deploy/resolveDeployTarget.js.map +1 -1
  16. package/dist/actions/init/bootstrapLocalTemplate.js +2 -3
  17. package/dist/actions/init/bootstrapLocalTemplate.js.map +1 -1
  18. package/dist/actions/init/createAppCliConfig.js.map +1 -1
  19. package/dist/actions/init/createCliConfig.js.map +1 -1
  20. package/dist/actions/init/initNextJs.js +1 -1
  21. package/dist/actions/init/initNextJs.js.map +1 -1
  22. package/dist/actions/init/initStudio.js +10 -6
  23. package/dist/actions/init/initStudio.js.map +1 -1
  24. package/dist/actions/init/templates/appSanityUi.js +1 -1
  25. package/dist/actions/init/templates/appSanityUi.js.map +1 -1
  26. package/dist/actions/init/templates/getStarted.js +2 -2
  27. package/dist/actions/init/templates/getStarted.js.map +1 -1
  28. package/dist/actions/init/templates/nextjs/schemaTypes/blog.js +4 -4
  29. package/dist/actions/init/templates/nextjs/schemaTypes/blog.js.map +1 -1
  30. package/dist/actions/init/templates/shopify.js +2 -2
  31. package/dist/actions/init/templates/shopify.js.map +1 -1
  32. package/dist/actions/init/templates/shopifyOnline.js +2 -2
  33. package/dist/actions/init/templates/shopifyOnline.js.map +1 -1
  34. package/dist/actions/manifest/extractCoreAppManifest.js +7 -7
  35. package/dist/actions/manifest/extractCoreAppManifest.js.map +1 -1
  36. package/dist/actions/scaffold/createFrontend.js +107 -0
  37. package/dist/actions/scaffold/createFrontend.js.map +1 -0
  38. package/dist/actions/scaffold/scaffoldProject.js +202 -0
  39. package/dist/actions/scaffold/scaffoldProject.js.map +1 -0
  40. package/dist/actions/telemetry/resolveConsent.js +4 -2
  41. package/dist/actions/telemetry/resolveConsent.js.map +1 -1
  42. package/dist/commands/backups/list.js +1 -1
  43. package/dist/commands/backups/list.js.map +1 -1
  44. package/dist/commands/datasets/copy.js +5 -3
  45. package/dist/commands/datasets/copy.js.map +1 -1
  46. package/dist/commands/documents/query.js +5 -4
  47. package/dist/commands/documents/query.js.map +1 -1
  48. package/dist/commands/migrations/list.js +1 -1
  49. package/dist/commands/migrations/list.js.map +1 -1
  50. package/dist/commands/migrations/run.js +1 -1
  51. package/dist/commands/migrations/run.js.map +1 -1
  52. package/dist/commands/new.js +309 -0
  53. package/dist/commands/new.js.map +1 -0
  54. package/dist/commands/organizations/list.js +1 -1
  55. package/dist/commands/organizations/list.js.map +1 -1
  56. package/dist/commands/projects/unclaimed.js +205 -0
  57. package/dist/commands/projects/unclaimed.js.map +1 -0
  58. package/dist/commands/tokens/list.js +1 -1
  59. package/dist/commands/tokens/list.js.map +1 -1
  60. package/dist/commands/users/list.js +5 -1
  61. package/dist/commands/users/list.js.map +1 -1
  62. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js +4 -0
  63. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js.map +1 -1
  64. package/dist/exports/invokeSanityCli/index.d.ts +5 -9
  65. package/dist/exports/invokeSanityCli/index.js +47 -20
  66. package/dist/exports/invokeSanityCli/index.js.map +1 -1
  67. package/dist/exports/runtime.d.ts +12 -0
  68. package/dist/exports/runtime.js.map +1 -1
  69. package/dist/services/mintProject.js +91 -0
  70. package/dist/services/mintProject.js.map +1 -0
  71. package/dist/services/projects.js +19 -0
  72. package/dist/services/projects.js.map +1 -1
  73. package/dist/services/telemetry.js +4 -0
  74. package/dist/services/telemetry.js.map +1 -1
  75. package/dist/util/formatClaimDeadline.js +15 -0
  76. package/dist/util/formatClaimDeadline.js.map +1 -0
  77. package/dist/util/mintProjectConstants.js +6 -0
  78. package/dist/util/mintProjectConstants.js.map +1 -0
  79. package/dist/util/newCommandSplash.js +27 -0
  80. package/dist/util/newCommandSplash.js.map +1 -0
  81. package/dist/util/packageManager/installPackages.js +57 -28
  82. package/dist/util/packageManager/installPackages.js.map +1 -1
  83. package/dist/util/unclaimedProjects.js +105 -0
  84. package/dist/util/unclaimedProjects.js.map +1 -0
  85. package/oclif.manifest.json +150 -41
  86. package/package.json +7 -7
  87. package/templates/get-started/plugins/sanity-plugin-tutorial/GetStartedTutorial.tsx +1 -1
  88. package/templates/shopify/components/inputs/CollectionHidden.tsx +1 -1
  89. package/templates/shopify/components/inputs/ProductHidden.tsx +1 -1
  90. package/templates/shopify/components/inputs/ProductVariantHidden.tsx +1 -1
  91. package/templates/shopify/components/inputs/ProxyString.tsx +1 -1
  92. package/templates/shopify/components/media/ShopifyDocumentStatus.tsx +3 -1
  93. package/templates/shopify/constants.ts +3 -1
  94. package/templates/shopify/plugins/customDocumentActions/shopifyDelete.tsx +1 -1
  95. package/templates/shopify/plugins/customDocumentActions/shopifyLink.ts +1 -1
  96. package/templates/shopify/schemaTypes/documents/collection.tsx +1 -1
  97. package/templates/shopify/schemaTypes/documents/colorTheme.tsx +1 -1
  98. package/templates/shopify/schemaTypes/documents/page.ts +1 -1
  99. package/templates/shopify/schemaTypes/documents/product.tsx +1 -1
  100. package/templates/shopify/schemaTypes/documents/productVariant.tsx +1 -1
  101. package/templates/shopify/schemaTypes/objects/collection/collectionGroupType.ts +1 -1
  102. package/templates/shopify/schemaTypes/objects/hotspot/imageWithProductHotspotsType.ts +1 -1
  103. package/templates/shopify/schemaTypes/objects/link/linkEmailType.tsx +1 -1
  104. package/templates/shopify/schemaTypes/objects/link/linkExternalType.tsx +1 -1
  105. package/templates/shopify/schemaTypes/objects/link/linkInternalType.tsx +1 -1
  106. package/templates/shopify/schemaTypes/objects/link/linkProductType.tsx +1 -1
  107. package/templates/shopify/schemaTypes/objects/module/accordionType.ts +1 -1
  108. package/templates/shopify/schemaTypes/objects/module/callToActionType.tsx +2 -1
  109. package/templates/shopify/schemaTypes/objects/module/calloutType.ts +1 -1
  110. package/templates/shopify/schemaTypes/objects/module/collectionReferenceType.tsx +1 -1
  111. package/templates/shopify/schemaTypes/objects/module/gridType.ts +1 -1
  112. package/templates/shopify/schemaTypes/objects/module/imageFeatureType.ts +1 -1
  113. package/templates/shopify/schemaTypes/objects/module/imageFeaturesType.tsx +1 -1
  114. package/templates/shopify/schemaTypes/objects/module/instagramType.ts +1 -1
  115. package/templates/shopify/schemaTypes/objects/module/productFeaturesType.tsx +1 -1
  116. package/templates/shopify/schemaTypes/objects/module/productReferenceType.tsx +1 -1
  117. package/templates/shopify/schemaTypes/objects/shopify/collectionRuleType.tsx +1 -1
  118. package/templates/shopify/schemaTypes/objects/shopify/optionType.tsx +1 -1
  119. package/templates/shopify/schemaTypes/objects/shopify/productWithVariantType.tsx +1 -1
  120. package/templates/shopify/schemaTypes/singletons/homeType.ts +1 -1
  121. package/templates/shopify/schemaTypes/singletons/settingsType.ts +5 -1
  122. package/templates/shopify/structure/pageStructure.ts +1 -1
  123. package/templates/shopify/structure/productStructure.ts +1 -1
  124. package/templates/shopify-online-storefront/components/inputs/CollectionHidden.tsx +1 -1
  125. package/templates/shopify-online-storefront/components/inputs/ProductHidden.tsx +1 -1
  126. package/templates/shopify-online-storefront/components/inputs/ProductVariantHidden.tsx +1 -1
  127. package/templates/shopify-online-storefront/components/inputs/ProxyString.tsx +1 -1
  128. package/templates/shopify-online-storefront/components/media/ShopifyDocumentStatus.tsx +3 -1
  129. package/templates/shopify-online-storefront/plugins/shopifyDocumentActions/shopifyDelete.tsx +1 -1
  130. package/templates/shopify-online-storefront/plugins/shopifyDocumentActions/shopifyLink.ts +1 -1
  131. package/templates/shopify-online-storefront/schemaTypes/documents/collection.tsx +1 -1
  132. package/templates/shopify-online-storefront/schemaTypes/documents/product.tsx +1 -1
  133. package/templates/shopify-online-storefront/schemaTypes/documents/productVariant.tsx +1 -1
  134. package/templates/shopify-online-storefront/schemaTypes/objects/accordion.ts +1 -1
  135. package/templates/shopify-online-storefront/schemaTypes/objects/callout.ts +1 -1
  136. package/templates/shopify-online-storefront/schemaTypes/objects/option.ts +1 -1
  137. package/templates/shopify-online-storefront/schemaTypes/objects/shopifyCollectionRule.tsx +1 -1
  138. package/templates/shopify-online-storefront/structure/productStructure.ts +1 -1
@@ -0,0 +1,107 @@
1
+ import path from 'node:path';
2
+ import { subdebug } from '@sanity/cli-core';
3
+ import { spinner } from '@sanity/cli-core/ux';
4
+ import { execa } from 'execa';
5
+ import { installNewPackages } from '../../util/packageManager/installPackages.js';
6
+ import { getPartialEnvWithNpmPath } from '../../util/packageManager/packageManagerChoice.js';
7
+ const debug = subdebug('scaffold');
8
+ const CREATE_NEXT_APP_SPEC = 'create-next-app@^16';
9
+ const NEXT_SANITY_SPEC = 'next-sanity';
10
+ const SCAFFOLD_TIMEOUT_MS = 10 * 60_000;
11
+ const packageManagerFlag = {
12
+ bun: '--use-bun',
13
+ manual: undefined,
14
+ npm: '--use-npm',
15
+ pnpm: '--use-pnpm',
16
+ yarn: '--use-yarn'
17
+ };
18
+ const scaffolderFlags = [
19
+ '--typescript',
20
+ '--app',
21
+ '--eslint',
22
+ '--tailwind',
23
+ '--no-src-dir',
24
+ '--disable-git',
25
+ '--yes'
26
+ ];
27
+ function scaffolderArgs(dirName, packageManager) {
28
+ const pmFlag = packageManager ? packageManagerFlag[packageManager] : undefined;
29
+ return [
30
+ '--yes',
31
+ CREATE_NEXT_APP_SPEC,
32
+ dirName,
33
+ ...scaffolderFlags,
34
+ ...pmFlag ? [
35
+ pmFlag
36
+ ] : []
37
+ ];
38
+ }
39
+ export class FrontendScaffoldError extends Error {
40
+ constructor(message){
41
+ super(message);
42
+ this.name = 'FrontendScaffoldError';
43
+ }
44
+ }
45
+ export async function createFrontend({ cancelSignal, dirName, output, packageManager, workDir }) {
46
+ const args = scaffolderArgs(dirName, packageManager);
47
+ debug('Scaffolding frontend: npx %s', args.join(' '));
48
+ const progress = spinner({
49
+ discardStdin: !cancelSignal,
50
+ text: 'Creating your Next.js app\n'
51
+ }).start();
52
+ let result;
53
+ try {
54
+ result = await execa('npx', args, {
55
+ cancelSignal,
56
+ cwd: workDir,
57
+ encoding: 'utf8',
58
+ env: {
59
+ ...getPartialEnvWithNpmPath(workDir),
60
+ npm_config_yes: 'true'
61
+ },
62
+ reject: false,
63
+ stdio: [
64
+ 'ignore',
65
+ 'pipe',
66
+ 'pipe'
67
+ ],
68
+ timeout: SCAFFOLD_TIMEOUT_MS
69
+ });
70
+ cancelSignal?.throwIfAborted();
71
+ } catch (err) {
72
+ progress.fail();
73
+ cancelSignal?.throwIfAborted();
74
+ throw new FrontendScaffoldError(`create-next-app failed: ${err instanceof Error ? err.message : String(err)}`);
75
+ }
76
+ if (!result || result.failed || result.exitCode) {
77
+ progress.fail();
78
+ const commandOutput = [
79
+ result?.stdout,
80
+ result?.stderr
81
+ ].filter((chunk)=>typeof chunk === 'string' && chunk.length > 0).join('\n');
82
+ if (commandOutput) output.log(commandOutput);
83
+ throw new FrontendScaffoldError(`create-next-app failed${result?.exitCode ? ` with exit code ${result.exitCode}` : ''}`);
84
+ }
85
+ progress.succeed();
86
+ }
87
+ export async function installFrontendDeps({ cancelSignal, dirName, output, packageManager, workDir }) {
88
+ try {
89
+ await installNewPackages({
90
+ packageManager,
91
+ packages: [
92
+ NEXT_SANITY_SPEC
93
+ ]
94
+ }, {
95
+ cancelSignal,
96
+ output,
97
+ timeout: SCAFFOLD_TIMEOUT_MS,
98
+ workDir: path.join(workDir, dirName)
99
+ });
100
+ cancelSignal?.throwIfAborted();
101
+ } catch (err) {
102
+ cancelSignal?.throwIfAborted();
103
+ throw new FrontendScaffoldError(`Installing next-sanity failed: ${err instanceof Error ? err.message : String(err)}`);
104
+ }
105
+ }
106
+
107
+ //# sourceMappingURL=createFrontend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/scaffold/createFrontend.ts"],"sourcesContent":["import path from 'node:path'\n\nimport {type Output, subdebug} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {execa} from 'execa'\n\nimport {installNewPackages} from '../../util/packageManager/installPackages.js'\nimport {\n getPartialEnvWithNpmPath,\n type PackageManager,\n} from '../../util/packageManager/packageManagerChoice.js'\n\nconst debug = subdebug('scaffold')\nconst CREATE_NEXT_APP_SPEC = 'create-next-app@^16'\nconst NEXT_SANITY_SPEC = 'next-sanity'\nconst SCAFFOLD_TIMEOUT_MS = 10 * 60_000\n\nconst packageManagerFlag: Record<PackageManager, string | undefined> = {\n bun: '--use-bun',\n manual: undefined,\n npm: '--use-npm',\n pnpm: '--use-pnpm',\n yarn: '--use-yarn',\n}\n\nconst scaffolderFlags = [\n '--typescript',\n '--app',\n '--eslint',\n '--tailwind',\n '--no-src-dir',\n '--disable-git',\n '--yes',\n]\n\nfunction scaffolderArgs(dirName: string, packageManager?: PackageManager): string[] {\n const pmFlag = packageManager ? packageManagerFlag[packageManager] : undefined\n return ['--yes', CREATE_NEXT_APP_SPEC, dirName, ...scaffolderFlags, ...(pmFlag ? [pmFlag] : [])]\n}\n\nexport class FrontendScaffoldError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'FrontendScaffoldError'\n }\n}\n\nexport async function createFrontend({\n cancelSignal,\n dirName,\n output,\n packageManager,\n workDir,\n}: {\n cancelSignal?: AbortSignal\n dirName: string\n output: Output\n packageManager: PackageManager\n workDir: string\n}): Promise<void> {\n const args = scaffolderArgs(dirName, packageManager)\n debug('Scaffolding frontend: npx %s', args.join(' '))\n\n const progress = spinner({\n discardStdin: !cancelSignal,\n text: 'Creating your Next.js app\\n',\n }).start()\n let result\n try {\n result = await execa('npx', args, {\n cancelSignal,\n cwd: workDir,\n encoding: 'utf8',\n env: {\n ...getPartialEnvWithNpmPath(workDir),\n npm_config_yes: 'true',\n },\n reject: false,\n stdio: ['ignore', 'pipe', 'pipe'],\n timeout: SCAFFOLD_TIMEOUT_MS,\n })\n cancelSignal?.throwIfAborted()\n } catch (err) {\n progress.fail()\n cancelSignal?.throwIfAborted()\n throw new FrontendScaffoldError(\n `create-next-app failed: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n\n if (!result || result.failed || result.exitCode) {\n progress.fail()\n const commandOutput = [result?.stdout, result?.stderr]\n .filter((chunk): chunk is string => typeof chunk === 'string' && chunk.length > 0)\n .join('\\n')\n if (commandOutput) output.log(commandOutput)\n throw new FrontendScaffoldError(\n `create-next-app failed${result?.exitCode ? ` with exit code ${result.exitCode}` : ''}`,\n )\n }\n\n progress.succeed()\n}\n\nexport async function installFrontendDeps({\n cancelSignal,\n dirName,\n output,\n packageManager,\n workDir,\n}: {\n cancelSignal?: AbortSignal\n dirName: string\n output: Output\n packageManager: PackageManager\n workDir: string\n}): Promise<void> {\n try {\n await installNewPackages(\n {packageManager, packages: [NEXT_SANITY_SPEC]},\n {\n cancelSignal,\n output,\n timeout: SCAFFOLD_TIMEOUT_MS,\n workDir: path.join(workDir, dirName),\n },\n )\n cancelSignal?.throwIfAborted()\n } catch (err) {\n cancelSignal?.throwIfAborted()\n throw new FrontendScaffoldError(\n `Installing next-sanity failed: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n}\n"],"names":["path","subdebug","spinner","execa","installNewPackages","getPartialEnvWithNpmPath","debug","CREATE_NEXT_APP_SPEC","NEXT_SANITY_SPEC","SCAFFOLD_TIMEOUT_MS","packageManagerFlag","bun","manual","undefined","npm","pnpm","yarn","scaffolderFlags","scaffolderArgs","dirName","packageManager","pmFlag","FrontendScaffoldError","Error","message","name","createFrontend","cancelSignal","output","workDir","args","join","progress","discardStdin","text","start","result","cwd","encoding","env","npm_config_yes","reject","stdio","timeout","throwIfAborted","err","fail","String","failed","exitCode","commandOutput","stdout","stderr","filter","chunk","length","log","succeed","installFrontendDeps","packages"],"mappings":"AAAA,OAAOA,UAAU,YAAW;AAE5B,SAAqBC,QAAQ,QAAO,mBAAkB;AACtD,SAAQC,OAAO,QAAO,sBAAqB;AAC3C,SAAQC,KAAK,QAAO,QAAO;AAE3B,SAAQC,kBAAkB,QAAO,+CAA8C;AAC/E,SACEC,wBAAwB,QAEnB,oDAAmD;AAE1D,MAAMC,QAAQL,SAAS;AACvB,MAAMM,uBAAuB;AAC7B,MAAMC,mBAAmB;AACzB,MAAMC,sBAAsB,KAAK;AAEjC,MAAMC,qBAAiE;IACrEC,KAAK;IACLC,QAAQC;IACRC,KAAK;IACLC,MAAM;IACNC,MAAM;AACR;AAEA,MAAMC,kBAAkB;IACtB;IACA;IACA;IACA;IACA;IACA;IACA;CACD;AAED,SAASC,eAAeC,OAAe,EAAEC,cAA+B;IACtE,MAAMC,SAASD,iBAAiBV,kBAAkB,CAACU,eAAe,GAAGP;IACrE,OAAO;QAAC;QAASN;QAAsBY;WAAYF;WAAqBI,SAAS;YAACA;SAAO,GAAG,EAAE;KAAE;AAClG;AAEA,OAAO,MAAMC,8BAA8BC;IACzC,YAAYC,OAAe,CAAE;QAC3B,KAAK,CAACA;QACN,IAAI,CAACC,IAAI,GAAG;IACd;AACF;AAEA,OAAO,eAAeC,eAAe,EACnCC,YAAY,EACZR,OAAO,EACPS,MAAM,EACNR,cAAc,EACdS,OAAO,EAOR;IACC,MAAMC,OAAOZ,eAAeC,SAASC;IACrCd,MAAM,gCAAgCwB,KAAKC,IAAI,CAAC;IAEhD,MAAMC,WAAW9B,QAAQ;QACvB+B,cAAc,CAACN;QACfO,MAAM;IACR,GAAGC,KAAK;IACR,IAAIC;IACJ,IAAI;QACFA,SAAS,MAAMjC,MAAM,OAAO2B,MAAM;YAChCH;YACAU,KAAKR;YACLS,UAAU;YACVC,KAAK;gBACH,GAAGlC,yBAAyBwB,QAAQ;gBACpCW,gBAAgB;YAClB;YACAC,QAAQ;YACRC,OAAO;gBAAC;gBAAU;gBAAQ;aAAO;YACjCC,SAASlC;QACX;QACAkB,cAAciB;IAChB,EAAE,OAAOC,KAAK;QACZb,SAASc,IAAI;QACbnB,cAAciB;QACd,MAAM,IAAItB,sBACR,CAAC,wBAAwB,EAAEuB,eAAetB,QAAQsB,IAAIrB,OAAO,GAAGuB,OAAOF,MAAM;IAEjF;IAEA,IAAI,CAACT,UAAUA,OAAOY,MAAM,IAAIZ,OAAOa,QAAQ,EAAE;QAC/CjB,SAASc,IAAI;QACb,MAAMI,gBAAgB;YAACd,QAAQe;YAAQf,QAAQgB;SAAO,CACnDC,MAAM,CAAC,CAACC,QAA2B,OAAOA,UAAU,YAAYA,MAAMC,MAAM,GAAG,GAC/ExB,IAAI,CAAC;QACR,IAAImB,eAAetB,OAAO4B,GAAG,CAACN;QAC9B,MAAM,IAAI5B,sBACR,CAAC,sBAAsB,EAAEc,QAAQa,WAAW,CAAC,gBAAgB,EAAEb,OAAOa,QAAQ,EAAE,GAAG,IAAI;IAE3F;IAEAjB,SAASyB,OAAO;AAClB;AAEA,OAAO,eAAeC,oBAAoB,EACxC/B,YAAY,EACZR,OAAO,EACPS,MAAM,EACNR,cAAc,EACdS,OAAO,EAOR;IACC,IAAI;QACF,MAAMzB,mBACJ;YAACgB;YAAgBuC,UAAU;gBAACnD;aAAiB;QAAA,GAC7C;YACEmB;YACAC;YACAe,SAASlC;YACToB,SAAS7B,KAAK+B,IAAI,CAACF,SAASV;QAC9B;QAEFQ,cAAciB;IAChB,EAAE,OAAOC,KAAK;QACZlB,cAAciB;QACd,MAAM,IAAItB,sBACR,CAAC,+BAA+B,EAAEuB,eAAetB,QAAQsB,IAAIrB,OAAO,GAAGuB,OAAOF,MAAM;IAExF;AACF"}
@@ -0,0 +1,202 @@
1
+ import { writeFileSync } from 'node:fs';
2
+ import { lstat } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { subdebug } from '@sanity/cli-core';
5
+ import { getErrorMessage } from '@sanity/cli-core/errors';
6
+ import { frameworks } from '@vercel/frameworks';
7
+ import { CLIInitStepCompleted } from '../../telemetry/init.telemetry.js';
8
+ import { detectFrameworkRecord } from '../../util/detectFramework.js';
9
+ import { dirIsEmptyOrNonExistent } from '../../util/dirIsEmptyOrNonExistent.js';
10
+ import { getProjectDefaults } from '../../util/getProjectDefaults.js';
11
+ import { initStudio } from '../init/initStudio.js';
12
+ import { resolvePackageManager } from '../init/resolvePackageManager.js';
13
+ import { createFrontend, FrontendScaffoldError, installFrontendDeps } from './createFrontend.js';
14
+ const debug = subdebug('scaffold');
15
+ export const STUDIO_DIR = 'sanity';
16
+ export const FRONTEND_DIR = 'web';
17
+ export const STUDIO_ENV_FILE = `${STUDIO_DIR}/.env.local`;
18
+ export const FRONTEND_ENV_FILE = `${FRONTEND_DIR}/.env.local`;
19
+ const STUDIO_PACKAGE_NAME = 'sanity-studio';
20
+ const STUDIO_PACKAGE_MANAGERS = new Set([
21
+ 'npm',
22
+ 'pnpm',
23
+ 'yarn'
24
+ ]);
25
+ export async function isStudioScaffoldTargetAvailable(workDir) {
26
+ return isScaffoldTargetAvailable(workDir, STUDIO_DIR);
27
+ }
28
+ async function isScaffoldTargetAvailable(workDir, targetDir) {
29
+ const targetPath = path.join(workDir, targetDir);
30
+ try {
31
+ if (!(await lstat(targetPath)).isDirectory()) {
32
+ return false;
33
+ }
34
+ } catch (err) {
35
+ if (err.code !== 'ENOENT') {
36
+ throw err;
37
+ }
38
+ }
39
+ return dirIsEmptyOrNonExistent(targetPath);
40
+ }
41
+ async function inspectScaffoldTargets(workDir) {
42
+ if (!await isStudioScaffoldTargetAvailable(workDir)) {
43
+ return {
44
+ unavailableTarget: STUDIO_DIR
45
+ };
46
+ }
47
+ const detectedFramework = await detectFrameworkRecord({
48
+ frameworkList: frameworks,
49
+ rootPath: workDir
50
+ });
51
+ if (!detectedFramework && !await isScaffoldTargetAvailable(workDir, FRONTEND_DIR)) {
52
+ return {
53
+ unavailableTarget: FRONTEND_DIR
54
+ };
55
+ }
56
+ return {
57
+ detectedFramework
58
+ };
59
+ }
60
+ export async function getUnavailableScaffoldTarget(workDir) {
61
+ const { unavailableTarget } = await inspectScaffoldTargets(workDir);
62
+ return unavailableTarget;
63
+ }
64
+ function createScaffoldEnv(dataset, projectId, token) {
65
+ return {
66
+ SANITY_AUTH_TOKEN: token,
67
+ SANITY_DATASET: dataset,
68
+ SANITY_PROJECT_ID: projectId
69
+ };
70
+ }
71
+ export async function scaffoldProject({ cancelSignal, dataset, displayName, output, packageManager, projectId, telemetry, token, workDir }) {
72
+ cancelSignal?.throwIfAborted();
73
+ const { detectedFramework: detected, unavailableTarget } = await inspectScaffoldTargets(workDir);
74
+ const studioPath = path.join(workDir, STUDIO_DIR);
75
+ if (unavailableTarget) {
76
+ throw new Error(`./${unavailableTarget} is not an empty directory`);
77
+ }
78
+ const resolvedPackageManager = await resolvePackageManager({
79
+ interactive: false,
80
+ output,
81
+ packageManager: packageManager,
82
+ targetDir: workDir
83
+ });
84
+ const options = {
85
+ autoUpdates: true,
86
+ bare: false,
87
+ dataset,
88
+ datasetDefault: true,
89
+ fromCreate: false,
90
+ git: false,
91
+ mcpMode: 'skip',
92
+ outputPath: studioPath,
93
+ packageManager: STUDIO_PACKAGE_MANAGERS.has(resolvedPackageManager) ? resolvedPackageManager : undefined,
94
+ project: projectId,
95
+ skillsMode: 'skip',
96
+ template: 'clean',
97
+ typescript: true,
98
+ unattended: true
99
+ };
100
+ const trace = telemetry.trace(CLIInitStepCompleted);
101
+ trace.start();
102
+ try {
103
+ // To avoid changing Studio initialization internals, we're deferring passing `cancelSignal`
104
+ // until post `sanity new` launch.
105
+ await initStudio({
106
+ datasetName: dataset,
107
+ defaults: await getProjectDefaults({
108
+ isPlugin: false,
109
+ workDir
110
+ }),
111
+ displayName,
112
+ isFirstProject: false,
113
+ mcpConfigured: [],
114
+ options,
115
+ organizationId: undefined,
116
+ output,
117
+ outputPath: studioPath,
118
+ preclaim: true,
119
+ projectId,
120
+ remoteTemplateInfo: undefined,
121
+ sluggedName: STUDIO_PACKAGE_NAME,
122
+ trace,
123
+ workbench: false,
124
+ workDir
125
+ });
126
+ trace.complete();
127
+ } catch (err) {
128
+ trace.error(err instanceof Error ? err : new Error(String(err)));
129
+ throw err;
130
+ }
131
+ cancelSignal?.throwIfAborted();
132
+ const scaffoldEnv = createScaffoldEnv(dataset, projectId, token);
133
+ writeScaffoldEnv(path.join(studioPath, '.env.local'), scaffoldEnv, `Studio scaffold completed, but writing ./${STUDIO_ENV_FILE} failed`);
134
+ if (detected) {
135
+ debug('Detected %s in %s, leaving the frontend alone', detected.name, workDir);
136
+ return {
137
+ detectedFramework: detected.name,
138
+ studioPath
139
+ };
140
+ }
141
+ try {
142
+ await createFrontend({
143
+ cancelSignal,
144
+ dirName: FRONTEND_DIR,
145
+ output,
146
+ packageManager: resolvedPackageManager,
147
+ workDir
148
+ });
149
+ cancelSignal?.throwIfAborted();
150
+ } catch (err) {
151
+ if (err instanceof FrontendScaffoldError) {
152
+ return {
153
+ frontendCreationError: err.message,
154
+ frontendPackageManager: resolvedPackageManager,
155
+ studioPath
156
+ };
157
+ }
158
+ throw err;
159
+ }
160
+ const frontendPath = path.join(workDir, FRONTEND_DIR);
161
+ writeScaffoldEnv(path.join(frontendPath, '.env.local'), scaffoldEnv, `Website scaffold completed, but writing ./${FRONTEND_ENV_FILE} failed`);
162
+ let frontendDependenciesInstalled = resolvedPackageManager !== 'manual';
163
+ let frontendDependencyError;
164
+ if (frontendDependenciesInstalled) {
165
+ try {
166
+ await installFrontendDeps({
167
+ cancelSignal,
168
+ dirName: FRONTEND_DIR,
169
+ output,
170
+ packageManager: resolvedPackageManager,
171
+ workDir
172
+ });
173
+ cancelSignal?.throwIfAborted();
174
+ } catch (err) {
175
+ if (!(err instanceof FrontendScaffoldError)) throw err;
176
+ frontendDependenciesInstalled = false;
177
+ frontendDependencyError = err.message;
178
+ }
179
+ }
180
+ return {
181
+ frontendDependenciesInstalled,
182
+ frontendDependencyError,
183
+ frontendPackageManager: resolvedPackageManager,
184
+ frontendPath,
185
+ studioPath
186
+ };
187
+ }
188
+ function writeScaffoldEnv(envPath, values, errorContext) {
189
+ const lines = [
190
+ '# Added by `sanity new`. Keep this file out of git: it holds a live project token.',
191
+ ...Object.entries(values).map(([key, value])=>`${key}="${value}"`)
192
+ ];
193
+ try {
194
+ writeFileSync(envPath, `${lines.join('\n')}\n`, 'utf8');
195
+ } catch (err) {
196
+ throw new Error(`${errorContext}: ${getErrorMessage(err)}`, {
197
+ cause: err
198
+ });
199
+ }
200
+ }
201
+
202
+ //# sourceMappingURL=scaffoldProject.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/scaffold/scaffoldProject.ts"],"sourcesContent":["import {writeFileSync} from 'node:fs'\nimport {lstat} from 'node:fs/promises'\nimport path from 'node:path'\n\nimport {type CLITelemetryStore, type Output, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {type Framework, frameworks} from '@vercel/frameworks'\n\nimport {CLIInitStepCompleted} from '../../telemetry/init.telemetry.js'\nimport {detectFrameworkRecord} from '../../util/detectFramework.js'\nimport {dirIsEmptyOrNonExistent} from '../../util/dirIsEmptyOrNonExistent.js'\nimport {getProjectDefaults} from '../../util/getProjectDefaults.js'\nimport {type PackageManager} from '../../util/packageManager/packageManagerChoice.js'\nimport {initStudio} from '../init/initStudio.js'\nimport {resolvePackageManager} from '../init/resolvePackageManager.js'\nimport {type InitOptions} from '../init/types.js'\nimport {createFrontend, FrontendScaffoldError, installFrontendDeps} from './createFrontend.js'\n\nconst debug = subdebug('scaffold')\n\nexport const STUDIO_DIR = 'sanity'\nexport const FRONTEND_DIR = 'web'\nexport const STUDIO_ENV_FILE = `${STUDIO_DIR}/.env.local`\nexport const FRONTEND_ENV_FILE = `${FRONTEND_DIR}/.env.local`\n\nconst STUDIO_PACKAGE_NAME = 'sanity-studio'\nconst STUDIO_PACKAGE_MANAGERS = new Set<PackageManager>(['npm', 'pnpm', 'yarn'])\n\nexport async function isStudioScaffoldTargetAvailable(workDir: string): Promise<boolean> {\n return isScaffoldTargetAvailable(workDir, STUDIO_DIR)\n}\n\nasync function isScaffoldTargetAvailable(workDir: string, targetDir: string): Promise<boolean> {\n const targetPath = path.join(workDir, targetDir)\n try {\n if (!(await lstat(targetPath)).isDirectory()) {\n return false\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw err\n }\n }\n\n return dirIsEmptyOrNonExistent(targetPath)\n}\n\nasync function inspectScaffoldTargets(workDir: string) {\n if (!(await isStudioScaffoldTargetAvailable(workDir))) {\n return {unavailableTarget: STUDIO_DIR}\n }\n\n const detectedFramework = await detectFrameworkRecord({\n frameworkList: frameworks as readonly Framework[],\n rootPath: workDir,\n })\n if (!detectedFramework && !(await isScaffoldTargetAvailable(workDir, FRONTEND_DIR))) {\n return {unavailableTarget: FRONTEND_DIR}\n }\n\n return {detectedFramework}\n}\n\nexport async function getUnavailableScaffoldTarget(workDir: string): Promise<string | undefined> {\n const {unavailableTarget} = await inspectScaffoldTargets(workDir)\n return unavailableTarget\n}\n\nexport interface ScaffoldResult {\n studioPath: string\n\n detectedFramework?: string\n frontendCreationError?: string\n frontendDependenciesInstalled?: boolean\n frontendDependencyError?: string\n frontendPackageManager?: PackageManager\n frontendPath?: string\n}\n\nfunction createScaffoldEnv(dataset: string, projectId: string, token: string) {\n return {\n SANITY_AUTH_TOKEN: token,\n SANITY_DATASET: dataset,\n SANITY_PROJECT_ID: projectId,\n }\n}\n\nexport async function scaffoldProject({\n cancelSignal,\n dataset,\n displayName,\n output,\n packageManager,\n projectId,\n telemetry,\n token,\n workDir,\n}: {\n cancelSignal?: AbortSignal\n dataset: string\n displayName: string\n output: Output\n packageManager?: PackageManager\n projectId: string\n telemetry: CLITelemetryStore\n token: string\n workDir: string\n}): Promise<ScaffoldResult> {\n cancelSignal?.throwIfAborted()\n const {detectedFramework: detected, unavailableTarget} = await inspectScaffoldTargets(workDir)\n const studioPath = path.join(workDir, STUDIO_DIR)\n if (unavailableTarget) {\n throw new Error(`./${unavailableTarget} is not an empty directory`)\n }\n\n const resolvedPackageManager = await resolvePackageManager({\n interactive: false,\n output,\n packageManager: packageManager as PackageManager,\n targetDir: workDir,\n })\n\n const options: InitOptions = {\n autoUpdates: true,\n bare: false,\n dataset,\n datasetDefault: true,\n fromCreate: false,\n git: false,\n mcpMode: 'skip',\n outputPath: studioPath,\n packageManager: STUDIO_PACKAGE_MANAGERS.has(resolvedPackageManager)\n ? (resolvedPackageManager as InitOptions['packageManager'])\n : undefined,\n project: projectId,\n skillsMode: 'skip',\n template: 'clean',\n typescript: true,\n unattended: true,\n }\n\n const trace = telemetry.trace(CLIInitStepCompleted)\n trace.start()\n try {\n // To avoid changing Studio initialization internals, we're deferring passing `cancelSignal`\n // until post `sanity new` launch.\n await initStudio({\n datasetName: dataset,\n defaults: await getProjectDefaults({isPlugin: false, workDir}),\n displayName,\n isFirstProject: false,\n mcpConfigured: [],\n options,\n organizationId: undefined,\n output,\n outputPath: studioPath,\n preclaim: true,\n projectId,\n remoteTemplateInfo: undefined,\n sluggedName: STUDIO_PACKAGE_NAME,\n trace,\n workbench: false,\n workDir,\n })\n trace.complete()\n } catch (err) {\n trace.error(err instanceof Error ? err : new Error(String(err)))\n throw err\n }\n\n cancelSignal?.throwIfAborted()\n const scaffoldEnv = createScaffoldEnv(dataset, projectId, token)\n writeScaffoldEnv(\n path.join(studioPath, '.env.local'),\n scaffoldEnv,\n `Studio scaffold completed, but writing ./${STUDIO_ENV_FILE} failed`,\n )\n\n if (detected) {\n debug('Detected %s in %s, leaving the frontend alone', detected.name, workDir)\n return {\n detectedFramework: detected.name,\n studioPath,\n }\n }\n\n try {\n await createFrontend({\n cancelSignal,\n dirName: FRONTEND_DIR,\n output,\n packageManager: resolvedPackageManager,\n workDir,\n })\n cancelSignal?.throwIfAborted()\n } catch (err) {\n if (err instanceof FrontendScaffoldError) {\n return {\n frontendCreationError: err.message,\n frontendPackageManager: resolvedPackageManager,\n studioPath,\n }\n }\n throw err\n }\n\n const frontendPath = path.join(workDir, FRONTEND_DIR)\n writeScaffoldEnv(\n path.join(frontendPath, '.env.local'),\n scaffoldEnv,\n `Website scaffold completed, but writing ./${FRONTEND_ENV_FILE} failed`,\n )\n\n let frontendDependenciesInstalled = resolvedPackageManager !== 'manual'\n let frontendDependencyError: string | undefined\n if (frontendDependenciesInstalled) {\n try {\n await installFrontendDeps({\n cancelSignal,\n dirName: FRONTEND_DIR,\n output,\n packageManager: resolvedPackageManager,\n workDir,\n })\n cancelSignal?.throwIfAborted()\n } catch (err) {\n if (!(err instanceof FrontendScaffoldError)) throw err\n frontendDependenciesInstalled = false\n frontendDependencyError = err.message\n }\n }\n\n return {\n frontendDependenciesInstalled,\n frontendDependencyError,\n frontendPackageManager: resolvedPackageManager,\n frontendPath,\n studioPath,\n }\n}\n\nfunction writeScaffoldEnv(\n envPath: string,\n values: Record<string, string>,\n errorContext: string,\n): void {\n const lines = [\n '# Added by `sanity new`. Keep this file out of git: it holds a live project token.',\n ...Object.entries(values).map(([key, value]) => `${key}=\"${value}\"`),\n ]\n try {\n writeFileSync(envPath, `${lines.join('\\n')}\\n`, 'utf8')\n } catch (err) {\n throw new Error(`${errorContext}: ${getErrorMessage(err)}`, {cause: err})\n }\n}\n"],"names":["writeFileSync","lstat","path","subdebug","getErrorMessage","frameworks","CLIInitStepCompleted","detectFrameworkRecord","dirIsEmptyOrNonExistent","getProjectDefaults","initStudio","resolvePackageManager","createFrontend","FrontendScaffoldError","installFrontendDeps","debug","STUDIO_DIR","FRONTEND_DIR","STUDIO_ENV_FILE","FRONTEND_ENV_FILE","STUDIO_PACKAGE_NAME","STUDIO_PACKAGE_MANAGERS","Set","isStudioScaffoldTargetAvailable","workDir","isScaffoldTargetAvailable","targetDir","targetPath","join","isDirectory","err","code","inspectScaffoldTargets","unavailableTarget","detectedFramework","frameworkList","rootPath","getUnavailableScaffoldTarget","createScaffoldEnv","dataset","projectId","token","SANITY_AUTH_TOKEN","SANITY_DATASET","SANITY_PROJECT_ID","scaffoldProject","cancelSignal","displayName","output","packageManager","telemetry","throwIfAborted","detected","studioPath","Error","resolvedPackageManager","interactive","options","autoUpdates","bare","datasetDefault","fromCreate","git","mcpMode","outputPath","has","undefined","project","skillsMode","template","typescript","unattended","trace","start","datasetName","defaults","isPlugin","isFirstProject","mcpConfigured","organizationId","preclaim","remoteTemplateInfo","sluggedName","workbench","complete","error","String","scaffoldEnv","writeScaffoldEnv","name","dirName","frontendCreationError","message","frontendPackageManager","frontendPath","frontendDependenciesInstalled","frontendDependencyError","envPath","values","errorContext","lines","Object","entries","map","key","value","cause"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { getCliToken, isCi } from '@sanity/cli-core';
2
+ import { getCliExecutionContext } from '@sanity/cli-core/executionContext';
2
3
  import { fetchTelemetryConsent, isValidApiConsentStatus, VALID_API_STATUSES } from '../../services/telemetry.js';
3
4
  import { isTrueish } from './isTrueish.js';
4
5
  import { telemetryDebug } from './telemetryDebug.js';
@@ -10,13 +11,14 @@ function parseApiConsentStatus(value) {
10
11
  }
11
12
  export async function resolveConsent() {
12
13
  telemetryDebug('Resolving consent…');
13
- if (isCi()) {
14
+ const hasCliExecutionContext = Boolean(getCliExecutionContext());
15
+ if (!hasCliExecutionContext && isCi()) {
14
16
  telemetryDebug('CI environment detected, treating telemetry consent as denied');
15
17
  return {
16
18
  status: 'denied'
17
19
  };
18
20
  }
19
- if (isTrueish(process.env.DO_NOT_TRACK)) {
21
+ if (!hasCliExecutionContext && isTrueish(process.env.DO_NOT_TRACK)) {
20
22
  telemetryDebug('DO_NOT_TRACK is set, consent is denied');
21
23
  return {
22
24
  reason: 'localOverride',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/telemetry/resolveConsent.ts"],"sourcesContent":["import {type ConsentInformation, getCliToken, isCi} from '@sanity/cli-core'\n\nimport {\n fetchTelemetryConsent,\n isValidApiConsentStatus,\n VALID_API_STATUSES,\n type ValidApiConsentStatus,\n} from '../../services/telemetry.js'\nimport {isTrueish} from './isTrueish.js'\nimport {telemetryDebug} from './telemetryDebug.js'\n\nfunction parseApiConsentStatus(value: unknown): ValidApiConsentStatus {\n if (typeof value === 'string' && isValidApiConsentStatus(value)) {\n return value\n }\n throw new Error(`Invalid consent status. Must be one of: ${VALID_API_STATUSES.join(', ')}`)\n}\n\nexport async function resolveConsent(): Promise<ConsentInformation> {\n telemetryDebug('Resolving consent…')\n if (isCi()) {\n telemetryDebug('CI environment detected, treating telemetry consent as denied')\n return {status: 'denied'}\n }\n\n if (isTrueish(process.env.DO_NOT_TRACK)) {\n telemetryDebug('DO_NOT_TRACK is set, consent is denied')\n return {\n reason: 'localOverride',\n status: 'denied',\n }\n }\n\n const token = await getCliToken()\n if (!token) {\n telemetryDebug('User is not logged in, consent is undetermined')\n return {\n reason: 'unauthenticated',\n status: 'undetermined',\n }\n }\n\n try {\n const response = await fetchTelemetryConsent()\n\n telemetryDebug('User consent status is %s', response.status)\n return {status: parseApiConsentStatus(response.status)}\n } catch (err) {\n telemetryDebug('Failed to fetch user consent status, treating it as \"undetermined\": %s', err)\n return {\n reason: 'fetchError',\n status: 'undetermined',\n }\n }\n}\n"],"names":["getCliToken","isCi","fetchTelemetryConsent","isValidApiConsentStatus","VALID_API_STATUSES","isTrueish","telemetryDebug","parseApiConsentStatus","value","Error","join","resolveConsent","status","process","env","DO_NOT_TRACK","reason","token","response","err"],"mappings":"AAAA,SAAiCA,WAAW,EAAEC,IAAI,QAAO,mBAAkB;AAE3E,SACEC,qBAAqB,EACrBC,uBAAuB,EACvBC,kBAAkB,QAEb,8BAA6B;AACpC,SAAQC,SAAS,QAAO,iBAAgB;AACxC,SAAQC,cAAc,QAAO,sBAAqB;AAElD,SAASC,sBAAsBC,KAAc;IAC3C,IAAI,OAAOA,UAAU,YAAYL,wBAAwBK,QAAQ;QAC/D,OAAOA;IACT;IACA,MAAM,IAAIC,MAAM,CAAC,wCAAwC,EAAEL,mBAAmBM,IAAI,CAAC,OAAO;AAC5F;AAEA,OAAO,eAAeC;IACpBL,eAAe;IACf,IAAIL,QAAQ;QACVK,eAAe;QACf,OAAO;YAACM,QAAQ;QAAQ;IAC1B;IAEA,IAAIP,UAAUQ,QAAQC,GAAG,CAACC,YAAY,GAAG;QACvCT,eAAe;QACf,OAAO;YACLU,QAAQ;YACRJ,QAAQ;QACV;IACF;IAEA,MAAMK,QAAQ,MAAMjB;IACpB,IAAI,CAACiB,OAAO;QACVX,eAAe;QACf,OAAO;YACLU,QAAQ;YACRJ,QAAQ;QACV;IACF;IAEA,IAAI;QACF,MAAMM,WAAW,MAAMhB;QAEvBI,eAAe,6BAA6BY,SAASN,MAAM;QAC3D,OAAO;YAACA,QAAQL,sBAAsBW,SAASN,MAAM;QAAC;IACxD,EAAE,OAAOO,KAAK;QACZb,eAAe,0EAA0Ea;QACzF,OAAO;YACLH,QAAQ;YACRJ,QAAQ;QACV;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/telemetry/resolveConsent.ts"],"sourcesContent":["import {type ConsentInformation, getCliToken, isCi} from '@sanity/cli-core'\nimport {getCliExecutionContext} from '@sanity/cli-core/executionContext'\n\nimport {\n fetchTelemetryConsent,\n isValidApiConsentStatus,\n VALID_API_STATUSES,\n type ValidApiConsentStatus,\n} from '../../services/telemetry.js'\nimport {isTrueish} from './isTrueish.js'\nimport {telemetryDebug} from './telemetryDebug.js'\n\nfunction parseApiConsentStatus(value: unknown): ValidApiConsentStatus {\n if (typeof value === 'string' && isValidApiConsentStatus(value)) {\n return value\n }\n throw new Error(`Invalid consent status. Must be one of: ${VALID_API_STATUSES.join(', ')}`)\n}\n\nexport async function resolveConsent(): Promise<ConsentInformation> {\n telemetryDebug('Resolving consent…')\n const hasCliExecutionContext = Boolean(getCliExecutionContext())\n\n if (!hasCliExecutionContext && isCi()) {\n telemetryDebug('CI environment detected, treating telemetry consent as denied')\n return {status: 'denied'}\n }\n\n if (!hasCliExecutionContext && isTrueish(process.env.DO_NOT_TRACK)) {\n telemetryDebug('DO_NOT_TRACK is set, consent is denied')\n return {\n reason: 'localOverride',\n status: 'denied',\n }\n }\n\n const token = await getCliToken()\n if (!token) {\n telemetryDebug('User is not logged in, consent is undetermined')\n return {\n reason: 'unauthenticated',\n status: 'undetermined',\n }\n }\n\n try {\n const response = await fetchTelemetryConsent()\n\n telemetryDebug('User consent status is %s', response.status)\n return {status: parseApiConsentStatus(response.status)}\n } catch (err) {\n telemetryDebug('Failed to fetch user consent status, treating it as \"undetermined\": %s', err)\n return {\n reason: 'fetchError',\n status: 'undetermined',\n }\n }\n}\n"],"names":["getCliToken","isCi","getCliExecutionContext","fetchTelemetryConsent","isValidApiConsentStatus","VALID_API_STATUSES","isTrueish","telemetryDebug","parseApiConsentStatus","value","Error","join","resolveConsent","hasCliExecutionContext","Boolean","status","process","env","DO_NOT_TRACK","reason","token","response","err"],"mappings":"AAAA,SAAiCA,WAAW,EAAEC,IAAI,QAAO,mBAAkB;AAC3E,SAAQC,sBAAsB,QAAO,oCAAmC;AAExE,SACEC,qBAAqB,EACrBC,uBAAuB,EACvBC,kBAAkB,QAEb,8BAA6B;AACpC,SAAQC,SAAS,QAAO,iBAAgB;AACxC,SAAQC,cAAc,QAAO,sBAAqB;AAElD,SAASC,sBAAsBC,KAAc;IAC3C,IAAI,OAAOA,UAAU,YAAYL,wBAAwBK,QAAQ;QAC/D,OAAOA;IACT;IACA,MAAM,IAAIC,MAAM,CAAC,wCAAwC,EAAEL,mBAAmBM,IAAI,CAAC,OAAO;AAC5F;AAEA,OAAO,eAAeC;IACpBL,eAAe;IACf,MAAMM,yBAAyBC,QAAQZ;IAEvC,IAAI,CAACW,0BAA0BZ,QAAQ;QACrCM,eAAe;QACf,OAAO;YAACQ,QAAQ;QAAQ;IAC1B;IAEA,IAAI,CAACF,0BAA0BP,UAAUU,QAAQC,GAAG,CAACC,YAAY,GAAG;QAClEX,eAAe;QACf,OAAO;YACLY,QAAQ;YACRJ,QAAQ;QACV;IACF;IAEA,MAAMK,QAAQ,MAAMpB;IACpB,IAAI,CAACoB,OAAO;QACVb,eAAe;QACf,OAAO;YACLY,QAAQ;YACRJ,QAAQ;QACV;IACF;IAEA,IAAI;QACF,MAAMM,WAAW,MAAMlB;QAEvBI,eAAe,6BAA6Bc,SAASN,MAAM;QAC3D,OAAO;YAACA,QAAQP,sBAAsBa,SAASN,MAAM;QAAC;IACxD,EAAE,OAAOO,KAAK;QACZf,eAAe,0EAA0Ee;QACzF,OAAO;YACLH,QAAQ;YACRJ,QAAQ;QACV;IACF;AACF"}
@@ -166,7 +166,7 @@ export class ListBackupCommand extends SanityCommand {
166
166
  resource: 'Dataset'
167
167
  });
168
168
  }
169
- table.printTable();
169
+ this.log(table.render());
170
170
  listBackupDebug(`Successfully listed ${response.backups.length} backups for dataset ${dataset}`);
171
171
  } catch (error) {
172
172
  const message = error instanceof Error ? error.message : String(error);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/backups/list.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\nimport {Table} from 'console-table-printer'\nimport {isAfter} from 'date-fns/isAfter'\nimport {isValid} from 'date-fns/isValid'\nimport {lightFormat} from 'date-fns/lightFormat'\nimport {parse} from 'date-fns/parse'\n\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {listBackups} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst listBackupDebug = subdebug('backup:list')\n\nconst DEFAULT_LIST_BACKUP_LIMIT = 30\n\ntype ListBackupRequestQueryParams = {\n after?: string\n before?: string\n limit: string\n}\n\nexport class ListBackupCommand extends SanityCommand<typeof ListBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to list backups for',\n required: false,\n }),\n }\n\n static override description = 'List available backups for a dataset'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List backups for a dataset interactively',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'List backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --limit 50',\n description: 'List up to 50 backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --after 2024-01-31 --limit 10',\n description: 'List up to 10 backups created after 2024-01-31',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to list backups for',\n semantics: 'override',\n }),\n after: Flags.string({\n description: 'Only return backups after this date (inclusive, YYYY-MM-DD format)',\n }),\n before: Flags.string({\n description: 'Only return backups before this date (exclusive, YYYY-MM-DD format)',\n }),\n limit: Flags.integer({\n char: 'l',\n default: DEFAULT_LIST_BACKUP_LIMIT,\n description: 'Maximum number of backups returned',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:list']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(ListBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'read', permission: 'sanity.project.datasets'}],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else if (this.isUnattended()) {\n this.error('Dataset is required in unattended mode. Pass it as the `<dataset>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n } else {\n dataset = await this.promptForDataset(datasets)\n }\n\n // Validate date flags\n if (flags.before || flags.after) {\n try {\n const parsedBefore = this.processDateFlag(flags.before, 'before')\n const parsedAfter = this.processDateFlag(flags.after, 'after')\n\n if (parsedAfter && parsedBefore && isAfter(parsedAfter, parsedBefore)) {\n this.error('--after date must be before --before', {exit: exitCodes.RUNTIME_ERROR})\n }\n } catch (err) {\n this.error(`Parsing date flags: ${err instanceof Error ? err.message : err}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n // Validate limit flag\n if (flags.limit < 1 || flags.limit > Number.MAX_SAFE_INTEGER) {\n this.error(`Parsing --limit: must be an integer between 1 and ${Number.MAX_SAFE_INTEGER}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const query: ListBackupRequestQueryParams = {\n limit: flags.limit.toString(),\n }\n\n if (flags.after) {\n query.after = flags.after\n }\n\n if (flags.before) {\n query.before = flags.before\n }\n\n try {\n const response = await listBackups({\n after: flags.after,\n before: flags.before,\n datasetName: dataset,\n limit: flags.limit,\n projectId,\n })\n\n if (response.backups.length === 0) {\n this.log('No backups found.')\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'resource', title: 'RESOURCE'},\n {alignment: 'left', name: 'createdAt', title: 'CREATED AT'},\n {alignment: 'left', name: 'backupId', title: 'BACKUP ID'},\n ],\n })\n\n for (const backup of response.backups) {\n const {createdAt, id} = backup\n table.addRow({\n backupId: id,\n createdAt: lightFormat(Date.parse(createdAt), 'yyyy-MM-dd HH:mm:ss'),\n resource: 'Dataset',\n })\n }\n\n table.printTable()\n\n listBackupDebug(\n `Successfully listed ${response.backups.length} backups for dataset ${dataset}`,\n )\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list backups for dataset ${dataset}:`, error)\n this.error(`List dataset backup failed: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n private processDateFlag(date: string | undefined, flagName: string): Date | undefined {\n if (!date) return undefined\n const parsedDate = parse(date, 'yyyy-MM-dd', new Date())\n if (isValid(parsedDate)) {\n return parsedDate\n }\n\n throw new Error(`Invalid date format for '--${flagName}' flag. Use YYYY-MM-DD`)\n }\n\n private async promptForDataset(datasets: DatasetsResponse): Promise<string> {\n try {\n const choices = datasets.map((dataset) => ({\n name: dataset.name,\n value: dataset.name,\n }))\n\n return select({\n choices,\n message: 'Select the dataset name:',\n })\n } catch (error) {\n listBackupDebug(`Error selecting dataset`, error)\n this.error(`Failed to select dataset:\\n${error instanceof Error ? error.message : error}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","select","Table","isAfter","isValid","lightFormat","parse","assertDatasetExists","promptForProject","listBackups","listDatasets","getProjectIdFlag","listBackupDebug","DEFAULT_LIST_BACKUP_LIMIT","ListBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","after","before","limit","integer","char","default","hiddenAliases","run","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","RUNTIME_ERROR","length","output","isUnattended","USAGE_ERROR","promptForDataset","parsedBefore","processDateFlag","parsedAfter","err","Number","MAX_SAFE_INTEGER","query","toString","response","datasetName","backups","log","table","columns","alignment","name","title","backup","createdAt","id","addRow","backupId","Date","resource","printTable","date","flagName","undefined","parsedDate","choices","map","value"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/commands/backups/list.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\nimport {Table} from 'console-table-printer'\nimport {isAfter} from 'date-fns/isAfter'\nimport {isValid} from 'date-fns/isValid'\nimport {lightFormat} from 'date-fns/lightFormat'\nimport {parse} from 'date-fns/parse'\n\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {listBackups} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst listBackupDebug = subdebug('backup:list')\n\nconst DEFAULT_LIST_BACKUP_LIMIT = 30\n\ntype ListBackupRequestQueryParams = {\n after?: string\n before?: string\n limit: string\n}\n\nexport class ListBackupCommand extends SanityCommand<typeof ListBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to list backups for',\n required: false,\n }),\n }\n\n static override description = 'List available backups for a dataset'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List backups for a dataset interactively',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'List backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --limit 50',\n description: 'List up to 50 backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --after 2024-01-31 --limit 10',\n description: 'List up to 10 backups created after 2024-01-31',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to list backups for',\n semantics: 'override',\n }),\n after: Flags.string({\n description: 'Only return backups after this date (inclusive, YYYY-MM-DD format)',\n }),\n before: Flags.string({\n description: 'Only return backups before this date (exclusive, YYYY-MM-DD format)',\n }),\n limit: Flags.integer({\n char: 'l',\n default: DEFAULT_LIST_BACKUP_LIMIT,\n description: 'Maximum number of backups returned',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:list']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(ListBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'read', permission: 'sanity.project.datasets'}],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else if (this.isUnattended()) {\n this.error('Dataset is required in unattended mode. Pass it as the `<dataset>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n } else {\n dataset = await this.promptForDataset(datasets)\n }\n\n // Validate date flags\n if (flags.before || flags.after) {\n try {\n const parsedBefore = this.processDateFlag(flags.before, 'before')\n const parsedAfter = this.processDateFlag(flags.after, 'after')\n\n if (parsedAfter && parsedBefore && isAfter(parsedAfter, parsedBefore)) {\n this.error('--after date must be before --before', {exit: exitCodes.RUNTIME_ERROR})\n }\n } catch (err) {\n this.error(`Parsing date flags: ${err instanceof Error ? err.message : err}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n // Validate limit flag\n if (flags.limit < 1 || flags.limit > Number.MAX_SAFE_INTEGER) {\n this.error(`Parsing --limit: must be an integer between 1 and ${Number.MAX_SAFE_INTEGER}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const query: ListBackupRequestQueryParams = {\n limit: flags.limit.toString(),\n }\n\n if (flags.after) {\n query.after = flags.after\n }\n\n if (flags.before) {\n query.before = flags.before\n }\n\n try {\n const response = await listBackups({\n after: flags.after,\n before: flags.before,\n datasetName: dataset,\n limit: flags.limit,\n projectId,\n })\n\n if (response.backups.length === 0) {\n this.log('No backups found.')\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'resource', title: 'RESOURCE'},\n {alignment: 'left', name: 'createdAt', title: 'CREATED AT'},\n {alignment: 'left', name: 'backupId', title: 'BACKUP ID'},\n ],\n })\n\n for (const backup of response.backups) {\n const {createdAt, id} = backup\n table.addRow({\n backupId: id,\n createdAt: lightFormat(Date.parse(createdAt), 'yyyy-MM-dd HH:mm:ss'),\n resource: 'Dataset',\n })\n }\n\n this.log(table.render())\n\n listBackupDebug(\n `Successfully listed ${response.backups.length} backups for dataset ${dataset}`,\n )\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list backups for dataset ${dataset}:`, error)\n this.error(`List dataset backup failed: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n private processDateFlag(date: string | undefined, flagName: string): Date | undefined {\n if (!date) return undefined\n const parsedDate = parse(date, 'yyyy-MM-dd', new Date())\n if (isValid(parsedDate)) {\n return parsedDate\n }\n\n throw new Error(`Invalid date format for '--${flagName}' flag. Use YYYY-MM-DD`)\n }\n\n private async promptForDataset(datasets: DatasetsResponse): Promise<string> {\n try {\n const choices = datasets.map((dataset) => ({\n name: dataset.name,\n value: dataset.name,\n }))\n\n return select({\n choices,\n message: 'Select the dataset name:',\n })\n } catch (error) {\n listBackupDebug(`Error selecting dataset`, error)\n this.error(`Failed to select dataset:\\n${error instanceof Error ? error.message : error}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","select","Table","isAfter","isValid","lightFormat","parse","assertDatasetExists","promptForProject","listBackups","listDatasets","getProjectIdFlag","listBackupDebug","DEFAULT_LIST_BACKUP_LIMIT","ListBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","after","before","limit","integer","char","default","hiddenAliases","run","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","RUNTIME_ERROR","length","output","isUnattended","USAGE_ERROR","promptForDataset","parsedBefore","processDateFlag","parsedAfter","err","Number","MAX_SAFE_INTEGER","query","toString","response","datasetName","backups","log","table","columns","alignment","name","title","backup","createdAt","id","addRow","backupId","Date","resource","render","date","flagName","undefined","parsedDate","choices","map","value"],"mappings":"AAAA,SAAQA,IAAI,EAAEC,KAAK,QAAO,cAAa;AACvC,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,SAAQC,MAAM,QAAO,sBAAqB;AAE1C,SAAQC,KAAK,QAAO,wBAAuB;AAC3C,SAAQC,OAAO,QAAO,mBAAkB;AACxC,SAAQC,OAAO,QAAO,mBAAkB;AACxC,SAAQC,WAAW,QAAO,uBAAsB;AAChD,SAAQC,KAAK,QAAO,iBAAgB;AAEpC,SAAQC,mBAAmB,QAAO,6CAA4C;AAC9E,SAAQC,gBAAgB,QAAO,oCAAmC;AAClE,SAAQC,WAAW,QAAO,2BAA0B;AACpD,SAAQC,YAAY,QAAO,6BAA4B;AACvD,SAAQC,gBAAgB,QAAO,4BAA2B;AAE1D,MAAMC,kBAAkBZ,SAAS;AAEjC,MAAMa,4BAA4B;AAQlC,OAAO,MAAMC,0BAA0Bf;IACrC,OAAgBgB,OAAO;QACrBC,SAASpB,KAAKqB,MAAM,CAAC;YACnBC,aAAa;YACbC,UAAU;QACZ;IACF,EAAC;IAED,OAAgBD,cAAc,uCAAsC;IAEpE,OAAgBE,WAAW;QACzB;YACEC,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;KACD,CAAA;IAED,OAAgBI,QAAQ;QACtB,GAAGX,iBAAiB;YAClBO,aAAa;YACbK,WAAW;QACb,EAAE;QACFC,OAAO3B,MAAMoB,MAAM,CAAC;YAClBC,aAAa;QACf;QACAO,QAAQ5B,MAAMoB,MAAM,CAAC;YACnBC,aAAa;QACf;QACAQ,OAAO7B,MAAM8B,OAAO,CAAC;YACnBC,MAAM;YACNC,SAAShB;YACTK,aAAa;QACf;IACF,EAAC;IAED,OAAgBY,gBAA0B;QAAC;KAAc,CAAA;IAEzD,MAAaC,MAAqB;QAChC,MAAM,EAAChB,IAAI,EAAEO,KAAK,EAAC,GAAG,MAAM,IAAI,CAAChB,KAAK,CAACQ;QACvC,IAAI,EAACE,OAAO,EAAC,GAAGD;QAEhB,MAAMiB,YAAY,MAAM,IAAI,CAACC,YAAY,CAAC;YACxCC,UAAU,IACR1B,iBAAiB;oBACf2B,qBAAqB;wBAAC;4BAACC,OAAO;4BAAQC,YAAY;wBAAyB;qBAAE;gBAC/E;QACJ;QAEA,IAAIC;QAEJ,IAAI;YACFA,WAAW,MAAM5B,aAAasB;QAChC,EAAE,OAAOO,OAAO;YACd,MAAMC,UAAUD,iBAAiBE,QAAQF,MAAMC,OAAO,GAAGE,OAAOH;YAChE3B,gBAAgB,CAAC,yBAAyB,EAAE4B,SAAS,EAAED;YACvD,IAAI,CAACA,KAAK,CAAC,CAAC,yBAAyB,EAAEC,SAAS,EAAE;gBAACG,MAAM7C,UAAU8C,aAAa;YAAA;QAClF;QAEA,IAAIN,SAASO,MAAM,KAAK,GAAG;YACzB,IAAI,CAACN,KAAK,CAAC,sCAAsC;gBAACI,MAAM7C,UAAU8C,aAAa;YAAA;QACjF;QAEA,IAAI5B,SAAS;YACXT,oBAAoB+B,UAAUtB,SAAS,IAAI,CAAC8B,MAAM;QACpD,OAAO,IAAI,IAAI,CAACC,YAAY,IAAI;YAC9B,IAAI,CAACR,KAAK,CAAC,gFAAgF;gBACzFI,MAAM7C,UAAUkD,WAAW;YAC7B;QACF,OAAO;YACLhC,UAAU,MAAM,IAAI,CAACiC,gBAAgB,CAACX;QACxC;QAEA,sBAAsB;QACtB,IAAIhB,MAAMG,MAAM,IAAIH,MAAME,KAAK,EAAE;YAC/B,IAAI;gBACF,MAAM0B,eAAe,IAAI,CAACC,eAAe,CAAC7B,MAAMG,MAAM,EAAE;gBACxD,MAAM2B,cAAc,IAAI,CAACD,eAAe,CAAC7B,MAAME,KAAK,EAAE;gBAEtD,IAAI4B,eAAeF,gBAAgB/C,QAAQiD,aAAaF,eAAe;oBACrE,IAAI,CAACX,KAAK,CAAC,wCAAwC;wBAACI,MAAM7C,UAAU8C,aAAa;oBAAA;gBACnF;YACF,EAAE,OAAOS,KAAK;gBACZ,IAAI,CAACd,KAAK,CAAC,CAAC,oBAAoB,EAAEc,eAAeZ,QAAQY,IAAIb,OAAO,GAAGa,KAAK,EAAE;oBAC5EV,MAAM7C,UAAU8C,aAAa;gBAC/B;YACF;QACF;QAEA,sBAAsB;QACtB,IAAItB,MAAMI,KAAK,GAAG,KAAKJ,MAAMI,KAAK,GAAG4B,OAAOC,gBAAgB,EAAE;YAC5D,IAAI,CAAChB,KAAK,CAAC,CAAC,kDAAkD,EAAEe,OAAOC,gBAAgB,EAAE,EAAE;gBACzFZ,MAAM7C,UAAU8C,aAAa;YAC/B;QACF;QAEA,MAAMY,QAAsC;YAC1C9B,OAAOJ,MAAMI,KAAK,CAAC+B,QAAQ;QAC7B;QAEA,IAAInC,MAAME,KAAK,EAAE;YACfgC,MAAMhC,KAAK,GAAGF,MAAME,KAAK;QAC3B;QAEA,IAAIF,MAAMG,MAAM,EAAE;YAChB+B,MAAM/B,MAAM,GAAGH,MAAMG,MAAM;QAC7B;QAEA,IAAI;YACF,MAAMiC,WAAW,MAAMjD,YAAY;gBACjCe,OAAOF,MAAME,KAAK;gBAClBC,QAAQH,MAAMG,MAAM;gBACpBkC,aAAa3C;gBACbU,OAAOJ,MAAMI,KAAK;gBAClBM;YACF;YAEA,IAAI0B,SAASE,OAAO,CAACf,MAAM,KAAK,GAAG;gBACjC,IAAI,CAACgB,GAAG,CAAC;gBACT;YACF;YAEA,MAAMC,QAAQ,IAAI5D,MAAM;gBACtB6D,SAAS;oBACP;wBAACC,WAAW;wBAAQC,MAAM;wBAAYC,OAAO;oBAAU;oBACvD;wBAACF,WAAW;wBAAQC,MAAM;wBAAaC,OAAO;oBAAY;oBAC1D;wBAACF,WAAW;wBAAQC,MAAM;wBAAYC,OAAO;oBAAW;iBACzD;YACH;YAEA,KAAK,MAAMC,UAAUT,SAASE,OAAO,CAAE;gBACrC,MAAM,EAACQ,SAAS,EAAEC,EAAE,EAAC,GAAGF;gBACxBL,MAAMQ,MAAM,CAAC;oBACXC,UAAUF;oBACVD,WAAW/D,YAAYmE,KAAKlE,KAAK,CAAC8D,YAAY;oBAC9CK,UAAU;gBACZ;YACF;YAEA,IAAI,CAACZ,GAAG,CAACC,MAAMY,MAAM;YAErB9D,gBACE,CAAC,oBAAoB,EAAE8C,SAASE,OAAO,CAACf,MAAM,CAAC,qBAAqB,EAAE7B,SAAS;QAEnF,EAAE,OAAOuB,OAAO;YACd,MAAMC,UAAUD,iBAAiBE,QAAQF,MAAMC,OAAO,GAAGE,OAAOH;YAChE3B,gBAAgB,CAAC,mCAAmC,EAAEI,QAAQ,CAAC,CAAC,EAAEuB;YAClE,IAAI,CAACA,KAAK,CAAC,CAAC,4BAA4B,EAAEC,SAAS,EAAE;gBAACG,MAAM7C,UAAU8C,aAAa;YAAA;QACrF;IACF;IAEQO,gBAAgBwB,IAAwB,EAAEC,QAAgB,EAAoB;QACpF,IAAI,CAACD,MAAM,OAAOE;QAClB,MAAMC,aAAaxE,MAAMqE,MAAM,cAAc,IAAIH;QACjD,IAAIpE,QAAQ0E,aAAa;YACvB,OAAOA;QACT;QAEA,MAAM,IAAIrC,MAAM,CAAC,2BAA2B,EAAEmC,SAAS,sBAAsB,CAAC;IAChF;IAEA,MAAc3B,iBAAiBX,QAA0B,EAAmB;QAC1E,IAAI;YACF,MAAMyC,UAAUzC,SAAS0C,GAAG,CAAC,CAAChE,UAAa,CAAA;oBACzCiD,MAAMjD,QAAQiD,IAAI;oBAClBgB,OAAOjE,QAAQiD,IAAI;gBACrB,CAAA;YAEA,OAAOhE,OAAO;gBACZ8E;gBACAvC,SAAS;YACX;QACF,EAAE,OAAOD,OAAO;YACd3B,gBAAgB,CAAC,uBAAuB,CAAC,EAAE2B;YAC3C,IAAI,CAACA,KAAK,CAAC,CAAC,2BAA2B,EAAEA,iBAAiBE,QAAQF,MAAMC,OAAO,GAAGD,OAAO,EAAE;gBACzFI,MAAM7C,UAAU8C,aAAa;YAC/B;QACF;IACF;AACF"}
@@ -3,6 +3,7 @@ import { Args, Flags } from '@oclif/core';
3
3
  import { exit } from '@oclif/core/errors';
4
4
  import { exitCodes } from '@sanity/cli-core';
5
5
  import { subdebug } from '@sanity/cli-core/debug';
6
+ import { getCliExecutionContext } from '@sanity/cli-core/executionContext';
6
7
  import { SanityCommand } from '@sanity/cli-core/SanityCommand';
7
8
  import { spinner } from '@sanity/cli-core/ux';
8
9
  import { Table } from 'console-table-printer';
@@ -385,6 +386,7 @@ export class CopyDatasetCommand extends SanityCommand {
385
386
  }
386
387
  async subscribeToProgress(projectId, jobId) {
387
388
  const spin = spinner('').start();
389
+ const hasCliExecutionContext = Boolean(getCliExecutionContext());
388
390
  return new Promise((resolve, reject)=>{
389
391
  const sigintHandler = ()=>{
390
392
  subscription.unsubscribe();
@@ -396,12 +398,12 @@ export class CopyDatasetCommand extends SanityCommand {
396
398
  projectId
397
399
  }).subscribe({
398
400
  complete: ()=>{
399
- process.off('SIGINT', sigintHandler);
401
+ if (!hasCliExecutionContext) process.off('SIGINT', sigintHandler);
400
402
  spin.succeed('Copy finished.');
401
403
  resolve();
402
404
  },
403
405
  error: (err)=>{
404
- process.off('SIGINT', sigintHandler);
406
+ if (!hasCliExecutionContext) process.off('SIGINT', sigintHandler);
405
407
  spin.fail('Copy failed.');
406
408
  reject(err);
407
409
  },
@@ -411,7 +413,7 @@ export class CopyDatasetCommand extends SanityCommand {
411
413
  }
412
414
  }
413
415
  });
414
- process.once('SIGINT', sigintHandler);
416
+ if (!hasCliExecutionContext) process.once('SIGINT', sigintHandler);
415
417
  });
416
418
  }
417
419
  }