@sanity/runtime-cli 1.1.2 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -20,7 +20,7 @@ $ npm install -g @sanity/runtime-cli
20
20
  $ sanity COMMAND
21
21
  running command...
22
22
  $ sanity (--version)
23
- @sanity/runtime-cli/1.1.2 linux-x64 node-v22.14.0
23
+ @sanity/runtime-cli/1.2.1 linux-x64 node-v22.14.0
24
24
  $ sanity --help [COMMAND]
25
25
  USAGE
26
26
  $ sanity COMMAND
@@ -29,6 +29,9 @@ USAGE
29
29
  <!-- usagestop -->
30
30
  # Commands
31
31
  <!-- commands -->
32
+ * [`sanity blueprints deploy`](#sanity-blueprints-deploy)
33
+ * [`sanity blueprints info`](#sanity-blueprints-info)
34
+ * [`sanity blueprints plan`](#sanity-blueprints-plan)
32
35
  * [`sanity functions dev`](#sanity-functions-dev)
33
36
  * [`sanity functions invoke ID`](#sanity-functions-invoke-id)
34
37
  * [`sanity functions logs ID`](#sanity-functions-logs-id)
@@ -45,6 +48,57 @@ USAGE
45
48
  * [`sanity plugins unlink [PLUGIN]`](#sanity-plugins-unlink-plugin)
46
49
  * [`sanity plugins update`](#sanity-plugins-update)
47
50
 
51
+ ## `sanity blueprints deploy`
52
+
53
+ Deploy a Blueprint
54
+
55
+ ```
56
+ USAGE
57
+ $ sanity blueprints deploy
58
+
59
+ DESCRIPTION
60
+ Deploy a Blueprint
61
+
62
+ EXAMPLES
63
+ $ sanity blueprints deploy
64
+ ```
65
+
66
+ _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v1.2.1/src/commands/blueprints/deploy.ts)_
67
+
68
+ ## `sanity blueprints info`
69
+
70
+ Show information about a Blueprint
71
+
72
+ ```
73
+ USAGE
74
+ $ sanity blueprints info
75
+
76
+ DESCRIPTION
77
+ Show information about a Blueprint
78
+
79
+ EXAMPLES
80
+ $ sanity blueprints info
81
+ ```
82
+
83
+ _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v1.2.1/src/commands/blueprints/info.ts)_
84
+
85
+ ## `sanity blueprints plan`
86
+
87
+ Enumerate resources to be deployed - will not modify any resources
88
+
89
+ ```
90
+ USAGE
91
+ $ sanity blueprints plan
92
+
93
+ DESCRIPTION
94
+ Enumerate resources to be deployed - will not modify any resources
95
+
96
+ EXAMPLES
97
+ $ sanity blueprints plan
98
+ ```
99
+
100
+ _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v1.2.1/src/commands/blueprints/plan.ts)_
101
+
48
102
  ## `sanity functions dev`
49
103
 
50
104
  Start the Sanity Function emulator
@@ -63,7 +117,7 @@ EXAMPLES
63
117
  $ sanity functions dev --port 8974
64
118
  ```
65
119
 
66
- _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v1.1.2/src/commands/functions/dev.ts)_
120
+ _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v1.2.1/src/commands/functions/dev.ts)_
67
121
 
68
122
  ## `sanity functions invoke ID`
69
123
 
@@ -89,7 +143,7 @@ EXAMPLES
89
143
  $ sanity functions invoke <ID> --file 'payload.json'
90
144
  ```
91
145
 
92
- _See code: [src/commands/functions/invoke.ts](https://github.com/sanity-io/runtime-cli/blob/v1.1.2/src/commands/functions/invoke.ts)_
146
+ _See code: [src/commands/functions/invoke.ts](https://github.com/sanity-io/runtime-cli/blob/v1.2.1/src/commands/functions/invoke.ts)_
93
147
 
94
148
  ## `sanity functions logs ID`
95
149
 
@@ -109,7 +163,7 @@ EXAMPLES
109
163
  $ sanity functions logs <ID>
110
164
  ```
111
165
 
112
- _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v1.1.2/src/commands/functions/logs.ts)_
166
+ _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v1.2.1/src/commands/functions/logs.ts)_
113
167
 
114
168
  ## `sanity functions test PATH`
115
169
 
@@ -138,7 +192,7 @@ EXAMPLES
138
192
  $ sanity functions test ./test.ts --data '{ "id": 1 }' --timeout 60
139
193
  ```
140
194
 
141
- _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v1.1.2/src/commands/functions/test.ts)_
195
+ _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v1.2.1/src/commands/functions/test.ts)_
142
196
 
143
197
  ## `sanity help [COMMAND]`
144
198
 
@@ -158,7 +212,7 @@ DESCRIPTION
158
212
  Display help for sanity.
159
213
  ```
160
214
 
161
- _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v6.2.25/src/commands/help.ts)_
215
+ _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v6.2.26/src/commands/help.ts)_
162
216
 
163
217
  ## `sanity plugins`
164
218
 
@@ -181,7 +235,7 @@ EXAMPLES
181
235
  $ sanity plugins
182
236
  ```
183
237
 
184
- _See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.31/src/commands/plugins/index.ts)_
238
+ _See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.34/src/commands/plugins/index.ts)_
185
239
 
186
240
  ## `sanity plugins add PLUGIN`
187
241
 
@@ -255,7 +309,7 @@ EXAMPLES
255
309
  $ sanity plugins inspect myplugin
256
310
  ```
257
311
 
258
- _See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.31/src/commands/plugins/inspect.ts)_
312
+ _See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.34/src/commands/plugins/inspect.ts)_
259
313
 
260
314
  ## `sanity plugins install PLUGIN`
261
315
 
@@ -304,7 +358,7 @@ EXAMPLES
304
358
  $ sanity plugins install someuser/someplugin
305
359
  ```
306
360
 
307
- _See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.31/src/commands/plugins/install.ts)_
361
+ _See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.34/src/commands/plugins/install.ts)_
308
362
 
309
363
  ## `sanity plugins link PATH`
310
364
 
@@ -335,7 +389,7 @@ EXAMPLES
335
389
  $ sanity plugins link myplugin
336
390
  ```
337
391
 
338
- _See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.31/src/commands/plugins/link.ts)_
392
+ _See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.34/src/commands/plugins/link.ts)_
339
393
 
340
394
  ## `sanity plugins remove [PLUGIN]`
341
395
 
@@ -376,7 +430,7 @@ FLAGS
376
430
  --reinstall Reinstall all plugins after uninstalling.
377
431
  ```
378
432
 
379
- _See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.31/src/commands/plugins/reset.ts)_
433
+ _See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.34/src/commands/plugins/reset.ts)_
380
434
 
381
435
  ## `sanity plugins uninstall [PLUGIN]`
382
436
 
@@ -404,7 +458,7 @@ EXAMPLES
404
458
  $ sanity plugins uninstall myplugin
405
459
  ```
406
460
 
407
- _See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.31/src/commands/plugins/uninstall.ts)_
461
+ _See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.34/src/commands/plugins/uninstall.ts)_
408
462
 
409
463
  ## `sanity plugins unlink [PLUGIN]`
410
464
 
@@ -448,7 +502,7 @@ DESCRIPTION
448
502
  Update installed plugins.
449
503
  ```
450
504
 
451
- _See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.31/src/commands/plugins/update.ts)_
505
+ _See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.34/src/commands/plugins/update.ts)_
452
506
  <!-- commandsstop -->
453
507
  * [`sanity functions dev`](#sanity-functions-dev)
454
508
  * [`sanity functions invoke`](#sanity-functions-invoke)
@@ -0,0 +1,2 @@
1
+ import type { Blueprint } from '../../utils/types.js';
2
+ export default function readBlueprint(): Blueprint;
@@ -0,0 +1,17 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { cwd } from 'node:process';
4
+ export default function readBlueprint() {
5
+ try {
6
+ const blueprintPath = join(cwd(), 'blueprint.json');
7
+ if (!existsSync(blueprintPath)) {
8
+ throw Error('Could not find blueprint.json');
9
+ }
10
+ const readBlueprint = readFileSync(blueprintPath, 'utf8').toString();
11
+ const blueprintJson = JSON.parse(readBlueprint);
12
+ return blueprintJson;
13
+ }
14
+ catch (err) {
15
+ throw Error('Unable to parse blueprint.json');
16
+ }
17
+ }
@@ -0,0 +1,22 @@
1
+ import type { BlueprintJob } from '../../utils/types.js';
2
+ declare function listStacks(): Promise<{
3
+ ok: boolean;
4
+ error: any;
5
+ stacks: any;
6
+ }>;
7
+ declare function getStack(stackId: string): Promise<{
8
+ ok: boolean;
9
+ error: any;
10
+ stack: any;
11
+ }>;
12
+ declare function createStack(blueprint: BlueprintJob): Promise<{
13
+ ok: boolean;
14
+ error: any;
15
+ stack: any;
16
+ }>;
17
+ declare function updateStack(stackId: string, blueprint: BlueprintJob): Promise<{
18
+ ok: boolean;
19
+ error: any;
20
+ stack: any;
21
+ }>;
22
+ export { listStacks, getStack, createStack, updateStack };
@@ -0,0 +1,57 @@
1
+ import config from '../../config.js';
2
+ const { blueprints } = config.server;
3
+ const HEADERS = {
4
+ Authorization: `Bearer ${config.token}`,
5
+ 'Content-Type': 'application/json',
6
+ };
7
+ async function listStacks() {
8
+ const response = await fetch(`${blueprints}/vX/stacks`, {
9
+ method: 'GET',
10
+ headers: HEADERS,
11
+ });
12
+ const stacks = await response.json();
13
+ return {
14
+ ok: response.ok,
15
+ error: response.ok ? null : stacks.message,
16
+ stacks,
17
+ };
18
+ }
19
+ async function getStack(stackId) {
20
+ const response = await fetch(`${blueprints}/vX/stacks/${stackId}`, {
21
+ method: 'GET',
22
+ headers: HEADERS,
23
+ });
24
+ const stack = await response.json();
25
+ return {
26
+ ok: response.ok,
27
+ error: response.ok ? null : stack.message,
28
+ stack,
29
+ };
30
+ }
31
+ async function createStack(blueprint) {
32
+ const response = await fetch(`${blueprints}/vX/stacks`, {
33
+ method: 'POST',
34
+ headers: HEADERS,
35
+ body: JSON.stringify(blueprint),
36
+ });
37
+ const stack = await response.json();
38
+ return {
39
+ ok: response.ok,
40
+ error: response.ok ? null : stack.message,
41
+ stack,
42
+ };
43
+ }
44
+ async function updateStack(stackId, blueprint) {
45
+ const response = await fetch(`${blueprints}/vX/stacks/${stackId}`, {
46
+ method: 'PUT',
47
+ headers: HEADERS,
48
+ body: JSON.stringify(blueprint),
49
+ });
50
+ const stack = await response.json();
51
+ return {
52
+ ok: response.ok,
53
+ error: response.ok ? null : stack.message,
54
+ stack,
55
+ };
56
+ }
57
+ export { listStacks, getStack, createStack, updateStack };
@@ -0,0 +1,10 @@
1
+ import type { BlueprintResource } from '../../utils/types.js';
2
+ export declare function stashAsset(resource: BlueprintResource): Promise<{
3
+ success: boolean;
4
+ assetId: any;
5
+ error?: undefined;
6
+ } | {
7
+ success: boolean;
8
+ error: any;
9
+ assetId?: undefined;
10
+ }>;
@@ -0,0 +1,42 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { cwd } from 'node:process';
4
+ import JSZip from 'jszip';
5
+ import config from '../../config.js';
6
+ const { blueprints } = config.server;
7
+ export async function stashAsset(resource) {
8
+ try {
9
+ // Read the function source
10
+ const source = await fs.readFileSync(path.join(cwd(), resource.src), 'utf8');
11
+ // Create zip with function source
12
+ const zip = new JSZip();
13
+ zip.file('index.js', source);
14
+ const zipBuffer = await zip.generateAsync({ type: 'nodebuffer' });
15
+ const base64Zip = zipBuffer.toString('base64');
16
+ // Stash the asset
17
+ const assetResponse = await fetch(`${blueprints}/assets/stash`, {
18
+ method: 'POST',
19
+ headers: {
20
+ Authorization: `Bearer ${config.token}`,
21
+ 'Content-Type': 'application/json',
22
+ },
23
+ body: JSON.stringify({
24
+ file: base64Zip,
25
+ filename: `${resource.name}.zip`,
26
+ }),
27
+ });
28
+ const assetJson = await assetResponse.json();
29
+ console.debug('ASSET RESPONSE:', assetJson);
30
+ if (assetResponse.ok) {
31
+ return { success: true, assetId: assetJson.id };
32
+ }
33
+ return { success: false, error: assetJson.message || 'Unknown error' };
34
+ }
35
+ catch (err) {
36
+ let error = '';
37
+ if (err instanceof Error) {
38
+ error = err.message;
39
+ }
40
+ return { success: false, error };
41
+ }
42
+ }
@@ -1,5 +1,4 @@
1
- import { serve } from '@hono/node-server';
2
1
  import app from '../../server/app.js';
3
2
  export async function dev(port) {
4
- serve({ fetch: app.fetch, port: Number(port) });
3
+ app(Number(port));
5
4
  }
@@ -0,0 +1,6 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Deploy extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ run(): Promise<void>;
6
+ }
@@ -0,0 +1,70 @@
1
+ import { Command } from '@oclif/core';
2
+ import readBlueprint from '../../actions/blueprints/read-blueprint.js';
3
+ import { createStack, listStacks, updateStack } from '../../actions/blueprints/stacks.js';
4
+ import { stashAsset } from '../../actions/blueprints/stash-asset.js';
5
+ import Spinner from '../../utils/spinner.js';
6
+ export default class Deploy extends Command {
7
+ static description = 'Deploy a Blueprint';
8
+ static examples = ['<%= config.bin %> <%= command.id %>'];
9
+ async run() {
10
+ const { displayName, name, projectId, resources } = readBlueprint();
11
+ const s = new Spinner();
12
+ s.start('Deploying blueprint...');
13
+ const functionResources = resources.filter((r) => r.kind === 'function');
14
+ // First stash all function assets
15
+ if (functionResources.length > 0) {
16
+ s.stop('Preparing functions...');
17
+ for (const resource of functionResources) {
18
+ const fnSpinner = new Spinner();
19
+ fnSpinner.start(`Processing ${resource.name}...`);
20
+ const result = await stashAsset(resource);
21
+ if (result.success) {
22
+ resource.assetId = result.assetId;
23
+ fnSpinner.stop(`✓ ${resource.name} (${result.assetId})`);
24
+ this.log(` Source: ${resource.src}`);
25
+ }
26
+ else {
27
+ fnSpinner.stop(`✗ Failed to process ${resource.name}`);
28
+ this.log(` Error: ${result.error}`);
29
+ return;
30
+ }
31
+ }
32
+ this.log('│');
33
+ s.start('Looking for existing stack...');
34
+ }
35
+ // Check for existing stack
36
+ const { ok: stacksOk, stacks, error: stacksError } = await listStacks();
37
+ if (!stacksOk) {
38
+ s.stop('Failed to list stacks');
39
+ this.log(`Error: ${stacksError || 'Unknown error'}`);
40
+ return;
41
+ }
42
+ const existingStack = stacks.find((st) => st.name === name);
43
+ const blueprint = {
44
+ name,
45
+ projectId,
46
+ document: {
47
+ resources: resources.map((r) => {
48
+ if (r.kind === 'function') {
49
+ return { ...r, src: r.assetId };
50
+ }
51
+ return r;
52
+ }),
53
+ },
54
+ };
55
+ this.debug('BLUEPRINT DOCUMENT:', blueprint);
56
+ // Create or update stack
57
+ const { ok, stack, error } = existingStack
58
+ ? await updateStack(existingStack.id, blueprint)
59
+ : await createStack(blueprint);
60
+ this.debug('STACK RESPONSE:', stack);
61
+ if (ok) {
62
+ s.stop(`Stack ${existingStack ? 'updated' : 'created'}! (${stack.id})`);
63
+ this.log('Use `blueprints info` to check deployment status');
64
+ }
65
+ else {
66
+ s.stop(`Failed to ${existingStack ? 'update' : 'create'} stack`);
67
+ this.log(`Error: ${error || 'Unknown error'}`);
68
+ }
69
+ }
70
+ }
@@ -0,0 +1,6 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Info extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ run(): Promise<void>;
6
+ }
@@ -0,0 +1,71 @@
1
+ import { Command } from '@oclif/core';
2
+ import readBlueprint from '../../actions/blueprints/read-blueprint.js';
3
+ import { getStack, listStacks } from '../../actions/blueprints/stacks.js';
4
+ import Spinner from '../../utils/spinner.js';
5
+ export default class Info extends Command {
6
+ static description = 'Show information about a Blueprint';
7
+ static examples = ['<%= config.bin %> <%= command.id %>'];
8
+ async run() {
9
+ const { displayName, name, projectId, resources } = readBlueprint();
10
+ const s = new Spinner();
11
+ if (!projectId) {
12
+ this.log(`No project for Blueprint "${displayName}"`);
13
+ return;
14
+ }
15
+ s.start(`Retrieving info for "${displayName}"`);
16
+ try {
17
+ const { ok, stacks, error } = await listStacks();
18
+ if (!ok) {
19
+ s.stop('Failed to retrieve stacks');
20
+ this.log(`Error: ${error || 'Unknown error'}`);
21
+ return;
22
+ }
23
+ if (stacks.length === 0) {
24
+ s.stop(`No stacks found for Blueprint "${displayName}"`);
25
+ return;
26
+ }
27
+ const foundStack = stacks.find((st) => st.name === name);
28
+ const { ok: stackOk, stack, error: stackError } = await getStack(foundStack.id);
29
+ if (!stackOk) {
30
+ s.stop('Failed to retrieve stack');
31
+ this.log(`Error: ${stackError || 'Unknown error'}`);
32
+ return;
33
+ }
34
+ s.stop(`Stack "${stack.name}" (${stack.recentOperation.status})`);
35
+ // Show resources
36
+ if (stack?.resources) {
37
+ const { resources: stackResources } = stack;
38
+ const functionResources = stackResources.filter((r) => r.kind === 'function');
39
+ const otherResources = stackResources.filter((r) => r.kind !== 'function');
40
+ if (functionResources.length > 0) {
41
+ this.log(`├─ Functions (${functionResources.length})`);
42
+ for (const fn of functionResources) {
43
+ this.log(`│ ├─ ${fn.name} <${fn.externalId}>`);
44
+ }
45
+ }
46
+ if (otherResources.length > 0) {
47
+ this.log(`└─Other Resources (${otherResources.length})`);
48
+ for (const other of otherResources) {
49
+ this.log(` ├─ ${other.name || other.src}`);
50
+ }
51
+ }
52
+ }
53
+ // Show operation details if not completed
54
+ if (stack.recentOperation.status !== 'COMPLETED') {
55
+ this.log('\nOperation Details:');
56
+ if (stack.recentOperation.error) {
57
+ this.log(`Error: ${stack.recentOperation.error}`);
58
+ }
59
+ if (stack.recentOperation.progress) {
60
+ this.log(`Progress: ${stack.recentOperation.progress}`);
61
+ }
62
+ }
63
+ }
64
+ catch (err) {
65
+ s.stop('Failed to retrieve info');
66
+ if (err instanceof Error) {
67
+ this.log(`Error: ${err.message}`);
68
+ }
69
+ }
70
+ }
71
+ }
@@ -0,0 +1,6 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Plan extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ run(): Promise<void>;
6
+ }
@@ -0,0 +1,31 @@
1
+ import { Command } from '@oclif/core';
2
+ import readBlueprint from '../../actions/blueprints/read-blueprint.js';
3
+ export default class Plan extends Command {
4
+ static description = 'Enumerate resources to be deployed - will not modify any resources';
5
+ static examples = ['<%= config.bin %> <%= command.id %>'];
6
+ async run() {
7
+ const { displayName, resources } = readBlueprint();
8
+ this.log(`Blueprint: "${displayName}"`);
9
+ const functionResources = resources.filter((r) => r.kind === 'function');
10
+ const otherResources = resources.filter((r) => r.kind === 'test');
11
+ this.log('Tracked resources:');
12
+ if (functionResources.length > 0) {
13
+ this.log(`├─ Functions (${functionResources.length})`);
14
+ for (const fn of functionResources) {
15
+ this.log(`│ ├─ ${fn.name}: ${fn.src}`);
16
+ }
17
+ }
18
+ if (otherResources.length > 0) {
19
+ this.log(`└─Other (${otherResources.length})`);
20
+ for (const other of otherResources) {
21
+ this.log(` ├─ ${other.name}`);
22
+ }
23
+ }
24
+ if (resources.length === 0) {
25
+ this.log('No resources to deploy');
26
+ }
27
+ else {
28
+ this.log('\nRun `blueprints deploy` to deploy these changes');
29
+ }
30
+ }
31
+ }
package/dist/config.d.ts CHANGED
@@ -2,6 +2,7 @@ declare const _default: {
2
2
  token: string;
3
3
  server: {
4
4
  functions: URL;
5
+ blueprints: URL;
5
6
  };
6
7
  };
7
8
  export default _default;
package/dist/config.js CHANGED
@@ -8,10 +8,17 @@ const functionsUrls = {
8
8
  staging: 'https://api.sanity.work/',
9
9
  default: 'http://localhost:4567',
10
10
  };
11
+ const blueprintsUrls = {
12
+ production: 'https://api.sanity.io/',
13
+ staging: 'https://api.sanity.work/',
14
+ default: 'http://localhost:4567',
15
+ };
11
16
  const functionsUrl = new URL(functionsUrls[nodeEnv] ?? functionsUrls.default);
17
+ const blueprintsUrl = new URL(blueprintsUrls[nodeEnv] ?? blueprintsUrls.default);
12
18
  export default {
13
19
  token: isDev ? 'token' : getToken(isProd),
14
20
  server: {
15
21
  functions: functionsUrl,
22
+ blueprints: blueprintsUrl,
16
23
  },
17
24
  };
@@ -1,3 +1,2 @@
1
- import { Hono } from 'hono';
2
- declare const app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
1
+ declare const app: (port: number) => void;
3
2
  export default app;
@@ -1,36 +1,66 @@
1
- import { readFileSync } from 'node:fs';
1
+ import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { cwd } from 'node:process';
4
- import { serveStatic } from '@hono/node-server/serve-static';
5
- import { Hono } from 'hono';
4
+ import { default as mime } from 'mime-types';
6
5
  import invoke from '../utils/invoke-local.js';
7
- import isDependency from '../utils/is-dependency.js';
8
- function errorResponse(code, message, details = {}) {
9
- return { error: { code, details, message } };
10
- }
11
- function getStaticPath() {
12
- return isDependency('./server/static')
13
- ? './node_modules/@sanity/runtime-cli/dist/server/static'
14
- : './src/server/static';
15
- }
16
- const app = new Hono();
17
- app.use('*', serveStatic({ root: getStaticPath() }));
18
- app.get('/blueprint', async (c) => {
19
- const response = JSON.parse(readFileSync(join(cwd(), './blueprints.json')).toString());
20
- return c.json(response);
21
- });
22
- app.post('/invoke', async (c) => {
23
- const { data = {}, func } = await c.req.json();
24
- const response = await invoke(func, { data: JSON.parse(data) });
25
- return c.json(response);
26
- });
27
- app
28
- .notFound((c) => c.json(errorResponse('NOT_FOUND', 'Not Found', {
29
- method: c.req.method,
30
- path: c.req.path,
31
- }), 404))
32
- .onError((err, c) => c.json(errorResponse('INTERNAL_SERVER_ERROR', 'Internal Server Error', {
33
- error: err.message,
34
- stack: err,
35
- }), 500));
6
+ import * as http from 'node:http';
7
+ const host = 'localhost';
8
+ const app = (port) => {
9
+ const requestListener = async (req, res) => {
10
+ res.setHeader('Content-Type', 'application/json');
11
+ switch (req.url) {
12
+ case '/blueprint': {
13
+ res.setHeader('Content-Type', 'application/json');
14
+ const blueprint = readFileSync(join(cwd(), './blueprint.json')).toString();
15
+ res.writeHead(200);
16
+ res.end(blueprint);
17
+ break;
18
+ }
19
+ case '/invoke': {
20
+ if (req.method === 'POST') {
21
+ let body = '';
22
+ req.on('data', (data) => {
23
+ body += data;
24
+ });
25
+ req.on('end', async () => {
26
+ const { data, func } = JSON.parse(body);
27
+ res.setHeader('Content-Type', 'application/json');
28
+ try {
29
+ const response = await invoke(func, { data: JSON.parse(data) });
30
+ res.writeHead(200);
31
+ res.end(JSON.stringify(response));
32
+ }
33
+ catch (error) {
34
+ const response = { logs: '', error: '' };
35
+ if (error instanceof Error) {
36
+ response.logs = error.message;
37
+ }
38
+ res.writeHead(200);
39
+ res.end(JSON.stringify(response));
40
+ }
41
+ });
42
+ }
43
+ break;
44
+ }
45
+ default: {
46
+ const requestPath = req.url?.endsWith('/') ? `${req.url}index.html` : req.url;
47
+ const filePath = new URL(`./static${requestPath}`, import.meta.url).pathname;
48
+ if (existsSync(filePath)) {
49
+ const mimeType = mime.lookup(filePath) || 'text/plain';
50
+ res.setHeader('Content-Type', mimeType);
51
+ const content = readFileSync(filePath).toString();
52
+ res.writeHead(200);
53
+ res.end(content);
54
+ }
55
+ else {
56
+ res.writeHead(404);
57
+ res.end();
58
+ }
59
+ break;
60
+ }
61
+ }
62
+ };
63
+ const server = http.createServer(requestListener);
64
+ server.listen(port, host, () => { });
65
+ };
36
66
  export default app;
@@ -8787,14 +8787,14 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
8787
8787
  let moveX = 0, moveY = 0;
8788
8788
  if (y == "nearest") {
8789
8789
  if (rect.top < bounding.top) {
8790
- moveY = -(bounding.top - rect.top + yMargin);
8790
+ moveY = rect.top - (bounding.top + yMargin);
8791
8791
  if (side > 0 && rect.bottom > bounding.bottom + moveY)
8792
- moveY = rect.bottom - bounding.bottom + moveY + yMargin;
8792
+ moveY = rect.bottom - bounding.bottom + yMargin;
8793
8793
  }
8794
8794
  else if (rect.bottom > bounding.bottom) {
8795
8795
  moveY = rect.bottom - bounding.bottom + yMargin;
8796
8796
  if (side < 0 && (rect.top - moveY) < bounding.top)
8797
- moveY = -(bounding.top + moveY - rect.top + yMargin);
8797
+ moveY = rect.top - (bounding.top + yMargin);
8798
8798
  }
8799
8799
  }
8800
8800
  else {
@@ -8806,14 +8806,14 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
8806
8806
  }
8807
8807
  if (x == "nearest") {
8808
8808
  if (rect.left < bounding.left) {
8809
- moveX = -(bounding.left - rect.left + xMargin);
8809
+ moveX = rect.left - (bounding.left + xMargin);
8810
8810
  if (side > 0 && rect.right > bounding.right + moveX)
8811
- moveX = rect.right - bounding.right + moveX + xMargin;
8811
+ moveX = rect.right - bounding.right + xMargin;
8812
8812
  }
8813
8813
  else if (rect.right > bounding.right) {
8814
8814
  moveX = rect.right - bounding.right + xMargin;
8815
8815
  if (side < 0 && rect.left < bounding.left + moveX)
8816
- moveX = -(bounding.left + moveX - rect.left + xMargin);
8816
+ moveX = rect.left - (bounding.left + xMargin);
8817
8817
  }
8818
8818
  }
8819
8819
  else {
@@ -8848,6 +8848,10 @@ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
8848
8848
  }
8849
8849
  if (top)
8850
8850
  break;
8851
+ if (rect.top < bounding.top || rect.bottom > bounding.bottom ||
8852
+ rect.left < bounding.left || rect.right > bounding.right)
8853
+ rect = { left: Math.max(rect.left, bounding.left), right: Math.min(rect.right, bounding.right),
8854
+ top: Math.max(rect.top, bounding.top), bottom: Math.min(rect.bottom, bounding.bottom) };
8851
8855
  cur = cur.assignedSlot || cur.parentNode;
8852
8856
  }
8853
8857
  else if (cur.nodeType == 11) { // A shadow root
@@ -23360,7 +23364,15 @@ function createLineDialog(view) {
23360
23364
  event.preventDefault();
23361
23365
  go();
23362
23366
  }
23363
- }, crelt("label", view.state.phrase("Go to line"), ": ", input), " ", crelt("button", { class: "cm-button", type: "submit" }, view.state.phrase("go")));
23367
+ }, crelt("label", view.state.phrase("Go to line"), ": ", input), " ", crelt("button", { class: "cm-button", type: "submit" }, view.state.phrase("go")), crelt("button", {
23368
+ name: "close",
23369
+ onclick: () => {
23370
+ view.dispatch({ effects: dialogEffect.of(false) });
23371
+ view.focus();
23372
+ },
23373
+ "aria-label": view.state.phrase("close"),
23374
+ type: "button"
23375
+ }, ["×"]));
23364
23376
  function go() {
23365
23377
  let match = /^([+-])?(\d+)?(:\d+)?(%)?$/.exec(input.value);
23366
23378
  if (!match)
@@ -23424,7 +23436,17 @@ const gotoLine = view => {
23424
23436
  const baseTheme$1$1 = /*@__PURE__*/EditorView.baseTheme({
23425
23437
  ".cm-panel.cm-gotoLine": {
23426
23438
  padding: "2px 6px 4px",
23427
- "& label": { fontSize: "80%" }
23439
+ position: "relative",
23440
+ "& label": { fontSize: "80%" },
23441
+ "& [name=close]": {
23442
+ position: "absolute",
23443
+ top: "0", bottom: "0",
23444
+ right: "4px",
23445
+ backgroundColor: "inherit",
23446
+ border: "none",
23447
+ font: "inherit",
23448
+ padding: "0"
23449
+ }
23428
23450
  }
23429
23451
  });
23430
23452
 
@@ -1,12 +1,8 @@
1
1
  import { spawn } from 'node:child_process';
2
- import { join } from 'node:path';
3
2
  import { cwd } from 'node:process';
4
3
  import { setTimeout } from 'node:timers';
5
- import isDependency from './is-dependency.js';
6
4
  function getChildProcessWrapperPath() {
7
- return isDependency('./utils/child-process-wrapper.js')
8
- ? join(cwd(), './node_modules/@sanity/runtime-cli/dist/utils/child-process-wrapper.js')
9
- : join(cwd(), './src/utils/child-process-wrapper.js');
5
+ return new URL('./child-process-wrapper.js', import.meta.url).pathname;
10
6
  }
11
7
  export default async function invoke(srcPath, payload, timeout = 5) {
12
8
  return new Promise((resolve, reject) => {
@@ -0,0 +1,9 @@
1
+ export default class Spinner {
2
+ frames: Array<string>;
3
+ interval: undefined | ReturnType<typeof setInterval>;
4
+ currentFrame: number;
5
+ text: string;
6
+ constructor();
7
+ start(message: string): void;
8
+ stop(message: string): void;
9
+ }
@@ -0,0 +1,25 @@
1
+ export default class Spinner {
2
+ frames;
3
+ interval;
4
+ currentFrame;
5
+ text;
6
+ constructor() {
7
+ this.frames = ['✶', '✸', '✹', '✺', '✹', '✷'];
8
+ this.interval = undefined;
9
+ this.currentFrame = 0;
10
+ this.text = '';
11
+ }
12
+ start(message) {
13
+ this.text = message;
14
+ this.interval = setInterval(() => {
15
+ const frame = this.frames[this.currentFrame];
16
+ process.stdout.write(`\r${frame} ${this.text}`);
17
+ this.currentFrame = (this.currentFrame + 1) % this.frames.length;
18
+ }, 80);
19
+ }
20
+ stop(message) {
21
+ clearInterval(this.interval);
22
+ process.stdout.write(`\r${' '.repeat(this.text.length + 3)}`);
23
+ process.stdout.write(`\r${message}\n`);
24
+ }
25
+ }
@@ -14,3 +14,51 @@ export interface InvocationResponse {
14
14
  json: object | undefined;
15
15
  logs: string | undefined;
16
16
  }
17
+ type LogFunction = (input: string) => void;
18
+ /**
19
+ * @internal
20
+ */
21
+ export interface BlueprintsContext {
22
+ log: LogFunction;
23
+ }
24
+ /**
25
+ * @internal
26
+ */
27
+ export interface Blueprint {
28
+ displayName: string;
29
+ name: string;
30
+ projectId: string;
31
+ resources: Array<BlueprintResource>;
32
+ }
33
+ /**
34
+ * @internal
35
+ */
36
+ export interface BlueprintDocument {
37
+ resources: Array<BlueprintResource>;
38
+ }
39
+ /**
40
+ * @internal
41
+ */
42
+ export interface BlueprintJob {
43
+ name: string;
44
+ projectId: string;
45
+ document: BlueprintDocument;
46
+ }
47
+ /**
48
+ * @internal
49
+ */
50
+ export interface BlueprintResource {
51
+ name: string;
52
+ kind: string;
53
+ src: string;
54
+ assetId: string;
55
+ }
56
+ /**
57
+ * @internal
58
+ */
59
+ export interface BlueprintStack {
60
+ name: string;
61
+ kind: string;
62
+ src: string;
63
+ }
64
+ export {};
@@ -1,5 +1,77 @@
1
1
  {
2
2
  "commands": {
3
+ "blueprints:deploy": {
4
+ "aliases": [],
5
+ "args": {},
6
+ "description": "Deploy a Blueprint",
7
+ "examples": [
8
+ "<%= config.bin %> <%= command.id %>"
9
+ ],
10
+ "flags": {},
11
+ "hasDynamicHelp": false,
12
+ "hiddenAliases": [],
13
+ "id": "blueprints:deploy",
14
+ "pluginAlias": "@sanity/runtime-cli",
15
+ "pluginName": "@sanity/runtime-cli",
16
+ "pluginType": "core",
17
+ "strict": true,
18
+ "enableJsonFlag": false,
19
+ "isESM": true,
20
+ "relativePath": [
21
+ "dist",
22
+ "commands",
23
+ "blueprints",
24
+ "deploy.js"
25
+ ]
26
+ },
27
+ "blueprints:info": {
28
+ "aliases": [],
29
+ "args": {},
30
+ "description": "Show information about a Blueprint",
31
+ "examples": [
32
+ "<%= config.bin %> <%= command.id %>"
33
+ ],
34
+ "flags": {},
35
+ "hasDynamicHelp": false,
36
+ "hiddenAliases": [],
37
+ "id": "blueprints:info",
38
+ "pluginAlias": "@sanity/runtime-cli",
39
+ "pluginName": "@sanity/runtime-cli",
40
+ "pluginType": "core",
41
+ "strict": true,
42
+ "enableJsonFlag": false,
43
+ "isESM": true,
44
+ "relativePath": [
45
+ "dist",
46
+ "commands",
47
+ "blueprints",
48
+ "info.js"
49
+ ]
50
+ },
51
+ "blueprints:plan": {
52
+ "aliases": [],
53
+ "args": {},
54
+ "description": "Enumerate resources to be deployed - will not modify any resources",
55
+ "examples": [
56
+ "<%= config.bin %> <%= command.id %>"
57
+ ],
58
+ "flags": {},
59
+ "hasDynamicHelp": false,
60
+ "hiddenAliases": [],
61
+ "id": "blueprints:plan",
62
+ "pluginAlias": "@sanity/runtime-cli",
63
+ "pluginName": "@sanity/runtime-cli",
64
+ "pluginType": "core",
65
+ "strict": true,
66
+ "enableJsonFlag": false,
67
+ "isESM": true,
68
+ "relativePath": [
69
+ "dist",
70
+ "commands",
71
+ "blueprints",
72
+ "plan.js"
73
+ ]
74
+ },
3
75
  "functions:dev": {
4
76
  "aliases": [],
5
77
  "args": {},
@@ -175,5 +247,5 @@
175
247
  ]
176
248
  }
177
249
  },
178
- "version": "1.1.2"
250
+ "version": "1.2.1"
179
251
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sanity/runtime-cli",
3
3
  "description": "A new CLI generated with oclif",
4
- "version": "1.1.2",
4
+ "version": "1.2.1",
5
5
  "author": "Sanity Runtime Team",
6
6
  "type": "module",
7
7
  "license": "MIT",
@@ -35,11 +35,11 @@
35
35
  "version": "oclif readme && git add README.md"
36
36
  },
37
37
  "dependencies": {
38
- "@hono/node-server": "^1.13.8",
39
38
  "@oclif/core": "^4",
40
39
  "@oclif/plugin-help": "^6",
41
40
  "@oclif/plugin-plugins": "^5",
42
- "hono": "^4.7.2",
41
+ "jszip": "^3.10.1",
42
+ "mime-types": "^2.1.35",
43
43
  "xdg-basedir": "^5.1.0"
44
44
  },
45
45
  "devDependencies": {
@@ -51,6 +51,7 @@
51
51
  "@oclif/test": "^4",
52
52
  "@rollup/plugin-node-resolve": "^16.0.0",
53
53
  "@types/chai": "^5",
54
+ "@types/mime-types": "^2.1.4",
54
55
  "@types/mocha": "^10",
55
56
  "@types/node": "^20",
56
57
  "chai": "^5",