@sanity/runtime-cli 16.0.3 → 17.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 (41) hide show
  1. package/README.md +27 -22
  2. package/dist/actions/functions/test.js +1 -1
  3. package/dist/baseCommands.js +13 -2
  4. package/dist/commands/blueprints/init.js +5 -2
  5. package/dist/config.d.ts +1 -0
  6. package/dist/config.js +7 -1
  7. package/dist/constants.d.ts +1 -0
  8. package/dist/constants.js +1 -0
  9. package/dist/cores/blueprints/init.js +16 -2
  10. package/dist/cores/functions/test.js +44 -0
  11. package/dist/server/app.js +31 -3
  12. package/dist/server/handlers/invoke.d.ts +2 -2
  13. package/dist/server/handlers/invoke.js +7 -4
  14. package/dist/server/static/components/console-panel.js +59 -8
  15. package/dist/server/static/components/rule-panel.js +11 -8
  16. package/dist/server/static/components/shared-styles.js +24 -21
  17. package/dist/server/static/index.html +1 -1
  18. package/dist/server/static/vendor/design-tokens/color.css +48 -48
  19. package/dist/server/static/vendor/design-tokens/index.css +0 -1
  20. package/dist/server/static/vendor/vendor.bundle.js +3807 -3259
  21. package/dist/server/static/web-socket.js +41 -0
  22. package/dist/utils/functions/child-process-wrapper.js +62 -7
  23. package/dist/utils/functions/{invoke-local.d.ts → invoke/local.d.ts} +1 -1
  24. package/dist/utils/functions/invoke/local.js +306 -0
  25. package/dist/utils/functions/invoke/nested.d.ts +25 -0
  26. package/dist/utils/functions/invoke/nested.js +41 -0
  27. package/dist/utils/functions/invoke/prepare.d.ts +20 -0
  28. package/dist/utils/functions/invoke/prepare.js +90 -0
  29. package/dist/utils/interactive.d.ts +1 -0
  30. package/dist/utils/interactive.js +4 -0
  31. package/dist/utils/isTrueish.d.ts +6 -0
  32. package/dist/utils/isTrueish.js +9 -0
  33. package/dist/utils/logger.d.ts +1 -0
  34. package/dist/utils/logger.js +1 -0
  35. package/dist/utils/style-text.js +4 -5
  36. package/dist/utils/traced-fetch.js +2 -0
  37. package/dist/utils/types.d.ts +62 -4
  38. package/oclif.manifest.json +928 -927
  39. package/package.json +18 -14
  40. package/dist/server/static/hot-reload.js +0 -16
  41. package/dist/utils/functions/invoke-local.js +0 -185
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": "16.0.3",
4
+ "version": "17.1.0",
5
5
  "author": "Sanity Runtime Team",
6
6
  "type": "module",
7
7
  "license": "MIT",
@@ -21,7 +21,7 @@
21
21
  }
22
22
  },
23
23
  "engines": {
24
- "node": ">=20.19"
24
+ "node": ">=22.12"
25
25
  },
26
26
  "files": [
27
27
  "./bin",
@@ -58,27 +58,27 @@
58
58
  "watch": "tsc --watch"
59
59
  },
60
60
  "dependencies": {
61
- "@architect/hydrate": "^5.0.2",
62
- "@architect/inventory": "^5.0.0",
63
- "@inquirer/prompts": "^8.4.2",
64
- "@oclif/core": "^4.11.0",
65
- "@oclif/plugin-help": "^6.2.46",
61
+ "@architect/hydrate": "~6.0.2",
62
+ "@architect/inventory": "~6.0.0",
63
+ "@inquirer/prompts": "^8.5.2",
64
+ "@oclif/core": "^4.11.6",
65
+ "@oclif/plugin-help": "^6.2.50",
66
66
  "@sanity-labs/design-tokens": "^0.0.2-alpha.2",
67
67
  "@sanity/blueprints": "^0.20.1",
68
68
  "@sanity/blueprints-parser": "^0.4.0",
69
- "@sanity/client": "^7.22.0",
69
+ "@sanity/client": "^7.22.1",
70
70
  "adm-zip": "^0.5.17",
71
71
  "array-treeify": "^0.1.5",
72
72
  "cardinal": "^2.1.1",
73
- "empathic": "^2.0.0",
73
+ "empathic": "^2.0.1",
74
74
  "eventsource": "^4.1.0",
75
- "groq-js": "^1.30.1",
75
+ "groq-js": "^1.30.2",
76
76
  "jiti": "^2.7.0",
77
77
  "mime-types": "^3.0.2",
78
78
  "ora": "^9.4.0",
79
79
  "tar-stream": "^3.2.0",
80
80
  "vite": "^8.0.16",
81
- "ws": "^8.20.0",
81
+ "ws": "^8.21.0",
82
82
  "xdg-basedir": "^5.1.0"
83
83
  },
84
84
  "devDependencies": {
@@ -94,10 +94,10 @@
94
94
  "@types/adm-zip": "^0.5.8",
95
95
  "@types/cardinal": "^2.1.1",
96
96
  "@types/mime-types": "^3.0.1",
97
- "@types/node": "20",
97
+ "@types/node": "^22",
98
98
  "@types/tar-stream": "^3.1.4",
99
99
  "@types/ws": "^8.18.1",
100
- "@vitest/coverage-v8": "^4.1.5",
100
+ "@vitest/coverage-v8": "4.1.9",
101
101
  "codemirror": "^6.0.2",
102
102
  "mentoss": "^0.13.0",
103
103
  "oclif": "^4.23.0",
@@ -107,7 +107,7 @@
107
107
  "shx": "^0.4.0",
108
108
  "ts-node": "^10.9.2",
109
109
  "typescript": "^6.0.3",
110
- "vitest": "4.1.5"
110
+ "vitest": "4.1.9"
111
111
  },
112
112
  "oclif": {
113
113
  "bin": "sanity-run",
@@ -129,5 +129,9 @@
129
129
  "publishConfig": {
130
130
  "access": "public",
131
131
  "provenance": false
132
+ },
133
+ "allowScripts": {
134
+ "fsevents@2.3.2": true,
135
+ "fsevents@2.3.3": true
132
136
  }
133
137
  }
@@ -1,16 +0,0 @@
1
- import apiConstructor from '../api.js'
2
-
3
- const api = apiConstructor()
4
-
5
- // Create WebSocket connection.
6
- const socket = new WebSocket('ws://localhost:8974')
7
-
8
- // Connection opened
9
- socket.addEventListener('open', () => {
10
- console.log('Watching for Blueprint changes')
11
- })
12
-
13
- // Listen for messages
14
- socket.addEventListener('message', (event) => {
15
- if (event.data === 'reload-blueprint') api.blueprint()
16
- })
@@ -1,185 +0,0 @@
1
- import { spawn } from 'node:child_process';
2
- import { performance } from 'node:perf_hooks';
3
- import { cwd } from 'node:process';
4
- import { setTimeout } from 'node:timers';
5
- import { fileURLToPath } from 'node:url';
6
- import * as groq from 'groq-js';
7
- import config from '../../config.js';
8
- import { groupBlueprintResources } from '../blueprints/group-resources.js';
9
- import { isGroqContextOptions, } from '../types.js';
10
- import { findFunctionEntryPoint } from './find-entry-point.js';
11
- import { cleanupTempPackageJson, createTempPackageJson, doesPackageJsonExists, } from './package-json.js';
12
- import { resolveResourceDependencies } from './resolve-dependencies.js';
13
- import { shouldAutoResolveDependencies } from './should-auto-resolve-deps.js';
14
- import { shouldTranspileFunction } from './should-transpile.js';
15
- import { transpileFunction } from './transpile/function.js';
16
- function getChildProcessWrapperPath() {
17
- return fileURLToPath(new URL('./child-process-wrapper.js', import.meta.url));
18
- }
19
- export function sanitizeLogs(logs) {
20
- return logs.replace(/([a-zA-Z0-9]{10})[a-zA-Z0-9]{65,}/g, '$1**********');
21
- }
22
- export async function applyGroqRule(resource, payload, projectId, dataset) {
23
- const { before, after, payload: data = null } = payload;
24
- // If there is no rule set return everything
25
- if (!resource.event)
26
- return data;
27
- // default groq rule is: gimme full doc content. otherwise, parse + eval custom rule
28
- // applying the GROQ filter may result in a slimmer set of documents
29
- if (resource.event.filter || resource.event.projection) {
30
- const query = `*[${resource.event.filter || ''}]${resource.event.projection || ''}`;
31
- try {
32
- const rule = groq.parse(query, { mode: 'delta' });
33
- const sanity = projectId && dataset ? { projectId, dataset } : undefined;
34
- const queryResults = await groq.evaluate(rule, { dataset: [data], before, after, sanity });
35
- const currentFunctionDocumentSet = await queryResults.get();
36
- return currentFunctionDocumentSet[0];
37
- }
38
- catch (groqErr) {
39
- const detail = groqErr instanceof Error && `; ${groqErr.message}`;
40
- throw new Error(`Invalid rule configuration: ${query}${detail}`, { cause: groqErr });
41
- }
42
- }
43
- // default groq rule so just return the data
44
- return data;
45
- }
46
- export default async function invoke(resource, payload, context, options) {
47
- if (!resource.src) {
48
- throw new Error(`Function resource "${resource.name}" is missing the 'src' property.`);
49
- }
50
- let cleanupBundle = async () => { };
51
- let functionPath = '';
52
- let bundleTimings;
53
- // If no package.json exists we will create a temporary one to avoid the
54
- // Module Type warning from node
55
- const existingPackageJson = doesPackageJsonExists(resource);
56
- const shouldTranspile = await shouldTranspileFunction(resource);
57
- if (shouldTranspile) {
58
- const transpileResult = await transpileFunction(resource);
59
- functionPath = await findFunctionEntryPoint(transpileResult.outputDir);
60
- bundleTimings = transpileResult.timings;
61
- cleanupBundle = transpileResult.cleanup;
62
- }
63
- else {
64
- functionPath = await findFunctionEntryPoint(resource.src, resource.displayName ?? resource.name);
65
- }
66
- const shouldResolveDependencies = await shouldAutoResolveDependencies(resource);
67
- if (shouldResolveDependencies && existingPackageJson) {
68
- await resolveResourceDependencies(resource, { transpiled: shouldTranspile });
69
- }
70
- if (!existingPackageJson) {
71
- createTempPackageJson(functionPath);
72
- }
73
- const { forceColor = true, timeout = 10 } = options;
74
- let filteredData = payload?.payload?.data || {};
75
- if (resource.type === 'sanity.function.document' ||
76
- resource.type === 'sanity.function.media-library.asset') {
77
- if (!isGroqContextOptions(context)) {
78
- throw new Error('GROQ-based functions require a context with clientOptions');
79
- }
80
- const { projectId, dataset } = context.clientOptions;
81
- if ('before' in payload) {
82
- filteredData = await applyGroqRule(resource, payload, projectId, dataset);
83
- if (typeof filteredData === 'undefined') {
84
- return {
85
- logs: `⚠️ Filter "${resource.event?.filter}" returned an empty result. Skipping invoke.`,
86
- error: undefined,
87
- json: undefined,
88
- };
89
- }
90
- }
91
- }
92
- return new Promise((resolve, reject) => {
93
- let child;
94
- let timer;
95
- let executionStart;
96
- function start() {
97
- executionStart = performance.now();
98
- child = spawn('node', ['--enable-source-maps', getChildProcessWrapperPath()], {
99
- cwd: cwd(),
100
- stdio: ['pipe', 'pipe', 'pipe', 'ipc'], // changed: use 'pipe' so we can capture output
101
- env: { ...process.env, ...resource.env, FORCE_COLOR: forceColor ? '1' : '0' },
102
- });
103
- // NEW: capture stdout and stderr
104
- let stdout = '';
105
- let stderr = '';
106
- child.stdout?.on('data', (data) => {
107
- stdout += data.toString();
108
- });
109
- child.stderr?.on('data', (data) => {
110
- stderr += data.toString();
111
- });
112
- child.on('message', (data) => {
113
- const executionTimeMs = performance.now() - executionStart;
114
- const { json, logs, error } = JSON.parse(data.toString());
115
- shutdown();
116
- resolve({
117
- json,
118
- logs: sanitizeLogs(logs),
119
- error,
120
- timings: {
121
- ...bundleTimings,
122
- execute: executionTimeMs,
123
- },
124
- });
125
- });
126
- child.on('error', (error) => {
127
- shutdown();
128
- reject(new Error(`encountered error ${error.message}`));
129
- });
130
- child.on('exit', (code) => {
131
- const executionTimeMs = performance.now() - executionStart;
132
- shutdown();
133
- if (code !== 0) {
134
- // use captured stderr if available
135
- const message = stderr.trim().length > 0
136
- ? `Process failed with code ${code}\n${stderr}`
137
- : `exited with code ${code}`;
138
- reject(new Error(message));
139
- }
140
- else {
141
- resolve({
142
- json: undefined,
143
- logs: sanitizeLogs(stdout),
144
- error: undefined,
145
- timings: {
146
- ...bundleTimings,
147
- execute: executionTimeMs,
148
- },
149
- });
150
- }
151
- });
152
- timer = setTimeout(() => {
153
- shutdown();
154
- reject(new Error(`Timeout: Function exceeded the ${timeout}-second time limit.`));
155
- }, timeout * 1000);
156
- const payload = {
157
- data: { ...filteredData },
158
- context: {
159
- ...context,
160
- local: true,
161
- resources: groupBlueprintResources(context.resources ?? []),
162
- ...(isGroqContextOptions(context) && {
163
- clientOptions: {
164
- ...context.clientOptions,
165
- apiHost: config.apiUrl,
166
- },
167
- }),
168
- },
169
- };
170
- child.send(JSON.stringify({ srcPath: functionPath, payload }, null, 2));
171
- }
172
- function shutdown() {
173
- clearTimeout(timer);
174
- if (child && !child.killed) {
175
- child.kill();
176
- }
177
- // If we previously created a temporary package.json, let's clean it up
178
- if (!existingPackageJson) {
179
- cleanupTempPackageJson(functionPath);
180
- }
181
- cleanupBundle().catch((err) => console.warn('Bundle cleanup failed:', err));
182
- }
183
- start();
184
- });
185
- }