@sanity/workbench-cli 2.1.0 → 2.2.2

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 (80) hide show
  1. package/README.md +1 -1
  2. package/dist/_exports/build.d.ts +4 -4
  3. package/dist/_exports/build.js +2 -1
  4. package/dist/_exports/build.js.map +1 -1
  5. package/dist/_exports/defineApp-DldjQwq6.d.ts +375 -0
  6. package/dist/_exports/deploy.d.ts +10 -9
  7. package/dist/_exports/deploy.js +1 -0
  8. package/dist/_exports/deploy.js.map +1 -1
  9. package/dist/_exports/dev.d.ts +1 -1
  10. package/dist/_exports/index.d.ts +23 -16
  11. package/dist/_exports/index.js +2 -1
  12. package/dist/_exports/index.js.map +1 -1
  13. package/dist/_exports/preview.d.ts +1 -1
  14. package/dist/_exports/{registry-DI7hnTof.d.ts → registry-HE_dEHFa.d.ts} +38 -15
  15. package/dist/_exports/resolveWorkbenchConfig-6iznHbED.d.ts +60 -0
  16. package/dist/_exports/{summarizeInterfaces-DLsq6P43.d.ts → summarizeInterfaces-CjkeWwkY.d.ts} +20 -17
  17. package/dist/_exports/undeploy.d.ts +12 -9
  18. package/dist/actions/build/artifact.js +1 -1
  19. package/dist/actions/build/artifact.js.map +1 -1
  20. package/dist/actions/build/views/artifact.js +1 -1
  21. package/dist/actions/build/views/artifact.js.map +1 -1
  22. package/dist/actions/build/vite/optimize-deps.js +3 -3
  23. package/dist/actions/build/vite/optimize-deps.js.map +1 -1
  24. package/dist/actions/build/vite/plugins/plugin-module-federation.js +1 -2
  25. package/dist/actions/build/vite/plugins/plugin-module-federation.js.map +1 -1
  26. package/dist/actions/build/vite/plugins/plugin-sanity-federation-runtime.js +1 -1
  27. package/dist/actions/build/vite/plugins/plugin-sanity-federation-runtime.js.map +1 -1
  28. package/dist/actions/build/vite/workbench-vite-plugins.js +1 -1
  29. package/dist/actions/build/vite/workbench-vite-plugins.js.map +1 -1
  30. package/dist/actions/deploy/deployPayload.js +0 -3
  31. package/dist/actions/deploy/deployPayload.js.map +1 -1
  32. package/dist/actions/deploy/deployWorkbenchApp.js +46 -4
  33. package/dist/actions/deploy/deployWorkbenchApp.js.map +1 -1
  34. package/dist/actions/deploy/getWorkbench.js +4 -5
  35. package/dist/actions/deploy/getWorkbench.js.map +1 -1
  36. package/dist/actions/deploy/summarizeInterfaces.js +14 -10
  37. package/dist/actions/deploy/summarizeInterfaces.js.map +1 -1
  38. package/dist/actions/deploy/viewDeployment.js +17 -5
  39. package/dist/actions/deploy/viewDeployment.js.map +1 -1
  40. package/dist/actions/dev/deriveConfigs.js +9 -6
  41. package/dist/actions/dev/deriveConfigs.js.map +1 -1
  42. package/dist/actions/dev/exposesSetId.js +2 -2
  43. package/dist/actions/dev/exposesSetId.js.map +1 -1
  44. package/dist/actions/dev/registry.js +31 -25
  45. package/dist/actions/dev/registry.js.map +1 -1
  46. package/dist/actions/dev/startDevManifestWatcher.js.map +1 -1
  47. package/dist/actions/dev/startDevServerRegistration.js +35 -60
  48. package/dist/actions/dev/startDevServerRegistration.js.map +1 -1
  49. package/dist/actions/dev/startWorkbenchDevServer.js +11 -7
  50. package/dist/actions/dev/startWorkbenchDevServer.js.map +1 -1
  51. package/dist/actions/dev/toWireInterface.js +17 -0
  52. package/dist/actions/dev/toWireInterface.js.map +1 -0
  53. package/dist/actions/preview/serveBuiltApplication.js +1 -1
  54. package/dist/actions/preview/serveBuiltApplication.js.map +1 -1
  55. package/dist/actions/preview/startWorkbenchPreview.js +1 -1
  56. package/dist/actions/preview/startWorkbenchPreview.js.map +1 -1
  57. package/dist/actions/undeploy/workbenchUndeployAdapter.js +24 -33
  58. package/dist/actions/undeploy/workbenchUndeployAdapter.js.map +1 -1
  59. package/dist/appId.js +13 -9
  60. package/dist/appId.js.map +1 -1
  61. package/dist/applicationReference.js +12 -0
  62. package/dist/applicationReference.js.map +1 -0
  63. package/dist/contract.js +69 -31
  64. package/dist/contract.js.map +1 -1
  65. package/dist/defineApp.js +52 -59
  66. package/dist/defineApp.js.map +1 -1
  67. package/dist/defineView.js +3 -3
  68. package/dist/defineView.js.map +1 -1
  69. package/dist/deriveInterfaces.js +45 -19
  70. package/dist/deriveInterfaces.js.map +1 -1
  71. package/dist/resolveWorkbenchApp.js +13 -9
  72. package/dist/resolveWorkbenchApp.js.map +1 -1
  73. package/dist/resolveWorkbenchConfig.js +30 -0
  74. package/dist/resolveWorkbenchConfig.js.map +1 -0
  75. package/dist/services/applications.js +7 -1
  76. package/dist/services/applications.js.map +1 -1
  77. package/package.json +3 -3
  78. package/dist/_exports/contract-DyG11fQ7.d.ts +0 -45
  79. package/dist/_exports/defineApp-DSvQx8rf.d.ts +0 -151
  80. package/dist/_exports/resolveWorkbenchApp-Be9eU8mu.d.ts +0 -41
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/undeploy/workbenchUndeployAdapter.ts"],"sourcesContent":["import {\n type UndeployAdapter,\n type UndeployApplicationTarget,\n type UndeployConfigTarget,\n type UndeployTargetResolution,\n} from '@sanity/cli-core/undeploy'\n\nimport {\n deleteApplication,\n getApplication,\n getApplicationUrl,\n getWorkbenchUrl,\n} from '../../services/applications.js'\nimport {type ConfigSnapshot, deleteConfig, listConfigs} from '../../services/installations.js'\nimport {resolveInstallationId, summarizeConfig} from '../deploy/deployConfig.js'\nimport {type DeployableWorkbenchApp} from '../deploy/getWorkbench.js'\nimport {type DeployedInterface, summarizeInterfaces} from '../deploy/summarizeInterfaces.js'\n\n/** The workbench extension of the shared target; serializes into `--json` as-is. */\nexport type WorkbenchUndeployTarget =\n | (UndeployApplicationTarget & {\n services: DeployedInterface[]\n views: DeployedInterface[]\n })\n | (UndeployConfigTarget & {\n configs: ConfigSnapshot[]\n })\n\n/**\n * The undeploy adapter for workbench apps, mirroring what a workbench deploy\n * creates: apps that expose interfaces delete their Brett application (the\n * server soft-deletes its deployments and refuses singletons with active\n * installations); a singleton without interfaces — the media library — deletes\n * its installation's config snapshots instead.\n */\nexport function createWorkbenchUndeployAdapter(options: {\n appId: string | undefined\n organizationId: string | undefined\n type: 'coreApp' | 'studio'\n workbench: DeployableWorkbenchApp\n}): UndeployAdapter<WorkbenchUndeployTarget> {\n const {appId, organizationId, type, workbench} = options\n // Keyed on singleton-ness, not on a locally declared config, so an undeploy\n // still reaches the server's config snapshots after the fields are removed\n // from sanity.cli.ts.\n const configOnly = !!workbench.isSingleton && !workbench.hasInterfaces\n // Workbench-internal, so kept off the reported target; resolveTarget stashes it for the delete.\n let installationId: string | undefined\n\n return {\n resolveTarget: async () => {\n if (!configOnly) return resolveApplicationTarget({appId, type, workbench})\n const resolved = await resolveConfigTarget({organizationId, workbench})\n installationId = resolved.installationId\n return resolved.resolution\n },\n type,\n async undeploy(target) {\n if (target.deletes === 'config') {\n if (!installationId) throw new Error('No installation resolved for the config undeploy')\n for (const snapshot of target.configs) {\n await deleteConfig(installationId, snapshot.id)\n }\n return\n }\n await deleteApplication(target.id)\n },\n }\n}\n\nasync function resolveApplicationTarget({\n appId,\n type,\n workbench,\n}: {\n appId: string | undefined\n type: 'coreApp' | 'studio'\n workbench: DeployableWorkbenchApp\n}): Promise<UndeployTargetResolution<WorkbenchUndeployTarget>> {\n if (!appId) {\n return {\n message: 'No `deployment.appId` configured',\n solution: 'Add `deployment.appId` to sanity.cli.ts',\n type: 'none',\n }\n }\n\n const application = await getApplication(appId)\n if (!application) {\n return {message: 'Application with the given ID does not exist', type: 'none'}\n }\n\n const {lines, services, views} = summarizeInterfaces(workbench)\n return {\n target: {\n application,\n deletes: 'application',\n id: application.id,\n payload: {appId: application.id, services, type, views},\n services,\n summary: [\n ...lines,\n ...(workbench.isSingleton === undefined ? [] : [`Singleton: ${workbench.isSingleton}`]),\n ],\n title: application.title,\n type,\n url: getApplicationUrl({...application, type}),\n views,\n },\n type: 'found',\n }\n}\n\nasync function resolveConfigTarget({\n organizationId,\n workbench,\n}: {\n organizationId: string | undefined\n workbench: DeployableWorkbenchApp\n}): Promise<{\n installationId?: string\n resolution: UndeployTargetResolution<WorkbenchUndeployTarget>\n}> {\n const config = workbench.config\n const appType = config?.appType ?? workbench.applicationType\n if (!appType) throw new Error('The app declares no app type to resolve its installation')\n if (!organizationId) {\n throw new Error(\n 'sanity.cli.ts does not contain an organization identifier (\"app.organizationId\"), which is required to resolve the installation',\n )\n }\n\n const installationId = await resolveInstallationId({appType, organizationId})\n if (!installationId) {\n return {\n resolution: {\n message: `No active \"${appType}\" installation for organization \"${organizationId}\"`,\n type: 'none',\n },\n }\n }\n\n const configs = await listConfigs(installationId)\n if (configs.length === 0) {\n return {\n installationId,\n resolution: {\n message: `No deployed config for the \"${appType}\" installation`,\n type: 'none',\n },\n }\n }\n\n return {\n installationId,\n resolution: {\n target: {\n configs,\n deletes: 'config',\n id: null,\n payload: {\n appId: null,\n ...(config ? {config: summarizeConfig(config)} : {}),\n type: 'coreApp',\n },\n summary: config ? [summarizeConfig(config)] : [],\n title: workbench.slug,\n type: 'coreApp',\n url: getWorkbenchUrl(organizationId),\n },\n type: 'found',\n },\n }\n}\n"],"names":["deleteApplication","getApplication","getApplicationUrl","getWorkbenchUrl","deleteConfig","listConfigs","resolveInstallationId","summarizeConfig","summarizeInterfaces","createWorkbenchUndeployAdapter","options","appId","organizationId","type","workbench","configOnly","isSingleton","hasInterfaces","installationId","resolveTarget","resolveApplicationTarget","resolved","resolveConfigTarget","resolution","undeploy","target","deletes","Error","snapshot","configs","id","message","solution","application","lines","services","views","payload","summary","undefined","title","url","config","appType","applicationType","length","slug"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/undeploy/workbenchUndeployAdapter.ts"],"sourcesContent":["import {\n type UndeployAdapter,\n type UndeployApplicationTarget,\n type UndeployConfigTarget,\n type UndeployTargetResolution,\n} from '@sanity/cli-core/undeploy'\n\nimport {type ResolvedMediaLibraryConfig} from '../../resolveWorkbenchConfig.js'\nimport {\n deleteApplication,\n getApplication,\n getApplicationUrl,\n getWorkbenchUrl,\n} from '../../services/applications.js'\nimport {type ConfigSnapshot, deleteConfig, listConfigs} from '../../services/installations.js'\nimport {resolveInstallationId, summarizeConfig} from '../deploy/deployConfig.js'\nimport {type DeployableWorkbenchApp} from '../deploy/getWorkbench.js'\nimport {\n type DeployedView,\n type DeployedWebWorker,\n summarizeInterfaces,\n} from '../deploy/summarizeInterfaces.js'\n\n/** The workbench extension of the shared target; serializes into `--json` as-is. */\nexport type WorkbenchUndeployTarget =\n | (UndeployApplicationTarget & {\n services: DeployedWebWorker[]\n views: DeployedView[]\n })\n | (UndeployConfigTarget & {\n configs: ConfigSnapshot[]\n })\n\n/**\n * The undeploy adapter for workbench apps and configs, mirroring what a\n * workbench deploy creates: an app that exposes interfaces deletes its Brett\n * application (the server soft-deletes its deployments and refuses singletons\n * with active installations); a config — the media library — deletes its\n * installation's config snapshots instead. The two are severed at the source,\n * so exactly one of `workbench`/`config` is supplied and the brand decides the path.\n */\nexport function createWorkbenchUndeployAdapter(options: {\n appId: string | undefined\n config?: ResolvedMediaLibraryConfig\n organizationId: string | undefined\n type: 'coreApp' | 'studio'\n workbench?: DeployableWorkbenchApp\n}): UndeployAdapter<WorkbenchUndeployTarget> {\n const {appId, config, organizationId, type, workbench} = options\n // Workbench-internal, so kept off the reported target; resolveTarget stashes it for the delete.\n let installationId: string | undefined\n\n return {\n resolveTarget: async () => {\n if (!config) {\n if (!workbench) throw new Error('No workbench app or config to undeploy')\n return resolveApplicationTarget({appId, type, workbench})\n }\n const resolved = await resolveConfigTarget({config, organizationId})\n installationId = resolved.installationId\n return resolved.resolution\n },\n type,\n async undeploy(target) {\n if (target.deletes === 'config') {\n if (!installationId) throw new Error('No installation resolved for the config undeploy')\n for (const snapshot of target.configs) {\n await deleteConfig(installationId, snapshot.id)\n }\n return\n }\n await deleteApplication(target.id)\n },\n }\n}\n\nasync function resolveApplicationTarget({\n appId,\n type,\n workbench,\n}: {\n appId: string | undefined\n type: 'coreApp' | 'studio'\n workbench: DeployableWorkbenchApp\n}): Promise<UndeployTargetResolution<WorkbenchUndeployTarget>> {\n if (!appId) {\n return {\n message: 'No `deployment.appId` configured',\n solution: 'Add `deployment.appId` to sanity.cli.ts',\n type: 'none',\n }\n }\n\n const application = await getApplication(appId)\n if (!application) {\n return {message: 'Application with the given ID does not exist', type: 'none'}\n }\n\n const {lines, services, views} = summarizeInterfaces(workbench)\n return {\n target: {\n application,\n deletes: 'application',\n id: application.id,\n payload: {appId: application.id, services, type, views},\n services,\n summary: lines,\n title: application.title,\n type,\n url: getApplicationUrl({...application, type}),\n views,\n },\n type: 'found',\n }\n}\n\nasync function resolveConfigTarget({\n config,\n organizationId,\n}: {\n config: ResolvedMediaLibraryConfig\n organizationId: string | undefined\n}): Promise<{\n installationId?: string\n resolution: UndeployTargetResolution<WorkbenchUndeployTarget>\n}> {\n const {appType} = config\n if (!organizationId) {\n throw new Error(\n 'sanity.cli.ts does not contain an organization identifier (\"app.organizationId\"), which is required to resolve the installation',\n )\n }\n\n const installationId = await resolveInstallationId({appType, organizationId})\n if (!installationId) {\n return {\n resolution: {\n message: `No active \"${appType}\" installation for organization \"${organizationId}\"`,\n type: 'none',\n },\n }\n }\n\n const configs = await listConfigs(installationId)\n if (configs.length === 0) {\n return {\n installationId,\n resolution: {\n message: `No deployed config for the \"${appType}\" installation`,\n type: 'none',\n },\n }\n }\n\n return {\n installationId,\n resolution: {\n target: {\n configs,\n deletes: 'config',\n id: null,\n payload: {\n appId: null,\n config: summarizeConfig(config),\n type: 'coreApp',\n },\n summary: [summarizeConfig(config)],\n title: appType,\n type: 'coreApp',\n url: getWorkbenchUrl(organizationId),\n },\n type: 'found',\n },\n }\n}\n"],"names":["deleteApplication","getApplication","getApplicationUrl","getWorkbenchUrl","deleteConfig","listConfigs","resolveInstallationId","summarizeConfig","summarizeInterfaces","createWorkbenchUndeployAdapter","options","appId","config","organizationId","type","workbench","installationId","resolveTarget","Error","resolveApplicationTarget","resolved","resolveConfigTarget","resolution","undeploy","target","deletes","snapshot","configs","id","message","solution","application","lines","services","views","payload","summary","title","url","appType","length"],"mappings":"AAQA,SACEA,iBAAiB,EACjBC,cAAc,EACdC,iBAAiB,EACjBC,eAAe,QACV,iCAAgC;AACvC,SAA6BC,YAAY,EAAEC,WAAW,QAAO,kCAAiC;AAC9F,SAAQC,qBAAqB,EAAEC,eAAe,QAAO,4BAA2B;AAEhF,SAGEC,mBAAmB,QACd,mCAAkC;AAYzC;;;;;;;CAOC,GACD,OAAO,SAASC,+BAA+BC,OAM9C;IACC,MAAM,EAACC,KAAK,EAAEC,MAAM,EAAEC,cAAc,EAAEC,IAAI,EAAEC,SAAS,EAAC,GAAGL;IACzD,gGAAgG;IAChG,IAAIM;IAEJ,OAAO;QACLC,eAAe;YACb,IAAI,CAACL,QAAQ;gBACX,IAAI,CAACG,WAAW,MAAM,IAAIG,MAAM;gBAChC,OAAOC,yBAAyB;oBAACR;oBAAOG;oBAAMC;gBAAS;YACzD;YACA,MAAMK,WAAW,MAAMC,oBAAoB;gBAACT;gBAAQC;YAAc;YAClEG,iBAAiBI,SAASJ,cAAc;YACxC,OAAOI,SAASE,UAAU;QAC5B;QACAR;QACA,MAAMS,UAASC,MAAM;YACnB,IAAIA,OAAOC,OAAO,KAAK,UAAU;gBAC/B,IAAI,CAACT,gBAAgB,MAAM,IAAIE,MAAM;gBACrC,KAAK,MAAMQ,YAAYF,OAAOG,OAAO,CAAE;oBACrC,MAAMvB,aAAaY,gBAAgBU,SAASE,EAAE;gBAChD;gBACA;YACF;YACA,MAAM5B,kBAAkBwB,OAAOI,EAAE;QACnC;IACF;AACF;AAEA,eAAeT,yBAAyB,EACtCR,KAAK,EACLG,IAAI,EACJC,SAAS,EAKV;IACC,IAAI,CAACJ,OAAO;QACV,OAAO;YACLkB,SAAS;YACTC,UAAU;YACVhB,MAAM;QACR;IACF;IAEA,MAAMiB,cAAc,MAAM9B,eAAeU;IACzC,IAAI,CAACoB,aAAa;QAChB,OAAO;YAACF,SAAS;YAAgDf,MAAM;QAAM;IAC/E;IAEA,MAAM,EAACkB,KAAK,EAAEC,QAAQ,EAAEC,KAAK,EAAC,GAAG1B,oBAAoBO;IACrD,OAAO;QACLS,QAAQ;YACNO;YACAN,SAAS;YACTG,IAAIG,YAAYH,EAAE;YAClBO,SAAS;gBAACxB,OAAOoB,YAAYH,EAAE;gBAAEK;gBAAUnB;gBAAMoB;YAAK;YACtDD;YACAG,SAASJ;YACTK,OAAON,YAAYM,KAAK;YACxBvB;YACAwB,KAAKpC,kBAAkB;gBAAC,GAAG6B,WAAW;gBAAEjB;YAAI;YAC5CoB;QACF;QACApB,MAAM;IACR;AACF;AAEA,eAAeO,oBAAoB,EACjCT,MAAM,EACNC,cAAc,EAIf;IAIC,MAAM,EAAC0B,OAAO,EAAC,GAAG3B;IAClB,IAAI,CAACC,gBAAgB;QACnB,MAAM,IAAIK,MACR;IAEJ;IAEA,MAAMF,iBAAiB,MAAMV,sBAAsB;QAACiC;QAAS1B;IAAc;IAC3E,IAAI,CAACG,gBAAgB;QACnB,OAAO;YACLM,YAAY;gBACVO,SAAS,CAAC,WAAW,EAAEU,QAAQ,iCAAiC,EAAE1B,eAAe,CAAC,CAAC;gBACnFC,MAAM;YACR;QACF;IACF;IAEA,MAAMa,UAAU,MAAMtB,YAAYW;IAClC,IAAIW,QAAQa,MAAM,KAAK,GAAG;QACxB,OAAO;YACLxB;YACAM,YAAY;gBACVO,SAAS,CAAC,4BAA4B,EAAEU,QAAQ,cAAc,CAAC;gBAC/DzB,MAAM;YACR;QACF;IACF;IAEA,OAAO;QACLE;QACAM,YAAY;YACVE,QAAQ;gBACNG;gBACAF,SAAS;gBACTG,IAAI;gBACJO,SAAS;oBACPxB,OAAO;oBACPC,QAAQL,gBAAgBK;oBACxBE,MAAM;gBACR;gBACAsB,SAAS;oBAAC7B,gBAAgBK;iBAAQ;gBAClCyB,OAAOE;gBACPzB,MAAM;gBACNwB,KAAKnC,gBAAgBU;YACvB;YACAC,MAAM;QACR;IACF;AACF"}
package/dist/appId.js CHANGED
@@ -9,18 +9,22 @@
9
9
  * advertised by `sanity start` resolve to the same id. `sanity deploy` resolves
10
10
  * its own id from the applications API.
11
11
  */ export async function buildAppId(app) {
12
- const canonical = (interfaces)=>(interfaces ?? []).map((i)=>[
13
- i.type,
14
- i.name,
15
- i.src
16
- ]).toSorted();
17
12
  const shape = JSON.stringify({
18
- config: app.config ?? null,
19
13
  entry: app.entry ?? null,
14
+ // Identity, not address: the id keys on `name` (which defaults to `slug`). The
15
+ // address (`slug`) is deliberately absent, so renaming it never shifts the id.
16
+ name: app.name,
20
17
  organizationId: app.organizationId,
21
- services: canonical(app.services),
22
- slug: app.slug,
23
- views: canonical(app.views)
18
+ services: app.webWorkers.map((webWorker)=>[
19
+ webWorker.type,
20
+ webWorker.name,
21
+ webWorker.src
22
+ ]).toSorted(),
23
+ views: app.views.map((view)=>[
24
+ view.surface,
25
+ view.name,
26
+ view.src
27
+ ]).toSorted()
24
28
  });
25
29
  // eslint-disable-next-line n/no-unsupported-features/node-builtins -- the Web Crypto global is available on our Node target and in the browser
26
30
  const digest = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(shape));
package/dist/appId.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/appId.ts"],"sourcesContent":["import {type ResolvedWorkbenchApp} from './resolveWorkbenchApp.js'\n\n/**\n * File the build writes into its output, carrying the id compiled into the\n * bundle. `sanity start` serves a build without recompiling, so it reads this\n * instead of recomputing — a deploy inlines the API id, not the shape hash.\n */\nexport const SANITY_APP_ID_FILE = 'sanity-app-id.txt'\n\n/**\n * The `build`/`start` id — a hash of the app's declared shape (its identity, not\n * its code), so the bundle inlined by `sanity build` and the registry entry\n * advertised by `sanity start` resolve to the same id. `sanity deploy` resolves\n * its own id from the applications API.\n */\nexport async function buildAppId(app: ResolvedWorkbenchApp): Promise<string> {\n const canonical = (\n interfaces: ReadonlyArray<{name: string; src: string; type: string}> | undefined,\n ): Array<[string, string, string]> =>\n (interfaces ?? []).map((i): [string, string, string] => [i.type, i.name, i.src]).toSorted()\n const shape = JSON.stringify({\n config: app.config ?? null,\n entry: app.entry ?? null,\n organizationId: app.organizationId,\n services: canonical(app.services),\n slug: app.slug,\n views: canonical(app.views),\n })\n // eslint-disable-next-line n/no-unsupported-features/node-builtins -- the Web Crypto global is available on our Node target and in the browser\n const digest = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(shape))\n return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, '0')).join('')\n}\n"],"names":["SANITY_APP_ID_FILE","buildAppId","app","canonical","interfaces","map","i","type","name","src","toSorted","shape","JSON","stringify","config","entry","organizationId","services","slug","views","digest","globalThis","crypto","subtle","TextEncoder","encode","Array","from","Uint8Array","byte","toString","padStart","join"],"mappings":"AAEA;;;;CAIC,GACD,OAAO,MAAMA,qBAAqB,oBAAmB;AAErD;;;;;CAKC,GACD,OAAO,eAAeC,WAAWC,GAAyB;IACxD,MAAMC,YAAY,CAChBC,aAEA,AAACA,CAAAA,cAAc,EAAE,AAAD,EAAGC,GAAG,CAAC,CAACC,IAAgC;gBAACA,EAAEC,IAAI;gBAAED,EAAEE,IAAI;gBAAEF,EAAEG,GAAG;aAAC,EAAEC,QAAQ;IAC3F,MAAMC,QAAQC,KAAKC,SAAS,CAAC;QAC3BC,QAAQZ,IAAIY,MAAM,IAAI;QACtBC,OAAOb,IAAIa,KAAK,IAAI;QACpBC,gBAAgBd,IAAIc,cAAc;QAClCC,UAAUd,UAAUD,IAAIe,QAAQ;QAChCC,MAAMhB,IAAIgB,IAAI;QACdC,OAAOhB,UAAUD,IAAIiB,KAAK;IAC5B;IACA,+IAA+I;IAC/I,MAAMC,SAAS,MAAMC,WAAWC,MAAM,CAACC,MAAM,CAACH,MAAM,CAAC,WAAW,IAAII,cAAcC,MAAM,CAACd;IACzF,OAAOe,MAAMC,IAAI,CAAC,IAAIC,WAAWR,SAAS,CAACS,OAASA,KAAKC,QAAQ,CAAC,IAAIC,QAAQ,CAAC,GAAG,MAAMC,IAAI,CAAC;AAC/F"}
1
+ {"version":3,"sources":["../src/appId.ts"],"sourcesContent":["import {type ResolvedWorkbenchApp} from './resolveWorkbenchApp.js'\n\n/**\n * File the build writes into its output, carrying the id compiled into the\n * bundle. `sanity start` serves a build without recompiling, so it reads this\n * instead of recomputing — a deploy inlines the API id, not the shape hash.\n */\nexport const SANITY_APP_ID_FILE = 'sanity-app-id.txt'\n\n/**\n * The `build`/`start` id — a hash of the app's declared shape (its identity, not\n * its code), so the bundle inlined by `sanity build` and the registry entry\n * advertised by `sanity start` resolve to the same id. `sanity deploy` resolves\n * its own id from the applications API.\n */\nexport async function buildAppId(app: ResolvedWorkbenchApp): Promise<string> {\n const shape = JSON.stringify({\n entry: app.entry ?? null,\n // Identity, not address: the id keys on `name` (which defaults to `slug`). The\n // address (`slug`) is deliberately absent, so renaming it never shifts the id.\n name: app.name,\n organizationId: app.organizationId,\n services: app.webWorkers\n .map((webWorker): [string, string, string] => [webWorker.type, webWorker.name, webWorker.src])\n .toSorted(),\n views: app.views\n .map((view): [string, string, string] => [view.surface, view.name, view.src])\n .toSorted(),\n })\n // eslint-disable-next-line n/no-unsupported-features/node-builtins -- the Web Crypto global is available on our Node target and in the browser\n const digest = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(shape))\n return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, '0')).join('')\n}\n"],"names":["SANITY_APP_ID_FILE","buildAppId","app","shape","JSON","stringify","entry","name","organizationId","services","webWorkers","map","webWorker","type","src","toSorted","views","view","surface","digest","globalThis","crypto","subtle","TextEncoder","encode","Array","from","Uint8Array","byte","toString","padStart","join"],"mappings":"AAEA;;;;CAIC,GACD,OAAO,MAAMA,qBAAqB,oBAAmB;AAErD;;;;;CAKC,GACD,OAAO,eAAeC,WAAWC,GAAyB;IACxD,MAAMC,QAAQC,KAAKC,SAAS,CAAC;QAC3BC,OAAOJ,IAAII,KAAK,IAAI;QACpB,+EAA+E;QAC/E,+EAA+E;QAC/EC,MAAML,IAAIK,IAAI;QACdC,gBAAgBN,IAAIM,cAAc;QAClCC,UAAUP,IAAIQ,UAAU,CACrBC,GAAG,CAAC,CAACC,YAAwC;gBAACA,UAAUC,IAAI;gBAAED,UAAUL,IAAI;gBAAEK,UAAUE,GAAG;aAAC,EAC5FC,QAAQ;QACXC,OAAOd,IAAIc,KAAK,CACbL,GAAG,CAAC,CAACM,OAAmC;gBAACA,KAAKC,OAAO;gBAAED,KAAKV,IAAI;gBAAEU,KAAKH,GAAG;aAAC,EAC3EC,QAAQ;IACb;IACA,+IAA+I;IAC/I,MAAMI,SAAS,MAAMC,WAAWC,MAAM,CAACC,MAAM,CAACH,MAAM,CAAC,WAAW,IAAII,cAAcC,MAAM,CAACrB;IACzF,OAAOsB,MAAMC,IAAI,CAAC,IAAIC,WAAWR,SAAS,CAACS,OAASA,KAAKC,QAAQ,CAAC,IAAIC,QAAQ,CAAC,GAAG,MAAMC,IAAI,CAAC;AAC/F"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The qualified, globally-unique handle for an application: `sanity/<name>` for
3
+ * singletons (always Sanity-owned), `<organizationId>/<name>` otherwise. Mirrors
4
+ * brett's server-side composition so a locally served app carries the same
5
+ * reference the deployed one would — the CLI is the authority for local apps,
6
+ * which never reach brett.
7
+ * @public
8
+ */ export function applicationReference(app) {
9
+ return `${app.isSingleton ? 'sanity' : app.organizationId}/${app.name}`;
10
+ }
11
+
12
+ //# sourceMappingURL=applicationReference.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/applicationReference.ts"],"sourcesContent":["/**\n * The qualified, globally-unique handle for an application: `sanity/<name>` for\n * singletons (always Sanity-owned), `<organizationId>/<name>` otherwise. Mirrors\n * brett's server-side composition so a locally served app carries the same\n * reference the deployed one would — the CLI is the authority for local apps,\n * which never reach brett.\n * @public\n */\nexport function applicationReference(app: {\n isSingleton: boolean\n name: string\n organizationId: string\n}): string {\n return `${app.isSingleton ? 'sanity' : app.organizationId}/${app.name}`\n}\n"],"names":["applicationReference","app","isSingleton","organizationId","name"],"mappings":"AAAA;;;;;;;CAOC,GACD,OAAO,SAASA,qBAAqBC,GAIpC;IACC,OAAO,GAAGA,IAAIC,WAAW,GAAG,WAAWD,IAAIE,cAAc,CAAC,CAAC,EAAEF,IAAIG,IAAI,EAAE;AACzE"}
package/dist/contract.js CHANGED
@@ -5,11 +5,8 @@ import { z } from 'zod/mini';
5
5
  /** @internal */ export const VIEW_CONTRACT_VERSION = 1;
6
6
  /** @internal */ export const SERVICE_CONTRACT_VERSION = 1;
7
7
  /** @internal */ export const MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION = 1;
8
- /**
9
- * Component slots each interface type exposes, in render order. Source of truth
10
- * for {@link InterfaceType} and the build; add a type by registering it here.
11
- * @internal
12
- */ export const VIEW_COMPONENTS = {
8
+ /** Component slots per view surface; windows load outside the component artifact path. @internal */ export const VIEW_COMPONENTS = {
9
+ app: [],
13
10
  asset_source: [
14
11
  'asset_source'
15
12
  ],
@@ -32,22 +29,25 @@ import { z } from 'zod/mini';
32
29
  'large',
33
30
  'banner'
34
31
  ]);
35
- /**
36
- * The `app` interface's dock-placement metadata. Interface metadata is
37
- * discriminated on `type`; `app` is the only type with a shape so far.
38
- * @internal
39
- */ export const AppInterfaceMetadataSchema = z.object({
40
- group: z.optional(z.string()),
41
- priority: z.optional(z.number())
32
+ const DockGroupSchema = z.enum([
33
+ 'dock.system',
34
+ 'dock.applications',
35
+ 'dock.user'
36
+ ]);
37
+ /** @internal */ export const DockSchema = z.object({
38
+ group: z.optional(DockGroupSchema),
39
+ order: z.optional(z.number())
40
+ });
41
+ /** @internal */ export const ViewPlacementMetadataSchema = z.object({
42
+ dock: DockSchema
42
43
  });
43
44
  /**
44
- * A tile's interface metadata: its footprint `size` and an optional `priority`
45
+ * A tile's interface metadata: its footprint `size` and an optional `order`
45
46
  * the dashboard sorts on, ascending. Both are authored as top-level view fields
46
47
  * (see {@link InterfaceDeclarationSchema}) but stored on the record as metadata.
47
- * Mirrors `app`'s dock metadata; tile is the first view type to carry any.
48
48
  * @internal
49
49
  */ export const TileInterfaceMetadataSchema = z.object({
50
- priority: z.optional(z.number()),
50
+ order: z.optional(z.number()),
51
51
  size: TileSizeSchema
52
52
  });
53
53
  /**
@@ -61,16 +61,16 @@ import { z } from 'zod/mini';
61
61
  tile: VIEW_CONTRACT_VERSION,
62
62
  worker: SERVICE_CONTRACT_VERSION
63
63
  };
64
- /** @internal */ export function interfaceContractVersion(type) {
65
- const version = INTERFACE_CONTRACT_VERSIONS[type];
64
+ /** @internal */ export function interfaceContractVersion(kind) {
65
+ const version = INTERFACE_CONTRACT_VERSIONS[kind];
66
66
  return version === undefined ? undefined : String(version);
67
67
  }
68
68
  /**
69
69
  * The module-federation id a build exposes an interface at. Dev stamps the same
70
70
  * id a deploy would, so the workbench loads a local interface like a deployed one.
71
71
  * @internal
72
- */ export function interfaceModuleId(type, name) {
73
- switch(type){
72
+ */ export function interfaceModuleId(kind, name) {
73
+ switch(kind){
74
74
  case 'app':
75
75
  {
76
76
  return 'App';
@@ -87,7 +87,7 @@ import { z } from 'zod/mini';
87
87
  }
88
88
  default:
89
89
  {
90
- throw new Error(`Cannot derive a moduleId for unknown interface type: ${type}`);
90
+ throw new Error(`Cannot derive a moduleId for unknown interface kind: ${kind}`);
91
91
  }
92
92
  }
93
93
  }
@@ -99,7 +99,7 @@ function extensionDeclarationFields(kind) {
99
99
  src: z.string()
100
100
  };
101
101
  }
102
- // Every interface (view, service) shares `name` + `src` + a display `title`,
102
+ // Every interface shares `name` + `src` + a display `title`,
103
103
  // which Brett requires on the record each becomes.
104
104
  function interfaceDeclarationFields(kind) {
105
105
  return {
@@ -108,30 +108,67 @@ function interfaceDeclarationFields(kind) {
108
108
  };
109
109
  }
110
110
  const PanelViewSchema = z.object({
111
- type: z.literal('panel'),
112
- ...interfaceDeclarationFields('View')
111
+ surface: z.literal('panel'),
112
+ ...interfaceDeclarationFields('View'),
113
+ dock: z.optional(DockSchema)
114
+ });
115
+ /** @public */ export function definePanelView(view) {
116
+ return {
117
+ ...view,
118
+ surface: 'panel'
119
+ };
120
+ }
121
+ const WindowViewSchema = z.object({
122
+ surface: z.literal('app'),
123
+ ...interfaceDeclarationFields('View'),
124
+ dock: z.optional(DockSchema)
113
125
  });
126
+ /** @public */ export function defineWindowView(view) {
127
+ return {
128
+ ...view,
129
+ surface: 'app'
130
+ };
131
+ }
114
132
  const AssetSourceViewSchema = z.object({
115
- type: z.literal('asset_source'),
133
+ surface: z.literal('asset_source'),
116
134
  ...interfaceDeclarationFields('View')
117
135
  });
136
+ /** @public */ export function defineAssetSourceView(view) {
137
+ return {
138
+ ...view,
139
+ surface: 'asset_source'
140
+ };
141
+ }
118
142
  const TileViewSchema = z.object({
119
- type: z.literal('tile'),
143
+ surface: z.literal('tile'),
120
144
  ...interfaceDeclarationFields('View'),
121
- /** Sort position within its layout track, ascending. Optional. */ priority: z.optional(z.number()),
145
+ /** Sort position within its layout track, ascending. Optional. */ order: z.optional(z.number()),
122
146
  /** Footprint family the dashboard lays the tile out by. */ size: TileSizeSchema
123
147
  });
124
- /** @internal */ export const InterfaceDeclarationSchema = z.discriminatedUnion('type', [
148
+ /** @public */ export function defineTileView(view) {
149
+ return {
150
+ ...view,
151
+ surface: 'tile'
152
+ };
153
+ }
154
+ /** @internal */ export const InterfaceDeclarationSchema = z.discriminatedUnion('surface', [
155
+ WindowViewSchema,
125
156
  PanelViewSchema,
126
157
  AssetSourceViewSchema,
127
158
  TileViewSchema
128
159
  ]);
129
- const WorkerServiceSchema = z.object({
160
+ const WebWorkerSchema = z.object({
130
161
  type: z.literal('worker'),
131
- ...interfaceDeclarationFields('Service')
162
+ ...interfaceDeclarationFields('Web worker')
132
163
  });
164
+ /** @public */ export function defineWebWorker(webWorker) {
165
+ return {
166
+ ...webWorker,
167
+ type: 'worker'
168
+ };
169
+ }
133
170
  /** @internal */ export const ServiceDeclarationSchema = z.discriminatedUnion('type', [
134
- WorkerServiceSchema
171
+ WebWorkerSchema
135
172
  ]);
136
173
  const MediaLibraryFieldSchema = z.object({
137
174
  ...extensionDeclarationFields('Field'),
@@ -148,7 +185,8 @@ const MediaLibraryConfigSchema = z.object({
148
185
  fields: z.array(MediaLibraryFieldSchema).check(z.refine((fields)=>new Set(fields.map((field)=>field.name)).size === fields.length, 'Field `name` must be unique within a media library'))
149
186
  });
150
187
  /**
151
- * An app's optional config, keyed by `appType`; deploys as a versioned snapshot, not an interface.
188
+ * A workbench config's built value, keyed by `appType`; deploys as a versioned
189
+ * snapshot, not an interface.
152
190
  * @internal
153
191
  */ export const ConfigSchema = z.discriminatedUnion('appType', [
154
192
  MediaLibraryConfigSchema
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/contract.ts"],"sourcesContent":["import {z} from 'zod/mini'\n\n// Shared module-federation extension contract: interface (view/service) and\n// config declaration schemas, plus the versions the build stamps.\n// `zod/mini` keeps the bundle small.\n\n/** @internal */\nexport const VIEW_CONTRACT_VERSION = 1\n\n/** @internal */\nexport const SERVICE_CONTRACT_VERSION = 1\n\n/** @internal */\nexport const MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION = 1\n\n/**\n * A view component. The return is opaque so the runtime helpers carry no React\n * dependency — the generated artifact renders it with the app's own React.\n * @public\n */\nexport type ViewComponent<TProps> = (props: TProps) => unknown\n\n/** @public */\nexport interface ViewComponentBaseProps<TView> {\n view: TView\n}\n\n/**\n * Component slots each interface type exposes, in render order. Source of truth\n * for {@link InterfaceType} and the build; add a type by registering it here.\n * @internal\n */\nexport const VIEW_COMPONENTS = {\n asset_source: ['asset_source'],\n panel: ['title', 'panel'],\n tile: ['tile'],\n} as const satisfies Record<string, readonly string[]>\n\n/** @public */\nexport type InterfaceType = keyof typeof VIEW_COMPONENTS\n\n/**\n * A tile's footprint family — the shape it occupies on the dashboard. Modelled\n * on iOS WidgetKit families, not a linear scale: `banner` is full-width and\n * shallow, which a `small`→`large` magnitude can't express. The host maps a\n * family to a layout slot; the component reads it to render per footprint.\n * @public\n */\nexport const TileSizeSchema = z.enum(['small', 'large', 'banner'])\n\n/** @public */\nexport type TileSize = z.infer<typeof TileSizeSchema>\n\n/** @public */\nexport type ServiceType = 'worker'\n\n/**\n * The `app` interface's dock-placement metadata. Interface metadata is\n * discriminated on `type`; `app` is the only type with a shape so far.\n * @internal\n */\nexport const AppInterfaceMetadataSchema = z.object({\n group: z.optional(z.string()),\n priority: z.optional(z.number()),\n})\n\n/** @internal */\nexport type AppInterfaceMetadata = z.infer<typeof AppInterfaceMetadataSchema>\n\n/**\n * A tile's interface metadata: its footprint `size` and an optional `priority`\n * the dashboard sorts on, ascending. Both are authored as top-level view fields\n * (see {@link InterfaceDeclarationSchema}) but stored on the record as metadata.\n * Mirrors `app`'s dock metadata; tile is the first view type to carry any.\n * @internal\n */\nexport const TileInterfaceMetadataSchema = z.object({\n priority: z.optional(z.number()),\n size: TileSizeSchema,\n})\n\n/** @internal */\nexport type TileInterfaceMetadata = z.infer<typeof TileInterfaceMetadataSchema>\n\n/**\n * The contract version each interface type advertises, so the host can check it\n * renders/runs what it expects.\n * @internal\n */\nconst INTERFACE_CONTRACT_VERSIONS = {\n app: undefined,\n asset_source: VIEW_CONTRACT_VERSION,\n panel: VIEW_CONTRACT_VERSION,\n tile: VIEW_CONTRACT_VERSION,\n worker: SERVICE_CONTRACT_VERSION,\n} as const\n\n/** Every interface type an app exposes — an `app` view, a view, or a service. */\nexport type InterfaceKind = keyof typeof INTERFACE_CONTRACT_VERSIONS\n\n/** @internal */\nexport function interfaceContractVersion(type: InterfaceKind): string | undefined {\n const version = INTERFACE_CONTRACT_VERSIONS[type]\n return version === undefined ? undefined : String(version)\n}\n\n/**\n * The module-federation id a build exposes an interface at. Dev stamps the same\n * id a deploy would, so the workbench loads a local interface like a deployed one.\n * @internal\n */\nexport function interfaceModuleId(type: string, name: string): string {\n switch (type) {\n case 'app': {\n return 'App'\n }\n case 'asset_source':\n case 'panel':\n case 'tile': {\n return `views/${name}`\n }\n case 'worker': {\n return `services/${name}`\n }\n default: {\n throw new Error(`Cannot derive a moduleId for unknown interface type: ${type}`)\n }\n }\n}\n\n// Shared `name` + `src`; `kind` only tailors the validation message.\nfunction extensionDeclarationFields(kind: 'Field' | 'Service' | 'View') {\n const pattern = /^[a-zA-Z0-9_-]+$/\n return {\n name: z.string().check(z.regex(pattern, `${kind} \\`name\\` must match ${pattern}`)),\n src: z.string(),\n }\n}\n\n// Every interface (view, service) shares `name` + `src` + a display `title`,\n// which Brett requires on the record each becomes.\nfunction interfaceDeclarationFields(kind: 'Service' | 'View') {\n return {\n ...extensionDeclarationFields(kind),\n title: z.string(`${kind} \\`title\\` is required`),\n }\n}\n\nconst PanelViewSchema = z.object({\n type: z.literal('panel'),\n ...interfaceDeclarationFields('View'),\n})\n\nconst AssetSourceViewSchema = z.object({\n type: z.literal('asset_source'),\n ...interfaceDeclarationFields('View'),\n})\n\nconst TileViewSchema = z.object({\n type: z.literal('tile'),\n ...interfaceDeclarationFields('View'),\n /** Sort position within its layout track, ascending. Optional. */\n priority: z.optional(z.number()),\n /** Footprint family the dashboard lays the tile out by. */\n size: TileSizeSchema,\n})\n\n/** @internal */\nexport const InterfaceDeclarationSchema = z.discriminatedUnion('type', [\n PanelViewSchema,\n AssetSourceViewSchema,\n TileViewSchema,\n])\n\nconst WorkerServiceSchema = z.object({\n type: z.literal('worker'),\n ...interfaceDeclarationFields('Service'),\n})\n\n/** @internal */\nexport const ServiceDeclarationSchema = z.discriminatedUnion('type', [WorkerServiceSchema])\n\nconst MediaLibraryFieldSchema = z.object({\n ...extensionDeclarationFields('Field'),\n public: z.optional(z.boolean()),\n title: z.string(),\n})\n\n/**\n * Stamped where the config crosses a boundary so the authoring model doesn't carry a constant discriminator.\n * @internal\n */\nexport const INSTALLATION_CONFIG_TYPE = 'installation_config'\n\n// `appType` is stamped by `unstable_defineMediaLibrary`, never authored.\nconst MediaLibraryConfigSchema = z.object({\n appType: z.literal('media-library'),\n fields: z\n .array(MediaLibraryFieldSchema)\n .check(\n z.refine(\n (fields) => new Set(fields.map((field) => field.name)).size === fields.length,\n 'Field `name` must be unique within a media library',\n ),\n ),\n})\n\n/**\n * An app's optional config, keyed by `appType`; deploys as a versioned snapshot, not an interface.\n * @internal\n */\nexport const ConfigSchema = z.discriminatedUnion('appType', [MediaLibraryConfigSchema])\n"],"names":["z","VIEW_CONTRACT_VERSION","SERVICE_CONTRACT_VERSION","MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION","VIEW_COMPONENTS","asset_source","panel","tile","TileSizeSchema","enum","AppInterfaceMetadataSchema","object","group","optional","string","priority","number","TileInterfaceMetadataSchema","size","INTERFACE_CONTRACT_VERSIONS","app","undefined","worker","interfaceContractVersion","type","version","String","interfaceModuleId","name","Error","extensionDeclarationFields","kind","pattern","check","regex","src","interfaceDeclarationFields","title","PanelViewSchema","literal","AssetSourceViewSchema","TileViewSchema","InterfaceDeclarationSchema","discriminatedUnion","WorkerServiceSchema","ServiceDeclarationSchema","MediaLibraryFieldSchema","public","boolean","INSTALLATION_CONFIG_TYPE","MediaLibraryConfigSchema","appType","fields","array","refine","Set","map","field","length","ConfigSchema"],"mappings":"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"}
1
+ {"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/** Component slots per view surface; windows load outside the component artifact path. @internal */\nexport const VIEW_COMPONENTS = {\n app: [],\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 ViewSurface = 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\nconst DockGroupSchema = z.enum(['dock.system', 'dock.applications', 'dock.user'])\n\n/** @public */\nexport type DockGroup = z.output<typeof DockGroupSchema>\n\n/** @internal */\nexport const DockSchema = z.object({\n group: z.optional(DockGroupSchema),\n order: z.optional(z.number()),\n})\n\n/** @internal */\nexport type Dock = z.infer<typeof DockSchema>\n\n/** @internal */\nexport const ViewPlacementMetadataSchema = z.object({dock: DockSchema})\n\n/** @internal */\nexport type ViewPlacementMetadata = z.infer<typeof ViewPlacementMetadataSchema>\n\n/**\n * A tile's interface metadata: its footprint `size` and an optional `order`\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 * @internal\n */\nexport const TileInterfaceMetadataSchema = z.object({\n order: 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(kind: InterfaceKind): string | undefined {\n const version = INTERFACE_CONTRACT_VERSIONS[kind]\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(kind: string, name: string): string {\n switch (kind) {\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 kind: ${kind}`)\n }\n }\n}\n\n// Shared `name` + `src`; `kind` only tailors the validation message.\nfunction extensionDeclarationFields(kind: 'Field' | 'View' | 'Web worker') {\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 shares `name` + `src` + a display `title`,\n// which Brett requires on the record each becomes.\nfunction interfaceDeclarationFields(kind: 'View' | 'Web worker') {\n return {\n ...extensionDeclarationFields(kind),\n title: z.string(`${kind} \\`title\\` is required`),\n }\n}\n\nconst PanelViewSchema = z.object({\n surface: z.literal('panel'),\n ...interfaceDeclarationFields('View'),\n dock: z.optional(DockSchema),\n})\n\n/** @public */\nexport type PanelView = z.output<typeof PanelViewSchema>\n\n/** @public */\nexport type DefinePanelViewInput = Omit<PanelView, 'surface'>\n\n/** @public */\nexport function definePanelView(view: DefinePanelViewInput): PanelView {\n return {...view, surface: 'panel'}\n}\n\nconst WindowViewSchema = z.object({\n surface: z.literal('app'),\n ...interfaceDeclarationFields('View'),\n dock: z.optional(DockSchema),\n})\n\n/** @public */\nexport type WindowView = z.output<typeof WindowViewSchema>\n\n/** @public */\nexport type DefineWindowViewInput = Omit<WindowView, 'surface'>\n\n/** @public */\nexport function defineWindowView(view: DefineWindowViewInput): WindowView {\n return {...view, surface: 'app'}\n}\n\nconst AssetSourceViewSchema = z.object({\n surface: z.literal('asset_source'),\n ...interfaceDeclarationFields('View'),\n})\n\n/** @public */\nexport type AssetSourceView = z.output<typeof AssetSourceViewSchema>\n\n/** @public */\nexport type DefineAssetSourceViewInput = Omit<AssetSourceView, 'surface'>\n\n/** @public */\nexport function defineAssetSourceView(view: DefineAssetSourceViewInput): AssetSourceView {\n return {...view, surface: 'asset_source'}\n}\n\nconst TileViewSchema = z.object({\n surface: z.literal('tile'),\n ...interfaceDeclarationFields('View'),\n /** Sort position within its layout track, ascending. Optional. */\n order: z.optional(z.number()),\n /** Footprint family the dashboard lays the tile out by. */\n size: TileSizeSchema,\n})\n\n/** @public */\nexport type TileView = z.output<typeof TileViewSchema>\n\n/** @public */\nexport type DefineTileViewInput = Omit<TileView, 'surface'>\n\n/** @public */\nexport function defineTileView(view: DefineTileViewInput): TileView {\n return {...view, surface: 'tile'}\n}\n\n/** @internal */\nexport const InterfaceDeclarationSchema = z.discriminatedUnion('surface', [\n WindowViewSchema,\n PanelViewSchema,\n AssetSourceViewSchema,\n TileViewSchema,\n])\n\n/** @public */\nexport type ViewDeclaration = z.output<typeof InterfaceDeclarationSchema>\n\nconst WebWorkerSchema = z.object({\n type: z.literal('worker'),\n ...interfaceDeclarationFields('Web worker'),\n})\n\n/** @public */\nexport type WebWorker = z.output<typeof WebWorkerSchema>\n\n/** @public */\nexport type DefineWebWorkerInput = Omit<WebWorker, 'type'>\n\n/** @public */\nexport function defineWebWorker(webWorker: DefineWebWorkerInput): WebWorker {\n return {...webWorker, type: 'worker'}\n}\n\n/** @internal */\nexport const ServiceDeclarationSchema = z.discriminatedUnion('type', [WebWorkerSchema])\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 * A workbench config's built value, keyed by `appType`; deploys as a versioned\n * snapshot, not an interface.\n * @internal\n */\nexport const ConfigSchema = z.discriminatedUnion('appType', [MediaLibraryConfigSchema])\n\n/**\n * The `{appType, fields}` config value the build expands into a federation\n * remote and the deploy summarizes.\n * @internal\n */\nexport type WorkbenchConfigValue = z.output<typeof ConfigSchema>\n"],"names":["z","VIEW_CONTRACT_VERSION","SERVICE_CONTRACT_VERSION","MEDIA_LIBRARY_CONFIG_CONTRACT_VERSION","VIEW_COMPONENTS","app","asset_source","panel","tile","TileSizeSchema","enum","DockGroupSchema","DockSchema","object","group","optional","order","number","ViewPlacementMetadataSchema","dock","TileInterfaceMetadataSchema","size","INTERFACE_CONTRACT_VERSIONS","undefined","worker","interfaceContractVersion","kind","version","String","interfaceModuleId","name","Error","extensionDeclarationFields","pattern","string","check","regex","src","interfaceDeclarationFields","title","PanelViewSchema","surface","literal","definePanelView","view","WindowViewSchema","defineWindowView","AssetSourceViewSchema","defineAssetSourceView","TileViewSchema","defineTileView","InterfaceDeclarationSchema","discriminatedUnion","WebWorkerSchema","type","defineWebWorker","webWorker","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
2
  import { APP_SLUG_PATTERN } from './appSlug.js';
3
- import { ConfigSchema, InterfaceDeclarationSchema, ServiceDeclarationSchema } from './contract.js';
3
+ import { DockSchema, InterfaceDeclarationSchema, ServiceDeclarationSchema } from './contract.js';
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
@@ -17,16 +17,10 @@ import { ConfigSchema, InterfaceDeclarationSchema, ServiceDeclarationSchema } fr
17
17
  'coreApp',
18
18
  'studio',
19
19
  'canvas',
20
- 'dashboard',
21
- 'media-library'
22
- ]);
23
- /** Dock groups an app can place itself into. */ const DockGroupSchema = z.enum([
24
- 'dock.system',
25
- 'dock.applications',
26
- 'dock.user'
20
+ 'dashboard'
27
21
  ]);
28
22
  /**
29
- * Runtime-validation schema for `unstable_defineApp`.
23
+ * Runtime-validation schema for `defineApplication`.
30
24
  * @internal
31
25
  */ export const DefineAppInputSchema = z.object({
32
26
  /**
@@ -34,31 +28,34 @@ import { ConfigSchema, InterfaceDeclarationSchema, ServiceDeclarationSchema } fr
34
28
  * from the public `DefineAppInput` type.
35
29
  * @internal
36
30
  */ applicationType: z.optional(ApplicationType),
37
- /**
38
- * Deployed as a versioned snapshot on the app's org installation, not the
39
- * application service. Singletons only. Internal, so excluded from the public
40
- * `DefineAppInput` and set via `@ts-expect-error` like `applicationType`.
41
- * @internal
42
- */ config: z.optional(ConfigSchema),
31
+ /** Default placement inherited by panel and window views. */ dock: z.optional(DockSchema),
43
32
  /**
44
33
  * App entrypoint module. Defaults to `./src/App.tsx` when omitted. The build
45
34
  * derives the app's navigable `app` view from it. SDK apps only — setting it
46
35
  * on a studio is rejected (studio app views are not yet implemented).
47
36
  */ entry: z.optional(z.string("must be a path to the app's entry file")),
48
- /** Dock group to render in. Defaults to `dock.applications` when omitted. */ group: z.optional(DockGroupSchema),
49
37
  /** Optional icon override (path to an SVG). Wins over manifest/studio icon. */ icon: z.optional(z.string()),
50
38
  /**
51
- * Sanity-owned app deployed once, installed per org; excluded from the public `DefineAppInput`.
52
- * @internal
53
- */ isSingleton: z.optional(z.boolean()),
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`"),
55
- /** Sort position within the group, ascending. Defaults to `100` when omitted. */ priority: z.optional(z.number()),
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'))),
39
+ * Stable identity, distinct from the `slug` address. Defaults to `slug` when
40
+ * omitted. Identity keys — the build id and derived interface ids — are built
41
+ * from this, so renaming the slug no longer changes identity.
42
+ */ name: z.optional(z.string().check(z.regex(APP_SLUG_PATTERN, 'App `name` must be lowercase alphanumerics and hyphens, starting with a letter and ending with an alphanumeric'))),
43
+ /** 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 `defineApplication`"),
57
44
  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')),
58
45
  /** User-facing app title. Wins over studio.config.ts title on merge. */ title: z.string(),
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'))),
60
- /** Dashboard visibility of the app. Defaults to `default` when omitted. */ visibility: z.optional(z.enum(APP_VISIBILITIES))
61
- }).check(// Studio app views are not implemented yet. A studio that declares `entry`
46
+ /** Views the app exposes (e.g. a window, dock panels, and tiles). */ views: z.optional(z.array(InterfaceDeclarationSchema, 'must be an array of views')),
47
+ /** Dashboard visibility of the app. Defaults to `default` when omitted. */ visibility: z.optional(z.enum(APP_VISIBILITIES)),
48
+ /** Background web workers the app runs. */ webWorkers: z.optional(z.array(ServiceDeclarationSchema, 'must be an array of web workers'))
49
+ }).check(z.refine((input)=>{
50
+ const names = [
51
+ ...input.entry === undefined ? [] : [
52
+ input.name ?? input.slug
53
+ ],
54
+ ...(input.views ?? []).map((view)=>view.name),
55
+ ...(input.webWorkers ?? []).map((webWorker)=>webWorker.name)
56
+ ];
57
+ return new Set(names).size === names.length;
58
+ }, '`name` must be unique across views and web workers. Rename one of the duplicates.'), // Studio app views are not implemented yet. A studio that declares `entry`
62
59
  // (the SDK app-view entrypoint) is rejected here rather than silently
63
60
  // generating one; studios keep navigating via their existing render path.
64
61
  z.refine((input)=>!(input.applicationType === 'studio' && input.entry !== undefined), {
@@ -66,14 +63,6 @@ z.refine((input)=>!(input.applicationType === 'studio' && input.entry !== undefi
66
63
  path: [
67
64
  'entry'
68
65
  ]
69
- })).check(// An config belongs to a Sanity-owned singleton (the Media
70
- // Library). A non-singleton declaring one is rejected — see
71
- // {@link readConfig} for the runtime guard.
72
- z.refine((input)=>!(input.config && !input.isSingleton), {
73
- error: '`config` is only supported for singleton apps',
74
- path: [
75
- 'config'
76
- ]
77
66
  }));
78
67
  /**
79
68
  * Nominal brand the CLI discriminates on to enable the workbench build/deploy
@@ -82,31 +71,19 @@ z.refine((input)=>!(input.config && !input.isSingleton), {
82
71
  * `Symbol.for` rather than importing it, so it stays internal to this module.
83
72
  */ const WORKBENCH_APP = Symbol.for('sanity.workbench.defineApp');
84
73
  /**
85
- * Whether `app` is a branded `unstable_defineApp(...)` result — the sole
74
+ * Whether `app` is a branded `defineApplication(...)` result — the sole
86
75
  * workbench opt-in.
87
76
  * @public
88
77
  */ export function isWorkbenchApp(app) {
89
78
  return typeof app === 'object' && app !== null && WORKBENCH_APP in app;
90
79
  }
91
- /**
92
- * The app's config, or `undefined` when it declares none. Throws
93
- * when a non-singleton declares one — configs belong to Sanity-owned singletons,
94
- * so build/dev/deploy all read it through here to reject the combination
95
- * consistently.
96
- * @internal
97
- */ export function readConfig(app) {
98
- if (app.config && !app.isSingleton) {
99
- throw new Error('`config` is only supported for singleton apps');
100
- }
101
- return app.config;
102
- }
103
80
  /**
104
81
  * Declare a Sanity Workbench application. Identity at runtime — returns the same
105
82
  * object reference, tagged with the workbench brand. Field validation (the
106
83
  * `slug` pattern etc.) runs at build time in the CLI via `DefineAppInputSchema`;
107
84
  * this helper stays a thin, pure identity wrapper.
108
85
  * @public
109
- */ export function unstable_defineApp(input) {
86
+ */ export function defineApplication(input) {
110
87
  return Object.defineProperty(input, WORKBENCH_APP, {
111
88
  configurable: false,
112
89
  enumerable: false,
@@ -115,20 +92,36 @@ z.refine((input)=>!(input.config && !input.isSingleton), {
115
92
  });
116
93
  }
117
94
  /**
118
- * Declare the Sanity Media Library as a workbench app a singleton whose `fields` become its config.
95
+ * Nominal brand the CLI discriminates a config on. A config is *not* an app: it
96
+ * carries no `slug`/`title`/application identity, only a target `appType`, its
97
+ * owning `organizationId`, and its `fields`. Registered via `Symbol.for` so the
98
+ * marker survives module-realm boundaries — `@sanity/cli-core` re-derives the
99
+ * same global symbol rather than importing it.
100
+ */ const WORKBENCH_CONFIG = Symbol.for('sanity.workbench.defineConfig');
101
+ /**
102
+ * Whether `app` is a branded `unstable_defineMediaLibrary(...)` config — a
103
+ * config-carrier rather than an app. Configs live in `cliConfig.app` alongside
104
+ * apps but resolve through `resolveWorkbenchConfig`, not `resolveWorkbenchApp`.
105
+ * @public
106
+ */ export function isWorkbenchConfig(app) {
107
+ return typeof app === 'object' && app !== null && WORKBENCH_CONFIG in app;
108
+ }
109
+ /**
110
+ * Declare the Sanity Media Library config. The Media Library is a Sanity-owned
111
+ * singleton, so this is not an app — it carries no slug or title, only the
112
+ * target `appType`, its owning organization, and the `fields` it contributes to
113
+ * the installation's config.
119
114
  * @public
120
115
  */ export function unstable_defineMediaLibrary(input) {
121
- return unstable_defineApp({
122
- // @ts-expect-error -- `applicationType`/`isSingleton`/`config` are internal, excluded from `DefineAppInput`; Sanity-owned apps set them
123
- applicationType: 'media-library',
124
- config: input.fields?.length ? {
125
- appType: 'media-library',
126
- fields: input.fields
127
- } : undefined,
128
- isSingleton: true,
129
- organizationId: input.organizationId,
130
- slug: 'media-library',
131
- title: 'Media Library'
116
+ return Object.defineProperty({
117
+ appType: 'media-library',
118
+ fields: input.fields ?? [],
119
+ organizationId: input.organizationId
120
+ }, WORKBENCH_CONFIG, {
121
+ configurable: false,
122
+ enumerable: false,
123
+ value: true,
124
+ writable: false
132
125
  });
133
126
  }
134
127
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/defineApp.ts"],"sourcesContent":["import {z} from 'zod/mini'\n\nimport {APP_SLUG_PATTERN} from './appSlug.js'\nimport {ConfigSchema, InterfaceDeclarationSchema, ServiceDeclarationSchema} from './contract.js'\n\n/**\n * Dashboard visibility values. Mirrors `APP_VISIBILITIES` in `@sanity/cli-core`\n * (which can't be imported here — pulling the barrel into this lean module bloats\n * the config-load path). Kept in sync by a type test in `defineApp.test.ts`.\n */\nconst APP_VISIBILITIES = ['default', 'unlisted', 'disabled'] as const\n\n/**\n * Internal application discriminator. Sanity-owned singleton apps only;\n * validated by the schema but excluded from the public `DefineAppInput` type.\n */\nconst ApplicationType = z.enum(['coreApp', 'studio', 'canvas', 'dashboard', 'media-library'])\n\n/** Dock groups an app can place itself into. */\nconst DockGroupSchema = z.enum(['dock.system', 'dock.applications', 'dock.user'])\n\n/**\n * Dock group identifier. The API does not block a user app from declaring a\n * reserved group (e.g. `dock.system`); priority conventions keep Sanity-owned\n * apps ahead.\n * @public\n */\nexport type DockGroup = z.output<typeof DockGroupSchema>\n\n/**\n * Runtime-validation schema for `unstable_defineApp`.\n * @internal\n */\nexport const DefineAppInputSchema = z\n .object({\n /**\n * Internal — Sanity-owned singleton apps only. Validated here but excluded\n * from the public `DefineAppInput` type.\n * @internal\n */\n applicationType: z.optional(ApplicationType),\n /**\n * Deployed as a versioned snapshot on the app's org installation, not the\n * application service. Singletons only. Internal, so excluded from the public\n * `DefineAppInput` and set via `@ts-expect-error` like `applicationType`.\n * @internal\n */\n config: z.optional(ConfigSchema),\n /**\n * App entrypoint module. Defaults to `./src/App.tsx` when omitted. The build\n * derives the app's navigable `app` view from it. SDK apps only — setting it\n * on a studio is rejected (studio app views are not yet implemented).\n */\n entry: z.optional(z.string(\"must be a path to the app's entry file\")),\n /** Dock group to render in. Defaults to `dock.applications` when omitted. */\n group: z.optional(DockGroupSchema),\n /** Optional icon override (path to an SVG). Wins over manifest/studio icon. */\n icon: z.optional(z.string()),\n /**\n * Sanity-owned app deployed once, installed per org; excluded from the public `DefineAppInput`.\n * @internal\n */\n isSingleton: z.optional(z.boolean()),\n /** Organization that owns the app — the workbench runs and deploys against it. */\n organizationId: z.string(\n \"App `organizationId` is required — pass the owning organization's ID to `unstable_defineApp`\",\n ),\n /** Sort position within the group, ascending. Defaults to `100` when omitted. */\n priority: z.optional(z.number()),\n /** Background services the app runs (e.g. a `worker` emitting dock badges). */\n services: z.optional(\n z\n .array(ServiceDeclarationSchema, 'must be an array of services')\n .check(\n z.refine(\n (services) => new Set(services.map((service) => service.name)).size === services.length,\n 'Service `name` must be unique within an app',\n ),\n ),\n ),\n slug: z\n .string('App `slug` is required — the hostname the application is created at on deploy')\n .check(\n z.regex(\n APP_SLUG_PATTERN,\n 'App `slug` must be lowercase alphanumerics and hyphens, starting with a letter and ending with an alphanumeric',\n ),\n ),\n /** User-facing app title. Wins over studio.config.ts title on merge. */\n title: z.string(),\n /** Views the app exposes (e.g. dock panels). */\n views: z.optional(\n z\n .array(InterfaceDeclarationSchema, 'must be an array of panels')\n .check(\n z.refine(\n (views) => new Set(views.map((view) => view.name)).size === views.length,\n 'View `name` must be unique within an app',\n ),\n ),\n ),\n /** Dashboard visibility of the app. Defaults to `default` when omitted. */\n visibility: z.optional(z.enum(APP_VISIBILITIES)),\n })\n .check(\n // Studio app views are not implemented yet. A studio that declares `entry`\n // (the SDK app-view entrypoint) is rejected here rather than silently\n // generating one; studios keep navigating via their existing render path.\n z.refine((input) => !(input.applicationType === 'studio' && input.entry !== undefined), {\n error: 'App views for studios are not implemented yet',\n path: ['entry'],\n }),\n )\n .check(\n // An config belongs to a Sanity-owned singleton (the Media\n // Library). A non-singleton declaring one is rejected — see\n // {@link readConfig} for the runtime guard.\n z.refine((input) => !(input.config && !input.isSingleton), {\n error: '`config` is only supported for singleton apps',\n path: ['config'],\n }),\n )\n\n/**\n * User-facing input for `unstable_defineApp`. Excludes the internal\n * `applicationType`, `isSingleton`, and `config`.\n * @public\n */\nexport type DefineAppInput = Omit<\n z.output<typeof DefineAppInputSchema>,\n 'applicationType' | 'config' | 'isSingleton'\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","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 {DockSchema, 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'])\n\n/**\n * Runtime-validation schema for `defineApplication`.\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 /** Default placement inherited by panel and window views. */\n dock: z.optional(DockSchema),\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 /** Optional icon override (path to an SVG). Wins over manifest/studio icon. */\n icon: z.optional(z.string()),\n /**\n * Stable identity, distinct from the `slug` address. Defaults to `slug` when\n * omitted. Identity keys — the build id and derived interface ids — are built\n * from this, so renaming the slug no longer changes identity.\n */\n name: z.optional(\n z\n .string()\n .check(\n z.regex(\n APP_SLUG_PATTERN,\n 'App `name` must be lowercase alphanumerics and hyphens, starting with a letter and ending with an alphanumeric',\n ),\n ),\n ),\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 `defineApplication`\",\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. a window, dock panels, and tiles). */\n views: z.optional(z.array(InterfaceDeclarationSchema, 'must be an array of views')),\n /** Dashboard visibility of the app. Defaults to `default` when omitted. */\n visibility: z.optional(z.enum(APP_VISIBILITIES)),\n /** Background web workers the app runs. */\n webWorkers: z.optional(z.array(ServiceDeclarationSchema, 'must be an array of web workers')),\n })\n .check(\n z.refine((input) => {\n const names = [\n ...(input.entry === undefined ? [] : [input.name ?? input.slug]),\n ...(input.views ?? []).map((view) => view.name),\n ...(input.webWorkers ?? []).map((webWorker) => webWorker.name),\n ]\n return new Set(names).size === names.length\n }, '`name` must be unique across views and web workers. Rename one of the duplicates.'),\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\n/**\n * User-facing input for `defineApplication`. Excludes the internal\n * `applicationType`.\n * @public\n */\nexport type DefineAppInput = Omit<z.output<typeof DefineAppInputSchema>, 'applicationType'>\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 `defineApplication`. 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 `defineApplication(...)` 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 * 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 defineApplication(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 * Nominal brand the CLI discriminates a config on. A config is *not* an app: it\n * carries no `slug`/`title`/application identity, only a target `appType`, its\n * owning `organizationId`, and its `fields`. Registered via `Symbol.for` so the\n * marker survives module-realm boundaries — `@sanity/cli-core` re-derives the\n * same global symbol rather than importing it.\n */\nconst WORKBENCH_CONFIG: unique symbol = Symbol.for('sanity.workbench.defineConfig')\n\n/**\n * A branded workbench config as the CLI reads it: bound to its target app by\n * `appType`, not carrying its own app identity. The deploy/undeploy/build paths\n * discriminate it via {@link isWorkbenchConfig}.\n * @public\n */\nexport interface WorkbenchConfig {\n appType: 'media-library'\n fields: MediaLibraryField[]\n organizationId: string\n readonly [WORKBENCH_CONFIG]: true\n}\n\n/**\n * Whether `app` is a branded `unstable_defineMediaLibrary(...)` config — a\n * config-carrier rather than an app. Configs live in `cliConfig.app` alongside\n * apps but resolve through `resolveWorkbenchConfig`, not `resolveWorkbenchApp`.\n * @public\n */\nexport function isWorkbenchConfig(app: unknown): app is WorkbenchConfig {\n return typeof app === 'object' && app !== null && WORKBENCH_CONFIG in app\n}\n\n/**\n * Declare the Sanity Media Library config. The Media Library is a Sanity-owned\n * singleton, so this is not an app — it carries no slug or title, only the\n * target `appType`, its owning organization, and the `fields` it contributes to\n * the installation's config.\n * @public\n */\nexport function unstable_defineMediaLibrary(input: DefineMediaLibraryInput): WorkbenchConfig {\n return Object.defineProperty(\n {appType: 'media-library', fields: input.fields ?? [], organizationId: input.organizationId},\n WORKBENCH_CONFIG,\n {configurable: false, enumerable: false, value: true, writable: false},\n ) as WorkbenchConfig\n}\n"],"names":["z","APP_SLUG_PATTERN","DockSchema","InterfaceDeclarationSchema","ServiceDeclarationSchema","APP_VISIBILITIES","ApplicationType","enum","DefineAppInputSchema","object","applicationType","optional","dock","entry","string","icon","name","check","regex","organizationId","slug","title","views","array","visibility","webWorkers","refine","input","names","undefined","map","view","webWorker","Set","size","length","error","path","WORKBENCH_APP","Symbol","for","isWorkbenchApp","app","defineApplication","Object","defineProperty","configurable","enumerable","value","writable","WORKBENCH_CONFIG","isWorkbenchConfig","unstable_defineMediaLibrary","appType","fields"],"mappings":"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"}
@@ -4,14 +4,14 @@ import { VIEW_CONTRACT_VERSION } from './contract.js';
4
4
  * and the props each component receives — `"panel"` yields a `{title, panel}`
5
5
  * record whose components are typed with the panel props.
6
6
  *
7
- * Returns the component(s) tagged with their type and the contract version, for
7
+ * Returns the component(s) tagged with their surface and contract version, for
8
8
  * the CLI build to generate render artifacts from. Used as the default export of
9
9
  * a view's `src` file.
10
10
  * @public
11
- */ export function unstable_defineView(type, components) {
11
+ */ export function unstable_defineView(surface, components) {
12
12
  return {
13
13
  components,
14
- type,
14
+ surface,
15
15
  version: VIEW_CONTRACT_VERSION
16
16
  };
17
17
  }