@sanity/runtime-cli 17.12.0 → 17.13.0-next.g2501ffa

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 (69) hide show
  1. package/README.md +19 -19
  2. package/dist/actions/blueprints/assets.d.ts +23 -20
  3. package/dist/actions/blueprints/assets.js +18 -87
  4. package/dist/actions/blueprints/logs-polling.d.ts +3 -5
  5. package/dist/actions/blueprints/logs-polling.js +4 -9
  6. package/dist/actions/blueprints/logs.d.ts +1 -2
  7. package/dist/actions/blueprints/logs.js +1 -3
  8. package/dist/actions/blueprints/operations-state.d.ts +10 -0
  9. package/dist/actions/blueprints/operations-state.js +42 -0
  10. package/dist/actions/blueprints/operations.d.ts +2 -9
  11. package/dist/actions/blueprints/operations.js +1 -15
  12. package/dist/actions/blueprints/pre-deploy.d.ts +19 -11
  13. package/dist/actions/blueprints/pre-deploy.js +95 -27
  14. package/dist/actions/blueprints/process-assets.d.ts +13 -11
  15. package/dist/actions/blueprints/process-assets.js +9 -7
  16. package/dist/actions/blueprints/stacks.d.ts +4 -4
  17. package/dist/actions/blueprints/stacks.js +2 -4
  18. package/dist/actions/blueprints/wait-for-operation.d.ts +1 -10
  19. package/dist/actions/blueprints/wait-for-operation.js +56 -101
  20. package/dist/baseCommands.js +6 -1
  21. package/dist/commands/blueprints/deploy.js +2 -0
  22. package/dist/config.d.ts +1 -0
  23. package/dist/config.js +6 -0
  24. package/dist/constants.d.ts +1 -0
  25. package/dist/constants.js +1 -0
  26. package/dist/cores/blueprints/config.js +1 -1
  27. package/dist/cores/blueprints/deploy.d.ts +2 -0
  28. package/dist/cores/blueprints/deploy.js +38 -7
  29. package/dist/cores/blueprints/destroy.js +4 -2
  30. package/dist/cores/blueprints/doctor.js +2 -2
  31. package/dist/cores/blueprints/init.js +3 -1
  32. package/dist/cores/blueprints/logs.js +11 -5
  33. package/dist/cores/blueprints/mint-deploy-token.js +2 -1
  34. package/dist/cores/functions/build.js +4 -5
  35. package/dist/cores/functions/test.js +3 -1
  36. package/dist/cores/index.js +3 -1
  37. package/dist/server/handlers/invoke.js +1 -1
  38. package/dist/server/static/vendor/vendor.bundle.js +21791 -25197
  39. package/dist/utils/blueprints/deploy-metadata.d.ts +10 -0
  40. package/dist/utils/blueprints/deploy-metadata.js +17 -0
  41. package/dist/utils/blueprints/resolve-deployed-resources.d.ts +2 -3
  42. package/dist/utils/blueprints/resolve-deployed-resources.js +2 -2
  43. package/dist/utils/blueprints/wait-for.d.ts +60 -0
  44. package/dist/utils/blueprints/wait-for.js +115 -0
  45. package/dist/utils/display/blueprints-formatting.d.ts +1 -0
  46. package/dist/utils/display/blueprints-formatting.js +17 -3
  47. package/dist/utils/display/dates.d.ts +0 -2
  48. package/dist/utils/display/dates.js +0 -25
  49. package/dist/utils/display/functions-formatting.js +1 -1
  50. package/dist/utils/display/prompt.js +5 -2
  51. package/dist/utils/display/resources-formatting.d.ts +6 -0
  52. package/dist/utils/display/resources-formatting.js +17 -0
  53. package/dist/utils/functions/child-runtime/durable.js +2 -2
  54. package/dist/utils/functions/dependency-info.d.ts +30 -0
  55. package/dist/utils/functions/dependency-info.js +57 -0
  56. package/dist/utils/functions/invoke/assert-console.d.ts +5 -0
  57. package/dist/utils/functions/invoke/assert-console.js +60 -0
  58. package/dist/utils/functions/invoke/local.js +9 -0
  59. package/dist/utils/functions/is-folder-larger-than.d.ts +5 -0
  60. package/dist/utils/functions/is-folder-larger-than.js +38 -0
  61. package/dist/utils/functions/prepare-asset.d.ts +2 -0
  62. package/dist/utils/functions/prepare-asset.js +59 -23
  63. package/dist/utils/studios/prepare-asset.js +36 -9
  64. package/dist/utils/types.d.ts +35 -4
  65. package/dist/utils/types.js +0 -3
  66. package/oclif.manifest.json +1002 -1002
  67. package/package.json +17 -19
  68. package/dist/utils/functions/get-folder-size.d.ts +0 -5
  69. package/dist/utils/functions/get-folder-size.js +0 -24
@@ -1,15 +1,52 @@
1
+ import { SANITY_FUNCTION_PREFIX } from '../../constants.js';
1
2
  import { resolveDeployedResources } from '../../utils/blueprints/resolve-deployed-resources.js';
2
3
  import { CODE_OPERATION_UNCONFIRMED, EXIT_OPERATION_UNCONFIRMED } from '../../utils/exit-codes.js';
4
+ import { prepareAsset as prepareFunctionAsset } from '../../utils/functions/prepare-asset.js';
3
5
  import { runCommand } from '../../utils/package-manager.js';
4
- import { styleText } from '../../utils/style-text.js';
5
- import { isAssetResource, } from '../../utils/types.js';
6
+ import { prepareStudioAsset } from '../../utils/studios/prepare-asset.js';
7
+ import { setupLogPolling } from './logs-polling.js';
8
+ import { getOperationStateInfo } from './operations-state.js';
6
9
  import { processAssets } from './process-assets.js';
7
10
  import { updateStack } from './stacks.js';
8
- import { waitForOperation } from './wait-for-operation.js';
11
+ /**
12
+ * A list of configurations for pre-deployment of resources. The first resourceType to match in the list will take precedence.
13
+ */
14
+ const preDeployConfigs = [
15
+ {
16
+ resourceType: 'sanity.studio',
17
+ async prepareAsset(options) {
18
+ return prepareStudioAsset({
19
+ resource: options.resource,
20
+ blueprintFileDir: options.blueprintFileDir,
21
+ });
22
+ },
23
+ },
24
+ {
25
+ resourceType: (type) => type.startsWith(SANITY_FUNCTION_PREFIX),
26
+ async prepareAsset(options) {
27
+ return prepareFunctionAsset({
28
+ resource: options.resource,
29
+ blueprintFileDir: options.blueprintFileDir,
30
+ installer: options.installer,
31
+ });
32
+ },
33
+ },
34
+ ];
35
+ function hasPreDeployResources(resources) {
36
+ return resources.some((r) => {
37
+ return !!getPredeployConfig(r);
38
+ });
39
+ }
40
+ function getPredeployConfig(r) {
41
+ return preDeployConfigs.find((c) => {
42
+ return ((typeof c.resourceType === 'function' && c.resourceType(r.type)) ||
43
+ (typeof c.resourceType !== 'function' && c.resourceType === r.type));
44
+ });
45
+ }
9
46
  export async function preDeploy(options) {
10
- const { resources, stackId, auth, blueprintFileDir, installer, logger } = options;
11
- const hasAssetResources = resources.some(isAssetResource);
12
- if (hasAssetResources) {
47
+ const { resources, stackId, auth, blueprintFileDir, installer, fetchFn } = options;
48
+ const resourceMetadata = [];
49
+ if (hasPreDeployResources(resources)) {
13
50
  const preDeployStackResult = await preDeployStack(options);
14
51
  if (!preDeployStackResult.success) {
15
52
  return preDeployStackResult;
@@ -19,17 +56,23 @@ export async function preDeploy(options) {
19
56
  resources,
20
57
  stackId,
21
58
  auth,
22
- logger,
59
+ fetchFn,
23
60
  });
24
- const assetResources = resolvedResources.filter(isAssetResource);
25
- let processedAssets;
61
+ const assetResources = [];
62
+ for (const r of resolvedResources) {
63
+ const config = getPredeployConfig(r);
64
+ if (typeof config !== 'undefined' && typeof config.prepareAsset === 'function') {
65
+ assetResources.push({ resource: r, prepareAsset: config.prepareAsset });
66
+ }
67
+ }
68
+ let processedAssets = [];
26
69
  try {
27
70
  processedAssets = await processAssets({
28
71
  assetResources,
29
72
  auth,
30
73
  blueprintFileDir,
31
74
  installer,
32
- logger,
75
+ fetchFn,
33
76
  onProgress: (progress) => options.onAssetEvent?.({ type: 'progress', progress }),
34
77
  onResult: (result) => options.onAssetEvent?.({ type: 'result', result }),
35
78
  });
@@ -47,13 +90,23 @@ export async function preDeploy(options) {
47
90
  if (idx === -1)
48
91
  throw new Error('Unable to update resource asset');
49
92
  resources[idx] = asset.resource;
93
+ if (asset.success) {
94
+ const { name, type } = asset.resource;
95
+ resourceMetadata.push({
96
+ name,
97
+ type,
98
+ assetHash: asset.hash,
99
+ assetReused: asset.exists,
100
+ ...asset.metadata,
101
+ });
102
+ }
50
103
  }
51
104
  }
52
- return { success: true };
105
+ return { success: true, resourceMetadata };
53
106
  }
54
107
  async function preDeployStack(options) {
55
- const { scopeType, scopeId, stackId, stackName, resources, auth, logger, bin, verbose } = options;
56
- const spinner = logger.ora('Starting pre-deploy...').start();
108
+ const { scopeType, scopeId, stackId, stackName, resources, auth, bin, fetchFn, waitForFn } = options;
109
+ options.onPreDeployStackStart?.();
57
110
  const stackMutation = {
58
111
  scopeType,
59
112
  scopeId,
@@ -61,9 +114,9 @@ async function preDeployStack(options) {
61
114
  preDeploy: true,
62
115
  document: { resources },
63
116
  };
64
- const { ok: deployOk, stack, error: deployError, } = await updateStack({ stackId, stackMutation, auth, logger });
117
+ const { ok: deployOk, stack, error: deployError, } = await updateStack({ stackId, stackMutation, auth, fetchFn });
65
118
  if (!deployOk) {
66
- spinner.fail(`${styleText('red', 'Failed')} to complete Stack pre-deployment`);
119
+ options.onPreDeployStackFail?.();
67
120
  return {
68
121
  success: false,
69
122
  error: deployError || 'Failed to complete Stack pre-deployment',
@@ -73,22 +126,37 @@ async function preDeployStack(options) {
73
126
  ],
74
127
  };
75
128
  }
76
- spinner.stop().clear();
129
+ options.onPreDeployStackDone?.();
77
130
  if (stack.changeCount === 0)
78
131
  return { success: true };
79
- logger(styleText('dim', 'Stack deployment progress:'));
80
- logger('');
81
- const outcome = await waitForOperation({
82
- stackId,
83
- operationId: stack.operationId,
84
- auth,
85
- log: logger,
86
- bin,
87
- verbose,
88
- progressNoun: 'pre-deployment',
132
+ options.onPreDeployOperationStart?.();
133
+ const outcome = await waitForFn({
134
+ getIdleMessage() {
135
+ return `The pre-deployment is still running on Sanity servers.`;
136
+ },
137
+ async onRequestLatestState() {
138
+ return await getOperationStateInfo({
139
+ stackId,
140
+ operationId: stack.operationId,
141
+ auth,
142
+ fetchFn,
143
+ includeDestroyed: false,
144
+ progressNoun: 'pre-deployment',
145
+ });
146
+ },
147
+ onStartLogPolling(options) {
148
+ return setupLogPolling({
149
+ auth,
150
+ fetchFn,
151
+ operationId: stack.operationId,
152
+ stackId,
153
+ onActivity: options.onActivity,
154
+ onLogEntry: options.onLogEntry,
155
+ });
156
+ },
89
157
  });
90
158
  if (outcome.type === 'completed') {
91
- logger(styleText(['bold', 'green'], 'Stack pre-deployment completed!'));
159
+ options.onPreDeployOperationDone?.();
92
160
  }
93
161
  else if (outcome.type === 'unconfirmed') {
94
162
  return {
@@ -1,29 +1,31 @@
1
- import type { Logger } from '../../utils/logger.js';
1
+ import type { BlueprintResource } from '@sanity/blueprints';
2
2
  import type { AssetResource, AuthParams, InstallerType } from '../../utils/types.js';
3
- type ProcessAssetsOptions = {
4
- assetResources: AssetResource[];
3
+ type ProcessAssetsOptions<R extends BlueprintResource> = {
4
+ assetResources: AssetResource<R>[];
5
5
  auth: AuthParams;
6
6
  blueprintFileDir: string;
7
7
  installer: InstallerType;
8
- logger: Logger;
8
+ fetchFn: typeof fetch;
9
9
  onProgress?: (progress: ProcessAssetsProgress) => void;
10
- onResult?: (result: ProcessedAsset) => void;
10
+ onResult?: (result: ProcessedAsset<R>) => void;
11
11
  };
12
12
  export type ProcessAssetsProgress = {
13
13
  completed: number;
14
14
  total: number;
15
15
  slow: boolean;
16
16
  };
17
- export type ProcessedAsset = {
17
+ export type ProcessedAsset<R extends BlueprintResource> = {
18
18
  success: false;
19
- resource: AssetResource;
19
+ resource: R;
20
20
  error: string;
21
21
  failureKind: 'processing' | 'upload';
22
22
  } | {
23
23
  success: true;
24
- resource: AssetResource;
25
- hash?: string;
26
- exists?: boolean;
24
+ resource: R;
25
+ assetParameter: string;
26
+ hash: string;
27
+ exists: boolean;
28
+ metadata?: Record<string, unknown>;
27
29
  };
28
- export declare function processAssets(options: ProcessAssetsOptions): Promise<ProcessedAsset[]>;
30
+ export declare function processAssets<R extends BlueprintResource>(options: ProcessAssetsOptions<R>): Promise<ProcessedAsset<R>[]>;
29
31
  export {};
@@ -21,8 +21,8 @@ export async function processAssets(options) {
21
21
  try {
22
22
  for (let start = 0; start < total; start += concurrency) {
23
23
  const batch = assetResources.slice(start, start + concurrency);
24
- const batchResults = await Promise.all(batch.map(async (resource) => {
25
- const result = await processAsset(resource, options);
24
+ const batchResults = await Promise.all(batch.map(async (assetResource) => {
25
+ const result = await processAsset(assetResource.resource, assetResource.prepareAsset, options);
26
26
  completed += 1;
27
27
  reportProgress();
28
28
  onResult?.(result);
@@ -42,13 +42,13 @@ function getAssetConcurrency() {
42
42
  const configured = Number(env.SANITY_ASSET_CONCURRENCY);
43
43
  return Number.isSafeInteger(configured) && configured > 0 ? configured : DEFAULT_ASSET_CONCURRENCY;
44
44
  }
45
- async function processAsset(resource, options) {
46
- const { auth, blueprintFileDir, installer, logger } = options;
45
+ async function processAsset(resource, prepAsset, options) {
46
+ const { auth, blueprintFileDir, installer, fetchFn } = options;
47
47
  let assetTimeoutTimer;
48
48
  let result;
49
49
  try {
50
50
  result = await Promise.race([
51
- stashAsset({ resource, blueprintFileDir, auth, logger, installer }),
51
+ stashAsset({ resource, prepAsset, blueprintFileDir, auth, fetchFn, installer }),
52
52
  new Promise((_, reject) => {
53
53
  assetTimeoutTimer = setTimeout(() => {
54
54
  reject(new Error(`Processing ${resource.name} timed out after ${assetTimeoutS}s`));
@@ -66,15 +66,17 @@ async function processAsset(resource, options) {
66
66
  if (result.success && result.assetId) {
67
67
  return {
68
68
  success: true,
69
- resource: { ...resource, src: result.assetId },
69
+ resource: { ...resource, [result.assetParameter]: result.assetId },
70
+ assetParameter: result.assetParameter,
70
71
  hash: result.hash,
71
72
  exists: result.exists,
73
+ metadata: result.metadata,
72
74
  };
73
75
  }
74
76
  return {
75
77
  success: false,
76
78
  resource,
77
- error: result.error || 'Failed to process asset',
79
+ error: ('error' in result && result.error) || 'Failed to process asset',
78
80
  failureKind: 'upload',
79
81
  };
80
82
  }
@@ -25,10 +25,10 @@ type GetStackResponse = {
25
25
  stack?: unknown;
26
26
  response?: Response;
27
27
  };
28
- export declare function getStack({ stackId, auth, logger, }: {
28
+ export declare function getStack({ stackId, auth, fetchFn, }: {
29
29
  stackId: string;
30
30
  auth: AuthParams;
31
- logger: Logger;
31
+ fetchFn: typeof fetch;
32
32
  }): Promise<GetStackResponse>;
33
33
  type CreateStackResponse = GetStackResponse;
34
34
  export declare function createStack({ stackMutation, auth, logger, }: {
@@ -55,11 +55,11 @@ type UpdateStackResponse = {
55
55
  error: string;
56
56
  stack?: unknown;
57
57
  };
58
- export declare function updateStack({ stackId, stackMutation, auth, logger, }: {
58
+ export declare function updateStack({ stackId, stackMutation, auth, fetchFn, }: {
59
59
  stackId: string;
60
60
  stackMutation: StackMutation;
61
61
  auth: AuthParams;
62
- logger: Logger;
62
+ fetchFn: typeof fetch;
63
63
  }): Promise<UpdateStackResponse>;
64
64
  interface PlanResourceSnapshot {
65
65
  name: string;
@@ -24,8 +24,7 @@ export async function listStacks(auth, logger) {
24
24
  };
25
25
  }
26
26
  }
27
- export async function getStack({ stackId, auth, logger, }) {
28
- const fetchFn = createTracedFetch(logger);
27
+ export async function getStack({ stackId, auth, fetchFn, }) {
29
28
  try {
30
29
  const response = await fetchFn(`${stacksUrl}/${stackId}`, {
31
30
  method: 'GET',
@@ -92,8 +91,7 @@ export async function createEmptyStack({ token, scopeType, scopeId, name, logger
92
91
  }
93
92
  return response.stack;
94
93
  }
95
- export async function updateStack({ stackId, stackMutation, auth, logger, }) {
96
- const fetchFn = createTracedFetch(logger);
94
+ export async function updateStack({ stackId, stackMutation, auth, fetchFn, }) {
97
95
  try {
98
96
  const response = await fetchFn(`${stacksUrl}/${stackId}`, {
99
97
  method: 'PUT',
@@ -1,15 +1,6 @@
1
+ import { type OperationOutcome } from '../../utils/blueprints/wait-for.js';
1
2
  import type { Logger } from '../../utils/logger.js';
2
3
  import type { AuthParams } from '../../utils/types.js';
3
- /** Result of waiting for a deploy/destroy operation to settle. */
4
- export type OperationOutcome = {
5
- type: 'completed';
6
- } | {
7
- type: 'failed';
8
- logHints: string[];
9
- } | {
10
- type: 'unconfirmed';
11
- error?: string;
12
- };
13
4
  export interface WaitForOperationOptions {
14
5
  stackId: string;
15
6
  operationId: string;
@@ -1,10 +1,7 @@
1
- import { setTimeout as sleep } from 'node:timers/promises';
2
- import { createHintCollector } from '../../utils/blueprints/hints.js';
3
- import { runCommand } from '../../utils/package-manager.js';
1
+ import { createWaitFor } from '../../utils/blueprints/wait-for.js';
2
+ import { createTracedFetch } from '../../utils/traced-fetch.js';
4
3
  import { setupLogPolling } from './logs-polling.js';
5
- import { classifyOperationPoll, getOperation, OPERATION_POLL_INITIAL_DELAY_MS, OPERATION_POLL_INTERVAL_MS, OPERATION_POLL_MAX_CONSECUTIVE_ERRORS, } from './operations.js';
6
- const IDLE_SPINNER_DELAY_MS = 30_000;
7
- const IDLE_SPINNER_UPDATE_INTERVAL_MS = 15_000;
4
+ import { classifyOperationPoll, getOperation } from './operations.js';
8
5
  /**
9
6
  * Stream logs and poll an operation until it settles.
10
7
  * handles log-stream lifecycle, retry budget, and idle messaging;
@@ -12,107 +9,65 @@ const IDLE_SPINNER_UPDATE_INTERVAL_MS = 15_000;
12
9
  */
13
10
  export async function waitForOperation(options) {
14
11
  const { stackId, operationId, auth, log, bin, verbose = false, includeDestroyed, progressNoun, } = options;
15
- const logHints = createHintCollector(bin);
16
- let logStreamCleanup = null;
17
- let idleSpinner;
18
- const stopIdleSpinner = (restoreOperationProgress = false) => {
19
- if (!idleSpinner)
20
- return;
21
- idleSpinner.stop();
22
- idleSpinner = undefined;
23
- if (restoreOperationProgress)
24
- log.showProgress();
25
- };
26
- try {
27
- log.showProgress();
28
- let lastLogAt = Date.now();
29
- let nextIdleSpinnerUpdateAt = IDLE_SPINNER_DELAY_MS;
30
- let consecutiveErrors = 0;
31
- let lastStatus;
32
- logStreamCleanup = setupLogPolling({
33
- stackId,
34
- operationId,
35
- auth,
36
- log,
37
- verbose,
38
- onActivity: () => {
39
- lastLogAt = Date.now();
40
- nextIdleSpinnerUpdateAt = IDLE_SPINNER_DELAY_MS;
41
- stopIdleSpinner(true);
42
- },
43
- onLogEntry: (logEntry) => logHints.inspectLog(logEntry),
44
- });
45
- // balk in case read replica lags
46
- await sleep(OPERATION_POLL_INITIAL_DELAY_MS);
47
- while (true) {
48
- const { ok, error: pollError, operation, response, } = await getOperation({
12
+ const fetchFn = createTracedFetch(log);
13
+ const waitFor = createWaitFor({
14
+ bin,
15
+ log,
16
+ verbose,
17
+ });
18
+ return await waitFor({
19
+ async onRequestLatestState() {
20
+ const { ok, error, operation, response } = await getOperation({
49
21
  stackId,
50
22
  operationId,
51
23
  auth,
52
- logger: log,
24
+ fetchFn,
53
25
  includeDestroyed,
54
26
  });
55
27
  const httpStatus = response?.status;
56
28
  const state = classifyOperationPoll({ ok, httpStatus, operation });
57
- if (state === 'completed') {
58
- if (logStreamCleanup)
59
- logStreamCleanup();
60
- stopIdleSpinner();
61
- log('');
62
- return { type: 'completed' };
29
+ switch (state) {
30
+ case 'completed':
31
+ case 'failed':
32
+ return {
33
+ state,
34
+ };
35
+ case 'error':
36
+ return {
37
+ state,
38
+ error,
39
+ verboseError: `Could not read ${progressNoun} status (HTTP ${httpStatus ?? 'unknown'}${error ? `: ${error}` : ''})`,
40
+ };
41
+ case 'pending':
42
+ if (httpStatus === 404 || !operation) {
43
+ return {
44
+ state: 'not_found',
45
+ verboseError: `Waiting for ${progressNoun} status (HTTP 404); retrying.`,
46
+ };
47
+ }
48
+ else {
49
+ return {
50
+ state,
51
+ latestStatus: operation.status,
52
+ verboseMessage: `${progressNoun} operation status: ${operation.status}.`,
53
+ };
54
+ }
55
+ default:
56
+ throw new Error(`Unexpected poll state: ${state}`);
63
57
  }
64
- if (state === 'failed') {
65
- if (logStreamCleanup)
66
- logStreamCleanup();
67
- stopIdleSpinner();
68
- log('');
69
- return { type: 'failed', logHints: logHints.getSuggestions() };
70
- }
71
- // tolerate a few consecutive errors; the operation was accepted and may still be running
72
- if (state === 'error') {
73
- consecutiveErrors += 1;
74
- log.verbose(`Could not read ${progressNoun} status (HTTP ${httpStatus ?? 'unknown'}${pollError ? `: ${pollError}` : ''}); retrying (${consecutiveErrors}/${OPERATION_POLL_MAX_CONSECUTIVE_ERRORS}).`);
75
- if (consecutiveErrors >= OPERATION_POLL_MAX_CONSECUTIVE_ERRORS) {
76
- if (logStreamCleanup)
77
- logStreamCleanup();
78
- stopIdleSpinner();
79
- log('');
80
- return { type: 'unconfirmed', error: pollError ?? undefined };
81
- }
82
- }
83
- else {
84
- consecutiveErrors = 0;
85
- if (httpStatus === 404) {
86
- log.verbose(`Waiting for ${progressNoun} status (HTTP 404); retrying.`);
87
- }
88
- else if (operation && operation.status !== lastStatus) {
89
- log.verbose(`${progressNoun} operation status: ${operation.status}.`);
90
- lastStatus = operation.status;
91
- }
92
- }
93
- const idleFor = Date.now() - lastLogAt;
94
- if (idleFor >= nextIdleSpinnerUpdateAt) {
95
- const elapsedIdleMs = IDLE_SPINNER_DELAY_MS +
96
- Math.floor((idleFor - IDLE_SPINNER_DELAY_MS) / IDLE_SPINNER_UPDATE_INTERVAL_MS) *
97
- IDLE_SPINNER_UPDATE_INTERVAL_MS;
98
- const elapsedIdleSeconds = elapsedIdleMs / 1000;
99
- const text = `No new activity for ${elapsedIdleSeconds} seconds. The ${progressNoun} is still running on Sanity servers. You can safely exit and check status later with \`${runCommand(bin)} blueprints info\`.`;
100
- if (idleSpinner)
101
- idleSpinner.start(text);
102
- else
103
- idleSpinner = log.ora(text).start();
104
- nextIdleSpinnerUpdateAt = elapsedIdleMs + IDLE_SPINNER_UPDATE_INTERVAL_MS;
105
- }
106
- await sleep(OPERATION_POLL_INTERVAL_MS);
107
- }
108
- }
109
- catch (error) {
110
- if (logStreamCleanup)
111
- logStreamCleanup();
112
- throw error;
113
- }
114
- finally {
115
- stopIdleSpinner();
116
- log.clearProgress();
117
- }
58
+ },
59
+ onStartLogPolling(options) {
60
+ return setupLogPolling({
61
+ stackId,
62
+ operationId,
63
+ auth,
64
+ fetchFn,
65
+ onActivity: options.onActivity,
66
+ onLogEntry: options.onLogEntry,
67
+ });
68
+ },
69
+ getIdleMessage() {
70
+ return `The ${progressNoun} is still running on Sanity servers.`;
71
+ },
72
+ });
118
73
  }
@@ -9,6 +9,7 @@ import { CI_SETUP_DOCS_URL } from './constants.js';
9
9
  import { presentBlueprintParserErrors } from './utils/display/errors.js';
10
10
  import { Logger } from './utils/logger.js';
11
11
  import { styleText } from './utils/style-text.js';
12
+ import { createTracedFetch } from './utils/traced-fetch.js';
12
13
  import { validTokenOrErrorMessage } from './utils/validated-token.js';
13
14
  /**
14
15
  * Fallback error-to-hint patterns for RuntimeCommand.catch().
@@ -378,7 +379,11 @@ export class ResolvedCommand extends RuntimeCommand {
378
379
  // 8. Deployed Stack
379
380
  if (needsDeployedStack) {
380
381
  const spinner = log.ora('Loading Stack deployment...').start();
381
- const response = await getStack({ stackId: this.stackId, auth: this.auth, logger: log });
382
+ const response = await getStack({
383
+ stackId: this.stackId,
384
+ auth: this.auth,
385
+ fetchFn: createTracedFetch(log),
386
+ });
382
387
  if (!response.ok) {
383
388
  spinner.fail('Could not load Stack deployment');
384
389
  this.error('Missing Stack deployment', {
@@ -1,5 +1,6 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import { organizationIdFlagConfig, projectIdFlagConfig, ResolvedCommand, stackFlagConfig, } from '../../baseCommands.js';
3
+ import { RUNTIME_CLI_NAME } from '../../config.js';
3
4
  import { blueprintDeployCore } from '../../cores/blueprints/deploy.js';
4
5
  import { Logger } from '../../utils/logger.js';
5
6
  import { INSTALLER_OPTIONS } from '../../utils/types.js';
@@ -79,6 +80,7 @@ Exit codes: 0 deployed, 2 deployment failed, 75 deployment accepted but completi
79
80
  scopeId: this.scopeId,
80
81
  deployedStack: this.deployedStack,
81
82
  blueprint: this.blueprint,
83
+ cliVersion: this.config.name === RUNTIME_CLI_NAME ? undefined : this.config.version,
82
84
  validateResources: this.flags['validate-resources'],
83
85
  flags: this.flags,
84
86
  });
package/dist/config.d.ts CHANGED
@@ -17,6 +17,7 @@ declare const _default: {
17
17
  isLive: boolean;
18
18
  apiUrl: string;
19
19
  populusApiUrl: string;
20
+ studioDomain: string;
20
21
  isCi: boolean;
21
22
  token: string | null;
22
23
  };
package/dist/config.js CHANGED
@@ -40,6 +40,11 @@ const apiUrls = {
40
40
  default: 'http://api.sanity/',
41
41
  };
42
42
  const apiUrl = sanityEnv ? (apiUrls[sanityEnv] ?? sanityEnv) : apiUrls.default;
43
+ const studioDomains = {
44
+ production: 'sanity.studio',
45
+ staging: 'studio.sanity.work',
46
+ };
47
+ const studioDomain = studioDomains[sanityEnv] ?? studioDomains.production;
43
48
  // allow override of populus URL
44
49
  const populusApiUrl = env.POPULUS_API_URL ?? apiUrl;
45
50
  /**
@@ -78,6 +83,7 @@ export default {
78
83
  isLive,
79
84
  apiUrl,
80
85
  populusApiUrl,
86
+ studioDomain,
81
87
  isCi,
82
88
  get token() {
83
89
  return _token === undefined ? loadToken() : _token;
@@ -51,6 +51,7 @@ export declare const LABEL_PUBSUB = "PubSub";
51
51
  export declare const LABEL_SCHEDULED = "Scheduled";
52
52
  export declare const LABEL_SYNC_TAG_INVALIDATE = "Sync Tag Invalidate";
53
53
  export declare const CI_SETUP_DOCS_URL = "https://www.sanity.io/docs/blueprints/deploy-blueprints-from-ci";
54
+ export declare const STACKS_DOCS_URL = "https://www.sanity.io/docs/blueprints/stacks-and-scope";
54
55
  export declare const STEP_PHASE_GLYPH: Record<DurableStepPhase, string>;
55
56
  /** Stands in for environment variable values, which are never echoed back to the user */
56
57
  export declare const ENV_MASK = "********";
package/dist/constants.js CHANGED
@@ -56,6 +56,7 @@ export const LABEL_PUBSUB = 'PubSub';
56
56
  export const LABEL_SCHEDULED = 'Scheduled';
57
57
  export const LABEL_SYNC_TAG_INVALIDATE = 'Sync Tag Invalidate';
58
58
  export const CI_SETUP_DOCS_URL = 'https://www.sanity.io/docs/blueprints/deploy-blueprints-from-ci';
59
+ export const STACKS_DOCS_URL = 'https://www.sanity.io/docs/blueprints/stacks-and-scope';
59
60
  export const STEP_PHASE_GLYPH = {
60
61
  start: '▶',
61
62
  complete: '✔',
@@ -1,6 +1,6 @@
1
+ import { formatDateTime } from '@sanity/cli-core/dates';
1
2
  import { patchConfigFile, writeConfigFile, } from '../../actions/blueprints/config.js';
2
3
  import { resolveStackIdByNameOrId } from '../../actions/blueprints/stacks.js';
3
- import { formatDateTime } from '../../utils/display/dates.js';
4
4
  import { filePathRelativeToCwd, labeledId, warn } from '../../utils/display/presenters.js';
5
5
  import { CancelledError, runScopeAndStackWizard } from '../../utils/display/prompt.js';
6
6
  import { runCommand } from '../../utils/package-manager.js';
@@ -8,6 +8,8 @@ export interface BlueprintDeployOptions extends CoreConfig {
8
8
  scopeId: string;
9
9
  deployedStack: Stack;
10
10
  blueprint: ReadBlueprintResult;
11
+ /** Version of the CLI that hosts this plugin. Absent when the runtime CLI runs on its own. */
12
+ cliVersion?: string;
11
13
  flags: {
12
14
  'fn-installer'?: string;
13
15
  'no-wait'?: boolean;