@sanity/runtime-cli 11.0.4 → 11.1.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.
Files changed (34) hide show
  1. package/README.md +30 -26
  2. package/dist/actions/blueprints/blueprint.d.ts +3 -3
  3. package/dist/actions/blueprints/resources.d.ts +2 -2
  4. package/dist/actions/blueprints/resources.js +3 -4
  5. package/dist/commands/blueprints/add.js +9 -1
  6. package/dist/commands/functions/add.js +9 -1
  7. package/dist/commands/functions/test.d.ts +1 -0
  8. package/dist/commands/functions/test.js +6 -0
  9. package/dist/cores/functions/add.js +31 -9
  10. package/dist/cores/functions/test.d.ts +1 -0
  11. package/dist/cores/functions/test.js +37 -7
  12. package/dist/server/app.js +99 -3
  13. package/dist/server/static/api.js +48 -2
  14. package/dist/server/static/components/app.css +16 -1
  15. package/dist/server/static/components/fetch-button.js +14 -5
  16. package/dist/server/static/components/filter-api-version.js +14 -0
  17. package/dist/server/static/components/filter-document-id.js +26 -0
  18. package/dist/server/static/components/filter-with-token.js +21 -0
  19. package/dist/server/static/components/filters.js +47 -42
  20. package/dist/server/static/components/function-list.js +15 -5
  21. package/dist/server/static/components/run-panel.js +11 -2
  22. package/dist/server/static/index.html +3 -0
  23. package/dist/utils/display/blueprints-formatting.d.ts +1 -1
  24. package/dist/utils/display/blueprints-formatting.js +4 -3
  25. package/dist/utils/display/resources-formatting.d.ts +2 -2
  26. package/dist/utils/functions/fetch-document.d.ts +2 -0
  27. package/dist/utils/functions/fetch-document.js +15 -0
  28. package/dist/utils/invoke-local.d.ts +2 -2
  29. package/dist/utils/invoke-local.js +11 -5
  30. package/dist/utils/types.d.ts +41 -5
  31. package/dist/utils/types.js +8 -0
  32. package/dist/utils/validate/resource.js +55 -30
  33. package/oclif.manifest.json +24 -3
  34. package/package.json +2 -2
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/11.0.4 linux-x64 node-v24.11.0
23
+ @sanity/runtime-cli/11.1.0 linux-x64 node-v24.11.0
24
24
  $ sanity-run --help [COMMAND]
25
25
  USAGE
26
26
  $ sanity-run COMMAND
@@ -55,8 +55,9 @@ Add a Resource to a Blueprint
55
55
  ```
56
56
  USAGE
57
57
  $ sanity-run blueprints add TYPE [--example <value> | -n <value> | --fn-type
58
- document-create|document-delete|document-update|document-publish... | --language ts|js | --javascript | --fn-helpers
59
- | --fn-installer skip|npm|pnpm|yarn] [-i | ]
58
+ document-create|document-delete|document-update|document-publish|media-library-asset-create|media-library-asset-upda
59
+ te|media-library-asset-delete... | --language ts|js | --javascript | --fn-helpers | --fn-installer
60
+ skip|npm|pnpm|yarn] [-i | ]
60
61
 
61
62
  ARGUMENTS
62
63
  TYPE (function) Type of Resource to add (e.g. function)
@@ -70,7 +71,8 @@ FLAGS
70
71
  <options: skip|npm|pnpm|yarn>
71
72
  --fn-type=<option>... Document change event(s) that should trigger the function; you can specify multiple
72
73
  events by specifying this flag multiple times
73
- <options: document-create|document-delete|document-update|document-publish>
74
+ <options: document-create|document-delete|document-update|document-publish|media-library-
75
+ asset-create|media-library-asset-update|media-library-asset-delete>
74
76
  --javascript Use JavaScript instead of TypeScript
75
77
  --language=<option> [default: ts] Language of the new Function
76
78
  <options: ts|js>
@@ -90,7 +92,7 @@ EXAMPLES
90
92
  $ sanity-run blueprints add function --name my-function --fn-type document-create --fn-type document-update --lang js
91
93
  ```
92
94
 
93
- _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v11.0.4/src/commands/blueprints/add.ts)_
95
+ _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.0/src/commands/blueprints/add.ts)_
94
96
 
95
97
  ## `sanity-run blueprints config`
96
98
 
@@ -122,7 +124,7 @@ EXAMPLES
122
124
  $ sanity-run blueprints config --edit --project-id <projectId> --stack-id <stackId>
123
125
  ```
124
126
 
125
- _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v11.0.4/src/commands/blueprints/config.ts)_
127
+ _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.0/src/commands/blueprints/config.ts)_
126
128
 
127
129
  ## `sanity-run blueprints deploy`
128
130
 
@@ -144,7 +146,7 @@ EXAMPLES
144
146
  $ sanity-run blueprints deploy --no-wait
145
147
  ```
146
148
 
147
- _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v11.0.4/src/commands/blueprints/deploy.ts)_
149
+ _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.0/src/commands/blueprints/deploy.ts)_
148
150
 
149
151
  ## `sanity-run blueprints destroy`
150
152
 
@@ -171,7 +173,7 @@ EXAMPLES
171
173
  $ sanity-run blueprints destroy --stack-id <stackId> --project-id <projectId> --force --no-wait
172
174
  ```
173
175
 
174
- _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v11.0.4/src/commands/blueprints/destroy.ts)_
176
+ _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.0/src/commands/blueprints/destroy.ts)_
175
177
 
176
178
  ## `sanity-run blueprints doctor`
177
179
 
@@ -189,7 +191,7 @@ DESCRIPTION
189
191
  Diagnose potential issues with Blueprint configuration
190
192
  ```
191
193
 
192
- _See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v11.0.4/src/commands/blueprints/doctor.ts)_
194
+ _See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.0/src/commands/blueprints/doctor.ts)_
193
195
 
194
196
  ## `sanity-run blueprints info`
195
197
 
@@ -211,7 +213,7 @@ EXAMPLES
211
213
  $ sanity-run blueprints info --stack-id <stackId>
212
214
  ```
213
215
 
214
- _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v11.0.4/src/commands/blueprints/info.ts)_
216
+ _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.0/src/commands/blueprints/info.ts)_
215
217
 
216
218
  ## `sanity-run blueprints init [DIR]`
217
219
 
@@ -250,7 +252,7 @@ EXAMPLES
250
252
  $ sanity-run blueprints init --blueprint-type <json|js|ts> --stack-name <stackName>
251
253
  ```
252
254
 
253
- _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v11.0.4/src/commands/blueprints/init.ts)_
255
+ _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.0/src/commands/blueprints/init.ts)_
254
256
 
255
257
  ## `sanity-run blueprints logs`
256
258
 
@@ -272,7 +274,7 @@ EXAMPLES
272
274
  $ sanity-run blueprints logs --watch
273
275
  ```
274
276
 
275
- _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v11.0.4/src/commands/blueprints/logs.ts)_
277
+ _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.0/src/commands/blueprints/logs.ts)_
276
278
 
277
279
  ## `sanity-run blueprints plan`
278
280
 
@@ -289,7 +291,7 @@ EXAMPLES
289
291
  $ sanity-run blueprints plan
290
292
  ```
291
293
 
292
- _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v11.0.4/src/commands/blueprints/plan.ts)_
294
+ _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.0/src/commands/blueprints/plan.ts)_
293
295
 
294
296
  ## `sanity-run blueprints stacks`
295
297
 
@@ -314,7 +316,7 @@ EXAMPLES
314
316
  $ sanity-run blueprints stacks --organization-id <organizationId>
315
317
  ```
316
318
 
317
- _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v11.0.4/src/commands/blueprints/stacks.ts)_
319
+ _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.0/src/commands/blueprints/stacks.ts)_
318
320
 
319
321
  ## `sanity-run functions add`
320
322
 
@@ -323,8 +325,8 @@ Add a Function to your Blueprint
323
325
  ```
324
326
  USAGE
325
327
  $ sanity-run functions add [--example <value> | -n <value> | | --language ts|js | --javascript | | ] [--type
326
- document-create|document-delete|document-update|document-publish... ] [--helpers] [--installer skip|npm|pnpm|yarn]
327
- [-i | ]
328
+ document-create|document-delete|document-update|document-publish|media-library-asset-create|media-library-asset-upda
329
+ te|media-library-asset-delete... ] [--helpers] [--installer skip|npm|pnpm|yarn] [-i | ]
328
330
 
329
331
  FLAGS
330
332
  -i, --install Shortcut for --fn-installer npm
@@ -338,7 +340,8 @@ FLAGS
338
340
  <options: ts|js>
339
341
  --type=<option>... Document change event(s) that should trigger the function; you can specify multiple events
340
342
  by specifying this flag multiple times
341
- <options: document-create|document-delete|document-update|document-publish>
343
+ <options: document-create|document-delete|document-update|document-publish|media-library-ass
344
+ et-create|media-library-asset-update|media-library-asset-delete>
342
345
 
343
346
  DESCRIPTION
344
347
  Add a Function to your Blueprint
@@ -355,7 +358,7 @@ EXAMPLES
355
358
  $ sanity-run functions add --name my-function --type document-create --type document-update --lang js
356
359
  ```
357
360
 
358
- _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v11.0.4/src/commands/functions/add.ts)_
361
+ _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.0/src/commands/functions/add.ts)_
359
362
 
360
363
  ## `sanity-run functions dev`
361
364
 
@@ -377,7 +380,7 @@ EXAMPLES
377
380
  $ sanity-run functions dev --port 8974
378
381
  ```
379
382
 
380
- _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v11.0.4/src/commands/functions/dev.ts)_
383
+ _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.0/src/commands/functions/dev.ts)_
381
384
 
382
385
  ## `sanity-run functions env add NAME KEY VALUE`
383
386
 
@@ -399,7 +402,7 @@ EXAMPLES
399
402
  $ sanity-run functions env add MyFunction API_URL https://api.example.com/
400
403
  ```
401
404
 
402
- _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v11.0.4/src/commands/functions/env/add.ts)_
405
+ _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.0/src/commands/functions/env/add.ts)_
403
406
 
404
407
  ## `sanity-run functions env list NAME`
405
408
 
@@ -419,7 +422,7 @@ EXAMPLES
419
422
  $ sanity-run functions env list MyFunction
420
423
  ```
421
424
 
422
- _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v11.0.4/src/commands/functions/env/list.ts)_
425
+ _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.0/src/commands/functions/env/list.ts)_
423
426
 
424
427
  ## `sanity-run functions env remove NAME KEY`
425
428
 
@@ -440,7 +443,7 @@ EXAMPLES
440
443
  $ sanity-run functions env remove MyFunction API_URL
441
444
  ```
442
445
 
443
- _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v11.0.4/src/commands/functions/env/remove.ts)_
446
+ _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.0/src/commands/functions/env/remove.ts)_
444
447
 
445
448
  ## `sanity-run functions logs NAME`
446
449
 
@@ -474,7 +477,7 @@ EXAMPLES
474
477
  $ sanity-run functions logs <name> --delete
475
478
  ```
476
479
 
477
- _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v11.0.4/src/commands/functions/logs.ts)_
480
+ _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.0/src/commands/functions/logs.ts)_
478
481
 
479
482
  ## `sanity-run functions test NAME`
480
483
 
@@ -484,8 +487,8 @@ Invoke a local Sanity Function
484
487
  USAGE
485
488
  $ sanity-run functions test NAME [--data-before <value> | [-d <value> | -f <value> | --document-id <value>] | |
486
489
  | --file-before <value> | --file-after <value> | --document-id-before <value> | --document-id-after <value>]
487
- [--data-after <value> | | | | | | | ] [-e create|update|delete] [-t <value>] [-a <value>] [--dataset <value>]
488
- [--project-id <value>] [--organization-id <value>] [--with-user-token]
490
+ [--data-after <value> | | | | | | | ] [-e create|update|delete] [-t <value>] [-a <value>] [--organization-id
491
+ <value>] [--with-user-token] [--media-library-id <value> | --project-id <value> | --dataset <value>]
489
492
 
490
493
  ARGUMENTS
491
494
  NAME The name of the Sanity Function
@@ -505,6 +508,7 @@ FLAGS
505
508
  --document-id-before=<value> Original document
506
509
  --file-after=<value> Current document
507
510
  --file-before=<value> Original document
511
+ --media-library-id=<value> Sanity Media Library ID to use
508
512
  --organization-id=<value> Sanity Organization ID to use
509
513
  --project-id=<value> Sanity Project ID to use
510
514
  --with-user-token Prime access token from CLI config
@@ -522,7 +526,7 @@ EXAMPLES
522
526
  $ sanity-run functions test <name> --event update --data-before '{ "title": "before" }' --data-after '{ "title": "after" }'
523
527
  ```
524
528
 
525
- _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v11.0.4/src/commands/functions/test.ts)_
529
+ _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v11.1.0/src/commands/functions/test.ts)_
526
530
 
527
531
  ## `sanity-run help [COMMAND]`
528
532
 
@@ -48,8 +48,8 @@ export declare function writeBlueprintToDisk({ blueprintFilePath, jsonContent, }
48
48
  blueprintFilePath: string;
49
49
  jsonContent?: Blueprint;
50
50
  }): string;
51
- export declare function addResourceToBlueprint({ blueprintFilePath, resource, }: {
51
+ export declare function addResourceToBlueprint<T extends Resource>({ blueprintFilePath, resource, }: {
52
52
  blueprintFilePath?: string;
53
- resource: Resource;
54
- }): Resource | undefined;
53
+ resource: T;
54
+ }): T | undefined;
55
55
  export {};
@@ -1,4 +1,4 @@
1
- import type { FunctionResource } from '../../utils/types.js';
1
+ import type { FunctionResourceBase } from '../../utils/types.js';
2
2
  interface FunctionResourceOptions {
3
3
  name: string;
4
4
  type: string[];
@@ -13,6 +13,6 @@ interface FunctionResourceOptions {
13
13
  export declare function createFunctionResource(options: FunctionResourceOptions): Promise<{
14
14
  filePath: string;
15
15
  resourceAdded: boolean;
16
- resource: FunctionResource;
16
+ resource: FunctionResourceBase;
17
17
  }>;
18
18
  export {};
@@ -56,10 +56,9 @@ export async function createFunctionResource(options) {
56
56
  throw new Error(`Failed to install dependencies using \`${installCommand}\``);
57
57
  }
58
58
  }
59
- // type looks like 'document-publish'
60
- const typeParts = type[0].split('-');
61
- const typeName = typeParts[0];
62
- const eventsOn = type.map((t) => t.split('-')[1]);
59
+ // type looks like 'document-publish' or 'media-library-asset-delete'
60
+ const typeName = type[0].substring(type[0].lastIndexOf('-') + 1);
61
+ const eventsOn = type.map((t) => t.substring(t.lastIndexOf('-') + 1));
63
62
  // Create resource definition
64
63
  const resourceJson = {
65
64
  name,
@@ -39,7 +39,15 @@ export default class AddCommand extends BlueprintCommand {
39
39
  }),
40
40
  'fn-type': Flags.string({
41
41
  description: 'Document change event(s) that should trigger the function; you can specify multiple events by specifying this flag multiple times',
42
- options: ['document-create', 'document-delete', 'document-update', 'document-publish'],
42
+ options: [
43
+ 'document-create',
44
+ 'document-delete',
45
+ 'document-update',
46
+ 'document-publish',
47
+ 'media-library-asset-create',
48
+ 'media-library-asset-update',
49
+ 'media-library-asset-delete',
50
+ ],
43
51
  aliases: ['function-type'],
44
52
  multiple: true,
45
53
  multipleNonGreedy: true,
@@ -22,7 +22,15 @@ export default class AddCommand extends BlueprintCommand {
22
22
  }),
23
23
  type: Flags.string({
24
24
  description: 'Document change event(s) that should trigger the function; you can specify multiple events by specifying this flag multiple times',
25
- options: ['document-create', 'document-delete', 'document-update', 'document-publish'],
25
+ options: [
26
+ 'document-create',
27
+ 'document-delete',
28
+ 'document-update',
29
+ 'document-publish',
30
+ 'media-library-asset-create',
31
+ 'media-library-asset-update',
32
+ 'media-library-asset-delete',
33
+ ],
26
34
  multiple: true,
27
35
  multipleNonGreedy: true,
28
36
  dependsOn: ['name'],
@@ -22,6 +22,7 @@ export default class TestCommand extends BlueprintCommand<typeof TestCommand> {
22
22
  'document-id-before': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
23
23
  'document-id-after': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
24
24
  'with-user-token': import("@oclif/core/interfaces").BooleanFlag<boolean>;
25
+ 'media-library-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
25
26
  };
26
27
  run(): Promise<void>;
27
28
  }
@@ -143,6 +143,12 @@ export default class TestCommand extends BlueprintCommand {
143
143
  description: 'Prime access token from CLI config',
144
144
  default: false,
145
145
  }),
146
+ 'media-library-id': Flags.string({
147
+ description: 'Sanity Media Library ID to use',
148
+ aliases: ['media'],
149
+ exclusive: ['project-id', 'dataset'],
150
+ required: false,
151
+ }),
146
152
  };
147
153
  async run() {
148
154
  if (this.flags.event === 'update') {
@@ -8,14 +8,21 @@ import { createFunctionResource } from '../../actions/blueprints/resources.js';
8
8
  import { verifyExampleExists, writeExample } from '../../actions/sanity/examples.js';
9
9
  import { check, indent, warn } from '../../utils/display/presenters.js';
10
10
  import { validateFunctionName } from '../../utils/validate/resource.js';
11
- const generateFunctionBlueprintResourceTemplate = (fnName, eventNames) => `
11
+ const generateFunctionBlueprintResourceTemplate = (fnName, eventNames) => {
12
+ const definer = eventNames[0].substring(0, eventNames[0].lastIndexOf('-')) === 'document'
13
+ ? 'Document'
14
+ : 'MediaLibraryAsset';
15
+ const isML = definer.startsWith('Media');
16
+ const eventOns = eventNames.map((e) => `'${e.substring(e.lastIndexOf('-') + 1)}'`);
17
+ return `
12
18
  export default defineBlueprint({
13
19
  resources: [
14
20
  // ...
15
- defineDocumentFunction({name: '${fnName}', event: {on: [${eventNames.map((e) => `'${e.replace('document-', '')}'`).join(', ')}]}}), // ← add this line
21
+ define${definer}Function({name: '${fnName}', event: {on: [${eventOns.join(', ')}]${isML ? ", resource: {type: 'media-library', id: 'my-media-library-id'}" : ''}}}), // ← add this line
16
22
  ],
17
23
  })
18
24
  `;
25
+ };
19
26
  export async function functionAddCore(options) {
20
27
  const root = cwd();
21
28
  const { log, blueprint, flags } = options;
@@ -115,8 +122,20 @@ export async function functionAddCore(options) {
115
122
  if (fnTypes.length === 0) {
116
123
  throw new Error('At least one function type must be provided.');
117
124
  }
118
- if (!fnTypes.every((evt) => ['document-publish', 'document-create', 'document-delete', 'document-update'].includes(evt))) {
119
- throw new Error('Invalid function type. Must be one of: document-publish, document-create, document-delete, document-update');
125
+ if (!fnTypes.every((evt) => [
126
+ 'document-publish',
127
+ 'document-create',
128
+ 'document-delete',
129
+ 'document-update',
130
+ 'media-library-asset-create',
131
+ 'media-library-asset-update',
132
+ 'media-library-asset-delete',
133
+ ].includes(evt))) {
134
+ throw new Error('Invalid function type. Must be one of: document-publish, document-create, document-delete, document-update, media-library-asset-create, media-library-asset-update, media-library-asset-delete');
135
+ }
136
+ const eventSources = new Set(fnTypes.map((t) => t.substring(0, t.lastIndexOf('-'))));
137
+ if (eventSources.size > 1) {
138
+ throw new Error('Invalid function type. Cannot mix document-* and media-library-asset-* types.');
120
139
  }
121
140
  let addHelpers;
122
141
  let installCommand;
@@ -195,21 +214,24 @@ async function promptForFunctionType() {
195
214
  {
196
215
  type: 'checkbox',
197
216
  name: 'functionType',
198
- message: 'Choose document change events to trigger your function:',
217
+ message: 'Choose events to trigger your function:',
199
218
  choices: [
200
219
  { name: 'Document Create', value: 'document-create' },
201
220
  { name: 'Document Update', value: 'document-update' },
202
221
  { name: 'Document Delete', value: 'document-delete' },
203
- {
204
- name: 'Document Publish (Deprecated - use Create + Update instead)',
205
- value: 'document-publish',
206
- },
222
+ { name: 'Media Library Asset Create', value: 'media-library-asset-create' },
223
+ { name: 'Media Library Asset Update', value: 'media-library-asset-update' },
224
+ { name: 'Media Library Asset Delete', value: 'media-library-asset-delete' },
207
225
  ],
208
226
  default: ['document-create', 'document-update'],
209
227
  validate(choices) {
210
228
  if (choices.length === 0) {
211
229
  return 'You must choose at least one function type / document change event';
212
230
  }
231
+ if (choices.some((c) => String(c.value).startsWith('media-library')) &&
232
+ choices.some((c) => String(c.value).startsWith('document'))) {
233
+ return 'You cannot mix both Document and Media Library Asset events together in one Function';
234
+ }
213
235
  return true;
214
236
  },
215
237
  },
@@ -22,6 +22,7 @@ export interface FunctionTestOptions extends CoreConfig {
22
22
  'file-after'?: string;
23
23
  'document-id-before'?: string;
24
24
  'document-id-after'?: string;
25
+ 'media-library-id'?: string;
25
26
  };
26
27
  }
27
28
  export declare function functionTestCore(options: FunctionTestOptions): Promise<CoreResult>;
@@ -6,13 +6,13 @@ import { testAction } from '../../actions/functions/test.js';
6
6
  import config from '../../config.js';
7
7
  import buildPayload from '../../utils/build-payload.js';
8
8
  import { findFunctionByName } from '../../utils/find-function.js';
9
- import { fetchDocument } from '../../utils/functions/fetch-document.js';
9
+ import { fetchAsset, fetchDocument } from '../../utils/functions/fetch-document.js';
10
10
  import { parseJsonObject } from '../../utils/parse-json-object.js';
11
11
  import { isEventType, } from '../../utils/types.js';
12
12
  export async function functionTestCore(options) {
13
13
  const { blueprint, log, args, flags } = options;
14
14
  const { name: fnName } = args;
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;
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, 'media-library-id': mediaLibraryId, } = flags;
16
16
  const { parsedBlueprint } = blueprint;
17
17
  const { 'project-id': projectId = blueprint?.projectId, 'organization-id': organizationId = blueprint?.organizationId, } = flags;
18
18
  let eventType;
@@ -30,6 +30,7 @@ export async function functionTestCore(options) {
30
30
  }
31
31
  try {
32
32
  const resource = findFunctionByName(parsedBlueprint, fnName); // throws if not found
33
+ const docFunction = resource.type === 'sanity.function.document';
33
34
  const contextOptions = {
34
35
  clientOptions: {
35
36
  apiVersion: api,
@@ -37,6 +38,10 @@ export async function functionTestCore(options) {
37
38
  projectId,
38
39
  organizationId,
39
40
  },
41
+ eventResourceType: docFunction ? 'dataset' : 'media-library',
42
+ eventResourceId: (docFunction ? `${projectId}.${dataset}` : mediaLibraryId) || '',
43
+ functionResourceType: 'project',
44
+ functionResourceId: (docFunction ? projectId : organizationId) || '',
40
45
  };
41
46
  // If the user sets the flag to use the real token set it in our options
42
47
  if (withUserToken) {
@@ -54,7 +59,7 @@ export async function functionTestCore(options) {
54
59
  fileContents = readFileSync(join(cwd(), fileAfter), 'utf8');
55
60
  after = parseJsonObject(fileContents);
56
61
  }
57
- else if (documentIdBefore && documentIdAfter) {
62
+ else if (documentIdBefore && documentIdAfter && docFunction) {
58
63
  before = await fetchDocument(documentIdBefore, {
59
64
  projectId,
60
65
  dataset,
@@ -70,20 +75,45 @@ export async function functionTestCore(options) {
70
75
  token: config.token || undefined,
71
76
  });
72
77
  }
78
+ else if (documentIdBefore && documentIdAfter) {
79
+ before = await fetchAsset(documentIdBefore, {
80
+ mediaLibraryId,
81
+ apiVersion: api,
82
+ apiHost: config.apiUrl,
83
+ token: config.token || undefined,
84
+ });
85
+ after = await fetchAsset(documentIdAfter, {
86
+ mediaLibraryId,
87
+ apiVersion: api,
88
+ apiHost: config.apiUrl,
89
+ token: config.token || undefined,
90
+ });
91
+ }
73
92
  let payload;
74
93
  if (after) {
75
94
  payload = after;
76
95
  }
77
96
  else {
78
- payload = documentId
79
- ? await fetchDocument(documentId, {
97
+ if (documentId && docFunction) {
98
+ payload = await fetchDocument(documentId, {
80
99
  projectId,
81
100
  dataset,
82
101
  apiVersion: api,
83
102
  apiHost: config.apiUrl,
84
103
  token: config.token || undefined,
85
- })
86
- : buildPayload({ data, file });
104
+ });
105
+ }
106
+ else if (documentId) {
107
+ payload = await fetchAsset(documentId, {
108
+ mediaLibraryId,
109
+ apiVersion: api,
110
+ apiHost: config.apiUrl,
111
+ token: config.token || undefined,
112
+ });
113
+ }
114
+ else {
115
+ payload = buildPayload({ data, file });
116
+ }
87
117
  }
88
118
  const invokeOptions = {
89
119
  event: eventType,
@@ -14,10 +14,10 @@ const app = (port, executionOptions) => {
14
14
  switch (true) {
15
15
  case req.url === '/blueprint': {
16
16
  try {
17
- const { parsedBlueprint, projectId } = await readLocalBlueprint();
17
+ const { parsedBlueprint, projectId, organizationId } = await readLocalBlueprint();
18
18
  res.setHeader('Content-Type', 'application/json');
19
19
  res.writeHead(200);
20
- res.end(JSON.stringify({ parsedBlueprint, projectId })); // Use blueprint directly
20
+ res.end(JSON.stringify({ parsedBlueprint, projectId, organizationId })); // Use blueprint directly
21
21
  }
22
22
  catch (error) {
23
23
  res.writeHead(404);
@@ -137,6 +137,75 @@ const app = (port, executionOptions) => {
137
137
  }
138
138
  break;
139
139
  }
140
+ case req.url === '/organizations': {
141
+ res.setHeader('Content-Type', 'application/json');
142
+ try {
143
+ const response = await fetch(`${config.apiUrl}/v2021-06-07/organizations?includeImplicitMemberships=true&includeMembers=false&includeFeatures=false`, {
144
+ headers: {
145
+ Authorization: `Bearer ${config.token}`,
146
+ },
147
+ });
148
+ const json = await response.json();
149
+ res.writeHead(200);
150
+ res.end(JSON.stringify(json));
151
+ }
152
+ catch {
153
+ res.writeHead(200);
154
+ res.end(JSON.stringify([]));
155
+ }
156
+ break;
157
+ }
158
+ case req.url?.startsWith('/media-libraries'): {
159
+ const url = req.url || '';
160
+ const matches = url.match(/[?&]organization=([^&]+)/) || [];
161
+ const organizationId = matches ? matches[1] : null;
162
+ res.setHeader('Content-Type', 'application/json');
163
+ try {
164
+ const response = await fetch(`${config.apiUrl}/vX/media-libraries?organizationId=${organizationId}`, {
165
+ headers: {
166
+ Authorization: `Bearer ${config.token}`,
167
+ },
168
+ });
169
+ const json = await response.json();
170
+ res.writeHead(200);
171
+ res.end(JSON.stringify(json.data));
172
+ }
173
+ catch {
174
+ res.writeHead(200);
175
+ res.end(JSON.stringify([]));
176
+ }
177
+ break;
178
+ }
179
+ case req.url?.startsWith('/asset'): {
180
+ const url = req.url || '';
181
+ const parsed = parseAssetUrl(url);
182
+ if (parsed) {
183
+ const { mediaLibraryId, docId } = parsed;
184
+ res.setHeader('Content-Type', 'application/json');
185
+ try {
186
+ let json = {};
187
+ const url = buildAssetUrl(mediaLibraryId, docId, config.apiUrl);
188
+ if (docId) {
189
+ const response = await fetch(url, {
190
+ headers: {
191
+ Authorization: `Bearer ${config.token}`,
192
+ },
193
+ });
194
+ const queryResponse = await response.json();
195
+ if (queryResponse?.documents[0]) {
196
+ json = queryResponse?.documents[0];
197
+ }
198
+ }
199
+ res.writeHead(200);
200
+ res.end(JSON.stringify(json));
201
+ }
202
+ catch {
203
+ res.writeHead(200);
204
+ res.end(JSON.stringify([]));
205
+ }
206
+ }
207
+ break;
208
+ }
140
209
  default: {
141
210
  const requestPath = req.url?.endsWith('/') ? `${req.url}index.html` : req.url;
142
211
  const filePath = new URL(`./static${requestPath}`, import.meta.url).pathname;
@@ -178,6 +247,18 @@ function parseDocumentUrl(url) {
178
247
  }
179
248
  return null;
180
249
  }
250
+ // Helper function to test URL parsing and document fetching logic
251
+ function parseAssetUrl(url) {
252
+ const matches = url.match(/[?&]organization=([^&]+).*?[&]medialibrary=([^&]+).*?[&]doc=([^&]+)/) || [];
253
+ if (matches && matches.length === 4) {
254
+ const [, organizationId, mediaLibraryId, docId] = matches;
255
+ // Ensure all parameters are present and non-empty
256
+ if (organizationId && mediaLibraryId && docId) {
257
+ return { organizationId, mediaLibraryId, docId };
258
+ }
259
+ }
260
+ return null;
261
+ }
181
262
  // Helper function to build expected API URL
182
263
  function buildApiUrl(projectId, dataset, docId, apiUrl) {
183
264
  const encodedQuery = encodeURIComponent(`*[_id == "${docId}"]`);
@@ -186,6 +267,10 @@ function buildApiUrl(projectId, dataset, docId, apiUrl) {
186
267
  : `https://${projectId}.api.sanity.work/`;
187
268
  return `${baseUrl}v1/data/query/${dataset}?query=${encodedQuery}`;
188
269
  }
270
+ // Helper function to build expected API URL
271
+ function buildAssetUrl(mediaLibraryId, docId, apiUrl) {
272
+ return `${apiUrl}/v2025-03-24/media-libraries/${mediaLibraryId}/doc/${docId}`;
273
+ }
189
274
  function parseInvokeRequest(body) {
190
275
  let json;
191
276
  try {
@@ -265,7 +350,18 @@ function parseInvokeRequest(body) {
265
350
  };
266
351
  return {
267
352
  func,
268
- data: { context: { ...context, clientOptions }, event },
353
+ data: {
354
+ context: {
355
+ ...context,
356
+ clientOptions,
357
+ // Provide default values for required properties if not present
358
+ eventResourceType: typeof context.eventResourceType === 'string' ? context.eventResourceType : '',
359
+ eventResourceId: typeof context.eventResourceId === 'string' ? context.eventResourceId : '',
360
+ functionResourceType: typeof context.functionResourceType === 'string' ? context.functionResourceType : '',
361
+ functionResourceId: typeof context.functionResourceId === 'string' ? context.functionResourceId : '',
362
+ },
363
+ event,
364
+ },
269
365
  metadata: { event: metadataEvent, before, after },
270
366
  };
271
367
  }