@magicvr/schema-ui-shell 0.1.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 (53) hide show
  1. package/app/App.d.ts +23 -0
  2. package/app/AuthGate.d.ts +20 -0
  3. package/app/HostFailureScreen.d.ts +18 -0
  4. package/app/LoginPage.d.ts +11 -0
  5. package/app/ManifestFailure.d.ts +3 -0
  6. package/app/branding.d.ts +33 -0
  7. package/app/config-events.d.ts +18 -0
  8. package/app/index.d.ts +15 -0
  9. package/app/navigation.d.ts +27 -0
  10. package/app/notification-bell.d.ts +10 -0
  11. package/components/data-table.d.ts +43 -0
  12. package/components/force-password-change.d.ts +1 -0
  13. package/components/invite-accept.d.ts +6 -0
  14. package/components/locale-switcher.d.ts +23 -0
  15. package/components/theme-toggle.d.ts +10 -0
  16. package/components/timezone-switcher.d.ts +16 -0
  17. package/components/ui/async-state.d.ts +27 -0
  18. package/components/ui/breadcrumbs.d.ts +78 -0
  19. package/components/ui/button.d.ts +11 -0
  20. package/components/ui/card.d.ts +8 -0
  21. package/components/ui/input.d.ts +5 -0
  22. package/components/ui/label.d.ts +3 -0
  23. package/components/ui/skeleton.d.ts +2 -0
  24. package/components/ui/textarea.d.ts +5 -0
  25. package/host/boot.d.ts +64 -0
  26. package/host/bootstrap.d.ts +105 -0
  27. package/host/claim.d.ts +75 -0
  28. package/host/failure.d.ts +97 -0
  29. package/host/return-intent.d.ts +55 -0
  30. package/i18n/catalog.d.ts +51 -0
  31. package/i18n/format.d.ts +17 -0
  32. package/i18n/locale.d.ts +51 -0
  33. package/i18n/runtime.d.ts +90 -0
  34. package/i18n/timezone.d.ts +57 -0
  35. package/index.js +23764 -0
  36. package/lib/datetime.d.ts +14 -0
  37. package/lib/fetch-timeout.d.ts +13 -0
  38. package/lib/utils.d.ts +2 -0
  39. package/package.json +30 -0
  40. package/renderer/confirm.d.ts +17 -0
  41. package/renderer/custom-components.d.ts +14 -0
  42. package/renderer/form-controls.d.ts +45 -0
  43. package/renderer/form-controls.types.d.ts +135 -0
  44. package/renderer/modal.d.ts +6 -0
  45. package/renderer/permissions.d.ts +53 -0
  46. package/renderer/reaction-engine.d.ts +92 -0
  47. package/renderer/reaction-expression.d.ts +74 -0
  48. package/renderer/reactions.d.ts +64 -0
  49. package/renderer/render.d.ts +194 -0
  50. package/renderer/render.types.d.ts +289 -0
  51. package/renderer/resource.d.ts +114 -0
  52. package/renderer/schema-table.d.ts +90 -0
  53. package/theme/theme.d.ts +64 -0
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Display-time formatting (GOAL-011 table style): ISO-8601 timestamps are
3
+ * rendered in a human-readable local-time form ("2026-08-01 18:02") instead
4
+ * of the raw wire value ("2026-08-01T18:02:44.000Z").
5
+ *
6
+ * Returns null when the value is not a renderable timestamp so callers fall
7
+ * back to the raw text (numbers, booleans, plain strings, arbitrary dates
8
+ * with other shapes stay untouched).
9
+ */
10
+ /**
11
+ * Formats an ISO-8601 timestamp string to local "YYYY-MM-DD HH:mm". Returns
12
+ * null for non-timestamp values, malformed dates, or non-string input.
13
+ */
14
+ export declare function formatDisplayTime(value: unknown): string | null;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * W10 F-002: fetch timeout wrapper.
3
+ *
4
+ * Wraps a fetch implementation so a hung request (slow network, stalled
5
+ * server) aborts after a bounded window instead of pending forever. The
6
+ * returned function is drop-in compatible with `typeof fetch`; a caller-
7
+ * provided AbortSignal in `init` is composed with the timeout signal (either
8
+ * firing aborts the request). Timers are cleared once the request settles, so
9
+ * no handles leak on the happy path.
10
+ */
11
+ /** Default ceiling for a single request (30s). */
12
+ export declare const DEFAULT_FETCH_TIMEOUT_MS = 30000;
13
+ export declare function withTimeout(fetchImpl?: typeof fetch, timeoutMs?: number): typeof fetch;
package/lib/utils.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { type ClassValue } from "clsx";
2
+ export declare function cn(...inputs: ClassValue[]): string;
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@magicvr/schema-ui-shell",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "schema-ui-core 包面(VP-023 R3 六包化)",
6
+ "main": "index.js",
7
+ "types": "./app/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./app/index.d.ts",
11
+ "import": "./index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "index.js",
16
+ "lib",
17
+ "theme",
18
+ "components",
19
+ "app",
20
+ "host",
21
+ "i18n",
22
+ "ui",
23
+ "renderer",
24
+ "render"
25
+ ],
26
+ "license": "UNLICENSED",
27
+ "publishConfig": {
28
+ "access": "public"
29
+ }
30
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Confirm dialog for row actions that declare a `confirm` string (S4 · GOAL-007).
3
+ *
4
+ * One-time renderer completion allowed by I-007-003 §9.5 (`confirm*.tsx`). The
5
+ * message text comes from the schema row action entry (`table.props.actions[].
6
+ * confirm` / `confirmKey`), so the dialog stays fixture-driven; cancelling
7
+ * mirrors the frozen `executeAction` CONFIRM_CANCELLED path and never issues
8
+ * a request.
9
+ *
10
+ * W14 F-12 (GOAL-018): keyboard/accessibility — initial focus on Cancel, ESC
11
+ * cancels, and Tab is trapped inside the dialog.
12
+ */
13
+ export declare function ConfirmDialog({ message, onConfirm, onCancel, }: {
14
+ message: string;
15
+ onConfirm: () => void;
16
+ onCancel: () => void;
17
+ }): import("react").JSX.Element;
@@ -0,0 +1,14 @@
1
+ import type { ComponentType } from "react";
2
+ import type { RenderCustomNode, RenderNode } from "@/renderer/render.types";
3
+ export interface CustomComponentProps {
4
+ node: RenderCustomNode;
5
+ context: Record<string, unknown>;
6
+ /** Children declared under the custom node (rarely used). */
7
+ children?: RenderNode[];
8
+ }
9
+ /** Registers a custom node component (idempotent; later registrations win). */
10
+ export declare function registerCustomComponent(key: string, component: ComponentType<CustomComponentProps>): void;
11
+ /** Returns the registered component for a key, or null when unregistered. */
12
+ export declare function getCustomComponent(key: string): ComponentType<CustomComponentProps> | null;
13
+ /** Test-only: clears the registry. */
14
+ export declare function resetCustomComponentsForTests(): void;
@@ -0,0 +1,45 @@
1
+ import { type ReactNode } from "react";
2
+ import { type MessageParams } from "@/i18n/catalog";
3
+ import type { UploadableFile } from "@/protocol/conformance/upload-orchestration";
4
+ import { type FormControlField } from "@/renderer/form-controls.types";
5
+ export interface FormControlsProps {
6
+ fields: FormControlField[];
7
+ values: Record<string, unknown>;
8
+ onChange: (id: string, value: unknown) => void;
9
+ disabled?: boolean;
10
+ /** Per-field disabled override (R5 renderer reaction state). */
11
+ fieldDisabled?: (id: string) => boolean;
12
+ idPrefix?: string;
13
+ /** Upload control transport (ADR-0012): validates + uploads + returns the field value. */
14
+ onUpload?: (field: FormControlField, files: UploadableFile[]) => Promise<unknown>;
15
+ /** GOAL-014 D-002 §4: inline field errors keyed by field id (submit-time
16
+ * validation + server fieldErrors echo). */
17
+ fieldErrors?: Record<string, string>;
18
+ /** GOAL-014 D-002 §4: column count (default 1 = single-column layout).
19
+ * >1 enables a responsive grid; the mobile layout stays single-column. */
20
+ columns?: number;
21
+ /** W11 · U-01/U-02: auth-aware transport for dynamic option sources
22
+ * (optionsSource); defaults to globalThis.fetch. */
23
+ fetcher?: typeof fetch;
24
+ /**
25
+ * A-003 (GOAL-013 audit response): search-mode presentation — compact
26
+ * responsive auto-grid (1..5 columns), keyword input with search prefix
27
+ * icon + clear affordance. Search schemas keep their exact JSON shape.
28
+ */
29
+ searchMode?: boolean;
30
+ /** A-003: action cluster (Reset button) rendered inside the
31
+ * search-mode grid so it aligns with the field row. */
32
+ actionSlot?: ReactNode;
33
+ /**
34
+ * A-003 (user pairing rule): one search button rendered side-by-side with
35
+ * EVERY keyword input field in search mode — as many buttons as there are
36
+ * text inputs, each pair adjacent in the same grid cell.
37
+ */
38
+ searchButtonSlot?: ReactNode;
39
+ }
40
+ export type FieldTranslator = (key: string, params?: MessageParams, literalFallback?: string) => string;
41
+ export declare function optionList(field: FormControlField, t: FieldTranslator): Array<{
42
+ value: string;
43
+ label: string;
44
+ }>;
45
+ export declare function FormControls({ fields, values, onChange, disabled, fieldDisabled, idPrefix, onUpload, fieldErrors, columns, fetcher, searchMode: searchModeProp, actionSlot, searchButtonSlot, }: FormControlsProps): import("react").JSX.Element;
@@ -0,0 +1,135 @@
1
+ /**
2
+ * D-FORM form control surface (frozen §5 whitelist + I-PROTO-FULL-001 full
3
+ * registry surface).
4
+ *
5
+ * Wire rules from schema-ui-docs@2.7.0 (fixed commit ca9e5fe…):
6
+ * - base: input → string, select (single) → string, inputNumber → number,
7
+ * datePicker → ISO 8601 string, dateRangePicker → {start,end} pair bound
8
+ * to startField/endField (registry props; no single-field wire)
9
+ * - 2.6 (capability form.controls.extended): textarea → string, switch → boolean,
10
+ * checkbox → boolean, radio → single string, select.mode=multiple → string[]
11
+ * - 2.7 (capability form.controls.advanced): cascader → path string[],
12
+ * checkboxGroup → value string[], richText → markdown string, password → string
13
+ * - 2.7 props.defaultValue must match the field's wire type
14
+ *
15
+ * The schema-driven page gate (meta.protocolVersion + requiredCapabilities) is
16
+ * enforced by checkFormCapabilities; the Renderer-level page gate lands in 2c.
17
+ */
18
+ export type FormControlType = "input" | "select" | "inputNumber" | "datePicker" | "dateRangePicker" | "textarea" | "switch" | "checkbox" | "radio" | "cascader" | "checkboxGroup" | "richText" | "password" | "upload";
19
+ export declare const FORM_CONTROLS_EXTENDED_CAPABILITY = "form.controls.extended";
20
+ export declare const FORM_CONTROLS_ADVANCED_CAPABILITY = "form.controls.advanced";
21
+ /** ADR-0021: `form.props.recordSource` prefill GET (registry since 2.1). */
22
+ export declare const FORM_RECORD_LOAD_CAPABILITY = "form.record.load";
23
+ /** ADR-0040 (since 2.9): `readOnly` field declaration (value still projects). */
24
+ export declare const FORM_CONTROLS_READONLY_CAPABILITY = "form.controls.readonly";
25
+ export type WireKind = "string" | "boolean" | "string-array" | "number" | "date-range";
26
+ export interface FormOption {
27
+ value: string;
28
+ label?: string;
29
+ /** S2 (VP-007): i18n key resolved before `label` (upstream registry field). */
30
+ labelKey?: string;
31
+ }
32
+ export interface DateRangeValue {
33
+ start: string;
34
+ end: string;
35
+ }
36
+ export interface FormControlField {
37
+ id: string;
38
+ label?: string;
39
+ /** S2 (VP-007): i18n key resolved before `label` (missing-key observable). */
40
+ labelKey?: string;
41
+ placeholder?: string;
42
+ /** S2 (VP-007): i18n key resolved before `placeholder` (local doc convention). */
43
+ placeholderKey?: string;
44
+ type: FormControlType;
45
+ /** select only: single (default) or multiple. */
46
+ mode?: "single" | "multiple";
47
+ options?: FormOption[];
48
+ /**
49
+ * W11 · U-01/U-02 — dynamic option source, aligned with the upstream
50
+ * registry shape (component-registry.json, since 0.2): an object with a
51
+ * required single-slash same-origin url plus the response item fields used
52
+ * for value/label. The response is {items:[...]} (or a bare array); while
53
+ * the source loads, static options (if any) remain the fallback; an invalid
54
+ * source or failed fetch fails closed to an empty option set.
55
+ */
56
+ optionsSource?: {
57
+ url: string;
58
+ /** Optional scalar query params appended to url (e.g. pageSize). */
59
+ params?: Record<string, string | number | boolean | null>;
60
+ labelField: string;
61
+ valueField: string;
62
+ };
63
+ defaultValue?: unknown;
64
+ /** dateRangePicker only: the two bound output fields (registry props). */
65
+ startField?: string;
66
+ endField?: string;
67
+ /** inputNumber constraints (registry props, since 0.2.1). */
68
+ min?: number;
69
+ max?: number;
70
+ step?: number;
71
+ precision?: number;
72
+ /** GOAL-014 D-002 §3: field-level validation constraints (optional). */
73
+ required?: boolean;
74
+ /** ADR-0040 (since 2.9): read-only field — user cannot edit, value still
75
+ * participates in values and the submit projection (bodyMapping);
76
+ * recordSource backfill and reactions keep writing. Requires protocol
77
+ * >= 2.9 and form.controls.readonly. */
78
+ readOnly?: boolean;
79
+ /** Regex pattern for string-typed fields (submit-time validation). */
80
+ pattern?: string;
81
+ /** String length bounds for input/textarea. */
82
+ minLength?: number;
83
+ maxLength?: number;
84
+ /** datePicker display format (display-only; data stays ISO 8601). */
85
+ format?: string;
86
+ /** upload only: direct-URL mode (registry oneOf with actionRef). */
87
+ action?: string;
88
+ /** upload only: references a top-level type=upload action (requires actions.upload). */
89
+ actionRef?: string;
90
+ /**
91
+ * W17: optional Host-local addon rendered under this field. The value is a
92
+ * registered custom-component key (e.g. cron-preview). Not a protocol
93
+ * control type.
94
+ */
95
+ afterComponent?: string;
96
+ /** upload constraints (direct-URL mode only; actionRef mode reads the action). */
97
+ accept?: string;
98
+ maxSize?: number;
99
+ multiple?: boolean;
100
+ }
101
+ export interface FormControlMeta {
102
+ protocolVersion: string;
103
+ requiredCapabilities: string[];
104
+ }
105
+ export interface FormControlGateError {
106
+ code: string;
107
+ path: string;
108
+ message: string;
109
+ }
110
+ export declare function isWhitelistedFormControl(type: string): type is FormControlType;
111
+ export declare function wireKindOf(field: FormControlField): WireKind;
112
+ /** Coerces a raw control value to its wire kind; defaultValue applies when raw is empty. */
113
+ export declare function coerceFieldValue(field: FormControlField, raw: unknown): unknown;
114
+ /** Fails closed when defaultValue does not match the field's wire type. */
115
+ export declare function validateDefaultValue(field: FormControlField): FormControlGateError | null;
116
+ /** Gates a control set against page meta (P-005 / frozen capability rules). */
117
+ export declare function checkFormCapabilities(meta: FormControlMeta, fields: FormControlField[]): FormControlGateError[];
118
+ /** Validates an arbitrary page-meta value without assuming record types. */
119
+ export declare function checkFormCapabilitiesRaw(metaValue: unknown, fields: FormControlField[]): FormControlGateError[];
120
+ /** One submit-time field validation failure (GOAL-014 D-002 §3). */
121
+ export interface FieldValidationError {
122
+ field: string;
123
+ code: "REQUIRED" | "PATTERN" | "MIN_LENGTH" | "MAX_LENGTH" | "MIN_VALUE" | "MAX_VALUE";
124
+ /** Stable i18n key (form.validation.*); message is the en fallback. */
125
+ messageKey: string;
126
+ message: string;
127
+ }
128
+ /**
129
+ * Validates form values against each field's declared constraints (GOAL-014
130
+ * D-002 §3.1). Pure function: returns all failures (not just the first) so
131
+ * the host can inline every field error. Boolean fields (switch/checkbox)
132
+ * never fail REQUIRED — their wire kind is boolean and the toggle has an
133
+ * explicit state.
134
+ */
135
+ export declare function validateFieldValues(fields: FormControlField[], values: Record<string, unknown>): FieldValidationError[];
@@ -0,0 +1,6 @@
1
+ import { type ReactNode } from "react";
2
+ export declare function ModalHost({ title, onClose, children, }: {
3
+ title?: string;
4
+ onClose: () => void;
5
+ children: ReactNode;
6
+ }): import("react").JSX.Element;
@@ -0,0 +1,53 @@
1
+ import { type NavigationContext } from "@/protocol/app-manifest";
2
+ /**
3
+ * R4 D-PERM permission evaluation engine (ADR-0023, frozen by GOAL-006 D-004).
4
+ *
5
+ * Coverage scope: the minimal renderer subset exercised by the frozen
6
+ * permissions-inheritance fixtures — permissionCascade / permissionIntent
7
+ * validation (fail-closed error codes) and effectivePermission evaluation for
8
+ * the approved target kinds (formField / formSubmit / rowAction /
9
+ * toolbarTrigger / actionButton). Execution-time gating (visibleWhen →
10
+ * permission → disabled/requiresSelection → confirm → action) is modeled for
11
+ * rowAction / toolbarTrigger / actionButton / default form submit.
12
+ */
13
+ export type PermissionKey = "view" | "edit" | "delete";
14
+ export type L2ErrorCode = "PROTOCOL_VERSION_TOO_LOW" | "CAPABILITY_REQUIRED" | "PERMISSION_CASCADE_TYPE_INVALID" | "PERMISSION_CASCADE_KEYS_INVALID" | "PERMISSION_CASCADE_SOURCE_MISSING" | "PERMISSION_INTENT_FORBIDDEN" | "PERMISSION_INTENT_INVALID";
15
+ export interface L2Error {
16
+ code: L2ErrorCode;
17
+ path: string;
18
+ }
19
+ export interface PermissionTarget {
20
+ targetId: string;
21
+ kind: "formField" | "formSubmit" | "rowAction" | "toolbarTrigger" | "actionButton" | "column";
22
+ key: PermissionKey;
23
+ cascadeApplied: boolean;
24
+ cascadedBy: string[];
25
+ effectivePermission: boolean;
26
+ }
27
+ export interface ExecutionRequest {
28
+ targetId: string;
29
+ visible: boolean;
30
+ confirm?: boolean;
31
+ confirmed?: boolean;
32
+ disabled?: boolean;
33
+ requiresSelection?: boolean;
34
+ }
35
+ export interface ExecutionResult {
36
+ outcome: "EXECUTED" | "BLOCKED" | "CONFIRM_CANCELLED";
37
+ reason?: "NOT_VISIBLE" | "PERMISSION_DENIED" | "DISABLED";
38
+ events: Array<{
39
+ type: "confirmShown" | "actionExecuted";
40
+ }>;
41
+ }
42
+ type JsonRecord = Record<string, unknown>;
43
+ /** Validates the permission fields of a page; returns all L2 errors. */
44
+ export declare function validatePermissions(page: JsonRecord): L2Error[];
45
+ /**
46
+ * Evaluates the effective permission of every approved target in the page
47
+ * against the frozen $context snapshot. Targets in modal content and
48
+ * navigated pages start new roots (ADR-0023 D2a).
49
+ */
50
+ export declare function evaluatePermissionTargets(page: JsonRecord, context: NavigationContext): PermissionTarget[];
51
+ /** Gates an action: visible → permission → disabled/requiresSelection → confirm. */
52
+ export declare function executeAction(page: JsonRecord, request: ExecutionRequest, context: NavigationContext): ExecutionResult;
53
+ export {};
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Multi-round snapshot reaction engine (schema-ui-docs@2.7.0 · 02-reaction-expression.md §14).
3
+ *
4
+ * Implements the upstream conformance contract (`conformance/fixtures/reactions/cases.json`):
5
+ * Snapshot → Evaluate → Commit → Next tick, with:
6
+ * - per-field reactions [{ when, fulfill, otherwise }] (value/visible/required/disabled)
7
+ * - observers evaluated against every round snapshot
8
+ * - baselines + resetMissingOtherwise (condition false without otherwise → restore baseline;
9
+ * explicit writes win over implicit baseline restores)
10
+ * - externalUpdates applied between rounds
11
+ * - deep-equal change detection (no commit / no next round on no-op writes)
12
+ * - loop protection (REACTION_LOOP_LIMIT after maxRounds, default 10)
13
+ * - MULTIPLE_VALUE_WRITES warnings (last write wins, deterministic order)
14
+ */
15
+ export interface ReactionRuleInput {
16
+ when: string;
17
+ fulfill?: Record<string, unknown>;
18
+ otherwise?: Record<string, unknown>;
19
+ }
20
+ export interface FieldReactionInput {
21
+ field: string;
22
+ reactions: ReactionRuleInput[];
23
+ }
24
+ export interface ReactionObserverInput {
25
+ id: string;
26
+ when: string;
27
+ }
28
+ export interface ExternalUpdateInput {
29
+ afterRound: number;
30
+ values: Record<string, unknown>;
31
+ }
32
+ export interface ReactionEngineInput {
33
+ initialValues: Record<string, unknown>;
34
+ fields?: FieldReactionInput[];
35
+ observers?: ReactionObserverInput[];
36
+ baselines?: Record<string, unknown>;
37
+ resetMissingOtherwise?: boolean;
38
+ externalUpdates?: ExternalUpdateInput[];
39
+ maxRounds?: number;
40
+ }
41
+ export interface ReactionRound {
42
+ round: number;
43
+ snapshot: Record<string, unknown>;
44
+ observations: Record<string, boolean>;
45
+ commits: Array<{
46
+ field: string;
47
+ value: unknown;
48
+ }>;
49
+ }
50
+ export interface MultipleValueWritesWarning {
51
+ code: "MULTIPLE_VALUE_WRITES";
52
+ field: string;
53
+ count: number;
54
+ }
55
+ export interface ReactionEngineOk {
56
+ ok: true;
57
+ values: Record<string, unknown>;
58
+ rounds: ReactionRound[];
59
+ warnings: MultipleValueWritesWarning[];
60
+ }
61
+ export interface ReactionEngineLoopError {
62
+ ok: false;
63
+ code: "REACTION_LOOP_LIMIT";
64
+ maxRounds: number;
65
+ values: Record<string, unknown>;
66
+ roundCount: number;
67
+ dependencyFields: string[];
68
+ }
69
+ export type ReactionEngineResult = ReactionEngineOk | ReactionEngineLoopError;
70
+ /** Per-field control state after convergence (renderer integration). */
71
+ export interface ReactionFieldState {
72
+ visible?: boolean;
73
+ required?: boolean;
74
+ disabled?: boolean;
75
+ }
76
+ export interface ReactionEngineDetailed {
77
+ result: ReactionEngineResult;
78
+ fieldStates: Record<string, ReactionFieldState>;
79
+ }
80
+ export declare const DEFAULT_MAX_ROUNDS = 10;
81
+ /**
82
+ * Runs the multi-round engine.
83
+ *
84
+ * A field's committed `value` only lands in `commits` when it deep-differs
85
+ * from the snapshot value; a no-op write neither commits nor schedules the
86
+ * next round. Rounds stop when no committed field is a dependency of any
87
+ * expression (reaction when or observer when).
88
+ */
89
+ export declare function runReactionEngine(input: ReactionEngineInput): ReactionEngineResult;
90
+ /** Engine + per-field control state (visible/required/disabled/value) from
91
+ * the final round — used by the Renderer's form integration. */
92
+ export declare function runReactionEngineDetailed(input: ReactionEngineInput): ReactionEngineDetailed;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Full protocol expression engine (schema-ui-docs@2.7.0 · docs/02-reaction-expression.md).
3
+ *
4
+ * Whitelisted grammar (no eval / new Function):
5
+ * - namespaces: $deps.<field>, $self, $context.user.* / $context.features.*
6
+ * - operators: == != > >= < <= contains && || !
7
+ * - grouping: ( )
8
+ * - literals: 'str' | "str" | number (int/float/exponent) | true | false | null
9
+ *
10
+ * Semantics (ADR-0016 / §7-§14):
11
+ * - strict typing, no coercion: 1 == 1.0 true; 1 == '1' false; true == 1 false
12
+ * - contains: array left operand, strict element equality; non-array → false
13
+ * - string ordering by Unicode code points (not UTF-16 code units)
14
+ * - undefined values compare as false (missing deps never throw)
15
+ */
16
+ export type ExprValue = string | number | boolean | null | ExprValue[] | {
17
+ [key: string]: ExprValue;
18
+ } | undefined;
19
+ export interface ReactionEnv {
20
+ /** $deps.<field> values (form field snapshot). */
21
+ deps?: Record<string, unknown>;
22
+ /** $self (current field value). */
23
+ self?: unknown;
24
+ /** $context.user.* / $context.features.* snapshots. */
25
+ context?: Record<string, unknown>;
26
+ }
27
+ export type ParseError = {
28
+ ok: false;
29
+ code: "SYNTAX";
30
+ message: string;
31
+ } | {
32
+ ok: false;
33
+ code: "FORBIDDEN_VARIABLE";
34
+ message: string;
35
+ } | {
36
+ ok: false;
37
+ code: "UNSUPPORTED_OPERATOR";
38
+ message: string;
39
+ };
40
+ export type ParsedExpr = {
41
+ kind: "literal";
42
+ value: ExprValue;
43
+ } | {
44
+ kind: "var";
45
+ path: string[];
46
+ } | {
47
+ kind: "self";
48
+ } | {
49
+ kind: "not";
50
+ operand: ParsedExpr;
51
+ } | {
52
+ kind: "and";
53
+ left: ParsedExpr;
54
+ right: ParsedExpr;
55
+ } | {
56
+ kind: "or";
57
+ left: ParsedExpr;
58
+ right: ParsedExpr;
59
+ } | {
60
+ kind: "compare";
61
+ op: "==" | "!=" | ">" | ">=" | "<" | "<=" | "contains";
62
+ left: ParsedExpr;
63
+ right: ParsedExpr;
64
+ };
65
+ /** Parses a whitelisted expression; returns the AST or a typed error. */
66
+ export declare function parseExpression(source: string): ParsedExpr | ParseError;
67
+ /** Deep equality (arrays/objects by structure; scalars by strict identity). */
68
+ export declare function deepEqual(left: unknown, right: unknown): boolean;
69
+ /** Evaluates a whitelisted expression to a boolean; invalid input → false. */
70
+ export declare function evaluateFullExpression(source: string, env: ReactionEnv): boolean;
71
+ /** True when `source` parses under the full grammar. */
72
+ export declare function isValidFullExpression(source: string): boolean;
73
+ /** Extracts the $deps.<root> field names referenced by an expression. */
74
+ export declare function expressionDependencyFields(source: string): string[];
@@ -0,0 +1,64 @@
1
+ /**
2
+ * R5 D-EXPR reaction surface (frozen Q: reactions operate on the $context
3
+ * namespace only; no field-value triggers).
4
+ *
5
+ * A reaction is a `{ when, apply }` rule: `when` is a frozen $context
6
+ * expression (evaluateExpression grammar), and `apply` turns a form control
7
+ * state on/off when the expression holds. This is the renderer-level
8
+ * equivalent of the navigation visibleWhen gate, applied to form fields.
9
+ *
10
+ * Fails closed: an unparseable `when` expression or an unknown apply target
11
+ * keeps the field at its default state instead of mutating it silently.
12
+ */
13
+ export interface ReactionApply {
14
+ fieldId: string;
15
+ /** visible/disabled toggles are explicit booleans (no implicit flip). */
16
+ visible?: boolean;
17
+ disabled?: boolean;
18
+ }
19
+ export interface ReactionRule {
20
+ id: string;
21
+ when: string;
22
+ apply: ReactionApply[];
23
+ }
24
+ export type ReactionErrorCode = "REACTION_EXPRESSION_INVALID" | "REACTION_APPLY_FIELD_UNKNOWN" | "REACTION_APPLY_INVALID";
25
+ export interface ReactionError {
26
+ code: ReactionErrorCode;
27
+ path: string;
28
+ message: string;
29
+ }
30
+ export interface FormControlState {
31
+ visible: boolean;
32
+ disabled: boolean;
33
+ }
34
+ export type FormControlStateMap = Record<string, FormControlState>;
35
+ export interface ReactionEvaluation {
36
+ state: FormControlStateMap;
37
+ errors: ReactionError[];
38
+ }
39
+ /** Parses a raw reaction rule, fail-closed on malformed shapes. */
40
+ export declare function parseReactionRule(value: unknown, path: string): ReactionRule | ReactionError;
41
+ /**
42
+ * Evaluates a reaction rule list against the frozen $context snapshot.
43
+ * Unknown apply fieldIds fail closed (keep default) and are reported.
44
+ */
45
+ export declare function evaluateReactions(rules: ReactionRule[], context: Record<string, unknown>, fieldIds: string[]): ReactionEvaluation;
46
+ /** Validates raw rules and returns parsed rules + fail-closed errors. */
47
+ export declare function parseAndEvaluateReactions(rawRules: unknown, context: Record<string, unknown>, fieldIds: string[]): ReactionEvaluation;
48
+ export interface FullReactionResult {
49
+ /** True when the form declares upstream-shaped per-field reactions ($deps). */
50
+ usesFullEngine: boolean;
51
+ /** Per-field control state after convergence (visible/disabled). */
52
+ state: FormControlStateMap;
53
+ /** Value commits to merge into the form values (last-wins, convergent). */
54
+ values: Record<string, unknown>;
55
+ errors: ReactionError[];
56
+ }
57
+ /**
58
+ * Resolves the full multi-round $deps reaction engine over a form's fields.
59
+ *
60
+ * Upstream shape (02-reaction-expression.md): each field node carries
61
+ * `reactions: [{ when, fulfill, otherwise }]`. Returns the convergent control
62
+ * state + value commits; malformed rules fail closed (reported, not applied).
63
+ */
64
+ export declare function resolveFullFormReactions(rawFields: unknown, values: Record<string, unknown>, baselines: Record<string, unknown>): FullReactionResult;