@sanity/runtime-cli 4.3.4 → 4.3.5-bundle.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 (42) hide show
  1. package/README.md +17 -17
  2. package/dist/actions/blueprints/assets.d.ts +1 -0
  3. package/dist/actions/blueprints/assets.js +21 -4
  4. package/dist/actions/functions/test.d.ts +2 -2
  5. package/dist/actions/functions/test.js +2 -2
  6. package/dist/commands/blueprints/deploy.js +3 -4
  7. package/dist/commands/functions/test.js +3 -6
  8. package/dist/server/app.js +80 -12
  9. package/dist/server/static/api.js +24 -3
  10. package/dist/server/static/components/app.css +74 -23
  11. package/dist/server/static/components/filters.js +1 -1
  12. package/dist/server/static/components/function-list.js +5 -5
  13. package/dist/server/static/components/payload-panel.js +4 -3
  14. package/dist/server/static/components/response-panel.js +37 -35
  15. package/dist/server/static/index.html +5 -3
  16. package/dist/server/static/vendor/vendor.bundle.d.ts +2 -2
  17. package/dist/utils/build-payload.d.ts +1 -1
  18. package/dist/utils/build-payload.js +3 -3
  19. package/dist/utils/bundle/bundle-function.d.ts +8 -0
  20. package/dist/utils/bundle/bundle-function.js +125 -0
  21. package/dist/utils/bundle/cleanup-source-maps.d.ts +10 -0
  22. package/dist/utils/bundle/cleanup-source-maps.js +53 -0
  23. package/dist/utils/bundle/find-up.d.ts +16 -0
  24. package/dist/utils/bundle/find-up.js +39 -0
  25. package/dist/utils/bundle/verify-handler.d.ts +2 -0
  26. package/dist/utils/bundle/verify-handler.js +13 -0
  27. package/dist/utils/child-process-wrapper.js +8 -6
  28. package/dist/utils/functions/find-entry-point.d.ts +11 -0
  29. package/dist/utils/functions/find-entry-point.js +75 -0
  30. package/dist/utils/functions/should-bundle.d.ts +2 -0
  31. package/dist/utils/functions/should-bundle.js +23 -0
  32. package/dist/utils/invoke-local.d.ts +2 -2
  33. package/dist/utils/invoke-local.js +48 -7
  34. package/dist/utils/is-record.d.ts +1 -0
  35. package/dist/utils/is-record.js +3 -0
  36. package/dist/utils/parse-json-object.d.ts +1 -0
  37. package/dist/utils/parse-json-object.js +10 -0
  38. package/dist/utils/types.d.ts +3 -1
  39. package/oclif.manifest.json +1 -1
  40. package/package.json +4 -1
  41. package/dist/utils/is-json.d.ts +0 -1
  42. package/dist/utils/is-json.js +0 -12
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/4.3.4 linux-x64 node-v22.14.0
23
+ @sanity/runtime-cli/4.3.5-bundle.0 darwin-arm64 node-v22.14.0
24
24
  $ sanity-run --help [COMMAND]
25
25
  USAGE
26
26
  $ sanity-run COMMAND
@@ -74,7 +74,7 @@ EXAMPLES
74
74
  $ sanity-run blueprints add function --name my-function --function-type document-publish
75
75
  ```
76
76
 
77
- _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.4/src/commands/blueprints/add.ts)_
77
+ _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.5-bundle.0/src/commands/blueprints/add.ts)_
78
78
 
79
79
  ## `sanity-run blueprints config`
80
80
 
@@ -103,7 +103,7 @@ EXAMPLES
103
103
  $ sanity-run blueprints config --edit --project-id <projectId> --stack-id <stackId>
104
104
  ```
105
105
 
106
- _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.4/src/commands/blueprints/config.ts)_
106
+ _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.5-bundle.0/src/commands/blueprints/config.ts)_
107
107
 
108
108
  ## `sanity-run blueprints deploy`
109
109
 
@@ -123,7 +123,7 @@ EXAMPLES
123
123
  $ sanity-run blueprints deploy
124
124
  ```
125
125
 
126
- _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.4/src/commands/blueprints/deploy.ts)_
126
+ _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.5-bundle.0/src/commands/blueprints/deploy.ts)_
127
127
 
128
128
  ## `sanity-run blueprints destroy`
129
129
 
@@ -146,7 +146,7 @@ EXAMPLES
146
146
  $ sanity-run blueprints destroy --id ST-a1b2c3
147
147
  ```
148
148
 
149
- _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.4/src/commands/blueprints/destroy.ts)_
149
+ _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.5-bundle.0/src/commands/blueprints/destroy.ts)_
150
150
 
151
151
  ## `sanity-run blueprints info`
152
152
 
@@ -168,7 +168,7 @@ EXAMPLES
168
168
  $ sanity-run blueprints info --id ST-a1b2c3
169
169
  ```
170
170
 
171
- _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.4/src/commands/blueprints/info.ts)_
171
+ _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.5-bundle.0/src/commands/blueprints/info.ts)_
172
172
 
173
173
  ## `sanity-run blueprints init`
174
174
 
@@ -198,7 +198,7 @@ EXAMPLES
198
198
  $ sanity-run blueprints init --blueprint-type <json|js|ts> --project-id <projectId> --stack-name <stackName>
199
199
  ```
200
200
 
201
- _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.4/src/commands/blueprints/init.ts)_
201
+ _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.5-bundle.0/src/commands/blueprints/init.ts)_
202
202
 
203
203
  ## `sanity-run blueprints logs`
204
204
 
@@ -220,7 +220,7 @@ EXAMPLES
220
220
  $ sanity-run blueprints logs --watch
221
221
  ```
222
222
 
223
- _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.4/src/commands/blueprints/logs.ts)_
223
+ _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.5-bundle.0/src/commands/blueprints/logs.ts)_
224
224
 
225
225
  ## `sanity-run blueprints plan`
226
226
 
@@ -237,7 +237,7 @@ EXAMPLES
237
237
  $ sanity-run blueprints plan
238
238
  ```
239
239
 
240
- _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.4/src/commands/blueprints/plan.ts)_
240
+ _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.5-bundle.0/src/commands/blueprints/plan.ts)_
241
241
 
242
242
  ## `sanity-run blueprints stacks`
243
243
 
@@ -259,7 +259,7 @@ EXAMPLES
259
259
  $ sanity-run blueprints stacks --projectId a1b2c3
260
260
  ```
261
261
 
262
- _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.4/src/commands/blueprints/stacks.ts)_
262
+ _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.5-bundle.0/src/commands/blueprints/stacks.ts)_
263
263
 
264
264
  ## `sanity-run functions dev`
265
265
 
@@ -279,7 +279,7 @@ EXAMPLES
279
279
  $ sanity-run functions dev --port 8974
280
280
  ```
281
281
 
282
- _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.4/src/commands/functions/dev.ts)_
282
+ _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.5-bundle.0/src/commands/functions/dev.ts)_
283
283
 
284
284
  ## `sanity-run functions env add NAME KEY VALUE`
285
285
 
@@ -301,7 +301,7 @@ EXAMPLES
301
301
  $ sanity-run functions env add MyFunction API_URL https://api.example.com/
302
302
  ```
303
303
 
304
- _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.4/src/commands/functions/env/add.ts)_
304
+ _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.5-bundle.0/src/commands/functions/env/add.ts)_
305
305
 
306
306
  ## `sanity-run functions env list NAME`
307
307
 
@@ -321,7 +321,7 @@ EXAMPLES
321
321
  $ sanity-run functions env list MyFunction
322
322
  ```
323
323
 
324
- _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.4/src/commands/functions/env/list.ts)_
324
+ _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.5-bundle.0/src/commands/functions/env/list.ts)_
325
325
 
326
326
  ## `sanity-run functions env remove NAME KEY`
327
327
 
@@ -342,7 +342,7 @@ EXAMPLES
342
342
  $ sanity-run functions env remove MyFunction API_URL
343
343
  ```
344
344
 
345
- _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.4/src/commands/functions/env/remove.ts)_
345
+ _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.5-bundle.0/src/commands/functions/env/remove.ts)_
346
346
 
347
347
  ## `sanity-run functions invoke NAME`
348
348
 
@@ -368,7 +368,7 @@ EXAMPLES
368
368
  $ sanity-run functions invoke <name> --file 'payload.json'
369
369
  ```
370
370
 
371
- _See code: [src/commands/functions/invoke.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.4/src/commands/functions/invoke.ts)_
371
+ _See code: [src/commands/functions/invoke.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.5-bundle.0/src/commands/functions/invoke.ts)_
372
372
 
373
373
  ## `sanity-run functions logs NAME`
374
374
 
@@ -400,7 +400,7 @@ EXAMPLES
400
400
  $ sanity-run functions logs <name> --delete
401
401
  ```
402
402
 
403
- _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.4/src/commands/functions/logs.ts)_
403
+ _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.5-bundle.0/src/commands/functions/logs.ts)_
404
404
 
405
405
  ## `sanity-run functions test NAME`
406
406
 
@@ -433,7 +433,7 @@ EXAMPLES
433
433
  $ sanity-run functions test <name> --data '{ "id": 1 }' --timeout 60
434
434
  ```
435
435
 
436
- _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.4/src/commands/functions/test.ts)_
436
+ _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v4.3.5-bundle.0/src/commands/functions/test.ts)_
437
437
 
438
438
  ## `sanity-run help [COMMAND]`
439
439
 
@@ -6,5 +6,6 @@ export declare function stashAsset({ resource, auth, }: {
6
6
  }): Promise<{
7
7
  success: boolean;
8
8
  assetId?: string;
9
+ outputPath?: string;
9
10
  error?: string;
10
11
  }>;
@@ -3,21 +3,35 @@ import path from 'node:path';
3
3
  import { cwd } from 'node:process';
4
4
  import AdmZip from 'adm-zip';
5
5
  import config from '../../config.js';
6
+ import { bundleFunction } from '../../utils/bundle/bundle-function.js';
7
+ import { shouldBundleFunction } from '../../utils/functions/should-bundle.js';
6
8
  import getHeaders from '../../utils/get-headers.js';
7
9
  const { apiUrl } = config;
8
10
  export const stashUrl = `${apiUrl}vX/blueprints/assets/stash`;
9
11
  export async function stashAsset({ resource, auth, }) {
10
12
  if (!resource.src)
11
13
  throw new Error('Resource src is required');
14
+ let functionPath = path.join(cwd(), resource.src);
15
+ let cleanup = async () => { };
16
+ const shouldBundle = await shouldBundleFunction(resource);
17
+ if (shouldBundle) {
18
+ try {
19
+ const result = await bundleFunction(resource);
20
+ functionPath = result.outputDir;
21
+ cleanup = result.cleanup;
22
+ }
23
+ catch (err) {
24
+ return { success: false, error: err instanceof Error ? err.message : `${err}` };
25
+ }
26
+ }
12
27
  try {
13
- const sourcePath = path.join(cwd(), resource.src);
14
- const stats = await fs.promises.stat(sourcePath);
28
+ const stats = await fs.promises.stat(functionPath);
15
29
  const zip = new AdmZip();
16
30
  if (stats.isDirectory()) {
17
- zip.addLocalFolder(sourcePath);
31
+ zip.addLocalFolder(functionPath);
18
32
  }
19
33
  else {
20
- zip.addLocalFile(sourcePath, '', 'index.js');
34
+ zip.addLocalFile(functionPath, '', 'index.js');
21
35
  }
22
36
  const zipBuffer = zip.toBuffer();
23
37
  const base64Zip = zipBuffer.toString('base64');
@@ -41,4 +55,7 @@ export async function stashAsset({ resource, auth, }) {
41
55
  error = err.message;
42
56
  return { success: false, error };
43
57
  }
58
+ finally {
59
+ await cleanup();
60
+ }
44
61
  }
@@ -1,2 +1,2 @@
1
- import type { InvocationResponse, InvokeContextOptions, InvokePayloadOptions } from '../../utils/types.js';
2
- export declare function testAction(srcPath: string, options: InvokePayloadOptions, context: InvokeContextOptions): Promise<InvocationResponse>;
1
+ import type { InvocationResponse, InvokeContextOptions, InvokePayloadOptions, LocalFunctionResource } from '../../utils/types.js';
2
+ export declare function testAction(resource: LocalFunctionResource, options: InvokePayloadOptions, context: InvokeContextOptions): Promise<InvocationResponse>;
@@ -1,10 +1,10 @@
1
1
  import buildPayload from '../../utils/build-payload.js';
2
2
  import invoke from '../../utils/invoke-local.js';
3
- export async function testAction(srcPath, options, context) {
3
+ export async function testAction(resource, options, context) {
4
4
  const payload = buildPayload(options);
5
5
  const { timeout } = options;
6
6
  try {
7
- const { json, logs } = await invoke(srcPath, payload, context, timeout);
7
+ const { json, logs } = await invoke(resource, payload, context, timeout);
8
8
  return { error: undefined, json, logs };
9
9
  }
10
10
  catch (error) {
@@ -1,4 +1,5 @@
1
1
  import { setTimeout } from 'node:timers/promises';
2
+ import { inspect } from 'node:util';
2
3
  import { Command, Flags } from '@oclif/core';
3
4
  import Spinner from 'yocto-spinner';
4
5
  import { stashAsset } from '../../actions/blueprints/assets.js';
@@ -24,9 +25,7 @@ export default class Deploy extends Command {
24
25
  const { errors, projectId, stackId, parsedBlueprint: { resources }, deployedStack, } = await readBlueprintOnDisk({ getStack: true, token });
25
26
  if (errors.length > 0) {
26
27
  // printErrors(errors) // TODO: error printer in formatting
27
- this.log('Blueprint parse errors:');
28
- console.dir(errors, { depth: null });
29
- return;
28
+ this.error(`Blueprint parse errors:\n${inspect(errors, { depth: null })}`);
30
29
  }
31
30
  if (!deployedStack || !stackId || !projectId) {
32
31
  this.error('Before deploying, run `sanity blueprints init`');
@@ -96,7 +95,7 @@ export default class Deploy extends Command {
96
95
  }
97
96
  else {
98
97
  this.debug('STACK ERROR RESPONSE:', stack);
99
- spinner.error(`${red('Failed')} to ${deployedStack ? 'update' : 'create'} stack`);
98
+ spinner.error(`${red('Failed')} to update stack`);
100
99
  this.log(`Error: ${deployError || JSON.stringify(stack, null, 2) || 'Unknown error'}`);
101
100
  }
102
101
  }
@@ -1,7 +1,7 @@
1
1
  import { Args, Command, Flags } from '@oclif/core';
2
2
  import { readBlueprintOnDisk } from '../../actions/blueprints/blueprint.js';
3
3
  import { testAction } from '../../actions/functions/test.js';
4
- import { getFunctionSource } from '../../utils/find-function.js';
4
+ import { findFunctionByName } from '../../utils/find-function.js';
5
5
  export default class Test extends Command {
6
6
  static args = {
7
7
  name: Args.string({ description: 'The name of the Sanity Function', required: true }),
@@ -42,11 +42,8 @@ export default class Test extends Command {
42
42
  const { args, flags } = await this.parse(Test);
43
43
  const { parsedBlueprint } = await readBlueprintOnDisk({ getStack: false });
44
44
  try {
45
- const src = getFunctionSource(parsedBlueprint, args.name);
46
- if (!src) {
47
- this.error(`Error: Function ${args.name} has no source code`);
48
- }
49
- const { json, logs, error } = await testAction(src, {
45
+ const resource = findFunctionByName(parsedBlueprint, args.name);
46
+ const { json, logs, error } = await testAction(resource, {
50
47
  data: flags.data,
51
48
  file: flags.file,
52
49
  timeout: flags.timeout,
@@ -1,9 +1,11 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
+ import * as http from 'node:http';
2
3
  import { default as mime } from 'mime-types';
3
4
  import { readBlueprintOnDisk } from '../actions/blueprints/blueprint.js';
4
5
  import config from '../config.js';
6
+ import { findFunctionByName } from '../utils/find-function.js';
5
7
  import invoke from '../utils/invoke-local.js';
6
- import * as http from 'node:http';
8
+ import { isRecord } from '../utils/is-record.js';
7
9
  const host = 'localhost';
8
10
  const app = (port) => {
9
11
  const requestListener = async (req, res) => {
@@ -14,30 +16,40 @@ const app = (port) => {
14
16
  const { parsedBlueprint } = await readBlueprintOnDisk({ getStack: false });
15
17
  res.setHeader('Content-Type', 'application/json');
16
18
  res.writeHead(200);
17
- res.end(JSON.stringify(parsedBlueprint));
19
+ res.end(JSON.stringify(parsedBlueprint)); // Use parsedBlueprint directly
18
20
  }
19
- catch {
21
+ catch (error) {
20
22
  res.writeHead(404);
21
- res.end();
23
+ res.end(JSON.stringify({ error: error instanceof Error ? error.message : 'Unknown error' }));
22
24
  }
23
25
  break;
24
26
  }
25
27
  case req.url === '/invoke': {
26
28
  if (req.method === 'POST') {
27
- let body = '';
28
- req.on('data', (data) => {
29
- body += data;
30
- });
29
+ const body = [];
30
+ req.on('data', (data) => body.push(data));
31
31
  req.on('end', async () => {
32
- const { data, func } = JSON.parse(body);
33
- const { context, event } = data;
34
32
  res.setHeader('Content-Type', 'application/json');
35
33
  try {
36
- const response = await invoke(func, event, context);
37
- res.writeHead(200);
34
+ const { data, func: functionName } = parseInvokeRequest(Buffer.concat(body));
35
+ const { context, event } = data;
36
+ const start = performance.now();
37
+ const { parsedBlueprint } = await readBlueprintOnDisk({ getStack: false });
38
+ const resource = findFunctionByName(parsedBlueprint, functionName);
39
+ const readBlueprintTime = performance.now() - start;
40
+ const response = await invoke(resource, event, context);
41
+ const timings = { ...response.timings, 'blueprint:read': readBlueprintTime };
42
+ const timingHeaders = [];
43
+ for (const [key, value] of Object.entries(timings)) {
44
+ timingHeaders.push(`${key.replace(/:/g, '-')};dur=${Math.abs(value).toFixed(1)}`);
45
+ }
46
+ if (timingHeaders.length > 0) {
47
+ res.setHeader('Server-Timing', timingHeaders.join(', '));
48
+ }
38
49
  res.end(JSON.stringify(response));
39
50
  }
40
51
  catch (error) {
52
+ console.error(error);
41
53
  const response = { logs: '', error: '' };
42
54
  if (error instanceof Error) {
43
55
  response.logs = error.message;
@@ -112,4 +124,60 @@ const app = (port) => {
112
124
  const server = http.createServer(requestListener);
113
125
  server.listen(port, host, () => { });
114
126
  };
127
+ function parseInvokeRequest(body) {
128
+ let json;
129
+ try {
130
+ json = JSON.parse(body.toString('utf8'));
131
+ }
132
+ catch (error) {
133
+ throw new Error('Request body is not valid JSON', { cause: error });
134
+ }
135
+ if (!isRecord(json)) {
136
+ throw new Error('Request body is not valid, must be an object');
137
+ }
138
+ if (!('func' in json)) {
139
+ throw new Error('Request body is not valid, missing `func` field');
140
+ }
141
+ if (!('data' in json)) {
142
+ throw new Error('Request body is not valid, missing `data` field');
143
+ }
144
+ const { data, func } = json;
145
+ if (typeof func !== 'string') {
146
+ throw new Error('Request body is not valid, `func` field is not a string');
147
+ }
148
+ if (!isRecord(data)) {
149
+ throw new Error('Request body is not valid, `data` field is not an object');
150
+ }
151
+ const { context, event } = data;
152
+ if (!isRecord(context)) {
153
+ throw new Error('Request body is not valid, `context` field is not an object');
154
+ }
155
+ if (!isRecord(event)) {
156
+ throw new Error('Request body is not valid, `event` field is not an object');
157
+ }
158
+ if (!('clientOptions' in context)) {
159
+ throw new Error('Request body is not valid, `context.clientOptions` field is missing');
160
+ }
161
+ if (!isRecord(context.clientOptions)) {
162
+ throw new Error('Request body is not valid, `context.clientOptions` field is not an object');
163
+ }
164
+ const { projectId, dataset, apiVersion } = context.clientOptions;
165
+ if (typeof projectId !== 'string' && typeof projectId !== 'undefined') {
166
+ throw new Error('Request body is not valid, `context.clientOptions.projectId` field is not a string');
167
+ }
168
+ if (typeof dataset !== 'string' && typeof dataset !== 'undefined') {
169
+ throw new Error('Request body is not valid, `context.clientOptions.dataset` field is not a string');
170
+ }
171
+ if (typeof apiVersion !== 'string' && typeof apiVersion !== 'undefined') {
172
+ throw new Error('Request body is not valid, `context.clientOptions.apiVersion` field is not a string');
173
+ }
174
+ const clientOptions = {
175
+ ...context.clientOptions,
176
+ projectId,
177
+ dataset,
178
+ // Prefer `undefined` over empty string, triggering the right warnings in the client
179
+ apiVersion: apiVersion || undefined,
180
+ };
181
+ return { func, data: { context: { ...context, clientOptions }, event } };
182
+ }
115
183
  export default app;
@@ -30,12 +30,15 @@ function invoke(payloadText = '{}') {
30
30
  },
31
31
  method: 'POST',
32
32
  })
33
- .then((response) => response.json())
34
- .then((data) => {
33
+ .then((response) => {
34
+ return response.json().then((data) => ({data, timings: getServerTimings(response)}))
35
+ })
36
+ .then(({data, timings}) => {
35
37
  store.inprogress = false
36
38
  store.result = {
37
39
  ...data,
38
40
  time: Date.now() - start,
41
+ timings,
39
42
  }
40
43
  })
41
44
  }
@@ -47,7 +50,7 @@ function blueprint() {
47
50
  const functions = blueprint?.resources.filter((r) => r.type.startsWith('sanity.function.'))
48
51
 
49
52
  store.functions = functions
50
- store.selectedIndex = functions[0].src
53
+ store.selectedIndex = functions[0].name
51
54
  })
52
55
  .catch(() => {
53
56
  store.functions = []
@@ -77,3 +80,21 @@ function datasets(selectedProject) {
77
80
  store.datasets = []
78
81
  })
79
82
  }
83
+
84
+ function getServerTimings(response) {
85
+ const timings = {}
86
+ const serverTiming = response.headers.get('Server-Timing')
87
+ if (!serverTiming) {
88
+ return timings
89
+ }
90
+
91
+ for (const entry of serverTiming.split(',')) {
92
+ const [name, ...params] = entry.split(';')
93
+ const durationParam = params.find((p) => p.startsWith('dur='))
94
+ if (durationParam) {
95
+ timings[name.trim()] = Number.parseFloat(durationParam.slice(4))
96
+ }
97
+ }
98
+
99
+ return timings
100
+ }
@@ -60,6 +60,8 @@
60
60
  -webkit-font-smoothing: antialiased;
61
61
  -moz-osx-font-smoothing: grayscale;
62
62
  font-family: var(--font-family-sans);
63
+
64
+ /* Gutter Gradient Variables */
63
65
  }
64
66
 
65
67
  /* ==========================================================================
@@ -651,12 +653,19 @@
651
653
 
652
654
  html {
653
655
  color-scheme: light dark;
656
+ min-height: 100dvh;
657
+ max-height: 100dvh;
658
+ height: 100%;
659
+ overflow: hidden;
654
660
  }
655
661
 
656
662
  body {
657
663
  font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', 'Liberation Sans', Helvetica, Arial, system-ui, sans-serif;
658
664
  color: var(--text-color) !important;
659
- min-height: 100vh;
665
+ min-height: 100dvh;
666
+ max-height: 100dvh;
667
+ height: 100%;
668
+ overflow: hidden;
660
669
  display: grid;
661
670
  grid-template-areas:
662
671
  'header'
@@ -667,12 +676,6 @@ body {
667
676
  grid-template-rows: min-content min-content min-content 1fr;
668
677
  }
669
678
 
670
- body,
671
- html {
672
- height: 100%;
673
- overflow: hidden;
674
- }
675
-
676
679
  /* Utilities */
677
680
  .flex {
678
681
  display: flex;
@@ -770,21 +773,7 @@ body > main {
770
773
  grid-area: main;
771
774
  }
772
775
 
773
- @media (min-width: 40rem) {
774
- body {
775
- grid-template:
776
- 'header header' min-content
777
- 'filters filters' min-content
778
- 'left-sidebar main ' 1fr
779
- / minmax(auto, 320px) minmax(var(--layout-min-content-width, 16rem), 1fr);
780
- }
781
- }
782
-
783
776
  @media (max-width: 40rem) {
784
- button {
785
- width: 100%;
786
- }
787
-
788
777
  .block-lg {
789
778
  display: block !important;
790
779
  }
@@ -794,6 +783,19 @@ body > main {
794
783
  }
795
784
  }
796
785
 
786
+ @media (min-width: 40em) {
787
+ body {
788
+ grid-template:
789
+ 'header header' min-content
790
+ 'filters filters' min-content
791
+ 'left-sidebar main ' 1fr
792
+ / minmax(auto, 320px) 1fr;
793
+ }
794
+ .border-top-none-l {
795
+ border-top: 0;
796
+ }
797
+ }
798
+
797
799
  header {
798
800
  border-bottom: 1px solid var(--card-border-color);
799
801
  background: light-dark(white, var(--gray-950));
@@ -811,7 +813,7 @@ footer {
811
813
 
812
814
  .logo-image {
813
815
  height: 25px;
814
- width: auto;
816
+ width: 25px;
815
817
  }
816
818
 
817
819
  .logo-image img {
@@ -842,6 +844,7 @@ footer {
842
844
  line-height: 1;
843
845
  padding: 0px 12px !important;
844
846
  height: 24px !important;
847
+ max-width: 160px;
845
848
  }
846
849
 
847
850
  .sanity-button:hover {
@@ -930,7 +933,19 @@ m-tabs {
930
933
  }
931
934
 
932
935
  .gutter-gradient {
933
- background-image: linear-gradient(to right, light-dark(var(--gray-50), var(--gray-900)) 30px, light-dark(var(--gray-100), var(--gray-800)) 30.0001px, light-dark(var(--gray-100), var(--gray-800)) 31px, transparent 31px);
936
+ --gutter-width: 30px;
937
+ --gutter-border-width: 1px;
938
+
939
+ /* Light mode default gradient */
940
+ background-image: linear-gradient(to right, var(--gray-50) var(--gutter-width), var(--gray-100) var(--gutter-width), var(--gray-100) calc(var(--gutter-width) + var(--gutter-border-width)), transparent calc(var(--gutter-width) + var(--gutter-border-width)));
941
+ }
942
+
943
+ /* Apply dark mode gradient override */
944
+ @media (prefers-color-scheme: dark) {
945
+ .gutter-gradient {
946
+ background-image: linear-gradient(to right, var(--gray-900) var(--gutter-width), var(--gray-800) var(--gutter-width), var(--gray-800) calc(var(--gutter-width) + var(--gutter-border-width)), transparent calc(var(--gutter-width) + var(--gutter-border-width)));
947
+ }
948
+ /* Add other dark-mode specific overrides here if needed */
934
949
  }
935
950
 
936
951
  .bg {
@@ -960,6 +975,42 @@ m-tabs {
960
975
  background-color: light-dark(rgba(0, 0, 0, 0.025), rgba(255, 255, 255, 0.1)) !important;
961
976
  }
962
977
 
978
+ .slab-stat {
979
+ font-family: var(--font-family-mono);
980
+ display: flex;
981
+ align-items: center;
982
+ gap: 0 8px;
983
+ font-size: var(--font-size-text-2);
984
+ margin: 0;
985
+ padding: var(--space-3);
986
+ }
987
+
988
+ .slab-stat dt {
989
+ font-weight: 400;
990
+ text-transform: none;
991
+ margin: 0;
992
+ padding: 0;
993
+ }
994
+
995
+ .slab-stat dd {
996
+ font-weight: 700;
997
+ text-transform: none;
998
+ margin: 0;
999
+ padding: 0;
1000
+ }
1001
+
963
1002
  pre {
1003
+ color: var(--base-text-color) !important;
964
1004
  background-color: transparent !important;
965
1005
  }
1006
+
1007
+ time {
1008
+ font-family: var(--font-family-mono);
1009
+ }
1010
+
1011
+ response-panel,
1012
+ payload-panel {
1013
+ max-height: 100%;
1014
+ height: 100%;
1015
+ overflow: hidden;
1016
+ }
@@ -1,7 +1,7 @@
1
1
  class FiltersComponent extends HTMLElement {
2
2
  connectedCallback() {
3
3
  this.innerHTML = `
4
- <form style="display:flex; gap: 8px;padding-left: 12px; padding-bottom: 24px; border-bottom: 1px solid var(--card-border-color);">
4
+ <form style="display:flex; gap: 8px;padding-left: var(--space-3); padding-bottom: var(--space-3); border-bottom: 1px solid var(--card-border-color);">
5
5
  <dataset-dropdown></dataset-dropdown>
6
6
  <fieldset class="mar-t-sm">
7
7
  <label class="config-label">
@@ -2,14 +2,14 @@
2
2
  import {ApiBaseElement} from './api-base.js'
3
3
 
4
4
  const template = `<ol class="hidden-lg" type="content" style="padding:0 16px;"></ol>
5
- <fieldset class="pad-sm hidden block-lg"><select></select></fieldset>
5
+ <fieldset class="hidden block-lg" style="padding:0 var(--space-3); margin-bottom: var(--space-3);"><select></select></fieldset>
6
6
  `
7
7
 
8
8
  class FunctionList extends ApiBaseElement {
9
9
  functionClicked = (event) => {
10
10
  // eslint-disable-next-line unicorn/prefer-dom-node-text-content
11
11
  const target = this.api.store.functions.find((func) => func.name === event.srcElement.innerText)
12
- this.api.store.selectedIndex = target.src
12
+ this.api.store.selectedIndex = target.name
13
13
  }
14
14
  functionSelected = (event) => {
15
15
  this.api.store.selectedIndex = event.srcElement.value
@@ -18,14 +18,14 @@ class FunctionList extends ApiBaseElement {
18
18
  if (this.api.store.functions.length > 0) {
19
19
  this.list.innerHTML = this.api.store.functions
20
20
  .map((func) => {
21
- const selected = this.api.store.selectedIndex === func.src ? 'selected' : ''
21
+ const selected = this.api.store.selectedIndex === func.name ? 'selected' : ''
22
22
  return `<li class="function-list-item ${selected}" style="padding: 16px 24px;">${func.name}</li>`
23
23
  })
24
24
  .join('')
25
25
  this.select.innerHTML = this.api.store.functions
26
26
  .map((func) => {
27
- const selected = this.api.store.selectedIndex === func.src ? 'selected' : ''
28
- return `<option value="${func.src}" ${selected}>${func.name}</option>`
27
+ const selected = this.api.store.selectedIndex === func.name ? 'selected' : ''
28
+ return `<option value="${func.name}" ${selected}>${func.name}</option>`
29
29
  })
30
30
  .join('')
31
31
  } else {