@sanity/runtime-cli 14.2.0 → 14.4.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 (38) hide show
  1. package/README.md +27 -27
  2. package/dist/actions/blueprints/assets.js +9 -0
  3. package/dist/actions/blueprints/blueprint.js +2 -2
  4. package/dist/actions/blueprints/resources.js +2 -1
  5. package/dist/actions/functions/logs.d.ts +0 -1
  6. package/dist/actions/functions/logs.js +0 -1
  7. package/dist/commands/blueprints/add.js +3 -10
  8. package/dist/commands/functions/add.js +2 -9
  9. package/dist/constants.d.ts +21 -1
  10. package/dist/constants.js +30 -1
  11. package/dist/cores/blueprints/config.js +1 -1
  12. package/dist/cores/blueprints/deploy.js +2 -2
  13. package/dist/cores/blueprints/destroy.js +1 -1
  14. package/dist/cores/blueprints/doctor.js +1 -1
  15. package/dist/cores/blueprints/init.js +1 -1
  16. package/dist/cores/blueprints/plan.js +6 -6
  17. package/dist/cores/functions/add.js +32 -28
  18. package/dist/cores/functions/logs.js +3 -5
  19. package/dist/cores/functions/test.js +1 -1
  20. package/dist/server/app.js +6 -4
  21. package/dist/server/static/components/api-base.js +1 -1
  22. package/dist/server/static/components/filters.js +1 -1
  23. package/dist/server/static/components/function-list.js +2 -2
  24. package/dist/server/static/components/payload-panel.js +1 -1
  25. package/dist/server/static/components/run-panel.js +2 -2
  26. package/dist/utils/display/blueprints-formatting.js +11 -4
  27. package/dist/utils/find-function.js +4 -3
  28. package/dist/utils/functions/getFolderSize.d.ts +5 -0
  29. package/dist/utils/functions/getFolderSize.js +24 -0
  30. package/dist/utils/functions/prepare-asset.js +6 -4
  31. package/dist/utils/transpile/transpile-function.d.ts +1 -0
  32. package/dist/utils/transpile/transpile-function.js +45 -6
  33. package/dist/utils/types.d.ts +6 -5
  34. package/dist/utils/types.js +7 -4
  35. package/dist/utils/validate/index.js +7 -4
  36. package/dist/utils/validate/resource.js +3 -2
  37. package/oclif.manifest.json +9 -7
  38. package/package.json +3 -4
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/14.2.0 linux-x64 node-v24.13.1
23
+ @sanity/runtime-cli/14.4.0 linux-x64 node-v24.14.0
24
24
  $ sanity-run --help [COMMAND]
25
25
  USAGE
26
26
  $ sanity-run COMMAND
@@ -55,9 +55,9 @@ Add a function 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|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 | ]
58
+ document-publish|document-create|document-update|document-delete|media-library-asset-create|media-library-asset-upda
59
+ te|media-library-asset-delete|scheduled-function... | --language ts|js | --javascript | --fn-helpers |
60
+ --fn-installer skip|npm|pnpm|yarn] [-i | ]
61
61
 
62
62
  ARGUMENTS
63
63
  TYPE (function) Type of resource to add (only "function" is supported)
@@ -72,8 +72,8 @@ FLAGS
72
72
  <options: skip|npm|pnpm|yarn>
73
73
  --fn-type=<option>... Document change event(s) that should trigger the function; you can specify multiple
74
74
  events by specifying this flag multiple times
75
- <options: document-create|document-delete|document-update|document-publish|media-library-
76
- asset-create|media-library-asset-update|media-library-asset-delete>
75
+ <options: document-publish|document-create|document-update|document-delete|media-library-
76
+ asset-create|media-library-asset-update|media-library-asset-delete|scheduled-function>
77
77
  --javascript Use JavaScript instead of TypeScript
78
78
  --language=<option> [default: ts] Language of the new function
79
79
  <options: ts|js>
@@ -98,7 +98,7 @@ EXAMPLES
98
98
  $ sanity-run blueprints add function --name my-function --fn-type document-create --fn-type document-update --lang js
99
99
  ```
100
100
 
101
- _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v14.2.0/src/commands/blueprints/add.ts)_
101
+ _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v14.4.0/src/commands/blueprints/add.ts)_
102
102
 
103
103
  ## `sanity-run blueprints config`
104
104
 
@@ -133,7 +133,7 @@ EXAMPLES
133
133
  $ sanity-run blueprints config --edit --project-id <projectId> --stack <name-or-id>
134
134
  ```
135
135
 
136
- _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v14.2.0/src/commands/blueprints/config.ts)_
136
+ _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v14.4.0/src/commands/blueprints/config.ts)_
137
137
 
138
138
  ## `sanity-run blueprints deploy`
139
139
 
@@ -166,7 +166,7 @@ EXAMPLES
166
166
  $ sanity-run blueprints deploy --no-wait
167
167
  ```
168
168
 
169
- _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v14.2.0/src/commands/blueprints/deploy.ts)_
169
+ _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v14.4.0/src/commands/blueprints/deploy.ts)_
170
170
 
171
171
  ## `sanity-run blueprints destroy`
172
172
 
@@ -198,7 +198,7 @@ EXAMPLES
198
198
  $ sanity-run blueprints destroy --stack <name-or-id> --project-id <projectId> --force --no-wait
199
199
  ```
200
200
 
201
- _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v14.2.0/src/commands/blueprints/destroy.ts)_
201
+ _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v14.4.0/src/commands/blueprints/destroy.ts)_
202
202
 
203
203
  ## `sanity-run blueprints doctor`
204
204
 
@@ -224,7 +224,7 @@ DESCRIPTION
224
224
  issues.
225
225
  ```
226
226
 
227
- _See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v14.2.0/src/commands/blueprints/doctor.ts)_
227
+ _See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v14.4.0/src/commands/blueprints/doctor.ts)_
228
228
 
229
229
  ## `sanity-run blueprints info`
230
230
 
@@ -254,7 +254,7 @@ EXAMPLES
254
254
  $ sanity-run blueprints info --stack <name-or-id>
255
255
  ```
256
256
 
257
- _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v14.2.0/src/commands/blueprints/info.ts)_
257
+ _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v14.4.0/src/commands/blueprints/info.ts)_
258
258
 
259
259
  ## `sanity-run blueprints init [DIR]`
260
260
 
@@ -304,7 +304,7 @@ EXAMPLES
304
304
  $ sanity-run blueprints init --blueprint-type <json|js|ts> --stack-name <stackName>
305
305
  ```
306
306
 
307
- _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v14.2.0/src/commands/blueprints/init.ts)_
307
+ _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v14.4.0/src/commands/blueprints/init.ts)_
308
308
 
309
309
  ## `sanity-run blueprints logs`
310
310
 
@@ -333,7 +333,7 @@ EXAMPLES
333
333
  $ sanity-run blueprints logs --watch
334
334
  ```
335
335
 
336
- _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v14.2.0/src/commands/blueprints/logs.ts)_
336
+ _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v14.4.0/src/commands/blueprints/logs.ts)_
337
337
 
338
338
  ## `sanity-run blueprints plan`
339
339
 
@@ -359,7 +359,7 @@ EXAMPLES
359
359
  $ sanity-run blueprints plan
360
360
  ```
361
361
 
362
- _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v14.2.0/src/commands/blueprints/plan.ts)_
362
+ _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v14.4.0/src/commands/blueprints/plan.ts)_
363
363
 
364
364
  ## `sanity-run blueprints stacks`
365
365
 
@@ -388,7 +388,7 @@ EXAMPLES
388
388
  $ sanity-run blueprints stacks --organization-id <organizationId>
389
389
  ```
390
390
 
391
- _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v14.2.0/src/commands/blueprints/stacks.ts)_
391
+ _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v14.4.0/src/commands/blueprints/stacks.ts)_
392
392
 
393
393
  ## `sanity-run functions add`
394
394
 
@@ -397,8 +397,8 @@ Add a Function to your Blueprint
397
397
  ```
398
398
  USAGE
399
399
  $ sanity-run functions add [--example <value> | -n <value> | | --language ts|js | --javascript | | ] [--type
400
- document-create|document-delete|document-update|document-publish|media-library-asset-create|media-library-asset-upda
401
- te|media-library-asset-delete... ] [--helpers] [--installer skip|npm|pnpm|yarn] [-i | ]
400
+ document-publish|document-create|document-update|document-delete|media-library-asset-create|media-library-asset-upda
401
+ te|media-library-asset-delete|scheduled-function... ] [--helpers] [--installer skip|npm|pnpm|yarn] [-i | ]
402
402
 
403
403
  FLAGS
404
404
  -i, --install Shortcut for --fn-installer npm
@@ -412,8 +412,8 @@ FLAGS
412
412
  <options: ts|js>
413
413
  --type=<option>... Document change event(s) that should trigger the function; you can specify multiple events
414
414
  by specifying this flag multiple times
415
- <options: document-create|document-delete|document-update|document-publish|media-library-ass
416
- et-create|media-library-asset-update|media-library-asset-delete>
415
+ <options: document-publish|document-create|document-update|document-delete|media-library-ass
416
+ et-create|media-library-asset-update|media-library-asset-delete|scheduled-function>
417
417
 
418
418
  DESCRIPTION
419
419
  Add a Function to your Blueprint
@@ -437,7 +437,7 @@ EXAMPLES
437
437
  $ sanity-run functions add --name my-function --type document-create --type document-update --lang js
438
438
  ```
439
439
 
440
- _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v14.2.0/src/commands/functions/add.ts)_
440
+ _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v14.4.0/src/commands/functions/add.ts)_
441
441
 
442
442
  ## `sanity-run functions dev`
443
443
 
@@ -471,7 +471,7 @@ EXAMPLES
471
471
  $ sanity-run functions dev --timeout 60
472
472
  ```
473
473
 
474
- _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v14.2.0/src/commands/functions/dev.ts)_
474
+ _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v14.4.0/src/commands/functions/dev.ts)_
475
475
 
476
476
  ## `sanity-run functions env add NAME KEY VALUE`
477
477
 
@@ -498,7 +498,7 @@ EXAMPLES
498
498
  $ sanity-run functions env add MyFunction API_URL https://api.example.com/
499
499
  ```
500
500
 
501
- _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v14.2.0/src/commands/functions/env/add.ts)_
501
+ _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v14.4.0/src/commands/functions/env/add.ts)_
502
502
 
503
503
  ## `sanity-run functions env list NAME`
504
504
 
@@ -522,7 +522,7 @@ EXAMPLES
522
522
  $ sanity-run functions env list MyFunction
523
523
  ```
524
524
 
525
- _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v14.2.0/src/commands/functions/env/list.ts)_
525
+ _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v14.4.0/src/commands/functions/env/list.ts)_
526
526
 
527
527
  ## `sanity-run functions env remove NAME KEY`
528
528
 
@@ -548,7 +548,7 @@ EXAMPLES
548
548
  $ sanity-run functions env remove MyFunction API_URL
549
549
  ```
550
550
 
551
- _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v14.2.0/src/commands/functions/env/remove.ts)_
551
+ _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v14.4.0/src/commands/functions/env/remove.ts)_
552
552
 
553
553
  ## `sanity-run functions logs [NAME]`
554
554
 
@@ -588,7 +588,7 @@ EXAMPLES
588
588
  $ sanity-run functions logs <name> --delete
589
589
  ```
590
590
 
591
- _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v14.2.0/src/commands/functions/logs.ts)_
591
+ _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v14.4.0/src/commands/functions/logs.ts)_
592
592
 
593
593
  ## `sanity-run functions test [NAME]`
594
594
 
@@ -642,7 +642,7 @@ EXAMPLES
642
642
  $ sanity-run functions test <name> --event update --data-before '{ "title": "before" }' --data-after '{ "title": "after" }'
643
643
  ```
644
644
 
645
- _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v14.2.0/src/commands/functions/test.ts)_
645
+ _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v14.4.0/src/commands/functions/test.ts)_
646
646
 
647
647
  ## `sanity-run help [COMMAND]`
648
648
 
@@ -4,6 +4,7 @@ import os from 'node:os';
4
4
  import path from 'node:path';
5
5
  import AdmZip from 'adm-zip';
6
6
  import config from '../../config.js';
7
+ import { CONVERT_BYTES_TO_MB, MAX_ASSET_SIZE } from '../../constants.js';
7
8
  import { detectNativeModules } from '../../utils/functions/detect-native-modules.js';
8
9
  import { prepareAsset } from '../../utils/functions/prepare-asset.js';
9
10
  import getHeaders from '../../utils/get-headers.js';
@@ -183,6 +184,14 @@ export async function pathToZip(path) {
183
184
  }
184
185
  export async function pathToB64ZipHash(path) {
185
186
  const zip = await pathToZip(path);
187
+ // Add up the uncompressed sizes of all entries to get a more accurate size of the asset being uploaded
188
+ const size = zip
189
+ .getEntries()
190
+ .map((entry) => entry.header.size)
191
+ .reduce((a, b) => a + b, 0);
192
+ if (size > MAX_ASSET_SIZE) {
193
+ throw new Error(`Asset size of ${(size / CONVERT_BYTES_TO_MB).toFixed(2)} MB exceeds the maximum allowed size of 200 MB.`);
194
+ }
186
195
  const hasNativeModules = detectNativeModules(zip);
187
196
  if (hasNativeModules?.length > 0) {
188
197
  const errorMsg = `Native modules detected in Function:\n${hasNativeModules.join('\n')}\n\n` +
@@ -5,7 +5,7 @@ import { pathToFileURL } from 'node:url';
5
5
  import blueprintParserValidator from '@sanity/blueprints-parser';
6
6
  import * as find from 'empathic/find';
7
7
  import { createJiti } from 'jiti';
8
- import { SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULE, } from '../../constants.js';
8
+ import { SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULED, } from '../../constants.js';
9
9
  import { isLocalFunctionResource, } from '../../utils/types.js';
10
10
  import { validateResources } from '../../utils/validate/index.js';
11
11
  import { validateFunctionResource } from '../../utils/validate/resource.js';
@@ -218,7 +218,7 @@ export async function readLocalBlueprint(logger, validate, blueprintPath) {
218
218
  invalidReferenceTypes: [
219
219
  SANITY_FUNCTION_DOCUMENT,
220
220
  SANITY_FUNCTION_MEDIA_LIBRARY_ASSET,
221
- SANITY_FUNCTION_SCHEDULE,
221
+ SANITY_FUNCTION_SCHEDULED,
222
222
  ],
223
223
  });
224
224
  const parsedBlueprint = parserResult.result === 'valid' ? parserResult.blueprint : undefined;
@@ -2,6 +2,7 @@ import { spawn } from 'node:child_process';
2
2
  import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { cwd } from 'node:process';
5
+ import { SANITY_FUNCTION_PREFIX } from '../../constants.js';
5
6
  import { styleText } from '../../utils/style-text.js';
6
7
  import { writeOrUpdateNodeDependency } from '../node.js';
7
8
  import { addResourceToBlueprint } from './blueprint.js';
@@ -63,7 +64,7 @@ export async function createFunctionResource(options, logger) {
63
64
  const resourceJson = {
64
65
  name,
65
66
  src: `functions/${name}`,
66
- type: `sanity.function.${typeName}`,
67
+ type: `${SANITY_FUNCTION_PREFIX}${typeName}`,
67
68
  event: {
68
69
  on: eventsOn,
69
70
  },
@@ -8,7 +8,6 @@ export declare function logs(id: string, options: LoggingOptions, auth: AuthPara
8
8
  ok: boolean;
9
9
  error: any;
10
10
  logs: any;
11
- total: any;
12
11
  }>;
13
12
  export declare function deleteLogs(id: string, auth: AuthParams, logger: ReturnType<typeof Logger>): Promise<{
14
13
  ok: boolean;
@@ -15,7 +15,6 @@ export async function logs(id, options, auth, logger) {
15
15
  ok: response.ok,
16
16
  error: response.ok ? null : json?.error?.message,
17
17
  logs: response.ok ? json.logs : [],
18
- total: response.ok ? json.total : 0,
19
18
  };
20
19
  }
21
20
  export async function deleteLogs(id, auth, logger) {
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { Args, Flags } from '@oclif/core';
7
7
  import { LocalBlueprintCommand } from '../../baseCommands.js';
8
+ import { FUNCTION_TYPES } from '../../constants.js';
8
9
  import { functionAddCore } from '../../cores/functions/index.js';
9
10
  import { Logger } from '../../utils/logger.js';
10
11
  // import {warn} from '../../utils/display/presenters.js'
@@ -43,15 +44,7 @@ After adding a function, use 'functions dev' to test locally, then 'blueprints d
43
44
  }),
44
45
  'fn-type': Flags.string({
45
46
  description: 'Document change event(s) that should trigger the function; you can specify multiple events by specifying this flag multiple times',
46
- options: [
47
- 'document-create',
48
- 'document-delete',
49
- 'document-update',
50
- 'document-publish',
51
- 'media-library-asset-create',
52
- 'media-library-asset-update',
53
- 'media-library-asset-delete',
54
- ],
47
+ options: FUNCTION_TYPES,
55
48
  aliases: ['function-type'],
56
49
  multiple: true,
57
50
  multipleNonGreedy: true,
@@ -91,7 +84,7 @@ After adding a function, use 'functions dev' to test locally, then 'blueprints d
91
84
  }
92
85
  // log(
93
86
  // warn(
94
- // `\`${bin} blueprints add function\` is deprecated. Use \`${bin} functions add\` instead.`,
87
+ // `\`npx ${bin} blueprints add function\` is deprecated. Use \`npx ${bin} functions add\` instead.`,
95
88
  // ),
96
89
  // )
97
90
  const { success, error } = await functionAddCore({
@@ -1,5 +1,6 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import { LocalBlueprintCommand } from '../../baseCommands.js';
3
+ import { FUNCTION_TYPES } from '../../constants.js';
3
4
  import { functionAddCore } from '../../cores/functions/index.js';
4
5
  import { Logger } from '../../utils/logger.js';
5
6
  export default class AddCommand extends LocalBlueprintCommand {
@@ -28,15 +29,7 @@ After adding, use 'functions dev' to test locally, then 'blueprints deploy' to p
28
29
  }),
29
30
  type: Flags.string({
30
31
  description: 'Document change event(s) that should trigger the function; you can specify multiple events by specifying this flag multiple times',
31
- options: [
32
- 'document-create',
33
- 'document-delete',
34
- 'document-update',
35
- 'document-publish',
36
- 'media-library-asset-create',
37
- 'media-library-asset-update',
38
- 'media-library-asset-delete',
39
- ],
32
+ options: FUNCTION_TYPES,
40
33
  multiple: true,
41
34
  multipleNonGreedy: true,
42
35
  dependsOn: ['name'],
@@ -1,8 +1,28 @@
1
1
  export declare const SANITY_ACCESS_ROLE = "sanity.access.role";
2
2
  export declare const SANITY_ACCESS_ROBOT = "sanity.access.robot";
3
+ export declare const SANITY_FUNCTION_PREFIX = "sanity.function.";
3
4
  export declare const SANITY_FUNCTION_DOCUMENT = "sanity.function.document";
4
5
  export declare const SANITY_FUNCTION_MEDIA_LIBRARY_ASSET = "sanity.function.media-library.asset";
5
- export declare const SANITY_FUNCTION_SCHEDULE = "sanity.function.cron";
6
+ export declare const SANITY_FUNCTION_SCHEDULED = "sanity.function.cron";
7
+ export declare const SANITY_PROJECT = "sanity.project";
6
8
  export declare const SANITY_PROJECT_CORS = "sanity.project.cors";
7
9
  export declare const SANITY_PROJECT_DATASET = "sanity.project.dataset";
8
10
  export declare const SANITY_PROJECT_WEBHOOK = "sanity.project.webhook";
11
+ export declare const EVENT_DOCUMENT_PUBLISH = "document-publish";
12
+ export declare const EVENT_DOCUMENT_CREATE = "document-create";
13
+ export declare const EVENT_DOCUMENT_UPDATE = "document-update";
14
+ export declare const EVENT_DOCUMENT_DELETE = "document-delete";
15
+ export declare const EVENT_MEDIA_LIBRARY_ASSET_CREATE = "media-library-asset-create";
16
+ export declare const EVENT_MEDIA_LIBRARY_ASSET_UPDATE = "media-library-asset-update";
17
+ export declare const EVENT_MEDIA_LIBRARY_ASSET_DELETE = "media-library-asset-delete";
18
+ export declare const EVENT_SCHEDULED = "scheduled-function";
19
+ export declare const FUNCTION_TYPES: string[];
20
+ export declare const MAX_ASSET_SIZE = 209715200;
21
+ export declare const CONVERT_BYTES_TO_MB = 1048576;
22
+ export declare const LABEL_DOCUMENT_CREATE = "Document Create";
23
+ export declare const LABEL_DOCUMENT_UPDATE = "Document Update";
24
+ export declare const LABEL_DOCUMENT_DELETE = "Document Delete";
25
+ export declare const LABEL_MEDIA_LIBRARY_ASSET_CREATE = "Media Library Asset Create";
26
+ export declare const LABEL_MEDIA_LIBRARY_ASSET_UPDATE = "Media Library Asset Update";
27
+ export declare const LABEL_MEDIA_LIBRARY_ASSET_DELETE = "Media Library Asset Delete";
28
+ export declare const LABEL_SCHEDULED = "Scheduled";
package/dist/constants.js CHANGED
@@ -1,8 +1,37 @@
1
1
  export const SANITY_ACCESS_ROLE = 'sanity.access.role';
2
2
  export const SANITY_ACCESS_ROBOT = 'sanity.access.robot';
3
+ export const SANITY_FUNCTION_PREFIX = 'sanity.function.';
3
4
  export const SANITY_FUNCTION_DOCUMENT = 'sanity.function.document';
4
5
  export const SANITY_FUNCTION_MEDIA_LIBRARY_ASSET = 'sanity.function.media-library.asset';
5
- export const SANITY_FUNCTION_SCHEDULE = 'sanity.function.cron';
6
+ export const SANITY_FUNCTION_SCHEDULED = 'sanity.function.cron';
7
+ export const SANITY_PROJECT = 'sanity.project';
6
8
  export const SANITY_PROJECT_CORS = 'sanity.project.cors';
7
9
  export const SANITY_PROJECT_DATASET = 'sanity.project.dataset';
8
10
  export const SANITY_PROJECT_WEBHOOK = 'sanity.project.webhook';
11
+ export const EVENT_DOCUMENT_PUBLISH = 'document-publish';
12
+ export const EVENT_DOCUMENT_CREATE = 'document-create';
13
+ export const EVENT_DOCUMENT_UPDATE = 'document-update';
14
+ export const EVENT_DOCUMENT_DELETE = 'document-delete';
15
+ export const EVENT_MEDIA_LIBRARY_ASSET_CREATE = 'media-library-asset-create';
16
+ export const EVENT_MEDIA_LIBRARY_ASSET_UPDATE = 'media-library-asset-update';
17
+ export const EVENT_MEDIA_LIBRARY_ASSET_DELETE = 'media-library-asset-delete';
18
+ export const EVENT_SCHEDULED = 'scheduled-function';
19
+ export const FUNCTION_TYPES = [
20
+ EVENT_DOCUMENT_PUBLISH,
21
+ EVENT_DOCUMENT_CREATE,
22
+ EVENT_DOCUMENT_UPDATE,
23
+ EVENT_DOCUMENT_DELETE,
24
+ EVENT_MEDIA_LIBRARY_ASSET_CREATE,
25
+ EVENT_MEDIA_LIBRARY_ASSET_UPDATE,
26
+ EVENT_MEDIA_LIBRARY_ASSET_DELETE,
27
+ EVENT_SCHEDULED,
28
+ ];
29
+ export const MAX_ASSET_SIZE = 209_715_200; // 200 MB in bytes
30
+ export const CONVERT_BYTES_TO_MB = 1_048_576; // Used to convert bytes to megabytes (1024 * 1024)
31
+ export const LABEL_DOCUMENT_CREATE = 'Document Create';
32
+ export const LABEL_DOCUMENT_UPDATE = 'Document Update';
33
+ export const LABEL_DOCUMENT_DELETE = 'Document Delete';
34
+ export const LABEL_MEDIA_LIBRARY_ASSET_CREATE = 'Media Library Asset Create';
35
+ export const LABEL_MEDIA_LIBRARY_ASSET_UPDATE = 'Media Library Asset Update';
36
+ export const LABEL_MEDIA_LIBRARY_ASSET_DELETE = 'Media Library Asset Delete';
37
+ export const LABEL_SCHEDULED = 'Scheduled';
@@ -13,7 +13,7 @@ export async function blueprintConfigCore(options) {
13
13
  log(warn('Incomplete configuration.'));
14
14
  if (!editConfig) {
15
15
  // blueprint.json exists but no config JSON
16
- log(`Run \`${bin} blueprints doctor\` for diagnostics.`);
16
+ log(`Run \`npx ${bin} blueprints doctor\` for diagnostics.`);
17
17
  return { success: true }; // not necessarily fatal
18
18
  }
19
19
  }
@@ -123,7 +123,7 @@ export async function blueprintDeployCore(options) {
123
123
  spinner.stop().clear();
124
124
  if (noWait) {
125
125
  log(styleText(['bold', 'green'], 'Stack deployment started!'));
126
- log(`Use \`${bin} blueprints info\` to check status`);
126
+ log(`Use \`npx ${bin} blueprints info\` to check status`);
127
127
  return { success: true, data: { resources } };
128
128
  }
129
129
  log(styleText('dim', 'Stack deployment progress:'));
@@ -167,7 +167,7 @@ export async function blueprintDeployCore(options) {
167
167
  }
168
168
  if (!idleMessageShown && Date.now() - lastLogAt > 60_000) {
169
169
  log(`No new activity for 60 seconds. The deployment is still running on Sanity servers.`);
170
- log(`You can safely exit and check status later with \`${bin} blueprints info\`.`);
170
+ log(`You can safely exit and check status later with \`npx ${bin} blueprints info\`.`);
171
171
  idleMessageShown = true;
172
172
  }
173
173
  await sleep(1500);
@@ -126,7 +126,7 @@ export async function blueprintDestroyCore(options) {
126
126
  }
127
127
  if (!idleMessageShown && Date.now() - lastLogAt > 60_000) {
128
128
  log(`No new activity for 60 seconds. The destruction is still running on Sanity servers.`);
129
- log(`You can safely exit and check status later with \`${bin} blueprints info\`.`);
129
+ log(`You can safely exit and check status later with \`npx ${bin} blueprints info\`.`);
130
130
  idleMessageShown = true;
131
131
  }
132
132
  await sleep(1500);
@@ -301,6 +301,6 @@ export async function blueprintDoctorCore(options) {
301
301
  flags: { edit: true, verbose: v },
302
302
  });
303
303
  }
304
- log(styleText('dim', ` Run \`${bin} blueprints doctor --fix\` to resolve configuration issues.`));
304
+ log(styleText('dim', ` Run \`npx ${bin} blueprints doctor --fix\` to resolve configuration issues.`));
305
305
  return { success: false, error: errorMessage, data: { diagnostics: flatDiagnostics } };
306
306
  }
@@ -354,7 +354,7 @@ export async function createBlueprintFiles(params) {
354
354
  nextStepParts.push(`cd ${userProvidedDirName}`);
355
355
  if (blueprintExtension !== 'json')
356
356
  nextStepParts.push('npm install');
357
- nextStepParts.push(`${bin} blueprints --help`);
357
+ nextStepParts.push(`npx ${bin} blueprints --help`);
358
358
  log(`\n Run "${styleText(['bold', 'magenta'], nextStepParts.join(' && '))}" to get started`);
359
359
  return { success: true };
360
360
  }
@@ -10,8 +10,8 @@ export async function blueprintPlanCore(options) {
10
10
  if (!token || !scopeType || !scopeId) {
11
11
  log(styleText('dim', 'Unable to retrieve live Stack deployment for comparison'));
12
12
  const errorMessage = !token
13
- ? `Missing authentication token. Run \`${bin} login\` to authenticate.`
14
- : `Missing Blueprint configuration. Run \`${bin} blueprints doctor --fix\` to repair.`;
13
+ ? `Missing authentication token. Run \`npx ${bin} login\` to authenticate.`
14
+ : `Missing Blueprint configuration. Run \`npx ${bin} blueprints doctor --fix\` to repair.`;
15
15
  return {
16
16
  success: false,
17
17
  error: errorMessage,
@@ -24,7 +24,7 @@ export async function blueprintPlanCore(options) {
24
24
  log(styleText('dim', 'Unable to retrieve live Stack deployment for comparison'));
25
25
  return {
26
26
  success: false,
27
- error: `Missing Stack deployment configuration. Run \`${bin} blueprints doctor --fix\` to repair.`,
27
+ error: `Missing Stack deployment configuration. Run \`npx ${bin} blueprints doctor --fix\` to repair.`,
28
28
  };
29
29
  }
30
30
  const auth = { token, scopeType, scopeId };
@@ -48,7 +48,7 @@ export async function blueprintPlanCore(options) {
48
48
  log(` ${styleText(['bold', 'red'], '✘')} ${msg}`);
49
49
  }
50
50
  }
51
- log(`\n Fix the issues above before running "${styleText(['bold', 'magenta'], `${bin} blueprints deploy`)}"`);
51
+ log(`\n Fix the issues above before running "${styleText(['bold', 'magenta'], `npx ${bin} blueprints deploy`)}"`);
52
52
  }
53
53
  else {
54
54
  log(styleText('dim', '\nUnable to retrieve deployment plan from server'));
@@ -58,10 +58,10 @@ export async function blueprintPlanCore(options) {
58
58
  log('');
59
59
  log(formatDeploymentPlan(planResponse.deploymentPlan));
60
60
  if (hasActionableChanges(planResponse.deploymentPlan)) {
61
- log(`\n Run "${styleText(['bold', 'magenta'], `${bin} blueprints deploy`)}" to apply these Stack changes`);
61
+ log(`\n Run "${styleText(['bold', 'magenta'], `npx ${bin} blueprints deploy`)}" to apply these Stack changes`);
62
62
  }
63
63
  else {
64
- log(`\n ${styleText('dim', `No significant changes to deploy. Run \`${bin} blueprints deploy\` to apply.`)}`);
64
+ log(`\n ${styleText('dim', `No significant changes to deploy. Run \`npx ${bin} blueprints deploy\` to apply.`)}`);
65
65
  }
66
66
  return { success: true };
67
67
  }
@@ -5,21 +5,30 @@ import { checkbox, confirm, input, select } from '@inquirer/prompts';
5
5
  import { highlight } from 'cardinal';
6
6
  import { createFunctionResource } from '../../actions/blueprints/resources.js';
7
7
  import { verifyExampleExists, writeExample } from '../../actions/sanity/examples.js';
8
- import { SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULE } from '../../constants.js';
8
+ import { EVENT_DOCUMENT_CREATE, EVENT_DOCUMENT_DELETE, EVENT_DOCUMENT_UPDATE, EVENT_MEDIA_LIBRARY_ASSET_CREATE, EVENT_MEDIA_LIBRARY_ASSET_DELETE, EVENT_MEDIA_LIBRARY_ASSET_UPDATE, EVENT_SCHEDULED, FUNCTION_TYPES, LABEL_DOCUMENT_CREATE, LABEL_DOCUMENT_DELETE, LABEL_DOCUMENT_UPDATE, LABEL_MEDIA_LIBRARY_ASSET_CREATE, LABEL_MEDIA_LIBRARY_ASSET_DELETE, LABEL_MEDIA_LIBRARY_ASSET_UPDATE, LABEL_SCHEDULED, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULED, } from '../../constants.js';
9
9
  import { check, indent, warn } from '../../utils/display/presenters.js';
10
10
  import { styleText } from '../../utils/style-text.js';
11
11
  import { validateFunctionName } from '../../utils/validate/resource.js';
12
12
  const generateFunctionBlueprintResourceTemplate = (fnName, eventNames) => {
13
- const definer = eventNames[0].substring(0, eventNames[0].lastIndexOf('-')) === 'document'
14
- ? 'Document'
15
- : 'MediaLibraryAsset';
16
- const isML = definer.startsWith('Media');
13
+ const functionType = eventNames[0].substring(0, eventNames[0].lastIndexOf('-'));
14
+ let definer = '';
17
15
  const eventOns = eventNames.map((e) => `'${e.substring(e.lastIndexOf('-') + 1)}'`);
16
+ switch (functionType) {
17
+ case 'document':
18
+ definer = `defineDocumentFunction({name: '${fnName}', event: {on: [${eventOns.join(', ')}]}}), // ← add this line`;
19
+ break;
20
+ case 'media':
21
+ definer = `defineMediaLibraryAssetFunction({name: '${fnName}', event: {on: [${eventOns.join(', ')}], resource: {type: 'media-library', id: 'my-media-library-id'}}}), // ← add this line`;
22
+ break;
23
+ case 'scheduled':
24
+ definer = `defineScheduleFunction({name: '${fnName}', event: {expression: '0 0 * * *'}}), // ← add this line`;
25
+ break;
26
+ }
18
27
  return `
19
28
  export default defineBlueprint({
20
29
  resources: [
21
30
  // ...
22
- define${definer}Function({name: '${fnName}', event: {on: [${eventOns.join(', ')}]${isML ? ", resource: {type: 'media-library', id: 'my-media-library-id'}" : ''}}}), // ← add this line
31
+ ${definer}
23
32
  ],
24
33
  })
25
34
  `;
@@ -77,8 +86,8 @@ export async function functionAddCore(options) {
77
86
  case SANITY_FUNCTION_MEDIA_LIBRARY_ASSET:
78
87
  type = 'MediaLibraryAsset';
79
88
  break;
80
- case SANITY_FUNCTION_SCHEDULE:
81
- type = 'Schedule';
89
+ case SANITY_FUNCTION_SCHEDULED:
90
+ type = LABEL_SCHEDULED;
82
91
  break;
83
92
  default:
84
93
  }
@@ -138,20 +147,12 @@ export async function functionAddCore(options) {
138
147
  if (fnTypes.length === 0) {
139
148
  throw new Error('At least one function type must be provided.');
140
149
  }
141
- if (!fnTypes.every((evt) => [
142
- 'document-publish',
143
- 'document-create',
144
- 'document-delete',
145
- 'document-update',
146
- 'media-library-asset-create',
147
- 'media-library-asset-update',
148
- 'media-library-asset-delete',
149
- ].includes(evt))) {
150
- 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');
150
+ if (!fnTypes.every((evt) => FUNCTION_TYPES.includes(evt))) {
151
+ throw new Error(`Invalid function type. Must be one of: ${FUNCTION_TYPES.join(', ').trim()}`);
151
152
  }
152
153
  const eventSources = new Set(fnTypes.map((t) => t.substring(0, t.lastIndexOf('-'))));
153
154
  if (eventSources.size > 1) {
154
- throw new Error('Invalid function type. Cannot mix document-* and media-library-asset-* types.');
155
+ throw new Error('Invalid function type. Cannot mix document-*, media-library-asset-*, and/or scheduled-* types.');
155
156
  }
156
157
  let addHelpers;
157
158
  let installCommand;
@@ -229,20 +230,23 @@ async function promptForFunctionType() {
229
230
  const functionTypes = await checkbox({
230
231
  message: 'Choose events to trigger your function:',
231
232
  choices: [
232
- { name: 'Document Create', value: 'document-create', checked: true },
233
- { name: 'Document Update', value: 'document-update', checked: true },
234
- { name: 'Document Delete', value: 'document-delete' },
235
- { name: 'Media Library Asset Create', value: 'media-library-asset-create' },
236
- { name: 'Media Library Asset Update', value: 'media-library-asset-update' },
237
- { name: 'Media Library Asset Delete', value: 'media-library-asset-delete' },
233
+ { name: LABEL_DOCUMENT_CREATE, value: EVENT_DOCUMENT_CREATE, checked: true },
234
+ { name: LABEL_DOCUMENT_UPDATE, value: EVENT_DOCUMENT_UPDATE, checked: true },
235
+ { name: LABEL_DOCUMENT_DELETE, value: EVENT_DOCUMENT_DELETE },
236
+ { name: LABEL_MEDIA_LIBRARY_ASSET_CREATE, value: EVENT_MEDIA_LIBRARY_ASSET_CREATE },
237
+ { name: LABEL_MEDIA_LIBRARY_ASSET_UPDATE, value: EVENT_MEDIA_LIBRARY_ASSET_UPDATE },
238
+ { name: LABEL_MEDIA_LIBRARY_ASSET_DELETE, value: EVENT_MEDIA_LIBRARY_ASSET_DELETE },
239
+ { name: LABEL_SCHEDULED, value: EVENT_SCHEDULED },
238
240
  ],
239
241
  validate(choices) {
240
242
  if (choices.length === 0) {
241
243
  return 'You must choose at least one function type / document change event';
242
244
  }
243
- if (choices.some((c) => String(c.value).startsWith('media-library')) &&
244
- choices.some((c) => String(c.value).startsWith('document'))) {
245
- return 'You cannot mix both Document and Media Library Asset events together in one Function';
245
+ const hasMedia = choices.some((c) => String(c.value).startsWith('media-library'));
246
+ const hasDocument = choices.some((c) => String(c.value).startsWith('document'));
247
+ const hasSchedule = choices.some((c) => String(c.value).startsWith('scheduled'));
248
+ if ((hasMedia && hasDocument) || (hasMedia && hasSchedule) || (hasDocument && hasSchedule)) {
249
+ return 'You cannot mix Document, Media Library Asset, and Scheduled events together in one Function';
246
250
  }
247
251
  return true;
248
252
  },
@@ -74,7 +74,7 @@ async function streamLogs({ name, externalId, auth, log, }) {
74
74
  }
75
75
  async function getLogs({ name, externalId, auth, limit, json, utc, log, }) {
76
76
  const spinner = log.ora(`Finding logs for function "${name}"`).start();
77
- const { ok, error, logs, total } = await getLogsAction(externalId, { limit }, auth, log);
77
+ const { ok, error, logs } = await getLogsAction(externalId, { limit }, auth, log);
78
78
  if (!ok) {
79
79
  spinner.fail(`${styleText('red', 'Failed')} to retrieve logs`);
80
80
  return { success: false, error: error || 'Unknown error' };
@@ -86,10 +86,8 @@ async function getLogs({ name, externalId, auth, limit, json, utc, log, }) {
86
86
  }
87
87
  spinner.succeed(`${formatTitle('Function', name)} Logs`);
88
88
  if (!json) {
89
- log(`Found ${styleText('bold', String(total))} log entries for function ${styleText('yellow', name)}`);
90
- if (logs.length < total) {
91
- log(`Here are the last ${styleText('bold', filteredLogs.length.toString())} entries`);
92
- }
89
+ const logLength = filteredLogs.length;
90
+ log(`Found ${styleText('bold', logLength.toString())} log ${logLength === 1 ? 'entry' : 'entries'} for function ${styleText('yellow', name)}`);
93
91
  log('\n');
94
92
  for (const { time, level, message } of filteredLogs) {
95
93
  log(formatLog(time, level, message, utc));
@@ -135,7 +135,7 @@ export async function functionTestCore(options) {
135
135
  after,
136
136
  }
137
137
  : {
138
- event: 'schedule',
138
+ event: 'scheduled',
139
139
  };
140
140
  const spinner = log.ora('Executing function...').start();
141
141
  const { json, logs, error } = await testAction(resource, invokeOptions, contextOptions, {
@@ -329,13 +329,13 @@ function parseInvokeRequest(body) {
329
329
  }
330
330
  const metadataEvent = metadata.event;
331
331
  if (typeof metadataEvent !== 'string' ||
332
- (!isEventType(metadataEvent) && metadataEvent !== 'schedule')) {
333
- throw new Error('Request body is not valid, `metadata.event` field is not one of `create`, `update`, `delete`, or `schedule`');
332
+ (!isEventType(metadataEvent) && metadataEvent !== 'scheduled')) {
333
+ throw new Error('Request body is not valid, `metadata.event` field is not one of `create`, `update`, `delete`, or `scheduled`');
334
334
  }
335
335
  let before = null;
336
336
  let after = null;
337
337
  // Only GROQ-based events (create, update, delete) have before and after fields
338
- if (metadataEvent !== 'schedule') {
338
+ if (metadataEvent !== 'scheduled') {
339
339
  if (!('before' in metadata)) {
340
340
  throw new Error('Request body is not valid, `metadata.before` field is missing');
341
341
  }
@@ -374,7 +374,9 @@ function parseInvokeRequest(body) {
374
374
  },
375
375
  event,
376
376
  },
377
- metadata: metadataEvent === 'schedule' ? { event: metadataEvent } : { event: metadataEvent, before, after },
377
+ metadata: metadataEvent === 'scheduled'
378
+ ? { event: metadataEvent }
379
+ : { event: metadataEvent, before, after },
378
380
  };
379
381
  }
380
382
  export { app, buildApiUrl, parseDocumentUrl };
@@ -9,6 +9,6 @@ export class ApiBaseElement extends HTMLElement {
9
9
  this.api = api
10
10
  this.SANITY_FUNCTION_DOCUMENT = 'sanity.function.document'
11
11
  this.SANITY_FUNCTION_MEDIA_LIBRARY_ASSET = 'sanity.function.media-library.asset'
12
- this.SANITY_FUNCTION_SCHEDULE = 'sanity.function.cron'
12
+ this.SANITY_FUNCTION_SCHEDULED = 'sanity.function.cron'
13
13
  }
14
14
  }
@@ -79,7 +79,7 @@ class FiltersComponent extends ApiBaseElement {
79
79
  const mediaFunction = this.api.store.selectedFunctionType?.startsWith(
80
80
  this.SANITY_FUNCTION_MEDIA_LIBRARY_ASSET,
81
81
  )
82
- const scheduleFunction = this.api.store.selectedFunctionType === this.SANITY_FUNCTION_SCHEDULE
82
+ const scheduleFunction = this.api.store.selectedFunctionType === this.SANITY_FUNCTION_SCHEDULED
83
83
 
84
84
  const container = this.shadowRoot.querySelector('fieldset')
85
85
  container.innerHTML = this.buildFilters(docFunction, mediaFunction, scheduleFunction)
@@ -58,8 +58,8 @@ class FunctionList extends ApiBaseElement {
58
58
  return 'Document'
59
59
  case this.SANITY_FUNCTION_MEDIA_LIBRARY_ASSET:
60
60
  return 'Media Library'
61
- case this.SANITY_FUNCTION_SCHEDULE:
62
- return 'Schedule'
61
+ case this.SANITY_FUNCTION_SCHEDULED:
62
+ return 'Scheduled'
63
63
  default:
64
64
  return type.split('.').pop().replaceAll('-', ' ')
65
65
  }
@@ -79,7 +79,7 @@ class PayloadPanel extends ApiBaseElement {
79
79
  }
80
80
 
81
81
  updateCodeMirror = ({selectedFunctionType}) => {
82
- if (selectedFunctionType === this.SANITY_FUNCTION_SCHEDULE) {
82
+ if (selectedFunctionType === this.SANITY_FUNCTION_SCHEDULED) {
83
83
  this.api.store.beforePayload.dispatch({
84
84
  effects: editableCompartment.reconfigure(EditorView.editable.of(false)),
85
85
  })
@@ -25,7 +25,7 @@ class RunPanel extends ApiBaseElement {
25
25
  const docFunction = this.api.store.selectedFunctionType === this.SANITY_FUNCTION_DOCUMENT
26
26
  const mediaFunction =
27
27
  this.api.store.selectedFunctionType === this.SANITY_FUNCTION_MEDIA_LIBRARY_ASSET
28
- const scheduleFunction = this.api.store.selectedFunctionType === this.SANITY_FUNCTION_SCHEDULE
28
+ const scheduleFunction = this.api.store.selectedFunctionType === this.SANITY_FUNCTION_SCHEDULED
29
29
  const docOrScheduleFunction = docFunction || scheduleFunction
30
30
 
31
31
  this.api.store.result = {logs: '', time: 0}
@@ -58,7 +58,7 @@ class RunPanel extends ApiBaseElement {
58
58
  token: this.api.store.withToken,
59
59
  },
60
60
  eventResourceType: scheduleFunction
61
- ? 'schedule'
61
+ ? 'scheduled'
62
62
  : mediaFunction
63
63
  ? 'media-library'
64
64
  : 'dataset',
@@ -1,5 +1,5 @@
1
1
  import { treeify } from 'array-treeify';
2
- import { SANITY_ACCESS_ROBOT, SANITY_ACCESS_ROLE, SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULE, SANITY_PROJECT_CORS, SANITY_PROJECT_DATASET, SANITY_PROJECT_WEBHOOK, } from '../../constants.js';
2
+ import { SANITY_ACCESS_ROBOT, SANITY_ACCESS_ROLE, SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULED, SANITY_PROJECT, SANITY_PROJECT_CORS, SANITY_PROJECT_DATASET, SANITY_PROJECT_WEBHOOK, } from '../../constants.js';
3
3
  import { styleText } from '../style-text.js';
4
4
  import { isCorsOriginResource, isDatasetResource, isRobotResource, isRoleResource, isWebhookResource, } from '../types.js';
5
5
  import { formatDate, formatDuration } from './dates.js';
@@ -27,7 +27,14 @@ const RESOURCE_CATEGORIES = {
27
27
  },
28
28
  [SANITY_FUNCTION_DOCUMENT]: functionCategory,
29
29
  [SANITY_FUNCTION_MEDIA_LIBRARY_ASSET]: functionCategory,
30
- [SANITY_FUNCTION_SCHEDULE]: functionCategory,
30
+ [SANITY_FUNCTION_SCHEDULED]: functionCategory,
31
+ [SANITY_PROJECT]: {
32
+ label: 'Projects',
33
+ displayNameAttribute: 'displayName',
34
+ formatDetails(_res) {
35
+ return [];
36
+ },
37
+ },
31
38
  [SANITY_PROJECT_CORS]: {
32
39
  label: 'CORS Origins',
33
40
  formatDetails(res) {
@@ -212,12 +219,12 @@ export function formatStacksListing(stacks, currentStackId) {
212
219
  const IGNORED_PARAMS = {
213
220
  [SANITY_FUNCTION_DOCUMENT]: new Set(['src']),
214
221
  [SANITY_FUNCTION_MEDIA_LIBRARY_ASSET]: new Set(['src']),
215
- [SANITY_FUNCTION_SCHEDULE]: new Set(['src']),
222
+ [SANITY_FUNCTION_SCHEDULED]: new Set(['src']),
216
223
  };
217
224
  const ASSET_RESOURCE_TYPES = new Set([
218
225
  SANITY_FUNCTION_DOCUMENT,
219
226
  SANITY_FUNCTION_MEDIA_LIBRARY_ASSET,
220
- SANITY_FUNCTION_SCHEDULE,
227
+ SANITY_FUNCTION_SCHEDULED,
221
228
  ]);
222
229
  function stringifyUnknown(val) {
223
230
  if (val === null || val === undefined)
@@ -1,17 +1,18 @@
1
+ import { SANITY_FUNCTION_PREFIX } from '../constants.js';
1
2
  export function getFunctionNames(resources) {
2
3
  return (resources
3
- ?.filter((r) => r?.type?.startsWith('sanity.function.'))
4
+ ?.filter((r) => r?.type?.startsWith(SANITY_FUNCTION_PREFIX))
4
5
  .map((r) => r.name)
5
6
  .filter((name) => typeof name === 'string') ?? []);
6
7
  }
7
8
  export function findFunctionInBlueprint(blueprint, name) {
8
- const func = blueprint?.resources?.find((r) => r?.type?.startsWith('sanity.function.') && r.name === name);
9
+ const func = blueprint?.resources?.find((r) => r?.type?.startsWith(SANITY_FUNCTION_PREFIX) && r.name === name);
9
10
  if (!func)
10
11
  throw Error(`Unable to find function ${name}`);
11
12
  return func;
12
13
  }
13
14
  export function findFunctionInStack(stack, name) {
14
- const func = stack?.resources?.find((r) => r?.type?.startsWith('sanity.function.') && r.name === name);
15
+ const func = stack?.resources?.find((r) => r?.type?.startsWith(SANITY_FUNCTION_PREFIX) && r.name === name);
15
16
  if (!func)
16
17
  throw Error(`Unable to find function: "${name}"`);
17
18
  if (!isDeployedResource(func))
@@ -0,0 +1,5 @@
1
+ import { type PathLike } from 'node:fs';
2
+ /**
3
+ * Recursively calculates the total size of a folder, including all nested files and subdirectories.
4
+ */
5
+ export declare const getFolderSize: (folder: PathLike) => number;
@@ -0,0 +1,24 @@
1
+ import { readdirSync, statSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ /**
4
+ * Recursively calculates the total size of a folder, including all nested files and subdirectories.
5
+ */
6
+ export const getFolderSize = (folder) => {
7
+ if (!statSync(folder).isDirectory()) {
8
+ throw new Error('Provided path is not a directory');
9
+ }
10
+ let totalSize = 0;
11
+ const fileEntries = readdirSync(folder);
12
+ for (const file of fileEntries) {
13
+ const filePath = path.join(folder.toString(), file);
14
+ const stats = statSync(filePath);
15
+ if (stats.isFile()) {
16
+ totalSize += stats.size;
17
+ }
18
+ else if (stats.isDirectory()) {
19
+ const recursiveSize = getFolderSize(filePath);
20
+ totalSize += recursiveSize;
21
+ }
22
+ }
23
+ return totalSize;
24
+ };
@@ -1,33 +1,35 @@
1
1
  import path from 'node:path';
2
2
  import { cwd } from 'node:process';
3
- import getFolderSize from 'get-folder-size';
3
+ import { MAX_ASSET_SIZE } from '../../constants.js';
4
4
  import { transpileFunction } from '../transpile/transpile-function.js';
5
+ import { getFolderSize } from './getFolderSize.js';
5
6
  import { resolveResourceDependencies } from './resolve-dependencies.js';
6
7
  import { shouldAutoResolveDependencies } from './should-auto-resolve-deps.js';
7
8
  import { shouldTranspileFunction } from './should-transpile.js';
8
- const MAX_ASSET_SIZE = 209_715_200; // 200 MB in bytes
9
9
  export async function prepareAsset({ resource, }) {
10
10
  if (!resource.src)
11
11
  throw new Error('Resource src is required');
12
12
  let functionPath = path.join(cwd(), resource.src);
13
13
  let cleanup = async () => { };
14
+ let wasBundled = false;
14
15
  const shouldTranspile = await shouldTranspileFunction(resource);
15
16
  if (shouldTranspile) {
16
17
  try {
17
18
  const result = await transpileFunction(resource);
18
19
  functionPath = result.outputDir;
19
20
  cleanup = result.cleanup;
21
+ wasBundled = result.bundled;
20
22
  }
21
23
  catch (err) {
22
24
  return { success: false, error: err instanceof Error ? err.message : `${err}` };
23
25
  }
24
26
  }
25
27
  const shouldResolveDependencies = await shouldAutoResolveDependencies(resource);
26
- if (shouldResolveDependencies) {
28
+ if (shouldResolveDependencies && !wasBundled) {
27
29
  await resolveResourceDependencies(resource, shouldTranspile);
28
30
  }
29
31
  try {
30
- const size = await getFolderSize.loose(functionPath);
32
+ const size = getFolderSize(functionPath);
31
33
  if (size > MAX_ASSET_SIZE) {
32
34
  throw new Error('Resource is larger than max asset size of 200 MB.');
33
35
  }
@@ -5,4 +5,5 @@ export declare function transpileFunction(resource: FunctionResource): Promise<{
5
5
  warnings: string[];
6
6
  cleanup: () => Promise<void>;
7
7
  timings: Record<string, number>;
8
+ bundled: boolean;
8
9
  }>;
@@ -1,4 +1,6 @@
1
+ import { existsSync } from 'node:fs';
1
2
  import { mkdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
3
+ import { createRequire } from 'node:module';
2
4
  import path from 'node:path';
3
5
  import { performance } from 'node:perf_hooks';
4
6
  import { cwd } from 'node:process';
@@ -33,6 +35,7 @@ export async function transpileFunction(resource) {
33
35
  }
34
36
  try {
35
37
  const viteStart = performance.now();
38
+ const bundle = existsSync(path.join(cwd(), 'pnpm-workspace.yaml'));
36
39
  const result = await viteBuild({
37
40
  root: fnRootDir,
38
41
  logLevel: 'silent',
@@ -48,20 +51,23 @@ export async function transpileFunction(resource) {
48
51
  output: {
49
52
  format: 'esm',
50
53
  entryFileNames: getOutputFilename(entry),
51
- // Do NOT inline anything from node_modules
52
- preserveModules: true, // Key setting
53
- preserveModulesRoot: fnRootDir,
54
+ ...(bundle
55
+ ? {}
56
+ : {
57
+ preserveModules: true,
58
+ preserveModulesRoot: fnRootDir,
59
+ }),
54
60
  },
55
- external: [/node_modules/], // treat all node_modules as external
61
+ external: bundle ? [] : [/node_modules/],
56
62
  },
57
63
  },
58
64
  ssr: {
59
- noExternal: [], // Do NOT bundle node_modules
65
+ noExternal: bundle ? true : [],
60
66
  resolve: {
61
67
  conditions: ['node'],
62
68
  },
63
69
  },
64
- plugins: [tsConfigPaths()],
70
+ plugins: [tsConfigPaths(), ...(bundle ? [pnpmResolvePlugin(cwd())] : [])],
65
71
  });
66
72
  timings['transpile:build'] = performance.now() - viteStart;
67
73
  const verifyStart = performance.now();
@@ -80,6 +86,7 @@ export async function transpileFunction(resource) {
80
86
  warnings: [],
81
87
  cleanup: cleanupTmpDir,
82
88
  timings,
89
+ bundled: bundle,
83
90
  };
84
91
  }
85
92
  catch (err) {
@@ -127,3 +134,35 @@ function getOutputFilename(entryFileName) {
127
134
  function logCleanupFailure(err) {
128
135
  console.warn(`[warn] Failed to clean up temporary files: ${err instanceof Error ? err.message : err}`);
129
136
  }
137
+ /**
138
+ * Vite plugin to resolve packages from pnpm's virtual store.
139
+ * In pnpm workspaces, hoisted dependencies live in node_modules/.pnpm/node_modules/
140
+ * which isn't reachable via standard Node resolution.
141
+ */
142
+ function pnpmResolvePlugin(workspaceRoot) {
143
+ const pnpmModulesPath = path.join(workspaceRoot, 'node_modules', '.pnpm', 'node_modules');
144
+ // createRequire gives us Node's full module resolution algorithm rooted at
145
+ // the pnpm virtual store — it reads package.json exports/main and returns
146
+ // the absolute path to the actual entry file, not just the directory.
147
+ const pnpmRequire = createRequire(path.join(pnpmModulesPath, '_virtual.js'));
148
+ return {
149
+ name: 'pnpm-resolve',
150
+ async resolveId(source, importer, options) {
151
+ // Only act as fallback — let Vite try first
152
+ const resolved = await this.resolve(source, importer, {
153
+ ...options,
154
+ skipSelf: true,
155
+ });
156
+ if (resolved)
157
+ return resolved;
158
+ // Fallback: use Node's require.resolve() from the pnpm virtual store.
159
+ // This properly follows package.json exports/main to the entry file.
160
+ try {
161
+ return pnpmRequire.resolve(source);
162
+ }
163
+ catch {
164
+ return null;
165
+ }
166
+ },
167
+ };
168
+ }
@@ -1,6 +1,6 @@
1
- import { type BlueprintCorsOriginResource, type BlueprintDatasetResource, type BlueprintDocumentWebhookResource, type BlueprintResource, type BlueprintRobotResource, type BlueprintRoleResource } from '@sanity/blueprints';
1
+ import { type BlueprintCorsOriginResource, type BlueprintDatasetResource, type BlueprintDocumentWebhookResource, type BlueprintProjectResource, type BlueprintResource, type BlueprintRobotResource, type BlueprintRoleResource } from '@sanity/blueprints';
2
2
  import type { Blueprint } from '@sanity/blueprints-parser';
3
- import { SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULE } from '../constants.js';
3
+ import { SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULED } from '../constants.js';
4
4
  export type ScopeType = 'organization' | 'project';
5
5
  /** Result utility type */
6
6
  export type Result<T, E = string> = {
@@ -65,6 +65,7 @@ export declare function isScheduleFunctionResource<T extends BlueprintResource>(
65
65
  export declare function isLocalFunctionCollection<T extends BlueprintResource>(r: T): r is T & FunctionsCollection;
66
66
  export declare function isScheduleEvent(e: unknown): e is FunctionResourceScheduleEvent;
67
67
  export declare function isCorsOriginResource(r: unknown): r is BlueprintCorsOriginResource;
68
+ export declare function isProjectResource(r: unknown): r is BlueprintProjectResource;
68
69
  export declare function isRobotResource(r: unknown): r is BlueprintRobotResource;
69
70
  export declare function isRoleResource(r: unknown): r is BlueprintRoleResource;
70
71
  export declare function isDatasetResource(r: unknown): r is BlueprintDatasetResource;
@@ -91,11 +92,11 @@ interface FunctionResourceMediaLibraryAsset extends FunctionResourceBase {
91
92
  event: GroqRuleMediaLibraryFunction;
92
93
  }
93
94
  interface FunctionResourceSchedule extends FunctionResourceBase {
94
- type: typeof SANITY_FUNCTION_SCHEDULE;
95
+ type: typeof SANITY_FUNCTION_SCHEDULED;
95
96
  event: FunctionResourceScheduleEvent;
96
97
  }
97
98
  export interface CollectionFunction {
98
- type: typeof SANITY_FUNCTION_DOCUMENT | typeof SANITY_FUNCTION_SCHEDULE | typeof SANITY_FUNCTION_MEDIA_LIBRARY_ASSET;
99
+ type: typeof SANITY_FUNCTION_DOCUMENT | typeof SANITY_FUNCTION_SCHEDULED | typeof SANITY_FUNCTION_MEDIA_LIBRARY_ASSET;
99
100
  src: string;
100
101
  name: string;
101
102
  displayName?: string;
@@ -159,7 +160,7 @@ export interface InvokeGroqPayloadOptions {
159
160
  }
160
161
  export interface InvokeSchedulePayloadOptions {
161
162
  payload?: Record<string, unknown>;
162
- event: 'schedule';
163
+ event: 'scheduled';
163
164
  }
164
165
  export type InvokePayloadOptions = InvokeGroqPayloadOptions | InvokeSchedulePayloadOptions;
165
166
  export type InvokePayloadMetadata = Pick<InvokeGroqPayloadOptions, 'event' | 'before' | 'after'> | Pick<InvokeSchedulePayloadOptions, 'event'>;
@@ -1,8 +1,8 @@
1
- import { validateCorsOrigin, validateDataset, validateDocumentWebhook, validateRobot, validateRole, } from '@sanity/blueprints';
2
- import { SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULE, } from '../constants.js';
1
+ import { validateCorsOrigin, validateDataset, validateDocumentWebhook, validateProject, validateRobot, validateRole, } from '@sanity/blueprints';
2
+ import { SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_PREFIX, SANITY_FUNCTION_SCHEDULED, } from '../constants.js';
3
3
  // type narrowing with predicate functions
4
4
  export function isLocalFunctionResource(r) {
5
- return r.type.startsWith('sanity.function.');
5
+ return r.type.startsWith(SANITY_FUNCTION_PREFIX);
6
6
  }
7
7
  export function isDocumentFunctionResource(r) {
8
8
  return r.type === SANITY_FUNCTION_DOCUMENT;
@@ -11,7 +11,7 @@ export function isMediaLibraryAssetFunctionResource(r) {
11
11
  return r.type === SANITY_FUNCTION_MEDIA_LIBRARY_ASSET;
12
12
  }
13
13
  export function isScheduleFunctionResource(r) {
14
- return r.type === SANITY_FUNCTION_SCHEDULE;
14
+ return r.type === SANITY_FUNCTION_SCHEDULED;
15
15
  }
16
16
  export function isLocalFunctionCollection(r) {
17
17
  return r.type === 'sanity.experimental.functions-collection';
@@ -22,6 +22,9 @@ export function isScheduleEvent(e) {
22
22
  export function isCorsOriginResource(r) {
23
23
  return validateCorsOrigin(r).length === 0;
24
24
  }
25
+ export function isProjectResource(r) {
26
+ return validateProject(r).length === 0;
27
+ }
25
28
  export function isRobotResource(r) {
26
29
  return validateRobot(r).length === 0;
27
30
  }
@@ -1,5 +1,5 @@
1
- import { validateCorsOrigin, validateDataset, validateDocumentFunction, validateDocumentWebhook, validateMediaLibraryAssetFunction, validateRole, validateScheduleFunction, } from '@sanity/blueprints';
2
- import { SANITY_ACCESS_ROLE, SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULE, SANITY_PROJECT_CORS, SANITY_PROJECT_DATASET, SANITY_PROJECT_WEBHOOK, } from '../../constants.js';
1
+ import { validateCorsOrigin, validateDataset, validateDocumentFunction, validateDocumentWebhook, validateMediaLibraryAssetFunction, validateProject, validateRole, validateScheduledFunction, } from '@sanity/blueprints';
2
+ import { SANITY_ACCESS_ROLE, SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULED, SANITY_PROJECT, SANITY_PROJECT_CORS, SANITY_PROJECT_DATASET, SANITY_PROJECT_WEBHOOK, } from '../../constants.js';
3
3
  export * as validate from './resource.js';
4
4
  const RESOURCE_VALIDATORS = {
5
5
  [SANITY_ACCESS_ROLE]: {
@@ -11,8 +11,11 @@ const RESOURCE_VALIDATORS = {
11
11
  [SANITY_FUNCTION_MEDIA_LIBRARY_ASSET]: {
12
12
  validate: validateMediaLibraryAssetFunction,
13
13
  },
14
- [SANITY_FUNCTION_SCHEDULE]: {
15
- validate: validateScheduleFunction,
14
+ [SANITY_FUNCTION_SCHEDULED]: {
15
+ validate: validateScheduledFunction,
16
+ },
17
+ [SANITY_PROJECT]: {
18
+ validate: validateProject,
16
19
  },
17
20
  [SANITY_PROJECT_CORS]: {
18
21
  validate: validateCorsOrigin,
@@ -1,3 +1,4 @@
1
+ import { SANITY_FUNCTION_PREFIX } from '../../constants.js';
1
2
  import { BlueprintParserErrorType, isDocumentFunctionResource, isMediaLibraryAssetFunctionResource, isScheduleEvent, } from '../types.js';
2
3
  export function validateFunctionName(name) {
3
4
  // must be 3+ characters, no special characters, no spaces, allow _ and -
@@ -14,9 +15,9 @@ export function validateFunctionResource(resource) {
14
15
  type: BlueprintParserErrorType.InvalidProperty,
15
16
  });
16
17
  }
17
- if (!resource.type.startsWith('sanity.function.')) {
18
+ if (!resource.type.startsWith(SANITY_FUNCTION_PREFIX)) {
18
19
  errors.push({
19
- message: `${msgPrefix} Resource type must start with "sanity.function."`,
20
+ message: `${msgPrefix} Resource type must start with "${SANITY_FUNCTION_PREFIX}"`,
20
21
  type: BlueprintParserErrorType.InvalidType,
21
22
  });
22
23
  }
@@ -100,13 +100,14 @@
100
100
  "hasDynamicHelp": false,
101
101
  "multiple": true,
102
102
  "options": [
103
+ "document-publish",
103
104
  "document-create",
104
- "document-delete",
105
105
  "document-update",
106
- "document-publish",
106
+ "document-delete",
107
107
  "media-library-asset-create",
108
108
  "media-library-asset-update",
109
- "media-library-asset-delete"
109
+ "media-library-asset-delete",
110
+ "scheduled-function"
110
111
  ],
111
112
  "type": "option"
112
113
  },
@@ -1199,13 +1200,14 @@
1199
1200
  "hasDynamicHelp": false,
1200
1201
  "multiple": true,
1201
1202
  "options": [
1203
+ "document-publish",
1202
1204
  "document-create",
1203
- "document-delete",
1204
1205
  "document-update",
1205
- "document-publish",
1206
+ "document-delete",
1206
1207
  "media-library-asset-create",
1207
1208
  "media-library-asset-update",
1208
- "media-library-asset-delete"
1209
+ "media-library-asset-delete",
1210
+ "scheduled-function"
1209
1211
  ],
1210
1212
  "type": "option"
1211
1213
  },
@@ -2161,5 +2163,5 @@
2161
2163
  ]
2162
2164
  }
2163
2165
  },
2164
- "version": "14.2.0"
2166
+ "version": "14.4.0"
2165
2167
  }
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": "14.2.0",
4
+ "version": "14.4.0",
5
5
  "author": "Sanity Runtime Team",
6
6
  "type": "module",
7
7
  "license": "MIT",
@@ -97,12 +97,12 @@
97
97
  "watch": "tsc --watch"
98
98
  },
99
99
  "dependencies": {
100
- "@architect/hydrate": "^5.0.1",
100
+ "@architect/hydrate": "^5.0.2",
101
101
  "@architect/inventory": "^5.0.0",
102
102
  "@inquirer/prompts": "^8.2.1",
103
103
  "@oclif/core": "^4.8.0",
104
104
  "@oclif/plugin-help": "^6.2.37",
105
- "@sanity/blueprints": "^0.12.2",
105
+ "@sanity/blueprints": "^0.13.1",
106
106
  "@sanity/blueprints-parser": "^0.4.0",
107
107
  "@sanity/client": "^7.15.0",
108
108
  "adm-zip": "^0.5.16",
@@ -110,7 +110,6 @@
110
110
  "cardinal": "^2.1.1",
111
111
  "empathic": "^2.0.0",
112
112
  "eventsource": "^4.1.0",
113
- "get-folder-size": "^5.0.0",
114
113
  "groq-js": "^1.27.1",
115
114
  "jiti": "^2.6.1",
116
115
  "mime-types": "^3.0.2",