@xemahq/ui-kernel 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/biome-host/frontend-biome.d.ts +6 -3
  6. package/dist/lib/biome-host/frontend-biome.d.ts.map +1 -1
  7. package/dist/lib/biome-host/frontend-biome.js +9 -0
  8. package/dist/lib/biome-host/frontend-biome.js.map +1 -1
  9. package/dist/offline/id.d.ts +2 -0
  10. package/dist/offline/id.d.ts.map +1 -0
  11. package/dist/offline/id.js +15 -0
  12. package/dist/offline/id.js.map +1 -0
  13. package/dist/offline/index.d.ts +9 -0
  14. package/dist/offline/index.d.ts.map +1 -0
  15. package/dist/offline/index.js +26 -0
  16. package/dist/offline/index.js.map +1 -0
  17. package/dist/offline/mutation.d.ts +15 -0
  18. package/dist/offline/mutation.d.ts.map +1 -0
  19. package/dist/offline/mutation.js +37 -0
  20. package/dist/offline/mutation.js.map +1 -0
  21. package/dist/offline/offline-status-badge.d.ts +7 -0
  22. package/dist/offline/offline-status-badge.d.ts.map +1 -0
  23. package/dist/offline/offline-status-badge.js +21 -0
  24. package/dist/offline/offline-status-badge.js.map +1 -0
  25. package/dist/offline/online-controller.d.ts +18 -0
  26. package/dist/offline/online-controller.d.ts.map +1 -0
  27. package/dist/offline/online-controller.js +79 -0
  28. package/dist/offline/online-controller.js.map +1 -0
  29. package/dist/offline/outbox.d.ts +57 -0
  30. package/dist/offline/outbox.d.ts.map +1 -0
  31. package/dist/offline/outbox.js +257 -0
  32. package/dist/offline/outbox.js.map +1 -0
  33. package/dist/offline/react.d.ts +13 -0
  34. package/dist/offline/react.d.ts.map +1 -0
  35. package/dist/offline/react.js +42 -0
  36. package/dist/offline/react.js.map +1 -0
  37. package/dist/offline/storage.d.ts +22 -0
  38. package/dist/offline/storage.d.ts.map +1 -0
  39. package/dist/offline/storage.js +90 -0
  40. package/dist/offline/storage.js.map +1 -0
  41. package/dist/offline/types.d.ts +38 -0
  42. package/dist/offline/types.d.ts.map +1 -0
  43. package/dist/offline/types.js +11 -0
  44. package/dist/offline/types.js.map +1 -0
  45. package/dist/ui/branding/app-branding-provider.d.ts +1 -1
  46. package/dist/ui/branding/app-branding-provider.d.ts.map +1 -1
  47. package/dist/ui/capture/field-spec-to-zod.d.ts.map +1 -1
  48. package/dist/ui/capture/field-spec-to-zod.js +9 -1
  49. package/dist/ui/capture/field-spec-to-zod.js.map +1 -1
  50. package/dist/ui/capture/field-spec.d.ts +6 -1
  51. package/dist/ui/capture/field-spec.d.ts.map +1 -1
  52. package/dist/ui/capture/field-spec.js +3 -0
  53. package/dist/ui/capture/field-spec.js.map +1 -1
  54. package/dist/ui/capture/schema-field-input.d.ts.map +1 -1
  55. package/dist/ui/capture/schema-field-input.js +63 -0
  56. package/dist/ui/capture/schema-field-input.js.map +1 -1
  57. package/package.json +2 -2
  58. package/src/index.ts +4 -0
  59. package/src/lib/biome-host/define-web-biome.spec.ts +221 -0
  60. package/src/lib/biome-host/frontend-biome.ts +32 -5
  61. package/src/offline/id.ts +16 -0
  62. package/src/offline/index.ts +52 -0
  63. package/src/offline/mutation.ts +75 -0
  64. package/src/offline/offline-status-badge.tsx +51 -0
  65. package/src/offline/online-controller.ts +105 -0
  66. package/src/offline/outbox.test.ts +156 -0
  67. package/src/offline/outbox.ts +344 -0
  68. package/src/offline/react.tsx +71 -0
  69. package/src/offline/storage.ts +107 -0
  70. package/src/offline/types.ts +77 -0
  71. package/src/ui/branding/app-branding-provider.tsx +1 -1
  72. package/src/ui/capture/field-spec-to-zod.test.ts +22 -0
  73. package/src/ui/capture/field-spec-to-zod.ts +12 -1
  74. package/src/ui/capture/field-spec.ts +24 -3
  75. package/src/ui/capture/schema-field-input.tsx +129 -1
  76. package/src/lib/biome-host/define-web-biome.test.js +0 -93
@@ -1 +1 @@
1
- {"version":3,"file":"schema-field-input.d.ts","sourceRoot":"","sources":["../../../src/ui/capture/schema-field-input.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AASvC,OAAO,EAAoB,KAAK,gBAAgB,EAAsB,MAAM,cAAc,CAAC;AAO3F,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE;IACnC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,KAAK,SAAS,CAAC;AAEhB,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAE5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,CAAC;CAC5C;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,+BA0D5D"}
1
+ {"version":3,"file":"schema-field-input.d.ts","sourceRoot":"","sources":["../../../src/ui/capture/schema-field-input.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAStE,OAAO,EAAoB,KAAK,gBAAgB,EAAsB,MAAM,cAAc,CAAC;AAO3F,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE;IACnC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,KAAK,SAAS,CAAC;AAEhB,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAE5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,CAAC;CAC5C;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,+BA+D5D"}
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SchemaFieldInput = SchemaFieldInput;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const lucide_react_1 = require("lucide-react");
6
+ const react_1 = require("react");
6
7
  const cn_1 = require("../cn");
7
8
  const button_1 = require("../primitives/button");
8
9
  const checkbox_1 = require("../primitives/checkbox");
@@ -38,10 +39,72 @@ function SchemaFieldInput(props) {
38
39
  return (0, jsx_runtime_1.jsx)(ObjectInput, { ...props });
39
40
  case field_spec_1.CaptureFieldType.List:
40
41
  return (0, jsx_runtime_1.jsx)(ListInput, { ...props });
42
+ case field_spec_1.CaptureFieldType.Image:
43
+ case field_spec_1.CaptureFieldType.File:
44
+ return (0, jsx_runtime_1.jsx)(MediaFieldInput, { ...props });
45
+ case field_spec_1.CaptureFieldType.Audio:
46
+ return (0, jsx_runtime_1.jsx)(AudioFieldInput, { ...props });
41
47
  default:
42
48
  return (0, jsx_runtime_1.jsx)(input_1.Input, { id: inputId, value: (0, value_format_1.asString)(value), onChange: (e) => onChange(e.target.value), disabled: disabled, placeholder: field.placeholder });
43
49
  }
44
50
  }
51
+ const FILE_INPUT_CLASS = 'block w-full text-body-2 text-foreground file:mr-3 file:rounded-md file:border-0 file:bg-secondary file:px-3 file:py-1.5 file:text-body-2 file:text-secondary-foreground hover:file:bg-secondary/80 disabled:cursor-not-allowed disabled:opacity-50';
52
+ function readAsDataUrl(file, onDone) {
53
+ const reader = new FileReader();
54
+ reader.onload = () => onDone(typeof reader.result === 'string' ? reader.result : '');
55
+ reader.readAsDataURL(file);
56
+ }
57
+ function MediaFieldInput({ field, value, onChange, disabled, path }) {
58
+ const inputId = `field-${path}`;
59
+ const current = (0, value_format_1.asString)(value);
60
+ const isImage = field.type === field_spec_1.CaptureFieldType.Image;
61
+ const accept = field.accept ?? (isImage ? 'image/*' : undefined);
62
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "mt-1 space-y-2", children: [(0, jsx_runtime_1.jsx)("input", { id: inputId, type: "file", className: FILE_INPUT_CLASS, disabled: disabled, ...(accept ? { accept } : {}), ...(field.capture ? { capture: field.capture } : {}), onChange: (e) => {
63
+ const file = e.target.files?.[0];
64
+ if (file) {
65
+ readAsDataUrl(file, onChange);
66
+ }
67
+ } }), current ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [isImage && current.startsWith('data:image') ? ((0, jsx_runtime_1.jsx)("img", { src: current, alt: field.label, className: "max-h-32 rounded-md border border-input" })) : ((0, jsx_runtime_1.jsx)("span", { className: "truncate text-body-2 text-muted-foreground", children: "Attached" })), (0, jsx_runtime_1.jsx)(button_1.Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onChange(''), disabled: disabled, children: "Clear" })] })) : null] }));
68
+ }
69
+ function AudioFieldInput({ value, onChange, disabled, path }) {
70
+ const inputId = `field-${path}`;
71
+ const current = (0, value_format_1.asString)(value);
72
+ const [recording, setRecording] = (0, react_1.useState)(false);
73
+ const [error, setError] = (0, react_1.useState)(null);
74
+ const recorderRef = (0, react_1.useRef)(null);
75
+ const chunksRef = (0, react_1.useRef)([]);
76
+ const start = (0, react_1.useCallback)(async () => {
77
+ setError(null);
78
+ try {
79
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
80
+ const recorder = new MediaRecorder(stream);
81
+ chunksRef.current = [];
82
+ recorder.ondataavailable = (e) => {
83
+ if (e.data.size > 0) {
84
+ chunksRef.current.push(e.data);
85
+ }
86
+ };
87
+ recorder.onstop = () => {
88
+ for (const track of stream.getTracks()) {
89
+ track.stop();
90
+ }
91
+ readAsDataUrl(new Blob(chunksRef.current, { type: recorder.mimeType || 'audio/webm' }), onChange);
92
+ };
93
+ recorderRef.current = recorder;
94
+ recorder.start();
95
+ setRecording(true);
96
+ }
97
+ catch {
98
+ setError('Microphone unavailable or permission denied.');
99
+ }
100
+ }, [onChange]);
101
+ const stop = (0, react_1.useCallback)(() => {
102
+ recorderRef.current?.stop();
103
+ recorderRef.current = null;
104
+ setRecording(false);
105
+ }, []);
106
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "mt-1 space-y-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [recording ? ((0, jsx_runtime_1.jsx)(button_1.Button, { type: "button", variant: "secondary", size: "sm", onClick: stop, disabled: disabled, children: "Stop" })) : ((0, jsx_runtime_1.jsx)(button_1.Button, { type: "button", variant: "secondary", size: "sm", onClick: () => void start(), disabled: disabled, children: current ? 'Re-record' : 'Record' })), current ? ((0, jsx_runtime_1.jsx)(button_1.Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onChange(''), disabled: disabled, children: "Clear" })) : null] }), error ? (0, jsx_runtime_1.jsx)("p", { className: "text-body-2 text-destructive", children: error }) : null, current && !recording ? (0, jsx_runtime_1.jsx)("audio", { id: inputId, controls: true, src: current, className: "w-full" }) : null] }));
107
+ }
45
108
  function NestedField(props) {
46
109
  const { field, path, errors } = props;
47
110
  const error = errors?.[path];
@@ -1 +1 @@
1
- {"version":3,"file":"schema-field-input.js","sourceRoot":"","sources":["../../../src/ui/capture/schema-field-input.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema-field-input.js","sourceRoot":"","sources":["../../../src/ui/capture/schema-field-input.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xemahq/ui-kernel",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "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.",
5
5
  "keywords": [
6
6
  "xema",
@@ -75,7 +75,7 @@
75
75
  "prettier": "3.6.2",
76
76
  "react": "^18.3.1",
77
77
  "typescript": "5.9.3",
78
- "@xemahq/kernel-contracts": "^0.39.0"
78
+ "@xemahq/kernel-contracts": "^0.48.0"
79
79
  },
80
80
  "exports": {
81
81
  ".": {
package/src/index.ts CHANGED
@@ -6,3 +6,7 @@ export * from './lib/capabilities';
6
6
  // subpath alongside the rest of the shared UI surface; re-exported here so the
7
7
  // capture primitives are reachable from the package root too.
8
8
  export * from './ui/capture';
9
+
10
+ // Offline field-capture kit — durable outbox that queues mutations while
11
+ // disconnected and drains them on reconnect. Also at `@xemahq/ui-kernel/offline`.
12
+ export * from './offline';
@@ -0,0 +1,221 @@
1
+ /**
2
+ * `defineWebBiome` — define-time scope/access locking, the fail-closed
3
+ * defaults, and closed-set completeness.
4
+ *
5
+ * This suite replaces an earlier `define-web-biome.test.js` written for
6
+ * Node's test runner against `dist/`. That file ran NOWHERE: the package's
7
+ * `test` script is a notice (the kernel carries no test runner by design) and
8
+ * the host-web vitest `include` is `*.{test,spec}.{ts,tsx}` — so a `.js` file
9
+ * was invisible to both. A security test that never executes reads as
10
+ * coverage while proving nothing, so it is gone; this vitest spec is synced
11
+ * into the host mirror by the frontend bridge and runs there with everything
12
+ * else.
13
+ *
14
+ * The invariant under test is the security one: a route is public ONLY when
15
+ * its author said so twice, explicitly. Nothing defaulted, inferred, or
16
+ * coerced can produce a public route.
17
+ */
18
+ import { describe, expect, it } from 'vitest';
19
+
20
+ import { defineWebBiome } from './define-web-biome';
21
+ import {
22
+ PAGE_CHROME_VALUES,
23
+ ROUTE_ACCESS_VALUES,
24
+ ROUTE_SCOPE_VALUES,
25
+ type PageChrome,
26
+ type RouteAccess,
27
+ type RouteScope,
28
+ } from './frontend-biome';
29
+
30
+ import type { HostBridge } from './host-bridge';
31
+
32
+ const load = () => Promise.resolve({ default: () => null });
33
+ const bridge = {} as HostBridge;
34
+
35
+ describe('defineWebBiome — public/chromeless scope', () => {
36
+ it('rejects a public page whose access defaults to member', () => {
37
+ expect(() =>
38
+ defineWebBiome({
39
+ id: 'test-biome',
40
+ displayName: 'Test',
41
+ pages: [{ slug: 'ext/:id', label: 'Ext', scope: 'public', load }],
42
+ }),
43
+ ).toThrow(/a 'public' scope page MUST declare access: 'public'/);
44
+ });
45
+
46
+ it('rejects a public page with an explicit non-public access tier', () => {
47
+ expect(() =>
48
+ defineWebBiome({
49
+ id: 'test-biome',
50
+ displayName: 'Test',
51
+ pages: [
52
+ {
53
+ slug: 'ext/:id',
54
+ label: 'Ext',
55
+ scope: 'public',
56
+ access: 'org-admin',
57
+ load,
58
+ },
59
+ ],
60
+ }),
61
+ ).toThrow(/a 'public' scope page MUST declare access: 'public'/);
62
+ });
63
+
64
+ it('rejects a non-public page that declares access public', () => {
65
+ expect(() =>
66
+ defineWebBiome({
67
+ id: 'test-biome',
68
+ displayName: 'Test',
69
+ pages: [{ slug: 'system/x', label: 'X', access: 'public', load }],
70
+ }),
71
+ ).toThrow(/access: 'public' is only valid on a 'public' scope page/);
72
+ });
73
+
74
+ it('accepts a public + access:public page and emits no nav item', () => {
75
+ const biome = defineWebBiome({
76
+ id: 'test-biome',
77
+ displayName: 'Test',
78
+ pages: [
79
+ {
80
+ slug: 'ext/:id',
81
+ label: 'Ext',
82
+ scope: 'public',
83
+ access: 'public',
84
+ load,
85
+ },
86
+ ],
87
+ })(bridge);
88
+
89
+ expect(biome.routes).toHaveLength(1);
90
+ const route = biome.routes?.[0];
91
+ expect(route?.path).toBe('ext/:id');
92
+ expect(route?.access).toBe('public');
93
+ expect(route?.projectScoped).toBe(false);
94
+ // A public route is un-authenticated; it never belongs in the authed rail.
95
+ expect(biome.navItems).toHaveLength(0);
96
+ });
97
+
98
+ it('chrome defaults to host and none passes through', () => {
99
+ const biome = defineWebBiome({
100
+ id: 'test-biome',
101
+ displayName: 'Test',
102
+ pages: [
103
+ { slug: 'system/a', label: 'A', load },
104
+ { slug: 'system/b', label: 'B', chrome: 'none', load },
105
+ ],
106
+ })(bridge);
107
+
108
+ expect(biome.routes?.find((r) => r.path === 'system/a')?.chrome).toBe(
109
+ 'host',
110
+ );
111
+ expect(biome.routes?.find((r) => r.path === 'system/b')?.chrome).toBe(
112
+ 'none',
113
+ );
114
+ });
115
+ });
116
+
117
+ describe('defineWebBiome — public is opt-in, never a default', () => {
118
+ /**
119
+ * THE fail-closed invariant. An ordinarily-declared page (no `scope`, no
120
+ * `access`) must emit a route that carries NO `access` marker at all — the
121
+ * host then applies its own `member` default. If the kernel ever started
122
+ * defaulting `access` (or `scope`) to `'public'`, every existing biome route
123
+ * in the fleet would silently become reachable without a session.
124
+ *
125
+ * Mutation-proof (recorded 2026-07-22): flipping the emit in
126
+ * `defineWebBiome` to `access: page.access ?? 'public'` turns this test RED
127
+ * ("expected 'public' to be undefined"), and flipping the validator default
128
+ * `page.access ?? 'member'` to `?? 'public'` makes the ordinary page throw
129
+ * at define time — RED in this test and in the two below. There is no
130
+ * mutation of a default that makes a route public and keeps the suite green.
131
+ */
132
+ it('an ordinary page emits NO access marker (host defaults to member)', () => {
133
+ const biome = defineWebBiome({
134
+ id: 'test-biome',
135
+ displayName: 'Test',
136
+ pages: [{ slug: 'system/a', label: 'A', load }],
137
+ })(bridge);
138
+
139
+ const route = biome.routes?.[0];
140
+ expect(route?.access).toBeUndefined();
141
+ expect(route?.projectScoped).toBe(false);
142
+ // …and it DOES contribute a nav item, unlike a public route.
143
+ expect(biome.navItems).toHaveLength(1);
144
+ });
145
+
146
+ it('an authenticated page can be chromeless without becoming public', () => {
147
+ // Chrome and access are orthogonal axes: the embedded-app case.
148
+ const biome = defineWebBiome({
149
+ id: 'test-biome',
150
+ displayName: 'Test',
151
+ pages: [
152
+ {
153
+ slug: 'apps/:appId/embed',
154
+ label: 'Embedded app',
155
+ chrome: 'none',
156
+ navHidden: true,
157
+ load,
158
+ },
159
+ ],
160
+ })(bridge);
161
+
162
+ const route = biome.routes?.[0];
163
+ expect(route?.chrome).toBe('none');
164
+ expect(route?.access).toBeUndefined();
165
+ });
166
+
167
+ it('every non-public access tier is rejected on a public page', () => {
168
+ for (const access of ROUTE_ACCESS_VALUES.filter((a) => a !== 'public')) {
169
+ expect(() =>
170
+ defineWebBiome({
171
+ id: 'test-biome',
172
+ displayName: 'Test',
173
+ pages: [{ slug: 'ext', label: 'Ext', scope: 'public', access, load }],
174
+ }),
175
+ ).toThrow(/MUST declare access: 'public'/);
176
+ }
177
+ });
178
+
179
+ it('every non-public scope rejects access public', () => {
180
+ for (const scope of ROUTE_SCOPE_VALUES.filter((s) => s !== 'public')) {
181
+ expect(() =>
182
+ defineWebBiome({
183
+ id: 'test-biome',
184
+ displayName: 'Test',
185
+ pages: [{ slug: 'x', label: 'X', scope, access: 'public', load }],
186
+ }),
187
+ ).toThrow(/only valid on a 'public' scope page/);
188
+ }
189
+ });
190
+ });
191
+
192
+ describe('closed-set completeness', () => {
193
+ /**
194
+ * The runtime tuples ARE the types (`RouteAccess = typeof
195
+ * ROUTE_ACCESS_VALUES[number]`), so a new member cannot be added to the type
196
+ * without appearing here — which is what makes the exhaustive iteration in
197
+ * this file, and the host resolver's `default`-less switch, complete by
198
+ * construction. These assertions are the compile-time proof made executable:
199
+ * the `satisfies` widening below fails to compile if a tuple ever drifts
200
+ * from its type.
201
+ */
202
+ it('the access tuple enumerates exactly the RouteAccess union', () => {
203
+ const all: readonly RouteAccess[] =
204
+ ROUTE_ACCESS_VALUES satisfies readonly RouteAccess[];
205
+ expect([...all].sort()).toEqual(
206
+ ['member', 'org-admin', 'platform-admin', 'public'].sort(),
207
+ );
208
+ });
209
+
210
+ it('the scope tuple enumerates exactly the RouteScope union', () => {
211
+ const all: readonly RouteScope[] =
212
+ ROUTE_SCOPE_VALUES satisfies readonly RouteScope[];
213
+ expect([...all].sort()).toEqual(['org', 'project', 'public'].sort());
214
+ });
215
+
216
+ it('the chrome tuple enumerates exactly the PageChrome union', () => {
217
+ const all: readonly PageChrome[] =
218
+ PAGE_CHROME_VALUES satisfies readonly PageChrome[];
219
+ expect([...all].sort()).toEqual(['host', 'none'].sort());
220
+ });
221
+ });
@@ -50,10 +50,25 @@ export interface NavItemContribution {
50
50
  * authenticated route group. It is value-locked to `RouteScope` `'public'`
51
51
  * (`defineWebBiome` rejects any other pairing at define time), so a route is
52
52
  * public in BOTH senses or in neither. Closed set — a new member is a
53
- * one-line change here and one `case` in the host resolver's exhaustive
54
- * `hasRouteAccess` switch.
53
+ * one-line change to {@link ROUTE_ACCESS_VALUES} and one `case` in the host
54
+ * resolver's exhaustive `hasRouteAccess` switch (the switch has no `default`,
55
+ * so an unhandled member is a compile error, never a silent allow).
56
+ *
57
+ * The tuple below is the SINGLE source of truth: the type is derived from it,
58
+ * so a member cannot exist in the type without also being enumerable at
59
+ * runtime — which is what lets a host prove exhaustiveness in a test rather
60
+ * than by inspection. Order is least → most privileged; `public` is the
61
+ * no-auth marker and is deliberately last.
55
62
  */
56
- export type RouteAccess = 'member' | 'org-admin' | 'platform-admin' | 'public';
63
+ export const ROUTE_ACCESS_VALUES = [
64
+ 'member',
65
+ 'org-admin',
66
+ 'platform-admin',
67
+ 'public',
68
+ ] as const;
69
+
70
+ /** Access tier required to render a route. See {@link ROUTE_ACCESS_VALUES}. */
71
+ export type RouteAccess = (typeof ROUTE_ACCESS_VALUES)[number];
57
72
 
58
73
  /**
59
74
  * Where a route mounts in the host's URL/auth topology. Closed set — adding a
@@ -65,8 +80,13 @@ export type RouteAccess = 'member' | 'org-admin' | 'platform-admin' | 'public';
65
80
  * authenticated group.
66
81
  * - `public` — mounts OUTSIDE the authenticated group; no Keycloak session,
67
82
  * no login redirect, no org switcher. Locked to `RouteAccess` `'public'`.
83
+ *
84
+ * Single source of truth for the closed set; the type is derived from it.
68
85
  */
69
- export type RouteScope = 'org' | 'project' | 'public';
86
+ export const ROUTE_SCOPE_VALUES = ['org', 'project', 'public'] as const;
87
+
88
+ /** Where a route mounts. See {@link ROUTE_SCOPE_VALUES}. */
89
+ export type RouteScope = (typeof ROUTE_SCOPE_VALUES)[number];
70
90
 
71
91
  /**
72
92
  * Whether a page renders wrapped in the host application shell (app rail,
@@ -78,8 +98,15 @@ export type RouteScope = 'org' | 'project' | 'public';
78
98
  * switcher (a full-bleed surface). `public` routes are structurally
79
99
  * chromeless (their route group ships no shell); `none` also lets an
80
100
  * authenticated route opt out of the shell.
101
+ *
102
+ * Single source of truth for the closed set; the type is derived from it.
103
+ * Chrome is ORTHOGONAL to access: `chrome: 'none'` on an authenticated route
104
+ * is the embedded-app case and grants no privilege change whatsoever.
81
105
  */
82
- export type PageChrome = 'host' | 'none';
106
+ export const PAGE_CHROME_VALUES = ['host', 'none'] as const;
107
+
108
+ /** Host-shell mode a page renders in. See {@link PAGE_CHROME_VALUES}. */
109
+ export type PageChrome = (typeof PAGE_CHROME_VALUES)[number];
83
110
 
84
111
  export interface RouteContribution {
85
112
  /** React-router path. May include params. */
@@ -0,0 +1,16 @@
1
+ /** RFC-4122 id, using the platform crypto when available (browser + Node 18+),
2
+ * with a non-cryptographic fallback for exotic runtimes. */
3
+ export function newId(): string {
4
+ const c = (globalThis as { crypto?: Crypto }).crypto;
5
+ if (c && typeof c.randomUUID === 'function') {
6
+ return c.randomUUID();
7
+ }
8
+ // Fallback — good enough for an outbox entry id (not a security token).
9
+ return (
10
+ 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (ch) => {
11
+ const r = (Math.floor(Math.random() * 16) + Date.now()) % 16;
12
+ const v = ch === 'x' ? r : (r & 0x3) | 0x8;
13
+ return v.toString(16);
14
+ })
15
+ );
16
+ }
@@ -0,0 +1,52 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════
2
+ // @xemahq/ui-kernel/offline — generic offline field-capture kit
3
+ //
4
+ // A durable, provider-agnostic mutation outbox: queue mutations while
5
+ // disconnected (IndexedDB) and drain them on reconnect with retry, backoff, and
6
+ // idempotency. Pairs with the existing guided-capture kit (`../ui/capture`) and
7
+ // the existing mobile off-canvas shell so a form captured in the field (trade
8
+ // fair, no signal) is never lost. The immediate-online path is unchanged; only
9
+ // a failed/offline send is queued.
10
+ // ═══════════════════════════════════════════════════════════════════════════
11
+
12
+ export { newId } from './id';
13
+ export {
14
+ OutboxEntryStatus,
15
+ type OutboxEntry,
16
+ type OutboxStorage,
17
+ type OutboxExecutor,
18
+ type OutboxExecuteContext,
19
+ type OutboxSnapshot,
20
+ } from './types';
21
+ export {
22
+ InMemoryOutboxStorage,
23
+ IndexedDbOutboxStorage,
24
+ createDefaultOutboxStorage,
25
+ } from './storage';
26
+ export {
27
+ OnlineController,
28
+ isNetworkUnavailableError,
29
+ type OnlineListener,
30
+ } from './online-controller';
31
+ export {
32
+ Outbox,
33
+ type OutboxOptions,
34
+ type OutboxListener,
35
+ type EnqueueOptions,
36
+ } from './outbox';
37
+ export {
38
+ createOfflineMutationFn,
39
+ type OfflineMutationResult,
40
+ type OfflineMutationOptions,
41
+ } from './mutation';
42
+ export {
43
+ OutboxProvider,
44
+ useOutbox,
45
+ useOutboxSnapshot,
46
+ useOnlineStatus,
47
+ type OutboxProviderProps,
48
+ } from './react';
49
+ export {
50
+ OfflineStatusBadge,
51
+ type OfflineStatusBadgeProps,
52
+ } from './offline-status-badge';
@@ -0,0 +1,75 @@
1
+ import { newId } from './id';
2
+ import { isNetworkUnavailableError } from './online-controller';
3
+
4
+ import type { Outbox } from './outbox';
5
+ import type { OutboxExecuteContext } from './types';
6
+
7
+ /** Outcome of an offline-aware mutation. */
8
+ export interface OfflineMutationResult<TResult> {
9
+ /** True when the mutation was queued for later delivery (offline / network
10
+ * failure) instead of applied immediately. */
11
+ queued: boolean;
12
+ /** The outbox entry id when queued (for optimistic UI correlation). */
13
+ entryId?: string;
14
+ /** The server result when applied immediately (`queued === false`). */
15
+ result?: TResult;
16
+ }
17
+
18
+ export interface OfflineMutationOptions<TPayload, TResult> {
19
+ outbox: Outbox;
20
+ /** Stable executor key for this mutation type (e.g. "app-forge:create-record"). */
21
+ kind: string;
22
+ /** Sends the mutation to the backend. Used BOTH for the immediate online path
23
+ * and for draining the queue. Pass `ctx.idempotencyKey` to the backend so a
24
+ * redelivered mutation is not double-applied. */
25
+ send: (payload: TPayload, ctx: OutboxExecuteContext) => Promise<TResult>;
26
+ /** Optional label for the pending-items UI. */
27
+ label?: (payload: TPayload) => string | undefined;
28
+ }
29
+
30
+ /**
31
+ * Build an offline-aware `mutationFn` (drop-in for `@tanstack/react-query`'s
32
+ * `useMutation`). When online it sends immediately; if the send fails because
33
+ * the network is unavailable — or the app is already offline — the payload is
34
+ * durably queued and the promise RESOLVES (queued: true) so the UI can update
35
+ * optimistically. The outbox drains on reconnect. A real server rejection (4xx)
36
+ * still throws so the form shows the validation error.
37
+ *
38
+ * Registers the outbox executor for `kind` once (idempotent).
39
+ */
40
+ export function createOfflineMutationFn<TPayload, TResult>(
41
+ opts: OfflineMutationOptions<TPayload, TResult>,
42
+ ): (payload: TPayload) => Promise<OfflineMutationResult<TResult>> {
43
+ const { outbox, kind, send, label } = opts;
44
+
45
+ if (!outbox.hasExecutor(kind)) {
46
+ outbox.registerExecutor<TPayload>(kind, async (payload, ctx) => {
47
+ await send(payload, ctx);
48
+ });
49
+ }
50
+
51
+ return async (payload: TPayload): Promise<OfflineMutationResult<TResult>> => {
52
+ const idempotencyKey = newId();
53
+
54
+ const queue = async (): Promise<OfflineMutationResult<TResult>> => {
55
+ const entry = await outbox.enqueue(kind, payload, {
56
+ idempotencyKey,
57
+ ...(label ? { label: label(payload) } : {}),
58
+ });
59
+ return { queued: true, entryId: entry.id };
60
+ };
61
+
62
+ if (!outbox.isOnline()) {
63
+ return queue();
64
+ }
65
+ try {
66
+ const result = await send(payload, { idempotencyKey, attempt: 1, entryId: 'direct' });
67
+ return { queued: false, result };
68
+ } catch (err) {
69
+ if (isNetworkUnavailableError(err)) {
70
+ return queue();
71
+ }
72
+ throw err; // genuine server rejection — surface it to the caller
73
+ }
74
+ };
75
+ }
@@ -0,0 +1,51 @@
1
+ import { useOutboxSnapshot } from './react';
2
+
3
+ import type { Outbox } from './outbox';
4
+
5
+ export interface OfflineStatusBadgeProps {
6
+ /** Explicit outbox; falls back to the nearest OutboxProvider. */
7
+ outbox?: Outbox;
8
+ className?: string;
9
+ }
10
+
11
+ const BASE =
12
+ 'inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-body-2 font-medium';
13
+
14
+ /**
15
+ * A compact connectivity + queue indicator for the offline field-capture kit.
16
+ * Renders nothing when online with an empty queue; otherwise surfaces the
17
+ * offline / syncing / dead-letter state so the user knows captures are safe and
18
+ * will sync. Self-contained (no UI-primitive deps) so it can live in the
19
+ * framework-level offline module.
20
+ */
21
+ export function OfflineStatusBadge({ outbox, className }: OfflineStatusBadgeProps) {
22
+ const { online, pendingCount, failedCount, draining } = useOutboxSnapshot(outbox);
23
+
24
+ if (online && pendingCount === 0 && failedCount === 0) {
25
+ return null;
26
+ }
27
+
28
+ const cls = (tone: string): string =>
29
+ `${BASE} ${tone}${className ? ` ${className}` : ''}`;
30
+
31
+ if (failedCount > 0) {
32
+ return (
33
+ <span className={cls('bg-destructive/10 text-destructive')} role="status">
34
+ ⚠ {failedCount} failed to sync
35
+ </span>
36
+ );
37
+ }
38
+ if (!online) {
39
+ return (
40
+ <span className={cls('bg-amber-500/10 text-amber-600')} role="status">
41
+ ● Offline{pendingCount > 0 ? ` — ${pendingCount} queued` : ''}
42
+ </span>
43
+ );
44
+ }
45
+ // Online with a non-empty queue → actively draining.
46
+ return (
47
+ <span className={cls('bg-secondary text-secondary-foreground')} role="status">
48
+ {draining ? '⟳' : '●'} Syncing {pendingCount}…
49
+ </span>
50
+ );
51
+ }