@sanity/cli 7.10.0 → 7.11.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 (157) hide show
  1. package/README.md +31 -22
  2. package/dist/actions/auth/ensureAuthenticated.js +2 -0
  3. package/dist/actions/auth/ensureAuthenticated.js.map +1 -1
  4. package/dist/actions/auth/login/loginInstructions.js +19 -0
  5. package/dist/actions/auth/login/loginInstructions.js.map +1 -0
  6. package/dist/actions/dataset/resolveDataset.js +7 -1
  7. package/dist/actions/dataset/resolveDataset.js.map +1 -1
  8. package/dist/actions/deploy/deployApp.js +17 -12
  9. package/dist/actions/deploy/deployApp.js.map +1 -1
  10. package/dist/actions/deploy/deployStudio.js +2 -0
  11. package/dist/actions/deploy/deployStudio.js.map +1 -1
  12. package/dist/actions/graphql/resolveApiGeneration.js +9 -6
  13. package/dist/actions/graphql/resolveApiGeneration.js.map +1 -1
  14. package/dist/actions/init/initAction.js +23 -15
  15. package/dist/actions/init/initAction.js.map +1 -1
  16. package/dist/actions/manifest/extractCoreAppManifest.js +5 -3
  17. package/dist/actions/manifest/extractCoreAppManifest.js.map +1 -1
  18. package/dist/actions/migration/constants.js +10 -0
  19. package/dist/actions/migration/constants.js.map +1 -0
  20. package/dist/actions/migration/ensureApiVersionFormat.js +16 -0
  21. package/dist/actions/migration/ensureApiVersionFormat.js.map +1 -0
  22. package/dist/actions/migration/fileExists.js +13 -0
  23. package/dist/actions/migration/fileExists.js.map +1 -0
  24. package/dist/actions/migration/prettyMutationFormatter.js +155 -0
  25. package/dist/actions/migration/prettyMutationFormatter.js.map +1 -0
  26. package/dist/actions/migration/resolveMigrationScript.js +75 -0
  27. package/dist/actions/migration/resolveMigrationScript.js.map +1 -0
  28. package/dist/actions/migration/resolveMigrations.js +50 -0
  29. package/dist/actions/migration/resolveMigrations.js.map +1 -0
  30. package/dist/actions/migration/templates/index.js +7 -0
  31. package/dist/actions/migration/templates/index.js.map +1 -0
  32. package/dist/actions/migration/templates/minimalAdvanced.js +21 -0
  33. package/dist/actions/migration/templates/minimalAdvanced.js.map +1 -0
  34. package/dist/actions/migration/templates/minimalSimple.js +61 -0
  35. package/dist/actions/migration/templates/minimalSimple.js.map +1 -0
  36. package/dist/actions/migration/templates/renameField.js +20 -0
  37. package/dist/actions/migration/templates/renameField.js.map +1 -0
  38. package/dist/actions/migration/templates/renameType.js +19 -0
  39. package/dist/actions/migration/templates/renameType.js.map +1 -0
  40. package/dist/actions/migration/templates/stringToPTE.js +32 -0
  41. package/dist/actions/migration/templates/stringToPTE.js.map +1 -0
  42. package/dist/actions/migration/tree.js +111 -0
  43. package/dist/actions/migration/tree.js.map +1 -0
  44. package/dist/actions/preview/previewAction.js +33 -1
  45. package/dist/actions/preview/previewAction.js.map +1 -1
  46. package/dist/actions/schema/listSchemas.js +6 -4
  47. package/dist/actions/schema/listSchemas.js.map +1 -1
  48. package/dist/actions/tokens/validateRole.js +2 -1
  49. package/dist/actions/tokens/validateRole.js.map +1 -1
  50. package/dist/actions/undeploy/runUndeploy.js +13 -4
  51. package/dist/actions/undeploy/runUndeploy.js.map +1 -1
  52. package/dist/commands/backups/disable.js +5 -1
  53. package/dist/commands/backups/disable.js.map +1 -1
  54. package/dist/commands/backups/download.js +30 -6
  55. package/dist/commands/backups/download.js.map +1 -1
  56. package/dist/commands/backups/enable.js +5 -1
  57. package/dist/commands/backups/enable.js.map +1 -1
  58. package/dist/commands/backups/list.js +5 -1
  59. package/dist/commands/backups/list.js.map +1 -1
  60. package/dist/commands/cors/add.js +15 -7
  61. package/dist/commands/cors/add.js.map +1 -1
  62. package/dist/commands/cors/delete.js +6 -1
  63. package/dist/commands/cors/delete.js.map +1 -1
  64. package/dist/commands/datasets/alias/create.js +25 -18
  65. package/dist/commands/datasets/alias/create.js.map +1 -1
  66. package/dist/commands/datasets/alias/delete.js +15 -8
  67. package/dist/commands/datasets/alias/delete.js.map +1 -1
  68. package/dist/commands/datasets/alias/link.js +39 -17
  69. package/dist/commands/datasets/alias/link.js.map +1 -1
  70. package/dist/commands/datasets/alias/unlink.js +23 -2
  71. package/dist/commands/datasets/alias/unlink.js.map +1 -1
  72. package/dist/commands/datasets/copy.js +18 -2
  73. package/dist/commands/datasets/copy.js.map +1 -1
  74. package/dist/commands/datasets/create.js +8 -2
  75. package/dist/commands/datasets/create.js.map +1 -1
  76. package/dist/commands/datasets/delete.js +14 -17
  77. package/dist/commands/datasets/delete.js.map +1 -1
  78. package/dist/commands/datasets/embeddings/disable.js +3 -0
  79. package/dist/commands/datasets/embeddings/disable.js.map +1 -1
  80. package/dist/commands/datasets/embeddings/enable.js +5 -2
  81. package/dist/commands/datasets/embeddings/enable.js.map +1 -1
  82. package/dist/commands/datasets/embeddings/status.js +3 -0
  83. package/dist/commands/datasets/embeddings/status.js.map +1 -1
  84. package/dist/commands/datasets/export.js +27 -15
  85. package/dist/commands/datasets/export.js.map +1 -1
  86. package/dist/commands/datasets/import.js +3 -3
  87. package/dist/commands/datasets/import.js.map +1 -1
  88. package/dist/commands/datasets/visibility/get.js +2 -2
  89. package/dist/commands/datasets/visibility/get.js.map +1 -1
  90. package/dist/commands/datasets/visibility/set.js +2 -2
  91. package/dist/commands/datasets/visibility/set.js.map +1 -1
  92. package/dist/commands/docs/read.js +3 -3
  93. package/dist/commands/docs/read.js.map +1 -1
  94. package/dist/commands/documents/create.js +18 -13
  95. package/dist/commands/documents/create.js.map +1 -1
  96. package/dist/commands/documents/delete.js +3 -3
  97. package/dist/commands/documents/delete.js.map +1 -1
  98. package/dist/commands/documents/get.js +3 -3
  99. package/dist/commands/documents/get.js.map +1 -1
  100. package/dist/commands/documents/query.js +3 -3
  101. package/dist/commands/documents/query.js.map +1 -1
  102. package/dist/commands/documents/validate.js +29 -26
  103. package/dist/commands/documents/validate.js.map +1 -1
  104. package/dist/commands/graphql/deploy.js +20 -16
  105. package/dist/commands/graphql/deploy.js.map +1 -1
  106. package/dist/commands/graphql/undeploy.js +15 -18
  107. package/dist/commands/graphql/undeploy.js.map +1 -1
  108. package/dist/commands/hooks/delete.js +2 -2
  109. package/dist/commands/hooks/delete.js.map +1 -1
  110. package/dist/commands/hooks/logs.js +2 -2
  111. package/dist/commands/hooks/logs.js.map +1 -1
  112. package/dist/commands/login.js +2 -1
  113. package/dist/commands/login.js.map +1 -1
  114. package/dist/commands/media/create-aspect.js +20 -8
  115. package/dist/commands/media/create-aspect.js.map +1 -1
  116. package/dist/commands/media/delete-aspect.js +31 -16
  117. package/dist/commands/media/delete-aspect.js.map +1 -1
  118. package/dist/commands/media/deploy-aspect.js +10 -5
  119. package/dist/commands/media/deploy-aspect.js.map +1 -1
  120. package/dist/commands/media/export.js +9 -4
  121. package/dist/commands/media/export.js.map +1 -1
  122. package/dist/commands/media/import.js +6 -1
  123. package/dist/commands/media/import.js.map +1 -1
  124. package/dist/commands/migrations/create.js +150 -0
  125. package/dist/commands/migrations/create.js.map +1 -0
  126. package/dist/commands/migrations/list.js +64 -0
  127. package/dist/commands/migrations/list.js.map +1 -0
  128. package/dist/commands/migrations/run.js +306 -0
  129. package/dist/commands/migrations/run.js.map +1 -0
  130. package/dist/commands/preview.js +1 -0
  131. package/dist/commands/preview.js.map +1 -1
  132. package/dist/commands/projects/create.js +2 -1
  133. package/dist/commands/projects/create.js.map +1 -1
  134. package/dist/commands/schemas/delete.js +23 -2
  135. package/dist/commands/schemas/delete.js.map +1 -1
  136. package/dist/commands/schemas/deploy.js +6 -4
  137. package/dist/commands/schemas/deploy.js.map +1 -1
  138. package/dist/commands/schemas/extract.js +22 -1
  139. package/dist/commands/schemas/extract.js.map +1 -1
  140. package/dist/commands/schemas/list.js +8 -8
  141. package/dist/commands/schemas/list.js.map +1 -1
  142. package/dist/commands/tokens/{add.js → create.js} +25 -18
  143. package/dist/commands/tokens/create.js.map +1 -0
  144. package/dist/commands/tokens/delete.js +41 -23
  145. package/dist/commands/tokens/delete.js.map +1 -1
  146. package/dist/commands/undeploy.js +2 -7
  147. package/dist/commands/undeploy.js.map +1 -1
  148. package/dist/commands/users/invite.js +25 -3
  149. package/dist/commands/users/invite.js.map +1 -1
  150. package/dist/topicAliases.js +3 -0
  151. package/dist/topicAliases.js.map +1 -1
  152. package/dist/util/formatCliErrorMessages.js +5 -0
  153. package/dist/util/formatCliErrorMessages.js.map +1 -0
  154. package/oclif.config.js +5 -1
  155. package/oclif.manifest.json +326 -121
  156. package/package.json +7 -7
  157. package/dist/commands/tokens/add.js.map +0 -1
@@ -1,10 +1,10 @@
1
1
  import { CLIError } from '@oclif/core/errors';
2
- import { isInteractive } from '@sanity/cli-core';
2
+ import { exitCodes } from '@sanity/cli-core';
3
3
  import { confirm } from '@sanity/cli-core/ux';
4
4
  import { oneline } from 'oneline';
5
5
  import { graphqlDebug } from './graphqlDebug.js';
6
6
  const LATEST_GENERATION = 'gen3';
7
- export async function resolveApiGeneration({ currentGeneration, force, index, output, specifiedGeneration }) {
7
+ export async function resolveApiGeneration({ currentGeneration, force, index, output, specifiedGeneration, unattended }) {
8
8
  // a) If no API is currently deployed:
9
9
  // use the specificed one from config, or use whichever generation is the latest
10
10
  // b) If an API generation is specified explicitly:
@@ -17,12 +17,12 @@ export async function resolveApiGeneration({ currentGeneration, force, index, ou
17
17
  return generation;
18
18
  }
19
19
  if (specifiedGeneration && specifiedGeneration !== currentGeneration) {
20
- if (!force && !isInteractive()) {
20
+ if (!force && unattended) {
21
21
  throw new CLIError(oneline`
22
22
  Specified generation (${specifiedGeneration}) for API at index ${index} differs from the one currently deployed (${currentGeneration}).
23
- Re-run the command with \`--force\` to force deployment.
23
+ Pass \`--force\` to continue.
24
24
  `, {
25
- exit: 1
25
+ exit: exitCodes.USAGE_ERROR
26
26
  });
27
27
  }
28
28
  output.warn(`Specified generation (${specifiedGeneration}) for API at index ${index} differs from the one currently deployed (${currentGeneration}).`);
@@ -30,7 +30,10 @@ export async function resolveApiGeneration({ currentGeneration, force, index, ou
30
30
  default: false,
31
31
  message: 'Are you sure you want to deploy?'
32
32
  });
33
- return confirmDeploy ? specifiedGeneration : undefined;
33
+ if (!confirmDeploy) {
34
+ return undefined;
35
+ }
36
+ return specifiedGeneration;
34
37
  }
35
38
  if (specifiedGeneration) {
36
39
  graphqlDebug('Using specified (%s) generation', specifiedGeneration);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/graphql/resolveApiGeneration.ts"],"sourcesContent":["import {CLIError} from '@oclif/core/errors'\nimport {isInteractive, type Output} from '@sanity/cli-core'\nimport {confirm} from '@sanity/cli-core/ux'\nimport {oneline} from 'oneline'\n\nimport {graphqlDebug} from './graphqlDebug.js'\n\nconst LATEST_GENERATION = 'gen3'\n\nexport async function resolveApiGeneration({\n currentGeneration,\n force,\n index,\n output,\n specifiedGeneration,\n}: {\n currentGeneration?: string\n force?: boolean\n index: number\n output: Output\n specifiedGeneration?: string\n}): Promise<string | undefined> {\n // a) If no API is currently deployed:\n // use the specificed one from config, or use whichever generation is the latest\n // b) If an API generation is specified explicitly:\n // use the given one, but _prompt_ if it differs from the current one\n // c) If no API generation is specified explicitly:\n // use whichever is already deployed, but warn if differs from latest\n if (!currentGeneration) {\n const generation = specifiedGeneration || LATEST_GENERATION\n graphqlDebug(\n 'There is no current generation deployed, using %s (%s)',\n generation,\n specifiedGeneration ? 'specified' : 'default',\n )\n return generation\n }\n\n if (specifiedGeneration && specifiedGeneration !== currentGeneration) {\n if (!force && !isInteractive()) {\n throw new CLIError(\n oneline`\n Specified generation (${specifiedGeneration}) for API at index ${index} differs from the one currently deployed (${currentGeneration}).\n Re-run the command with \\`--force\\` to force deployment.\n `,\n {exit: 1},\n )\n }\n\n output.warn(\n `Specified generation (${specifiedGeneration}) for API at index ${index} differs from the one currently deployed (${currentGeneration}).`,\n )\n\n const confirmDeploy =\n force ||\n (await confirm({\n default: false,\n message: 'Are you sure you want to deploy?',\n }))\n\n return confirmDeploy ? specifiedGeneration : undefined\n }\n\n if (specifiedGeneration) {\n graphqlDebug('Using specified (%s) generation', specifiedGeneration)\n return specifiedGeneration\n }\n\n graphqlDebug('Using the currently deployed version (%s)', currentGeneration)\n return currentGeneration\n}\n"],"names":["CLIError","isInteractive","confirm","oneline","graphqlDebug","LATEST_GENERATION","resolveApiGeneration","currentGeneration","force","index","output","specifiedGeneration","generation","exit","warn","confirmDeploy","default","message","undefined"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,qBAAoB;AAC3C,SAAQC,aAAa,QAAoB,mBAAkB;AAC3D,SAAQC,OAAO,QAAO,sBAAqB;AAC3C,SAAQC,OAAO,QAAO,UAAS;AAE/B,SAAQC,YAAY,QAAO,oBAAmB;AAE9C,MAAMC,oBAAoB;AAE1B,OAAO,eAAeC,qBAAqB,EACzCC,iBAAiB,EACjBC,KAAK,EACLC,KAAK,EACLC,MAAM,EACNC,mBAAmB,EAOpB;IACC,sCAAsC;IACtC,mFAAmF;IACnF,mDAAmD;IACnD,wEAAwE;IACxE,mDAAmD;IACnD,wEAAwE;IACxE,IAAI,CAACJ,mBAAmB;QACtB,MAAMK,aAAaD,uBAAuBN;QAC1CD,aACE,0DACAQ,YACAD,sBAAsB,cAAc;QAEtC,OAAOC;IACT;IAEA,IAAID,uBAAuBA,wBAAwBJ,mBAAmB;QACpE,IAAI,CAACC,SAAS,CAACP,iBAAiB;YAC9B,MAAM,IAAID,SACRG,OAAO,CAAC;8BACc,EAAEQ,oBAAoB,mBAAmB,EAAEF,MAAM,0CAA0C,EAAEF,kBAAkB;;MAEvI,CAAC,EACC;gBAACM,MAAM;YAAC;QAEZ;QAEAH,OAAOI,IAAI,CACT,CAAC,sBAAsB,EAAEH,oBAAoB,mBAAmB,EAAEF,MAAM,0CAA0C,EAAEF,kBAAkB,EAAE,CAAC;QAG3I,MAAMQ,gBACJP,SACC,MAAMN,QAAQ;YACbc,SAAS;YACTC,SAAS;QACX;QAEF,OAAOF,gBAAgBJ,sBAAsBO;IAC/C;IAEA,IAAIP,qBAAqB;QACvBP,aAAa,mCAAmCO;QAChD,OAAOA;IACT;IAEAP,aAAa,6CAA6CG;IAC1D,OAAOA;AACT"}
1
+ {"version":3,"sources":["../../../src/actions/graphql/resolveApiGeneration.ts"],"sourcesContent":["import {CLIError} from '@oclif/core/errors'\nimport {exitCodes, type Output} from '@sanity/cli-core'\nimport {confirm} from '@sanity/cli-core/ux'\nimport {oneline} from 'oneline'\n\nimport {graphqlDebug} from './graphqlDebug.js'\n\nconst LATEST_GENERATION = 'gen3'\n\nexport async function resolveApiGeneration({\n currentGeneration,\n force,\n index,\n output,\n specifiedGeneration,\n unattended,\n}: {\n currentGeneration?: string\n force?: boolean\n index: number\n output: Output\n specifiedGeneration?: string\n unattended: boolean\n}): Promise<string | undefined> {\n // a) If no API is currently deployed:\n // use the specificed one from config, or use whichever generation is the latest\n // b) If an API generation is specified explicitly:\n // use the given one, but _prompt_ if it differs from the current one\n // c) If no API generation is specified explicitly:\n // use whichever is already deployed, but warn if differs from latest\n if (!currentGeneration) {\n const generation = specifiedGeneration || LATEST_GENERATION\n graphqlDebug(\n 'There is no current generation deployed, using %s (%s)',\n generation,\n specifiedGeneration ? 'specified' : 'default',\n )\n return generation\n }\n\n if (specifiedGeneration && specifiedGeneration !== currentGeneration) {\n if (!force && unattended) {\n throw new CLIError(\n oneline`\n Specified generation (${specifiedGeneration}) for API at index ${index} differs from the one currently deployed (${currentGeneration}).\n Pass \\`--force\\` to continue.\n `,\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n output.warn(\n `Specified generation (${specifiedGeneration}) for API at index ${index} differs from the one currently deployed (${currentGeneration}).`,\n )\n\n const confirmDeploy =\n force ||\n (await confirm({\n default: false,\n message: 'Are you sure you want to deploy?',\n }))\n\n if (!confirmDeploy) {\n return undefined\n }\n\n return specifiedGeneration\n }\n\n if (specifiedGeneration) {\n graphqlDebug('Using specified (%s) generation', specifiedGeneration)\n return specifiedGeneration\n }\n\n graphqlDebug('Using the currently deployed version (%s)', currentGeneration)\n return currentGeneration\n}\n"],"names":["CLIError","exitCodes","confirm","oneline","graphqlDebug","LATEST_GENERATION","resolveApiGeneration","currentGeneration","force","index","output","specifiedGeneration","unattended","generation","exit","USAGE_ERROR","warn","confirmDeploy","default","message","undefined"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,qBAAoB;AAC3C,SAAQC,SAAS,QAAoB,mBAAkB;AACvD,SAAQC,OAAO,QAAO,sBAAqB;AAC3C,SAAQC,OAAO,QAAO,UAAS;AAE/B,SAAQC,YAAY,QAAO,oBAAmB;AAE9C,MAAMC,oBAAoB;AAE1B,OAAO,eAAeC,qBAAqB,EACzCC,iBAAiB,EACjBC,KAAK,EACLC,KAAK,EACLC,MAAM,EACNC,mBAAmB,EACnBC,UAAU,EAQX;IACC,sCAAsC;IACtC,mFAAmF;IACnF,mDAAmD;IACnD,wEAAwE;IACxE,mDAAmD;IACnD,wEAAwE;IACxE,IAAI,CAACL,mBAAmB;QACtB,MAAMM,aAAaF,uBAAuBN;QAC1CD,aACE,0DACAS,YACAF,sBAAsB,cAAc;QAEtC,OAAOE;IACT;IAEA,IAAIF,uBAAuBA,wBAAwBJ,mBAAmB;QACpE,IAAI,CAACC,SAASI,YAAY;YACxB,MAAM,IAAIZ,SACRG,OAAO,CAAC;8BACc,EAAEQ,oBAAoB,mBAAmB,EAAEF,MAAM,0CAA0C,EAAEF,kBAAkB;;MAEvI,CAAC,EACC;gBAACO,MAAMb,UAAUc,WAAW;YAAA;QAEhC;QAEAL,OAAOM,IAAI,CACT,CAAC,sBAAsB,EAAEL,oBAAoB,mBAAmB,EAAEF,MAAM,0CAA0C,EAAEF,kBAAkB,EAAE,CAAC;QAG3I,MAAMU,gBACJT,SACC,MAAMN,QAAQ;YACbgB,SAAS;YACTC,SAAS;QACX;QAEF,IAAI,CAACF,eAAe;YAClB,OAAOG;QACT;QAEA,OAAOT;IACT;IAEA,IAAIA,qBAAqB;QACvBP,aAAa,mCAAmCO;QAChD,OAAOA;IACT;IAEAP,aAAa,6CAA6CG;IAC1D,OAAOA;AACT"}
@@ -1,5 +1,5 @@
1
1
  import { styleText } from 'node:util';
2
- import { subdebug } from '@sanity/cli-core';
2
+ import { exitCodes, subdebug } from '@sanity/cli-core';
3
3
  import { logSymbols, spinner } from '@sanity/cli-core/ux';
4
4
  import { frameworks } from '@vercel/frameworks';
5
5
  import deburr from 'lodash-es/deburr.js';
@@ -7,10 +7,12 @@ import { promptForConfigFiles } from '../../prompts/init/nextjs.js';
7
7
  import { getCliUser } from '../../services/user.js';
8
8
  import { CLIInitStepCompleted } from '../../telemetry/init.telemetry.js';
9
9
  import { detectFrameworkRecord } from '../../util/detectFramework.js';
10
+ import { formatCliErrorMessages } from '../../util/formatCliErrorMessages.js';
10
11
  import { getProjectDefaults } from '../../util/getProjectDefaults.js';
11
12
  import { validateSession } from '../auth/ensureAuthenticated.js';
12
13
  import { getProviderName } from '../auth/getProviderName.js';
13
14
  import { login } from '../auth/login/login.js';
15
+ import { LOGIN_REQUIRED_MESSAGE } from '../auth/login/loginInstructions.js';
14
16
  import { detectAvailableEditors } from '../mcp/detectAvailableEditors.js';
15
17
  import { setupMCP } from '../mcp/setupMCP.js';
16
18
  import { setupSkills } from '../skills/setupSkills.js';
@@ -268,24 +270,29 @@ export async function initAction(options, context) {
268
270
  }
269
271
  function checkFlagsInUnattendedMode(options, { isAppTemplate, isNextJs }) {
270
272
  debug('Unattended mode, validating required options');
271
- if (options.projectName && !options.organization) {
272
- throw new InitError('`--project-name` requires `--organization <id>` in unattended mode', 1);
273
- }
273
+ const errors = [];
274
274
  if (isAppTemplate) {
275
275
  if (!options.outputPath) {
276
- throw new InitError('`--output-path` must be specified in unattended mode', 1);
276
+ errors.push('Output path is required in unattended mode. Pass it with `--output-path <path>`.');
277
277
  }
278
- const hasProjectFlag = Boolean(options.project || options.projectName);
279
- if (!hasProjectFlag && !options.organization) {
280
- throw new InitError('The --organization flag is required for app templates in unattended mode. ' + 'Use --organization <id>, or pass --project <id> / --project-name <name>.', 1);
278
+ if (options.projectName && !options.organization) {
279
+ errors.push('Organization is required when creating a project in unattended mode. Pass it with `--organization <id>`.');
280
+ } else if (!options.project && !options.organization) {
281
+ errors.push('Project or organization is required for app templates in unattended mode. ' + 'Pass `--project <id>` or `--organization <id>`. To create a project, pass ' + '`--project-name <name>` with `--organization <id>`.');
282
+ }
283
+ } else {
284
+ if (!isNextJs && !options.bare && !options.outputPath) {
285
+ errors.push('Output path is required in unattended mode. Pass it with `--output-path <path>`.');
286
+ }
287
+ if (!options.project && !options.projectName) {
288
+ errors.push('Project is required in unattended mode. Pass it with `--project <id>` or `--project-name <name>`.');
289
+ }
290
+ if (!options.project && !options.organization) {
291
+ errors.push('Organization is required when creating a project in unattended mode. Pass it with `--organization <id>`.');
281
292
  }
282
- return;
283
- }
284
- if (!isNextJs && !options.bare && !options.outputPath) {
285
- throw new InitError('`--output-path` must be specified in unattended mode', 1);
286
293
  }
287
- if (!options.project && !options.projectName) {
288
- throw new InitError('`--project <id>` or `--project-name <name>` must be specified in unattended mode', 1);
294
+ if (errors.length > 0) {
295
+ throw new InitError(formatCliErrorMessages(errors), exitCodes.USAGE_ERROR);
289
296
  }
290
297
  }
291
298
  async function ensureAuthenticated(options, output, trace) {
@@ -301,11 +308,12 @@ async function ensureAuthenticated(options, output, trace) {
301
308
  };
302
309
  }
303
310
  if (options.unattended) {
304
- throw new InitError('Must be logged in to run this command in unattended mode, run `sanity login`', 1);
311
+ throw new InitError(LOGIN_REQUIRED_MESSAGE, exitCodes.RUNTIME_ERROR);
305
312
  }
306
313
  trace.log({
307
314
  step: 'login'
308
315
  });
316
+ output.warn(LOGIN_REQUIRED_MESSAGE);
309
317
  try {
310
318
  await login({
311
319
  output,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/init/initAction.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {type SanityOrgUser, subdebug, type TelemetryUserProperties} from '@sanity/cli-core'\nimport {logSymbols, spinner} from '@sanity/cli-core/ux'\nimport {type TelemetryTrace} from '@sanity/telemetry'\nimport {type Framework, frameworks} from '@vercel/frameworks'\nimport deburr from 'lodash-es/deburr.js'\n\nimport {promptForConfigFiles} from '../../prompts/init/nextjs.js'\nimport {getCliUser} from '../../services/user.js'\nimport {CLIInitStepCompleted, type InitStepResult} from '../../telemetry/init.telemetry.js'\nimport {detectFrameworkRecord} from '../../util/detectFramework.js'\nimport {getProjectDefaults} from '../../util/getProjectDefaults.js'\nimport {validateSession} from '../auth/ensureAuthenticated.js'\nimport {getProviderName} from '../auth/getProviderName.js'\nimport {login} from '../auth/login/login.js'\nimport {detectAvailableEditors} from '../mcp/detectAvailableEditors.js'\nimport {setupMCP} from '../mcp/setupMCP.js'\nimport {setupSkills} from '../skills/setupSkills.js'\nimport {checkNextJsReactCompatibility} from './checkNextJsReactCompatibility.js'\nimport {determineAppTemplate} from './determineAppTemplate.js'\nimport {createOrAppendEnvVars} from './env/createOrAppendEnvVars.js'\nimport {initApp} from './initApp.js'\nimport {InitError} from './initError.js'\nimport {flagOrDefault, shouldPrompt, writeStagingEnvIfNeeded} from './initHelpers.js'\nimport {initNextJs} from './initNextJs.js'\nimport {initStudio} from './initStudio.js'\nimport {getPlan} from './plan/getPlan.js'\nimport {createProjectFromName} from './project/createProjectFromName.js'\nimport {getProjectDetails} from './project/getProjectDetails.js'\nimport {getProjectOutputPath} from './project/getProjectOutputPath.js'\nimport {checkIsRemoteTemplate, getGitHubRepoInfo, type RepoInfo} from './remoteTemplate.js'\nimport {type InitContext, type InitOptions} from './types.js'\n\nconst debug = subdebug('init')\n\nexport async function initAction(options: InitOptions, context: InitContext): Promise<void> {\n const {output, workDir} = context\n\n if (options.argType) {\n throw new InitError(\n options.argType === 'plugin'\n ? 'Initializing plugins through the CLI is no longer supported'\n : `Unknown init type \"${options.argType}\"`,\n 1,\n )\n }\n\n const trace = context.telemetry.trace(CLIInitStepCompleted)\n\n if (options.reconfigure) {\n throw new InitError('--reconfigure is deprecated - manual configuration is now required', 1)\n }\n\n if (options.project && options.organization) {\n throw new InitError(\n 'You have specified both a project and an organization. To move a project to an organization please visit https://www.sanity.io/manage',\n 1,\n )\n }\n\n const defaultConfig = options.datasetDefault\n let showDefaultConfigPrompt = !defaultConfig\n if (options.dataset || options.visibility || options.datasetDefault || options.unattended) {\n showDefaultConfigPrompt = false\n }\n\n const detectedFramework = await detectFrameworkRecord({\n frameworkList: frameworks as readonly Framework[],\n rootPath: workDir,\n })\n const isNextJs = detectedFramework?.slug === 'nextjs'\n\n let remoteTemplateInfo: RepoInfo | undefined\n if (options.template && checkIsRemoteTemplate(options.template)) {\n remoteTemplateInfo = await getGitHubRepoInfo(options.template, options.templateToken)\n }\n\n if (detectedFramework && detectedFramework.slug !== 'sanity' && remoteTemplateInfo) {\n throw new InitError(\n `A remote template cannot be used with a detected framework. Detected: ${detectedFramework.name}`,\n 1,\n )\n }\n\n const isAppTemplate = options.template ? determineAppTemplate(options.template) : false\n\n if (options.unattended) {\n checkFlagsInUnattendedMode(options, {isAppTemplate, isNextJs})\n }\n\n trace.start()\n trace.log({\n flags: {\n bare: options.bare,\n coupon: options.coupon,\n defaultConfig,\n env: options.env,\n git: typeof options.git === 'string' ? options.git : undefined,\n plan: options.projectPlan,\n reconfigure: options.reconfigure,\n unattended: options.unattended,\n },\n step: 'start',\n })\n\n const planId = await getPlan(options, output, trace)\n\n let envFilenameDefault = '.env'\n if (detectedFramework && detectedFramework.slug === 'nextjs') {\n envFilenameDefault = '.env.local'\n }\n const envFilename = typeof options.env === 'string' ? options.env : envFilenameDefault\n\n const {user} = await ensureAuthenticated(options, output, trace)\n if (!isAppTemplate) {\n output.log(`${logSymbols.success} Fetching existing projects`)\n output.log('')\n }\n\n let newProject: string | undefined\n if (options.projectName) {\n newProject = await createProjectFromName({\n coupon: options.coupon,\n createProjectName: options.projectName,\n dataset: options.dataset,\n organization: options.organization,\n planId,\n user,\n visibility: options.visibility,\n })\n }\n\n const {datasetName, displayName, isFirstProject, organizationId, projectId} =\n await getProjectDetails({\n coupon: options.coupon,\n dataset: options.dataset,\n datasetDefault: options.datasetDefault,\n isAppTemplate,\n newProject,\n organization: options.organization,\n output,\n planId,\n project: options.project,\n showDefaultConfigPrompt,\n trace,\n unattended: options.unattended,\n user,\n visibility: options.visibility,\n })\n\n if (options.bare) {\n output.log(`${logSymbols.success} Below are your project details`)\n output.log('')\n output.log(`Project ID: ${styleText('cyan', projectId)}`)\n output.log(`Dataset: ${styleText('cyan', datasetName)}`)\n output.log(\n `\\nYou can find your project on Sanity Manage — https://www.sanity.io/manage/project/${projectId}\\n`,\n )\n trace.complete()\n return\n }\n\n // Detect editors once, then share the result with MCP and skills setup so\n // we don't pay the detection cost (filesystem probes + CLI execa calls) twice.\n const detectedEditors =\n options.mcpMode === 'skip' && options.skillsMode === 'skip'\n ? []\n : await detectAvailableEditors()\n\n const mcpResult = await setupMCP({\n editors: detectedEditors,\n mode: options.mcpMode,\n output,\n skillsMode: options.skillsMode,\n })\n\n trace.log({\n configuredEditors: mcpResult.configuredEditors,\n detectedEditors: mcpResult.detectedEditors,\n skipped: mcpResult.skipped,\n step: 'mcpSetup',\n })\n if (mcpResult.error) {\n trace.error(mcpResult.error)\n }\n const mcpConfigured = mcpResult.configuredEditors\n\n async function installSkills(): Promise<void> {\n if (mcpResult.skillsToInstall.length === 0) return\n try {\n const skillsResult = await setupSkills({agents: mcpResult.skillsToInstall, output})\n trace.log({\n installedAgents: skillsResult.installedAgents,\n skipped: skillsResult.skipped,\n step: 'skillsSetup',\n })\n if (skillsResult.error) {\n trace.error(skillsResult.error)\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n debug('Unexpected error from setupSkills %O', err)\n output.warn(`Could not install Sanity agent skills: ${err.message}`)\n trace.error(err)\n }\n }\n\n // Install skills right after the MCP/skills prompt so the progress + result\n // surface in sequence, rather than trailing the studio \"Success!\" output.\n await installSkills()\n\n const {alreadyConfiguredEditors} = mcpResult\n if (alreadyConfiguredEditors.length > 0) {\n const label =\n alreadyConfiguredEditors.length === 1\n ? `${alreadyConfiguredEditors[0]} already configured for Sanity MCP`\n : `${alreadyConfiguredEditors.length} editors already configured for Sanity MCP`\n spinner(label).start().succeed()\n }\n\n let initNext = flagOrDefault(options.nextjsAddConfigFiles, false)\n if (isNextJs && shouldPrompt(options.unattended, options.nextjsAddConfigFiles)) {\n initNext = await promptForConfigFiles()\n }\n\n trace.log({\n detectedFramework: detectedFramework?.name,\n selectedOption: initNext ? 'yes' : 'no',\n step: 'useDetectedFramework',\n })\n\n const sluggedName = deburr(displayName.toLowerCase())\n .replaceAll(/\\s+/g, '-')\n .replaceAll(/[^a-z0-9-]/g, '')\n\n const initFramework = initNext\n\n const defaults = await getProjectDefaults({isPlugin: false, workDir})\n\n const outputPath = await getProjectOutputPath({\n initFramework,\n outputPath: options.outputPath,\n sluggedName,\n unattended: options.unattended,\n useEnv: Boolean(options.env),\n workDir,\n })\n\n if (isNextJs) {\n await checkNextJsReactCompatibility({\n detectedFramework,\n output,\n outputPath,\n })\n }\n\n if (initNext) {\n await initNextJs({\n datasetName,\n detectedFramework,\n envFilename,\n mcpConfigured,\n options,\n output,\n projectId,\n trace,\n workDir,\n })\n trace.complete()\n return\n }\n\n if (options.env) {\n await createOrAppendEnvVars({\n envVars: {\n DATASET: datasetName,\n PROJECT_ID: projectId,\n },\n filename: envFilename,\n framework: detectedFramework,\n log: false,\n output,\n outputPath,\n })\n await writeStagingEnvIfNeeded(output, outputPath)\n trace.complete()\n return\n }\n\n // Workbench is opt-in (no prompt): the flag swaps the scaffolded\n // `sanity.cli.*` over to `unstable_defineApp` — the branded app is the sole\n // workbench opt-in.\n const workbench = flagOrDefault(options.unstableWorkbench, false)\n\n const sharedParams = {\n defaults,\n mcpConfigured,\n options,\n organizationId,\n output,\n outputPath,\n remoteTemplateInfo,\n sluggedName,\n trace,\n workbench,\n workDir,\n }\n\n await (isAppTemplate\n ? initApp({...sharedParams, datasetName, projectId})\n : initStudio({\n ...sharedParams,\n datasetName,\n displayName,\n isFirstProject,\n projectId,\n }))\n\n trace.complete()\n}\n\nfunction checkFlagsInUnattendedMode(\n options: InitOptions,\n {isAppTemplate, isNextJs}: {isAppTemplate: boolean; isNextJs: boolean},\n): void {\n debug('Unattended mode, validating required options')\n\n if (options.projectName && !options.organization) {\n throw new InitError('`--project-name` requires `--organization <id>` in unattended mode', 1)\n }\n\n if (isAppTemplate) {\n if (!options.outputPath) {\n throw new InitError('`--output-path` must be specified in unattended mode', 1)\n }\n\n const hasProjectFlag = Boolean(options.project || options.projectName)\n\n if (!hasProjectFlag && !options.organization) {\n throw new InitError(\n 'The --organization flag is required for app templates in unattended mode. ' +\n 'Use --organization <id>, or pass --project <id> / --project-name <name>.',\n 1,\n )\n }\n\n return\n }\n\n if (!isNextJs && !options.bare && !options.outputPath) {\n throw new InitError('`--output-path` must be specified in unattended mode', 1)\n }\n\n if (!options.project && !options.projectName) {\n throw new InitError(\n '`--project <id>` or `--project-name <name>` must be specified in unattended mode',\n 1,\n )\n }\n}\n\nasync function ensureAuthenticated(\n options: InitOptions,\n output: InitContext['output'],\n trace: TelemetryTrace<TelemetryUserProperties, InitStepResult>,\n): Promise<{user: SanityOrgUser}> {\n const user = await validateSession()\n\n if (user) {\n trace.log({alreadyLoggedIn: true, step: 'login'})\n output.log(\n `${logSymbols.success} You are logged in as ${user.email} using ${getProviderName(user.provider)}`,\n )\n return {user}\n }\n\n if (options.unattended) {\n throw new InitError(\n 'Must be logged in to run this command in unattended mode, run `sanity login`',\n 1,\n )\n }\n\n trace.log({step: 'login'})\n\n try {\n await login({\n output,\n telemetry: trace.newContext('login'),\n })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new InitError(`Login failed: ${message}`, 1)\n }\n\n const loggedInUser = await getCliUser()\n\n output.log(\n `${logSymbols.success} You are logged in as ${loggedInUser.email} using ${getProviderName(loggedInUser.provider)}`,\n )\n return {user: loggedInUser}\n}\n"],"names":["styleText","subdebug","logSymbols","spinner","frameworks","deburr","promptForConfigFiles","getCliUser","CLIInitStepCompleted","detectFrameworkRecord","getProjectDefaults","validateSession","getProviderName","login","detectAvailableEditors","setupMCP","setupSkills","checkNextJsReactCompatibility","determineAppTemplate","createOrAppendEnvVars","initApp","InitError","flagOrDefault","shouldPrompt","writeStagingEnvIfNeeded","initNextJs","initStudio","getPlan","createProjectFromName","getProjectDetails","getProjectOutputPath","checkIsRemoteTemplate","getGitHubRepoInfo","debug","initAction","options","context","output","workDir","argType","trace","telemetry","reconfigure","project","organization","defaultConfig","datasetDefault","showDefaultConfigPrompt","dataset","visibility","unattended","detectedFramework","frameworkList","rootPath","isNextJs","slug","remoteTemplateInfo","template","templateToken","name","isAppTemplate","checkFlagsInUnattendedMode","start","log","flags","bare","coupon","env","git","undefined","plan","projectPlan","step","planId","envFilenameDefault","envFilename","user","ensureAuthenticated","success","newProject","projectName","createProjectName","datasetName","displayName","isFirstProject","organizationId","projectId","complete","detectedEditors","mcpMode","skillsMode","mcpResult","editors","mode","configuredEditors","skipped","error","mcpConfigured","installSkills","skillsToInstall","length","skillsResult","agents","installedAgents","err","Error","String","warn","message","alreadyConfiguredEditors","label","succeed","initNext","nextjsAddConfigFiles","selectedOption","sluggedName","toLowerCase","replaceAll","initFramework","defaults","isPlugin","outputPath","useEnv","Boolean","envVars","DATASET","PROJECT_ID","filename","framework","workbench","unstableWorkbench","sharedParams","hasProjectFlag","alreadyLoggedIn","email","provider","newContext","loggedInUser"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/init/initAction.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {\n exitCodes,\n type SanityOrgUser,\n subdebug,\n type TelemetryUserProperties,\n} from '@sanity/cli-core'\nimport {logSymbols, spinner} from '@sanity/cli-core/ux'\nimport {type TelemetryTrace} from '@sanity/telemetry'\nimport {type Framework, frameworks} from '@vercel/frameworks'\nimport deburr from 'lodash-es/deburr.js'\n\nimport {promptForConfigFiles} from '../../prompts/init/nextjs.js'\nimport {getCliUser} from '../../services/user.js'\nimport {CLIInitStepCompleted, type InitStepResult} from '../../telemetry/init.telemetry.js'\nimport {detectFrameworkRecord} from '../../util/detectFramework.js'\nimport {formatCliErrorMessages} from '../../util/formatCliErrorMessages.js'\nimport {getProjectDefaults} from '../../util/getProjectDefaults.js'\nimport {validateSession} from '../auth/ensureAuthenticated.js'\nimport {getProviderName} from '../auth/getProviderName.js'\nimport {login} from '../auth/login/login.js'\nimport {LOGIN_REQUIRED_MESSAGE} from '../auth/login/loginInstructions.js'\nimport {detectAvailableEditors} from '../mcp/detectAvailableEditors.js'\nimport {setupMCP} from '../mcp/setupMCP.js'\nimport {setupSkills} from '../skills/setupSkills.js'\nimport {checkNextJsReactCompatibility} from './checkNextJsReactCompatibility.js'\nimport {determineAppTemplate} from './determineAppTemplate.js'\nimport {createOrAppendEnvVars} from './env/createOrAppendEnvVars.js'\nimport {initApp} from './initApp.js'\nimport {InitError} from './initError.js'\nimport {flagOrDefault, shouldPrompt, writeStagingEnvIfNeeded} from './initHelpers.js'\nimport {initNextJs} from './initNextJs.js'\nimport {initStudio} from './initStudio.js'\nimport {getPlan} from './plan/getPlan.js'\nimport {createProjectFromName} from './project/createProjectFromName.js'\nimport {getProjectDetails} from './project/getProjectDetails.js'\nimport {getProjectOutputPath} from './project/getProjectOutputPath.js'\nimport {checkIsRemoteTemplate, getGitHubRepoInfo, type RepoInfo} from './remoteTemplate.js'\nimport {type InitContext, type InitOptions} from './types.js'\n\nconst debug = subdebug('init')\n\nexport async function initAction(options: InitOptions, context: InitContext): Promise<void> {\n const {output, workDir} = context\n\n if (options.argType) {\n throw new InitError(\n options.argType === 'plugin'\n ? 'Initializing plugins through the CLI is no longer supported'\n : `Unknown init type \"${options.argType}\"`,\n 1,\n )\n }\n\n const trace = context.telemetry.trace(CLIInitStepCompleted)\n\n if (options.reconfigure) {\n throw new InitError('--reconfigure is deprecated - manual configuration is now required', 1)\n }\n\n if (options.project && options.organization) {\n throw new InitError(\n 'You have specified both a project and an organization. To move a project to an organization please visit https://www.sanity.io/manage',\n 1,\n )\n }\n\n const defaultConfig = options.datasetDefault\n let showDefaultConfigPrompt = !defaultConfig\n if (options.dataset || options.visibility || options.datasetDefault || options.unattended) {\n showDefaultConfigPrompt = false\n }\n\n const detectedFramework = await detectFrameworkRecord({\n frameworkList: frameworks as readonly Framework[],\n rootPath: workDir,\n })\n const isNextJs = detectedFramework?.slug === 'nextjs'\n\n let remoteTemplateInfo: RepoInfo | undefined\n if (options.template && checkIsRemoteTemplate(options.template)) {\n remoteTemplateInfo = await getGitHubRepoInfo(options.template, options.templateToken)\n }\n\n if (detectedFramework && detectedFramework.slug !== 'sanity' && remoteTemplateInfo) {\n throw new InitError(\n `A remote template cannot be used with a detected framework. Detected: ${detectedFramework.name}`,\n 1,\n )\n }\n\n const isAppTemplate = options.template ? determineAppTemplate(options.template) : false\n\n if (options.unattended) {\n checkFlagsInUnattendedMode(options, {isAppTemplate, isNextJs})\n }\n\n trace.start()\n trace.log({\n flags: {\n bare: options.bare,\n coupon: options.coupon,\n defaultConfig,\n env: options.env,\n git: typeof options.git === 'string' ? options.git : undefined,\n plan: options.projectPlan,\n reconfigure: options.reconfigure,\n unattended: options.unattended,\n },\n step: 'start',\n })\n\n const planId = await getPlan(options, output, trace)\n\n let envFilenameDefault = '.env'\n if (detectedFramework && detectedFramework.slug === 'nextjs') {\n envFilenameDefault = '.env.local'\n }\n const envFilename = typeof options.env === 'string' ? options.env : envFilenameDefault\n\n const {user} = await ensureAuthenticated(options, output, trace)\n if (!isAppTemplate) {\n output.log(`${logSymbols.success} Fetching existing projects`)\n output.log('')\n }\n\n let newProject: string | undefined\n if (options.projectName) {\n newProject = await createProjectFromName({\n coupon: options.coupon,\n createProjectName: options.projectName,\n dataset: options.dataset,\n organization: options.organization,\n planId,\n user,\n visibility: options.visibility,\n })\n }\n\n const {datasetName, displayName, isFirstProject, organizationId, projectId} =\n await getProjectDetails({\n coupon: options.coupon,\n dataset: options.dataset,\n datasetDefault: options.datasetDefault,\n isAppTemplate,\n newProject,\n organization: options.organization,\n output,\n planId,\n project: options.project,\n showDefaultConfigPrompt,\n trace,\n unattended: options.unattended,\n user,\n visibility: options.visibility,\n })\n\n if (options.bare) {\n output.log(`${logSymbols.success} Below are your project details`)\n output.log('')\n output.log(`Project ID: ${styleText('cyan', projectId)}`)\n output.log(`Dataset: ${styleText('cyan', datasetName)}`)\n output.log(\n `\\nYou can find your project on Sanity Manage — https://www.sanity.io/manage/project/${projectId}\\n`,\n )\n trace.complete()\n return\n }\n\n // Detect editors once, then share the result with MCP and skills setup so\n // we don't pay the detection cost (filesystem probes + CLI execa calls) twice.\n const detectedEditors =\n options.mcpMode === 'skip' && options.skillsMode === 'skip'\n ? []\n : await detectAvailableEditors()\n\n const mcpResult = await setupMCP({\n editors: detectedEditors,\n mode: options.mcpMode,\n output,\n skillsMode: options.skillsMode,\n })\n\n trace.log({\n configuredEditors: mcpResult.configuredEditors,\n detectedEditors: mcpResult.detectedEditors,\n skipped: mcpResult.skipped,\n step: 'mcpSetup',\n })\n if (mcpResult.error) {\n trace.error(mcpResult.error)\n }\n const mcpConfigured = mcpResult.configuredEditors\n\n async function installSkills(): Promise<void> {\n if (mcpResult.skillsToInstall.length === 0) return\n try {\n const skillsResult = await setupSkills({\n agents: mcpResult.skillsToInstall,\n output,\n })\n trace.log({\n installedAgents: skillsResult.installedAgents,\n skipped: skillsResult.skipped,\n step: 'skillsSetup',\n })\n if (skillsResult.error) {\n trace.error(skillsResult.error)\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n debug('Unexpected error from setupSkills %O', err)\n output.warn(`Could not install Sanity agent skills: ${err.message}`)\n trace.error(err)\n }\n }\n\n // Install skills right after the MCP/skills prompt so the progress + result\n // surface in sequence, rather than trailing the studio \"Success!\" output.\n await installSkills()\n\n const {alreadyConfiguredEditors} = mcpResult\n if (alreadyConfiguredEditors.length > 0) {\n const label =\n alreadyConfiguredEditors.length === 1\n ? `${alreadyConfiguredEditors[0]} already configured for Sanity MCP`\n : `${alreadyConfiguredEditors.length} editors already configured for Sanity MCP`\n spinner(label).start().succeed()\n }\n\n let initNext = flagOrDefault(options.nextjsAddConfigFiles, false)\n if (isNextJs && shouldPrompt(options.unattended, options.nextjsAddConfigFiles)) {\n initNext = await promptForConfigFiles()\n }\n\n trace.log({\n detectedFramework: detectedFramework?.name,\n selectedOption: initNext ? 'yes' : 'no',\n step: 'useDetectedFramework',\n })\n\n const sluggedName = deburr(displayName.toLowerCase())\n .replaceAll(/\\s+/g, '-')\n .replaceAll(/[^a-z0-9-]/g, '')\n\n const initFramework = initNext\n\n const defaults = await getProjectDefaults({isPlugin: false, workDir})\n\n const outputPath = await getProjectOutputPath({\n initFramework,\n outputPath: options.outputPath,\n sluggedName,\n unattended: options.unattended,\n useEnv: Boolean(options.env),\n workDir,\n })\n\n if (isNextJs) {\n await checkNextJsReactCompatibility({\n detectedFramework,\n output,\n outputPath,\n })\n }\n\n if (initNext) {\n await initNextJs({\n datasetName,\n detectedFramework,\n envFilename,\n mcpConfigured,\n options,\n output,\n projectId,\n trace,\n workDir,\n })\n trace.complete()\n return\n }\n\n if (options.env) {\n await createOrAppendEnvVars({\n envVars: {\n DATASET: datasetName,\n PROJECT_ID: projectId,\n },\n filename: envFilename,\n framework: detectedFramework,\n log: false,\n output,\n outputPath,\n })\n await writeStagingEnvIfNeeded(output, outputPath)\n trace.complete()\n return\n }\n\n // Workbench is opt-in (no prompt): the flag swaps the scaffolded\n // `sanity.cli.*` over to `unstable_defineApp` — the branded app is the sole\n // workbench opt-in.\n const workbench = flagOrDefault(options.unstableWorkbench, false)\n\n const sharedParams = {\n defaults,\n mcpConfigured,\n options,\n organizationId,\n output,\n outputPath,\n remoteTemplateInfo,\n sluggedName,\n trace,\n workbench,\n workDir,\n }\n\n await (isAppTemplate\n ? initApp({...sharedParams, datasetName, projectId})\n : initStudio({\n ...sharedParams,\n datasetName,\n displayName,\n isFirstProject,\n projectId,\n }))\n\n trace.complete()\n}\n\nfunction checkFlagsInUnattendedMode(\n options: InitOptions,\n {isAppTemplate, isNextJs}: {isAppTemplate: boolean; isNextJs: boolean},\n): void {\n debug('Unattended mode, validating required options')\n\n const errors: string[] = []\n\n if (isAppTemplate) {\n if (!options.outputPath) {\n errors.push(\n 'Output path is required in unattended mode. Pass it with `--output-path <path>`.',\n )\n }\n\n if (options.projectName && !options.organization) {\n errors.push(\n 'Organization is required when creating a project in unattended mode. Pass it with `--organization <id>`.',\n )\n } else if (!options.project && !options.organization) {\n errors.push(\n 'Project or organization is required for app templates in unattended mode. ' +\n 'Pass `--project <id>` or `--organization <id>`. To create a project, pass ' +\n '`--project-name <name>` with `--organization <id>`.',\n )\n }\n } else {\n if (!isNextJs && !options.bare && !options.outputPath) {\n errors.push(\n 'Output path is required in unattended mode. Pass it with `--output-path <path>`.',\n )\n }\n\n if (!options.project && !options.projectName) {\n errors.push(\n 'Project is required in unattended mode. Pass it with `--project <id>` or `--project-name <name>`.',\n )\n }\n\n if (!options.project && !options.organization) {\n errors.push(\n 'Organization is required when creating a project in unattended mode. Pass it with `--organization <id>`.',\n )\n }\n }\n\n if (errors.length > 0) {\n throw new InitError(formatCliErrorMessages(errors), exitCodes.USAGE_ERROR)\n }\n}\n\nasync function ensureAuthenticated(\n options: InitOptions,\n output: InitContext['output'],\n trace: TelemetryTrace<TelemetryUserProperties, InitStepResult>,\n): Promise<{user: SanityOrgUser}> {\n const user = await validateSession()\n\n if (user) {\n trace.log({alreadyLoggedIn: true, step: 'login'})\n output.log(\n `${logSymbols.success} You are logged in as ${user.email} using ${getProviderName(user.provider)}`,\n )\n return {user}\n }\n\n if (options.unattended) {\n throw new InitError(LOGIN_REQUIRED_MESSAGE, exitCodes.RUNTIME_ERROR)\n }\n\n trace.log({step: 'login'})\n output.warn(LOGIN_REQUIRED_MESSAGE)\n\n try {\n await login({\n output,\n telemetry: trace.newContext('login'),\n })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new InitError(`Login failed: ${message}`, 1)\n }\n\n const loggedInUser = await getCliUser()\n\n output.log(\n `${logSymbols.success} You are logged in as ${loggedInUser.email} using ${getProviderName(loggedInUser.provider)}`,\n )\n return {user: loggedInUser}\n}\n"],"names":["styleText","exitCodes","subdebug","logSymbols","spinner","frameworks","deburr","promptForConfigFiles","getCliUser","CLIInitStepCompleted","detectFrameworkRecord","formatCliErrorMessages","getProjectDefaults","validateSession","getProviderName","login","LOGIN_REQUIRED_MESSAGE","detectAvailableEditors","setupMCP","setupSkills","checkNextJsReactCompatibility","determineAppTemplate","createOrAppendEnvVars","initApp","InitError","flagOrDefault","shouldPrompt","writeStagingEnvIfNeeded","initNextJs","initStudio","getPlan","createProjectFromName","getProjectDetails","getProjectOutputPath","checkIsRemoteTemplate","getGitHubRepoInfo","debug","initAction","options","context","output","workDir","argType","trace","telemetry","reconfigure","project","organization","defaultConfig","datasetDefault","showDefaultConfigPrompt","dataset","visibility","unattended","detectedFramework","frameworkList","rootPath","isNextJs","slug","remoteTemplateInfo","template","templateToken","name","isAppTemplate","checkFlagsInUnattendedMode","start","log","flags","bare","coupon","env","git","undefined","plan","projectPlan","step","planId","envFilenameDefault","envFilename","user","ensureAuthenticated","success","newProject","projectName","createProjectName","datasetName","displayName","isFirstProject","organizationId","projectId","complete","detectedEditors","mcpMode","skillsMode","mcpResult","editors","mode","configuredEditors","skipped","error","mcpConfigured","installSkills","skillsToInstall","length","skillsResult","agents","installedAgents","err","Error","String","warn","message","alreadyConfiguredEditors","label","succeed","initNext","nextjsAddConfigFiles","selectedOption","sluggedName","toLowerCase","replaceAll","initFramework","defaults","isPlugin","outputPath","useEnv","Boolean","envVars","DATASET","PROJECT_ID","filename","framework","workbench","unstableWorkbench","sharedParams","errors","push","USAGE_ERROR","alreadyLoggedIn","email","provider","RUNTIME_ERROR","newContext","loggedInUser"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SACEC,SAAS,EAETC,QAAQ,QAEH,mBAAkB;AACzB,SAAQC,UAAU,EAAEC,OAAO,QAAO,sBAAqB;AAEvD,SAAwBC,UAAU,QAAO,qBAAoB;AAC7D,OAAOC,YAAY,sBAAqB;AAExC,SAAQC,oBAAoB,QAAO,+BAA8B;AACjE,SAAQC,UAAU,QAAO,yBAAwB;AACjD,SAAQC,oBAAoB,QAA4B,oCAAmC;AAC3F,SAAQC,qBAAqB,QAAO,gCAA+B;AACnE,SAAQC,sBAAsB,QAAO,uCAAsC;AAC3E,SAAQC,kBAAkB,QAAO,mCAAkC;AACnE,SAAQC,eAAe,QAAO,iCAAgC;AAC9D,SAAQC,eAAe,QAAO,6BAA4B;AAC1D,SAAQC,KAAK,QAAO,yBAAwB;AAC5C,SAAQC,sBAAsB,QAAO,qCAAoC;AACzE,SAAQC,sBAAsB,QAAO,mCAAkC;AACvE,SAAQC,QAAQ,QAAO,qBAAoB;AAC3C,SAAQC,WAAW,QAAO,2BAA0B;AACpD,SAAQC,6BAA6B,QAAO,qCAAoC;AAChF,SAAQC,oBAAoB,QAAO,4BAA2B;AAC9D,SAAQC,qBAAqB,QAAO,iCAAgC;AACpE,SAAQC,OAAO,QAAO,eAAc;AACpC,SAAQC,SAAS,QAAO,iBAAgB;AACxC,SAAQC,aAAa,EAAEC,YAAY,EAAEC,uBAAuB,QAAO,mBAAkB;AACrF,SAAQC,UAAU,QAAO,kBAAiB;AAC1C,SAAQC,UAAU,QAAO,kBAAiB;AAC1C,SAAQC,OAAO,QAAO,oBAAmB;AACzC,SAAQC,qBAAqB,QAAO,qCAAoC;AACxE,SAAQC,iBAAiB,QAAO,iCAAgC;AAChE,SAAQC,oBAAoB,QAAO,oCAAmC;AACtE,SAAQC,qBAAqB,EAAEC,iBAAiB,QAAsB,sBAAqB;AAG3F,MAAMC,QAAQlC,SAAS;AAEvB,OAAO,eAAemC,WAAWC,OAAoB,EAAEC,OAAoB;IACzE,MAAM,EAACC,MAAM,EAAEC,OAAO,EAAC,GAAGF;IAE1B,IAAID,QAAQI,OAAO,EAAE;QACnB,MAAM,IAAIlB,UACRc,QAAQI,OAAO,KAAK,WAChB,gEACA,CAAC,mBAAmB,EAAEJ,QAAQI,OAAO,CAAC,CAAC,CAAC,EAC5C;IAEJ;IAEA,MAAMC,QAAQJ,QAAQK,SAAS,CAACD,KAAK,CAAClC;IAEtC,IAAI6B,QAAQO,WAAW,EAAE;QACvB,MAAM,IAAIrB,UAAU,sEAAsE;IAC5F;IAEA,IAAIc,QAAQQ,OAAO,IAAIR,QAAQS,YAAY,EAAE;QAC3C,MAAM,IAAIvB,UACR,yIACA;IAEJ;IAEA,MAAMwB,gBAAgBV,QAAQW,cAAc;IAC5C,IAAIC,0BAA0B,CAACF;IAC/B,IAAIV,QAAQa,OAAO,IAAIb,QAAQc,UAAU,IAAId,QAAQW,cAAc,IAAIX,QAAQe,UAAU,EAAE;QACzFH,0BAA0B;IAC5B;IAEA,MAAMI,oBAAoB,MAAM5C,sBAAsB;QACpD6C,eAAelD;QACfmD,UAAUf;IACZ;IACA,MAAMgB,WAAWH,mBAAmBI,SAAS;IAE7C,IAAIC;IACJ,IAAIrB,QAAQsB,QAAQ,IAAI1B,sBAAsBI,QAAQsB,QAAQ,GAAG;QAC/DD,qBAAqB,MAAMxB,kBAAkBG,QAAQsB,QAAQ,EAAEtB,QAAQuB,aAAa;IACtF;IAEA,IAAIP,qBAAqBA,kBAAkBI,IAAI,KAAK,YAAYC,oBAAoB;QAClF,MAAM,IAAInC,UACR,CAAC,sEAAsE,EAAE8B,kBAAkBQ,IAAI,EAAE,EACjG;IAEJ;IAEA,MAAMC,gBAAgBzB,QAAQsB,QAAQ,GAAGvC,qBAAqBiB,QAAQsB,QAAQ,IAAI;IAElF,IAAItB,QAAQe,UAAU,EAAE;QACtBW,2BAA2B1B,SAAS;YAACyB;YAAeN;QAAQ;IAC9D;IAEAd,MAAMsB,KAAK;IACXtB,MAAMuB,GAAG,CAAC;QACRC,OAAO;YACLC,MAAM9B,QAAQ8B,IAAI;YAClBC,QAAQ/B,QAAQ+B,MAAM;YACtBrB;YACAsB,KAAKhC,QAAQgC,GAAG;YAChBC,KAAK,OAAOjC,QAAQiC,GAAG,KAAK,WAAWjC,QAAQiC,GAAG,GAAGC;YACrDC,MAAMnC,QAAQoC,WAAW;YACzB7B,aAAaP,QAAQO,WAAW;YAChCQ,YAAYf,QAAQe,UAAU;QAChC;QACAsB,MAAM;IACR;IAEA,MAAMC,SAAS,MAAM9C,QAAQQ,SAASE,QAAQG;IAE9C,IAAIkC,qBAAqB;IACzB,IAAIvB,qBAAqBA,kBAAkBI,IAAI,KAAK,UAAU;QAC5DmB,qBAAqB;IACvB;IACA,MAAMC,cAAc,OAAOxC,QAAQgC,GAAG,KAAK,WAAWhC,QAAQgC,GAAG,GAAGO;IAEpE,MAAM,EAACE,IAAI,EAAC,GAAG,MAAMC,oBAAoB1C,SAASE,QAAQG;IAC1D,IAAI,CAACoB,eAAe;QAClBvB,OAAO0B,GAAG,CAAC,GAAG/D,WAAW8E,OAAO,CAAC,2BAA2B,CAAC;QAC7DzC,OAAO0B,GAAG,CAAC;IACb;IAEA,IAAIgB;IACJ,IAAI5C,QAAQ6C,WAAW,EAAE;QACvBD,aAAa,MAAMnD,sBAAsB;YACvCsC,QAAQ/B,QAAQ+B,MAAM;YACtBe,mBAAmB9C,QAAQ6C,WAAW;YACtChC,SAASb,QAAQa,OAAO;YACxBJ,cAAcT,QAAQS,YAAY;YAClC6B;YACAG;YACA3B,YAAYd,QAAQc,UAAU;QAChC;IACF;IAEA,MAAM,EAACiC,WAAW,EAAEC,WAAW,EAAEC,cAAc,EAAEC,cAAc,EAAEC,SAAS,EAAC,GACzE,MAAMzD,kBAAkB;QACtBqC,QAAQ/B,QAAQ+B,MAAM;QACtBlB,SAASb,QAAQa,OAAO;QACxBF,gBAAgBX,QAAQW,cAAc;QACtCc;QACAmB;QACAnC,cAAcT,QAAQS,YAAY;QAClCP;QACAoC;QACA9B,SAASR,QAAQQ,OAAO;QACxBI;QACAP;QACAU,YAAYf,QAAQe,UAAU;QAC9B0B;QACA3B,YAAYd,QAAQc,UAAU;IAChC;IAEF,IAAId,QAAQ8B,IAAI,EAAE;QAChB5B,OAAO0B,GAAG,CAAC,GAAG/D,WAAW8E,OAAO,CAAC,+BAA+B,CAAC;QACjEzC,OAAO0B,GAAG,CAAC;QACX1B,OAAO0B,GAAG,CAAC,CAAC,YAAY,EAAElE,UAAU,QAAQyF,YAAY;QACxDjD,OAAO0B,GAAG,CAAC,CAAC,SAAS,EAAElE,UAAU,QAAQqF,cAAc;QACvD7C,OAAO0B,GAAG,CACR,CAAC,oFAAoF,EAAEuB,UAAU,EAAE,CAAC;QAEtG9C,MAAM+C,QAAQ;QACd;IACF;IAEA,0EAA0E;IAC1E,+EAA+E;IAC/E,MAAMC,kBACJrD,QAAQsD,OAAO,KAAK,UAAUtD,QAAQuD,UAAU,KAAK,SACjD,EAAE,GACF,MAAM5E;IAEZ,MAAM6E,YAAY,MAAM5E,SAAS;QAC/B6E,SAASJ;QACTK,MAAM1D,QAAQsD,OAAO;QACrBpD;QACAqD,YAAYvD,QAAQuD,UAAU;IAChC;IAEAlD,MAAMuB,GAAG,CAAC;QACR+B,mBAAmBH,UAAUG,iBAAiB;QAC9CN,iBAAiBG,UAAUH,eAAe;QAC1CO,SAASJ,UAAUI,OAAO;QAC1BvB,MAAM;IACR;IACA,IAAImB,UAAUK,KAAK,EAAE;QACnBxD,MAAMwD,KAAK,CAACL,UAAUK,KAAK;IAC7B;IACA,MAAMC,gBAAgBN,UAAUG,iBAAiB;IAEjD,eAAeI;QACb,IAAIP,UAAUQ,eAAe,CAACC,MAAM,KAAK,GAAG;QAC5C,IAAI;YACF,MAAMC,eAAe,MAAMrF,YAAY;gBACrCsF,QAAQX,UAAUQ,eAAe;gBACjC9D;YACF;YACAG,MAAMuB,GAAG,CAAC;gBACRwC,iBAAiBF,aAAaE,eAAe;gBAC7CR,SAASM,aAAaN,OAAO;gBAC7BvB,MAAM;YACR;YACA,IAAI6B,aAAaL,KAAK,EAAE;gBACtBxD,MAAMwD,KAAK,CAACK,aAAaL,KAAK;YAChC;QACF,EAAE,OAAOA,OAAO;YACd,MAAMQ,MAAMR,iBAAiBS,QAAQT,QAAQ,IAAIS,MAAMC,OAAOV;YAC9D/D,MAAM,wCAAwCuE;YAC9CnE,OAAOsE,IAAI,CAAC,CAAC,uCAAuC,EAAEH,IAAII,OAAO,EAAE;YACnEpE,MAAMwD,KAAK,CAACQ;QACd;IACF;IAEA,4EAA4E;IAC5E,0EAA0E;IAC1E,MAAMN;IAEN,MAAM,EAACW,wBAAwB,EAAC,GAAGlB;IACnC,IAAIkB,yBAAyBT,MAAM,GAAG,GAAG;QACvC,MAAMU,QACJD,yBAAyBT,MAAM,KAAK,IAChC,GAAGS,wBAAwB,CAAC,EAAE,CAAC,kCAAkC,CAAC,GAClE,GAAGA,yBAAyBT,MAAM,CAAC,0CAA0C,CAAC;QACpFnG,QAAQ6G,OAAOhD,KAAK,GAAGiD,OAAO;IAChC;IAEA,IAAIC,WAAW1F,cAAca,QAAQ8E,oBAAoB,EAAE;IAC3D,IAAI3D,YAAY/B,aAAaY,QAAQe,UAAU,EAAEf,QAAQ8E,oBAAoB,GAAG;QAC9ED,WAAW,MAAM5G;IACnB;IAEAoC,MAAMuB,GAAG,CAAC;QACRZ,mBAAmBA,mBAAmBQ;QACtCuD,gBAAgBF,WAAW,QAAQ;QACnCxC,MAAM;IACR;IAEA,MAAM2C,cAAchH,OAAOgF,YAAYiC,WAAW,IAC/CC,UAAU,CAAC,QAAQ,KACnBA,UAAU,CAAC,eAAe;IAE7B,MAAMC,gBAAgBN;IAEtB,MAAMO,WAAW,MAAM9G,mBAAmB;QAAC+G,UAAU;QAAOlF;IAAO;IAEnE,MAAMmF,aAAa,MAAM3F,qBAAqB;QAC5CwF;QACAG,YAAYtF,QAAQsF,UAAU;QAC9BN;QACAjE,YAAYf,QAAQe,UAAU;QAC9BwE,QAAQC,QAAQxF,QAAQgC,GAAG;QAC3B7B;IACF;IAEA,IAAIgB,UAAU;QACZ,MAAMrC,8BAA8B;YAClCkC;YACAd;YACAoF;QACF;IACF;IAEA,IAAIT,UAAU;QACZ,MAAMvF,WAAW;YACfyD;YACA/B;YACAwB;YACAsB;YACA9D;YACAE;YACAiD;YACA9C;YACAF;QACF;QACAE,MAAM+C,QAAQ;QACd;IACF;IAEA,IAAIpD,QAAQgC,GAAG,EAAE;QACf,MAAMhD,sBAAsB;YAC1ByG,SAAS;gBACPC,SAAS3C;gBACT4C,YAAYxC;YACd;YACAyC,UAAUpD;YACVqD,WAAW7E;YACXY,KAAK;YACL1B;YACAoF;QACF;QACA,MAAMjG,wBAAwBa,QAAQoF;QACtCjF,MAAM+C,QAAQ;QACd;IACF;IAEA,iEAAiE;IACjE,4EAA4E;IAC5E,oBAAoB;IACpB,MAAM0C,YAAY3G,cAAca,QAAQ+F,iBAAiB,EAAE;IAE3D,MAAMC,eAAe;QACnBZ;QACAtB;QACA9D;QACAkD;QACAhD;QACAoF;QACAjE;QACA2D;QACA3E;QACAyF;QACA3F;IACF;IAEA,MAAOsB,CAAAA,gBACHxC,QAAQ;QAAC,GAAG+G,YAAY;QAAEjD;QAAaI;IAAS,KAChD5D,WAAW;QACT,GAAGyG,YAAY;QACfjD;QACAC;QACAC;QACAE;IACF,EAAC;IAEL9C,MAAM+C,QAAQ;AAChB;AAEA,SAAS1B,2BACP1B,OAAoB,EACpB,EAACyB,aAAa,EAAEN,QAAQ,EAA8C;IAEtErB,MAAM;IAEN,MAAMmG,SAAmB,EAAE;IAE3B,IAAIxE,eAAe;QACjB,IAAI,CAACzB,QAAQsF,UAAU,EAAE;YACvBW,OAAOC,IAAI,CACT;QAEJ;QAEA,IAAIlG,QAAQ6C,WAAW,IAAI,CAAC7C,QAAQS,YAAY,EAAE;YAChDwF,OAAOC,IAAI,CACT;QAEJ,OAAO,IAAI,CAAClG,QAAQQ,OAAO,IAAI,CAACR,QAAQS,YAAY,EAAE;YACpDwF,OAAOC,IAAI,CACT,+EACE,+EACA;QAEN;IACF,OAAO;QACL,IAAI,CAAC/E,YAAY,CAACnB,QAAQ8B,IAAI,IAAI,CAAC9B,QAAQsF,UAAU,EAAE;YACrDW,OAAOC,IAAI,CACT;QAEJ;QAEA,IAAI,CAAClG,QAAQQ,OAAO,IAAI,CAACR,QAAQ6C,WAAW,EAAE;YAC5CoD,OAAOC,IAAI,CACT;QAEJ;QAEA,IAAI,CAAClG,QAAQQ,OAAO,IAAI,CAACR,QAAQS,YAAY,EAAE;YAC7CwF,OAAOC,IAAI,CACT;QAEJ;IACF;IAEA,IAAID,OAAOhC,MAAM,GAAG,GAAG;QACrB,MAAM,IAAI/E,UAAUb,uBAAuB4H,SAAStI,UAAUwI,WAAW;IAC3E;AACF;AAEA,eAAezD,oBACb1C,OAAoB,EACpBE,MAA6B,EAC7BG,KAA8D;IAE9D,MAAMoC,OAAO,MAAMlE;IAEnB,IAAIkE,MAAM;QACRpC,MAAMuB,GAAG,CAAC;YAACwE,iBAAiB;YAAM/D,MAAM;QAAO;QAC/CnC,OAAO0B,GAAG,CACR,GAAG/D,WAAW8E,OAAO,CAAC,sBAAsB,EAAEF,KAAK4D,KAAK,CAAC,OAAO,EAAE7H,gBAAgBiE,KAAK6D,QAAQ,GAAG;QAEpG,OAAO;YAAC7D;QAAI;IACd;IAEA,IAAIzC,QAAQe,UAAU,EAAE;QACtB,MAAM,IAAI7B,UAAUR,wBAAwBf,UAAU4I,aAAa;IACrE;IAEAlG,MAAMuB,GAAG,CAAC;QAACS,MAAM;IAAO;IACxBnC,OAAOsE,IAAI,CAAC9F;IAEZ,IAAI;QACF,MAAMD,MAAM;YACVyB;YACAI,WAAWD,MAAMmG,UAAU,CAAC;QAC9B;IACF,EAAE,OAAO3C,OAAO;QACd,MAAMY,UAAUZ,iBAAiBS,QAAQT,MAAMY,OAAO,GAAGF,OAAOV;QAChE,MAAM,IAAI3E,UAAU,CAAC,cAAc,EAAEuF,SAAS,EAAE;IAClD;IAEA,MAAMgC,eAAe,MAAMvI;IAE3BgC,OAAO0B,GAAG,CACR,GAAG/D,WAAW8E,OAAO,CAAC,sBAAsB,EAAE8D,aAAaJ,KAAK,CAAC,OAAO,EAAE7H,gBAAgBiI,aAAaH,QAAQ,GAAG;IAEpH,OAAO;QAAC7D,MAAMgE;IAAY;AAC5B"}
@@ -31,13 +31,15 @@ const sanitizeIconPath = new URL('sanitizeIcon.js', import.meta.url).href;
31
31
  return mod.sanitizeIcon;
32
32
  }
33
33
  /**
34
- * Resolves app.icon from config (a file path) to an SVG string for the manifest.
35
- * The manifest expects the SVG string inline, not a path.
34
+ * Resolves app.icon from config (a file path) to a sanitized SVG string. Used
35
+ * both for the manifest and, on deploy, for the icon shipped to Brett the
36
+ * manifest expects the SVG inline, not a path.
36
37
  *
37
38
  * The file is sanitized through the same allowlist as the studio manifest's
38
39
  * icon resolver (see {@link lazySanitizeIcon}) so both manifest paths inline the
39
40
  * same trusted subset of SVG markup.
40
- */ async function readIconFromPath(workDir, iconPath) {
41
+ * @internal
42
+ */ export async function readIconFromPath(workDir, iconPath) {
41
43
  const resolvedPath = resolve(workDir, iconPath);
42
44
  const pathRelativeToWorkDir = relative(workDir, resolvedPath);
43
45
  if (pathRelativeToWorkDir.startsWith('..')) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/manifest/extractCoreAppManifest.ts"],"sourcesContent":["import {readFile} from 'node:fs/promises'\nimport {relative, resolve} from 'node:path'\n\nimport {doImport, getCliConfigUncached} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {isWorkbenchApp} from '@sanity/workbench-cli'\n\nimport {type sanitizeIcon as sanitizeIconFn} from './sanitizeIcon.js'\nimport {type CoreAppManifest, coreAppManifestSchema} from './types.js'\n\ninterface ExtractCoreAppManifestOptions {\n workDir: string\n}\n\n/**\n * The title change a deploy would sync from the manifest to the user\n * application, or null when the titles already match (or none is set).\n *\n * @internal\n */\nexport function resolveTitleUpdate(\n manifest: CoreAppManifest | undefined,\n application: {title: string | null},\n): {from: string | null; to: string} | null {\n if (manifest?.title === undefined || manifest.title === application.title) {\n return null\n }\n return {from: application.title, to: manifest.title}\n}\n\nconst sanitizeIconPath = new URL('sanitizeIcon.js', import.meta.url).href\n\n/**\n * Lazy-load {@link sanitizeIconFn} so `isomorphic-dompurify` (and its jsdom\n * dependency) stays out of the CLI's eager import graph. The studio manifest\n * resolver lazy-loads its icon machinery for the same reason; this path runs in\n * the main process (not the manifest worker), so only an app deploy that\n * actually has an icon pays the cost.\n */\nasync function lazySanitizeIcon(): Promise<typeof sanitizeIconFn> {\n const mod = await doImport(sanitizeIconPath)\n return mod.sanitizeIcon\n}\n\n/**\n * Resolves app.icon from config (a file path) to an SVG string for the manifest.\n * The manifest expects the SVG string inline, not a path.\n *\n * The file is sanitized through the same allowlist as the studio manifest's\n * icon resolver (see {@link lazySanitizeIcon}) so both manifest paths inline the\n * same trusted subset of SVG markup.\n */\nasync function readIconFromPath(workDir: string, iconPath: string): Promise<string> {\n const resolvedPath = resolve(workDir, iconPath)\n const pathRelativeToWorkDir = relative(workDir, resolvedPath)\n if (pathRelativeToWorkDir.startsWith('..')) {\n throw new Error(\n `Icon path \"${iconPath}\" resolves outside the project directory and is not allowed.`,\n )\n }\n\n let content: string\n try {\n content = await readFile(resolvedPath, 'utf8')\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new Error(\n `Could not read icon file at \"${iconPath}\" (resolved: ${resolvedPath}): ${message}`,\n {cause: err},\n )\n }\n\n const trimmed = content.trim()\n if (!/<svg[\\s>]/i.test(trimmed)) {\n throw new Error(\n `Icon file at \"${iconPath}\" does not contain an SVG element. App manifest icons must be SVG files.`,\n )\n }\n\n const sanitizeIcon = await lazySanitizeIcon()\n return sanitizeIcon(trimmed)\n}\n\n/**\n * Unlike studio manifest extraction, skips schema/tool parsing. The config's\n * `app.icon` is a file path; its content is read and inlined in the manifest.\n */\nexport async function extractCoreAppManifest(\n options: ExtractCoreAppManifestOptions,\n): Promise<CoreAppManifest | undefined> {\n const {workDir} = options\n const {app} = await getCliConfigUncached(workDir)\n if (!app) {\n return undefined\n }\n\n const spin = spinner('Extracting manifest').start()\n\n try {\n let icon: string | undefined\n if (app.icon) {\n icon = await readIconFromPath(workDir, app.icon)\n }\n\n if (!icon && !app.title) {\n spin.info('Manifest creation skipped: no icon or title found in app configuration')\n return undefined\n }\n\n const slug = isWorkbenchApp(app) ? app.slug : undefined\n\n const manifest: CoreAppManifest = coreAppManifestSchema.parse({\n version: '1',\n ...(icon ? {icon} : {}),\n ...(app.title ? {title: app.title} : {}),\n ...(app.group ? {group: app.group} : {}),\n ...(app.priority === undefined ? {} : {priority: app.priority}),\n ...(slug ? {slug} : {}),\n })\n\n spin.succeed(`Extracted manifest`)\n\n return manifest\n } catch (err) {\n const message = getErrorMessage(err)\n spin.fail(message)\n throw err\n }\n}\n"],"names":["readFile","relative","resolve","doImport","getCliConfigUncached","getErrorMessage","spinner","isWorkbenchApp","coreAppManifestSchema","resolveTitleUpdate","manifest","application","title","undefined","from","to","sanitizeIconPath","URL","url","href","lazySanitizeIcon","mod","sanitizeIcon","readIconFromPath","workDir","iconPath","resolvedPath","pathRelativeToWorkDir","startsWith","Error","content","err","message","String","cause","trimmed","trim","test","extractCoreAppManifest","options","app","spin","start","icon","info","slug","parse","version","group","priority","succeed","fail"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/manifest/extractCoreAppManifest.ts"],"sourcesContent":["import {readFile} from 'node:fs/promises'\nimport {relative, resolve} from 'node:path'\n\nimport {doImport, getCliConfigUncached} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {isWorkbenchApp} from '@sanity/workbench-cli'\n\nimport {type sanitizeIcon as sanitizeIconFn} from './sanitizeIcon.js'\nimport {type CoreAppManifest, coreAppManifestSchema} from './types.js'\n\ninterface ExtractCoreAppManifestOptions {\n workDir: string\n}\n\n/**\n * The title change a deploy would sync from the manifest to the user\n * application, or null when the titles already match (or none is set).\n *\n * @internal\n */\nexport function resolveTitleUpdate(\n manifest: CoreAppManifest | undefined,\n application: {title: string | null},\n): {from: string | null; to: string} | null {\n if (manifest?.title === undefined || manifest.title === application.title) {\n return null\n }\n return {from: application.title, to: manifest.title}\n}\n\nconst sanitizeIconPath = new URL('sanitizeIcon.js', import.meta.url).href\n\n/**\n * Lazy-load {@link sanitizeIconFn} so `isomorphic-dompurify` (and its jsdom\n * dependency) stays out of the CLI's eager import graph. The studio manifest\n * resolver lazy-loads its icon machinery for the same reason; this path runs in\n * the main process (not the manifest worker), so only an app deploy that\n * actually has an icon pays the cost.\n */\nasync function lazySanitizeIcon(): Promise<typeof sanitizeIconFn> {\n const mod = await doImport(sanitizeIconPath)\n return mod.sanitizeIcon\n}\n\n/**\n * Resolves app.icon from config (a file path) to a sanitized SVG string. Used\n * both for the manifest and, on deploy, for the icon shipped to Brett — the\n * manifest expects the SVG inline, not a path.\n *\n * The file is sanitized through the same allowlist as the studio manifest's\n * icon resolver (see {@link lazySanitizeIcon}) so both manifest paths inline the\n * same trusted subset of SVG markup.\n * @internal\n */\nexport async function readIconFromPath(workDir: string, iconPath: string): Promise<string> {\n const resolvedPath = resolve(workDir, iconPath)\n const pathRelativeToWorkDir = relative(workDir, resolvedPath)\n if (pathRelativeToWorkDir.startsWith('..')) {\n throw new Error(\n `Icon path \"${iconPath}\" resolves outside the project directory and is not allowed.`,\n )\n }\n\n let content: string\n try {\n content = await readFile(resolvedPath, 'utf8')\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new Error(\n `Could not read icon file at \"${iconPath}\" (resolved: ${resolvedPath}): ${message}`,\n {cause: err},\n )\n }\n\n const trimmed = content.trim()\n if (!/<svg[\\s>]/i.test(trimmed)) {\n throw new Error(\n `Icon file at \"${iconPath}\" does not contain an SVG element. App manifest icons must be SVG files.`,\n )\n }\n\n const sanitizeIcon = await lazySanitizeIcon()\n return sanitizeIcon(trimmed)\n}\n\n/**\n * Unlike studio manifest extraction, skips schema/tool parsing. The config's\n * `app.icon` is a file path; its content is read and inlined in the manifest.\n */\nexport async function extractCoreAppManifest(\n options: ExtractCoreAppManifestOptions,\n): Promise<CoreAppManifest | undefined> {\n const {workDir} = options\n const {app} = await getCliConfigUncached(workDir)\n if (!app) {\n return undefined\n }\n\n const spin = spinner('Extracting manifest').start()\n\n try {\n let icon: string | undefined\n if (app.icon) {\n icon = await readIconFromPath(workDir, app.icon)\n }\n\n if (!icon && !app.title) {\n spin.info('Manifest creation skipped: no icon or title found in app configuration')\n return undefined\n }\n\n const slug = isWorkbenchApp(app) ? app.slug : undefined\n\n const manifest: CoreAppManifest = coreAppManifestSchema.parse({\n version: '1',\n ...(icon ? {icon} : {}),\n ...(app.title ? {title: app.title} : {}),\n ...(app.group ? {group: app.group} : {}),\n ...(app.priority === undefined ? {} : {priority: app.priority}),\n ...(slug ? {slug} : {}),\n })\n\n spin.succeed(`Extracted manifest`)\n\n return manifest\n } catch (err) {\n const message = getErrorMessage(err)\n spin.fail(message)\n throw err\n }\n}\n"],"names":["readFile","relative","resolve","doImport","getCliConfigUncached","getErrorMessage","spinner","isWorkbenchApp","coreAppManifestSchema","resolveTitleUpdate","manifest","application","title","undefined","from","to","sanitizeIconPath","URL","url","href","lazySanitizeIcon","mod","sanitizeIcon","readIconFromPath","workDir","iconPath","resolvedPath","pathRelativeToWorkDir","startsWith","Error","content","err","message","String","cause","trimmed","trim","test","extractCoreAppManifest","options","app","spin","start","icon","info","slug","parse","version","group","priority","succeed","fail"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export const MIGRATIONS_DIRECTORY = 'migrations';
2
+ export const MIGRATION_SCRIPT_EXTENSIONS = [
3
+ 'mjs',
4
+ 'js',
5
+ 'ts',
6
+ 'cjs'
7
+ ];
8
+ export const DEFAULT_API_VERSION = 'v2024-01-29';
9
+
10
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/migration/constants.ts"],"sourcesContent":["export const MIGRATIONS_DIRECTORY = 'migrations'\nexport const MIGRATION_SCRIPT_EXTENSIONS = ['mjs', 'js', 'ts', 'cjs']\nexport const DEFAULT_API_VERSION = 'v2024-01-29'\n"],"names":["MIGRATIONS_DIRECTORY","MIGRATION_SCRIPT_EXTENSIONS","DEFAULT_API_VERSION"],"mappings":"AAAA,OAAO,MAAMA,uBAAuB,aAAY;AAChD,OAAO,MAAMC,8BAA8B;IAAC;IAAO;IAAM;IAAM;CAAM,CAAA;AACrE,OAAO,MAAMC,sBAAsB,cAAa"}
@@ -0,0 +1,16 @@
1
+ const VERSION_PATTERN = /^v\d+-\d+-\d+$|^vX$/ // Matches version strings like vYYYY-MM-DD or vX
2
+ ;
3
+ /**
4
+ * Ensures that the provided API version string is in the correct format.
5
+ * If the version does not start with 'v', it will be prefixed with 'v'.
6
+ * If the version does not match the expected pattern, an error will be thrown.
7
+ */ export function ensureApiVersionFormat(version) {
8
+ const normalizedVersion = version.startsWith('v') ? version : `v${version}`;
9
+ // Check if the version matches the expected pattern
10
+ if (!VERSION_PATTERN.test(normalizedVersion)) {
11
+ throw new Error(`Invalid API version format: ${normalizedVersion}. Expected format: vYYYY-MM-DD or vX`);
12
+ }
13
+ return normalizedVersion;
14
+ }
15
+
16
+ //# sourceMappingURL=ensureApiVersionFormat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/migration/ensureApiVersionFormat.ts"],"sourcesContent":["import {type APIConfig} from '@sanity/migrate'\n\ntype ApiVersion = APIConfig['apiVersion']\n\nconst VERSION_PATTERN = /^v\\d+-\\d+-\\d+$|^vX$/ // Matches version strings like vYYYY-MM-DD or vX\n\n/**\n * Ensures that the provided API version string is in the correct format.\n * If the version does not start with 'v', it will be prefixed with 'v'.\n * If the version does not match the expected pattern, an error will be thrown.\n */\nexport function ensureApiVersionFormat(version: string): ApiVersion {\n const normalizedVersion = version.startsWith('v') ? version : `v${version}`\n\n // Check if the version matches the expected pattern\n if (!VERSION_PATTERN.test(normalizedVersion)) {\n throw new Error(\n `Invalid API version format: ${normalizedVersion}. Expected format: vYYYY-MM-DD or vX`,\n )\n }\n\n return normalizedVersion as ApiVersion\n}\n"],"names":["VERSION_PATTERN","ensureApiVersionFormat","version","normalizedVersion","startsWith","test","Error"],"mappings":"AAIA,MAAMA,kBAAkB,sBAAsB,iDAAiD;;AAE/F;;;;CAIC,GACD,OAAO,SAASC,uBAAuBC,OAAe;IACpD,MAAMC,oBAAoBD,QAAQE,UAAU,CAAC,OAAOF,UAAU,CAAC,CAAC,EAAEA,SAAS;IAE3E,oDAAoD;IACpD,IAAI,CAACF,gBAAgBK,IAAI,CAACF,oBAAoB;QAC5C,MAAM,IAAIG,MACR,CAAC,4BAA4B,EAAEH,kBAAkB,oCAAoC,CAAC;IAE1F;IAEA,OAAOA;AACT"}
@@ -0,0 +1,13 @@
1
+ import { access } from 'node:fs/promises';
2
+ /**
3
+ * Checks if a file exists and can be "accessed".
4
+ * Prone to race conditions, but good enough for our use cases.
5
+ *
6
+ * @param filePath - The path to the file to check
7
+ * @returns A promise that resolves to true if the file exists, false otherwise
8
+ * @internal
9
+ */ export function fileExists(filePath) {
10
+ return access(filePath).then(()=>true, ()=>false);
11
+ }
12
+
13
+ //# sourceMappingURL=fileExists.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/migration/fileExists.ts"],"sourcesContent":["import {access} from 'node:fs/promises'\n\n/**\n * Checks if a file exists and can be \"accessed\".\n * Prone to race conditions, but good enough for our use cases.\n *\n * @param filePath - The path to the file to check\n * @returns A promise that resolves to true if the file exists, false otherwise\n * @internal\n */\nexport function fileExists(filePath: string): Promise<boolean> {\n return access(filePath).then(\n () => true,\n () => false,\n )\n}\n"],"names":["access","fileExists","filePath","then"],"mappings":"AAAA,SAAQA,MAAM,QAAO,mBAAkB;AAEvC;;;;;;;CAOC,GACD,OAAO,SAASC,WAAWC,QAAgB;IACzC,OAAOF,OAAOE,UAAUC,IAAI,CAC1B,IAAM,MACN,IAAM;AAEV"}
@@ -0,0 +1,155 @@
1
+ import { isatty } from 'node:tty';
2
+ import { styleText } from 'node:util';
3
+ import { convertToTree, formatTree, maxKeyLength } from './tree.js';
4
+ const isTty = isatty(1);
5
+ export function prettyFormat({ indentSize = 0, migration, subject }) {
6
+ return (Array.isArray(subject) ? subject : [
7
+ subject
8
+ ]).map((subjectEntry)=>{
9
+ if (subjectEntry.type === 'transaction') {
10
+ return [
11
+ [
12
+ badge('transaction', 'info'),
13
+ subjectEntry.id === undefined ? null : styleText('underline', subjectEntry.id)
14
+ ].filter(Boolean).join(' '),
15
+ indent(prettyFormat({
16
+ indentSize: indentSize,
17
+ migration,
18
+ subject: subjectEntry.mutations
19
+ }))
20
+ ].join('\n\n');
21
+ }
22
+ return prettyFormatMutation({
23
+ indentSize,
24
+ migration,
25
+ subject: subjectEntry
26
+ });
27
+ }).join('\n\n');
28
+ }
29
+ function encodeItemRef(ref) {
30
+ return typeof ref === 'number' ? ref : ref._key;
31
+ }
32
+ function badgeStyle(variant, label) {
33
+ const styles = {
34
+ destructive: styleText([
35
+ 'bgRed',
36
+ 'black',
37
+ 'bold'
38
+ ], label),
39
+ incremental: styleText([
40
+ 'bgGreen',
41
+ 'black',
42
+ 'bold'
43
+ ], label),
44
+ info: styleText([
45
+ 'bgWhite',
46
+ 'black'
47
+ ], label),
48
+ maybeDestructive: styleText([
49
+ 'bgYellow',
50
+ 'black',
51
+ 'bold'
52
+ ], label)
53
+ };
54
+ return styles[variant];
55
+ }
56
+ function badge(label, variant) {
57
+ if (!isTty) {
58
+ return `[${label}]`;
59
+ }
60
+ return badgeStyle(variant, ` ${label} `);
61
+ }
62
+ const mutationImpact = {
63
+ create: 'incremental',
64
+ createIfNotExists: 'incremental',
65
+ createOrReplace: 'maybeDestructive',
66
+ delete: 'destructive',
67
+ patch: 'maybeDestructive'
68
+ };
69
+ function documentId(mutation) {
70
+ if ('id' in mutation) {
71
+ return mutation.id;
72
+ }
73
+ if ('document' in mutation) {
74
+ return mutation.document._id;
75
+ }
76
+ return undefined;
77
+ }
78
+ const listFormatter = new Intl.ListFormat('en-US', {
79
+ type: 'disjunction'
80
+ });
81
+ function mutationHeader(mutation, migration) {
82
+ const mutationType = badge(mutation.type, mutationImpact[mutation.type]);
83
+ const documentType = 'document' in mutation || migration.documentTypes ? badge('document' in mutation ? mutation.document._type : listFormatter.format(migration.documentTypes ?? []), 'info') : null;
84
+ // TODO: Should we list documentType when a mutation can be yielded for any document type?
85
+ return [
86
+ mutationType,
87
+ documentType,
88
+ styleText('underline', documentId(mutation) ?? '')
89
+ ].filter(Boolean).join(' ');
90
+ }
91
+ function prettyFormatMutation({ indentSize = 0, migration, subject }) {
92
+ const lock = 'options' in subject ? styleText('cyan', `(if revision==${subject.options?.ifRevision})`) : '';
93
+ const header = [
94
+ mutationHeader(subject, migration),
95
+ lock
96
+ ].join(' ');
97
+ const padding = ' '.repeat(indentSize);
98
+ if (subject.type === 'create' || subject.type === 'createIfNotExists' || subject.type === 'createOrReplace') {
99
+ return [
100
+ header,
101
+ '\n',
102
+ indent(JSON.stringify(subject.document, null, 2), indentSize)
103
+ ].join('');
104
+ }
105
+ if (subject.type === 'patch') {
106
+ const tree = convertToTree(subject.patches.flat());
107
+ const paddingLength = Math.max(maxKeyLength(tree.children) + 2, 30);
108
+ return [
109
+ header,
110
+ '\n',
111
+ formatTree({
112
+ getMessage: (patch)=>formatPatchMutation(patch),
113
+ indent: padding,
114
+ node: tree.children,
115
+ paddingLength
116
+ })
117
+ ].join('');
118
+ }
119
+ return header;
120
+ }
121
+ function formatPatchMutation(patch) {
122
+ const { op } = patch;
123
+ const formattedType = styleText('bold', op.type);
124
+ if (op.type === 'unset') {
125
+ return `${styleText('red', formattedType)}()`;
126
+ }
127
+ if (op.type === 'diffMatchPatch') {
128
+ return `${styleText('yellow', formattedType)}(${op.value})`;
129
+ }
130
+ if (op.type === 'inc' || op.type === 'dec') {
131
+ return `${styleText('yellow', formattedType)}(${op.amount})`;
132
+ }
133
+ if (op.type === 'set') {
134
+ return `${styleText('yellow', formattedType)}(${JSON.stringify(op.value)})`;
135
+ }
136
+ if (op.type === 'setIfMissing') {
137
+ return `${styleText('green', formattedType)}(${JSON.stringify(op.value)})`;
138
+ }
139
+ if (op.type === 'insert') {
140
+ return `${styleText('green', formattedType)}(${op.position}, ${encodeItemRef(op.referenceItem)}, ${JSON.stringify(op.items)})`;
141
+ }
142
+ if (op.type === 'replace') {
143
+ return `${styleText('yellow', formattedType)}(${encodeItemRef(op.referenceItem)}, ${JSON.stringify(op.items)})`;
144
+ }
145
+ if (op.type === 'truncate') {
146
+ return `${styleText('red', formattedType)}(${op.startIndex}, ${op.endIndex})`;
147
+ }
148
+ throw new Error(`Invalid operation type: ${op.type}`);
149
+ }
150
+ function indent(subject, size = 2) {
151
+ const padding = ' '.repeat(size);
152
+ return subject.split('\n').map((line)=>padding + line).join('\n');
153
+ }
154
+
155
+ //# sourceMappingURL=prettyMutationFormatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/migration/prettyMutationFormatter.ts"],"sourcesContent":["import {isatty} from 'node:tty'\nimport {styleText} from 'node:util'\n\nimport {type Migration, type Mutation, type NodePatch, type Transaction} from '@sanity/migrate'\nimport {type KeyedSegment} from '@sanity/types'\n\nimport {convertToTree, formatTree, maxKeyLength} from './tree.js'\n\ntype ItemRef = number | string\ntype Impact = 'destructive' | 'incremental' | 'maybeDestructive'\ntype Variant = 'info' | Impact\n\nconst isTty = isatty(1)\n\ninterface FormatterOptions<Subject> {\n migration: Migration\n subject: Subject\n\n indentSize?: number\n}\n\nexport function prettyFormat({\n indentSize = 0,\n migration,\n subject,\n}: FormatterOptions<(Mutation | Transaction)[] | Mutation | Transaction>): string {\n return (Array.isArray(subject) ? subject : [subject])\n .map((subjectEntry) => {\n if (subjectEntry.type === 'transaction') {\n return [\n [\n badge('transaction', 'info'),\n subjectEntry.id === undefined ? null : styleText('underline', subjectEntry.id),\n ]\n .filter(Boolean)\n .join(' '),\n indent(\n prettyFormat({\n indentSize: indentSize,\n migration,\n subject: subjectEntry.mutations,\n }),\n ),\n ].join('\\n\\n')\n }\n return prettyFormatMutation({\n indentSize,\n migration,\n subject: subjectEntry,\n })\n })\n .join('\\n\\n')\n}\n\nfunction encodeItemRef(ref: KeyedSegment | number): ItemRef {\n return typeof ref === 'number' ? ref : ref._key\n}\n\nfunction badgeStyle(variant: Variant, label: string) {\n const styles: Record<Variant, string> = {\n destructive: styleText(['bgRed', 'black', 'bold'], label),\n incremental: styleText(['bgGreen', 'black', 'bold'], label),\n info: styleText(['bgWhite', 'black'], label),\n maybeDestructive: styleText(['bgYellow', 'black', 'bold'], label),\n }\n\n return styles[variant]\n}\n\nfunction badge(label: string, variant: Variant): string {\n if (!isTty) {\n return `[${label}]`\n }\n\n return badgeStyle(variant, ` ${label} `)\n}\n\nconst mutationImpact: Record<Mutation['type'], Impact> = {\n create: 'incremental',\n createIfNotExists: 'incremental',\n createOrReplace: 'maybeDestructive',\n delete: 'destructive',\n patch: 'maybeDestructive',\n}\n\nfunction documentId(mutation: Mutation): string | undefined {\n if ('id' in mutation) {\n return mutation.id\n }\n\n if ('document' in mutation) {\n return mutation.document._id\n }\n\n return undefined\n}\n\nconst listFormatter = new Intl.ListFormat('en-US', {\n type: 'disjunction',\n})\n\nfunction mutationHeader(mutation: Mutation, migration: Migration): string {\n const mutationType = badge(mutation.type, mutationImpact[mutation.type])\n\n const documentType =\n 'document' in mutation || migration.documentTypes\n ? badge(\n 'document' in mutation\n ? mutation.document._type\n : listFormatter.format(migration.documentTypes ?? []),\n 'info',\n )\n : null\n\n // TODO: Should we list documentType when a mutation can be yielded for any document type?\n return [mutationType, documentType, styleText('underline', documentId(mutation) ?? '')]\n .filter(Boolean)\n .join(' ')\n}\n\nfunction prettyFormatMutation({\n indentSize = 0,\n migration,\n subject,\n}: FormatterOptions<Mutation>): string {\n const lock =\n 'options' in subject ? styleText('cyan', `(if revision==${subject.options?.ifRevision})`) : ''\n const header = [mutationHeader(subject, migration), lock].join(' ')\n const padding = ' '.repeat(indentSize)\n\n if (\n subject.type === 'create' ||\n subject.type === 'createIfNotExists' ||\n subject.type === 'createOrReplace'\n ) {\n return [header, '\\n', indent(JSON.stringify(subject.document, null, 2), indentSize)].join('')\n }\n\n if (subject.type === 'patch') {\n const tree = convertToTree<NodePatch>(subject.patches.flat())\n const paddingLength = Math.max(maxKeyLength(tree.children) + 2, 30)\n\n return [\n header,\n '\\n',\n formatTree<NodePatch>({\n getMessage: (patch: NodePatch) => formatPatchMutation(patch),\n indent: padding,\n node: tree.children,\n paddingLength,\n }),\n ].join('')\n }\n\n return header\n}\n\nfunction formatPatchMutation(patch: NodePatch): string {\n const {op} = patch\n const formattedType = styleText('bold', op.type)\n if (op.type === 'unset') {\n return `${styleText('red', formattedType)}()`\n }\n if (op.type === 'diffMatchPatch') {\n return `${styleText('yellow', formattedType)}(${op.value})`\n }\n if (op.type === 'inc' || op.type === 'dec') {\n return `${styleText('yellow', formattedType)}(${op.amount})`\n }\n if (op.type === 'set') {\n return `${styleText('yellow', formattedType)}(${JSON.stringify(op.value)})`\n }\n if (op.type === 'setIfMissing') {\n return `${styleText('green', formattedType)}(${JSON.stringify(op.value)})`\n }\n if (op.type === 'insert') {\n return `${styleText('green', formattedType)}(${op.position}, ${encodeItemRef(\n op.referenceItem,\n )}, ${JSON.stringify(op.items)})`\n }\n if (op.type === 'replace') {\n return `${styleText('yellow', formattedType)}(${encodeItemRef(op.referenceItem)}, ${JSON.stringify(\n op.items,\n )})`\n }\n if (op.type === 'truncate') {\n return `${styleText('red', formattedType)}(${op.startIndex}, ${op.endIndex})`\n }\n\n throw new Error(`Invalid operation type: ${(op as {type: string}).type}`)\n}\n\nfunction indent(subject: string, size = 2): string {\n const padding = ' '.repeat(size)\n\n return subject\n .split('\\n')\n .map((line) => padding + line)\n .join('\\n')\n}\n"],"names":["isatty","styleText","convertToTree","formatTree","maxKeyLength","isTty","prettyFormat","indentSize","migration","subject","Array","isArray","map","subjectEntry","type","badge","id","undefined","filter","Boolean","join","indent","mutations","prettyFormatMutation","encodeItemRef","ref","_key","badgeStyle","variant","label","styles","destructive","incremental","info","maybeDestructive","mutationImpact","create","createIfNotExists","createOrReplace","delete","patch","documentId","mutation","document","_id","listFormatter","Intl","ListFormat","mutationHeader","mutationType","documentType","documentTypes","_type","format","lock","options","ifRevision","header","padding","repeat","JSON","stringify","tree","patches","flat","paddingLength","Math","max","children","getMessage","formatPatchMutation","node","op","formattedType","value","amount","position","referenceItem","items","startIndex","endIndex","Error","size","split","line"],"mappings":"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"}