@sanity/runtime-cli 17.7.1-next.g09b9981 → 17.8.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 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.7.1-next.g09b9981/src/commands/blueprints/add.ts)_
93
+ _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.8.0/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.7.1-next.g09b9981/src/commands/blueprints/config.ts)_
130
+ _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v17.8.0/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.7.1-next.g09b9981/src/commands/blueprints/deploy.ts)_
186
+ _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.8.0/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.7.1-next.g09b9981/src/commands/blueprints/destroy.ts)_
224
+ _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.8.0/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.7.1-next.g09b9981/src/commands/blueprints/doctor.ts)_
256
+ _See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v17.8.0/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.7.1-next.g09b9981/src/commands/blueprints/info.ts)_
294
+ _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v17.8.0/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.7.1-next.g09b9981/src/commands/blueprints/init.ts)_
358
+ _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v17.8.0/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.7.1-next.g09b9981/src/commands/blueprints/logs.ts)_
404
+ _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.8.0/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.7.1-next.g09b9981/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.8.0/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.7.1-next.g09b9981/src/commands/blueprints/plan.ts)_
482
+ _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v17.8.0/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.7.1-next.g09b9981/src/commands/blueprints/promote.ts)_
518
+ _See code: [src/commands/blueprints/promote.ts](https://github.com/sanity-io/runtime-cli/blob/v17.8.0/src/commands/blueprints/promote.ts)_
519
519
 
520
520
  ## `sanity-run blueprints stacks`
521
521
 
@@ -551,7 +551,7 @@ EXAMPLES
551
551
  $ sanity-run blueprints stacks --organization-id <organizationId> --include-projects
552
552
  ```
553
553
 
554
- _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v17.7.1-next.g09b9981/src/commands/blueprints/stacks.ts)_
554
+ _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v17.8.0/src/commands/blueprints/stacks.ts)_
555
555
 
556
556
  ## `sanity-run functions add`
557
557
 
@@ -603,7 +603,7 @@ EXAMPLES
603
603
  $ sanity-run functions add --name my-function --type document-create --type document-update --lang js
604
604
  ```
605
605
 
606
- _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.7.1-next.g09b9981/src/commands/functions/add.ts)_
606
+ _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.8.0/src/commands/functions/add.ts)_
607
607
 
608
608
  ## `sanity-run functions dev`
609
609
 
@@ -638,7 +638,7 @@ EXAMPLES
638
638
  $ sanity-run functions dev --timeout 60
639
639
  ```
640
640
 
641
- _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v17.7.1-next.g09b9981/src/commands/functions/dev.ts)_
641
+ _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v17.8.0/src/commands/functions/dev.ts)_
642
642
 
643
643
  ## `sanity-run functions env add NAME KEY VALUE`
644
644
 
@@ -671,7 +671,7 @@ EXAMPLES
671
671
  $ sanity-run functions env add --stack <name-or-id> MyFunction API_URL https://api.example.com/
672
672
  ```
673
673
 
674
- _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.7.1-next.g09b9981/src/commands/functions/env/add.ts)_
674
+ _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.8.0/src/commands/functions/env/add.ts)_
675
675
 
676
676
  ## `sanity-run functions env list NAME`
677
677
 
@@ -701,7 +701,7 @@ EXAMPLES
701
701
  $ sanity-run functions env list --stack <name-or-id> MyFunction
702
702
  ```
703
703
 
704
- _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v17.7.1-next.g09b9981/src/commands/functions/env/list.ts)_
704
+ _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v17.8.0/src/commands/functions/env/list.ts)_
705
705
 
706
706
  ## `sanity-run functions env remove NAME KEY`
707
707
 
@@ -733,7 +733,7 @@ EXAMPLES
733
733
  $ sanity-run functions env remove --stack <name-or-id> MyFunction API_URL
734
734
  ```
735
735
 
736
- _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v17.7.1-next.g09b9981/src/commands/functions/env/remove.ts)_
736
+ _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v17.8.0/src/commands/functions/env/remove.ts)_
737
737
 
738
738
  ## `sanity-run functions logs [NAME]`
739
739
 
@@ -773,7 +773,7 @@ EXAMPLES
773
773
  $ sanity-run functions logs <name> --delete
774
774
  ```
775
775
 
776
- _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.7.1-next.g09b9981/src/commands/functions/logs.ts)_
776
+ _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.8.0/src/commands/functions/logs.ts)_
777
777
 
778
778
  ## `sanity-run functions test [NAME]`
779
779
 
@@ -830,7 +830,7 @@ EXAMPLES
830
830
  $ sanity-run functions test <name> --event update --data-before '{ "title": "before" }' --data-after '{ "title": "after" }'
831
831
  ```
832
832
 
833
- _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v17.7.1-next.g09b9981/src/commands/functions/test.ts)_
833
+ _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v17.8.0/src/commands/functions/test.ts)_
834
834
 
835
835
  ## `sanity-run help [COMMAND]`
836
836
 
@@ -1,3 +1,4 @@
1
+ import type { DurableStepPhase } from './utils/types.js';
1
2
  export declare const SANITY_ACCESS_ROLE = "sanity.access.role";
2
3
  export declare const SANITY_ACCESS_ROBOT = "sanity.access.robot";
3
4
  export declare const SANITY_FUNCTION_PREFIX = "sanity.function.";
@@ -50,3 +51,4 @@ export declare const LABEL_PUBSUB = "PubSub";
50
51
  export declare const LABEL_SCHEDULED = "Scheduled";
51
52
  export declare const LABEL_SYNC_TAG_INVALIDATE = "Sync Tag Invalidate";
52
53
  export declare const CI_SETUP_DOCS_URL = "https://www.sanity.io/docs/blueprints/deploy-blueprints-from-ci";
54
+ export declare const STEP_PHASE_GLYPH: Record<DurableStepPhase, string>;
package/dist/constants.js CHANGED
@@ -56,3 +56,8 @@ export const LABEL_PUBSUB = 'PubSub';
56
56
  export const LABEL_SCHEDULED = 'Scheduled';
57
57
  export const LABEL_SYNC_TAG_INVALIDATE = 'Sync Tag Invalidate';
58
58
  export const CI_SETUP_DOCS_URL = 'https://www.sanity.io/docs/blueprints/deploy-blueprints-from-ci';
59
+ export const STEP_PHASE_GLYPH = {
60
+ start: '▶',
61
+ complete: '✔',
62
+ error: '✖',
63
+ };
@@ -3,7 +3,7 @@ import { dirname, join } from 'node:path';
3
3
  import { cwd } from 'node:process';
4
4
  import { testAction } from '../../actions/functions/test.js';
5
5
  import config from '../../config.js';
6
- import { SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET } from '../../constants.js';
6
+ import { SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_DURABLE, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, STEP_PHASE_GLYPH, } from '../../constants.js';
7
7
  import { resolveDeployedResources } from '../../utils/blueprints/resolve-deployed-resources.js';
8
8
  import buildPayload from '../../utils/functions/build-payload.js';
9
9
  import { fetchAsset, fetchDocument } from '../../utils/functions/fetch-document.js';
@@ -11,6 +11,20 @@ import { findFunctionInResources, getFunctionNames } from '../../utils/functions
11
11
  import { runFunction } from '../../utils/functions/invoke/nested.js';
12
12
  import { parseJsonObject } from '../../utils/parse-json-object.js';
13
13
  import { isEventType, } from '../../utils/types.js';
14
+ /**
15
+ * CLI lines for a durable run, in emission order; bare `start` events dropped.
16
+ * @param events
17
+ */
18
+ function renderStepTimeline(events) {
19
+ return events
20
+ .filter((event) => event.phase !== 'start')
21
+ .map((event) => {
22
+ const glyph = STEP_PHASE_GLYPH[event.phase] ?? '•';
23
+ const duration = typeof event.durationMs === 'number' ? ` (${Math.round(event.durationMs)}ms)` : '';
24
+ const detail = event.phase === 'error' && event.error ? ` ${event.error.message}` : '';
25
+ return ` ${glyph} ${event.name} [${event.kind}]${duration}${detail}`;
26
+ });
27
+ }
14
28
  export async function functionTestCore(options) {
15
29
  const { blueprint, log, error, args, flags, helpText } = options;
16
30
  const { name: fnName } = args;
@@ -42,6 +56,7 @@ export async function functionTestCore(options) {
42
56
  const resource = findFunctionInResources(blueprint.resources, fnName); // throws if not found
43
57
  const docFunction = resource.type === SANITY_FUNCTION_DOCUMENT;
44
58
  const mediaFunction = resource.type === SANITY_FUNCTION_MEDIA_LIBRARY_ASSET;
59
+ const durableFunction = resource.type === SANITY_FUNCTION_DURABLE;
45
60
  const resolvedResources = await resolveDeployedResources({
46
61
  resources: blueprint.resources,
47
62
  stackId: blueprint.stackId,
@@ -51,7 +66,10 @@ export async function functionTestCore(options) {
51
66
  logger: log,
52
67
  });
53
68
  const blueprintFileDir = dirname(blueprint.fileInfo.blueprintFilePath);
54
- const contextOptions = docFunction || mediaFunction
69
+ const isMediaSource = mediaFunction || (durableFunction && resource.event?.type === 'media-library');
70
+ const eventResourceType = isMediaSource ? 'media-library' : 'dataset';
71
+ const eventResourceId = (isMediaSource ? mediaLibraryId : `${projectId}.${dataset}`) || '';
72
+ const contextOptions = docFunction || mediaFunction || durableFunction
55
73
  ? {
56
74
  clientOptions: {
57
75
  apiVersion: api,
@@ -59,8 +77,8 @@ export async function functionTestCore(options) {
59
77
  projectId,
60
78
  organizationId,
61
79
  },
62
- eventResourceType: docFunction ? 'dataset' : 'media-library',
63
- eventResourceId: (docFunction ? `${projectId}.${dataset}` : mediaLibraryId) || '',
80
+ eventResourceType,
81
+ eventResourceId,
64
82
  functionResourceType: 'project',
65
83
  functionResourceId: projectId || '',
66
84
  resources: resolvedResources,
@@ -139,19 +157,22 @@ export async function functionTestCore(options) {
139
157
  payload = buildPayload({ data, file });
140
158
  }
141
159
  }
142
- const invokeOptions = docFunction || mediaFunction
143
- ? {
144
- event: eventType,
145
- payload,
146
- before,
147
- after,
148
- }
149
- : {
150
- event: 'scheduled',
151
- };
160
+ let invokeOptions;
161
+ if (docFunction || mediaFunction) {
162
+ invokeOptions = { event: eventType, payload, before, after };
163
+ }
164
+ else {
165
+ invokeOptions = { event: durableFunction ? 'durable' : 'scheduled', payload: { data: payload } };
166
+ }
152
167
  // Records each nested context.invoke() so they can be surfaced in order after the spinner resolves.
153
168
  const nestedRuns = [];
154
169
  let nestedOrder = 0;
170
+ const stepEvents = [];
171
+ const onStepEvent = durableFunction
172
+ ? (event) => {
173
+ stepEvents.push(event);
174
+ }
175
+ : undefined;
155
176
  /**
156
177
  * Re-entrant callback that resolves a nested context.invoke(name, payload):
157
178
  * it finds the named function in the same blueprint, reuses the already
@@ -188,6 +209,7 @@ export async function functionTestCore(options) {
188
209
  onNestedInvoke: runNested,
189
210
  depth: 0,
190
211
  callStack: [fnName],
212
+ onStepEvent,
191
213
  });
192
214
  if (error) {
193
215
  spinner.fail('Function execution failed.');
@@ -199,6 +221,12 @@ export async function functionTestCore(options) {
199
221
  logNestedRuns();
200
222
  const errorMessage = error.stack || error.message || error.name;
201
223
  const isTimeout = error.message?.includes('Timeout');
224
+ if (durableFunction && stepEvents.length > 0) {
225
+ log('Steps:');
226
+ for (const line of renderStepTimeline(stepEvents)) {
227
+ log(line);
228
+ }
229
+ }
202
230
  return {
203
231
  success: false,
204
232
  error: errorMessage,
@@ -216,6 +244,12 @@ export async function functionTestCore(options) {
216
244
  log('Response:');
217
245
  log(JSON.stringify(response, null, 2));
218
246
  }
247
+ if (durableFunction && stepEvents.length > 0) {
248
+ log('Steps:');
249
+ for (const line of renderStepTimeline(stepEvents)) {
250
+ log(line);
251
+ }
252
+ }
219
253
  return { success: true, json: { response, logs } };
220
254
  }
221
255
  catch (error) {
@@ -3,6 +3,7 @@ import {isAbsolute, join} from 'node:path'
3
3
  import process from 'node:process'
4
4
  import {pathToFileURL} from 'node:url'
5
5
  import {installConsole} from './child-runtime/console.js'
6
+ import {runDurable} from './child-runtime/durable.js'
6
7
  import {createInvoker} from './child-runtime/invoke.js'
7
8
  import {createResourcesApi} from './child-runtime/resources.js'
8
9
 
@@ -40,7 +41,7 @@ process.on('message', async (data) => {
40
41
 
41
42
  // Otherwise this is the initial start message. Support the legacy untyped
42
43
  // `{srcPath, payload}` shape as well as the new `{type: 'start', ...}`.
43
- const {srcPath, payload} = jsonData
44
+ const {srcPath, payload, isDurable} = jsonData
44
45
  const {context, ...event} = payload
45
46
 
46
47
  let logs = ''
@@ -78,11 +79,22 @@ process.on('message', async (data) => {
78
79
 
79
80
  // Allow the handler to invoke other blueprint functions by name.
80
81
  context.invoke = invoker.invoke
81
- json = await handler({
82
- context,
83
- event,
84
- done: async () => new Response(null, {status: 204}),
85
- })
82
+
83
+ if (isDurable) {
84
+ json = await runDurable(
85
+ handler,
86
+ {context, event, logger: console},
87
+ {
88
+ emit: (e) => process.send(JSON.stringify({type: 'step-event', event: e})),
89
+ },
90
+ )
91
+ } else {
92
+ json = await handler({
93
+ context,
94
+ event,
95
+ done: async () => new Response(null, {status: 204}),
96
+ })
97
+ }
86
98
 
87
99
  // Restore streams
88
100
  process.stdout.write = originalStdoutWrite
@@ -0,0 +1,18 @@
1
+ import type {DurableContext, DurableHandler, DurableLogger, GenericEvent} from '@sanity/functions'
2
+ import type {DurableStepEvent} from '../../types.js'
3
+
4
+ type RunDurableArgs = {
5
+ context: DurableContext
6
+ event: GenericEvent
7
+ logger: DurableLogger
8
+ }
9
+
10
+ type DurableIO = {
11
+ emit: (event: DurableStepEvent) => void
12
+ }
13
+
14
+ export function runDurable(
15
+ handler: DurableHandler,
16
+ args: RunDurableArgs,
17
+ io: DurableIO,
18
+ ): Promise<unknown>
@@ -0,0 +1,46 @@
1
+ import {performance} from 'node:perf_hooks'
2
+
3
+ /**
4
+ * Runs a durable function handler
5
+ * @param handler - callback being run
6
+ * @param context - data to send to the operation
7
+ * @param event - the step being run
8
+ * @param logger
9
+ * @param io
10
+ */
11
+ export async function runDurable(handler, {context, event, logger}, io) {
12
+ const track = async (name, kind, produce) => {
13
+ io.emit({name, kind, phase: 'start'})
14
+ const startedAt = performance.now()
15
+ try {
16
+ const value = await produce()
17
+ io.emit({
18
+ name,
19
+ kind,
20
+ phase: 'complete',
21
+ output: value,
22
+ durationMs: performance.now() - startedAt,
23
+ })
24
+ return value
25
+ } catch (err) {
26
+ io.emit({
27
+ name,
28
+ kind,
29
+ phase: 'error',
30
+ error: {
31
+ name: err?.name ?? 'Error',
32
+ message: err?.message ?? String(err),
33
+ stack: err?.stack ?? '',
34
+ },
35
+ durationMs: performance.now() - startedAt,
36
+ })
37
+ throw err
38
+ }
39
+ }
40
+
41
+ const step = {
42
+ run: ({name, handler: fn}) => track(name, 'run', () => fn({logger, attempt: 1})),
43
+ }
44
+
45
+ return handler({context, event, step, logger})
46
+ }
@@ -5,6 +5,7 @@ import { setTimeout } from 'node:timers';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import * as groq from 'groq-js';
7
7
  import config from '../../../config.js';
8
+ import { SANITY_FUNCTION_DURABLE } from '../../../constants.js';
8
9
  import { groupBlueprintResources } from '../../blueprints/group-resources.js';
9
10
  import { isGroqContextOptions, } from '../../types.js';
10
11
  import { acquireFunctionBuild } from './prepare.js';
@@ -42,7 +43,7 @@ export default async function invoke(resource, payload, context, options) {
42
43
  if (!resource.src) {
43
44
  throw new Error(`Function resource "${resource.name}" is missing the 'src' property.`);
44
45
  }
45
- const { forceColor = true, timeout = 10, onNestedInvoke, depth = 0, callStack = [] } = options;
46
+ const { forceColor = true, timeout = 10, onNestedInvoke, depth = 0, callStack = [], onStepEvent = () => { }, } = options;
46
47
  // Apply the GROQ rule before preparing a build so a filtered-out event skips
47
48
  // the (potentially expensive) transpile entirely.
48
49
  let filteredData = payload?.payload?.data || {};
@@ -208,6 +209,10 @@ export default async function invoke(resource, payload, context, options) {
208
209
  catch {
209
210
  return;
210
211
  }
212
+ if (msg?.type === 'step-event') {
213
+ onStepEvent(msg.event);
214
+ return;
215
+ }
211
216
  // Nested invoke request — handle without tearing down the child.
212
217
  if (msg?.type === 'invoke-request') {
213
218
  handleNestedInvoke({ id: msg.id, name: msg.name, payload: msg.payload });
@@ -289,7 +294,12 @@ export default async function invoke(resource, payload, context, options) {
289
294
  }),
290
295
  },
291
296
  };
292
- const startMessage = { type: 'start', srcPath: functionPath, payload };
297
+ const startMessage = {
298
+ type: 'start',
299
+ srcPath: functionPath,
300
+ payload,
301
+ isDurable: resource.type === SANITY_FUNCTION_DURABLE,
302
+ };
293
303
  child.send(JSON.stringify(startMessage));
294
304
  }
295
305
  function shutdown() {
@@ -2,6 +2,9 @@ import { stderr } from 'node:process';
2
2
  import { onExit } from 'signal-exit';
3
3
  const ST = '\x1b\\';
4
4
  export const HEARTBEAT_INTERVAL_MS = 5_000;
5
+ // Ora clobbers stderr.write and messes with OSC output.
6
+ // Keep the original writer so progress sequences do not collide.
7
+ const writeToStderr = stderr.write.bind(stderr);
5
8
  const STATES = {
6
9
  clear: 0,
7
10
  normal: 1,
@@ -24,7 +27,7 @@ function startHeartbeat(sequence) {
24
27
  return;
25
28
  heartbeat = setInterval(() => {
26
29
  if (stderr.isTTY && heartbeatSequence)
27
- stderr.write(heartbeatSequence);
30
+ writeToStderr(heartbeatSequence);
28
31
  }, HEARTBEAT_INTERVAL_MS);
29
32
  heartbeat.unref();
30
33
  }
@@ -37,7 +40,7 @@ function write(state, value = 0) {
37
40
  exitHookRegistered = true;
38
41
  function clear() {
39
42
  stopHeartbeat();
40
- stderr.write(`\x1b]9;4;0;0${ST}`);
43
+ writeToStderr(`\x1b]9;4;0;0${ST}`);
41
44
  }
42
45
  // arc/utils SIGINT handler clobbers our signal-exit; luckily it then calls process.exit()
43
46
  process.once('exit', clear); // triggered by arc/utils SIGINT handler
@@ -47,7 +50,7 @@ function write(state, value = 0) {
47
50
  const active = state === 'normal' || state === 'paused' || state === 'busy';
48
51
  if (!active)
49
52
  stopHeartbeat();
50
- stderr.write(sequence);
53
+ writeToStderr(sequence);
51
54
  if (active)
52
55
  startHeartbeat(sequence);
53
56
  }
@@ -111,7 +111,7 @@ export interface InvokeNonGroqPayloadOptions {
111
111
  export type InvokePayloadOptions = InvokeGroqPayloadOptions | InvokeNonGroqPayloadOptions;
112
112
  export type InvokePayloadMetadata = Pick<InvokeGroqPayloadOptions, 'event' | 'before' | 'after'> | Pick<InvokeNonGroqPayloadOptions, 'event'>;
113
113
  export type EventType = 'create' | 'update' | 'delete';
114
- export type NonGroqEventType = 'scheduled' | 'nested';
114
+ export type NonGroqEventType = 'scheduled' | 'nested' | 'durable';
115
115
  export declare function isEventType(arg: string): arg is EventType;
116
116
  /** @internal */
117
117
  export interface InvokeGroqContextOptions {
@@ -161,6 +161,8 @@ export interface InvokeExecutionOptions {
161
161
  depth?: number;
162
162
  /** Function names already on the invoke call stack (innermost last), used to detect cycles. @internal */
163
163
  callStack?: string[];
164
+ /** Stream a durable step transition to the caller. @internal */
165
+ onStepEvent?: (event: DurableStepEvent) => void;
164
166
  }
165
167
  /**
166
168
  * Re-entrant callback that executes a named function from the blueprint on behalf
@@ -178,6 +180,7 @@ export type ParentToChildMessage = {
178
180
  type: 'start';
179
181
  srcPath: string;
180
182
  payload: unknown;
183
+ isDurable?: boolean;
181
184
  } | {
182
185
  type: 'invoke-response';
183
186
  id: string;
@@ -190,6 +193,9 @@ export type ParentToChildMessage = {
190
193
  id: string;
191
194
  ok: false;
192
195
  error: InvocationError;
196
+ } | {
197
+ type: 'step-event';
198
+ event: DurableStepEvent;
193
199
  };
194
200
  /**
195
201
  * IPC messages sent from a function child process back up to the parent.
@@ -202,12 +208,16 @@ export type ChildToParentMessage = {
202
208
  id: string;
203
209
  name: string;
204
210
  payload: Record<string, unknown>;
211
+ isDurable?: boolean;
205
212
  } | {
206
213
  type: 'result';
207
214
  json?: JsonInvocationResponse;
208
215
  logs?: string;
209
216
  errorLogs?: string;
210
217
  error?: InvocationError;
218
+ } | {
219
+ type: 'step-event';
220
+ event: DurableStepEvent;
211
221
  };
212
222
  /** @internal */
213
223
  export interface InvocationResponse {
@@ -290,4 +300,19 @@ export type Warning = {
290
300
  code: string;
291
301
  suggestions: string[];
292
302
  };
303
+ /** @internal */
304
+ export type DurableOperationSteps = 'run';
305
+ /** @internal */
306
+ export type DurableStepPhase = 'start' | 'complete' | 'error';
307
+ /**
308
+ * Represents a single step in a durable operation
309
+ */
310
+ export interface DurableStepEvent {
311
+ name: string;
312
+ kind: DurableOperationSteps;
313
+ phase: DurableStepPhase;
314
+ output?: unknown;
315
+ error?: InvocationError;
316
+ durationMs?: number;
317
+ }
293
318
  export {};
@@ -3127,5 +3127,5 @@
3127
3127
  ]
3128
3128
  }
3129
3129
  },
3130
- "version": "17.7.1-next.g09b9981"
3130
+ "version": "17.8.0"
3131
3131
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sanity/runtime-cli",
3
3
  "description": "Sanity's Runtime CLI for Blueprints and Functions",
4
- "version": "17.7.1-next.g09b9981",
4
+ "version": "17.8.0",
5
5
  "author": "Sanity Runtime Team",
6
6
  "type": "module",
7
7
  "license": "MIT",