@sanity/runtime-cli 17.2.0-next.ga4b9ae7 → 17.3.0-blueprinit.gaf4f184

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 (41) hide show
  1. package/README.md +35 -26
  2. package/SKILL.md +21 -0
  3. package/dist/actions/blueprinit/manifest.d.ts +13 -0
  4. package/dist/actions/blueprinit/manifest.js +57 -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 +15 -0
  10. package/dist/actions/blueprinit/workspace.js +142 -0
  11. package/dist/actions/sanity/datasets.d.ts +15 -0
  12. package/dist/actions/sanity/datasets.js +26 -0
  13. package/dist/baseCommands.d.ts +7 -0
  14. package/dist/baseCommands.js +21 -0
  15. package/dist/commands/blueprinit.d.ts +14 -0
  16. package/dist/commands/blueprinit.js +45 -0
  17. package/dist/commands/functions/env/add.d.ts +3 -0
  18. package/dist/commands/functions/env/add.js +6 -2
  19. package/dist/commands/functions/env/list.d.ts +3 -0
  20. package/dist/commands/functions/env/list.js +9 -3
  21. package/dist/commands/functions/env/remove.d.ts +3 -0
  22. package/dist/commands/functions/env/remove.js +9 -3
  23. package/dist/cores/blueprinit/adopt.d.ts +6 -0
  24. package/dist/cores/blueprinit/adopt.js +90 -0
  25. package/dist/cores/blueprinit/finish.d.ts +14 -0
  26. package/dist/cores/blueprinit/finish.js +36 -0
  27. package/dist/cores/blueprinit/fresh.d.ts +3 -0
  28. package/dist/cores/blueprinit/fresh.js +88 -0
  29. package/dist/cores/blueprinit/prompts.d.ts +4 -0
  30. package/dist/cores/blueprinit/prompts.js +14 -0
  31. package/dist/cores/blueprinit/selection.d.ts +15 -0
  32. package/dist/cores/blueprinit/selection.js +63 -0
  33. package/dist/cores/blueprinit/types.d.ts +13 -0
  34. package/dist/cores/blueprinit/types.js +1 -0
  35. package/dist/cores/blueprinit.d.ts +7 -0
  36. package/dist/cores/blueprinit.js +39 -0
  37. package/dist/utils/command-skills.d.ts +2 -0
  38. package/dist/utils/command-skills.js +17 -0
  39. package/oclif.manifest.json +1263 -957
  40. package/package.json +5 -3
  41. package/skills/blueprinit.md +137 -0
package/README.md CHANGED
@@ -20,7 +20,7 @@ $ npm install -g @sanity/runtime-cli
20
20
  $ sanity-run COMMAND
21
21
  running command...
22
22
  $ sanity-run (--version)
23
- @sanity/runtime-cli/17.2.0-next.ga4b9ae7 linux-x64 node-v24.18.0
23
+ @sanity/runtime-cli/17.3.0-blueprinit.gaf4f184 linux-x64 node-v24.18.0
24
24
  $ sanity-run --help [COMMAND]
25
25
  USAGE
26
26
  $ sanity-run COMMAND
@@ -103,7 +103,7 @@ EXAMPLES
103
103
  $ sanity-run blueprints add function --name my-function --fn-type document-create --fn-type document-update --lang js
104
104
  ```
105
105
 
106
- _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.2.0-next.ga4b9ae7/src/commands/blueprints/add.ts)_
106
+ _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.0-blueprinit.gaf4f184/src/commands/blueprints/add.ts)_
107
107
 
108
108
  ## `sanity-run blueprints config`
109
109
 
@@ -140,7 +140,7 @@ EXAMPLES
140
140
  $ sanity-run blueprints config --edit --project-id <projectId> --stack <name-or-id>
141
141
  ```
142
142
 
143
- _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v17.2.0-next.ga4b9ae7/src/commands/blueprints/config.ts)_
143
+ _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.0-blueprinit.gaf4f184/src/commands/blueprints/config.ts)_
144
144
 
145
145
  ## `sanity-run blueprints deploy`
146
146
 
@@ -194,7 +194,7 @@ EXAMPLES
194
194
  $ sanity-run blueprints deploy --new-stack-name <new-name>
195
195
  ```
196
196
 
197
- _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.2.0-next.ga4b9ae7/src/commands/blueprints/deploy.ts)_
197
+ _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.0-blueprinit.gaf4f184/src/commands/blueprints/deploy.ts)_
198
198
 
199
199
  ## `sanity-run blueprints destroy`
200
200
 
@@ -232,7 +232,7 @@ EXAMPLES
232
232
  $ sanity-run blueprints destroy --stack <name-or-id> --project-id <projectId> --force --no-wait
233
233
  ```
234
234
 
235
- _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.2.0-next.ga4b9ae7/src/commands/blueprints/destroy.ts)_
235
+ _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.0-blueprinit.gaf4f184/src/commands/blueprints/destroy.ts)_
236
236
 
237
237
  ## `sanity-run blueprints doctor`
238
238
 
@@ -264,7 +264,7 @@ EXAMPLES
264
264
  $ sanity-run blueprints doctor --fix
265
265
  ```
266
266
 
267
- _See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v17.2.0-next.ga4b9ae7/src/commands/blueprints/doctor.ts)_
267
+ _See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.0-blueprinit.gaf4f184/src/commands/blueprints/doctor.ts)_
268
268
 
269
269
  ## `sanity-run blueprints info`
270
270
 
@@ -301,7 +301,7 @@ EXAMPLES
301
301
  $ sanity-run blueprints info --organization-id <orgId> --stack <name-or-id>
302
302
  ```
303
303
 
304
- _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v17.2.0-next.ga4b9ae7/src/commands/blueprints/info.ts)_
304
+ _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.0-blueprinit.gaf4f184/src/commands/blueprints/info.ts)_
305
305
 
306
306
  ## `sanity-run blueprints init [DIR]`
307
307
 
@@ -365,7 +365,7 @@ EXAMPLES
365
365
  $ sanity-run blueprints init old-stack --type <json|js|ts> --project-id <projectId> --stack-id <existingStackId>
366
366
  ```
367
367
 
368
- _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v17.2.0-next.ga4b9ae7/src/commands/blueprints/init.ts)_
368
+ _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.0-blueprinit.gaf4f184/src/commands/blueprints/init.ts)_
369
369
 
370
370
  ## `sanity-run blueprints logs`
371
371
 
@@ -411,7 +411,7 @@ EXAMPLES
411
411
  $ sanity-run blueprints logs --before 2026-05-01T00:00:00Z
412
412
  ```
413
413
 
414
- _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.2.0-next.ga4b9ae7/src/commands/blueprints/logs.ts)_
414
+ _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.0-blueprinit.gaf4f184/src/commands/blueprints/logs.ts)_
415
415
 
416
416
  ## `sanity-run blueprints mint-deploy-token`
417
417
 
@@ -456,7 +456,7 @@ EXAMPLES
456
456
  $ sanity-run blueprints mint-deploy-token --organization-id <orgId>
457
457
  ```
458
458
 
459
- _See code: [src/commands/blueprints/mint-deploy-token.ts](https://github.com/sanity-io/runtime-cli/blob/v17.2.0-next.ga4b9ae7/src/commands/blueprints/mint-deploy-token.ts)_
459
+ _See code: [src/commands/blueprints/mint-deploy-token.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.0-blueprinit.gaf4f184/src/commands/blueprints/mint-deploy-token.ts)_
460
460
 
461
461
  ## `sanity-run blueprints plan`
462
462
 
@@ -489,7 +489,7 @@ EXAMPLES
489
489
  $ sanity-run blueprints plan --organization-id <orgId> --stack <name-or-id>
490
490
  ```
491
491
 
492
- _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v17.2.0-next.ga4b9ae7/src/commands/blueprints/plan.ts)_
492
+ _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.0-blueprinit.gaf4f184/src/commands/blueprints/plan.ts)_
493
493
 
494
494
  ## `sanity-run blueprints promote`
495
495
 
@@ -525,7 +525,7 @@ EXAMPLES
525
525
  $ sanity-run blueprints promote --new-stack-name <new-name>
526
526
  ```
527
527
 
528
- _See code: [src/commands/blueprints/promote.ts](https://github.com/sanity-io/runtime-cli/blob/v17.2.0-next.ga4b9ae7/src/commands/blueprints/promote.ts)_
528
+ _See code: [src/commands/blueprints/promote.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.0-blueprinit.gaf4f184/src/commands/blueprints/promote.ts)_
529
529
 
530
530
  ## `sanity-run blueprints stacks`
531
531
 
@@ -561,7 +561,7 @@ EXAMPLES
561
561
  $ sanity-run blueprints stacks --organization-id <organizationId> --include-projects
562
562
  ```
563
563
 
564
- _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v17.2.0-next.ga4b9ae7/src/commands/blueprints/stacks.ts)_
564
+ _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.0-blueprinit.gaf4f184/src/commands/blueprints/stacks.ts)_
565
565
 
566
566
  ## `sanity-run functions add`
567
567
 
@@ -613,7 +613,7 @@ EXAMPLES
613
613
  $ sanity-run functions add --name my-function --type document-create --type document-update --lang js
614
614
  ```
615
615
 
616
- _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.2.0-next.ga4b9ae7/src/commands/functions/add.ts)_
616
+ _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.0-blueprinit.gaf4f184/src/commands/functions/add.ts)_
617
617
 
618
618
  ## `sanity-run functions dev`
619
619
 
@@ -648,7 +648,7 @@ EXAMPLES
648
648
  $ sanity-run functions dev --timeout 60
649
649
  ```
650
650
 
651
- _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v17.2.0-next.ga4b9ae7/src/commands/functions/dev.ts)_
651
+ _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.0-blueprinit.gaf4f184/src/commands/functions/dev.ts)_
652
652
 
653
653
  ## `sanity-run functions env add NAME KEY VALUE`
654
654
 
@@ -656,7 +656,7 @@ Add or set an environment variable for a deployed function
656
656
 
657
657
  ```
658
658
  USAGE
659
- $ sanity-run functions env add NAME KEY VALUE [--json]
659
+ $ sanity-run functions env add NAME KEY VALUE [--json] [--stack <value>]
660
660
 
661
661
  ARGUMENTS
662
662
  NAME The name of the Sanity Function
@@ -664,7 +664,8 @@ ARGUMENTS
664
664
  VALUE The value of the environment variable
665
665
 
666
666
  FLAGS
667
- --json Format output as json
667
+ --json Format output as json
668
+ --stack=<value> Stack name or ID to use instead of the locally configured Stack
668
669
 
669
670
  DESCRIPTION
670
671
  Add or set an environment variable for a deployed function
@@ -676,9 +677,11 @@ DESCRIPTION
676
677
 
677
678
  EXAMPLES
678
679
  $ sanity-run functions env add MyFunction API_URL https://api.example.com/
680
+
681
+ $ sanity-run functions env add --stack <name-or-id> MyFunction API_URL https://api.example.com/
679
682
  ```
680
683
 
681
- _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.2.0-next.ga4b9ae7/src/commands/functions/env/add.ts)_
684
+ _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.0-blueprinit.gaf4f184/src/commands/functions/env/add.ts)_
682
685
 
683
686
  ## `sanity-run functions env list NAME`
684
687
 
@@ -686,13 +689,14 @@ List environment variables for a deployed function
686
689
 
687
690
  ```
688
691
  USAGE
689
- $ sanity-run functions env list NAME [--json]
692
+ $ sanity-run functions env list NAME [--json] [--stack <value>]
690
693
 
691
694
  ARGUMENTS
692
695
  NAME The name of the Sanity Function
693
696
 
694
697
  FLAGS
695
- --json Format output as json
698
+ --json Format output as json
699
+ --stack=<value> Stack name or ID to use instead of the locally configured Stack
696
700
 
697
701
  DESCRIPTION
698
702
  List environment variables for a deployed function
@@ -703,9 +707,11 @@ DESCRIPTION
703
707
 
704
708
  EXAMPLES
705
709
  $ sanity-run functions env list MyFunction
710
+
711
+ $ sanity-run functions env list --stack <name-or-id> MyFunction
706
712
  ```
707
713
 
708
- _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v17.2.0-next.ga4b9ae7/src/commands/functions/env/list.ts)_
714
+ _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.0-blueprinit.gaf4f184/src/commands/functions/env/list.ts)_
709
715
 
710
716
  ## `sanity-run functions env remove NAME KEY`
711
717
 
@@ -713,14 +719,15 @@ Remove an environment variable from a deployed function
713
719
 
714
720
  ```
715
721
  USAGE
716
- $ sanity-run functions env remove NAME KEY [--json]
722
+ $ sanity-run functions env remove NAME KEY [--json] [--stack <value>]
717
723
 
718
724
  ARGUMENTS
719
725
  NAME The name of the Sanity Function
720
726
  KEY The name of the environment variable
721
727
 
722
728
  FLAGS
723
- --json Format output as json
729
+ --json Format output as json
730
+ --stack=<value> Stack name or ID to use instead of the locally configured Stack
724
731
 
725
732
  DESCRIPTION
726
733
  Remove an environment variable from a deployed function
@@ -732,9 +739,11 @@ DESCRIPTION
732
739
 
733
740
  EXAMPLES
734
741
  $ sanity-run functions env remove MyFunction API_URL
742
+
743
+ $ sanity-run functions env remove --stack <name-or-id> MyFunction API_URL
735
744
  ```
736
745
 
737
- _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v17.2.0-next.ga4b9ae7/src/commands/functions/env/remove.ts)_
746
+ _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.0-blueprinit.gaf4f184/src/commands/functions/env/remove.ts)_
738
747
 
739
748
  ## `sanity-run functions logs [NAME]`
740
749
 
@@ -774,7 +783,7 @@ EXAMPLES
774
783
  $ sanity-run functions logs <name> --delete
775
784
  ```
776
785
 
777
- _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.2.0-next.ga4b9ae7/src/commands/functions/logs.ts)_
786
+ _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.0-blueprinit.gaf4f184/src/commands/functions/logs.ts)_
778
787
 
779
788
  ## `sanity-run functions test [NAME]`
780
789
 
@@ -831,7 +840,7 @@ EXAMPLES
831
840
  $ sanity-run functions test <name> --event update --data-before '{ "title": "before" }' --data-after '{ "title": "after" }'
832
841
  ```
833
842
 
834
- _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v17.2.0-next.ga4b9ae7/src/commands/functions/test.ts)_
843
+ _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v17.3.0-blueprinit.gaf4f184/src/commands/functions/test.ts)_
835
844
 
836
845
  ## `sanity-run help [COMMAND]`
837
846
 
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,13 @@
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
+ withFunction: boolean;
11
+ }
12
+ export declare function renderBlueprint(options: BlueprintManifestOptions): string;
13
+ export declare function slugify(value: string): string;
@@ -0,0 +1,57 @@
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, withFunction } = options;
6
+ const definers = ['defineBlueprint', 'defineDataset', 'defineProject', 'defineStudio'];
7
+ if (withFunction)
8
+ definers.splice(2, 0, 'defineDocumentFunction');
9
+ const functionResource = withFunction
10
+ ? ` defineDocumentFunction({
11
+ name: ${quote(STARTER_FUNCTION_NAME)},
12
+ project: ${quote(projectId)},
13
+ event: {
14
+ on: ['create', 'update'],
15
+ filter: "_type == '${STARTER_DOCUMENT_TYPE}' && !defined(slug.current)",
16
+ projection: '{_id, title, slug}',
17
+ },
18
+ }),
19
+ `
20
+ : '';
21
+ // Project references stay literal until the deploy API can resolve resource references here.
22
+ return `import {${definers.join(', ')}} from '@sanity/blueprints'
23
+
24
+ export default defineBlueprint({
25
+ resources: [
26
+ defineProject({
27
+ name: ${quote(projectName)},
28
+ displayName: ${quote(displayName)},
29
+ lifecycle: {
30
+ deletionPolicy: 'retain',
31
+ ownershipAction: {type: 'attach', id: ${quote(projectId)}},
32
+ },
33
+ }),
34
+ defineDataset({
35
+ name: ${quote(datasetName)},
36
+ project: ${quote(projectId)},
37
+ lifecycle: {
38
+ deletionPolicy: 'retain',
39
+ ownershipAction: {type: 'attach', id: ${quote(datasetName)}, projectId: ${quote(projectId)}},
40
+ },
41
+ }),
42
+ defineStudio({
43
+ name: ${quote(studioName)},
44
+ src: ${quote(studioSrc)},
45
+ autoUpdates: {enabled: true},
46
+ project: ${quote(projectId)},
47
+ }),
48
+ ${functionResource} ],
49
+ })
50
+ `;
51
+ }
52
+ export function slugify(value) {
53
+ return value
54
+ .toLowerCase()
55
+ .replace(/[^a-z0-9]+/g, '-')
56
+ .replace(/^-+|-+$/g, '');
57
+ }
@@ -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,15 @@
1
+ export interface WorkspaceOptions {
2
+ dir: string;
3
+ projectId: string;
4
+ displayName: string;
5
+ datasetName: string;
6
+ stackName: string;
7
+ studioName: string;
8
+ blueprintsVersion: string;
9
+ }
10
+ export interface WorkspaceResult {
11
+ blueprintFilePath: string;
12
+ files: string[];
13
+ }
14
+ export declare function scaffoldWorkspace(options: WorkspaceOptions): WorkspaceResult;
15
+ export declare function adoptWorkspace(options: WorkspaceOptions): WorkspaceResult;
@@ -0,0 +1,142 @@
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
+ };
52
+ }
53
+ function assertTargetsAvailable(dir, paths) {
54
+ const conflicts = paths.filter((path) => existsSync(join(dir, path)));
55
+ if (conflicts.length > 0) {
56
+ throw new Error(`Refusing to overwrite existing paths: ${conflicts.join(', ')}`);
57
+ }
58
+ }
59
+ function createWriter(dir, files) {
60
+ return (relativePath, content) => {
61
+ const filePath = join(dir, relativePath);
62
+ mkdirSync(dirname(filePath), { recursive: true });
63
+ writeFileSync(filePath, content);
64
+ files.push(relativePath);
65
+ return filePath;
66
+ };
67
+ }
68
+ function writePreparedRootFiles(dir, packageJson, files) {
69
+ if (writeGitignore(dir))
70
+ files.push('.gitignore');
71
+ writeFileSync(join(dir, 'package.json'), formatJson(packageJson));
72
+ files.push('package.json');
73
+ }
74
+ function writeGitignore(dir) {
75
+ const filePath = join(dir, '.gitignore');
76
+ if (!existsSync(filePath)) {
77
+ writeFileSync(filePath, DEFAULT_GITIGNORE);
78
+ return true;
79
+ }
80
+ const existing = readFileSync(filePath, 'utf8');
81
+ if (existing.split(/\r?\n/).some((line) => line.trim() === '.sanity/'))
82
+ return false;
83
+ writeFileSync(filePath, `${existing.trimEnd()}\n\n${GITIGNORE_ENTRY}\n`);
84
+ return true;
85
+ }
86
+ function preparePackageJson(options, requiredWorkspaces) {
87
+ const filePath = join(options.dir, 'package.json');
88
+ const parsed = existsSync(filePath)
89
+ ? JSON.parse(readFileSync(filePath, 'utf8'))
90
+ : {
91
+ name: options.stackName,
92
+ version: '0.1.0',
93
+ private: true,
94
+ type: 'module',
95
+ };
96
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
97
+ throw new Error('The existing package.json must contain a JSON object.');
98
+ }
99
+ const packageJson = parsed;
100
+ addWorkspaces(packageJson, requiredWorkspaces);
101
+ addBlueprintsDependency(packageJson, options.blueprintsVersion);
102
+ return packageJson;
103
+ }
104
+ function addWorkspaces(packageJson, required) {
105
+ if (required.length === 0)
106
+ return;
107
+ if (!packageJson.workspaces) {
108
+ packageJson.workspaces = required;
109
+ return;
110
+ }
111
+ if (Array.isArray(packageJson.workspaces)) {
112
+ packageJson.workspaces = [...new Set([...packageJson.workspaces, ...required])];
113
+ return;
114
+ }
115
+ if (!packageJson.workspaces || typeof packageJson.workspaces !== 'object') {
116
+ throw new Error('Cannot add generated packages to the existing package.json workspaces.');
117
+ }
118
+ const workspaces = packageJson.workspaces;
119
+ if (Array.isArray(workspaces.packages)) {
120
+ workspaces.packages = [...new Set([...workspaces.packages, ...required])];
121
+ return;
122
+ }
123
+ throw new Error('Cannot add generated packages to the existing package.json workspaces.');
124
+ }
125
+ function addBlueprintsDependency(packageJson, blueprintsVersion) {
126
+ const dependencies = recordOrEmpty(packageJson.dependencies);
127
+ const devDependencies = recordOrEmpty(packageJson.devDependencies);
128
+ if (dependencies['@sanity/blueprints'] || devDependencies['@sanity/blueprints'])
129
+ return;
130
+ packageJson.devDependencies = {
131
+ ...devDependencies,
132
+ '@sanity/blueprints': blueprintsVersion,
133
+ };
134
+ }
135
+ function recordOrEmpty(value) {
136
+ return value && typeof value === 'object' && !Array.isArray(value)
137
+ ? value
138
+ : {};
139
+ }
140
+ function formatJson(value) {
141
+ return `${JSON.stringify(value, null, 2)}\n`;
142
+ }