@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.
- package/package.json +1 -2
- package/src/index.ts +0 -17
- package/src/lib/biome-host/agent-validation.ts +0 -239
- package/src/lib/biome-host/biome-builders.ts +0 -112
- package/src/lib/biome-host/biome-mode.ts +0 -6
- package/src/lib/biome-host/biome-navigation.ts +0 -37
- package/src/lib/biome-host/biome-registry.ts +0 -299
- package/src/lib/biome-host/biome-scope-hooks.ts +0 -41
- package/src/lib/biome-host/biome-scope.tsx +0 -52
- package/src/lib/biome-host/biome-scoped-query.ts +0 -134
- package/src/lib/biome-host/create-biome-orval-config.ts +0 -82
- package/src/lib/biome-host/define-web-biome.spec.ts +0 -221
- package/src/lib/biome-host/define-web-biome.ts +0 -222
- package/src/lib/biome-host/errors.ts +0 -220
- package/src/lib/biome-host/frontend-biome.ts +0 -256
- package/src/lib/biome-host/host-bridge.ts +0 -281
- package/src/lib/biome-host/host-sources.ts +0 -54
- package/src/lib/biome-host/i18n-hooks.ts +0 -51
- package/src/lib/biome-host/i18n-port.test.ts +0 -50
- package/src/lib/biome-host/i18n-port.ts +0 -96
- package/src/lib/biome-host/index.ts +0 -35
- package/src/lib/biome-host/realtime-hooks.ts +0 -75
- package/src/lib/biome-host/realtime-port.ts +0 -115
- package/src/lib/biome-host/session-contributions.ts +0 -296
- package/src/lib/biome-host/use-mutation-with-error-toast.ts +0 -90
- package/src/lib/biome-host/use-page-state.ts +0 -248
- package/src/lib/capabilities/capability-context.ts +0 -25
- package/src/lib/capabilities/capability-provider.tsx +0 -73
- package/src/lib/capabilities/index.ts +0 -17
- package/src/lib/capabilities/types.ts +0 -69
- package/src/lib/capabilities/use-capability.ts +0 -79
- package/src/lib/contribution-registry/contribution-registry.spec.ts +0 -141
- package/src/lib/contribution-registry/contribution-registry.ts +0 -204
- package/src/lib/contribution-registry/index.ts +0 -9
- package/src/lib/contribution-registry/renderable-registry.ts +0 -142
- package/src/lib/system-bus/capability-invoke.ts +0 -92
- package/src/lib/system-bus/deeplink.ts +0 -210
- package/src/lib/system-bus/enums.ts +0 -86
- package/src/lib/system-bus/host-ports.ts +0 -93
- package/src/lib/system-bus/index.ts +0 -16
- package/src/lib/system-bus/intent-registry.ts +0 -109
- package/src/lib/system-bus/intents.ts +0 -113
- package/src/lib/system-bus/palette.ts +0 -78
- package/src/lib/system-bus/system-bus-builder.ts +0 -159
- package/src/lib/system-bus/system-bus.ts +0 -37
- package/src/lib/system-bus/windows.ts +0 -51
- package/src/offline/id.ts +0 -16
- package/src/offline/index.ts +0 -54
- package/src/offline/mutation.ts +0 -75
- package/src/offline/offline-status-badge.tsx +0 -51
- package/src/offline/online-controller.ts +0 -105
- package/src/offline/outbox.test.ts +0 -172
- package/src/offline/outbox.ts +0 -348
- package/src/offline/react-hooks.ts +0 -40
- package/src/offline/react.tsx +0 -32
- package/src/offline/storage.ts +0 -112
- package/src/offline/types.ts +0 -77
- package/src/org-db/components/DbResultTable.tsx +0 -176
- package/src/org-db/index.ts +0 -1
- package/src/pdf/index.ts +0 -11
- package/src/registry/index.ts +0 -9
- package/src/registry/lib/agent-validation-host.ts +0 -36
- package/src/registry/lib/biome-slot.tsx +0 -50
- package/src/registry/lib/biomes-enabled-context.ts +0 -20
- package/src/registry/lib/extension-points.ts +0 -134
- package/src/registry/lib/primitives/SessionEventCard.tsx +0 -164
- package/src/registry/lib/primitives/SessionEventTimeline.tsx +0 -95
- package/src/registry/lib/primitives/SessionMutationBar.tsx +0 -76
- package/src/registry/lib/primitives/SessionTabbedDrawer.tsx +0 -165
- package/src/registry/lib/primitives/index.ts +0 -18
- package/src/registry/lib/session-context-builder.ts +0 -84
- package/src/registry/lib/session-context-provider.tsx +0 -33
- package/src/registry/lib/session-context.ts +0 -21
- package/src/registry/lib/session-selectors.ts +0 -272
- package/src/session/comments/CommentRail.tsx +0 -164
- package/src/session/index.ts +0 -3
- package/src/session/lib/cn.ts +0 -11
- package/src/session/shell/SessionWorkspaceShell.tsx +0 -147
- package/src/session-kit/approvals/ApprovalButton.tsx +0 -89
- package/src/session-kit/approvals/ApprovalCard.tsx +0 -341
- package/src/session-kit/approvals/ApprovalsCenter.tsx +0 -336
- package/src/session-kit/approvals/CapabilityApprovalStickyBar.tsx +0 -118
- package/src/session-kit/approvals/Obligation.tsx +0 -111
- package/src/session-kit/approvals/ScopedBody.tsx +0 -392
- package/src/session-kit/approvals/approval-icons.ts +0 -31
- package/src/session-kit/approvals/approval-model.ts +0 -204
- package/src/session-kit/approvals/index.ts +0 -22
- package/src/session-kit/approvals/obligation-display.ts +0 -100
- package/src/session-kit/approvals/risk-accent.ts +0 -47
- package/src/session-kit/approvals/scope-icons.ts +0 -19
- package/src/session-kit/combobox/Combobox.tsx +0 -358
- package/src/session-kit/combobox/use-click-outside.ts +0 -21
- package/src/session-kit/display/ContextHeader.tsx +0 -148
- package/src/session-kit/display/FileDiffCard.tsx +0 -140
- package/src/session-kit/display/MD.tsx +0 -162
- package/src/session-kit/display/MessageTurn.tsx +0 -157
- package/src/session-kit/display/ThinkingPanel.tsx +0 -81
- package/src/session-kit/display/TodoChecklist.tsx +0 -120
- package/src/session-kit/display/TokenMeter.tsx +0 -89
- package/src/session-kit/display/ToolStrip.tsx +0 -278
- package/src/session-kit/display/TypingDots.tsx +0 -24
- package/src/session-kit/index.ts +0 -44
- package/src/session-kit/lib/enums.ts +0 -66
- package/src/session-kit/lib/portal-accent.ts +0 -30
- package/src/session-kit/lib/status-dot.ts +0 -68
- package/src/session-kit/primitives/Avatar.tsx +0 -44
- package/src/session-kit/primitives/PortalGlyph.tsx +0 -51
- package/src/session-kit/primitives/RiskPill.tsx +0 -95
- package/src/session-kit/primitives/ScopeDot.tsx +0 -47
- package/src/session-kit/primitives/Segmented.tsx +0 -71
- package/src/ui/branding/app-branding-context.ts +0 -20
- package/src/ui/branding/app-branding-provider.tsx +0 -123
- package/src/ui/branding/sanitize.ts +0 -89
- package/src/ui/breakpoints.ts +0 -14
- package/src/ui/capture/document-preview.tsx +0 -166
- package/src/ui/capture/field-spec-to-zod.test.ts +0 -81
- package/src/ui/capture/field-spec-to-zod.ts +0 -174
- package/src/ui/capture/field-spec-validate.ts +0 -236
- package/src/ui/capture/field-spec.test.ts +0 -49
- package/src/ui/capture/field-spec.ts +0 -274
- package/src/ui/capture/index.ts +0 -51
- package/src/ui/capture/schema-field-input.tsx +0 -620
- package/src/ui/capture/schema-form.tsx +0 -149
- package/src/ui/capture/submit-values.test.ts +0 -70
- package/src/ui/capture/submit-values.ts +0 -63
- package/src/ui/capture/use-prefill.ts +0 -186
- package/src/ui/capture/use-schema-validation.ts +0 -145
- package/src/ui/capture/use-wizard.ts +0 -180
- package/src/ui/capture/value-format.ts +0 -32
- package/src/ui/capture/wizard.tsx +0 -105
- package/src/ui/charts/bar-chart.tsx +0 -139
- package/src/ui/charts/chart-frame.tsx +0 -125
- package/src/ui/charts/chart-types.ts +0 -52
- package/src/ui/charts/chart-utils.ts +0 -190
- package/src/ui/charts/charts.test.tsx +0 -204
- package/src/ui/charts/donut-chart.tsx +0 -129
- package/src/ui/charts/index.ts +0 -27
- package/src/ui/charts/line-chart.tsx +0 -202
- package/src/ui/charts/sparkline.tsx +0 -109
- package/src/ui/chrome/AsyncBoundary.tsx +0 -74
- package/src/ui/chrome/EmptyState.tsx +0 -257
- package/src/ui/chrome/ErrorCard.tsx +0 -63
- package/src/ui/chrome/LoadingState.tsx +0 -61
- package/src/ui/chrome/PageHeader.tsx +0 -137
- package/src/ui/chrome/RecordCardList.tsx +0 -98
- package/src/ui/chrome/StateCard.tsx +0 -150
- package/src/ui/chrome/confirm-delete-dialog.tsx +0 -59
- package/src/ui/chrome/page-shell.spec.tsx +0 -117
- package/src/ui/chrome/page-shell.tsx +0 -221
- package/src/ui/chrome/scope-badge.tsx +0 -40
- package/src/ui/chrome/status-badge.tsx +0 -79
- package/src/ui/cn.ts +0 -32
- package/src/ui/design-tokens.ts +0 -372
- package/src/ui/hooks/use-debounced-value.ts +0 -16
- package/src/ui/hooks/use-is-mobile.ts +0 -28
- package/src/ui/index.ts +0 -110
- package/src/ui/pdf/pdf-preview.tsx +0 -122
- package/src/ui/primitives/alert-dialog.tsx +0 -104
- package/src/ui/primitives/async-combobox.tsx +0 -182
- package/src/ui/primitives/badge-variants.ts +0 -21
- package/src/ui/primitives/badge.tsx +0 -25
- package/src/ui/primitives/button-variants.ts +0 -31
- package/src/ui/primitives/button.tsx +0 -29
- package/src/ui/primitives/card.tsx +0 -43
- package/src/ui/primitives/checkbox.tsx +0 -26
- package/src/ui/primitives/collapsible.tsx +0 -9
- package/src/ui/primitives/dialog.tsx +0 -103
- package/src/ui/primitives/dropdown-menu.tsx +0 -179
- package/src/ui/primitives/form-stepper.tsx +0 -94
- package/src/ui/primitives/form-stepper.types.ts +0 -15
- package/src/ui/primitives/input.tsx +0 -22
- package/src/ui/primitives/label.tsx +0 -17
- package/src/ui/primitives/overflow-tabs.tsx +0 -331
- package/src/ui/primitives/popover.tsx +0 -33
- package/src/ui/primitives/preview-device-frame.tsx +0 -213
- package/src/ui/primitives/radio-group.tsx +0 -36
- package/src/ui/primitives/resizable.tsx +0 -67
- package/src/ui/primitives/scroll-area.tsx +0 -38
- package/src/ui/primitives/select.tsx +0 -143
- package/src/ui/primitives/separator.tsx +0 -20
- package/src/ui/primitives/sheet.tsx +0 -107
- package/src/ui/primitives/skeleton.tsx +0 -99
- package/src/ui/primitives/switch.tsx +0 -27
- package/src/ui/primitives/table.tsx +0 -72
- package/src/ui/primitives/tabs.tsx +0 -53
- package/src/ui/primitives/tag-multi-select.tsx +0 -261
- package/src/ui/primitives/textarea.tsx +0 -21
- package/src/ui/primitives/tooltip.tsx +0 -30
- package/src/ui/primitives/url-preview-chrome.tsx +0 -103
- package/src/vista/GenericCatalogueSurface.tsx +0 -179
- package/src/vista/SurfaceFallbackCard.tsx +0 -109
- package/src/vista/VistaPanel.tsx +0 -163
- package/src/vista/form-payload.spec.ts +0 -139
- package/src/vista/html/CustomUiFrame.spec.tsx +0 -127
- package/src/vista/html/CustomUiFrame.tsx +0 -206
- package/src/vista/html/custom-ui-frame-bridge.spec.ts +0 -191
- package/src/vista/html/custom-ui-frame-bridge.ts +0 -98
- package/src/vista/html/custom-ui-frame-contract.ts +0 -88
- package/src/vista/html/custom-ui-intents.spec.ts +0 -135
- package/src/vista/html/custom-ui-intents.ts +0 -155
- package/src/vista/html/custom-ui-srcdoc.spec.ts +0 -151
- package/src/vista/html/custom-ui-srcdoc.ts +0 -193
- package/src/vista/html/index.ts +0 -13
- package/src/vista/index.ts +0 -52
- package/src/vista/surface-catalogue.spec.ts +0 -140
- package/src/vista/surface-catalogue.ts +0 -90
- package/src/vista/surface-icons.ts +0 -57
- package/src/vista/surface-registry.ts +0 -56
- package/src/vista/surfaces/ChartSurface.tsx +0 -34
- package/src/vista/surfaces/FilesSurface.tsx +0 -202
- package/src/vista/surfaces/FormSurface.tsx +0 -380
- package/src/vista/surfaces/HtmlSurface.tsx +0 -43
- package/src/vista/surfaces/TableSurface.tsx +0 -100
- package/src/vista/surfaces/UrlSurface.tsx +0 -40
- package/src/vista/surfaces/blob-surfaces.tsx +0 -139
- package/src/vista/surfaces/elicitation-transport.spec.ts +0 -369
- package/src/vista/surfaces/elicitation-transport.ts +0 -367
- package/src/vista/surfaces/file-surface-utils.ts +0 -69
- package/src/vista/surfaces/index.ts +0 -24
- package/src/vista/surfaces/register-surfaces.tsx +0 -139
- package/src/vista/surfaces/text-surfaces.tsx +0 -132
- package/src/vista/use-vista-tabs.ts +0 -196
- package/src/vista/vista-code-renderer-context.ts +0 -16
- package/src/vista/vista-code-renderer.tsx +0 -35
- package/src/vista/vista-file-context.ts +0 -14
- package/src/vista/vista-file-source.tsx +0 -141
- package/src/vista/vista-form-context.ts +0 -20
- package/src/vista/vista-form-host.tsx +0 -48
- package/src/vista/vista-model.ts +0 -137
- package/src/vista/vista-open-context.tsx +0 -27
- package/src/vista/vista-open-state.ts +0 -11
- package/src/vista/vista-payloads.ts +0 -635
- package/src/vista/vista-tabs-manifest.ts +0 -138
- 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.
|
|
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,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
|
-
}
|