@sanity/workbench-cli 1.6.0 → 1.7.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/dist/_exports/build.d.ts +13 -2
- package/dist/_exports/build.js +1 -0
- package/dist/_exports/build.js.map +1 -1
- package/dist/_exports/deploy.d.ts +56 -37
- package/dist/_exports/deploy.js +1 -1
- package/dist/_exports/deploy.js.map +1 -1
- package/dist/_exports/dev.d.ts +0 -2
- package/dist/_exports/index.d.ts +11 -0
- package/dist/_exports/index.js +1 -0
- package/dist/_exports/index.js.map +1 -1
- package/dist/_exports/init.d.ts +2 -2
- package/dist/_exports/undeploy.d.ts +4 -2
- package/dist/actions/build/vite/plugins/plugin-sanity-app-id.js +15 -1
- package/dist/actions/build/vite/plugins/plugin-sanity-app-id.js.map +1 -1
- package/dist/actions/deploy/deployWorkbenchApp.js +55 -87
- package/dist/actions/deploy/deployWorkbenchApp.js.map +1 -1
- package/dist/actions/dev/deriveInterfaces.js +11 -3
- package/dist/actions/dev/deriveInterfaces.js.map +1 -1
- package/dist/actions/dev/startDevServerRegistration.js +11 -4
- package/dist/actions/dev/startDevServerRegistration.js.map +1 -1
- package/dist/actions/dev/startWorkbenchDev.js +1 -2
- package/dist/actions/dev/startWorkbenchDev.js.map +1 -1
- package/dist/actions/init/cliConfig.js +2 -0
- package/dist/actions/init/cliConfig.js.map +1 -1
- package/dist/actions/preview/startWorkbenchPreview.js +21 -3
- package/dist/actions/preview/startWorkbenchPreview.js.map +1 -1
- package/dist/appId.js +39 -0
- package/dist/appId.js.map +1 -0
- package/dist/resolveWorkbenchApp.js +1 -0
- package/dist/resolveWorkbenchApp.js.map +1 -1
- package/dist/services/applications.js +31 -27
- package/dist/services/applications.js.map +1 -1
- package/package.json +1 -1
package/dist/_exports/build.d.ts
CHANGED
|
@@ -3,6 +3,15 @@ import { CliConfig } from "@sanity/cli-core";
|
|
|
3
3
|
import { PluginOption } from "vite";
|
|
4
4
|
import { z } from "zod/mini";
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* The `build`/`start` id — a hash of the app's declared shape (its identity, not
|
|
8
|
+
* its code), so the bundle inlined by `sanity build` and the registry entry
|
|
9
|
+
* advertised by `sanity start` resolve to the same id. Hashed with the Web Crypto
|
|
10
|
+
* API rather than `node:crypto` for parity with `resolveAppId`'s browser-safe
|
|
11
|
+
* home. `sanity deploy` resolves its own id from the applications API.
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildAppId(app: ResolvedWorkbenchApp): Promise<string>;
|
|
14
|
+
|
|
6
15
|
/**
|
|
7
16
|
* User-facing input for `unstable_defineApp`. Excludes the internal
|
|
8
17
|
* `applicationType`, `isSingleton`, and `config` — validated by the
|
|
@@ -131,8 +140,12 @@ declare interface DefineAppResult extends DefineAppInput {
|
|
|
131
140
|
declare interface ResolvedWorkbenchApp {
|
|
132
141
|
/** The app's unique `name` from `unstable_defineApp`. */
|
|
133
142
|
readonly name: string;
|
|
143
|
+
/** Organization that owns the app — part of its build-id identity. */
|
|
144
|
+
readonly organizationId: string;
|
|
134
145
|
/** Background worker services the app declares. */
|
|
135
146
|
readonly services: NonNullable<DefineAppInput["services"]>;
|
|
147
|
+
/** Hostname the application is created at on first deploy. */
|
|
148
|
+
readonly slug: string;
|
|
136
149
|
/** Dock panel views the app declares. */
|
|
137
150
|
readonly views: NonNullable<DefineAppInput["views"]>;
|
|
138
151
|
/** Resolved app kind — `studio` or one of the SDK app types. */
|
|
@@ -145,8 +158,6 @@ declare interface ResolvedWorkbenchApp {
|
|
|
145
158
|
readonly icon?: string;
|
|
146
159
|
/** Explicit singleton flag (a Sanity-owned app); `undefined` when the app doesn't set it. */
|
|
147
160
|
readonly isSingleton?: boolean;
|
|
148
|
-
/** Hostname the application is created at on first deploy. */
|
|
149
|
-
readonly slug?: string;
|
|
150
161
|
/** Dashboard visibility declared by the app; `undefined` when unset. */
|
|
151
162
|
readonly visibility?: AppVisibility;
|
|
152
163
|
}
|
package/dist/_exports/build.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// view (`getWorkbench`) is the deploy entry's export.
|
|
6
6
|
export { workbenchOptimizeDeps } from '../actions/build/vite/optimize-deps.js';
|
|
7
7
|
export { workbenchVitePlugins } from '../actions/build/vite/workbench-vite-plugins.js';
|
|
8
|
+
export { buildAppId } from '../appId.js';
|
|
8
9
|
export { resolveWorkbenchApp } from '../resolveWorkbenchApp.js';
|
|
9
10
|
|
|
10
11
|
//# sourceMappingURL=build.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/_exports/build.ts"],"sourcesContent":["// Node-only build entry: the module-federation Vite plugins that\n// `@sanity/cli-build`'s `getViteConfig` swaps in for a workbench app, plus the\n// resolver the build reads declared views/services from. The build needs no\n// deploy-time guards, so it takes the bare `resolveWorkbenchApp` — the guarded\n// view (`getWorkbench`) is the deploy entry's export.\n\nexport {workbenchOptimizeDeps} from '../actions/build/vite/optimize-deps.js'\nexport {workbenchVitePlugins} from '../actions/build/vite/workbench-vite-plugins.js'\nexport {resolveWorkbenchApp, type WorkbenchExposes} from '../resolveWorkbenchApp.js'\n"],"names":["workbenchOptimizeDeps","workbenchVitePlugins","resolveWorkbenchApp"],"mappings":"AAAA,iEAAiE;AACjE,+EAA+E;AAC/E,4EAA4E;AAC5E,+EAA+E;AAC/E,sDAAsD;AAEtD,SAAQA,qBAAqB,QAAO,yCAAwC;AAC5E,SAAQC,oBAAoB,QAAO,kDAAiD;AACpF,SAAQC,mBAAmB,QAA8B,4BAA2B"}
|
|
1
|
+
{"version":3,"sources":["../../src/_exports/build.ts"],"sourcesContent":["// Node-only build entry: the module-federation Vite plugins that\n// `@sanity/cli-build`'s `getViteConfig` swaps in for a workbench app, plus the\n// resolver the build reads declared views/services from. The build needs no\n// deploy-time guards, so it takes the bare `resolveWorkbenchApp` — the guarded\n// view (`getWorkbench`) is the deploy entry's export.\n\nexport {workbenchOptimizeDeps} from '../actions/build/vite/optimize-deps.js'\nexport {workbenchVitePlugins} from '../actions/build/vite/workbench-vite-plugins.js'\nexport {buildAppId} from '../appId.js'\nexport {resolveWorkbenchApp, type WorkbenchExposes} from '../resolveWorkbenchApp.js'\n"],"names":["workbenchOptimizeDeps","workbenchVitePlugins","buildAppId","resolveWorkbenchApp"],"mappings":"AAAA,iEAAiE;AACjE,+EAA+E;AAC/E,4EAA4E;AAC5E,+EAA+E;AAC/E,sDAAsD;AAEtD,SAAQA,qBAAqB,QAAO,yCAAwC;AAC5E,SAAQC,oBAAoB,QAAO,kDAAiD;AACpF,SAAQC,UAAU,QAAO,cAAa;AACtC,SAAQC,mBAAmB,QAA8B,4BAA2B"}
|
|
@@ -59,6 +59,8 @@ declare interface BrettInterfaceBase {
|
|
|
59
59
|
export declare interface BrettWorkspace {
|
|
60
60
|
dataset: string;
|
|
61
61
|
projectId: string;
|
|
62
|
+
/** Lexicon schema descriptor id; Brett requires one per workspace. */
|
|
63
|
+
schemaDescriptorId: string;
|
|
62
64
|
basePath?: string;
|
|
63
65
|
icon?: string;
|
|
64
66
|
name?: string;
|
|
@@ -93,6 +95,41 @@ declare interface BuildExposesContext {
|
|
|
93
95
|
*/
|
|
94
96
|
export declare function checkBuiltOutput(sourceDir: string): Promise<void>;
|
|
95
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Create a coreApp record (no deployment), so the CLI can build with its id
|
|
100
|
+
* before shipping the first deployment. First deploy only.
|
|
101
|
+
* @internal
|
|
102
|
+
*/
|
|
103
|
+
export declare function createCoreApp(options: {
|
|
104
|
+
isSingleton?: boolean;
|
|
105
|
+
organizationId: string;
|
|
106
|
+
slug: string;
|
|
107
|
+
title: string;
|
|
108
|
+
visibility?: AppVisibility;
|
|
109
|
+
}): Promise<CreatedApplication>;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* A freshly created application record: its id, plus a way to undo the creation.
|
|
113
|
+
* The caller builds and deploys with the id, and calls `rollback` if a later
|
|
114
|
+
* step fails so the record isn't stranded at its slug.
|
|
115
|
+
* @internal
|
|
116
|
+
*/
|
|
117
|
+
export declare interface CreatedApplication {
|
|
118
|
+
applicationId: string;
|
|
119
|
+
rollback: () => Promise<void>;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Create a studio record (no deployment).
|
|
124
|
+
* @internal
|
|
125
|
+
*/
|
|
126
|
+
export declare function createStudio(options: {
|
|
127
|
+
organizationId: string;
|
|
128
|
+
projectId: string | undefined;
|
|
129
|
+
slug: string;
|
|
130
|
+
title: string;
|
|
131
|
+
}): Promise<CreatedApplication>;
|
|
132
|
+
|
|
96
133
|
/**
|
|
97
134
|
* User-facing input for `unstable_defineApp`. Excludes the internal
|
|
98
135
|
* `applicationType`, `isSingleton`, and `config` — validated by the
|
|
@@ -253,28 +290,6 @@ export declare function deployConfig(options: {
|
|
|
253
290
|
version: string;
|
|
254
291
|
}): Promise<void>;
|
|
255
292
|
|
|
256
|
-
/**
|
|
257
|
-
* Deploy a workbench coreApp through Brett: redeploy when `appId` is set,
|
|
258
|
-
* otherwise create the application at `slug`. Returns the application id for the
|
|
259
|
-
* shell to report.
|
|
260
|
-
* @internal
|
|
261
|
-
*/
|
|
262
|
-
export declare function deployCoreApp(options: {
|
|
263
|
-
appId: string | undefined;
|
|
264
|
-
icon?: string;
|
|
265
|
-
interfaces: readonly BrettInterface[];
|
|
266
|
-
isAutoUpdating: boolean;
|
|
267
|
-
isSingleton?: boolean;
|
|
268
|
-
organizationId: string;
|
|
269
|
-
slug: string;
|
|
270
|
-
sourceDir: string;
|
|
271
|
-
title: string;
|
|
272
|
-
version: string;
|
|
273
|
-
visibility?: AppVisibility;
|
|
274
|
-
}): Promise<{
|
|
275
|
-
applicationId: string;
|
|
276
|
-
}>;
|
|
277
|
-
|
|
278
293
|
/** A view or service as the deploy report and `--json` output surface it. */
|
|
279
294
|
export declare interface DeployedExpose {
|
|
280
295
|
name: string;
|
|
@@ -284,27 +299,29 @@ export declare interface DeployedExpose {
|
|
|
284
299
|
}
|
|
285
300
|
|
|
286
301
|
/**
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
*
|
|
302
|
+
* Ship a deployment to an already-created (or `deployment.appId`) application,
|
|
303
|
+
* then sync its mutable metadata (`title`, and `icon`/`visibility` when set)
|
|
304
|
+
* from config. The deploy endpoint ignores these, so a redeploy patches them
|
|
305
|
+
* here alongside the new deployment.
|
|
306
|
+
*
|
|
307
|
+
* `onDeployed` fires the instant the deployment is live, before the metadata
|
|
308
|
+
* sync — so a caller can disarm a create-time rollback that must not delete an
|
|
309
|
+
* application once it has an active deployment.
|
|
290
310
|
* @internal
|
|
291
311
|
*/
|
|
292
|
-
export declare function
|
|
293
|
-
|
|
312
|
+
export declare function deployWorkbenchApp(options: {
|
|
313
|
+
applicationId: string;
|
|
294
314
|
icon?: string;
|
|
295
315
|
interfaces: readonly BrettInterface[];
|
|
296
316
|
isAutoUpdating: boolean;
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
projectId: string | undefined;
|
|
317
|
+
label?: string;
|
|
318
|
+
onDeployed?: () => void;
|
|
300
319
|
sourceDir: string;
|
|
301
|
-
studioHost: string | undefined;
|
|
302
320
|
title: string;
|
|
303
321
|
version: string;
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}>;
|
|
322
|
+
visibility?: AppVisibility;
|
|
323
|
+
workspaces?: readonly BrettWorkspace[];
|
|
324
|
+
}): Promise<void>;
|
|
308
325
|
|
|
309
326
|
export declare function getApplication(
|
|
310
327
|
applicationId: string,
|
|
@@ -325,8 +342,12 @@ export declare function getWorkbenchUrl(organizationId: string): string;
|
|
|
325
342
|
declare interface ResolvedWorkbenchApp {
|
|
326
343
|
/** The app's unique `name` from `unstable_defineApp`. */
|
|
327
344
|
readonly name: string;
|
|
345
|
+
/** Organization that owns the app — part of its build-id identity. */
|
|
346
|
+
readonly organizationId: string;
|
|
328
347
|
/** Background worker services the app declares. */
|
|
329
348
|
readonly services: NonNullable<DefineAppInput["services"]>;
|
|
349
|
+
/** Hostname the application is created at on first deploy. */
|
|
350
|
+
readonly slug: string;
|
|
330
351
|
/** Dock panel views the app declares. */
|
|
331
352
|
readonly views: NonNullable<DefineAppInput["views"]>;
|
|
332
353
|
/** Resolved app kind — `studio` or one of the SDK app types. */
|
|
@@ -339,8 +360,6 @@ declare interface ResolvedWorkbenchApp {
|
|
|
339
360
|
readonly icon?: string;
|
|
340
361
|
/** Explicit singleton flag (a Sanity-owned app); `undefined` when the app doesn't set it. */
|
|
341
362
|
readonly isSingleton?: boolean;
|
|
342
|
-
/** Hostname the application is created at on first deploy. */
|
|
343
|
-
readonly slug?: string;
|
|
344
363
|
/** Dashboard visibility declared by the app; `undefined` when unset. */
|
|
345
364
|
readonly visibility?: AppVisibility;
|
|
346
365
|
}
|
package/dist/_exports/deploy.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { buildExposes, summarizeExposes } from '../actions/deploy/buildExposes.js';
|
|
2
2
|
export { checkBuiltOutput } from '../actions/deploy/checkBuiltOutput.js';
|
|
3
3
|
export { deployConfig, resolveInstallationId, summarizeConfig } from '../actions/deploy/deployConfig.js';
|
|
4
|
-
export {
|
|
4
|
+
export { createCoreApp, createStudio, deployWorkbenchApp } from '../actions/deploy/deployWorkbenchApp.js';
|
|
5
5
|
export { getWorkbench } from '../actions/deploy/getWorkbench.js';
|
|
6
6
|
export { getApplication, getApplicationUrl, getWorkbenchUrl } from '../services/applications.js';
|
|
7
7
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/_exports/deploy.ts"],"sourcesContent":["export {\n buildExposes,\n type DeployedExpose,\n summarizeExposes,\n} from '../actions/deploy/buildExposes.js'\nexport {checkBuiltOutput} from '../actions/deploy/checkBuiltOutput.js'\nexport {\n deployConfig,\n resolveInstallationId,\n summarizeConfig,\n} from '../actions/deploy/deployConfig.js'\nexport {
|
|
1
|
+
{"version":3,"sources":["../../src/_exports/deploy.ts"],"sourcesContent":["export {\n buildExposes,\n type DeployedExpose,\n summarizeExposes,\n} from '../actions/deploy/buildExposes.js'\nexport {checkBuiltOutput} from '../actions/deploy/checkBuiltOutput.js'\nexport {\n deployConfig,\n resolveInstallationId,\n summarizeConfig,\n} from '../actions/deploy/deployConfig.js'\nexport {\n createCoreApp,\n type CreatedApplication,\n createStudio,\n deployWorkbenchApp,\n} from '../actions/deploy/deployWorkbenchApp.js'\nexport {getWorkbench} from '../actions/deploy/getWorkbench.js'\nexport {\n type Application,\n type BrettInterface,\n type BrettWorkspace,\n getApplication,\n getApplicationUrl,\n getWorkbenchUrl,\n} from '../services/applications.js'\n"],"names":["buildExposes","summarizeExposes","checkBuiltOutput","deployConfig","resolveInstallationId","summarizeConfig","createCoreApp","createStudio","deployWorkbenchApp","getWorkbench","getApplication","getApplicationUrl","getWorkbenchUrl"],"mappings":"AAAA,SACEA,YAAY,EAEZC,gBAAgB,QACX,oCAAmC;AAC1C,SAAQC,gBAAgB,QAAO,wCAAuC;AACtE,SACEC,YAAY,EACZC,qBAAqB,EACrBC,eAAe,QACV,oCAAmC;AAC1C,SACEC,aAAa,EAEbC,YAAY,EACZC,kBAAkB,QACb,0CAAyC;AAChD,SAAQC,YAAY,QAAO,oCAAmC;AAC9D,SAIEC,cAAc,EACdC,iBAAiB,EACjBC,eAAe,QACV,8BAA6B"}
|
package/dist/_exports/dev.d.ts
CHANGED
|
@@ -162,8 +162,6 @@ export declare function startWorkbenchDev(
|
|
|
162
162
|
}>;
|
|
163
163
|
|
|
164
164
|
export declare interface StartWorkbenchDevOptions {
|
|
165
|
-
/** Resolved app id for the registry entry (the CLI owns id resolution). */
|
|
166
|
-
appId: string | undefined;
|
|
167
165
|
/** Directory for the workbench Vite server's dependency cache. */
|
|
168
166
|
cacheDir: string;
|
|
169
167
|
/** CLI-domain `app.id`/`deployment.appId` deprecation check, run before registering. */
|
package/dist/_exports/index.d.ts
CHANGED
|
@@ -230,6 +230,17 @@ export declare type PanelViewProps = ViewComponentBaseProps<{
|
|
|
230
230
|
type: "panel";
|
|
231
231
|
}>;
|
|
232
232
|
|
|
233
|
+
/**
|
|
234
|
+
* The dev id for a workbench app — the address the server bound. `sanity dev`
|
|
235
|
+
* keys on where the app is served so a running app can't collide with its
|
|
236
|
+
* deployed twin. Sync and dependency-free: it's re-exported from the package's
|
|
237
|
+
* browser-facing entry, so it must not pull in `node:crypto`.
|
|
238
|
+
*/
|
|
239
|
+
export declare function resolveAppId(source: {
|
|
240
|
+
host: string;
|
|
241
|
+
port: number;
|
|
242
|
+
}): string;
|
|
243
|
+
|
|
233
244
|
/** @internal */
|
|
234
245
|
declare const SERVICE_CONTRACT_VERSION = 1;
|
|
235
246
|
|
package/dist/_exports/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/_exports/index.ts"],"sourcesContent":["export type {InterfaceType, ServiceType} from '../contract.js'\nexport {isWorkbenchApp, unstable_defineApp, unstable_defineMediaLibrary} from '../defineApp.js'\nexport type {\n DefineAppInput,\n DefineAppResult,\n DefineMediaLibraryInput,\n DockGroup,\n MediaLibraryField,\n WorkbenchApp,\n} from '../defineApp.js'\nexport {unstable_defineService} from '../defineService.js'\nexport type {\n DefinedService,\n ServiceCallback,\n ServiceContext,\n ServiceInfo,\n} from '../defineService.js'\nexport {unstable_defineView} from '../defineView.js'\nexport type {\n DefinedView,\n PanelComponent,\n PanelViewComponents,\n PanelViewProps,\n ViewComponentsByType,\n} from '../defineView.js'\n"],"names":["isWorkbenchApp","unstable_defineApp","unstable_defineMediaLibrary","unstable_defineService","unstable_defineView"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/_exports/index.ts"],"sourcesContent":["export {resolveAppId} from '../appId.js'\nexport type {InterfaceType, ServiceType} from '../contract.js'\nexport {isWorkbenchApp, unstable_defineApp, unstable_defineMediaLibrary} from '../defineApp.js'\nexport type {\n DefineAppInput,\n DefineAppResult,\n DefineMediaLibraryInput,\n DockGroup,\n MediaLibraryField,\n WorkbenchApp,\n} from '../defineApp.js'\nexport {unstable_defineService} from '../defineService.js'\nexport type {\n DefinedService,\n ServiceCallback,\n ServiceContext,\n ServiceInfo,\n} from '../defineService.js'\nexport {unstable_defineView} from '../defineView.js'\nexport type {\n DefinedView,\n PanelComponent,\n PanelViewComponents,\n PanelViewProps,\n ViewComponentsByType,\n} from '../defineView.js'\n"],"names":["resolveAppId","isWorkbenchApp","unstable_defineApp","unstable_defineMediaLibrary","unstable_defineService","unstable_defineView"],"mappings":"AAAA,SAAQA,YAAY,QAAO,cAAa;AAExC,SAAQC,cAAc,EAAEC,kBAAkB,EAAEC,2BAA2B,QAAO,kBAAiB;AAS/F,SAAQC,sBAAsB,QAAO,sBAAqB;AAO1D,SAAQC,mBAAmB,QAAO,mBAAkB"}
|
package/dist/_exports/init.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/** App scaffold — `entry` auto-declares the navigable app view. */
|
|
2
2
|
export declare const workbenchAppConfigTemplate =
|
|
3
|
-
"\nimport {defineCliConfig, unstable_defineApp} from 'sanity/cli'\n\nexport default defineCliConfig({\n app: unstable_defineApp({\n name: '%name%',\n title: '%title%',\n organizationId: '%organizationId%',\n entry: '%entry%',\n }),\n})\n";
|
|
3
|
+
"\nimport {defineCliConfig, unstable_defineApp} from 'sanity/cli'\n\nexport default defineCliConfig({\n app: unstable_defineApp({\n name: '%name%',\n title: '%title%',\n slug: '%slug%',\n organizationId: '%organizationId%',\n entry: '%entry%',\n }),\n})\n";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Studio scaffold — brands with name/title only, no `entry` (studio app views
|
|
7
7
|
* aren't implemented yet).
|
|
8
8
|
*/
|
|
9
9
|
export declare const workbenchStudioConfigTemplate =
|
|
10
|
-
"\nimport {defineCliConfig, unstable_defineApp} from 'sanity/cli'\n\nexport default defineCliConfig({\n api: {\n projectId: '%projectId%',\n dataset: '%dataset%'\n },\n app: unstable_defineApp({\n name: '%name%',\n title: '%title%',\n organizationId: '%organizationId%',\n }),\n deployment: {\n /**\n * Enable auto-updates for studios.\n * Learn more at https://www.sanity.io/docs/studio/latest-version-of-sanity#k47faf43faf56\n */\n autoUpdates: __BOOL__autoUpdates__,\n },\n})\n";
|
|
10
|
+
"\nimport {defineCliConfig, unstable_defineApp} from 'sanity/cli'\n\nexport default defineCliConfig({\n api: {\n projectId: '%projectId%',\n dataset: '%dataset%'\n },\n app: unstable_defineApp({\n name: '%name%',\n title: '%title%',\n slug: '%slug%',\n organizationId: '%organizationId%',\n }),\n deployment: {\n /**\n * Enable auto-updates for studios.\n * Learn more at https://www.sanity.io/docs/studio/latest-version-of-sanity#k47faf43faf56\n */\n autoUpdates: __BOOL__autoUpdates__,\n },\n})\n";
|
|
11
11
|
|
|
12
12
|
export {};
|
|
@@ -175,8 +175,12 @@ declare interface DeployedExpose {
|
|
|
175
175
|
declare interface ResolvedWorkbenchApp {
|
|
176
176
|
/** The app's unique `name` from `unstable_defineApp`. */
|
|
177
177
|
readonly name: string;
|
|
178
|
+
/** Organization that owns the app — part of its build-id identity. */
|
|
179
|
+
readonly organizationId: string;
|
|
178
180
|
/** Background worker services the app declares. */
|
|
179
181
|
readonly services: NonNullable<DefineAppInput["services"]>;
|
|
182
|
+
/** Hostname the application is created at on first deploy. */
|
|
183
|
+
readonly slug: string;
|
|
180
184
|
/** Dock panel views the app declares. */
|
|
181
185
|
readonly views: NonNullable<DefineAppInput["views"]>;
|
|
182
186
|
/** Resolved app kind — `studio` or one of the SDK app types. */
|
|
@@ -189,8 +193,6 @@ declare interface ResolvedWorkbenchApp {
|
|
|
189
193
|
readonly icon?: string;
|
|
190
194
|
/** Explicit singleton flag (a Sanity-owned app); `undefined` when the app doesn't set it. */
|
|
191
195
|
readonly isSingleton?: boolean;
|
|
192
|
-
/** Hostname the application is created at on first deploy. */
|
|
193
|
-
readonly slug?: string;
|
|
194
196
|
/** Dashboard visibility declared by the app; `undefined` when unset. */
|
|
195
197
|
readonly visibility?: AppVisibility;
|
|
196
198
|
}
|
|
@@ -1,13 +1,18 @@
|
|
|
1
|
+
import { SANITY_APP_ID_FILE } from '../../../../appId.js';
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
+
* Inline the app's bus identity into its bundle: `@sanity/runtime` reads
|
|
3
4
|
* `__SANITY_APP_ID__` where it connects. `define` covers everything the
|
|
4
5
|
* pipeline transforms (all of a production build, and dev-served source); the
|
|
5
6
|
* rolldown define covers dev's pre-bundled dependencies, which skip Vite's
|
|
6
7
|
* define transform.
|
|
8
|
+
*
|
|
9
|
+
* A build also writes the id to disk so `sanity start`, which serves the build
|
|
10
|
+
* without recompiling, can advertise the exact id the bundle uses.
|
|
7
11
|
*/ export function sanityAppId(appId) {
|
|
8
12
|
const define = {
|
|
9
13
|
__SANITY_APP_ID__: JSON.stringify(appId)
|
|
10
14
|
};
|
|
15
|
+
let emitted = false;
|
|
11
16
|
return {
|
|
12
17
|
config: ()=>({
|
|
13
18
|
define,
|
|
@@ -19,6 +24,15 @@
|
|
|
19
24
|
}
|
|
20
25
|
}
|
|
21
26
|
}),
|
|
27
|
+
generateBundle () {
|
|
28
|
+
if (emitted) return;
|
|
29
|
+
emitted = true;
|
|
30
|
+
this.emitFile({
|
|
31
|
+
fileName: SANITY_APP_ID_FILE,
|
|
32
|
+
source: appId,
|
|
33
|
+
type: 'asset'
|
|
34
|
+
});
|
|
35
|
+
},
|
|
22
36
|
name: 'sanity/workbench/app-id'
|
|
23
37
|
};
|
|
24
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/actions/build/vite/plugins/plugin-sanity-app-id.ts"],"sourcesContent":["import {type Plugin} from 'vite'\n\n/**\n *
|
|
1
|
+
{"version":3,"sources":["../../../../../src/actions/build/vite/plugins/plugin-sanity-app-id.ts"],"sourcesContent":["import {type Plugin} from 'vite'\n\nimport {SANITY_APP_ID_FILE} from '../../../../appId.js'\n\n/**\n * Inline the app's bus identity into its bundle: `@sanity/runtime` reads\n * `__SANITY_APP_ID__` where it connects. `define` covers everything the\n * pipeline transforms (all of a production build, and dev-served source); the\n * rolldown define covers dev's pre-bundled dependencies, which skip Vite's\n * define transform.\n *\n * A build also writes the id to disk so `sanity start`, which serves the build\n * without recompiling, can advertise the exact id the bundle uses.\n */\nexport function sanityAppId(appId: string): Plugin {\n const define = {__SANITY_APP_ID__: JSON.stringify(appId)}\n let emitted = false\n return {\n config: () => ({define, optimizeDeps: {rolldownOptions: {transform: {define}}}}),\n generateBundle() {\n if (emitted) return\n emitted = true\n this.emitFile({fileName: SANITY_APP_ID_FILE, source: appId, type: 'asset'})\n },\n name: 'sanity/workbench/app-id',\n }\n}\n"],"names":["SANITY_APP_ID_FILE","sanityAppId","appId","define","__SANITY_APP_ID__","JSON","stringify","emitted","config","optimizeDeps","rolldownOptions","transform","generateBundle","emitFile","fileName","source","type","name"],"mappings":"AAEA,SAAQA,kBAAkB,QAAO,uBAAsB;AAEvD;;;;;;;;;CASC,GACD,OAAO,SAASC,YAAYC,KAAa;IACvC,MAAMC,SAAS;QAACC,mBAAmBC,KAAKC,SAAS,CAACJ;IAAM;IACxD,IAAIK,UAAU;IACd,OAAO;QACLC,QAAQ,IAAO,CAAA;gBAACL;gBAAQM,cAAc;oBAACC,iBAAiB;wBAACC,WAAW;4BAACR;wBAAM;oBAAC;gBAAC;YAAC,CAAA;QAC9ES;YACE,IAAIL,SAAS;YACbA,UAAU;YACV,IAAI,CAACM,QAAQ,CAAC;gBAACC,UAAUd;gBAAoBe,QAAQb;gBAAOc,MAAM;YAAO;QAC3E;QACAC,MAAM;IACR;AACF"}
|
|
@@ -1,121 +1,89 @@
|
|
|
1
1
|
import { basename, dirname } from 'node:path';
|
|
2
2
|
import { createGzip } from 'node:zlib';
|
|
3
|
-
import { exitCodes } from '@sanity/cli-core';
|
|
4
3
|
import { spinner } from '@sanity/cli-core/ux';
|
|
5
4
|
import { pack } from 'tar-fs';
|
|
6
|
-
import { createApplication, createDeployment, updateApplication } from '../../services/applications.js';
|
|
5
|
+
import { createApplication, createDeployment, deleteApplication, updateApplication } from '../../services/applications.js';
|
|
7
6
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* shell to report.
|
|
7
|
+
* Create a coreApp record (no deployment), so the CLI can build with its id
|
|
8
|
+
* before shipping the first deployment. First deploy only.
|
|
11
9
|
* @internal
|
|
12
|
-
*/ export async function
|
|
13
|
-
const
|
|
14
|
-
const tarball = pack(dirname(sourceDir), {
|
|
15
|
-
entries: [
|
|
16
|
-
basename(sourceDir)
|
|
17
|
-
]
|
|
18
|
-
}).pipe(createGzip());
|
|
19
|
-
const spin = spinner('Deploying...').start();
|
|
10
|
+
*/ export async function createCoreApp(options) {
|
|
11
|
+
const spin = spinner('Creating application...').start();
|
|
20
12
|
try {
|
|
21
|
-
if (appId) {
|
|
22
|
-
await createDeployment({
|
|
23
|
-
applicationId: appId,
|
|
24
|
-
interfaces,
|
|
25
|
-
isAutoUpdating,
|
|
26
|
-
tarball,
|
|
27
|
-
version
|
|
28
|
-
});
|
|
29
|
-
await updateApplication(appId, {
|
|
30
|
-
title,
|
|
31
|
-
...icon ? {
|
|
32
|
-
icon
|
|
33
|
-
} : {}
|
|
34
|
-
});
|
|
35
|
-
spin.succeed();
|
|
36
|
-
return {
|
|
37
|
-
applicationId: appId
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
13
|
const { id } = await createApplication({
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
isSingleton,
|
|
44
|
-
organizationId,
|
|
45
|
-
slug,
|
|
46
|
-
tarball,
|
|
47
|
-
title,
|
|
48
|
-
type: 'coreApp',
|
|
49
|
-
version,
|
|
50
|
-
visibility
|
|
14
|
+
...options,
|
|
15
|
+
type: 'coreApp'
|
|
51
16
|
});
|
|
52
17
|
spin.succeed();
|
|
53
18
|
return {
|
|
54
|
-
applicationId: id
|
|
19
|
+
applicationId: id,
|
|
20
|
+
rollback: ()=>deleteApplication(id)
|
|
55
21
|
};
|
|
56
22
|
} catch (error) {
|
|
57
|
-
spin.
|
|
23
|
+
spin.fail();
|
|
58
24
|
throw error;
|
|
59
25
|
}
|
|
60
26
|
}
|
|
61
27
|
/**
|
|
62
|
-
*
|
|
63
|
-
* otherwise create the studio at `studioHost`. Returns the application id for
|
|
64
|
-
* the shell to report; a missing `studioHost` on create is a usage error.
|
|
28
|
+
* Create a studio record (no deployment).
|
|
65
29
|
* @internal
|
|
66
|
-
*/ export async function
|
|
67
|
-
const
|
|
30
|
+
*/ export async function createStudio(options) {
|
|
31
|
+
const spin = spinner('Creating studio...').start();
|
|
32
|
+
try {
|
|
33
|
+
const { id } = await createApplication({
|
|
34
|
+
...options,
|
|
35
|
+
type: 'studio'
|
|
36
|
+
});
|
|
37
|
+
spin.succeed();
|
|
38
|
+
return {
|
|
39
|
+
applicationId: id,
|
|
40
|
+
rollback: ()=>deleteApplication(id)
|
|
41
|
+
};
|
|
42
|
+
} catch (error) {
|
|
43
|
+
spin.fail();
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Ship a deployment to an already-created (or `deployment.appId`) application,
|
|
49
|
+
* then sync its mutable metadata (`title`, and `icon`/`visibility` when set)
|
|
50
|
+
* from config. The deploy endpoint ignores these, so a redeploy patches them
|
|
51
|
+
* here alongside the new deployment.
|
|
52
|
+
*
|
|
53
|
+
* `onDeployed` fires the instant the deployment is live, before the metadata
|
|
54
|
+
* sync — so a caller can disarm a create-time rollback that must not delete an
|
|
55
|
+
* application once it has an active deployment.
|
|
56
|
+
* @internal
|
|
57
|
+
*/ export async function deployWorkbenchApp(options) {
|
|
58
|
+
const { applicationId, icon, interfaces, isAutoUpdating, label = 'Deploying...', onDeployed, sourceDir, title, version, visibility, workspaces } = options;
|
|
68
59
|
const tarball = pack(dirname(sourceDir), {
|
|
69
60
|
entries: [
|
|
70
61
|
basename(sourceDir)
|
|
71
62
|
]
|
|
72
63
|
}).pipe(createGzip());
|
|
73
|
-
const spin = spinner(
|
|
64
|
+
const spin = spinner(label).start();
|
|
74
65
|
try {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
applicationId: appId,
|
|
78
|
-
interfaces,
|
|
79
|
-
isAutoUpdating,
|
|
80
|
-
tarball,
|
|
81
|
-
version,
|
|
82
|
-
workspaces
|
|
83
|
-
});
|
|
84
|
-
await updateApplication(appId, {
|
|
85
|
-
title,
|
|
86
|
-
...icon ? {
|
|
87
|
-
icon
|
|
88
|
-
} : {}
|
|
89
|
-
});
|
|
90
|
-
spin.succeed();
|
|
91
|
-
return {
|
|
92
|
-
applicationId: appId
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
if (!studioHost) {
|
|
96
|
-
spin.fail();
|
|
97
|
-
return output.error('No studio hostname configured. Set `studioHost` in sanity.cli.ts to create a studio.', {
|
|
98
|
-
exit: exitCodes.USAGE_ERROR
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
const application = await createApplication({
|
|
102
|
-
icon,
|
|
66
|
+
await createDeployment({
|
|
67
|
+
applicationId,
|
|
103
68
|
interfaces,
|
|
104
|
-
|
|
105
|
-
projectId,
|
|
106
|
-
slug: studioHost,
|
|
69
|
+
isAutoUpdating,
|
|
107
70
|
tarball,
|
|
108
|
-
title,
|
|
109
|
-
type: 'studio',
|
|
110
71
|
version,
|
|
111
72
|
workspaces
|
|
112
73
|
});
|
|
74
|
+
onDeployed?.();
|
|
75
|
+
await updateApplication(applicationId, {
|
|
76
|
+
title,
|
|
77
|
+
...icon ? {
|
|
78
|
+
icon
|
|
79
|
+
} : {},
|
|
80
|
+
...visibility ? {
|
|
81
|
+
visibility
|
|
82
|
+
} : {}
|
|
83
|
+
});
|
|
113
84
|
spin.succeed();
|
|
114
|
-
return {
|
|
115
|
-
applicationId: application.id
|
|
116
|
-
};
|
|
117
85
|
} catch (error) {
|
|
118
|
-
spin.
|
|
86
|
+
spin.clear();
|
|
119
87
|
throw error;
|
|
120
88
|
}
|
|
121
89
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/deploy/deployWorkbenchApp.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {createGzip} from 'node:zlib'\n\nimport {type AppVisibility
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/deploy/deployWorkbenchApp.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {createGzip} from 'node:zlib'\n\nimport {type AppVisibility} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {pack} from 'tar-fs'\n\nimport {\n type BrettInterface,\n type BrettWorkspace,\n createApplication,\n createDeployment,\n deleteApplication,\n updateApplication,\n} from '../../services/applications.js'\n\n/**\n * A freshly created application record: its id, plus a way to undo the creation.\n * The caller builds and deploys with the id, and calls `rollback` if a later\n * step fails so the record isn't stranded at its slug.\n * @internal\n */\nexport interface CreatedApplication {\n applicationId: string\n rollback: () => Promise<void>\n}\n\n/**\n * Create a coreApp record (no deployment), so the CLI can build with its id\n * before shipping the first deployment. First deploy only.\n * @internal\n */\nexport async function createCoreApp(options: {\n isSingleton?: boolean\n organizationId: string\n slug: string\n title: string\n visibility?: AppVisibility\n}): Promise<CreatedApplication> {\n const spin = spinner('Creating application...').start()\n try {\n const {id} = await createApplication({...options, type: 'coreApp'})\n spin.succeed()\n return {applicationId: id, rollback: () => deleteApplication(id)}\n } catch (error) {\n spin.fail()\n throw error\n }\n}\n\n/**\n * Create a studio record (no deployment).\n * @internal\n */\nexport async function createStudio(options: {\n organizationId: string\n projectId: string | undefined\n slug: string\n title: string\n}): Promise<CreatedApplication> {\n const spin = spinner('Creating studio...').start()\n try {\n const {id} = await createApplication({...options, type: 'studio'})\n spin.succeed()\n return {applicationId: id, rollback: () => deleteApplication(id)}\n } catch (error) {\n spin.fail()\n throw error\n }\n}\n\n/**\n * Ship a deployment to an already-created (or `deployment.appId`) application,\n * then sync its mutable metadata (`title`, and `icon`/`visibility` when set)\n * from config. The deploy endpoint ignores these, so a redeploy patches them\n * here alongside the new deployment.\n *\n * `onDeployed` fires the instant the deployment is live, before the metadata\n * sync — so a caller can disarm a create-time rollback that must not delete an\n * application once it has an active deployment.\n * @internal\n */\nexport async function deployWorkbenchApp(options: {\n applicationId: string\n icon?: string\n interfaces: readonly BrettInterface[]\n isAutoUpdating: boolean\n label?: string\n onDeployed?: () => void\n sourceDir: string\n title: string\n version: string\n visibility?: AppVisibility\n workspaces?: readonly BrettWorkspace[]\n}): Promise<void> {\n const {\n applicationId,\n icon,\n interfaces,\n isAutoUpdating,\n label = 'Deploying...',\n onDeployed,\n sourceDir,\n title,\n version,\n visibility,\n workspaces,\n } = options\n const tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n\n const spin = spinner(label).start()\n try {\n await createDeployment({\n applicationId,\n interfaces,\n isAutoUpdating,\n tarball,\n version,\n workspaces,\n })\n onDeployed?.()\n await updateApplication(applicationId, {\n title,\n ...(icon ? {icon} : {}),\n ...(visibility ? {visibility} : {}),\n })\n spin.succeed()\n } catch (error) {\n spin.clear()\n throw error\n }\n}\n"],"names":["basename","dirname","createGzip","spinner","pack","createApplication","createDeployment","deleteApplication","updateApplication","createCoreApp","options","spin","start","id","type","succeed","applicationId","rollback","error","fail","createStudio","deployWorkbenchApp","icon","interfaces","isAutoUpdating","label","onDeployed","sourceDir","title","version","visibility","workspaces","tarball","entries","pipe","clear"],"mappings":"AAAA,SAAQA,QAAQ,EAAEC,OAAO,QAAO,YAAW;AAC3C,SAAQC,UAAU,QAAO,YAAW;AAGpC,SAAQC,OAAO,QAAO,sBAAqB;AAC3C,SAAQC,IAAI,QAAO,SAAQ;AAE3B,SAGEC,iBAAiB,EACjBC,gBAAgB,EAChBC,iBAAiB,EACjBC,iBAAiB,QACZ,iCAAgC;AAavC;;;;CAIC,GACD,OAAO,eAAeC,cAAcC,OAMnC;IACC,MAAMC,OAAOR,QAAQ,2BAA2BS,KAAK;IACrD,IAAI;QACF,MAAM,EAACC,EAAE,EAAC,GAAG,MAAMR,kBAAkB;YAAC,GAAGK,OAAO;YAAEI,MAAM;QAAS;QACjEH,KAAKI,OAAO;QACZ,OAAO;YAACC,eAAeH;YAAII,UAAU,IAAMV,kBAAkBM;QAAG;IAClE,EAAE,OAAOK,OAAO;QACdP,KAAKQ,IAAI;QACT,MAAMD;IACR;AACF;AAEA;;;CAGC,GACD,OAAO,eAAeE,aAAaV,OAKlC;IACC,MAAMC,OAAOR,QAAQ,sBAAsBS,KAAK;IAChD,IAAI;QACF,MAAM,EAACC,EAAE,EAAC,GAAG,MAAMR,kBAAkB;YAAC,GAAGK,OAAO;YAAEI,MAAM;QAAQ;QAChEH,KAAKI,OAAO;QACZ,OAAO;YAACC,eAAeH;YAAII,UAAU,IAAMV,kBAAkBM;QAAG;IAClE,EAAE,OAAOK,OAAO;QACdP,KAAKQ,IAAI;QACT,MAAMD;IACR;AACF;AAEA;;;;;;;;;;CAUC,GACD,OAAO,eAAeG,mBAAmBX,OAYxC;IACC,MAAM,EACJM,aAAa,EACbM,IAAI,EACJC,UAAU,EACVC,cAAc,EACdC,QAAQ,cAAc,EACtBC,UAAU,EACVC,SAAS,EACTC,KAAK,EACLC,OAAO,EACPC,UAAU,EACVC,UAAU,EACX,GAAGrB;IACJ,MAAMsB,UAAU5B,KAAKH,QAAQ0B,YAAY;QAACM,SAAS;YAACjC,SAAS2B;SAAW;IAAA,GAAGO,IAAI,CAAChC;IAEhF,MAAMS,OAAOR,QAAQsB,OAAOb,KAAK;IACjC,IAAI;QACF,MAAMN,iBAAiB;YACrBU;YACAO;YACAC;YACAQ;YACAH;YACAE;QACF;QACAL;QACA,MAAMlB,kBAAkBQ,eAAe;YACrCY;YACA,GAAIN,OAAO;gBAACA;YAAI,IAAI,CAAC,CAAC;YACtB,GAAIQ,aAAa;gBAACA;YAAU,IAAI,CAAC,CAAC;QACpC;QACAnB,KAAKI,OAAO;IACd,EAAE,OAAOG,OAAO;QACdP,KAAKwB,KAAK;QACV,MAAMjB;IACR;AACF"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { hash } from 'node:crypto';
|
|
2
1
|
import { interfaceModuleId, MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION, SERVICE_CONTRACT_VERSION, VIEW_CONTRACT_VERSION } from '../../contract.js';
|
|
3
2
|
import { isWorkbenchApp, readConfig } from '../../defineApp.js';
|
|
4
3
|
/**
|
|
@@ -78,7 +77,7 @@ import { isWorkbenchApp, readConfig } from '../../defineApp.js';
|
|
|
78
77
|
* installation-config id slot deployed apps get from the applications API,
|
|
79
78
|
* and the workbench keys change detection on it. `version` is the config
|
|
80
79
|
* contract version the generated module exports, known before the module runs.
|
|
81
|
-
*/ export function deriveConfigs(app) {
|
|
80
|
+
*/ export async function deriveConfigs(app) {
|
|
82
81
|
if (!isWorkbenchApp(app)) return [];
|
|
83
82
|
const config = readConfig(app);
|
|
84
83
|
if (!config) return [];
|
|
@@ -96,9 +95,18 @@ import { isWorkbenchApp, readConfig } from '../../defineApp.js';
|
|
|
96
95
|
return [
|
|
97
96
|
{
|
|
98
97
|
...entry,
|
|
99
|
-
id:
|
|
98
|
+
id: await contentHash(JSON.stringify(entry))
|
|
100
99
|
}
|
|
101
100
|
];
|
|
102
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* SHA-256 of a string, as hex, via the Web Crypto API — available in both Node
|
|
104
|
+
* and the browser. `node:crypto` can't be used: the Vite dev server's dep scan
|
|
105
|
+
* pulls this module into the browser graph.
|
|
106
|
+
*/ async function contentHash(input) {
|
|
107
|
+
// eslint-disable-next-line n/no-unsupported-features/node-builtins -- the Web Crypto global is available on our Node target and in the browser
|
|
108
|
+
const digest = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(input));
|
|
109
|
+
return Array.from(new Uint8Array(digest), (byte)=>byte.toString(16).padStart(2, '0')).join('');
|
|
110
|
+
}
|
|
103
111
|
|
|
104
112
|
//# sourceMappingURL=deriveInterfaces.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/dev/deriveInterfaces.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/dev/deriveInterfaces.ts"],"sourcesContent":["import {type CliConfig} from '@sanity/cli-core'\n\nimport {\n interfaceModuleId,\n MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION,\n SERVICE_CONTRACT_VERSION,\n VIEW_CONTRACT_VERSION,\n} from '../../contract.js'\nimport {isWorkbenchApp, readConfig} from '../../defineApp.js'\nimport {type DevServerManifest} from './registry.js'\n\n/** One forwarded interface record on the dev-server registry entry. */\nexport type DevServerInterface = NonNullable<DevServerManifest['interfaces']>[number]\n\n/** One forwarded config on the dev-server registry entry. */\nexport type DevServerConfig = NonNullable<DevServerManifest['configs']>[number]\n\n/**\n * Map a workbench app's declarations to its registry interface records:\n * `views` → panels, `services` → workers, `entry` → the `app` view. Each mirrors\n * a deployed record so the workbench loads a local interface like a deployed one.\n * `undefined` for a non-branded app; a studio that declares `entry` is rejected\n * (studio app views aren't implemented yet).\n */\nexport function deriveInterfaces(\n app: CliConfig['app'],\n options: {isApp: boolean},\n): DevServerInterface[] | undefined {\n if (!isWorkbenchApp(app)) return undefined\n\n if (!options.isApp && app.entry !== undefined) {\n throw new Error('App views for studios are not implemented yet')\n }\n\n const interfaceId = (type: string, name: string): string => `${app.name}-${type}-${name}`\n\n const views = (app.views ?? []).map(\n (view): DevServerInterface => ({\n id: interfaceId('panel', view.name),\n metadata: null,\n moduleId: interfaceModuleId('panel', view.name),\n name: view.name,\n src: view.src,\n title: view.title ?? view.name,\n type: 'panel',\n version: String(VIEW_CONTRACT_VERSION),\n }),\n )\n\n const services = (app.services ?? []).map(\n (service): DevServerInterface => ({\n id: interfaceId('worker', service.name),\n metadata: null,\n moduleId: interfaceModuleId('worker', service.name),\n name: service.name,\n src: service.src,\n title: service.title ?? service.name,\n type: 'worker',\n version: String(SERVICE_CONTRACT_VERSION),\n }),\n )\n\n const appView: DevServerInterface[] =\n app.entry === undefined\n ? []\n : [\n {\n id: interfaceId('app', app.name),\n metadata: null,\n moduleId: interfaceModuleId('app', app.name),\n name: app.name,\n src: app.entry,\n title: app.title,\n type: 'app',\n },\n ]\n\n return [...views, ...services, ...appView]\n}\n\n/**\n * The named source files a config's generated module is built from, dispatched\n * per app type — the projection the exposes-set id keys on, so the generic HMR\n * tracker owns none of the per-type shape. Throws on an app type it can't\n * handle, so a new config family has to register its shape here.\n */\nexport function deriveConfigEntries(config: DevServerConfig): {name: string; src: string}[] {\n switch (config.appType) {\n case 'media-library': {\n return config.fields.map((field) => ({name: field.name, src: field.src}))\n }\n default: {\n throw new Error(`Cannot derive entries for unknown config appType: ${config.appType}`)\n }\n }\n}\n\n/**\n * The fields' schema *values* can't serialize — the workbench loads them from\n * the federation module. `src` stays on so the exposes-set id keys on it and a\n * repoint rebuilds. `appType` routes the config to the singleton (no app id to\n * key on). `id` is a content hash of the entry — it fills the\n * installation-config id slot deployed apps get from the applications API,\n * and the workbench keys change detection on it. `version` is the config\n * contract version the generated module exports, known before the module runs.\n */\nexport async function deriveConfigs(app: CliConfig['app']): Promise<DevServerConfig[]> {\n if (!isWorkbenchApp(app)) return []\n const config = readConfig(app)\n if (!config) return []\n const entry = {\n appType: config.appType,\n fields: config.fields.map((field) => ({\n name: field.name,\n public: field.public,\n src: field.src,\n title: field.title,\n })),\n moduleName: app.name,\n version: MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION,\n }\n return [{...entry, id: await contentHash(JSON.stringify(entry))}]\n}\n\n/**\n * SHA-256 of a string, as hex, via the Web Crypto API — available in both Node\n * and the browser. `node:crypto` can't be used: the Vite dev server's dep scan\n * pulls this module into the browser graph.\n */\nasync function contentHash(input: string): Promise<string> {\n // eslint-disable-next-line n/no-unsupported-features/node-builtins -- the Web Crypto global is available on our Node target and in the browser\n const digest = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(input))\n return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, '0')).join('')\n}\n"],"names":["interfaceModuleId","MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION","SERVICE_CONTRACT_VERSION","VIEW_CONTRACT_VERSION","isWorkbenchApp","readConfig","deriveInterfaces","app","options","undefined","isApp","entry","Error","interfaceId","type","name","views","map","view","id","metadata","moduleId","src","title","version","String","services","service","appView","deriveConfigEntries","config","appType","fields","field","deriveConfigs","public","moduleName","contentHash","JSON","stringify","input","digest","globalThis","crypto","subtle","TextEncoder","encode","Array","from","Uint8Array","byte","toString","padStart","join"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getCliConfigUncached } from '@sanity/cli-core';
|
|
2
|
+
import { resolveAppId } from '../../appId.js';
|
|
2
3
|
import { deriveConfigs, deriveInterfaces } from './deriveInterfaces.js';
|
|
3
4
|
import { trackExposesSet } from './exposesSetId.js';
|
|
4
5
|
import { registerDevServer } from './registry.js';
|
|
@@ -16,18 +17,24 @@ import { startDevManifestWatcher } from './startDevManifestWatcher.js';
|
|
|
16
17
|
* interface changes. The workbench reads the entry to locate and render the
|
|
17
18
|
* server; the watcher keeps it current as `sanity.cli.ts` is edited.
|
|
18
19
|
*/ export async function startDevServerRegistration(options) {
|
|
19
|
-
const {
|
|
20
|
+
const { cliConfig, extractManifest, isApp, onInterfaceSetChange, output, server, workDir } = options;
|
|
20
21
|
const { host: appHost, port: appPort } = serverAddress(server);
|
|
21
22
|
// Forwarded alongside (not inside) the manifest so the workbench renders local
|
|
22
23
|
// panels/workers and reads the configs without a deploy.
|
|
23
24
|
const interfaces = deriveInterfaces(cliConfig.app, {
|
|
24
25
|
isApp
|
|
25
26
|
});
|
|
26
|
-
const configs = deriveConfigs(cliConfig.app);
|
|
27
|
+
const configs = await deriveConfigs(cliConfig.app);
|
|
27
28
|
const registration = registerDevServer({
|
|
28
29
|
configs,
|
|
29
30
|
host: appHost,
|
|
30
|
-
id
|
|
31
|
+
// Keyed by where it's served (not the deployment id), so a running app can't
|
|
32
|
+
// collide with its deployed twin — on the configured port, not the bound one,
|
|
33
|
+
// to match `__SANITY_APP_ID__`, compiled before any non-strict shift.
|
|
34
|
+
id: resolveAppId({
|
|
35
|
+
host: appHost,
|
|
36
|
+
port: server.config.server.port ?? appPort
|
|
37
|
+
}),
|
|
31
38
|
interfaces,
|
|
32
39
|
port: appPort,
|
|
33
40
|
projectId: cliConfig?.api?.projectId,
|
|
@@ -44,7 +51,7 @@ import { startDevManifestWatcher } from './startDevManifestWatcher.js';
|
|
|
44
51
|
extract: async (params)=>{
|
|
45
52
|
const app = (await getCliConfigUncached(params.workDir)).app;
|
|
46
53
|
return {
|
|
47
|
-
configs: deriveConfigs(app),
|
|
54
|
+
configs: await deriveConfigs(app),
|
|
48
55
|
interfaces: deriveInterfaces(app, {
|
|
49
56
|
isApp
|
|
50
57
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/dev/startDevServerRegistration.ts"],"sourcesContent":["import {type CliConfig, getCliConfigUncached, type Output} from '@sanity/cli-core'\nimport {type ViteDevServer} from 'vite'\n\nimport {deriveConfigs, deriveInterfaces} from './deriveInterfaces.js'\nimport {trackExposesSet} from './exposesSetId.js'\nimport {type DevServerManifest, registerDevServer} from './registry.js'\nimport {startDevManifestWatcher} from './startDevManifestWatcher.js'\n\ninterface DevServerRegistrationOptions {\n
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/dev/startDevServerRegistration.ts"],"sourcesContent":["import {type CliConfig, getCliConfigUncached, type Output} from '@sanity/cli-core'\nimport {type ViteDevServer} from 'vite'\n\nimport {resolveAppId} from '../../appId.js'\nimport {deriveConfigs, deriveInterfaces} from './deriveInterfaces.js'\nimport {trackExposesSet} from './exposesSetId.js'\nimport {type DevServerManifest, registerDevServer} from './registry.js'\nimport {startDevManifestWatcher} from './startDevManifestWatcher.js'\n\ninterface DevServerRegistrationOptions {\n cliConfig: CliConfig\n /**\n * Extract the project manifest to inline into the registry. The caller owns the\n * studio-vs-app split (manifest formats are CLI-domain); registration re-derives\n * the interface set alongside it.\n */\n extractManifest: (params: {\n configPath: string\n workDir: string\n }) => Promise<DevServerManifest['manifest']>\n isApp: boolean\n output: Output\n server: ViteDevServer\n workDir: string\n\n /**\n * Rebuild the app's federation remote when its interface set changes, awaited\n * *before* the registry patch — the patch reloads the workbench page, which must\n * re-fetch a remote that already exposes the new interface. Resolves with the\n * recreated server so the entry gets its actual address (non-strict ports may\n * shift it); must reject if the restart produces no server, so the set stays\n * uncommitted and the next save retries instead of advertising a dead port.\n */\n onInterfaceSetChange?: () => Promise<ViteDevServer>\n}\n\ninterface DevServerRegistrationHandle {\n close: () => Promise<void>\n}\n\n/** The address the server actually bound — the live socket, which can differ from the configured port under non-strict ports. */\nfunction serverAddress(server: ViteDevServer) {\n const resolvedHost = server.config.server.host\n const addr = server.httpServer?.address()\n return {\n host: typeof resolvedHost === 'string' ? resolvedHost : 'localhost',\n port: typeof addr === 'object' && addr ? addr.port : server.config.server.port,\n }\n}\n\n/**\n * Register the dev server in the registry and watch its config for manifest +\n * interface changes. The workbench reads the entry to locate and render the\n * server; the watcher keeps it current as `sanity.cli.ts` is edited.\n */\nexport async function startDevServerRegistration(\n options: DevServerRegistrationOptions,\n): Promise<DevServerRegistrationHandle> {\n const {cliConfig, extractManifest, isApp, onInterfaceSetChange, output, server, workDir} = options\n\n const {host: appHost, port: appPort} = serverAddress(server)\n\n // Forwarded alongside (not inside) the manifest so the workbench renders local\n // panels/workers and reads the configs without a deploy.\n const interfaces = deriveInterfaces(cliConfig.app, {isApp})\n const configs = await deriveConfigs(cliConfig.app)\n\n const registration = registerDevServer({\n configs,\n host: appHost,\n // Keyed by where it's served (not the deployment id), so a running app can't\n // collide with its deployed twin — on the configured port, not the bound one,\n // to match `__SANITY_APP_ID__`, compiled before any non-strict shift.\n id: resolveAppId({host: appHost, port: server.config.server.port ?? appPort}),\n interfaces,\n port: appPort,\n projectId: cliConfig?.api?.projectId,\n type: isApp ? 'coreApp' : 'studio',\n workDir,\n })\n\n const exposesSet = trackExposesSet({configs, interfaces})\n\n const watcher = await startDevManifestWatcher({\n // Re-derive every pass (don't omit): the registry patch is a shallow merge,\n // so omitting would wipe the registered set.\n extract: async (params) => {\n const app = (await getCliConfigUncached(params.workDir)).app\n return {\n configs: await deriveConfigs(app),\n interfaces: deriveInterfaces(app, {isApp}),\n manifest: await extractManifest(params),\n }\n },\n // A studio's root resolves to `sanity.config.*` but its interfaces live in\n // `sanity.cli.*` — watch that too. Apps already root at `sanity.cli.*`.\n extraWatchFilenames: isApp ? undefined : ['sanity.cli.js', 'sanity.cli.ts'],\n output,\n update: async (patch) => {\n if (\n !exposesSet.changed({\n configs: patch.configs,\n interfaces: patch.interfaces,\n })\n ) {\n registration.update(patch)\n return\n }\n // Rebuild the remote *before* patching the registry — the patch reloads the\n // page, which must re-fetch a remote that already exposes the new interface.\n const rebuiltServer = await onInterfaceSetChange?.()\n // Commit only after a successful rebuild, so a thrown one retries next pass.\n exposesSet.commit({\n configs: patch.configs,\n interfaces: patch.interfaces,\n })\n // The recreated server can bind a different port (non-strict ports).\n registration.update(rebuiltServer ? {...patch, ...serverAddress(rebuiltServer)} : patch)\n },\n workDir,\n })\n\n return {\n close: async () => {\n registration.release()\n await watcher.close()\n },\n }\n}\n"],"names":["getCliConfigUncached","resolveAppId","deriveConfigs","deriveInterfaces","trackExposesSet","registerDevServer","startDevManifestWatcher","serverAddress","server","resolvedHost","config","host","addr","httpServer","address","port","startDevServerRegistration","options","cliConfig","extractManifest","isApp","onInterfaceSetChange","output","workDir","appHost","appPort","interfaces","app","configs","registration","id","projectId","api","type","exposesSet","watcher","extract","params","manifest","extraWatchFilenames","undefined","update","patch","changed","rebuiltServer","commit","close","release"],"mappings":"AAAA,SAAwBA,oBAAoB,QAAoB,mBAAkB;AAGlF,SAAQC,YAAY,QAAO,iBAAgB;AAC3C,SAAQC,aAAa,EAAEC,gBAAgB,QAAO,wBAAuB;AACrE,SAAQC,eAAe,QAAO,oBAAmB;AACjD,SAAgCC,iBAAiB,QAAO,gBAAe;AACvE,SAAQC,uBAAuB,QAAO,+BAA8B;AAiCpE,+HAA+H,GAC/H,SAASC,cAAcC,MAAqB;IAC1C,MAAMC,eAAeD,OAAOE,MAAM,CAACF,MAAM,CAACG,IAAI;IAC9C,MAAMC,OAAOJ,OAAOK,UAAU,EAAEC;IAChC,OAAO;QACLH,MAAM,OAAOF,iBAAiB,WAAWA,eAAe;QACxDM,MAAM,OAAOH,SAAS,YAAYA,OAAOA,KAAKG,IAAI,GAAGP,OAAOE,MAAM,CAACF,MAAM,CAACO,IAAI;IAChF;AACF;AAEA;;;;CAIC,GACD,OAAO,eAAeC,2BACpBC,OAAqC;IAErC,MAAM,EAACC,SAAS,EAAEC,eAAe,EAAEC,KAAK,EAAEC,oBAAoB,EAAEC,MAAM,EAAEd,MAAM,EAAEe,OAAO,EAAC,GAAGN;IAE3F,MAAM,EAACN,MAAMa,OAAO,EAAET,MAAMU,OAAO,EAAC,GAAGlB,cAAcC;IAErD,+EAA+E;IAC/E,yDAAyD;IACzD,MAAMkB,aAAavB,iBAAiBe,UAAUS,GAAG,EAAE;QAACP;IAAK;IACzD,MAAMQ,UAAU,MAAM1B,cAAcgB,UAAUS,GAAG;IAEjD,MAAME,eAAexB,kBAAkB;QACrCuB;QACAjB,MAAMa;QACN,6EAA6E;QAC7E,8EAA8E;QAC9E,sEAAsE;QACtEM,IAAI7B,aAAa;YAACU,MAAMa;YAAST,MAAMP,OAAOE,MAAM,CAACF,MAAM,CAACO,IAAI,IAAIU;QAAO;QAC3EC;QACAX,MAAMU;QACNM,WAAWb,WAAWc,KAAKD;QAC3BE,MAAMb,QAAQ,YAAY;QAC1BG;IACF;IAEA,MAAMW,aAAa9B,gBAAgB;QAACwB;QAASF;IAAU;IAEvD,MAAMS,UAAU,MAAM7B,wBAAwB;QAC5C,4EAA4E;QAC5E,6CAA6C;QAC7C8B,SAAS,OAAOC;YACd,MAAMV,MAAM,AAAC,CAAA,MAAM3B,qBAAqBqC,OAAOd,OAAO,CAAA,EAAGI,GAAG;YAC5D,OAAO;gBACLC,SAAS,MAAM1B,cAAcyB;gBAC7BD,YAAYvB,iBAAiBwB,KAAK;oBAACP;gBAAK;gBACxCkB,UAAU,MAAMnB,gBAAgBkB;YAClC;QACF;QACA,2EAA2E;QAC3E,wEAAwE;QACxEE,qBAAqBnB,QAAQoB,YAAY;YAAC;YAAiB;SAAgB;QAC3ElB;QACAmB,QAAQ,OAAOC;YACb,IACE,CAACR,WAAWS,OAAO,CAAC;gBAClBf,SAASc,MAAMd,OAAO;gBACtBF,YAAYgB,MAAMhB,UAAU;YAC9B,IACA;gBACAG,aAAaY,MAAM,CAACC;gBACpB;YACF;YACA,4EAA4E;YAC5E,6EAA6E;YAC7E,MAAME,gBAAgB,MAAMvB;YAC5B,6EAA6E;YAC7Ea,WAAWW,MAAM,CAAC;gBAChBjB,SAASc,MAAMd,OAAO;gBACtBF,YAAYgB,MAAMhB,UAAU;YAC9B;YACA,qEAAqE;YACrEG,aAAaY,MAAM,CAACG,gBAAgB;gBAAC,GAAGF,KAAK;gBAAE,GAAGnC,cAAcqC,cAAc;YAAA,IAAIF;QACpF;QACAnB;IACF;IAEA,OAAO;QACLuB,OAAO;YACLjB,aAAakB,OAAO;YACpB,MAAMZ,QAAQW,KAAK;QACrB;IACF;AACF"}
|
|
@@ -13,7 +13,7 @@ import { startWorkbenchDevServer, startWorkbenchRemoteCoordinator } from './star
|
|
|
13
13
|
* server falls back to the configured port and announces its own URL, exactly as
|
|
14
14
|
* a plain `sanity dev` would.
|
|
15
15
|
*/ export async function startWorkbenchDev(options) {
|
|
16
|
-
const {
|
|
16
|
+
const { cacheDir, checkForDeprecatedAppId, cliConfig, extractManifest, httpHost, httpPort, isApp, output, reactStrictMode, startAppServer, workDir } = options;
|
|
17
17
|
// The remote can't render itself, so it runs as a plain app server (not the
|
|
18
18
|
// shell) that still claims the lock and bridges the registry, so app
|
|
19
19
|
// `sanity dev`s register into it.
|
|
@@ -83,7 +83,6 @@ import { startWorkbenchDevServer, startWorkbenchRemoteCoordinator } from './star
|
|
|
83
83
|
// The deprecated-id check and manifest extractor are CLI-domain, injected here.
|
|
84
84
|
checkForDeprecatedAppId();
|
|
85
85
|
const registration = await startDevServerRegistration({
|
|
86
|
-
appId,
|
|
87
86
|
cliConfig,
|
|
88
87
|
extractManifest,
|
|
89
88
|
isApp,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/dev/startWorkbenchDev.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {type CliConfig, type Output} from '@sanity/cli-core'\n\nimport {createServerLifecycle, toDisplayHost} from '../../util/serverOrchestration.js'\nimport {type AppServerResult, startAppServerSupervisor} from './appServerSupervisor.js'\nimport {type DevServerManifest} from './registry.js'\nimport {startDevServerRegistration} from './startDevServerRegistration.js'\nimport {\n startWorkbenchDevServer,\n startWorkbenchRemoteCoordinator,\n} from './startWorkbenchDevServer.js'\n\nexport interface StartWorkbenchDevOptions {\n /**
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/dev/startWorkbenchDev.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {type CliConfig, type Output} from '@sanity/cli-core'\n\nimport {createServerLifecycle, toDisplayHost} from '../../util/serverOrchestration.js'\nimport {type AppServerResult, startAppServerSupervisor} from './appServerSupervisor.js'\nimport {type DevServerManifest} from './registry.js'\nimport {startDevServerRegistration} from './startDevServerRegistration.js'\nimport {\n startWorkbenchDevServer,\n startWorkbenchRemoteCoordinator,\n} from './startWorkbenchDevServer.js'\n\nexport interface StartWorkbenchDevOptions {\n /** Directory for the workbench Vite server's dependency cache. */\n cacheDir: string\n /** CLI-domain `app.id`/`deployment.appId` deprecation check, run before registering. */\n checkForDeprecatedAppId: () => void\n cliConfig: CliConfig\n /** Extract the project manifest to inline into the registry (studio-vs-app handled by the CLI). */\n extractManifest: (params: {\n configPath: string\n workDir: string\n }) => Promise<DevServerManifest['manifest']>\n httpHost: string | undefined\n httpPort: number\n isApp: boolean\n output: Output\n reactStrictMode: boolean\n /** Start the app/studio dev server — the CLI owns the server, this orchestrates it. */\n startAppServer: (params: {\n announceUrl: boolean\n cliConfig: CliConfig\n httpPort: number\n }) => Promise<AppServerResult>\n workDir: string\n}\n\n/**\n * Orchestrate the dev servers a workbench project needs: a singleton workbench\n * Vite server plus the app/studio dev server it renders, wired to the dev-server\n * registry so view/service edits re-sync live.\n *\n * A running workbench claims the configured port, so the app server binds the\n * next one. If the workbench can't start (package or port unavailable), the app\n * server falls back to the configured port and announces its own URL, exactly as\n * a plain `sanity dev` would.\n */\nexport async function startWorkbenchDev(\n options: StartWorkbenchDevOptions,\n): Promise<{close: () => Promise<void>}> {\n const {\n cacheDir,\n checkForDeprecatedAppId,\n cliConfig,\n extractManifest,\n httpHost,\n httpPort,\n isApp,\n output,\n reactStrictMode,\n startAppServer,\n workDir,\n } = options\n\n // The remote can't render itself, so it runs as a plain app server (not the\n // shell) that still claims the lock and bridges the registry, so app\n // `sanity dev`s register into it.\n if (process.env.SANITY_INTERNAL_IS_WORKBENCH_REMOTE === 'true') {\n const remote = await startAppServer({announceUrl: true, cliConfig, httpPort})\n if (!remote.started) return {close: async () => {}}\n\n const addr = remote.server.httpServer?.address()\n const port =\n (typeof addr === 'object' && addr ? addr.port : remote.server.config.server.port) ?? httpPort\n const coordinator = startWorkbenchRemoteCoordinator({httpHost, port, server: remote.server})\n\n return {\n close: async () => {\n await coordinator.close()\n await remote.close()\n },\n }\n }\n\n // Unwound in reverse on any failure or on close(): the watcher stops before\n // the app server, and the supervisor waits out an in-flight rebuild.\n const {close, closers, installSignalHandlers} = createServerLifecycle()\n\n const workbench = await startWorkbenchDevServer({\n cacheDir,\n cliConfig,\n httpHost,\n httpPort,\n mode: 'development',\n output,\n reactStrictMode,\n workDir,\n })\n closers.push(workbench.close)\n\n // A running workbench owns the configured port; the app server takes the next.\n // Without one it claims the configured port and announces its own URL.\n const appPort = workbench.workbenchAvailable ? workbench.workbenchPort + 1 : httpPort\n const announceUrl = !workbench.workbenchAvailable\n\n const supervised = await startAppServerSupervisor({\n cliConfig,\n start: (config) => startAppServer({announceUrl, cliConfig: config, httpPort: appPort}),\n workDir,\n }).catch(async (err) => {\n await close()\n throw err\n })\n\n if (!supervised.started) {\n // The app server already reported why (e.g. missing organization id). Hand\n // back a close that releases the workbench lock; nothing else came up.\n return {close}\n }\n const {supervisor} = supervised\n closers.push(supervisor.close)\n\n try {\n // The deprecated-id check and manifest extractor are CLI-domain, injected here.\n checkForDeprecatedAppId()\n const registration = await startDevServerRegistration({\n cliConfig,\n extractManifest,\n isApp,\n onInterfaceSetChange: () => supervisor.rebuild(),\n output,\n server: supervisor.server,\n workDir,\n })\n closers.push(registration.close)\n } catch (err) {\n // Registration runs after both servers are up; a failure here would leak the\n // workbench lock and dev servers without this teardown.\n await close()\n throw err\n }\n\n if (workbench.workbenchAvailable) {\n const workbenchUrl = `http://${toDisplayHost(workbench.httpHost)}:${workbench.workbenchPort}`\n const addr = supervisor.server.httpServer?.address()\n const port = typeof addr === 'object' && addr ? addr.port : supervisor.server.config.server.port\n output.log(\n `Workbench dev server started at ${styleText(['blue', 'underline'], workbenchUrl)} (app on port ${port})`,\n )\n }\n\n installSignalHandlers()\n\n return {close}\n}\n"],"names":["styleText","createServerLifecycle","toDisplayHost","startAppServerSupervisor","startDevServerRegistration","startWorkbenchDevServer","startWorkbenchRemoteCoordinator","startWorkbenchDev","options","cacheDir","checkForDeprecatedAppId","cliConfig","extractManifest","httpHost","httpPort","isApp","output","reactStrictMode","startAppServer","workDir","process","env","SANITY_INTERNAL_IS_WORKBENCH_REMOTE","remote","announceUrl","started","close","addr","server","httpServer","address","port","config","coordinator","closers","installSignalHandlers","workbench","mode","push","appPort","workbenchAvailable","workbenchPort","supervised","start","catch","err","supervisor","registration","onInterfaceSetChange","rebuild","workbenchUrl","log"],"mappings":"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"}
|
|
@@ -11,6 +11,7 @@ export default defineCliConfig({
|
|
|
11
11
|
app: unstable_defineApp({
|
|
12
12
|
name: '%name%',
|
|
13
13
|
title: '%title%',
|
|
14
|
+
slug: '%slug%',
|
|
14
15
|
organizationId: '%organizationId%',
|
|
15
16
|
entry: '%entry%',
|
|
16
17
|
}),
|
|
@@ -30,6 +31,7 @@ export default defineCliConfig({
|
|
|
30
31
|
app: unstable_defineApp({
|
|
31
32
|
name: '%name%',
|
|
32
33
|
title: '%title%',
|
|
34
|
+
slug: '%slug%',
|
|
33
35
|
organizationId: '%organizationId%',
|
|
34
36
|
}),
|
|
35
37
|
deployment: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/init/cliConfig.ts"],"sourcesContent":["// `sanity.cli.ts` templates for workbench (`unstable_defineApp`) projects,\n// consumed by the CLI's `init` scaffolding. The branded `unstable_defineApp`\n// result is the sole workbench (module-federation) opt-in, so its config shape\n// is workbench's to own; the CLI keeps the non-workbench templates and the\n// `%placeholder%` substitution. `%name%`/`%title%`/etc. are filled in by the\n// CLI's template processor.\n\n/** App scaffold — `entry` auto-declares the navigable app view. */\nexport const workbenchAppConfigTemplate = `\nimport {defineCliConfig, unstable_defineApp} from 'sanity/cli'\n\nexport default defineCliConfig({\n app: unstable_defineApp({\n name: '%name%',\n title: '%title%',\n organizationId: '%organizationId%',\n entry: '%entry%',\n }),\n})\n`\n\n/**\n * Studio scaffold — brands with name/title only, no `entry` (studio app views\n * aren't implemented yet).\n */\nexport const workbenchStudioConfigTemplate = `\nimport {defineCliConfig, unstable_defineApp} from 'sanity/cli'\n\nexport default defineCliConfig({\n api: {\n projectId: '%projectId%',\n dataset: '%dataset%'\n },\n app: unstable_defineApp({\n name: '%name%',\n title: '%title%',\n organizationId: '%organizationId%',\n }),\n deployment: {\n /**\n * Enable auto-updates for studios.\n * Learn more at https://www.sanity.io/docs/studio/latest-version-of-sanity#k47faf43faf56\n */\n autoUpdates: __BOOL__autoUpdates__,\n },\n})\n`\n"],"names":["workbenchAppConfigTemplate","workbenchStudioConfigTemplate"],"mappings":"AAAA,2EAA2E;AAC3E,6EAA6E;AAC7E,+EAA+E;AAC/E,2EAA2E;AAC3E,6EAA6E;AAC7E,4BAA4B;AAE5B,iEAAiE,GACjE,OAAO,MAAMA,6BAA6B,CAAC
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/init/cliConfig.ts"],"sourcesContent":["// `sanity.cli.ts` templates for workbench (`unstable_defineApp`) projects,\n// consumed by the CLI's `init` scaffolding. The branded `unstable_defineApp`\n// result is the sole workbench (module-federation) opt-in, so its config shape\n// is workbench's to own; the CLI keeps the non-workbench templates and the\n// `%placeholder%` substitution. `%name%`/`%title%`/etc. are filled in by the\n// CLI's template processor.\n\n/** App scaffold — `entry` auto-declares the navigable app view. */\nexport const workbenchAppConfigTemplate = `\nimport {defineCliConfig, unstable_defineApp} from 'sanity/cli'\n\nexport default defineCliConfig({\n app: unstable_defineApp({\n name: '%name%',\n title: '%title%',\n slug: '%slug%',\n organizationId: '%organizationId%',\n entry: '%entry%',\n }),\n})\n`\n\n/**\n * Studio scaffold — brands with name/title only, no `entry` (studio app views\n * aren't implemented yet).\n */\nexport const workbenchStudioConfigTemplate = `\nimport {defineCliConfig, unstable_defineApp} from 'sanity/cli'\n\nexport default defineCliConfig({\n api: {\n projectId: '%projectId%',\n dataset: '%dataset%'\n },\n app: unstable_defineApp({\n name: '%name%',\n title: '%title%',\n slug: '%slug%',\n organizationId: '%organizationId%',\n }),\n deployment: {\n /**\n * Enable auto-updates for studios.\n * Learn more at https://www.sanity.io/docs/studio/latest-version-of-sanity#k47faf43faf56\n */\n autoUpdates: __BOOL__autoUpdates__,\n },\n})\n`\n"],"names":["workbenchAppConfigTemplate","workbenchStudioConfigTemplate"],"mappings":"AAAA,2EAA2E;AAC3E,6EAA6E;AAC7E,+EAA+E;AAC/E,2EAA2E;AAC3E,6EAA6E;AAC7E,4BAA4B;AAE5B,iEAAiE,GACjE,OAAO,MAAMA,6BAA6B,CAAC;;;;;;;;;;;;AAY3C,CAAC,CAAA;AAED;;;CAGC,GACD,OAAO,MAAMC,gCAAgC,CAAC;;;;;;;;;;;;;;;;;;;;;;AAsB9C,CAAC,CAAA"}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
1
3
|
import { styleText } from 'node:util';
|
|
2
4
|
import { findProjectRoot } from '@sanity/cli-core';
|
|
5
|
+
import { buildAppId, SANITY_APP_ID_FILE } from '../../appId.js';
|
|
6
|
+
import { resolveWorkbenchApp } from '../../resolveWorkbenchApp.js';
|
|
3
7
|
import { createServerLifecycle, toDisplayHost } from '../../util/serverOrchestration.js';
|
|
4
8
|
import { deriveConfigs, deriveInterfaces } from '../dev/deriveInterfaces.js';
|
|
5
9
|
import { registerDevServer } from '../dev/registry.js';
|
|
@@ -46,11 +50,18 @@ import { serveBuiltApplication } from './serveBuiltApplication.js';
|
|
|
46
50
|
// out of workbench-cli.
|
|
47
51
|
checkForDeprecatedAppId();
|
|
48
52
|
const configPath = (await findProjectRoot(workDir)).path;
|
|
53
|
+
const workbench = resolveWorkbenchApp(cliConfig);
|
|
54
|
+
// Read the id the build inlined so start matches it even for a deploy build
|
|
55
|
+
// (which carries the API id, not the shape hash); fall back for older builds.
|
|
56
|
+
const inlinedId = await readInlinedAppId(outDir);
|
|
57
|
+
const configs = await deriveConfigs(cliConfig.app);
|
|
58
|
+
// `start` serves a build, so it advertises the build's inlined id (matching
|
|
59
|
+
// the bundle's `__SANITY_APP_ID__`), not the dev host-port.
|
|
60
|
+
const id = workbench ? inlinedId ?? await buildAppId(workbench) : `${remote.host}-${remote.port}`;
|
|
49
61
|
const registration = registerDevServer({
|
|
50
|
-
configs
|
|
62
|
+
configs,
|
|
51
63
|
host: remote.host,
|
|
52
|
-
|
|
53
|
-
id: `${remote.host}-${remote.port}`,
|
|
64
|
+
id,
|
|
54
65
|
interfaces: deriveInterfaces(cliConfig.app, {
|
|
55
66
|
isApp
|
|
56
67
|
}),
|
|
@@ -87,5 +98,12 @@ import { serveBuiltApplication } from './serveBuiltApplication.js';
|
|
|
87
98
|
close
|
|
88
99
|
};
|
|
89
100
|
}
|
|
101
|
+
/** The id the build inlined into its bundle, or undefined when absent. */ async function readInlinedAppId(outDir) {
|
|
102
|
+
try {
|
|
103
|
+
return (await readFile(path.join(outDir, SANITY_APP_ID_FILE), 'utf8')).trim() || undefined;
|
|
104
|
+
} catch {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
90
108
|
|
|
91
109
|
//# sourceMappingURL=startWorkbenchPreview.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/preview/startWorkbenchPreview.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {type CliConfig, findProjectRoot, type Output} from '@sanity/cli-core'\n\nimport {createServerLifecycle, toDisplayHost} from '../../util/serverOrchestration.js'\nimport {deriveConfigs, deriveInterfaces} from '../dev/deriveInterfaces.js'\nimport {type DevServerManifest, registerDevServer} from '../dev/registry.js'\nimport {startWorkbenchDevServer} from '../dev/startWorkbenchDevServer.js'\nimport {serveBuiltApplication} from './serveBuiltApplication.js'\n\nexport interface StartWorkbenchPreviewOptions {\n /** Directory for the workbench Vite server's dependency cache. */\n cacheDir: string\n /** CLI-domain `app.id`/`deployment.appId` deprecation check, run before registering. */\n checkForDeprecatedAppId: () => void\n cliConfig: CliConfig\n /** Extract the project manifest to inline into the registry (studio-vs-app handled by the CLI). */\n extractManifest: (params: {\n configPath: string\n workDir: string\n }) => Promise<DevServerManifest['manifest']>\n httpHost: string\n httpPort: number\n isApp: boolean\n /** The built `dist` directory to serve as the federation remote. */\n outDir: string\n output: Output\n reactStrictMode: boolean\n workDir: string\n}\n\n/**\n * `sanity start` for a workbench app: serve a production build the way dev serves\n * a live one. The same singleton workbench shell renders it and the same registry\n * advertises it — only the remote differs, static files from the build output\n * instead of a live Vite dev server. There's no config watcher or rebuild: a\n * build is fixed, so nothing re-syncs.\n *\n * A running workbench claims the configured port, so the built remote binds the\n * next one. Without one the remote takes the configured port and announces its\n * own URL.\n */\nexport async function startWorkbenchPreview(\n options: StartWorkbenchPreviewOptions,\n): Promise<{close: () => Promise<void>}> {\n const {\n cacheDir,\n checkForDeprecatedAppId,\n cliConfig,\n extractManifest,\n httpHost,\n httpPort,\n isApp,\n outDir,\n output,\n reactStrictMode,\n workDir,\n } = options\n\n const {close, closers, installSignalHandlers} = createServerLifecycle()\n\n const workbench = await startWorkbenchDevServer({\n cacheDir,\n cliConfig,\n httpHost,\n httpPort,\n mode: 'preview',\n output,\n reactStrictMode,\n workDir,\n })\n closers.push(workbench.close)\n\n const remotePort = workbench.workbenchAvailable ? workbench.workbenchPort + 1 : httpPort\n\n const remote = await serveBuiltApplication({\n cacheDir,\n httpHost,\n httpPort: remotePort,\n outDir,\n workDir,\n }).catch(async (err) => {\n await close()\n throw err\n })\n closers.push(remote.close)\n\n try {\n // Callers provide CLI-only validation and manifest extraction to keep them\n // out of workbench-cli.\n checkForDeprecatedAppId()\n const configPath = (await findProjectRoot(workDir)).path\n const
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/preview/startWorkbenchPreview.ts"],"sourcesContent":["import {readFile} from 'node:fs/promises'\nimport path from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {type CliConfig, findProjectRoot, type Output} from '@sanity/cli-core'\n\nimport {buildAppId, SANITY_APP_ID_FILE} from '../../appId.js'\nimport {resolveWorkbenchApp} from '../../resolveWorkbenchApp.js'\nimport {createServerLifecycle, toDisplayHost} from '../../util/serverOrchestration.js'\nimport {deriveConfigs, deriveInterfaces} from '../dev/deriveInterfaces.js'\nimport {type DevServerManifest, registerDevServer} from '../dev/registry.js'\nimport {startWorkbenchDevServer} from '../dev/startWorkbenchDevServer.js'\nimport {serveBuiltApplication} from './serveBuiltApplication.js'\n\nexport interface StartWorkbenchPreviewOptions {\n /** Directory for the workbench Vite server's dependency cache. */\n cacheDir: string\n /** CLI-domain `app.id`/`deployment.appId` deprecation check, run before registering. */\n checkForDeprecatedAppId: () => void\n cliConfig: CliConfig\n /** Extract the project manifest to inline into the registry (studio-vs-app handled by the CLI). */\n extractManifest: (params: {\n configPath: string\n workDir: string\n }) => Promise<DevServerManifest['manifest']>\n httpHost: string\n httpPort: number\n isApp: boolean\n /** The built `dist` directory to serve as the federation remote. */\n outDir: string\n output: Output\n reactStrictMode: boolean\n workDir: string\n}\n\n/**\n * `sanity start` for a workbench app: serve a production build the way dev serves\n * a live one. The same singleton workbench shell renders it and the same registry\n * advertises it — only the remote differs, static files from the build output\n * instead of a live Vite dev server. There's no config watcher or rebuild: a\n * build is fixed, so nothing re-syncs.\n *\n * A running workbench claims the configured port, so the built remote binds the\n * next one. Without one the remote takes the configured port and announces its\n * own URL.\n */\nexport async function startWorkbenchPreview(\n options: StartWorkbenchPreviewOptions,\n): Promise<{close: () => Promise<void>}> {\n const {\n cacheDir,\n checkForDeprecatedAppId,\n cliConfig,\n extractManifest,\n httpHost,\n httpPort,\n isApp,\n outDir,\n output,\n reactStrictMode,\n workDir,\n } = options\n\n const {close, closers, installSignalHandlers} = createServerLifecycle()\n\n const workbench = await startWorkbenchDevServer({\n cacheDir,\n cliConfig,\n httpHost,\n httpPort,\n mode: 'preview',\n output,\n reactStrictMode,\n workDir,\n })\n closers.push(workbench.close)\n\n const remotePort = workbench.workbenchAvailable ? workbench.workbenchPort + 1 : httpPort\n\n const remote = await serveBuiltApplication({\n cacheDir,\n httpHost,\n httpPort: remotePort,\n outDir,\n workDir,\n }).catch(async (err) => {\n await close()\n throw err\n })\n closers.push(remote.close)\n\n try {\n // Callers provide CLI-only validation and manifest extraction to keep them\n // out of workbench-cli.\n checkForDeprecatedAppId()\n const configPath = (await findProjectRoot(workDir)).path\n const workbench = resolveWorkbenchApp(cliConfig)\n // Read the id the build inlined so start matches it even for a deploy build\n // (which carries the API id, not the shape hash); fall back for older builds.\n const inlinedId = await readInlinedAppId(outDir)\n const configs = await deriveConfigs(cliConfig.app)\n // `start` serves a build, so it advertises the build's inlined id (matching\n // the bundle's `__SANITY_APP_ID__`), not the dev host-port.\n const id = workbench\n ? (inlinedId ?? (await buildAppId(workbench)))\n : `${remote.host}-${remote.port}`\n const registration = registerDevServer({\n configs,\n host: remote.host,\n id,\n interfaces: deriveInterfaces(cliConfig.app, {isApp}),\n manifest: await extractManifest({configPath, workDir}),\n manifestUpdatedAt: new Date().toISOString(),\n port: remote.port,\n projectId: cliConfig?.api?.projectId,\n type: isApp ? 'coreApp' : 'studio',\n workDir,\n })\n closers.push(async () => registration.release())\n } catch (err) {\n await close()\n throw err\n }\n\n if (workbench.workbenchAvailable) {\n const workbenchUrl = `http://${toDisplayHost(workbench.httpHost)}:${workbench.workbenchPort}`\n output.log(\n `Workbench preview server started at ${styleText(['blue', 'underline'], workbenchUrl)} (serving build on port ${remote.port})`,\n )\n } else {\n const remoteUrl = `http://${toDisplayHost(remote.host)}:${remote.port}`\n output.log(`Serving build at ${styleText(['blue', 'underline'], remoteUrl)}`)\n }\n\n installSignalHandlers()\n\n return {close}\n}\n\n/** The id the build inlined into its bundle, or undefined when absent. */\nasync function readInlinedAppId(outDir: string): Promise<string | undefined> {\n try {\n return (await readFile(path.join(outDir, SANITY_APP_ID_FILE), 'utf8')).trim() || undefined\n } catch {\n return undefined\n }\n}\n"],"names":["readFile","path","styleText","findProjectRoot","buildAppId","SANITY_APP_ID_FILE","resolveWorkbenchApp","createServerLifecycle","toDisplayHost","deriveConfigs","deriveInterfaces","registerDevServer","startWorkbenchDevServer","serveBuiltApplication","startWorkbenchPreview","options","cacheDir","checkForDeprecatedAppId","cliConfig","extractManifest","httpHost","httpPort","isApp","outDir","output","reactStrictMode","workDir","close","closers","installSignalHandlers","workbench","mode","push","remotePort","workbenchAvailable","workbenchPort","remote","catch","err","configPath","inlinedId","readInlinedAppId","configs","app","id","host","port","registration","interfaces","manifest","manifestUpdatedAt","Date","toISOString","projectId","api","type","release","workbenchUrl","log","remoteUrl","join","trim","undefined"],"mappings":"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"}
|
package/dist/appId.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File the build writes into its output, carrying the id compiled into the
|
|
3
|
+
* bundle. `sanity start` serves a build without recompiling, so it reads this
|
|
4
|
+
* instead of recomputing — a deploy inlines the API id, not the shape hash.
|
|
5
|
+
*/ export const SANITY_APP_ID_FILE = 'sanity-app-id.txt';
|
|
6
|
+
/**
|
|
7
|
+
* The dev id for a workbench app — the address the server bound. `sanity dev`
|
|
8
|
+
* keys on where the app is served so a running app can't collide with its
|
|
9
|
+
* deployed twin. Sync and dependency-free: it's re-exported from the package's
|
|
10
|
+
* browser-facing entry, so it must not pull in `node:crypto`.
|
|
11
|
+
*/ export function resolveAppId(source) {
|
|
12
|
+
return `${source.host}-${source.port}`;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The `build`/`start` id — a hash of the app's declared shape (its identity, not
|
|
16
|
+
* its code), so the bundle inlined by `sanity build` and the registry entry
|
|
17
|
+
* advertised by `sanity start` resolve to the same id. Hashed with the Web Crypto
|
|
18
|
+
* API rather than `node:crypto` for parity with `resolveAppId`'s browser-safe
|
|
19
|
+
* home. `sanity deploy` resolves its own id from the applications API.
|
|
20
|
+
*/ export async function buildAppId(app) {
|
|
21
|
+
const canonical = (interfaces)=>(interfaces ?? []).map((i)=>[
|
|
22
|
+
i.type,
|
|
23
|
+
i.name,
|
|
24
|
+
i.src
|
|
25
|
+
]).toSorted();
|
|
26
|
+
const shape = JSON.stringify({
|
|
27
|
+
config: app.config ?? null,
|
|
28
|
+
entry: app.entry ?? null,
|
|
29
|
+
name: app.name,
|
|
30
|
+
organizationId: app.organizationId,
|
|
31
|
+
services: canonical(app.services),
|
|
32
|
+
views: canonical(app.views)
|
|
33
|
+
});
|
|
34
|
+
// eslint-disable-next-line n/no-unsupported-features/node-builtins -- the Web Crypto global is available on our Node target and in the browser
|
|
35
|
+
const digest = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(shape));
|
|
36
|
+
return Array.from(new Uint8Array(digest), (byte)=>byte.toString(16).padStart(2, '0')).join('');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=appId.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/appId.ts"],"sourcesContent":["import {type ResolvedWorkbenchApp} from './resolveWorkbenchApp.js'\n\n/**\n * File the build writes into its output, carrying the id compiled into the\n * bundle. `sanity start` serves a build without recompiling, so it reads this\n * instead of recomputing — a deploy inlines the API id, not the shape hash.\n */\nexport const SANITY_APP_ID_FILE = 'sanity-app-id.txt'\n\n/**\n * The dev id for a workbench app — the address the server bound. `sanity dev`\n * keys on where the app is served so a running app can't collide with its\n * deployed twin. Sync and dependency-free: it's re-exported from the package's\n * browser-facing entry, so it must not pull in `node:crypto`.\n */\nexport function resolveAppId(source: {host: string; port: number}): string {\n return `${source.host}-${source.port}`\n}\n\n/**\n * The `build`/`start` id — a hash of the app's declared shape (its identity, not\n * its code), so the bundle inlined by `sanity build` and the registry entry\n * advertised by `sanity start` resolve to the same id. Hashed with the Web Crypto\n * API rather than `node:crypto` for parity with `resolveAppId`'s browser-safe\n * home. `sanity deploy` resolves its own id from the applications API.\n */\nexport async function buildAppId(app: ResolvedWorkbenchApp): Promise<string> {\n const canonical = (\n interfaces: ReadonlyArray<{name: string; src: string; type: string}> | undefined,\n ): Array<[string, string, string]> =>\n (interfaces ?? []).map((i): [string, string, string] => [i.type, i.name, i.src]).toSorted()\n const shape = JSON.stringify({\n config: app.config ?? null,\n entry: app.entry ?? null,\n name: app.name,\n organizationId: app.organizationId,\n services: canonical(app.services),\n views: canonical(app.views),\n })\n // eslint-disable-next-line n/no-unsupported-features/node-builtins -- the Web Crypto global is available on our Node target and in the browser\n const digest = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(shape))\n return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, '0')).join('')\n}\n"],"names":["SANITY_APP_ID_FILE","resolveAppId","source","host","port","buildAppId","app","canonical","interfaces","map","i","type","name","src","toSorted","shape","JSON","stringify","config","entry","organizationId","services","views","digest","globalThis","crypto","subtle","TextEncoder","encode","Array","from","Uint8Array","byte","toString","padStart","join"],"mappings":"AAEA;;;;CAIC,GACD,OAAO,MAAMA,qBAAqB,oBAAmB;AAErD;;;;;CAKC,GACD,OAAO,SAASC,aAAaC,MAAoC;IAC/D,OAAO,GAAGA,OAAOC,IAAI,CAAC,CAAC,EAAED,OAAOE,IAAI,EAAE;AACxC;AAEA;;;;;;CAMC,GACD,OAAO,eAAeC,WAAWC,GAAyB;IACxD,MAAMC,YAAY,CAChBC,aAEA,AAACA,CAAAA,cAAc,EAAE,AAAD,EAAGC,GAAG,CAAC,CAACC,IAAgC;gBAACA,EAAEC,IAAI;gBAAED,EAAEE,IAAI;gBAAEF,EAAEG,GAAG;aAAC,EAAEC,QAAQ;IAC3F,MAAMC,QAAQC,KAAKC,SAAS,CAAC;QAC3BC,QAAQZ,IAAIY,MAAM,IAAI;QACtBC,OAAOb,IAAIa,KAAK,IAAI;QACpBP,MAAMN,IAAIM,IAAI;QACdQ,gBAAgBd,IAAIc,cAAc;QAClCC,UAAUd,UAAUD,IAAIe,QAAQ;QAChCC,OAAOf,UAAUD,IAAIgB,KAAK;IAC5B;IACA,+IAA+I;IAC/I,MAAMC,SAAS,MAAMC,WAAWC,MAAM,CAACC,MAAM,CAACH,MAAM,CAAC,WAAW,IAAII,cAAcC,MAAM,CAACb;IACzF,OAAOc,MAAMC,IAAI,CAAC,IAAIC,WAAWR,SAAS,CAACS,OAASA,KAAKC,QAAQ,CAAC,IAAIC,QAAQ,CAAC,GAAG,MAAMC,IAAI,CAAC;AAC/F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/resolveWorkbenchApp.ts"],"sourcesContent":["// Package-internal shared resolver: turn a CLI config's branded\n// `unstable_defineApp` app into its declared interfaces, or `null` for a plain\n// project. The build and deploy accessors (actions/build, actions/deploy) each\n// build their command-specific view on top of this one brand-check +\n// extraction, so the discrimination lives in exactly one place.\n\nimport {type AppVisibility, type CliConfig} from '@sanity/cli-core'\n\nimport {type DefineAppInput, isWorkbenchApp, readConfig, type WorkbenchApp} from './defineApp.js'\n\n/**\n * Bundled so adding a declaration family touches this type and the artifact\n * expanders, not every hop of build/dev plumbing in between.\n * @internal\n */\nexport interface WorkbenchExposes {\n config?: WorkbenchApp['config']\n services?: DefineAppInput['services']\n views?: DefineAppInput['views']\n}\n\n/** @public */\nexport interface ResolvedWorkbenchApp {\n /** The app's unique `name` from `unstable_defineApp`. */\n readonly name: string\n /** Background worker services the app declares. */\n readonly services: NonNullable<DefineAppInput['services']>\n\n /** Dock panel views the app declares. */\n readonly views: NonNullable<DefineAppInput['views']>\n\n /** Resolved app kind — `studio` or one of the SDK app types. */\n readonly applicationType?: string\n /** Deploys on its own path, separate from the interfaces. */\n readonly config?: WorkbenchApp['config']\n /** SDK app-view entrypoint, when declared. */\n readonly entry?: string\n /** Path to the app's icon SVG, resolved and shipped to Brett on deploy. */\n readonly icon?: string\n /** Explicit singleton flag (a Sanity-owned app); `undefined` when the app doesn't set it. */\n readonly isSingleton?: boolean\n /**
|
|
1
|
+
{"version":3,"sources":["../src/resolveWorkbenchApp.ts"],"sourcesContent":["// Package-internal shared resolver: turn a CLI config's branded\n// `unstable_defineApp` app into its declared interfaces, or `null` for a plain\n// project. The build and deploy accessors (actions/build, actions/deploy) each\n// build their command-specific view on top of this one brand-check +\n// extraction, so the discrimination lives in exactly one place.\n\nimport {type AppVisibility, type CliConfig} from '@sanity/cli-core'\n\nimport {type DefineAppInput, isWorkbenchApp, readConfig, type WorkbenchApp} from './defineApp.js'\n\n/**\n * Bundled so adding a declaration family touches this type and the artifact\n * expanders, not every hop of build/dev plumbing in between.\n * @internal\n */\nexport interface WorkbenchExposes {\n config?: WorkbenchApp['config']\n services?: DefineAppInput['services']\n views?: DefineAppInput['views']\n}\n\n/** @public */\nexport interface ResolvedWorkbenchApp {\n /** The app's unique `name` from `unstable_defineApp`. */\n readonly name: string\n /** Organization that owns the app — part of its build-id identity. */\n readonly organizationId: string\n /** Background worker services the app declares. */\n readonly services: NonNullable<DefineAppInput['services']>\n\n /** Hostname the application is created at on first deploy. */\n readonly slug: string\n\n /** Dock panel views the app declares. */\n readonly views: NonNullable<DefineAppInput['views']>\n\n /** Resolved app kind — `studio` or one of the SDK app types. */\n readonly applicationType?: string\n /** Deploys on its own path, separate from the interfaces. */\n readonly config?: WorkbenchApp['config']\n /** SDK app-view entrypoint, when declared. */\n readonly entry?: string\n /** Path to the app's icon SVG, resolved and shipped to Brett on deploy. */\n readonly icon?: string\n /** Explicit singleton flag (a Sanity-owned app); `undefined` when the app doesn't set it. */\n readonly isSingleton?: boolean\n /** Dashboard visibility declared by the app; `undefined` when unset. */\n readonly visibility?: AppVisibility\n}\n\n/**\n * Resolve the workbench app for a CLI config, or `null` for a plain project.\n * @public\n */\nexport function resolveWorkbenchApp(\n cliConfig: CliConfig | null | undefined,\n): ResolvedWorkbenchApp | null {\n const app = cliConfig?.app\n if (!isWorkbenchApp(app)) return null\n\n return {\n applicationType: app.applicationType,\n config: readConfig(app),\n entry: app.entry,\n icon: app.icon,\n isSingleton: app.isSingleton,\n name: app.name,\n organizationId: app.organizationId,\n services: app.services ?? [],\n slug: app.slug,\n views: app.views ?? [],\n visibility: app.visibility,\n }\n}\n"],"names":["isWorkbenchApp","readConfig","resolveWorkbenchApp","cliConfig","app","applicationType","config","entry","icon","isSingleton","name","organizationId","services","slug","views","visibility"],"mappings":"AAAA,gEAAgE;AAChE,+EAA+E;AAC/E,+EAA+E;AAC/E,qEAAqE;AACrE,gEAAgE;AAIhE,SAA6BA,cAAc,EAAEC,UAAU,QAA0B,iBAAgB;AA0CjG;;;CAGC,GACD,OAAO,SAASC,oBACdC,SAAuC;IAEvC,MAAMC,MAAMD,WAAWC;IACvB,IAAI,CAACJ,eAAeI,MAAM,OAAO;IAEjC,OAAO;QACLC,iBAAiBD,IAAIC,eAAe;QACpCC,QAAQL,WAAWG;QACnBG,OAAOH,IAAIG,KAAK;QAChBC,MAAMJ,IAAII,IAAI;QACdC,aAAaL,IAAIK,WAAW;QAC5BC,MAAMN,IAAIM,IAAI;QACdC,gBAAgBP,IAAIO,cAAc;QAClCC,UAAUR,IAAIQ,QAAQ,IAAI,EAAE;QAC5BC,MAAMT,IAAIS,IAAI;QACdC,OAAOV,IAAIU,KAAK,IAAI,EAAE;QACtBC,YAAYX,IAAIW,UAAU;IAC5B;AACF"}
|
|
@@ -27,35 +27,39 @@ export async function getApplication(applicationId) {
|
|
|
27
27
|
throw err;
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Create an application record (no deployment), so the CLI can build with the
|
|
32
|
+
* returned id, then ship it via {@link createDeployment}.
|
|
33
|
+
*/ export async function createApplication(options) {
|
|
34
|
+
const { isSingleton, organizationId, projectId, slug, title, type, visibility } = options;
|
|
35
|
+
const client = await getClient();
|
|
36
|
+
return client.request({
|
|
37
|
+
body: {
|
|
38
|
+
organizationId,
|
|
39
|
+
slug,
|
|
40
|
+
title,
|
|
41
|
+
type,
|
|
42
|
+
...isSingleton === undefined ? {} : {
|
|
43
|
+
isSingleton
|
|
44
|
+
},
|
|
45
|
+
...visibility ? {
|
|
46
|
+
visibility
|
|
47
|
+
} : {},
|
|
48
|
+
// Studio config is set once, at create — it's immutable on redeploy.
|
|
49
|
+
...projectId ? {
|
|
50
|
+
config: {
|
|
51
|
+
studio: {
|
|
52
|
+
projectId
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
} : {}
|
|
56
|
+
},
|
|
57
|
+
method: 'POST',
|
|
58
|
+
uri: `/applications`
|
|
52
59
|
});
|
|
53
|
-
return request(`/applications`, formData);
|
|
54
60
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
* redeploy syncs the title (and icon) from config here alongside the new deployment.
|
|
58
|
-
*/ export async function updateApplication(applicationId, update) {
|
|
61
|
+
// Patch an application's mutable fields.
|
|
62
|
+
export async function updateApplication(applicationId, update) {
|
|
59
63
|
const client = await getClient();
|
|
60
64
|
await client.request({
|
|
61
65
|
body: update,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/services/applications.ts"],"sourcesContent":["import {PassThrough} from 'node:stream'\nimport {type Gzip} from 'node:zlib'\n\nimport {type AppVisibility, getGlobalCliClient} from '@sanity/cli-core'\nimport {isStaging} from '@sanity/cli-core/util'\nimport FormData from 'form-data'\n\nimport {type AppInterfaceMetadata} from '../contract.js'\nimport {APP_WORKBENCH_API_VERSION} from './apiVersion.js'\n\nexport type ApplicationType = 'coreApp' | 'studio'\n\nexport interface Application {\n id: string\n organizationId: string\n slug: string | null\n title: string\n type: ApplicationType\n}\n\ninterface BrettInterfaceBase {\n moduleId: string\n name: string\n title: string\n version: string\n}\n\n/**\n * An interface as Brett stores it, discriminated on `type`. `moduleId` is\n * remote-relative — the host prepends the app's id. Brett assigns the id.\n * @internal\n */\nexport type BrettInterface =\n | (BrettInterfaceBase & {metadata: AppInterfaceMetadata | null; type: 'app'})\n | (BrettInterfaceBase & {metadata: null; type: 'panel'})\n | (BrettInterfaceBase & {metadata: null; type: 'worker'})\n\n/** A studio workspace as Brett stores it. */\nexport interface BrettWorkspace {\n dataset: string\n projectId: string\n\n basePath?: string\n icon?: string\n name?: string\n subtitle?: string\n title?: string\n}\n\nexport function getWorkbenchUrl(organizationId: string): string {\n return `https://${organizationId}.${isStaging() ? 'run.sanity.work' : 'sanity.run'}`\n}\n\n/** Where a deployed application is served on its organization's workbench. */\nexport function getApplicationUrl(\n application: Pick<Application, 'id' | 'organizationId' | 'type'>,\n): string {\n const segment = application.type === 'studio' ? 'studio' : 'application'\n return `${getWorkbenchUrl(application.organizationId)}/${segment}/${application.id}`\n}\n\nasync function getClient() {\n return getGlobalCliClient({apiVersion: APP_WORKBENCH_API_VERSION, requireUser: true})\n}\n\nexport async function getApplication(applicationId: string): Promise<Application | null> {\n const client = await getClient()\n try {\n return await client.request({uri: `/applications/${applicationId}`})\n } catch (err) {\n if ((err as {statusCode?: number})?.statusCode === 404) return null\n throw err\n }\n}\n\n
|
|
1
|
+
{"version":3,"sources":["../../src/services/applications.ts"],"sourcesContent":["import {PassThrough} from 'node:stream'\nimport {type Gzip} from 'node:zlib'\n\nimport {type AppVisibility, getGlobalCliClient} from '@sanity/cli-core'\nimport {isStaging} from '@sanity/cli-core/util'\nimport FormData from 'form-data'\n\nimport {type AppInterfaceMetadata} from '../contract.js'\nimport {APP_WORKBENCH_API_VERSION} from './apiVersion.js'\n\nexport type ApplicationType = 'coreApp' | 'studio'\n\nexport interface Application {\n id: string\n organizationId: string\n slug: string | null\n title: string\n type: ApplicationType\n}\n\ninterface BrettInterfaceBase {\n moduleId: string\n name: string\n title: string\n version: string\n}\n\n/**\n * An interface as Brett stores it, discriminated on `type`. `moduleId` is\n * remote-relative — the host prepends the app's id. Brett assigns the id.\n * @internal\n */\nexport type BrettInterface =\n | (BrettInterfaceBase & {metadata: AppInterfaceMetadata | null; type: 'app'})\n | (BrettInterfaceBase & {metadata: null; type: 'panel'})\n | (BrettInterfaceBase & {metadata: null; type: 'worker'})\n\n/** A studio workspace as Brett stores it. */\nexport interface BrettWorkspace {\n dataset: string\n projectId: string\n /** Lexicon schema descriptor id; Brett requires one per workspace. */\n schemaDescriptorId: string\n\n basePath?: string\n icon?: string\n name?: string\n subtitle?: string\n title?: string\n}\n\nexport function getWorkbenchUrl(organizationId: string): string {\n return `https://${organizationId}.${isStaging() ? 'run.sanity.work' : 'sanity.run'}`\n}\n\n/** Where a deployed application is served on its organization's workbench. */\nexport function getApplicationUrl(\n application: Pick<Application, 'id' | 'organizationId' | 'type'>,\n): string {\n const segment = application.type === 'studio' ? 'studio' : 'application'\n return `${getWorkbenchUrl(application.organizationId)}/${segment}/${application.id}`\n}\n\nasync function getClient() {\n return getGlobalCliClient({apiVersion: APP_WORKBENCH_API_VERSION, requireUser: true})\n}\n\nexport async function getApplication(applicationId: string): Promise<Application | null> {\n const client = await getClient()\n try {\n return await client.request({uri: `/applications/${applicationId}`})\n } catch (err) {\n if ((err as {statusCode?: number})?.statusCode === 404) return null\n throw err\n }\n}\n\n/**\n * Create an application record (no deployment), so the CLI can build with the\n * returned id, then ship it via {@link createDeployment}.\n */\nexport async function createApplication(options: {\n isSingleton?: boolean\n organizationId: string\n projectId?: string\n slug: string\n title: string\n type: ApplicationType\n visibility?: AppVisibility\n}): Promise<Application> {\n const {isSingleton, organizationId, projectId, slug, title, type, visibility} = options\n const client = await getClient()\n return client.request({\n body: {\n organizationId,\n slug,\n title,\n type,\n ...(isSingleton === undefined ? {} : {isSingleton}),\n ...(visibility ? {visibility} : {}),\n // Studio config is set once, at create — it's immutable on redeploy.\n ...(projectId ? {config: {studio: {projectId}}} : {}),\n },\n method: 'POST',\n uri: `/applications`,\n })\n}\n\n/** Mutable application fields the deploy flow patches after create. */\nexport interface ApplicationUpdate {\n icon?: string | null\n title?: string\n visibility?: AppVisibility\n}\n\n// Patch an application's mutable fields.\nexport async function updateApplication(\n applicationId: string,\n update: ApplicationUpdate,\n): Promise<void> {\n const client = await getClient()\n await client.request({body: update, method: 'PATCH', uri: `/applications/${applicationId}`})\n}\n\n/** Deploy a new active version to an existing application. */\nexport async function createDeployment(options: {\n applicationId: string\n interfaces: readonly BrettInterface[]\n isAutoUpdating: boolean\n tarball: Gzip\n version: string\n workspaces?: readonly BrettWorkspace[]\n}): Promise<{id: string}> {\n const {applicationId, interfaces, isAutoUpdating, tarball, version, workspaces} = options\n const formData = new FormData()\n formData.append('isAutoUpdating', isAutoUpdating.toString())\n appendDeploymentParts(formData, {interfaces, tarball, version, workspaces})\n return request(`/applications/${applicationId}/deployments`, formData)\n}\n\n/** Soft-deletes the application and all its deployments; already deleted counts as done. */\nexport async function deleteApplication(applicationId: string): Promise<void> {\n const client = await getClient()\n try {\n await client.request({method: 'DELETE', uri: `/applications/${applicationId}`})\n } catch (err) {\n if ((err as {statusCode?: number})?.statusCode !== 404) throw err\n }\n}\n\nfunction appendDeploymentParts(\n formData: FormData,\n {\n interfaces,\n tarball,\n version,\n workspaces,\n }: {\n interfaces: readonly BrettInterface[]\n tarball: Gzip\n version: string\n workspaces?: readonly BrettWorkspace[]\n },\n): void {\n formData.append('version', version)\n appendJson(formData, 'interfaces', interfaces)\n // Studio-only — the server rejects a workspaces part on non-studio types.\n if (workspaces?.length) appendJson(formData, 'workspaces', workspaces)\n formData.append('tarball', tarball, {contentType: 'application/gzip', filename: 'app.tar.gz'})\n}\n\n/** Structured parts must arrive as JSON so the server parses them. */\nfunction appendJson(formData: FormData, name: string, value: unknown): void {\n formData.append(name, JSON.stringify(value), {contentType: 'application/json'})\n}\n\nasync function request<T>(uri: string, formData: FormData): Promise<T> {\n const client = await getClient()\n return client.request({\n body: formData.pipe(new PassThrough()),\n headers: formData.getHeaders(),\n method: 'POST',\n uri,\n })\n}\n"],"names":["PassThrough","getGlobalCliClient","isStaging","FormData","APP_WORKBENCH_API_VERSION","getWorkbenchUrl","organizationId","getApplicationUrl","application","segment","type","id","getClient","apiVersion","requireUser","getApplication","applicationId","client","request","uri","err","statusCode","createApplication","options","isSingleton","projectId","slug","title","visibility","body","undefined","config","studio","method","updateApplication","update","createDeployment","interfaces","isAutoUpdating","tarball","version","workspaces","formData","append","toString","appendDeploymentParts","deleteApplication","appendJson","length","contentType","filename","name","value","JSON","stringify","pipe","headers","getHeaders"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/workbench-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "Internal implementation detail of the Sanity CLI's unstable workbench support. Not intended for direct use.",
|
|
5
5
|
"homepage": "https://github.com/sanity-io/cli",
|
|
6
6
|
"bugs": "https://github.com/sanity-io/cli/issues",
|