@sanity/runtime-cli 17.12.1 → 17.13.0-next.g06897b0
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 +21 -21
- package/dist/actions/blueprints/assets.d.ts +2 -0
- package/dist/actions/blueprints/assets.js +4 -3
- package/dist/actions/blueprints/pre-deploy.d.ts +6 -3
- package/dist/actions/blueprints/pre-deploy.js +12 -1
- package/dist/actions/blueprints/process-assets.d.ts +1 -0
- package/dist/actions/blueprints/process-assets.js +1 -0
- package/dist/commands/blueprints/deploy.js +2 -0
- package/dist/commands/functions/dev.js +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.js +6 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/cores/blueprints/config.js +1 -1
- package/dist/cores/blueprints/deploy.d.ts +2 -0
- package/dist/cores/blueprints/deploy.js +10 -1
- package/dist/cores/blueprints/doctor.js +1 -1
- package/dist/cores/blueprints/mint-deploy-token.js +2 -1
- package/dist/server/app.d.ts +2 -1
- package/dist/server/app.js +4 -1
- package/dist/server/static/vendor/vendor.bundle.js +21791 -25197
- package/dist/server/static/web-socket.js +4 -2
- package/dist/utils/blueprints/deploy-metadata.d.ts +10 -0
- package/dist/utils/blueprints/deploy-metadata.js +17 -0
- package/dist/utils/display/blueprints-formatting.d.ts +1 -0
- package/dist/utils/display/blueprints-formatting.js +17 -3
- package/dist/utils/display/dates.d.ts +0 -2
- package/dist/utils/display/dates.js +0 -25
- package/dist/utils/display/functions-formatting.js +1 -1
- package/dist/utils/display/prompt.js +5 -2
- package/dist/utils/display/resources-formatting.d.ts +6 -0
- package/dist/utils/display/resources-formatting.js +17 -0
- package/dist/utils/functions/dependency-info.d.ts +30 -0
- package/dist/utils/functions/dependency-info.js +57 -0
- package/dist/utils/functions/prepare-asset.js +17 -18
- package/dist/utils/studios/prepare-asset.js +31 -6
- package/dist/utils/types.d.ts +17 -0
- package/oclif.manifest.json +2 -2
- package/package.json +16 -18
- package/skills/functions-dev.md +1 -1
|
@@ -2,8 +2,10 @@ import apiConstructor from '../api.js'
|
|
|
2
2
|
|
|
3
3
|
const api = apiConstructor()
|
|
4
4
|
|
|
5
|
-
// Create WebSocket connection
|
|
6
|
-
|
|
5
|
+
// Create WebSocket connection on the same origin the emulator is served from,
|
|
6
|
+
// so it follows the --host and --port the server was started with.
|
|
7
|
+
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
|
|
8
|
+
const socket = new WebSocket(`${protocol}//${window.location.host}`)
|
|
7
9
|
|
|
8
10
|
// Connection opened
|
|
9
11
|
socket.addEventListener('open', () => {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Facts about the environment that runs a deploy.
|
|
3
|
+
* Sent as the `client` part of deploy metadata. Keys with an `undefined` value drop out of the JSON body.
|
|
4
|
+
*/
|
|
5
|
+
export declare function getClientMetadata({ cliVersion, dir, }: {
|
|
6
|
+
/** Version of the CLI that hosts this plugin, when known */
|
|
7
|
+
cliVersion?: string;
|
|
8
|
+
/** Directory of the Blueprint file, used to detect the package manager */
|
|
9
|
+
dir: string;
|
|
10
|
+
}): Record<string, unknown>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { platform, versions } from 'node:process';
|
|
2
|
+
import config, { RUNTIME_CLI_VERSION } from '../../config.js';
|
|
3
|
+
import { detectPackageManager } from '../package-manager.js';
|
|
4
|
+
/**
|
|
5
|
+
* Facts about the environment that runs a deploy.
|
|
6
|
+
* Sent as the `client` part of deploy metadata. Keys with an `undefined` value drop out of the JSON body.
|
|
7
|
+
*/
|
|
8
|
+
export function getClientMetadata({ cliVersion, dir, }) {
|
|
9
|
+
return {
|
|
10
|
+
cliVersion,
|
|
11
|
+
runtimeCliVersion: RUNTIME_CLI_VERSION,
|
|
12
|
+
nodeVersion: versions.node,
|
|
13
|
+
nodePackageManager: detectPackageManager(dir),
|
|
14
|
+
platform,
|
|
15
|
+
ci: config.isCi,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -4,6 +4,7 @@ import type { DeploymentPlan } from '../../actions/blueprints/stacks.js';
|
|
|
4
4
|
import { type BlueprintResourceRecord, type Stack, type StackSummary } from '../types.js';
|
|
5
5
|
export declare function formatTitle(title: string, name: string): string;
|
|
6
6
|
export declare function formatDeployedResourceTree(resources: BlueprintResourceRecord[] | undefined, verbose?: boolean): string;
|
|
7
|
+
export declare function formatStudioUrls(resources: BlueprintResourceRecord[] | undefined): string[];
|
|
7
8
|
export declare function formatResourceTree(resources: BlueprintResource[] | undefined, verbose?: boolean): string;
|
|
8
9
|
export declare function formatStackInfo(stack: Stack | StackSummary | Blueprint, isCurrentStack?: boolean): string;
|
|
9
10
|
export declare function formatStacksListing(stacks: StackSummary[], currentStackId?: string): string;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { formatDateTime, formatElapsed } from '@sanity/cli-core/dates';
|
|
1
2
|
import { treeify } from 'array-treeify';
|
|
2
3
|
import { SANITY_ACCESS_ROBOT, SANITY_ACCESS_ROLE, SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_DURABLE, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_PUBSUB, SANITY_FUNCTION_SCHEDULED, SANITY_FUNCTION_SYNC_TAG_INVALIDATE, SANITY_PROJECT, SANITY_PROJECT_CORS, SANITY_PROJECT_DATASET, SANITY_PROJECT_WEBHOOK, SANITY_STUDIO, } from '../../constants.js';
|
|
3
4
|
import { styleText } from '../style-text.js';
|
|
4
5
|
import { isCorsOriginResource, isDatasetResource, isRobotResource, isRoleResource, isStudioResource, isWebhookResource, } from '../types.js';
|
|
5
|
-
import { formatDateTime, formatDuration } from './dates.js';
|
|
6
6
|
import { capitalize, niceId, renderSection } from './presenters.js';
|
|
7
|
-
import { arrayifyCors, arrayifyDataset, arrayifyFunction, arrayifyRobot, arrayifyRole, arrayifyStudio, arrayifyWebhook, } from './resources-formatting.js';
|
|
7
|
+
import { arrayifyCors, arrayifyDataset, arrayifyFunction, arrayifyRobot, arrayifyRole, arrayifyStudio, arrayifyWebhook, studioHost, studioUrl, } from './resources-formatting.js';
|
|
8
8
|
const functionCategory = {
|
|
9
9
|
label: 'Functions',
|
|
10
10
|
displayNameAttribute: 'displayName',
|
|
@@ -165,6 +165,19 @@ export function formatDeployedResourceTree(resources, verbose = false) {
|
|
|
165
165
|
(res) => ({ name: res.name, type: res.type, ...(res.resolvedParameters ?? res.parameters) }), verbose);
|
|
166
166
|
return `${treeify(output)}\n`;
|
|
167
167
|
}
|
|
168
|
+
export function formatStudioUrls(resources) {
|
|
169
|
+
if (!resources)
|
|
170
|
+
return [];
|
|
171
|
+
const lines = [];
|
|
172
|
+
for (const res of resources) {
|
|
173
|
+
if (res.type !== SANITY_STUDIO)
|
|
174
|
+
continue;
|
|
175
|
+
const host = studioHost(res.resolvedParameters ?? res.parameters);
|
|
176
|
+
if (host)
|
|
177
|
+
lines.push(`Studio ${styleText(['bold', 'green'], res.name)} ${studioUrl(host)}`);
|
|
178
|
+
}
|
|
179
|
+
return lines;
|
|
180
|
+
}
|
|
168
181
|
export function formatResourceTree(resources, verbose = false) {
|
|
169
182
|
if (!resources || resources.length === 0)
|
|
170
183
|
return ' Zero resources';
|
|
@@ -207,7 +220,8 @@ export function formatStackInfo(stack, isCurrentStack = false) {
|
|
|
207
220
|
if (time)
|
|
208
221
|
parts.push(styleText('dim', formatDateTime(time)));
|
|
209
222
|
if (op.completedAt && op.createdAt) {
|
|
210
|
-
|
|
223
|
+
const duration = new Date(op.completedAt).getTime() - new Date(op.createdAt).getTime();
|
|
224
|
+
parts.push(styleText('yellow', `(${formatElapsed(duration)})`));
|
|
211
225
|
}
|
|
212
226
|
rows.push(['Operation', parts.join(' ')]);
|
|
213
227
|
if (op.userMessage)
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
type DateInput = string | number | Date;
|
|
2
2
|
export declare function formatDate(input: DateInput): string;
|
|
3
3
|
export declare function formatTime(input: DateInput): string;
|
|
4
|
-
export declare function formatDateTime(input: DateInput): string;
|
|
5
|
-
export declare function formatDuration(startDate: string, endDate: string): string;
|
|
6
4
|
export {};
|
|
@@ -19,28 +19,3 @@ export function formatTime(input) {
|
|
|
19
19
|
const s = String(d.getSeconds()).padStart(2, '0');
|
|
20
20
|
return `${h}:${m}:${s}`;
|
|
21
21
|
}
|
|
22
|
-
export function formatDateTime(input) {
|
|
23
|
-
const d = toDate(input);
|
|
24
|
-
if (Number.isNaN(d.getTime()))
|
|
25
|
-
return '';
|
|
26
|
-
return `${formatDate(d)} ${formatTime(d)}`;
|
|
27
|
-
}
|
|
28
|
-
export function formatDuration(startDate, endDate) {
|
|
29
|
-
const start = new Date(startDate).getTime();
|
|
30
|
-
const end = new Date(endDate).getTime();
|
|
31
|
-
if (start > end)
|
|
32
|
-
return 'Invalid duration';
|
|
33
|
-
const durationMs = end - start;
|
|
34
|
-
if (durationMs < 1000)
|
|
35
|
-
return `${durationMs}ms`;
|
|
36
|
-
if (durationMs < 60000)
|
|
37
|
-
return `${Math.round(durationMs / 1000)}s`;
|
|
38
|
-
if (durationMs < 3600000) {
|
|
39
|
-
const minutes = Math.floor(durationMs / 60000);
|
|
40
|
-
const seconds = Math.floor((durationMs % 60000) / 1000);
|
|
41
|
-
return `${minutes}m ${seconds}s`;
|
|
42
|
-
}
|
|
43
|
-
const hours = Math.floor(durationMs / 3600000);
|
|
44
|
-
const minutes = Math.floor((durationMs % 3600000) / 60000);
|
|
45
|
-
return `${hours}h ${minutes}m`;
|
|
46
|
-
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { formatDateTime } from '@sanity/cli-core/dates';
|
|
1
2
|
import { SANITY_FUNCTION_PREFIX } from '../../constants.js';
|
|
2
3
|
import { styleText } from '../style-text.js';
|
|
3
|
-
import { formatDateTime } from './dates.js';
|
|
4
4
|
import { niceId, renderSection } from './presenters.js';
|
|
5
5
|
export function formatFunctionsList(functions, options = {}) {
|
|
6
6
|
if (!options.verbose)
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { confirm, input, Separator, select } from '@inquirer/prompts';
|
|
2
2
|
import { createEmptyStack, listStacks } from '../../actions/blueprints/stacks.js';
|
|
3
3
|
import { groupProjectsByOrganization } from '../../actions/sanity/projects.js';
|
|
4
|
+
import { STACKS_DOCS_URL } from '../../constants.js';
|
|
4
5
|
import { styleText } from '../style-text.js';
|
|
5
6
|
import { niceId } from './presenters.js';
|
|
6
7
|
// --- Wizard infrastructure ---
|
|
7
8
|
export const BACK = Symbol('back');
|
|
9
|
+
/** Shown under the highlighted Stack choice; a printed line would break the wizard's two-line erase on Back */
|
|
10
|
+
const STACK_DEFINITION = `A Stack is the deployed copy of a Blueprint: the live resources on Sanity's side. ${STACKS_DOCS_URL}`;
|
|
8
11
|
/** Thrown when the user picks "Cancel" in a prompt. Cores translate it to a clean result. */
|
|
9
12
|
export class CancelledError extends Error {
|
|
10
13
|
constructor(message = 'Cancelled by user.') {
|
|
@@ -270,7 +273,7 @@ export async function promptForStack({ scopeType, scopeId, token, logger, allowB
|
|
|
270
273
|
if (stacks.length > 0) {
|
|
271
274
|
const stackChoices = [
|
|
272
275
|
new Separator(styleText('underline', 'Create a new Stack:')),
|
|
273
|
-
{ name: styleText('bold', 'New Stack'), value: NEW_STACK_ID },
|
|
276
|
+
{ name: styleText('bold', 'New Stack'), value: NEW_STACK_ID, description: STACK_DEFINITION },
|
|
274
277
|
new Separator(styleText('underline', 'Use an existing Stack:')),
|
|
275
278
|
...stacks.map((s) => ({
|
|
276
279
|
name: `"${s.name}" ${niceId(s.id)} ${styleText('dim', `(${s.resourceCount} ${s.resourceCount === 1 ? 'resource' : 'resources'})`)}`,
|
|
@@ -301,7 +304,7 @@ export async function promptForStack({ scopeType, scopeId, token, logger, allowB
|
|
|
301
304
|
{
|
|
302
305
|
name: 'Create the first Stack',
|
|
303
306
|
value: NEW_STACK_ID,
|
|
304
|
-
description:
|
|
307
|
+
description: STACK_DEFINITION,
|
|
305
308
|
},
|
|
306
309
|
{ name: 'Cancel', value: CANCEL, description: 'Stop here and change nothing' },
|
|
307
310
|
], allowBack),
|
|
@@ -5,5 +5,11 @@ export declare function arrayifyDataset(resource: BlueprintDatasetResource): Tre
|
|
|
5
5
|
export declare function arrayifyFunction(fn: BlueprintResource): TreeInput;
|
|
6
6
|
export declare function arrayifyRobot(resource: BlueprintRobotResource): TreeInput;
|
|
7
7
|
export declare function arrayifyRole(resource: BlueprintRoleResource): TreeInput;
|
|
8
|
+
/**
|
|
9
|
+
* Hostname label of a Studio: the resolved `appHost` when present, else the authored `slug`.
|
|
10
|
+
* undefined when a `$.resources` reference
|
|
11
|
+
*/
|
|
12
|
+
export declare function studioHost(params: Record<string, unknown>): string | undefined;
|
|
13
|
+
export declare function studioUrl(host: string): string;
|
|
8
14
|
export declare function arrayifyStudio(resource: BlueprintStudioResource): TreeInput;
|
|
9
15
|
export declare function arrayifyWebhook(resource: BlueprintDocumentWebhookResource): TreeInput;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import config from '../../config.js';
|
|
1
2
|
import { hasEventSource, isLocalFunctionResource, isScheduleEvent, } from '../../utils/types.js';
|
|
2
3
|
import { styleText } from '../style-text.js';
|
|
3
4
|
import { niceId } from './presenters.js';
|
|
@@ -120,8 +121,24 @@ export function arrayifyRole(resource) {
|
|
|
120
121
|
}
|
|
121
122
|
return details;
|
|
122
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Hostname label of a Studio: the resolved `appHost` when present, else the authored `slug`.
|
|
126
|
+
* undefined when a `$.resources` reference
|
|
127
|
+
*/
|
|
128
|
+
export function studioHost(params) {
|
|
129
|
+
const host = params.appHost ?? params.slug;
|
|
130
|
+
if (typeof host !== 'string' || host.length === 0 || host.startsWith('$.'))
|
|
131
|
+
return undefined;
|
|
132
|
+
return host;
|
|
133
|
+
}
|
|
134
|
+
export function studioUrl(host) {
|
|
135
|
+
return `https://${host}.${config.studioDomain}`;
|
|
136
|
+
}
|
|
123
137
|
export function arrayifyStudio(resource) {
|
|
124
138
|
const details = [formatLabeledValue('appHost', resource.slug)];
|
|
139
|
+
const host = studioHost(resource);
|
|
140
|
+
if (host)
|
|
141
|
+
details.push(formatLabeledValue('url', studioUrl(host)));
|
|
125
142
|
if (resource.project)
|
|
126
143
|
details.push(formatLabeledValue('project', niceId(resource.project)));
|
|
127
144
|
return details;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Where a function's dependency list came from. */
|
|
2
|
+
export type DependencySource = 'manifest' | 'autoinstall' | 'bundled' | 'none';
|
|
3
|
+
export type DependencyInfo = {
|
|
4
|
+
dependencySource: DependencySource;
|
|
5
|
+
dependencyCount: number;
|
|
6
|
+
/** Declared versions */
|
|
7
|
+
dependencies?: Record<string, string>;
|
|
8
|
+
/** Source files the Arc treeshaker parsed - autoinstall only */
|
|
9
|
+
parsedFileCount?: number;
|
|
10
|
+
};
|
|
11
|
+
/** The package.json Architect generates during autoinstall. */
|
|
12
|
+
export type AutoInstallManifest = {
|
|
13
|
+
dependencies?: Record<string, string>;
|
|
14
|
+
_parsed?: string[];
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Read and delete the manifest Architect generated during autoinstall.
|
|
18
|
+
* Arc moves it to `node_modules/_arc-autoinstall/` after the install.
|
|
19
|
+
* Has a timestamp, so it must be deleted to avoid hash changes on every deploy.
|
|
20
|
+
*/
|
|
21
|
+
export declare function takeAutoInstallManifest(installDir: string): Promise<AutoInstallManifest | undefined>;
|
|
22
|
+
/**
|
|
23
|
+
* Describe a function's dependencies from the manifest that declared them.
|
|
24
|
+
* The user's own package.json wins. Otherwise the Architect autoinstall manifest is used.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getDependencyInfo({ sourceDir, autoInstall, bundled, }: {
|
|
27
|
+
sourceDir: string;
|
|
28
|
+
autoInstall?: AutoInstallManifest;
|
|
29
|
+
bundled: boolean;
|
|
30
|
+
}): Promise<DependencyInfo>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { readFile, rm } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Read and delete the manifest Architect generated during autoinstall.
|
|
5
|
+
* Arc moves it to `node_modules/_arc-autoinstall/` after the install.
|
|
6
|
+
* Has a timestamp, so it must be deleted to avoid hash changes on every deploy.
|
|
7
|
+
*/
|
|
8
|
+
export async function takeAutoInstallManifest(installDir) {
|
|
9
|
+
const file = path.join(installDir, 'node_modules', '_arc-autoinstall', 'package.json');
|
|
10
|
+
let manifest;
|
|
11
|
+
try {
|
|
12
|
+
manifest = JSON.parse(await readFile(file, 'utf8'));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// no autoinstall happened, or the file is unreadable
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
await rm(file, { force: true });
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// best effort
|
|
22
|
+
}
|
|
23
|
+
return manifest;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Describe a function's dependencies from the manifest that declared them.
|
|
27
|
+
* The user's own package.json wins. Otherwise the Architect autoinstall manifest is used.
|
|
28
|
+
*/
|
|
29
|
+
export async function getDependencyInfo({ sourceDir, autoInstall, bundled, }) {
|
|
30
|
+
const declared = await readDeclaredDependencies(sourceDir);
|
|
31
|
+
if (declared && Object.keys(declared).length > 0) {
|
|
32
|
+
return {
|
|
33
|
+
dependencySource: 'manifest',
|
|
34
|
+
dependencyCount: Object.keys(declared).length,
|
|
35
|
+
dependencies: declared,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (autoInstall) {
|
|
39
|
+
const dependencies = autoInstall.dependencies ?? {};
|
|
40
|
+
return {
|
|
41
|
+
dependencySource: 'autoinstall',
|
|
42
|
+
dependencyCount: Object.keys(dependencies).length,
|
|
43
|
+
dependencies,
|
|
44
|
+
parsedFileCount: autoInstall._parsed?.length,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return { dependencySource: bundled ? 'bundled' : 'none', dependencyCount: 0 };
|
|
48
|
+
}
|
|
49
|
+
async function readDeclaredDependencies(dir) {
|
|
50
|
+
try {
|
|
51
|
+
const pkg = JSON.parse(await readFile(path.join(dir, 'package.json'), 'utf8'));
|
|
52
|
+
return pkg.dependencies;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import fs, { existsSync } from 'node:fs';
|
|
2
|
-
import {
|
|
2
|
+
import { stat } from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import AdmZip from 'adm-zip';
|
|
5
5
|
import { CONVERT_BYTES_TO_MB, MAX_ASSET_SIZE } from '../../constants.js';
|
|
6
|
+
import { getDependencyInfo, takeAutoInstallManifest, } from './dependency-info.js';
|
|
6
7
|
import { detectNativeModules, errorMessage } from './detect-native-modules.js';
|
|
7
8
|
import { isFolderLargerThan } from './is-folder-larger-than.js';
|
|
8
9
|
import { resolveResourceDependencies } from './resolve-dependencies.js';
|
|
@@ -16,6 +17,8 @@ export async function prepareAsset({ blueprintFileDir, resource, installer, }) {
|
|
|
16
17
|
let functionPath = resolvedFunctionPath;
|
|
17
18
|
let cleanup = async () => { };
|
|
18
19
|
let wasBundled = false;
|
|
20
|
+
let dependenciesInstalled = false;
|
|
21
|
+
let autoInstall;
|
|
19
22
|
const shouldTranspile = await shouldTranspileFunction(resource, functionPath);
|
|
20
23
|
if (shouldTranspile.transpile) {
|
|
21
24
|
try {
|
|
@@ -42,7 +45,8 @@ export async function prepareAsset({ blueprintFileDir, resource, installer, }) {
|
|
|
42
45
|
transpiled: shouldTranspile.transpile,
|
|
43
46
|
installer,
|
|
44
47
|
});
|
|
45
|
-
await
|
|
48
|
+
autoInstall = await takeAutoInstallManifest(functionPath);
|
|
49
|
+
dependenciesInstalled = true;
|
|
46
50
|
}
|
|
47
51
|
if (existsSync(path.join(functionPath, 'node_modules'))) {
|
|
48
52
|
const modulesPath = path.join(functionPath, 'node_modules');
|
|
@@ -72,6 +76,17 @@ export async function prepareAsset({ blueprintFileDir, resource, installer, }) {
|
|
|
72
76
|
fileExtension: 'zip',
|
|
73
77
|
assetParameter: 'src',
|
|
74
78
|
cleanup,
|
|
79
|
+
metadata: {
|
|
80
|
+
transpiled: shouldTranspile.transpile,
|
|
81
|
+
bundled: wasBundled,
|
|
82
|
+
dependenciesInstalled,
|
|
83
|
+
...(await getDependencyInfo({
|
|
84
|
+
sourceDir: resolvedFunctionPath,
|
|
85
|
+
autoInstall,
|
|
86
|
+
bundled: wasBundled,
|
|
87
|
+
})),
|
|
88
|
+
assetBytes: zipEntrySize,
|
|
89
|
+
},
|
|
75
90
|
};
|
|
76
91
|
}
|
|
77
92
|
catch (err) {
|
|
@@ -98,19 +113,3 @@ export async function pathToZip(path) {
|
|
|
98
113
|
zip.addLocalFile(path, '', 'index.js');
|
|
99
114
|
return zip;
|
|
100
115
|
}
|
|
101
|
-
/**
|
|
102
|
-
* Remove file that contains the current date to prevent the hash from changing in the final asset.
|
|
103
|
-
* @param functionPath The path to the built function
|
|
104
|
-
*/
|
|
105
|
-
async function removeArcAutoInstallPackageJson(functionPath) {
|
|
106
|
-
const arcAutoInstallPackageJson = path.join(functionPath, 'node_modules', '_arc-autoinstall', 'package.json');
|
|
107
|
-
try {
|
|
108
|
-
const fileStat = await stat(arcAutoInstallPackageJson);
|
|
109
|
-
if (fileStat.isFile()) {
|
|
110
|
-
await rm(arcAutoInstallPackageJson);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
catch {
|
|
114
|
-
// ignore errors
|
|
115
|
-
}
|
|
116
|
-
}
|
|
@@ -5,12 +5,12 @@ import path, { basename, dirname, join } from 'node:path';
|
|
|
5
5
|
import { pipeline } from 'node:stream/promises';
|
|
6
6
|
import { createGzip } from 'node:zlib';
|
|
7
7
|
import { buildStudio, compareDependencyVersions } from '@sanity/cli-build/_internal/build';
|
|
8
|
+
import { getLocalPackageVersion } from '@sanity/cli-core';
|
|
8
9
|
import { pack } from 'tar-fs';
|
|
9
10
|
export async function prepareStudioAsset({ blueprintFileDir, resource, }) {
|
|
10
|
-
// validate resource has an
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
!resource.externalId) {
|
|
11
|
+
// validate resource has an appId
|
|
12
|
+
const appId = getAppId(resource);
|
|
13
|
+
if (!appId) {
|
|
14
14
|
return {
|
|
15
15
|
success: false,
|
|
16
16
|
async cleanup() {
|
|
@@ -23,6 +23,8 @@ export async function prepareStudioAsset({ blueprintFileDir, resource, }) {
|
|
|
23
23
|
// no op
|
|
24
24
|
};
|
|
25
25
|
const studioPath = path.resolve(blueprintFileDir, resource.src);
|
|
26
|
+
// set only when auto-updates is on, because the build compares versions only then
|
|
27
|
+
let mismatchedPackages;
|
|
26
28
|
try {
|
|
27
29
|
const studioPathStat = await stat(studioPath);
|
|
28
30
|
if (!studioPathStat.isDirectory()) {
|
|
@@ -48,14 +50,15 @@ export async function prepareStudioAsset({ blueprintFileDir, resource, }) {
|
|
|
48
50
|
await rm(outDir, { force: true, recursive: true });
|
|
49
51
|
await rm(outFile, { force: true });
|
|
50
52
|
};
|
|
51
|
-
const appId = resource.externalId;
|
|
52
53
|
try {
|
|
53
54
|
await buildStudio({
|
|
54
55
|
appId,
|
|
55
56
|
autoUpdatesEnabled: resource.autoUpdates.enabled,
|
|
56
57
|
checkAppId() { },
|
|
57
58
|
async compareDependencyVersions(packages) {
|
|
58
|
-
|
|
59
|
+
const result = await compareDependencyVersions(packages, studioPath, { appId });
|
|
60
|
+
mismatchedPackages = result.mismatched.map((m) => m.pkg);
|
|
61
|
+
return result;
|
|
59
62
|
},
|
|
60
63
|
determineBasePath() {
|
|
61
64
|
return resource.basePath ?? '/';
|
|
@@ -112,6 +115,11 @@ export async function prepareStudioAsset({ blueprintFileDir, resource, }) {
|
|
|
112
115
|
outputBuffer: buffer,
|
|
113
116
|
fileExtension: 'tar.gz',
|
|
114
117
|
assetParameter: 'src',
|
|
118
|
+
metadata: {
|
|
119
|
+
sanityVersion: await getInstalledSanityVersion(studioPath),
|
|
120
|
+
mismatchedPackages,
|
|
121
|
+
assetBytes: buffer.length,
|
|
122
|
+
},
|
|
115
123
|
};
|
|
116
124
|
}
|
|
117
125
|
catch (error) {
|
|
@@ -139,3 +147,20 @@ function createSilentOutput() {
|
|
|
139
147
|
},
|
|
140
148
|
};
|
|
141
149
|
}
|
|
150
|
+
async function getInstalledSanityVersion(studioPath) {
|
|
151
|
+
try {
|
|
152
|
+
return (await getLocalPackageVersion('sanity', studioPath)) ?? undefined;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function getAppId(resource) {
|
|
159
|
+
if ('externalId' in resource && typeof resource.externalId === 'string' && resource.externalId) {
|
|
160
|
+
return resource.externalId;
|
|
161
|
+
}
|
|
162
|
+
else if (resource.lifecycle?.ownershipAction?.type === 'attach') {
|
|
163
|
+
return resource.lifecycle.ownershipAction.id;
|
|
164
|
+
}
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
package/dist/utils/types.d.ts
CHANGED
|
@@ -87,7 +87,22 @@ export interface StackMutation {
|
|
|
87
87
|
};
|
|
88
88
|
userMessage?: string;
|
|
89
89
|
preDeploy?: boolean;
|
|
90
|
+
metadata?: DeployMetadata;
|
|
90
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Facts about one deploy, stored with the Blueprint on the server.
|
|
94
|
+
* The API accepts it on stack update and never returns it.
|
|
95
|
+
*/
|
|
96
|
+
export interface DeployMetadata {
|
|
97
|
+
/** The environment that ran the deploy */
|
|
98
|
+
client?: Record<string, unknown>;
|
|
99
|
+
/** One entry per resource that had an asset prepared, plus what was learned while preparing it */
|
|
100
|
+
resources?: ResourceMetadata[];
|
|
101
|
+
}
|
|
102
|
+
export type ResourceMetadata = {
|
|
103
|
+
name: string;
|
|
104
|
+
type: string;
|
|
105
|
+
} & Record<string, unknown>;
|
|
91
106
|
/** @internal */
|
|
92
107
|
export interface BuildPayloadOptions {
|
|
93
108
|
data?: string;
|
|
@@ -302,6 +317,8 @@ export type AssetPrepResult = {
|
|
|
302
317
|
fileExtension: string;
|
|
303
318
|
assetParameter: string;
|
|
304
319
|
cleanup: () => Promise<void>;
|
|
320
|
+
/** Facts learned about the resource while preparing its asset. Sent as deploy metadata. */
|
|
321
|
+
metadata?: Record<string, unknown>;
|
|
305
322
|
} | {
|
|
306
323
|
success: false;
|
|
307
324
|
cleanup: () => Promise<void>;
|
package/oclif.manifest.json
CHANGED
|
@@ -2042,7 +2042,7 @@
|
|
|
2042
2042
|
"functions:dev": {
|
|
2043
2043
|
"aliases": [],
|
|
2044
2044
|
"args": {},
|
|
2045
|
-
"description": "Runs a local, web-based development server to test your functions before deploying.\n\nOpen the emulator in your browser to interactively test your functions with the payload editor.\n\nOptionally, set the host and port with the --host and --port flags.
|
|
2045
|
+
"description": "Runs a local, web-based development server to test your functions before deploying.\n\nOpen the emulator in your browser to interactively test your functions with the payload editor.\n\nOptionally, set the host and port with the --host and --port flags. The live-reload WebSocket server shares this host and port. Function timeout can be configured with the --timeout flag.\n\nTo invoke a function with the CLI, use 'functions test'.",
|
|
2046
2046
|
"examples": [
|
|
2047
2047
|
"<%= config.bin %> <%= command.id %> --host 127.0.0.1 --port 3333",
|
|
2048
2048
|
"<%= config.bin %> <%= command.id %> --timeout 60"
|
|
@@ -3148,5 +3148,5 @@
|
|
|
3148
3148
|
]
|
|
3149
3149
|
}
|
|
3150
3150
|
},
|
|
3151
|
-
"version": "17.
|
|
3151
|
+
"version": "17.13.0-next.g06897b0"
|
|
3152
3152
|
}
|
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": "17.
|
|
4
|
+
"version": "17.13.0-next.g06897b0",
|
|
5
5
|
"author": "Sanity Runtime Team",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"sanity-run": "bin/run.js"
|
|
35
35
|
},
|
|
36
36
|
"scripts": {
|
|
37
|
-
"build": "npm run build:
|
|
38
|
-
"build:
|
|
37
|
+
"build": "npm run build:vendor && npm run build:ts && npm run build:static && npm run build:oclif",
|
|
38
|
+
"build:vendor": "vite build --config vite.vendor.config.mjs",
|
|
39
39
|
"build:oclif": "oclif manifest && oclif readme",
|
|
40
40
|
"build:ts": "shx rm -rf dist *.tsbuildinfo && tsc -b",
|
|
41
41
|
"build:static": "npm run copy:wrapper && npm run copy:design-tokens && npm run copy:server",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"postpack": "shx rm -f oclif.manifest.json",
|
|
53
53
|
"test": "npm run typecheck && vitest run && npm run test:depmgmt && npm run lint",
|
|
54
54
|
"test:depmgmt": "vitest run --config ./test-depmgmt/vitest.config.ts",
|
|
55
|
-
"test:coverage": "vitest run --coverage --coverage.include \"
|
|
55
|
+
"test:coverage": "vitest run --coverage --coverage.include \"src/**/*\" --coverage.exclude \"src/server/static/**\"",
|
|
56
56
|
"test:scenarios": "vitest run test/scenarios",
|
|
57
57
|
"test:playwright": "playwright install chromium && playwright install-deps chromium && playwright test",
|
|
58
58
|
"test:bp-version-smoke": "node ./test/blueprints-version-smoke.js",
|
|
@@ -64,16 +64,16 @@
|
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@architect/hydrate": "~6.0.2",
|
|
66
66
|
"@architect/inventory": "~6.1.0",
|
|
67
|
-
"@inquirer/prompts": "^8.7.
|
|
67
|
+
"@inquirer/prompts": "^8.7.2",
|
|
68
68
|
"@oclif/core": "^4.14.0",
|
|
69
69
|
"@oclif/plugin-help": "^6.3.0",
|
|
70
70
|
"@sanity-labs/oclif-plugin-skills-flag": "^0.2.1",
|
|
71
71
|
"@sanity/blueprints": "^0.24.0",
|
|
72
72
|
"@sanity/blueprints-parser": "^0.5.0",
|
|
73
|
-
"@sanity/cli-build": "^
|
|
74
|
-
"@sanity/cli-core": "^3.
|
|
75
|
-
"@sanity/client": "^8.
|
|
76
|
-
"adm-zip": "^0.6.
|
|
73
|
+
"@sanity/cli-build": "^6.3.0",
|
|
74
|
+
"@sanity/cli-core": "^3.7.0",
|
|
75
|
+
"@sanity/client": "^8.6.1",
|
|
76
|
+
"adm-zip": "^0.6.1",
|
|
77
77
|
"array-treeify": "^0.2.0",
|
|
78
78
|
"cardinal": "^2.1.1",
|
|
79
79
|
"empathic": "^2.0.1",
|
|
@@ -86,20 +86,19 @@
|
|
|
86
86
|
"strip-ansi": "^7.2.0",
|
|
87
87
|
"tar-fs": "^3.1.3",
|
|
88
88
|
"tar-stream": "^3.2.1",
|
|
89
|
-
"vite": "^8.
|
|
89
|
+
"vite": "^8.3.0",
|
|
90
90
|
"ws": "^8.21.3",
|
|
91
91
|
"xdg-basedir": "^5.1.0"
|
|
92
92
|
},
|
|
93
93
|
"devDependencies": {
|
|
94
|
-
"@biomejs/biome": "2.5.
|
|
94
|
+
"@biomejs/biome": "2.5.13",
|
|
95
95
|
"@codemirror/lang-json": "^6.0.2",
|
|
96
96
|
"@codemirror/language": "^6.12.4",
|
|
97
|
-
"@codemirror/state": "^6.7.
|
|
97
|
+
"@codemirror/state": "^6.7.4",
|
|
98
98
|
"@enhance/store": "^1.0.2",
|
|
99
99
|
"@lezer/highlight": "^1.2.3",
|
|
100
100
|
"@oclif/test": "^4.2.0",
|
|
101
|
-
"@playwright/test": "^1.
|
|
102
|
-
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
101
|
+
"@playwright/test": "^1.63.0",
|
|
103
102
|
"@sanity-labs/design-tokens": "^0.0.2-alpha.2",
|
|
104
103
|
"@sanity/functions": "^1.7.2",
|
|
105
104
|
"@types/cardinal": "^2.1.1",
|
|
@@ -107,16 +106,15 @@
|
|
|
107
106
|
"@types/node": "^22",
|
|
108
107
|
"@types/tar-fs": "^2.0.4",
|
|
109
108
|
"@types/ws": "^8.18.1",
|
|
110
|
-
"@vitest/coverage-v8": "
|
|
109
|
+
"@vitest/coverage-v8": "5.0.0",
|
|
111
110
|
"codemirror": "^6.0.2",
|
|
112
111
|
"mentoss": "^0.13.0",
|
|
113
112
|
"oclif": "^4.24.0",
|
|
114
|
-
"pretty-bytes": "^7.1.
|
|
113
|
+
"pretty-bytes": "^7.1.3",
|
|
115
114
|
"pretty-ms": "^9.3.1",
|
|
116
|
-
"rollup": "^4.63.1",
|
|
117
115
|
"shx": "^0.4.0",
|
|
118
116
|
"typescript": "^7.0.2",
|
|
119
|
-
"vitest": "
|
|
117
|
+
"vitest": "5.0.0"
|
|
120
118
|
},
|
|
121
119
|
"oclif": {
|
|
122
120
|
"bin": "sanity-run",
|
package/skills/functions-dev.md
CHANGED
|
@@ -16,6 +16,6 @@ The emulator is available at `http://localhost:8080`. Choose another loopback po
|
|
|
16
16
|
npx @sanity/cli functions dev --host 127.0.0.1 --port 3333 --timeout 60
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
The emulator's live-reload WebSocket server shares the same host and port, so no additional port is reserved. `--timeout` sets the execution limit in seconds for every Function, taking precedence over each Function's configured `timeout` (default 10 seconds). Bind to a non-loopback interface only when network access is requested and exposing the development server is acceptable.
|
|
20
20
|
|
|
21
21
|
Run this command in a persistent terminal, report the URL, and stop the process when development is complete. For one bounded invocation without a web server, use `functions test`.
|