@sanity/runtime-cli 17.8.0-next.g5972da4 → 17.9.0-blueprinit.g14aaebd
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 +21 -20
- package/dist/actions/blueprinit/manifest.d.ts +46 -0
- package/dist/actions/blueprinit/manifest.js +121 -0
- package/dist/actions/blueprinit/quote.d.ts +6 -0
- package/dist/actions/blueprinit/quote.js +8 -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 +73 -0
- package/dist/actions/blueprinit/templates.js +138 -0
- package/dist/actions/blueprinit/versions.d.ts +8 -0
- package/dist/actions/blueprinit/versions.js +11 -0
- package/dist/actions/blueprinit/workspace.d.ts +27 -0
- package/dist/actions/blueprinit/workspace.js +145 -0
- package/dist/actions/blueprints/pre-deploy.js +2 -0
- package/dist/actions/blueprints/stacks.d.ts +1 -0
- package/dist/actions/sanity/create-project.d.ts +19 -0
- package/dist/actions/sanity/create-project.js +32 -0
- package/dist/actions/sanity/datasets.d.ts +15 -0
- package/dist/actions/sanity/datasets.js +26 -0
- package/dist/commands/functions/test.d.ts +1 -0
- package/dist/commands/functions/test.js +5 -0
- package/dist/commands/init.d.ts +21 -0
- package/dist/commands/init.js +82 -0
- package/dist/cores/blueprinit/adopt.d.ts +6 -0
- package/dist/cores/blueprinit/adopt.js +137 -0
- package/dist/cores/blueprinit/columns.d.ts +26 -0
- package/dist/cores/blueprinit/columns.js +27 -0
- package/dist/cores/blueprinit/finish.d.ts +15 -0
- package/dist/cores/blueprinit/finish.js +72 -0
- package/dist/cores/blueprinit/fresh.d.ts +3 -0
- package/dist/cores/blueprinit/fresh.js +219 -0
- package/dist/cores/blueprinit/naming.d.ts +27 -0
- package/dist/cores/blueprinit/naming.js +43 -0
- package/dist/cores/blueprinit/preview.d.ts +34 -0
- package/dist/cores/blueprinit/preview.js +110 -0
- package/dist/cores/blueprinit/project-choice.d.ts +28 -0
- package/dist/cores/blueprinit/project-choice.js +82 -0
- package/dist/cores/blueprinit/prompts.d.ts +13 -0
- package/dist/cores/blueprinit/prompts.js +42 -0
- package/dist/cores/blueprinit/results.d.ts +8 -0
- package/dist/cores/blueprinit/results.js +4 -0
- package/dist/cores/blueprinit/selection.d.ts +15 -0
- package/dist/cores/blueprinit/selection.js +90 -0
- package/dist/cores/blueprinit/stack-name.d.ts +39 -0
- package/dist/cores/blueprinit/stack-name.js +56 -0
- package/dist/cores/blueprinit/target.d.ts +33 -0
- package/dist/cores/blueprinit/target.js +80 -0
- package/dist/cores/blueprinit/types.d.ts +20 -0
- package/dist/cores/blueprinit/types.js +1 -0
- package/dist/cores/blueprinit.d.ts +7 -0
- package/dist/cores/blueprinit.js +64 -0
- package/dist/cores/functions/test.js +41 -26
- package/dist/utils/display/intro.d.ts +6 -0
- package/dist/utils/display/intro.js +63 -0
- package/dist/utils/functions/child-process-wrapper.js +3 -2
- package/dist/utils/functions/child-runtime/durable.d.ts +2 -2
- package/dist/utils/functions/child-runtime/durable.js +52 -2
- package/dist/utils/functions/durable-logs.d.ts +16 -0
- package/dist/utils/functions/durable-logs.js +52 -0
- package/dist/utils/functions/invoke/local.js +2 -1
- package/dist/utils/types.d.ts +18 -4
- package/oclif.manifest.json +159 -1
- package/package.json +1 -1
- package/skills/init.md +199 -0
|
@@ -41,7 +41,7 @@ process.on('message', async (data) => {
|
|
|
41
41
|
|
|
42
42
|
// Otherwise this is the initial start message. Support the legacy untyped
|
|
43
43
|
// `{srcPath, payload}` shape as well as the new `{type: 'start', ...}`.
|
|
44
|
-
const {srcPath, payload, isDurable} = jsonData
|
|
44
|
+
const {srcPath, payload, isDurable, noWait} = jsonData
|
|
45
45
|
const {context, ...event} = payload
|
|
46
46
|
|
|
47
47
|
let logs = ''
|
|
@@ -83,9 +83,10 @@ process.on('message', async (data) => {
|
|
|
83
83
|
if (isDurable) {
|
|
84
84
|
json = await runDurable(
|
|
85
85
|
handler,
|
|
86
|
-
{context, event
|
|
86
|
+
{context, event},
|
|
87
87
|
{
|
|
88
88
|
emit: (e) => process.send(JSON.stringify({type: 'step-event', event: e})),
|
|
89
|
+
wait: (ms) => new Promise((resolve) => setTimeout(resolve, noWait ? 0 : ms)),
|
|
89
90
|
},
|
|
90
91
|
)
|
|
91
92
|
} else {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type {DurableContext, DurableHandler, DurableLogger, GenericEvent} from '@sanity/functions'
|
|
2
|
-
import type {
|
|
2
|
+
import type {DurableRuntimeEvent} from '../../types.js'
|
|
3
3
|
|
|
4
4
|
type RunDurableArgs = {
|
|
5
5
|
context: DurableContext
|
|
@@ -8,7 +8,7 @@ type RunDurableArgs = {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
type DurableIO = {
|
|
11
|
-
emit: (event:
|
|
11
|
+
emit: (event: DurableRuntimeEvent) => void
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function runDurable(
|
|
@@ -1,17 +1,60 @@
|
|
|
1
1
|
import {performance} from 'node:perf_hooks'
|
|
2
|
+
import {inspect} from 'node:util'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param duration
|
|
7
|
+
* @return {number}
|
|
8
|
+
*/
|
|
9
|
+
const durationToMs = (duration = {}) => {
|
|
10
|
+
const {days = 0, hours = 0, minutes = 0, seconds = 0} = duration
|
|
11
|
+
return (((days * 24 + hours) * 60 + minutes) * 60 + seconds) * 1000
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
* @param args
|
|
17
|
+
*/
|
|
18
|
+
const formatArgs = (args) =>
|
|
19
|
+
args.map((a) => (typeof a === 'string' ? a : inspect(a, {depth: null}))).join(' ')
|
|
2
20
|
|
|
3
21
|
/**
|
|
4
22
|
* Runs a durable function handler
|
|
5
23
|
* @param handler - callback being run
|
|
6
24
|
* @param context - data to send to the operation
|
|
7
25
|
* @param event - the step being run
|
|
8
|
-
* @param logger
|
|
9
26
|
* @param io
|
|
10
27
|
*/
|
|
11
|
-
export async function runDurable(handler, {context, event
|
|
28
|
+
export async function runDurable(handler, {context, event}, io) {
|
|
29
|
+
let currentStep = null
|
|
30
|
+
const streamLog = (level, args) => {
|
|
31
|
+
const date = new Date()
|
|
32
|
+
const timestamp = `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`
|
|
33
|
+
|
|
34
|
+
return io.emit({
|
|
35
|
+
timestamp,
|
|
36
|
+
kind: 'log',
|
|
37
|
+
phase: 'log',
|
|
38
|
+
step: currentStep,
|
|
39
|
+
level,
|
|
40
|
+
message: formatArgs(args),
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// @todo: need to throw error for use of `console.log` in a durable function
|
|
45
|
+
const logger = {
|
|
46
|
+
log: (...a) => streamLog('info', a),
|
|
47
|
+
info: (...a) => streamLog('info', a),
|
|
48
|
+
warn: (...a) => streamLog('warn', a),
|
|
49
|
+
error: (...a) => streamLog('error', a),
|
|
50
|
+
}
|
|
51
|
+
|
|
12
52
|
const track = async (name, kind, produce) => {
|
|
13
53
|
io.emit({name, kind, phase: 'start'})
|
|
14
54
|
const startedAt = performance.now()
|
|
55
|
+
const prev = currentStep
|
|
56
|
+
currentStep = name
|
|
57
|
+
|
|
15
58
|
try {
|
|
16
59
|
const value = await produce()
|
|
17
60
|
io.emit({
|
|
@@ -35,11 +78,18 @@ export async function runDurable(handler, {context, event, logger}, io) {
|
|
|
35
78
|
durationMs: performance.now() - startedAt,
|
|
36
79
|
})
|
|
37
80
|
throw err
|
|
81
|
+
} finally {
|
|
82
|
+
currentStep = prev
|
|
38
83
|
}
|
|
39
84
|
}
|
|
40
85
|
|
|
41
86
|
const step = {
|
|
42
87
|
run: ({name, handler: fn}) => track(name || 'step-run', 'run', () => fn({logger, attempt: 1})),
|
|
88
|
+
wait: ({name, duration}) =>
|
|
89
|
+
track(name, 'wait', async () => {
|
|
90
|
+
await io.wait(durationToMs(duration))
|
|
91
|
+
return undefined
|
|
92
|
+
}),
|
|
43
93
|
}
|
|
44
94
|
|
|
45
95
|
return handler({context, event, step, logger})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DurableLogEvent, DurableRuntimeEvent, DurableStepEvent } from '../types.js';
|
|
2
|
+
type DurableLogLine = Pick<DurableLogEvent, 'level' | 'message'>;
|
|
3
|
+
type DurableJsonStep = DurableStepEvent & {
|
|
4
|
+
logs: DurableLogLine[];
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Records a durable log event
|
|
8
|
+
*/
|
|
9
|
+
export declare const createDurableJsonCollector: () => {
|
|
10
|
+
record: (event: DurableRuntimeEvent) => void;
|
|
11
|
+
result: () => {
|
|
12
|
+
steps: DurableJsonStep[];
|
|
13
|
+
logs: DurableLogLine[];
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Records a durable log event
|
|
3
|
+
*/
|
|
4
|
+
export const createDurableJsonCollector = () => {
|
|
5
|
+
const steps = [];
|
|
6
|
+
const logLines = [];
|
|
7
|
+
const activeSteps = new Map();
|
|
8
|
+
function record(event) {
|
|
9
|
+
if (event.kind === 'log') {
|
|
10
|
+
const logLine = {
|
|
11
|
+
level: event.level,
|
|
12
|
+
message: event.message,
|
|
13
|
+
};
|
|
14
|
+
if (event.step === null) {
|
|
15
|
+
logLines.push(logLine);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const step = activeSteps.get(event.step);
|
|
19
|
+
if (step) {
|
|
20
|
+
step.logs.push(logLine);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
logLines.push(logLine);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (event.phase === 'start') {
|
|
27
|
+
const step = {
|
|
28
|
+
...event,
|
|
29
|
+
logs: [],
|
|
30
|
+
};
|
|
31
|
+
steps.push(step);
|
|
32
|
+
activeSteps.set(event.name, step);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const step = activeSteps.get(event.name);
|
|
36
|
+
if (!step)
|
|
37
|
+
return;
|
|
38
|
+
step.phase = event.phase;
|
|
39
|
+
step.durationMs = event.durationMs;
|
|
40
|
+
if (event.phase === 'complete') {
|
|
41
|
+
step.output = event.output;
|
|
42
|
+
}
|
|
43
|
+
if (event.phase === 'error') {
|
|
44
|
+
step.error = event.error;
|
|
45
|
+
}
|
|
46
|
+
activeSteps.delete(event.name);
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
record,
|
|
50
|
+
result: () => ({ steps, logs: logLines }),
|
|
51
|
+
};
|
|
52
|
+
};
|
|
@@ -47,7 +47,7 @@ export default async function invoke(resource, payload, context, options) {
|
|
|
47
47
|
if (!resource.src) {
|
|
48
48
|
throw new Error(`Function resource "${resource.name}" is missing the 'src' property.`);
|
|
49
49
|
}
|
|
50
|
-
const { forceColor = true, timeout = 10, onNestedInvoke, depth = 0, callStack = [], onStepEvent = () => { }, } = options;
|
|
50
|
+
const { forceColor = true, timeout = 10, onNestedInvoke, depth = 0, callStack = [], onStepEvent = () => { }, noWait = false, } = options;
|
|
51
51
|
// `payload.payload` is the event body for every function type: the document for
|
|
52
52
|
// GROQ-based events, whatever the producer sent for the rest. The handler always
|
|
53
53
|
// receives it as `event.data` (see the `payload` built in `start()` below).
|
|
@@ -306,6 +306,7 @@ export default async function invoke(resource, payload, context, options) {
|
|
|
306
306
|
srcPath: functionPath,
|
|
307
307
|
payload,
|
|
308
308
|
isDurable: resource.type === SANITY_FUNCTION_DURABLE,
|
|
309
|
+
noWait,
|
|
309
310
|
};
|
|
310
311
|
child.send(JSON.stringify(startMessage));
|
|
311
312
|
}
|
package/dist/utils/types.d.ts
CHANGED
|
@@ -162,7 +162,9 @@ export interface InvokeExecutionOptions {
|
|
|
162
162
|
/** Function names already on the invoke call stack (innermost last), used to detect cycles. @internal */
|
|
163
163
|
callStack?: string[];
|
|
164
164
|
/** Stream a durable step transition to the caller. @internal */
|
|
165
|
-
onStepEvent?: (event:
|
|
165
|
+
onStepEvent?: (event: DurableRuntimeEvent) => void;
|
|
166
|
+
/** Don't wait the full duration during a wait durable event @internal */
|
|
167
|
+
noWait?: boolean;
|
|
166
168
|
}
|
|
167
169
|
/**
|
|
168
170
|
* Re-entrant callback that executes a named function from the blueprint on behalf
|
|
@@ -181,6 +183,7 @@ export type ParentToChildMessage = {
|
|
|
181
183
|
srcPath: string;
|
|
182
184
|
payload: unknown;
|
|
183
185
|
isDurable?: boolean;
|
|
186
|
+
noWait?: boolean;
|
|
184
187
|
} | {
|
|
185
188
|
type: 'invoke-response';
|
|
186
189
|
id: string;
|
|
@@ -195,7 +198,7 @@ export type ParentToChildMessage = {
|
|
|
195
198
|
error: InvocationError;
|
|
196
199
|
} | {
|
|
197
200
|
type: 'step-event';
|
|
198
|
-
event:
|
|
201
|
+
event: DurableRuntimeEvent;
|
|
199
202
|
};
|
|
200
203
|
/**
|
|
201
204
|
* IPC messages sent from a function child process back up to the parent.
|
|
@@ -217,7 +220,7 @@ export type ChildToParentMessage = {
|
|
|
217
220
|
error?: InvocationError;
|
|
218
221
|
} | {
|
|
219
222
|
type: 'step-event';
|
|
220
|
-
event:
|
|
223
|
+
event: DurableRuntimeEvent;
|
|
221
224
|
};
|
|
222
225
|
/** @internal */
|
|
223
226
|
export interface InvocationResponse {
|
|
@@ -301,7 +304,7 @@ export type Warning = {
|
|
|
301
304
|
suggestions: string[];
|
|
302
305
|
};
|
|
303
306
|
/** @internal */
|
|
304
|
-
export type DurableOperationSteps = 'run';
|
|
307
|
+
export type DurableOperationSteps = 'run' | 'wait';
|
|
305
308
|
/** @internal */
|
|
306
309
|
export type DurableStepPhase = 'start' | 'complete' | 'error';
|
|
307
310
|
/**
|
|
@@ -315,6 +318,15 @@ export interface DurableStepEvent {
|
|
|
315
318
|
error?: InvocationError;
|
|
316
319
|
durationMs?: number;
|
|
317
320
|
}
|
|
321
|
+
export interface DurableLogEvent {
|
|
322
|
+
kind: 'log';
|
|
323
|
+
phase: 'log';
|
|
324
|
+
step: string | null;
|
|
325
|
+
level: 'info' | 'warn' | 'error';
|
|
326
|
+
message: string;
|
|
327
|
+
timestamp?: string;
|
|
328
|
+
}
|
|
329
|
+
export type DurableRuntimeEvent = DurableStepEvent | DurableLogEvent;
|
|
318
330
|
export interface FunctionTestFlags {
|
|
319
331
|
data?: string;
|
|
320
332
|
event?: string;
|
|
@@ -333,5 +345,7 @@ export interface FunctionTestFlags {
|
|
|
333
345
|
'document-id-before'?: string;
|
|
334
346
|
'document-id-after'?: string;
|
|
335
347
|
'media-library-id'?: string;
|
|
348
|
+
'no-wait'?: boolean;
|
|
349
|
+
json?: boolean;
|
|
336
350
|
}
|
|
337
351
|
export {};
|
package/oclif.manifest.json
CHANGED
|
@@ -1,5 +1,156 @@
|
|
|
1
1
|
{
|
|
2
2
|
"commands": {
|
|
3
|
+
"init": {
|
|
4
|
+
"aliases": [],
|
|
5
|
+
"args": {
|
|
6
|
+
"directory": {
|
|
7
|
+
"description": "Directory to initialize. Interactive runs are asked for one instead.",
|
|
8
|
+
"name": "directory",
|
|
9
|
+
"required": false
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"description": "Scaffolds a Blueprint, Studio, and starter function around a Sanity project.\n\nUse a project you already have, or create one with --create-project. A new project has no\ndatasets, so the Blueprint creates the dataset rather than attaching it.\n\nPass a directory to scaffold into it. Without one, an interactive run asks where the files go.\n\nThis hidden command is an experiment; its interface and generated files are not stable.",
|
|
13
|
+
"examples": [
|
|
14
|
+
"<%= config.bin %> <%= command.id %>",
|
|
15
|
+
"<%= config.bin %> <%= command.id %> ./my-workspace",
|
|
16
|
+
"<%= config.bin %> <%= command.id %> --yes",
|
|
17
|
+
"<%= config.bin %> <%= command.id %> --project-id <id> --dataset <name> --yes",
|
|
18
|
+
"<%= config.bin %> <%= command.id %> --create-project \"My Shop\" --yes"
|
|
19
|
+
],
|
|
20
|
+
"flags": {
|
|
21
|
+
"json": {
|
|
22
|
+
"description": "Format output as json",
|
|
23
|
+
"name": "json",
|
|
24
|
+
"allowNo": false,
|
|
25
|
+
"type": "boolean"
|
|
26
|
+
},
|
|
27
|
+
"path": {
|
|
28
|
+
"aliases": [
|
|
29
|
+
"blueprint-path"
|
|
30
|
+
],
|
|
31
|
+
"char": "p",
|
|
32
|
+
"description": "Path to a Blueprint file or directory containing one",
|
|
33
|
+
"env": "SANITY_BLUEPRINT_PATH",
|
|
34
|
+
"hidden": true,
|
|
35
|
+
"name": "path",
|
|
36
|
+
"hasDynamicHelp": false,
|
|
37
|
+
"multiple": false,
|
|
38
|
+
"type": "option"
|
|
39
|
+
},
|
|
40
|
+
"trace": {
|
|
41
|
+
"description": "Trace output",
|
|
42
|
+
"hidden": true,
|
|
43
|
+
"name": "trace",
|
|
44
|
+
"allowNo": false,
|
|
45
|
+
"type": "boolean"
|
|
46
|
+
},
|
|
47
|
+
"validate-resources": {
|
|
48
|
+
"description": "Validate resources",
|
|
49
|
+
"hidden": true,
|
|
50
|
+
"name": "validate-resources",
|
|
51
|
+
"allowNo": true,
|
|
52
|
+
"type": "boolean"
|
|
53
|
+
},
|
|
54
|
+
"verbose": {
|
|
55
|
+
"description": "Verbose output",
|
|
56
|
+
"hidden": true,
|
|
57
|
+
"name": "verbose",
|
|
58
|
+
"allowNo": false,
|
|
59
|
+
"type": "boolean"
|
|
60
|
+
},
|
|
61
|
+
"llms": {
|
|
62
|
+
"aliases": [
|
|
63
|
+
"skill"
|
|
64
|
+
],
|
|
65
|
+
"description": "Show agent-oriented guidance for this command",
|
|
66
|
+
"hidden": true,
|
|
67
|
+
"name": "llms",
|
|
68
|
+
"allowNo": false,
|
|
69
|
+
"type": "boolean"
|
|
70
|
+
},
|
|
71
|
+
"yes": {
|
|
72
|
+
"char": "y",
|
|
73
|
+
"description": "Skip confirmation prompts",
|
|
74
|
+
"name": "yes",
|
|
75
|
+
"allowNo": false,
|
|
76
|
+
"type": "boolean"
|
|
77
|
+
},
|
|
78
|
+
"organization-id": {
|
|
79
|
+
"aliases": [
|
|
80
|
+
"organization",
|
|
81
|
+
"organizationId",
|
|
82
|
+
"org"
|
|
83
|
+
],
|
|
84
|
+
"description": "Organization to use when the account has several",
|
|
85
|
+
"name": "organization-id",
|
|
86
|
+
"hasDynamicHelp": false,
|
|
87
|
+
"multiple": false,
|
|
88
|
+
"type": "option"
|
|
89
|
+
},
|
|
90
|
+
"project-id": {
|
|
91
|
+
"aliases": [
|
|
92
|
+
"project",
|
|
93
|
+
"projectId"
|
|
94
|
+
],
|
|
95
|
+
"description": "Project to use when the organization has several",
|
|
96
|
+
"name": "project-id",
|
|
97
|
+
"hasDynamicHelp": false,
|
|
98
|
+
"multiple": false,
|
|
99
|
+
"type": "option"
|
|
100
|
+
},
|
|
101
|
+
"create-project": {
|
|
102
|
+
"aliases": [
|
|
103
|
+
"createProject",
|
|
104
|
+
"new-project"
|
|
105
|
+
],
|
|
106
|
+
"description": "Create a project with this display name instead of using an existing one",
|
|
107
|
+
"exclusive": [
|
|
108
|
+
"project-id"
|
|
109
|
+
],
|
|
110
|
+
"name": "create-project",
|
|
111
|
+
"hasDynamicHelp": false,
|
|
112
|
+
"multiple": false,
|
|
113
|
+
"type": "option"
|
|
114
|
+
},
|
|
115
|
+
"dataset": {
|
|
116
|
+
"description": "Dataset to attach, or to create alongside a new project",
|
|
117
|
+
"name": "dataset",
|
|
118
|
+
"hasDynamicHelp": false,
|
|
119
|
+
"multiple": false,
|
|
120
|
+
"type": "option"
|
|
121
|
+
},
|
|
122
|
+
"stack-name": {
|
|
123
|
+
"description": "Name for the new organization-scoped Stack, and the hosted Studio slug",
|
|
124
|
+
"name": "stack-name",
|
|
125
|
+
"hasDynamicHelp": false,
|
|
126
|
+
"multiple": false,
|
|
127
|
+
"type": "option"
|
|
128
|
+
},
|
|
129
|
+
"studio-name": {
|
|
130
|
+
"description": "Blueprint-local name for the Studio resource and its directory",
|
|
131
|
+
"name": "studio-name",
|
|
132
|
+
"hasDynamicHelp": false,
|
|
133
|
+
"multiple": false,
|
|
134
|
+
"type": "option"
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
"hasDynamicHelp": false,
|
|
138
|
+
"hidden": true,
|
|
139
|
+
"hiddenAliases": [],
|
|
140
|
+
"id": "init",
|
|
141
|
+
"pluginAlias": "@sanity/runtime-cli",
|
|
142
|
+
"pluginName": "@sanity/runtime-cli",
|
|
143
|
+
"pluginType": "core",
|
|
144
|
+
"strict": true,
|
|
145
|
+
"summary": "Experimental blueprints-first project init",
|
|
146
|
+
"enableJsonFlag": true,
|
|
147
|
+
"isESM": true,
|
|
148
|
+
"relativePath": [
|
|
149
|
+
"dist",
|
|
150
|
+
"commands",
|
|
151
|
+
"init.js"
|
|
152
|
+
]
|
|
153
|
+
},
|
|
3
154
|
"blueprints:add": {
|
|
4
155
|
"aliases": [],
|
|
5
156
|
"args": {
|
|
@@ -2755,6 +2906,13 @@
|
|
|
2755
2906
|
"hasDynamicHelp": false,
|
|
2756
2907
|
"multiple": false,
|
|
2757
2908
|
"type": "option"
|
|
2909
|
+
},
|
|
2910
|
+
"no-wait": {
|
|
2911
|
+
"description": "Skip durable wait delays instead of sleeping",
|
|
2912
|
+
"name": "no-wait",
|
|
2913
|
+
"required": false,
|
|
2914
|
+
"allowNo": false,
|
|
2915
|
+
"type": "boolean"
|
|
2758
2916
|
}
|
|
2759
2917
|
},
|
|
2760
2918
|
"hasDynamicHelp": false,
|
|
@@ -3141,5 +3299,5 @@
|
|
|
3141
3299
|
]
|
|
3142
3300
|
}
|
|
3143
3301
|
},
|
|
3144
|
-
"version": "17.
|
|
3302
|
+
"version": "17.9.0-blueprinit.g14aaebd"
|
|
3145
3303
|
}
|
package/package.json
CHANGED
package/skills/init.md
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# init
|
|
2
|
+
|
|
3
|
+
Use `init` to scaffold a Blueprint around an existing Sanity project. The command is
|
|
4
|
+
experimental, hidden from root help, and available from the `blueprinit` npm dist-tag.
|
|
5
|
+
|
|
6
|
+
Use it for demos and evaluation, not as a stable project initializer. Its flags, generated files,
|
|
7
|
+
and resource model may change.
|
|
8
|
+
|
|
9
|
+
## Know what it does
|
|
10
|
+
|
|
11
|
+
`init` uses an existing Sanity organization. It uses a project that exists, or creates one with
|
|
12
|
+
`--create-project`. It writes local Blueprint files, then creates an empty organization-scoped
|
|
13
|
+
Stack. The generated Blueprint attaches the project and declares resources for a later deployment.
|
|
14
|
+
|
|
15
|
+
The dataset works one of two ways:
|
|
16
|
+
|
|
17
|
+
- **Existing project:** the user picks one of its datasets, and the Blueprint attaches it.
|
|
18
|
+
- **New project:** a new project has no datasets, so the Blueprint creates one. Its name comes from
|
|
19
|
+
`--dataset` and defaults to `production`. It does not exist until `blueprints deploy` runs.
|
|
20
|
+
|
|
21
|
+
It does not create an organization. It does not deploy the attached resources, CORS origin, Studio,
|
|
22
|
+
or Function. Those changes happen only if `blueprints deploy` is run later.
|
|
23
|
+
|
|
24
|
+
## Inspect the target directory
|
|
25
|
+
|
|
26
|
+
Pass the directory as an argument, or run the command from the directory that should contain the
|
|
27
|
+
Blueprint. Without an argument an interactive run asks where the files go; an unattended run keeps
|
|
28
|
+
the current directory.
|
|
29
|
+
|
|
30
|
+
Before running:
|
|
31
|
+
|
|
32
|
+
1. Check the working tree and existing files. Do not discard unrelated user changes.
|
|
33
|
+
2. Look for a root `sanity.blueprint.*` file. If one exists, use the existing Blueprint instead of
|
|
34
|
+
initializing again.
|
|
35
|
+
3. Look for a root `sanity.config.*` file. Its presence selects the Studio adoption flow.
|
|
36
|
+
4. Confirm the user asked to initialize this directory. The command writes files and creates a
|
|
37
|
+
remote Stack.
|
|
38
|
+
5. Confirm the user is signed in. If needed, have them run:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
npx sanity login
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The account must have at least one organization. A run against an existing project also needs one
|
|
45
|
+
project in the selected organization and one dataset in that project. A run with `--create-project`
|
|
46
|
+
needs neither.
|
|
47
|
+
|
|
48
|
+
Interactive runs show the Blueprint resources, hosted Studio URL, local changes, and remote actions
|
|
49
|
+
before the confirmation prompt. Everything the Blueprint declares stays pending until a later
|
|
50
|
+
deployment.
|
|
51
|
+
|
|
52
|
+
A run that creates a project makes two remote changes rather than one: the project, then the Stack.
|
|
53
|
+
Both happen after the confirmation, so a cancelled run creates nothing. The project is created
|
|
54
|
+
before any file is written, because the generated Studio config needs its ID.
|
|
55
|
+
|
|
56
|
+
## Choose the flow
|
|
57
|
+
|
|
58
|
+
- **Existing Blueprint with a configured Stack:** Exit without changes and use
|
|
59
|
+
`blueprints config --edit` or `blueprints doctor`.
|
|
60
|
+
- **Existing Blueprint without a configured Stack:** Finish setup with `blueprints init`; it can
|
|
61
|
+
connect the existing manifest without rewriting it.
|
|
62
|
+
- **Root Studio config:** Adopt the Studio in place. Read literal `projectId` and `dataset` strings
|
|
63
|
+
from `sanity.config.*`, falling back to `sanity.cli.*`. Do not move the Studio.
|
|
64
|
+
- **Fresh workspace:** Create a Blueprint, an npm-workspace Studio, and a starter `set-slug`
|
|
65
|
+
document function.
|
|
66
|
+
|
|
67
|
+
Do not rewrite a Studio config merely to make adoption work unless the user asks. Dynamic
|
|
68
|
+
`projectId` or `dataset` values built from environment variables or imports cannot be detected.
|
|
69
|
+
|
|
70
|
+
For an existing Blueprint, inspect it with:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
npx @sanity/runtime-cli@blueprinit blueprints doctor
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Run unattended
|
|
77
|
+
|
|
78
|
+
When the organization and project IDs are known, run this from the target directory:
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
npx @sanity/runtime-cli@blueprinit init --yes \
|
|
82
|
+
--organization-id <organization-id> \
|
|
83
|
+
--project-id <project-id> \
|
|
84
|
+
--dataset <dataset-name>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
To create the project instead, replace `--project-id` with `--create-project`:
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
npx @sanity/runtime-cli@blueprinit init --yes \
|
|
91
|
+
--organization-id <organization-id> \
|
|
92
|
+
--create-project "<project display name>" \
|
|
93
|
+
--dataset <dataset-name>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`--create-project` and `--project-id` cannot both be passed. With `--create-project`, `--dataset`
|
|
97
|
+
names a dataset to create and defaults to `production`.
|
|
98
|
+
|
|
99
|
+
Replace all placeholders with real values. The project must belong to the organization, and the
|
|
100
|
+
dataset must belong to the project. Do not guess any value.
|
|
101
|
+
|
|
102
|
+
`--yes` skips confirmation. Without explicit values, an unattended run stops when the account has
|
|
103
|
+
several organizations, projects, or datasets. Ask the user for the missing value or have them run
|
|
104
|
+
the command interactively.
|
|
105
|
+
|
|
106
|
+
Use `--stack-name <name>` and `--studio-name <name>` when those generated names need to be stable.
|
|
107
|
+
The command normalizes both names to lowercase kebab case.
|
|
108
|
+
|
|
109
|
+
The Stack name defaults to `<project>-<dataset>` and is also the hosted Studio slug, so it must be
|
|
110
|
+
available across every Sanity organization. No API reports whether a slug is free, so a taken one
|
|
111
|
+
fails during `blueprints deploy`.
|
|
112
|
+
|
|
113
|
+
The Studio name is a Blueprint-local resource name and its directory under `studios/`. It defaults
|
|
114
|
+
to `studio`. Every other generated name carries its resource type, so the defaults cannot collide.
|
|
115
|
+
A dataset named `studio`, or `--studio-name production` against a `production` dataset, does
|
|
116
|
+
collide; the command names the repeated value and writes nothing.
|
|
117
|
+
|
|
118
|
+
When `SANITY_INTERNAL_ENV=staging`, the generated Studio URL and CORS origin use
|
|
119
|
+
`https://<stack-name>.studio.sanity.work`. Other environments use
|
|
120
|
+
`https://<stack-name>.sanity.studio`.
|
|
121
|
+
|
|
122
|
+
For interactive organization, project, dataset, Stack-name, and confirmation prompts, have the
|
|
123
|
+
user run:
|
|
124
|
+
|
|
125
|
+
```sh
|
|
126
|
+
npx @sanity/runtime-cli@blueprinit init
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
When the project has several datasets, the interactive command prompts for one and lists
|
|
130
|
+
`production` first. Report the selected dataset from the command output.
|
|
131
|
+
|
|
132
|
+
## Review the result
|
|
133
|
+
|
|
134
|
+
A successful fresh run creates or updates:
|
|
135
|
+
|
|
136
|
+
```text
|
|
137
|
+
.
|
|
138
|
+
├── .sanity/blueprint.config.json
|
|
139
|
+
├── sanity.blueprint.ts
|
|
140
|
+
├── package.json
|
|
141
|
+
├── functions/
|
|
142
|
+
│ └── set-slug/
|
|
143
|
+
└── studios/
|
|
144
|
+
└── studio/
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The command also creates or updates `.gitignore`. It preserves existing package metadata and adds
|
|
148
|
+
the required npm workspaces and `@sanity/blueprints` dependency.
|
|
149
|
+
|
|
150
|
+
The generated Studio matches `sanity init`: the same `sanity`, `@sanity/vision`, `react`,
|
|
151
|
+
`react-dom`, `styled-components`, `@types/react`, and `typescript` versions, the same
|
|
152
|
+
`tsconfig.json`, and `visionTool()` beside `structureTool()`. `sanity` and `@sanity/vision` are
|
|
153
|
+
resolved from the registry at scaffold time, so no version is written into this repository.
|
|
154
|
+
The Studio has no `deploy` script, because the Blueprint deploys it.
|
|
155
|
+
|
|
156
|
+
An adopted Studio stays at the repository root. The command adds the Blueprint, Blueprint config,
|
|
157
|
+
dependency, and `.gitignore` entry, but does not generate the starter Function.
|
|
158
|
+
|
|
159
|
+
After the command exits:
|
|
160
|
+
|
|
161
|
+
1. Confirm the output names the expected organization, project, and dataset.
|
|
162
|
+
2. Confirm `.sanity/blueprint.config.json` exists. Its presence means Stack creation completed.
|
|
163
|
+
3. Review `sanity.blueprint.ts`, including the Studio slug and credentialed CORS origin, plus
|
|
164
|
+
`package.json`, `.gitignore`, and the working-tree diff.
|
|
165
|
+
4. Report the Stack ID and every created or modified path.
|
|
166
|
+
|
|
167
|
+
Do not deploy as part of initialization unless the user separately asks for deployment. To continue
|
|
168
|
+
after reviewing the scaffold, install dependencies and preview the deployment:
|
|
169
|
+
|
|
170
|
+
```sh
|
|
171
|
+
npm install
|
|
172
|
+
npx @sanity/runtime-cli@blueprinit blueprints plan
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Treat dependency installation and planning as separate follow-up actions when they were not part of
|
|
176
|
+
the user's request.
|
|
177
|
+
|
|
178
|
+
## Handle failures
|
|
179
|
+
|
|
180
|
+
- **`No Sanity session found.`** Stop and ask the user to run `npx sanity login`. Authentication
|
|
181
|
+
fails before files are written.
|
|
182
|
+
- **Multiple organizations or projects found.** Rerun with the matching `--organization-id` and
|
|
183
|
+
`--project-id`. Selection fails before files are written.
|
|
184
|
+
- **No datasets found.** Ask the user to choose a project with a dataset, or to create a project
|
|
185
|
+
with `--create-project`, which makes the Blueprint create the dataset too.
|
|
186
|
+
- **No projects found in an organization.** In an unattended run, pass `--create-project <name>`.
|
|
187
|
+
- **Dataset name is not valid.** The projects API allows 2 to 64 characters from `a-z`, `0-9`, `-`,
|
|
188
|
+
and `_`, starting with a letter or a digit. Report the rule and ask for another name. Do not
|
|
189
|
+
rewrite the name the user gave.
|
|
190
|
+
- **Could not create project.** Nothing was written. Report the API message. A quota error needs a
|
|
191
|
+
plan change or another organization.
|
|
192
|
+
- **Could not read `projectId` and `dataset`.** The detected Studio config does not contain both
|
|
193
|
+
values as string literals. Pass explicit `--project-id` and `--dataset` values or report the config
|
|
194
|
+
file and let the user decide whether to change it.
|
|
195
|
+
- **Refusing to overwrite an existing path.** Inspect the reported Studio or Function directory.
|
|
196
|
+
Do not delete or replace it without the user's direction.
|
|
197
|
+
- **Stack quota or duplicate-name error.** Local scaffold files may already exist while
|
|
198
|
+
`.sanity/blueprint.config.json` does not. Resolve the Stack error, then run the recovery command
|
|
199
|
+
printed by `init`, which is `blueprints init`. It connects the existing manifest without rewriting it.
|