@xemahq/ui-kernel 0.11.1 → 0.11.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 (234) hide show
  1. package/package.json +1 -2
  2. package/src/index.ts +0 -17
  3. package/src/lib/biome-host/agent-validation.ts +0 -239
  4. package/src/lib/biome-host/biome-builders.ts +0 -112
  5. package/src/lib/biome-host/biome-mode.ts +0 -6
  6. package/src/lib/biome-host/biome-navigation.ts +0 -37
  7. package/src/lib/biome-host/biome-registry.ts +0 -299
  8. package/src/lib/biome-host/biome-scope-hooks.ts +0 -41
  9. package/src/lib/biome-host/biome-scope.tsx +0 -52
  10. package/src/lib/biome-host/biome-scoped-query.ts +0 -134
  11. package/src/lib/biome-host/create-biome-orval-config.ts +0 -82
  12. package/src/lib/biome-host/define-web-biome.spec.ts +0 -221
  13. package/src/lib/biome-host/define-web-biome.ts +0 -222
  14. package/src/lib/biome-host/errors.ts +0 -220
  15. package/src/lib/biome-host/frontend-biome.ts +0 -256
  16. package/src/lib/biome-host/host-bridge.ts +0 -281
  17. package/src/lib/biome-host/host-sources.ts +0 -54
  18. package/src/lib/biome-host/i18n-hooks.ts +0 -51
  19. package/src/lib/biome-host/i18n-port.test.ts +0 -50
  20. package/src/lib/biome-host/i18n-port.ts +0 -96
  21. package/src/lib/biome-host/index.ts +0 -35
  22. package/src/lib/biome-host/realtime-hooks.ts +0 -75
  23. package/src/lib/biome-host/realtime-port.ts +0 -115
  24. package/src/lib/biome-host/session-contributions.ts +0 -296
  25. package/src/lib/biome-host/use-mutation-with-error-toast.ts +0 -90
  26. package/src/lib/biome-host/use-page-state.ts +0 -248
  27. package/src/lib/capabilities/capability-context.ts +0 -25
  28. package/src/lib/capabilities/capability-provider.tsx +0 -73
  29. package/src/lib/capabilities/index.ts +0 -17
  30. package/src/lib/capabilities/types.ts +0 -69
  31. package/src/lib/capabilities/use-capability.ts +0 -79
  32. package/src/lib/contribution-registry/contribution-registry.spec.ts +0 -141
  33. package/src/lib/contribution-registry/contribution-registry.ts +0 -204
  34. package/src/lib/contribution-registry/index.ts +0 -9
  35. package/src/lib/contribution-registry/renderable-registry.ts +0 -142
  36. package/src/lib/system-bus/capability-invoke.ts +0 -92
  37. package/src/lib/system-bus/deeplink.ts +0 -210
  38. package/src/lib/system-bus/enums.ts +0 -86
  39. package/src/lib/system-bus/host-ports.ts +0 -93
  40. package/src/lib/system-bus/index.ts +0 -16
  41. package/src/lib/system-bus/intent-registry.ts +0 -109
  42. package/src/lib/system-bus/intents.ts +0 -113
  43. package/src/lib/system-bus/palette.ts +0 -78
  44. package/src/lib/system-bus/system-bus-builder.ts +0 -159
  45. package/src/lib/system-bus/system-bus.ts +0 -37
  46. package/src/lib/system-bus/windows.ts +0 -51
  47. package/src/offline/id.ts +0 -16
  48. package/src/offline/index.ts +0 -54
  49. package/src/offline/mutation.ts +0 -75
  50. package/src/offline/offline-status-badge.tsx +0 -51
  51. package/src/offline/online-controller.ts +0 -105
  52. package/src/offline/outbox.test.ts +0 -172
  53. package/src/offline/outbox.ts +0 -348
  54. package/src/offline/react-hooks.ts +0 -40
  55. package/src/offline/react.tsx +0 -32
  56. package/src/offline/storage.ts +0 -112
  57. package/src/offline/types.ts +0 -77
  58. package/src/org-db/components/DbResultTable.tsx +0 -176
  59. package/src/org-db/index.ts +0 -1
  60. package/src/pdf/index.ts +0 -11
  61. package/src/registry/index.ts +0 -9
  62. package/src/registry/lib/agent-validation-host.ts +0 -36
  63. package/src/registry/lib/biome-slot.tsx +0 -50
  64. package/src/registry/lib/biomes-enabled-context.ts +0 -20
  65. package/src/registry/lib/extension-points.ts +0 -134
  66. package/src/registry/lib/primitives/SessionEventCard.tsx +0 -164
  67. package/src/registry/lib/primitives/SessionEventTimeline.tsx +0 -95
  68. package/src/registry/lib/primitives/SessionMutationBar.tsx +0 -76
  69. package/src/registry/lib/primitives/SessionTabbedDrawer.tsx +0 -165
  70. package/src/registry/lib/primitives/index.ts +0 -18
  71. package/src/registry/lib/session-context-builder.ts +0 -84
  72. package/src/registry/lib/session-context-provider.tsx +0 -33
  73. package/src/registry/lib/session-context.ts +0 -21
  74. package/src/registry/lib/session-selectors.ts +0 -272
  75. package/src/session/comments/CommentRail.tsx +0 -164
  76. package/src/session/index.ts +0 -3
  77. package/src/session/lib/cn.ts +0 -11
  78. package/src/session/shell/SessionWorkspaceShell.tsx +0 -147
  79. package/src/session-kit/approvals/ApprovalButton.tsx +0 -89
  80. package/src/session-kit/approvals/ApprovalCard.tsx +0 -341
  81. package/src/session-kit/approvals/ApprovalsCenter.tsx +0 -336
  82. package/src/session-kit/approvals/CapabilityApprovalStickyBar.tsx +0 -118
  83. package/src/session-kit/approvals/Obligation.tsx +0 -111
  84. package/src/session-kit/approvals/ScopedBody.tsx +0 -392
  85. package/src/session-kit/approvals/approval-icons.ts +0 -31
  86. package/src/session-kit/approvals/approval-model.ts +0 -204
  87. package/src/session-kit/approvals/index.ts +0 -22
  88. package/src/session-kit/approvals/obligation-display.ts +0 -100
  89. package/src/session-kit/approvals/risk-accent.ts +0 -47
  90. package/src/session-kit/approvals/scope-icons.ts +0 -19
  91. package/src/session-kit/combobox/Combobox.tsx +0 -358
  92. package/src/session-kit/combobox/use-click-outside.ts +0 -21
  93. package/src/session-kit/display/ContextHeader.tsx +0 -148
  94. package/src/session-kit/display/FileDiffCard.tsx +0 -140
  95. package/src/session-kit/display/MD.tsx +0 -162
  96. package/src/session-kit/display/MessageTurn.tsx +0 -157
  97. package/src/session-kit/display/ThinkingPanel.tsx +0 -81
  98. package/src/session-kit/display/TodoChecklist.tsx +0 -120
  99. package/src/session-kit/display/TokenMeter.tsx +0 -89
  100. package/src/session-kit/display/ToolStrip.tsx +0 -278
  101. package/src/session-kit/display/TypingDots.tsx +0 -24
  102. package/src/session-kit/index.ts +0 -44
  103. package/src/session-kit/lib/enums.ts +0 -66
  104. package/src/session-kit/lib/portal-accent.ts +0 -30
  105. package/src/session-kit/lib/status-dot.ts +0 -68
  106. package/src/session-kit/primitives/Avatar.tsx +0 -44
  107. package/src/session-kit/primitives/PortalGlyph.tsx +0 -51
  108. package/src/session-kit/primitives/RiskPill.tsx +0 -95
  109. package/src/session-kit/primitives/ScopeDot.tsx +0 -47
  110. package/src/session-kit/primitives/Segmented.tsx +0 -71
  111. package/src/ui/branding/app-branding-context.ts +0 -20
  112. package/src/ui/branding/app-branding-provider.tsx +0 -123
  113. package/src/ui/branding/sanitize.ts +0 -89
  114. package/src/ui/breakpoints.ts +0 -14
  115. package/src/ui/capture/document-preview.tsx +0 -166
  116. package/src/ui/capture/field-spec-to-zod.test.ts +0 -81
  117. package/src/ui/capture/field-spec-to-zod.ts +0 -174
  118. package/src/ui/capture/field-spec-validate.ts +0 -236
  119. package/src/ui/capture/field-spec.test.ts +0 -49
  120. package/src/ui/capture/field-spec.ts +0 -274
  121. package/src/ui/capture/index.ts +0 -51
  122. package/src/ui/capture/schema-field-input.tsx +0 -620
  123. package/src/ui/capture/schema-form.tsx +0 -149
  124. package/src/ui/capture/submit-values.test.ts +0 -70
  125. package/src/ui/capture/submit-values.ts +0 -63
  126. package/src/ui/capture/use-prefill.ts +0 -186
  127. package/src/ui/capture/use-schema-validation.ts +0 -145
  128. package/src/ui/capture/use-wizard.ts +0 -180
  129. package/src/ui/capture/value-format.ts +0 -32
  130. package/src/ui/capture/wizard.tsx +0 -105
  131. package/src/ui/charts/bar-chart.tsx +0 -139
  132. package/src/ui/charts/chart-frame.tsx +0 -125
  133. package/src/ui/charts/chart-types.ts +0 -52
  134. package/src/ui/charts/chart-utils.ts +0 -190
  135. package/src/ui/charts/charts.test.tsx +0 -204
  136. package/src/ui/charts/donut-chart.tsx +0 -129
  137. package/src/ui/charts/index.ts +0 -27
  138. package/src/ui/charts/line-chart.tsx +0 -202
  139. package/src/ui/charts/sparkline.tsx +0 -109
  140. package/src/ui/chrome/AsyncBoundary.tsx +0 -74
  141. package/src/ui/chrome/EmptyState.tsx +0 -257
  142. package/src/ui/chrome/ErrorCard.tsx +0 -63
  143. package/src/ui/chrome/LoadingState.tsx +0 -61
  144. package/src/ui/chrome/PageHeader.tsx +0 -137
  145. package/src/ui/chrome/RecordCardList.tsx +0 -98
  146. package/src/ui/chrome/StateCard.tsx +0 -150
  147. package/src/ui/chrome/confirm-delete-dialog.tsx +0 -59
  148. package/src/ui/chrome/page-shell.spec.tsx +0 -117
  149. package/src/ui/chrome/page-shell.tsx +0 -221
  150. package/src/ui/chrome/scope-badge.tsx +0 -40
  151. package/src/ui/chrome/status-badge.tsx +0 -79
  152. package/src/ui/cn.ts +0 -32
  153. package/src/ui/design-tokens.ts +0 -372
  154. package/src/ui/hooks/use-debounced-value.ts +0 -16
  155. package/src/ui/hooks/use-is-mobile.ts +0 -28
  156. package/src/ui/index.ts +0 -110
  157. package/src/ui/pdf/pdf-preview.tsx +0 -122
  158. package/src/ui/primitives/alert-dialog.tsx +0 -104
  159. package/src/ui/primitives/async-combobox.tsx +0 -182
  160. package/src/ui/primitives/badge-variants.ts +0 -21
  161. package/src/ui/primitives/badge.tsx +0 -25
  162. package/src/ui/primitives/button-variants.ts +0 -31
  163. package/src/ui/primitives/button.tsx +0 -29
  164. package/src/ui/primitives/card.tsx +0 -43
  165. package/src/ui/primitives/checkbox.tsx +0 -26
  166. package/src/ui/primitives/collapsible.tsx +0 -9
  167. package/src/ui/primitives/dialog.tsx +0 -103
  168. package/src/ui/primitives/dropdown-menu.tsx +0 -179
  169. package/src/ui/primitives/form-stepper.tsx +0 -94
  170. package/src/ui/primitives/form-stepper.types.ts +0 -15
  171. package/src/ui/primitives/input.tsx +0 -22
  172. package/src/ui/primitives/label.tsx +0 -17
  173. package/src/ui/primitives/overflow-tabs.tsx +0 -331
  174. package/src/ui/primitives/popover.tsx +0 -33
  175. package/src/ui/primitives/preview-device-frame.tsx +0 -213
  176. package/src/ui/primitives/radio-group.tsx +0 -36
  177. package/src/ui/primitives/resizable.tsx +0 -67
  178. package/src/ui/primitives/scroll-area.tsx +0 -38
  179. package/src/ui/primitives/select.tsx +0 -143
  180. package/src/ui/primitives/separator.tsx +0 -20
  181. package/src/ui/primitives/sheet.tsx +0 -107
  182. package/src/ui/primitives/skeleton.tsx +0 -99
  183. package/src/ui/primitives/switch.tsx +0 -27
  184. package/src/ui/primitives/table.tsx +0 -72
  185. package/src/ui/primitives/tabs.tsx +0 -53
  186. package/src/ui/primitives/tag-multi-select.tsx +0 -261
  187. package/src/ui/primitives/textarea.tsx +0 -21
  188. package/src/ui/primitives/tooltip.tsx +0 -30
  189. package/src/ui/primitives/url-preview-chrome.tsx +0 -103
  190. package/src/vista/GenericCatalogueSurface.tsx +0 -179
  191. package/src/vista/SurfaceFallbackCard.tsx +0 -109
  192. package/src/vista/VistaPanel.tsx +0 -163
  193. package/src/vista/form-payload.spec.ts +0 -139
  194. package/src/vista/html/CustomUiFrame.spec.tsx +0 -127
  195. package/src/vista/html/CustomUiFrame.tsx +0 -206
  196. package/src/vista/html/custom-ui-frame-bridge.spec.ts +0 -191
  197. package/src/vista/html/custom-ui-frame-bridge.ts +0 -98
  198. package/src/vista/html/custom-ui-frame-contract.ts +0 -88
  199. package/src/vista/html/custom-ui-intents.spec.ts +0 -135
  200. package/src/vista/html/custom-ui-intents.ts +0 -155
  201. package/src/vista/html/custom-ui-srcdoc.spec.ts +0 -151
  202. package/src/vista/html/custom-ui-srcdoc.ts +0 -193
  203. package/src/vista/html/index.ts +0 -13
  204. package/src/vista/index.ts +0 -52
  205. package/src/vista/surface-catalogue.spec.ts +0 -140
  206. package/src/vista/surface-catalogue.ts +0 -90
  207. package/src/vista/surface-icons.ts +0 -57
  208. package/src/vista/surface-registry.ts +0 -56
  209. package/src/vista/surfaces/ChartSurface.tsx +0 -34
  210. package/src/vista/surfaces/FilesSurface.tsx +0 -202
  211. package/src/vista/surfaces/FormSurface.tsx +0 -380
  212. package/src/vista/surfaces/HtmlSurface.tsx +0 -43
  213. package/src/vista/surfaces/TableSurface.tsx +0 -100
  214. package/src/vista/surfaces/UrlSurface.tsx +0 -40
  215. package/src/vista/surfaces/blob-surfaces.tsx +0 -139
  216. package/src/vista/surfaces/elicitation-transport.spec.ts +0 -369
  217. package/src/vista/surfaces/elicitation-transport.ts +0 -367
  218. package/src/vista/surfaces/file-surface-utils.ts +0 -69
  219. package/src/vista/surfaces/index.ts +0 -24
  220. package/src/vista/surfaces/register-surfaces.tsx +0 -139
  221. package/src/vista/surfaces/text-surfaces.tsx +0 -132
  222. package/src/vista/use-vista-tabs.ts +0 -196
  223. package/src/vista/vista-code-renderer-context.ts +0 -16
  224. package/src/vista/vista-code-renderer.tsx +0 -35
  225. package/src/vista/vista-file-context.ts +0 -14
  226. package/src/vista/vista-file-source.tsx +0 -141
  227. package/src/vista/vista-form-context.ts +0 -20
  228. package/src/vista/vista-form-host.tsx +0 -48
  229. package/src/vista/vista-model.ts +0 -137
  230. package/src/vista/vista-open-context.tsx +0 -27
  231. package/src/vista/vista-open-state.ts +0 -11
  232. package/src/vista/vista-payloads.ts +0 -635
  233. package/src/vista/vista-tabs-manifest.ts +0 -138
  234. package/src/vista/vista-tools.ts +0 -232
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xemahq/ui-kernel",
3
- "version": "0.11.1",
3
+ "version": "0.11.2",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Host-framework-agnostic UI kernel for the Xema OS. Defines the SystemBus orchestration contract (capability.invoke, cross-biome intents, command palette, xema:// deeplinks, window manager) AND the biome-host contract surface (FrontendBiome/FrontendBiomeFactory, HostBridge, the singleton biomeRegistry, session contributions) that every frontend biome composes against. No Vite, Next.js, or React-Router — React itself IS allowed as the shared component model (the contracts traffic in ReactNode/ComponentType and a React context). Concrete host adapters (router/auth/toast wiring) live in separate packages that consume this kernel. The SystemBus is pure orchestration: it never authorizes, the backend capability-router enforces all policy.",
@@ -31,7 +31,6 @@
31
31
  "types": "dist/index.d.ts",
32
32
  "files": [
33
33
  "dist",
34
- "src",
35
34
  "README.md",
36
35
  "LICENSE"
37
36
  ],
package/src/index.ts DELETED
@@ -1,17 +0,0 @@
1
- export * from './lib/system-bus';
2
- export * from './lib/biome-host';
3
- export * from './lib/capabilities';
4
-
5
- // The shared closed-first-party + open-biome contribution registry core (and
6
- // its versioned/validated renderable flavour). Every plane that lets a biome
7
- // contribute into a set the platform also ships builtins for is built on this.
8
- export * from './lib/contribution-registry';
9
-
10
- // Guided-capture UX kit (B5). Also available at the `@xemahq/ui-kernel/ui`
11
- // subpath alongside the rest of the shared UI surface; re-exported here so the
12
- // capture primitives are reachable from the package root too.
13
- export * from './ui/capture';
14
-
15
- // Offline field-capture kit — durable outbox that queues mutations while
16
- // disconnected and drains them on reconnect. Also at `@xemahq/ui-kernel/offline`.
17
- export * from './offline';
@@ -1,239 +0,0 @@
1
- /**
2
- * Lightweight cross-biome composition validation.
3
- *
4
- * After the host bootstrap registers every enabled web biome, the merged
5
- * surface must be internally consistent: no two biomes may claim the same
6
- * route, no two may claim the same nav id, and every panel must target a
7
- * slot the composition can actually render. The runtime tolerates a lot of
8
- * this silently (the nav merge last-writer-wins on a duplicate id; a panel
9
- * pointing at a non-existent slot simply never renders), which is exactly
10
- * the kind of hidden degradation the platform forbids — so we surface it.
11
- *
12
- * This module is the PURE validator: it takes the registered biomes plus
13
- * the set of host-defined slot ids and returns structured diagnostics. It
14
- * is host-framework-agnostic and knows nothing about `HostExtensionSlots`
15
- * (that catalog lives in `biome-registry-web`, which passes it in). It does
16
- * NOT throw and it does NOT log — callers decide the policy (the host
17
- * bootstrap fail-fasts on `Error`-severity diagnostics and surfaces
18
- * `Warning`-severity ones). This is deliberately NOT a permission matrix:
19
- * it validates structural composition only, never authorization.
20
- */
21
-
22
- import type { FrontendBiome } from './frontend-biome';
23
-
24
- /** Closed set — how serious a composition diagnostic is. */
25
- export enum CompositionDiagnosticSeverity {
26
- /**
27
- * A genuine composition bug that makes the merged surface ambiguous
28
- * (two routes/nav ids collide). Deterministic and author-fixable; the
29
- * host fail-fasts so it is caught in dev/CI, never silently shipped.
30
- */
31
- Error = 'error',
32
- /**
33
- * A probable mistake that the runtime tolerates (a panel targeting an
34
- * unknown or not-yet-loaded slot). Non-fatal because it can also be a
35
- * legitimate cross-deployment state (host shell older than the biome,
36
- * or the slot-owning biome disabled for this org).
37
- */
38
- Warning = 'warning',
39
- }
40
-
41
- /** Closed set — the kind of composition problem detected. */
42
- export enum CompositionDiagnosticCode {
43
- /** Two biomes contribute a route with the same `(path, projectScoped)`. */
44
- DuplicateRoutePath = 'duplicate_route_path',
45
- /** Two biomes contribute a nav item with the same `id`. */
46
- DuplicateNavId = 'duplicate_nav_id',
47
- /**
48
- * A panel targets a slot that is neither a known host slot nor a
49
- * well-formed `<owner>/<name>` biome-owned slot — almost always a typo.
50
- */
51
- UnknownSlot = 'unknown_slot',
52
- /**
53
- * A panel targets a biome-owned slot (`<owner>/<name>`) whose owning
54
- * biome is not registered, so the panel can never render.
55
- */
56
- OrphanedPanelSlot = 'orphaned_panel_slot',
57
- }
58
-
59
- export interface CompositionDiagnostic {
60
- readonly code: CompositionDiagnosticCode;
61
- readonly severity: CompositionDiagnosticSeverity;
62
- /** The biome id(s) implicated, for actionable reporting. */
63
- readonly biomeIds: readonly string[];
64
- /** Human-readable, actionable description. */
65
- readonly message: string;
66
- }
67
-
68
- export interface ValidateBiomeCompositionOptions {
69
- /**
70
- * Host-defined slot ids the shell renders (the `HostExtensionSlots`
71
- * values). A panel slot that is neither one of these nor a well-formed
72
- * `<owner>/<name>` biome-owned slot is flagged as `UnknownSlot`.
73
- */
74
- readonly knownSlots: ReadonlySet<string>;
75
- }
76
-
77
- /**
78
- * Biome-owned slots are namespaced `<owner>/<name>` where `owner` is the
79
- * id of the biome that publishes the slot. Exactly one `/`, both segments
80
- * non-empty. Returns the owner id, or `null` if the slot is not a
81
- * well-formed biome-owned slot.
82
- */
83
- function biomeOwnedSlotOwner(slot: string): string | null {
84
- const slashIndex = slot.indexOf('/');
85
- if (slashIndex <= 0) {
86
- return null;
87
- } // no slash, or leading slash (empty owner)
88
- if (slot.indexOf('/', slashIndex + 1) !== -1) {
89
- return null;
90
- } // more than one slash
91
- const owner = slot.slice(0, slashIndex);
92
- const name = slot.slice(slashIndex + 1);
93
- if (owner.length === 0 || name.length === 0) {
94
- return null;
95
- }
96
- return owner;
97
- }
98
-
99
- /**
100
- * Validate the merged composition of every registered biome. Pure: returns
101
- * diagnostics, never throws, never logs. Diagnostics are returned in a
102
- * deterministic order (by code, then by the offending key) so callers and
103
- * tests see stable output.
104
- */
105
- export function validateBiomeComposition(
106
- biomes: readonly FrontendBiome[],
107
- options: ValidateBiomeCompositionOptions,
108
- ): readonly CompositionDiagnostic[] {
109
- const diagnostics: CompositionDiagnostic[] = [];
110
- const biomeIds = new Set(biomes.map((b) => b.id));
111
-
112
- // ── Duplicate route paths ────────────────────────────────────────────
113
- // Routes collide only within the same scope: a project-scoped `/x`
114
- // mounts under `/spaces/projects/:projectId/x`, an org-scoped `/x` under `/x`,
115
- // so they never clash. Key on `(scope, path)`.
116
- const routeOwners = new Map<string, string[]>();
117
- for (const biome of biomes) {
118
- for (const route of biome.routes ?? []) {
119
- const scope = route.projectScoped ? 'project' : 'root';
120
- const key = `${scope}:${route.path}`;
121
- const owners = routeOwners.get(key);
122
- if (owners) {
123
- owners.push(biome.id);
124
- } else {
125
- routeOwners.set(key, [biome.id]);
126
- }
127
- }
128
- }
129
- for (const [key, owners] of routeOwners) {
130
- if (owners.length < 2) {
131
- continue;
132
- }
133
- const [scope, path] = splitFirst(key, ':');
134
- diagnostics.push({
135
- code: CompositionDiagnosticCode.DuplicateRoutePath,
136
- severity: CompositionDiagnosticSeverity.Error,
137
- biomeIds: owners,
138
- message:
139
- `Route path '${path}' (${scope}-scoped) is contributed by multiple biomes: ` +
140
- `${owners.join(', ')}. Each route must be unique within its scope — ` +
141
- `only one would mount and the others would be unreachable.`,
142
- });
143
- }
144
-
145
- // ── Duplicate nav ids ────────────────────────────────────────────────
146
- // The nav merge keys on `navItem.id`; a duplicate id makes one item
147
- // silently overwrite the other in the feature-route map and collide as a
148
- // React key in the rendered list.
149
- const navOwners = new Map<string, string[]>();
150
- for (const biome of biomes) {
151
- for (const navItem of biome.navItems ?? []) {
152
- const owners = navOwners.get(navItem.id);
153
- if (owners) {
154
- owners.push(biome.id);
155
- } else {
156
- navOwners.set(navItem.id, [biome.id]);
157
- }
158
- }
159
- }
160
- for (const [navId, owners] of navOwners) {
161
- if (owners.length < 2) {
162
- continue;
163
- }
164
- diagnostics.push({
165
- code: CompositionDiagnosticCode.DuplicateNavId,
166
- severity: CompositionDiagnosticSeverity.Error,
167
- biomeIds: owners,
168
- message:
169
- `Nav id '${navId}' is contributed by multiple biomes: ${owners.join(', ')}. ` +
170
- `Nav ids must be unique — a duplicate silently overwrites the ` +
171
- `feature-route mapping and collides as a render key.`,
172
- });
173
- }
174
-
175
- // ── Panel slots: known-slot + dependency-closure ─────────────────────
176
- for (const biome of biomes) {
177
- for (const panel of biome.panels ?? []) {
178
- if (options.knownSlots.has(panel.slot)) {
179
- continue;
180
- }
181
- const owner = biomeOwnedSlotOwner(panel.slot);
182
- if (owner === null) {
183
- diagnostics.push({
184
- code: CompositionDiagnosticCode.UnknownSlot,
185
- severity: CompositionDiagnosticSeverity.Warning,
186
- biomeIds: [biome.id],
187
- message:
188
- `Biome '${biome.id}' panel '${panel.id}' targets slot '${panel.slot}', ` +
189
- `which is neither a known host slot nor a well-formed ` +
190
- `'<owner>/<name>' biome-owned slot. It will never render — ` +
191
- `check for a typo against the host slot catalog.`,
192
- });
193
- continue;
194
- }
195
- // Well-formed biome-owned slot — its owner must be registered for the
196
- // panel to ever render (dependency-closure sanity). A biome may own
197
- // its own slot, which is always satisfied.
198
- if (!biomeIds.has(owner)) {
199
- diagnostics.push({
200
- code: CompositionDiagnosticCode.OrphanedPanelSlot,
201
- severity: CompositionDiagnosticSeverity.Warning,
202
- biomeIds: [biome.id],
203
- message:
204
- `Biome '${biome.id}' panel '${panel.id}' targets biome-owned slot ` +
205
- `'${panel.slot}', but its owner biome '${owner}' is not registered. ` +
206
- `The panel will never render until '${owner}' is enabled.`,
207
- });
208
- }
209
- }
210
- }
211
-
212
- return sortDiagnostics(diagnostics);
213
- }
214
-
215
- /** Split on the first occurrence of `sep` into a `[head, tail]` pair. */
216
- function splitFirst(value: string, sep: string): [string, string] {
217
- const idx = value.indexOf(sep);
218
- if (idx === -1) {
219
- return [value, ''];
220
- }
221
- return [value.slice(0, idx), value.slice(idx + sep.length)];
222
- }
223
-
224
- /** Stable ordering: by code, then by the joined biome ids, then message. */
225
- function sortDiagnostics(
226
- diagnostics: CompositionDiagnostic[],
227
- ): CompositionDiagnostic[] {
228
- return diagnostics.slice().sort((a, b) => {
229
- if (a.code !== b.code) {
230
- return a.code.localeCompare(b.code);
231
- }
232
- const ai = a.biomeIds.join(',');
233
- const bi = b.biomeIds.join(',');
234
- if (ai !== bi) {
235
- return ai.localeCompare(bi);
236
- }
237
- return a.message.localeCompare(b.message);
238
- });
239
- }
@@ -1,112 +0,0 @@
1
- /**
2
- * Frontend-biome route/biome builders.
3
- *
4
- * The `createElement(Suspense, { fallback: null }, createElement(LazyPage))`
5
- * wrapper is hand-repeated ~74× across biome `index.ts` files — once per lazy
6
- * route. `lazyRoute` collapses it to one call; `defineSinglePageBiome` builds
7
- * the whole one-page biome (nav item + single route) that the vast majority of
8
- * biomes are. Host-framework-agnostic: pure React (`lazy` / `Suspense` /
9
- * `createElement`), no router and no Next.js.
10
- */
11
- import {
12
- Suspense,
13
- createElement,
14
- lazy,
15
- type ComponentType,
16
- type ReactNode,
17
- } from 'react';
18
-
19
- import type {
20
- FrontendBiome,
21
- NavItemContribution,
22
- RouteAccess,
23
- RouteContribution,
24
- } from './frontend-biome';
25
-
26
- /** A dynamic import of a module whose default export is a React component. */
27
- export type LazyComponentImport = () => Promise<{ default: ComponentType }>;
28
-
29
- /**
30
- * Wrap a dynamic page import as a `RouteContribution['element']` factory: lazy
31
- * import + `<Suspense>` boundary. Replaces the hand-written
32
- * `createElement(Suspense, { fallback }, createElement(lazy(importFn)))` block.
33
- *
34
- * The `lazy(...)` call is hoisted out of the returned thunk so the component is
35
- * created ONCE (not on every route render), matching how biomes hand-declare
36
- * `const Page = lazy(() => import('./Page'))` at module scope.
37
- *
38
- * @param importFn Dynamic import of the page module (`() => import('./Page')`).
39
- * @param fallback Suspense fallback. Defaults to `null` (the prevailing biome
40
- * convention) — pass a skeleton to be explicit.
41
- */
42
- export function lazyRoute(
43
- importFn: LazyComponentImport,
44
- fallback: ReactNode = null,
45
- ): () => ReactNode {
46
- const Lazy = lazy(importFn);
47
- const LazyRoute = () =>
48
- createElement(Suspense, { fallback }, createElement(Lazy));
49
- LazyRoute.displayName = 'LazyRoute';
50
- return LazyRoute;
51
- }
52
-
53
- export interface SinglePageBiomeRoute {
54
- /** Route path. May include `:param` segments. */
55
- readonly path: string;
56
- /** Dynamic import of the page module. */
57
- readonly page: LazyComponentImport;
58
- /**
59
- * Whether the route mounts under `/projects/:projectId` (project-scoped) or
60
- * at the org root. REQUIRED — mirrors {@link RouteContribution.projectScoped}.
61
- */
62
- readonly projectScoped: boolean;
63
- /** Access tier required to render. Defaults to `'member'`. */
64
- readonly access?: RouteAccess;
65
- /** Suspense fallback for the lazy page. Defaults to `null`. */
66
- readonly fallback?: ReactNode;
67
- }
68
-
69
- export interface SinglePageBiomeSpec {
70
- /** Stable biome id; matches `xema-biome.json` `xema.id`. */
71
- readonly id: string;
72
- /** Displayed name. */
73
- readonly displayName: string;
74
- /** The single nav item this biome contributes. */
75
- readonly nav: NavItemContribution;
76
- /** The single route this biome contributes. */
77
- readonly route: SinglePageBiomeRoute;
78
- }
79
-
80
- /**
81
- * Build a `FrontendBiome` for the common one-nav-item / one-route biome.
82
- *
83
- * Most biomes are exactly this shape — a nav entry plus a single lazy page —
84
- * yet each re-declares the nav array, the route array, the `lazy(...)`, and the
85
- * `Suspense` wrapper by hand. This builder centralizes all four.
86
- *
87
- * @example
88
- * const biome: FrontendBiomeFactory = () =>
89
- * defineSinglePageBiome({
90
- * id: 'org-databases',
91
- * displayName: 'Organization Databases',
92
- * nav: { id: 'org-databases', label: 'Databases', route: 'org-databases',
93
- * icon: Database, section: 'Organization', weight: 30 },
94
- * route: { path: 'org-databases', projectScoped: false,
95
- * page: () => import('./pages/DatabasesListPage') },
96
- * });
97
- */
98
- export function defineSinglePageBiome(spec: SinglePageBiomeSpec): FrontendBiome {
99
- const { route } = spec;
100
- const contribution: RouteContribution = {
101
- path: route.path,
102
- projectScoped: route.projectScoped,
103
- element: lazyRoute(route.page, route.fallback ?? null),
104
- ...(route.access ? { access: route.access } : {}),
105
- };
106
- return {
107
- id: spec.id,
108
- displayName: spec.displayName,
109
- navItems: [spec.nav],
110
- routes: [contribution],
111
- };
112
- }
@@ -1,6 +0,0 @@
1
- /**
2
- * Master switch for the biome subsystem. A host passes `'disabled'` to opt
3
- * the whole biome runtime out (no biomes load / register) — e.g. for a
4
- * minimal shell or a degraded mode. Framework-agnostic.
5
- */
6
- export type BiomesMode = 'enabled' | 'disabled';
@@ -1,37 +0,0 @@
1
- /**
2
- * Bridge-backed navigation convenience hooks for frontend biomes.
3
- *
4
- * These wrap the host-agnostic `HostBridge.navigation` surface so biome code
5
- * never imports a router primitive (`next/navigation`, `react-router-dom`)
6
- * directly — the host wires the concrete implementation once on the bridge and
7
- * every biome reads it through here. Host-framework-agnostic: no Next.js, no
8
- * React-Router. React itself IS used (these are React hooks).
9
- */
10
- import { useMemo } from 'react';
11
-
12
- import { useHostBridge } from './host-bridge';
13
-
14
- /**
15
- * Current URL query string as a read-only `URLSearchParams`, read through the
16
- * host-provided navigation adapter (`bridge.navigation.useSearchParams`).
17
- *
18
- * The one router primitive biomes could not previously reach through the
19
- * bridge. Use this INSTEAD of `import { useSearchParams } from
20
- * 'next/navigation'` so biome code stays portable across hosts.
21
- */
22
- export function useSearchParams(): URLSearchParams {
23
- return useHostBridge().navigation.useSearchParams();
24
- }
25
-
26
- /**
27
- * Read a single query-string parameter by key, or `null` when absent.
28
- *
29
- * Sugar over {@link useSearchParams} for the common "one param" case (e.g.
30
- * `?tab=overview`, `?runId=…`) that otherwise forces a direct
31
- * `next/navigation` import. Returns the first value when a key repeats,
32
- * matching `URLSearchParams.get` semantics.
33
- */
34
- export function useQueryParam(key: string): string | null {
35
- const searchParams = useSearchParams();
36
- return useMemo(() => searchParams.get(key), [searchParams, key]);
37
- }