@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,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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"}
@@ -34,6 +34,9 @@
34
34
  hooks: [
35
35
  'hook'
36
36
  ],
37
+ migrations: [
38
+ 'migration'
39
+ ],
37
40
  organizations: [
38
41
  'organization',
39
42
  'organisations',
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/topicAliases.ts"],"sourcesContent":["/**\n * Topic Alias Configuration\n *\n * Maps canonical topic names to their aliases. Used by:\n * - The command_not_found hook (runtime alias resolution)\n * - The check-topic-aliases script (build-time validation)\n *\n * Every topic with aliases must be listed here. The key is the canonical\n * topic name (the actual directory under commands/), and values are\n * alternative names that should resolve to it.\n *\n * Convention: prefer plural canonical names with singular aliases.\n * datasets: ['dataset'] - \"sanity dataset create\" works\n * cors: [] - no alias needed (no natural singular/plural pair)\n *\n * While the config technically supports any direction, keeping it consistent\n * (plural to singular) makes the CLI predictable for users.\n */\nexport const topicAliases: Record<string, string[]> = {\n backups: ['backup'],\n blueprints: ['blueprint'],\n datasets: ['dataset'],\n documents: ['document'],\n functions: ['function'],\n hooks: ['hook'],\n organizations: ['organization', 'organisations', 'organisation', 'org', 'orgs'],\n projects: ['project'],\n schemas: ['schema'],\n tokens: ['token'],\n users: ['user'],\n}\n"],"names":["topicAliases","backups","blueprints","datasets","documents","functions","hooks","organizations","projects","schemas","tokens","users"],"mappings":"AAAA;;;;;;;;;;;;;;;;;CAiBC,GACD,OAAO,MAAMA,eAAyC;IACpDC,SAAS;QAAC;KAAS;IACnBC,YAAY;QAAC;KAAY;IACzBC,UAAU;QAAC;KAAU;IACrBC,WAAW;QAAC;KAAW;IACvBC,WAAW;QAAC;KAAW;IACvBC,OAAO;QAAC;KAAO;IACfC,eAAe;QAAC;QAAgB;QAAiB;QAAgB;QAAO;KAAO;IAC/EC,UAAU;QAAC;KAAU;IACrBC,SAAS;QAAC;KAAS;IACnBC,QAAQ;QAAC;KAAQ;IACjBC,OAAO;QAAC;KAAO;AACjB,EAAC"}
1
+ {"version":3,"sources":["../src/topicAliases.ts"],"sourcesContent":["/**\n * Topic Alias Configuration\n *\n * Maps canonical topic names to their aliases. Used by:\n * - The command_not_found hook (runtime alias resolution)\n * - The check-topic-aliases script (build-time validation)\n *\n * Every topic with aliases must be listed here. The key is the canonical\n * topic name (the actual directory under commands/), and values are\n * alternative names that should resolve to it.\n *\n * Convention: prefer plural canonical names with singular aliases.\n * datasets: ['dataset'] - \"sanity dataset create\" works\n * cors: [] - no alias needed (no natural singular/plural pair)\n *\n * While the config technically supports any direction, keeping it consistent\n * (plural to singular) makes the CLI predictable for users.\n */\nexport const topicAliases: Record<string, string[]> = {\n backups: ['backup'],\n blueprints: ['blueprint'],\n datasets: ['dataset'],\n documents: ['document'],\n functions: ['function'],\n hooks: ['hook'],\n migrations: ['migration'],\n organizations: ['organization', 'organisations', 'organisation', 'org', 'orgs'],\n projects: ['project'],\n schemas: ['schema'],\n tokens: ['token'],\n users: ['user'],\n}\n"],"names":["topicAliases","backups","blueprints","datasets","documents","functions","hooks","migrations","organizations","projects","schemas","tokens","users"],"mappings":"AAAA;;;;;;;;;;;;;;;;;CAiBC,GACD,OAAO,MAAMA,eAAyC;IACpDC,SAAS;QAAC;KAAS;IACnBC,YAAY;QAAC;KAAY;IACzBC,UAAU;QAAC;KAAU;IACrBC,WAAW;QAAC;KAAW;IACvBC,WAAW;QAAC;KAAW;IACvBC,OAAO;QAAC;KAAO;IACfC,YAAY;QAAC;KAAY;IACzBC,eAAe;QAAC;QAAgB;QAAiB;QAAgB;QAAO;KAAO;IAC/EC,UAAU;QAAC;KAAU;IACrBC,SAAS;QAAC;KAAS;IACnBC,QAAQ;QAAC;KAAQ;IACjBC,OAAO;QAAC;KAAO;AACjB,EAAC"}
@@ -0,0 +1,5 @@
1
+ export function formatCliErrorMessages(messages) {
2
+ return messages.join('\nError: ');
3
+ }
4
+
5
+ //# sourceMappingURL=formatCliErrorMessages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/util/formatCliErrorMessages.ts"],"sourcesContent":["export function formatCliErrorMessages(messages: readonly string[]): string {\n return messages.join('\\nError: ')\n}\n"],"names":["formatCliErrorMessages","messages","join"],"mappings":"AAAA,OAAO,SAASA,uBAAuBC,QAA2B;IAChE,OAAOA,SAASC,IAAI,CAAC;AACvB"}
package/oclif.config.js CHANGED
@@ -12,7 +12,10 @@ export default {
12
12
  './dist/hooks/prerun/warnings.js',
13
13
  ],
14
14
  },
15
- plugins: ['@oclif/plugin-help', '@sanity/runtime-cli', '@sanity/migrate', '@sanity/codegen'],
15
+ // Note: do not add '@sanity/migrate' here. The `migrations` commands now ship
16
+ // natively (see commands/migrations/); re-adding the plugin would register
17
+ // duplicate command ids.
18
+ plugins: ['@oclif/plugin-help', '@sanity/runtime-cli', '@sanity/codegen'],
16
19
  topics: {
17
20
  backups: {description: 'Manage dataset backups'},
18
21
  cors: {description: 'Manage CORS origins for your project'},
@@ -24,6 +27,7 @@ export default {
24
27
  manifest: {description: 'Extract studio configuration as JSON manifests'},
25
28
  mcp: {description: 'Configure Sanity MCP server for AI agents'},
26
29
  media: {description: 'Manage media assets and aspect definitions'},
30
+ migrations: {description: 'Run and manage content migrations'},
27
31
  openapi: {description: 'Manage OpenAPI specifications'},
28
32
  organizations: {description: 'Manage your organizations'},
29
33
  projects: {description: 'Manage Sanity projects'},