@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,200 @@
|
|
|
1
|
+
import { DeeplinkTargetKind } from './enums';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Typed parse/resolve of the SystemBus `xema://` deeplink scheme.
|
|
5
|
+
*
|
|
6
|
+
* The deeplink scheme addresses *places in the running shell*. It is a
|
|
7
|
+
* distinct, narrower grammar from the kernel's `XemaObjectRef`
|
|
8
|
+
* (`xema://<scope>/<kind>/<slug>`): a deeplink's first segment is a
|
|
9
|
+
* {@link DeeplinkTargetKind} discriminator, not a scope path. The two
|
|
10
|
+
* schemes share the `xema://` scheme prefix but never the same body —
|
|
11
|
+
* `parseDeeplink` rejects anything whose head is not a known target kind,
|
|
12
|
+
* so an `XemaObjectRef` is only valid as a deeplink when wrapped as
|
|
13
|
+
* `xema://object/<XemaObjectRef-without-scheme>`.
|
|
14
|
+
*
|
|
15
|
+
* Grammar:
|
|
16
|
+
* xema://biome/<biomeId>/<route...>?<ctx> → BiomeRoute
|
|
17
|
+
* xema://object/<scope>/<kind>/<slug>[@<ver>] → Object
|
|
18
|
+
* xema://capability/<capability-ref>?<input> → Capability
|
|
19
|
+
* xema://window/<windowId> → Window
|
|
20
|
+
*
|
|
21
|
+
* Every parse is fail-fast: an unknown target kind, a missing required
|
|
22
|
+
* segment, or a malformed query is a {@link DeeplinkParseError}, never a
|
|
23
|
+
* silent fallback.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const DEEPLINK_SCHEME = 'xema://';
|
|
27
|
+
|
|
28
|
+
/** Structured, fail-fast error raised by `parseDeeplink`. */
|
|
29
|
+
export class DeeplinkParseError extends Error {
|
|
30
|
+
public readonly code = 'DEEPLINK_INVALID';
|
|
31
|
+
public readonly raw: string;
|
|
32
|
+
public readonly reason: string;
|
|
33
|
+
|
|
34
|
+
public constructor(raw: string, reason: string) {
|
|
35
|
+
super(`Invalid deeplink "${raw}": ${reason}`);
|
|
36
|
+
this.name = 'DeeplinkParseError';
|
|
37
|
+
this.raw = raw;
|
|
38
|
+
this.reason = reason;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** A parsed `xema://biome/...` deeplink. */
|
|
43
|
+
export interface BiomeRouteDeeplink {
|
|
44
|
+
readonly kind: DeeplinkTargetKind.BiomeRoute;
|
|
45
|
+
readonly biomeId: string;
|
|
46
|
+
/** The route path within the biome, with a leading slash. */
|
|
47
|
+
readonly route: string;
|
|
48
|
+
/** Parsed `?ctx` query params, empty when absent. */
|
|
49
|
+
readonly ctx: Readonly<Record<string, string>>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** A parsed `xema://object/...` deeplink. The body is the scheme-less
|
|
53
|
+
* `XemaObjectRef` (e.g. `system/capability/kb.page.read@1`). */
|
|
54
|
+
export interface ObjectDeeplink {
|
|
55
|
+
readonly kind: DeeplinkTargetKind.Object;
|
|
56
|
+
/** Full `XemaObjectRef` (`xema://` re-prefixed) for handing to the
|
|
57
|
+
* kernel object resolver. */
|
|
58
|
+
readonly objectRef: `xema://${string}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** A parsed `xema://capability/...` deeplink. */
|
|
62
|
+
export interface CapabilityDeeplink {
|
|
63
|
+
readonly kind: DeeplinkTargetKind.Capability;
|
|
64
|
+
/** The raw capability ref string (validated by the kernel
|
|
65
|
+
* `parseCapabilityRef` when the host launches it). */
|
|
66
|
+
readonly capabilityRef: string;
|
|
67
|
+
/** Parsed `?...` query params used as launch input, empty when absent. */
|
|
68
|
+
readonly input: Readonly<Record<string, string>>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** A parsed `xema://window/...` deeplink. */
|
|
72
|
+
export interface WindowDeeplink {
|
|
73
|
+
readonly kind: DeeplinkTargetKind.Window;
|
|
74
|
+
readonly windowId: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Discriminated union of every deeplink target. */
|
|
78
|
+
export type ParsedDeeplink =
|
|
79
|
+
| BiomeRouteDeeplink
|
|
80
|
+
| ObjectDeeplink
|
|
81
|
+
| CapabilityDeeplink
|
|
82
|
+
| WindowDeeplink;
|
|
83
|
+
|
|
84
|
+
function parseQuery(search: string): Record<string, string> {
|
|
85
|
+
const out: Record<string, string> = {};
|
|
86
|
+
if (search.length === 0) return out;
|
|
87
|
+
for (const pair of search.split('&')) {
|
|
88
|
+
if (pair.length === 0) continue;
|
|
89
|
+
const eq = pair.indexOf('=');
|
|
90
|
+
if (eq === -1) {
|
|
91
|
+
out[decodeURIComponent(pair)] = '';
|
|
92
|
+
} else {
|
|
93
|
+
out[decodeURIComponent(pair.slice(0, eq))] = decodeURIComponent(
|
|
94
|
+
pair.slice(eq + 1),
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Parse a `xema://` deeplink string into a typed, discriminated
|
|
103
|
+
* {@link ParsedDeeplink}. Throws {@link DeeplinkParseError} on any
|
|
104
|
+
* malformed input — never returns null, never coerces.
|
|
105
|
+
*/
|
|
106
|
+
export function parseDeeplink(raw: string): ParsedDeeplink {
|
|
107
|
+
if (typeof raw !== 'string' || !raw.startsWith(DEEPLINK_SCHEME)) {
|
|
108
|
+
throw new DeeplinkParseError(String(raw), `must start with "${DEEPLINK_SCHEME}"`);
|
|
109
|
+
}
|
|
110
|
+
const afterScheme = raw.slice(DEEPLINK_SCHEME.length);
|
|
111
|
+
if (afterScheme.length === 0) {
|
|
112
|
+
throw new DeeplinkParseError(raw, 'empty body after scheme');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const qIndex = afterScheme.indexOf('?');
|
|
116
|
+
const path = qIndex === -1 ? afterScheme : afterScheme.slice(0, qIndex);
|
|
117
|
+
const search = qIndex === -1 ? '' : afterScheme.slice(qIndex + 1);
|
|
118
|
+
const segments = path.split('/').filter((s) => s.length > 0);
|
|
119
|
+
|
|
120
|
+
const head = segments[0];
|
|
121
|
+
if (head === undefined) {
|
|
122
|
+
throw new DeeplinkParseError(raw, 'missing target-kind head segment');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
switch (head) {
|
|
126
|
+
case DeeplinkTargetKind.BiomeRoute: {
|
|
127
|
+
const biomeId = segments[1];
|
|
128
|
+
if (biomeId === undefined) {
|
|
129
|
+
throw new DeeplinkParseError(raw, 'biome deeplink missing <biomeId>');
|
|
130
|
+
}
|
|
131
|
+
const routeSegments = segments.slice(2);
|
|
132
|
+
const route = `/${routeSegments.join('/')}`;
|
|
133
|
+
return {
|
|
134
|
+
kind: DeeplinkTargetKind.BiomeRoute,
|
|
135
|
+
biomeId,
|
|
136
|
+
route,
|
|
137
|
+
ctx: parseQuery(search),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
case DeeplinkTargetKind.Object: {
|
|
141
|
+
const body = segments.slice(1).join('/');
|
|
142
|
+
if (body.length === 0) {
|
|
143
|
+
throw new DeeplinkParseError(raw, 'object deeplink missing <XemaObjectRef>');
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
kind: DeeplinkTargetKind.Object,
|
|
147
|
+
objectRef: `${DEEPLINK_SCHEME}${body}` as `xema://${string}`,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
case DeeplinkTargetKind.Capability: {
|
|
151
|
+
const capabilityRef = segments.slice(1).join('/');
|
|
152
|
+
if (capabilityRef.length === 0) {
|
|
153
|
+
throw new DeeplinkParseError(
|
|
154
|
+
raw,
|
|
155
|
+
'capability deeplink missing <capability-ref>',
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
kind: DeeplinkTargetKind.Capability,
|
|
160
|
+
capabilityRef,
|
|
161
|
+
input: parseQuery(search),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
case DeeplinkTargetKind.Window: {
|
|
165
|
+
const windowId = segments[1];
|
|
166
|
+
if (windowId === undefined) {
|
|
167
|
+
throw new DeeplinkParseError(raw, 'window deeplink missing <windowId>');
|
|
168
|
+
}
|
|
169
|
+
return { kind: DeeplinkTargetKind.Window, windowId };
|
|
170
|
+
}
|
|
171
|
+
default:
|
|
172
|
+
throw new DeeplinkParseError(
|
|
173
|
+
raw,
|
|
174
|
+
`unknown deeplink target kind "${head}" (expected one of: ${Object.values(
|
|
175
|
+
DeeplinkTargetKind,
|
|
176
|
+
).join(', ')})`,
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* The `deeplink` surface of the SystemBus. `parse` is pure
|
|
183
|
+
* (framework-agnostic) and re-exported from the runtime as
|
|
184
|
+
* {@link parseDeeplink}. `resolve` navigates the running shell to the
|
|
185
|
+
* parsed target; the host supplies the concrete navigation. Resolution
|
|
186
|
+
* is orchestration only — for a capability target the actual auth check
|
|
187
|
+
* happens when `capability.invoke` reaches the backend router.
|
|
188
|
+
*/
|
|
189
|
+
export interface DeeplinkBus {
|
|
190
|
+
/** Parse a `xema://` deeplink string. Throws on malformed input. */
|
|
191
|
+
parse(raw: string): ParsedDeeplink;
|
|
192
|
+
/**
|
|
193
|
+
* Navigate the running shell to the deeplink target. The host
|
|
194
|
+
* implements the actual navigation (biome route push, object detail
|
|
195
|
+
* open, capability launch, window focus). Rejects if the target cannot
|
|
196
|
+
* be resolved (e.g. unknown biome / window) — fail-fast, no silent
|
|
197
|
+
* no-op.
|
|
198
|
+
*/
|
|
199
|
+
resolve(target: string | ParsedDeeplink): Promise<void>;
|
|
200
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Closed sets for the `SystemBus` contract (Phase 6b).
|
|
3
|
+
*
|
|
4
|
+
* Every closed domain the SystemBus exposes is a real TypeScript enum so
|
|
5
|
+
* the contract is stable across hosts and serialises predictably. The
|
|
6
|
+
* `ObjectKind` domain deliberately REUSES the kernel's
|
|
7
|
+
* {@link XemaObjectKind} rather than re-declaring a parallel set — see
|
|
8
|
+
* `intents.ts`. The enums below describe SystemBus-specific closed
|
|
9
|
+
* domains that have no kernel equivalent.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The kind of target a `xema://` deeplink addresses. The deeplink scheme
|
|
14
|
+
* is intentionally narrower than the full `XemaObjectRef` grammar — a
|
|
15
|
+
* deeplink resolves to a *place in the running shell* (a biome route, an
|
|
16
|
+
* object detail page, a capability launcher, a window), not to every
|
|
17
|
+
* addressable object. New target kinds are added here by one-line kernel
|
|
18
|
+
* change; an unknown target kind is a fail-fast parse error.
|
|
19
|
+
*/
|
|
20
|
+
export enum DeeplinkTargetKind {
|
|
21
|
+
/** `xema://biome/<biomeId>/<route>?<ctx>` — navigate to a biome route. */
|
|
22
|
+
BiomeRoute = 'biome-route',
|
|
23
|
+
/** `xema://object/<xema-object-ref>` — open an object's detail surface. */
|
|
24
|
+
Object = 'object',
|
|
25
|
+
/** `xema://capability/<capability-ref>?<input>` — launch a capability. */
|
|
26
|
+
Capability = 'capability',
|
|
27
|
+
/** `xema://window/<windowId>` — focus an already-open window. */
|
|
28
|
+
Window = 'window',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Window operations the host window manager exposes through
|
|
33
|
+
* `SystemBus.windows`. Closed set — a host that cannot honour an op
|
|
34
|
+
* (e.g. a single-window mobile shell) fails fast rather than silently
|
|
35
|
+
* degrading; the SystemBus never invents a partial window model.
|
|
36
|
+
*/
|
|
37
|
+
export enum WindowOp {
|
|
38
|
+
Open = 'open',
|
|
39
|
+
Focus = 'focus',
|
|
40
|
+
Close = 'close',
|
|
41
|
+
Arrange = 'arrange',
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Layout arrangements a host may apply to its open windows when
|
|
46
|
+
* `WindowOp.Arrange` is requested. Closed set so the contract is stable
|
|
47
|
+
* across desktop / tiling / tabbed shells.
|
|
48
|
+
*/
|
|
49
|
+
export enum WindowArrangement {
|
|
50
|
+
/** Tile all open windows in a grid. */
|
|
51
|
+
Tile = 'tile',
|
|
52
|
+
/** Stack windows with the focused one on top. */
|
|
53
|
+
Stack = 'stack',
|
|
54
|
+
/** Cascade windows with a fixed offset. */
|
|
55
|
+
Cascade = 'cascade',
|
|
56
|
+
/** Maximise the focused window, hide the rest. */
|
|
57
|
+
Maximize = 'maximize',
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The kind of action an intent provides. An intent contributes either a
|
|
62
|
+
* cross-biome *capability* launch ("Send to Slack" → a capability ref)
|
|
63
|
+
* or an in-shell *deeplink* navigation ("Open with Document Buddy" → a
|
|
64
|
+
* biome route). Closed set — these are the only two ways one biome can
|
|
65
|
+
* hand an object to another without importing it.
|
|
66
|
+
*/
|
|
67
|
+
export enum ActionRefKind {
|
|
68
|
+
/** Resolves to a `CapabilityRef` invoked through `capability.invoke`. */
|
|
69
|
+
Capability = 'capability',
|
|
70
|
+
/** Resolves to a `xema://` deeplink navigated through `deeplink`. */
|
|
71
|
+
Deeplink = 'deeplink',
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The source that contributed a palette entry. The palette is the union
|
|
76
|
+
* of (a) every capability the actor may invoke (sourced from
|
|
77
|
+
* `capability-registry-api`) and (b) every registered cross-biome intent
|
|
78
|
+
* action. This enum tags each entry's provenance so the UI can group /
|
|
79
|
+
* filter and so the host knows which subsystem resolves the entry.
|
|
80
|
+
*/
|
|
81
|
+
export enum PaletteEntrySource {
|
|
82
|
+
/** Backed by a capability descriptor from `capability-registry-api`. */
|
|
83
|
+
Capability = 'capability',
|
|
84
|
+
/** Backed by a registered intent (`intents.register`). */
|
|
85
|
+
Intent = 'intent',
|
|
86
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { CapabilityRef } from '@xemahq/kernel-contracts/capability';
|
|
2
|
+
import type {
|
|
3
|
+
CapabilityInvokeMeta,
|
|
4
|
+
CapabilityInvokeResult,
|
|
5
|
+
} from './capability-invoke';
|
|
6
|
+
import type { PaletteEntry, PaletteQuery } from './palette';
|
|
7
|
+
import type {
|
|
8
|
+
OpenWindowRequest,
|
|
9
|
+
WindowId,
|
|
10
|
+
WindowState,
|
|
11
|
+
} from './windows';
|
|
12
|
+
import type { WindowArrangement } from './enums';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Framework-agnostic host ports the concrete host implements so the
|
|
16
|
+
* SystemBus sub-buses can be constructed by the framework-agnostic
|
|
17
|
+
* `buildSystemBus` assembler (shipped by this kernel).
|
|
18
|
+
*
|
|
19
|
+
* These ports carry NO React, NO router, NO transport client — they are
|
|
20
|
+
* the seam between "the SystemBus contract" (kernel) and "this host's
|
|
21
|
+
* real backend + shell" (the Next.js App Router host). The assembler wires a
|
|
22
|
+
* `SystemBus` out of `IntentRegistry` + `parseDeeplink` (both already in
|
|
23
|
+
* the kernel) PLUS these three host-supplied ports. A missing port is a
|
|
24
|
+
* wiring bug and MUST fail fast at bridge construction — never a silent
|
|
25
|
+
* no-op.
|
|
26
|
+
*
|
|
27
|
+
* None of these ports authorize. `CapabilityInvoker` relays to the
|
|
28
|
+
* backend capability-router (the single authority); `PaletteSource`
|
|
29
|
+
* indexes an already-actor-scoped registry; `WindowManager` is pure
|
|
30
|
+
* shell orchestration; `DeeplinkResolver` navigates. Policy lives in the
|
|
31
|
+
* backend, full stop.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Port the host implements to relay a `capability.invoke` to the backend
|
|
36
|
+
* capability-router. The adapter wraps this into `SystemBus.capability`.
|
|
37
|
+
*
|
|
38
|
+
* The host implementation POSTs the router's invoke endpoint with the
|
|
39
|
+
* Gate-E `ExecutionContext` built from {@link CapabilityInvokeMeta}. It
|
|
40
|
+
* MUST surface a policy denial as the typed result (`output === undefined`
|
|
41
|
+
* + populated `auditId`) and MUST reject on transport failure (fail-fast).
|
|
42
|
+
* It adds NO authorization of its own.
|
|
43
|
+
*/
|
|
44
|
+
export interface CapabilityInvoker {
|
|
45
|
+
invoke<TInput = unknown, TOutput = unknown>(
|
|
46
|
+
ref: CapabilityRef,
|
|
47
|
+
input: TInput,
|
|
48
|
+
meta: CapabilityInvokeMeta,
|
|
49
|
+
): Promise<CapabilityInvokeResult<TOutput>>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Port the host implements to source the capability half of the command
|
|
54
|
+
* palette from `capability-registry-api` (already actor-scoped
|
|
55
|
+
* server-side). The intent half is sourced by the adapter from the
|
|
56
|
+
* SystemBus `IntentRegistry`; the adapter merges both behind
|
|
57
|
+
* `SystemBus.palette`.
|
|
58
|
+
*
|
|
59
|
+
* `queryCapabilities` returns ONLY capability-backed entries; the adapter
|
|
60
|
+
* unions them with intent entries and applies the host ranker.
|
|
61
|
+
* `subscribe` fires when the capability source refreshes so a palette
|
|
62
|
+
* overlay can re-query without polling. Returns an unsubscribe fn.
|
|
63
|
+
*/
|
|
64
|
+
export interface PaletteSource {
|
|
65
|
+
queryCapabilities(query: PaletteQuery): Promise<readonly PaletteEntry[]>;
|
|
66
|
+
subscribe(listener: () => void): () => void;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Port the host implements to realise the window plane. The adapter
|
|
71
|
+
* exposes it verbatim as `SystemBus.windows`. Every op maps to a closed
|
|
72
|
+
* {@link WindowArrangement}/`WindowOp`; an op a host cannot honour MUST
|
|
73
|
+
* reject fail-fast rather than silently no-op.
|
|
74
|
+
*/
|
|
75
|
+
export interface WindowManager {
|
|
76
|
+
open(request: OpenWindowRequest): Promise<WindowId>;
|
|
77
|
+
focus(id: WindowId): Promise<void>;
|
|
78
|
+
close(id: WindowId): Promise<void>;
|
|
79
|
+
arrange(arrangement: WindowArrangement): Promise<void>;
|
|
80
|
+
list(): readonly WindowState[];
|
|
81
|
+
subscribe(listener: () => void): () => void;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Port the host implements to navigate the running shell to a parsed
|
|
86
|
+
* deeplink target. The adapter combines kernel `parseDeeplink` (pure)
|
|
87
|
+
* with this host-supplied `resolve` to build `SystemBus.deeplink`.
|
|
88
|
+
*
|
|
89
|
+
* `resolve` receives the ALREADY-PARSED, validated target (the adapter
|
|
90
|
+
* parses first, so a malformed deeplink fails fast before the host sees
|
|
91
|
+
* it). It MUST reject when the target cannot be resolved (unknown biome /
|
|
92
|
+
* window) — fail-fast, never a silent no-op.
|
|
93
|
+
*/
|
|
94
|
+
export interface DeeplinkResolver {
|
|
95
|
+
resolve(target: import('./deeplink').ParsedDeeplink): Promise<void>;
|
|
96
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SystemBus — the framework-agnostic OS-orchestration contract plane
|
|
3
|
+
* (Phase 6b). Pure types + a small framework-agnostic runtime
|
|
4
|
+
* ({@link IntentRegistry} + {@link parseDeeplink}). No React, no Vite, no
|
|
5
|
+
* Next.js, no React-Router, no transport client.
|
|
6
|
+
*/
|
|
7
|
+
export * from './enums';
|
|
8
|
+
export * from './capability-invoke';
|
|
9
|
+
export * from './intents';
|
|
10
|
+
export * from './intent-registry';
|
|
11
|
+
export * from './deeplink';
|
|
12
|
+
export * from './palette';
|
|
13
|
+
export * from './windows';
|
|
14
|
+
export * from './host-ports';
|
|
15
|
+
export * from './system-bus';
|
|
16
|
+
export * from './system-bus-builder';
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseXemaObjectRef,
|
|
3
|
+
type XemaObjectKind,
|
|
4
|
+
type XemaObjectRef,
|
|
5
|
+
} from '@xemahq/kernel-contracts/object';
|
|
6
|
+
import { ActionRefKind } from './enums';
|
|
7
|
+
import type {
|
|
8
|
+
ActionRef,
|
|
9
|
+
IntentBus,
|
|
10
|
+
IntentRegistration,
|
|
11
|
+
ResolvedIntent,
|
|
12
|
+
} from './intents';
|
|
13
|
+
|
|
14
|
+
const VALID_ACTION_KINDS: ReadonlySet<ActionRefKind> = new Set([
|
|
15
|
+
ActionRefKind.Capability,
|
|
16
|
+
ActionRefKind.Deeplink,
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
function assertValidRegistration(registration: IntentRegistration): void {
|
|
20
|
+
if (!registration.biomeId) {
|
|
21
|
+
throw new Error('[xema-ui-kernel] IntentRegistration.biomeId is required.');
|
|
22
|
+
}
|
|
23
|
+
for (const action of registration.provides) {
|
|
24
|
+
if (!VALID_ACTION_KINDS.has(action.kind)) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`[xema-ui-kernel] IntentRegistration from "${registration.biomeId}" ` +
|
|
27
|
+
`has an ActionRef with unknown kind "${(action as ActionRef).kind}". ` +
|
|
28
|
+
`Expected one of: ${[...VALID_ACTION_KINDS].join(', ')}.`,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Framework-agnostic, in-memory implementation of {@link IntentBus}.
|
|
36
|
+
*
|
|
37
|
+
* No React, no router — a plain registry a host can instantiate once and
|
|
38
|
+
* expose through `SystemBus.intents`. Registration order is preserved so
|
|
39
|
+
* `resolve` returns actions deterministically (first-registered first).
|
|
40
|
+
* Re-registering the same `biomeId` REPLACES its prior contribution while
|
|
41
|
+
* keeping its original ordering slot (HMR / re-bootstrap safe).
|
|
42
|
+
*
|
|
43
|
+
* Pure orchestration: it indexes and resolves contributions and performs
|
|
44
|
+
* no authorization. Whether the actor may run a resolved action is decided
|
|
45
|
+
* when the action fires.
|
|
46
|
+
*/
|
|
47
|
+
export class IntentRegistry implements IntentBus {
|
|
48
|
+
/** Insertion-ordered map: biomeId → its registration. */
|
|
49
|
+
private readonly registrations = new Map<string, IntentRegistration>();
|
|
50
|
+
private readonly listeners = new Set<() => void>();
|
|
51
|
+
|
|
52
|
+
register(registration: IntentRegistration): void {
|
|
53
|
+
assertValidRegistration(registration);
|
|
54
|
+
// Map preserves first-insertion order; deleting then setting on an
|
|
55
|
+
// update would move it to the end, so only delete when truly replacing.
|
|
56
|
+
this.registrations.set(registration.biomeId, registration);
|
|
57
|
+
this.notify();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
unregister(biomeId: string): boolean {
|
|
61
|
+
const removed = this.registrations.delete(biomeId);
|
|
62
|
+
if (removed) this.notify();
|
|
63
|
+
return removed;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
resolve(kind: XemaObjectKind): readonly ResolvedIntent[] {
|
|
67
|
+
const out: ResolvedIntent[] = [];
|
|
68
|
+
for (const registration of this.registrations.values()) {
|
|
69
|
+
if (!registration.handles.includes(kind)) continue;
|
|
70
|
+
for (const action of registration.provides) {
|
|
71
|
+
out.push({ biomeId: registration.biomeId, action });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
resolveForObject(ref: XemaObjectRef): readonly ResolvedIntent[] {
|
|
78
|
+
// Fail-fast on a malformed ref rather than returning an empty menu —
|
|
79
|
+
// a bad ref is a wiring bug, not "no handlers".
|
|
80
|
+
const { kind } = parseXemaObjectRef(ref);
|
|
81
|
+
return this.resolve(kind);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Insertion-ordered snapshot of every current registration. The command
|
|
86
|
+
* palette needs every provided action regardless of object kind (unlike
|
|
87
|
+
* `resolve`, which filters by a single kind), so it reads this snapshot
|
|
88
|
+
* and flattens `provides`. Returns a defensive shallow copy — callers
|
|
89
|
+
* MUST NOT mutate the registry through it.
|
|
90
|
+
*/
|
|
91
|
+
snapshot(): readonly IntentRegistration[] {
|
|
92
|
+
return [...this.registrations.values()];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Subscribe to registry changes; returns an unsubscribe fn. */
|
|
96
|
+
subscribe(listener: () => void): () => void {
|
|
97
|
+
this.listeners.add(listener);
|
|
98
|
+
return () => {
|
|
99
|
+
this.listeners.delete(listener);
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private notify(): void {
|
|
104
|
+
for (const listener of this.listeners) listener();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { CapabilityRef } from '@xemahq/kernel-contracts/capability';
|
|
2
|
+
import type { XemaObjectKind, XemaObjectRef } from '@xemahq/kernel-contracts/object';
|
|
3
|
+
import { ActionRefKind } from './enums';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Cross-biome intent registry — the "Open with… / Send to…" plane.
|
|
7
|
+
*
|
|
8
|
+
* A biome registers the object kinds it can *handle* and the actions it
|
|
9
|
+
* *provides*. When the user invokes "Open with… / Send to…" on an object,
|
|
10
|
+
* `intents.resolve(kind)` returns every action any biome registered for
|
|
11
|
+
* that kind. This is how one biome hands an object to another WITHOUT
|
|
12
|
+
* importing it — the macOS "Open With" / Share-Sheet analogy from the
|
|
13
|
+
* plan-of-record (§12.3).
|
|
14
|
+
*
|
|
15
|
+
* The registry is pure orchestration: it indexes contributions and
|
|
16
|
+
* resolves them. It performs NO authorization — whether the actor may
|
|
17
|
+
* actually run a resolved action is decided when the action fires
|
|
18
|
+
* (`capability.invoke` → backend router for capability actions; the host
|
|
19
|
+
* navigation guard for deeplink actions).
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A resolvable cross-biome action. Discriminated on {@link ActionRefKind}
|
|
24
|
+
* so a single resolved list can mix capability launches and deeplink
|
|
25
|
+
* navigations. Closed via the enum — there is no third way to hand an
|
|
26
|
+
* object across biomes.
|
|
27
|
+
*/
|
|
28
|
+
export type ActionRef =
|
|
29
|
+
| {
|
|
30
|
+
readonly kind: ActionRefKind.Capability;
|
|
31
|
+
/** Stable id, unique within the providing biome. */
|
|
32
|
+
readonly id: string;
|
|
33
|
+
/** Human label rendered in the "Send to…" menu. */
|
|
34
|
+
readonly label: string;
|
|
35
|
+
/** The capability the action invokes. */
|
|
36
|
+
readonly capabilityRef: CapabilityRef;
|
|
37
|
+
}
|
|
38
|
+
| {
|
|
39
|
+
readonly kind: ActionRefKind.Deeplink;
|
|
40
|
+
/** Stable id, unique within the providing biome. */
|
|
41
|
+
readonly id: string;
|
|
42
|
+
/** Human label rendered in the "Open with…" menu. */
|
|
43
|
+
readonly label: string;
|
|
44
|
+
/**
|
|
45
|
+
* Deeplink template the action navigates to. The host substitutes
|
|
46
|
+
* the selected object's ref into the template before navigating
|
|
47
|
+
* (e.g. `xema://biome/document-buddy/edit?object={ref}`).
|
|
48
|
+
*/
|
|
49
|
+
readonly deeplinkTemplate: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* One biome's intent contribution. `handles` is the closed set of object
|
|
54
|
+
* kinds this biome can receive; `provides` is the set of actions it
|
|
55
|
+
* offers for those kinds. Both reference the kernel's {@link XemaObjectKind}
|
|
56
|
+
* so the contract never drifts from the canonical object universe.
|
|
57
|
+
*/
|
|
58
|
+
export interface IntentRegistration {
|
|
59
|
+
/** Biome that owns this registration (provenance + dedup key). */
|
|
60
|
+
readonly biomeId: string;
|
|
61
|
+
/** Object kinds this biome can be a target of ("Open with…"). */
|
|
62
|
+
readonly handles: readonly XemaObjectKind[];
|
|
63
|
+
/** Actions this biome provides for the handled kinds. */
|
|
64
|
+
readonly provides: readonly ActionRef[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* A resolved intent action for a specific object kind — an {@link ActionRef}
|
|
69
|
+
* tagged with the biome that provides it. Returned by `intents.resolve`.
|
|
70
|
+
*/
|
|
71
|
+
export interface ResolvedIntent {
|
|
72
|
+
readonly biomeId: string;
|
|
73
|
+
readonly action: ActionRef;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The `intents` surface of the SystemBus. Framework-agnostic runtime
|
|
78
|
+
* registry — no React, no router. Hosts drive it at biome bootstrap
|
|
79
|
+
* (`register`) and at "Open with…" menu build time (`resolve`).
|
|
80
|
+
*/
|
|
81
|
+
export interface IntentBus {
|
|
82
|
+
/**
|
|
83
|
+
* Register a biome's intent contribution. Idempotent per `biomeId`:
|
|
84
|
+
* re-registering the same biome REPLACES its prior contribution (HMR /
|
|
85
|
+
* re-bootstrap safe). Fail-fast on a malformed registration (e.g. an
|
|
86
|
+
* `ActionRef` whose `kind` is outside {@link ActionRefKind}).
|
|
87
|
+
*/
|
|
88
|
+
register(registration: IntentRegistration): void;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Remove a biome's intent contribution (admin disable / unregister).
|
|
92
|
+
* Returns `true` if a registration was removed.
|
|
93
|
+
*/
|
|
94
|
+
unregister(biomeId: string): boolean;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Resolve every action any biome registered for `kind`, in stable
|
|
98
|
+
* registration order. Returns an empty array when no biome handles the
|
|
99
|
+
* kind — the caller renders an empty "Open with…" menu, never an error.
|
|
100
|
+
*/
|
|
101
|
+
resolve(kind: XemaObjectKind): readonly ResolvedIntent[];
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Resolve actions for a concrete object ref. Convenience over
|
|
105
|
+
* `resolve(kind)` that parses the kind out of the `XemaObjectRef` and
|
|
106
|
+
* is the entry point the "Open with…" menu uses on a selected object.
|
|
107
|
+
*/
|
|
108
|
+
resolveForObject(ref: XemaObjectRef): readonly ResolvedIntent[];
|
|
109
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { CapabilityRef } from '@xemahq/kernel-contracts/capability';
|
|
2
|
+
import { PaletteEntrySource } from './enums';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Command-palette plane of the SystemBus.
|
|
6
|
+
*
|
|
7
|
+
* The palette is the UNION of two sources (tagged by
|
|
8
|
+
* {@link PaletteEntrySource}):
|
|
9
|
+
* 1. every capability the actor may invoke — sourced from
|
|
10
|
+
* `capability-registry-api` (the host's capability-registry client);
|
|
11
|
+
* 2. every registered cross-biome intent action — sourced from the
|
|
12
|
+
* SystemBus `intents` registry.
|
|
13
|
+
*
|
|
14
|
+
* The palette performs NO authorization. The capability source is already
|
|
15
|
+
* actor-scoped server-side (the registry returns only capabilities the
|
|
16
|
+
* subject can see); selecting an entry routes through `capability.invoke`,
|
|
17
|
+
* where the backend router makes the final allow/deny decision. The
|
|
18
|
+
* palette is pure presentation orchestration.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/** A palette entry backed by a capability descriptor. */
|
|
22
|
+
export interface CapabilityPaletteEntry {
|
|
23
|
+
readonly source: PaletteEntrySource.Capability;
|
|
24
|
+
/** Stable id (the capability ref doubles as the id). */
|
|
25
|
+
readonly id: string;
|
|
26
|
+
/** Human label rendered in the palette row. */
|
|
27
|
+
readonly label: string;
|
|
28
|
+
/** Optional one-line description from the capability descriptor. */
|
|
29
|
+
readonly summary?: string;
|
|
30
|
+
/** The capability the entry invokes when selected. */
|
|
31
|
+
readonly capabilityRef: CapabilityRef;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** A palette entry backed by a registered cross-biome intent. */
|
|
35
|
+
export interface IntentPaletteEntry {
|
|
36
|
+
readonly source: PaletteEntrySource.Intent;
|
|
37
|
+
/** Stable id, unique across registered intents. */
|
|
38
|
+
readonly id: string;
|
|
39
|
+
/** Human label rendered in the palette row. */
|
|
40
|
+
readonly label: string;
|
|
41
|
+
/** Biome that provides the intent action. */
|
|
42
|
+
readonly biomeId: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Discriminated union of palette entries. */
|
|
46
|
+
export type PaletteEntry = CapabilityPaletteEntry | IntentPaletteEntry;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Query against the palette. `text` is matched by the host's own ranker
|
|
50
|
+
* (the contract does not prescribe a fuzzy algorithm). `sources` narrows
|
|
51
|
+
* to a subset of {@link PaletteEntrySource}; omitted means "all sources".
|
|
52
|
+
*/
|
|
53
|
+
export interface PaletteQuery {
|
|
54
|
+
readonly text: string;
|
|
55
|
+
readonly sources?: readonly PaletteEntrySource[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The `palette` surface of the SystemBus. Framework-agnostic — no React.
|
|
60
|
+
* The host wires a capability-registry client + the SystemBus intent
|
|
61
|
+
* registry behind `query`. `subscribe` lets a palette UI re-render when
|
|
62
|
+
* either source changes (a biome registers an intent, the registry
|
|
63
|
+
* refreshes).
|
|
64
|
+
*/
|
|
65
|
+
export interface PaletteBus {
|
|
66
|
+
/**
|
|
67
|
+
* Resolve palette entries matching `query`, ranked by the host. Async
|
|
68
|
+
* because the capability source may hit `capability-registry-api`.
|
|
69
|
+
*/
|
|
70
|
+
query(query: PaletteQuery): Promise<readonly PaletteEntry[]>;
|
|
71
|
+
/**
|
|
72
|
+
* Subscribe to palette-source changes. Returns an unsubscribe fn. Fires
|
|
73
|
+
* when a biome registers/unregisters an intent or the capability source
|
|
74
|
+
* is refreshed — so a palette overlay can re-query without polling.
|
|
75
|
+
*/
|
|
76
|
+
subscribe(listener: () => void): () => void;
|
|
77
|
+
}
|