@xemahq/ui-kernel 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +17 -0
- package/README.md +72 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/biome-host/biome-mode.d.ts +2 -0
- package/dist/lib/biome-host/biome-mode.d.ts.map +1 -0
- package/dist/lib/biome-host/biome-mode.js +3 -0
- package/dist/lib/biome-host/biome-mode.js.map +1 -0
- package/dist/lib/biome-host/biome-registry.d.ts +30 -0
- package/dist/lib/biome-host/biome-registry.d.ts.map +1 -0
- package/dist/lib/biome-host/biome-registry.js +134 -0
- package/dist/lib/biome-host/biome-registry.js.map +1 -0
- package/dist/lib/biome-host/composition-validation.d.ts +22 -0
- package/dist/lib/biome-host/composition-validation.d.ts.map +1 -0
- package/dist/lib/biome-host/composition-validation.js +127 -0
- package/dist/lib/biome-host/composition-validation.js.map +1 -0
- package/dist/lib/biome-host/frontend-biome.d.ts +47 -0
- package/dist/lib/biome-host/frontend-biome.d.ts.map +1 -0
- package/dist/lib/biome-host/frontend-biome.js +3 -0
- package/dist/lib/biome-host/frontend-biome.js.map +1 -0
- package/dist/lib/biome-host/host-bridge.d.ts +55 -0
- package/dist/lib/biome-host/host-bridge.d.ts.map +1 -0
- package/dist/lib/biome-host/host-bridge.js +16 -0
- package/dist/lib/biome-host/host-bridge.js.map +1 -0
- package/dist/lib/biome-host/host-sources.d.ts +12 -0
- package/dist/lib/biome-host/host-sources.d.ts.map +1 -0
- package/dist/lib/biome-host/host-sources.js +3 -0
- package/dist/lib/biome-host/host-sources.js.map +1 -0
- package/dist/lib/biome-host/index.d.ts +10 -0
- package/dist/lib/biome-host/index.d.ts.map +1 -0
- package/dist/lib/biome-host/index.js +26 -0
- package/dist/lib/biome-host/index.js.map +1 -0
- package/dist/lib/biome-host/nav.d.ts +17 -0
- package/dist/lib/biome-host/nav.d.ts.map +1 -0
- package/dist/lib/biome-host/nav.js +52 -0
- package/dist/lib/biome-host/nav.js.map +1 -0
- package/dist/lib/biome-host/session-contributions.d.ts +143 -0
- package/dist/lib/biome-host/session-contributions.d.ts.map +1 -0
- package/dist/lib/biome-host/session-contributions.js +3 -0
- package/dist/lib/biome-host/session-contributions.js.map +1 -0
- package/dist/lib/biome-host/session-profiles.d.ts +18 -0
- package/dist/lib/biome-host/session-profiles.d.ts.map +1 -0
- package/dist/lib/biome-host/session-profiles.js +39 -0
- package/dist/lib/biome-host/session-profiles.js.map +1 -0
- package/dist/lib/system-bus/capability-invoke.d.ts +18 -0
- package/dist/lib/system-bus/capability-invoke.d.ts.map +1 -0
- package/dist/lib/system-bus/capability-invoke.js +3 -0
- package/dist/lib/system-bus/capability-invoke.js.map +1 -0
- package/dist/lib/system-bus/deeplink.d.ts +33 -0
- package/dist/lib/system-bus/deeplink.d.ts.map +1 -0
- package/dist/lib/system-bus/deeplink.js +99 -0
- package/dist/lib/system-bus/deeplink.js.map +1 -0
- package/dist/lib/system-bus/enums.d.ts +27 -0
- package/dist/lib/system-bus/enums.d.ts.map +1 -0
- package/dist/lib/system-bus/enums.js +35 -0
- package/dist/lib/system-bus/enums.js.map +1 -0
- package/dist/lib/system-bus/host-ports.d.ts +24 -0
- package/dist/lib/system-bus/host-ports.d.ts.map +1 -0
- package/dist/lib/system-bus/host-ports.js +3 -0
- package/dist/lib/system-bus/host-ports.js.map +1 -0
- package/dist/lib/system-bus/index.d.ts +11 -0
- package/dist/lib/system-bus/index.d.ts.map +1 -0
- package/dist/lib/system-bus/index.js +27 -0
- package/dist/lib/system-bus/index.js.map +1 -0
- package/dist/lib/system-bus/intent-registry.d.ts +14 -0
- package/dist/lib/system-bus/intent-registry.d.ts.map +1 -0
- package/dist/lib/system-bus/intent-registry.js +66 -0
- package/dist/lib/system-bus/intent-registry.js.map +1 -0
- package/dist/lib/system-bus/intents.d.ts +30 -0
- package/dist/lib/system-bus/intents.d.ts.map +1 -0
- package/dist/lib/system-bus/intents.js +3 -0
- package/dist/lib/system-bus/intents.js.map +1 -0
- package/dist/lib/system-bus/palette.d.ts +25 -0
- package/dist/lib/system-bus/palette.d.ts.map +1 -0
- package/dist/lib/system-bus/palette.js +3 -0
- package/dist/lib/system-bus/palette.js.map +1 -0
- package/dist/lib/system-bus/system-bus-builder.d.ts +10 -0
- package/dist/lib/system-bus/system-bus-builder.d.ts.map +1 -0
- package/dist/lib/system-bus/system-bus-builder.js +82 -0
- package/dist/lib/system-bus/system-bus-builder.js.map +1 -0
- package/dist/lib/system-bus/system-bus.d.ts +13 -0
- package/dist/lib/system-bus/system-bus.d.ts.map +1 -0
- package/dist/lib/system-bus/system-bus.js +3 -0
- package/dist/lib/system-bus/system-bus.js.map +1 -0
- package/dist/lib/system-bus/windows.d.ts +21 -0
- package/dist/lib/system-bus/windows.d.ts.map +1 -0
- package/dist/lib/system-bus/windows.js +3 -0
- package/dist/lib/system-bus/windows.js.map +1 -0
- package/dist/org-db/components/DbResultTable.d.ts +13 -0
- package/dist/org-db/components/DbResultTable.d.ts.map +1 -0
- package/dist/org-db/components/DbResultTable.js +58 -0
- package/dist/org-db/components/DbResultTable.js.map +1 -0
- package/dist/org-db/index.d.ts +2 -0
- package/dist/org-db/index.d.ts.map +1 -0
- package/dist/org-db/index.js +6 -0
- package/dist/org-db/index.js.map +1 -0
- package/dist/registry/index.d.ts +9 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +25 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/lib/biome-slot.d.ts +7 -0
- package/dist/registry/lib/biome-slot.d.ts.map +1 -0
- package/dist/registry/lib/biome-slot.js +18 -0
- package/dist/registry/lib/biome-slot.js.map +1 -0
- package/dist/registry/lib/biomes-enabled-context.d.ts +3 -0
- package/dist/registry/lib/biomes-enabled-context.d.ts.map +1 -0
- package/dist/registry/lib/biomes-enabled-context.js +11 -0
- package/dist/registry/lib/biomes-enabled-context.js.map +1 -0
- package/dist/registry/lib/composition-validation-host.d.ts +3 -0
- package/dist/registry/lib/composition-validation-host.d.ts.map +1 -0
- package/dist/registry/lib/composition-validation-host.js +10 -0
- package/dist/registry/lib/composition-validation-host.js.map +1 -0
- package/dist/registry/lib/extension-points.d.ts +15 -0
- package/dist/registry/lib/extension-points.d.ts.map +1 -0
- package/dist/registry/lib/extension-points.js +17 -0
- package/dist/registry/lib/extension-points.js.map +1 -0
- package/dist/registry/lib/primitives/SessionEventCard.d.ts +14 -0
- package/dist/registry/lib/primitives/SessionEventCard.d.ts.map +1 -0
- package/dist/registry/lib/primitives/SessionEventCard.js +60 -0
- package/dist/registry/lib/primitives/SessionEventCard.js.map +1 -0
- package/dist/registry/lib/primitives/SessionEventTimeline.d.ts +13 -0
- package/dist/registry/lib/primitives/SessionEventTimeline.d.ts.map +1 -0
- package/dist/registry/lib/primitives/SessionEventTimeline.js +35 -0
- package/dist/registry/lib/primitives/SessionEventTimeline.js.map +1 -0
- package/dist/registry/lib/primitives/SessionMutationBar.d.ts +10 -0
- package/dist/registry/lib/primitives/SessionMutationBar.d.ts.map +1 -0
- package/dist/registry/lib/primitives/SessionMutationBar.js +37 -0
- package/dist/registry/lib/primitives/SessionMutationBar.js.map +1 -0
- package/dist/registry/lib/primitives/SessionTabbedDrawer.d.ts +19 -0
- package/dist/registry/lib/primitives/SessionTabbedDrawer.d.ts.map +1 -0
- package/dist/registry/lib/primitives/SessionTabbedDrawer.js +75 -0
- package/dist/registry/lib/primitives/SessionTabbedDrawer.js.map +1 -0
- package/dist/registry/lib/primitives/index.d.ts +5 -0
- package/dist/registry/lib/primitives/index.d.ts.map +1 -0
- package/dist/registry/lib/primitives/index.js +21 -0
- package/dist/registry/lib/primitives/index.js.map +1 -0
- package/dist/registry/lib/session-context-builder.d.ts +13 -0
- package/dist/registry/lib/session-context-builder.d.ts.map +1 -0
- package/dist/registry/lib/session-context-builder.js +39 -0
- package/dist/registry/lib/session-context-builder.js.map +1 -0
- package/dist/registry/lib/session-context-provider.d.ts +10 -0
- package/dist/registry/lib/session-context-provider.d.ts.map +1 -0
- package/dist/registry/lib/session-context-provider.js +24 -0
- package/dist/registry/lib/session-context-provider.js.map +1 -0
- package/dist/registry/lib/session-selectors.d.ts +9 -0
- package/dist/registry/lib/session-selectors.d.ts.map +1 -0
- package/dist/registry/lib/session-selectors.js +149 -0
- package/dist/registry/lib/session-selectors.js.map +1 -0
- package/dist/session/comments/CommentRail.d.ts +20 -0
- package/dist/session/comments/CommentRail.d.ts.map +1 -0
- package/dist/session/comments/CommentRail.js +16 -0
- package/dist/session/comments/CommentRail.js.map +1 -0
- package/dist/session/index.d.ts +4 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +10 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/lib/cn.d.ts +3 -0
- package/dist/session/lib/cn.d.ts.map +1 -0
- package/dist/session/lib/cn.js +9 -0
- package/dist/session/lib/cn.js.map +1 -0
- package/dist/session/shell/SessionWorkspaceShell.d.ts +16 -0
- package/dist/session/shell/SessionWorkspaceShell.d.ts.map +1 -0
- package/dist/session/shell/SessionWorkspaceShell.js +15 -0
- package/dist/session/shell/SessionWorkspaceShell.js.map +1 -0
- package/package.json +84 -0
- package/src/index.ts +2 -0
- package/src/lib/biome-host/biome-mode.ts +6 -0
- package/src/lib/biome-host/biome-registry.ts +245 -0
- package/src/lib/biome-host/composition-validation.ts +215 -0
- package/src/lib/biome-host/frontend-biome.ts +162 -0
- package/src/lib/biome-host/host-bridge.ts +178 -0
- package/src/lib/biome-host/host-sources.ts +41 -0
- package/src/lib/biome-host/index.ts +23 -0
- package/src/lib/biome-host/nav.ts +83 -0
- package/src/lib/biome-host/session-contributions.ts +293 -0
- package/src/lib/biome-host/session-profiles.ts +99 -0
- package/src/lib/system-bus/capability-invoke.ts +92 -0
- package/src/lib/system-bus/deeplink.ts +200 -0
- package/src/lib/system-bus/enums.ts +86 -0
- package/src/lib/system-bus/host-ports.ts +96 -0
- package/src/lib/system-bus/index.ts +16 -0
- package/src/lib/system-bus/intent-registry.ts +106 -0
- package/src/lib/system-bus/intents.ts +109 -0
- package/src/lib/system-bus/palette.ts +77 -0
- package/src/lib/system-bus/system-bus-builder.ts +157 -0
- package/src/lib/system-bus/system-bus.ts +37 -0
- package/src/lib/system-bus/windows.ts +51 -0
- package/src/org-db/components/DbResultTable.tsx +143 -0
- package/src/org-db/index.ts +1 -0
- package/src/registry/index.ts +8 -0
- package/src/registry/lib/biome-slot.tsx +47 -0
- package/src/registry/lib/biomes-enabled-context.ts +20 -0
- package/src/registry/lib/composition-validation-host.ts +37 -0
- package/src/registry/lib/extension-points.ts +134 -0
- package/src/registry/lib/primitives/SessionEventCard.tsx +138 -0
- package/src/registry/lib/primitives/SessionEventTimeline.tsx +89 -0
- package/src/registry/lib/primitives/SessionMutationBar.tsx +76 -0
- package/src/registry/lib/primitives/SessionTabbedDrawer.tsx +155 -0
- package/src/registry/lib/primitives/index.ts +18 -0
- package/src/registry/lib/session-context-builder.ts +68 -0
- package/src/registry/lib/session-context-provider.tsx +50 -0
- package/src/registry/lib/session-selectors.ts +231 -0
- package/src/session/comments/CommentRail.tsx +164 -0
- package/src/session/index.ts +3 -0
- package/src/session/lib/cn.ts +11 -0
- package/src/session/shell/SessionWorkspaceShell.tsx +141 -0
|
@@ -0,0 +1,215 @@
|
|
|
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) return null; // no slash, or leading slash (empty owner)
|
|
86
|
+
if (slot.indexOf('/', slashIndex + 1) !== -1) return null; // more than one slash
|
|
87
|
+
const owner = slot.slice(0, slashIndex);
|
|
88
|
+
const name = slot.slice(slashIndex + 1);
|
|
89
|
+
if (owner.length === 0 || name.length === 0) return null;
|
|
90
|
+
return owner;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Validate the merged composition of every registered biome. Pure: returns
|
|
95
|
+
* diagnostics, never throws, never logs. Diagnostics are returned in a
|
|
96
|
+
* deterministic order (by code, then by the offending key) so callers and
|
|
97
|
+
* tests see stable output.
|
|
98
|
+
*/
|
|
99
|
+
export function validateBiomeComposition(
|
|
100
|
+
biomes: readonly FrontendBiome[],
|
|
101
|
+
options: ValidateBiomeCompositionOptions,
|
|
102
|
+
): readonly CompositionDiagnostic[] {
|
|
103
|
+
const diagnostics: CompositionDiagnostic[] = [];
|
|
104
|
+
const biomeIds = new Set(biomes.map((b) => b.id));
|
|
105
|
+
|
|
106
|
+
// ── Duplicate route paths ────────────────────────────────────────────
|
|
107
|
+
// Routes collide only within the same scope: a project-scoped `/x`
|
|
108
|
+
// mounts under `/projects/:projectId/x`, an org-scoped `/x` under `/x`,
|
|
109
|
+
// so they never clash. Key on `(scope, path)`.
|
|
110
|
+
const routeOwners = new Map<string, string[]>();
|
|
111
|
+
for (const biome of biomes) {
|
|
112
|
+
for (const route of biome.routes ?? []) {
|
|
113
|
+
const scope = route.projectScoped ? 'project' : 'root';
|
|
114
|
+
const key = `${scope}:${route.path}`;
|
|
115
|
+
const owners = routeOwners.get(key);
|
|
116
|
+
if (owners) owners.push(biome.id);
|
|
117
|
+
else routeOwners.set(key, [biome.id]);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
for (const [key, owners] of routeOwners) {
|
|
121
|
+
if (owners.length < 2) continue;
|
|
122
|
+
const [scope, path] = splitFirst(key, ':');
|
|
123
|
+
diagnostics.push({
|
|
124
|
+
code: CompositionDiagnosticCode.DuplicateRoutePath,
|
|
125
|
+
severity: CompositionDiagnosticSeverity.Error,
|
|
126
|
+
biomeIds: owners,
|
|
127
|
+
message:
|
|
128
|
+
`Route path '${path}' (${scope}-scoped) is contributed by multiple biomes: ` +
|
|
129
|
+
`${owners.join(', ')}. Each route must be unique within its scope — ` +
|
|
130
|
+
`only one would mount and the others would be unreachable.`,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ── Duplicate nav ids ────────────────────────────────────────────────
|
|
135
|
+
// The nav merge keys on `navItem.id`; a duplicate id makes one item
|
|
136
|
+
// silently overwrite the other in the feature-route map and collide as a
|
|
137
|
+
// React key in the rendered list.
|
|
138
|
+
const navOwners = new Map<string, string[]>();
|
|
139
|
+
for (const biome of biomes) {
|
|
140
|
+
for (const navItem of biome.navItems ?? []) {
|
|
141
|
+
const owners = navOwners.get(navItem.id);
|
|
142
|
+
if (owners) owners.push(biome.id);
|
|
143
|
+
else navOwners.set(navItem.id, [biome.id]);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
for (const [navId, owners] of navOwners) {
|
|
147
|
+
if (owners.length < 2) continue;
|
|
148
|
+
diagnostics.push({
|
|
149
|
+
code: CompositionDiagnosticCode.DuplicateNavId,
|
|
150
|
+
severity: CompositionDiagnosticSeverity.Error,
|
|
151
|
+
biomeIds: owners,
|
|
152
|
+
message:
|
|
153
|
+
`Nav id '${navId}' is contributed by multiple biomes: ${owners.join(', ')}. ` +
|
|
154
|
+
`Nav ids must be unique — a duplicate silently overwrites the ` +
|
|
155
|
+
`feature-route mapping and collides as a render key.`,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ── Panel slots: known-slot + dependency-closure ─────────────────────
|
|
160
|
+
for (const biome of biomes) {
|
|
161
|
+
for (const panel of biome.panels ?? []) {
|
|
162
|
+
if (options.knownSlots.has(panel.slot)) continue;
|
|
163
|
+
const owner = biomeOwnedSlotOwner(panel.slot);
|
|
164
|
+
if (owner === null) {
|
|
165
|
+
diagnostics.push({
|
|
166
|
+
code: CompositionDiagnosticCode.UnknownSlot,
|
|
167
|
+
severity: CompositionDiagnosticSeverity.Warning,
|
|
168
|
+
biomeIds: [biome.id],
|
|
169
|
+
message:
|
|
170
|
+
`Biome '${biome.id}' panel '${panel.id}' targets slot '${panel.slot}', ` +
|
|
171
|
+
`which is neither a known host slot nor a well-formed ` +
|
|
172
|
+
`'<owner>/<name>' biome-owned slot. It will never render — ` +
|
|
173
|
+
`check for a typo against the host slot catalog.`,
|
|
174
|
+
});
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
// Well-formed biome-owned slot — its owner must be registered for the
|
|
178
|
+
// panel to ever render (dependency-closure sanity). A biome may own
|
|
179
|
+
// its own slot, which is always satisfied.
|
|
180
|
+
if (!biomeIds.has(owner)) {
|
|
181
|
+
diagnostics.push({
|
|
182
|
+
code: CompositionDiagnosticCode.OrphanedPanelSlot,
|
|
183
|
+
severity: CompositionDiagnosticSeverity.Warning,
|
|
184
|
+
biomeIds: [biome.id],
|
|
185
|
+
message:
|
|
186
|
+
`Biome '${biome.id}' panel '${panel.id}' targets biome-owned slot ` +
|
|
187
|
+
`'${panel.slot}', but its owner biome '${owner}' is not registered. ` +
|
|
188
|
+
`The panel will never render until '${owner}' is enabled.`,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return sortDiagnostics(diagnostics);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Split on the first occurrence of `sep` into a `[head, tail]` pair. */
|
|
198
|
+
function splitFirst(value: string, sep: string): [string, string] {
|
|
199
|
+
const idx = value.indexOf(sep);
|
|
200
|
+
if (idx === -1) return [value, ''];
|
|
201
|
+
return [value.slice(0, idx), value.slice(idx + sep.length)];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Stable ordering: by code, then by the joined biome ids, then message. */
|
|
205
|
+
function sortDiagnostics(
|
|
206
|
+
diagnostics: CompositionDiagnostic[],
|
|
207
|
+
): CompositionDiagnostic[] {
|
|
208
|
+
return diagnostics.slice().sort((a, b) => {
|
|
209
|
+
if (a.code !== b.code) return a.code.localeCompare(b.code);
|
|
210
|
+
const ai = a.biomeIds.join(',');
|
|
211
|
+
const bi = b.biomeIds.join(',');
|
|
212
|
+
if (ai !== bi) return ai.localeCompare(bi);
|
|
213
|
+
return a.message.localeCompare(b.message);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from 'react';
|
|
2
|
+
import type { HostBridge } from './host-bridge';
|
|
3
|
+
import type { SessionContributions } from './session-contributions';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Runtime contribution types a `target: 'web'` biome composes. Web
|
|
7
|
+
* biomes default-export a `FrontendBiomeFactory` from their package
|
|
8
|
+
* (the import specifier is `xema-biome.json#name`); the SPA's
|
|
9
|
+
* auto-generated `xema:web-biome-loaders` virtual module imports each
|
|
10
|
+
* factory at bootstrap, calls it with the host's `HostBridge`, and
|
|
11
|
+
* registers the resulting `FrontendBiome` into the singleton
|
|
12
|
+
* `biomeRegistry`.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export interface NavItemContribution {
|
|
16
|
+
/** Stable id used for active-state matching + analytics. */
|
|
17
|
+
readonly id: string;
|
|
18
|
+
/** Displayed label. */
|
|
19
|
+
readonly label: string;
|
|
20
|
+
/** Route path the nav item links to. */
|
|
21
|
+
readonly route: string;
|
|
22
|
+
/**
|
|
23
|
+
* Optional icon component. Typed with `className: string | undefined` so
|
|
24
|
+
* any host icon kit whose props include `className` (lucide-react,
|
|
25
|
+
* heroicons, custom SVG components) is assignable under
|
|
26
|
+
* `exactOptionalPropertyTypes: true`. The host renders with whatever
|
|
27
|
+
* className it computes from its own theme.
|
|
28
|
+
*/
|
|
29
|
+
readonly icon?: ComponentType<{ className?: string | undefined }>;
|
|
30
|
+
/** Section grouping key (matches host nav-registry sections). */
|
|
31
|
+
readonly section?: string;
|
|
32
|
+
/** Sort weight within the section (lower first). Defaults to 100. */
|
|
33
|
+
readonly weight?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface RouteContribution {
|
|
37
|
+
/** React-router path. May include params. */
|
|
38
|
+
readonly path: string;
|
|
39
|
+
/** Element factory — host calls it inside the route tree. */
|
|
40
|
+
readonly element: () => ReactNode;
|
|
41
|
+
/** Whether the route is project-scoped (mounted under /projects/:projectId). */
|
|
42
|
+
readonly projectScoped?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Slot fillers — biomes contribute UI fragments rendered by named
|
|
47
|
+
* `<BiomeSlot name="…">` boundaries placed in the host app shell.
|
|
48
|
+
* Initial slots: 'org-header-actions', 'project-overview-cards',
|
|
49
|
+
* 'run-detail-side-panel', 'settings-tabs', 'session-actions'.
|
|
50
|
+
*/
|
|
51
|
+
export interface PanelContribution {
|
|
52
|
+
/** Slot name the contribution renders into. */
|
|
53
|
+
readonly slot: string;
|
|
54
|
+
/** Stable id for ordering + replacement. */
|
|
55
|
+
readonly id: string;
|
|
56
|
+
/** React node factory — host calls it inside the slot. */
|
|
57
|
+
readonly render: () => ReactNode;
|
|
58
|
+
/** Sort weight within the slot (lower first). Defaults to 100. */
|
|
59
|
+
readonly weight?: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Lifecycle context passed to `FrontendBiome.init`. The host invokes
|
|
64
|
+
* `init()` exactly once after the biome's factory returns and before
|
|
65
|
+
* any session contribution is read. Biomes use it to wire any
|
|
66
|
+
* imperative one-time work (Orval client setup, feature-flag registration,
|
|
67
|
+
* background-cache prefetch) — declarative contributions go on the
|
|
68
|
+
* `FrontendBiome` itself, not here.
|
|
69
|
+
*/
|
|
70
|
+
export interface BiomeInitContext {
|
|
71
|
+
readonly bridge: HostBridge;
|
|
72
|
+
/** Biome id, mirrored from `FrontendBiome.id` for convenience. */
|
|
73
|
+
readonly biomeId: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface FrontendBiome {
|
|
77
|
+
/** Stable biome id; matches xema-biome.json `xema.id`. */
|
|
78
|
+
readonly id: string;
|
|
79
|
+
/** Displayed name. */
|
|
80
|
+
readonly displayName: string;
|
|
81
|
+
/**
|
|
82
|
+
* One-shot initializer invoked after registration. Used for imperative
|
|
83
|
+
* setup that doesn't fit a declarative contribution (e.g. seeding a
|
|
84
|
+
* shared cache, mounting a side-effect listener). Idempotent: the
|
|
85
|
+
* registry guarantees `init()` runs at most once per biome per SPA
|
|
86
|
+
* session, even across HMR reloads.
|
|
87
|
+
*/
|
|
88
|
+
readonly init?: (ctx: BiomeInitContext) => void | Promise<void>;
|
|
89
|
+
/**
|
|
90
|
+
* Optional teardown counterpart to `init()`. Invoked when the biome
|
|
91
|
+
* is explicitly unregistered (admin disable flow). Biomes MUST NOT
|
|
92
|
+
* rely on `dispose()` running on tab close — browsers don't guarantee
|
|
93
|
+
* it. Use it for live-subscription cleanup, not for persistence.
|
|
94
|
+
*/
|
|
95
|
+
readonly dispose?: () => void | Promise<void>;
|
|
96
|
+
readonly navItems?: readonly NavItemContribution[];
|
|
97
|
+
readonly routes?: readonly RouteContribution[];
|
|
98
|
+
readonly panels?: readonly PanelContribution[];
|
|
99
|
+
/**
|
|
100
|
+
* Declarative contributions to the agent-session shell. See
|
|
101
|
+
* `session-contributions.ts` for the full taxonomy. Each contribution
|
|
102
|
+
* is a small data object; the host's session components read them
|
|
103
|
+
* through typed selector hooks and render the matching primitives.
|
|
104
|
+
*/
|
|
105
|
+
readonly session?: SessionContributions;
|
|
106
|
+
/**
|
|
107
|
+
* Output renderer contributions. Each entry binds a biome-defined
|
|
108
|
+
* artifact `OutputKind` (e.g. `catalog_entity_info`) to the React
|
|
109
|
+
* component that renders it inside `<OutputCard>`. The host's renderer
|
|
110
|
+
* registry overlays biome contributions on top of the built-in set —
|
|
111
|
+
* built-in kinds (`markdown_doc`, `json_payload`, …) cannot be
|
|
112
|
+
* overridden, but new kinds light up the moment the biome's frontend
|
|
113
|
+
* loads.
|
|
114
|
+
*
|
|
115
|
+
* See `packages/output-contracts` for the `OutputKind` taxonomy and
|
|
116
|
+
* `submodules/xema-host-web/src/components/outputs/renderer-registry.tsx`
|
|
117
|
+
* for the resolver. Biome renderers receive the same `OutputItem`
|
|
118
|
+
* shape as built-ins.
|
|
119
|
+
*/
|
|
120
|
+
readonly outputRenderers?: readonly OutputRendererContribution[];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* One biome → one renderer for a biome-defined `OutputKind`. The
|
|
125
|
+
* `outputKind` string MUST be unique across all biomes; the renderer
|
|
126
|
+
* registry refuses to register a duplicate at bootstrap (fail-fast,
|
|
127
|
+
* surfaced in the dev console + biome admin UI).
|
|
128
|
+
*
|
|
129
|
+
* The `render` factory mirrors `RouteContribution.element` /
|
|
130
|
+
* `PanelContribution.render` — typed as a thunk so the host can defer
|
|
131
|
+
* import-cost until the renderer actually fires.
|
|
132
|
+
*/
|
|
133
|
+
export interface OutputRendererContribution {
|
|
134
|
+
/**
|
|
135
|
+
* The artifact `OutputKind` this renderer handles. Free-form string
|
|
136
|
+
* because biome-defined kinds are not in the built-in enum; the
|
|
137
|
+
* registry validates it doesn't collide with a built-in kind at
|
|
138
|
+
* registration time.
|
|
139
|
+
*/
|
|
140
|
+
readonly outputKind: string;
|
|
141
|
+
/**
|
|
142
|
+
* React component that renders the artifact body inside `<OutputCard>`.
|
|
143
|
+
* Receives the resolved `OutputItem` as a prop. Biomes import the
|
|
144
|
+
* `OutputRendererProps` type from
|
|
145
|
+
* `submodules/xema-host-web/src/components/outputs/renderer-registry.tsx`
|
|
146
|
+
* (re-exported through the host's biome SDK barrel).
|
|
147
|
+
*/
|
|
148
|
+
readonly render: () => ComponentType<{ readonly output: unknown }>;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Biome module export contract. Each biome's entry module exports a
|
|
153
|
+
* default function of this shape; the host bootstrap calls it with its
|
|
154
|
+
* concrete `HostBridge` implementation, then registers the returned
|
|
155
|
+
* `FrontendBiome` with `biomeRegistry`.
|
|
156
|
+
*
|
|
157
|
+
* This factory shape keeps biome code free of any direct dependency
|
|
158
|
+
* on host-shell primitives (router, auth client, toast library), which
|
|
159
|
+
* is what allows the same biome package to load into a Vite SPA host
|
|
160
|
+
* today and a Next.js host tomorrow.
|
|
161
|
+
*/
|
|
162
|
+
export type FrontendBiomeFactory = (bridge: HostBridge) => FrontendBiome;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import type { QueryClient } from '@tanstack/react-query';
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext, type ReactNode } from 'react';
|
|
4
|
+
|
|
5
|
+
import type { SystemBus } from '../system-bus';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `HostBridge` is the host-agnostic abstraction every frontend biome
|
|
9
|
+
* receives at registration time. It decouples biome code from any
|
|
10
|
+
* particular React shell (Vite SPA today, Next.js tomorrow) and from
|
|
11
|
+
* any particular auth / toast / router primitive.
|
|
12
|
+
*
|
|
13
|
+
* Each concrete host (e.g. `submodules/xema-host-web`) implements this
|
|
14
|
+
* interface once, against its router (`react-router-dom`), its auth
|
|
15
|
+
* client (`keycloak-js`), its toast library (`sonner`), its `QueryClient`,
|
|
16
|
+
* and its request-context store. Biome code is identical across hosts.
|
|
17
|
+
*/
|
|
18
|
+
export interface HostBridgeNavigation {
|
|
19
|
+
/** Push a new entry on the navigation stack. */
|
|
20
|
+
push(path: string): void;
|
|
21
|
+
/** Replace the current entry. */
|
|
22
|
+
replace(path: string): void;
|
|
23
|
+
/**
|
|
24
|
+
* Hook returning the current location. Must be a stable React hook
|
|
25
|
+
* implemented by the host (e.g. `react-router-dom`'s `useLocation`).
|
|
26
|
+
*/
|
|
27
|
+
useLocation(): HostLocation;
|
|
28
|
+
/**
|
|
29
|
+
* Hook returning the params bound by the host's route matcher for the
|
|
30
|
+
* currently-rendered biome route (the `:param` segments declared on the
|
|
31
|
+
* biome's `RouteContribution.path`, plus `projectId` for project-scoped
|
|
32
|
+
* routes). Must be a stable React hook implemented by the host against
|
|
33
|
+
* its router (`react-router-dom`'s `useParams`, the App Router's
|
|
34
|
+
* matcher-bound params context, …).
|
|
35
|
+
*
|
|
36
|
+
* Biomes call this INSTEAD of importing a router primitive directly, so
|
|
37
|
+
* biome code stays decoupled from any particular host shell — the same
|
|
38
|
+
* decoupling `useLocation` / `push` / `replace` already provide. Values
|
|
39
|
+
* are `string | undefined` because a param is absent until the route that
|
|
40
|
+
* declares it is the matched route.
|
|
41
|
+
*/
|
|
42
|
+
useRouteParams<
|
|
43
|
+
T extends Record<string, string | undefined> = Record<string, string | undefined>,
|
|
44
|
+
>(): T;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface HostLocation {
|
|
48
|
+
readonly pathname: string;
|
|
49
|
+
readonly search: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface HostBridgeAuth {
|
|
53
|
+
/** Returns the current actor's bearer token, or null if unauthenticated. */
|
|
54
|
+
getActorToken(): string | null;
|
|
55
|
+
/** Returns the current actor's organization id, or null if not selected. */
|
|
56
|
+
getOrgId(): string | null;
|
|
57
|
+
/** Returns the current actor's project id, or null if not selected. */
|
|
58
|
+
getProjectId(): string | null;
|
|
59
|
+
/** Returns the current actor's user id (subject), or null. */
|
|
60
|
+
getUserId(): string | null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface HostBridgeToast {
|
|
64
|
+
/**
|
|
65
|
+
* Success toast. `detail` is an optional secondary line (rendered as the
|
|
66
|
+
* toast description) — use it to echo what succeeded (e.g. the deeplink
|
|
67
|
+
* that was copied) or to carry a load-bearing consequence note. Mirrors
|
|
68
|
+
* {@link HostBridgeToast.error}'s two-arg shape.
|
|
69
|
+
*/
|
|
70
|
+
success(message: string, detail?: string): void;
|
|
71
|
+
error(message: string, detail?: string): void;
|
|
72
|
+
/** Info toast with an optional secondary description line. */
|
|
73
|
+
info(message: string, detail?: string): void;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface HostBridgeRequestContext {
|
|
77
|
+
readonly correlationId: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Where the topbar's leading back-button takes you. Pages that have a
|
|
82
|
+
* meaningful "parent context" register this; pages without one omit it
|
|
83
|
+
* and no back button is shown. `to` is an internal route; `label` is a
|
|
84
|
+
* screen-reader label (the button itself is icon-only).
|
|
85
|
+
*/
|
|
86
|
+
export interface PageBackTarget {
|
|
87
|
+
readonly to: string;
|
|
88
|
+
/** Screen-reader label. Defaults to "Back". */
|
|
89
|
+
readonly label?: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Page chrome registered by a page into the host's topbar store. The host
|
|
94
|
+
* `AppTopbar` renders the active meta as the single page-chrome bar. This
|
|
95
|
+
* is the host-agnostic mirror of the host's page-meta store payload —
|
|
96
|
+
* biomes register through {@link HostBridgePageMeta.usePageMeta} so the
|
|
97
|
+
* meta reaches the host topbar across a Module-Federation boundary (the
|
|
98
|
+
* bridge is a shared singleton; a remotely-bundled module store is not).
|
|
99
|
+
*/
|
|
100
|
+
export interface PageMeta {
|
|
101
|
+
/** Short title rendered in the topbar. e.g. "Sessions", "Documents". */
|
|
102
|
+
readonly title: string;
|
|
103
|
+
/** Optional eyebrow rendered before the title (small mono caption). */
|
|
104
|
+
readonly eyebrow?: string;
|
|
105
|
+
/** Long-form description shown inside the info popover. */
|
|
106
|
+
readonly description?: string;
|
|
107
|
+
/** Optional bullet list of "what you can do here" shown in the info popover. */
|
|
108
|
+
readonly actions?: readonly string[];
|
|
109
|
+
/** Leftmost back affordance, rendered as an icon-only button. */
|
|
110
|
+
readonly backTo?: PageBackTarget;
|
|
111
|
+
/** Right-aligned action cluster rendered in the topbar (1-3 small buttons). */
|
|
112
|
+
readonly topbarActions?: ReactNode;
|
|
113
|
+
/** Short secondary line rendered inline with the title in the topbar. */
|
|
114
|
+
readonly topbarMeta?: ReactNode;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The page-meta surface — how a biome page drives the host topbar's title,
|
|
119
|
+
* description, help-actions, back affordance, and action cluster.
|
|
120
|
+
*
|
|
121
|
+
* Exposed as a hook (mirroring `navigation.useLocation` / `useRouteParams`)
|
|
122
|
+
* because registration is lifecycle-bound: the host pushes the meta on mount
|
|
123
|
+
* and pops it on unmount, restoring the outer page's meta. Biomes call this
|
|
124
|
+
* INSTEAD of importing the host's page-meta module directly, so the page
|
|
125
|
+
* component bundles cleanly into an MF remote and still updates the one
|
|
126
|
+
* host-owned store via the shared bridge singleton.
|
|
127
|
+
*/
|
|
128
|
+
export interface HostBridgePageMeta {
|
|
129
|
+
/**
|
|
130
|
+
* Register the current page's meta with the host topbar. Stable React hook
|
|
131
|
+
* implemented by the host against its page-meta store. Call it from a stable
|
|
132
|
+
* place at the top of the page component.
|
|
133
|
+
*/
|
|
134
|
+
usePageMeta(meta: PageMeta): void;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface HostBridge {
|
|
138
|
+
readonly navigation: HostBridgeNavigation;
|
|
139
|
+
readonly auth: HostBridgeAuth;
|
|
140
|
+
readonly toast: HostBridgeToast;
|
|
141
|
+
readonly queryClient: QueryClient;
|
|
142
|
+
readonly requestContext: HostBridgeRequestContext;
|
|
143
|
+
/**
|
|
144
|
+
* Page chrome surface — how a biome page drives the host topbar (title,
|
|
145
|
+
* description, help-actions, back, action cluster). Backed by the host's
|
|
146
|
+
* single page-meta store so the topbar is the one source of truth, while
|
|
147
|
+
* pages stay portable into MF remotes (no module-singleton import).
|
|
148
|
+
*/
|
|
149
|
+
readonly pageMeta: HostBridgePageMeta;
|
|
150
|
+
/**
|
|
151
|
+
* The OS-orchestration plane — capability invocation, cross-biome
|
|
152
|
+
* intents, command palette, `xema://` deeplinks, and windowing. Every
|
|
153
|
+
* frontend biome reaches the platform's orchestration surface through
|
|
154
|
+
* here. Pure orchestration: the SystemBus NEVER authorizes — the
|
|
155
|
+
* backend capability-router is the single authority on auth / tenancy /
|
|
156
|
+
* policy / audit (see {@link SystemBus}).
|
|
157
|
+
*/
|
|
158
|
+
readonly system: SystemBus;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export const HostBridgeContext = createContext<HostBridge | null>(null);
|
|
162
|
+
HostBridgeContext.displayName = 'HostBridgeContext';
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Hook biome components use to reach the host. Throws fail-fast if no
|
|
166
|
+
* `<HostBridgeContext.Provider>` is mounted above — biomes must never
|
|
167
|
+
* import host primitives directly, so a missing bridge is a wiring bug.
|
|
168
|
+
*/
|
|
169
|
+
export function useHostBridge(): HostBridge {
|
|
170
|
+
const bridge = useContext(HostBridgeContext);
|
|
171
|
+
if (!bridge) {
|
|
172
|
+
throw new Error(
|
|
173
|
+
'[xema-ui-kernel] useHostBridge() called outside <HostBridgeContext.Provider>. ' +
|
|
174
|
+
'The host app must wrap biome-rendered subtrees with the bridge provider.',
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
return bridge;
|
|
178
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pluggable host sources — keep the kernel independent of any specific
|
|
3
|
+
* auth or toast library. The concrete host adapter (Next.js, React-Router,
|
|
4
|
+
* …) implements each getter/method against whatever client it uses and
|
|
5
|
+
* passes them in when building the `HostBridge` + `SystemBus`.
|
|
6
|
+
*
|
|
7
|
+
* Framework-agnostic: pure interface contracts, no React, no router.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Pluggable auth source — keeps the kernel independent of any specific
|
|
12
|
+
* auth library (keycloak-js, auth0, custom OIDC, …). The host implements
|
|
13
|
+
* each getter against whatever auth client it uses.
|
|
14
|
+
*
|
|
15
|
+
* Only the four call-sites a typical biome needs are exposed; if a host
|
|
16
|
+
* has no concept of "org id" or "project id" it returns `null`. Biomes
|
|
17
|
+
* tolerate `null` per the `HostBridge` contract.
|
|
18
|
+
*/
|
|
19
|
+
export interface AuthSource {
|
|
20
|
+
/** Bearer token for outbound API calls, or null when unauthenticated. */
|
|
21
|
+
getActorToken(): string | null;
|
|
22
|
+
/** Active organization id, or null when none is selected. */
|
|
23
|
+
getOrgId(): string | null;
|
|
24
|
+
/** Active project id, or null when none is selected. */
|
|
25
|
+
getProjectId(): string | null;
|
|
26
|
+
/** Subject (user) id of the actor, or null when unauthenticated. */
|
|
27
|
+
getUserId(): string | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Pluggable toast source — keeps the kernel independent of any specific
|
|
32
|
+
* toast library (sonner, react-hot-toast, custom). The host wires the three
|
|
33
|
+
* call-sites the `HostBridge` exposes against whatever it uses.
|
|
34
|
+
*/
|
|
35
|
+
export interface ToastSource {
|
|
36
|
+
/** Success toast with an optional secondary description line. */
|
|
37
|
+
success(message: string, detail?: string): void;
|
|
38
|
+
error(message: string, detail?: string): void;
|
|
39
|
+
/** Info toast with an optional secondary description line. */
|
|
40
|
+
info(message: string, detail?: string): void;
|
|
41
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Biome-host contract surface — the framework-agnostic shape every
|
|
3
|
+
* frontend biome composes against, plus the singleton runtime registry
|
|
4
|
+
* that hosts populate at bootstrap.
|
|
5
|
+
*
|
|
6
|
+
* Host-framework-agnostic: no Vite, no Next.js, no React-Router. React
|
|
7
|
+
* itself IS used (it is the shared component model for any host) — the
|
|
8
|
+
* contracts traffic in `ReactNode`/`ComponentType`, expose a React
|
|
9
|
+
* context (`HostBridgeContext`/`useHostBridge`), and the registry ships
|
|
10
|
+
* a `useSyncExternalStore` revision hook. The concrete host adapter only
|
|
11
|
+
* wires framework-specific navigation against these contracts (e.g. the
|
|
12
|
+
* Next.js App Router host); the agnostic host sources (`AuthSource`,
|
|
13
|
+
* `ToastSource`), nav merging, and the `SystemBus` builder all live here.
|
|
14
|
+
*/
|
|
15
|
+
export * from './frontend-biome';
|
|
16
|
+
export * from './host-bridge';
|
|
17
|
+
export * from './biome-registry';
|
|
18
|
+
export * from './session-contributions';
|
|
19
|
+
export * from './session-profiles';
|
|
20
|
+
export * from './host-sources';
|
|
21
|
+
export * from './nav';
|
|
22
|
+
export * from './biome-mode';
|
|
23
|
+
export * from './composition-validation';
|