@sanity/workbench-cli 1.9.0 → 1.10.0

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.
@@ -25,8 +25,8 @@ export declare function buildAppId(app: ResolvedWorkbenchApp): Promise<string>;
25
25
  * `applicationType`, `isSingleton`, and `config` — validated by the schema but
26
26
  * not part of the public surface (Sanity-owned apps set them via
27
27
  * `@ts-expect-error`). A union: an app declares an app `entry` (navigable) or
28
- * panel `views`, never both — but an `asset_source` view is a separate kind and
29
- * may accompany either.
28
+ * panel `views`, never both — but `asset_source` and `tile` views are separate
29
+ * kinds and may accompany either.
30
30
  * @public
31
31
  */
32
32
  declare type DefineAppInput = Omit<
@@ -40,7 +40,7 @@ declare type DefineAppInput = Omit<
40
40
  }
41
41
  | {
42
42
  entry?: string;
43
- views?: AssetSourceView[];
43
+ views?: (AssetSourceView | TileView)[];
44
44
  }
45
45
  );
46
46
 
@@ -137,6 +137,21 @@ declare const DefineAppInputSchema: z.ZodMiniObject<
137
137
  },
138
138
  z.core.$strip
139
139
  >,
140
+ z.ZodMiniObject<
141
+ {
142
+ priority: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
143
+ size: z.ZodMiniEnum<{
144
+ small: "small";
145
+ large: "large";
146
+ banner: "banner";
147
+ }>;
148
+ title: z.ZodMiniString<string>;
149
+ name: z.ZodMiniString<string>;
150
+ src: z.ZodMiniString<string>;
151
+ type: z.ZodMiniLiteral<"tile">;
152
+ },
153
+ z.core.$strip
154
+ >,
140
155
  ],
141
156
  "type"
142
157
  >
@@ -193,6 +208,14 @@ export declare function resolveWorkbenchApp(
193
208
  cliConfig: CliConfig | null | undefined,
194
209
  ): ResolvedWorkbenchApp | null;
195
210
 
211
+ /** The `tile` variant of an app's `views`. @public */
212
+ declare type TileView = Extract<
213
+ NonNullable<z.output<typeof DefineAppInputSchema>["views"]>[number],
214
+ {
215
+ type: "tile";
216
+ }
217
+ >;
218
+
196
219
  /**
197
220
  * Nominal brand the CLI discriminates on to enable the workbench build/deploy
198
221
  * codepath. Registered via `Symbol.for` so the marker survives module-realm
@@ -84,8 +84,8 @@ export declare function createStudio(options: {
84
84
  * `applicationType`, `isSingleton`, and `config` — validated by the schema but
85
85
  * not part of the public surface (Sanity-owned apps set them via
86
86
  * `@ts-expect-error`). A union: an app declares an app `entry` (navigable) or
87
- * panel `views`, never both — but an `asset_source` view is a separate kind and
88
- * may accompany either.
87
+ * panel `views`, never both — but `asset_source` and `tile` views are separate
88
+ * kinds and may accompany either.
89
89
  * @public
90
90
  */
91
91
  declare type DefineAppInput = Omit<
@@ -99,7 +99,7 @@ declare type DefineAppInput = Omit<
99
99
  }
100
100
  | {
101
101
  entry?: string;
102
- views?: AssetSourceView[];
102
+ views?: (AssetSourceView | TileView)[];
103
103
  }
104
104
  );
105
105
 
@@ -196,6 +196,21 @@ declare const DefineAppInputSchema: z.ZodMiniObject<
196
196
  },
197
197
  z.core.$strip
198
198
  >,
199
+ z.ZodMiniObject<
200
+ {
201
+ priority: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
202
+ size: z.ZodMiniEnum<{
203
+ small: "small";
204
+ large: "large";
205
+ banner: "banner";
206
+ }>;
207
+ title: z.ZodMiniString<string>;
208
+ name: z.ZodMiniString<string>;
209
+ src: z.ZodMiniString<string>;
210
+ type: z.ZodMiniLiteral<"tile">;
211
+ },
212
+ z.core.$strip
213
+ >,
199
214
  ],
200
215
  "type"
201
216
  >
@@ -371,6 +386,14 @@ export declare function summarizeInterfaces({
371
386
  lines: string[];
372
387
  };
373
388
 
389
+ /** The `tile` variant of an app's `views`. @public */
390
+ declare type TileView = Extract<
391
+ NonNullable<z.output<typeof DefineAppInputSchema>["views"]>[number],
392
+ {
393
+ type: "tile";
394
+ }
395
+ >;
396
+
374
397
  declare type ViewDeploymentPayload = z.infer<
375
398
  typeof viewDeploymentPayloadSchema
376
399
  >;
@@ -393,6 +416,7 @@ declare const viewDeploymentPayloadSchema: z.ZodMiniObject<
393
416
  type: z.ZodMiniEnum<{
394
417
  asset_source: "asset_source";
395
418
  panel: "panel";
419
+ tile: "tile";
396
420
  }>;
397
421
  },
398
422
  z.core.$loose
@@ -107,6 +107,29 @@ declare const devServerManifestSchema: z.ZodMiniObject<
107
107
  },
108
108
  z.core.$strip
109
109
  >,
110
+ z.ZodMiniObject<
111
+ {
112
+ metadata: z.ZodMiniObject<
113
+ {
114
+ priority: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
115
+ size: z.ZodMiniEnum<{
116
+ small: "small";
117
+ large: "large";
118
+ banner: "banner";
119
+ }>;
120
+ },
121
+ z.core.$strip
122
+ >;
123
+ type: z.ZodMiniLiteral<"tile">;
124
+ id: z.ZodMiniString<string>;
125
+ moduleId: z.ZodMiniString<string>;
126
+ name: z.ZodMiniString<string>;
127
+ src: z.ZodMiniString<string>;
128
+ title: z.ZodMiniString<string>;
129
+ version: z.ZodMiniOptional<z.ZodMiniString<string>>;
130
+ },
131
+ z.core.$strip
132
+ >,
110
133
  z.ZodMiniObject<
111
134
  {
112
135
  metadata: z.ZodMiniNull;
@@ -32,8 +32,8 @@ export declare interface AssetSourceViewComponents {
32
32
  * `applicationType`, `isSingleton`, and `config` — validated by the schema but
33
33
  * not part of the public surface (Sanity-owned apps set them via
34
34
  * `@ts-expect-error`). A union: an app declares an app `entry` (navigable) or
35
- * panel `views`, never both — but an `asset_source` view is a separate kind and
36
- * may accompany either.
35
+ * panel `views`, never both — but `asset_source` and `tile` views are separate
36
+ * kinds and may accompany either.
37
37
  * @public
38
38
  */
39
39
  export declare type DefineAppInput = Omit<
@@ -47,7 +47,7 @@ export declare type DefineAppInput = Omit<
47
47
  }
48
48
  | {
49
49
  entry?: string;
50
- views?: AssetSourceView[];
50
+ views?: (AssetSourceView | TileView)[];
51
51
  }
52
52
  );
53
53
 
@@ -144,6 +144,21 @@ declare const DefineAppInputSchema: z.ZodMiniObject<
144
144
  },
145
145
  z.core.$strip
146
146
  >,
147
+ z.ZodMiniObject<
148
+ {
149
+ priority: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
150
+ size: z.ZodMiniEnum<{
151
+ small: "small";
152
+ large: "large";
153
+ banner: "banner";
154
+ }>;
155
+ title: z.ZodMiniString<string>;
156
+ name: z.ZodMiniString<string>;
157
+ src: z.ZodMiniString<string>;
158
+ type: z.ZodMiniLiteral<"tile">;
159
+ },
160
+ z.core.$strip
161
+ >,
147
162
  ],
148
163
  "type"
149
164
  >
@@ -324,6 +339,60 @@ export declare interface ServiceInfo {
324
339
  /** @public */
325
340
  export declare type ServiceType = "worker";
326
341
 
342
+ /**
343
+ * A tile's view-component slot — the module-federation expose for its one island.
344
+ * @public
345
+ */
346
+ export declare type TileComponent = keyof TileViewComponents;
347
+
348
+ /** @public */
349
+ export declare type TileSize = z.infer<typeof TileSizeSchema>;
350
+
351
+ /**
352
+ * A tile's footprint family — the shape it occupies on the dashboard. Modelled
353
+ * on iOS WidgetKit families, not a linear scale: `banner` is full-width and
354
+ * shallow, which a `small`→`large` magnitude can't express. The host maps a
355
+ * family to a layout slot; the component reads it to render per footprint.
356
+ * @public
357
+ */
358
+ declare const TileSizeSchema: z.ZodMiniEnum<{
359
+ small: "small";
360
+ large: "large";
361
+ banner: "banner";
362
+ }>;
363
+
364
+ /** The `tile` variant of an app's `views`. @public */
365
+ declare type TileView = Extract<
366
+ NonNullable<z.output<typeof DefineAppInputSchema>["views"]>[number],
367
+ {
368
+ type: "tile";
369
+ }
370
+ >;
371
+
372
+ /**
373
+ * The component slot a `tile` view exposes — a single island, typed with the
374
+ * tile props.
375
+ * @public
376
+ */
377
+ export declare interface TileViewComponents {
378
+ tile: ViewComponent<TileViewProps>;
379
+ }
380
+
381
+ /**
382
+ * Props a tile component receives: its own interface record (name/src/title/
383
+ * type) plus its footprint `size`, so it can render per family. Mirrors the
384
+ * `tile` record the dashboard host renders from; drift is guarded by the stamped
385
+ * contract version. Placement `priority` is host-only metadata, not surfaced here.
386
+ * @public
387
+ */
388
+ export declare type TileViewProps = ViewComponentBaseProps<{
389
+ name: string;
390
+ size: TileSize;
391
+ src: string;
392
+ title: string;
393
+ type: "tile";
394
+ }>;
395
+
327
396
  /**
328
397
  * Declare a Sanity Workbench application. Identity at runtime — returns the same
329
398
  * object reference, tagged with the workbench brand. Field validation (the
@@ -381,6 +450,7 @@ export declare function unstable_defineView<TType extends InterfaceType>(
381
450
  declare const VIEW_COMPONENTS: {
382
451
  readonly asset_source: readonly ["asset_source"];
383
452
  readonly panel: readonly ["title", "panel"];
453
+ readonly tile: readonly ["tile"];
384
454
  };
385
455
 
386
456
  /** @internal */
@@ -405,6 +475,7 @@ declare interface ViewComponentBaseProps<TView> {
405
475
  export declare interface ViewComponentsByType {
406
476
  asset_source: AssetSourceViewComponents;
407
477
  panel: PanelViewComponents;
478
+ tile: TileViewComponents;
408
479
  }
409
480
 
410
481
  /**
@@ -1 +1 @@
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 AssetSourceComponent,\n AssetSourceViewComponents,\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"}
1
+ {"version":3,"sources":["../../src/_exports/index.ts"],"sourcesContent":["export {resolveAppId} from '../appId.js'\nexport type {InterfaceType, ServiceType, TileSize} 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 AssetSourceComponent,\n AssetSourceViewComponents,\n DefinedView,\n PanelComponent,\n PanelViewComponents,\n PanelViewProps,\n TileComponent,\n TileViewComponents,\n TileViewProps,\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"}
@@ -90,6 +90,29 @@ declare const devServerManifestSchema: z.ZodMiniObject<
90
90
  },
91
91
  z.core.$strip
92
92
  >,
93
+ z.ZodMiniObject<
94
+ {
95
+ metadata: z.ZodMiniObject<
96
+ {
97
+ priority: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
98
+ size: z.ZodMiniEnum<{
99
+ small: "small";
100
+ large: "large";
101
+ banner: "banner";
102
+ }>;
103
+ },
104
+ z.core.$strip
105
+ >;
106
+ type: z.ZodMiniLiteral<"tile">;
107
+ id: z.ZodMiniString<string>;
108
+ moduleId: z.ZodMiniString<string>;
109
+ name: z.ZodMiniString<string>;
110
+ src: z.ZodMiniString<string>;
111
+ title: z.ZodMiniString<string>;
112
+ version: z.ZodMiniOptional<z.ZodMiniString<string>>;
113
+ },
114
+ z.core.$strip
115
+ >,
93
116
  z.ZodMiniObject<
94
117
  {
95
118
  metadata: z.ZodMiniNull;
@@ -31,8 +31,8 @@ export declare function createWorkbenchUndeployAdapter(options: {
31
31
  * `applicationType`, `isSingleton`, and `config` — validated by the schema but
32
32
  * not part of the public surface (Sanity-owned apps set them via
33
33
  * `@ts-expect-error`). A union: an app declares an app `entry` (navigable) or
34
- * panel `views`, never both — but an `asset_source` view is a separate kind and
35
- * may accompany either.
34
+ * panel `views`, never both — but `asset_source` and `tile` views are separate
35
+ * kinds and may accompany either.
36
36
  * @public
37
37
  */
38
38
  declare type DefineAppInput = Omit<
@@ -46,7 +46,7 @@ declare type DefineAppInput = Omit<
46
46
  }
47
47
  | {
48
48
  entry?: string;
49
- views?: AssetSourceView[];
49
+ views?: (AssetSourceView | TileView)[];
50
50
  }
51
51
  );
52
52
 
@@ -143,6 +143,21 @@ declare const DefineAppInputSchema: z.ZodMiniObject<
143
143
  },
144
144
  z.core.$strip
145
145
  >,
146
+ z.ZodMiniObject<
147
+ {
148
+ priority: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
149
+ size: z.ZodMiniEnum<{
150
+ small: "small";
151
+ large: "large";
152
+ banner: "banner";
153
+ }>;
154
+ title: z.ZodMiniString<string>;
155
+ name: z.ZodMiniString<string>;
156
+ src: z.ZodMiniString<string>;
157
+ type: z.ZodMiniLiteral<"tile">;
158
+ },
159
+ z.core.$strip
160
+ >,
146
161
  ],
147
162
  "type"
148
163
  >
@@ -220,6 +235,14 @@ declare interface ResolvedWorkbenchApp {
220
235
  readonly visibility?: AppVisibility;
221
236
  }
222
237
 
238
+ /** The `tile` variant of an app's `views`. @public */
239
+ declare type TileView = Extract<
240
+ NonNullable<z.output<typeof DefineAppInputSchema>["views"]>[number],
241
+ {
242
+ type: "tile";
243
+ }
244
+ >;
245
+
223
246
  declare type ViewDeploymentPayload = z.infer<
224
247
  typeof viewDeploymentPayloadSchema
225
248
  >;
@@ -242,6 +265,7 @@ declare const viewDeploymentPayloadSchema: z.ZodMiniObject<
242
265
  type: z.ZodMiniEnum<{
243
266
  asset_source: "asset_source";
244
267
  panel: "panel";
268
+ tile: "tile";
245
269
  }>;
246
270
  },
247
271
  z.core.$loose
@@ -7,7 +7,8 @@ import { z } from 'zod/mini';
7
7
  src: z.string(),
8
8
  type: z.enum([
9
9
  'panel',
10
- 'asset_source'
10
+ 'asset_source',
11
+ 'tile'
11
12
  ])
12
13
  });
13
14
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/deploy/viewDeployment.ts"],"sourcesContent":["import {z} from 'zod/mini'\n\n/**\n * A view record as persisted to the application service: `type`, `name`, `src`,\n * plus any view-type-specific attributes (passed through for storage).\n */\nconst viewRecordSchema = z.looseObject({\n name: z.string().check(z.regex(/^[a-zA-Z0-9_-]+$/, 'View `name` must match /^[a-zA-Z0-9_-]+$/')),\n src: z.string(),\n type: z.enum(['panel', 'asset_source']),\n})\n\n/**\n * Payload registering an app's views with the application service on deploy.\n *\n * Phase 1 stub: the service that stores views does not exist yet, so the\n * payload is validated and logged only — never sent. Builds the contract the\n * application-service endpoint will accept.\n */\nconst viewDeploymentPayloadSchema = z.object({\n applicationId: z.string(),\n views: z.array(viewRecordSchema),\n})\n\nexport type ViewDeploymentPayload = z.infer<typeof viewDeploymentPayloadSchema>\n\n/**\n * Validates an app's declared views into the application-service payload.\n * Throws (via Zod) when a view declaration is malformed.\n */\nexport function buildViewDeploymentPayload(input: {\n applicationId: string\n views?: ReadonlyArray<Record<string, unknown>>\n}): ViewDeploymentPayload {\n return viewDeploymentPayloadSchema.parse({\n applicationId: input.applicationId,\n views: input.views ?? [],\n })\n}\n"],"names":["z","viewRecordSchema","looseObject","name","string","check","regex","src","type","enum","viewDeploymentPayloadSchema","object","applicationId","views","array","buildViewDeploymentPayload","input","parse"],"mappings":"AAAA,SAAQA,CAAC,QAAO,WAAU;AAE1B;;;CAGC,GACD,MAAMC,mBAAmBD,EAAEE,WAAW,CAAC;IACrCC,MAAMH,EAAEI,MAAM,GAAGC,KAAK,CAACL,EAAEM,KAAK,CAAC,oBAAoB;IACnDC,KAAKP,EAAEI,MAAM;IACbI,MAAMR,EAAES,IAAI,CAAC;QAAC;QAAS;KAAe;AACxC;AAEA;;;;;;CAMC,GACD,MAAMC,8BAA8BV,EAAEW,MAAM,CAAC;IAC3CC,eAAeZ,EAAEI,MAAM;IACvBS,OAAOb,EAAEc,KAAK,CAACb;AACjB;AAIA;;;CAGC,GACD,OAAO,SAASc,2BAA2BC,KAG1C;IACC,OAAON,4BAA4BO,KAAK,CAAC;QACvCL,eAAeI,MAAMJ,aAAa;QAClCC,OAAOG,MAAMH,KAAK,IAAI,EAAE;IAC1B;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/deploy/viewDeployment.ts"],"sourcesContent":["import {z} from 'zod/mini'\n\n/**\n * A view record as persisted to the application service: `type`, `name`, `src`,\n * plus any view-type-specific attributes (passed through for storage).\n */\nconst viewRecordSchema = z.looseObject({\n name: z.string().check(z.regex(/^[a-zA-Z0-9_-]+$/, 'View `name` must match /^[a-zA-Z0-9_-]+$/')),\n src: z.string(),\n type: z.enum(['panel', 'asset_source', 'tile']),\n})\n\n/**\n * Payload registering an app's views with the application service on deploy.\n *\n * Phase 1 stub: the service that stores views does not exist yet, so the\n * payload is validated and logged only — never sent. Builds the contract the\n * application-service endpoint will accept.\n */\nconst viewDeploymentPayloadSchema = z.object({\n applicationId: z.string(),\n views: z.array(viewRecordSchema),\n})\n\nexport type ViewDeploymentPayload = z.infer<typeof viewDeploymentPayloadSchema>\n\n/**\n * Validates an app's declared views into the application-service payload.\n * Throws (via Zod) when a view declaration is malformed.\n */\nexport function buildViewDeploymentPayload(input: {\n applicationId: string\n views?: ReadonlyArray<Record<string, unknown>>\n}): ViewDeploymentPayload {\n return viewDeploymentPayloadSchema.parse({\n applicationId: input.applicationId,\n views: input.views ?? [],\n })\n}\n"],"names":["z","viewRecordSchema","looseObject","name","string","check","regex","src","type","enum","viewDeploymentPayloadSchema","object","applicationId","views","array","buildViewDeploymentPayload","input","parse"],"mappings":"AAAA,SAAQA,CAAC,QAAO,WAAU;AAE1B;;;CAGC,GACD,MAAMC,mBAAmBD,EAAEE,WAAW,CAAC;IACrCC,MAAMH,EAAEI,MAAM,GAAGC,KAAK,CAACL,EAAEM,KAAK,CAAC,oBAAoB;IACnDC,KAAKP,EAAEI,MAAM;IACbI,MAAMR,EAAES,IAAI,CAAC;QAAC;QAAS;QAAgB;KAAO;AAChD;AAEA;;;;;;CAMC,GACD,MAAMC,8BAA8BV,EAAEW,MAAM,CAAC;IAC3CC,eAAeZ,EAAEI,MAAM;IACvBS,OAAOb,EAAEc,KAAK,CAACb;AACjB;AAIA;;;CAGC,GACD,OAAO,SAASc,2BAA2BC,KAG1C;IACC,OAAON,4BAA4BO,KAAK,CAAC;QACvCL,eAAeI,MAAMJ,aAAa;QAClCC,OAAOG,MAAMH,KAAK,IAAI,EAAE;IAC1B;AACF"}
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, watch, wr
2
2
  import { join } from 'node:path';
3
3
  import { coreAppManifestSchema, getSanityDataDir, studioManifestSchema, subdebug } from '@sanity/cli-core';
4
4
  import { z } from 'zod/mini';
5
- import { AppInterfaceMetadataSchema } from '../../contract.js';
5
+ import { AppInterfaceMetadataSchema, TileInterfaceMetadataSchema } from '../../contract.js';
6
6
  import { canonicalizeWatchDir } from './canonicalizeWatchDir.js';
7
7
  import { getProcessStartTime, isOurProcess } from './processLiveness.js';
8
8
  /**
@@ -60,6 +60,11 @@ const interfaceBaseFields = {
60
60
  metadata: z.null(),
61
61
  type: z.literal('asset_source')
62
62
  }),
63
+ z.object({
64
+ ...interfaceBaseFields,
65
+ metadata: TileInterfaceMetadataSchema,
66
+ type: z.literal('tile')
67
+ }),
63
68
  z.object({
64
69
  ...interfaceBaseFields,
65
70
  metadata: z.null(),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/dev/registry.ts"],"sourcesContent":["import {\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n unlinkSync,\n watch,\n writeFileSync,\n} from 'node:fs'\nimport {join} from 'node:path'\n\nimport {\n coreAppManifestSchema,\n getSanityDataDir,\n studioManifestSchema,\n subdebug,\n} from '@sanity/cli-core'\nimport {z} from 'zod/mini'\n\nimport {AppInterfaceMetadataSchema} from '../../contract.js'\nimport {canonicalizeWatchDir} from './canonicalizeWatchDir.js'\nimport {getProcessStartTime, isOurProcess} from './processLiveness.js'\n\n/**\n * The dev-server registry: how a running `sanity dev` / `sanity start` process\n * advertises itself so the workbench on this machine can find and load it.\n *\n * Two kinds of file under `~/.sanity/dev-servers/` do the coordinating:\n *\n * - `<pid>.json` — one per running app/studio server, holding where it's served\n * plus its inlined manifest and interfaces. The workbench reads these to\n * discover and render local apps. Written by `registerDevServer`, watched by\n * `watchRegistry`.\n * - `workbench.lock` — a single machine-wide lock, so only one workbench shell\n * runs at a time and later `dev`s register into it instead of starting their\n * own. Managed by `acquireWorkbenchLock` / `readWorkbenchLock`.\n *\n * Both files belong to the process that created them and must not outlive it.\n * Three things keep that true: an explicit `release()` on clean shutdown, an\n * `exit` backstop for abrupt exits (`unlinkOnProcessExit`), and a dead-pid prune\n * on read (`isOurProcess`) that clears whatever a crashed process left behind.\n */\n\nconst devDebug = subdebug('dev')\n\n/** Bump when the manifest/lock shape changes in a breaking way. */\nconst REGISTRY_VERSION = 1\n\n/**\n * The current process's start time as reported by the OS, for the `startedAt`\n * that `isOurProcess` checks on re-read. Falls back to now when the OS time is\n * unavailable — `new Date()` alone records the write time, which drifts from\n * process start by enough to look stale and get pruned right after writing.\n */\nfunction ownStartedAt(): string {\n return (getProcessStartTime(process.pid) ?? new Date()).toISOString()\n}\n\nconst interfaceBaseFields = {\n /** CLI-minted for a local interface; a deployed one gets its id from Brett. */\n id: z.string(),\n moduleId: z.string(),\n name: z.string(),\n /** Raw source vite serves; a deployed interface carries only the `moduleId`. */\n src: z.string(),\n title: z.string(),\n version: z.optional(z.string()),\n}\n\n/**\n * A forwarded interface, discriminated on `type`. Kept outside the manifest so\n * the workbench renders local panels and runs workers without a deploy.\n */\nconst devServerInterfaceSchema = z.discriminatedUnion('type', [\n z.object({\n ...interfaceBaseFields,\n metadata: z.nullable(AppInterfaceMetadataSchema),\n type: z.literal('app'),\n }),\n z.object({...interfaceBaseFields, metadata: z.null(), type: z.literal('panel')}),\n z.object({...interfaceBaseFields, metadata: z.null(), type: z.literal('asset_source')}),\n z.object({...interfaceBaseFields, metadata: z.null(), type: z.literal('worker')}),\n])\n\nconst devServerManifestSchema = z.object({\n /**\n * Field schema *values* load from the federation module; each field's `src`\n * rides along so a repoint bumps the exposes-set id and forces a rebuild.\n * Lenient — the workbench is the authority.\n */\n configs: z.optional(\n z.array(\n z.object({\n // Identifies the owning app when it has no app id (singletons).\n appType: z.optional(z.string()),\n fields: z.array(\n z.object({\n name: z.string(),\n public: z.optional(z.boolean()),\n src: z.string(),\n title: z.string(),\n }),\n ),\n // Content hash of the config — the workbench's change-detection key\n // (see deriveConfigs).\n id: z.string(),\n // The app's `unstable_defineApp` slug — the module-federation alias the\n // workbench loads this config's live values from.\n moduleName: z.optional(z.string()),\n // The version the workbench federates this config's module under —\n // a string, like the one Brett returns on a deployed `activeConfig`.\n version: z.string(),\n }),\n ),\n ),\n host: z.string(),\n id: z.optional(z.string()),\n interfaces: z.optional(z.array(devServerInterfaceSchema)),\n /**\n * Inlined manifest — either a {@link StudioManifest} or {@link CoreAppManifest},\n * validated against the shared cli-core schemas. The registry stores and\n * rebroadcasts it; the CLI is what extracts and writes it.\n */\n manifest: z.optional(z.union([studioManifestSchema, coreAppManifestSchema])),\n /**\n * ISO timestamp of the most recent successful manifest extraction. Bumped\n * on every regeneration so re-writing this registry entry triggers the\n * workbench `watchRegistry` watcher and forces a rebroadcast to clients.\n */\n manifestUpdatedAt: z.optional(z.string()),\n pid: z.number(),\n port: z.number(),\n projectId: z.optional(z.string()),\n startedAt: z.string(),\n type: z.enum(['coreApp', 'studio']),\n version: z.literal(REGISTRY_VERSION),\n workDir: z.string(),\n})\n/**\n * A manifest describing a running dev server process (studio or app).\n * Stored as `~/.sanity/dev-servers/<pid>.json`.\n *\n * The workbench singleton is tracked separately via the lock file — see\n * `acquireWorkbenchLock` and `readWorkbenchLock` below.\n */\nexport type DevServerManifest = z.infer<typeof devServerManifestSchema>\n\n/**\n * Path to the dev server registry directory. Lives under the shared Sanity\n * config directory to stay consistent with other CLI paths.\n */\nfunction getRegistryDir(): string {\n return join(getSanityDataDir(), 'dev-servers')\n}\n\n// One shared `exit` listener drives every registered cleanup, so N locks/entries\n// don't each add a listener and trip Node's MaxListeners warning.\nconst exitCleanups = new Set<() => void>()\nlet exitListenerInstalled = false\n\nfunction runExitCleanups(): void {\n for (const cleanup of exitCleanups) cleanup()\n}\n\n/** Exercise the exit backstop in tests without terminating the process; not part\n * of the package's public surface. */\nexport const runRegistryExitCleanupForTesting = runExitCleanups\n\n/**\n * Delete a registry file synchronously on process exit, as a backstop for abrupt\n * termination. Vite installs its own SIGTERM handler that calls `process.exit()`,\n * which can outrun the async server teardown and leave the lock or registry entry\n * behind — a stray dev-server that lingers until the dead-pid prune clears it. The\n * `exit` event only runs synchronous work, hence `unlinkSync`. `ownedByUs` guards\n * the shared lock so a successor that reacquired it isn't wiped. Returns a\n * detacher to call after a clean release.\n */\nfunction unlinkOnProcessExit(filePath: string, ownedByUs: () => boolean): () => void {\n const cleanup = () => {\n if (!ownedByUs()) return\n try {\n unlinkSync(filePath)\n } catch {\n // The file may already have been removed during shutdown.\n }\n }\n exitCleanups.add(cleanup)\n\n if (!exitListenerInstalled) {\n exitListenerInstalled = true\n process.once('exit', runExitCleanups)\n }\n\n return () => exitCleanups.delete(cleanup)\n}\n\ninterface DevServerRegistration {\n /** Remove the registry entry. */\n release: () => void\n /**\n * Rewrite the registry entry with partial updates merged in. Also bumps the\n * file's mtime, which fires `watchRegistry` in any workbench process and\n * triggers a rebroadcast to connected clients.\n */\n update: (patch: Partial<Omit<DevServerManifest, 'pid' | 'startedAt' | 'version'>>) => void\n}\n\n/**\n * Write a manifest file for the current process and return a handle with a\n * `release` function that removes it plus an `update` function for patching\n * fields post-registration. Uses synchronous I/O so the file exists before\n * any signal handler could fire.\n */\nexport function registerDevServer(\n manifest: Omit<DevServerManifest, 'pid' | 'startedAt' | 'version'>,\n): DevServerRegistration {\n const registryDir = getRegistryDir()\n mkdirSync(registryDir, {recursive: true})\n\n let current: DevServerManifest = {\n ...manifest,\n pid: process.pid,\n startedAt: ownStartedAt(),\n version: REGISTRY_VERSION,\n }\n\n const filePath = join(registryDir, `${process.pid}.json`)\n writeFileSync(filePath, JSON.stringify(current, null, 2))\n\n // Guard against late updates from background tasks (e.g. the initial\n // manifest extraction) landing after `release()` has deleted the file —\n // without this, the update would re-create the registry entry and leak.\n let released = false\n\n // The file is pid-named, so it's always ours to remove on exit.\n const detachExitCleanup = unlinkOnProcessExit(filePath, () => !released)\n\n return {\n release() {\n released = true\n detachExitCleanup()\n try {\n unlinkSync(filePath)\n } catch {\n // ENOENT is fine — already cleaned up\n }\n },\n update(patch) {\n if (released) return\n current = {...current, ...patch}\n writeFileSync(filePath, JSON.stringify(current, null, 2))\n },\n }\n}\n\n/**\n * Read all manifest files from the registry, prune stale entries (dead PIDs),\n * and return the live ones.\n */\nexport function getRegisteredServers(): DevServerManifest[] {\n const registryDir = getRegistryDir()\n\n if (!existsSync(registryDir)) {\n return []\n }\n\n const files = readdirSync(registryDir).filter((f) => f.endsWith('.json'))\n const servers: DevServerManifest[] = []\n\n for (const file of files) {\n const filePath = join(registryDir, file)\n let raw: unknown\n try {\n raw = JSON.parse(readFileSync(filePath, 'utf8'))\n } catch {\n continue\n }\n\n const {data, success} = devServerManifestSchema.safeParse(raw)\n if (!success) continue\n\n if (isOurProcess(data.pid, data.startedAt)) {\n servers.push(data)\n } else {\n try {\n unlinkSync(filePath)\n } catch {\n // Ignore — another process may have already cleaned it up\n }\n }\n }\n\n return servers\n}\n\ninterface RegistryWatcher {\n close(): void\n}\n\n/**\n * Watch the registry directory for changes and invoke the callback with the\n * current list of live servers whenever a change is detected.\n *\n * Uses `fs.watch` with a debounce to coalesce rapid file changes (e.g. a\n * server starting and writing its manifest triggers multiple FS events).\n */\nexport function watchRegistry(callback: (servers: DevServerManifest[]) => void): RegistryWatcher {\n const registryDir = getRegistryDir()\n mkdirSync(registryDir, {recursive: true})\n\n // Canonicalize to the real long path so `fs.watch` doesn't abort on Windows\n // short-path dirs. See `canonicalizeWatchDir`.\n const watchDir = canonicalizeWatchDir(registryDir)\n\n let debounceTimer: ReturnType<typeof setTimeout> | undefined\n\n const notify = () => {\n clearTimeout(debounceTimer)\n debounceTimer = setTimeout(() => {\n callback(getRegisteredServers())\n }, 50)\n }\n\n const watcher = watch(watchDir, notify)\n\n return {\n close() {\n clearTimeout(debounceTimer)\n watcher.close()\n },\n }\n}\n\n// The workbench singleton lock — \"one workbench per machine\". Lives in the same\n// registry dir and shares the liveness/prune model: a stale lock left by a\n// crashed process is pruned on read so the next acquire isn't blocked forever.\n\nconst workbenchLockSchema = z.object({\n host: z.string(),\n pid: z.number(),\n port: z.number(),\n startedAt: z.string(),\n version: z.literal(REGISTRY_VERSION),\n})\n\n/**\n * Read the workbench lock file and return its contents if the holding\n * process is still alive. Prunes stale locks from crashed processes.\n */\nexport function readWorkbenchLock(): z.infer<typeof workbenchLockSchema> | undefined {\n const lockPath = join(getRegistryDir(), 'workbench.lock')\n\n let contents: string\n try {\n contents = readFileSync(lockPath, 'utf8')\n } catch {\n // File doesn't exist — nothing to prune, nothing to return\n return undefined\n }\n\n // Past this point the file exists. Anything that isn't a live, valid lock\n // (unparsable JSON, schema mismatch, dead/reused PID) is stale and must be\n // pruned — otherwise the next `acquireWorkbenchLock` call is blocked by\n // EEXIST forever and `sanity dev` silently no-ops the workbench server.\n const data = parseLockContents(contents)\n devDebug('Read workbench lock: %o', data)\n if (data && isOurProcess(data.pid, data.startedAt)) {\n devDebug('Workbench process is alive at pid %d on port %d', data.pid, data.port)\n return data\n }\n\n pruneWorkbenchLock(lockPath)\n return undefined\n}\n\nfunction parseLockContents(contents: string): z.infer<typeof workbenchLockSchema> | undefined {\n try {\n const {data, success} = workbenchLockSchema.safeParse(JSON.parse(contents))\n return success ? data : undefined\n } catch {\n return undefined\n }\n}\n\nfunction pruneWorkbenchLock(lockPath: string): void {\n try {\n devDebug('Removing stale workbench lock')\n unlinkSync(lockPath)\n devDebug('Stale workbench lock removed')\n } catch {\n // Another process may have already cleaned it up\n }\n}\n\ninterface WorkbenchLock {\n /** Release the lock file. */\n release: () => void\n /** Update the lock with the actual port after the server starts listening. */\n updatePort: (port: number) => void\n}\n\n/**\n * Attempt to acquire an exclusive lock for the workbench process.\n * Uses `O_EXCL` (the `wx` flag) which is atomic at the OS level — only one\n * process can create the file.\n *\n * The lock stores `{pid, host, port}` so other processes can find the\n * running workbench. Call `updatePort` after the Vite server starts to\n * write the actual port (Vite may pick a different one).\n *\n * @returns A {@link WorkbenchLock} if acquired, or `undefined` if another\n * live process already holds it.\n */\nexport function acquireWorkbenchLock(\n info: {host: string; port: number},\n retries = 1,\n): WorkbenchLock | undefined {\n const registryDir = getRegistryDir()\n mkdirSync(registryDir, {recursive: true})\n\n const lockPath = join(registryDir, 'workbench.lock')\n const startedAt = ownStartedAt()\n const lockData = {\n host: info.host,\n pid: process.pid,\n port: info.port,\n startedAt,\n version: REGISTRY_VERSION,\n }\n\n devDebug('Acquiring workbench lock at %s', lockPath)\n\n try {\n writeFileSync(lockPath, JSON.stringify(lockData), {flag: 'wx'})\n devDebug('Workbench lock acquired')\n\n let released = false\n // Only wipe the lock on exit if it's still ours — a successor that reacquired\n // it after our own release must not be clobbered.\n const detachExitCleanup = unlinkOnProcessExit(lockPath, () => {\n if (released) return false\n try {\n const disk = parseLockContents(readFileSync(lockPath, 'utf8'))\n return disk?.pid === process.pid && disk.startedAt === startedAt\n } catch {\n return false\n }\n })\n\n return {\n release() {\n released = true\n detachExitCleanup()\n try {\n unlinkSync(lockPath)\n } catch {\n // Already cleaned up\n }\n },\n updatePort(port: number) {\n writeFileSync(lockPath, JSON.stringify({...lockData, port}))\n },\n }\n } catch (err: unknown) {\n devDebug(\n 'Failed to acquire workbench lock: %s',\n err instanceof Error ? err.message : String(err),\n )\n if (!isNodeError(err) || err.code !== 'EEXIST') return undefined\n\n // Lock exists — check if the holder is still alive\n const existing = readWorkbenchLock()\n if (existing) return undefined\n\n // Stale lock was pruned by readWorkbenchLock — retry (with guard against infinite recursion)\n if (retries <= 0) return undefined\n return acquireWorkbenchLock(info, retries - 1)\n }\n}\n\nfunction isNodeError(err: unknown): err is NodeJS.ErrnoException {\n return err instanceof Error && 'code' in err\n}\n"],"names":["existsSync","mkdirSync","readdirSync","readFileSync","unlinkSync","watch","writeFileSync","join","coreAppManifestSchema","getSanityDataDir","studioManifestSchema","subdebug","z","AppInterfaceMetadataSchema","canonicalizeWatchDir","getProcessStartTime","isOurProcess","devDebug","REGISTRY_VERSION","ownStartedAt","process","pid","Date","toISOString","interfaceBaseFields","id","string","moduleId","name","src","title","version","optional","devServerInterfaceSchema","discriminatedUnion","object","metadata","nullable","type","literal","null","devServerManifestSchema","configs","array","appType","fields","public","boolean","moduleName","host","interfaces","manifest","union","manifestUpdatedAt","number","port","projectId","startedAt","enum","workDir","getRegistryDir","exitCleanups","Set","exitListenerInstalled","runExitCleanups","cleanup","runRegistryExitCleanupForTesting","unlinkOnProcessExit","filePath","ownedByUs","add","once","delete","registerDevServer","registryDir","recursive","current","JSON","stringify","released","detachExitCleanup","release","update","patch","getRegisteredServers","files","filter","f","endsWith","servers","file","raw","parse","data","success","safeParse","push","watchRegistry","callback","watchDir","debounceTimer","notify","clearTimeout","setTimeout","watcher","close","workbenchLockSchema","readWorkbenchLock","lockPath","contents","undefined","parseLockContents","pruneWorkbenchLock","acquireWorkbenchLock","info","retries","lockData","flag","disk","updatePort","err","Error","message","String","isNodeError","code","existing"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/dev/registry.ts"],"sourcesContent":["import {\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n unlinkSync,\n watch,\n writeFileSync,\n} from 'node:fs'\nimport {join} from 'node:path'\n\nimport {\n coreAppManifestSchema,\n getSanityDataDir,\n studioManifestSchema,\n subdebug,\n} from '@sanity/cli-core'\nimport {z} from 'zod/mini'\n\nimport {AppInterfaceMetadataSchema, TileInterfaceMetadataSchema} from '../../contract.js'\nimport {canonicalizeWatchDir} from './canonicalizeWatchDir.js'\nimport {getProcessStartTime, isOurProcess} from './processLiveness.js'\n\n/**\n * The dev-server registry: how a running `sanity dev` / `sanity start` process\n * advertises itself so the workbench on this machine can find and load it.\n *\n * Two kinds of file under `~/.sanity/dev-servers/` do the coordinating:\n *\n * - `<pid>.json` — one per running app/studio server, holding where it's served\n * plus its inlined manifest and interfaces. The workbench reads these to\n * discover and render local apps. Written by `registerDevServer`, watched by\n * `watchRegistry`.\n * - `workbench.lock` — a single machine-wide lock, so only one workbench shell\n * runs at a time and later `dev`s register into it instead of starting their\n * own. Managed by `acquireWorkbenchLock` / `readWorkbenchLock`.\n *\n * Both files belong to the process that created them and must not outlive it.\n * Three things keep that true: an explicit `release()` on clean shutdown, an\n * `exit` backstop for abrupt exits (`unlinkOnProcessExit`), and a dead-pid prune\n * on read (`isOurProcess`) that clears whatever a crashed process left behind.\n */\n\nconst devDebug = subdebug('dev')\n\n/** Bump when the manifest/lock shape changes in a breaking way. */\nconst REGISTRY_VERSION = 1\n\n/**\n * The current process's start time as reported by the OS, for the `startedAt`\n * that `isOurProcess` checks on re-read. Falls back to now when the OS time is\n * unavailable — `new Date()` alone records the write time, which drifts from\n * process start by enough to look stale and get pruned right after writing.\n */\nfunction ownStartedAt(): string {\n return (getProcessStartTime(process.pid) ?? new Date()).toISOString()\n}\n\nconst interfaceBaseFields = {\n /** CLI-minted for a local interface; a deployed one gets its id from Brett. */\n id: z.string(),\n moduleId: z.string(),\n name: z.string(),\n /** Raw source vite serves; a deployed interface carries only the `moduleId`. */\n src: z.string(),\n title: z.string(),\n version: z.optional(z.string()),\n}\n\n/**\n * A forwarded interface, discriminated on `type`. Kept outside the manifest so\n * the workbench renders local panels and runs workers without a deploy.\n */\nconst devServerInterfaceSchema = z.discriminatedUnion('type', [\n z.object({\n ...interfaceBaseFields,\n metadata: z.nullable(AppInterfaceMetadataSchema),\n type: z.literal('app'),\n }),\n z.object({...interfaceBaseFields, metadata: z.null(), type: z.literal('panel')}),\n z.object({...interfaceBaseFields, metadata: z.null(), type: z.literal('asset_source')}),\n z.object({\n ...interfaceBaseFields,\n metadata: TileInterfaceMetadataSchema,\n type: z.literal('tile'),\n }),\n z.object({...interfaceBaseFields, metadata: z.null(), type: z.literal('worker')}),\n])\n\nconst devServerManifestSchema = z.object({\n /**\n * Field schema *values* load from the federation module; each field's `src`\n * rides along so a repoint bumps the exposes-set id and forces a rebuild.\n * Lenient — the workbench is the authority.\n */\n configs: z.optional(\n z.array(\n z.object({\n // Identifies the owning app when it has no app id (singletons).\n appType: z.optional(z.string()),\n fields: z.array(\n z.object({\n name: z.string(),\n public: z.optional(z.boolean()),\n src: z.string(),\n title: z.string(),\n }),\n ),\n // Content hash of the config — the workbench's change-detection key\n // (see deriveConfigs).\n id: z.string(),\n // The app's `unstable_defineApp` slug — the module-federation alias the\n // workbench loads this config's live values from.\n moduleName: z.optional(z.string()),\n // The version the workbench federates this config's module under —\n // a string, like the one Brett returns on a deployed `activeConfig`.\n version: z.string(),\n }),\n ),\n ),\n host: z.string(),\n id: z.optional(z.string()),\n interfaces: z.optional(z.array(devServerInterfaceSchema)),\n /**\n * Inlined manifest — either a {@link StudioManifest} or {@link CoreAppManifest},\n * validated against the shared cli-core schemas. The registry stores and\n * rebroadcasts it; the CLI is what extracts and writes it.\n */\n manifest: z.optional(z.union([studioManifestSchema, coreAppManifestSchema])),\n /**\n * ISO timestamp of the most recent successful manifest extraction. Bumped\n * on every regeneration so re-writing this registry entry triggers the\n * workbench `watchRegistry` watcher and forces a rebroadcast to clients.\n */\n manifestUpdatedAt: z.optional(z.string()),\n pid: z.number(),\n port: z.number(),\n projectId: z.optional(z.string()),\n startedAt: z.string(),\n type: z.enum(['coreApp', 'studio']),\n version: z.literal(REGISTRY_VERSION),\n workDir: z.string(),\n})\n/**\n * A manifest describing a running dev server process (studio or app).\n * Stored as `~/.sanity/dev-servers/<pid>.json`.\n *\n * The workbench singleton is tracked separately via the lock file — see\n * `acquireWorkbenchLock` and `readWorkbenchLock` below.\n */\nexport type DevServerManifest = z.infer<typeof devServerManifestSchema>\n\n/**\n * Path to the dev server registry directory. Lives under the shared Sanity\n * config directory to stay consistent with other CLI paths.\n */\nfunction getRegistryDir(): string {\n return join(getSanityDataDir(), 'dev-servers')\n}\n\n// One shared `exit` listener drives every registered cleanup, so N locks/entries\n// don't each add a listener and trip Node's MaxListeners warning.\nconst exitCleanups = new Set<() => void>()\nlet exitListenerInstalled = false\n\nfunction runExitCleanups(): void {\n for (const cleanup of exitCleanups) cleanup()\n}\n\n/** Exercise the exit backstop in tests without terminating the process; not part\n * of the package's public surface. */\nexport const runRegistryExitCleanupForTesting = runExitCleanups\n\n/**\n * Delete a registry file synchronously on process exit, as a backstop for abrupt\n * termination. Vite installs its own SIGTERM handler that calls `process.exit()`,\n * which can outrun the async server teardown and leave the lock or registry entry\n * behind — a stray dev-server that lingers until the dead-pid prune clears it. The\n * `exit` event only runs synchronous work, hence `unlinkSync`. `ownedByUs` guards\n * the shared lock so a successor that reacquired it isn't wiped. Returns a\n * detacher to call after a clean release.\n */\nfunction unlinkOnProcessExit(filePath: string, ownedByUs: () => boolean): () => void {\n const cleanup = () => {\n if (!ownedByUs()) return\n try {\n unlinkSync(filePath)\n } catch {\n // The file may already have been removed during shutdown.\n }\n }\n exitCleanups.add(cleanup)\n\n if (!exitListenerInstalled) {\n exitListenerInstalled = true\n process.once('exit', runExitCleanups)\n }\n\n return () => exitCleanups.delete(cleanup)\n}\n\ninterface DevServerRegistration {\n /** Remove the registry entry. */\n release: () => void\n /**\n * Rewrite the registry entry with partial updates merged in. Also bumps the\n * file's mtime, which fires `watchRegistry` in any workbench process and\n * triggers a rebroadcast to connected clients.\n */\n update: (patch: Partial<Omit<DevServerManifest, 'pid' | 'startedAt' | 'version'>>) => void\n}\n\n/**\n * Write a manifest file for the current process and return a handle with a\n * `release` function that removes it plus an `update` function for patching\n * fields post-registration. Uses synchronous I/O so the file exists before\n * any signal handler could fire.\n */\nexport function registerDevServer(\n manifest: Omit<DevServerManifest, 'pid' | 'startedAt' | 'version'>,\n): DevServerRegistration {\n const registryDir = getRegistryDir()\n mkdirSync(registryDir, {recursive: true})\n\n let current: DevServerManifest = {\n ...manifest,\n pid: process.pid,\n startedAt: ownStartedAt(),\n version: REGISTRY_VERSION,\n }\n\n const filePath = join(registryDir, `${process.pid}.json`)\n writeFileSync(filePath, JSON.stringify(current, null, 2))\n\n // Guard against late updates from background tasks (e.g. the initial\n // manifest extraction) landing after `release()` has deleted the file —\n // without this, the update would re-create the registry entry and leak.\n let released = false\n\n // The file is pid-named, so it's always ours to remove on exit.\n const detachExitCleanup = unlinkOnProcessExit(filePath, () => !released)\n\n return {\n release() {\n released = true\n detachExitCleanup()\n try {\n unlinkSync(filePath)\n } catch {\n // ENOENT is fine — already cleaned up\n }\n },\n update(patch) {\n if (released) return\n current = {...current, ...patch}\n writeFileSync(filePath, JSON.stringify(current, null, 2))\n },\n }\n}\n\n/**\n * Read all manifest files from the registry, prune stale entries (dead PIDs),\n * and return the live ones.\n */\nexport function getRegisteredServers(): DevServerManifest[] {\n const registryDir = getRegistryDir()\n\n if (!existsSync(registryDir)) {\n return []\n }\n\n const files = readdirSync(registryDir).filter((f) => f.endsWith('.json'))\n const servers: DevServerManifest[] = []\n\n for (const file of files) {\n const filePath = join(registryDir, file)\n let raw: unknown\n try {\n raw = JSON.parse(readFileSync(filePath, 'utf8'))\n } catch {\n continue\n }\n\n const {data, success} = devServerManifestSchema.safeParse(raw)\n if (!success) continue\n\n if (isOurProcess(data.pid, data.startedAt)) {\n servers.push(data)\n } else {\n try {\n unlinkSync(filePath)\n } catch {\n // Ignore — another process may have already cleaned it up\n }\n }\n }\n\n return servers\n}\n\ninterface RegistryWatcher {\n close(): void\n}\n\n/**\n * Watch the registry directory for changes and invoke the callback with the\n * current list of live servers whenever a change is detected.\n *\n * Uses `fs.watch` with a debounce to coalesce rapid file changes (e.g. a\n * server starting and writing its manifest triggers multiple FS events).\n */\nexport function watchRegistry(callback: (servers: DevServerManifest[]) => void): RegistryWatcher {\n const registryDir = getRegistryDir()\n mkdirSync(registryDir, {recursive: true})\n\n // Canonicalize to the real long path so `fs.watch` doesn't abort on Windows\n // short-path dirs. See `canonicalizeWatchDir`.\n const watchDir = canonicalizeWatchDir(registryDir)\n\n let debounceTimer: ReturnType<typeof setTimeout> | undefined\n\n const notify = () => {\n clearTimeout(debounceTimer)\n debounceTimer = setTimeout(() => {\n callback(getRegisteredServers())\n }, 50)\n }\n\n const watcher = watch(watchDir, notify)\n\n return {\n close() {\n clearTimeout(debounceTimer)\n watcher.close()\n },\n }\n}\n\n// The workbench singleton lock — \"one workbench per machine\". Lives in the same\n// registry dir and shares the liveness/prune model: a stale lock left by a\n// crashed process is pruned on read so the next acquire isn't blocked forever.\n\nconst workbenchLockSchema = z.object({\n host: z.string(),\n pid: z.number(),\n port: z.number(),\n startedAt: z.string(),\n version: z.literal(REGISTRY_VERSION),\n})\n\n/**\n * Read the workbench lock file and return its contents if the holding\n * process is still alive. Prunes stale locks from crashed processes.\n */\nexport function readWorkbenchLock(): z.infer<typeof workbenchLockSchema> | undefined {\n const lockPath = join(getRegistryDir(), 'workbench.lock')\n\n let contents: string\n try {\n contents = readFileSync(lockPath, 'utf8')\n } catch {\n // File doesn't exist — nothing to prune, nothing to return\n return undefined\n }\n\n // Past this point the file exists. Anything that isn't a live, valid lock\n // (unparsable JSON, schema mismatch, dead/reused PID) is stale and must be\n // pruned — otherwise the next `acquireWorkbenchLock` call is blocked by\n // EEXIST forever and `sanity dev` silently no-ops the workbench server.\n const data = parseLockContents(contents)\n devDebug('Read workbench lock: %o', data)\n if (data && isOurProcess(data.pid, data.startedAt)) {\n devDebug('Workbench process is alive at pid %d on port %d', data.pid, data.port)\n return data\n }\n\n pruneWorkbenchLock(lockPath)\n return undefined\n}\n\nfunction parseLockContents(contents: string): z.infer<typeof workbenchLockSchema> | undefined {\n try {\n const {data, success} = workbenchLockSchema.safeParse(JSON.parse(contents))\n return success ? data : undefined\n } catch {\n return undefined\n }\n}\n\nfunction pruneWorkbenchLock(lockPath: string): void {\n try {\n devDebug('Removing stale workbench lock')\n unlinkSync(lockPath)\n devDebug('Stale workbench lock removed')\n } catch {\n // Another process may have already cleaned it up\n }\n}\n\ninterface WorkbenchLock {\n /** Release the lock file. */\n release: () => void\n /** Update the lock with the actual port after the server starts listening. */\n updatePort: (port: number) => void\n}\n\n/**\n * Attempt to acquire an exclusive lock for the workbench process.\n * Uses `O_EXCL` (the `wx` flag) which is atomic at the OS level — only one\n * process can create the file.\n *\n * The lock stores `{pid, host, port}` so other processes can find the\n * running workbench. Call `updatePort` after the Vite server starts to\n * write the actual port (Vite may pick a different one).\n *\n * @returns A {@link WorkbenchLock} if acquired, or `undefined` if another\n * live process already holds it.\n */\nexport function acquireWorkbenchLock(\n info: {host: string; port: number},\n retries = 1,\n): WorkbenchLock | undefined {\n const registryDir = getRegistryDir()\n mkdirSync(registryDir, {recursive: true})\n\n const lockPath = join(registryDir, 'workbench.lock')\n const startedAt = ownStartedAt()\n const lockData = {\n host: info.host,\n pid: process.pid,\n port: info.port,\n startedAt,\n version: REGISTRY_VERSION,\n }\n\n devDebug('Acquiring workbench lock at %s', lockPath)\n\n try {\n writeFileSync(lockPath, JSON.stringify(lockData), {flag: 'wx'})\n devDebug('Workbench lock acquired')\n\n let released = false\n // Only wipe the lock on exit if it's still ours — a successor that reacquired\n // it after our own release must not be clobbered.\n const detachExitCleanup = unlinkOnProcessExit(lockPath, () => {\n if (released) return false\n try {\n const disk = parseLockContents(readFileSync(lockPath, 'utf8'))\n return disk?.pid === process.pid && disk.startedAt === startedAt\n } catch {\n return false\n }\n })\n\n return {\n release() {\n released = true\n detachExitCleanup()\n try {\n unlinkSync(lockPath)\n } catch {\n // Already cleaned up\n }\n },\n updatePort(port: number) {\n writeFileSync(lockPath, JSON.stringify({...lockData, port}))\n },\n }\n } catch (err: unknown) {\n devDebug(\n 'Failed to acquire workbench lock: %s',\n err instanceof Error ? err.message : String(err),\n )\n if (!isNodeError(err) || err.code !== 'EEXIST') return undefined\n\n // Lock exists — check if the holder is still alive\n const existing = readWorkbenchLock()\n if (existing) return undefined\n\n // Stale lock was pruned by readWorkbenchLock — retry (with guard against infinite recursion)\n if (retries <= 0) return undefined\n return acquireWorkbenchLock(info, retries - 1)\n }\n}\n\nfunction isNodeError(err: unknown): err is NodeJS.ErrnoException {\n return err instanceof Error && 'code' in err\n}\n"],"names":["existsSync","mkdirSync","readdirSync","readFileSync","unlinkSync","watch","writeFileSync","join","coreAppManifestSchema","getSanityDataDir","studioManifestSchema","subdebug","z","AppInterfaceMetadataSchema","TileInterfaceMetadataSchema","canonicalizeWatchDir","getProcessStartTime","isOurProcess","devDebug","REGISTRY_VERSION","ownStartedAt","process","pid","Date","toISOString","interfaceBaseFields","id","string","moduleId","name","src","title","version","optional","devServerInterfaceSchema","discriminatedUnion","object","metadata","nullable","type","literal","null","devServerManifestSchema","configs","array","appType","fields","public","boolean","moduleName","host","interfaces","manifest","union","manifestUpdatedAt","number","port","projectId","startedAt","enum","workDir","getRegistryDir","exitCleanups","Set","exitListenerInstalled","runExitCleanups","cleanup","runRegistryExitCleanupForTesting","unlinkOnProcessExit","filePath","ownedByUs","add","once","delete","registerDevServer","registryDir","recursive","current","JSON","stringify","released","detachExitCleanup","release","update","patch","getRegisteredServers","files","filter","f","endsWith","servers","file","raw","parse","data","success","safeParse","push","watchRegistry","callback","watchDir","debounceTimer","notify","clearTimeout","setTimeout","watcher","close","workbenchLockSchema","readWorkbenchLock","lockPath","contents","undefined","parseLockContents","pruneWorkbenchLock","acquireWorkbenchLock","info","retries","lockData","flag","disk","updatePort","err","Error","message","String","isNodeError","code","existing"],"mappings":"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"}
package/dist/contract.js CHANGED
@@ -16,8 +16,22 @@ import { z } from 'zod/mini';
16
16
  panel: [
17
17
  'title',
18
18
  'panel'
19
+ ],
20
+ tile: [
21
+ 'tile'
19
22
  ]
20
23
  };
24
+ /**
25
+ * A tile's footprint family — the shape it occupies on the dashboard. Modelled
26
+ * on iOS WidgetKit families, not a linear scale: `banner` is full-width and
27
+ * shallow, which a `small`→`large` magnitude can't express. The host maps a
28
+ * family to a layout slot; the component reads it to render per footprint.
29
+ * @public
30
+ */ export const TileSizeSchema = z.enum([
31
+ 'small',
32
+ 'large',
33
+ 'banner'
34
+ ]);
21
35
  /**
22
36
  * The `app` interface's dock-placement metadata. Interface metadata is
23
37
  * discriminated on `type`; `app` is the only type with a shape so far.
@@ -26,6 +40,16 @@ import { z } from 'zod/mini';
26
40
  group: z.optional(z.string()),
27
41
  priority: z.optional(z.number())
28
42
  });
43
+ /**
44
+ * A tile's interface metadata: its footprint `size` and an optional `priority`
45
+ * the dashboard sorts on, ascending. Both are authored as top-level view fields
46
+ * (see {@link InterfaceDeclarationSchema}) but stored on the record as metadata.
47
+ * Mirrors `app`'s dock metadata; tile is the first view type to carry any.
48
+ * @internal
49
+ */ export const TileInterfaceMetadataSchema = z.object({
50
+ priority: z.optional(z.number()),
51
+ size: TileSizeSchema
52
+ });
29
53
  /**
30
54
  * The contract version each interface type advertises, so the host can check it
31
55
  * renders/runs what it expects.
@@ -34,6 +58,7 @@ import { z } from 'zod/mini';
34
58
  app: undefined,
35
59
  asset_source: VIEW_CONTRACT_VERSION,
36
60
  panel: VIEW_CONTRACT_VERSION,
61
+ tile: VIEW_CONTRACT_VERSION,
37
62
  worker: SERVICE_CONTRACT_VERSION
38
63
  };
39
64
  /** @internal */ export function interfaceContractVersion(type) {
@@ -52,6 +77,7 @@ import { z } from 'zod/mini';
52
77
  }
53
78
  case 'asset_source':
54
79
  case 'panel':
80
+ case 'tile':
55
81
  {
56
82
  return `views/${name}`;
57
83
  }
@@ -89,9 +115,16 @@ const AssetSourceViewSchema = z.object({
89
115
  type: z.literal('asset_source'),
90
116
  ...interfaceDeclarationFields('View')
91
117
  });
118
+ const TileViewSchema = z.object({
119
+ type: z.literal('tile'),
120
+ ...interfaceDeclarationFields('View'),
121
+ /** Sort position within its layout track, ascending. Optional. */ priority: z.optional(z.number()),
122
+ /** Footprint family the dashboard lays the tile out by. */ size: TileSizeSchema
123
+ });
92
124
  /** @internal */ export const InterfaceDeclarationSchema = z.discriminatedUnion('type', [
93
125
  PanelViewSchema,
94
- AssetSourceViewSchema
126
+ AssetSourceViewSchema,
127
+ TileViewSchema
95
128
  ]);
96
129
  const WorkerServiceSchema = z.object({
97
130
  type: z.literal('worker'),
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/contract.ts"],"sourcesContent":["import {z} from 'zod/mini'\n\n// Shared module-federation extension contract: interface (view/service) and\n// config declaration schemas, plus the versions the build stamps.\n// `zod/mini` keeps the bundle small.\n\n/** @internal */\nexport const VIEW_CONTRACT_VERSION = 1\n\n/** @internal */\nexport const SERVICE_CONTRACT_VERSION = 1\n\n/** @internal */\nexport const MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION = 1\n\n/**\n * A view component. The return is opaque so the runtime helpers carry no React\n * dependency — the generated artifact renders it with the app's own React.\n * @public\n */\nexport type ViewComponent<TProps> = (props: TProps) => unknown\n\n/** @public */\nexport interface ViewComponentBaseProps<TView> {\n view: TView\n}\n\n/**\n * Component slots each interface type exposes, in render order. Source of truth\n * for {@link InterfaceType} and the build; add a type by registering it here.\n * @internal\n */\nexport const VIEW_COMPONENTS = {\n asset_source: ['asset_source'],\n panel: ['title', 'panel'],\n} as const satisfies Record<string, readonly string[]>\n\n/** @public */\nexport type InterfaceType = keyof typeof VIEW_COMPONENTS\n\n/** @public */\nexport type ServiceType = 'worker'\n\n/**\n * The `app` interface's dock-placement metadata. Interface metadata is\n * discriminated on `type`; `app` is the only type with a shape so far.\n * @internal\n */\nexport const AppInterfaceMetadataSchema = z.object({\n group: z.optional(z.string()),\n priority: z.optional(z.number()),\n})\n\n/** @internal */\nexport type AppInterfaceMetadata = z.infer<typeof AppInterfaceMetadataSchema>\n\n/**\n * The contract version each interface type advertises, so the host can check it\n * renders/runs what it expects.\n * @internal\n */\nconst INTERFACE_CONTRACT_VERSIONS = {\n app: undefined,\n asset_source: VIEW_CONTRACT_VERSION,\n panel: VIEW_CONTRACT_VERSION,\n worker: SERVICE_CONTRACT_VERSION,\n} as const\n\n/** Every interface type an app exposes — an `app` view, a view, or a service. */\nexport type InterfaceKind = keyof typeof INTERFACE_CONTRACT_VERSIONS\n\n/** @internal */\nexport function interfaceContractVersion(type: InterfaceKind): string | undefined {\n const version = INTERFACE_CONTRACT_VERSIONS[type]\n return version === undefined ? undefined : String(version)\n}\n\n/**\n * The module-federation id a build exposes an interface at. Dev stamps the same\n * id a deploy would, so the workbench loads a local interface like a deployed one.\n * @internal\n */\nexport function interfaceModuleId(type: string, name: string): string {\n switch (type) {\n case 'app': {\n return 'App'\n }\n case 'asset_source':\n case 'panel': {\n return `views/${name}`\n }\n case 'worker': {\n return `services/${name}`\n }\n default: {\n throw new Error(`Cannot derive a moduleId for unknown interface type: ${type}`)\n }\n }\n}\n\n// Shared `name` + `src`; `kind` only tailors the validation message.\nfunction extensionDeclarationFields(kind: 'Field' | 'Service' | 'View') {\n const pattern = /^[a-zA-Z0-9_-]+$/\n return {\n name: z.string().check(z.regex(pattern, `${kind} \\`name\\` must match ${pattern}`)),\n src: z.string(),\n }\n}\n\n// Every interface (view, service) shares `name` + `src` + a display `title`,\n// which Brett requires on the record each becomes.\nfunction interfaceDeclarationFields(kind: 'Service' | 'View') {\n return {\n ...extensionDeclarationFields(kind),\n title: z.string(`${kind} \\`title\\` is required`),\n }\n}\n\nconst PanelViewSchema = z.object({\n type: z.literal('panel'),\n ...interfaceDeclarationFields('View'),\n})\n\nconst AssetSourceViewSchema = z.object({\n type: z.literal('asset_source'),\n ...interfaceDeclarationFields('View'),\n})\n\n/** @internal */\nexport const InterfaceDeclarationSchema = z.discriminatedUnion('type', [\n PanelViewSchema,\n AssetSourceViewSchema,\n])\n\nconst WorkerServiceSchema = z.object({\n type: z.literal('worker'),\n ...interfaceDeclarationFields('Service'),\n})\n\n/** @internal */\nexport const ServiceDeclarationSchema = z.discriminatedUnion('type', [WorkerServiceSchema])\n\nconst MediaLibraryFieldSchema = z.object({\n ...extensionDeclarationFields('Field'),\n public: z.optional(z.boolean()),\n title: z.string(),\n})\n\n/**\n * Stamped where the config crosses a boundary so the authoring model doesn't carry a constant discriminator.\n * @internal\n */\nexport const INSTALLATION_CONFIG_TYPE = 'installation_config'\n\n// `appType` is stamped by `unstable_defineMediaLibrary`, never authored.\nconst MediaLibraryConfigSchema = z.object({\n appType: z.literal('media-library'),\n fields: z\n .array(MediaLibraryFieldSchema)\n .check(\n z.refine(\n (fields) => new Set(fields.map((field) => field.name)).size === fields.length,\n 'Field `name` must be unique within a media library',\n ),\n ),\n})\n\n/**\n * An app's optional config, keyed by `appType`; deploys as a versioned snapshot, not an interface.\n * @internal\n */\nexport const ConfigSchema = z.discriminatedUnion('appType', [MediaLibraryConfigSchema])\n"],"names":["z","VIEW_CONTRACT_VERSION","SERVICE_CONTRACT_VERSION","MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION","VIEW_COMPONENTS","asset_source","panel","AppInterfaceMetadataSchema","object","group","optional","string","priority","number","INTERFACE_CONTRACT_VERSIONS","app","undefined","worker","interfaceContractVersion","type","version","String","interfaceModuleId","name","Error","extensionDeclarationFields","kind","pattern","check","regex","src","interfaceDeclarationFields","title","PanelViewSchema","literal","AssetSourceViewSchema","InterfaceDeclarationSchema","discriminatedUnion","WorkerServiceSchema","ServiceDeclarationSchema","MediaLibraryFieldSchema","public","boolean","INSTALLATION_CONFIG_TYPE","MediaLibraryConfigSchema","appType","fields","array","refine","Set","map","field","size","length","ConfigSchema"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/contract.ts"],"sourcesContent":["import {z} from 'zod/mini'\n\n// Shared module-federation extension contract: interface (view/service) and\n// config declaration schemas, plus the versions the build stamps.\n// `zod/mini` keeps the bundle small.\n\n/** @internal */\nexport const VIEW_CONTRACT_VERSION = 1\n\n/** @internal */\nexport const SERVICE_CONTRACT_VERSION = 1\n\n/** @internal */\nexport const MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION = 1\n\n/**\n * A view component. The return is opaque so the runtime helpers carry no React\n * dependency — the generated artifact renders it with the app's own React.\n * @public\n */\nexport type ViewComponent<TProps> = (props: TProps) => unknown\n\n/** @public */\nexport interface ViewComponentBaseProps<TView> {\n view: TView\n}\n\n/**\n * Component slots each interface type exposes, in render order. Source of truth\n * for {@link InterfaceType} and the build; add a type by registering it here.\n * @internal\n */\nexport const VIEW_COMPONENTS = {\n asset_source: ['asset_source'],\n panel: ['title', 'panel'],\n tile: ['tile'],\n} as const satisfies Record<string, readonly string[]>\n\n/** @public */\nexport type InterfaceType = keyof typeof VIEW_COMPONENTS\n\n/**\n * A tile's footprint family — the shape it occupies on the dashboard. Modelled\n * on iOS WidgetKit families, not a linear scale: `banner` is full-width and\n * shallow, which a `small`→`large` magnitude can't express. The host maps a\n * family to a layout slot; the component reads it to render per footprint.\n * @public\n */\nexport const TileSizeSchema = z.enum(['small', 'large', 'banner'])\n\n/** @public */\nexport type TileSize = z.infer<typeof TileSizeSchema>\n\n/** @public */\nexport type ServiceType = 'worker'\n\n/**\n * The `app` interface's dock-placement metadata. Interface metadata is\n * discriminated on `type`; `app` is the only type with a shape so far.\n * @internal\n */\nexport const AppInterfaceMetadataSchema = z.object({\n group: z.optional(z.string()),\n priority: z.optional(z.number()),\n})\n\n/** @internal */\nexport type AppInterfaceMetadata = z.infer<typeof AppInterfaceMetadataSchema>\n\n/**\n * A tile's interface metadata: its footprint `size` and an optional `priority`\n * the dashboard sorts on, ascending. Both are authored as top-level view fields\n * (see {@link InterfaceDeclarationSchema}) but stored on the record as metadata.\n * Mirrors `app`'s dock metadata; tile is the first view type to carry any.\n * @internal\n */\nexport const TileInterfaceMetadataSchema = z.object({\n priority: z.optional(z.number()),\n size: TileSizeSchema,\n})\n\n/** @internal */\nexport type TileInterfaceMetadata = z.infer<typeof TileInterfaceMetadataSchema>\n\n/**\n * The contract version each interface type advertises, so the host can check it\n * renders/runs what it expects.\n * @internal\n */\nconst INTERFACE_CONTRACT_VERSIONS = {\n app: undefined,\n asset_source: VIEW_CONTRACT_VERSION,\n panel: VIEW_CONTRACT_VERSION,\n tile: VIEW_CONTRACT_VERSION,\n worker: SERVICE_CONTRACT_VERSION,\n} as const\n\n/** Every interface type an app exposes — an `app` view, a view, or a service. */\nexport type InterfaceKind = keyof typeof INTERFACE_CONTRACT_VERSIONS\n\n/** @internal */\nexport function interfaceContractVersion(type: InterfaceKind): string | undefined {\n const version = INTERFACE_CONTRACT_VERSIONS[type]\n return version === undefined ? undefined : String(version)\n}\n\n/**\n * The module-federation id a build exposes an interface at. Dev stamps the same\n * id a deploy would, so the workbench loads a local interface like a deployed one.\n * @internal\n */\nexport function interfaceModuleId(type: string, name: string): string {\n switch (type) {\n case 'app': {\n return 'App'\n }\n case 'asset_source':\n case 'panel':\n case 'tile': {\n return `views/${name}`\n }\n case 'worker': {\n return `services/${name}`\n }\n default: {\n throw new Error(`Cannot derive a moduleId for unknown interface type: ${type}`)\n }\n }\n}\n\n// Shared `name` + `src`; `kind` only tailors the validation message.\nfunction extensionDeclarationFields(kind: 'Field' | 'Service' | 'View') {\n const pattern = /^[a-zA-Z0-9_-]+$/\n return {\n name: z.string().check(z.regex(pattern, `${kind} \\`name\\` must match ${pattern}`)),\n src: z.string(),\n }\n}\n\n// Every interface (view, service) shares `name` + `src` + a display `title`,\n// which Brett requires on the record each becomes.\nfunction interfaceDeclarationFields(kind: 'Service' | 'View') {\n return {\n ...extensionDeclarationFields(kind),\n title: z.string(`${kind} \\`title\\` is required`),\n }\n}\n\nconst PanelViewSchema = z.object({\n type: z.literal('panel'),\n ...interfaceDeclarationFields('View'),\n})\n\nconst AssetSourceViewSchema = z.object({\n type: z.literal('asset_source'),\n ...interfaceDeclarationFields('View'),\n})\n\nconst TileViewSchema = z.object({\n type: z.literal('tile'),\n ...interfaceDeclarationFields('View'),\n /** Sort position within its layout track, ascending. Optional. */\n priority: z.optional(z.number()),\n /** Footprint family the dashboard lays the tile out by. */\n size: TileSizeSchema,\n})\n\n/** @internal */\nexport const InterfaceDeclarationSchema = z.discriminatedUnion('type', [\n PanelViewSchema,\n AssetSourceViewSchema,\n TileViewSchema,\n])\n\nconst WorkerServiceSchema = z.object({\n type: z.literal('worker'),\n ...interfaceDeclarationFields('Service'),\n})\n\n/** @internal */\nexport const ServiceDeclarationSchema = z.discriminatedUnion('type', [WorkerServiceSchema])\n\nconst MediaLibraryFieldSchema = z.object({\n ...extensionDeclarationFields('Field'),\n public: z.optional(z.boolean()),\n title: z.string(),\n})\n\n/**\n * Stamped where the config crosses a boundary so the authoring model doesn't carry a constant discriminator.\n * @internal\n */\nexport const INSTALLATION_CONFIG_TYPE = 'installation_config'\n\n// `appType` is stamped by `unstable_defineMediaLibrary`, never authored.\nconst MediaLibraryConfigSchema = z.object({\n appType: z.literal('media-library'),\n fields: z\n .array(MediaLibraryFieldSchema)\n .check(\n z.refine(\n (fields) => new Set(fields.map((field) => field.name)).size === fields.length,\n 'Field `name` must be unique within a media library',\n ),\n ),\n})\n\n/**\n * An app's optional config, keyed by `appType`; deploys as a versioned snapshot, not an interface.\n * @internal\n */\nexport const ConfigSchema = z.discriminatedUnion('appType', [MediaLibraryConfigSchema])\n"],"names":["z","VIEW_CONTRACT_VERSION","SERVICE_CONTRACT_VERSION","MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION","VIEW_COMPONENTS","asset_source","panel","tile","TileSizeSchema","enum","AppInterfaceMetadataSchema","object","group","optional","string","priority","number","TileInterfaceMetadataSchema","size","INTERFACE_CONTRACT_VERSIONS","app","undefined","worker","interfaceContractVersion","type","version","String","interfaceModuleId","name","Error","extensionDeclarationFields","kind","pattern","check","regex","src","interfaceDeclarationFields","title","PanelViewSchema","literal","AssetSourceViewSchema","TileViewSchema","InterfaceDeclarationSchema","discriminatedUnion","WorkerServiceSchema","ServiceDeclarationSchema","MediaLibraryFieldSchema","public","boolean","INSTALLATION_CONFIG_TYPE","MediaLibraryConfigSchema","appType","fields","array","refine","Set","map","field","length","ConfigSchema"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/defineApp.ts"],"sourcesContent":["import {z} from 'zod/mini'\n\nimport {APP_SLUG_PATTERN} from './appSlug.js'\nimport {ConfigSchema, InterfaceDeclarationSchema, ServiceDeclarationSchema} from './contract.js'\n\n/**\n * Dashboard visibility values. Mirrors `APP_VISIBILITIES` in `@sanity/cli-core`\n * (which can't be imported here — pulling the barrel into this lean module bloats\n * the config-load path). Kept in sync by a type test in `defineApp.test.ts`.\n */\nconst APP_VISIBILITIES = ['default', 'unlisted', 'disabled'] as const\n\n/**\n * Internal application discriminator. Sanity-owned singleton apps only;\n * validated by the schema but excluded from the public `DefineAppInput` type.\n */\nconst ApplicationType = z.enum(['coreApp', 'studio', 'canvas', 'dashboard', 'media-library'])\n\n/** Dock groups an app can place itself into. */\nconst DockGroupSchema = z.enum(['dock.system', 'dock.applications', 'dock.user'])\n\n/**\n * Dock group identifier. The API does not block a user app from declaring a\n * reserved group (e.g. `dock.system`); priority conventions keep Sanity-owned\n * apps ahead.\n * @public\n */\nexport type DockGroup = z.output<typeof DockGroupSchema>\n\n/**\n * Runtime-validation schema for `unstable_defineApp`.\n * @internal\n */\nexport const DefineAppInputSchema = z\n .object({\n /**\n * Internal — Sanity-owned singleton apps only. Validated here but excluded\n * from the public `DefineAppInput` type.\n * @internal\n */\n applicationType: z.optional(ApplicationType),\n /**\n * Deployed as a versioned snapshot on the app's org installation, not the\n * application service. Singletons only. Internal, so excluded from the public\n * `DefineAppInput` and set via `@ts-expect-error` like `applicationType`.\n * @internal\n */\n config: z.optional(ConfigSchema),\n /**\n * App entrypoint module. Defaults to `./src/App.tsx` when omitted. The build\n * derives the app's navigable `app` view from it. SDK apps only — setting it\n * on a studio is rejected (studio app views are not yet implemented).\n */\n entry: z.optional(z.string(\"must be a path to the app's entry file\")),\n /** Dock group to render in. Defaults to `dock.applications` when omitted. */\n group: z.optional(DockGroupSchema),\n /** Optional icon override (path to an SVG). Wins over manifest/studio icon. */\n icon: z.optional(z.string()),\n /**\n * Sanity-owned app deployed once, installed per org; excluded from the public `DefineAppInput`.\n * @internal\n */\n isSingleton: z.optional(z.boolean()),\n /** Organization that owns the app — the workbench runs and deploys against it. */\n organizationId: z.string(\n \"App `organizationId` is required — pass the owning organization's ID to `unstable_defineApp`\",\n ),\n /** Sort position within the group, ascending. Defaults to `100` when omitted. */\n priority: z.optional(z.number()),\n /** Background services the app runs (e.g. a `worker` emitting dock badges). */\n services: z.optional(\n z\n .array(ServiceDeclarationSchema, 'must be an array of services')\n .check(\n z.refine(\n (services) => new Set(services.map((service) => service.name)).size === services.length,\n 'Service `name` must be unique within an app',\n ),\n ),\n ),\n slug: z\n .string('App `slug` is required — the hostname the application is created at on deploy')\n .check(\n z.regex(\n APP_SLUG_PATTERN,\n 'App `slug` must be lowercase alphanumerics and hyphens, starting with a letter and ending with an alphanumeric',\n ),\n ),\n /** User-facing app title. Wins over studio.config.ts title on merge. */\n title: z.string(),\n /** Views the app exposes (e.g. dock panels). */\n views: z.optional(\n z\n .array(InterfaceDeclarationSchema, 'must be an array of panels')\n .check(\n z.refine(\n (views) => new Set(views.map((view) => view.name)).size === views.length,\n 'View `name` must be unique within an app',\n ),\n ),\n ),\n /** Dashboard visibility of the app. Defaults to `default` when omitted. */\n visibility: z.optional(z.enum(APP_VISIBILITIES)),\n })\n .check(\n // Studio app views are not implemented yet. A studio that declares `entry`\n // (the SDK app-view entrypoint) is rejected here rather than silently\n // generating one; studios keep navigating via their existing render path.\n z.refine((input) => !(input.applicationType === 'studio' && input.entry !== undefined), {\n error: 'App views for studios are not implemented yet',\n path: ['entry'],\n }),\n )\n .check(\n // An config belongs to a Sanity-owned singleton (the Media\n // Library). A non-singleton declaring one is rejected — see\n // {@link readConfig} for the runtime guard.\n z.refine((input) => !(input.config && !input.isSingleton), {\n error: '`config` is only supported for singleton apps',\n path: ['config'],\n }),\n )\n .check(\n // A navigable app view (`entry`) and dock panels are the mutually-exclusive\n // navigable kinds. An `asset_source` view is a separate kind — a picker\n // brokered to other apps — so it may sit alongside either.\n z.refine(\n (input) =>\n !(\n input.entry !== undefined &&\n (input.views?.some((view) => view.type === 'panel') ?? false)\n ),\n 'An app cannot expose both an app view (`entry`) and panel views. Declare one or the other.',\n ),\n )\n .check(\n z.refine(\n (input) => (input.views?.filter((view) => view.type === 'panel').length ?? 0) <= 1,\n 'An app can expose at most one panel view.',\n ),\n )\n\n/** The `asset_source` variant of an app's `views`. @public */\nexport type AssetSourceView = Extract<\n NonNullable<z.output<typeof DefineAppInputSchema>['views']>[number],\n {type: 'asset_source'}\n>\n\n/**\n * User-facing input for `unstable_defineApp`. Excludes the internal\n * `applicationType`, `isSingleton`, and `config` — validated by the schema but\n * not part of the public surface (Sanity-owned apps set them via\n * `@ts-expect-error`). A union: an app declares an app `entry` (navigable) or\n * panel `views`, never both — but an `asset_source` view is a separate kind and\n * may accompany either.\n * @public\n */\nexport type DefineAppInput = Omit<\n z.output<typeof DefineAppInputSchema>,\n 'applicationType' | 'config' | 'entry' | 'isSingleton' | 'views'\n> &\n (\n | {entry?: never; views?: NonNullable<z.output<typeof DefineAppInputSchema>['views']>}\n | {entry?: string; views?: AssetSourceView[]}\n )\n\n/**\n * Nominal brand the CLI discriminates on to enable the workbench build/deploy\n * codepath. Registered via `Symbol.for` so the marker survives module-realm\n * boundaries — `@sanity/cli-core` re-derives the same global symbol with\n * `Symbol.for` rather than importing it, so it stays internal to this module.\n */\nconst WORKBENCH_APP: unique symbol = Symbol.for('sanity.workbench.defineApp')\n\n/**\n * The branded result of `unstable_defineApp`. Carries the same fields as the\n * input plus the internal brand — users only ever see `DefineAppInput`.\n * @public\n */\nexport type DefineAppResult = DefineAppInput & {readonly [WORKBENCH_APP]: true}\n\n/**\n * A branded app as the CLI reads it — the full schema shape, including the\n * internal fields `DefineAppInput` omits. Schema-derived so the narrowing\n * can't drift from what the schema validates.\n * @public\n */\nexport type WorkbenchApp = DefineAppResult & z.output<typeof DefineAppInputSchema>\n\n/**\n * Whether `app` is a branded `unstable_defineApp(...)` result — the sole\n * workbench opt-in.\n * @public\n */\nexport function isWorkbenchApp(app: unknown): app is WorkbenchApp {\n return typeof app === 'object' && app !== null && WORKBENCH_APP in app\n}\n\n/**\n * The app's config, or `undefined` when it declares none. Throws\n * when a non-singleton declares one — configs belong to Sanity-owned singletons,\n * so build/dev/deploy all read it through here to reject the combination\n * consistently.\n * @internal\n */\nexport function readConfig(app: WorkbenchApp): WorkbenchApp['config'] | undefined {\n if (app.config && !app.isSingleton) {\n throw new Error('`config` is only supported for singleton apps')\n }\n return app.config\n}\n\n/**\n * Declare a Sanity Workbench application. Identity at runtime — returns the same\n * object reference, tagged with the workbench brand. Field validation (the\n * `slug` pattern etc.) runs at build time in the CLI via `DefineAppInputSchema`;\n * this helper stays a thin, pure identity wrapper.\n * @public\n */\nexport function unstable_defineApp(input: DefineAppInput): DefineAppResult {\n return Object.defineProperty(input, WORKBENCH_APP, {\n configurable: false,\n enumerable: false,\n value: true,\n writable: false,\n }) as DefineAppResult\n}\n\n/**\n * One custom field a media library exposes. `src` default-exports a `defineField(...)` schema type.\n * @public\n */\nexport interface MediaLibraryField {\n /** Unique within the media library. */\n name: string\n src: string\n title: string\n\n /** Readable outside the owning organization. */\n public?: boolean\n}\n\n/**\n * Sanity-owned singleton, so authors don't name or title the app — only `organizationId` is required.\n * @public\n */\nexport interface DefineMediaLibraryInput {\n /** Organization that owns the media library — the CLI runs and deploys against it. */\n organizationId: string\n\n fields?: MediaLibraryField[]\n}\n\n/**\n * Declare the Sanity Media Library as a workbench app — a singleton whose `fields` become its config.\n * @public\n */\nexport function unstable_defineMediaLibrary(input: DefineMediaLibraryInput): DefineAppResult {\n return unstable_defineApp({\n // @ts-expect-error -- `applicationType`/`isSingleton`/`config` are internal, excluded from `DefineAppInput`; Sanity-owned apps set them\n applicationType: 'media-library',\n config: input.fields?.length ? {appType: 'media-library', fields: input.fields} : undefined,\n isSingleton: true,\n organizationId: input.organizationId,\n slug: 'media-library',\n title: 'Media Library',\n })\n}\n"],"names":["z","APP_SLUG_PATTERN","ConfigSchema","InterfaceDeclarationSchema","ServiceDeclarationSchema","APP_VISIBILITIES","ApplicationType","enum","DockGroupSchema","DefineAppInputSchema","object","applicationType","optional","config","entry","string","group","icon","isSingleton","boolean","organizationId","priority","number","services","array","check","refine","Set","map","service","name","size","length","slug","regex","title","views","view","visibility","input","undefined","error","path","some","type","filter","WORKBENCH_APP","Symbol","for","isWorkbenchApp","app","readConfig","Error","unstable_defineApp","Object","defineProperty","configurable","enumerable","value","writable","unstable_defineMediaLibrary","fields","appType"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/defineApp.ts"],"sourcesContent":["import {z} from 'zod/mini'\n\nimport {APP_SLUG_PATTERN} from './appSlug.js'\nimport {ConfigSchema, InterfaceDeclarationSchema, ServiceDeclarationSchema} from './contract.js'\n\n/**\n * Dashboard visibility values. Mirrors `APP_VISIBILITIES` in `@sanity/cli-core`\n * (which can't be imported here — pulling the barrel into this lean module bloats\n * the config-load path). Kept in sync by a type test in `defineApp.test.ts`.\n */\nconst APP_VISIBILITIES = ['default', 'unlisted', 'disabled'] as const\n\n/**\n * Internal application discriminator. Sanity-owned singleton apps only;\n * validated by the schema but excluded from the public `DefineAppInput` type.\n */\nconst ApplicationType = z.enum(['coreApp', 'studio', 'canvas', 'dashboard', 'media-library'])\n\n/** Dock groups an app can place itself into. */\nconst DockGroupSchema = z.enum(['dock.system', 'dock.applications', 'dock.user'])\n\n/**\n * Dock group identifier. The API does not block a user app from declaring a\n * reserved group (e.g. `dock.system`); priority conventions keep Sanity-owned\n * apps ahead.\n * @public\n */\nexport type DockGroup = z.output<typeof DockGroupSchema>\n\n/**\n * Runtime-validation schema for `unstable_defineApp`.\n * @internal\n */\nexport const DefineAppInputSchema = z\n .object({\n /**\n * Internal — Sanity-owned singleton apps only. Validated here but excluded\n * from the public `DefineAppInput` type.\n * @internal\n */\n applicationType: z.optional(ApplicationType),\n /**\n * Deployed as a versioned snapshot on the app's org installation, not the\n * application service. Singletons only. Internal, so excluded from the public\n * `DefineAppInput` and set via `@ts-expect-error` like `applicationType`.\n * @internal\n */\n config: z.optional(ConfigSchema),\n /**\n * App entrypoint module. Defaults to `./src/App.tsx` when omitted. The build\n * derives the app's navigable `app` view from it. SDK apps only — setting it\n * on a studio is rejected (studio app views are not yet implemented).\n */\n entry: z.optional(z.string(\"must be a path to the app's entry file\")),\n /** Dock group to render in. Defaults to `dock.applications` when omitted. */\n group: z.optional(DockGroupSchema),\n /** Optional icon override (path to an SVG). Wins over manifest/studio icon. */\n icon: z.optional(z.string()),\n /**\n * Sanity-owned app deployed once, installed per org; excluded from the public `DefineAppInput`.\n * @internal\n */\n isSingleton: z.optional(z.boolean()),\n /** Organization that owns the app — the workbench runs and deploys against it. */\n organizationId: z.string(\n \"App `organizationId` is required — pass the owning organization's ID to `unstable_defineApp`\",\n ),\n /** Sort position within the group, ascending. Defaults to `100` when omitted. */\n priority: z.optional(z.number()),\n /** Background services the app runs (e.g. a `worker` emitting dock badges). */\n services: z.optional(\n z\n .array(ServiceDeclarationSchema, 'must be an array of services')\n .check(\n z.refine(\n (services) => new Set(services.map((service) => service.name)).size === services.length,\n 'Service `name` must be unique within an app',\n ),\n ),\n ),\n slug: z\n .string('App `slug` is required — the hostname the application is created at on deploy')\n .check(\n z.regex(\n APP_SLUG_PATTERN,\n 'App `slug` must be lowercase alphanumerics and hyphens, starting with a letter and ending with an alphanumeric',\n ),\n ),\n /** User-facing app title. Wins over studio.config.ts title on merge. */\n title: z.string(),\n /** Views the app exposes (e.g. dock panels). */\n views: z.optional(\n z\n .array(InterfaceDeclarationSchema, 'must be an array of panels')\n .check(\n z.refine(\n (views) => new Set(views.map((view) => view.name)).size === views.length,\n 'View `name` must be unique within an app',\n ),\n ),\n ),\n /** Dashboard visibility of the app. Defaults to `default` when omitted. */\n visibility: z.optional(z.enum(APP_VISIBILITIES)),\n })\n .check(\n // Studio app views are not implemented yet. A studio that declares `entry`\n // (the SDK app-view entrypoint) is rejected here rather than silently\n // generating one; studios keep navigating via their existing render path.\n z.refine((input) => !(input.applicationType === 'studio' && input.entry !== undefined), {\n error: 'App views for studios are not implemented yet',\n path: ['entry'],\n }),\n )\n .check(\n // An config belongs to a Sanity-owned singleton (the Media\n // Library). A non-singleton declaring one is rejected — see\n // {@link readConfig} for the runtime guard.\n z.refine((input) => !(input.config && !input.isSingleton), {\n error: '`config` is only supported for singleton apps',\n path: ['config'],\n }),\n )\n .check(\n // A navigable app view (`entry`) and dock panels are the mutually-exclusive\n // navigable kinds. An `asset_source` view is a separate kind — a picker\n // brokered to other apps — so it may sit alongside either.\n z.refine(\n (input) =>\n !(\n input.entry !== undefined &&\n (input.views?.some((view) => view.type === 'panel') ?? false)\n ),\n 'An app cannot expose both an app view (`entry`) and panel views. Declare one or the other.',\n ),\n )\n .check(\n z.refine(\n (input) => (input.views?.filter((view) => view.type === 'panel').length ?? 0) <= 1,\n 'An app can expose at most one panel view.',\n ),\n )\n\n/** The `asset_source` variant of an app's `views`. @public */\nexport type AssetSourceView = Extract<\n NonNullable<z.output<typeof DefineAppInputSchema>['views']>[number],\n {type: 'asset_source'}\n>\n\n/** The `tile` variant of an app's `views`. @public */\nexport type TileView = Extract<\n NonNullable<z.output<typeof DefineAppInputSchema>['views']>[number],\n {type: 'tile'}\n>\n\n/**\n * User-facing input for `unstable_defineApp`. Excludes the internal\n * `applicationType`, `isSingleton`, and `config` — validated by the schema but\n * not part of the public surface (Sanity-owned apps set them via\n * `@ts-expect-error`). A union: an app declares an app `entry` (navigable) or\n * panel `views`, never both — but `asset_source` and `tile` views are separate\n * kinds and may accompany either.\n * @public\n */\nexport type DefineAppInput = Omit<\n z.output<typeof DefineAppInputSchema>,\n 'applicationType' | 'config' | 'entry' | 'isSingleton' | 'views'\n> &\n (\n | {entry?: never; views?: NonNullable<z.output<typeof DefineAppInputSchema>['views']>}\n | {entry?: string; views?: (AssetSourceView | TileView)[]}\n )\n\n/**\n * Nominal brand the CLI discriminates on to enable the workbench build/deploy\n * codepath. Registered via `Symbol.for` so the marker survives module-realm\n * boundaries — `@sanity/cli-core` re-derives the same global symbol with\n * `Symbol.for` rather than importing it, so it stays internal to this module.\n */\nconst WORKBENCH_APP: unique symbol = Symbol.for('sanity.workbench.defineApp')\n\n/**\n * The branded result of `unstable_defineApp`. Carries the same fields as the\n * input plus the internal brand — users only ever see `DefineAppInput`.\n * @public\n */\nexport type DefineAppResult = DefineAppInput & {readonly [WORKBENCH_APP]: true}\n\n/**\n * A branded app as the CLI reads it — the full schema shape, including the\n * internal fields `DefineAppInput` omits. Schema-derived so the narrowing\n * can't drift from what the schema validates.\n * @public\n */\nexport type WorkbenchApp = DefineAppResult & z.output<typeof DefineAppInputSchema>\n\n/**\n * Whether `app` is a branded `unstable_defineApp(...)` result — the sole\n * workbench opt-in.\n * @public\n */\nexport function isWorkbenchApp(app: unknown): app is WorkbenchApp {\n return typeof app === 'object' && app !== null && WORKBENCH_APP in app\n}\n\n/**\n * The app's config, or `undefined` when it declares none. Throws\n * when a non-singleton declares one — configs belong to Sanity-owned singletons,\n * so build/dev/deploy all read it through here to reject the combination\n * consistently.\n * @internal\n */\nexport function readConfig(app: WorkbenchApp): WorkbenchApp['config'] | undefined {\n if (app.config && !app.isSingleton) {\n throw new Error('`config` is only supported for singleton apps')\n }\n return app.config\n}\n\n/**\n * Declare a Sanity Workbench application. Identity at runtime — returns the same\n * object reference, tagged with the workbench brand. Field validation (the\n * `slug` pattern etc.) runs at build time in the CLI via `DefineAppInputSchema`;\n * this helper stays a thin, pure identity wrapper.\n * @public\n */\nexport function unstable_defineApp(input: DefineAppInput): DefineAppResult {\n return Object.defineProperty(input, WORKBENCH_APP, {\n configurable: false,\n enumerable: false,\n value: true,\n writable: false,\n }) as DefineAppResult\n}\n\n/**\n * One custom field a media library exposes. `src` default-exports a `defineField(...)` schema type.\n * @public\n */\nexport interface MediaLibraryField {\n /** Unique within the media library. */\n name: string\n src: string\n title: string\n\n /** Readable outside the owning organization. */\n public?: boolean\n}\n\n/**\n * Sanity-owned singleton, so authors don't name or title the app — only `organizationId` is required.\n * @public\n */\nexport interface DefineMediaLibraryInput {\n /** Organization that owns the media library — the CLI runs and deploys against it. */\n organizationId: string\n\n fields?: MediaLibraryField[]\n}\n\n/**\n * Declare the Sanity Media Library as a workbench app — a singleton whose `fields` become its config.\n * @public\n */\nexport function unstable_defineMediaLibrary(input: DefineMediaLibraryInput): DefineAppResult {\n return unstable_defineApp({\n // @ts-expect-error -- `applicationType`/`isSingleton`/`config` are internal, excluded from `DefineAppInput`; Sanity-owned apps set them\n applicationType: 'media-library',\n config: input.fields?.length ? {appType: 'media-library', fields: input.fields} : undefined,\n isSingleton: true,\n organizationId: input.organizationId,\n slug: 'media-library',\n title: 'Media Library',\n })\n}\n"],"names":["z","APP_SLUG_PATTERN","ConfigSchema","InterfaceDeclarationSchema","ServiceDeclarationSchema","APP_VISIBILITIES","ApplicationType","enum","DockGroupSchema","DefineAppInputSchema","object","applicationType","optional","config","entry","string","group","icon","isSingleton","boolean","organizationId","priority","number","services","array","check","refine","Set","map","service","name","size","length","slug","regex","title","views","view","visibility","input","undefined","error","path","some","type","filter","WORKBENCH_APP","Symbol","for","isWorkbenchApp","app","readConfig","Error","unstable_defineApp","Object","defineProperty","configurable","enumerable","value","writable","unstable_defineMediaLibrary","fields","appType"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/defineView.ts"],"sourcesContent":["import {type AssetSourceComponentProps} from '@sanity/types'\n\nimport {\n type InterfaceType,\n VIEW_CONTRACT_VERSION,\n type ViewComponent,\n type ViewComponentBaseProps,\n} from './contract.js'\n\n/**\n * Props a panel component receives: its interface record, minus the\n * service-assigned `id`/`deployment_id` a local dev server can't provide. Mirrors\n * the `panel` record the workbench host renders from (the wire format owned by\n * `@sanity/workbench`); drift is guarded by the stamped contract version.\n * @public\n */\nexport type PanelViewProps = ViewComponentBaseProps<{\n name: string\n src: string\n title: string\n type: 'panel'\n}>\n\n/**\n * The component slots a `panel` view exposes — each its own module-federation\n * island, typed with the panel props.\n * @public\n */\nexport interface PanelViewComponents {\n panel: ViewComponent<PanelViewProps>\n title: ViewComponent<PanelViewProps>\n}\n\n/**\n * A panel's view-component slot — the module-federation expose for one island.\n * @public\n */\nexport type PanelComponent = keyof PanelViewComponents\n\n/**\n * The component slots an `asset_source` view exposes — a single picker island,\n * typed with the studio asset-source props it renders behind. The props are\n * `@sanity/types`' `AssetSourceComponentProps` directly, so an authored picker\n * receives exactly what a studio `AssetSource.component` does.\n * @public\n */\nexport interface AssetSourceViewComponents {\n asset_source: ViewComponent<AssetSourceComponentProps>\n}\n\n/**\n * An asset source's view-component slot — the module-federation expose for its\n * one island.\n * @public\n */\nexport type AssetSourceComponent = keyof AssetSourceViewComponents\n\n/**\n * The components each interface type exposes, keyed by type.\n * @public\n */\nexport interface ViewComponentsByType {\n asset_source: AssetSourceViewComponents\n panel: PanelViewComponents\n}\n\n/**\n * The result of `unstable_defineView`: the author's component(s), the view type,\n * and the internal contract version the build artifact targets.\n * @public\n */\nexport interface DefinedView<TType extends InterfaceType = InterfaceType> {\n readonly components: ViewComponentsByType[TType]\n readonly type: TType\n /** @internal */\n readonly version: typeof VIEW_CONTRACT_VERSION\n}\n\n/**\n * Define a Sanity Workbench view. The first argument narrows the component shape\n * and the props each component receives — `\"panel\"` yields a `{title, panel}`\n * record whose components are typed with the panel props.\n *\n * Returns the component(s) tagged with their type and the contract version, for\n * the CLI build to generate render artifacts from. Used as the default export of\n * a view's `src` file.\n * @public\n */\nexport function unstable_defineView<TType extends InterfaceType>(\n type: TType,\n components: ViewComponentsByType[TType],\n): DefinedView<TType> {\n return {components, type, version: VIEW_CONTRACT_VERSION}\n}\n"],"names":["VIEW_CONTRACT_VERSION","unstable_defineView","type","components","version"],"mappings":"AAEA,SAEEA,qBAAqB,QAGhB,gBAAe;AAuEtB;;;;;;;;;CASC,GACD,OAAO,SAASC,oBACdC,IAAW,EACXC,UAAuC;IAEvC,OAAO;QAACA;QAAYD;QAAME,SAASJ;IAAqB;AAC1D"}
1
+ {"version":3,"sources":["../src/defineView.ts"],"sourcesContent":["import {type AssetSourceComponentProps} from '@sanity/types'\n\nimport {\n type InterfaceType,\n type TileSize,\n VIEW_CONTRACT_VERSION,\n type ViewComponent,\n type ViewComponentBaseProps,\n} from './contract.js'\n\n/**\n * Props a panel component receives: its interface record, minus the\n * service-assigned `id`/`deployment_id` a local dev server can't provide. Mirrors\n * the `panel` record the workbench host renders from (the wire format owned by\n * `@sanity/workbench`); drift is guarded by the stamped contract version.\n * @public\n */\nexport type PanelViewProps = ViewComponentBaseProps<{\n name: string\n src: string\n title: string\n type: 'panel'\n}>\n\n/**\n * The component slots a `panel` view exposes — each its own module-federation\n * island, typed with the panel props.\n * @public\n */\nexport interface PanelViewComponents {\n panel: ViewComponent<PanelViewProps>\n title: ViewComponent<PanelViewProps>\n}\n\n/**\n * A panel's view-component slot — the module-federation expose for one island.\n * @public\n */\nexport type PanelComponent = keyof PanelViewComponents\n\n/**\n * The component slots an `asset_source` view exposes — a single picker island,\n * typed with the studio asset-source props it renders behind. The props are\n * `@sanity/types`' `AssetSourceComponentProps` directly, so an authored picker\n * receives exactly what a studio `AssetSource.component` does.\n * @public\n */\nexport interface AssetSourceViewComponents {\n asset_source: ViewComponent<AssetSourceComponentProps>\n}\n\n/**\n * An asset source's view-component slot — the module-federation expose for its\n * one island.\n * @public\n */\nexport type AssetSourceComponent = keyof AssetSourceViewComponents\n\n/**\n * Props a tile component receives: its own interface record (name/src/title/\n * type) plus its footprint `size`, so it can render per family. Mirrors the\n * `tile` record the dashboard host renders from; drift is guarded by the stamped\n * contract version. Placement `priority` is host-only metadata, not surfaced here.\n * @public\n */\nexport type TileViewProps = ViewComponentBaseProps<{\n name: string\n size: TileSize\n src: string\n title: string\n type: 'tile'\n}>\n\n/**\n * The component slot a `tile` view exposes — a single island, typed with the\n * tile props.\n * @public\n */\nexport interface TileViewComponents {\n tile: ViewComponent<TileViewProps>\n}\n\n/**\n * A tile's view-component slot — the module-federation expose for its one island.\n * @public\n */\nexport type TileComponent = keyof TileViewComponents\n\n/**\n * The components each interface type exposes, keyed by type.\n * @public\n */\nexport interface ViewComponentsByType {\n asset_source: AssetSourceViewComponents\n panel: PanelViewComponents\n tile: TileViewComponents\n}\n\n/**\n * The result of `unstable_defineView`: the author's component(s), the view type,\n * and the internal contract version the build artifact targets.\n * @public\n */\nexport interface DefinedView<TType extends InterfaceType = InterfaceType> {\n readonly components: ViewComponentsByType[TType]\n readonly type: TType\n /** @internal */\n readonly version: typeof VIEW_CONTRACT_VERSION\n}\n\n/**\n * Define a Sanity Workbench view. The first argument narrows the component shape\n * and the props each component receives — `\"panel\"` yields a `{title, panel}`\n * record whose components are typed with the panel props.\n *\n * Returns the component(s) tagged with their type and the contract version, for\n * the CLI build to generate render artifacts from. Used as the default export of\n * a view's `src` file.\n * @public\n */\nexport function unstable_defineView<TType extends InterfaceType>(\n type: TType,\n components: ViewComponentsByType[TType],\n): DefinedView<TType> {\n return {components, type, version: VIEW_CONTRACT_VERSION}\n}\n"],"names":["VIEW_CONTRACT_VERSION","unstable_defineView","type","components","version"],"mappings":"AAEA,SAGEA,qBAAqB,QAGhB,gBAAe;AAsGtB;;;;;;;;;CASC,GACD,OAAO,SAASC,oBACdC,IAAW,EACXC,UAAuC;IAEvC,OAAO;QAACA;QAAYD;QAAME,SAASJ;IAAqB;AAC1D"}
@@ -24,10 +24,25 @@ import { isWorkbenchApp } from './defineApp.js';
24
24
  version: interfaceContractVersion(type)
25
25
  });
26
26
  return [
27
- ...(app.views ?? []).map((view)=>({
27
+ ...(app.views ?? []).map((view)=>{
28
+ // Tile is the only view type with interface metadata: its footprint `size`
29
+ // (required) and an optional sort `priority`. Both authored top-level.
30
+ if (view.type === 'tile') {
31
+ return {
32
+ ...shared(view.type, view),
33
+ metadata: view.priority === undefined ? {
34
+ size: view.size
35
+ } : {
36
+ priority: view.priority,
37
+ size: view.size
38
+ }
39
+ };
40
+ }
41
+ return {
28
42
  ...shared(view.type, view),
29
43
  metadata: null
30
- })),
44
+ };
45
+ }),
31
46
  ...(app.services ?? []).map((service)=>({
32
47
  ...shared('worker', service),
33
48
  metadata: null
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/deriveInterfaces.ts"],"sourcesContent":["import {type CliConfig} from '@sanity/cli-core'\n\nimport {FEDERATION_FILE_NAME, RUNTIME_DIR} from './actions/build/vite/constants.js'\nimport {\n type AppInterfaceMetadata,\n interfaceContractVersion,\n type InterfaceKind,\n interfaceModuleId,\n} from './contract.js'\nimport {isWorkbenchApp} from './defineApp.js'\n\n/** What a studio exposes as `./App` — the generated entry that renders `sanity.config.ts`. */\nconst GENERATED_ENTRY = `./${RUNTIME_DIR}/${FEDERATION_FILE_NAME}.jsx`\n\ninterface DerivedInterfaceBase {\n /** CLI-minted for a local interface; a deployed one gets its id from Brett. */\n id: string\n moduleId: string\n name: string\n src: string\n title: string\n\n version?: string\n}\n\n/** @internal */\nexport type DerivedInterface =\n | (DerivedInterfaceBase & {metadata: AppInterfaceMetadata | null; type: 'app'})\n | (DerivedInterfaceBase & {metadata: null; type: 'asset_source'})\n | (DerivedInterfaceBase & {metadata: null; type: 'panel'})\n | (DerivedInterfaceBase & {metadata: null; type: 'worker'})\n\n/**\n * `appTitle` titles the app view where a deploy resolved one through `--title`;\n * dev takes it from the config.\n * @internal\n */\nexport function deriveInterfaces(\n app: CliConfig['app'],\n {appTitle, isApp}: {appTitle?: string; isApp: boolean},\n): DerivedInterface[] {\n if (!isWorkbenchApp(app)) return []\n\n // The schema rejects a *declared* studio with an `entry`; `isApp` is resolved\n // from the project, so a detected one only fails here.\n if (!isApp && app.entry !== undefined) {\n throw new Error('App views for studios are not implemented yet')\n }\n const entry = isApp ? app.entry : GENERATED_ENTRY\n\n const shared = <T extends InterfaceKind>(\n type: T,\n declaration: {name: string; src: string; title: string},\n ) => ({\n id: `${app.slug}-${type}-${declaration.name}`,\n moduleId: interfaceModuleId(type, declaration.name),\n name: declaration.name,\n src: declaration.src,\n title: declaration.title,\n type,\n version: interfaceContractVersion(type),\n })\n\n return [\n ...(app.views ?? []).map(\n (view): DerivedInterface => ({...shared(view.type, view), metadata: null}),\n ),\n ...(app.services ?? []).map(\n (service): DerivedInterface => ({...shared('worker', service), metadata: null}),\n ),\n ...(entry === undefined\n ? []\n : [\n {\n ...shared('app', {name: app.slug, src: entry, title: appTitle ?? app.title}),\n metadata: null,\n },\n ]),\n ]\n}\n"],"names":["FEDERATION_FILE_NAME","RUNTIME_DIR","interfaceContractVersion","interfaceModuleId","isWorkbenchApp","GENERATED_ENTRY","deriveInterfaces","app","appTitle","isApp","entry","undefined","Error","shared","type","declaration","id","slug","name","moduleId","src","title","version","views","map","view","metadata","services","service"],"mappings":"AAEA,SAAQA,oBAAoB,EAAEC,WAAW,QAAO,oCAAmC;AACnF,SAEEC,wBAAwB,EAExBC,iBAAiB,QACZ,gBAAe;AACtB,SAAQC,cAAc,QAAO,iBAAgB;AAE7C,4FAA4F,GAC5F,MAAMC,kBAAkB,CAAC,EAAE,EAAEJ,YAAY,CAAC,EAAED,qBAAqB,IAAI,CAAC;AAoBtE;;;;CAIC,GACD,OAAO,SAASM,iBACdC,GAAqB,EACrB,EAACC,QAAQ,EAAEC,KAAK,EAAsC;IAEtD,IAAI,CAACL,eAAeG,MAAM,OAAO,EAAE;IAEnC,8EAA8E;IAC9E,uDAAuD;IACvD,IAAI,CAACE,SAASF,IAAIG,KAAK,KAAKC,WAAW;QACrC,MAAM,IAAIC,MAAM;IAClB;IACA,MAAMF,QAAQD,QAAQF,IAAIG,KAAK,GAAGL;IAElC,MAAMQ,SAAS,CACbC,MACAC,cACI,CAAA;YACJC,IAAI,GAAGT,IAAIU,IAAI,CAAC,CAAC,EAAEH,KAAK,CAAC,EAAEC,YAAYG,IAAI,EAAE;YAC7CC,UAAUhB,kBAAkBW,MAAMC,YAAYG,IAAI;YAClDA,MAAMH,YAAYG,IAAI;YACtBE,KAAKL,YAAYK,GAAG;YACpBC,OAAON,YAAYM,KAAK;YACxBP;YACAQ,SAASpB,yBAAyBY;QACpC,CAAA;IAEA,OAAO;WACF,AAACP,CAAAA,IAAIgB,KAAK,IAAI,EAAE,AAAD,EAAGC,GAAG,CACtB,CAACC,OAA4B,CAAA;gBAAC,GAAGZ,OAAOY,KAAKX,IAAI,EAAEW,KAAK;gBAAEC,UAAU;YAAI,CAAA;WAEvE,AAACnB,CAAAA,IAAIoB,QAAQ,IAAI,EAAE,AAAD,EAAGH,GAAG,CACzB,CAACI,UAA+B,CAAA;gBAAC,GAAGf,OAAO,UAAUe,QAAQ;gBAAEF,UAAU;YAAI,CAAA;WAE3EhB,UAAUC,YACV,EAAE,GACF;YACE;gBACE,GAAGE,OAAO,OAAO;oBAACK,MAAMX,IAAIU,IAAI;oBAAEG,KAAKV;oBAAOW,OAAOb,YAAYD,IAAIc,KAAK;gBAAA,EAAE;gBAC5EK,UAAU;YACZ;SACD;KACN;AACH"}
1
+ {"version":3,"sources":["../src/deriveInterfaces.ts"],"sourcesContent":["import {type CliConfig} from '@sanity/cli-core'\n\nimport {FEDERATION_FILE_NAME, RUNTIME_DIR} from './actions/build/vite/constants.js'\nimport {\n type AppInterfaceMetadata,\n interfaceContractVersion,\n type InterfaceKind,\n interfaceModuleId,\n type TileInterfaceMetadata,\n} from './contract.js'\nimport {isWorkbenchApp} from './defineApp.js'\n\n/** What a studio exposes as `./App` — the generated entry that renders `sanity.config.ts`. */\nconst GENERATED_ENTRY = `./${RUNTIME_DIR}/${FEDERATION_FILE_NAME}.jsx`\n\ninterface DerivedInterfaceBase {\n /** CLI-minted for a local interface; a deployed one gets its id from Brett. */\n id: string\n moduleId: string\n name: string\n src: string\n title: string\n\n version?: string\n}\n\n/** @internal */\nexport type DerivedInterface =\n | (DerivedInterfaceBase & {metadata: AppInterfaceMetadata | null; type: 'app'})\n | (DerivedInterfaceBase & {metadata: null; type: 'asset_source'})\n | (DerivedInterfaceBase & {metadata: null; type: 'panel'})\n | (DerivedInterfaceBase & {metadata: null; type: 'worker'})\n | (DerivedInterfaceBase & {metadata: TileInterfaceMetadata; type: 'tile'})\n\n/**\n * `appTitle` titles the app view where a deploy resolved one through `--title`;\n * dev takes it from the config.\n * @internal\n */\nexport function deriveInterfaces(\n app: CliConfig['app'],\n {appTitle, isApp}: {appTitle?: string; isApp: boolean},\n): DerivedInterface[] {\n if (!isWorkbenchApp(app)) return []\n\n // The schema rejects a *declared* studio with an `entry`; `isApp` is resolved\n // from the project, so a detected one only fails here.\n if (!isApp && app.entry !== undefined) {\n throw new Error('App views for studios are not implemented yet')\n }\n const entry = isApp ? app.entry : GENERATED_ENTRY\n\n const shared = <T extends InterfaceKind>(\n type: T,\n declaration: {name: string; src: string; title: string},\n ) => ({\n id: `${app.slug}-${type}-${declaration.name}`,\n moduleId: interfaceModuleId(type, declaration.name),\n name: declaration.name,\n src: declaration.src,\n title: declaration.title,\n type,\n version: interfaceContractVersion(type),\n })\n\n return [\n ...(app.views ?? []).map((view): DerivedInterface => {\n // Tile is the only view type with interface metadata: its footprint `size`\n // (required) and an optional sort `priority`. Both authored top-level.\n if (view.type === 'tile') {\n return {\n ...shared(view.type, view),\n metadata:\n view.priority === undefined\n ? {size: view.size}\n : {priority: view.priority, size: view.size},\n }\n }\n return {...shared(view.type, view), metadata: null}\n }),\n ...(app.services ?? []).map(\n (service): DerivedInterface => ({...shared('worker', service), metadata: null}),\n ),\n ...(entry === undefined\n ? []\n : [\n {\n ...shared('app', {name: app.slug, src: entry, title: appTitle ?? app.title}),\n metadata: null,\n },\n ]),\n ]\n}\n"],"names":["FEDERATION_FILE_NAME","RUNTIME_DIR","interfaceContractVersion","interfaceModuleId","isWorkbenchApp","GENERATED_ENTRY","deriveInterfaces","app","appTitle","isApp","entry","undefined","Error","shared","type","declaration","id","slug","name","moduleId","src","title","version","views","map","view","metadata","priority","size","services","service"],"mappings":"AAEA,SAAQA,oBAAoB,EAAEC,WAAW,QAAO,oCAAmC;AACnF,SAEEC,wBAAwB,EAExBC,iBAAiB,QAEZ,gBAAe;AACtB,SAAQC,cAAc,QAAO,iBAAgB;AAE7C,4FAA4F,GAC5F,MAAMC,kBAAkB,CAAC,EAAE,EAAEJ,YAAY,CAAC,EAAED,qBAAqB,IAAI,CAAC;AAqBtE;;;;CAIC,GACD,OAAO,SAASM,iBACdC,GAAqB,EACrB,EAACC,QAAQ,EAAEC,KAAK,EAAsC;IAEtD,IAAI,CAACL,eAAeG,MAAM,OAAO,EAAE;IAEnC,8EAA8E;IAC9E,uDAAuD;IACvD,IAAI,CAACE,SAASF,IAAIG,KAAK,KAAKC,WAAW;QACrC,MAAM,IAAIC,MAAM;IAClB;IACA,MAAMF,QAAQD,QAAQF,IAAIG,KAAK,GAAGL;IAElC,MAAMQ,SAAS,CACbC,MACAC,cACI,CAAA;YACJC,IAAI,GAAGT,IAAIU,IAAI,CAAC,CAAC,EAAEH,KAAK,CAAC,EAAEC,YAAYG,IAAI,EAAE;YAC7CC,UAAUhB,kBAAkBW,MAAMC,YAAYG,IAAI;YAClDA,MAAMH,YAAYG,IAAI;YACtBE,KAAKL,YAAYK,GAAG;YACpBC,OAAON,YAAYM,KAAK;YACxBP;YACAQ,SAASpB,yBAAyBY;QACpC,CAAA;IAEA,OAAO;WACF,AAACP,CAAAA,IAAIgB,KAAK,IAAI,EAAE,AAAD,EAAGC,GAAG,CAAC,CAACC;YACxB,2EAA2E;YAC3E,uEAAuE;YACvE,IAAIA,KAAKX,IAAI,KAAK,QAAQ;gBACxB,OAAO;oBACL,GAAGD,OAAOY,KAAKX,IAAI,EAAEW,KAAK;oBAC1BC,UACED,KAAKE,QAAQ,KAAKhB,YACd;wBAACiB,MAAMH,KAAKG,IAAI;oBAAA,IAChB;wBAACD,UAAUF,KAAKE,QAAQ;wBAAEC,MAAMH,KAAKG,IAAI;oBAAA;gBACjD;YACF;YACA,OAAO;gBAAC,GAAGf,OAAOY,KAAKX,IAAI,EAAEW,KAAK;gBAAEC,UAAU;YAAI;QACpD;WACG,AAACnB,CAAAA,IAAIsB,QAAQ,IAAI,EAAE,AAAD,EAAGL,GAAG,CACzB,CAACM,UAA+B,CAAA;gBAAC,GAAGjB,OAAO,UAAUiB,QAAQ;gBAAEJ,UAAU;YAAI,CAAA;WAE3EhB,UAAUC,YACV,EAAE,GACF;YACE;gBACE,GAAGE,OAAO,OAAO;oBAACK,MAAMX,IAAIU,IAAI;oBAAEG,KAAKV;oBAAOW,OAAOb,YAAYD,IAAIc,KAAK;gBAAA,EAAE;gBAC5EK,UAAU;YACZ;SACD;KACN;AACH"}
@@ -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: 'asset_source'})\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/** Every application in an organization, in one page (`limit=none`). */\nexport async function listApplications(organizationId: string): Promise<Application[]> {\n const client = await getClient()\n const {data}: {data: Application[]} = await client.request({\n query: {limit: 'none', organizationId},\n uri: '/applications',\n })\n return data\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","listApplications","data","query","limit","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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"}
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, type TileInterfaceMetadata} 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: 'asset_source'})\n | (BrettInterfaceBase & {metadata: null; type: 'panel'})\n | (BrettInterfaceBase & {metadata: null; type: 'worker'})\n | (BrettInterfaceBase & {metadata: TileInterfaceMetadata; type: 'tile'})\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/** Every application in an organization, in one page (`limit=none`). */\nexport async function listApplications(organizationId: string): Promise<Application[]> {\n const client = await getClient()\n const {data}: {data: Application[]} = await client.request({\n query: {limit: 'none', organizationId},\n uri: '/applications',\n })\n return data\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","listApplications","data","query","limit","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.9.0",
3
+ "version": "1.10.0",
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",
@@ -55,10 +55,10 @@
55
55
  "dependencies": {
56
56
  "@module-federation/vite": "1.20.1",
57
57
  "@sanity/types": "^6.7.0",
58
- "@vitejs/plugin-react": "^6.0.3",
58
+ "@vitejs/plugin-react": "^6.0.5",
59
59
  "form-data": "^4.0.5",
60
60
  "tar-fs": "^3.1.2",
61
- "vite": "^8.1.5",
61
+ "vite": "^8.2.0",
62
62
  "zod": "^4.4.3",
63
63
  "@sanity/cli-core": "^2.8.0"
64
64
  },