@sanity/workbench-cli 1.8.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.
Files changed (53) hide show
  1. package/dist/_exports/build.d.ts +48 -11
  2. package/dist/_exports/deploy.d.ts +53 -72
  3. package/dist/_exports/deploy.js +1 -1
  4. package/dist/_exports/deploy.js.map +1 -1
  5. package/dist/_exports/dev.d.ts +36 -0
  6. package/dist/_exports/index.d.ts +118 -9
  7. package/dist/_exports/index.js.map +1 -1
  8. package/dist/_exports/init.d.ts +5 -3
  9. package/dist/_exports/init.js +2 -1
  10. package/dist/_exports/init.js.map +1 -1
  11. package/dist/_exports/preview.d.ts +36 -0
  12. package/dist/_exports/undeploy.d.ts +50 -11
  13. package/dist/actions/deploy/deployConfig.js +1 -1
  14. package/dist/actions/deploy/deployConfig.js.map +1 -1
  15. package/dist/actions/deploy/deployWorkbenchApp.js +10 -2
  16. package/dist/actions/deploy/deployWorkbenchApp.js.map +1 -1
  17. package/dist/actions/deploy/summarizeInterfaces.js +33 -0
  18. package/dist/actions/deploy/summarizeInterfaces.js.map +1 -0
  19. package/dist/actions/deploy/viewDeployment.js +3 -1
  20. package/dist/actions/deploy/viewDeployment.js.map +1 -1
  21. package/dist/actions/dev/{deriveInterfaces.js → deriveConfigs.js} +3 -54
  22. package/dist/actions/dev/deriveConfigs.js.map +1 -0
  23. package/dist/actions/dev/exposesSetId.js +1 -1
  24. package/dist/actions/dev/exposesSetId.js.map +1 -1
  25. package/dist/actions/dev/registry.js +12 -2
  26. package/dist/actions/dev/registry.js.map +1 -1
  27. package/dist/actions/dev/startDevManifestWatcher.js.map +1 -1
  28. package/dist/actions/dev/startDevServerRegistration.js +2 -1
  29. package/dist/actions/dev/startDevServerRegistration.js.map +1 -1
  30. package/dist/actions/init/cliConfig.js +2 -4
  31. package/dist/actions/init/cliConfig.js.map +1 -1
  32. package/dist/actions/preview/startWorkbenchPreview.js +2 -1
  33. package/dist/actions/preview/startWorkbenchPreview.js.map +1 -1
  34. package/dist/actions/undeploy/workbenchUndeployAdapter.js +3 -3
  35. package/dist/actions/undeploy/workbenchUndeployAdapter.js.map +1 -1
  36. package/dist/appId.js +1 -1
  37. package/dist/appId.js.map +1 -1
  38. package/dist/appSlug.js +7 -0
  39. package/dist/appSlug.js.map +1 -0
  40. package/dist/contract.js +60 -4
  41. package/dist/contract.js.map +1 -1
  42. package/dist/defineApp.js +7 -7
  43. package/dist/defineApp.js.map +1 -1
  44. package/dist/defineView.js.map +1 -1
  45. package/dist/deriveInterfaces.js +63 -0
  46. package/dist/deriveInterfaces.js.map +1 -0
  47. package/dist/resolveWorkbenchApp.js +0 -1
  48. package/dist/resolveWorkbenchApp.js.map +1 -1
  49. package/dist/services/applications.js.map +1 -1
  50. package/package.json +7 -6
  51. package/dist/actions/deploy/buildExposes.js +0 -72
  52. package/dist/actions/deploy/buildExposes.js.map +0 -1
  53. package/dist/actions/dev/deriveInterfaces.js.map +0 -1
@@ -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 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 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 '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` + an optional display\n// `title` that defaults to `name` on deploy.\nfunction interfaceDeclarationFields(kind: 'Service' | 'View') {\n return {...extensionDeclarationFields(kind), title: z.optional(z.string())}\n}\n\nconst PanelViewSchema = z.object({\n type: z.literal('panel'),\n ...interfaceDeclarationFields('View'),\n})\n\n/** @internal */\nexport const InterfaceDeclarationSchema = z.discriminatedUnion('type', [PanelViewSchema])\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","panel","AppInterfaceMetadataSchema","object","group","optional","string","priority","number","interfaceModuleId","type","name","Error","extensionDeclarationFields","kind","pattern","check","regex","src","interfaceDeclarationFields","title","PanelViewSchema","literal","InterfaceDeclarationSchema","discriminatedUnion","WorkerServiceSchema","ServiceDeclarationSchema","MediaLibraryFieldSchema","public","boolean","INSTALLATION_CONFIG_TYPE","MediaLibraryConfigSchema","appType","fields","array","refine","Set","map","field","size","length","ConfigSchema"],"mappings":"AAAA,SAAQA,CAAC,QAAO,WAAU;AAE1B,4EAA4E;AAC5E,kEAAkE;AAClE,qCAAqC;AAErC,cAAc,GACd,OAAO,MAAMC,wBAAwB,EAAC;AAEtC,cAAc,GACd,OAAO,MAAMC,2BAA2B,EAAC;AAEzC,cAAc,GACd,OAAO,MAAMC,wCAAwC,EAAC;AActD;;;;CAIC,GACD,OAAO,MAAMC,kBAAkB;IAC7BC,OAAO;QAAC;QAAS;KAAQ;AAC3B,EAAsD;AAQtD;;;;CAIC,GACD,OAAO,MAAMC,6BAA6BN,EAAEO,MAAM,CAAC;IACjDC,OAAOR,EAAES,QAAQ,CAACT,EAAEU,MAAM;IAC1BC,UAAUX,EAAES,QAAQ,CAACT,EAAEY,MAAM;AAC/B,GAAE;AAKF;;;;CAIC,GACD,OAAO,SAASC,kBAAkBC,IAAY,EAAEC,IAAY;IAC1D,OAAQD;QACN,KAAK;YAAO;gBACV,OAAO;YACT;QACA,KAAK;YAAS;gBACZ,OAAO,CAAC,MAAM,EAAEC,MAAM;YACxB;QACA,KAAK;YAAU;gBACb,OAAO,CAAC,SAAS,EAAEA,MAAM;YAC3B;QACA;YAAS;gBACP,MAAM,IAAIC,MAAM,CAAC,qDAAqD,EAAEF,MAAM;YAChF;IACF;AACF;AAEA,qEAAqE;AACrE,SAASG,2BAA2BC,IAAkC;IACpE,MAAMC,UAAU;IAChB,OAAO;QACLJ,MAAMf,EAAEU,MAAM,GAAGU,KAAK,CAACpB,EAAEqB,KAAK,CAACF,SAAS,GAAGD,KAAK,qBAAqB,EAAEC,SAAS;QAChFG,KAAKtB,EAAEU,MAAM;IACf;AACF;AAEA,8EAA8E;AAC9E,6CAA6C;AAC7C,SAASa,2BAA2BL,IAAwB;IAC1D,OAAO;QAAC,GAAGD,2BAA2BC,KAAK;QAAEM,OAAOxB,EAAES,QAAQ,CAACT,EAAEU,MAAM;IAAG;AAC5E;AAEA,MAAMe,kBAAkBzB,EAAEO,MAAM,CAAC;IAC/BO,MAAMd,EAAE0B,OAAO,CAAC;IAChB,GAAGH,2BAA2B,OAAO;AACvC;AAEA,cAAc,GACd,OAAO,MAAMI,6BAA6B3B,EAAE4B,kBAAkB,CAAC,QAAQ;IAACH;CAAgB,EAAC;AAEzF,MAAMI,sBAAsB7B,EAAEO,MAAM,CAAC;IACnCO,MAAMd,EAAE0B,OAAO,CAAC;IAChB,GAAGH,2BAA2B,UAAU;AAC1C;AAEA,cAAc,GACd,OAAO,MAAMO,2BAA2B9B,EAAE4B,kBAAkB,CAAC,QAAQ;IAACC;CAAoB,EAAC;AAE3F,MAAME,0BAA0B/B,EAAEO,MAAM,CAAC;IACvC,GAAGU,2BAA2B,QAAQ;IACtCe,QAAQhC,EAAES,QAAQ,CAACT,EAAEiC,OAAO;IAC5BT,OAAOxB,EAAEU,MAAM;AACjB;AAEA;;;CAGC,GACD,OAAO,MAAMwB,2BAA2B,sBAAqB;AAE7D,yEAAyE;AACzE,MAAMC,2BAA2BnC,EAAEO,MAAM,CAAC;IACxC6B,SAASpC,EAAE0B,OAAO,CAAC;IACnBW,QAAQrC,EACLsC,KAAK,CAACP,yBACNX,KAAK,CACJpB,EAAEuC,MAAM,CACN,CAACF,SAAW,IAAIG,IAAIH,OAAOI,GAAG,CAAC,CAACC,QAAUA,MAAM3B,IAAI,GAAG4B,IAAI,KAAKN,OAAOO,MAAM,EAC7E;AAGR;AAEA;;;CAGC,GACD,OAAO,MAAMC,eAAe7C,EAAE4B,kBAAkB,CAAC,WAAW;IAACO;CAAyB,EAAC"}
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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"}
package/dist/defineApp.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod/mini';
2
+ import { APP_SLUG_PATTERN } from './appSlug.js';
2
3
  import { ConfigSchema, InterfaceDeclarationSchema, ServiceDeclarationSchema } from './contract.js';
3
- /** Allowed characters for an app `name`. */ const APP_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
4
4
  /**
5
5
  * Dashboard visibility values. Mirrors `APP_VISIBILITIES` in `@sanity/cli-core`
6
6
  * (which can't be imported here — pulling the barrel into this lean module bloats
@@ -51,11 +51,10 @@ import { ConfigSchema, InterfaceDeclarationSchema, ServiceDeclarationSchema } fr
51
51
  * Sanity-owned app deployed once, installed per org; excluded from the public `DefineAppInput`.
52
52
  * @internal
53
53
  */ isSingleton: z.optional(z.boolean()),
54
- /** Unique app identifier — must match `APP_NAME_PATTERN`. */ name: z.string().check(z.regex(APP_NAME_PATTERN, 'App `name` must match /^[a-zA-Z0-9_-]+$/')),
55
54
  /** Organization that owns the app — the workbench runs and deploys against it. */ organizationId: z.string("App `organizationId` is required — pass the owning organization's ID to `unstable_defineApp`"),
56
55
  /** Sort position within the group, ascending. Defaults to `100` when omitted. */ priority: z.optional(z.number()),
57
56
  /** Background services the app runs (e.g. a `worker` emitting dock badges). */ services: z.optional(z.array(ServiceDeclarationSchema, 'must be an array of services').check(z.refine((services)=>new Set(services.map((service)=>service.name)).size === services.length, 'Service `name` must be unique within an app'))),
58
- slug: z.string('App `slug` is required — the hostname the application is created at on deploy'),
57
+ slug: z.string('App `slug` is required — the hostname the application is created at on deploy').check(z.regex(APP_SLUG_PATTERN, 'App `slug` must be lowercase alphanumerics and hyphens, starting with a letter and ending with an alphanumeric')),
59
58
  /** User-facing app title. Wins over studio.config.ts title on merge. */ title: z.string(),
60
59
  /** Views the app exposes (e.g. dock panels). */ views: z.optional(z.array(InterfaceDeclarationSchema, 'must be an array of panels').check(z.refine((views)=>new Set(views.map((view)=>view.name)).size === views.length, 'View `name` must be unique within an app'))),
61
60
  /** Dashboard visibility of the app. Defaults to `default` when omitted. */ visibility: z.optional(z.enum(APP_VISIBILITIES))
@@ -75,8 +74,10 @@ z.refine((input)=>!(input.config && !input.isSingleton), {
75
74
  path: [
76
75
  'config'
77
76
  ]
78
- })).check(// An app exposes one interface kind: an app view (`entry`) or panels.
79
- z.refine((input)=>!(input.entry !== undefined && (input.views?.length ?? 0) > 0), 'An app cannot expose both an app view (`entry`) and panel views. Declare one or the other.')).check(z.refine((input)=>(input.views?.length ?? 0) <= 1, 'An app can expose at most one panel view.'));
77
+ })).check(// A navigable app view (`entry`) and dock panels are the mutually-exclusive
78
+ // navigable kinds. An `asset_source` view is a separate kind a picker
79
+ // brokered to other apps — so it may sit alongside either.
80
+ z.refine((input)=>!(input.entry !== undefined && (input.views?.some((view)=>view.type === 'panel') ?? false)), 'An app cannot expose both an app view (`entry`) and panel views. Declare one or the other.')).check(z.refine((input)=>(input.views?.filter((view)=>view.type === 'panel').length ?? 0) <= 1, 'An app can expose at most one panel view.'));
80
81
  /**
81
82
  * Nominal brand the CLI discriminates on to enable the workbench build/deploy
82
83
  * codepath. Registered via `Symbol.for` so the marker survives module-realm
@@ -105,7 +106,7 @@ z.refine((input)=>!(input.entry !== undefined && (input.views?.length ?? 0) > 0)
105
106
  /**
106
107
  * Declare a Sanity Workbench application. Identity at runtime — returns the same
107
108
  * object reference, tagged with the workbench brand. Field validation (the
108
- * `name` pattern etc.) runs at build time in the CLI via `DefineAppInputSchema`;
109
+ * `slug` pattern etc.) runs at build time in the CLI via `DefineAppInputSchema`;
109
110
  * this helper stays a thin, pure identity wrapper.
110
111
  * @public
111
112
  */ export function unstable_defineApp(input) {
@@ -128,7 +129,6 @@ z.refine((input)=>!(input.entry !== undefined && (input.views?.length ?? 0) > 0)
128
129
  fields: input.fields
129
130
  } : undefined,
130
131
  isSingleton: true,
131
- name: 'media-library',
132
132
  organizationId: input.organizationId,
133
133
  slug: 'media-library',
134
134
  title: 'Media Library'
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/defineApp.ts"],"sourcesContent":["import {z} from 'zod/mini'\n\nimport {ConfigSchema, InterfaceDeclarationSchema, ServiceDeclarationSchema} from './contract.js'\n\n/** Allowed characters for an app `name`. */\nconst APP_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/\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 /** Unique app identifier — must match `APP_NAME_PATTERN`. */\n name: z.string().check(z.regex(APP_NAME_PATTERN, 'App `name` must match /^[a-zA-Z0-9_-]+$/')),\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.string('App `slug` is required — the hostname the application is created at on deploy'),\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 // An app exposes one interface kind: an app view (`entry`) or panels.\n z.refine(\n (input) => !(input.entry !== undefined && (input.views?.length ?? 0) > 0),\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?.length ?? 0) <= 1,\n 'An app can expose at most one panel view.',\n ),\n )\n\n/**\n * User-facing input for `unstable_defineApp`. Excludes the internal\n * `applicationType`, `isSingleton`, and `config` — validated by the\n * schema but not part of the public surface (Sanity-owned apps set them via\n * `@ts-expect-error`). A union so an app declares an app `entry` or `views`,\n * never both.\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?: never}\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 * `name` 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 name: 'media-library',\n organizationId: input.organizationId,\n slug: 'media-library',\n title: 'Media Library',\n })\n}\n"],"names":["z","ConfigSchema","InterfaceDeclarationSchema","ServiceDeclarationSchema","APP_NAME_PATTERN","APP_VISIBILITIES","ApplicationType","enum","DockGroupSchema","DefineAppInputSchema","object","applicationType","optional","config","entry","string","group","icon","isSingleton","boolean","name","check","regex","organizationId","priority","number","services","array","refine","Set","map","service","size","length","slug","title","views","view","visibility","input","undefined","error","path","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 {\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 components each interface type exposes, keyed by type.\n * @public\n */\nexport interface ViewComponentsByType {\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":"AAAA,SAEEA,qBAAqB,QAGhB,gBAAe;AAoDtB;;;;;;;;;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"}
@@ -0,0 +1,63 @@
1
+ import { FEDERATION_FILE_NAME, RUNTIME_DIR } from './actions/build/vite/constants.js';
2
+ import { interfaceContractVersion, interfaceModuleId } from './contract.js';
3
+ import { isWorkbenchApp } from './defineApp.js';
4
+ /** What a studio exposes as `./App` — the generated entry that renders `sanity.config.ts`. */ const GENERATED_ENTRY = `./${RUNTIME_DIR}/${FEDERATION_FILE_NAME}.jsx`;
5
+ /**
6
+ * `appTitle` titles the app view where a deploy resolved one through `--title`;
7
+ * dev takes it from the config.
8
+ * @internal
9
+ */ export function deriveInterfaces(app, { appTitle, isApp }) {
10
+ if (!isWorkbenchApp(app)) return [];
11
+ // The schema rejects a *declared* studio with an `entry`; `isApp` is resolved
12
+ // from the project, so a detected one only fails here.
13
+ if (!isApp && app.entry !== undefined) {
14
+ throw new Error('App views for studios are not implemented yet');
15
+ }
16
+ const entry = isApp ? app.entry : GENERATED_ENTRY;
17
+ const shared = (type, declaration)=>({
18
+ id: `${app.slug}-${type}-${declaration.name}`,
19
+ moduleId: interfaceModuleId(type, declaration.name),
20
+ name: declaration.name,
21
+ src: declaration.src,
22
+ title: declaration.title,
23
+ type,
24
+ version: interfaceContractVersion(type)
25
+ });
26
+ return [
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 {
42
+ ...shared(view.type, view),
43
+ metadata: null
44
+ };
45
+ }),
46
+ ...(app.services ?? []).map((service)=>({
47
+ ...shared('worker', service),
48
+ metadata: null
49
+ })),
50
+ ...entry === undefined ? [] : [
51
+ {
52
+ ...shared('app', {
53
+ name: app.slug,
54
+ src: entry,
55
+ title: appTitle ?? app.title
56
+ }),
57
+ metadata: null
58
+ }
59
+ ]
60
+ ];
61
+ }
62
+
63
+ //# sourceMappingURL=deriveInterfaces.js.map
@@ -0,0 +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 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"}
@@ -19,7 +19,6 @@ import { formatWorkbenchAppErrors, validateWorkbenchApp } from './validateWorkbe
19
19
  entry: app.entry,
20
20
  icon: app.icon,
21
21
  isSingleton: app.isSingleton,
22
- name: app.name,
23
22
  organizationId: app.organizationId,
24
23
  services: app.services ?? [],
25
24
  slug: app.slug,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/resolveWorkbenchApp.ts"],"sourcesContent":["// Package-internal shared resolver: turn a CLI config's branded\n// `unstable_defineApp` app into its declared interfaces, or `null` for a plain\n// project. The build and deploy accessors (actions/build, actions/deploy) each\n// build their command-specific view on top of this one brand-check +\n// extraction, so the discrimination lives in exactly one place.\n\nimport {type AppVisibility, type CliConfig} from '@sanity/cli-core'\n\nimport {type DefineAppInput, isWorkbenchApp, readConfig, type WorkbenchApp} from './defineApp.js'\nimport {formatWorkbenchAppErrors, validateWorkbenchApp} from './validateWorkbenchApp.js'\n\n/**\n * Bundled so adding a declaration family touches this type and the artifact\n * expanders, not every hop of build/dev plumbing in between.\n * @internal\n */\nexport interface WorkbenchExposes {\n config?: WorkbenchApp['config']\n services?: DefineAppInput['services']\n views?: DefineAppInput['views']\n}\n\n/** @public */\nexport interface ResolvedWorkbenchApp {\n /** The app's unique `name` from `unstable_defineApp`. */\n readonly name: string\n /** Organization that owns the app — part of its build-id identity. */\n readonly organizationId: string\n /** Background worker services the app declares. */\n readonly services: NonNullable<DefineAppInput['services']>\n\n /** Hostname the application is created at on first deploy. */\n readonly slug: string\n\n /** Dock panel views the app declares. */\n readonly views: NonNullable<DefineAppInput['views']>\n\n /** Resolved app kind — `studio` or one of the SDK app types. */\n readonly applicationType?: string\n /** Deploys on its own path, separate from the interfaces. */\n readonly config?: WorkbenchApp['config']\n /** SDK app-view entrypoint, when declared. */\n readonly entry?: string\n /** Path to the app's icon SVG, resolved and shipped to Brett on deploy. */\n readonly icon?: string\n /** Explicit singleton flag (a Sanity-owned app); `undefined` when the app doesn't set it. */\n readonly isSingleton?: boolean\n /** Dashboard visibility declared by the app; `undefined` when unset. */\n readonly visibility?: AppVisibility\n}\n\n/**\n * Resolve the workbench app for a CLI config, or `null` for a plain project.\n * @public\n */\nexport function resolveWorkbenchApp(\n cliConfig: CliConfig | null | undefined,\n): ResolvedWorkbenchApp | null {\n const app = cliConfig?.app\n if (!isWorkbenchApp(app)) return null\n\n const errors = validateWorkbenchApp(app)\n if (errors.length > 0) throw new Error(formatWorkbenchAppErrors(errors))\n\n return {\n applicationType: app.applicationType,\n config: readConfig(app),\n entry: app.entry,\n icon: app.icon,\n isSingleton: app.isSingleton,\n name: app.name,\n organizationId: app.organizationId,\n services: app.services ?? [],\n slug: app.slug,\n views: app.views ?? [],\n visibility: app.visibility,\n }\n}\n"],"names":["isWorkbenchApp","readConfig","formatWorkbenchAppErrors","validateWorkbenchApp","resolveWorkbenchApp","cliConfig","app","errors","length","Error","applicationType","config","entry","icon","isSingleton","name","organizationId","services","slug","views","visibility"],"mappings":"AAAA,gEAAgE;AAChE,+EAA+E;AAC/E,+EAA+E;AAC/E,qEAAqE;AACrE,gEAAgE;AAIhE,SAA6BA,cAAc,EAAEC,UAAU,QAA0B,iBAAgB;AACjG,SAAQC,wBAAwB,EAAEC,oBAAoB,QAAO,4BAA2B;AA0CxF;;;CAGC,GACD,OAAO,SAASC,oBACdC,SAAuC;IAEvC,MAAMC,MAAMD,WAAWC;IACvB,IAAI,CAACN,eAAeM,MAAM,OAAO;IAEjC,MAAMC,SAASJ,qBAAqBG;IACpC,IAAIC,OAAOC,MAAM,GAAG,GAAG,MAAM,IAAIC,MAAMP,yBAAyBK;IAEhE,OAAO;QACLG,iBAAiBJ,IAAII,eAAe;QACpCC,QAAQV,WAAWK;QACnBM,OAAON,IAAIM,KAAK;QAChBC,MAAMP,IAAIO,IAAI;QACdC,aAAaR,IAAIQ,WAAW;QAC5BC,MAAMT,IAAIS,IAAI;QACdC,gBAAgBV,IAAIU,cAAc;QAClCC,UAAUX,IAAIW,QAAQ,IAAI,EAAE;QAC5BC,MAAMZ,IAAIY,IAAI;QACdC,OAAOb,IAAIa,KAAK,IAAI,EAAE;QACtBC,YAAYd,IAAIc,UAAU;IAC5B;AACF"}
1
+ {"version":3,"sources":["../src/resolveWorkbenchApp.ts"],"sourcesContent":["// Package-internal shared resolver: turn a CLI config's branded\n// `unstable_defineApp` app into its declared interfaces, or `null` for a plain\n// project. The build and deploy accessors (actions/build, actions/deploy) each\n// build their command-specific view on top of this one brand-check +\n// extraction, so the discrimination lives in exactly one place.\n\nimport {type AppVisibility, type CliConfig} from '@sanity/cli-core'\n\nimport {type DefineAppInput, isWorkbenchApp, readConfig, type WorkbenchApp} from './defineApp.js'\nimport {formatWorkbenchAppErrors, validateWorkbenchApp} from './validateWorkbenchApp.js'\n\n/**\n * Bundled so adding a declaration family touches this type and the artifact\n * expanders, not every hop of build/dev plumbing in between.\n * @internal\n */\nexport interface WorkbenchExposes {\n config?: WorkbenchApp['config']\n services?: DefineAppInput['services']\n views?: DefineAppInput['views']\n}\n\n/** @public */\nexport interface ResolvedWorkbenchApp {\n /** Organization that owns the app — part of its build-id identity. */\n readonly organizationId: string\n /** Background worker services the app declares. */\n readonly services: NonNullable<DefineAppInput['services']>\n\n readonly slug: string\n\n /** Dock panel views the app declares. */\n readonly views: NonNullable<DefineAppInput['views']>\n\n /** Resolved app kind — `studio` or one of the SDK app types. */\n readonly applicationType?: string\n /** Deploys on its own path, separate from the interfaces. */\n readonly config?: WorkbenchApp['config']\n /** SDK app-view entrypoint, when declared. */\n readonly entry?: string\n /** Path to the app's icon SVG, resolved and shipped to Brett on deploy. */\n readonly icon?: string\n /** Explicit singleton flag (a Sanity-owned app); `undefined` when the app doesn't set it. */\n readonly isSingleton?: boolean\n /** Dashboard visibility declared by the app; `undefined` when unset. */\n readonly visibility?: AppVisibility\n}\n\n/**\n * Resolve the workbench app for a CLI config, or `null` for a plain project.\n * @public\n */\nexport function resolveWorkbenchApp(\n cliConfig: CliConfig | null | undefined,\n): ResolvedWorkbenchApp | null {\n const app = cliConfig?.app\n if (!isWorkbenchApp(app)) return null\n\n const errors = validateWorkbenchApp(app)\n if (errors.length > 0) throw new Error(formatWorkbenchAppErrors(errors))\n\n return {\n applicationType: app.applicationType,\n config: readConfig(app),\n entry: app.entry,\n icon: app.icon,\n isSingleton: app.isSingleton,\n organizationId: app.organizationId,\n services: app.services ?? [],\n slug: app.slug,\n views: app.views ?? [],\n visibility: app.visibility,\n }\n}\n"],"names":["isWorkbenchApp","readConfig","formatWorkbenchAppErrors","validateWorkbenchApp","resolveWorkbenchApp","cliConfig","app","errors","length","Error","applicationType","config","entry","icon","isSingleton","organizationId","services","slug","views","visibility"],"mappings":"AAAA,gEAAgE;AAChE,+EAA+E;AAC/E,+EAA+E;AAC/E,qEAAqE;AACrE,gEAAgE;AAIhE,SAA6BA,cAAc,EAAEC,UAAU,QAA0B,iBAAgB;AACjG,SAAQC,wBAAwB,EAAEC,oBAAoB,QAAO,4BAA2B;AAuCxF;;;CAGC,GACD,OAAO,SAASC,oBACdC,SAAuC;IAEvC,MAAMC,MAAMD,WAAWC;IACvB,IAAI,CAACN,eAAeM,MAAM,OAAO;IAEjC,MAAMC,SAASJ,qBAAqBG;IACpC,IAAIC,OAAOC,MAAM,GAAG,GAAG,MAAM,IAAIC,MAAMP,yBAAyBK;IAEhE,OAAO;QACLG,iBAAiBJ,IAAII,eAAe;QACpCC,QAAQV,WAAWK;QACnBM,OAAON,IAAIM,KAAK;QAChBC,MAAMP,IAAIO,IAAI;QACdC,aAAaR,IAAIQ,WAAW;QAC5BC,gBAAgBT,IAAIS,cAAc;QAClCC,UAAUV,IAAIU,QAAQ,IAAI,EAAE;QAC5BC,MAAMX,IAAIW,IAAI;QACdC,OAAOZ,IAAIY,KAAK,IAAI,EAAE;QACtBC,YAAYb,IAAIa,UAAU;IAC5B;AACF"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/services/applications.ts"],"sourcesContent":["import {PassThrough} from 'node:stream'\nimport {type Gzip} from 'node:zlib'\n\nimport {type AppVisibility, getGlobalCliClient} from '@sanity/cli-core'\nimport {isStaging} from '@sanity/cli-core/util'\nimport FormData from 'form-data'\n\nimport {type AppInterfaceMetadata} from '../contract.js'\nimport {APP_WORKBENCH_API_VERSION} from './apiVersion.js'\n\nexport type ApplicationType = 'coreApp' | 'studio'\n\nexport interface Application {\n id: string\n organizationId: string\n slug: string | null\n title: string\n type: ApplicationType\n}\n\ninterface BrettInterfaceBase {\n moduleId: string\n name: string\n title: string\n version: string\n}\n\n/**\n * An interface as Brett stores it, discriminated on `type`. `moduleId` is\n * remote-relative — the host prepends the app's id. Brett assigns the id.\n * @internal\n */\nexport type BrettInterface =\n | (BrettInterfaceBase & {metadata: AppInterfaceMetadata | null; type: 'app'})\n | (BrettInterfaceBase & {metadata: null; type: 'panel'})\n | (BrettInterfaceBase & {metadata: null; type: 'worker'})\n\n/** A studio workspace as Brett stores it. */\nexport interface BrettWorkspace {\n dataset: string\n projectId: string\n /** 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.8.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",
@@ -53,17 +53,18 @@
53
53
  "access": "public"
54
54
  },
55
55
  "dependencies": {
56
- "@module-federation/vite": "1.19.1",
57
- "@vitejs/plugin-react": "^6.0.3",
56
+ "@module-federation/vite": "1.20.1",
57
+ "@sanity/types": "^6.7.0",
58
+ "@vitejs/plugin-react": "^6.0.5",
58
59
  "form-data": "^4.0.5",
59
60
  "tar-fs": "^3.1.2",
60
- "vite": "^8.1.5",
61
+ "vite": "^8.2.0",
61
62
  "zod": "^4.4.3",
62
- "@sanity/cli-core": "^2.7.0"
63
+ "@sanity/cli-core": "^2.8.0"
63
64
  },
64
65
  "devDependencies": {
65
66
  "@eslint/compat": "^2.1.0",
66
- "@sanity/pkg-utils": "^11.0.9",
67
+ "@sanity/pkg-utils": "^11.0.17",
67
68
  "@swc/cli": "^0.8.1",
68
69
  "@swc/core": "^1.15.43",
69
70
  "@types/node": "^22.20.0",
@@ -1,72 +0,0 @@
1
- import { interfaceModuleId } from '../../contract.js';
2
- /**
3
- * The interface records deploy sends: the app view (only when `exposesAppView`),
4
- * every view, and every service.
5
- * @internal
6
- */ export function buildExposes(exposes, { appName, appTitle, exposesAppView, version }) {
7
- const records = [];
8
- if (exposesAppView) {
9
- records.push({
10
- metadata: null,
11
- moduleId: interfaceModuleId('app', appName),
12
- name: appName,
13
- title: appTitle,
14
- type: 'app',
15
- version
16
- });
17
- }
18
- for (const view of exposes.views ?? []){
19
- records.push({
20
- metadata: null,
21
- moduleId: interfaceModuleId('panel', view.name),
22
- name: view.name,
23
- title: view.title ?? view.name,
24
- type: 'panel',
25
- version
26
- });
27
- }
28
- for (const service of exposes.services ?? []){
29
- records.push({
30
- metadata: null,
31
- moduleId: interfaceModuleId('worker', service.name),
32
- name: service.name,
33
- title: service.title ?? service.name,
34
- type: 'worker',
35
- version
36
- });
37
- }
38
- return records;
39
- }
40
- const label = (item)=>item.title === item.name ? item.name : `${item.title} (${item.name})`;
41
- /**
42
- * One `Title (name): src` report line per declared entry point.
43
- * @internal
44
- */ export function summarizeExposeGroup(heading, items) {
45
- return `${heading}:\n${items.map((item)=>` ${label(item)}: ${item.src}`).join('\n')}`;
46
- }
47
- /**
48
- * The deploy summary of an app's exposes: the structured records (for `--json`)
49
- * and one report line per non-empty group (for the human report).
50
- * @internal
51
- */ export function summarizeExposes({ services, views }) {
52
- const toExpose = (decl)=>({
53
- name: decl.name,
54
- src: decl.src,
55
- title: decl.title ?? decl.name,
56
- type: decl.type
57
- });
58
- const viewExposes = (views ?? []).map((view)=>toExpose(view));
59
- const serviceExposes = (services ?? []).map((service)=>toExpose(service));
60
- const lines = [];
61
- if (viewExposes.length > 0) lines.push(summarizeExposeGroup('Views', viewExposes));
62
- if (serviceExposes.length > 0) lines.push(summarizeExposeGroup('Services', serviceExposes));
63
- return {
64
- exposes: [
65
- ...viewExposes,
66
- ...serviceExposes
67
- ],
68
- lines
69
- };
70
- }
71
-
72
- //# sourceMappingURL=buildExposes.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/actions/deploy/buildExposes.ts"],"sourcesContent":["import {interfaceModuleId} from '../../contract.js'\nimport {type WorkbenchExposes} from '../../resolveWorkbenchApp.js'\nimport {type BrettInterface} from '../../services/applications.js'\n\ninterface BuildExposesContext {\n appName: string\n appTitle: string\n /** Whether the build exposes the app view (`./App`) — apps with an `entry`, and every studio. */\n exposesAppView: boolean\n version: string\n}\n\n/**\n * The interface records deploy sends: the app view (only when `exposesAppView`),\n * every view, and every service.\n * @internal\n */\nexport function buildExposes(\n exposes: WorkbenchExposes,\n {appName, appTitle, exposesAppView, version}: BuildExposesContext,\n): BrettInterface[] {\n const records: BrettInterface[] = []\n if (exposesAppView) {\n records.push({\n metadata: null,\n moduleId: interfaceModuleId('app', appName),\n name: appName,\n title: appTitle,\n type: 'app',\n version,\n })\n }\n for (const view of exposes.views ?? []) {\n records.push({\n metadata: null,\n moduleId: interfaceModuleId('panel', view.name),\n name: view.name,\n title: view.title ?? view.name,\n type: 'panel',\n version,\n })\n }\n for (const service of exposes.services ?? []) {\n records.push({\n metadata: null,\n moduleId: interfaceModuleId('worker', service.name),\n name: service.name,\n title: service.title ?? service.name,\n type: 'worker',\n version,\n })\n }\n return records\n}\n\n/** A view or service as the deploy report and `--json` output surface it. */\nexport interface DeployedExpose {\n name: string\n src: string\n title: string\n type: string\n}\n\nconst label = (item: {name: string; title: string}) =>\n item.title === item.name ? item.name : `${item.title} (${item.name})`\n\n/**\n * One `Title (name): src` report line per declared entry point.\n * @internal\n */\nexport function summarizeExposeGroup(\n heading: string,\n items: readonly {name: string; src: string; title: string}[],\n): string {\n return `${heading}:\\n${items.map((item) => ` ${label(item)}: ${item.src}`).join('\\n')}`\n}\n\n/**\n * The deploy summary of an app's exposes: the structured records (for `--json`)\n * and one report line per non-empty group (for the human report).\n * @internal\n */\nexport function summarizeExposes({services, views}: WorkbenchExposes): {\n exposes: DeployedExpose[]\n lines: string[]\n} {\n const toExpose = (decl: {\n name: string\n src: string\n title?: string\n type: string\n }): DeployedExpose => ({\n name: decl.name,\n src: decl.src,\n title: decl.title ?? decl.name,\n type: decl.type,\n })\n const viewExposes = (views ?? []).map((view) => toExpose(view))\n const serviceExposes = (services ?? []).map((service) => toExpose(service))\n\n const lines: string[] = []\n if (viewExposes.length > 0) lines.push(summarizeExposeGroup('Views', viewExposes))\n if (serviceExposes.length > 0) lines.push(summarizeExposeGroup('Services', serviceExposes))\n return {exposes: [...viewExposes, ...serviceExposes], lines}\n}\n"],"names":["interfaceModuleId","buildExposes","exposes","appName","appTitle","exposesAppView","version","records","push","metadata","moduleId","name","title","type","view","views","service","services","label","item","summarizeExposeGroup","heading","items","map","src","join","summarizeExposes","toExpose","decl","viewExposes","serviceExposes","lines","length"],"mappings":"AAAA,SAAQA,iBAAiB,QAAO,oBAAmB;AAYnD;;;;CAIC,GACD,OAAO,SAASC,aACdC,OAAyB,EACzB,EAACC,OAAO,EAAEC,QAAQ,EAAEC,cAAc,EAAEC,OAAO,EAAsB;IAEjE,MAAMC,UAA4B,EAAE;IACpC,IAAIF,gBAAgB;QAClBE,QAAQC,IAAI,CAAC;YACXC,UAAU;YACVC,UAAUV,kBAAkB,OAAOG;YACnCQ,MAAMR;YACNS,OAAOR;YACPS,MAAM;YACNP;QACF;IACF;IACA,KAAK,MAAMQ,QAAQZ,QAAQa,KAAK,IAAI,EAAE,CAAE;QACtCR,QAAQC,IAAI,CAAC;YACXC,UAAU;YACVC,UAAUV,kBAAkB,SAASc,KAAKH,IAAI;YAC9CA,MAAMG,KAAKH,IAAI;YACfC,OAAOE,KAAKF,KAAK,IAAIE,KAAKH,IAAI;YAC9BE,MAAM;YACNP;QACF;IACF;IACA,KAAK,MAAMU,WAAWd,QAAQe,QAAQ,IAAI,EAAE,CAAE;QAC5CV,QAAQC,IAAI,CAAC;YACXC,UAAU;YACVC,UAAUV,kBAAkB,UAAUgB,QAAQL,IAAI;YAClDA,MAAMK,QAAQL,IAAI;YAClBC,OAAOI,QAAQJ,KAAK,IAAII,QAAQL,IAAI;YACpCE,MAAM;YACNP;QACF;IACF;IACA,OAAOC;AACT;AAUA,MAAMW,QAAQ,CAACC,OACbA,KAAKP,KAAK,KAAKO,KAAKR,IAAI,GAAGQ,KAAKR,IAAI,GAAG,GAAGQ,KAAKP,KAAK,CAAC,EAAE,EAAEO,KAAKR,IAAI,CAAC,CAAC,CAAC;AAEvE;;;CAGC,GACD,OAAO,SAASS,qBACdC,OAAe,EACfC,KAA4D;IAE5D,OAAO,GAAGD,QAAQ,GAAG,EAAEC,MAAMC,GAAG,CAAC,CAACJ,OAAS,CAAC,EAAE,EAAED,MAAMC,MAAM,EAAE,EAAEA,KAAKK,GAAG,EAAE,EAAEC,IAAI,CAAC,OAAO;AAC1F;AAEA;;;;CAIC,GACD,OAAO,SAASC,iBAAiB,EAACT,QAAQ,EAAEF,KAAK,EAAmB;IAIlE,MAAMY,WAAW,CAACC,OAKK,CAAA;YACrBjB,MAAMiB,KAAKjB,IAAI;YACfa,KAAKI,KAAKJ,GAAG;YACbZ,OAAOgB,KAAKhB,KAAK,IAAIgB,KAAKjB,IAAI;YAC9BE,MAAMe,KAAKf,IAAI;QACjB,CAAA;IACA,MAAMgB,cAAc,AAACd,CAAAA,SAAS,EAAE,AAAD,EAAGQ,GAAG,CAAC,CAACT,OAASa,SAASb;IACzD,MAAMgB,iBAAiB,AAACb,CAAAA,YAAY,EAAE,AAAD,EAAGM,GAAG,CAAC,CAACP,UAAYW,SAASX;IAElE,MAAMe,QAAkB,EAAE;IAC1B,IAAIF,YAAYG,MAAM,GAAG,GAAGD,MAAMvB,IAAI,CAACY,qBAAqB,SAASS;IACrE,IAAIC,eAAeE,MAAM,GAAG,GAAGD,MAAMvB,IAAI,CAACY,qBAAqB,YAAYU;IAC3E,OAAO;QAAC5B,SAAS;eAAI2B;eAAgBC;SAAe;QAAEC;IAAK;AAC7D"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/actions/dev/deriveInterfaces.ts"],"sourcesContent":["import {type CliConfig} from '@sanity/cli-core'\n\nimport {\n interfaceModuleId,\n MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION,\n SERVICE_CONTRACT_VERSION,\n VIEW_CONTRACT_VERSION,\n} from '../../contract.js'\nimport {isWorkbenchApp, readConfig} from '../../defineApp.js'\nimport {FEDERATION_FILE_NAME, RUNTIME_DIR} from '../build/vite/constants.js'\nimport {type DevServerManifest} from './registry.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\n/** One forwarded interface record on the dev-server registry entry. */\nexport type DevServerInterface = NonNullable<DevServerManifest['interfaces']>[number]\n\n/** One forwarded config on the dev-server registry entry. */\nexport type DevServerConfig = NonNullable<DevServerManifest['configs']>[number]\n\n/**\n * Map a workbench app's declarations to its registry interface records. A studio\n * that declares `entry` is rejected (studio app views aren't implemented yet).\n */\nexport function deriveInterfaces(\n app: CliConfig['app'],\n options: {isApp: boolean},\n): DevServerInterface[] | undefined {\n if (!isWorkbenchApp(app)) return undefined\n\n if (!options.isApp && app.entry !== undefined) {\n throw new Error('App views for studios are not implemented yet')\n }\n\n const interfaceId = (type: string, name: string): string => `${app.name}-${type}-${name}`\n\n const views = (app.views ?? []).map(\n (view): DevServerInterface => ({\n id: interfaceId('panel', view.name),\n metadata: null,\n moduleId: interfaceModuleId('panel', view.name),\n name: view.name,\n src: view.src,\n title: view.title ?? view.name,\n type: 'panel',\n version: String(VIEW_CONTRACT_VERSION),\n }),\n )\n\n const services = (app.services ?? []).map(\n (service): DevServerInterface => ({\n id: interfaceId('worker', service.name),\n metadata: null,\n moduleId: interfaceModuleId('worker', service.name),\n name: service.name,\n src: service.src,\n title: service.title ?? service.name,\n type: 'worker',\n version: String(SERVICE_CONTRACT_VERSION),\n }),\n )\n\n // Tracks what the build exposes as `./App`: an app's declared `entry`, and a\n // studio's generated entry. A dock-only app (no `entry`) exposes none.\n const appEntry = options.isApp ? app.entry : GENERATED_ENTRY\n\n const appView: DevServerInterface[] =\n appEntry === undefined\n ? []\n : [\n {\n id: interfaceId('app', app.name),\n metadata: null,\n moduleId: interfaceModuleId('app', app.name),\n name: app.name,\n src: appEntry,\n title: app.title,\n type: 'app',\n },\n ]\n\n return [...views, ...services, ...appView]\n}\n\n/**\n * The named source files a config's generated module is built from, dispatched\n * per app type — the projection the exposes-set id keys on, so the generic HMR\n * tracker owns none of the per-type shape. Throws on an app type it can't\n * handle, so a new config family has to register its shape here.\n */\nexport function deriveConfigEntries(config: DevServerConfig): {name: string; src: string}[] {\n switch (config.appType) {\n case 'media-library': {\n return config.fields.map((field) => ({name: field.name, src: field.src}))\n }\n default: {\n throw new Error(`Cannot derive entries for unknown config appType: ${config.appType}`)\n }\n }\n}\n\n/**\n * The fields' schema *values* can't serialize — the workbench loads them from\n * the federation module. `src` stays on so the exposes-set id keys on it and a\n * repoint rebuilds. `appType` routes the config to the singleton (no app id to\n * key on). `id` is a content hash of the entry — it fills the\n * installation-config id slot deployed apps get from the applications API,\n * and the workbench keys change detection on it. `version` is a string, like\n * the one Brett returns on a deployed `activeConfig`.\n */\nexport async function deriveConfigs(app: CliConfig['app']): Promise<DevServerConfig[]> {\n if (!isWorkbenchApp(app)) return []\n const config = readConfig(app)\n if (!config) return []\n const entry = {\n appType: config.appType,\n fields: config.fields.map((field) => ({\n name: field.name,\n public: field.public,\n src: field.src,\n title: field.title,\n })),\n moduleName: app.name,\n version: String(MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION),\n }\n return [{...entry, id: await contentHash(JSON.stringify(entry))}]\n}\n\n/**\n * SHA-256 of a string, as hex, via the Web Crypto API — available in both Node\n * and the browser. `node:crypto` can't be used: the Vite dev server's dep scan\n * pulls this module into the browser graph.\n */\nasync function contentHash(input: string): Promise<string> {\n // eslint-disable-next-line n/no-unsupported-features/node-builtins -- the Web Crypto global is available on our Node target and in the browser\n const digest = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(input))\n return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, '0')).join('')\n}\n"],"names":["interfaceModuleId","MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION","SERVICE_CONTRACT_VERSION","VIEW_CONTRACT_VERSION","isWorkbenchApp","readConfig","FEDERATION_FILE_NAME","RUNTIME_DIR","GENERATED_ENTRY","deriveInterfaces","app","options","undefined","isApp","entry","Error","interfaceId","type","name","views","map","view","id","metadata","moduleId","src","title","version","String","services","service","appEntry","appView","deriveConfigEntries","config","appType","fields","field","deriveConfigs","public","moduleName","contentHash","JSON","stringify","input","digest","globalThis","crypto","subtle","TextEncoder","encode","Array","from","Uint8Array","byte","toString","padStart","join"],"mappings":"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"}