@sanity/runtime-cli 10.5.1 → 10.6.1

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
@@ -20,7 +20,7 @@ $ npm install -g @sanity/runtime-cli
20
20
  $ sanity-run COMMAND
21
21
  running command...
22
22
  $ sanity-run (--version)
23
- @sanity/runtime-cli/10.5.1 linux-x64 node-v22.19.0
23
+ @sanity/runtime-cli/10.6.1 linux-x64 node-v22.19.0
24
24
  $ sanity-run --help [COMMAND]
25
25
  USAGE
26
26
  $ sanity-run COMMAND
@@ -88,7 +88,7 @@ EXAMPLES
88
88
  $ sanity-run blueprints add function --name my-function --fn-type document-create --fn-type document-update --lang js
89
89
  ```
90
90
 
91
- _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v10.5.1/src/commands/blueprints/add.ts)_
91
+ _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v10.6.1/src/commands/blueprints/add.ts)_
92
92
 
93
93
  ## `sanity-run blueprints config`
94
94
 
@@ -119,7 +119,7 @@ EXAMPLES
119
119
  $ sanity-run blueprints config --edit --project-id <projectId> --stack-id <stackId>
120
120
  ```
121
121
 
122
- _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v10.5.1/src/commands/blueprints/config.ts)_
122
+ _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v10.6.1/src/commands/blueprints/config.ts)_
123
123
 
124
124
  ## `sanity-run blueprints deploy`
125
125
 
@@ -141,7 +141,7 @@ EXAMPLES
141
141
  $ sanity-run blueprints deploy --no-wait
142
142
  ```
143
143
 
144
- _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v10.5.1/src/commands/blueprints/deploy.ts)_
144
+ _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v10.6.1/src/commands/blueprints/deploy.ts)_
145
145
 
146
146
  ## `sanity-run blueprints destroy`
147
147
 
@@ -166,7 +166,7 @@ EXAMPLES
166
166
  $ sanity-run blueprints destroy --stack-id <stackId> --project-id <projectId> --force --no-wait
167
167
  ```
168
168
 
169
- _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v10.5.1/src/commands/blueprints/destroy.ts)_
169
+ _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v10.6.1/src/commands/blueprints/destroy.ts)_
170
170
 
171
171
  ## `sanity-run blueprints info`
172
172
 
@@ -188,7 +188,7 @@ EXAMPLES
188
188
  $ sanity-run blueprints info --stack-id <stackId>
189
189
  ```
190
190
 
191
- _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v10.5.1/src/commands/blueprints/info.ts)_
191
+ _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v10.6.1/src/commands/blueprints/info.ts)_
192
192
 
193
193
  ## `sanity-run blueprints init [DIR]`
194
194
 
@@ -226,7 +226,7 @@ EXAMPLES
226
226
  $ sanity-run blueprints init --blueprint-type <json|js|ts> --stack-name <stackName>
227
227
  ```
228
228
 
229
- _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v10.5.1/src/commands/blueprints/init.ts)_
229
+ _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v10.6.1/src/commands/blueprints/init.ts)_
230
230
 
231
231
  ## `sanity-run blueprints logs`
232
232
 
@@ -248,7 +248,7 @@ EXAMPLES
248
248
  $ sanity-run blueprints logs --watch
249
249
  ```
250
250
 
251
- _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v10.5.1/src/commands/blueprints/logs.ts)_
251
+ _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v10.6.1/src/commands/blueprints/logs.ts)_
252
252
 
253
253
  ## `sanity-run blueprints plan`
254
254
 
@@ -265,7 +265,7 @@ EXAMPLES
265
265
  $ sanity-run blueprints plan
266
266
  ```
267
267
 
268
- _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v10.5.1/src/commands/blueprints/plan.ts)_
268
+ _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v10.6.1/src/commands/blueprints/plan.ts)_
269
269
 
270
270
  ## `sanity-run blueprints stacks`
271
271
 
@@ -287,7 +287,7 @@ EXAMPLES
287
287
  $ sanity-run blueprints stacks --project-id <projectId>
288
288
  ```
289
289
 
290
- _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v10.5.1/src/commands/blueprints/stacks.ts)_
290
+ _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v10.6.1/src/commands/blueprints/stacks.ts)_
291
291
 
292
292
  ## `sanity-run functions dev`
293
293
 
@@ -307,7 +307,7 @@ EXAMPLES
307
307
  $ sanity-run functions dev --port 8974
308
308
  ```
309
309
 
310
- _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v10.5.1/src/commands/functions/dev.ts)_
310
+ _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v10.6.1/src/commands/functions/dev.ts)_
311
311
 
312
312
  ## `sanity-run functions env add NAME KEY VALUE`
313
313
 
@@ -329,7 +329,7 @@ EXAMPLES
329
329
  $ sanity-run functions env add MyFunction API_URL https://api.example.com/
330
330
  ```
331
331
 
332
- _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v10.5.1/src/commands/functions/env/add.ts)_
332
+ _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v10.6.1/src/commands/functions/env/add.ts)_
333
333
 
334
334
  ## `sanity-run functions env list NAME`
335
335
 
@@ -349,7 +349,7 @@ EXAMPLES
349
349
  $ sanity-run functions env list MyFunction
350
350
  ```
351
351
 
352
- _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v10.5.1/src/commands/functions/env/list.ts)_
352
+ _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v10.6.1/src/commands/functions/env/list.ts)_
353
353
 
354
354
  ## `sanity-run functions env remove NAME KEY`
355
355
 
@@ -370,7 +370,7 @@ EXAMPLES
370
370
  $ sanity-run functions env remove MyFunction API_URL
371
371
  ```
372
372
 
373
- _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v10.5.1/src/commands/functions/env/remove.ts)_
373
+ _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v10.6.1/src/commands/functions/env/remove.ts)_
374
374
 
375
375
  ## `sanity-run functions logs NAME`
376
376
 
@@ -404,7 +404,7 @@ EXAMPLES
404
404
  $ sanity-run functions logs <name> --delete
405
405
  ```
406
406
 
407
- _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v10.5.1/src/commands/functions/logs.ts)_
407
+ _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v10.6.1/src/commands/functions/logs.ts)_
408
408
 
409
409
  ## `sanity-run functions test NAME`
410
410
 
@@ -412,21 +412,31 @@ Invoke a local Sanity Function
412
412
 
413
413
  ```
414
414
  USAGE
415
- $ sanity-run functions test NAME [-d <value> | -f <value> | --document-id <value>] [-t <value>] [-a <value>]
416
- [--dataset <value>] [--project-id <value>] [--with-user-token]
415
+ $ sanity-run functions test NAME [--data-before <value> | [-d <value> | -f <value> | --document-id <value>] | |
416
+ | --file-before <value> | --file-after <value> | --document-id-before <value> | --document-id-after <value>]
417
+ [--data-after <value> | | | | | | | ] [-e create|update|delete] [-t <value>] [-a <value>] [--dataset <value>]
418
+ [--project-id <value>] [--with-user-token]
417
419
 
418
420
  ARGUMENTS
419
421
  NAME The name of the Sanity Function
420
422
 
421
423
  FLAGS
422
- -a, --api=<value> Sanity API Version to use
423
- -d, --data=<value> Data to send to the function
424
- -f, --file=<value> Read data from file and send to the function
425
- -t, --timeout=<value> Execution timeout value in seconds
426
- --dataset=<value> The Sanity dataset to use
427
- --document-id=<value> Document to fetch and send to function
428
- --project-id=<value> Sanity Project ID to use
429
- --with-user-token Prime access token from CLI config
424
+ -a, --api=<value> Sanity API Version to use
425
+ -d, --data=<value> Data to send to the function
426
+ -e, --event=<option> Type of event (create, update, delete)
427
+ <options: create|update|delete>
428
+ -f, --file=<value> Read data from file and send to the function
429
+ -t, --timeout=<value> Execution timeout value in seconds
430
+ --data-after=<value> Current document
431
+ --data-before=<value> Original document
432
+ --dataset=<value> The Sanity dataset to use
433
+ --document-id=<value> Document to fetch and send to function
434
+ --document-id-after=<value> Current document
435
+ --document-id-before=<value> Original document
436
+ --file-after=<value> Current document
437
+ --file-before=<value> Original document
438
+ --project-id=<value> Sanity Project ID to use
439
+ --with-user-token Prime access token from CLI config
430
440
 
431
441
  DESCRIPTION
432
442
  Invoke a local Sanity Function
@@ -439,7 +449,7 @@ EXAMPLES
439
449
  $ sanity-run functions test <name> --data '{ "id": 1 }' --timeout 60
440
450
  ```
441
451
 
442
- _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v10.5.1/src/commands/functions/test.ts)_
452
+ _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v10.6.1/src/commands/functions/test.ts)_
443
453
 
444
454
  ## `sanity-run help [COMMAND]`
445
455
 
@@ -1,2 +1,2 @@
1
- import type { FunctionResource, InvocationResponse, InvokeContextOptions, InvokePayloadOptions } from '../../utils/types.js';
2
- export declare function testAction(resource: FunctionResource, options: InvokePayloadOptions, context: InvokeContextOptions): Promise<InvocationResponse>;
1
+ import type { FunctionResource, InvocationResponse, InvokeContextOptions, InvokeExecutionOptions, InvokePayloadOptions } from '../../utils/types.js';
2
+ export declare function testAction(resource: FunctionResource, payload: InvokePayloadOptions, context: InvokeContextOptions, options: InvokeExecutionOptions): Promise<InvocationResponse>;
@@ -1,8 +1,7 @@
1
1
  import invoke from '../../utils/invoke-local.js';
2
- export async function testAction(resource, options, context) {
3
- const { payload = null, timeout } = options;
2
+ export async function testAction(resource, payload, context, options) {
4
3
  try {
5
- const { json, logs } = await invoke(resource, payload, context, { timeout });
4
+ const { json, logs } = await invoke(resource, payload, context, options);
6
5
  return { error: undefined, json, logs };
7
6
  }
8
7
  catch (error) {
@@ -7,12 +7,19 @@ export default class TestCommand extends BlueprintCommand<typeof TestCommand> {
7
7
  static examples: string[];
8
8
  static flags: {
9
9
  data: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ 'data-before': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ 'data-after': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ event: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
13
  file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ 'file-before': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ 'file-after': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
16
  timeout: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
17
  api: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
18
  dataset: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
19
  'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
20
  'document-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
+ 'document-id-before': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
22
+ 'document-id-after': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
23
  'with-user-token': import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
24
  };
18
25
  run(): Promise<void>;
@@ -18,12 +18,70 @@ export default class TestCommand extends BlueprintCommand {
18
18
  exclusive: ['file', 'document-id'],
19
19
  required: false,
20
20
  }),
21
+ 'data-before': Flags.string({
22
+ description: 'Original document',
23
+ exclusive: [
24
+ 'data',
25
+ 'file',
26
+ 'document-id',
27
+ 'file-before',
28
+ 'file-after',
29
+ 'document-id-before',
30
+ 'document-id-after',
31
+ ],
32
+ required: false,
33
+ }),
34
+ 'data-after': Flags.string({
35
+ description: 'Current document',
36
+ exclusive: [
37
+ 'data',
38
+ 'file',
39
+ 'document-id',
40
+ 'file-before',
41
+ 'file-after',
42
+ 'document-id-before',
43
+ 'document-id-after',
44
+ ],
45
+ required: false,
46
+ }),
47
+ event: Flags.string({
48
+ char: 'e',
49
+ description: 'Type of event (create, update, delete)',
50
+ required: false,
51
+ options: ['create', 'update', 'delete'],
52
+ }),
21
53
  file: Flags.string({
22
54
  char: 'f',
23
55
  description: 'Read data from file and send to the function',
24
56
  exclusive: ['data', 'document-id'],
25
57
  required: false,
26
58
  }),
59
+ 'file-before': Flags.string({
60
+ description: 'Original document',
61
+ exclusive: [
62
+ 'data',
63
+ 'file',
64
+ 'document-id',
65
+ 'data-before',
66
+ 'data-after',
67
+ 'document-id-before',
68
+ 'document-id-after',
69
+ ],
70
+ required: false,
71
+ }),
72
+ 'file-after': Flags.string({
73
+ description: 'Current document',
74
+ exclusive: [
75
+ 'data',
76
+ 'file',
77
+ 'document-id',
78
+ 'data-before',
79
+ 'data-after',
80
+ 'document-id-before',
81
+ 'document-id-after',
82
+ ],
83
+ required: false,
84
+ }),
27
85
  timeout: Flags.integer({
28
86
  char: 't',
29
87
  description: 'Execution timeout value in seconds',
@@ -49,12 +107,49 @@ export default class TestCommand extends BlueprintCommand {
49
107
  exclusive: ['data', 'file'],
50
108
  required: false,
51
109
  }),
110
+ 'document-id-before': Flags.string({
111
+ description: 'Original document',
112
+ exclusive: [
113
+ 'data',
114
+ 'file',
115
+ 'document-id',
116
+ 'data-before',
117
+ 'data-after',
118
+ 'file-before',
119
+ 'file-after',
120
+ ],
121
+ required: false,
122
+ }),
123
+ 'document-id-after': Flags.string({
124
+ description: 'Current document',
125
+ exclusive: [
126
+ 'data',
127
+ 'file',
128
+ 'document-id',
129
+ 'data-before',
130
+ 'data-after',
131
+ 'file-before',
132
+ 'file-after',
133
+ ],
134
+ required: false,
135
+ }),
52
136
  'with-user-token': Flags.boolean({
53
137
  description: 'Prime access token from CLI config',
54
138
  default: false,
55
139
  }),
56
140
  };
57
141
  async run() {
142
+ if (this.flags.event === 'update') {
143
+ const hasDataPair = this.flags['data-before'] && this.flags['data-after'];
144
+ const hasFilePair = this.flags['file-before'] && this.flags['file-before'];
145
+ const hasDocPair = this.flags['document-id-before'] && this.flags['document-id-after'];
146
+ if (!(hasDataPair || hasFilePair || hasDocPair)) {
147
+ this.error('When using --event=update, you must provide one of the following flag pairs:\n' +
148
+ ' --data-before and --data-after\n' +
149
+ ' --file-before and --file-after\n' +
150
+ ' --document-id-before and --document-id-after');
151
+ }
152
+ }
58
153
  const { success, error } = await functionTestCore({
59
154
  bin: this.config.bin,
60
155
  log: (msg) => this.log(msg),
@@ -7,6 +7,7 @@ export interface FunctionTestOptions extends CoreConfig {
7
7
  };
8
8
  flags: {
9
9
  data?: string;
10
+ event?: string;
10
11
  file?: string;
11
12
  timeout?: number;
12
13
  api?: string;
@@ -14,6 +15,12 @@ export interface FunctionTestOptions extends CoreConfig {
14
15
  'project-id'?: string;
15
16
  'document-id'?: string;
16
17
  'with-user-token'?: boolean;
18
+ 'data-before'?: string;
19
+ 'data-after'?: string;
20
+ 'file-before'?: string;
21
+ 'file-after'?: string;
22
+ 'document-id-before'?: string;
23
+ 'document-id-after'?: string;
17
24
  };
18
25
  }
19
26
  export declare function functionTestCore(options: FunctionTestOptions): Promise<CoreResult>;
@@ -1,18 +1,36 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { cwd } from 'node:process';
1
4
  import ora from 'ora';
2
5
  import { testAction } from '../../actions/functions/test.js';
3
6
  import config from '../../config.js';
4
7
  import buildPayload from '../../utils/build-payload.js';
5
8
  import { findFunctionByName } from '../../utils/find-function.js';
6
9
  import { fetchDocument } from '../../utils/functions/fetch-document.js';
10
+ import { parseJsonObject } from '../../utils/parse-json-object.js';
11
+ import { isEventType, } from '../../utils/types.js';
7
12
  export async function functionTestCore(options) {
8
13
  const { blueprint, log, args, flags } = options;
9
14
  const { name: fnName } = args;
10
- const { data, file, timeout, api, dataset, 'document-id': documentId, 'with-user-token': withUserToken, } = flags;
15
+ const { data, event, file, timeout, api, dataset, 'document-id': documentId, 'with-user-token': withUserToken, 'data-before': dataBefore, 'data-after': dataAfter, 'file-before': fileBefore, 'file-after': fileAfter, 'document-id-before': documentIdBefore, 'document-id-after': documentIdAfter, } = flags;
11
16
  let { 'project-id': projectId } = flags;
12
17
  const { parsedBlueprint } = blueprint;
13
18
  if (!projectId && blueprint?.projectId) {
14
19
  projectId = blueprint.projectId;
15
20
  }
21
+ let eventType;
22
+ if (!event) {
23
+ eventType = 'create';
24
+ }
25
+ else if (isEventType(event)) {
26
+ eventType = event;
27
+ }
28
+ else {
29
+ return {
30
+ success: false,
31
+ error: 'The event must be one of `create`, `update` or `delete`.',
32
+ };
33
+ }
16
34
  try {
17
35
  const resource = findFunctionByName(parsedBlueprint, fnName); // throws if not found
18
36
  const contextOptions = {
@@ -26,21 +44,59 @@ export async function functionTestCore(options) {
26
44
  if (withUserToken) {
27
45
  contextOptions.clientOptions.token = config.token || undefined;
28
46
  }
29
- const payload = documentId
30
- ? await fetchDocument(documentId, {
47
+ let before = null;
48
+ let after = null;
49
+ if (dataBefore && dataAfter) {
50
+ before = parseJsonObject(dataBefore);
51
+ after = parseJsonObject(dataAfter);
52
+ }
53
+ else if (fileBefore && fileAfter) {
54
+ let fileContents = readFileSync(join(cwd(), fileBefore), 'utf8');
55
+ before = parseJsonObject(fileContents);
56
+ fileContents = readFileSync(join(cwd(), fileAfter), 'utf8');
57
+ after = parseJsonObject(fileContents);
58
+ }
59
+ else if (documentIdBefore && documentIdAfter) {
60
+ before = await fetchDocument(documentIdBefore, {
61
+ projectId,
62
+ dataset,
63
+ apiVersion: api,
64
+ apiHost: config.apiUrl,
65
+ token: config.token || undefined,
66
+ });
67
+ after = await fetchDocument(documentIdAfter, {
31
68
  projectId,
32
69
  dataset,
33
70
  apiVersion: api,
34
71
  apiHost: config.apiUrl,
35
72
  token: config.token || undefined,
36
- })
37
- : buildPayload({ data, file });
73
+ });
74
+ }
75
+ let payload;
76
+ if (after) {
77
+ payload = after;
78
+ }
79
+ else {
80
+ payload = documentId
81
+ ? await fetchDocument(documentId, {
82
+ projectId,
83
+ dataset,
84
+ apiVersion: api,
85
+ apiHost: config.apiUrl,
86
+ token: config.token || undefined,
87
+ })
88
+ : buildPayload({ data, file });
89
+ }
38
90
  const invokeOptions = {
91
+ event: eventType,
39
92
  payload,
40
- timeout: timeout ? timeout : resource.timeout,
93
+ before,
94
+ after,
41
95
  };
42
96
  const spinner = ora('Executing function...').start();
43
- const { json, logs, error } = await testAction(resource, invokeOptions, contextOptions);
97
+ const { json, logs, error } = await testAction(resource, invokeOptions, contextOptions, {
98
+ timeout: timeout ? timeout : resource.timeout,
99
+ });
44
100
  if (error) {
45
101
  spinner.fail('Function execution failed.');
46
102
  return {
@@ -5,4 +5,4 @@ declare function parseDocumentUrl(url: string): {
5
5
  docId: string;
6
6
  } | null;
7
7
  declare function buildApiUrl(projectId: string, dataset: string, docId: string, apiUrl: string): string;
8
- export { app, parseDocumentUrl, buildApiUrl };
8
+ export { app, buildApiUrl, parseDocumentUrl };
@@ -7,6 +7,7 @@ import config from '../config.js';
7
7
  import { findFunctionByName } from '../utils/find-function.js';
8
8
  import invoke from '../utils/invoke-local.js';
9
9
  import { isRecord } from '../utils/is-record.js';
10
+ import { isEventType } from '../utils/types.js';
10
11
  const host = 'localhost';
11
12
  const app = (port) => {
12
13
  const requestListener = async (req, res) => {
@@ -32,7 +33,7 @@ const app = (port) => {
32
33
  req.on('end', async () => {
33
34
  res.setHeader('Content-Type', 'application/json');
34
35
  try {
35
- const { data, func: functionName } = parseInvokeRequest(Buffer.concat(body));
36
+ const { data, func: functionName, metadata } = parseInvokeRequest(Buffer.concat(body));
36
37
  const { context, event } = data;
37
38
  // replace user token if required
38
39
  if (context.clientOptions.token) {
@@ -45,7 +46,13 @@ const app = (port) => {
45
46
  const { parsedBlueprint } = await readLocalBlueprint();
46
47
  const resource = findFunctionByName(parsedBlueprint, functionName);
47
48
  const readBlueprintTime = performance.now() - start;
48
- const response = await invoke(resource, event, context, { forceColor: false });
49
+ const payload = {
50
+ payload: event,
51
+ ...metadata,
52
+ };
53
+ const response = await invoke(resource, payload, context, {
54
+ forceColor: false,
55
+ });
49
56
  const timings = { ...response.timings, 'blueprint:read': readBlueprintTime };
50
57
  const timingHeaders = [];
51
58
  for (const [key, value] of Object.entries(timings)) {
@@ -207,7 +214,7 @@ function parseInvokeRequest(body) {
207
214
  if (!('data' in json)) {
208
215
  throw new Error('Request body is not valid, missing `data` field');
209
216
  }
210
- const { data, func } = json;
217
+ const { data, func, metadata } = json;
211
218
  if (typeof func !== 'string') {
212
219
  throw new Error('Request body is not valid, `func` field is not a string');
213
220
  }
@@ -237,6 +244,29 @@ function parseInvokeRequest(body) {
237
244
  if (typeof apiVersion !== 'string' && typeof apiVersion !== 'undefined') {
238
245
  throw new Error('Request body is not valid, `context.clientOptions.apiVersion` field is not a string');
239
246
  }
247
+ if (!isRecord(metadata)) {
248
+ throw new Error('Request body is not valid, `metadata` field is not an object');
249
+ }
250
+ if (!('event' in metadata)) {
251
+ throw new Error('Request body is not valid, `metadata.event` field is missing');
252
+ }
253
+ const metadataEvent = metadata.event;
254
+ if (typeof metadataEvent !== 'string' || !isEventType(metadataEvent)) {
255
+ throw new Error('Request body is not valid, `metadata.event` field is not one of `create`, `update`, or `delete`');
256
+ }
257
+ if (!('before' in metadata)) {
258
+ throw new Error('Request body is not valid, `metadata.before` field is missing');
259
+ }
260
+ if (!('after' in metadata)) {
261
+ throw new Error('Request body is not valid, `metadata.after` field is missing');
262
+ }
263
+ const { before, after } = metadata;
264
+ if (!isRecord(before) && before !== null) {
265
+ throw new Error('Request body is not valid, `metadata.before` field is not an object');
266
+ }
267
+ if (!isRecord(after) && after !== null) {
268
+ throw new Error('Request body is not valid, `metadata.after` field is not an object');
269
+ }
240
270
  const clientOptions = {
241
271
  ...context.clientOptions,
242
272
  projectId,
@@ -244,6 +274,10 @@ function parseInvokeRequest(body) {
244
274
  // Prefer `undefined` over empty string, triggering the right warnings in the client
245
275
  apiVersion: apiVersion || undefined,
246
276
  };
247
- return { func, data: { context: { ...context, clientOptions }, event } };
277
+ return {
278
+ func,
279
+ data: { context: { ...context, clientOptions }, event },
280
+ metadata: { event: metadataEvent, before, after },
281
+ };
248
282
  }
249
- export { app, parseDocumentUrl, buildApiUrl };
283
+ export { app, buildApiUrl, parseDocumentUrl };
@@ -14,7 +14,11 @@ declare function document({ projectId, dataset, docId }: {
14
14
  dataset: any;
15
15
  docId: any;
16
16
  }): Promise<void>;
17
- declare function invoke(payloadText?: string): void;
17
+ declare function invoke({ context, event, metadata }: {
18
+ context: any;
19
+ event: any;
20
+ metadata: any;
21
+ }): void;
18
22
  declare function projects(): void;
19
23
  declare function datasets(selectedProject: any): void;
20
24
  export {};
@@ -1,8 +1,11 @@
1
1
  /* eslint-disable n/no-unsupported-features/node-builtins */
2
2
  import {Store} from './vendor/vendor.bundle.js'
3
3
 
4
+ // list of events to simulate
5
+ const events = ['create', 'update', 'delete'].map((e) => ({name: e}))
6
+
4
7
  // eslint-disable-next-line new-cap
5
- const store = Store()
8
+ const store = Store({events, selectedEvent: events[0].name})
6
9
 
7
10
  export default function API() {
8
11
  return {
@@ -17,12 +20,13 @@ export default function API() {
17
20
  }
18
21
  }
19
22
 
20
- function invoke(payloadText = '{}') {
23
+ function invoke({context, event, metadata}) {
21
24
  store.inprogress = true
22
25
  const start = Date.now()
23
26
  const payload = {
24
- data: payloadText,
27
+ data: {context, event},
25
28
  func: store.selectedIndex,
29
+ metadata,
26
30
  }
27
31
  fetch('/invoke', {
28
32
  body: JSON.stringify(payload),
@@ -6,7 +6,11 @@ export class ApiBaseElement extends HTMLElement {
6
6
  dataset: any;
7
7
  docId: any;
8
8
  }) => Promise<void>;
9
- invoke: (payloadText?: string) => void;
9
+ invoke: ({ context, event, metadata }: {
10
+ context: any;
11
+ event: any;
12
+ metadata: any;
13
+ }) => void;
10
14
  projects: () => void;
11
15
  datasets: (selectedProject: any) => void;
12
16
  store: any;
@@ -20,6 +20,13 @@ class FiltersComponent extends HTMLElement {
20
20
  label-prop="name"
21
21
  subscribe-to="selectedProject"
22
22
  ></select-dropdown>
23
+ <select-dropdown
24
+ label="Event"
25
+ store-key="events"
26
+ selected-key="selectedEvent"
27
+ value-prop="name"
28
+ label-prop="name"
29
+ ></select-dropdown>
23
30
  <fieldset class="mar-t-sm">
24
31
  <label class="slab-text">
25
32
  <span style="display: block; margin-bottom: var(--space-1);">API Version</span>
@@ -4,40 +4,82 @@ import {basicSetup, EditorView, json} from '../vendor/vendor.bundle.js'
4
4
  import {ApiBaseElement} from './api-base.js'
5
5
  import {sanityCodeMirrorTheme} from './codemirror-theme.js'
6
6
 
7
- const template = `<div class="gutter-gradient relative h-100 max-h-100 y-scroll border-top border-top-none-l">
8
- <div class="bg gutter-gradient sticky top-0 right-0 left-0 z-100">
9
- <div class="flex items-center space-between" style="padding: 8px 20px 8px 48px;">
7
+ function template({selectedEvent}) {
8
+ const singleModeStyle = selectedEvent === 'update' ? ' style="display: none;"' : ''
9
+ const deltaModeStyle = selectedEvent !== 'update' ? '' : ' style="display: none;"'
10
+ return `<div id="payloadContainer" class="gutter-gradient relative h-100 max-h-100 y-scroll border-top border-top-none-l"${singleModeStyle}>
11
+ <div class="bg gutter-gradient sticky top-0 right-0 left-0 z-100">
12
+ <div class="flex items-center space-between" style="padding: 8px 20px 8px 48px;">
10
13
  <h2 class="config-label mar-t-0 mar-b-0">Document</h2>
14
+ </div>
15
+ <hr class='hr-border' style='margin-left: 31px; ' />
16
+ </div>
17
+ <div id="payload" name="payload" class='max-h-100 min-h-0'></div>
18
+ </div>
19
+ <div id="deltaPayloadContainer" class="gutter-gradient relative h-100 max-h-100 y-scroll border-top border-top-none-l"${deltaModeStyle}>
20
+ <div class="bg gutter-gradient sticky top-0 right-0 left-0 z-100">
21
+ <div class="flex items-center space-between" style="padding: 8px 20px 8px 48px;">
22
+ <h2 class="config-label mar-t-0 mar-b-0">Before Document</h2>
23
+ </div>
24
+ <hr class='hr-border' style='margin-left: 31px; ' />
11
25
  </div>
26
+ <div id="beforePayload" name="beforePayload" class='max-h-50 min-h-0'></div>
27
+ <div class="bg gutter-gradient sticky top-0 right-0 left-0 z-100">
12
28
  <hr class='hr-border' style='margin-left: 31px; ' />
29
+ <div class="flex items-center space-between" style="padding: 8px 20px 8px 48px;">
30
+ <h2 class="config-label mar-t-0 mar-b-0">After Document</h2>
13
31
  </div>
14
- <div id="payload" name="payload" class='max-h-100 min-h-0'></div>
32
+ <hr class='hr-border' style='margin-left: 31px; ' />
33
+ </div>
34
+ <div id="afterPayload" name="afterPayload" class='max-h-50 min-h-0'></div>
15
35
  </div>
16
36
  `
37
+ }
38
+
17
39
  class PayloadPanel extends ApiBaseElement {
18
40
  updatePayload = ({document}) => {
19
41
  if (!document) return
20
42
 
21
- const transaction = this.api.store.payload.state.update({
43
+ this.#updateCodeView(this.api.store.payload, document)
44
+ this.#updateCodeView(this.api.store.beforePayload, document)
45
+ this.#updateCodeView(this.api.store.afterPayload, document)
46
+ }
47
+ #updateCodeView = (view, document) => {
48
+ const transaction = view.state.update({
22
49
  changes: {
23
50
  from: 0,
24
51
  insert: JSON.stringify(document, null, 2),
25
- to: this.api.store.payload.state.doc.length,
52
+ to: view.state.doc.length,
26
53
  },
27
54
  })
28
- this.api.store.payload.dispatch(transaction)
55
+ view.dispatch(transaction)
29
56
  }
30
- connectedCallback() {
31
- this.innerHTML = template
32
- this.payload = this.querySelector('#payload')
57
+ updateSelectedEvent = ({selectedEvent}) => {
58
+ console.log('updateSelectedEvent', selectedEvent)
59
+
60
+ const payloadContainer = this.querySelector('#payloadContainer')
61
+ const deltaPayloadContainer = this.querySelector('#deltaPayloadContainer')
33
62
 
34
- this.api.store.payload = new EditorView({
35
- doc: '\n\n\n\n',
36
- extensions: [basicSetup, json(), sanityCodeMirrorTheme],
37
- parent: this.payload,
63
+ const isUpdateEvent = selectedEvent === 'update'
64
+ payloadContainer.style.display = isUpdateEvent ? 'none' : 'block'
65
+ deltaPayloadContainer.style.display = isUpdateEvent ? 'block' : 'none'
66
+ }
67
+ connectedCallback() {
68
+ this.innerHTML = template({
69
+ selectedEvent: this.api.store.selectedEvent,
38
70
  })
39
71
 
72
+ const beforePayloadElem = this.querySelector('#beforePayload')
73
+ const afterPayloadElem = this.querySelector('#afterPayload')
74
+
75
+ this.api.store.beforePayload = attachEditorView(beforePayloadElem)
76
+ this.api.store.afterPayload = attachEditorView(afterPayloadElem)
77
+
78
+ const payloadElem = this.querySelector('#payload')
79
+ this.api.store.payload = attachEditorView(payloadElem)
80
+
40
81
  this.api.subscribe(this.updatePayload, ['document'])
82
+ this.api.subscribe(this.updateSelectedEvent, ['selectedEvent'])
41
83
  }
42
84
  disconnectedCallback() {
43
85
  if (this.api) {
@@ -46,4 +88,12 @@ class PayloadPanel extends ApiBaseElement {
46
88
  }
47
89
  }
48
90
 
91
+ function attachEditorView(parent) {
92
+ return new EditorView({
93
+ doc: '\n\n\n\n',
94
+ extensions: [basicSetup, json(), sanityCodeMirrorTheme],
95
+ parent,
96
+ })
97
+ }
98
+
49
99
  customElements.define('payload-panel', PayloadPanel)
@@ -5,7 +5,7 @@ import {basicSetup, EditorState, EditorView, json} from '../vendor/vendor.bundle
5
5
  import {ApiBaseElement} from './api-base.js'
6
6
  import {sanityCodeMirrorTheme} from './codemirror-theme.js'
7
7
 
8
- const template = `<div style='overflow-y:scroll; min-height: 0;'>
8
+ const template = `<div class="border-left" style='overflow-y:scroll; min-height: 0;'>
9
9
  <div>
10
10
  <h3 class="config-label" style='display: none; margin-top: 0;'>Filter/Projection</h3>
11
11
  <header class='flex space-between'>
@@ -2,7 +2,7 @@
2
2
  import {ApiBaseElement} from './api-base.js'
3
3
 
4
4
  const runTemplate = `<svg data-sanity-icon="play" width="1em" height="1em" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.5 18.5V6.5L17.5 12.5L7.5 18.5Z" fill="currentColor" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"></path></svg>
5
- <span>Run</span>`
5
+ <span title="Type CTRL + Enter to Run">Run</span>`
6
6
 
7
7
  const template = `<div style="padding: var(--space-3);">
8
8
  <button ord="primary" class="sanity-button">
@@ -14,9 +14,29 @@ const template = `<div style="padding: var(--space-3);">
14
14
  `
15
15
  class RunPanel extends ApiBaseElement {
16
16
  invoke = () => {
17
+ const selectedEvent = this.api.store.selectedEvent
17
18
  this.api.store.result = {logs: '', time: 0}
18
- const payloadText = this.createPayloadText(this.api.store.payload.state.doc)
19
- const event = JSON.parse(payloadText)
19
+ let event = {}
20
+ let before = null
21
+ let after = null
22
+ if (selectedEvent === 'update') {
23
+ const beforePayloadText = this.createPayloadText(this.api.store.beforePayload.state.doc)
24
+ before = JSON.parse(beforePayloadText)
25
+
26
+ const afterPayloadText = this.createPayloadText(this.api.store.afterPayload.state.doc)
27
+ after = JSON.parse(afterPayloadText)
28
+
29
+ event = after
30
+ } else {
31
+ const payloadText = this.createPayloadText(this.api.store.payload.state.doc)
32
+ event = JSON.parse(payloadText)
33
+
34
+ if (selectedEvent === 'create') {
35
+ after = event
36
+ } else if (selectedEvent === 'delete') {
37
+ before = event
38
+ }
39
+ }
20
40
  const context = {
21
41
  clientOptions: {
22
42
  apiVersion: this.apiVersion.value,
@@ -25,7 +45,12 @@ class RunPanel extends ApiBaseElement {
25
45
  token: this.api.store.withToken,
26
46
  },
27
47
  }
28
- this.api.invoke({context, event})
48
+ const metadata = {
49
+ event: selectedEvent,
50
+ before,
51
+ after,
52
+ }
53
+ this.api.invoke({context, event, metadata})
29
54
  }
30
55
  createPayloadText = (doc) => {
31
56
  if (doc.text) {
@@ -74,10 +74,10 @@ class SelectDropdown extends ApiBaseElement {
74
74
  if (this.hasAttribute('trigger-fetch')) {
75
75
  // e.g. this.api.projects()
76
76
  this.api[this.storeKey]?.()
77
- }
78
-
79
- if (this.subscribeTo) {
77
+ } else if (this.subscribeTo) {
80
78
  this.api.subscribe(this.refreshOnChange, [this.subscribeTo])
79
+ } else {
80
+ this.renderOptions()
81
81
  }
82
82
  }
83
83
 
@@ -1,2 +1,2 @@
1
1
  import { type ClientConfig } from '@sanity/client';
2
- export declare function fetchDocument(documentId: string, { projectId, dataset, useCdn, apiVersion, apiHost, token }: ClientConfig): Promise<Record<string, unknown> | undefined>;
2
+ export declare function fetchDocument(documentId: string, { projectId, dataset, useCdn, apiVersion, apiHost, token }: ClientConfig): Promise<Record<string, unknown>>;
@@ -5,5 +5,8 @@ export async function fetchDocument(documentId, { projectId, dataset, useCdn = t
5
5
  const client = createClient({ projectId, dataset, useCdn, apiVersion, apiHost, token });
6
6
  const data = await client.fetch(`*[_id == "${documentId}"]`);
7
7
  spinner.stop();
8
- return data[0] ? data[0] : undefined;
8
+ if (!data[0]) {
9
+ throw Error(`Could not fetch document ID ${documentId}`);
10
+ }
11
+ return data[0];
9
12
  }
@@ -1,4 +1,4 @@
1
- import type { FunctionResource, GroqRule, InvocationResponse, InvokeContextOptions, InvokeExecutionOptions } from './types.js';
1
+ import type { FunctionResource, GroqRule, InvocationResponse, InvokeContextOptions, InvokeExecutionOptions, InvokePayloadOptions } from './types.js';
2
2
  export declare function sanitizeLogs(logs: string): string;
3
3
  export declare const DEFAULT_GROQ_RULE: {
4
4
  on: string[];
@@ -6,5 +6,5 @@ export declare const DEFAULT_GROQ_RULE: {
6
6
  projection: string;
7
7
  };
8
8
  export declare function isDefaultGROQRule(rule: GroqRule | undefined): boolean;
9
- export declare function applyGroqRule(resource: FunctionResource, data: Record<string, unknown> | null): Promise<any>;
10
- export default function invoke(resource: FunctionResource, data: Record<string, unknown> | null, context: InvokeContextOptions, options: InvokeExecutionOptions): Promise<InvocationResponse>;
9
+ export declare function applyGroqRule(resource: FunctionResource, data: Record<string, unknown> | null, before: Record<string, unknown> | null, after: Record<string, unknown> | null, projectId: string | undefined, dataset: string | undefined): Promise<any>;
10
+ export default function invoke(resource: FunctionResource, payload: InvokePayloadOptions, context: InvokeContextOptions, options: InvokeExecutionOptions): Promise<InvocationResponse>;
@@ -34,7 +34,7 @@ function getEvent(rule) {
34
34
  projection: rule.projection || DEFAULT_GROQ_RULE.projection,
35
35
  };
36
36
  }
37
- export async function applyGroqRule(resource, data) {
37
+ export async function applyGroqRule(resource, data, before, after, projectId, dataset) {
38
38
  // If there is no rule set return everything
39
39
  if (!resource.event)
40
40
  return data;
@@ -45,28 +45,30 @@ export async function applyGroqRule(resource, data) {
45
45
  const hasProjection = event.projection?.length;
46
46
  const projection = hasProjection ? `${event?.projection}` : '';
47
47
  const query = `*[${event?.filter}]${projection}`;
48
- try {
49
- const rule = groq.parse(query);
50
- const queryResults = await groq.evaluate(rule, { dataset: [data] });
51
- const currentFunctionDocumentSet = await queryResults.get();
52
- // TODO can this be multiple documents?
53
- return currentFunctionDocumentSet[0] || {};
54
- }
55
- catch {
56
- // parsing/validating the groq rule we do up front as part of functions HTTP API
57
- // so this likely would be triggered by evaluating the query, if anything
58
- throw Error('⚠️ failed parsing/evaluating GROQ rule! Skipping invoke.');
59
- }
48
+ const rule = groq.parse(query, { mode: 'delta' });
49
+ const sanity = projectId && dataset ? { projectId, dataset } : undefined;
50
+ const queryResults = await groq.evaluate(rule, { dataset: [data], before, after, sanity });
51
+ const currentFunctionDocumentSet = await queryResults.get();
52
+ return currentFunctionDocumentSet[0];
60
53
  }
61
54
  // default groq rule so just return the data
62
55
  return data;
63
56
  }
64
- export default async function invoke(resource, data, context, options) {
57
+ export default async function invoke(resource, payload, context, options) {
65
58
  if (!resource.src) {
66
59
  throw new Error(`Function resource "${resource.name}" is missing the 'src' property.`);
67
60
  }
61
+ const { before, after, payload: data = null } = payload;
68
62
  const { forceColor = true, timeout = 10 } = options;
69
- const filteredData = await applyGroqRule(resource, data);
63
+ const { projectId, dataset } = context.clientOptions;
64
+ const filteredData = await applyGroqRule(resource, data, before, after, projectId, dataset);
65
+ if (typeof filteredData === 'undefined') {
66
+ return {
67
+ logs: `⚠️ Filter "${resource.event?.filter}" returned an empty result. Skipping invoke.`,
68
+ error: undefined,
69
+ json: undefined,
70
+ };
71
+ }
70
72
  let cleanupBundle = async () => { };
71
73
  let functionPath = '';
72
74
  let bundleTimings;
@@ -79,8 +79,12 @@ export interface BuildPayloadOptions {
79
79
  /** @internal */
80
80
  export interface InvokePayloadOptions {
81
81
  payload?: Record<string, unknown>;
82
- timeout?: number;
82
+ event: EventType;
83
+ before: Record<string, unknown> | null;
84
+ after: Record<string, unknown> | null;
83
85
  }
86
+ export type EventType = 'create' | 'update' | 'delete';
87
+ export declare function isEventType(arg: string): arg is EventType;
84
88
  /** @internal */
85
89
  export interface InvokeContextOptions {
86
90
  clientOptions: {
@@ -1,6 +1,9 @@
1
1
  export function isLocalFunctionResource(r) {
2
2
  return r.type.startsWith('sanity.function.');
3
3
  }
4
+ export function isEventType(arg) {
5
+ return ['create', 'update', 'delete'].includes(arg);
6
+ }
4
7
  /** @internal */
5
8
  export var BlueprintParserErrorType;
6
9
  (function (BlueprintParserErrorType) {
@@ -712,6 +712,54 @@
712
712
  "multiple": false,
713
713
  "type": "option"
714
714
  },
715
+ "data-before": {
716
+ "description": "Original document",
717
+ "exclusive": [
718
+ "data",
719
+ "file",
720
+ "document-id",
721
+ "file-before",
722
+ "file-after",
723
+ "document-id-before",
724
+ "document-id-after"
725
+ ],
726
+ "name": "data-before",
727
+ "required": false,
728
+ "hasDynamicHelp": false,
729
+ "multiple": false,
730
+ "type": "option"
731
+ },
732
+ "data-after": {
733
+ "description": "Current document",
734
+ "exclusive": [
735
+ "data",
736
+ "file",
737
+ "document-id",
738
+ "file-before",
739
+ "file-after",
740
+ "document-id-before",
741
+ "document-id-after"
742
+ ],
743
+ "name": "data-after",
744
+ "required": false,
745
+ "hasDynamicHelp": false,
746
+ "multiple": false,
747
+ "type": "option"
748
+ },
749
+ "event": {
750
+ "char": "e",
751
+ "description": "Type of event (create, update, delete)",
752
+ "name": "event",
753
+ "required": false,
754
+ "hasDynamicHelp": false,
755
+ "multiple": false,
756
+ "options": [
757
+ "create",
758
+ "update",
759
+ "delete"
760
+ ],
761
+ "type": "option"
762
+ },
715
763
  "file": {
716
764
  "char": "f",
717
765
  "description": "Read data from file and send to the function",
@@ -725,6 +773,40 @@
725
773
  "multiple": false,
726
774
  "type": "option"
727
775
  },
776
+ "file-before": {
777
+ "description": "Original document",
778
+ "exclusive": [
779
+ "data",
780
+ "file",
781
+ "document-id",
782
+ "data-before",
783
+ "data-after",
784
+ "document-id-before",
785
+ "document-id-after"
786
+ ],
787
+ "name": "file-before",
788
+ "required": false,
789
+ "hasDynamicHelp": false,
790
+ "multiple": false,
791
+ "type": "option"
792
+ },
793
+ "file-after": {
794
+ "description": "Current document",
795
+ "exclusive": [
796
+ "data",
797
+ "file",
798
+ "document-id",
799
+ "data-before",
800
+ "data-after",
801
+ "document-id-before",
802
+ "document-id-after"
803
+ ],
804
+ "name": "file-after",
805
+ "required": false,
806
+ "hasDynamicHelp": false,
807
+ "multiple": false,
808
+ "type": "option"
809
+ },
728
810
  "timeout": {
729
811
  "char": "t",
730
812
  "description": "Execution timeout value in seconds",
@@ -779,6 +861,40 @@
779
861
  "multiple": false,
780
862
  "type": "option"
781
863
  },
864
+ "document-id-before": {
865
+ "description": "Original document",
866
+ "exclusive": [
867
+ "data",
868
+ "file",
869
+ "document-id",
870
+ "data-before",
871
+ "data-after",
872
+ "file-before",
873
+ "file-after"
874
+ ],
875
+ "name": "document-id-before",
876
+ "required": false,
877
+ "hasDynamicHelp": false,
878
+ "multiple": false,
879
+ "type": "option"
880
+ },
881
+ "document-id-after": {
882
+ "description": "Current document",
883
+ "exclusive": [
884
+ "data",
885
+ "file",
886
+ "document-id",
887
+ "data-before",
888
+ "data-after",
889
+ "file-before",
890
+ "file-after"
891
+ ],
892
+ "name": "document-id-after",
893
+ "required": false,
894
+ "hasDynamicHelp": false,
895
+ "multiple": false,
896
+ "type": "option"
897
+ },
782
898
  "with-user-token": {
783
899
  "description": "Prime access token from CLI config",
784
900
  "name": "with-user-token",
@@ -907,5 +1023,5 @@
907
1023
  ]
908
1024
  }
909
1025
  },
910
- "version": "10.5.1"
1026
+ "version": "10.6.1"
911
1027
  }
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": "10.5.1",
4
+ "version": "10.6.1",
5
5
  "author": "Sanity Runtime Team",
6
6
  "type": "module",
7
7
  "license": "MIT",
@@ -89,7 +89,7 @@
89
89
  "eventsource": "^4.0.0",
90
90
  "find-up": "^7.0.0",
91
91
  "get-folder-size": "^5.0.0",
92
- "groq-js": "^1.17.3",
92
+ "groq-js": "^1.18.0",
93
93
  "inquirer": "^12.9.2",
94
94
  "jiti": "^2.5.1",
95
95
  "mime-types": "^3.0.1",