@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.
- package/README.md +19 -19
- package/SKILL.md +21 -0
- package/dist/actions/blueprinit/manifest.d.ts +15 -0
- package/dist/actions/blueprinit/manifest.js +76 -0
- package/dist/actions/blueprinit/studio.d.ts +6 -0
- package/dist/actions/blueprinit/studio.js +43 -0
- package/dist/actions/blueprinit/templates.d.ts +34 -0
- package/dist/actions/blueprinit/templates.js +83 -0
- package/dist/actions/blueprinit/workspace.d.ts +16 -0
- package/dist/actions/blueprinit/workspace.js +143 -0
- package/dist/actions/sanity/datasets.d.ts +15 -0
- package/dist/actions/sanity/datasets.js +26 -0
- package/dist/commands/blueprinit.d.ts +17 -0
- package/dist/commands/blueprinit.js +59 -0
- package/dist/cores/blueprinit/adopt.d.ts +6 -0
- package/dist/cores/blueprinit/adopt.js +119 -0
- package/dist/cores/blueprinit/finish.d.ts +14 -0
- package/dist/cores/blueprinit/finish.js +64 -0
- package/dist/cores/blueprinit/fresh.d.ts +3 -0
- package/dist/cores/blueprinit/fresh.js +120 -0
- package/dist/cores/blueprinit/preview.d.ts +15 -0
- package/dist/cores/blueprinit/preview.js +14 -0
- package/dist/cores/blueprinit/prompts.d.ts +5 -0
- package/dist/cores/blueprinit/prompts.js +22 -0
- package/dist/cores/blueprinit/selection.d.ts +15 -0
- package/dist/cores/blueprinit/selection.js +90 -0
- package/dist/cores/blueprinit/types.d.ts +16 -0
- package/dist/cores/blueprinit/types.js +1 -0
- package/dist/cores/blueprinit.d.ts +7 -0
- package/dist/cores/blueprinit.js +48 -0
- package/oclif.manifest.json +129 -1
- package/package.json +7 -3
- 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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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,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
|
+
}
|