@sanity/runtime-cli 17.15.0-next.gd07e8fe → 17.15.0-next.ge190195

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 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.15.0-next.gd07e8fe/src/commands/blueprints/add.ts)_
93
+ _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.ge190195/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.15.0-next.gd07e8fe/src/commands/blueprints/config.ts)_
130
+ _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.ge190195/src/commands/blueprints/config.ts)_
131
131
 
132
132
  ## `sanity-run blueprints deploy`
133
133
 
@@ -183,7 +183,7 @@ EXAMPLES
183
183
  $ sanity-run blueprints deploy --new-stack-name <new-name>
184
184
  ```
185
185
 
186
- _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.gd07e8fe/src/commands/blueprints/deploy.ts)_
186
+ _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.ge190195/src/commands/blueprints/deploy.ts)_
187
187
 
188
188
  ## `sanity-run blueprints destroy`
189
189
 
@@ -221,7 +221,7 @@ EXAMPLES
221
221
  $ sanity-run blueprints destroy --stack <name-or-id> --project-id <projectId> --force --no-wait
222
222
  ```
223
223
 
224
- _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.gd07e8fe/src/commands/blueprints/destroy.ts)_
224
+ _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.ge190195/src/commands/blueprints/destroy.ts)_
225
225
 
226
226
  ## `sanity-run blueprints doctor`
227
227
 
@@ -253,7 +253,7 @@ EXAMPLES
253
253
  $ sanity-run blueprints doctor --fix
254
254
  ```
255
255
 
256
- _See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.gd07e8fe/src/commands/blueprints/doctor.ts)_
256
+ _See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.ge190195/src/commands/blueprints/doctor.ts)_
257
257
 
258
258
  ## `sanity-run blueprints info`
259
259
 
@@ -291,7 +291,7 @@ EXAMPLES
291
291
  $ sanity-run blueprints info --organization-id <orgId> --stack <name-or-id>
292
292
  ```
293
293
 
294
- _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.gd07e8fe/src/commands/blueprints/info.ts)_
294
+ _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.ge190195/src/commands/blueprints/info.ts)_
295
295
 
296
296
  ## `sanity-run blueprints init [DIR]`
297
297
 
@@ -355,7 +355,7 @@ EXAMPLES
355
355
  $ sanity-run blueprints init old-stack --type <json|js|ts> --project-id <projectId> --stack-id <existingStackId>
356
356
  ```
357
357
 
358
- _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.gd07e8fe/src/commands/blueprints/init.ts)_
358
+ _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.ge190195/src/commands/blueprints/init.ts)_
359
359
 
360
360
  ## `sanity-run blueprints logs`
361
361
 
@@ -401,7 +401,7 @@ EXAMPLES
401
401
  $ sanity-run blueprints logs --before 2026-05-01T00:00:00Z
402
402
  ```
403
403
 
404
- _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.gd07e8fe/src/commands/blueprints/logs.ts)_
404
+ _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.ge190195/src/commands/blueprints/logs.ts)_
405
405
 
406
406
  ## `sanity-run blueprints mint-deploy-token`
407
407
 
@@ -446,7 +446,7 @@ EXAMPLES
446
446
  $ sanity-run blueprints mint-deploy-token --organization-id <orgId>
447
447
  ```
448
448
 
449
- _See code: [src/commands/blueprints/mint-deploy-token.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.gd07e8fe/src/commands/blueprints/mint-deploy-token.ts)_
449
+ _See code: [src/commands/blueprints/mint-deploy-token.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.ge190195/src/commands/blueprints/mint-deploy-token.ts)_
450
450
 
451
451
  ## `sanity-run blueprints plan`
452
452
 
@@ -479,7 +479,7 @@ EXAMPLES
479
479
  $ sanity-run blueprints plan --organization-id <orgId> --stack <name-or-id>
480
480
  ```
481
481
 
482
- _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.gd07e8fe/src/commands/blueprints/plan.ts)_
482
+ _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.ge190195/src/commands/blueprints/plan.ts)_
483
483
 
484
484
  ## `sanity-run blueprints promote`
485
485
 
@@ -515,7 +515,7 @@ EXAMPLES
515
515
  $ sanity-run blueprints promote --new-stack-name <new-name>
516
516
  ```
517
517
 
518
- _See code: [src/commands/blueprints/promote.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.gd07e8fe/src/commands/blueprints/promote.ts)_
518
+ _See code: [src/commands/blueprints/promote.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.ge190195/src/commands/blueprints/promote.ts)_
519
519
 
520
520
  ## `sanity-run blueprints stacks`
521
521
 
@@ -556,7 +556,7 @@ EXAMPLES
556
556
  $ sanity-run blueprints stacks --organization-id <organizationId> --include-projects
557
557
  ```
558
558
 
559
- _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.gd07e8fe/src/commands/blueprints/stacks.ts)_
559
+ _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.ge190195/src/commands/blueprints/stacks.ts)_
560
560
 
561
561
  ## `sanity-run functions add`
562
562
 
@@ -608,7 +608,7 @@ EXAMPLES
608
608
  $ sanity-run functions add --name my-function --type document-create --type document-update --lang js
609
609
  ```
610
610
 
611
- _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.gd07e8fe/src/commands/functions/add.ts)_
611
+ _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.ge190195/src/commands/functions/add.ts)_
612
612
 
613
613
  ## `sanity-run functions dev`
614
614
 
@@ -643,7 +643,7 @@ EXAMPLES
643
643
  $ sanity-run functions dev --timeout 60
644
644
  ```
645
645
 
646
- _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.gd07e8fe/src/commands/functions/dev.ts)_
646
+ _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.ge190195/src/commands/functions/dev.ts)_
647
647
 
648
648
  ## `sanity-run functions env add NAME KEY VALUE`
649
649
 
@@ -676,7 +676,7 @@ EXAMPLES
676
676
  $ sanity-run functions env add --stack <name-or-id> MyFunction API_URL https://api.example.com/
677
677
  ```
678
678
 
679
- _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.gd07e8fe/src/commands/functions/env/add.ts)_
679
+ _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.ge190195/src/commands/functions/env/add.ts)_
680
680
 
681
681
  ## `sanity-run functions env list NAME`
682
682
 
@@ -706,7 +706,7 @@ EXAMPLES
706
706
  $ sanity-run functions env list --stack <name-or-id> MyFunction
707
707
  ```
708
708
 
709
- _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.gd07e8fe/src/commands/functions/env/list.ts)_
709
+ _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.ge190195/src/commands/functions/env/list.ts)_
710
710
 
711
711
  ## `sanity-run functions env remove NAME KEY`
712
712
 
@@ -738,7 +738,7 @@ EXAMPLES
738
738
  $ sanity-run functions env remove --stack <name-or-id> MyFunction API_URL
739
739
  ```
740
740
 
741
- _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.gd07e8fe/src/commands/functions/env/remove.ts)_
741
+ _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.ge190195/src/commands/functions/env/remove.ts)_
742
742
 
743
743
  ## `sanity-run functions logs [NAME]`
744
744
 
@@ -778,7 +778,7 @@ EXAMPLES
778
778
  $ sanity-run functions logs <name> --delete
779
779
  ```
780
780
 
781
- _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.gd07e8fe/src/commands/functions/logs.ts)_
781
+ _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.ge190195/src/commands/functions/logs.ts)_
782
782
 
783
783
  ## `sanity-run functions test [NAME]`
784
784
 
@@ -836,7 +836,7 @@ EXAMPLES
836
836
  $ sanity-run functions test <name> --event update --data-before '{ "title": "before" }' --data-after '{ "title": "after" }'
837
837
  ```
838
838
 
839
- _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.gd07e8fe/src/commands/functions/test.ts)_
839
+ _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v17.15.0-next.ge190195/src/commands/functions/test.ts)_
840
840
 
841
841
  ## `sanity-run help [COMMAND]`
842
842
 
@@ -125,6 +125,16 @@ async function handleExistingBlueprint(options) {
125
125
  error: 'Flag --example cannot be used with an existing Blueprint.',
126
126
  };
127
127
  }
128
+ const existingBlueprint = await readLocalBlueprint(log, { resources: validateResources || false }, existingBlueprintFile.blueprintFilePath);
129
+ const hasStack = Boolean(existingBlueprint.blueprintConfig?.stackId);
130
+ if (hasStack) {
131
+ const overwrite = await confirm({
132
+ message: 'This Blueprint has an existing Stack. Are you sure you want to re-initialize?',
133
+ default: true,
134
+ });
135
+ if (!overwrite)
136
+ return { success: false, error: 'Initialization cancelled.' };
137
+ }
128
138
  // --stack-name with a scope flag means create a new stack before configuring
129
139
  let resolvedStackId = flagStackId;
130
140
  if (!resolvedStackId && flagStackName) {
@@ -142,7 +152,7 @@ async function handleExistingBlueprint(options) {
142
152
  }
143
153
  // unknown intent, prompt for confirmation
144
154
  const flagProvidedConfig = [flagProjectId, flagOrganizationId, flagStackId].some(Boolean);
145
- if (!flagProvidedConfig && !flagStackName) {
155
+ if (!flagProvidedConfig && !hasStack && !flagStackName) {
146
156
  const overwrite = await confirm({
147
157
  message: "This Blueprint doesn't have a deployment Stack yet. Configure one now?",
148
158
  default: true,
@@ -150,7 +160,6 @@ async function handleExistingBlueprint(options) {
150
160
  if (!overwrite)
151
161
  return { success: false, error: 'Initialization cancelled.' };
152
162
  }
153
- const existingBlueprint = await readLocalBlueprint(log, { resources: validateResources || false }, existingBlueprintFile.blueprintFilePath);
154
163
  return blueprintConfigCore({
155
164
  blueprint: existingBlueprint,
156
165
  bin,
@@ -1,7 +1,7 @@
1
1
  import { dirname } from 'node:path';
2
2
  import { testAction } from '../../actions/functions/test.js';
3
3
  import config from '../../config.js';
4
- import { STEP_PHASE_GLYPH } from '../../constants.js';
4
+ import { SANITY_FUNCTION_DURABLE, STEP_PHASE_GLYPH } from '../../constants.js';
5
5
  import { resolveDeployedResources } from '../../utils/blueprints/resolve-deployed-resources.js';
6
6
  import { createDurableJsonCollector } from '../../utils/functions/durable-logs.js';
7
7
  import { findFunctionInResources, getFunctionNames } from '../../utils/functions/find.js';
@@ -88,15 +88,38 @@ export async function functionTestCore(options) {
88
88
  * surfaced after the spinner resolves, including failed ones — a downstream
89
89
  * error is fire-and-forget (it doesn't reject the caller) but is still shown.
90
90
  */
91
- const runNested = async (nestedName, nestedPayload, ctx, depth, callStack) => {
91
+ const runNested = async (nestedName, nestedPayload, ctx, depth, callStack, metadata) => {
92
92
  const order = ++nestedOrder;
93
93
  const nestedResource = findFunctionInResources(blueprint.resources, nestedName);
94
- const result = await runFunction(nestedResource, { payload: nestedPayload, event: 'nested' }, { ...ctx, resources: resolvedResources }, { depth, callStack, onNestedInvoke: runNested });
95
- nestedRuns.push({ order, name: nestedName, logs: result.logs, error: result.error });
96
- return result;
94
+ const isLiveDurable = metadata?.origin === 'step.delegate' && nestedResource.type === SANITY_FUNCTION_DURABLE;
95
+ if (isLiveDurable) {
96
+ log(`↳ BEGIN: invoke('${nestedName}')`);
97
+ }
98
+ try {
99
+ const result = await runFunction(nestedResource, { payload: nestedPayload, event: 'nested' }, { ...ctx, resources: resolvedResources }, { depth, callStack, onNestedInvoke: runNested, onStepEvent });
100
+ if (result.error && isLiveDurable) {
101
+ log(result.error.stack || result.error.message);
102
+ }
103
+ nestedRuns.push({
104
+ order,
105
+ name: nestedName,
106
+ logs: result.logs,
107
+ error: result.error,
108
+ isLiveDurable,
109
+ });
110
+ return result;
111
+ }
112
+ finally {
113
+ if (isLiveDurable) {
114
+ log(`↳ END: invoke('${nestedName}')`);
115
+ }
116
+ }
97
117
  };
98
118
  const logNestedRuns = () => {
99
119
  for (const run of [...nestedRuns].sort((a, b) => a.order - b.order)) {
120
+ // Skip if is Durable so log ordering is retained
121
+ if (run.isLiveDurable)
122
+ continue;
100
123
  log(`↳ BEGIN: invoke('${run.name}')`);
101
124
  // Show any logs received
102
125
  if (run.logs?.trim()) {
@@ -86,7 +86,7 @@ process.on('message', async (data) => {
86
86
  {
87
87
  emit: (e) => process.send(JSON.stringify({type: 'step-event', event: e})),
88
88
  wait: (ms) => new Promise((resolve) => setTimeout(resolve, noWait ? 0 : ms)),
89
- invoke: invoker.invoke,
89
+ invoke: invoker.delegate,
90
90
  },
91
91
  )
92
92
  } else {
@@ -1,4 +1,5 @@
1
1
  export function createInvoker(): {
2
2
  invoke: (name: string, payload?: Record<string, unknown>) => Promise<unknown>
3
+ delegate: (name: string, payload?: Record<string, unknown>) => Promise<unknown>
3
4
  handleResponse: (msg: unknown) => void
4
5
  }
@@ -10,6 +10,17 @@ export function createInvoker() {
10
10
  const pending = new Map()
11
11
  let invokeSeq = 0
12
12
 
13
+ function sendInvokeRequest(name, payload = {}, metadata = {}) {
14
+ if (typeof name !== 'string' || name.length === 0) {
15
+ return Promise.reject(new Error('context.invoke requires a function name'))
16
+ }
17
+ const id = String(++invokeSeq)
18
+ return new Promise((resolve, reject) => {
19
+ pending.set(id, {resolve, reject})
20
+ process.send(JSON.stringify({type: 'invoke-request', id, name, payload, metadata}))
21
+ })
22
+ }
23
+
13
24
  /**
14
25
  * `context.invoke(name, payload)` — ask the parent to execute another blueprint
15
26
  * function and resolve with its return value. Sent over IPC and correlated by id.
@@ -18,14 +29,20 @@ export function createInvoker() {
18
29
  * @returns {Promise<unknown>}
19
30
  */
20
31
  function invoke(name, payload = {}) {
21
- if (typeof name !== 'string' || name.length === 0) {
22
- return Promise.reject(new Error('context.invoke requires a function name'))
23
- }
24
- const id = String(++invokeSeq)
25
- return new Promise((resolve, reject) => {
26
- pending.set(id, {resolve, reject})
27
- process.send(JSON.stringify({type: 'invoke-request', id, name, payload}))
28
- })
32
+ return sendInvokeRequest(name, payload)
33
+ }
34
+
35
+ /**
36
+ * durable `step.delegate(name, payload)` — ask the parent durable to execute another blueprint function
37
+ * and resolve with its return value. Sent over IPC and correlated by id. Adds an `origin` field to the message so the parent can
38
+ * distinguish between a normal invoke and a durable delegate for logging purposes.
39
+ * @param {string} name
40
+ * @param {Record<string, unknown>} [payload]
41
+ * @returns {Promise<unknown>}
42
+ */
43
+ function delegate(name, payload = {}) {
44
+ const metadata = {origin: 'step.delegate'}
45
+ return sendInvokeRequest(name, payload, metadata)
29
46
  }
30
47
 
31
48
  function handleResponse(msg) {
@@ -43,5 +60,5 @@ export function createInvoker() {
43
60
  }
44
61
  }
45
62
 
46
- return {invoke, handleResponse}
63
+ return {delegate, invoke, handleResponse}
47
64
  }
@@ -156,14 +156,14 @@ export default async function invoke(resource, payload, context, options) {
156
156
  // Resolve a nested `context.invoke(name, payload)` by re-entering the invoke
157
157
  // pipeline via the supplied callback, then hand the result back to the child.
158
158
  async function handleNestedInvoke(msg) {
159
- const { id, name, payload: nestedPayload } = msg;
159
+ const { id, name, payload: nestedPayload, metadata } = msg;
160
160
  pauseTimer();
161
161
  const nestedStart = performance.now();
162
162
  try {
163
163
  if (!onNestedInvoke) {
164
164
  throw new Error('Nested context.invoke is not supported in this context');
165
165
  }
166
- const nested = await onNestedInvoke(name, nestedPayload ?? {}, context, depth + 1, callStack);
166
+ const nested = await onNestedInvoke(name, nestedPayload ?? {}, context, depth + 1, callStack, metadata);
167
167
  nestedTimings[`invoke:${name}`] =
168
168
  (nestedTimings[`invoke:${name}`] ?? 0) + (performance.now() - nestedStart);
169
169
  // Fire-and-forget contract: in production `context.invoke` returns once the
@@ -231,7 +231,12 @@ export default async function invoke(resource, payload, context, options) {
231
231
  }
232
232
  // Nested invoke request — handle without tearing down the child.
233
233
  if (msg?.type === 'invoke-request') {
234
- handleNestedInvoke({ id: msg.id, name: msg.name, payload: msg.payload });
234
+ handleNestedInvoke({
235
+ id: msg.id,
236
+ name: msg.name,
237
+ payload: msg.payload,
238
+ metadata: msg.metadata,
239
+ });
235
240
  return;
236
241
  }
237
242
  // Terminal result message.
@@ -28,7 +28,7 @@ export function assertNestedInvokeAllowed(name, depth, callStack) {
28
28
  * stack extended by this function, and normalize timings.
29
29
  */
30
30
  export async function runFunction(resource, payload, context, options) {
31
- const { forceColor, timeout, depth = 0, callStack = [], onNestedInvoke } = options;
31
+ const { forceColor, timeout, depth = 0, callStack = [], onNestedInvoke, onStepEvent } = options;
32
32
  assertNestedInvokeAllowed(resource.name, depth, callStack);
33
33
  const response = await invoke(resource, payload, context, {
34
34
  forceColor,
@@ -36,6 +36,7 @@ export async function runFunction(resource, payload, context, options) {
36
36
  onNestedInvoke,
37
37
  depth,
38
38
  callStack: [...callStack, resource.name],
39
+ onStepEvent,
39
40
  });
40
41
  return { ...response, timings: response.timings ?? {} };
41
42
  }
@@ -187,12 +187,15 @@ export interface InvokeExecutionOptions {
187
187
  /** Don't wait the full duration during a wait durable event @internal */
188
188
  noWait?: boolean;
189
189
  }
190
+ export type NestedInvokeMetadata = {
191
+ origin?: 'step.delegate';
192
+ };
190
193
  /**
191
194
  * Re-entrant callback that executes a named function from the blueprint on behalf
192
195
  * of a nested `context.invoke(name, payload)` and returns its invocation result.
193
196
  * @internal
194
197
  */
195
- export type NestedInvoke = (name: string, payload: Record<string, unknown>, context: InvokeContextOptions, depth: number, callStack: string[]) => Promise<InvocationResponse & {
198
+ export type NestedInvoke = (name: string, payload: Record<string, unknown>, context: InvokeContextOptions, depth: number, callStack: string[], metadata?: NestedInvokeMetadata) => Promise<InvocationResponse & {
196
199
  timings: Record<string, number>;
197
200
  }>;
198
201
  /**
@@ -233,6 +236,7 @@ export type ChildToParentMessage = {
233
236
  name: string;
234
237
  payload: Record<string, unknown>;
235
238
  isDurable?: boolean;
239
+ metadata?: NestedInvokeMetadata;
236
240
  } | {
237
241
  type: 'result';
238
242
  json?: JsonInvocationResponse;