@sanity/runtime-cli 14.13.0 → 14.13.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-run COMMAND
21
21
  running command...
22
22
  $ sanity-run (--version)
23
- @sanity/runtime-cli/14.13.0 linux-x64 node-v24.14.1
23
+ @sanity/runtime-cli/14.13.1 linux-x64 node-v24.14.1
24
24
  $ sanity-run --help [COMMAND]
25
25
  USAGE
26
26
  $ sanity-run COMMAND
@@ -50,7 +50,7 @@ USAGE
50
50
 
51
51
  ## `sanity-run blueprints add TYPE`
52
52
 
53
- Add a function resource to a Blueprint
53
+ [deprecated] Use "functions add" instead
54
54
 
55
55
  ```
56
56
  USAGE
@@ -82,12 +82,13 @@ FLAGS
82
82
  --[no-]validate-resources Validate resources
83
83
 
84
84
  DESCRIPTION
85
- Add a function resource to a Blueprint
85
+ [deprecated] Use "functions add" instead
86
86
 
87
- Scaffolds a new Sanity Function in your Blueprint. Functions are serverless handlers triggered by document events
88
- (create, update, delete, publish) or media library events.
87
+ This command is deprecated. Use "functions add" instead.
89
88
 
90
- After adding a function, use 'functions dev' to test locally, then 'blueprints deploy' to publish it.
89
+ Equivalent usage:
90
+ $ sanity-run functions add
91
+ $ sanity-run functions add --name my-function --type document-create
91
92
 
92
93
  EXAMPLES
93
94
  $ sanity-run blueprints add function
@@ -101,7 +102,7 @@ EXAMPLES
101
102
  $ sanity-run blueprints add function --name my-function --fn-type document-create --fn-type document-update --lang js
102
103
  ```
103
104
 
104
- _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.0/src/commands/blueprints/add.ts)_
105
+ _See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.1/src/commands/blueprints/add.ts)_
105
106
 
106
107
  ## `sanity-run blueprints config`
107
108
 
@@ -140,7 +141,7 @@ EXAMPLES
140
141
  $ sanity-run blueprints config --edit --project-id <projectId> --stack <name-or-id>
141
142
  ```
142
143
 
143
- _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.0/src/commands/blueprints/config.ts)_
144
+ _See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.1/src/commands/blueprints/config.ts)_
144
145
 
145
146
  ## `sanity-run blueprints deploy`
146
147
 
@@ -182,7 +183,7 @@ EXAMPLES
182
183
  $ sanity-run blueprints deploy --fn-installer npm
183
184
  ```
184
185
 
185
- _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.0/src/commands/blueprints/deploy.ts)_
186
+ _See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.1/src/commands/blueprints/deploy.ts)_
186
187
 
187
188
  ## `sanity-run blueprints destroy`
188
189
 
@@ -218,7 +219,7 @@ EXAMPLES
218
219
  $ sanity-run blueprints destroy --stack <name-or-id> --project-id <projectId> --force --no-wait
219
220
  ```
220
221
 
221
- _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.0/src/commands/blueprints/destroy.ts)_
222
+ _See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.1/src/commands/blueprints/destroy.ts)_
222
223
 
223
224
  ## `sanity-run blueprints doctor`
224
225
 
@@ -250,7 +251,7 @@ EXAMPLES
250
251
  $ sanity-run blueprints doctor --fix
251
252
  ```
252
253
 
253
- _See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.0/src/commands/blueprints/doctor.ts)_
254
+ _See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.1/src/commands/blueprints/doctor.ts)_
254
255
 
255
256
  ## `sanity-run blueprints info`
256
257
 
@@ -285,7 +286,7 @@ EXAMPLES
285
286
  $ sanity-run blueprints info --project-id <id> --stack <name-or-id>
286
287
  ```
287
288
 
288
- _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.0/src/commands/blueprints/info.ts)_
289
+ _See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.1/src/commands/blueprints/info.ts)_
289
290
 
290
291
  ## `sanity-run blueprints init [DIR]`
291
292
 
@@ -338,7 +339,7 @@ EXAMPLES
338
339
  $ sanity-run blueprints init --blueprint-type <json|js|ts> --stack-name <stackName>
339
340
  ```
340
341
 
341
- _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.0/src/commands/blueprints/init.ts)_
342
+ _See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.1/src/commands/blueprints/init.ts)_
342
343
 
343
344
  ## `sanity-run blueprints logs`
344
345
 
@@ -369,7 +370,7 @@ EXAMPLES
369
370
  $ sanity-run blueprints logs --watch
370
371
  ```
371
372
 
372
- _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.0/src/commands/blueprints/logs.ts)_
373
+ _See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.1/src/commands/blueprints/logs.ts)_
373
374
 
374
375
  ## `sanity-run blueprints plan`
375
376
 
@@ -397,7 +398,7 @@ EXAMPLES
397
398
  $ sanity-run blueprints plan
398
399
  ```
399
400
 
400
- _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.0/src/commands/blueprints/plan.ts)_
401
+ _See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.1/src/commands/blueprints/plan.ts)_
401
402
 
402
403
  ## `sanity-run blueprints stacks`
403
404
 
@@ -435,7 +436,7 @@ EXAMPLES
435
436
  $ sanity-run blueprints stacks --organization-id <organizationId> --include-projects
436
437
  ```
437
438
 
438
- _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.0/src/commands/blueprints/stacks.ts)_
439
+ _See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.1/src/commands/blueprints/stacks.ts)_
439
440
 
440
441
  ## `sanity-run functions add`
441
442
 
@@ -488,7 +489,7 @@ EXAMPLES
488
489
  $ sanity-run functions add --name my-function --type document-create --type document-update --lang js
489
490
  ```
490
491
 
491
- _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.0/src/commands/functions/add.ts)_
492
+ _See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.1/src/commands/functions/add.ts)_
492
493
 
493
494
  ## `sanity-run functions dev`
494
495
 
@@ -524,7 +525,7 @@ EXAMPLES
524
525
  $ sanity-run functions dev --timeout 60
525
526
  ```
526
527
 
527
- _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.0/src/commands/functions/dev.ts)_
528
+ _See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.1/src/commands/functions/dev.ts)_
528
529
 
529
530
  ## `sanity-run functions env add NAME KEY VALUE`
530
531
 
@@ -555,7 +556,7 @@ EXAMPLES
555
556
  $ sanity-run functions env add MyFunction API_URL https://api.example.com/
556
557
  ```
557
558
 
558
- _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.0/src/commands/functions/env/add.ts)_
559
+ _See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.1/src/commands/functions/env/add.ts)_
559
560
 
560
561
  ## `sanity-run functions env list NAME`
561
562
 
@@ -583,7 +584,7 @@ EXAMPLES
583
584
  $ sanity-run functions env list MyFunction
584
585
  ```
585
586
 
586
- _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.0/src/commands/functions/env/list.ts)_
587
+ _See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.1/src/commands/functions/env/list.ts)_
587
588
 
588
589
  ## `sanity-run functions env remove NAME KEY`
589
590
 
@@ -613,7 +614,7 @@ EXAMPLES
613
614
  $ sanity-run functions env remove MyFunction API_URL
614
615
  ```
615
616
 
616
- _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.0/src/commands/functions/env/remove.ts)_
617
+ _See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.1/src/commands/functions/env/remove.ts)_
617
618
 
618
619
  ## `sanity-run functions logs [NAME]`
619
620
 
@@ -655,7 +656,7 @@ EXAMPLES
655
656
  $ sanity-run functions logs <name> --delete
656
657
  ```
657
658
 
658
- _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.0/src/commands/functions/logs.ts)_
659
+ _See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.1/src/commands/functions/logs.ts)_
659
660
 
660
661
  ## `sanity-run functions test [NAME]`
661
662
 
@@ -713,7 +714,7 @@ EXAMPLES
713
714
  $ sanity-run functions test <name> --event update --data-before '{ "title": "before" }' --data-after '{ "title": "after" }'
714
715
  ```
715
716
 
716
- _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.0/src/commands/functions/test.ts)_
717
+ _See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v14.13.1/src/commands/functions/test.ts)_
717
718
 
718
719
  ## `sanity-run help [COMMAND]`
719
720
 
@@ -0,0 +1,20 @@
1
+ import type { Logger } from '../../utils/logger.js';
2
+ import type { AuthParams, BlueprintLog } from '../../utils/types.js';
3
+ export declare const LOG_POLL_INTERVAL_MS = 725;
4
+ export interface LogPollingConfig {
5
+ stackId: string;
6
+ operationId?: string;
7
+ knownIds?: string[];
8
+ auth: AuthParams;
9
+ showBanner?: boolean;
10
+ verbose?: boolean;
11
+ log: Logger;
12
+ onActivity?: () => void;
13
+ onLogEntry?: (log: BlueprintLog) => void;
14
+ }
15
+ /**
16
+ * Polls the logs list endpoint at a fixed interval and displays new entries.
17
+ * Deduplicates by log ID so no dependency on server timestamps.
18
+ * @returns A cleanup function that stops polling
19
+ */
20
+ export declare function setupLogPolling(config: LogPollingConfig): () => void;
@@ -0,0 +1,48 @@
1
+ import { formatLogEntry } from '../../utils/display/logs-formatting.js';
2
+ import { styleText } from '../../utils/style-text.js';
3
+ import { getLogs } from './logs.js';
4
+ export const LOG_POLL_INTERVAL_MS = 725;
5
+ /**
6
+ * Polls the logs list endpoint at a fixed interval and displays new entries.
7
+ * Deduplicates by log ID so no dependency on server timestamps.
8
+ * @returns A cleanup function that stops polling
9
+ */
10
+ export function setupLogPolling(config) {
11
+ const { stackId, operationId, auth, log, showBanner, verbose = false } = config;
12
+ const seenIds = new Set(config.knownIds);
13
+ let previousLog;
14
+ let stopped = false;
15
+ if (showBanner) {
16
+ log(`Watching logs... ${styleText('bold', 'ctrl+c')} to cancel`);
17
+ }
18
+ const fetchAndProcessLogs = async () => {
19
+ if (stopped)
20
+ return;
21
+ try {
22
+ const params = operationId ? { operationId } : { stackId };
23
+ const { ok, logs } = await getLogs(params, auth, log);
24
+ if (!ok || stopped)
25
+ return;
26
+ // API returns newest first; reverse so we display in chronological order
27
+ const newLogs = logs.filter((entry) => !seenIds.has(entry.id)).reverse();
28
+ for (const logEntry of newLogs) {
29
+ seenIds.add(logEntry.id);
30
+ config.onActivity?.();
31
+ config.onLogEntry?.(logEntry);
32
+ log(formatLogEntry(logEntry, verbose, previousLog));
33
+ previousLog = logEntry;
34
+ }
35
+ }
36
+ catch {
37
+ // Swallow fetch errors; the operation status poll in callers handles terminal failure.
38
+ // Logging noise here is not useful.
39
+ }
40
+ };
41
+ // Fire immediately, then on interval
42
+ fetchAndProcessLogs();
43
+ const intervalId = setInterval(fetchAndProcessLogs, LOG_POLL_INTERVAL_MS);
44
+ return () => {
45
+ stopped = true;
46
+ clearInterval(intervalId);
47
+ };
48
+ }
@@ -1,7 +1,13 @@
1
1
  import type { Logger } from '../../utils/logger.js';
2
2
  import type { ActionResponse, AuthParams, BlueprintLog } from '../../utils/types.js';
3
3
  export declare const logsUrl: string;
4
- export declare function getLogs(stackId: string, auth: AuthParams, logger: Logger): Promise<ActionResponse & {
4
+ export interface GetLogsParams {
5
+ stackId?: string;
6
+ operationId?: string;
7
+ limit?: number;
8
+ before?: string;
9
+ }
10
+ export declare function getLogs(params: GetLogsParams, auth: AuthParams, logger: Logger): Promise<ActionResponse & {
5
11
  logs: BlueprintLog[];
6
12
  }>;
7
13
  export declare function getRecentLogs(logs: BlueprintLog[], limit?: number): BlueprintLog[];
@@ -3,10 +3,17 @@ import getHeaders from '../../utils/get-headers.js';
3
3
  import { createTracedFetch } from '../../utils/traced-fetch.js';
4
4
  const { apiUrl } = config;
5
5
  export const logsUrl = `${apiUrl}vX/blueprints/logs`;
6
- export async function getLogs(stackId, auth, logger) {
6
+ export async function getLogs(params, auth, logger) {
7
7
  const fetchFn = createTracedFetch(logger);
8
8
  const url = new URL(logsUrl);
9
- url.searchParams.append('stackId', stackId);
9
+ if (params.stackId)
10
+ url.searchParams.append('stackId', params.stackId);
11
+ if (params.operationId)
12
+ url.searchParams.append('operationId', params.operationId);
13
+ if (params.limit)
14
+ url.searchParams.append('limit', String(params.limit));
15
+ if (params.before)
16
+ url.searchParams.append('before', params.before);
10
17
  const response = await fetchFn(url.toString(), {
11
18
  headers: getHeaders(auth),
12
19
  method: 'GET',
@@ -37,7 +37,9 @@ export declare function createEmptyStack({ token, scopeType, scopeId, name, logg
37
37
  logger: Logger;
38
38
  }): Promise<Stack>;
39
39
  interface UpdateStackResponse extends ActionResponse {
40
- stack: Stack;
40
+ stack: Stack & {
41
+ operationId?: string;
42
+ };
41
43
  }
42
44
  export declare function updateStack({ stackId, stackMutation, auth, logger, }: {
43
45
  stackId: string;
@@ -96,7 +98,9 @@ export declare function planStack({ stackId, document, auth, logger, }: {
96
98
  logger: Logger;
97
99
  }): Promise<PlanStackResponse>;
98
100
  interface DestroyStackResponse extends ActionResponse {
99
- stack: Stack;
101
+ stack: Stack & {
102
+ operationId?: string;
103
+ };
100
104
  }
101
105
  export declare function resolveStackIdByNameOrId(value: string, auth: AuthParams, logger: Logger): Promise<string>;
102
106
  interface PromoteStackResponse extends ActionResponse {
@@ -1,11 +1,14 @@
1
1
  /**
2
2
  * @file
3
3
  * @deprecated Use `functions add` instead.
4
- * We're in the process of deprecating the `blueprints add` command.
5
4
  */
6
5
  import { ResolvedCommand } from '../../baseCommands.js';
7
6
  export default class AddCommand extends ResolvedCommand<typeof AddCommand> {
8
7
  static needs: readonly ["blueprint"];
8
+ static state: string;
9
+ static deprecationOptions: {
10
+ to: string;
11
+ };
9
12
  static summary: string;
10
13
  static description: string;
11
14
  static examples: string[];
@@ -1,25 +1,26 @@
1
1
  /**
2
2
  * @file
3
3
  * @deprecated Use `functions add` instead.
4
- * We're in the process of deprecating the `blueprints add` command.
5
4
  */
6
5
  import { Args, Flags } from '@oclif/core';
7
6
  import { ResolvedCommand } from '../../baseCommands.js';
8
7
  import { FUNCTION_TYPES } from '../../constants.js';
9
8
  import { functionAddCore } from '../../cores/functions/index.js';
9
+ import { warn } from '../../utils/display/presenters.js';
10
10
  import { Logger } from '../../utils/logger.js';
11
11
  import { INSTALLER_OPTIONS } from '../../utils/types.js';
12
- // import {warn} from '../../utils/display/presenters.js'
13
12
  export default class AddCommand extends ResolvedCommand {
14
13
  static needs = ['blueprint'];
15
- // static state = 'deprecated'
16
- // static deprecationOptions = {
17
- // message: '`blueprints add` is deprecated. Use `functions add` instead.',
18
- // }
19
- static summary = 'Add a function resource to a Blueprint';
20
- static description = `Scaffolds a new Sanity Function in your Blueprint. Functions are serverless handlers triggered by document events (create, update, delete, publish) or media library events.
14
+ static state = 'deprecated';
15
+ static deprecationOptions = {
16
+ to: 'functions add',
17
+ };
18
+ static summary = '[deprecated] Use "functions add" instead';
19
+ static description = `This command is deprecated. Use "functions add" instead.
21
20
 
22
- After adding a function, use 'functions dev' to test locally, then 'blueprints deploy' to publish it.`;
21
+ Equivalent usage:
22
+ $ <%= config.bin %> functions add
23
+ $ <%= config.bin %> functions add --name my-function --type document-create`;
23
24
  static examples = [
24
25
  '<%= config.bin %> <%= command.id %> function',
25
26
  '<%= config.bin %> <%= command.id %> function --helpers',
@@ -84,11 +85,7 @@ After adding a function, use 'functions dev' to test locally, then 'blueprints d
84
85
  if (resourceType !== 'function') {
85
86
  this.error(`Unsupported Resource type: ${resourceType}`);
86
87
  }
87
- // log(
88
- // warn(
89
- // `\`npx ${bin} blueprints add function\` is deprecated. Use \`npx ${bin} functions add\` instead.`,
90
- // ),
91
- // )
88
+ this.log(warn(`"${bin} blueprints add" is deprecated and will be removed in a future release. Use "${bin} functions add" instead.`));
92
89
  const result = await functionAddCore({
93
90
  bin,
94
91
  log: Logger(this.log.bind(this), this.flags),
@@ -1,6 +1,6 @@
1
1
  import { setTimeout as sleep } from 'node:timers/promises';
2
2
  import { stashAsset } from '../../actions/blueprints/assets.js';
3
- import { setupLogStreaming } from '../../actions/blueprints/logs-streaming.js';
3
+ import { setupLogPolling } from '../../actions/blueprints/logs-polling.js';
4
4
  import { getStack, updateStack } from '../../actions/blueprints/stacks.js';
5
5
  import { createLogHintCollector } from '../../utils/blueprints/log-hints.js';
6
6
  import { niceId } from '../../utils/display/presenters.js';
@@ -47,7 +47,6 @@ export async function blueprintDeployCore(options) {
47
47
  }
48
48
  }
49
49
  const spinner = log.ora('Deploying...').start();
50
- const isoNow = new Date().toISOString();
51
50
  const { ok: deployOk, stack, error: deployError, } = await updateStack({
52
51
  stackId,
53
52
  stackMutation: {
@@ -87,9 +86,9 @@ export async function blueprintDeployCore(options) {
87
86
  try {
88
87
  let lastLogAt = Date.now();
89
88
  let idleMessageShown = false;
90
- logStreamCleanup = await setupLogStreaming({
89
+ logStreamCleanup = setupLogPolling({
91
90
  stackId: stack.id,
92
- after: isoNow,
91
+ operationId: stack.operationId,
93
92
  auth,
94
93
  log,
95
94
  verbose,
@@ -1,6 +1,6 @@
1
1
  import { setTimeout as sleep } from 'node:timers/promises';
2
2
  import { confirm } from '@inquirer/prompts';
3
- import { setupLogStreaming } from '../../actions/blueprints/logs-streaming.js';
3
+ import { setupLogPolling } from '../../actions/blueprints/logs-polling.js';
4
4
  import { destroyStack, getStack, resolveStackIdByNameOrId } from '../../actions/blueprints/stacks.js';
5
5
  import { createLogHintCollector } from '../../utils/blueprints/log-hints.js';
6
6
  import { niceId } from '../../utils/display/presenters.js';
@@ -25,7 +25,6 @@ export async function blueprintDestroyCore(options) {
25
25
  }
26
26
  const auth = { token, scopeType, scopeId };
27
27
  const resolvedId = await resolveStackIdByNameOrId(flagStack, auth, log);
28
- const isoNow = new Date().toISOString();
29
28
  const { ok, error, stack } = await destroyStack({
30
29
  stackId: resolvedId,
31
30
  auth,
@@ -40,10 +39,10 @@ export async function blueprintDestroyCore(options) {
40
39
  return waitForDestruction({
41
40
  stackId: stack.id,
42
41
  stackName: stack.name,
42
+ operationId: stack.operationId,
43
43
  auth,
44
44
  log,
45
45
  bin,
46
- since: isoNow,
47
46
  });
48
47
  }
49
48
  const { scopeType, scopeId, stackId } = blueprint;
@@ -103,8 +102,11 @@ export async function blueprintDestroyCore(options) {
103
102
  else {
104
103
  destroySpinner.start();
105
104
  }
106
- const isoNow = new Date().toISOString();
107
- const { ok, error } = await destroyStack({ stackId: stack.id, auth, logger: log });
105
+ const { ok, error, stack: destroyedStack, } = await destroyStack({
106
+ stackId: stack.id,
107
+ auth,
108
+ logger: log,
109
+ });
108
110
  if (!ok) {
109
111
  destroySpinner.fail('Failed to destroy Stack deployment');
110
112
  return { success: false, error: error || 'Failed to destroy Stack deployment' };
@@ -117,10 +119,10 @@ export async function blueprintDestroyCore(options) {
117
119
  return waitForDestruction({
118
120
  stackId: stack.id,
119
121
  stackName: stack.name,
122
+ operationId: destroyedStack.operationId,
120
123
  auth,
121
124
  log,
122
125
  bin,
123
- since: isoNow,
124
126
  });
125
127
  }
126
128
  catch (error) {
@@ -129,16 +131,16 @@ export async function blueprintDestroyCore(options) {
129
131
  }
130
132
  }
131
133
  async function waitForDestruction(options) {
132
- const { stackId, stackName, auth, log, bin, since } = options;
134
+ const { stackId, stackName, operationId, auth, log, bin } = options;
133
135
  log(styleText('dim', 'Stack destruction progress:'));
134
136
  const logHints = createLogHintCollector(bin);
135
137
  let logStreamCleanup = null;
136
138
  try {
137
139
  let lastLogAt = Date.now();
138
140
  let idleMessageShown = false;
139
- logStreamCleanup = await setupLogStreaming({
141
+ logStreamCleanup = setupLogPolling({
140
142
  stackId,
141
- after: since,
143
+ operationId,
142
144
  auth,
143
145
  log,
144
146
  onActivity: () => {
@@ -1,5 +1,5 @@
1
1
  import { getLogs, getRecentLogs } from '../../actions/blueprints/logs.js';
2
- import { setupLogStreaming } from '../../actions/blueprints/logs-streaming.js';
2
+ import { setupLogPolling } from '../../actions/blueprints/logs-polling.js';
3
3
  import { formatTitle } from '../../utils/display/blueprints-formatting.js';
4
4
  import { formatLogEntry, formatLogs } from '../../utils/display/logs-formatting.js';
5
5
  import { niceId } from '../../utils/display/presenters.js';
@@ -10,7 +10,7 @@ export async function blueprintLogsCore(options) {
10
10
  const spinner = log.ora(`Fetching recent logs for Stack deployment ${niceId(stackId)}`).start();
11
11
  try {
12
12
  if (watch) {
13
- const { ok, logs, error } = await getLogs(stackId, auth, log);
13
+ const { ok, logs, error } = await getLogs({ stackId }, auth, log);
14
14
  if (!ok) {
15
15
  spinner.fail(`${styleText('red', 'Failed')} to retrieve logs`);
16
16
  log.error(`Error: ${error || 'Unknown error'}`);
@@ -30,22 +30,23 @@ export async function blueprintLogsCore(options) {
30
30
  else {
31
31
  log(`No recent logs found for Stack deployment ${niceId(stackId)}`);
32
32
  }
33
- // Set up streaming log display
34
- await setupLogStreaming({
33
+ // Set up polling log display, seeding with already-displayed log IDs
34
+ setupLogPolling({
35
35
  stackId,
36
+ knownIds: logs.map((l) => l.id),
36
37
  auth,
37
38
  log,
38
39
  showBanner: true,
39
40
  verbose,
40
41
  });
41
- // Return a special key for streaming mode
42
+ // Return a special key for polling mode
42
43
  return {
43
44
  success: true,
44
45
  streaming: new Promise(() => { }),
45
46
  };
46
47
  }
47
48
  // Regular non-streaming logs
48
- const { ok, logs, error } = await getLogs(stackId, auth, log);
49
+ const { ok, logs, error } = await getLogs({ stackId }, auth, log);
49
50
  if (!ok) {
50
51
  spinner.fail(`${styleText('red', 'Failed')} to retrieve Stack deployment logs`);
51
52
  log.error(`Error: ${error || 'Unknown error'}`);
@@ -28,11 +28,13 @@ function arrayifyEvent(event) {
28
28
  if ('projection' in event && event.projection) {
29
29
  details.push(formatLabeledValue('projection', event.projection));
30
30
  }
31
- // TODO: expose event.resource details once we are ready
32
- // https://linear.app/sanity/issue/RUN-1246/cli-should-show-eventresource-details-in-blueprints-infoplans
33
- // if (event.resource) {
34
- // details.push(formatLabeledValue('resource', JSON.stringify(event.resource)))
35
- // }
31
+ if (event.resource) {
32
+ details.push(formatLabel('resource'));
33
+ details.push([
34
+ formatLabeledValue('type', event.resource.type),
35
+ formatLabeledValue('id', event.resource.id),
36
+ ]);
37
+ }
36
38
  }
37
39
  return details;
38
40
  }
@@ -12,7 +12,10 @@
12
12
  "required": true
13
13
  }
14
14
  },
15
- "description": "Scaffolds a new Sanity Function in your Blueprint. Functions are serverless handlers triggered by document events (create, update, delete, publish) or media library events.\n\nAfter adding a function, use 'functions dev' to test locally, then 'blueprints deploy' to publish it.",
15
+ "deprecationOptions": {
16
+ "to": "functions add"
17
+ },
18
+ "description": "This command is deprecated. Use \"functions add\" instead.\n\nEquivalent usage:\n $ <%= config.bin %> functions add\n $ <%= config.bin %> functions add --name my-function --type document-create",
16
19
  "examples": [
17
20
  "<%= config.bin %> <%= command.id %> function",
18
21
  "<%= config.bin %> <%= command.id %> function --helpers",
@@ -216,8 +219,9 @@
216
219
  "pluginAlias": "@sanity/runtime-cli",
217
220
  "pluginName": "@sanity/runtime-cli",
218
221
  "pluginType": "core",
222
+ "state": "deprecated",
219
223
  "strict": true,
220
- "summary": "Add a function resource to a Blueprint",
224
+ "summary": "[deprecated] Use \"functions add\" instead",
221
225
  "enableJsonFlag": true,
222
226
  "needs": [
223
227
  "blueprint"
@@ -2688,5 +2692,5 @@
2688
2692
  ]
2689
2693
  }
2690
2694
  },
2691
- "version": "14.13.0"
2695
+ "version": "14.13.1"
2692
2696
  }
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.13.0",
4
+ "version": "14.13.1",
5
5
  "author": "Sanity Runtime Team",
6
6
  "type": "module",
7
7
  "license": "MIT",
@@ -94,6 +94,7 @@
94
94
  "test:api": "cd test/api-types && npm it",
95
95
  "test:depmgmt": "vitest run --config ./test-depmgmt/vitest.config.ts",
96
96
  "test:vitest": "vitest run",
97
+ "test:coverage": "npm run test:vitest -- --coverage --coverage.include \"runtime-cli/src/**/*\"",
97
98
  "typecheck": "tsc --project tsconfig.typecheck.json",
98
99
  "watch": "tsc --watch"
99
100
  },
@@ -137,6 +138,7 @@
137
138
  "@types/node": "20",
138
139
  "@types/tar-stream": "^3.1.4",
139
140
  "@types/ws": "^8.18.1",
141
+ "@vitest/coverage-v8": "^4.1.0",
140
142
  "codemirror": "^6.0.2",
141
143
  "mentoss": "^0.13.0",
142
144
  "oclif": "^4.22.92",
@@ -1,30 +0,0 @@
1
- import type { Logger } from '../../utils/logger.js';
2
- import type { AuthParams, BlueprintLog } from '../../utils/types.js';
3
- export interface LogStreamingConfig {
4
- stackId: string;
5
- after?: string;
6
- auth: AuthParams;
7
- showBanner?: boolean;
8
- verbose?: boolean;
9
- log: Logger;
10
- onActivity?: () => void;
11
- onLogEntry?: (log: BlueprintLog) => void;
12
- }
13
- export interface StreamLogsOptions {
14
- stackId: string;
15
- after?: string;
16
- auth: AuthParams;
17
- onLog: (log: BlueprintLog) => void;
18
- onOpen: () => void;
19
- onError: (error: string) => void;
20
- logger: Logger;
21
- }
22
- export declare function streamLogs({ stackId, after, auth, onLog, onOpen, onError, logger, }: StreamLogsOptions): () => void;
23
- /** Check if a log is newer than a given timestamp */
24
- export declare function isNewerLog(log: BlueprintLog, timestamp: number): boolean;
25
- /**
26
- * Sets up log streaming for operations like deploy or destroy with spinner integration
27
- * @param config Configuration for log streaming
28
- * @returns A cleanup function for closing the log stream
29
- */
30
- export declare function setupLogStreaming(config: LogStreamingConfig): Promise<() => void>;
@@ -1,107 +0,0 @@
1
- import { EventSource } from 'eventsource';
2
- import { formatLogEntry } from '../../utils/display/logs-formatting.js';
3
- import getHeaders from '../../utils/get-headers.js';
4
- import { styleText } from '../../utils/style-text.js';
5
- import { createTracedFetch } from '../../utils/traced-fetch.js';
6
- import { logsUrl } from './logs.js';
7
- export function streamLogs({ stackId, after, auth, onLog, onOpen, onError, logger, }) {
8
- const fetchFn = createTracedFetch(logger);
9
- const url = new URL(`${logsUrl}/stream`);
10
- url.searchParams.append('stackId', stackId);
11
- if (after)
12
- url.searchParams.append('after', after);
13
- const headers = getHeaders(auth);
14
- const eventSource = new EventSource(url.toString(), {
15
- fetch: (input, init) => fetchFn(input, {
16
- ...init,
17
- headers: {
18
- ...init?.headers,
19
- ...headers,
20
- },
21
- }),
22
- });
23
- eventSource.onopen = onOpen;
24
- eventSource.onmessage = (event) => {
25
- try {
26
- const log = JSON.parse(event.data);
27
- onLog(log);
28
- }
29
- catch (err) {
30
- onError(`Failed to parse log data: ${err instanceof Error ? err.message : String(err)}`);
31
- }
32
- };
33
- eventSource.addEventListener('logs', (event) => {
34
- try {
35
- const logData = JSON.parse(event.data);
36
- // usually an array
37
- if (Array.isArray(logData)) {
38
- for (const log of logData)
39
- onLog(log);
40
- }
41
- else {
42
- onLog(logData);
43
- }
44
- }
45
- catch (err) {
46
- console.error('Error parsing logs event:', err);
47
- }
48
- });
49
- eventSource.onerror = () => {
50
- onError('Connection to log stream failed or was closed');
51
- if (eventSource.readyState === eventSource.CLOSED) {
52
- console.log('Connection is CLOSED');
53
- }
54
- else if (eventSource.readyState === eventSource.CONNECTING) {
55
- console.log('Connection is attempting to reconnect...');
56
- return; // Don't close if trying to reconnect
57
- }
58
- eventSource.close();
59
- };
60
- return () => {
61
- eventSource.close();
62
- };
63
- }
64
- /** Check if a log is newer than a given timestamp */
65
- export function isNewerLog(log, timestamp) {
66
- const logTimestamp = new Date(log.timestamp).getTime();
67
- return logTimestamp > timestamp;
68
- }
69
- /**
70
- * Sets up log streaming for operations like deploy or destroy with spinner integration
71
- * @param config Configuration for log streaming
72
- * @returns A cleanup function for closing the log stream
73
- */
74
- export async function setupLogStreaming(config) {
75
- const { stackId, auth, log, showBanner, verbose = false, after } = config;
76
- let newestTimestamp = Date.now();
77
- let previousLog;
78
- const onLogReceived = (logEntry) => {
79
- if (!isNewerLog(logEntry, newestTimestamp))
80
- return;
81
- newestTimestamp = new Date(logEntry.timestamp).getTime();
82
- config.onActivity?.();
83
- config.onLogEntry?.(logEntry);
84
- log(formatLogEntry(logEntry, verbose, previousLog));
85
- previousLog = logEntry;
86
- };
87
- let alreadyOpened = false;
88
- const onStreamOpen = () => {
89
- if (!alreadyOpened && showBanner)
90
- log(`Streaming logs... ${styleText('bold', 'ctrl+c')} to cancel`);
91
- if (alreadyOpened)
92
- log(`${styleText('green', 'Reconnected')}`);
93
- alreadyOpened = true;
94
- };
95
- const onStreamError = (error) => {
96
- log(`${styleText('red', 'Stream error:')} ${error}`);
97
- };
98
- return streamLogs({
99
- stackId,
100
- after,
101
- auth,
102
- onLog: onLogReceived,
103
- onOpen: onStreamOpen,
104
- onError: onStreamError,
105
- logger: log,
106
- });
107
- }