@sanity/cli 7.15.1 → 7.17.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 (128) hide show
  1. package/README.md +87 -0
  2. package/dist/actions/build/shouldAutoUpdate.js +28 -5
  3. package/dist/actions/build/shouldAutoUpdate.js.map +1 -1
  4. package/dist/actions/deploy/deployApp.js +4 -8
  5. package/dist/actions/deploy/deployApp.js.map +1 -1
  6. package/dist/actions/deploy/deployChecks.js +49 -31
  7. package/dist/actions/deploy/deployChecks.js.map +1 -1
  8. package/dist/actions/deploy/deployRunner.js +1 -1
  9. package/dist/actions/deploy/deployRunner.js.map +1 -1
  10. package/dist/actions/deploy/deployStudio.js +12 -10
  11. package/dist/actions/deploy/deployStudio.js.map +1 -1
  12. package/dist/actions/deploy/deploymentPlan.js +2 -2
  13. package/dist/actions/deploy/deploymentPlan.js.map +1 -1
  14. package/dist/actions/deploy/resolveDeployTarget.js +13 -13
  15. package/dist/actions/deploy/resolveDeployTarget.js.map +1 -1
  16. package/dist/actions/init/bootstrapLocalTemplate.js +2 -3
  17. package/dist/actions/init/bootstrapLocalTemplate.js.map +1 -1
  18. package/dist/actions/init/createAppCliConfig.js.map +1 -1
  19. package/dist/actions/init/createCliConfig.js.map +1 -1
  20. package/dist/actions/init/initNextJs.js +1 -1
  21. package/dist/actions/init/initNextJs.js.map +1 -1
  22. package/dist/actions/init/initStudio.js +10 -6
  23. package/dist/actions/init/initStudio.js.map +1 -1
  24. package/dist/actions/init/templates/appSanityUi.js +1 -1
  25. package/dist/actions/init/templates/appSanityUi.js.map +1 -1
  26. package/dist/actions/init/templates/getStarted.js +2 -2
  27. package/dist/actions/init/templates/getStarted.js.map +1 -1
  28. package/dist/actions/init/templates/nextjs/schemaTypes/blog.js +4 -4
  29. package/dist/actions/init/templates/nextjs/schemaTypes/blog.js.map +1 -1
  30. package/dist/actions/init/templates/shopify.js +5 -5
  31. package/dist/actions/init/templates/shopify.js.map +1 -1
  32. package/dist/actions/init/templates/shopifyOnline.js +3 -3
  33. package/dist/actions/init/templates/shopifyOnline.js.map +1 -1
  34. package/dist/actions/manifest/extractCoreAppManifest.js +7 -7
  35. package/dist/actions/manifest/extractCoreAppManifest.js.map +1 -1
  36. package/dist/actions/scaffold/createFrontend.js +107 -0
  37. package/dist/actions/scaffold/createFrontend.js.map +1 -0
  38. package/dist/actions/scaffold/scaffoldProject.js +202 -0
  39. package/dist/actions/scaffold/scaffoldProject.js.map +1 -0
  40. package/dist/commands/backups/list.js +1 -1
  41. package/dist/commands/backups/list.js.map +1 -1
  42. package/dist/commands/migrations/list.js +1 -1
  43. package/dist/commands/migrations/list.js.map +1 -1
  44. package/dist/commands/migrations/run.js +1 -1
  45. package/dist/commands/migrations/run.js.map +1 -1
  46. package/dist/commands/new.js +309 -0
  47. package/dist/commands/new.js.map +1 -0
  48. package/dist/commands/organizations/list.js +1 -1
  49. package/dist/commands/organizations/list.js.map +1 -1
  50. package/dist/commands/projects/unclaimed.js +205 -0
  51. package/dist/commands/projects/unclaimed.js.map +1 -0
  52. package/dist/commands/tokens/list.js +1 -1
  53. package/dist/commands/tokens/list.js.map +1 -1
  54. package/dist/commands/users/list.js +5 -1
  55. package/dist/commands/users/list.js.map +1 -1
  56. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js +4 -0
  57. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js.map +1 -1
  58. package/dist/exports/runtime.d.ts +24 -0
  59. package/dist/exports/runtime.js.map +1 -1
  60. package/dist/services/mintProject.js +91 -0
  61. package/dist/services/mintProject.js.map +1 -0
  62. package/dist/services/projects.js +19 -0
  63. package/dist/services/projects.js.map +1 -1
  64. package/dist/util/formatClaimDeadline.js +15 -0
  65. package/dist/util/formatClaimDeadline.js.map +1 -0
  66. package/dist/util/mintProjectConstants.js +6 -0
  67. package/dist/util/mintProjectConstants.js.map +1 -0
  68. package/dist/util/newCommandSplash.js +27 -0
  69. package/dist/util/newCommandSplash.js.map +1 -0
  70. package/dist/util/packageManager/installPackages.js +57 -28
  71. package/dist/util/packageManager/installPackages.js.map +1 -1
  72. package/dist/util/unclaimedProjects.js +105 -0
  73. package/dist/util/unclaimedProjects.js.map +1 -0
  74. package/oclif.config.js +1 -0
  75. package/oclif.manifest.json +111 -1
  76. package/package.json +6 -6
  77. package/templates/get-started/plugins/sanity-plugin-tutorial/GetStartedTutorial.tsx +1 -1
  78. package/templates/shopify/components/inputs/CollectionHidden.tsx +1 -1
  79. package/templates/shopify/components/inputs/ProductHidden.tsx +1 -1
  80. package/templates/shopify/components/inputs/ProductVariantHidden.tsx +1 -1
  81. package/templates/shopify/components/inputs/ProxyString.tsx +1 -1
  82. package/templates/shopify/components/media/ShopifyDocumentStatus.tsx +3 -1
  83. package/templates/shopify/constants.ts +3 -1
  84. package/templates/shopify/plugins/customDocumentActions/shopifyDelete.tsx +1 -1
  85. package/templates/shopify/plugins/customDocumentActions/shopifyLink.ts +1 -1
  86. package/templates/shopify/schemaTypes/documents/collection.tsx +1 -1
  87. package/templates/shopify/schemaTypes/documents/colorTheme.tsx +1 -1
  88. package/templates/shopify/schemaTypes/documents/page.ts +1 -1
  89. package/templates/shopify/schemaTypes/documents/product.tsx +1 -1
  90. package/templates/shopify/schemaTypes/documents/productVariant.tsx +1 -1
  91. package/templates/shopify/schemaTypes/objects/collection/collectionGroupType.ts +1 -1
  92. package/templates/shopify/schemaTypes/objects/hotspot/imageWithProductHotspotsType.ts +1 -1
  93. package/templates/shopify/schemaTypes/objects/link/linkEmailType.tsx +1 -1
  94. package/templates/shopify/schemaTypes/objects/link/linkExternalType.tsx +1 -1
  95. package/templates/shopify/schemaTypes/objects/link/linkInternalType.tsx +1 -1
  96. package/templates/shopify/schemaTypes/objects/link/linkProductType.tsx +1 -1
  97. package/templates/shopify/schemaTypes/objects/module/accordionType.ts +1 -1
  98. package/templates/shopify/schemaTypes/objects/module/callToActionType.tsx +2 -1
  99. package/templates/shopify/schemaTypes/objects/module/calloutType.ts +1 -1
  100. package/templates/shopify/schemaTypes/objects/module/collectionReferenceType.tsx +1 -1
  101. package/templates/shopify/schemaTypes/objects/module/gridType.ts +1 -1
  102. package/templates/shopify/schemaTypes/objects/module/imageFeatureType.ts +1 -1
  103. package/templates/shopify/schemaTypes/objects/module/imageFeaturesType.tsx +1 -1
  104. package/templates/shopify/schemaTypes/objects/module/instagramType.ts +1 -1
  105. package/templates/shopify/schemaTypes/objects/module/productFeaturesType.tsx +1 -1
  106. package/templates/shopify/schemaTypes/objects/module/productReferenceType.tsx +1 -1
  107. package/templates/shopify/schemaTypes/objects/shopify/collectionRuleType.tsx +1 -1
  108. package/templates/shopify/schemaTypes/objects/shopify/optionType.tsx +1 -1
  109. package/templates/shopify/schemaTypes/objects/shopify/productWithVariantType.tsx +1 -1
  110. package/templates/shopify/schemaTypes/singletons/homeType.ts +1 -1
  111. package/templates/shopify/schemaTypes/singletons/settingsType.ts +5 -1
  112. package/templates/shopify/structure/pageStructure.ts +1 -1
  113. package/templates/shopify/structure/productStructure.ts +1 -1
  114. package/templates/shopify-online-storefront/components/inputs/CollectionHidden.tsx +1 -1
  115. package/templates/shopify-online-storefront/components/inputs/ProductHidden.tsx +1 -1
  116. package/templates/shopify-online-storefront/components/inputs/ProductVariantHidden.tsx +1 -1
  117. package/templates/shopify-online-storefront/components/inputs/ProxyString.tsx +1 -1
  118. package/templates/shopify-online-storefront/components/media/ShopifyDocumentStatus.tsx +3 -1
  119. package/templates/shopify-online-storefront/plugins/shopifyDocumentActions/shopifyDelete.tsx +1 -1
  120. package/templates/shopify-online-storefront/plugins/shopifyDocumentActions/shopifyLink.ts +1 -1
  121. package/templates/shopify-online-storefront/schemaTypes/documents/collection.tsx +1 -1
  122. package/templates/shopify-online-storefront/schemaTypes/documents/product.tsx +1 -1
  123. package/templates/shopify-online-storefront/schemaTypes/documents/productVariant.tsx +1 -1
  124. package/templates/shopify-online-storefront/schemaTypes/objects/accordion.ts +1 -1
  125. package/templates/shopify-online-storefront/schemaTypes/objects/callout.ts +1 -1
  126. package/templates/shopify-online-storefront/schemaTypes/objects/option.ts +1 -1
  127. package/templates/shopify-online-storefront/schemaTypes/objects/shopifyCollectionRule.tsx +1 -1
  128. package/templates/shopify-online-storefront/structure/productStructure.ts +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/util/packageManager/installPackages.ts"],"sourcesContent":["import {type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {execa, type Options} from 'execa'\n\nimport {getPartialEnvWithNpmPath, type PackageManager} from './packageManagerChoice.js'\n\ntype PackageManagerLibs = Exclude<PackageManager, 'manual'>\n\n/**\n * @internal\n */\ninterface InstallOptions {\n packageManager: PackageManager\n packages: string[]\n}\n\ninterface PackageManagerCommands {\n add: {[key in PackageManagerLibs]: (packages: string[]) => string[]}\n install: {[key in PackageManagerLibs]: string[]}\n}\n\nconst PACKAGE_MANAGER_COMMANDS: PackageManagerCommands = {\n add: {\n bun: (packages) => ['add', ...packages],\n npm: (packages) => ['install', '--save', ...packages],\n pnpm: (packages) => ['add', '--save-prod', ...packages],\n yarn: (packages) => ['add', ...packages],\n },\n install: {\n bun: ['install'],\n npm: ['install'],\n pnpm: ['install'],\n yarn: ['install'],\n },\n}\n\nconst IGNORED_BUILDS_NOTICE =\n 'pnpm skipped build scripts for some dependencies. Run \"pnpm approve-builds\" in the project directory to pick which dependencies should be allowed to run scripts.'\n\n// Matches pnpm's `ERR_PNPM_IGNORED_BUILDS` error against whitespace-normalized\n// output (pnpm may wrap the message), capturing the list of skipped packages,\n// eg `esbuild@0.25.0, sharp@0.34.0.` - the `<pkg>@<version>` token sequence\n// ends the capture where the trailing `Run \"pnpm approve-builds\"…` hint starts\nconst IGNORED_BUILDS_PATTERN =\n /ERR_PNPM_IGNORED_BUILDS.*?Ignored build scripts: ?((?:[^\\s,]+@[^\\s,]+[, ]*)+)/\n\nfunction getIgnoredBuildScripts(commandOutput: string): string[] | undefined {\n const match = commandOutput.replaceAll(/\\s+/g, ' ').match(IGNORED_BUILDS_PATTERN)\n if (!match) {\n return undefined\n }\n\n // The capture allows both comma and whitespace separators (pnpm may print\n // either, and wrapping can drop the comma), so split on both.\n return match[1]\n .split(/[\\s,]+/)\n .map((entry) => entry.replace(/\\.$/, ''))\n .filter(Boolean)\n}\n\nfunction isEsbuild(ignoredEntry: string): boolean {\n // Entries are on the form `<pkg-name>@<version>` - strip the version,\n // keeping in mind that scoped package names also start with `@`\n return ignoredEntry.replace(/@[^@]+$/, '') === 'esbuild'\n}\n\nasync function executePackageManagerCommand(\n packageManager: PackageManagerLibs,\n args: string[],\n execOptions: Options,\n output: Output,\n errorMessage: string,\n): Promise<void> {\n const progress = spinner(`Running ${packageManager} ${args.join(' ')}\\n`).start()\n\n const result = await execa(packageManager, args, execOptions)\n\n if (result?.exitCode || result?.failed) {\n // pnpm exits non-zero if dependency build scripts were skipped, even though\n // the install itself succeeded. Treat it as a success, but point to\n // `pnpm approve-builds` if anything other than esbuild was skipped\n // (esbuild works without its build script through a JS fallback).\n const commandOutput = [result.stdout, result.stderr]\n .filter((chunk): chunk is string => typeof chunk === 'string')\n .join('\\n')\n const ignoredBuilds =\n packageManager === 'pnpm' ? getIgnoredBuildScripts(commandOutput) : undefined\n\n if (ignoredBuilds) {\n progress.succeed()\n if (ignoredBuilds.some((entry) => !isEsbuild(entry))) {\n output.warn(IGNORED_BUILDS_NOTICE)\n }\n return\n }\n\n progress.fail()\n // Log both streams - package managers often print the actionable error\n // details to stderr, so logging stdout alone can hide the failure reason.\n output.log(commandOutput)\n output.error(errorMessage, {exit: 1})\n } else {\n progress.succeed()\n }\n}\n\nexport async function installDeclaredPackages(\n cwd: string,\n packageManager: PackageManager,\n context: {output: Output; workDir: string},\n): Promise<void> {\n const {output} = context\n const execOptions: Options = {\n cwd,\n encoding: 'utf8',\n env: getPartialEnvWithNpmPath(cwd),\n reject: false,\n stdio: 'pipe',\n }\n\n if (packageManager === 'manual') {\n const npmCommand = PACKAGE_MANAGER_COMMANDS.install.npm\n output.log(`Manual installation selected — run 'npm ${npmCommand.join(' ')}' or equivalent`)\n } else {\n const args = PACKAGE_MANAGER_COMMANDS.install[packageManager]\n await executePackageManagerCommand(\n packageManager,\n args,\n execOptions,\n output,\n 'Dependency installation failed',\n )\n }\n}\n\nexport async function installNewPackages(\n options: InstallOptions,\n context: {output: Output; workDir: string},\n): Promise<void> {\n const {packageManager, packages} = options\n const {output, workDir} = context\n const execOptions: Options = {\n cwd: workDir,\n encoding: 'utf8',\n env: getPartialEnvWithNpmPath(workDir),\n reject: false,\n stdio: 'pipe',\n }\n\n if (packageManager === 'manual') {\n const npmCommand = PACKAGE_MANAGER_COMMANDS.add.npm(packages)\n output.log(`Manual installation selected - run 'npm ${npmCommand.join(' ')}' or equivalent`)\n } else {\n const args = PACKAGE_MANAGER_COMMANDS.add[packageManager](packages)\n await executePackageManagerCommand(\n packageManager,\n args,\n execOptions,\n output,\n 'Package installation failed',\n )\n }\n}\n"],"names":["spinner","execa","getPartialEnvWithNpmPath","PACKAGE_MANAGER_COMMANDS","add","bun","packages","npm","pnpm","yarn","install","IGNORED_BUILDS_NOTICE","IGNORED_BUILDS_PATTERN","getIgnoredBuildScripts","commandOutput","match","replaceAll","undefined","split","map","entry","replace","filter","Boolean","isEsbuild","ignoredEntry","executePackageManagerCommand","packageManager","args","execOptions","output","errorMessage","progress","join","start","result","exitCode","failed","stdout","stderr","chunk","ignoredBuilds","succeed","some","warn","fail","log","error","exit","installDeclaredPackages","cwd","context","encoding","env","reject","stdio","npmCommand","installNewPackages","options","workDir"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/util/packageManager/installPackages.ts"],"sourcesContent":["import {CLIError} from '@oclif/core/errors'\nimport {exitCodes, type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {execa, type Options} from 'execa'\n\nimport {getPartialEnvWithNpmPath, type PackageManager} from './packageManagerChoice.js'\n\ntype PackageManagerLibs = Exclude<PackageManager, 'manual'>\n\n/**\n * @internal\n */\ninterface InstallOptions {\n packageManager: PackageManager\n packages: string[]\n}\n\ninterface PackageManagerCommands {\n add: {[key in PackageManagerLibs]: (packages: string[]) => string[]}\n install: {[key in PackageManagerLibs]: string[]}\n}\n\nconst PACKAGE_MANAGER_COMMANDS: PackageManagerCommands = {\n add: {\n bun: (packages) => ['add', ...packages],\n npm: (packages) => ['install', '--save', ...packages],\n pnpm: (packages) => ['add', '--save-prod', ...packages],\n yarn: (packages) => ['add', ...packages],\n },\n install: {\n bun: ['install'],\n npm: ['install'],\n pnpm: ['install'],\n yarn: ['install'],\n },\n}\n\nconst IGNORED_BUILDS_NOTICE =\n 'pnpm skipped build scripts for some dependencies. Run \"pnpm approve-builds\" in the project directory to pick which dependencies should be allowed to run scripts.'\n\n// Matches pnpm's `ERR_PNPM_IGNORED_BUILDS` error against whitespace-normalized\n// output (pnpm may wrap the message), capturing the list of skipped packages,\n// eg `esbuild@0.25.0, sharp@0.34.0.` - the `<pkg>@<version>` token sequence\n// ends the capture where the trailing `Run \"pnpm approve-builds\"…` hint starts\nconst IGNORED_BUILDS_PATTERN =\n /ERR_PNPM_IGNORED_BUILDS.*?Ignored build scripts: ?((?:[^\\s,]+@[^\\s,]+[, ]*)+)/\n\nfunction getIgnoredBuildScripts(commandOutput: string): string[] | undefined {\n const match = commandOutput.replaceAll(/\\s+/g, ' ').match(IGNORED_BUILDS_PATTERN)\n if (!match) {\n return undefined\n }\n\n // The capture allows both comma and whitespace separators (pnpm may print\n // either, and wrapping can drop the comma), so split on both.\n return match[1]\n .split(/[\\s,]+/)\n .map((entry) => entry.replace(/\\.$/, ''))\n .filter(Boolean)\n}\n\nfunction isEsbuild(ignoredEntry: string): boolean {\n // Entries are on the form `<pkg-name>@<version>` - strip the version,\n // keeping in mind that scoped package names also start with `@`\n return ignoredEntry.replace(/@[^@]+$/, '') === 'esbuild'\n}\n\nasync function executePackageManagerCommand(\n packageManager: PackageManagerLibs,\n args: string[],\n execOptions: Options,\n output: Output,\n errorMessage: string,\n): Promise<void> {\n const progress = spinner({\n discardStdin: !execOptions.cancelSignal,\n text: `Running ${packageManager} ${args.join(' ')}\\n`,\n }).start()\n let progressSettled = false\n const fail = () => {\n progressSettled = true\n progress.fail()\n }\n const succeed = () => {\n progressSettled = true\n progress.succeed()\n }\n\n try {\n const result = await execa(packageManager, args, execOptions)\n\n if (execOptions.cancelSignal?.aborted) {\n fail()\n execOptions.cancelSignal.throwIfAborted()\n }\n\n if (result?.exitCode || result?.failed) {\n // pnpm exits non-zero if dependency build scripts were skipped, even though\n // the install itself succeeded. Treat it as a success, but point to\n // `pnpm approve-builds` if anything other than esbuild was skipped\n // (esbuild works without its build script through a JS fallback).\n const commandOutput = [result.stdout, result.stderr]\n .filter((chunk): chunk is string => typeof chunk === 'string')\n .join('\\n')\n const ignoredBuilds =\n packageManager === 'pnpm' ? getIgnoredBuildScripts(commandOutput) : undefined\n\n if (ignoredBuilds) {\n succeed()\n if (ignoredBuilds.some((entry) => !isEsbuild(entry))) {\n output.warn(IGNORED_BUILDS_NOTICE)\n }\n return\n }\n\n fail()\n // Log both streams - package managers often print the actionable error\n // details to stderr, so logging stdout alone can hide the failure reason.\n output.log(commandOutput)\n throw new CLIError(errorMessage, {exit: exitCodes.RUNTIME_ERROR})\n } else {\n succeed()\n }\n } catch (err) {\n if (!progressSettled) {\n fail()\n }\n throw err\n } finally {\n progress.stop()\n }\n}\n\nexport async function installDeclaredPackages(\n cwd: string,\n packageManager: PackageManager,\n context: {output: Output; workDir: string},\n): Promise<void> {\n const {output} = context\n const execOptions: Options = {\n cwd,\n encoding: 'utf8',\n env: getPartialEnvWithNpmPath(cwd),\n reject: false,\n stdio: 'pipe',\n }\n\n if (packageManager === 'manual') {\n const npmCommand = PACKAGE_MANAGER_COMMANDS.install.npm\n output.log(`Manual installation selected — run 'npm ${npmCommand.join(' ')}' or equivalent`)\n } else {\n const args = PACKAGE_MANAGER_COMMANDS.install[packageManager]\n await executePackageManagerCommand(\n packageManager,\n args,\n execOptions,\n output,\n 'Dependency installation failed',\n )\n }\n}\n\nexport async function installNewPackages(\n options: InstallOptions,\n context: {cancelSignal?: AbortSignal; output: Output; timeout?: number; workDir: string},\n): Promise<void> {\n const {packageManager, packages} = options\n const {cancelSignal, output, timeout, workDir} = context\n const execOptions: Options = {\n cancelSignal,\n cwd: workDir,\n encoding: 'utf8',\n env: getPartialEnvWithNpmPath(workDir),\n reject: false,\n stdio: 'pipe',\n timeout,\n }\n\n if (packageManager === 'manual') {\n const npmCommand = PACKAGE_MANAGER_COMMANDS.add.npm(packages)\n output.log(`Manual installation selected - run 'npm ${npmCommand.join(' ')}' or equivalent`)\n } else {\n const args = PACKAGE_MANAGER_COMMANDS.add[packageManager](packages)\n await executePackageManagerCommand(\n packageManager,\n args,\n execOptions,\n output,\n 'Package installation failed',\n )\n }\n}\n"],"names":["CLIError","exitCodes","spinner","execa","getPartialEnvWithNpmPath","PACKAGE_MANAGER_COMMANDS","add","bun","packages","npm","pnpm","yarn","install","IGNORED_BUILDS_NOTICE","IGNORED_BUILDS_PATTERN","getIgnoredBuildScripts","commandOutput","match","replaceAll","undefined","split","map","entry","replace","filter","Boolean","isEsbuild","ignoredEntry","executePackageManagerCommand","packageManager","args","execOptions","output","errorMessage","progress","discardStdin","cancelSignal","text","join","start","progressSettled","fail","succeed","result","aborted","throwIfAborted","exitCode","failed","stdout","stderr","chunk","ignoredBuilds","some","warn","log","exit","RUNTIME_ERROR","err","stop","installDeclaredPackages","cwd","context","encoding","env","reject","stdio","npmCommand","installNewPackages","options","timeout","workDir"],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ import { getUserConfig } from '@sanity/cli-core/config';
2
+ import { subdebug } from '@sanity/cli-core/debug';
3
+ const debug = subdebug('projects:unclaimed:registry');
4
+ export const UNCLAIMED_PROJECTS_CONFIG_KEY = 'unclaimedProjects';
5
+ function asRecord(value) {
6
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
7
+ }
8
+ /**
9
+ * Record a mint response in the passive, project-ID-keyed user-config registry.
10
+ *
11
+ * This storage is deliberately not connected to CLI credential resolution.
12
+ */ export function recordUnclaimedProject(minted, mintedAt = new Date().toISOString()) {
13
+ try {
14
+ const config = getUserConfig();
15
+ const records = asRecord(config.get(UNCLAIMED_PROJECTS_CONFIG_KEY));
16
+ const record = {
17
+ claimToken: minted.claimToken,
18
+ claimUrl: minted.claimUrl,
19
+ dataset: minted.datasetName,
20
+ expiresAt: minted.expiresAt,
21
+ mintedAt,
22
+ projectId: minted.resourceId,
23
+ token: minted.token
24
+ };
25
+ config.set(UNCLAIMED_PROJECTS_CONFIG_KEY, {
26
+ ...records,
27
+ [minted.resourceId]: record
28
+ });
29
+ return true;
30
+ } catch (error) {
31
+ debug('failed to record unclaimed project: %s', error instanceof Error ? error.message : `${error}`);
32
+ return false;
33
+ }
34
+ }
35
+ function requireString(value, key, projectId) {
36
+ const field = value[key];
37
+ if (typeof field !== 'string' || field.length === 0) {
38
+ throw new Error(`Local recovery record for project "${projectId}" is malformed: "${key}" is missing or invalid.`);
39
+ }
40
+ return field;
41
+ }
42
+ function parseRecord(projectId, value) {
43
+ const record = asRecord(value);
44
+ const parsed = {
45
+ claimToken: requireString(record, 'claimToken', projectId),
46
+ claimUrl: requireString(record, 'claimUrl', projectId),
47
+ dataset: requireString(record, 'dataset', projectId),
48
+ expiresAt: requireString(record, 'expiresAt', projectId),
49
+ mintedAt: requireString(record, 'mintedAt', projectId),
50
+ projectId: requireString(record, 'projectId', projectId),
51
+ token: requireString(record, 'token', projectId)
52
+ };
53
+ if (parsed.projectId !== projectId) {
54
+ throw new Error(`Local recovery record for project "${projectId}" is malformed: its project ID does not match.`);
55
+ }
56
+ let claimUrlValid = false;
57
+ try {
58
+ const protocol = new URL(parsed.claimUrl).protocol;
59
+ claimUrlValid = protocol === 'https:' || protocol === 'http:';
60
+ } catch {
61
+ // The actionable error is reported below.
62
+ }
63
+ if (!claimUrlValid) {
64
+ throw new TypeError(`Local recovery record for project "${projectId}" is malformed: "claimUrl" is not a valid URL.`);
65
+ }
66
+ if (!Number.isFinite(Date.parse(parsed.mintedAt))) {
67
+ throw new TypeError(`Local recovery record for project "${projectId}" is malformed: "mintedAt" is not a valid date.`);
68
+ }
69
+ if (!Number.isFinite(Date.parse(parsed.expiresAt))) {
70
+ throw new TypeError(`Local recovery record for project "${projectId}" is malformed: "expiresAt" is not a valid date.`);
71
+ }
72
+ return parsed;
73
+ }
74
+ /**
75
+ * Read and validate the passive local recovery registry without changing it.
76
+ */ export function readUnclaimedProjects() {
77
+ const stored = getUserConfig().get(UNCLAIMED_PROJECTS_CONFIG_KEY);
78
+ if (stored === undefined) return [];
79
+ if (!stored || typeof stored !== 'object' || Array.isArray(stored)) {
80
+ throw new Error('The local unclaimed-project registry is malformed.');
81
+ }
82
+ return Object.entries(stored).map(([projectId, record])=>parseRecord(projectId, record)).toSorted((left, right)=>Date.parse(left.expiresAt) - Date.parse(right.expiresAt));
83
+ }
84
+ /**
85
+ * Remove a record only when its claim token still matches the record that was checked.
86
+ */ export function removeUnclaimedProject(projectId, expectedClaimToken) {
87
+ try {
88
+ const config = getUserConfig();
89
+ const records = asRecord(config.get(UNCLAIMED_PROJECTS_CONFIG_KEY));
90
+ const current = asRecord(records[projectId]);
91
+ if (current.claimToken !== expectedClaimToken) return false;
92
+ const { [projectId]: _, ...remaining } = records;
93
+ if (Object.keys(remaining).length === 0) {
94
+ config.delete(UNCLAIMED_PROJECTS_CONFIG_KEY);
95
+ } else {
96
+ config.set(UNCLAIMED_PROJECTS_CONFIG_KEY, remaining);
97
+ }
98
+ return true;
99
+ } catch (error) {
100
+ debug('failed to remove unclaimed project: %s', error instanceof Error ? error.message : `${error}`);
101
+ return false;
102
+ }
103
+ }
104
+
105
+ //# sourceMappingURL=unclaimedProjects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/util/unclaimedProjects.ts"],"sourcesContent":["import {getUserConfig} from '@sanity/cli-core/config'\nimport {subdebug} from '@sanity/cli-core/debug'\n\nimport {type MintedProject} from '../services/mintProject.js'\n\nconst debug = subdebug('projects:unclaimed:registry')\n\nexport const UNCLAIMED_PROJECTS_CONFIG_KEY = 'unclaimedProjects'\n\nexport interface UnclaimedProjectRecord {\n claimToken: string\n claimUrl: string\n dataset: string\n expiresAt: string\n mintedAt: string\n projectId: string\n token: string\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {}\n}\n\n/**\n * Record a mint response in the passive, project-ID-keyed user-config registry.\n *\n * This storage is deliberately not connected to CLI credential resolution.\n */\nexport function recordUnclaimedProject(\n minted: MintedProject,\n mintedAt: string = new Date().toISOString(),\n): boolean {\n try {\n const config = getUserConfig()\n const records = asRecord(config.get(UNCLAIMED_PROJECTS_CONFIG_KEY))\n const record: UnclaimedProjectRecord = {\n claimToken: minted.claimToken,\n claimUrl: minted.claimUrl,\n dataset: minted.datasetName,\n expiresAt: minted.expiresAt,\n mintedAt,\n projectId: minted.resourceId,\n token: minted.token,\n }\n\n config.set(UNCLAIMED_PROJECTS_CONFIG_KEY, {\n ...records,\n [minted.resourceId]: record,\n })\n return true\n } catch (error) {\n debug(\n 'failed to record unclaimed project: %s',\n error instanceof Error ? error.message : `${error}`,\n )\n return false\n }\n}\n\nfunction requireString(\n value: Record<string, unknown>,\n key: keyof UnclaimedProjectRecord,\n projectId: string,\n): string {\n const field = value[key]\n if (typeof field !== 'string' || field.length === 0) {\n throw new Error(\n `Local recovery record for project \"${projectId}\" is malformed: \"${key}\" is missing or invalid.`,\n )\n }\n return field\n}\n\nfunction parseRecord(projectId: string, value: unknown): UnclaimedProjectRecord {\n const record = asRecord(value)\n const parsed: UnclaimedProjectRecord = {\n claimToken: requireString(record, 'claimToken', projectId),\n claimUrl: requireString(record, 'claimUrl', projectId),\n dataset: requireString(record, 'dataset', projectId),\n expiresAt: requireString(record, 'expiresAt', projectId),\n mintedAt: requireString(record, 'mintedAt', projectId),\n projectId: requireString(record, 'projectId', projectId),\n token: requireString(record, 'token', projectId),\n }\n\n if (parsed.projectId !== projectId) {\n throw new Error(\n `Local recovery record for project \"${projectId}\" is malformed: its project ID does not match.`,\n )\n }\n let claimUrlValid = false\n try {\n const protocol = new URL(parsed.claimUrl).protocol\n claimUrlValid = protocol === 'https:' || protocol === 'http:'\n } catch {\n // The actionable error is reported below.\n }\n if (!claimUrlValid) {\n throw new TypeError(\n `Local recovery record for project \"${projectId}\" is malformed: \"claimUrl\" is not a valid URL.`,\n )\n }\n if (!Number.isFinite(Date.parse(parsed.mintedAt))) {\n throw new TypeError(\n `Local recovery record for project \"${projectId}\" is malformed: \"mintedAt\" is not a valid date.`,\n )\n }\n if (!Number.isFinite(Date.parse(parsed.expiresAt))) {\n throw new TypeError(\n `Local recovery record for project \"${projectId}\" is malformed: \"expiresAt\" is not a valid date.`,\n )\n }\n\n return parsed\n}\n\n/**\n * Read and validate the passive local recovery registry without changing it.\n */\nexport function readUnclaimedProjects(): UnclaimedProjectRecord[] {\n const stored = getUserConfig().get(UNCLAIMED_PROJECTS_CONFIG_KEY)\n if (stored === undefined) return []\n if (!stored || typeof stored !== 'object' || Array.isArray(stored)) {\n throw new Error('The local unclaimed-project registry is malformed.')\n }\n\n return Object.entries(stored)\n .map(([projectId, record]) => parseRecord(projectId, record))\n .toSorted((left, right) => Date.parse(left.expiresAt) - Date.parse(right.expiresAt))\n}\n\n/**\n * Remove a record only when its claim token still matches the record that was checked.\n */\nexport function removeUnclaimedProject(projectId: string, expectedClaimToken: string): boolean {\n try {\n const config = getUserConfig()\n const records = asRecord(config.get(UNCLAIMED_PROJECTS_CONFIG_KEY))\n const current = asRecord(records[projectId])\n if (current.claimToken !== expectedClaimToken) return false\n\n const {[projectId]: _, ...remaining} = records\n if (Object.keys(remaining).length === 0) {\n config.delete(UNCLAIMED_PROJECTS_CONFIG_KEY)\n } else {\n config.set(UNCLAIMED_PROJECTS_CONFIG_KEY, remaining)\n }\n return true\n } catch (error) {\n debug(\n 'failed to remove unclaimed project: %s',\n error instanceof Error ? error.message : `${error}`,\n )\n return false\n }\n}\n"],"names":["getUserConfig","subdebug","debug","UNCLAIMED_PROJECTS_CONFIG_KEY","asRecord","value","Array","isArray","recordUnclaimedProject","minted","mintedAt","Date","toISOString","config","records","get","record","claimToken","claimUrl","dataset","datasetName","expiresAt","projectId","resourceId","token","set","error","Error","message","requireString","key","field","length","parseRecord","parsed","claimUrlValid","protocol","URL","TypeError","Number","isFinite","parse","readUnclaimedProjects","stored","undefined","Object","entries","map","toSorted","left","right","removeUnclaimedProject","expectedClaimToken","current","_","remaining","keys","delete"],"mappings":"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"}
package/oclif.config.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export default {
2
+ additionalVersionFlags: ['-v'],
2
3
  bin: 'sanity',
3
4
  commands: './dist/commands',
4
5
  dirname: 'sanity',
@@ -1141,6 +1141,76 @@
1141
1141
  "manage.js"
1142
1142
  ]
1143
1143
  },
1144
+ "new": {
1145
+ "aliases": [],
1146
+ "args": {
1147
+ "projectName": {
1148
+ "description": "Display name for the new project",
1149
+ "name": "projectName",
1150
+ "required": false
1151
+ }
1152
+ },
1153
+ "description": "Sets up two folders here: ./sanity, a Studio where you write and edit your\ncontent, and ./web, a Next.js website that reads it. Both are already\nconnected to your new project, so you can start them straight away. Use\n--no-scaffold if you just want the project and nothing else.\n\nThe project is real and works immediately, but it is only yours for 72 hours.\nClaim it with a Sanity account before the deadline and everything you have\nbuilt stays exactly as it is. Claiming is free and takes about a minute. Miss\nthe deadline and the project and its content are deleted.\n\nTwo things to keep private: the claim link, because anyone who opens it\nbecomes the owner, and the access token saved in ./sanity/.env.local and\n./web/.env.local, because it can read and change everything in the project.\nKeep both env files out of git, and never put the token in code that runs in\nthe browser.\n\nFetch https://sanity.new for full instructions, or point your AI agent at it.",
1154
+ "examples": [
1155
+ {
1156
+ "command": "<%= config.bin %> <%= command.id %>",
1157
+ "description": "Create a project with a Studio and a website"
1158
+ },
1159
+ {
1160
+ "command": "<%= config.bin %> <%= command.id %> \"My New Project\"",
1161
+ "description": "Create a project called \"My New Project\""
1162
+ },
1163
+ {
1164
+ "command": "<%= config.bin %> <%= command.id %> --yes",
1165
+ "description": "Create a project without being asked anything"
1166
+ },
1167
+ {
1168
+ "command": "<%= config.bin %> <%= command.id %> --no-scaffold",
1169
+ "description": "Create the project only, with no Studio or website"
1170
+ },
1171
+ {
1172
+ "command": "<%= config.bin %> <%= command.id %> --json",
1173
+ "description": "Create a project and print its details as JSON"
1174
+ }
1175
+ ],
1176
+ "flags": {
1177
+ "json": {
1178
+ "description": "Format output as json.",
1179
+ "helpGroup": "GLOBAL",
1180
+ "name": "json",
1181
+ "allowNo": false,
1182
+ "type": "boolean"
1183
+ },
1184
+ "scaffold": {
1185
+ "description": "Set up a Studio in ./sanity and a Next.js website in ./web (on by default)",
1186
+ "name": "scaffold",
1187
+ "allowNo": true,
1188
+ "type": "boolean"
1189
+ },
1190
+ "yes": {
1191
+ "char": "y",
1192
+ "description": "Skip prompts and use defaults (project: \"My Sanity project\")",
1193
+ "name": "yes",
1194
+ "allowNo": false,
1195
+ "type": "boolean"
1196
+ }
1197
+ },
1198
+ "hasDynamicHelp": false,
1199
+ "hiddenAliases": [],
1200
+ "id": "new",
1201
+ "pluginAlias": "@sanity/cli",
1202
+ "pluginName": "@sanity/cli",
1203
+ "pluginType": "core",
1204
+ "strict": true,
1205
+ "summary": "Create a Sanity project without an account, and claim it within 72 hours to keep it.",
1206
+ "enableJsonFlag": true,
1207
+ "isESM": true,
1208
+ "relativePath": [
1209
+ "dist",
1210
+ "commands",
1211
+ "new.js"
1212
+ ]
1213
+ },
1144
1214
  "preview": {
1145
1215
  "aliases": [],
1146
1216
  "args": {
@@ -4451,6 +4521,46 @@
4451
4521
  "list.js"
4452
4522
  ]
4453
4523
  },
4524
+ "projects:unclaimed": {
4525
+ "aliases": [],
4526
+ "args": {},
4527
+ "description": "Recover details for unclaimed projects created on this machine",
4528
+ "examples": [
4529
+ {
4530
+ "command": "<%= config.bin %> <%= command.id %>",
4531
+ "description": "List locally recorded unclaimed projects"
4532
+ },
4533
+ {
4534
+ "command": "<%= config.bin %> <%= command.id %> --project-id abc123",
4535
+ "description": "Show recovery details for one project"
4536
+ }
4537
+ ],
4538
+ "flags": {
4539
+ "project-id": {
4540
+ "description": "Project ID to recover",
4541
+ "name": "project-id",
4542
+ "hasDynamicHelp": false,
4543
+ "multiple": false,
4544
+ "type": "option"
4545
+ }
4546
+ },
4547
+ "hasDynamicHelp": false,
4548
+ "hiddenAliases": [
4549
+ "project:unclaimed"
4550
+ ],
4551
+ "id": "projects:unclaimed",
4552
+ "pluginAlias": "@sanity/cli",
4553
+ "pluginName": "@sanity/cli",
4554
+ "pluginType": "core",
4555
+ "strict": true,
4556
+ "isESM": true,
4557
+ "relativePath": [
4558
+ "dist",
4559
+ "commands",
4560
+ "projects",
4561
+ "unclaimed.js"
4562
+ ]
4563
+ },
4454
4564
  "schemas:delete": {
4455
4565
  "aliases": [],
4456
4566
  "args": {},
@@ -5829,5 +5939,5 @@
5829
5939
  ]
5830
5940
  }
5831
5941
  },
5832
- "version": "7.15.1"
5942
+ "version": "7.17.0"
5833
5943
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/cli",
3
- "version": "7.15.1",
3
+ "version": "7.17.0",
4
4
  "description": "Sanity CLI tool for managing Sanity projects and organizations",
5
5
  "keywords": [
6
6
  "cli",
@@ -119,13 +119,13 @@
119
119
  "tinyglobby": "^0.2.17",
120
120
  "tsx": "^4.23.1",
121
121
  "typeid-js": "^1.2.0",
122
- "vite": "^8.1.5",
122
+ "vite": "^8.2.0",
123
123
  "which": "^6.0.1",
124
124
  "yaml": "^2.9.0",
125
125
  "zod": "^4.4.3",
126
- "@sanity/workbench-cli": "^1.8.0",
127
- "@sanity/cli-core": "^2.7.1",
128
- "@sanity/cli-build": "^5.1.0"
126
+ "@sanity/cli-build": "^5.1.0",
127
+ "@sanity/cli-core": "^2.8.0",
128
+ "@sanity/workbench-cli": "^1.10.0"
129
129
  },
130
130
  "devDependencies": {
131
131
  "@eslint/compat": "^2.1.0",
@@ -160,7 +160,7 @@
160
160
  "vitest": "^4.1.10",
161
161
  "@repo/package.config": "0.0.1",
162
162
  "@repo/tsconfig": "3.70.0",
163
- "@sanity/cli-test": "8.0.1",
163
+ "@sanity/cli-test": "9.0.1",
164
164
  "@sanity/eslint-config-cli": "1.1.3"
165
165
  },
166
166
  "peerDependencies": {
@@ -11,7 +11,7 @@ import {
11
11
  useElementSize,
12
12
  useTheme,
13
13
  } from '@sanity/ui'
14
- import {CloseIcon} from '@sanity/icons'
14
+ import {CloseIcon} from '@sanity/icons/Close'
15
15
  import {css, styled} from 'styled-components'
16
16
 
17
17
  const BlueColor = css`
@@ -1,4 +1,4 @@
1
- import {WarningOutlineIcon} from '@sanity/icons'
1
+ import {WarningOutlineIcon} from '@sanity/icons/WarningOutline'
2
2
  import {StringFieldProps} from 'sanity'
3
3
  import {Box, Card, Flex, Stack, Text} from '@sanity/ui'
4
4
 
@@ -1,4 +1,4 @@
1
- import {WarningOutlineIcon} from '@sanity/icons'
1
+ import {WarningOutlineIcon} from '@sanity/icons/WarningOutline'
2
2
  import {StringFieldProps, useFormValue} from 'sanity'
3
3
  import {Box, Card, Flex, Stack, Text} from '@sanity/ui'
4
4
  import {productUrl} from '../../utils/shopifyUrls'
@@ -1,4 +1,4 @@
1
- import {WarningOutlineIcon} from '@sanity/icons'
1
+ import {WarningOutlineIcon} from '@sanity/icons/WarningOutline'
2
2
  import {Box, Card, Flex, Stack, Text} from '@sanity/ui'
3
3
 
4
4
  export default function ProductVariantHiddenInput() {
@@ -1,4 +1,4 @@
1
- import {LockIcon} from '@sanity/icons'
1
+ import {LockIcon} from '@sanity/icons/Lock'
2
2
  import {Box, Text, TextInput, Tooltip} from '@sanity/ui'
3
3
  import {StringInputProps, useFormValue, SanityDocument, StringSchemaType} from 'sanity'
4
4
  import get from 'lodash.get'
@@ -1,4 +1,6 @@
1
- import {CloseIcon, ImageIcon, LinkRemovedIcon} from '@sanity/icons'
1
+ import {CloseIcon} from '@sanity/icons/Close'
2
+ import {ImageIcon} from '@sanity/icons/Image'
3
+ import {LinkRemovedIcon} from '@sanity/icons/LinkRemoved'
2
4
  import React, {forwardRef, useState} from 'react'
3
5
 
4
6
  type Props = {
@@ -1,7 +1,9 @@
1
1
  // Currency code (ISO 4217) to use when displaying prices in the studio
2
2
 
3
3
  import ShopifyIcon from './components/icons/Shopify'
4
- import {ColorWheelIcon, ComposeIcon, SearchIcon} from '@sanity/icons'
4
+ import {ColorWheelIcon} from '@sanity/icons/ColorWheel'
5
+ import {ComposeIcon} from '@sanity/icons/Compose'
6
+ import {SearchIcon} from '@sanity/icons/Search'
5
7
 
6
8
  // https://en.wikipedia.org/wiki/ISO_4217
7
9
  export const DEFAULT_CURRENCY_CODE = 'USD'
@@ -1,5 +1,5 @@
1
1
  import React, {useState} from 'react'
2
- import {TrashIcon} from '@sanity/icons'
2
+ import {TrashIcon} from '@sanity/icons/Trash'
3
3
  import {Stack, Text, useToast} from '@sanity/ui'
4
4
  import {
5
5
  type DocumentActionDescription,
@@ -1,4 +1,4 @@
1
- import {EarthGlobeIcon} from '@sanity/icons'
1
+ import {EarthGlobeIcon} from '@sanity/icons/EarthGlobe'
2
2
  import {collectionUrl, productUrl, productVariantUrl} from '../../utils/shopifyUrls'
3
3
  import {type DocumentActionDescription} from 'sanity'
4
4
  import type {ShopifyDocument, ShopifyDocumentActionProps} from './types'
@@ -1,5 +1,5 @@
1
1
  import {defineArrayMember, defineField, defineType} from 'sanity'
2
- import {PackageIcon} from '@sanity/icons'
2
+ import {PackageIcon} from '@sanity/icons/Package'
3
3
  import {getExtension} from '@sanity/asset-utils'
4
4
  import pluralize from 'pluralize-esm'
5
5
  import CollectionHiddenInput from '../../components/inputs/CollectionHidden'
@@ -1,4 +1,4 @@
1
- import {IceCreamIcon} from '@sanity/icons'
1
+ import {IceCreamIcon} from '@sanity/icons/IceCream'
2
2
  import {defineField, defineType} from 'sanity'
3
3
 
4
4
  import ColorTheme from '../../components/media/ColorTheme'
@@ -1,4 +1,4 @@
1
- import {DocumentIcon} from '@sanity/icons'
1
+ import {DocumentIcon} from '@sanity/icons/Document'
2
2
  import {defineField} from 'sanity'
3
3
 
4
4
  import {validateSlug} from '../../utils/validateSlug'
@@ -1,4 +1,4 @@
1
- import {TagIcon} from '@sanity/icons'
1
+ import {TagIcon} from '@sanity/icons/Tag'
2
2
  import pluralize from 'pluralize-esm'
3
3
  import ProductHiddenInput from '../../components/inputs/ProductHidden'
4
4
  import ShopifyDocumentStatus from '../../components/media/ShopifyDocumentStatus'
@@ -1,4 +1,4 @@
1
- import {CopyIcon} from '@sanity/icons'
1
+ import {CopyIcon} from '@sanity/icons/Copy'
2
2
  import {defineField, defineType} from 'sanity'
3
3
 
4
4
  import ProductVariantHiddenInput from '../../components/inputs/ProductVariantHidden'
@@ -1,4 +1,4 @@
1
- import {PackageIcon} from '@sanity/icons'
1
+ import {PackageIcon} from '@sanity/icons/Package'
2
2
  import {defineField} from 'sanity'
3
3
 
4
4
  export const collectionGroupType = defineField({
@@ -1,4 +1,4 @@
1
- import {ImageIcon} from '@sanity/icons'
1
+ import {ImageIcon} from '@sanity/icons/Image'
2
2
  import pluralize from 'pluralize-esm'
3
3
  import {defineField} from 'sanity'
4
4
 
@@ -1,4 +1,4 @@
1
- import {EnvelopeIcon} from '@sanity/icons'
1
+ import {EnvelopeIcon} from '@sanity/icons/Envelope'
2
2
  import {defineField} from 'sanity'
3
3
 
4
4
  export const linkEmailType = defineField({
@@ -1,4 +1,4 @@
1
- import {EarthGlobeIcon} from '@sanity/icons'
1
+ import {EarthGlobeIcon} from '@sanity/icons/EarthGlobe'
2
2
  import {defineField} from 'sanity'
3
3
 
4
4
  export const linkExternalType = defineField({
@@ -1,4 +1,4 @@
1
- import {LinkIcon} from '@sanity/icons'
1
+ import {LinkIcon} from '@sanity/icons/Link'
2
2
  import {defineField} from 'sanity'
3
3
  import {PAGE_REFERENCES} from '../../../constants'
4
4
 
@@ -1,4 +1,4 @@
1
- import {TagIcon} from '@sanity/icons'
1
+ import {TagIcon} from '@sanity/icons/Tag'
2
2
  import {defineField} from 'sanity'
3
3
 
4
4
  export const linkProductType = defineField({
@@ -1,4 +1,4 @@
1
- import {StackCompactIcon} from '@sanity/icons'
1
+ import {StackCompactIcon} from '@sanity/icons/StackCompact'
2
2
  import pluralize from 'pluralize-esm'
3
3
  import {defineField} from 'sanity'
4
4
 
@@ -1,4 +1,5 @@
1
- import {BlockElementIcon, ImageIcon} from '@sanity/icons'
1
+ import {BlockElementIcon} from '@sanity/icons/BlockElement'
2
+ import {ImageIcon} from '@sanity/icons/Image'
2
3
  import {defineArrayMember, defineField} from 'sanity'
3
4
 
4
5
  export const callToActionType = defineField({
@@ -1,4 +1,4 @@
1
- import {BulbOutlineIcon} from '@sanity/icons'
1
+ import {BulbOutlineIcon} from '@sanity/icons/BulbOutline'
2
2
  import {defineField} from 'sanity'
3
3
 
4
4
  export const calloutType = defineField({
@@ -1,4 +1,4 @@
1
- import {PackageIcon} from '@sanity/icons'
1
+ import {PackageIcon} from '@sanity/icons/Package'
2
2
  import {defineField} from 'sanity'
3
3
 
4
4
  import ShopifyDocumentStatus from '../../../components/media/ShopifyDocumentStatus'
@@ -1,4 +1,4 @@
1
- import {ThLargeIcon} from '@sanity/icons'
1
+ import {ThLargeIcon} from '@sanity/icons/ThLarge'
2
2
  import pluralize from 'pluralize-esm'
3
3
  import {defineArrayMember, defineField} from 'sanity'
4
4
 
@@ -1,4 +1,4 @@
1
- import {ImageIcon} from '@sanity/icons'
1
+ import {ImageIcon} from '@sanity/icons/Image'
2
2
  import {defineField} from 'sanity'
3
3
 
4
4
  const VARIANTS = [
@@ -1,4 +1,4 @@
1
- import {ImageIcon} from '@sanity/icons'
1
+ import {ImageIcon} from '@sanity/icons/Image'
2
2
  import pluralize from 'pluralize-esm'
3
3
  import {defineField} from 'sanity'
4
4
 
@@ -1,4 +1,4 @@
1
- import {UserIcon} from '@sanity/icons'
1
+ import {UserIcon} from '@sanity/icons/User'
2
2
  import {defineField} from 'sanity'
3
3
 
4
4
  export const instagramType = defineField({
@@ -1,4 +1,4 @@
1
- import {TagIcon} from '@sanity/icons'
1
+ import {TagIcon} from '@sanity/icons/Tag'
2
2
  import pluralize from 'pluralize-esm'
3
3
  import {defineField} from 'sanity'
4
4
 
@@ -1,4 +1,4 @@
1
- import {TagIcon} from '@sanity/icons'
1
+ import {TagIcon} from '@sanity/icons/Tag'
2
2
 
3
3
  import {defineField} from 'sanity'
4
4
 
@@ -1,4 +1,4 @@
1
- import {FilterIcon} from '@sanity/icons'
1
+ import {FilterIcon} from '@sanity/icons/Filter'
2
2
  import {defineField} from 'sanity'
3
3
 
4
4
  export const collectionRuleType = defineField({
@@ -1,4 +1,4 @@
1
- import {SunIcon} from '@sanity/icons'
1
+ import {SunIcon} from '@sanity/icons/Sun'
2
2
  import {defineField} from 'sanity'
3
3
 
4
4
  export const optionType = defineField({
@@ -1,4 +1,4 @@
1
- import {TagIcon} from '@sanity/icons'
1
+ import {TagIcon} from '@sanity/icons/Tag'
2
2
  import pluralize from 'pluralize-esm'
3
3
 
4
4
  import {defineField} from 'sanity'
@@ -1,4 +1,4 @@
1
- import {HomeIcon} from '@sanity/icons'
1
+ import {HomeIcon} from '@sanity/icons/Home'
2
2
  import {defineArrayMember, defineField} from 'sanity'
3
3
  import {GROUPS} from '../../constants'
4
4
 
@@ -1,4 +1,8 @@
1
- import {CogIcon, ControlsIcon, ErrorOutlineIcon, MenuIcon, SearchIcon} from '@sanity/icons'
1
+ import {CogIcon} from '@sanity/icons/Cog'
2
+ import {ControlsIcon} from '@sanity/icons/Controls'
3
+ import {ErrorOutlineIcon} from '@sanity/icons/ErrorOutline'
4
+ import {MenuIcon} from '@sanity/icons/Menu'
5
+ import {SearchIcon} from '@sanity/icons/Search'
2
6
  import {defineType, defineField} from 'sanity'
3
7
 
4
8
  const TITLE = 'Settings'