@sanity/runtime-cli 17.5.0 → 17.6.0-blueprinit.g77b7a64

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 (49) hide show
  1. package/README.md +25 -25
  2. package/dist/actions/blueprinit/manifest.d.ts +15 -0
  3. package/dist/actions/blueprinit/manifest.js +82 -0
  4. package/dist/actions/blueprinit/studio.d.ts +6 -0
  5. package/dist/actions/blueprinit/studio.js +43 -0
  6. package/dist/actions/blueprinit/templates.d.ts +34 -0
  7. package/dist/actions/blueprinit/templates.js +83 -0
  8. package/dist/actions/blueprinit/workspace.d.ts +16 -0
  9. package/dist/actions/blueprinit/workspace.js +143 -0
  10. package/dist/actions/blueprints/blueprint.js +2 -1
  11. package/dist/actions/blueprints/resources.js +17 -1
  12. package/dist/actions/sanity/datasets.d.ts +15 -0
  13. package/dist/actions/sanity/datasets.js +26 -0
  14. package/dist/commands/init.d.ts +17 -0
  15. package/dist/commands/init.js +59 -0
  16. package/dist/constants.d.ts +5 -0
  17. package/dist/constants.js +6 -0
  18. package/dist/cores/blueprinit/adopt.d.ts +6 -0
  19. package/dist/cores/blueprinit/adopt.js +119 -0
  20. package/dist/cores/blueprinit/finish.d.ts +14 -0
  21. package/dist/cores/blueprinit/finish.js +64 -0
  22. package/dist/cores/blueprinit/fresh.d.ts +3 -0
  23. package/dist/cores/blueprinit/fresh.js +120 -0
  24. package/dist/cores/blueprinit/preview.d.ts +15 -0
  25. package/dist/cores/blueprinit/preview.js +14 -0
  26. package/dist/cores/blueprinit/prompts.d.ts +5 -0
  27. package/dist/cores/blueprinit/prompts.js +22 -0
  28. package/dist/cores/blueprinit/selection.d.ts +15 -0
  29. package/dist/cores/blueprinit/selection.js +90 -0
  30. package/dist/cores/blueprinit/types.d.ts +16 -0
  31. package/dist/cores/blueprinit/types.js +1 -0
  32. package/dist/cores/blueprinit.d.ts +7 -0
  33. package/dist/cores/blueprinit.js +48 -0
  34. package/dist/cores/blueprints/deploy.js +2 -0
  35. package/dist/cores/blueprints/plan.js +2 -0
  36. package/dist/cores/functions/add.js +10 -7
  37. package/dist/server/static/components/api-base.js +1 -0
  38. package/dist/server/static/components/filters.js +4 -2
  39. package/dist/server/static/components/payload-panel.js +3 -0
  40. package/dist/utils/blueprints/extract-durable-config.d.ts +8 -0
  41. package/dist/utils/blueprints/extract-durable-config.js +60 -0
  42. package/dist/utils/blueprints/validate-resources.js +5 -2
  43. package/dist/utils/display/blueprints-formatting.js +5 -1
  44. package/dist/utils/display/resources-formatting.js +2 -2
  45. package/dist/utils/types.d.ts +8 -2
  46. package/dist/utils/types.js +8 -2
  47. package/oclif.manifest.json +132 -1
  48. package/package.json +2 -2
  49. package/skills/init.md +155 -0
package/README.md CHANGED
@@ -45,7 +45,7 @@ All `sanity-run` commands can also be run as `sanity <command>` when `@sanity/cl
45
45
  USAGE
46
46
  $ sanity-run blueprints add TYPE [--json] [--example <value> | -n <value> | --fn-type
47
47
  document-publish|document-create|document-delete|document-update|media-library-asset-create|media-library-asset-dele
48
- te|media-library-asset-update|scheduled-function|sync-tag-invalidate... | --language ts|js | --javascript |
48
+ te|media-library-asset-update|pub-sub|scheduled-function|sync-tag-invalidate... | --language ts|js | --javascript |
49
49
  --fn-helpers | --fn-installer skip|npm|pnpm|yarn] [-i | ]
50
50
 
51
51
  ARGUMENTS
@@ -62,8 +62,8 @@ FLAGS
62
62
  --fn-type=<option>... Document change event(s) that should trigger the function; you can specify multiple
63
63
  events by specifying this flag multiple times
64
64
  <options: document-publish|document-create|document-delete|document-update|media-library-
65
- asset-create|media-library-asset-delete|media-library-asset-update|scheduled-function|syn
66
- c-tag-invalidate>
65
+ asset-create|media-library-asset-delete|media-library-asset-update|pub-sub|scheduled-func
66
+ tion|sync-tag-invalidate>
67
67
  --javascript Use JavaScript instead of TypeScript
68
68
  --json Format output as json
69
69
  --language=<option> [default: ts] Language of the new function
@@ -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.5.0/src/commands/blueprints/add.ts)_
93
+ _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.6.0-blueprinit.g77b7a64/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.5.0/src/commands/blueprints/config.ts)_
130
+ _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v17.6.0-blueprinit.g77b7a64/src/commands/blueprints/config.ts)_
131
131
 
132
132
  ## `sanity-run blueprints deploy`
133
133
 
@@ -183,7 +183,7 @@ EXAMPLES
183
183
  $ sanity-run blueprints deploy --new-stack-name <new-name>
184
184
  ```
185
185
 
186
- _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.5.0/src/commands/blueprints/deploy.ts)_
186
+ _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.6.0-blueprinit.g77b7a64/src/commands/blueprints/deploy.ts)_
187
187
 
188
188
  ## `sanity-run blueprints destroy`
189
189
 
@@ -221,7 +221,7 @@ EXAMPLES
221
221
  $ sanity-run blueprints destroy --stack <name-or-id> --project-id <projectId> --force --no-wait
222
222
  ```
223
223
 
224
- _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.5.0/src/commands/blueprints/destroy.ts)_
224
+ _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.6.0-blueprinit.g77b7a64/src/commands/blueprints/destroy.ts)_
225
225
 
226
226
  ## `sanity-run blueprints doctor`
227
227
 
@@ -253,7 +253,7 @@ EXAMPLES
253
253
  $ sanity-run blueprints doctor --fix
254
254
  ```
255
255
 
256
- _See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v17.5.0/src/commands/blueprints/doctor.ts)_
256
+ _See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v17.6.0-blueprinit.g77b7a64/src/commands/blueprints/doctor.ts)_
257
257
 
258
258
  ## `sanity-run blueprints info`
259
259
 
@@ -291,7 +291,7 @@ EXAMPLES
291
291
  $ sanity-run blueprints info --organization-id <orgId> --stack <name-or-id>
292
292
  ```
293
293
 
294
- _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v17.5.0/src/commands/blueprints/info.ts)_
294
+ _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v17.6.0-blueprinit.g77b7a64/src/commands/blueprints/info.ts)_
295
295
 
296
296
  ## `sanity-run blueprints init [DIR]`
297
297
 
@@ -355,7 +355,7 @@ EXAMPLES
355
355
  $ sanity-run blueprints init old-stack --type <json|js|ts> --project-id <projectId> --stack-id <existingStackId>
356
356
  ```
357
357
 
358
- _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v17.5.0/src/commands/blueprints/init.ts)_
358
+ _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v17.6.0-blueprinit.g77b7a64/src/commands/blueprints/init.ts)_
359
359
 
360
360
  ## `sanity-run blueprints logs`
361
361
 
@@ -401,7 +401,7 @@ EXAMPLES
401
401
  $ sanity-run blueprints logs --before 2026-05-01T00:00:00Z
402
402
  ```
403
403
 
404
- _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.5.0/src/commands/blueprints/logs.ts)_
404
+ _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.6.0-blueprinit.g77b7a64/src/commands/blueprints/logs.ts)_
405
405
 
406
406
  ## `sanity-run blueprints mint-deploy-token`
407
407
 
@@ -446,7 +446,7 @@ EXAMPLES
446
446
  $ sanity-run blueprints mint-deploy-token --organization-id <orgId>
447
447
  ```
448
448
 
449
- _See code: [src/commands/blueprints/mint-deploy-token.ts](https://github.com/sanity-io/runtime-cli/blob/v17.5.0/src/commands/blueprints/mint-deploy-token.ts)_
449
+ _See code: [src/commands/blueprints/mint-deploy-token.ts](https://github.com/sanity-io/runtime-cli/blob/v17.6.0-blueprinit.g77b7a64/src/commands/blueprints/mint-deploy-token.ts)_
450
450
 
451
451
  ## `sanity-run blueprints plan`
452
452
 
@@ -479,7 +479,7 @@ EXAMPLES
479
479
  $ sanity-run blueprints plan --organization-id <orgId> --stack <name-or-id>
480
480
  ```
481
481
 
482
- _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v17.5.0/src/commands/blueprints/plan.ts)_
482
+ _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v17.6.0-blueprinit.g77b7a64/src/commands/blueprints/plan.ts)_
483
483
 
484
484
  ## `sanity-run blueprints promote`
485
485
 
@@ -515,7 +515,7 @@ EXAMPLES
515
515
  $ sanity-run blueprints promote --new-stack-name <new-name>
516
516
  ```
517
517
 
518
- _See code: [src/commands/blueprints/promote.ts](https://github.com/sanity-io/runtime-cli/blob/v17.5.0/src/commands/blueprints/promote.ts)_
518
+ _See code: [src/commands/blueprints/promote.ts](https://github.com/sanity-io/runtime-cli/blob/v17.6.0-blueprinit.g77b7a64/src/commands/blueprints/promote.ts)_
519
519
 
520
520
  ## `sanity-run blueprints stacks`
521
521
 
@@ -551,7 +551,7 @@ EXAMPLES
551
551
  $ sanity-run blueprints stacks --organization-id <organizationId> --include-projects
552
552
  ```
553
553
 
554
- _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v17.5.0/src/commands/blueprints/stacks.ts)_
554
+ _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v17.6.0-blueprinit.g77b7a64/src/commands/blueprints/stacks.ts)_
555
555
 
556
556
  ## `sanity-run functions add`
557
557
 
@@ -561,7 +561,7 @@ Add a Function to your Blueprint
561
561
  USAGE
562
562
  $ sanity-run functions add [--json] [--example <value> | -n <value> | --type
563
563
  document-publish|document-create|document-delete|document-update|media-library-asset-create|media-library-asset-dele
564
- te|media-library-asset-update|scheduled-function|sync-tag-invalidate... | --language ts|js | --javascript |
564
+ te|media-library-asset-update|pub-sub|scheduled-function|sync-tag-invalidate... | --language ts|js | --javascript |
565
565
  --helpers | --installer skip|npm|pnpm|yarn | -i]
566
566
 
567
567
  FLAGS
@@ -578,8 +578,8 @@ FLAGS
578
578
  --type=<option>... Document change event(s) that should trigger the function; you can specify multiple events
579
579
  by specifying this flag multiple times
580
580
  <options: document-publish|document-create|document-delete|document-update|media-library-ass
581
- et-create|media-library-asset-delete|media-library-asset-update|scheduled-function|sync-tag-
582
- invalidate>
581
+ et-create|media-library-asset-delete|media-library-asset-update|pub-sub|scheduled-function|s
582
+ ync-tag-invalidate>
583
583
 
584
584
  DESCRIPTION
585
585
  Add a Function to your Blueprint
@@ -603,7 +603,7 @@ EXAMPLES
603
603
  $ sanity-run functions add --name my-function --type document-create --type document-update --lang js
604
604
  ```
605
605
 
606
- _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.5.0/src/commands/functions/add.ts)_
606
+ _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.6.0-blueprinit.g77b7a64/src/commands/functions/add.ts)_
607
607
 
608
608
  ## `sanity-run functions dev`
609
609
 
@@ -638,7 +638,7 @@ EXAMPLES
638
638
  $ sanity-run functions dev --timeout 60
639
639
  ```
640
640
 
641
- _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v17.5.0/src/commands/functions/dev.ts)_
641
+ _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v17.6.0-blueprinit.g77b7a64/src/commands/functions/dev.ts)_
642
642
 
643
643
  ## `sanity-run functions env add NAME KEY VALUE`
644
644
 
@@ -671,7 +671,7 @@ EXAMPLES
671
671
  $ sanity-run functions env add --stack <name-or-id> MyFunction API_URL https://api.example.com/
672
672
  ```
673
673
 
674
- _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.5.0/src/commands/functions/env/add.ts)_
674
+ _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.6.0-blueprinit.g77b7a64/src/commands/functions/env/add.ts)_
675
675
 
676
676
  ## `sanity-run functions env list NAME`
677
677
 
@@ -701,7 +701,7 @@ EXAMPLES
701
701
  $ sanity-run functions env list --stack <name-or-id> MyFunction
702
702
  ```
703
703
 
704
- _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v17.5.0/src/commands/functions/env/list.ts)_
704
+ _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v17.6.0-blueprinit.g77b7a64/src/commands/functions/env/list.ts)_
705
705
 
706
706
  ## `sanity-run functions env remove NAME KEY`
707
707
 
@@ -733,7 +733,7 @@ EXAMPLES
733
733
  $ sanity-run functions env remove --stack <name-or-id> MyFunction API_URL
734
734
  ```
735
735
 
736
- _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v17.5.0/src/commands/functions/env/remove.ts)_
736
+ _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v17.6.0-blueprinit.g77b7a64/src/commands/functions/env/remove.ts)_
737
737
 
738
738
  ## `sanity-run functions logs [NAME]`
739
739
 
@@ -773,7 +773,7 @@ EXAMPLES
773
773
  $ sanity-run functions logs <name> --delete
774
774
  ```
775
775
 
776
- _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.5.0/src/commands/functions/logs.ts)_
776
+ _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.6.0-blueprinit.g77b7a64/src/commands/functions/logs.ts)_
777
777
 
778
778
  ## `sanity-run functions test [NAME]`
779
779
 
@@ -830,7 +830,7 @@ EXAMPLES
830
830
  $ sanity-run functions test <name> --event update --data-before '{ "title": "before" }' --data-after '{ "title": "after" }'
831
831
  ```
832
832
 
833
- _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v17.5.0/src/commands/functions/test.ts)_
833
+ _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v17.6.0-blueprinit.g77b7a64/src/commands/functions/test.ts)_
834
834
 
835
835
  ## `sanity-run help [COMMAND]`
836
836
 
@@ -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,82 @@
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
+ 'defineBlueprint',
8
+ 'defineCorsOrigin',
9
+ 'defineDataset',
10
+ 'defineProject',
11
+ 'defineStudio',
12
+ ];
13
+ if (withFunction)
14
+ definers.splice(3, 0, 'defineDocumentFunction');
15
+ const project = quote(`$.resources.${projectName}.id`);
16
+ const inlineImport = `import {${definers.join(', ')}} from '@sanity/blueprints'`;
17
+ const imports = inlineImport.length > 100
18
+ ? `import {\n${definers.map((definer) => ` ${definer},`).join('\n')}\n} from '@sanity/blueprints'`
19
+ : inlineImport;
20
+ const functionResource = withFunction
21
+ ? ` defineDocumentFunction({
22
+ name: ${quote(STARTER_FUNCTION_NAME)},
23
+ displayName: 'Set slug',
24
+ project: ${project},
25
+ event: {
26
+ on: ['create', 'update'],
27
+ filter: "_type == '${STARTER_DOCUMENT_TYPE}' && !defined(slug.current)",
28
+ projection: '{_id, title, slug}',
29
+ },
30
+ }),
31
+ `
32
+ : '';
33
+ return `${imports}
34
+
35
+ export default defineBlueprint({
36
+ resources: [
37
+ defineProject({
38
+ name: ${quote(projectName)},
39
+ displayName: ${quote(displayName)},
40
+ lifecycle: {
41
+ deletionPolicy: 'retain',
42
+ ownershipAction: {type: 'attach', id: ${quote(projectId)}},
43
+ },
44
+ }),
45
+ defineDataset({
46
+ project: ${project},
47
+ name: ${quote(datasetName)},
48
+ lifecycle: {
49
+ deletionPolicy: 'retain',
50
+ ownershipAction: {
51
+ type: 'attach',
52
+ id: ${quote(datasetName)},
53
+ projectId: ${project},
54
+ },
55
+ },
56
+ }),
57
+ defineCorsOrigin({
58
+ project: ${project},
59
+ name: ${quote(`${studioName}-cors`)},
60
+ origin: ${quote(studioOrigin)},
61
+ allowCredentials: true,
62
+ }),
63
+ defineStudio({
64
+ project: ${project},
65
+ name: ${quote(studioName)},
66
+ title: ${quote(displayName)},
67
+ src: ${quote(studioSrc)},
68
+ }),
69
+ ${functionResource} ],
70
+ })
71
+ `;
72
+ }
73
+ export function hostedStudioOrigin(studioName, sanityEnv) {
74
+ const hostname = sanityEnv?.toLowerCase() === 'staging' ? 'studio.sanity.work' : 'sanity.studio';
75
+ return `https://${studioName}.${hostname}`;
76
+ }
77
+ export function slugify(value) {
78
+ return value
79
+ .toLowerCase()
80
+ .replace(/[^a-z0-9]+/g, '-')
81
+ .replace(/^-+|-+$/g, '');
82
+ }
@@ -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
+ }
@@ -5,7 +5,7 @@ import { pathToFileURL } from 'node:url';
5
5
  import blueprintParserValidator from '@sanity/blueprints-parser';
6
6
  import * as find from 'empathic/find';
7
7
  import { createJiti } from 'jiti';
8
- import { SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULED, } from '../../constants.js';
8
+ import { SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_DURABLE, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULED, } from '../../constants.js';
9
9
  import { validateResources } from '../../utils/blueprints/validate-resources.js';
10
10
  import { backfillProjectBasedStackId, readConfigFile, } from './config.js';
11
11
  import { resolveIds } from './resolve.js';
@@ -122,6 +122,7 @@ export function parseBlueprintContent(rawBlueprint, options) {
122
122
  SANITY_FUNCTION_DOCUMENT,
123
123
  SANITY_FUNCTION_MEDIA_LIBRARY_ASSET,
124
124
  SANITY_FUNCTION_SCHEDULED,
125
+ SANITY_FUNCTION_DURABLE,
125
126
  ],
126
127
  });
127
128
  const parsedBlueprint = parserResult.result === 'valid' ? parserResult.blueprint : undefined;