@magicvr/schema-ui-renderer 0.3.2 → 0.3.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.
Files changed (37) hide show
  1. package/components/account-session-toolbar.d.ts +2 -0
  2. package/components/activity-export.d.ts +2 -0
  3. package/components/cron-preview.d.ts +2 -0
  4. package/components/data-permission-scopes.d.ts +2 -0
  5. package/components/data-table.d.ts +43 -0
  6. package/components/email-identity.d.ts +2 -0
  7. package/components/force-password-change.d.ts +1 -0
  8. package/components/import-template-download.d.ts +2 -0
  9. package/components/invite-accept.d.ts +6 -0
  10. package/components/invite-issue-card.d.ts +7 -0
  11. package/components/invite-resend-dialog.d.ts +9 -0
  12. package/components/locale-switcher.d.ts +23 -0
  13. package/components/mail-admin-tab.d.ts +2 -0
  14. package/components/mfa-manager.d.ts +2 -0
  15. package/components/monitoring-auto-refresh.d.ts +2 -0
  16. package/components/notification-center.d.ts +2 -0
  17. package/components/password-policy-tab.d.ts +6 -0
  18. package/components/qr-code.d.ts +16 -0
  19. package/components/theme-toggle.d.ts +10 -0
  20. package/components/timezone-switcher.d.ts +16 -0
  21. package/components/wallet-ensure.d.ts +2 -0
  22. package/package.json +1 -8
  23. package/renderer/confirm.d.ts +17 -0
  24. package/renderer/custom-components.d.ts +14 -0
  25. package/renderer/form-controls.d.ts +45 -0
  26. package/renderer/form-controls.types.d.ts +135 -0
  27. package/renderer/index.d.ts +13 -0
  28. package/renderer/modal.d.ts +6 -0
  29. package/renderer/permissions.d.ts +53 -0
  30. package/renderer/reaction-engine.d.ts +92 -0
  31. package/renderer/reaction-expression.d.ts +74 -0
  32. package/renderer/reactions.d.ts +64 -0
  33. package/renderer/render.d.ts +194 -0
  34. package/renderer/render.types.d.ts +289 -0
  35. package/renderer/resource.d.ts +114 -0
  36. package/renderer/row-action.d.ts +28 -0
  37. package/renderer/schema-table.d.ts +90 -0
@@ -0,0 +1,2 @@
1
+ import { type CustomComponentProps } from "@magicvr/schema-ui-renderer/renderer/custom-components";
2
+ export declare function AccountSessionToolbar(_props: CustomComponentProps): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ import { type CustomComponentProps } from "@magicvr/schema-ui-renderer/renderer/custom-components";
2
+ export declare function ActivityExport({ node }: CustomComponentProps): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ import { type CustomComponentProps } from "@magicvr/schema-ui-renderer/renderer/custom-components";
2
+ export declare function CronPreview(props: CustomComponentProps): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ import { type CustomComponentProps } from "@magicvr/schema-ui-renderer/renderer/custom-components";
2
+ export declare function DataPermissionScopes(_props: CustomComponentProps): import("react").JSX.Element;
@@ -0,0 +1,43 @@
1
+ import type { ReactNode } from "react";
2
+ export type SortOrder = "asc" | "desc";
3
+ export interface SortState {
4
+ field: string;
5
+ order: SortOrder;
6
+ }
7
+ export interface DataTableColumn<T> {
8
+ key: string;
9
+ /** Cell content or header node (checkboxes render in headers for selection). */
10
+ label: ReactNode;
11
+ sortable?: boolean;
12
+ /** W4 · GOAL-005: render the string fallback single-line truncated with a
13
+ * native title full-text affordance so long values do not crowd out
14
+ * sibling columns. Custom render cells are unaffected. Since the
15
+ * table-style refresh every string cell truncates by default; this flag
16
+ * is kept for schema declarations that opt into the 16rem cap. */
17
+ truncate?: boolean;
18
+ /** Column width hint (px number or CSS length). Content-driven auto layout
19
+ * otherwise; a column that declares a width skips the default max-width
20
+ * cap so it can exceed it (table-layout auto still grows on content). */
21
+ width?: number | string;
22
+ /** Minimum column width (px number or CSS length). */
23
+ minWidth?: number | string;
24
+ render?: (row: T) => ReactNode;
25
+ }
26
+ export interface DataTableProps<T> {
27
+ columns: DataTableColumn<T>[];
28
+ rows: T[];
29
+ rowKey: (row: T) => string;
30
+ sort?: SortState;
31
+ onSortChange?: (sort: SortState | null) => void;
32
+ loading?: boolean;
33
+ error?: string | null;
34
+ emptyMessage?: string;
35
+ caption?: string;
36
+ /** Invoked when a data row is clicked (row selection, S4 · GOAL-007). */
37
+ onRowClick?: (row: T) => void;
38
+ /** Row key of the currently selected row (highlight), S4 · GOAL-007. */
39
+ selectedKey?: string;
40
+ /** W15-F02: retry control shown in the error state. */
41
+ onRetry?: () => void;
42
+ }
43
+ export declare function DataTable<T>({ columns, rows, rowKey, sort, onSortChange, loading, error, emptyMessage, caption, onRowClick, selectedKey, onRetry, }: DataTableProps<T>): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ import { type CustomComponentProps } from "@magicvr/schema-ui-renderer/renderer/custom-components";
2
+ export declare function EmailIdentityCard(_props: CustomComponentProps): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ export declare function ForcePasswordChange(): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ import { type CustomComponentProps } from "@magicvr/schema-ui-renderer/renderer/custom-components";
2
+ export declare function ImportTemplateDownload(_props: CustomComponentProps): import("react").JSX.Element;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Public invitation acceptance surface (workspace-019 R3 · GOAL-004 C4):
3
+ * mounted at /invite/accept?token=… for unauthenticated visitors. Success
4
+ * returns WITHOUT tokens — the new user signs in with their chosen password.
5
+ */
6
+ export declare function InviteAcceptPage(): import("react").JSX.Element;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Invitation issue card (workspace-019): the create operation PLUS one-time
3
+ * link disclosure for creation and resend. The records list lives on the
4
+ * schema table of the same page; resend accepts the invite id from the table
5
+ * (column "id") because the schema engine cannot disclose response bodies.
6
+ */
7
+ export declare function InviteIssueCard(_props: unknown): import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ import { type CustomComponentProps } from "@magicvr/schema-ui-renderer/renderer/custom-components";
2
+ /**
3
+ * Resend dialog (workspace-019, protocol modal action): the row-level
4
+ * "Resend" opens this modal with the triggering invite in context.modalRow
5
+ * (renderer injects it for modal content). Sending rotates the token via the
6
+ * admin API and discloses the ONE-TIME link inline — the schema engine cannot
7
+ * stream response bodies into the page, so the modal is the interaction.
8
+ */
9
+ export declare function InviteResendDialog(props: CustomComponentProps): import("react").JSX.Element;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Language switcher (S1 · C4) — refactored to a lightweight header
3
+ * dropdown (2026-08-14, user request):
4
+ *
5
+ * - Trigger: pure icon button (lucide Languages), same size/shape as the
6
+ * theme toggle and notification bell (size-9 rounded-md ghost).
7
+ * - Menu: absolutely positioned panel (NO portal — the app root carries
8
+ * the `dark` class, so the panel inherits the dark theme variables
9
+ * automatically; no Radix/Headless portal scope issue).
10
+ * - Dark-mode styling via design tokens (shadcn convention): bg-popover
11
+ * resolves to ~neutral-900 in dark, border-border to white/10
12
+ * (~neutral-800), accent hover to ~neutral-800, popover-foreground to
13
+ * ~neutral-200 — exactly the requested palette; light mode stays
14
+ * correct automatically.
15
+ * - Selected item shows a checkmark (lucide Check) on the right.
16
+ *
17
+ * Reachable from the Shell and the anonymous login page — no settings
18
+ * permission required (VP-007 requirement).
19
+ */
20
+ export interface LocaleSwitcherProps {
21
+ className?: string;
22
+ }
23
+ export declare function LocaleSwitcher({ className }: LocaleSwitcherProps): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ import { type CustomComponentProps } from "@magicvr/schema-ui-renderer/renderer/custom-components";
2
+ export declare function MailAdminTab(_props: CustomComponentProps): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ import { type CustomComponentProps } from "@magicvr/schema-ui-renderer/renderer/custom-components";
2
+ export declare function MfaManager(_props: CustomComponentProps): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ import { type CustomComponentProps } from "@magicvr/schema-ui-renderer/renderer/custom-components";
2
+ export declare function MonitoringAutoRefresh(_props: CustomComponentProps): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ import { type CustomComponentProps } from "@magicvr/schema-ui-renderer/renderer/custom-components";
2
+ export declare function NotificationCenter({ node, context }: CustomComponentProps): import("react").JSX.Element;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Password-policy configuration block (workspace-019 R3 · GOAL-004 D-001
3
+ * §2): the admin.settings tab extension. GET/PATCH /api/settings/password-
4
+ * policy; the server enforces range validation (8–72 / 0–4 / 0–10).
5
+ */
6
+ export declare function PasswordPolicyTab(_props: unknown): import("react").JSX.Element;
@@ -0,0 +1,16 @@
1
+ export interface QrCodeProps {
2
+ /** Text to encode (e.g. an otpauth:// URI). Empty → renders nothing. */
3
+ value: string;
4
+ /** Rendered size in CSS pixels (the matrix is square). */
5
+ size?: number;
6
+ /** Extra class names for the wrapping figure. */
7
+ className?: string;
8
+ /** Accessible label. */
9
+ label?: string;
10
+ }
11
+ /**
12
+ * Renders the QR module matrix as SVG. Each dark module becomes a 1×1 rect in
13
+ * a unit viewBox scaled to `size`; a white background rect keeps scanners
14
+ * happy (quiet zone included via the 4-module default margin of the encoder).
15
+ */
16
+ export declare function QrCode({ value, size, className, label }: QrCodeProps): import("react").JSX.Element | null;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Theme toggle (S1 · C3) — W8 visual unification (GOAL-009):
3
+ *
4
+ * The trigger now matches the neighbouring header icon buttons (language
5
+ * switcher and notification bell): size-9 rounded-md ghost with
6
+ * text-muted-foreground + hover:bg-accent — instead of the form-styled
7
+ * outline Button. The tooltip / aria-label follow the active locale via
8
+ * the i18n catalog (shell.theme.toggle) — no hardcoded English.
9
+ */
10
+ export declare function ThemeToggle(): import("react").JSX.Element;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Timezone switcher (workspace-020 · R2 · C2).
3
+ *
4
+ * User-level timezone override next to the language switcher in the header
5
+ * locale channel (contract GOAL-002 D-001 §4.2): persists via the single
6
+ * localStorage channel "schema-ui:timezone"; "auto" removes the key.
7
+ * The option list is the documented common set (contract §6 permits a
8
+ * verifiable, extendable list); the effective timezone always degrades
9
+ * safely through the L1–L4 resolver in i18n/timezone.
10
+ */
11
+ /** Common IANA set offered in the header menu (extendable per contract §6). */
12
+ export declare const TIMEZONE_OPTIONS: readonly string[];
13
+ export interface TimezoneSwitcherProps {
14
+ className?: string;
15
+ }
16
+ export declare function TimezoneSwitcher({ className }: TimezoneSwitcherProps): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ import { type CustomComponentProps } from "@magicvr/schema-ui-renderer/renderer/custom-components";
2
+ export declare function WalletEnsure(_props: CustomComponentProps): import("react").JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magicvr/schema-ui-renderer",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "type": "module",
5
5
  "description": "schema-ui-core 包面(renderer)",
6
6
  "main": "index.js",
@@ -18,13 +18,6 @@
18
18
  "components"
19
19
  ],
20
20
  "license": "UNLICENSED",
21
- "peerDependencies": {
22
- "react": "^19.0.0",
23
- "react-dom": "^19.0.0",
24
- "@magicvr/schema-ui-protocol": "^0.2.1",
25
- "@magicvr/schema-ui-lib": "^0.1.1",
26
- "@magicvr/schema-ui-ui": "^0.1.1"
27
- },
28
21
  "publishConfig": {
29
22
  "access": "public"
30
23
  }
@@ -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 "@magicvr/schema-ui-renderer/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 "@magicvr/schema-ui-lib/i18n/catalog";
3
+ import type { UploadableFile } from "@magicvr/schema-ui-protocol/conformance/upload-orchestration";
4
+ import { type FormControlField } from "@magicvr/schema-ui-renderer/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,13 @@
1
+ /**
2
+ * @magicvr/schema-ui-renderer 聚合导出(试点 v0.1.0 · GOAL-004 S3 · 粗粒度单包)。
3
+ *
4
+ * 渲染闭环:RenderPage(schema 文档 → DOM)+ I18nProvider(i18n 运行时面)
5
+ * + 扩展接缝(registerCustomComponent)+ 核心类型。
6
+ * 内部 bundle 面(components/i18n/lib/protocol)不导出为包级 API。
7
+ */
8
+ export { RenderPage, useSchemaCrud, SchemaCrudContext, type RendererComponentProps, type SchemaCrudFeedback, type SchemaCrudConfirm, type TableSelection, type ActionResult, type SchemaCrudValue, type RunRequestOptions, } from "./render.js";
9
+ export { registerCustomComponent } from "./custom-components.js";
10
+ export type * from "./render.types.js";
11
+ export { I18nProvider, useI18n, getActiveLocale, setActiveLocale, applyLocaleToDocument, type I18nProviderProps, type I18nState, } from "@magicvr/schema-ui-lib/i18n/runtime";
12
+ export { resolveTextProp, type MessageParams } from "@magicvr/schema-ui-lib/i18n/catalog";
13
+ export type { Locale, LocalePreference } from "@magicvr/schema-ui-lib/i18n/locale";
@@ -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 "@magicvr/schema-ui-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;