@sanity/cli 7.9.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 (188) hide show
  1. package/README.md +163 -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/getProvider.js +7 -2
  5. package/dist/actions/auth/login/getProvider.js.map +1 -1
  6. package/dist/actions/auth/login/loginInstructions.js +19 -0
  7. package/dist/actions/auth/login/loginInstructions.js.map +1 -0
  8. package/dist/actions/dataset/create.js +2 -2
  9. package/dist/actions/dataset/create.js.map +1 -1
  10. package/dist/actions/dataset/resolveDataset.js +7 -1
  11. package/dist/actions/dataset/resolveDataset.js.map +1 -1
  12. package/dist/actions/deploy/createUserApplication.js +8 -5
  13. package/dist/actions/deploy/createUserApplication.js.map +1 -1
  14. package/dist/actions/deploy/deployApp.js +47 -24
  15. package/dist/actions/deploy/deployApp.js.map +1 -1
  16. package/dist/actions/deploy/deployStudio.js +2 -0
  17. package/dist/actions/deploy/deployStudio.js.map +1 -1
  18. package/dist/actions/graphql/resolveApiGeneration.js +9 -6
  19. package/dist/actions/graphql/resolveApiGeneration.js.map +1 -1
  20. package/dist/actions/init/initAction.js +23 -15
  21. package/dist/actions/init/initAction.js.map +1 -1
  22. package/dist/actions/manifest/extractCoreAppManifest.js +11 -4
  23. package/dist/actions/manifest/extractCoreAppManifest.js.map +1 -1
  24. package/dist/actions/migration/constants.js +10 -0
  25. package/dist/actions/migration/constants.js.map +1 -0
  26. package/dist/actions/migration/ensureApiVersionFormat.js +16 -0
  27. package/dist/actions/migration/ensureApiVersionFormat.js.map +1 -0
  28. package/dist/actions/migration/fileExists.js +13 -0
  29. package/dist/actions/migration/fileExists.js.map +1 -0
  30. package/dist/actions/migration/prettyMutationFormatter.js +155 -0
  31. package/dist/actions/migration/prettyMutationFormatter.js.map +1 -0
  32. package/dist/actions/migration/resolveMigrationScript.js +75 -0
  33. package/dist/actions/migration/resolveMigrationScript.js.map +1 -0
  34. package/dist/actions/migration/resolveMigrations.js +50 -0
  35. package/dist/actions/migration/resolveMigrations.js.map +1 -0
  36. package/dist/actions/migration/templates/index.js +7 -0
  37. package/dist/actions/migration/templates/index.js.map +1 -0
  38. package/dist/actions/migration/templates/minimalAdvanced.js +21 -0
  39. package/dist/actions/migration/templates/minimalAdvanced.js.map +1 -0
  40. package/dist/actions/migration/templates/minimalSimple.js +61 -0
  41. package/dist/actions/migration/templates/minimalSimple.js.map +1 -0
  42. package/dist/actions/migration/templates/renameField.js +20 -0
  43. package/dist/actions/migration/templates/renameField.js.map +1 -0
  44. package/dist/actions/migration/templates/renameType.js +19 -0
  45. package/dist/actions/migration/templates/renameType.js.map +1 -0
  46. package/dist/actions/migration/templates/stringToPTE.js +32 -0
  47. package/dist/actions/migration/templates/stringToPTE.js.map +1 -0
  48. package/dist/actions/migration/tree.js +111 -0
  49. package/dist/actions/migration/tree.js.map +1 -0
  50. package/dist/actions/organizations/getOrganization.js +7 -3
  51. package/dist/actions/organizations/getOrganization.js.map +1 -1
  52. package/dist/actions/organizations/validateOrganizationSlug.js +20 -0
  53. package/dist/actions/organizations/validateOrganizationSlug.js.map +1 -0
  54. package/dist/actions/preview/previewAction.js +33 -1
  55. package/dist/actions/preview/previewAction.js.map +1 -1
  56. package/dist/actions/schema/listSchemas.js +6 -4
  57. package/dist/actions/schema/listSchemas.js.map +1 -1
  58. package/dist/actions/tokens/validateRole.js +2 -1
  59. package/dist/actions/tokens/validateRole.js.map +1 -1
  60. package/dist/actions/undeploy/runUndeploy.js +13 -4
  61. package/dist/actions/undeploy/runUndeploy.js.map +1 -1
  62. package/dist/commands/backups/disable.js +5 -1
  63. package/dist/commands/backups/disable.js.map +1 -1
  64. package/dist/commands/backups/download.js +30 -6
  65. package/dist/commands/backups/download.js.map +1 -1
  66. package/dist/commands/backups/enable.js +5 -1
  67. package/dist/commands/backups/enable.js.map +1 -1
  68. package/dist/commands/backups/list.js +5 -1
  69. package/dist/commands/backups/list.js.map +1 -1
  70. package/dist/commands/cors/add.js +15 -7
  71. package/dist/commands/cors/add.js.map +1 -1
  72. package/dist/commands/cors/delete.js +6 -1
  73. package/dist/commands/cors/delete.js.map +1 -1
  74. package/dist/commands/datasets/alias/create.js +25 -18
  75. package/dist/commands/datasets/alias/create.js.map +1 -1
  76. package/dist/commands/datasets/alias/delete.js +15 -8
  77. package/dist/commands/datasets/alias/delete.js.map +1 -1
  78. package/dist/commands/datasets/alias/link.js +39 -17
  79. package/dist/commands/datasets/alias/link.js.map +1 -1
  80. package/dist/commands/datasets/alias/unlink.js +23 -2
  81. package/dist/commands/datasets/alias/unlink.js.map +1 -1
  82. package/dist/commands/datasets/copy.js +18 -2
  83. package/dist/commands/datasets/copy.js.map +1 -1
  84. package/dist/commands/datasets/create.js +8 -2
  85. package/dist/commands/datasets/create.js.map +1 -1
  86. package/dist/commands/datasets/delete.js +14 -17
  87. package/dist/commands/datasets/delete.js.map +1 -1
  88. package/dist/commands/datasets/embeddings/disable.js +3 -0
  89. package/dist/commands/datasets/embeddings/disable.js.map +1 -1
  90. package/dist/commands/datasets/embeddings/enable.js +5 -2
  91. package/dist/commands/datasets/embeddings/enable.js.map +1 -1
  92. package/dist/commands/datasets/embeddings/status.js +3 -0
  93. package/dist/commands/datasets/embeddings/status.js.map +1 -1
  94. package/dist/commands/datasets/export.js +27 -15
  95. package/dist/commands/datasets/export.js.map +1 -1
  96. package/dist/commands/datasets/import.js +3 -3
  97. package/dist/commands/datasets/import.js.map +1 -1
  98. package/dist/commands/datasets/visibility/get.js +2 -2
  99. package/dist/commands/datasets/visibility/get.js.map +1 -1
  100. package/dist/commands/datasets/visibility/set.js +2 -2
  101. package/dist/commands/datasets/visibility/set.js.map +1 -1
  102. package/dist/commands/docs/read.js +3 -3
  103. package/dist/commands/docs/read.js.map +1 -1
  104. package/dist/commands/documents/create.js +18 -13
  105. package/dist/commands/documents/create.js.map +1 -1
  106. package/dist/commands/documents/delete.js +3 -3
  107. package/dist/commands/documents/delete.js.map +1 -1
  108. package/dist/commands/documents/get.js +3 -3
  109. package/dist/commands/documents/get.js.map +1 -1
  110. package/dist/commands/documents/query.js +3 -3
  111. package/dist/commands/documents/query.js.map +1 -1
  112. package/dist/commands/documents/validate.js +29 -26
  113. package/dist/commands/documents/validate.js.map +1 -1
  114. package/dist/commands/graphql/deploy.js +20 -16
  115. package/dist/commands/graphql/deploy.js.map +1 -1
  116. package/dist/commands/graphql/undeploy.js +15 -18
  117. package/dist/commands/graphql/undeploy.js.map +1 -1
  118. package/dist/commands/hooks/create.js +4 -0
  119. package/dist/commands/hooks/create.js.map +1 -1
  120. package/dist/commands/hooks/delete.js +6 -1
  121. package/dist/commands/hooks/delete.js.map +1 -1
  122. package/dist/commands/hooks/logs.js +6 -1
  123. package/dist/commands/hooks/logs.js.map +1 -1
  124. package/dist/commands/install.js +1 -1
  125. package/dist/commands/install.js.map +1 -1
  126. package/dist/commands/login.js +2 -1
  127. package/dist/commands/login.js.map +1 -1
  128. package/dist/commands/media/create-aspect.js +20 -8
  129. package/dist/commands/media/create-aspect.js.map +1 -1
  130. package/dist/commands/media/delete-aspect.js +31 -16
  131. package/dist/commands/media/delete-aspect.js.map +1 -1
  132. package/dist/commands/media/deploy-aspect.js +10 -5
  133. package/dist/commands/media/deploy-aspect.js.map +1 -1
  134. package/dist/commands/media/export.js +9 -4
  135. package/dist/commands/media/export.js.map +1 -1
  136. package/dist/commands/media/import.js +6 -1
  137. package/dist/commands/media/import.js.map +1 -1
  138. package/dist/commands/migrations/create.js +150 -0
  139. package/dist/commands/migrations/create.js.map +1 -0
  140. package/dist/commands/migrations/list.js +64 -0
  141. package/dist/commands/migrations/list.js.map +1 -0
  142. package/dist/commands/migrations/run.js +306 -0
  143. package/dist/commands/migrations/run.js.map +1 -0
  144. package/dist/commands/organizations/create.js +79 -0
  145. package/dist/commands/organizations/create.js.map +1 -0
  146. package/dist/commands/organizations/delete.js +100 -0
  147. package/dist/commands/organizations/delete.js.map +1 -0
  148. package/dist/commands/organizations/get.js +48 -0
  149. package/dist/commands/organizations/get.js.map +1 -0
  150. package/dist/commands/organizations/list.js +60 -0
  151. package/dist/commands/organizations/list.js.map +1 -0
  152. package/dist/commands/organizations/update.js +115 -0
  153. package/dist/commands/organizations/update.js.map +1 -0
  154. package/dist/commands/preview.js +1 -0
  155. package/dist/commands/preview.js.map +1 -1
  156. package/dist/commands/projects/create.js +3 -1
  157. package/dist/commands/projects/create.js.map +1 -1
  158. package/dist/commands/schemas/delete.js +23 -2
  159. package/dist/commands/schemas/delete.js.map +1 -1
  160. package/dist/commands/schemas/deploy.js +6 -4
  161. package/dist/commands/schemas/deploy.js.map +1 -1
  162. package/dist/commands/schemas/extract.js +22 -1
  163. package/dist/commands/schemas/extract.js.map +1 -1
  164. package/dist/commands/schemas/list.js +8 -8
  165. package/dist/commands/schemas/list.js.map +1 -1
  166. package/dist/commands/tokens/{add.js → create.js} +25 -18
  167. package/dist/commands/tokens/create.js.map +1 -0
  168. package/dist/commands/tokens/delete.js +41 -23
  169. package/dist/commands/tokens/delete.js.map +1 -1
  170. package/dist/commands/undeploy.js +2 -7
  171. package/dist/commands/undeploy.js.map +1 -1
  172. package/dist/commands/users/invite.js +25 -3
  173. package/dist/commands/users/invite.js.map +1 -1
  174. package/dist/prompts/promptForOrganizationName.js +2 -1
  175. package/dist/prompts/promptForOrganizationName.js.map +1 -1
  176. package/dist/services/organizations.js +45 -2
  177. package/dist/services/organizations.js.map +1 -1
  178. package/dist/services/userApplications.js.map +1 -1
  179. package/dist/topicAliases.js +10 -0
  180. package/dist/topicAliases.js.map +1 -1
  181. package/dist/util/formatCliErrorMessages.js +5 -0
  182. package/dist/util/formatCliErrorMessages.js.map +1 -0
  183. package/dist/util/organizationAliases.js +12 -0
  184. package/dist/util/organizationAliases.js.map +1 -0
  185. package/oclif.config.js +6 -1
  186. package/oclif.manifest.json +464 -11
  187. package/package.json +28 -28
  188. package/dist/commands/tokens/add.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/schemas/deploy.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Flags} from '@oclif/core'\nimport {CLIError} from '@oclif/core/errors'\nimport {formatSchemaValidation, SchemaExtractionError} from '@sanity/cli-build/_internal/extract'\nimport {SanityCommand} from '@sanity/cli-core'\n\nimport {deploySchemas} from '../../actions/schema/deploySchemas.js'\nimport {schemasDeployDebug} from '../../actions/schema/utils/debug.js'\nimport {parseTag} from '../../actions/schema/utils/schemaStoreValidation.js'\n\nconst description = `\nDeploy schema documents into workspace datasets.\n\nNote: This command is experimental and subject to change.\n\nRegenerates a manifest file by default. To re-use an existing manifest, use --no-extract-manifest.\n`.trim()\n\nexport class DeploySchemaCommand extends SanityCommand<typeof DeploySchemaCommand> {\n static override description = description\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Deploy all workspace schemas',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --workspace default',\n description: 'Deploy the schema for only the workspace \"default\"',\n },\n ]\n\n static override flags = {\n 'extract-manifest': Flags.boolean({\n allowNo: true,\n default: true,\n description: 'Regenerate manifest before deploying (use --no-extract-manifest to skip)',\n }),\n 'manifest-dir': Flags.directory({\n default: './dist/static',\n description: 'Directory containing manifest file',\n helpValue: '<directory>',\n }),\n tag: Flags.string({\n description: 'Add a tag suffix to the schema id',\n helpValue: '<tag>',\n parse: parseTag,\n }),\n verbose: Flags.boolean({\n default: false,\n description: 'Print detailed information during deployment',\n }),\n workspace: Flags.string({\n description: 'The name of the workspace to deploy a schema for',\n helpValue: '<name>',\n parse: async (input) => {\n if (!input) throw new CLIError('workspace argument cannot be empty if specified', {exit: 1})\n return input\n },\n }),\n }\n\n static override hiddenAliases: string[] = ['schema:deploy']\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(DeploySchemaCommand)\n const {tag, workspace} = flags\n\n try {\n const workDir = (await this.getProjectRoot()).directory\n\n await deploySchemas({\n tag,\n verbose: flags['verbose'],\n workDir,\n workspaceName: workspace,\n })\n\n this.log(\n `${styleText('gray', '↳ List deployed schemas with:')} ${styleText('cyan', 'sanity schema list')}`,\n )\n } catch (error) {\n if (\n error instanceof SchemaExtractionError &&\n error.validation &&\n error.validation.length > 0\n ) {\n this.output.log(formatSchemaValidation(error.validation))\n this.exit(1)\n }\n\n schemasDeployDebug('Failed to deploy schemas', error)\n const errorMessage = error instanceof Error ? error.message : String(error)\n this.error(`Failed to deploy schemas:\\n${errorMessage}`, {exit: 1})\n }\n }\n}\n"],"names":["styleText","Flags","CLIError","formatSchemaValidation","SchemaExtractionError","SanityCommand","deploySchemas","schemasDeployDebug","parseTag","description","trim","DeploySchemaCommand","examples","command","flags","boolean","allowNo","default","directory","helpValue","tag","string","parse","verbose","workspace","input","exit","hiddenAliases","run","workDir","getProjectRoot","workspaceName","log","error","validation","length","output","errorMessage","Error","message","String"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SAAQC,KAAK,QAAO,cAAa;AACjC,SAAQC,QAAQ,QAAO,qBAAoB;AAC3C,SAAQC,sBAAsB,EAAEC,qBAAqB,QAAO,sCAAqC;AACjG,SAAQC,aAAa,QAAO,mBAAkB;AAE9C,SAAQC,aAAa,QAAO,wCAAuC;AACnE,SAAQC,kBAAkB,QAAO,sCAAqC;AACtE,SAAQC,QAAQ,QAAO,sDAAqD;AAE5E,MAAMC,cAAc,CAAC;;;;;;AAMrB,CAAC,CAACC,IAAI;AAEN,OAAO,MAAMC,4BAA4BN;IACvC,OAAgBI,cAAcA,YAAW;IAEzC,OAAgBG,WAAW;QACzB;YACEC,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;KACD,CAAA;IAED,OAAgBK,QAAQ;QACtB,oBAAoBb,MAAMc,OAAO,CAAC;YAChCC,SAAS;YACTC,SAAS;YACTR,aAAa;QACf;QACA,gBAAgBR,MAAMiB,SAAS,CAAC;YAC9BD,SAAS;YACTR,aAAa;YACbU,WAAW;QACb;QACAC,KAAKnB,MAAMoB,MAAM,CAAC;YAChBZ,aAAa;YACbU,WAAW;YACXG,OAAOd;QACT;QACAe,SAAStB,MAAMc,OAAO,CAAC;YACrBE,SAAS;YACTR,aAAa;QACf;QACAe,WAAWvB,MAAMoB,MAAM,CAAC;YACtBZ,aAAa;YACbU,WAAW;YACXG,OAAO,OAAOG;gBACZ,IAAI,CAACA,OAAO,MAAM,IAAIvB,SAAS,mDAAmD;oBAACwB,MAAM;gBAAC;gBAC1F,OAAOD;YACT;QACF;IACF,EAAC;IAED,OAAgBE,gBAA0B;QAAC;KAAgB,CAAA;IAE3D,MAAaC,MAAqB;QAChC,MAAM,EAACd,KAAK,EAAC,GAAG,MAAM,IAAI,CAACQ,KAAK,CAACX;QACjC,MAAM,EAACS,GAAG,EAAEI,SAAS,EAAC,GAAGV;QAEzB,IAAI;YACF,MAAMe,UAAU,AAAC,CAAA,MAAM,IAAI,CAACC,cAAc,EAAC,EAAGZ,SAAS;YAEvD,MAAMZ,cAAc;gBAClBc;gBACAG,SAAST,KAAK,CAAC,UAAU;gBACzBe;gBACAE,eAAeP;YACjB;YAEA,IAAI,CAACQ,GAAG,CACN,GAAGhC,UAAU,QAAQ,iCAAiC,CAAC,EAAEA,UAAU,QAAQ,uBAAuB;QAEtG,EAAE,OAAOiC,OAAO;YACd,IACEA,iBAAiB7B,yBACjB6B,MAAMC,UAAU,IAChBD,MAAMC,UAAU,CAACC,MAAM,GAAG,GAC1B;gBACA,IAAI,CAACC,MAAM,CAACJ,GAAG,CAAC7B,uBAAuB8B,MAAMC,UAAU;gBACvD,IAAI,CAACR,IAAI,CAAC;YACZ;YAEAnB,mBAAmB,4BAA4B0B;YAC/C,MAAMI,eAAeJ,iBAAiBK,QAAQL,MAAMM,OAAO,GAAGC,OAAOP;YACrE,IAAI,CAACA,KAAK,CAAC,CAAC,2BAA2B,EAAEI,cAAc,EAAE;gBAACX,MAAM;YAAC;QACnE;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/commands/schemas/deploy.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Flags} from '@oclif/core'\nimport {CLIError} from '@oclif/core/errors'\nimport {formatSchemaValidation, SchemaExtractionError} from '@sanity/cli-build/_internal/extract'\nimport {exitCodes, SanityCommand} from '@sanity/cli-core'\n\nimport {deploySchemas} from '../../actions/schema/deploySchemas.js'\nimport {schemasDeployDebug} from '../../actions/schema/utils/debug.js'\nimport {parseTag} from '../../actions/schema/utils/schemaStoreValidation.js'\n\nconst description = `\nDeploy schema documents into workspace datasets.\n\nNote: This command is experimental and subject to change.\n\nRegenerates a manifest file by default. To re-use an existing manifest, use --no-extract-manifest.\n`.trim()\n\nexport class DeploySchemaCommand extends SanityCommand<typeof DeploySchemaCommand> {\n static override description = description\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Deploy all workspace schemas',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --workspace default',\n description: 'Deploy the schema for only the workspace \"default\"',\n },\n ]\n\n static override flags = {\n 'extract-manifest': Flags.boolean({\n allowNo: true,\n default: true,\n description: 'Regenerate manifest before deploying (use --no-extract-manifest to skip)',\n }),\n 'manifest-dir': Flags.directory({\n default: './dist/static',\n description: 'Directory containing manifest file',\n helpValue: '<directory>',\n }),\n tag: Flags.string({\n description: 'Add a tag suffix to the schema id',\n helpValue: '<tag>',\n parse: parseTag,\n }),\n verbose: Flags.boolean({\n default: false,\n description: 'Print detailed information during deployment',\n }),\n workspace: Flags.string({\n description: 'The name of the workspace to deploy a schema for',\n helpValue: '<name>',\n parse: async (input) => {\n if (!input) {\n throw new CLIError(\n 'Workspace name cannot be empty. Pass a value with `--workspace <name>`.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n return input\n },\n }),\n }\n\n static override hiddenAliases: string[] = ['schema:deploy']\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(DeploySchemaCommand)\n const {tag, workspace} = flags\n\n try {\n const workDir = (await this.getProjectRoot()).directory\n\n await deploySchemas({\n tag,\n verbose: flags['verbose'],\n workDir,\n workspaceName: workspace,\n })\n\n this.log(\n `${styleText('gray', '↳ List deployed schemas with:')} ${styleText('cyan', 'sanity schema list')}`,\n )\n } catch (error) {\n if (\n error instanceof SchemaExtractionError &&\n error.validation &&\n error.validation.length > 0\n ) {\n this.output.log(formatSchemaValidation(error.validation))\n this.exit(1)\n }\n\n schemasDeployDebug('Failed to deploy schemas', error)\n const errorMessage = error instanceof Error ? error.message : String(error)\n this.error(`Failed to deploy schemas:\\n${errorMessage}`, {exit: 1})\n }\n }\n}\n"],"names":["styleText","Flags","CLIError","formatSchemaValidation","SchemaExtractionError","exitCodes","SanityCommand","deploySchemas","schemasDeployDebug","parseTag","description","trim","DeploySchemaCommand","examples","command","flags","boolean","allowNo","default","directory","helpValue","tag","string","parse","verbose","workspace","input","exit","USAGE_ERROR","hiddenAliases","run","workDir","getProjectRoot","workspaceName","log","error","validation","length","output","errorMessage","Error","message","String"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SAAQC,KAAK,QAAO,cAAa;AACjC,SAAQC,QAAQ,QAAO,qBAAoB;AAC3C,SAAQC,sBAAsB,EAAEC,qBAAqB,QAAO,sCAAqC;AACjG,SAAQC,SAAS,EAAEC,aAAa,QAAO,mBAAkB;AAEzD,SAAQC,aAAa,QAAO,wCAAuC;AACnE,SAAQC,kBAAkB,QAAO,sCAAqC;AACtE,SAAQC,QAAQ,QAAO,sDAAqD;AAE5E,MAAMC,cAAc,CAAC;;;;;;AAMrB,CAAC,CAACC,IAAI;AAEN,OAAO,MAAMC,4BAA4BN;IACvC,OAAgBI,cAAcA,YAAW;IAEzC,OAAgBG,WAAW;QACzB;YACEC,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;KACD,CAAA;IAED,OAAgBK,QAAQ;QACtB,oBAAoBd,MAAMe,OAAO,CAAC;YAChCC,SAAS;YACTC,SAAS;YACTR,aAAa;QACf;QACA,gBAAgBT,MAAMkB,SAAS,CAAC;YAC9BD,SAAS;YACTR,aAAa;YACbU,WAAW;QACb;QACAC,KAAKpB,MAAMqB,MAAM,CAAC;YAChBZ,aAAa;YACbU,WAAW;YACXG,OAAOd;QACT;QACAe,SAASvB,MAAMe,OAAO,CAAC;YACrBE,SAAS;YACTR,aAAa;QACf;QACAe,WAAWxB,MAAMqB,MAAM,CAAC;YACtBZ,aAAa;YACbU,WAAW;YACXG,OAAO,OAAOG;gBACZ,IAAI,CAACA,OAAO;oBACV,MAAM,IAAIxB,SACR,2EACA;wBAACyB,MAAMtB,UAAUuB,WAAW;oBAAA;gBAEhC;gBACA,OAAOF;YACT;QACF;IACF,EAAC;IAED,OAAgBG,gBAA0B;QAAC;KAAgB,CAAA;IAE3D,MAAaC,MAAqB;QAChC,MAAM,EAACf,KAAK,EAAC,GAAG,MAAM,IAAI,CAACQ,KAAK,CAACX;QACjC,MAAM,EAACS,GAAG,EAAEI,SAAS,EAAC,GAAGV;QAEzB,IAAI;YACF,MAAMgB,UAAU,AAAC,CAAA,MAAM,IAAI,CAACC,cAAc,EAAC,EAAGb,SAAS;YAEvD,MAAMZ,cAAc;gBAClBc;gBACAG,SAAST,KAAK,CAAC,UAAU;gBACzBgB;gBACAE,eAAeR;YACjB;YAEA,IAAI,CAACS,GAAG,CACN,GAAGlC,UAAU,QAAQ,iCAAiC,CAAC,EAAEA,UAAU,QAAQ,uBAAuB;QAEtG,EAAE,OAAOmC,OAAO;YACd,IACEA,iBAAiB/B,yBACjB+B,MAAMC,UAAU,IAChBD,MAAMC,UAAU,CAACC,MAAM,GAAG,GAC1B;gBACA,IAAI,CAACC,MAAM,CAACJ,GAAG,CAAC/B,uBAAuBgC,MAAMC,UAAU;gBACvD,IAAI,CAACT,IAAI,CAAC;YACZ;YAEAnB,mBAAmB,4BAA4B2B;YAC/C,MAAMI,eAAeJ,iBAAiBK,QAAQL,MAAMM,OAAO,GAAGC,OAAOP;YACrE,IAAI,CAACA,KAAK,CAAC,CAAC,2BAA2B,EAAEI,cAAc,EAAE;gBAACZ,MAAM;YAAC;QACnE;IACF;AACF"}
@@ -1,5 +1,7 @@
1
+ import { access } from 'node:fs/promises';
1
2
  import { Flags } from '@oclif/core';
2
- import { SanityCommand } from '@sanity/cli-core';
3
+ import { exitCodes, SanityCommand } from '@sanity/cli-core';
4
+ import { confirm } from '@sanity/cli-core/ux';
3
5
  import { extractSchema } from '../../actions/schema/extractSchema.js';
4
6
  import { getExtractOptions } from '../../actions/schema/getExtractOptions.js';
5
7
  import { watchExtractSchema } from '../../actions/schema/watchExtractSchema.js';
@@ -28,6 +30,9 @@ export class ExtractSchemaCommand extends SanityCommand {
28
30
  'enforce-required-fields': Flags.boolean({
29
31
  description: 'Makes the schema generated treat fields marked as required as non-optional'
30
32
  }),
33
+ force: Flags.boolean({
34
+ description: 'Overwrite an existing schema file'
35
+ }),
31
36
  format: Flags.string({
32
37
  default: 'groq-type-nodes',
33
38
  description: 'Output format (currently only groq-type-nodes)',
@@ -61,6 +66,22 @@ export class ExtractSchemaCommand extends SanityCommand {
61
66
  projectRoot,
62
67
  schemaExtraction
63
68
  });
69
+ const outputExists = await access(extractOptions.outputPath).then(()=>true, ()=>false);
70
+ if (outputExists && !flags.force) {
71
+ if (this.isUnattended()) {
72
+ this.error(`Schema file already exists at "${extractOptions.outputPath}". Pass \`--force\` to overwrite it.`, {
73
+ exit: exitCodes.USAGE_ERROR
74
+ });
75
+ }
76
+ const shouldOverwrite = await confirm({
77
+ default: false,
78
+ message: `Schema file already exists at "${extractOptions.outputPath}". Overwrite it?`
79
+ });
80
+ if (!shouldOverwrite) {
81
+ this.output.log('Schema extraction cancelled');
82
+ return this.exit(exitCodes.USER_ABORT);
83
+ }
84
+ }
64
85
  if (flags.watch) {
65
86
  return watchExtractSchema({
66
87
  extractOptions,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/schemas/extract.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\nimport {SanityCommand} from '@sanity/cli-core'\n\nimport {extractSchema} from '../../actions/schema/extractSchema.js'\nimport {getExtractOptions} from '../../actions/schema/getExtractOptions.js'\nimport {watchExtractSchema} from '../../actions/schema/watchExtractSchema.js'\n\nconst description = `\nExtract a JSON representation of a Sanity schema within a Studio context.\n\nNote: This command is experimental and subject to change.\n`.trim()\n\nexport class ExtractSchemaCommand extends SanityCommand<typeof ExtractSchemaCommand> {\n static override description = description\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> --workspace default',\n description: 'Extracts schema types in a Sanity project with more than one workspace',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --watch',\n description: 'Watch mode - re-extract on changes',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --watch --watch-patterns \"lib/**/*.ts\"',\n description: 'Watch with custom glob patterns',\n },\n ]\n\n static override flags = {\n 'enforce-required-fields': Flags.boolean({\n description: 'Makes the schema generated treat fields marked as required as non-optional',\n }),\n format: Flags.string({\n default: 'groq-type-nodes',\n description: 'Output format (currently only groq-type-nodes)',\n helpValue: '<format>',\n }),\n path: Flags.string({\n description: 'Optional path to specify destination of the schema file',\n }),\n watch: Flags.boolean({\n description: 'Enable watch mode to re-extract schema on file changes',\n }),\n 'watch-patterns': Flags.string({\n description: 'Additional glob pattern(s) to watch (can be specified multiple times)',\n helpValue: '<glob>',\n multiple: true,\n }),\n workspace: Flags.string({\n description: 'The name of the workspace to generate a schema for',\n helpValue: '<name>',\n }),\n }\n\n static override hiddenAliases: string[] = ['schema:extract']\n\n public async run(): Promise<{close?: () => Promise<void>} | void> {\n const {flags} = await this.parse(ExtractSchemaCommand)\n const projectRoot = await this.getProjectRoot()\n\n const {schemaExtraction} = await this.getCliConfig()\n const extractOptions = getExtractOptions({\n flags,\n projectRoot,\n schemaExtraction,\n })\n\n if (flags.watch) {\n return watchExtractSchema({\n extractOptions,\n output: this.output,\n })\n }\n\n return extractSchema({\n extractOptions,\n output: this.output,\n })\n }\n}\n"],"names":["Flags","SanityCommand","extractSchema","getExtractOptions","watchExtractSchema","description","trim","ExtractSchemaCommand","examples","command","flags","boolean","format","string","default","helpValue","path","watch","multiple","workspace","hiddenAliases","run","parse","projectRoot","getProjectRoot","schemaExtraction","getCliConfig","extractOptions","output"],"mappings":"AAAA,SAAQA,KAAK,QAAO,cAAa;AACjC,SAAQC,aAAa,QAAO,mBAAkB;AAE9C,SAAQC,aAAa,QAAO,wCAAuC;AACnE,SAAQC,iBAAiB,QAAO,4CAA2C;AAC3E,SAAQC,kBAAkB,QAAO,6CAA4C;AAE7E,MAAMC,cAAc,CAAC;;;;AAIrB,CAAC,CAACC,IAAI;AAEN,OAAO,MAAMC,6BAA6BN;IACxC,OAAgBI,cAAcA,YAAW;IAEzC,OAAgBG,WAAW;QACzB;YACEC,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;KACD,CAAA;IAED,OAAgBK,QAAQ;QACtB,2BAA2BV,MAAMW,OAAO,CAAC;YACvCN,aAAa;QACf;QACAO,QAAQZ,MAAMa,MAAM,CAAC;YACnBC,SAAS;YACTT,aAAa;YACbU,WAAW;QACb;QACAC,MAAMhB,MAAMa,MAAM,CAAC;YACjBR,aAAa;QACf;QACAY,OAAOjB,MAAMW,OAAO,CAAC;YACnBN,aAAa;QACf;QACA,kBAAkBL,MAAMa,MAAM,CAAC;YAC7BR,aAAa;YACbU,WAAW;YACXG,UAAU;QACZ;QACAC,WAAWnB,MAAMa,MAAM,CAAC;YACtBR,aAAa;YACbU,WAAW;QACb;IACF,EAAC;IAED,OAAgBK,gBAA0B;QAAC;KAAiB,CAAA;IAE5D,MAAaC,MAAqD;QAChE,MAAM,EAACX,KAAK,EAAC,GAAG,MAAM,IAAI,CAACY,KAAK,CAACf;QACjC,MAAMgB,cAAc,MAAM,IAAI,CAACC,cAAc;QAE7C,MAAM,EAACC,gBAAgB,EAAC,GAAG,MAAM,IAAI,CAACC,YAAY;QAClD,MAAMC,iBAAiBxB,kBAAkB;YACvCO;YACAa;YACAE;QACF;QAEA,IAAIf,MAAMO,KAAK,EAAE;YACf,OAAOb,mBAAmB;gBACxBuB;gBACAC,QAAQ,IAAI,CAACA,MAAM;YACrB;QACF;QAEA,OAAO1B,cAAc;YACnByB;YACAC,QAAQ,IAAI,CAACA,MAAM;QACrB;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/commands/schemas/extract.ts"],"sourcesContent":["import {access} from 'node:fs/promises'\n\nimport {Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand} from '@sanity/cli-core'\nimport {confirm} from '@sanity/cli-core/ux'\n\nimport {extractSchema} from '../../actions/schema/extractSchema.js'\nimport {getExtractOptions} from '../../actions/schema/getExtractOptions.js'\nimport {watchExtractSchema} from '../../actions/schema/watchExtractSchema.js'\n\nconst description = `\nExtract a JSON representation of a Sanity schema within a Studio context.\n\nNote: This command is experimental and subject to change.\n`.trim()\n\nexport class ExtractSchemaCommand extends SanityCommand<typeof ExtractSchemaCommand> {\n static override description = description\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> --workspace default',\n description: 'Extracts schema types in a Sanity project with more than one workspace',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --watch',\n description: 'Watch mode - re-extract on changes',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --watch --watch-patterns \"lib/**/*.ts\"',\n description: 'Watch with custom glob patterns',\n },\n ]\n\n static override flags = {\n 'enforce-required-fields': Flags.boolean({\n description: 'Makes the schema generated treat fields marked as required as non-optional',\n }),\n force: Flags.boolean({\n description: 'Overwrite an existing schema file',\n }),\n format: Flags.string({\n default: 'groq-type-nodes',\n description: 'Output format (currently only groq-type-nodes)',\n helpValue: '<format>',\n }),\n path: Flags.string({\n description: 'Optional path to specify destination of the schema file',\n }),\n watch: Flags.boolean({\n description: 'Enable watch mode to re-extract schema on file changes',\n }),\n 'watch-patterns': Flags.string({\n description: 'Additional glob pattern(s) to watch (can be specified multiple times)',\n helpValue: '<glob>',\n multiple: true,\n }),\n workspace: Flags.string({\n description: 'The name of the workspace to generate a schema for',\n helpValue: '<name>',\n }),\n }\n\n static override hiddenAliases: string[] = ['schema:extract']\n\n public async run(): Promise<{close?: () => Promise<void>} | void> {\n const {flags} = await this.parse(ExtractSchemaCommand)\n const projectRoot = await this.getProjectRoot()\n\n const {schemaExtraction} = await this.getCliConfig()\n const extractOptions = getExtractOptions({\n flags,\n projectRoot,\n schemaExtraction,\n })\n\n const outputExists = await access(extractOptions.outputPath).then(\n () => true,\n () => false,\n )\n if (outputExists && !flags.force) {\n if (this.isUnattended()) {\n this.error(\n `Schema file already exists at \"${extractOptions.outputPath}\". Pass \\`--force\\` to overwrite it.`,\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n const shouldOverwrite = await confirm({\n default: false,\n message: `Schema file already exists at \"${extractOptions.outputPath}\". Overwrite it?`,\n })\n\n if (!shouldOverwrite) {\n this.output.log('Schema extraction cancelled')\n return this.exit(exitCodes.USER_ABORT)\n }\n }\n\n if (flags.watch) {\n return watchExtractSchema({\n extractOptions,\n output: this.output,\n })\n }\n\n return extractSchema({\n extractOptions,\n output: this.output,\n })\n }\n}\n"],"names":["access","Flags","exitCodes","SanityCommand","confirm","extractSchema","getExtractOptions","watchExtractSchema","description","trim","ExtractSchemaCommand","examples","command","flags","boolean","force","format","string","default","helpValue","path","watch","multiple","workspace","hiddenAliases","run","parse","projectRoot","getProjectRoot","schemaExtraction","getCliConfig","extractOptions","outputExists","outputPath","then","isUnattended","error","exit","USAGE_ERROR","shouldOverwrite","message","output","log","USER_ABORT"],"mappings":"AAAA,SAAQA,MAAM,QAAO,mBAAkB;AAEvC,SAAQC,KAAK,QAAO,cAAa;AACjC,SAAQC,SAAS,EAAEC,aAAa,QAAO,mBAAkB;AACzD,SAAQC,OAAO,QAAO,sBAAqB;AAE3C,SAAQC,aAAa,QAAO,wCAAuC;AACnE,SAAQC,iBAAiB,QAAO,4CAA2C;AAC3E,SAAQC,kBAAkB,QAAO,6CAA4C;AAE7E,MAAMC,cAAc,CAAC;;;;AAIrB,CAAC,CAACC,IAAI;AAEN,OAAO,MAAMC,6BAA6BP;IACxC,OAAgBK,cAAcA,YAAW;IAEzC,OAAgBG,WAAW;QACzB;YACEC,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;KACD,CAAA;IAED,OAAgBK,QAAQ;QACtB,2BAA2BZ,MAAMa,OAAO,CAAC;YACvCN,aAAa;QACf;QACAO,OAAOd,MAAMa,OAAO,CAAC;YACnBN,aAAa;QACf;QACAQ,QAAQf,MAAMgB,MAAM,CAAC;YACnBC,SAAS;YACTV,aAAa;YACbW,WAAW;QACb;QACAC,MAAMnB,MAAMgB,MAAM,CAAC;YACjBT,aAAa;QACf;QACAa,OAAOpB,MAAMa,OAAO,CAAC;YACnBN,aAAa;QACf;QACA,kBAAkBP,MAAMgB,MAAM,CAAC;YAC7BT,aAAa;YACbW,WAAW;YACXG,UAAU;QACZ;QACAC,WAAWtB,MAAMgB,MAAM,CAAC;YACtBT,aAAa;YACbW,WAAW;QACb;IACF,EAAC;IAED,OAAgBK,gBAA0B;QAAC;KAAiB,CAAA;IAE5D,MAAaC,MAAqD;QAChE,MAAM,EAACZ,KAAK,EAAC,GAAG,MAAM,IAAI,CAACa,KAAK,CAAChB;QACjC,MAAMiB,cAAc,MAAM,IAAI,CAACC,cAAc;QAE7C,MAAM,EAACC,gBAAgB,EAAC,GAAG,MAAM,IAAI,CAACC,YAAY;QAClD,MAAMC,iBAAiBzB,kBAAkB;YACvCO;YACAc;YACAE;QACF;QAEA,MAAMG,eAAe,MAAMhC,OAAO+B,eAAeE,UAAU,EAAEC,IAAI,CAC/D,IAAM,MACN,IAAM;QAER,IAAIF,gBAAgB,CAACnB,MAAME,KAAK,EAAE;YAChC,IAAI,IAAI,CAACoB,YAAY,IAAI;gBACvB,IAAI,CAACC,KAAK,CACR,CAAC,+BAA+B,EAAEL,eAAeE,UAAU,CAAC,oCAAoC,CAAC,EACjG;oBAACI,MAAMnC,UAAUoC,WAAW;gBAAA;YAEhC;YAEA,MAAMC,kBAAkB,MAAMnC,QAAQ;gBACpCc,SAAS;gBACTsB,SAAS,CAAC,+BAA+B,EAAET,eAAeE,UAAU,CAAC,gBAAgB,CAAC;YACxF;YAEA,IAAI,CAACM,iBAAiB;gBACpB,IAAI,CAACE,MAAM,CAACC,GAAG,CAAC;gBAChB,OAAO,IAAI,CAACL,IAAI,CAACnC,UAAUyC,UAAU;YACvC;QACF;QAEA,IAAI9B,MAAMQ,KAAK,EAAE;YACf,OAAOd,mBAAmB;gBACxBwB;gBACAU,QAAQ,IAAI,CAACA,MAAM;YACrB;QACF;QAEA,OAAOpC,cAAc;YACnB0B;YACAU,QAAQ,IAAI,CAACA,MAAM;QACrB;IACF;AACF"}
@@ -1,5 +1,5 @@
1
1
  import { Flags } from '@oclif/core';
2
- import { SanityCommand } from '@sanity/cli-core';
2
+ import { exitCodes, SanityCommand } from '@sanity/cli-core';
3
3
  import { listSchemas } from '../../actions/schema/listSchemas.js';
4
4
  import { schemasListDebug } from '../../actions/schema/utils/debug.js';
5
5
  import { parseWorkspaceSchemaId } from '../../actions/schema/utils/schemaStoreValidation.js';
@@ -56,15 +56,15 @@ export class ListSchemaCommand extends SanityCommand {
56
56
  ];
57
57
  async run() {
58
58
  const { flags } = await this.parse(ListSchemaCommand);
59
+ const errors = [];
60
+ const id = parseWorkspaceSchemaId(errors, flags.id)?.schemaId;
61
+ if (errors.length > 0) {
62
+ this.error(`Invalid arguments:\n${errors.map((error)=>` - ${error}`).join('\n')}`, {
63
+ exit: exitCodes.USAGE_ERROR
64
+ });
65
+ }
59
66
  try {
60
67
  const projectRoot = await this.getProjectRoot();
61
- const errors = [];
62
- const id = parseWorkspaceSchemaId(errors, flags.id)?.schemaId;
63
- if (errors.length > 0) {
64
- this.error(`Invalid arguments:\n${errors.map((error)=>` - ${error}`).join('\n')}`, {
65
- exit: 1
66
- });
67
- }
68
68
  await listSchemas({
69
69
  configPath: projectRoot.path,
70
70
  id,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/schemas/list.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\nimport {SanityCommand} from '@sanity/cli-core'\n\nimport {listSchemas} from '../../actions/schema/listSchemas.js'\nimport {schemasListDebug} from '../../actions/schema/utils/debug.js'\nimport {parseWorkspaceSchemaId} from '../../actions/schema/utils/schemaStoreValidation.js'\n\nconst description = `\nList all schemas in the current dataset.\n\nNote: This command is experimental and subject to change.\n\nRegenerates a manifest file by default. To reuse an existing manifest, use --no-extract-manifest.\n`.trim()\n\nexport class ListSchemaCommand extends SanityCommand<typeof ListSchemaCommand> {\n static override description = description\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List all schemas found in any workspace dataset in a table',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --id _.schemas.workspaceName',\n description: 'Get a schema for a given id',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --json',\n description: 'Get stored schemas as pretty-printed json-array',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --json --id _.schemas.workspaceName',\n description: 'Get singular stored schema as pretty-printed json-object',\n },\n ]\n\n static override flags = {\n 'extract-manifest': Flags.boolean({\n allowNo: true,\n default: true,\n description: 'Regenerate manifest before listing (use --no-extract-manifest to skip)',\n hidden: true,\n }),\n id: Flags.string({\n description: 'Fetch a single schema by id',\n helpValue: '<schema_id>',\n }),\n json: Flags.boolean({\n description: 'Get schema as json',\n }),\n 'manifest-dir': Flags.directory({\n default: './dist/static',\n description: 'Directory containing manifest file',\n helpValue: '<directory>',\n hidden: true,\n }),\n }\n\n static override hiddenAliases: string[] = ['schema:list']\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(ListSchemaCommand)\n\n try {\n const projectRoot = await this.getProjectRoot()\n\n const errors: string[] = []\n const id = parseWorkspaceSchemaId(errors, flags.id)?.schemaId\n if (errors.length > 0) {\n this.error(`Invalid arguments:\\n${errors.map((error) => ` - ${error}`).join('\\n')}`, {\n exit: 1,\n })\n }\n\n await listSchemas({\n configPath: projectRoot.path,\n id,\n json: !!flags.json,\n output: this.output,\n workDir: projectRoot.directory,\n })\n } catch (error) {\n schemasListDebug('Failed to list schemas', error)\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n this.error(`Failed to list schemas:\\n${errorMessage}`, {exit: 1})\n }\n }\n}\n"],"names":["Flags","SanityCommand","listSchemas","schemasListDebug","parseWorkspaceSchemaId","description","trim","ListSchemaCommand","examples","command","flags","boolean","allowNo","default","hidden","id","string","helpValue","json","directory","hiddenAliases","run","parse","projectRoot","getProjectRoot","errors","schemaId","length","error","map","join","exit","configPath","path","output","workDir","errorMessage","Error","message","String"],"mappings":"AAAA,SAAQA,KAAK,QAAO,cAAa;AACjC,SAAQC,aAAa,QAAO,mBAAkB;AAE9C,SAAQC,WAAW,QAAO,sCAAqC;AAC/D,SAAQC,gBAAgB,QAAO,sCAAqC;AACpE,SAAQC,sBAAsB,QAAO,sDAAqD;AAE1F,MAAMC,cAAc,CAAC;;;;;;AAMrB,CAAC,CAACC,IAAI;AAEN,OAAO,MAAMC,0BAA0BN;IACrC,OAAgBI,cAAcA,YAAW;IAEzC,OAAgBG,WAAW;QACzB;YACEC,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;KACD,CAAA;IAED,OAAgBK,QAAQ;QACtB,oBAAoBV,MAAMW,OAAO,CAAC;YAChCC,SAAS;YACTC,SAAS;YACTR,aAAa;YACbS,QAAQ;QACV;QACAC,IAAIf,MAAMgB,MAAM,CAAC;YACfX,aAAa;YACbY,WAAW;QACb;QACAC,MAAMlB,MAAMW,OAAO,CAAC;YAClBN,aAAa;QACf;QACA,gBAAgBL,MAAMmB,SAAS,CAAC;YAC9BN,SAAS;YACTR,aAAa;YACbY,WAAW;YACXH,QAAQ;QACV;IACF,EAAC;IAED,OAAgBM,gBAA0B;QAAC;KAAc,CAAA;IAEzD,MAAaC,MAAqB;QAChC,MAAM,EAACX,KAAK,EAAC,GAAG,MAAM,IAAI,CAACY,KAAK,CAACf;QAEjC,IAAI;YACF,MAAMgB,cAAc,MAAM,IAAI,CAACC,cAAc;YAE7C,MAAMC,SAAmB,EAAE;YAC3B,MAAMV,KAAKX,uBAAuBqB,QAAQf,MAAMK,EAAE,GAAGW;YACrD,IAAID,OAAOE,MAAM,GAAG,GAAG;gBACrB,IAAI,CAACC,KAAK,CAAC,CAAC,oBAAoB,EAAEH,OAAOI,GAAG,CAAC,CAACD,QAAU,CAAC,IAAI,EAAEA,OAAO,EAAEE,IAAI,CAAC,OAAO,EAAE;oBACpFC,MAAM;gBACR;YACF;YAEA,MAAM7B,YAAY;gBAChB8B,YAAYT,YAAYU,IAAI;gBAC5BlB;gBACAG,MAAM,CAAC,CAACR,MAAMQ,IAAI;gBAClBgB,QAAQ,IAAI,CAACA,MAAM;gBACnBC,SAASZ,YAAYJ,SAAS;YAChC;QACF,EAAE,OAAOS,OAAO;YACdzB,iBAAiB,0BAA0ByB;YAE3C,MAAMQ,eAAeR,iBAAiBS,QAAQT,MAAMU,OAAO,GAAGC,OAAOX;YACrE,IAAI,CAACA,KAAK,CAAC,CAAC,yBAAyB,EAAEQ,cAAc,EAAE;gBAACL,MAAM;YAAC;QACjE;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/commands/schemas/list.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand} from '@sanity/cli-core'\n\nimport {listSchemas} from '../../actions/schema/listSchemas.js'\nimport {schemasListDebug} from '../../actions/schema/utils/debug.js'\nimport {parseWorkspaceSchemaId} from '../../actions/schema/utils/schemaStoreValidation.js'\n\nconst description = `\nList all schemas in the current dataset.\n\nNote: This command is experimental and subject to change.\n\nRegenerates a manifest file by default. To reuse an existing manifest, use --no-extract-manifest.\n`.trim()\n\nexport class ListSchemaCommand extends SanityCommand<typeof ListSchemaCommand> {\n static override description = description\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List all schemas found in any workspace dataset in a table',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --id _.schemas.workspaceName',\n description: 'Get a schema for a given id',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --json',\n description: 'Get stored schemas as pretty-printed json-array',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --json --id _.schemas.workspaceName',\n description: 'Get singular stored schema as pretty-printed json-object',\n },\n ]\n\n static override flags = {\n 'extract-manifest': Flags.boolean({\n allowNo: true,\n default: true,\n description: 'Regenerate manifest before listing (use --no-extract-manifest to skip)',\n hidden: true,\n }),\n id: Flags.string({\n description: 'Fetch a single schema by id',\n helpValue: '<schema_id>',\n }),\n json: Flags.boolean({\n description: 'Get schema as json',\n }),\n 'manifest-dir': Flags.directory({\n default: './dist/static',\n description: 'Directory containing manifest file',\n helpValue: '<directory>',\n hidden: true,\n }),\n }\n\n static override hiddenAliases: string[] = ['schema:list']\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(ListSchemaCommand)\n const errors: string[] = []\n const id = parseWorkspaceSchemaId(errors, flags.id)?.schemaId\n if (errors.length > 0) {\n this.error(`Invalid arguments:\\n${errors.map((error) => ` - ${error}`).join('\\n')}`, {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n try {\n const projectRoot = await this.getProjectRoot()\n\n await listSchemas({\n configPath: projectRoot.path,\n id,\n json: !!flags.json,\n output: this.output,\n workDir: projectRoot.directory,\n })\n } catch (error) {\n schemasListDebug('Failed to list schemas', error)\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n this.error(`Failed to list schemas:\\n${errorMessage}`, {exit: 1})\n }\n }\n}\n"],"names":["Flags","exitCodes","SanityCommand","listSchemas","schemasListDebug","parseWorkspaceSchemaId","description","trim","ListSchemaCommand","examples","command","flags","boolean","allowNo","default","hidden","id","string","helpValue","json","directory","hiddenAliases","run","parse","errors","schemaId","length","error","map","join","exit","USAGE_ERROR","projectRoot","getProjectRoot","configPath","path","output","workDir","errorMessage","Error","message","String"],"mappings":"AAAA,SAAQA,KAAK,QAAO,cAAa;AACjC,SAAQC,SAAS,EAAEC,aAAa,QAAO,mBAAkB;AAEzD,SAAQC,WAAW,QAAO,sCAAqC;AAC/D,SAAQC,gBAAgB,QAAO,sCAAqC;AACpE,SAAQC,sBAAsB,QAAO,sDAAqD;AAE1F,MAAMC,cAAc,CAAC;;;;;;AAMrB,CAAC,CAACC,IAAI;AAEN,OAAO,MAAMC,0BAA0BN;IACrC,OAAgBI,cAAcA,YAAW;IAEzC,OAAgBG,WAAW;QACzB;YACEC,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;KACD,CAAA;IAED,OAAgBK,QAAQ;QACtB,oBAAoBX,MAAMY,OAAO,CAAC;YAChCC,SAAS;YACTC,SAAS;YACTR,aAAa;YACbS,QAAQ;QACV;QACAC,IAAIhB,MAAMiB,MAAM,CAAC;YACfX,aAAa;YACbY,WAAW;QACb;QACAC,MAAMnB,MAAMY,OAAO,CAAC;YAClBN,aAAa;QACf;QACA,gBAAgBN,MAAMoB,SAAS,CAAC;YAC9BN,SAAS;YACTR,aAAa;YACbY,WAAW;YACXH,QAAQ;QACV;IACF,EAAC;IAED,OAAgBM,gBAA0B;QAAC;KAAc,CAAA;IAEzD,MAAaC,MAAqB;QAChC,MAAM,EAACX,KAAK,EAAC,GAAG,MAAM,IAAI,CAACY,KAAK,CAACf;QACjC,MAAMgB,SAAmB,EAAE;QAC3B,MAAMR,KAAKX,uBAAuBmB,QAAQb,MAAMK,EAAE,GAAGS;QACrD,IAAID,OAAOE,MAAM,GAAG,GAAG;YACrB,IAAI,CAACC,KAAK,CAAC,CAAC,oBAAoB,EAAEH,OAAOI,GAAG,CAAC,CAACD,QAAU,CAAC,IAAI,EAAEA,OAAO,EAAEE,IAAI,CAAC,OAAO,EAAE;gBACpFC,MAAM7B,UAAU8B,WAAW;YAC7B;QACF;QAEA,IAAI;YACF,MAAMC,cAAc,MAAM,IAAI,CAACC,cAAc;YAE7C,MAAM9B,YAAY;gBAChB+B,YAAYF,YAAYG,IAAI;gBAC5BnB;gBACAG,MAAM,CAAC,CAACR,MAAMQ,IAAI;gBAClBiB,QAAQ,IAAI,CAACA,MAAM;gBACnBC,SAASL,YAAYZ,SAAS;YAChC;QACF,EAAE,OAAOO,OAAO;YACdvB,iBAAiB,0BAA0BuB;YAE3C,MAAMW,eAAeX,iBAAiBY,QAAQZ,MAAMa,OAAO,GAAGC,OAAOd;YACrE,IAAI,CAACA,KAAK,CAAC,CAAC,yBAAyB,EAAEW,cAAc,EAAE;gBAACR,MAAM;YAAC;QACjE;IACF;AACF"}
@@ -1,12 +1,12 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
- import { SanityCommand, subdebug } from '@sanity/cli-core';
2
+ import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
3
3
  import { input, select } from '@sanity/cli-core/ux';
4
4
  import { validateRole } from '../../actions/tokens/validateRole.js';
5
5
  import { promptForProject } from '../../prompts/promptForProject.js';
6
6
  import { createToken, getTokenRoles } from '../../services/tokens.js';
7
7
  import { getProjectIdFlag } from '../../util/sharedFlags.js';
8
- const tokensAddDebug = subdebug('tokens:add');
9
- export class AddTokenCommand extends SanityCommand {
8
+ const tokensCreateDebug = subdebug('tokens:create');
9
+ export class CreateTokenCommand extends SanityCommand {
10
10
  static args = {
11
11
  label: Args.string({
12
12
  description: 'Label for the new token',
@@ -38,7 +38,7 @@ export class AddTokenCommand extends SanityCommand {
38
38
  ];
39
39
  static flags = {
40
40
  ...getProjectIdFlag({
41
- description: 'Project ID to add token to',
41
+ description: 'Project ID to create token in',
42
42
  semantics: 'override'
43
43
  }),
44
44
  json: Flags.boolean({
@@ -46,7 +46,7 @@ export class AddTokenCommand extends SanityCommand {
46
46
  description: 'Output as JSON'
47
47
  }),
48
48
  role: Flags.string({
49
- description: 'Role to assign to the token',
49
+ description: 'Role to assign to the token (defaults to viewer in unattended mode)',
50
50
  helpValue: 'viewer'
51
51
  }),
52
52
  yes: Flags.boolean({
@@ -56,12 +56,20 @@ export class AddTokenCommand extends SanityCommand {
56
56
  })
57
57
  };
58
58
  static hiddenAliases = [
59
- 'token:add'
59
+ 'tokens:add',
60
+ 'token:add',
61
+ 'token:create'
60
62
  ];
61
63
  async run() {
62
- const { args, flags } = await this.parse(AddTokenCommand);
64
+ const { args, flags } = await this.parse(CreateTokenCommand);
63
65
  const { label: givenLabel } = args;
64
66
  const { json, role } = flags;
67
+ const label = givenLabel === undefined ? await this.promptForLabel() : givenLabel.trim();
68
+ if (!label) {
69
+ this.error('Token label cannot be empty. Pass a non-empty value as the `<label>` argument.', {
70
+ exit: exitCodes.USAGE_ERROR
71
+ });
72
+ }
65
73
  const projectId = await this.getProjectId({
66
74
  fallback: ()=>promptForProject({
67
75
  requiredPermissions: [
@@ -76,10 +84,9 @@ export class AddTokenCommand extends SanityCommand {
76
84
  ]
77
85
  })
78
86
  });
87
+ const roleName = await (role ? validateRole(role, projectId, this.output) : this.promptForRole(projectId));
79
88
  try {
80
- const label = givenLabel || await this.promptForLabel();
81
- const roleName = await (role ? validateRole(role, projectId, this.output) : this.promptForRole(projectId));
82
- tokensAddDebug(`Creating token for project ${projectId}`, {
89
+ tokensCreateDebug(`Creating token for project ${projectId}`, {
83
90
  label,
84
91
  roleName
85
92
  });
@@ -92,16 +99,16 @@ export class AddTokenCommand extends SanityCommand {
92
99
  this.log(JSON.stringify(token, null, 2));
93
100
  return;
94
101
  }
95
- this.log('Token created successfully!');
102
+ this.log('API token created');
96
103
  this.log(`Label: ${token.label}`);
97
104
  this.log(`ID: ${token.id}`);
98
105
  this.log(`Role: ${token.roles.map((r)=>r.title).join(', ')}`);
99
106
  this.log(`Token: ${token.key}`);
100
107
  this.log('');
101
- this.log('Copy the token above this is your only chance to do so!');
108
+ this.log("Copy the token now. It won't be shown again.");
102
109
  } catch (error) {
103
110
  const err = error;
104
- tokensAddDebug(`Error creating token for project ${projectId}`, err);
111
+ tokensCreateDebug(`Error creating token for project ${projectId}`, err);
105
112
  this.error(`Token creation failed:\n${err.message}`, {
106
113
  exit: 1
107
114
  });
@@ -109,8 +116,8 @@ export class AddTokenCommand extends SanityCommand {
109
116
  }
110
117
  async promptForLabel() {
111
118
  if (this.isUnattended()) {
112
- this.error('Token label is required in non-interactive mode. Provide a label as an argument.', {
113
- exit: 1
119
+ this.error('Token label is required. Pass it as the `<label>` argument.', {
120
+ exit: exitCodes.USAGE_ERROR
114
121
  });
115
122
  }
116
123
  const label = await input({
@@ -122,7 +129,7 @@ export class AddTokenCommand extends SanityCommand {
122
129
  return true;
123
130
  }
124
131
  });
125
- return label;
132
+ return label.trim();
126
133
  }
127
134
  async promptForRole(projectId) {
128
135
  if (this.isUnattended()) {
@@ -131,7 +138,7 @@ export class AddTokenCommand extends SanityCommand {
131
138
  }
132
139
  const roles = await getTokenRoles(projectId);
133
140
  const robotRoles = roles.filter((role)=>role.appliesToRobots);
134
- tokensAddDebug('Robot roles', {
141
+ tokensCreateDebug('Robot roles', {
135
142
  robotRoles
136
143
  });
137
144
  if (robotRoles.length === 0) {
@@ -152,4 +159,4 @@ export class AddTokenCommand extends SanityCommand {
152
159
  }
153
160
  }
154
161
 
155
- //# sourceMappingURL=add.js.map
162
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/commands/tokens/create.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {input, select} from '@sanity/cli-core/ux'\n\nimport {validateRole} from '../../actions/tokens/validateRole.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {createToken, getTokenRoles} from '../../services/tokens.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst tokensCreateDebug = subdebug('tokens:create')\n\nexport class CreateTokenCommand extends SanityCommand<typeof CreateTokenCommand> {\n static override args = {\n label: Args.string({\n description: 'Label for the new token',\n required: false,\n }),\n }\n\n static override description = 'Create a new API token for the project'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> \"My API Token\"',\n description: 'Create a token with a label',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"My API Token\" --role=editor',\n description: 'Create a token with editor role',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"CI Token\" --role=editor --yes',\n description: 'Create a token in unattended mode',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"API Token\" --json',\n description: 'Output token information as JSON',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"My Token\" --project-id abc123 --role=editor',\n description: 'Create a token for a specific project',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to create token in',\n semantics: 'override',\n }),\n json: Flags.boolean({\n default: false,\n description: 'Output as JSON',\n }),\n role: Flags.string({\n description: 'Role to assign to the token (defaults to viewer in unattended mode)',\n helpValue: 'viewer',\n }),\n yes: Flags.boolean({\n char: 'y',\n default: false,\n description: 'Skip prompts and use defaults (unattended mode)',\n }),\n }\n\n static override hiddenAliases: string[] = ['tokens:add', 'token:add', 'token:create']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CreateTokenCommand)\n const {label: givenLabel} = args\n const {json, role} = flags\n\n const label = givenLabel === undefined ? await this.promptForLabel() : givenLabel.trim()\n if (!label) {\n this.error('Token label cannot be empty. Pass a non-empty value as the `<label>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.roles'},\n {grant: 'create', permission: 'sanity.project.tokens'},\n ],\n }),\n })\n\n const roleName = await (role\n ? validateRole(role, projectId, this.output)\n : this.promptForRole(projectId))\n\n try {\n tokensCreateDebug(`Creating token for project ${projectId}`, {\n label,\n roleName,\n })\n const token = await createToken({\n label,\n projectId,\n roleName,\n })\n\n if (json) {\n this.log(JSON.stringify(token, null, 2))\n return\n }\n\n this.log('API token created')\n this.log(`Label: ${token.label}`)\n this.log(`ID: ${token.id}`)\n this.log(`Role: ${token.roles.map((r) => r.title).join(', ')}`)\n this.log(`Token: ${token.key}`)\n this.log('')\n this.log(\"Copy the token now. It won't be shown again.\")\n } catch (error) {\n const err = error as Error\n\n tokensCreateDebug(`Error creating token for project ${projectId}`, err)\n this.error(`Token creation failed:\\n${err.message}`, {exit: 1})\n }\n }\n\n private async promptForLabel(): Promise<string> {\n if (this.isUnattended()) {\n this.error('Token label is required. Pass it as the `<label>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const label = await input({\n message: 'Token label:',\n validate: (value) => {\n if (!value || !value.trim()) {\n return 'Label cannot be empty'\n }\n return true\n },\n })\n\n return label.trim()\n }\n\n private async promptForRole(projectId: string): Promise<string> {\n if (this.isUnattended()) {\n return 'viewer' // Default role for unattended mode\n }\n\n const roles = await getTokenRoles(projectId)\n const robotRoles = roles.filter((role) => role.appliesToRobots)\n\n tokensCreateDebug('Robot roles', {robotRoles})\n\n if (robotRoles.length === 0) {\n this.error('No roles available for tokens', {exit: 1})\n }\n\n const selectedRoleName = await select({\n choices: robotRoles.map((role) => ({\n name: `${role.title} (${role.name})`,\n short: role.title,\n value: role.name,\n })),\n default: 'viewer',\n message: 'Select role for the token:',\n })\n\n return selectedRoleName\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","input","select","validateRole","promptForProject","createToken","getTokenRoles","getProjectIdFlag","tokensCreateDebug","CreateTokenCommand","args","label","string","description","required","examples","command","flags","semantics","json","boolean","default","role","helpValue","yes","char","hiddenAliases","run","parse","givenLabel","undefined","promptForLabel","trim","error","exit","USAGE_ERROR","projectId","getProjectId","fallback","requiredPermissions","grant","permission","roleName","output","promptForRole","token","log","JSON","stringify","id","roles","map","r","title","join","key","err","message","isUnattended","validate","value","robotRoles","filter","appliesToRobots","length","selectedRoleName","choices","name","short"],"mappings":"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"}
@@ -1,9 +1,10 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
- import { SanityCommand, subdebug } from '@sanity/cli-core';
2
+ import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
3
3
  import { confirm, select } from '@sanity/cli-core/ux';
4
4
  import { ClientError } from '@sanity/client';
5
5
  import { promptForProject } from '../../prompts/promptForProject.js';
6
6
  import { deleteToken, getTokens } from '../../services/tokens.js';
7
+ import { formatCliErrorMessages } from '../../util/formatCliErrorMessages.js';
7
8
  import { getProjectIdFlag } from '../../util/sharedFlags.js';
8
9
  const deleteTokenDebug = subdebug('tokens:delete');
9
10
  export class DeleteTokensCommand extends SanityCommand {
@@ -51,12 +52,22 @@ export class DeleteTokensCommand extends SanityCommand {
51
52
  projectId;
52
53
  async run() {
53
54
  const { args, flags } = await this.parse(DeleteTokensCommand);
54
- const unattended = flags.yes;
55
+ const skipConfirmation = flags.yes;
56
+ const unattended = this.isUnattended();
55
57
  const { tokenId: givenTokenId } = args;
56
- if (unattended && !givenTokenId) {
57
- this.error('Token ID is required in non-interactive mode. Provide a token ID as an argument.', {
58
- exit: 1
59
- });
58
+ if (unattended) {
59
+ const errors = [];
60
+ if (!givenTokenId) {
61
+ errors.push('Token ID is required. Pass it as the `<tokenId>` argument.');
62
+ }
63
+ if (!skipConfirmation) {
64
+ errors.push('Deletion requires confirmation. Pass `--yes` to delete the token.');
65
+ }
66
+ if (errors.length > 0) {
67
+ this.error(formatCliErrorMessages(errors), {
68
+ exit: exitCodes.USAGE_ERROR
69
+ });
70
+ }
60
71
  }
61
72
  // Ensure we have project context
62
73
  const projectId = await this.getProjectId({
@@ -70,25 +81,23 @@ export class DeleteTokensCommand extends SanityCommand {
70
81
  })
71
82
  });
72
83
  this.projectId = projectId;
73
- let tokenId;
74
- try {
75
- tokenId = givenTokenId || await this.getTokenIdFromList();
76
- if (!unattended) {
77
- const confirmed = await confirm({
78
- default: false,
79
- message: `Are you sure you want to delete the token with ID "${tokenId}"?`
80
- });
81
- if (!confirmed) {
82
- this.error('Operation cancelled', {
83
- exit: 1
84
- });
85
- }
84
+ const tokenId = givenTokenId || await this.getTokenIdFromList();
85
+ if (!skipConfirmation) {
86
+ const confirmed = await confirm({
87
+ default: false,
88
+ message: `Delete API token "${tokenId}"?`
89
+ });
90
+ if (!confirmed) {
91
+ this.log('API token not deleted');
92
+ this.exit(exitCodes.USER_ABORT);
86
93
  }
94
+ }
95
+ try {
87
96
  await deleteToken({
88
97
  projectId: this.projectId,
89
98
  tokenId
90
99
  });
91
- this.log('Token deleted successfully');
100
+ this.log('API token deleted');
92
101
  } catch (error) {
93
102
  if (error instanceof ClientError && error.response.statusCode === 404) {
94
103
  this.error(`Token with ID "${tokenId}" not found`, {
@@ -103,10 +112,19 @@ export class DeleteTokensCommand extends SanityCommand {
103
112
  }
104
113
  }
105
114
  async getTokenIdFromList() {
106
- const tokens = await getTokens(this.projectId);
115
+ let tokens;
116
+ try {
117
+ tokens = await getTokens(this.projectId);
118
+ } catch (error) {
119
+ const err = error;
120
+ deleteTokenDebug(`Error fetching tokens for project ${this.projectId}`, err);
121
+ this.error(`Could not list API tokens:\n${err.message}\nCheck the project ID and your access permissions, then try again.`, {
122
+ exit: exitCodes.RUNTIME_ERROR
123
+ });
124
+ }
107
125
  if (tokens.length === 0) {
108
- this.error('No tokens found', {
109
- exit: 1
126
+ this.error('No API tokens found for this project.', {
127
+ exit: exitCodes.RUNTIME_ERROR
110
128
  });
111
129
  }
112
130
  const choices = tokens.map((token)=>({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/tokens/delete.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {SanityCommand, subdebug} from '@sanity/cli-core'\nimport {confirm, select} from '@sanity/cli-core/ux'\nimport {ClientError} from '@sanity/client'\n\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {deleteToken, getTokens} from '../../services/tokens.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst deleteTokenDebug = subdebug('tokens:delete')\n\nexport class DeleteTokensCommand extends SanityCommand<typeof DeleteTokensCommand> {\n static override args = {\n tokenId: Args.string({\n description: 'Token ID to delete (will prompt if not provided)',\n required: false,\n }),\n }\n\n static override description = 'Delete an API token from the project'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively select and delete a token',\n },\n {\n command: '<%= config.bin %> <%= command.id %> silJ2lFmK6dONB',\n description: 'Delete a specific token by ID',\n },\n {\n command: '<%= config.bin %> <%= command.id %> silJ2lFmK6dONB --yes',\n description: 'Delete a specific token without confirmation prompt',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project-id abc123',\n description: 'Delete a token from a specific project',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to delete token from',\n semantics: 'override',\n }),\n yes: Flags.boolean({\n aliases: ['y'],\n description: 'Skip confirmation prompt (unattended mode)',\n required: false,\n }),\n }\n\n static override hiddenAliases: string[] = ['token:delete']\n\n private projectId!: string\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(DeleteTokensCommand)\n\n const unattended = flags.yes\n const {tokenId: givenTokenId} = args\n\n if (unattended && !givenTokenId) {\n this.error(\n 'Token ID is required in non-interactive mode. Provide a token ID as an argument.',\n {exit: 1},\n )\n }\n\n // Ensure we have project context\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'delete', permission: 'sanity.project.tokens'}],\n }),\n })\n\n this.projectId = projectId\n\n let tokenId: string | undefined\n\n try {\n tokenId = givenTokenId || (await this.getTokenIdFromList())\n\n if (!unattended) {\n const confirmed = await confirm({\n default: false,\n message: `Are you sure you want to delete the token with ID \"${tokenId}\"?`,\n })\n\n if (!confirmed) {\n this.error('Operation cancelled', {exit: 1})\n }\n }\n\n await deleteToken({\n projectId: this.projectId,\n tokenId,\n })\n\n this.log('Token deleted successfully')\n } catch (error) {\n if (error instanceof ClientError && error.response.statusCode === 404) {\n this.error(`Token with ID \"${tokenId}\" not found`, {exit: 1})\n }\n\n const err = error as Error\n deleteTokenDebug(`Error deleting token`, err)\n this.error(`Token deletion failed:\\n${err.message}`, {exit: 1})\n }\n }\n\n private async getTokenIdFromList() {\n const tokens = await getTokens(this.projectId)\n\n if (tokens.length === 0) {\n this.error('No tokens found', {exit: 1})\n }\n\n const choices = tokens.map((token) => ({\n name: `${token.label} (${(token.roles || []).map((r) => r.title).join(', ')})`,\n value: token.id,\n }))\n\n return select({\n choices,\n message: 'Select token to delete:',\n })\n }\n}\n"],"names":["Args","Flags","SanityCommand","subdebug","confirm","select","ClientError","promptForProject","deleteToken","getTokens","getProjectIdFlag","deleteTokenDebug","DeleteTokensCommand","args","tokenId","string","description","required","examples","command","flags","semantics","yes","boolean","aliases","hiddenAliases","projectId","run","parse","unattended","givenTokenId","error","exit","getProjectId","fallback","requiredPermissions","grant","permission","getTokenIdFromList","confirmed","default","message","log","response","statusCode","err","tokens","length","choices","map","token","name","label","roles","r","title","join","value","id"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/commands/tokens/delete.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {confirm, select} from '@sanity/cli-core/ux'\nimport {ClientError} from '@sanity/client'\n\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {deleteToken, getTokens} from '../../services/tokens.js'\nimport {formatCliErrorMessages} from '../../util/formatCliErrorMessages.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst deleteTokenDebug = subdebug('tokens:delete')\n\nexport class DeleteTokensCommand extends SanityCommand<typeof DeleteTokensCommand> {\n static override args = {\n tokenId: Args.string({\n description: 'Token ID to delete (will prompt if not provided)',\n required: false,\n }),\n }\n\n static override description = 'Delete an API token from the project'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively select and delete a token',\n },\n {\n command: '<%= config.bin %> <%= command.id %> silJ2lFmK6dONB',\n description: 'Delete a specific token by ID',\n },\n {\n command: '<%= config.bin %> <%= command.id %> silJ2lFmK6dONB --yes',\n description: 'Delete a specific token without confirmation prompt',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project-id abc123',\n description: 'Delete a token from a specific project',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to delete token from',\n semantics: 'override',\n }),\n yes: Flags.boolean({\n aliases: ['y'],\n description: 'Skip confirmation prompt (unattended mode)',\n required: false,\n }),\n }\n\n static override hiddenAliases: string[] = ['token:delete']\n\n private projectId!: string\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(DeleteTokensCommand)\n\n const skipConfirmation = flags.yes\n const unattended = this.isUnattended()\n const {tokenId: givenTokenId} = args\n\n if (unattended) {\n const errors: string[] = []\n\n if (!givenTokenId) {\n errors.push('Token ID is required. Pass it as the `<tokenId>` argument.')\n }\n if (!skipConfirmation) {\n errors.push('Deletion requires confirmation. Pass `--yes` to delete the token.')\n }\n\n if (errors.length > 0) {\n this.error(formatCliErrorMessages(errors), {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n }\n\n // Ensure we have project context\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'delete', permission: 'sanity.project.tokens'}],\n }),\n })\n\n this.projectId = projectId\n\n const tokenId = givenTokenId || (await this.getTokenIdFromList())\n\n if (!skipConfirmation) {\n const confirmed = await confirm({\n default: false,\n message: `Delete API token \"${tokenId}\"?`,\n })\n\n if (!confirmed) {\n this.log('API token not deleted')\n this.exit(exitCodes.USER_ABORT)\n }\n }\n\n try {\n await deleteToken({\n projectId: this.projectId,\n tokenId,\n })\n\n this.log('API token deleted')\n } catch (error) {\n if (error instanceof ClientError && error.response.statusCode === 404) {\n this.error(`Token with ID \"${tokenId}\" not found`, {exit: 1})\n }\n\n const err = error as Error\n deleteTokenDebug(`Error deleting token`, err)\n this.error(`Token deletion failed:\\n${err.message}`, {exit: 1})\n }\n }\n\n private async getTokenIdFromList() {\n let tokens: Awaited<ReturnType<typeof getTokens>>\n try {\n tokens = await getTokens(this.projectId)\n } catch (error) {\n const err = error as Error\n deleteTokenDebug(`Error fetching tokens for project ${this.projectId}`, err)\n this.error(\n `Could not list API tokens:\\n${err.message}\\nCheck the project ID and your access permissions, then try again.`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n\n if (tokens.length === 0) {\n this.error('No API tokens found for this project.', {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const choices = tokens.map((token) => ({\n name: `${token.label} (${(token.roles || []).map((r) => r.title).join(', ')})`,\n value: token.id,\n }))\n\n return select({\n choices,\n message: 'Select token to delete:',\n })\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","confirm","select","ClientError","promptForProject","deleteToken","getTokens","formatCliErrorMessages","getProjectIdFlag","deleteTokenDebug","DeleteTokensCommand","args","tokenId","string","description","required","examples","command","flags","semantics","yes","boolean","aliases","hiddenAliases","projectId","run","parse","skipConfirmation","unattended","isUnattended","givenTokenId","errors","push","length","error","exit","USAGE_ERROR","getProjectId","fallback","requiredPermissions","grant","permission","getTokenIdFromList","confirmed","default","message","log","USER_ABORT","response","statusCode","err","tokens","RUNTIME_ERROR","choices","map","token","name","label","roles","r","title","join","value","id"],"mappings":"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"}
@@ -51,14 +51,9 @@ export class UndeployCommand extends SanityCommand {
51
51
  type: isApp ? 'coreApp' : 'studio',
52
52
  workbench
53
53
  }) : isApp ? createAppUndeployAdapter(cliConfig) : createStudioUndeployAdapter(cliConfig);
54
- // An unattended run (--yes, --json, non-TTY) can't answer the confirmation
55
- // prompt, so it consents up front; machine callers preview with --dry-run.
56
- const undeployFlags = this.isUnattended() ? {
57
- ...flags,
58
- yes: true
59
- } : flags;
60
54
  await runUndeploy({
61
- flags: undeployFlags,
55
+ flags,
56
+ isUnattended: this.isUnattended(),
62
57
  output: this.output
63
58
  }, adapter);
64
59
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/undeploy.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\nimport {SanityCommand} from '@sanity/cli-core'\nimport {type UndeployAdapter} from '@sanity/cli-core/undeploy'\nimport {getWorkbench} from '@sanity/workbench-cli/deploy'\nimport {createWorkbenchUndeployAdapter} from '@sanity/workbench-cli/undeploy'\n\nimport {\n createAppUndeployAdapter,\n createStudioUndeployAdapter,\n} from '../actions/undeploy/adapters.js'\nimport {runUndeploy} from '../actions/undeploy/runUndeploy.js'\nimport {getAppId} from '../util/appId.js'\nimport {determineIsApp} from '../util/determineIsApp.js'\n\nexport class UndeployCommand extends SanityCommand<typeof UndeployCommand> {\n static override description = 'Removes the deployed Sanity Studio/App from Sanity hosting'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Undeploy the studio or application after confirming',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --dry-run',\n description: 'Report what would be undeployed without deleting anything',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --json --yes',\n description: 'Undeploy without prompting and report the result as JSON',\n },\n ]\n\n static override flags = {\n 'dry-run': Flags.boolean({\n default: false,\n description: 'Report what would be undeployed without deleting anything',\n }),\n json: Flags.boolean({\n char: 'j',\n default: false,\n description: 'Output the result as JSON',\n }),\n yes: Flags.boolean({\n char: 'y',\n default: false,\n description:\n 'Unattended mode, answers \"yes\" to any \"yes/no\" prompt and otherwise uses defaults',\n }),\n }\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(UndeployCommand)\n\n const cliConfig = await this.getCliConfig()\n const isApp = determineIsApp(cliConfig)\n\n // Workbench apps deploy through Brett, so they undeploy through it too;\n // plain projects keep the user-applications backend.\n const workbench = getWorkbench(cliConfig)\n const adapter: UndeployAdapter = workbench\n ? createWorkbenchUndeployAdapter({\n appId: getAppId(cliConfig),\n organizationId: cliConfig.app?.organizationId,\n type: isApp ? 'coreApp' : 'studio',\n workbench,\n })\n : isApp\n ? createAppUndeployAdapter(cliConfig)\n : createStudioUndeployAdapter(cliConfig)\n\n // An unattended run (--yes, --json, non-TTY) can't answer the confirmation\n // prompt, so it consents up front; machine callers preview with --dry-run.\n const undeployFlags = this.isUnattended() ? {...flags, yes: true} : flags\n await runUndeploy({flags: undeployFlags, output: this.output}, adapter)\n }\n}\n"],"names":["Flags","SanityCommand","getWorkbench","createWorkbenchUndeployAdapter","createAppUndeployAdapter","createStudioUndeployAdapter","runUndeploy","getAppId","determineIsApp","UndeployCommand","description","examples","command","flags","boolean","default","json","char","yes","run","parse","cliConfig","getCliConfig","isApp","workbench","adapter","appId","organizationId","app","type","undeployFlags","isUnattended","output"],"mappings":"AAAA,SAAQA,KAAK,QAAO,cAAa;AACjC,SAAQC,aAAa,QAAO,mBAAkB;AAE9C,SAAQC,YAAY,QAAO,+BAA8B;AACzD,SAAQC,8BAA8B,QAAO,iCAAgC;AAE7E,SACEC,wBAAwB,EACxBC,2BAA2B,QACtB,kCAAiC;AACxC,SAAQC,WAAW,QAAO,qCAAoC;AAC9D,SAAQC,QAAQ,QAAO,mBAAkB;AACzC,SAAQC,cAAc,QAAO,4BAA2B;AAExD,OAAO,MAAMC,wBAAwBR;IACnC,OAAgBS,cAAc,6DAA4D;IAE1F,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;KACD,CAAA;IAED,OAAgBG,QAAQ;QACtB,WAAWb,MAAMc,OAAO,CAAC;YACvBC,SAAS;YACTL,aAAa;QACf;QACAM,MAAMhB,MAAMc,OAAO,CAAC;YAClBG,MAAM;YACNF,SAAS;YACTL,aAAa;QACf;QACAQ,KAAKlB,MAAMc,OAAO,CAAC;YACjBG,MAAM;YACNF,SAAS;YACTL,aACE;QACJ;IACF,EAAC;IAED,MAAaS,MAAqB;QAChC,MAAM,EAACN,KAAK,EAAC,GAAG,MAAM,IAAI,CAACO,KAAK,CAACX;QAEjC,MAAMY,YAAY,MAAM,IAAI,CAACC,YAAY;QACzC,MAAMC,QAAQf,eAAea;QAE7B,wEAAwE;QACxE,qDAAqD;QACrD,MAAMG,YAAYtB,aAAamB;QAC/B,MAAMI,UAA2BD,YAC7BrB,+BAA+B;YAC7BuB,OAAOnB,SAASc;YAChBM,gBAAgBN,UAAUO,GAAG,EAAED;YAC/BE,MAAMN,QAAQ,YAAY;YAC1BC;QACF,KACAD,QACEnB,yBAAyBiB,aACzBhB,4BAA4BgB;QAElC,2EAA2E;QAC3E,2EAA2E;QAC3E,MAAMS,gBAAgB,IAAI,CAACC,YAAY,KAAK;YAAC,GAAGlB,KAAK;YAAEK,KAAK;QAAI,IAAIL;QACpE,MAAMP,YAAY;YAACO,OAAOiB;YAAeE,QAAQ,IAAI,CAACA,MAAM;QAAA,GAAGP;IACjE;AACF"}
1
+ {"version":3,"sources":["../../src/commands/undeploy.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\nimport {SanityCommand} from '@sanity/cli-core'\nimport {type UndeployAdapter} from '@sanity/cli-core/undeploy'\nimport {getWorkbench} from '@sanity/workbench-cli/deploy'\nimport {createWorkbenchUndeployAdapter} from '@sanity/workbench-cli/undeploy'\n\nimport {\n createAppUndeployAdapter,\n createStudioUndeployAdapter,\n} from '../actions/undeploy/adapters.js'\nimport {runUndeploy} from '../actions/undeploy/runUndeploy.js'\nimport {getAppId} from '../util/appId.js'\nimport {determineIsApp} from '../util/determineIsApp.js'\n\nexport class UndeployCommand extends SanityCommand<typeof UndeployCommand> {\n static override description = 'Removes the deployed Sanity Studio/App from Sanity hosting'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Undeploy the studio or application after confirming',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --dry-run',\n description: 'Report what would be undeployed without deleting anything',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --json --yes',\n description: 'Undeploy without prompting and report the result as JSON',\n },\n ]\n\n static override flags = {\n 'dry-run': Flags.boolean({\n default: false,\n description: 'Report what would be undeployed without deleting anything',\n }),\n json: Flags.boolean({\n char: 'j',\n default: false,\n description: 'Output the result as JSON',\n }),\n yes: Flags.boolean({\n char: 'y',\n default: false,\n description:\n 'Unattended mode, answers \"yes\" to any \"yes/no\" prompt and otherwise uses defaults',\n }),\n }\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(UndeployCommand)\n\n const cliConfig = await this.getCliConfig()\n const isApp = determineIsApp(cliConfig)\n\n // Workbench apps deploy through Brett, so they undeploy through it too;\n // plain projects keep the user-applications backend.\n const workbench = getWorkbench(cliConfig)\n const adapter: UndeployAdapter = workbench\n ? createWorkbenchUndeployAdapter({\n appId: getAppId(cliConfig),\n organizationId: cliConfig.app?.organizationId,\n type: isApp ? 'coreApp' : 'studio',\n workbench,\n })\n : isApp\n ? createAppUndeployAdapter(cliConfig)\n : createStudioUndeployAdapter(cliConfig)\n\n await runUndeploy({flags, isUnattended: this.isUnattended(), output: this.output}, adapter)\n }\n}\n"],"names":["Flags","SanityCommand","getWorkbench","createWorkbenchUndeployAdapter","createAppUndeployAdapter","createStudioUndeployAdapter","runUndeploy","getAppId","determineIsApp","UndeployCommand","description","examples","command","flags","boolean","default","json","char","yes","run","parse","cliConfig","getCliConfig","isApp","workbench","adapter","appId","organizationId","app","type","isUnattended","output"],"mappings":"AAAA,SAAQA,KAAK,QAAO,cAAa;AACjC,SAAQC,aAAa,QAAO,mBAAkB;AAE9C,SAAQC,YAAY,QAAO,+BAA8B;AACzD,SAAQC,8BAA8B,QAAO,iCAAgC;AAE7E,SACEC,wBAAwB,EACxBC,2BAA2B,QACtB,kCAAiC;AACxC,SAAQC,WAAW,QAAO,qCAAoC;AAC9D,SAAQC,QAAQ,QAAO,mBAAkB;AACzC,SAAQC,cAAc,QAAO,4BAA2B;AAExD,OAAO,MAAMC,wBAAwBR;IACnC,OAAgBS,cAAc,6DAA4D;IAE1F,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;KACD,CAAA;IAED,OAAgBG,QAAQ;QACtB,WAAWb,MAAMc,OAAO,CAAC;YACvBC,SAAS;YACTL,aAAa;QACf;QACAM,MAAMhB,MAAMc,OAAO,CAAC;YAClBG,MAAM;YACNF,SAAS;YACTL,aAAa;QACf;QACAQ,KAAKlB,MAAMc,OAAO,CAAC;YACjBG,MAAM;YACNF,SAAS;YACTL,aACE;QACJ;IACF,EAAC;IAED,MAAaS,MAAqB;QAChC,MAAM,EAACN,KAAK,EAAC,GAAG,MAAM,IAAI,CAACO,KAAK,CAACX;QAEjC,MAAMY,YAAY,MAAM,IAAI,CAACC,YAAY;QACzC,MAAMC,QAAQf,eAAea;QAE7B,wEAAwE;QACxE,qDAAqD;QACrD,MAAMG,YAAYtB,aAAamB;QAC/B,MAAMI,UAA2BD,YAC7BrB,+BAA+B;YAC7BuB,OAAOnB,SAASc;YAChBM,gBAAgBN,UAAUO,GAAG,EAAED;YAC/BE,MAAMN,QAAQ,YAAY;YAC1BC;QACF,KACAD,QACEnB,yBAAyBiB,aACzBhB,4BAA4BgB;QAElC,MAAMf,YAAY;YAACO;YAAOiB,cAAc,IAAI,CAACA,YAAY;YAAIC,QAAQ,IAAI,CAACA,MAAM;QAAA,GAAGN;IACrF;AACF"}
@@ -1,9 +1,10 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
- import { SanityCommand, subdebug } from '@sanity/cli-core';
2
+ import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
3
3
  import { input, select } from '@sanity/cli-core/ux';
4
4
  import { validateEmail } from '../../actions/users/validateEmail.js';
5
5
  import { promptForProject } from '../../prompts/promptForProject.js';
6
6
  import { getProjectRoles, inviteUser } from '../../services/projects.js';
7
+ import { formatCliErrorMessages } from '../../util/formatCliErrorMessages.js';
7
8
  import { getProjectIdFlag } from '../../util/sharedFlags.js';
8
9
  const QUOTA_ERROR_MESSAGE = 'Project is already at user quota, add billing details to the project in order to allow overage charges.';
9
10
  const usersInviteDebug = subdebug('users:invite');
@@ -49,6 +50,27 @@ export class UsersInviteCommand extends SanityCommand {
49
50
  async run() {
50
51
  const { email: selectedEmail } = this.args;
51
52
  const { role: selectedRole } = this.flags;
53
+ const normalizedEmail = selectedEmail?.trim();
54
+ if (this.isUnattended()) {
55
+ const errors = [];
56
+ if (!normalizedEmail) {
57
+ errors.push('Email address is required. Pass it as the `<email>` argument.');
58
+ }
59
+ if (!selectedRole) {
60
+ errors.push('User role is required. Pass it with `--role <role>`.');
61
+ }
62
+ if (errors.length > 0) {
63
+ this.error(formatCliErrorMessages(errors), {
64
+ exit: exitCodes.USAGE_ERROR
65
+ });
66
+ }
67
+ }
68
+ if (selectedEmail !== undefined) {
69
+ const validation = validateEmail(normalizedEmail ?? '');
70
+ if (validation !== true) this.error(validation, {
71
+ exit: exitCodes.USAGE_ERROR
72
+ });
73
+ }
52
74
  const projectId = await this.getProjectId({
53
75
  fallback: ()=>promptForProject({
54
76
  requiredPermissions: [
@@ -72,12 +94,12 @@ export class UsersInviteCommand extends SanityCommand {
72
94
  exit: 1
73
95
  });
74
96
  }
75
- const email = selectedEmail || await this.promptForEmail();
97
+ const email = normalizedEmail || await this.promptForEmail();
76
98
  const roleSelection = selectedRole || await this.promptForRole(roles);
77
99
  const role = roles.find(({ name })=>name.toLowerCase() === roleSelection.toLowerCase());
78
100
  if (!role) {
79
101
  this.error(`Role name "${roleSelection}" not found. Available roles: ${roles.map((r)=>r.name).join(', ')}`, {
80
- exit: 1
102
+ exit: exitCodes.USAGE_ERROR
81
103
  });
82
104
  }
83
105
  try {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/users/invite.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {SanityCommand, subdebug} from '@sanity/cli-core'\nimport {input, select} from '@sanity/cli-core/ux'\n\nimport {type Role} from '../../actions/users/types.js'\nimport {validateEmail} from '../../actions/users/validateEmail.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {getProjectRoles, inviteUser} from '../../services/projects.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst QUOTA_ERROR_MESSAGE =\n 'Project is already at user quota, add billing details to the project in order to allow overage charges.'\n\nconst usersInviteDebug = subdebug('users:invite')\n\nexport class UsersInviteCommand extends SanityCommand<typeof UsersInviteCommand> {\n static override args = {\n email: Args.string({\n description: 'Email address to invite',\n required: false,\n }),\n }\n\n static override description = 'Invite a new user to the project'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Invite a new user to the project (prompt for details)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> pippi@sanity.io',\n description: 'Send a new user invite to the email \"pippi@sanity.io\", prompt for role',\n },\n {\n command: '<%= config.bin %> <%= command.id %> pippi@sanity.io --role administrator',\n description: 'Send a new user invite to the email \"pippi@sanity.io\", as administrator',\n },\n {\n command: '<%= config.bin %> <%= command.id %> pippi@sanity.io --project-id abc123',\n description: 'Invite a user to a specific project',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to invite user to',\n semantics: 'override',\n }),\n role: Flags.string({\n description: 'Role to invite the user as',\n required: false,\n }),\n }\n\n static override hiddenAliases: string[] = ['user:invite']\n\n public async run(): Promise<void> {\n const {email: selectedEmail} = this.args\n const {role: selectedRole} = this.flags\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.roles'},\n {grant: 'invite', permission: 'sanity.project.members'},\n ],\n }),\n })\n\n let roles: Role[]\n try {\n roles = (await getProjectRoles(projectId)).filter((role) => role.appliesToUsers)\n } catch (error) {\n usersInviteDebug('Error fetching roles', error)\n this.error('Error fetching roles', {exit: 1})\n }\n\n const email = selectedEmail || (await this.promptForEmail())\n const roleSelection = selectedRole || (await this.promptForRole(roles))\n const role = roles.find(({name}) => name.toLowerCase() === roleSelection.toLowerCase())\n\n if (!role) {\n this.error(\n `Role name \"${roleSelection}\" not found. Available roles: ${roles.map((r) => r.name).join(', ')}`,\n {exit: 1},\n )\n }\n\n try {\n await inviteUser({email, projectId, role: role.name})\n\n this.log(`Invitation sent to ${email}`)\n } catch (error) {\n usersInviteDebug(`Error inviting user`, error)\n if ((error as Error & {statusCode: number}).statusCode === 402) {\n this.error(QUOTA_ERROR_MESSAGE, {exit: 1})\n }\n\n this.error(`Error inviting user`, {exit: 1})\n }\n }\n\n private async promptForEmail(): Promise<string> {\n return input({\n message: 'Email to invite:',\n transformer: (val: string) => val.trim(),\n validate: validateEmail,\n })\n }\n\n private async promptForRole(roles: Role[]): Promise<string> {\n return select({\n choices: roles.map((role) => ({\n name: `${role.title} (${role.description || 'No description'})`,\n value: role.name,\n })),\n message: 'Which role should the user have?',\n })\n }\n}\n"],"names":["Args","Flags","SanityCommand","subdebug","input","select","validateEmail","promptForProject","getProjectRoles","inviteUser","getProjectIdFlag","QUOTA_ERROR_MESSAGE","usersInviteDebug","UsersInviteCommand","args","email","string","description","required","examples","command","flags","semantics","role","hiddenAliases","run","selectedEmail","selectedRole","projectId","getProjectId","fallback","requiredPermissions","grant","permission","roles","filter","appliesToUsers","error","exit","promptForEmail","roleSelection","promptForRole","find","name","toLowerCase","map","r","join","log","statusCode","message","transformer","val","trim","validate","choices","title","value"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/commands/users/invite.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {input, select} from '@sanity/cli-core/ux'\n\nimport {type Role} from '../../actions/users/types.js'\nimport {validateEmail} from '../../actions/users/validateEmail.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {getProjectRoles, inviteUser} from '../../services/projects.js'\nimport {formatCliErrorMessages} from '../../util/formatCliErrorMessages.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst QUOTA_ERROR_MESSAGE =\n 'Project is already at user quota, add billing details to the project in order to allow overage charges.'\n\nconst usersInviteDebug = subdebug('users:invite')\n\nexport class UsersInviteCommand extends SanityCommand<typeof UsersInviteCommand> {\n static override args = {\n email: Args.string({\n description: 'Email address to invite',\n required: false,\n }),\n }\n\n static override description = 'Invite a new user to the project'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Invite a new user to the project (prompt for details)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> pippi@sanity.io',\n description: 'Send a new user invite to the email \"pippi@sanity.io\", prompt for role',\n },\n {\n command: '<%= config.bin %> <%= command.id %> pippi@sanity.io --role administrator',\n description: 'Send a new user invite to the email \"pippi@sanity.io\", as administrator',\n },\n {\n command: '<%= config.bin %> <%= command.id %> pippi@sanity.io --project-id abc123',\n description: 'Invite a user to a specific project',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to invite user to',\n semantics: 'override',\n }),\n role: Flags.string({\n description: 'Role to invite the user as',\n required: false,\n }),\n }\n\n static override hiddenAliases: string[] = ['user:invite']\n\n public async run(): Promise<void> {\n const {email: selectedEmail} = this.args\n const {role: selectedRole} = this.flags\n const normalizedEmail = selectedEmail?.trim()\n\n if (this.isUnattended()) {\n const errors: string[] = []\n\n if (!normalizedEmail) {\n errors.push('Email address is required. Pass it as the `<email>` argument.')\n }\n if (!selectedRole) {\n errors.push('User role is required. Pass it with `--role <role>`.')\n }\n\n if (errors.length > 0) {\n this.error(formatCliErrorMessages(errors), {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n }\n\n if (selectedEmail !== undefined) {\n const validation = validateEmail(normalizedEmail ?? '')\n if (validation !== true) this.error(validation, {exit: exitCodes.USAGE_ERROR})\n }\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.roles'},\n {grant: 'invite', permission: 'sanity.project.members'},\n ],\n }),\n })\n\n let roles: Role[]\n try {\n roles = (await getProjectRoles(projectId)).filter((role) => role.appliesToUsers)\n } catch (error) {\n usersInviteDebug('Error fetching roles', error)\n this.error('Error fetching roles', {exit: 1})\n }\n\n const email = normalizedEmail || (await this.promptForEmail())\n const roleSelection = selectedRole || (await this.promptForRole(roles))\n const role = roles.find(({name}) => name.toLowerCase() === roleSelection.toLowerCase())\n\n if (!role) {\n this.error(\n `Role name \"${roleSelection}\" not found. Available roles: ${roles.map((r) => r.name).join(', ')}`,\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n try {\n await inviteUser({email, projectId, role: role.name})\n\n this.log(`Invitation sent to ${email}`)\n } catch (error) {\n usersInviteDebug(`Error inviting user`, error)\n if ((error as Error & {statusCode: number}).statusCode === 402) {\n this.error(QUOTA_ERROR_MESSAGE, {exit: 1})\n }\n\n this.error(`Error inviting user`, {exit: 1})\n }\n }\n\n private async promptForEmail(): Promise<string> {\n return input({\n message: 'Email to invite:',\n transformer: (val: string) => val.trim(),\n validate: validateEmail,\n })\n }\n\n private async promptForRole(roles: Role[]): Promise<string> {\n return select({\n choices: roles.map((role) => ({\n name: `${role.title} (${role.description || 'No description'})`,\n value: role.name,\n })),\n message: 'Which role should the user have?',\n })\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","input","select","validateEmail","promptForProject","getProjectRoles","inviteUser","formatCliErrorMessages","getProjectIdFlag","QUOTA_ERROR_MESSAGE","usersInviteDebug","UsersInviteCommand","args","email","string","description","required","examples","command","flags","semantics","role","hiddenAliases","run","selectedEmail","selectedRole","normalizedEmail","trim","isUnattended","errors","push","length","error","exit","USAGE_ERROR","undefined","validation","projectId","getProjectId","fallback","requiredPermissions","grant","permission","roles","filter","appliesToUsers","promptForEmail","roleSelection","promptForRole","find","name","toLowerCase","map","r","join","log","statusCode","message","transformer","val","validate","choices","title","value"],"mappings":"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"}