@sanity/runtime-cli 17.3.2-next.ga266544 → 17.4.0-blueprinit.g0ac8878

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 (33) hide show
  1. package/README.md +19 -19
  2. package/SKILL.md +21 -0
  3. package/dist/actions/blueprinit/manifest.d.ts +15 -0
  4. package/dist/actions/blueprinit/manifest.js +76 -0
  5. package/dist/actions/blueprinit/studio.d.ts +6 -0
  6. package/dist/actions/blueprinit/studio.js +43 -0
  7. package/dist/actions/blueprinit/templates.d.ts +34 -0
  8. package/dist/actions/blueprinit/templates.js +83 -0
  9. package/dist/actions/blueprinit/workspace.d.ts +16 -0
  10. package/dist/actions/blueprinit/workspace.js +143 -0
  11. package/dist/actions/sanity/datasets.d.ts +15 -0
  12. package/dist/actions/sanity/datasets.js +26 -0
  13. package/dist/commands/blueprinit.d.ts +17 -0
  14. package/dist/commands/blueprinit.js +59 -0
  15. package/dist/cores/blueprinit/adopt.d.ts +6 -0
  16. package/dist/cores/blueprinit/adopt.js +119 -0
  17. package/dist/cores/blueprinit/finish.d.ts +14 -0
  18. package/dist/cores/blueprinit/finish.js +64 -0
  19. package/dist/cores/blueprinit/fresh.d.ts +3 -0
  20. package/dist/cores/blueprinit/fresh.js +120 -0
  21. package/dist/cores/blueprinit/preview.d.ts +15 -0
  22. package/dist/cores/blueprinit/preview.js +14 -0
  23. package/dist/cores/blueprinit/prompts.d.ts +5 -0
  24. package/dist/cores/blueprinit/prompts.js +22 -0
  25. package/dist/cores/blueprinit/selection.d.ts +15 -0
  26. package/dist/cores/blueprinit/selection.js +90 -0
  27. package/dist/cores/blueprinit/types.d.ts +16 -0
  28. package/dist/cores/blueprinit/types.js +1 -0
  29. package/dist/cores/blueprinit.d.ts +7 -0
  30. package/dist/cores/blueprinit.js +48 -0
  31. package/oclif.manifest.json +129 -1
  32. package/package.json +7 -3
  33. package/skills/blueprinit.md +155 -0
package/README.md CHANGED
@@ -90,7 +90,7 @@ EXAMPLES
90
90
  $ sanity-run blueprints add function --name my-function --fn-type document-create --fn-type document-update --lang js
91
91
  ```
92
92
 
93
- _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.2-next.ga266544/src/commands/blueprints/add.ts)_
93
+ _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.4.0-blueprinit.g0ac8878/src/commands/blueprints/add.ts)_
94
94
 
95
95
  ## `sanity-run blueprints config`
96
96
 
@@ -127,7 +127,7 @@ EXAMPLES
127
127
  $ sanity-run blueprints config --edit --project-id <projectId> --stack <name-or-id>
128
128
  ```
129
129
 
130
- _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.2-next.ga266544/src/commands/blueprints/config.ts)_
130
+ _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v17.4.0-blueprinit.g0ac8878/src/commands/blueprints/config.ts)_
131
131
 
132
132
  ## `sanity-run blueprints deploy`
133
133
 
@@ -181,7 +181,7 @@ EXAMPLES
181
181
  $ sanity-run blueprints deploy --new-stack-name <new-name>
182
182
  ```
183
183
 
184
- _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.2-next.ga266544/src/commands/blueprints/deploy.ts)_
184
+ _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.4.0-blueprinit.g0ac8878/src/commands/blueprints/deploy.ts)_
185
185
 
186
186
  ## `sanity-run blueprints destroy`
187
187
 
@@ -219,7 +219,7 @@ EXAMPLES
219
219
  $ sanity-run blueprints destroy --stack <name-or-id> --project-id <projectId> --force --no-wait
220
220
  ```
221
221
 
222
- _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.2-next.ga266544/src/commands/blueprints/destroy.ts)_
222
+ _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.4.0-blueprinit.g0ac8878/src/commands/blueprints/destroy.ts)_
223
223
 
224
224
  ## `sanity-run blueprints doctor`
225
225
 
@@ -251,7 +251,7 @@ EXAMPLES
251
251
  $ sanity-run blueprints doctor --fix
252
252
  ```
253
253
 
254
- _See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.2-next.ga266544/src/commands/blueprints/doctor.ts)_
254
+ _See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v17.4.0-blueprinit.g0ac8878/src/commands/blueprints/doctor.ts)_
255
255
 
256
256
  ## `sanity-run blueprints info`
257
257
 
@@ -288,7 +288,7 @@ EXAMPLES
288
288
  $ sanity-run blueprints info --organization-id <orgId> --stack <name-or-id>
289
289
  ```
290
290
 
291
- _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.2-next.ga266544/src/commands/blueprints/info.ts)_
291
+ _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v17.4.0-blueprinit.g0ac8878/src/commands/blueprints/info.ts)_
292
292
 
293
293
  ## `sanity-run blueprints init [DIR]`
294
294
 
@@ -352,7 +352,7 @@ EXAMPLES
352
352
  $ sanity-run blueprints init old-stack --type <json|js|ts> --project-id <projectId> --stack-id <existingStackId>
353
353
  ```
354
354
 
355
- _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.2-next.ga266544/src/commands/blueprints/init.ts)_
355
+ _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v17.4.0-blueprinit.g0ac8878/src/commands/blueprints/init.ts)_
356
356
 
357
357
  ## `sanity-run blueprints logs`
358
358
 
@@ -398,7 +398,7 @@ EXAMPLES
398
398
  $ sanity-run blueprints logs --before 2026-05-01T00:00:00Z
399
399
  ```
400
400
 
401
- _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.2-next.ga266544/src/commands/blueprints/logs.ts)_
401
+ _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.4.0-blueprinit.g0ac8878/src/commands/blueprints/logs.ts)_
402
402
 
403
403
  ## `sanity-run blueprints mint-deploy-token`
404
404
 
@@ -443,7 +443,7 @@ EXAMPLES
443
443
  $ sanity-run blueprints mint-deploy-token --organization-id <orgId>
444
444
  ```
445
445
 
446
- _See code: [src/commands/blueprints/mint-deploy-token.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.2-next.ga266544/src/commands/blueprints/mint-deploy-token.ts)_
446
+ _See code: [src/commands/blueprints/mint-deploy-token.ts](https://github.com/sanity-io/runtime-cli/blob/v17.4.0-blueprinit.g0ac8878/src/commands/blueprints/mint-deploy-token.ts)_
447
447
 
448
448
  ## `sanity-run blueprints plan`
449
449
 
@@ -476,7 +476,7 @@ EXAMPLES
476
476
  $ sanity-run blueprints plan --organization-id <orgId> --stack <name-or-id>
477
477
  ```
478
478
 
479
- _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.2-next.ga266544/src/commands/blueprints/plan.ts)_
479
+ _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v17.4.0-blueprinit.g0ac8878/src/commands/blueprints/plan.ts)_
480
480
 
481
481
  ## `sanity-run blueprints promote`
482
482
 
@@ -512,7 +512,7 @@ EXAMPLES
512
512
  $ sanity-run blueprints promote --new-stack-name <new-name>
513
513
  ```
514
514
 
515
- _See code: [src/commands/blueprints/promote.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.2-next.ga266544/src/commands/blueprints/promote.ts)_
515
+ _See code: [src/commands/blueprints/promote.ts](https://github.com/sanity-io/runtime-cli/blob/v17.4.0-blueprinit.g0ac8878/src/commands/blueprints/promote.ts)_
516
516
 
517
517
  ## `sanity-run blueprints stacks`
518
518
 
@@ -548,7 +548,7 @@ EXAMPLES
548
548
  $ sanity-run blueprints stacks --organization-id <organizationId> --include-projects
549
549
  ```
550
550
 
551
- _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.2-next.ga266544/src/commands/blueprints/stacks.ts)_
551
+ _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v17.4.0-blueprinit.g0ac8878/src/commands/blueprints/stacks.ts)_
552
552
 
553
553
  ## `sanity-run functions add`
554
554
 
@@ -600,7 +600,7 @@ EXAMPLES
600
600
  $ sanity-run functions add --name my-function --type document-create --type document-update --lang js
601
601
  ```
602
602
 
603
- _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.2-next.ga266544/src/commands/functions/add.ts)_
603
+ _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.4.0-blueprinit.g0ac8878/src/commands/functions/add.ts)_
604
604
 
605
605
  ## `sanity-run functions dev`
606
606
 
@@ -635,7 +635,7 @@ EXAMPLES
635
635
  $ sanity-run functions dev --timeout 60
636
636
  ```
637
637
 
638
- _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.2-next.ga266544/src/commands/functions/dev.ts)_
638
+ _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v17.4.0-blueprinit.g0ac8878/src/commands/functions/dev.ts)_
639
639
 
640
640
  ## `sanity-run functions env add NAME KEY VALUE`
641
641
 
@@ -668,7 +668,7 @@ EXAMPLES
668
668
  $ sanity-run functions env add --stack <name-or-id> MyFunction API_URL https://api.example.com/
669
669
  ```
670
670
 
671
- _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.2-next.ga266544/src/commands/functions/env/add.ts)_
671
+ _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.4.0-blueprinit.g0ac8878/src/commands/functions/env/add.ts)_
672
672
 
673
673
  ## `sanity-run functions env list NAME`
674
674
 
@@ -698,7 +698,7 @@ EXAMPLES
698
698
  $ sanity-run functions env list --stack <name-or-id> MyFunction
699
699
  ```
700
700
 
701
- _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.2-next.ga266544/src/commands/functions/env/list.ts)_
701
+ _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v17.4.0-blueprinit.g0ac8878/src/commands/functions/env/list.ts)_
702
702
 
703
703
  ## `sanity-run functions env remove NAME KEY`
704
704
 
@@ -730,7 +730,7 @@ EXAMPLES
730
730
  $ sanity-run functions env remove --stack <name-or-id> MyFunction API_URL
731
731
  ```
732
732
 
733
- _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.2-next.ga266544/src/commands/functions/env/remove.ts)_
733
+ _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v17.4.0-blueprinit.g0ac8878/src/commands/functions/env/remove.ts)_
734
734
 
735
735
  ## `sanity-run functions logs [NAME]`
736
736
 
@@ -770,7 +770,7 @@ EXAMPLES
770
770
  $ sanity-run functions logs <name> --delete
771
771
  ```
772
772
 
773
- _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.2-next.ga266544/src/commands/functions/logs.ts)_
773
+ _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.4.0-blueprinit.g0ac8878/src/commands/functions/logs.ts)_
774
774
 
775
775
  ## `sanity-run functions test [NAME]`
776
776
 
@@ -827,7 +827,7 @@ EXAMPLES
827
827
  $ sanity-run functions test <name> --event update --data-before '{ "title": "before" }' --data-after '{ "title": "after" }'
828
828
  ```
829
829
 
830
- _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.2-next.ga266544/src/commands/functions/test.ts)_
830
+ _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v17.4.0-blueprinit.g0ac8878/src/commands/functions/test.ts)_
831
831
 
832
832
  ## `sanity-run help [COMMAND]`
833
833
 
package/SKILL.md ADDED
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: runtime-cli
3
+ description: Use the Sanity Runtime CLI for Blueprints and Functions workflows. Use when an agent needs to run or explain sanity-run commands, especially unattended command flows, local Blueprint management, Stack operations, Function development, or the experimental blueprinit command.
4
+ ---
5
+
6
+ # Sanity Runtime CLI
7
+
8
+ Use `sanity-run <command> --help` for flags and arguments. Use
9
+ `sanity-run <command> --llms` (or `--skill`) for the agent-oriented workflow and examples for that
10
+ command.
11
+
12
+ Read only the command skill relevant to the task:
13
+
14
+ - `blueprinit` → `skills/blueprinit.md`
15
+ - `blueprints deploy` → `skills/blueprints-deploy.md`
16
+ - `functions test` → `skills/functions-test.md`
17
+
18
+ Derive other filenames by joining the command words with hyphens.
19
+
20
+ Command skills are added incrementally. If one is absent, inspect the command's `--help` output and
21
+ the repository implementation before acting.
@@ -0,0 +1,15 @@
1
+ export declare const STARTER_DOCUMENT_TYPE = "post";
2
+ export declare const STARTER_FUNCTION_NAME = "set-slug";
3
+ export interface BlueprintManifestOptions {
4
+ projectName: string;
5
+ displayName: string;
6
+ projectId: string;
7
+ datasetName: string;
8
+ studioName: string;
9
+ studioSrc: string;
10
+ studioOrigin: string;
11
+ withFunction: boolean;
12
+ }
13
+ export declare function renderBlueprint(options: BlueprintManifestOptions): string;
14
+ export declare function hostedStudioOrigin(studioName: string, sanityEnv?: string): string;
15
+ export declare function slugify(value: string): string;
@@ -0,0 +1,76 @@
1
+ const quote = (value) => `'${value.replaceAll('\\', '\\\\').replaceAll("'", "\\'")}'`;
2
+ export const STARTER_DOCUMENT_TYPE = 'post';
3
+ export const STARTER_FUNCTION_NAME = 'set-slug';
4
+ export function renderBlueprint(options) {
5
+ const { projectName, displayName, projectId, datasetName, studioName, studioSrc, studioOrigin, withFunction, } = options;
6
+ const definers = [
7
+ 'type BlueprintResource',
8
+ 'defineBlueprint',
9
+ 'defineCorsOrigin',
10
+ 'defineDataset',
11
+ 'defineProject',
12
+ ];
13
+ if (withFunction)
14
+ definers.splice(4, 0, 'defineDocumentFunction');
15
+ const functionResource = withFunction
16
+ ? ` defineDocumentFunction({
17
+ name: ${quote(STARTER_FUNCTION_NAME)},
18
+ project: ${quote(projectId)},
19
+ event: {
20
+ on: ['create', 'update'],
21
+ filter: "_type == '${STARTER_DOCUMENT_TYPE}' && !defined(slug.current)",
22
+ projection: '{_id, title, slug}',
23
+ },
24
+ }),
25
+ `
26
+ : '';
27
+ // Project references stay literal until the deploy API can resolve resource references here.
28
+ return `import {${definers.join(', ')}} from '@sanity/blueprints'
29
+
30
+ export default defineBlueprint({
31
+ resources: [
32
+ defineProject({
33
+ name: ${quote(projectName)},
34
+ displayName: ${quote(displayName)},
35
+ lifecycle: {
36
+ deletionPolicy: 'retain',
37
+ ownershipAction: {type: 'attach', id: ${quote(projectId)}},
38
+ },
39
+ }),
40
+ defineDataset({
41
+ name: ${quote(datasetName)},
42
+ project: ${quote(projectId)},
43
+ lifecycle: {
44
+ deletionPolicy: 'retain',
45
+ ownershipAction: {type: 'attach', id: ${quote(datasetName)}, projectId: ${quote(projectId)}},
46
+ },
47
+ }),
48
+ defineCorsOrigin({
49
+ name: ${quote(`${studioName}-cors`)},
50
+ origin: ${quote(studioOrigin)},
51
+ allowCredentials: true,
52
+ project: ${quote(projectId)},
53
+ }),
54
+ {
55
+ name: ${quote(studioName)},
56
+ type: 'sanity.studio',
57
+ src: ${quote(studioSrc)},
58
+ slug: ${quote(studioName)},
59
+ title: ${quote(displayName)},
60
+ autoUpdates: {enabled: false},
61
+ project: ${quote(projectId)},
62
+ } as BlueprintResource,
63
+ ${functionResource} ],
64
+ })
65
+ `;
66
+ }
67
+ export function hostedStudioOrigin(studioName, sanityEnv) {
68
+ const hostname = sanityEnv?.toLowerCase() === 'staging' ? 'studio.sanity.work' : 'sanity.studio';
69
+ return `https://${studioName}.${hostname}`;
70
+ }
71
+ export function slugify(value) {
72
+ return value
73
+ .toLowerCase()
74
+ .replace(/[^a-z0-9]+/g, '-')
75
+ .replace(/^-+|-+$/g, '');
76
+ }
@@ -0,0 +1,6 @@
1
+ export interface DetectedStudio {
2
+ configFile: string;
3
+ projectId?: string;
4
+ dataset?: string;
5
+ }
6
+ export declare function detectStudio(dir: string): DetectedStudio | undefined;
@@ -0,0 +1,43 @@
1
+ import { readdirSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ const STUDIO_CONFIG_PATTERN = /^sanity\.config\.[cm]?[jt]sx?$/;
4
+ const STUDIO_CLI_PATTERN = /^sanity\.cli\.[cm]?[jt]sx?$/;
5
+ export function detectStudio(dir) {
6
+ let entries;
7
+ try {
8
+ entries = readdirSync(dir, { withFileTypes: true })
9
+ .filter((entry) => entry.isFile())
10
+ .map((entry) => entry.name);
11
+ }
12
+ catch {
13
+ return undefined;
14
+ }
15
+ const configFile = entries.find((name) => STUDIO_CONFIG_PATTERN.test(name));
16
+ if (!configFile)
17
+ return undefined;
18
+ const detected = { configFile };
19
+ const cliFile = entries.find((name) => STUDIO_CLI_PATTERN.test(name));
20
+ for (const file of [configFile, cliFile]) {
21
+ if (!file)
22
+ continue;
23
+ const source = readSource(join(dir, file));
24
+ if (!source)
25
+ continue;
26
+ detected.projectId ??= extractLiteral(source, 'projectId');
27
+ detected.dataset ??= extractLiteral(source, 'dataset');
28
+ if (detected.projectId && detected.dataset)
29
+ break;
30
+ }
31
+ return detected;
32
+ }
33
+ function readSource(filePath) {
34
+ try {
35
+ return readFileSync(filePath, 'utf8');
36
+ }
37
+ catch {
38
+ return undefined;
39
+ }
40
+ }
41
+ function extractLiteral(source, key) {
42
+ return source.match(new RegExp(`\\b${key}\\s*:\\s*(['"\`])([^'"\`\\\\]+)\\1`))?.[2];
43
+ }
@@ -0,0 +1,34 @@
1
+ export declare function renderStudioConfig(options: {
2
+ displayName: string;
3
+ projectId: string;
4
+ datasetName: string;
5
+ }): string;
6
+ export declare const STUDIO_SCHEMA_TYPES = "import {post} from './post'\n\nexport const schemaTypes = [post]\n";
7
+ export declare const STUDIO_POST_TYPE = "import {defineField, defineType} from 'sanity'\n\nexport const post = defineType({\n name: 'post',\n title: 'Post',\n type: 'document',\n fields: [\n defineField({name: 'title', type: 'string', validation: (rule) => rule.required()}),\n // The set-slug function fills this when it is empty.\n defineField({name: 'slug', type: 'slug', options: {source: 'title'}}),\n defineField({name: 'publishedAt', title: 'Published at', type: 'datetime'}),\n defineField({name: 'body', type: 'text'}),\n ],\n})\n";
8
+ export declare const FUNCTION_SET_SLUG = "import {createClient} from '@sanity/client'\nimport {documentEventHandler} from '@sanity/functions'\n\nconst slugify = (title: string) =>\n title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 96)\n\nexport const handler = documentEventHandler(async ({context, event}) => {\n const {_id, title, slug} = event.data\n if (!title || slug?.current) return\n\n const client = createClient({apiVersion: '2025-05-01', ...context.clientOptions})\n const current = slugify(title)\n await client.patch(_id).set({slug: {_type: 'slug', current}}).commit()\n\n console.log(`Set slug for ${_id}: ${current}`)\n})\n";
9
+ export declare const studioPackageJson: (name: string) => {
10
+ name: string;
11
+ version: string;
12
+ private: boolean;
13
+ scripts: {
14
+ dev: string;
15
+ build: string;
16
+ deploy: string;
17
+ };
18
+ dependencies: {
19
+ react: string;
20
+ 'react-dom': string;
21
+ sanity: string;
22
+ 'styled-components': string;
23
+ };
24
+ };
25
+ export declare const functionPackageJson: (name: string) => {
26
+ name: string;
27
+ version: string;
28
+ private: boolean;
29
+ type: string;
30
+ dependencies: {
31
+ '@sanity/client': string;
32
+ '@sanity/functions': string;
33
+ };
34
+ };
@@ -0,0 +1,83 @@
1
+ import { STARTER_DOCUMENT_TYPE, STARTER_FUNCTION_NAME } from './manifest.js';
2
+ const quote = (value) => `'${value.replaceAll('\\', '\\\\').replaceAll("'", "\\'")}'`;
3
+ export function renderStudioConfig(options) {
4
+ return `import {defineConfig} from 'sanity'
5
+ import {structureTool} from 'sanity/structure'
6
+ import {schemaTypes} from './schemaTypes'
7
+
8
+ export default defineConfig({
9
+ name: 'default',
10
+ title: ${quote(options.displayName)},
11
+ projectId: ${quote(options.projectId)},
12
+ dataset: ${quote(options.datasetName)},
13
+ plugins: [structureTool()],
14
+ schema: {types: schemaTypes},
15
+ })
16
+ `;
17
+ }
18
+ export const STUDIO_SCHEMA_TYPES = `import {post} from './post'
19
+
20
+ export const schemaTypes = [post]
21
+ `;
22
+ export const STUDIO_POST_TYPE = `import {defineField, defineType} from 'sanity'
23
+
24
+ export const post = defineType({
25
+ name: '${STARTER_DOCUMENT_TYPE}',
26
+ title: 'Post',
27
+ type: 'document',
28
+ fields: [
29
+ defineField({name: 'title', type: 'string', validation: (rule) => rule.required()}),
30
+ // The ${STARTER_FUNCTION_NAME} function fills this when it is empty.
31
+ defineField({name: 'slug', type: 'slug', options: {source: 'title'}}),
32
+ defineField({name: 'publishedAt', title: 'Published at', type: 'datetime'}),
33
+ defineField({name: 'body', type: 'text'}),
34
+ ],
35
+ })
36
+ `;
37
+ export const FUNCTION_SET_SLUG = `import {createClient} from '@sanity/client'
38
+ import {documentEventHandler} from '@sanity/functions'
39
+
40
+ const slugify = (title: string) =>
41
+ title
42
+ .toLowerCase()
43
+ .replace(/[^a-z0-9]+/g, '-')
44
+ .replace(/^-+|-+$/g, '')
45
+ .slice(0, 96)
46
+
47
+ export const handler = documentEventHandler(async ({context, event}) => {
48
+ const {_id, title, slug} = event.data
49
+ if (!title || slug?.current) return
50
+
51
+ const client = createClient({apiVersion: '2025-05-01', ...context.clientOptions})
52
+ const current = slugify(title)
53
+ await client.patch(_id).set({slug: {_type: 'slug', current}}).commit()
54
+
55
+ console.log(\`Set slug for \${_id}: \${current}\`)
56
+ })
57
+ `;
58
+ export const studioPackageJson = (name) => ({
59
+ name,
60
+ version: '0.1.0',
61
+ private: true,
62
+ scripts: {
63
+ dev: 'sanity dev',
64
+ build: 'sanity build',
65
+ deploy: 'sanity deploy',
66
+ },
67
+ dependencies: {
68
+ react: '^19',
69
+ 'react-dom': '^19',
70
+ sanity: '^4',
71
+ 'styled-components': '^6',
72
+ },
73
+ });
74
+ export const functionPackageJson = (name) => ({
75
+ name,
76
+ version: '0.1.0',
77
+ private: true,
78
+ type: 'module',
79
+ dependencies: {
80
+ '@sanity/client': '^7',
81
+ '@sanity/functions': '^1',
82
+ },
83
+ });
@@ -0,0 +1,16 @@
1
+ export interface WorkspaceOptions {
2
+ dir: string;
3
+ projectId: string;
4
+ displayName: string;
5
+ datasetName: string;
6
+ stackName: string;
7
+ studioName: string;
8
+ studioOrigin: string;
9
+ blueprintsVersion: string;
10
+ }
11
+ export interface WorkspaceResult {
12
+ blueprintFilePath: string;
13
+ files: string[];
14
+ }
15
+ export declare function scaffoldWorkspace(options: WorkspaceOptions): WorkspaceResult;
16
+ export declare function adoptWorkspace(options: WorkspaceOptions): WorkspaceResult;
@@ -0,0 +1,143 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join, posix } from 'node:path';
3
+ import { renderBlueprint, STARTER_FUNCTION_NAME } from './manifest.js';
4
+ import { FUNCTION_SET_SLUG, functionPackageJson, renderStudioConfig, STUDIO_POST_TYPE, STUDIO_SCHEMA_TYPES, studioPackageJson, } from './templates.js';
5
+ const GITIGNORE_ENTRY = '# Sanity Blueprints\n.sanity/';
6
+ const DEFAULT_GITIGNORE = `node_modules
7
+ .env
8
+
9
+ ${GITIGNORE_ENTRY}
10
+ `;
11
+ export function scaffoldWorkspace(options) {
12
+ const { dir, studioName } = options;
13
+ assertTargetsAvailable(dir, [
14
+ posix.join('studios', studioName),
15
+ posix.join('functions', STARTER_FUNCTION_NAME),
16
+ ]);
17
+ const packageJson = preparePackageJson(options, ['studios/*', 'functions/*']);
18
+ const files = [];
19
+ const write = createWriter(dir, files);
20
+ const blueprintFilePath = write('sanity.blueprint.ts', renderBlueprint({
21
+ ...manifestOptions(options),
22
+ studioSrc: `./studios/${studioName}`,
23
+ withFunction: true,
24
+ }));
25
+ write(posix.join('studios', studioName, 'package.json'), formatJson(studioPackageJson(studioName)));
26
+ write(posix.join('studios', studioName, 'sanity.config.ts'), renderStudioConfig(options));
27
+ write(posix.join('studios', studioName, 'schemaTypes', 'index.ts'), STUDIO_SCHEMA_TYPES);
28
+ write(posix.join('studios', studioName, 'schemaTypes', 'post.ts'), STUDIO_POST_TYPE);
29
+ write(posix.join('studios', studioName, 'static', '.gitkeep'), '');
30
+ write(posix.join('functions', STARTER_FUNCTION_NAME, 'package.json'), formatJson(functionPackageJson(STARTER_FUNCTION_NAME)));
31
+ write(posix.join('functions', STARTER_FUNCTION_NAME, 'index.ts'), FUNCTION_SET_SLUG);
32
+ writePreparedRootFiles(dir, packageJson, files);
33
+ return { blueprintFilePath, files };
34
+ }
35
+ export function adoptWorkspace(options) {
36
+ const { dir } = options;
37
+ const packageJson = preparePackageJson(options, []);
38
+ const files = [];
39
+ const write = createWriter(dir, files);
40
+ const blueprintFilePath = write('sanity.blueprint.ts', renderBlueprint({ ...manifestOptions(options), studioSrc: '.', withFunction: false }));
41
+ writePreparedRootFiles(dir, packageJson, files);
42
+ return { blueprintFilePath, files };
43
+ }
44
+ function manifestOptions(options) {
45
+ return {
46
+ projectName: options.stackName,
47
+ displayName: options.displayName,
48
+ projectId: options.projectId,
49
+ datasetName: options.datasetName,
50
+ studioName: options.studioName,
51
+ studioOrigin: options.studioOrigin,
52
+ };
53
+ }
54
+ function assertTargetsAvailable(dir, paths) {
55
+ const conflicts = paths.filter((path) => existsSync(join(dir, path)));
56
+ if (conflicts.length > 0) {
57
+ throw new Error(`Refusing to overwrite existing paths: ${conflicts.join(', ')}`);
58
+ }
59
+ }
60
+ function createWriter(dir, files) {
61
+ return (relativePath, content) => {
62
+ const filePath = join(dir, relativePath);
63
+ mkdirSync(dirname(filePath), { recursive: true });
64
+ writeFileSync(filePath, content);
65
+ files.push(relativePath);
66
+ return filePath;
67
+ };
68
+ }
69
+ function writePreparedRootFiles(dir, packageJson, files) {
70
+ if (writeGitignore(dir))
71
+ files.push('.gitignore');
72
+ writeFileSync(join(dir, 'package.json'), formatJson(packageJson));
73
+ files.push('package.json');
74
+ }
75
+ function writeGitignore(dir) {
76
+ const filePath = join(dir, '.gitignore');
77
+ if (!existsSync(filePath)) {
78
+ writeFileSync(filePath, DEFAULT_GITIGNORE);
79
+ return true;
80
+ }
81
+ const existing = readFileSync(filePath, 'utf8');
82
+ if (existing.split(/\r?\n/).some((line) => line.trim() === '.sanity/'))
83
+ return false;
84
+ writeFileSync(filePath, `${existing.trimEnd()}\n\n${GITIGNORE_ENTRY}\n`);
85
+ return true;
86
+ }
87
+ function preparePackageJson(options, requiredWorkspaces) {
88
+ const filePath = join(options.dir, 'package.json');
89
+ const parsed = existsSync(filePath)
90
+ ? JSON.parse(readFileSync(filePath, 'utf8'))
91
+ : {
92
+ name: options.stackName,
93
+ version: '0.1.0',
94
+ private: true,
95
+ type: 'module',
96
+ };
97
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
98
+ throw new Error('The existing package.json must contain a JSON object.');
99
+ }
100
+ const packageJson = parsed;
101
+ addWorkspaces(packageJson, requiredWorkspaces);
102
+ addBlueprintsDependency(packageJson, options.blueprintsVersion);
103
+ return packageJson;
104
+ }
105
+ function addWorkspaces(packageJson, required) {
106
+ if (required.length === 0)
107
+ return;
108
+ if (!packageJson.workspaces) {
109
+ packageJson.workspaces = required;
110
+ return;
111
+ }
112
+ if (Array.isArray(packageJson.workspaces)) {
113
+ packageJson.workspaces = [...new Set([...packageJson.workspaces, ...required])];
114
+ return;
115
+ }
116
+ if (!packageJson.workspaces || typeof packageJson.workspaces !== 'object') {
117
+ throw new Error('Cannot add generated packages to the existing package.json workspaces.');
118
+ }
119
+ const workspaces = packageJson.workspaces;
120
+ if (Array.isArray(workspaces.packages)) {
121
+ workspaces.packages = [...new Set([...workspaces.packages, ...required])];
122
+ return;
123
+ }
124
+ throw new Error('Cannot add generated packages to the existing package.json workspaces.');
125
+ }
126
+ function addBlueprintsDependency(packageJson, blueprintsVersion) {
127
+ const dependencies = recordOrEmpty(packageJson.dependencies);
128
+ const devDependencies = recordOrEmpty(packageJson.devDependencies);
129
+ if (dependencies['@sanity/blueprints'] || devDependencies['@sanity/blueprints'])
130
+ return;
131
+ packageJson.devDependencies = {
132
+ ...devDependencies,
133
+ '@sanity/blueprints': blueprintsVersion,
134
+ };
135
+ }
136
+ function recordOrEmpty(value) {
137
+ return value && typeof value === 'object' && !Array.isArray(value)
138
+ ? value
139
+ : {};
140
+ }
141
+ function formatJson(value) {
142
+ return `${JSON.stringify(value, null, 2)}\n`;
143
+ }
@@ -0,0 +1,15 @@
1
+ import type { Logger } from '../../utils/logger.js';
2
+ import type { ActionResponse } from '../../utils/types.js';
3
+ export interface Dataset {
4
+ name: string;
5
+ aclMode: string;
6
+ }
7
+ interface ListDatasetsResponse extends ActionResponse {
8
+ datasets: Dataset[];
9
+ }
10
+ export declare function listDatasets({ token, projectId, logger, }: {
11
+ token: string;
12
+ projectId: string;
13
+ logger: Logger;
14
+ }): Promise<ListDatasetsResponse>;
15
+ export {};
@@ -0,0 +1,26 @@
1
+ import { createTracedFetch } from '../../utils/traced-fetch.js';
2
+ import { projectsApiPath } from './projects.js';
3
+ export async function listDatasets({ token, projectId, logger, }) {
4
+ const fetchFn = createTracedFetch(logger);
5
+ const url = `${projectsApiPath}/${encodeURIComponent(projectId)}/datasets`;
6
+ const response = await fetchFn(url, {
7
+ method: 'GET',
8
+ headers: {
9
+ Authorization: `Bearer ${token}`,
10
+ 'Content-Type': 'application/json',
11
+ },
12
+ });
13
+ const body = await response.json();
14
+ if (!response.ok) {
15
+ return {
16
+ ok: false,
17
+ error: body.error?.message ?? response.statusText,
18
+ datasets: [],
19
+ };
20
+ }
21
+ return {
22
+ ok: true,
23
+ error: null,
24
+ datasets: body,
25
+ };
26
+ }