@sanity/runtime-cli 17.4.0-blueprinit.g0ac8878 → 17.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -47
- package/SKILL.md +50 -12
- package/dist/actions/blueprints/pre-deploy.d.ts +15 -6
- package/dist/actions/blueprints/pre-deploy.js +29 -70
- package/dist/actions/blueprints/process-assets.d.ts +29 -0
- package/dist/actions/blueprints/process-assets.js +80 -0
- package/dist/commands/blueprints/deploy.js +3 -1
- package/dist/commands/blueprints/destroy.js +1 -1
- package/dist/commands/blueprints/info.d.ts +1 -0
- package/dist/commands/blueprints/info.js +5 -0
- package/dist/commands/blueprints/init.js +1 -2
- package/dist/commands/blueprints/logs.js +1 -0
- package/dist/commands/functions/add.js +3 -3
- package/dist/commands/functions/dev.js +2 -2
- package/dist/commands/functions/logs.d.ts +1 -1
- package/dist/commands/functions/logs.js +3 -0
- package/dist/cores/blueprints/deploy.js +69 -19
- package/dist/cores/functions/add.js +1 -1
- package/dist/utils/display/blueprints-formatting.js +3 -3
- package/dist/utils/style-text.js +1 -0
- package/oclif.manifest.json +20 -143
- package/package.json +27 -17
- package/skills/_functions-build.md +23 -0
- package/skills/_functions-list.md +28 -0
- package/skills/blueprints-add.md +8 -0
- package/skills/blueprints-config.md +33 -0
- package/skills/blueprints-deploy.md +36 -0
- package/skills/blueprints-destroy.md +30 -0
- package/skills/blueprints-doctor.md +31 -0
- package/skills/blueprints-info.md +23 -0
- package/skills/blueprints-init.md +48 -0
- package/skills/blueprints-logs.md +33 -0
- package/skills/blueprints-mint-deploy-token.md +25 -0
- package/skills/blueprints-plan.md +28 -0
- package/skills/blueprints-promote.md +31 -0
- package/skills/blueprints-stacks.md +28 -0
- package/skills/functions-add.md +35 -0
- package/skills/functions-dev.md +21 -0
- package/skills/functions-env-add.md +29 -0
- package/skills/functions-env-list.md +21 -0
- package/skills/functions-env-remove.md +22 -0
- package/skills/functions-logs.md +39 -0
- package/skills/functions-test.md +31 -0
- package/dist/actions/blueprinit/manifest.d.ts +0 -15
- package/dist/actions/blueprinit/manifest.js +0 -76
- package/dist/actions/blueprinit/studio.d.ts +0 -6
- package/dist/actions/blueprinit/studio.js +0 -43
- package/dist/actions/blueprinit/templates.d.ts +0 -34
- package/dist/actions/blueprinit/templates.js +0 -83
- package/dist/actions/blueprinit/workspace.d.ts +0 -16
- package/dist/actions/blueprinit/workspace.js +0 -143
- package/dist/actions/sanity/datasets.d.ts +0 -15
- package/dist/actions/sanity/datasets.js +0 -26
- package/dist/commands/blueprinit.d.ts +0 -17
- package/dist/commands/blueprinit.js +0 -59
- package/dist/cores/blueprinit/adopt.d.ts +0 -6
- package/dist/cores/blueprinit/adopt.js +0 -119
- package/dist/cores/blueprinit/finish.d.ts +0 -14
- package/dist/cores/blueprinit/finish.js +0 -64
- package/dist/cores/blueprinit/fresh.d.ts +0 -3
- package/dist/cores/blueprinit/fresh.js +0 -120
- package/dist/cores/blueprinit/preview.d.ts +0 -15
- package/dist/cores/blueprinit/preview.js +0 -14
- package/dist/cores/blueprinit/prompts.d.ts +0 -5
- package/dist/cores/blueprinit/prompts.js +0 -22
- package/dist/cores/blueprinit/selection.d.ts +0 -15
- package/dist/cores/blueprinit/selection.js +0 -90
- package/dist/cores/blueprinit/types.d.ts +0 -16
- package/dist/cores/blueprinit/types.js +0 -1
- package/dist/cores/blueprinit.d.ts +0 -7
- package/dist/cores/blueprinit.js +0 -48
- package/skills/blueprinit.md +0 -155
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# `npx @sanity/cli functions env remove`
|
|
2
|
+
|
|
3
|
+
Delete one environment variable from a deployed Function. The command does not ask for confirmation and does not redeploy the Stack.
|
|
4
|
+
|
|
5
|
+
Run from the Blueprint directory or a subdirectory; the manifest is required even though the command only touches remote state.
|
|
6
|
+
|
|
7
|
+
## Required checks
|
|
8
|
+
|
|
9
|
+
1. Obtain authorization to remove the key.
|
|
10
|
+
2. Resolve the exact Function and Stack.
|
|
11
|
+
3. Run `functions env list` and confirm that the key is present.
|
|
12
|
+
4. Check whether the Function still requires the variable.
|
|
13
|
+
|
|
14
|
+
## Remove
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
npx @sanity/cli functions env remove process-order LEGACY_API_URL --json
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Override the locally configured Stack with `--stack NAME_OR_ID`.
|
|
21
|
+
|
|
22
|
+
The JSON result contains the Function name and removed key, not the old value. If the value may be needed again, arrange a user-approved backup before removal; this command cannot retrieve it.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# `npx @sanity/cli functions logs`
|
|
2
|
+
|
|
3
|
+
Fetch, stream, or delete logs for one deployed Function. Always provide the Function name; omitting it only prints valid names and command help.
|
|
4
|
+
|
|
5
|
+
Run from the Blueprint directory or a subdirectory; the manifest supplies the valid Function names.
|
|
6
|
+
|
|
7
|
+
## Fetch logs
|
|
8
|
+
|
|
9
|
+
Prefer a bounded, structured query for agent work:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npx @sanity/cli functions logs process-order --limit 100 --json
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`--limit` defaults to 50. Entries without both a level and a message are dropped, so a returned count can be lower than the limit even when more logs exist.
|
|
16
|
+
|
|
17
|
+
When entries are found, the JSON result contains a `logs` array of `{time, requestId, level, message}`. Human-readable output drops `requestId` and prints local timestamps unless `--utc` is passed. Pass `--stack NAME_OR_ID` to override the locally configured Stack.
|
|
18
|
+
|
|
19
|
+
Logs may contain payload data or application secrets. Summarize only what is needed and do not reproduce credentials or personal data.
|
|
20
|
+
|
|
21
|
+
## Stream logs
|
|
22
|
+
|
|
23
|
+
`--watch` (alias `--follow`, `-w`) keeps running until interrupted and emits human-readable output:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
npx @sanity/cli functions logs process-order --watch
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`--watch` cannot be combined with `--json`. Streamed timestamps are always UTC, so `--utc` is ignored. Use watch mode only when continuous monitoring is requested, and stop it when monitoring is complete.
|
|
30
|
+
|
|
31
|
+
## Delete logs
|
|
32
|
+
|
|
33
|
+
Deleting removes all logs for the Function. Obtain explicit authorization and confirm the Function and Stack first:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
npx @sanity/cli functions logs process-order --delete
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The command prompts (defaulting to no) unless `--force` is supplied, and `--force` requires `--delete`. Use `--force` only for an authorized non-interactive deletion. `--delete` is rejected alongside `--limit` or `--json`.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# `npx @sanity/cli functions test`
|
|
2
|
+
|
|
3
|
+
Invoke a local Function with a test payload. Local execution can still make network requests or mutate external systems, so inspect the handler and payload before running it.
|
|
4
|
+
|
|
5
|
+
## Run a bounded test
|
|
6
|
+
|
|
7
|
+
Always provide the Function name. Prefer a JSON file for nontrivial payloads:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npx @sanity/cli functions test process-order --file payload.json --json
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Use `--data '{"orderId":"order-123"}'` for a small inline JSON object. `--data`, `--file`, and `--document-id` are alternative input modes, and each must resolve to a JSON object. File paths are joined to the current working directory, so pass relative paths.
|
|
14
|
+
|
|
15
|
+
Payloads reach document and media-library Functions only. Scheduled and sync-tag-invalidation Functions are invoked with a fixed scheduled event, and any payload flag is accepted and then ignored.
|
|
16
|
+
|
|
17
|
+
For document and media-library Functions, the event defaults to `create` and also accepts `update` and `delete`. For an update event, provide one complete before/after pair:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
npx @sanity/cli functions test process-order --event update --file-before before.json --file-after after.json --json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The Blueprint's `event.filter` and `event.projection` are applied to the payload before the handler runs, and a payload the filter excludes is skipped rather than executed. `--timeout SECONDS` overrides the Function's configured `timeout` for this run (default 10 seconds).
|
|
24
|
+
|
|
25
|
+
## Remote data and credentials
|
|
26
|
+
|
|
27
|
+
`--document-id` fetches a document (`--project-id`, `--dataset`) or a media-library asset (`--media-library-id`) with the CLI's stored token before local execution. `--api` sets the API version used for that fetch.
|
|
28
|
+
|
|
29
|
+
`--with-user-token` injects the authenticated CLI token into the Function context. Use it only for trusted local code when authenticated API behavior is part of the test. Never include the token in payloads or output.
|
|
30
|
+
|
|
31
|
+
The JSON result contains logs and any Function response. Review logs for sensitive data before sharing them.
|
|
@@ -1,15 +0,0 @@
|
|
|
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;
|
|
@@ -1,76 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,83 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,16 +0,0 @@
|
|
|
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;
|
|
@@ -1,143 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
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 {};
|
|
@@ -1,26 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { RuntimeCommand } from '../baseCommands.js';
|
|
2
|
-
export default class BlueprinitCommand extends RuntimeCommand<typeof BlueprinitCommand> {
|
|
3
|
-
static hidden: boolean;
|
|
4
|
-
static summary: string;
|
|
5
|
-
static description: string;
|
|
6
|
-
static examples: string[];
|
|
7
|
-
static flags: {
|
|
8
|
-
llms: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
|
-
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
-
'organization-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
-
'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
-
dataset: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
-
'stack-name': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
-
'studio-name': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
-
};
|
|
16
|
-
run(): Promise<Record<string, unknown> | undefined>;
|
|
17
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { Flags } from '@oclif/core';
|
|
2
|
-
import { llmsFlagConfig } from '@sanity-labs/oclif-plugin-skills-flag';
|
|
3
|
-
import { RuntimeCommand } from '../baseCommands.js';
|
|
4
|
-
import { blueprinitCore } from '../cores/blueprinit.js';
|
|
5
|
-
import { Logger } from '../utils/logger.js';
|
|
6
|
-
export default class BlueprinitCommand extends RuntimeCommand {
|
|
7
|
-
static hidden = true;
|
|
8
|
-
static summary = 'Experimental blueprints-first project init';
|
|
9
|
-
static description = `Scaffolds a Blueprint, Studio, and starter function around an existing Sanity project.
|
|
10
|
-
|
|
11
|
-
This hidden command is an experiment; its interface and generated files are not stable.`;
|
|
12
|
-
static examples = [
|
|
13
|
-
'<%= config.bin %> <%= command.id %>',
|
|
14
|
-
'<%= config.bin %> <%= command.id %> --yes',
|
|
15
|
-
'<%= config.bin %> <%= command.id %> --project-id <id> --dataset <name> --yes',
|
|
16
|
-
];
|
|
17
|
-
static flags = {
|
|
18
|
-
llms: Flags.boolean(llmsFlagConfig),
|
|
19
|
-
yes: Flags.boolean({
|
|
20
|
-
char: 'y',
|
|
21
|
-
description: 'Skip confirmation prompts',
|
|
22
|
-
default: false,
|
|
23
|
-
}),
|
|
24
|
-
'organization-id': Flags.string({
|
|
25
|
-
description: 'Organization to use when the account has several',
|
|
26
|
-
aliases: ['organization', 'organizationId', 'org'],
|
|
27
|
-
}),
|
|
28
|
-
'project-id': Flags.string({
|
|
29
|
-
description: 'Project to use when the organization has several',
|
|
30
|
-
aliases: ['project', 'projectId'],
|
|
31
|
-
}),
|
|
32
|
-
dataset: Flags.string({
|
|
33
|
-
description: 'Dataset to attach',
|
|
34
|
-
}),
|
|
35
|
-
'stack-name': Flags.string({
|
|
36
|
-
description: 'Name for the new organization-scoped Stack',
|
|
37
|
-
}),
|
|
38
|
-
'studio-name': Flags.string({
|
|
39
|
-
description: 'Name and hosted slug for the Studio resource',
|
|
40
|
-
}),
|
|
41
|
-
};
|
|
42
|
-
async run() {
|
|
43
|
-
const result = await blueprinitCore({
|
|
44
|
-
bin: this.config.bin,
|
|
45
|
-
log: Logger(this.log.bind(this), this.flags),
|
|
46
|
-
flags: {
|
|
47
|
-
yes: this.flags.yes,
|
|
48
|
-
organizationId: this.flags['organization-id'],
|
|
49
|
-
projectId: this.flags['project-id'],
|
|
50
|
-
datasetName: this.flags.dataset,
|
|
51
|
-
stackName: this.flags['stack-name'],
|
|
52
|
-
studioName: this.flags['studio-name'],
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
if (!result.success)
|
|
56
|
-
this.coreError(result);
|
|
57
|
-
return result.json;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { DetectedStudio } from '../../actions/blueprinit/studio.js';
|
|
2
|
-
import type { CoreResult } from '../index.js';
|
|
3
|
-
import type { BlueprinitFlowOptions } from './types.js';
|
|
4
|
-
export declare function adoptFlow(options: BlueprinitFlowOptions & {
|
|
5
|
-
studio: DetectedStudio;
|
|
6
|
-
}): Promise<CoreResult>;
|