@hyperfixi/intent-element 3.1.1 → 3.2.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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,151 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.2.0] - 2026-09-30
11
+
12
+ A correctness release for multilingual hyperscript. The semantic parser behind
13
+ `translate()`, `@lokascript/hyperscript-adapter`, MCP `translate_code` and hyperfixi's
14
+ non-English path now keeps whole programs that it used to shorten or drop: values,
15
+ conditions, loops and handler heads. Every value shape in a generated matrix of 4,205
16
+ (executed in 24 languages, directly and through the adapter, against upstream
17
+ \_hyperscript) now matches upstream, apart from two documented differences. Core
18
+ matches upstream \_hyperscript on more forms.
19
+
20
+ ### Added
21
+
22
+ - **Counted loops are written in each language's own words**: es `repetir 3 veces`,
23
+ fr `répéter 3 fois`, ja `3 回 を 繰り返し`, hi `3 बार को दोहराएं`, where translations
24
+ wrote English `times` in 18 languages and English `repeat` in the SOV six. English
25
+ `times`/`repeat` and each language's other count words (es `vez`, ru `раза`, …) still
26
+ read. 18 i18n dictionaries gain a `temporal.times` word.
27
+ - **Variable names that are words of the target language.** A variable spelled like a
28
+ structure word (es `si`, pl `w`, tr `al`) is written in parentheses, `(si)`, where the
29
+ plain translation would be misread, and read back as the variable. The language server
30
+ warns (`name-collision`, with a rename quick fix), MCP `validate_hyperscript` reports
31
+ `NAME_COLLISION`, and `translate_code` warns when a variable reads as a value word in
32
+ the target (tl `ako` is `me`). `@lokascript/semantic` exports `nameCollision`,
33
+ `findNameCollisions` and `findTranslationCollisions`.
34
+ - Event-handler modifiers (`once`, `debounced at`, `throttled at`, `from`) render in every
35
+ language and are scored by the fidelity signals.
36
+ - `of`-paths and pseudo-commands (`reset() the closest <form/>`) survive translation.
37
+ - `@lokascript/hyperscript-adapter`'s host gate also rejects a translation that reads a
38
+ reference as a handler's event (`on click on me toggle .active`, an empty click handler
39
+ plus a handler for an event named `me`), falling back to the author's text with a
40
+ warning; such a translation used to parse cleanly and do nothing.
41
+ - **Localized htmx attributes: all 23 non-English languages now cover
42
+ every attribute the _Hypermedia Systems_ book's code listings use** —
43
+ `hx-vals`, `hx-select`, `hx-swap-oob` and `hx-sync` join the Contact.app
44
+ twelve (`hx-値`, `hx-valores`, `hx-값`, `hx-werte`, `hx-選択`, `hx-selección`,
45
+ `hx-seçim`, `hx-auswahl`, `hx-sélection`, `hx-选择`, …). tr/de/fr/zh are new
46
+ in the table: the Contact.app seven they lacked (`hx-sil`, `hx-löschen`,
47
+ `hx-supprimer`, `hx-删除`, …) plus audited trigger/swap primaries following
48
+ loka-js (`hx-tetikleyici`, `hx-auslöser`, `hx-déclencheur`, `hx-替换`; the
49
+ profile words `hx-tetikle`, `hx-auslösen`, `hx-déclencher`, `hx-交换` still
50
+ resolve). de gains a lowercase `hx-ziel`: the profile's `hx-Ziel` could never
51
+ match a parsed attribute, since HTML lowercases attribute names. The other
52
+ 15 languages (ar, bn, he, hi, id, it, ms, pl, qu, ru, sw, th, tl, uk, vi)
53
+ are unreviewed drafts, every entry flagged `lowConfidence`; they also gain
54
+ the trigger heads their dictionaries left in English (ms and tl all six,
55
+ vi/he/ar some). id gains `hx-sasaran` for a target that was the English
56
+ identity; tl keeps `hx-target` on purpose. `hx-ext` is left in English on
57
+ purpose: htmx 4 removed it. The vocab table gains an `events` block for
58
+ trigger heads the i18n dictionaries do not name; ja, es, pt and ko author
59
+ the DOM `search` event (`検索`, `buscar`, `검색`), which the book's and
60
+ Contact.app's search box fires. htmx's own trigger words (`revealed`,
61
+ `every`) stay English, like the `delay:` / `from:` modifiers.
62
+ - Adapter-only vocab keys now resolve from the authored table alone, never
63
+ from a semantic profile: 22 profiles carry a `select` keyword that means
64
+ mark/highlight text (de `markieren`, tr `vurgula`), and it would otherwise
65
+ have shipped as `hx-select` in 22 unreviewed languages.
66
+
67
+ - **Localized htmx attributes: ja, es, pt and ko now cover every attribute the
68
+ _Hypermedia Systems_ Contact.app uses** — `hx-post`, `hx-delete`,
69
+ `hx-confirm`, `hx-push-url`, `hx-boost`, `hx-indicator` and `hx-include` join
70
+ the existing five (e.g. `hx-削除`, `hx-eliminar`, `hx-excluir`, `hx-삭제`).
71
+ `hx-indicator` / `hx-include` localize under `@hyperfixi/htmx-adapter` (stock
72
+ htmx implements them; the embedded layer does not).
73
+ - **Vocab aliases.** Several localized names may map to one attribute or event;
74
+ the first is the form to teach, later ones keep already-authored pages
75
+ working. Core's embedded orchestrator now reads whichever form an element
76
+ carries (it previously kept a single name per attribute).
77
+
78
+ ### Changed
79
+
80
+ - **Rendered vocabulary** (owner decisions): `null` is written `null` in ar, hi, id, qu,
81
+ sw, th and tr, where it shared the word for `empty`; hi writes `no` as `कोई नहीं`; qu
82
+ writes `and` as `hinallataq`; tl and tr write `includes` with `contains`' word (both
83
+ engines read the two as one operator).
84
+ - `@lokascript/semantic` exports its real `VERSION` (it was `0.1.0` since the first
85
+ release); browser bundles report `<version>-<bundle>`.
86
+ - MCP `translate_hyperscript` is described accurately: it uses the same semantic engine as
87
+ `translate_code`, without the verification.
88
+ - **Audited primaries for `hx-trigger` / `hx-swap`** in ja (`hx-トリガー`,
89
+ `hx-置換`), es (`hx-disparador`, `hx-intercambio`), pt (`hx-gatilho`,
90
+ `hx-troca`) and ko (`hx-교체`), following loka-js's terminology reviews. The
91
+ names that shipped before (`hx-引き金`, `hx-disparar`, …) still resolve.
92
+ - The vocab modules are regenerated against the current i18n dictionaries.
93
+ **No working name was removed** — 141 retired event names and three attribute
94
+ names are kept as aliases. One name changed meaning with its dictionary: sw
95
+ `panya_juu` is now `mouseup` (was `mouseover`).
96
+
97
+ ### Fixed
98
+
99
+ - **Translations that lost code** (semantic parse and render, in every language):
100
+ - `get` of a literal (`get "hello"`, `get 3`, `get true`) dropped the whole command; an
101
+ object literal after `get` was cut to `get {`.
102
+ - Loops keep their extent and body; `repeat until` stops; a loop keeps its index
103
+ variable, the tail after an `if` inside it, and the command after an empty loop its own
104
+ `end` closes; a count may be any value (a variable, `$n`, `it`, a possessive), where
105
+ some translations read `forever`, a silent infinite loop.
106
+ - Handler heads keep `or` events, filters and event parameters; a translated
107
+ `wait for <event>` waits instead of throwing; behaviors keep their handlers and init
108
+ blocks; an else-if chain shares one `end`.
109
+ - Values keep their comparison phrases (`is equal to`, `includes`, `is an Element`, …),
110
+ `and`/`or`/`not`, `mod`, possessive and `of` chains, object and array literals, every
111
+ conversion core supports (`as Int`, `as Fixed:2`, piped conversions), unary minus and
112
+ property paths; `true`/`false`/`null` keep their meaning.
113
+ - Commands keep `put … into <variable>`, `tell … to show`, fetch's `do not throw` and
114
+ response type, event-source/socket URLs, show/hide `with` strategies, `go`/`scroll`
115
+ positions, `${…}` URLs, core's `has`, and a class query's `in` scope.
116
+ - **`@hyperfixi/core` follows upstream \_hyperscript**:
117
+ - `X of Y` binds as property access, and the X of a null target is null.
118
+ - `increment` reads its amount and counter as upstream does, and writing an object's
119
+ property through `'s`/`of` works.
120
+ - A loop's count reads as upstream's `index < times` does: `"6.5"` loops 7 times and
121
+ `"6abc"` none, in the full runtime and in the hybrid bundles (`hyperfixi-hx.js`,
122
+ plugin bundles), which read `parseInt`.
123
+ - Blocks close at the end of input; `morph … to`; `render … with name: value`; `beep!`
124
+ as an expression; `transition`'s owners, several properties, `from` and `using`; three
125
+ handler forms from _Hypermedia Systems_; four upstream-valid forms that compiled and
126
+ then failed at run time; `.item in #list` keeps its scope; `on click once` fires on
127
+ the first click.
128
+ - htmx: camelCase event names survive an attribute name.
129
+ - MCP: the validators report what the parsers actually did.
130
+ - The AOT compiler (experimental): loops were compiled to `while (true)`; targets,
131
+ property access, scoped queries, `empty` and variable scope now compile to what they
132
+ mean.
133
+ - `@lokascript/hyperscript-adapter` and `@hyperscript-tools/multilingual` READMEs: the
134
+ Japanese quickstart example was a dead button, and the multi-language example loaded
135
+ only the Spanish bundle; the examples are now native and run in a test. Bundle sizes are
136
+ measured.
137
+ - Bare CDN URLs now serve a browser bundle: `unpkg.com/@lokascript/hyperscript-adapter@3` (and
138
+ jsDelivr) served `dist/index.cjs`, which throws in a `<script>` tag. The adapter,
139
+ `@hyperscript-tools/multilingual`, `@lokascript/semantic`, `@hyperfixi/core` and
140
+ `@lokascript/htmx-adapter` declare `unpkg`/`jsdelivr` entries.
141
+ - `hyperfixi-multilingual.js`'s error names the semantic bundle that exists (the full
142
+ `browser.global.js`, the only one that defines `LokaScriptSemantic`).
143
+ - READMEs: `@lokascript/i18n` no longer claims to translate (and its `/lsp` export and CLI,
144
+ which do not exist, are gone); `@lokascript/semantic`, `@hyperfixi/vite-plugin` and
145
+ `@hyperfixi/core` have correct package names, API calls, language lists (24) and measured
146
+ bundle sizes.
147
+ - Dependencies: the js-yaml and smol-toml advisories; Vite 8, vitest 5 and happy-dom 20.14.
148
+ - **Vietnamese `hx-get` / `hx-target` / `hx-swap` / `hx-trigger` / `sse-swap`
149
+ never worked**: the names were emitted with spaces (`hx-lấy giá trị`), which
150
+ HTML reads as three attributes. They are now hyphen-joined
151
+ (`hx-lấy-giá-trị`), like vi's existing `hx-trực-tiếp`.
152
+ - 29 multi-word event names (ar, vi) were removed from the vocab modules: an
153
+ event is a single token of an `hx-trigger` value, so they could never match.
154
+
10
155
  ## [3.1.1] - 2026-09-04
11
156
 
12
157
  ### Fixed
@@ -1 +1 @@
1
- {"version":3,"sources":["../../intent/src/types.ts","../../intent/src/diagnostics.ts","../../intent/src/schema.ts","../../intent/src/inference/infer-roles.ts","../../intent/src/ir/references.ts","../../intent/src/ir/parser.ts","../../intent/src/ir/renderer.ts","../../intent/src/ir/protocol.ts","../src/schema-registry.ts","../src/sandbox.ts","../src/lse-intent.ts","../src/index.ts"],"sourcesContent":["/**\n * Universal UI Intent Types\n *\n * Canonical semantic representation for DSL commands. Language-neutral —\n * captures the MEANING of commands independent of surface syntax.\n *\n * These types are domain-agnostic and work for any DSL (hyperscript, SQL,\n * voice commands, animations, etc.) and any target framework (React, Vue,\n * Alpine, vanilla JS).\n */\n\nimport type { Diagnostic } from './diagnostics';\n\n// =============================================================================\n// Action & Role\n// =============================================================================\n\n/**\n * The command/operation in a DSL (e.g., 'toggle', 'fetch', 'select').\n */\nexport type ActionType = string;\n\n/**\n * The grammatical function of a part of a command (e.g., 'patient', 'destination').\n */\nexport type SemanticRole = string;\n\n// =============================================================================\n// Semantic Values (7 types)\n// =============================================================================\n\nexport type SemanticValue =\n LiteralValue | SelectorValue | ReferenceValue | PropertyPathValue | ExpressionValue | FlagValue;\n\n/** Expected value types for role tokens. */\nexport type ExpectedType = SemanticValue['type'];\n\nexport interface LiteralValue {\n readonly type: 'literal';\n readonly value: string | number | boolean;\n readonly dataType?: 'string' | 'number' | 'boolean' | 'duration';\n}\n\nexport interface SelectorValue {\n readonly type: 'selector';\n readonly value: string;\n readonly selectorKind?: 'id' | 'class' | 'attribute' | 'element' | 'complex' | 'identifier';\n}\n\nexport interface ReferenceValue {\n readonly type: 'reference';\n readonly value: string;\n}\n\nexport interface PropertyPathValue {\n readonly type: 'property-path';\n readonly object: SemanticValue;\n readonly property: string;\n}\n\nexport interface ExpressionValue {\n readonly type: 'expression';\n readonly raw: string;\n}\n\nexport interface FlagValue {\n readonly type: 'flag';\n readonly name: string;\n readonly enabled: boolean;\n}\n\n// =============================================================================\n// Annotations & Protocol Diagnostics (v1.2)\n// =============================================================================\n\n/** A metadata annotation on a node (v1.2). */\nexport interface Annotation {\n readonly name: string;\n readonly value?: string;\n}\n\n/**\n * A type constraint diagnostic attached to a node (v1.2).\n * Named `ProtocolDiagnostic` to avoid collision with the `Diagnostic` type.\n *\n * Uses `severity` (not `level`) to match the `Diagnostic` type convention.\n * The wire-format equivalent, `ProtocolDiagnosticJSON`, uses `level` per the\n * LSE protocol spec — this is intentional and kept separate.\n */\nexport interface ProtocolDiagnostic {\n readonly severity: 'error' | 'warning' | 'info';\n readonly role: string;\n readonly message: string;\n readonly code: string;\n}\n\n/** Async coordination variant (v1.2). */\nexport type AsyncVariant = 'all' | 'race';\n\n/** A single arm in a match command (v1.2). */\nexport interface MatchArm {\n readonly pattern: SemanticValue;\n readonly body: SemanticNode[];\n}\n\n// =============================================================================\n// Semantic Nodes (5 kinds)\n// =============================================================================\n\n/** Base interface for all semantic nodes. */\nexport interface SemanticNode {\n readonly kind: 'command' | 'event-handler' | 'conditional' | 'compound' | 'loop';\n readonly action: ActionType;\n readonly roles: ReadonlyMap<SemanticRole, SemanticValue>;\n readonly metadata?: SemanticMetadata;\n readonly annotations?: readonly Annotation[];\n readonly diagnostics?: readonly Diagnostic[];\n}\n\nexport interface SemanticMetadata {\n readonly sourceLanguage?: string;\n readonly sourceText?: string;\n readonly sourcePosition?: SourcePosition;\n readonly patternId?: string;\n readonly confidence?: number;\n}\n\nexport interface SourcePosition {\n readonly start: number;\n readonly end: number;\n readonly line?: number;\n readonly column?: number;\n}\n\n/** A single DSL command. */\nexport interface CommandSemanticNode extends SemanticNode {\n readonly kind: 'command';\n readonly body?: readonly SemanticNode[];\n readonly catchBranch?: readonly SemanticNode[];\n readonly finallyBranch?: readonly SemanticNode[];\n readonly asyncVariant?: AsyncVariant;\n readonly asyncBody?: readonly SemanticNode[];\n readonly arms?: readonly MatchArm[];\n readonly defaultArm?: readonly SemanticNode[];\n}\n\n/** Trigger-based commands (e.g., \"on click\"). */\nexport interface EventHandlerSemanticNode extends SemanticNode {\n readonly kind: 'event-handler';\n readonly body: SemanticNode[];\n readonly eventModifiers?: EventModifiers;\n readonly additionalEvents?: readonly SemanticValue[];\n readonly parameterNames?: readonly string[];\n}\n\nexport interface EventModifiers {\n readonly once?: boolean;\n readonly debounce?: number;\n readonly throttle?: number;\n readonly queue?: 'first' | 'last' | 'all' | 'none';\n readonly from?: SemanticValue;\n}\n\n/** Conditional: \"if [condition] then [body] else [body]\". */\nexport interface ConditionalSemanticNode extends SemanticNode {\n readonly kind: 'conditional';\n readonly thenBranch: SemanticNode[];\n readonly elseBranch?: SemanticNode[];\n}\n\n/** Multiple chained statements. */\nexport interface CompoundSemanticNode extends SemanticNode {\n readonly kind: 'compound';\n readonly statements: SemanticNode[];\n readonly chainType: 'then' | 'and' | 'async' | 'sequential' | 'pipe';\n}\n\n/** Loop variant discriminant. */\nexport type LoopVariant = 'forever' | 'times' | 'for' | 'while' | 'until';\n\n/** Repeat/for/while loops. */\nexport interface LoopSemanticNode extends SemanticNode {\n readonly kind: 'loop';\n readonly loopVariant: LoopVariant;\n readonly body: SemanticNode[];\n readonly loopVariable?: string;\n readonly indexVariable?: string;\n}\n\n/** Versioned envelope for multi-node LSE documents (v1.2). */\nexport interface LSEEnvelope {\n readonly lseVersion: string;\n readonly features?: readonly string[];\n readonly nodes: readonly SemanticNode[];\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\nexport function createLiteral(\n value: string | number | boolean,\n dataType?: 'string' | 'number' | 'boolean' | 'duration'\n): LiteralValue {\n return dataType ? { type: 'literal', value, dataType } : { type: 'literal', value };\n}\n\nexport function createSelector(\n value: string,\n selectorKind?: SelectorValue['selectorKind']\n): SelectorValue {\n return selectorKind ? { type: 'selector', value, selectorKind } : { type: 'selector', value };\n}\n\nexport function createReference(value: string): ReferenceValue {\n return { type: 'reference', value };\n}\n\nexport function createPropertyPath(object: SemanticValue, property: string): PropertyPathValue {\n return { type: 'property-path', object, property };\n}\n\nexport function createExpression(raw: string): ExpressionValue {\n return { type: 'expression', raw };\n}\n\nexport function createFlag(name: string, enabled: boolean = true): FlagValue {\n return { type: 'flag', name, enabled };\n}\n\nexport function createCommandNode(\n action: ActionType,\n roles: Record<SemanticRole, SemanticValue> | Map<SemanticRole, SemanticValue>,\n metadata?: SemanticMetadata\n): CommandSemanticNode {\n const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));\n const node: CommandSemanticNode = { kind: 'command', action, roles: rolesMap };\n return metadata ? { ...node, metadata } : node;\n}\n\nexport function createEventHandlerNode(\n action: ActionType,\n roles: Record<SemanticRole, SemanticValue> | Map<SemanticRole, SemanticValue>,\n body: SemanticNode[],\n metadata?: SemanticMetadata,\n eventModifiers?: EventModifiers\n): EventHandlerSemanticNode {\n const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));\n return {\n kind: 'event-handler',\n action,\n roles: rolesMap,\n body,\n ...(eventModifiers && { eventModifiers }),\n ...(metadata && { metadata }),\n };\n}\n\nexport function createConditionalNode(\n action: ActionType,\n roles: Record<SemanticRole, SemanticValue> | Map<SemanticRole, SemanticValue>,\n thenBranch: SemanticNode[],\n elseBranch?: SemanticNode[],\n metadata?: SemanticMetadata\n): ConditionalSemanticNode {\n const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));\n return {\n kind: 'conditional',\n action,\n roles: rolesMap,\n thenBranch,\n ...(elseBranch && { elseBranch }),\n ...(metadata && { metadata }),\n };\n}\n\nexport function createCompoundNode(\n statements: SemanticNode[],\n chainType: CompoundSemanticNode['chainType'] = 'sequential',\n metadata?: SemanticMetadata\n): CompoundSemanticNode {\n const base = {\n kind: 'compound' as const,\n action: 'compound' as const,\n roles: new Map(),\n statements,\n chainType,\n };\n return metadata ? { ...base, metadata } : base;\n}\n\nexport function createLoopNode(\n action: ActionType,\n roles: Record<SemanticRole, SemanticValue> | Map<SemanticRole, SemanticValue>,\n loopVariant: LoopVariant,\n body: SemanticNode[],\n loopVariable?: string,\n indexVariable?: string,\n metadata?: SemanticMetadata\n): LoopSemanticNode {\n const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));\n return {\n kind: 'loop',\n action,\n roles: rolesMap,\n loopVariant,\n body,\n ...(loopVariable && { loopVariable }),\n ...(indexVariable && { indexVariable }),\n ...(metadata && { metadata }),\n };\n}\n\nexport function createTryNode(\n body: SemanticNode[],\n catchBranch?: SemanticNode[],\n finallyBranch?: SemanticNode[],\n metadata?: SemanticMetadata\n): CommandSemanticNode {\n return {\n kind: 'command',\n action: 'try',\n roles: new Map(),\n body,\n ...(catchBranch && catchBranch.length > 0 ? { catchBranch } : {}),\n ...(finallyBranch && finallyBranch.length > 0 ? { finallyBranch } : {}),\n ...(metadata ? { metadata } : {}),\n };\n}\n\nexport function createAsyncNode(\n variant: AsyncVariant,\n asyncBody: SemanticNode[],\n metadata?: SemanticMetadata\n): CommandSemanticNode {\n return {\n kind: 'command',\n action: variant,\n roles: new Map(),\n asyncVariant: variant,\n asyncBody,\n ...(metadata ? { metadata } : {}),\n };\n}\n\nexport function createMatchNode(\n roles: Record<SemanticRole, SemanticValue> | Map<SemanticRole, SemanticValue>,\n arms: MatchArm[],\n defaultArm?: SemanticNode[],\n metadata?: SemanticMetadata\n): CommandSemanticNode {\n const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));\n return {\n kind: 'command',\n action: 'match',\n roles: rolesMap,\n arms,\n ...(defaultArm && defaultArm.length > 0 ? { defaultArm } : {}),\n ...(metadata ? { metadata } : {}),\n };\n}\n\n// =============================================================================\n// Value Extraction Helpers\n// =============================================================================\n\n/**\n * Extract a string representation from any SemanticValue.\n */\nexport function extractValue(value: SemanticValue): string {\n if (value.type === 'flag') return value.name;\n if ('raw' in value && value.raw !== undefined) return String(value.raw);\n if ('value' in value && value.value !== undefined) return String(value.value);\n if (value.type === 'property-path') return `${extractValue(value.object)}.${value.property}`;\n return '';\n}\n\n/**\n * Get the typed `SemanticValue` for a named role on a SemanticNode.\n * Returns `undefined` if the role is missing.\n *\n * Use this when you need the structured value (e.g. to branch on\n * `value.type`, read `value.selectorKind`, etc.). See {@link extractRoleValue}\n * for the string-convenience companion.\n */\nexport function getRoleValue(node: SemanticNode, role: string): SemanticValue | undefined {\n return node.roles.get(role);\n}\n\n/**\n * Extract a string representation from a named role on a SemanticNode.\n * Returns empty string if the role is missing.\n *\n * String-convenience companion to {@link getRoleValue}. Use this when you\n * just want a stringified role value; use `getRoleValue` when you need to\n * inspect the typed `SemanticValue` (e.g. to branch on `value.type`).\n */\nexport function extractRoleValue(node: SemanticNode, role: string): string {\n const value = node.roles.get(role);\n if (!value) return '';\n return extractValue(value);\n}\n","/**\n * Structured Error Diagnostics\n *\n * Generic diagnostic types for reporting errors, warnings, and hints\n * during parsing, validation, and compilation. These provide richer feedback\n * than simple error strings, enabling:\n *\n * - Severity levels (error, warning, info)\n * - Machine-readable error codes\n * - Source location tracking\n * - Actionable suggestions\n * - Programmatic fix lookup\n */\n\n// =============================================================================\n// Diagnostic Types\n// =============================================================================\n\n/**\n * Severity level for a diagnostic.\n */\nexport type DiagnosticSeverity = 'error' | 'warning' | 'info';\n\n/**\n * A structured diagnostic message.\n *\n * Diagnostics provide rich error information beyond simple strings.\n * They're used for parser errors, validation warnings, and hints.\n */\nexport interface Diagnostic {\n /** Human-readable message */\n readonly message: string;\n\n /** Severity level */\n readonly severity: DiagnosticSeverity;\n\n /** Machine-readable code for programmatic handling (e.g., 'parse-error', 'missing-role') */\n readonly code?: string;\n\n /**\n * The role this diagnostic is about (e.g. `patient`). The wire format's\n * `Diagnostic` shape requires it, so it must survive a protocol-JSON round-trip.\n */\n readonly role?: string;\n\n /** 1-based line number in source */\n readonly line?: number;\n\n /** 0-based column offset in line */\n readonly column?: number;\n\n /** Original source text that triggered the diagnostic */\n readonly source?: string;\n\n /** Actionable suggestions for fixing the issue */\n readonly suggestions?: readonly string[];\n}\n\n/**\n * Result of a diagnostic-aware validation.\n */\nexport interface DiagnosticResult {\n /** Whether validation passed (no errors, though warnings/info may exist) */\n readonly ok: boolean;\n\n /** All diagnostics, including errors, warnings, and info */\n readonly diagnostics: readonly Diagnostic[];\n\n /** Convenience: count of each severity level */\n readonly summary: DiagnosticSummary;\n}\n\n/**\n * Summary counts by severity.\n */\nexport interface DiagnosticSummary {\n readonly errors: number;\n readonly warnings: number;\n readonly infos: number;\n}\n\n// =============================================================================\n// Diagnostic Collector\n// =============================================================================\n\n/**\n * Collects diagnostics during a parsing/validation/compilation pass.\n *\n * @example\n * ```typescript\n * const collector = createDiagnosticCollector();\n * collector.error('Unknown command: foo', { code: 'unknown-command', line: 3 });\n * const result = collector.toResult();\n * ```\n */\nexport interface DiagnosticCollector {\n /** Message-first form: `error(message, options?)` */\n error(message: string, options?: DiagnosticOptions): void;\n /** Code-first form: `error(code, message, options?)` — useful at call sites where the code is the stable key */\n error(code: string, message: string, options?: DiagnosticOptions): void;\n warning(message: string, options?: DiagnosticOptions): void;\n info(message: string, options?: DiagnosticOptions): void;\n add(diagnostic: Diagnostic): void;\n hasErrors(): boolean;\n getDiagnostics(): readonly Diagnostic[];\n toResult(): DiagnosticResult;\n}\n\n/**\n * Options for adding a diagnostic via the collector's convenience methods.\n */\nexport interface DiagnosticOptions {\n readonly code?: string;\n readonly line?: number;\n readonly column?: number;\n readonly source?: string;\n readonly suggestions?: readonly string[];\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\n/**\n * Create a new diagnostic collector.\n */\nexport function createDiagnosticCollector(): DiagnosticCollector {\n const diagnostics: Diagnostic[] = [];\n\n function addWithSeverity(\n severity: DiagnosticSeverity,\n message: string,\n options?: DiagnosticOptions\n ): void {\n const diag: Diagnostic = {\n message,\n severity,\n ...(options?.code != null && { code: options.code }),\n ...(options?.line != null && { line: options.line }),\n ...(options?.column != null && { column: options.column }),\n ...(options?.source != null && { source: options.source }),\n ...(options?.suggestions != null &&\n options.suggestions.length > 0 && { suggestions: options.suggestions }),\n };\n diagnostics.push(diag);\n }\n\n return {\n error(\n messageOrCode: string,\n messageOrOptions?: string | DiagnosticOptions,\n options?: DiagnosticOptions\n ) {\n if (typeof messageOrOptions === 'string') {\n // Code-first form: error(code, message, options?)\n addWithSeverity('error', messageOrOptions, { ...options, code: messageOrCode });\n } else {\n // Message-first form: error(message, options?)\n addWithSeverity('error', messageOrCode, messageOrOptions);\n }\n },\n warning(message, options) {\n addWithSeverity('warning', message, options);\n },\n info(message, options) {\n addWithSeverity('info', message, options);\n },\n add(diagnostic) {\n diagnostics.push(diagnostic);\n },\n hasErrors() {\n return diagnostics.some(d => d.severity === 'error');\n },\n getDiagnostics() {\n return diagnostics;\n },\n toResult(): DiagnosticResult {\n let errors = 0;\n let warnings = 0;\n let infos = 0;\n for (const d of diagnostics) {\n if (d.severity === 'error') errors++;\n else if (d.severity === 'warning') warnings++;\n else infos++;\n }\n return {\n ok: errors === 0,\n diagnostics,\n summary: { errors, warnings, infos },\n };\n },\n };\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/**\n * Create a diagnostic from an Error object.\n */\nexport function fromError(\n error: unknown,\n options?: Omit<DiagnosticOptions, 'code'> & { code?: string }\n): Diagnostic {\n const message = error instanceof Error ? error.message : String(error);\n return {\n message,\n severity: 'error',\n ...(options?.code != null && { code: options.code }),\n ...(options?.line != null && { line: options.line }),\n ...(options?.column != null && { column: options.column }),\n ...(options?.source != null && { source: options.source }),\n ...(options?.suggestions != null &&\n options.suggestions.length > 0 && { suggestions: options.suggestions }),\n };\n}\n\n/**\n * Filter diagnostics by severity.\n */\nexport function filterBySeverity(\n diagnostics: readonly Diagnostic[],\n severity: DiagnosticSeverity\n): readonly Diagnostic[] {\n return diagnostics.filter(d => d.severity === severity);\n}\n","/**\n * Command Schema Types\n *\n * Defines the structure of DSL commands for pattern generation and validation.\n * Language-neutral — describes semantic structure, not surface syntax.\n */\n\nimport type { ActionType, SemanticRole, SemanticValue, ExpectedType } from './types';\n\nexport interface CommandSchema {\n readonly action: ActionType;\n readonly description: string;\n readonly roles: RoleSpec[];\n readonly primaryRole: SemanticRole;\n readonly category: string;\n readonly hasBody?: boolean;\n readonly notes?: string;\n /**\n * Identifier tokens that may appear in the core parser's positional `args`\n * but are not bound to any role — e.g., scroll's `top|bottom|smoothly`.\n * These are skipped when scanning args for role values.\n */\n readonly argSkipTokens?: ReadonlyArray<string>;\n /**\n * Which role consumes the core parser's `target` field (the trailing\n * `on X` / `to X` / `from X` captured separately from args). Defaults to\n * `'destination'`. Set to `'source'` for commands like `remove .x from #y`.\n */\n readonly targetRole?: SemanticRole;\n}\n\nexport interface RoleSpec {\n readonly role: SemanticRole;\n readonly description: string;\n readonly required: boolean;\n readonly expectedTypes: Array<ExpectedType>;\n readonly default?: SemanticValue;\n /**\n * Ordering priority for SVO (Subject-Verb-Object) languages.\n * **Higher values appear EARLIER** in the surface form (descending sort).\n *\n * Example — `select <columns> from <source> where <condition>`:\n * `columns: 2, source: 1, condition: 0`\n *\n * Pick values so the most \"central\" role is highest and optional / trailing\n * clauses are lowest. When adding a role between two existing ones, bump all\n * higher positions rather than using fractional numbers.\n */\n readonly svoPosition?: number;\n /**\n * Ordering priority for SOV (Subject-Object-Verb) languages.\n * **Higher values appear EARLIER** in the surface form (descending sort).\n *\n * Same convention as {@link svoPosition} — the verb is appended last by the\n * pattern generator, so role positions order the pre-verb material.\n */\n readonly sovPosition?: number;\n /**\n * Per-language primary marker keyword for this role.\n *\n * - Non-empty string (e.g., `'to'`) — the canonical marker.\n * - Empty string `''` — explicitly no marker (bare positional arg).\n * - Multi-word strings (e.g., `'partials in'`) — match consecutive identifier tokens.\n * - `undefined` — the language profile's default marker for the role applies.\n *\n * For roles that accept multiple alternate markers (e.g., `put`'s\n * `into|before|after`), declare the alternates separately in {@link markerVariants}.\n */\n readonly markerOverride?: Record<string, string>;\n /**\n * Additional alternate marker keywords for this role, beyond {@link markerOverride}.\n * Used by schema-driven role inference to recognize any of the listed markers as\n * signaling this role; the matched marker is recorded in {@link methodCarrier} if set.\n *\n * Pattern-generation consumers typically read only `markerOverride`; this field is\n * specifically for bridge-side inference (see `inferRolesFromSchema`).\n */\n readonly markerVariants?: Record<string, readonly string[]>;\n /**\n * Per-language marker to use when RENDERING, overriding {@link markerOverride}\n * and the profile default. Empty string `''` means \"render this role bare\" —\n * for a marker that exists to help the parser but is not written by hand\n * (SQL's `insert` takes its values with no `set` before them, though the\n * profile gives the `values` role that marker).\n *\n * The key `'*'` is a blanket statement about the PROFILE default marker, so\n * it ranks below a per-language {@link markerOverride}: `{ '*': '' }` renders\n * the role bare in every language except those with an explicit override.\n * Full precedence: `renderOverride[lang]` → `markerOverride[lang]` →\n * `renderOverride['*']` → profile default.\n */\n readonly renderOverride?: Record<string, string>;\n readonly markerPosition?: 'before' | 'after';\n /**\n * Per-language override of {@link markerPosition}, for languages whose marker\n * sits on the other side of its value than their word order implies.\n *\n * Resolution when rendering: this → {@link markerPosition} → the profile's\n * marker position for the role → the word-order default (SOV: after, else before).\n */\n readonly markerPositionOverride?: Record<string, 'before' | 'after'>;\n /**\n * Which side of the verb this role renders on in SOV languages.\n *\n * SOV puts roles before the verb by default (`preVerb`). `postVerb` moves this\n * role after it — e.g. summarize's format renders `content 要約 として format`,\n * not `content として format 要約`. Ignored for non-SOV languages.\n */\n readonly sovSlot?: 'preVerb' | 'postVerb';\n /**\n * Wrap this role's value in double quotes when rendering, if it contains\n * whitespace and is not already quoted. For free-text roles (a question, a\n * prompt) whose value would otherwise be re-parsed as several tokens.\n */\n readonly quoteMultiword?: boolean;\n readonly greedy?: boolean;\n readonly selectorKinds?: ReadonlyArray<'id' | 'class' | 'attribute' | 'element' | 'complex'>;\n /**\n * When this role's marker has alternates (via {@link markerVariants}), the\n * matched marker keyword is recorded as a literal in the role named here.\n * Used by `put` to expose `into|before|after` as the `method` role.\n */\n readonly methodCarrier?: SemanticRole;\n}\n\n/**\n * Helper to create a command schema with sensible defaults.\n *\n * @throws TypeError when `schema` is missing `action` or `roles`, or when they\n * have the wrong shape. The types catch this for TypeScript callers; the\n * runtime check exists so JavaScript callers and hand-built objects fail with\n * a message naming the field instead of a `roles[0] of undefined` crash from\n * deep inside the package.\n */\nexport function defineCommand(\n schema: Partial<CommandSchema> & Pick<CommandSchema, 'action' | 'roles'>\n): CommandSchema {\n if (typeof schema !== 'object' || schema === null) {\n throw new TypeError(`defineCommand: expected a schema object, got ${describeValue(schema)}.`);\n }\n if (typeof schema.action !== 'string' || schema.action.length === 0) {\n throw new TypeError(\n `defineCommand: 'action' must be a non-empty string (got ${describeValue(schema.action)}).`\n );\n }\n if (!Array.isArray(schema.roles)) {\n throw new TypeError(\n `defineCommand(\"${schema.action}\"): 'roles' must be an array of RoleSpec ` +\n `(got ${describeValue(schema.roles)}). Build entries with defineRole().`\n );\n }\n for (const [index, role] of schema.roles.entries()) {\n if (typeof role !== 'object' || role === null || typeof role.role !== 'string') {\n throw new TypeError(\n `defineCommand(\"${schema.action}\"): roles[${index}] must be a RoleSpec with a string ` +\n `'role' field (got ${describeValue(role)}). Build it with defineRole().`\n );\n }\n }\n\n return {\n description: schema.description || `${schema.action} command`,\n category: schema.category || 'general',\n primaryRole: schema.primaryRole || schema.roles[0]?.role || 'patient',\n ...schema,\n action: schema.action,\n roles: schema.roles,\n } as CommandSchema;\n}\n\n/**\n * Helper to create a role spec with sensible defaults.\n */\nexport function defineRole(\n role: Partial<RoleSpec> & Pick<RoleSpec, 'role' | 'required' | 'expectedTypes'>\n): RoleSpec {\n return {\n description: role.description || `${role.role} role`,\n ...role,\n role: role.role,\n required: role.required,\n expectedTypes: role.expectedTypes,\n };\n}\n\n/**\n * Look up a role spec by name within a command schema.\n * Returns `undefined` if no role with that name exists.\n *\n * @example\n * const spec = getRoleSpec(toggleSchema, 'patient');\n * if (spec?.required) { ... }\n */\nexport function getRoleSpec(schema: CommandSchema, role: string): RoleSpec | undefined {\n // Contract is already \"undefined when not found\", so a malformed schema\n // answers the same question rather than throwing.\n if (!schema || !Array.isArray(schema.roles)) return undefined;\n return schema.roles.find(r => r.role === role);\n}\n\n/** Render a value for an error message without dumping large objects. */\nfunction describeValue(value: unknown): string {\n if (value === null) return 'null';\n if (Array.isArray(value)) return `an array of length ${value.length}`;\n if (typeof value === 'string') return `the string ${JSON.stringify(value)}`;\n if (typeof value === 'object') return 'an object';\n return typeof value === 'undefined' ? 'undefined' : String(value);\n}\n","/**\n * Schema-driven role inference.\n *\n * Maps a parser's positional args + modifiers + target → a role-keyed map,\n * driven by a `CommandSchema`. Used by IR bridges (core's `from-core.ts`,\n * framework's `from-interchange.ts`) to convert positional ASTs into the\n * role-based semantic IR without hand-rolling per-command switch cases.\n *\n * The function is generic over input/output value types via a small `ValueAdapter`\n * interface so callers with different node shapes (InterchangeNode, structural\n * INode, SemanticValue) can all share the same role-mapping logic.\n *\n * The schema-driven inference operates on the **English form** only (the surface\n * form the core hyperscript parser produces); per-language profile defaults\n * are not consulted here.\n */\nimport type { CommandSchema, RoleSpec } from '../schema';\nimport type { SemanticRole } from '../types';\n\n/**\n * Adapter interface that abstracts over the caller's node shape.\n *\n * @typeParam T - The input value type (e.g., `InterchangeNode`, `INode`).\n * @typeParam V - The output role-value type (e.g., `InterchangeNode`, `SemanticValue`).\n */\nexport interface ValueAdapter<T, V> {\n /**\n * If `node` is an identifier, return its keyword string; otherwise return\n * `undefined`. Used to match marker keywords and skip-tokens against args.\n */\n getIdentifierName(node: T): string | undefined;\n /**\n * Convert an input node to the output role-value type.\n */\n convertValue(node: T): V;\n /**\n * Wrap a raw string as a literal output value (used for `methodCarrier`\n * literals and for string-shaped modifier values).\n */\n createLiteralValue(text: string): V;\n}\n\n/**\n * Infer semantic roles from a `CommandSchema` plus the parser's positional\n * args, modifier object, and trailing-target node.\n *\n * Returns the role map (possibly empty). Returns `null` only if the schema is\n * not applicable (currently never — the function always returns a map).\n *\n * @example\n * ```ts\n * const roles = inferRolesFromSchema(toggleSchema, args, modifiers, target, adapter);\n * // → { patient: <args[0]>, destination: <target> }\n * ```\n */\nexport function inferRolesFromSchema<T, V>(\n schema: CommandSchema,\n args: readonly T[],\n modifiers: Readonly<Record<string, unknown>> | undefined,\n target: T | undefined,\n adapter: ValueAdapter<T, V>\n): Record<SemanticRole, V> {\n const roles: Record<SemanticRole, V> = {};\n const skipTokens = new Set(schema.argSkipTokens ?? []);\n const usedArgIndices = new Set<number>();\n\n // Pass 1: marker-keyed roles (modifier hit, then marker-in-args).\n // Iterate in declaration order so methodCarrier writes happen alongside their owning role.\n for (const role of schema.roles) {\n const markers = englishMarkers(role);\n if (markers.length === 0) continue; // No marker → handled in pass 2\n\n // Try modifiers first.\n let matched = tryModifierMarkers(role, markers, modifiers, adapter);\n\n // Fall back to scanning args for marker-in-args (e.g., scroll's `to`).\n if (!matched) {\n matched = tryArgMarkers(role, markers, args, skipTokens, usedArgIndices, adapter);\n }\n\n if (matched) {\n roles[role.role] = matched.value;\n if (role.methodCarrier) {\n roles[role.methodCarrier] = adapter.createLiteralValue(matched.matchedMarker);\n }\n }\n }\n\n // Pass 2: target field → targetRole (default 'destination').\n // Done before the positional pass so the target-filled role isn't also\n // considered for a positional arg slot.\n if (target !== undefined) {\n const tgtRole = schema.targetRole ?? 'destination';\n if (roles[tgtRole] === undefined) {\n roles[tgtRole] = adapter.convertValue(target);\n }\n }\n\n // Pass 3: positional (no-marker, not-yet-filled) roles, ordered by\n // svoPosition ASCENDING (semantic's convention — lower number = earlier\n // in surface form, matches `sortRolesByWordOrder` in @lokascript/semantic).\n const positionalRoles = schema.roles\n .filter(r => englishMarkers(r).length === 0 && roles[r.role] === undefined)\n .sort((a, b) => (a.svoPosition ?? 99) - (b.svoPosition ?? 99));\n\n let cursor = 0;\n for (const role of positionalRoles) {\n while (cursor < args.length) {\n if (usedArgIndices.has(cursor)) {\n cursor++;\n continue;\n }\n const name = adapter.getIdentifierName(args[cursor]);\n if (name !== undefined && skipTokens.has(name)) {\n cursor++;\n continue;\n }\n roles[role.role] = adapter.convertValue(args[cursor]);\n usedArgIndices.add(cursor);\n cursor++;\n break;\n }\n }\n\n return roles;\n}\n\n/**\n * Return the role's English marker(s). Empty array means \"positional\"\n * (either explicit empty string `''` or no `markerOverride.en` at all).\n */\nfunction englishMarkers(role: RoleSpec): readonly string[] {\n const primary = role.markerOverride?.en;\n const variants = role.markerVariants?.en ?? [];\n\n const out: string[] = [];\n if (typeof primary === 'string' && primary !== '') out.push(primary);\n for (const v of variants) if (v !== '') out.push(v);\n return out;\n}\n\ninterface MarkerMatch<V> {\n readonly value: V;\n readonly matchedMarker: string;\n}\n\n/**\n * Look up each candidate marker as a key in the modifiers object. Returns the\n * first hit. Modifier values may be nodes (objects with `type`) or strings.\n */\nfunction tryModifierMarkers<T, V>(\n _role: RoleSpec,\n markers: readonly string[],\n modifiers: Readonly<Record<string, unknown>> | undefined,\n adapter: ValueAdapter<T, V>\n): MarkerMatch<V> | undefined {\n if (!modifiers) return undefined;\n for (const m of markers) {\n if (!(m in modifiers)) continue;\n const raw = modifiers[m];\n if (raw === undefined || raw === null) continue;\n if (typeof raw === 'string') {\n return { value: adapter.createLiteralValue(raw), matchedMarker: m };\n }\n if (typeof raw === 'object' && 'type' in (raw as object)) {\n return { value: adapter.convertValue(raw as T), matchedMarker: m };\n }\n }\n return undefined;\n}\n\n/**\n * Scan args for a marker token (or multi-word marker phrase) followed by a\n * non-skip value arg. Mutates `usedArgIndices` to record consumed positions.\n *\n * Longest-match-first ordering ensures e.g. `partials in` matches before a\n * bare `in` would.\n */\nfunction tryArgMarkers<T, V>(\n _role: RoleSpec,\n markers: readonly string[],\n args: readonly T[],\n skipTokens: ReadonlySet<string>,\n usedArgIndices: Set<number>,\n adapter: ValueAdapter<T, V>\n): MarkerMatch<V> | undefined {\n const sorted = [...markers].sort((a, b) => b.split(/\\s+/).length - a.split(/\\s+/).length);\n\n for (const marker of sorted) {\n const words = marker.split(/\\s+/).filter(w => w.length > 0);\n if (words.length === 0) continue;\n\n for (let i = 0; i + words.length <= args.length; i++) {\n // Check that `args[i..i+words.length)` are unused identifiers matching `words`.\n let allMatch = true;\n for (let w = 0; w < words.length; w++) {\n if (usedArgIndices.has(i + w)) {\n allMatch = false;\n break;\n }\n const name = adapter.getIdentifierName(args[i + w]);\n if (name !== words[w]) {\n allMatch = false;\n break;\n }\n }\n if (!allMatch) continue;\n\n // Find the next non-used, non-skip arg as the role value.\n for (let v = i + words.length; v < args.length; v++) {\n if (usedArgIndices.has(v)) continue;\n const name = adapter.getIdentifierName(args[v]);\n if (name !== undefined && skipTokens.has(name)) continue;\n\n for (let w = 0; w < words.length; w++) usedArgIndices.add(i + w);\n usedArgIndices.add(v);\n return { value: adapter.convertValue(args[v]), matchedMarker: marker };\n }\n }\n }\n\n return undefined;\n}\n","/**\n * Reference Validation\n *\n * Configurable validation for reference values (me, you, it, result, etc.).\n * Domain DSLs can provide custom reference sets.\n */\n\n/** Default set of valid references for hyperscript-style DSLs. */\nexport const DEFAULT_REFERENCES: ReadonlySet<string> = new Set([\n 'me',\n 'you',\n 'it',\n 'result',\n 'event',\n 'target',\n 'body',\n // DOM context globals — as reference-typed as `body`, and already advertised as\n // references by core's REFERENCE_KEYWORDS and the parser's PROPERTY_ACCESS_BASES.\n // Without them here, a foreign surface (ja ドキュメント, es documento) lexes as a\n // keyword but fails isValidReference, degrades to a literal, and leaks verbatim.\n 'document',\n 'window',\n 'detail',\n]);\n\n/**\n * Check if a value is a valid reference name.\n *\n * @param value - The string to check\n * @param referenceSet - Custom reference set (defaults to DEFAULT_REFERENCES)\n */\nexport function isValidReference(\n value: string,\n referenceSet: ReadonlySet<string> = DEFAULT_REFERENCES\n): boolean {\n return referenceSet.has(value);\n}\n","/**\n * Explicit Syntax Parser\n *\n * Parses the universal [command role:value ...] bracket syntax.\n * Domain-agnostic — schema validation is injectable via ParseExplicitOptions.\n *\n * Three parsing levels matching the ABNF grammar:\n * parseDocument() → parseCompound() → parseExplicit()\n * (multi-line) (chain ops) (single bracket)\n */\n\nimport type {\n SemanticNode,\n SemanticValue,\n SemanticRole,\n ActionType,\n LoopVariant,\n Annotation,\n LSEEnvelope,\n CompoundSemanticNode,\n} from '../types';\nimport {\n createCommandNode,\n createEventHandlerNode,\n createConditionalNode,\n createLoopNode,\n createCompoundNode,\n createSelector,\n createLiteral,\n createReference,\n createFlag,\n} from '../types';\nimport type { Diagnostic } from '../diagnostics';\nimport type { ParseExplicitOptions } from './types';\nimport { isValidReference, DEFAULT_REFERENCES } from './references';\n\n/**\n * Structural role names whose bracket-enclosed values are nested commands.\n *\n * In these roles a value starting with `[` is ALWAYS a nested command; write an\n * attribute selector as a selector literal (`<[data-active]/>`). Prior to v2.0 the\n * parser guessed from the inner content, which silently dropped zero-argument\n * commands — `[halt]` is both a valid command and a valid attribute selector.\n */\nexport const STRUCTURAL_ROLES = new Set([\n 'body',\n 'then',\n 'else',\n 'condition',\n 'loop-body',\n 'variable',\n 'catch',\n 'finally',\n]);\n\n// =============================================================================\n// Explicit Syntax Parser\n// =============================================================================\n\nexport function parseExplicit(input: string, options: ParseExplicitOptions = {}): SemanticNode {\n const trimmed = input.trim();\n const collect = options.collectDiagnostics === true;\n const diagnostics: Diagnostic[] = [];\n\n if (!trimmed.startsWith('[') || !trimmed.endsWith(']')) {\n throw new Error('Explicit syntax must be wrapped in brackets: [command role:value ...]');\n }\n\n const content = trimmed.slice(1, -1).trim();\n if (!content) {\n throw new Error('Empty explicit statement');\n }\n\n const tokens = tokenizeExplicit(content);\n if (tokens.length === 0) {\n throw new Error('No command specified in explicit statement');\n }\n\n const command = tokens[0].toLowerCase() as ActionType;\n const roles = new Map<SemanticRole, SemanticValue>();\n const refSet = options.referenceSet ?? DEFAULT_REFERENCES;\n\n const schema = options.schemaLookup?.getSchema(command);\n // Build a Set for O(1) membership checks across all role tokens in this parse call.\n // For single lookups outside a parse loop, use getRoleSpec() from schema.ts instead.\n const validRoleNames = schema ? new Set(schema.roles.map(r => r.role)) : null;\n\n for (let i = 1; i < tokens.length; i++) {\n const token = tokens[i];\n\n if (token.startsWith('+') || token.startsWith('~')) {\n const enabled = token.startsWith('+');\n const flagName = token.slice(1);\n if (!flagName) {\n if (collect) {\n diagnostics.push({\n severity: 'error',\n code: 'EMPTY_FLAG',\n message: `Empty flag name: \"${token}\"`,\n source: 'schema',\n });\n continue;\n }\n throw new Error(`Empty flag name: \"${token}\"`);\n }\n if (validRoleNames && !validRoleNames.has(flagName as SemanticRole)) {\n if (collect) {\n diagnostics.push({\n severity: 'error',\n code: 'UNKNOWN_FLAG',\n message: `Unknown flag \"${flagName}\" for command \"${command}\"`,\n source: 'schema',\n suggestions: [...validRoleNames].map(r => `+${r}`),\n });\n roles.set(flagName as SemanticRole, createFlag(flagName, enabled));\n continue;\n }\n throw new Error(\n `Unknown flag \"${flagName}\" for command \"${command}\". Valid roles: ${[...validRoleNames].join(', ')}`\n );\n }\n roles.set(flagName as SemanticRole, createFlag(flagName, enabled));\n continue;\n }\n\n const colonIndex = token.indexOf(':');\n\n if (colonIndex === -1) {\n if (collect) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_ROLE_FORMAT',\n message: `Invalid role format: \"${token}\". Expected role:value or +flag`,\n source: 'schema',\n });\n continue;\n }\n throw new Error(`Invalid role format: \"${token}\". Expected role:value or +flag`);\n }\n\n const roleName = token.slice(0, colonIndex);\n\n if (\n validRoleNames &&\n !STRUCTURAL_ROLES.has(roleName) &&\n !validRoleNames.has(roleName as SemanticRole)\n ) {\n if (collect) {\n diagnostics.push({\n severity: 'error',\n code: 'UNKNOWN_ROLE',\n message: `Unknown role \"${roleName}\" for command \"${command}\"`,\n source: 'schema',\n suggestions: [...validRoleNames],\n });\n } else {\n throw new Error(\n `Unknown role \"${roleName}\" for command \"${command}\". Valid roles: ${[...validRoleNames].join(', ')}`\n );\n }\n }\n\n const role = roleName as SemanticRole;\n const valueStr = token.slice(colonIndex + 1);\n\n // In a structural role, `[...]` is always a nested command.\n if (STRUCTURAL_ROLES.has(roleName) && valueStr.startsWith('[')) {\n const nestedEnd = findMatchingBracket(token, colonIndex + 1);\n const nestedSyntax = token.slice(colonIndex + 1, nestedEnd + 1);\n roles.set(role, { type: 'expression', raw: nestedSyntax });\n continue;\n }\n\n const value = parseExplicitValue(valueStr, refSet);\n roles.set(role, value);\n }\n\n if (schema && command !== 'on') {\n for (const roleSpec of schema.roles) {\n if (roleSpec.required && !roles.has(roleSpec.role as SemanticRole) && !roleSpec.default) {\n if (collect) {\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_REQUIRED_ROLE',\n message: `Missing required role \"${roleSpec.role}\" for command \"${command}\": ${roleSpec.description}`,\n source: 'schema',\n });\n } else {\n throw new Error(\n `Missing required role \"${roleSpec.role}\" for command \"${command}\": ${roleSpec.description}`\n );\n }\n }\n }\n }\n\n const attachDiagnostics = (node: SemanticNode): SemanticNode => {\n if (diagnostics.length === 0) return node;\n return { ...node, diagnostics };\n };\n\n switch (command) {\n case 'on': {\n const eventValue = roles.get('event');\n if (!eventValue) {\n if (collect) {\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_EVENT_ROLE',\n message: 'Event handler requires event role: [on event:click ...]',\n source: 'schema',\n });\n return attachDiagnostics(\n createEventHandlerNode('on', roles, [], { sourceLanguage: 'explicit' })\n );\n }\n throw new Error('Event handler requires event role: [on event:click ...]');\n }\n const body = extractStructuralBody(roles, 'body', options);\n roles.delete('body' as SemanticRole);\n return attachDiagnostics(\n createEventHandlerNode('on', roles, body, { sourceLanguage: 'explicit' })\n );\n }\n\n case 'if': {\n const thenBranch = extractStructuralBody(roles, 'then', options);\n const elseBranch = extractStructuralBody(roles, 'else', options);\n roles.delete('then' as SemanticRole);\n roles.delete('else' as SemanticRole);\n return attachDiagnostics(\n createConditionalNode(\n command,\n roles,\n thenBranch,\n elseBranch.length > 0 ? elseBranch : undefined,\n { sourceLanguage: 'explicit' }\n )\n );\n }\n\n case 'repeat': {\n const loopBody = extractStructuralBody(roles, 'loop-body', options);\n const loopVariantValue = roles.get('loop-variant' as SemanticRole);\n const variableValue = roles.get('variable' as SemanticRole);\n const indexVariableValue = roles.get('index-variable' as SemanticRole);\n\n roles.delete('loop-body' as SemanticRole);\n roles.delete('loop-variant' as SemanticRole);\n roles.delete('variable' as SemanticRole);\n roles.delete('index-variable' as SemanticRole);\n\n let variant: LoopVariant;\n if (\n loopVariantValue &&\n loopVariantValue.type === 'literal' &&\n typeof loopVariantValue.value === 'string'\n ) {\n variant = loopVariantValue.value as LoopVariant;\n } else if (roles.has('quantity' as SemanticRole)) {\n variant = 'times';\n } else if (roles.has('source' as SemanticRole)) {\n variant = 'for';\n } else if (roles.has('condition' as SemanticRole)) {\n variant = 'while';\n } else {\n variant = 'forever';\n }\n\n const loopVariable =\n variableValue?.type === 'literal' && typeof variableValue.value === 'string'\n ? variableValue.value\n : variableValue?.type === 'expression'\n ? variableValue.raw\n : undefined;\n const indexVariable =\n indexVariableValue?.type === 'literal' && typeof indexVariableValue.value === 'string'\n ? indexVariableValue.value\n : undefined;\n\n return attachDiagnostics(\n createLoopNode(command, roles, variant, loopBody, loopVariable, indexVariable, {\n sourceLanguage: 'explicit',\n })\n );\n }\n\n default:\n return attachDiagnostics(createCommandNode(command, roles, { sourceLanguage: 'explicit' }));\n }\n}\n\nexport function isExplicitSyntax(input: string): boolean {\n const trimmed = input.trim();\n return trimmed.startsWith('[') && trimmed.endsWith(']');\n}\n\n// =============================================================================\n// Compound Statement Parsing\n// =============================================================================\n\nconst CHAIN_KEYWORDS = new Set(['then', 'and', 'async', 'sequential']);\n\nfunction splitCompoundSegments(input: string): {\n segments: string[];\n chainType: CompoundSemanticNode['chainType'] | null;\n} {\n const segments: string[] = [];\n let chainType: CompoundSemanticNode['chainType'] | null = null;\n let current = '';\n let bracketDepth = 0;\n let inString = false;\n let stringChar = '';\n\n for (let i = 0; i < input.length; i++) {\n const char = input[i];\n\n if (inString) {\n current += char;\n if (char === stringChar && input[i - 1] !== '\\\\') inString = false;\n continue;\n }\n\n if (char === '\"' || char === \"'\") {\n inString = true;\n stringChar = char;\n current += char;\n continue;\n }\n\n if (char === '[') {\n bracketDepth++;\n current += char;\n continue;\n }\n if (char === ']') {\n bracketDepth--;\n current += char;\n if (bracketDepth === 0) {\n const rest = input.slice(i + 1);\n const pipeMatch = rest.match(/^\\s*\\|>\\s*/);\n if (pipeMatch) {\n segments.push(current.trim());\n current = '';\n chainType ??= 'pipe';\n i += pipeMatch[0].length;\n continue;\n }\n const keywordMatch = rest.match(/^\\s+(then|and|async|sequential)\\s+/);\n if (keywordMatch && CHAIN_KEYWORDS.has(keywordMatch[1])) {\n segments.push(current.trim());\n current = '';\n chainType ??= keywordMatch[1] as CompoundSemanticNode['chainType'];\n i += keywordMatch[0].length;\n continue;\n }\n }\n continue;\n }\n\n current += char;\n }\n\n if (current.trim()) segments.push(current.trim());\n return { segments, chainType };\n}\n\nexport function parseCompound(input: string, options?: ParseExplicitOptions): SemanticNode {\n const trimmed = input.trim();\n const { annotations, remainder } = extractAnnotations(trimmed);\n const { segments, chainType } = splitCompoundSegments(remainder);\n\n let node: SemanticNode;\n if (segments.length <= 1) {\n node = parseExplicit(segments[0] || remainder, options);\n } else {\n const statements = segments.map(seg => parseExplicit(seg, options));\n node = createCompoundNode(statements, chainType ?? 'sequential');\n }\n\n if (annotations.length > 0) return { ...node, annotations };\n return node;\n}\n\n// =============================================================================\n// Annotation Parsing\n// =============================================================================\n\nfunction extractAnnotations(input: string): { annotations: Annotation[]; remainder: string } {\n const annotations: Annotation[] = [];\n let pos = 0;\n\n while (pos < input.length) {\n while (pos < input.length && (input[pos] === ' ' || input[pos] === '\\t')) pos++;\n if (pos >= input.length || input[pos] !== '@') break;\n\n pos++;\n const nameStart = pos;\n while (pos < input.length && /[A-Za-z0-9\\-_]/.test(input[pos])) pos++;\n const name = input.slice(nameStart, pos);\n if (!name) break;\n\n let value: string | undefined;\n if (pos < input.length && input[pos] === '(') {\n pos++;\n const valueStart = pos;\n if (pos < input.length && (input[pos] === '\"' || input[pos] === \"'\")) {\n const quote = input[pos];\n pos++;\n while (pos < input.length && input[pos] !== quote) {\n if (input[pos] === '\\\\') pos++;\n pos++;\n }\n pos++;\n value = input.slice(valueStart + 1, pos - 1);\n } else {\n while (pos < input.length && input[pos] !== ')') pos++;\n value = input.slice(valueStart, pos);\n }\n if (pos < input.length && input[pos] === ')') pos++;\n }\n\n annotations.push(value !== undefined ? { name, value } : { name });\n }\n\n return { annotations, remainder: input.slice(pos).trim() };\n}\n\n// =============================================================================\n// Document Parsing\n// =============================================================================\n\nconst VERSION_HEADER_RE = /^#!lse\\s+(\\d+\\.\\d+(?:\\.\\d+)?)\\s*$/;\n\nexport function parseDocument(input: string, options?: ParseExplicitOptions): LSEEnvelope {\n const lines = input.split('\\n');\n let lseVersion = '1.0';\n const nodes: SemanticNode[] = [];\n let startLine = 0;\n\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i].trim();\n if (!trimmed) continue;\n const versionMatch = trimmed.match(VERSION_HEADER_RE);\n if (versionMatch) {\n lseVersion = versionMatch[1];\n startLine = i + 1;\n }\n break;\n }\n\n for (let i = startLine; i < lines.length; i++) {\n const trimmed = lines[i].trim();\n if (!trimmed) continue;\n if (trimmed.startsWith('//') || (trimmed.startsWith('#') && !trimmed.startsWith('#!')))\n continue;\n nodes.push(parseCompound(trimmed, options));\n }\n\n return { lseVersion, nodes };\n}\n\n// =============================================================================\n// Detection Helpers\n// =============================================================================\n\nexport function isCompoundSyntax(input: string): boolean {\n const { segments } = splitCompoundSegments(input.trim());\n return segments.length > 1;\n}\n\nexport function isDocumentSyntax(input: string): boolean {\n const trimmed = input.trim();\n if (trimmed.includes('\\n')) return true;\n if (VERSION_HEADER_RE.test(trimmed)) return true;\n if (trimmed.startsWith('//') || (trimmed.startsWith('#') && !trimmed.startsWith('#!')))\n return true;\n return false;\n}\n\n// =============================================================================\n// Internal Helpers\n// =============================================================================\n\nfunction extractStructuralBody(\n roles: Map<SemanticRole, SemanticValue>,\n roleName: string,\n options: ParseExplicitOptions\n): SemanticNode[] {\n const value = roles.get(roleName as SemanticRole);\n if (!value) return [];\n if (value.type === 'expression') return [parseExplicit(value.raw, options)];\n return [];\n}\n\n/** Whether a combinator appears outside any quoted span. */\nfunction hasUnquotedCombinator(value: string): boolean {\n let quote = '';\n for (const ch of value) {\n if (quote) {\n if (ch === quote) quote = '';\n continue;\n }\n if (ch === '\"' || ch === \"'\") {\n quote = ch;\n continue;\n }\n if (ch === ' ' || ch === '>' || ch === '+' || ch === '~' || ch === ',') return true;\n }\n return false;\n}\n\n/**\n * Infers a selector's kind. Combinators are tested first, so `.a > .b` is\n * `complex` rather than `class`. A combinator inside a quoted span does not\n * count, so `[aria-label=\"Close menu\"]` is `attribute`.\n */\nfunction inferSelectorKind(\n value: string\n): 'id' | 'class' | 'attribute' | 'element' | 'complex' | undefined {\n if (!value) return undefined;\n if (hasUnquotedCombinator(value)) return 'complex';\n if (value.startsWith('#')) return 'id';\n if (value.startsWith('.')) return 'class';\n if (value.startsWith('[')) return 'attribute';\n if (value.startsWith('*')) return 'element';\n return undefined;\n}\n\n/** Expands the escape sequences defined by the grammar: \\\\ \\\" \\' \\n \\r \\t */\nfunction unescapeString(s: string): string {\n if (!s.includes('\\\\')) return s;\n\n let out = '';\n for (let i = 0; i < s.length; i++) {\n if (s[i] === '\\\\' && i + 1 < s.length) {\n const next = s[i + 1];\n const mapped =\n next === '\\\\'\n ? '\\\\'\n : next === '\"'\n ? '\"'\n : next === \"'\"\n ? \"'\"\n : next === 'n'\n ? '\\n'\n : next === 'r'\n ? '\\r'\n : next === 't'\n ? '\\t'\n : '';\n if (mapped) {\n out += mapped;\n i++;\n continue;\n }\n }\n out += s[i];\n }\n return out;\n}\n\n/**\n * Splits on spaces at bracket-depth 0, outside quoted strings, and outside\n * selector literals (`<...selector.../>`). The branch order is normative: the\n * string check precedes the selector check, so a `/>` inside a quoted string does\n * not close the literal. A `<` opens a selector literal only at the start of a\n * value — when the token buffer is empty or ends with `:`.\n */\nfunction tokenizeExplicit(content: string): string[] {\n const tokens: string[] = [];\n let current = '';\n let inString = false;\n let stringChar = '';\n let inSelector = false;\n let bracketDepth = 0;\n\n for (let i = 0; i < content.length; i++) {\n const char = content[i];\n if (inString) {\n current += char;\n if (char === stringChar) {\n // Even backslash count means the quote closes the string.\n let backslashes = 0;\n for (let j = i - 1; j >= 0 && content[j] === '\\\\'; j--) backslashes++;\n if (backslashes % 2 === 0) inString = false;\n }\n continue;\n }\n if (char === '\"' || char === \"'\") {\n inString = true;\n stringChar = char;\n current += char;\n continue;\n }\n if (inSelector) {\n current += char;\n if (char === '>' && current.endsWith('/>')) inSelector = false;\n continue;\n }\n if (char === '<' && (current === '' || current.endsWith(':'))) {\n inSelector = true;\n current += char;\n continue;\n }\n if (char === '[') {\n bracketDepth++;\n current += char;\n continue;\n }\n if (char === ']') {\n bracketDepth--;\n current += char;\n continue;\n }\n if (char === ' ' && bracketDepth === 0) {\n if (current) {\n tokens.push(current);\n current = '';\n }\n continue;\n }\n current += char;\n }\n\n if (current) tokens.push(current);\n return tokens;\n}\n\nfunction parseExplicitValue(valueStr: string, referenceSet: ReadonlySet<string>): SemanticValue {\n // Selector literal — the delimiters force selector interpretation.\n if (valueStr.startsWith('<')) {\n if (!valueStr.endsWith('/>')) {\n throw new Error(`Unterminated selector literal: \"${valueStr}\". Expected a closing \"/>\"`);\n }\n const inner = valueStr.slice(1, -2);\n if (!inner) throw new Error(`Empty selector literal: \"${valueStr}\"`);\n return createSelector(inner, inferSelectorKind(inner));\n }\n if (\n valueStr.startsWith('#') ||\n valueStr.startsWith('.') ||\n valueStr.startsWith('[') ||\n valueStr.startsWith('@') ||\n valueStr.startsWith('*')\n ) {\n return createSelector(valueStr, inferSelectorKind(valueStr));\n }\n if (valueStr.startsWith('\"') || valueStr.startsWith(\"'\")) {\n return createLiteral(unescapeString(valueStr.slice(1, -1)), 'string');\n }\n if (valueStr === 'true') return createLiteral(true, 'boolean');\n if (valueStr === 'false') return createLiteral(false, 'boolean');\n const lowerRef = valueStr.toLowerCase();\n if (isValidReference(lowerRef, referenceSet)) return createReference(lowerRef);\n const numMatch = valueStr.match(/^(-?\\d+(?:\\.\\d+)?)(ms|s|m|h)?$/);\n if (numMatch) {\n const num = parseFloat(numMatch[1]);\n const suffix = numMatch[2];\n if (suffix) return createLiteral(valueStr, 'duration');\n return createLiteral(num, 'number');\n }\n return createLiteral(valueStr);\n}\n\nfunction findMatchingBracket(str: string, start: number): number {\n let depth = 0;\n for (let i = start; i < str.length; i++) {\n if (str[i] === '[') depth++;\n else if (str[i] === ']') {\n depth--;\n if (depth === 0) return i;\n }\n }\n return str.length - 1;\n}\n","/**\n * Explicit Syntax Renderer\n *\n * Serializes SemanticNode to the universal [command role:value ...] bracket syntax.\n * Zero dependencies beyond core types — no language-specific logic.\n */\n\nimport type {\n SemanticNode,\n SemanticValue,\n CommandSemanticNode,\n CompoundSemanticNode,\n EventHandlerSemanticNode,\n ConditionalSemanticNode,\n LoopSemanticNode,\n LSEEnvelope,\n} from '../types';\n\n/**\n * Render a semantic node as explicit bracket syntax.\n *\n * @example\n * ```typescript\n * renderExplicit(node) // \"[toggle patient:.active destination:#button]\"\n * renderExplicit(annotatedNode) // \"@timeout(5s) [fetch source:\\\"/api/users\\\"]\"\n * ```\n */\nexport function renderExplicit(node: SemanticNode): string {\n let result: string;\n\n if (node.kind === 'compound') {\n const compoundNode = node as CompoundSemanticNode;\n result = compoundNode.statements\n .map(stmt => renderExplicit(stmt))\n .join(` ${compoundNode.chainType} `);\n } else {\n result = renderBracketCommand(node);\n }\n\n if (node.annotations && node.annotations.length > 0) {\n const annParts = node.annotations.map(ann =>\n ann.value !== undefined ? `@${ann.name}(${ann.value})` : `@${ann.name}`\n );\n return annParts.join(' ') + ' ' + result;\n }\n\n return result;\n}\n\n/**\n * Render an LSEEnvelope as a multi-line document string.\n */\nexport function renderDocument(envelope: LSEEnvelope): string {\n const lines: string[] = [];\n if (envelope.lseVersion && envelope.lseVersion !== '1.0') {\n lines.push(`#!lse ${envelope.lseVersion}`);\n }\n for (const node of envelope.nodes) {\n lines.push(renderExplicit(node));\n }\n return lines.join('\\n');\n}\n\n// =============================================================================\n// Internal Helpers\n// =============================================================================\n\nfunction renderBracketCommand(node: SemanticNode): string {\n const parts: string[] = [node.action];\n\n for (const [role, value] of node.roles) {\n if (value.type === 'flag') {\n parts.push(value.enabled ? `+${value.name}` : `~${value.name}`);\n } else {\n parts.push(`${role}:${valueToString(value)}`);\n }\n }\n\n if (node.kind === 'event-handler') {\n const eventNode = node as EventHandlerSemanticNode;\n if (eventNode.body && eventNode.body.length > 0) {\n parts.push(`body:${eventNode.body.map(n => renderExplicit(n)).join(' ')}`);\n }\n }\n\n if (node.kind === 'conditional') {\n const condNode = node as ConditionalSemanticNode;\n if (condNode.thenBranch && condNode.thenBranch.length > 0) {\n parts.push(`then:${condNode.thenBranch.map(n => renderExplicit(n)).join(' ')}`);\n }\n if (condNode.elseBranch && condNode.elseBranch.length > 0) {\n parts.push(`else:${condNode.elseBranch.map(n => renderExplicit(n)).join(' ')}`);\n }\n }\n\n if (node.kind === 'loop') {\n const loopNode = node as LoopSemanticNode;\n if (loopNode.loopVariant) parts.push(`loop-variant:${loopNode.loopVariant}`);\n if (loopNode.loopVariable) parts.push(`variable:${loopNode.loopVariable}`);\n if (loopNode.indexVariable) parts.push(`index-variable:${loopNode.indexVariable}`);\n if (loopNode.body && loopNode.body.length > 0) {\n parts.push(`loop-body:${loopNode.body.map(n => renderExplicit(n)).join(' ')}`);\n }\n }\n\n if (node.kind === 'command') {\n const cmd = node as CommandSemanticNode;\n if (cmd.body && cmd.body.length > 0)\n parts.push(`body:${cmd.body.map(n => renderExplicit(n)).join(' ')}`);\n if (cmd.catchBranch && cmd.catchBranch.length > 0)\n parts.push(`catch:${cmd.catchBranch.map(n => renderExplicit(n)).join(' ')}`);\n if (cmd.finallyBranch && cmd.finallyBranch.length > 0)\n parts.push(`finally:${cmd.finallyBranch.map(n => renderExplicit(n)).join(' ')}`);\n if (cmd.asyncVariant) parts.push(`async-variant:${cmd.asyncVariant}`);\n if (cmd.asyncBody && cmd.asyncBody.length > 0)\n parts.push(`async-body:${cmd.asyncBody.map(n => renderExplicit(n)).join(' ')}`);\n }\n\n return `[${parts.join(' ')}]`;\n}\n\n/**\n * Whether a selector must be wrapped in `<.../>` to survive a re-parse.\n *\n * A bare token re-classifies as a selector only if it starts with one of\n * `# . @ *`. A space would split the token, and a leading `[` in a structural\n * role would re-parse as a nested command — so both must wrap.\n */\nfunction needsSelectorWrap(value: string): boolean {\n if (value.includes(' ')) return true;\n const first = value[0];\n return !(first === '#' || first === '.' || first === '@' || first === '*');\n}\n\nfunction valueToString(value: SemanticValue): string {\n switch (value.type) {\n case 'literal':\n if (typeof value.value === 'string') {\n // JSON.stringify escapes backslashes and quotes; a raw `\"${v}\"` would emit\n // an unparseable literal for a value containing a quote.\n if (value.dataType === 'string' || /\\s/.test(value.value))\n return JSON.stringify(value.value);\n return value.value;\n }\n return String(value.value);\n case 'selector':\n if (!value.value) return value.value;\n return needsSelectorWrap(value.value) ? `<${value.value}/>` : value.value;\n case 'reference':\n return value.value;\n case 'property-path':\n return `${valueToString(value.object)}'s ${value.property}`;\n case 'expression':\n return value.raw;\n case 'flag':\n return value.name;\n }\n}\n","/**\n * Protocol JSON Serialization\n *\n * Converts between TypeScript SemanticNode (internal representation) and\n * the protocol wire format defined in protocol/spec/wire-format.md.\n *\n * Use toProtocolJSON() to serialize for tool-to-tool interchange.\n * Use fromProtocolJSON() to deserialize from protocol-conformant sources.\n */\n\nimport type {\n SemanticNode,\n SemanticValue,\n SemanticRole,\n CommandSemanticNode,\n EventHandlerSemanticNode,\n CompoundSemanticNode,\n ConditionalSemanticNode,\n LoopSemanticNode,\n LoopVariant,\n AsyncVariant,\n LSEEnvelope,\n} from '../types';\nimport {\n createCommandNode,\n createEventHandlerNode,\n createCompoundNode,\n createConditionalNode,\n createLoopNode,\n createTryNode,\n createAsyncNode,\n createMatchNode,\n createSelector,\n createLiteral,\n createReference,\n createExpression,\n createFlag,\n extractValue,\n} from '../types';\nimport type {\n ProtocolNodeJSON,\n ProtocolValueJSON,\n ProtocolDiagnosticJSON,\n ProtocolChainType,\n IRDiagnostic,\n LSEEnvelopeJSON,\n} from './types';\n\n// =============================================================================\n// Serialization: SemanticNode → ProtocolNodeJSON\n// =============================================================================\n\nexport function toProtocolJSON(node: SemanticNode): ProtocolNodeJSON {\n const roles = rolesToProtocol(node.roles);\n let result: ProtocolNodeJSON;\n\n switch (node.kind) {\n case 'command': {\n const cmd = node as CommandSemanticNode;\n result = { kind: 'command', action: node.action, roles };\n if (cmd.body && cmd.body.length > 0) result.body = cmd.body.map(toProtocolJSON);\n if (cmd.catchBranch && cmd.catchBranch.length > 0)\n result.catchBranch = cmd.catchBranch.map(toProtocolJSON);\n if (cmd.finallyBranch && cmd.finallyBranch.length > 0)\n result.finallyBranch = cmd.finallyBranch.map(toProtocolJSON);\n if (cmd.asyncVariant) result.asyncVariant = cmd.asyncVariant;\n if (cmd.asyncBody && cmd.asyncBody.length > 0)\n result.asyncBody = cmd.asyncBody.map(toProtocolJSON);\n if (cmd.arms && cmd.arms.length > 0) {\n result.arms = cmd.arms.map(arm => ({\n pattern: valueToProtocol(arm.pattern),\n body: arm.body.map(toProtocolJSON),\n }));\n }\n if (cmd.defaultArm && cmd.defaultArm.length > 0)\n result.defaultArm = cmd.defaultArm.map(toProtocolJSON);\n break;\n }\n case 'event-handler': {\n const eh = node as EventHandlerSemanticNode;\n\n // Emit compact form with `trigger` sugar when it's lossless. The compact\n // form is `{action: bodyAction, roles: bodyRoles, trigger: {event, modifiers?}}`\n // and deserializes back to an identical event-handler node via the\n // `if (json.trigger)` branch in fromProtocolJSON. The compact form is\n // also the only form the runtime dispatch path currently executes\n // correctly for most commands — see examples/llm-native-todo-demo.\n if (canEmitCompactTrigger(eh)) {\n const body = eh.body[0] as CommandSemanticNode;\n const eventValue = eh.roles.get('event' as SemanticRole);\n // Safe because canEmitCompactTrigger verified type + value.\n const eventName = (eventValue as { value: string }).value;\n\n const trigger: { event: string; modifiers?: Record<string, unknown> } = {\n event: eventName,\n };\n const triggerModifiers = serializeEventModifiers(eh.eventModifiers);\n if (triggerModifiers) trigger.modifiers = triggerModifiers;\n\n result = {\n action: body.action,\n roles: rolesToProtocol(body.roles),\n trigger,\n };\n break;\n }\n\n // Fallback: verbose form (multi-command body, nested control flow,\n // additional events, parameter names, annotations on body, etc.)\n result = {\n kind: 'event-handler',\n action: node.action,\n roles,\n body: eh.body.map(toProtocolJSON),\n };\n break;\n }\n case 'compound': {\n const c = node as CompoundSemanticNode;\n result = {\n kind: 'compound',\n action: 'compound',\n statements: c.statements.map(toProtocolJSON),\n chainType: c.chainType,\n } as ProtocolNodeJSON;\n break;\n }\n case 'conditional': {\n const cond = node as ConditionalSemanticNode;\n result = { kind: 'command', action: node.action, roles };\n result.thenBranch = cond.thenBranch.map(toProtocolJSON);\n if (cond.elseBranch && cond.elseBranch.length > 0)\n result.elseBranch = cond.elseBranch.map(toProtocolJSON);\n break;\n }\n case 'loop': {\n const loop = node as LoopSemanticNode;\n result = { kind: 'command', action: node.action, roles };\n result.loopVariant = loop.loopVariant;\n result.loopBody = loop.body.map(toProtocolJSON);\n if (loop.loopVariable) result.loopVariable = loop.loopVariable;\n if (loop.indexVariable) result.indexVariable = loop.indexVariable;\n break;\n }\n }\n\n if (node.annotations && node.annotations.length > 0) {\n result.annotations = node.annotations.map(a =>\n a.value !== undefined ? { name: a.name, value: a.value } : { name: a.name }\n );\n }\n\n if (node.diagnostics && node.diagnostics.length > 0) {\n result.diagnostics = node.diagnostics.map(d => {\n const json: ProtocolDiagnosticJSON = { level: d.severity, message: d.message };\n // `role` is part of the wire format's Diagnostic shape; dropping it here\n // silently lost which role a type-constraint diagnostic was about.\n if (d.role) json.role = d.role;\n if (d.code) json.code = d.code;\n if (d.source) json.source = d.source;\n if (d.suggestions && d.suggestions.length > 0) json.suggestions = [...d.suggestions];\n return json;\n });\n }\n\n return result;\n}\n\nfunction rolesToProtocol(\n roles: ReadonlyMap<SemanticRole, SemanticValue>\n): Record<string, ProtocolValueJSON> {\n const result: Record<string, ProtocolValueJSON> = {};\n for (const [role, value] of roles) result[role] = valueToProtocol(value);\n return result;\n}\n\nfunction valueToProtocol(value: SemanticValue): ProtocolValueJSON {\n switch (value.type) {\n case 'selector': {\n const PROTOCOL_SELECTOR_KINDS = new Set([\n 'id',\n 'class',\n 'attribute',\n 'element',\n 'complex',\n ] as const);\n const sk = 'selectorKind' in value ? value.selectorKind : undefined;\n const sv: ProtocolValueJSON = { type: 'selector', value: value.value };\n if (sk && PROTOCOL_SELECTOR_KINDS.has(sk as 'id'))\n sv.selectorKind = sk as 'id' | 'class' | 'attribute' | 'element' | 'complex';\n return sv;\n }\n case 'literal':\n return value.dataType\n ? { type: 'literal', value: value.value, dataType: value.dataType }\n : { type: 'literal', value: value.value };\n case 'reference':\n return { type: 'reference', value: value.value };\n case 'expression':\n return { type: 'expression', raw: value.raw };\n case 'property-path':\n return { type: 'expression', raw: extractValue(value) };\n case 'flag':\n return { type: 'flag', name: value.name, enabled: value.enabled };\n }\n}\n\n/**\n * Decide whether an event-handler node can be emitted in the compact\n * `{action, roles, trigger}` form without losing information.\n *\n * The compact form is lossless when the event-handler wraps exactly one plain\n * command, uses a simple literal event name, and carries no metadata that\n * compact form can't represent (body-level annotations, additional events,\n * parameter names, etc.). Round-trip: `fromProtocolJSON(compact)` rebuilds an\n * equivalent event-handler via the `if (json.trigger)` branch.\n */\nfunction canEmitCompactTrigger(eh: EventHandlerSemanticNode): boolean {\n // Must wrap exactly one body node and that node must be a plain command.\n if (!eh.body || eh.body.length !== 1) return false;\n const body = eh.body[0];\n if (body.kind !== 'command') return false;\n\n // The body command must not carry its own control flow or v1.2 extensions.\n const cmd = body as CommandSemanticNode;\n if (cmd.body && cmd.body.length > 0) return false;\n if (cmd.catchBranch && cmd.catchBranch.length > 0) return false;\n if (cmd.finallyBranch && cmd.finallyBranch.length > 0) return false;\n if (cmd.asyncVariant) return false;\n if (cmd.asyncBody && cmd.asyncBody.length > 0) return false;\n if (cmd.arms && cmd.arms.length > 0) return false;\n if (cmd.defaultArm && cmd.defaultArm.length > 0) return false;\n\n // Event handler must expose a simple literal string event name.\n const eventValue = eh.roles.get('event' as SemanticRole);\n if (!eventValue || eventValue.type !== 'literal') return false;\n if (typeof (eventValue as { value: unknown }).value !== 'string') return false;\n\n // The body command must NOT have its own `event` role — in compact form\n // `roles.event` is reserved for the trigger and would collide on re-parse.\n if (cmd.roles.has('event' as SemanticRole)) return false;\n\n // Event handler must not carry features compact form cannot represent.\n if (eh.additionalEvents && eh.additionalEvents.length > 0) return false;\n if (eh.parameterNames && eh.parameterNames.length > 0) return false;\n\n // Annotations/diagnostics: compact form collapses event-handler and body\n // command into one node, so only EH-level annotations round-trip. If either\n // the EH or the body command carries annotations/diagnostics, fall back\n // to verbose form to preserve them losslessly.\n if (eh.annotations && eh.annotations.length > 0) return false;\n if (eh.diagnostics && eh.diagnostics.length > 0) return false;\n if (cmd.annotations && cmd.annotations.length > 0) return false;\n if (cmd.diagnostics && cmd.diagnostics.length > 0) return false;\n\n return true;\n}\n\n/**\n * Serialize EventModifiers to the shape expected by `trigger.modifiers` in the\n * wire format. Returns undefined when there are no modifiers, so the caller\n * can omit the field entirely.\n *\n * `from` is a SemanticValue reference and is not round-trip-safe through the\n * trigger sugar path; if present, it signals that canEmitCompactTrigger\n * should have rejected the node. We still emit the simple keys here so\n * partial information survives if the caller bypasses the guard.\n */\nfunction serializeEventModifiers(\n mods: EventHandlerSemanticNode['eventModifiers']\n): Record<string, unknown> | undefined {\n if (!mods) return undefined;\n const out: Record<string, unknown> = {};\n if (mods.once) out.once = true;\n if (typeof mods.debounce === 'number') out.debounce = mods.debounce;\n if (typeof mods.throttle === 'number') out.throttle = mods.throttle;\n if (mods.queue) out.queue = mods.queue;\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\n// =============================================================================\n// Deserialization: ProtocolNodeJSON → SemanticNode\n// =============================================================================\n\nexport function fromProtocolJSON(json: ProtocolNodeJSON): SemanticNode {\n const roles = new Map<SemanticRole, SemanticValue>(\n Object.entries(json.roles ?? {}).map(([role, val]) => [role, valueFromProtocol(val)])\n );\n\n const annotations = json.annotations?.map(a =>\n a.value !== undefined ? { name: a.name, value: a.value } : { name: a.name }\n );\n const diagnostics = json.diagnostics?.map(d => ({\n severity: d.level as 'error' | 'warning' | 'info',\n message: d.message,\n ...(d.role ? { role: d.role } : {}),\n ...(d.code ? { code: d.code } : {}),\n ...(d.source ? { source: d.source } : {}),\n ...(d.suggestions && d.suggestions.length > 0 ? { suggestions: d.suggestions } : {}),\n }));\n\n if (json.trigger) {\n const eventName = json.trigger.event;\n roles.set('event' as SemanticRole, createLiteral(eventName, 'string'));\n const bodyRoles = new Map(roles);\n bodyRoles.delete('event' as SemanticRole);\n const bodyNode = createCommandNode(json.action, bodyRoles);\n const node = createEventHandlerNode(\n 'on',\n roles,\n [bodyNode],\n { sourceLanguage: 'json' },\n (json.trigger.modifiers as Record<string, unknown> | undefined) ?? {}\n );\n return applyV12Metadata(node, annotations, diagnostics);\n }\n\n const kind = json.kind ?? 'command';\n\n if (kind === 'event-handler') {\n const body = (json.body ?? []).map(fromProtocolJSON);\n return applyV12Metadata(\n createEventHandlerNode(json.action, roles, body),\n annotations,\n diagnostics\n );\n }\n\n if (kind === 'compound') {\n const statements = (json.statements ?? []).map(fromProtocolJSON);\n return applyV12Metadata(\n createCompoundNode(statements, (json.chainType ?? 'sequential') as ProtocolChainType),\n annotations,\n diagnostics\n );\n }\n\n if (json.thenBranch && json.thenBranch.length > 0) {\n const thenBranch = json.thenBranch.map(fromProtocolJSON);\n const elseBranch = json.elseBranch ? json.elseBranch.map(fromProtocolJSON) : undefined;\n return applyV12Metadata(\n createConditionalNode(json.action, roles, thenBranch, elseBranch),\n annotations,\n diagnostics\n );\n }\n\n if (json.loopVariant) {\n const loopBody = (json.loopBody ?? []).map(fromProtocolJSON);\n return applyV12Metadata(\n createLoopNode(\n json.action,\n roles,\n json.loopVariant as LoopVariant,\n loopBody,\n json.loopVariable,\n json.indexVariable\n ),\n annotations,\n diagnostics\n );\n }\n\n if (json.catchBranch || json.finallyBranch) {\n const body = (json.body ?? []).map(fromProtocolJSON);\n const catchBranch = json.catchBranch ? json.catchBranch.map(fromProtocolJSON) : undefined;\n const finallyBranch = json.finallyBranch ? json.finallyBranch.map(fromProtocolJSON) : undefined;\n return applyV12Metadata(\n createTryNode(body, catchBranch, finallyBranch),\n annotations,\n diagnostics\n );\n }\n\n if (json.asyncVariant) {\n const asyncBody = (json.asyncBody ?? []).map(fromProtocolJSON);\n return applyV12Metadata(\n createAsyncNode(json.asyncVariant as AsyncVariant, asyncBody),\n annotations,\n diagnostics\n );\n }\n\n if (json.arms && json.arms.length > 0) {\n const arms = json.arms.map(arm => ({\n pattern: valueFromProtocol(arm.pattern),\n body: arm.body.map(fromProtocolJSON),\n }));\n const defaultArm = json.defaultArm ? json.defaultArm.map(fromProtocolJSON) : undefined;\n return applyV12Metadata(createMatchNode(roles, arms, defaultArm), annotations, diagnostics);\n }\n\n if (json.body && json.body.length > 0 && kind === 'command') {\n return applyV12Metadata(\n createTryNode(json.body.map(fromProtocolJSON)),\n annotations,\n diagnostics\n );\n }\n\n return applyV12Metadata(createCommandNode(json.action, roles), annotations, diagnostics);\n}\n\nfunction applyV12Metadata(\n node: SemanticNode,\n annotations: Array<{ name: string; value?: string }> | undefined,\n diagnostics:\n | Array<{\n severity: 'error' | 'warning' | 'info';\n message: string;\n code?: string;\n source?: string;\n suggestions?: string[];\n }>\n | undefined\n): SemanticNode {\n if ((!annotations || annotations.length === 0) && (!diagnostics || diagnostics.length === 0))\n return node;\n return {\n ...node,\n ...(annotations && annotations.length > 0 ? { annotations } : {}),\n ...(diagnostics && diagnostics.length > 0 ? { diagnostics } : {}),\n };\n}\n\nfunction valueFromProtocol(value: ProtocolValueJSON): SemanticValue {\n switch (value.type) {\n case 'selector':\n return createSelector(String(value.value ?? ''), value.selectorKind);\n case 'literal':\n return createLiteral(\n value.value !== undefined ? value.value : '',\n value.dataType as 'string' | 'number' | 'boolean' | 'duration' | undefined\n );\n case 'reference':\n return createReference(String(value.value ?? ''));\n case 'expression':\n return createExpression(value.raw ?? String(value.value ?? ''));\n case 'flag':\n return createFlag(\n value.name ?? String(value.value ?? ''),\n value.enabled ?? (typeof value.value === 'boolean' ? value.value : true)\n );\n case 'property-path':\n return createExpression(String(value.value ?? value.raw ?? ''));\n }\n}\n\n// =============================================================================\n// Envelope (v1.2)\n// =============================================================================\n\nexport function toEnvelopeJSON(envelope: LSEEnvelope): LSEEnvelopeJSON {\n const result: LSEEnvelopeJSON = {\n lseVersion: envelope.lseVersion,\n nodes: envelope.nodes.map(toProtocolJSON),\n };\n if (envelope.features && envelope.features.length > 0) result.features = [...envelope.features];\n return result;\n}\n\nexport function fromEnvelopeJSON(json: LSEEnvelopeJSON): LSEEnvelope {\n return {\n lseVersion: json.lseVersion,\n ...(json.features && json.features.length > 0 ? { features: json.features } : {}),\n nodes: json.nodes.map(fromProtocolJSON),\n };\n}\n\nexport function isEnvelope(json: unknown): json is LSEEnvelopeJSON {\n return (\n typeof json === 'object' &&\n json !== null &&\n 'lseVersion' in json &&\n 'nodes' in json &&\n Array.isArray((json as Record<string, unknown>).nodes)\n );\n}\n\n// =============================================================================\n// Validation\n// =============================================================================\n\nconst VALID_KINDS = new Set(['command', 'event-handler', 'compound']);\nconst VALID_VALUE_TYPES = new Set([\n 'selector',\n 'literal',\n 'reference',\n 'expression',\n 'property-path',\n 'flag',\n]);\nconst VALID_CHAIN_TYPES = new Set(['then', 'and', 'async', 'sequential', 'pipe']);\nconst VALID_ASYNC_VARIANTS = new Set(['all', 'race']);\nconst VALID_DIAGNOSTIC_LEVELS = new Set(['error', 'warning', 'info']);\n\nexport function validateProtocolJSON(json: unknown): IRDiagnostic[] {\n const diagnostics: IRDiagnostic[] = [];\n\n if (typeof json !== 'object' || json === null) {\n return [{ severity: 'error', code: 'INVALID_ROOT', message: 'Root must be a JSON object' }];\n }\n\n const node = json as Record<string, unknown>;\n const kind = 'kind' in node ? String(node.kind) : 'command';\n\n if ('kind' in node && !VALID_KINDS.has(kind)) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_KIND',\n message: `Invalid kind \"${node.kind}\". Must be one of: command, event-handler, compound`,\n });\n }\n\n if (!('action' in node)) {\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_ACTION',\n message: 'Missing required field: action',\n });\n } else if (typeof node.action !== 'string' || node.action.length === 0) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_ACTION',\n message: 'action must be a non-empty string',\n });\n }\n\n if (!('roles' in node) && kind !== 'compound') {\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_ROLES',\n message: 'Missing required field: roles',\n });\n } else if (\n 'roles' in node &&\n (typeof node.roles !== 'object' || node.roles === null || Array.isArray(node.roles))\n ) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_ROLES',\n message: 'roles must be a plain object',\n });\n } else if ('roles' in node && node.roles) {\n for (const [role, value] of Object.entries(node.roles as Record<string, unknown>)) {\n if (typeof value !== 'object' || value === null) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_ROLE_VALUE',\n message: `Role \"${role}\" value must be an object`,\n });\n continue;\n }\n const v = value as Record<string, unknown>;\n if (!('type' in v) || !VALID_VALUE_TYPES.has(String(v.type))) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_VALUE_TYPE',\n message: `Role \"${role}\" has invalid type \"${v.type}\"`,\n });\n }\n if (v.type === 'flag') {\n if (typeof v.name !== 'string' && !('value' in v))\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_FLAG_NAME',\n message: `Flag role \"${role}\" missing required field: name (or value)`,\n });\n if (typeof v.enabled !== 'boolean' && !('value' in v))\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_FLAG_ENABLED',\n message: `Flag role \"${role}\" missing required field: enabled (or value)`,\n });\n }\n if (v.type === 'expression' && typeof v.raw !== 'string' && !('value' in v)) {\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_EXPRESSION_RAW',\n message: `Expression role \"${role}\" missing required field: raw (or value)`,\n });\n }\n }\n }\n\n if ('trigger' in node && node.trigger) {\n const trigger = node.trigger as Record<string, unknown>;\n if (typeof trigger !== 'object' || trigger === null) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_TRIGGER',\n message: 'trigger must be an object',\n });\n } else if (!trigger.event || typeof trigger.event !== 'string') {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_TRIGGER',\n message: 'trigger.event is required and must be a non-empty string',\n });\n }\n }\n\n if (kind === 'event-handler' && !('trigger' in node)) {\n if (!('body' in node) || !Array.isArray(node.body)) {\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_BODY',\n message: 'event-handler node requires a body array',\n });\n }\n }\n\n if (kind === 'compound') {\n if (!('statements' in node) || !Array.isArray(node.statements)) {\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_STATEMENTS',\n message: 'compound node requires a statements array',\n });\n }\n if ('chainType' in node && !VALID_CHAIN_TYPES.has(String(node.chainType))) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_CHAIN_TYPE',\n message: `Invalid chainType \"${node.chainType}\"`,\n });\n }\n }\n\n if ('diagnostics' in node && !Array.isArray(node.diagnostics)) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_DIAGNOSTICS',\n message: 'diagnostics must be an array',\n });\n }\n\n if ('annotations' in node) {\n if (!Array.isArray(node.annotations)) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_ANNOTATIONS',\n message: 'annotations must be an array',\n });\n } else {\n for (let i = 0; i < (node.annotations as unknown[]).length; i++) {\n const a = (node.annotations as unknown[])[i] as Record<string, unknown> | null;\n if (!a || typeof a !== 'object') {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_ANNOTATION_ENTRY',\n message: `annotations[${i}] must be an object`,\n });\n continue;\n }\n if (typeof a.name !== 'string' || a.name.length === 0)\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_ANNOTATION_NAME',\n message: `annotations[${i}] missing required field: name`,\n });\n }\n }\n }\n\n if ('asyncVariant' in node && !VALID_ASYNC_VARIANTS.has(String(node.asyncVariant))) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_ASYNC_VARIANT',\n message: `Invalid asyncVariant \"${node.asyncVariant}\". Must be \"all\" or \"race\"`,\n });\n }\n\n if ('arms' in node) {\n if (!Array.isArray(node.arms)) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_ARMS',\n message: 'arms must be an array',\n });\n } else {\n for (let i = 0; i < (node.arms as unknown[]).length; i++) {\n const arm = (node.arms as unknown[])[i] as Record<string, unknown> | null;\n if (!arm || typeof arm !== 'object') {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_ARM_ENTRY',\n message: `arms[${i}] must be an object`,\n });\n continue;\n }\n if (!arm.pattern || typeof arm.pattern !== 'object')\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_ARM_PATTERN',\n message: `arms[${i}] missing required field: pattern`,\n });\n if (!Array.isArray(arm.body))\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_ARM_BODY',\n message: `arms[${i}] missing required field: body (array)`,\n });\n }\n }\n }\n\n if ('diagnostics' in node && Array.isArray(node.diagnostics)) {\n for (let i = 0; i < (node.diagnostics as unknown[]).length; i++) {\n const d = (node.diagnostics as unknown[])[i] as Record<string, unknown> | null;\n if (!d || typeof d !== 'object') {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_DIAGNOSTIC_ENTRY',\n message: `diagnostics[${i}] must be an object`,\n });\n continue;\n }\n if (!VALID_DIAGNOSTIC_LEVELS.has(String(d.level)))\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_DIAGNOSTIC_LEVEL',\n message: `diagnostics[${i}].level must be \"error\", \"warning\", or \"info\"`,\n });\n if (typeof d.message !== 'string')\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_DIAGNOSTIC_MESSAGE',\n message: `diagnostics[${i}] missing required field: message`,\n });\n }\n }\n\n return diagnostics;\n}\n","/**\n * Client-side schema registry for <lse-intent>.\n *\n * Schemas registered here are used to validate protocol JSON before execution.\n * Register once per page; the registry is a singleton per module.\n */\n\nimport type { CommandSchema } from '@lokascript/intent';\n\nclass IntentSchemaRegistry {\n private schemas = new Map<string, CommandSchema>();\n\n /** Register a command schema. Overwrites any existing schema for the same action. */\n register(schema: CommandSchema): void {\n this.schemas.set(schema.action, schema);\n }\n\n /** Register multiple schemas at once. */\n registerAll(schemas: CommandSchema[]): void {\n for (const schema of schemas) this.register(schema);\n }\n\n /** Look up a schema by action name. Returns undefined if not registered. */\n get(action: string): CommandSchema | undefined {\n return this.schemas.get(action);\n }\n\n /** Check whether a schema is registered for the given action. */\n has(action: string): boolean {\n return this.schemas.has(action);\n }\n\n /** Remove a schema. */\n unregister(action: string): void {\n this.schemas.delete(action);\n }\n\n /** Clear all registered schemas. */\n clear(): void {\n this.schemas.clear();\n }\n\n /** Number of registered schemas. */\n get size(): number {\n return this.schemas.size;\n }\n\n /**\n * Fetch and register schemas from a JSON endpoint.\n * The endpoint must return an array of CommandSchema objects.\n * Throws if the response is not OK, not an array, or contains malformed schemas.\n */\n async loadFrom(url: string): Promise<void> {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(\n `Failed to load schemas from ${url}: ${response.status} ${response.statusText}`\n );\n }\n const body: unknown = await response.json();\n if (!Array.isArray(body)) {\n throw new Error(`Expected array of schemas from ${url}, got ${typeof body}`);\n }\n for (let i = 0; i < body.length; i++) {\n const s = body[i] as Record<string, unknown>;\n if (typeof s?.action !== 'string' || !Array.isArray(s?.roles)) {\n throw new Error(\n `Schema at index ${i} from ${url} is missing required fields \"action\" (string) and \"roles\" (array)`\n );\n }\n }\n this.registerAll(body as CommandSchema[]);\n }\n}\n\n/** Singleton registry — shared across all <lse-intent> elements on the page. */\nexport const intentRegistry = new IntentSchemaRegistry();\n","/**\n * Sandboxed execution wrapper for <lse-intent>.\n *\n * Wraps the hyperfixi runtime call with:\n * - try/catch for execution errors\n * - configurable timeout (default 5s)\n * - structured error reporting\n */\n\nexport interface SandboxResult {\n ok: boolean;\n result?: unknown;\n error?: Error;\n timedOut?: boolean;\n}\n\nconst DEFAULT_TIMEOUT_MS = 5000;\n\n/**\n * Execute `fn` with a timeout. If it exceeds `timeoutMs`, the promise\n * rejects with an error whose `timedOut` flag is set.\n */\nexport async function sandboxed(\n fn: () => Promise<unknown>,\n timeoutMs = DEFAULT_TIMEOUT_MS\n): Promise<SandboxResult> {\n let timedOut = false;\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutHandle = setTimeout(() => {\n timedOut = true;\n reject(new Error(`LSE execution timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n });\n\n try {\n const result = await Promise.race([fn(), timeoutPromise]);\n clearTimeout(timeoutHandle);\n return { ok: true, result };\n } catch (err) {\n clearTimeout(timeoutHandle);\n const error = err instanceof Error ? err : new Error(String(err));\n return { ok: false, error, timedOut };\n }\n}\n","/**\n * <lse-intent> Custom Element\n *\n * Accepts LSE protocol JSON via an inline <script type=\"application/lse+json\">\n * child (or a `src` attribute), validates it, and executes it via the hyperfixi\n * runtime according to a declarative trigger model. Degrades gracefully when\n * the runtime is unavailable.\n *\n * ## Trigger modes\n *\n * The `trigger` attribute controls when validation + execution fires:\n *\n * - `load` (default) — fire immediately on `connectedCallback`\n * - `click` — fire on click anywhere inside the element\n * - `submit` — fire on submit of the closest ancestor `<form>`; preventDefault is called synchronously\n * - `intersect` — fire when the element scrolls into view (IntersectionObserver; one-shot)\n * - `manual` — never fire automatically; caller must invoke `.refresh()`\n * - (any other value) — treated as a DOM event name, wired via addEventListener on the element\n *\n * If no `trigger` attribute is set, the element inspects the JSON wire-format\n * `trigger.event` sugar field (protocol/spec/wire-format.md lines 441–474) and\n * uses it as the event name. If neither is present, defaults to `load`.\n *\n * @example\n * ```html\n * <!-- Load on connect (default) -->\n * <lse-intent>\n * <script type=\"application/lse+json\">\n * {\"action\":\"toggle\",\"roles\":{\"patient\":{\"type\":\"selector\",\"value\":\".active\"}}}\n * </script>\n * </lse-intent>\n *\n * <!-- Fire on click -->\n * <lse-intent trigger=\"click\">\n * <button slot=\"trigger\">Toggle</button>\n * <script type=\"application/lse+json\">\n * {\"action\":\"toggle\",\"roles\":{\"patient\":{\"type\":\"selector\",\"value\":\".active\"}}}\n * </script>\n * </lse-intent>\n *\n * <!-- Auto-wire from JSON trigger.event sugar -->\n * <lse-intent>\n * <button>Toggle</button>\n * <script type=\"application/lse+json\">\n * {\"action\":\"toggle\",\"roles\":{\"patient\":{\"type\":\"selector\",\"value\":\".active\"}},\"trigger\":{\"event\":\"click\"}}\n * </script>\n * </lse-intent>\n *\n * <!-- Manual (caller drives refresh) -->\n * <lse-intent trigger=\"manual\" id=\"my-intent\">...</lse-intent>\n * <script>document.getElementById('my-intent').refresh()</script>\n *\n * <!-- Fetch from URL, fire on intersect -->\n * <lse-intent src=\"/intents/toggle-sidebar.json\" trigger=\"intersect\"></lse-intent>\n * ```\n *\n * Events dispatched on the element:\n * - `lse:validated` — after schema check, before execution (detail: { node, diagnostics })\n * - `lse:executed` — after successful execution (detail: { node, result })\n * - `lse:error` — on validation or execution failure (detail: { diagnostics, error })\n */\n\nimport { fromProtocolJSON, validateProtocolJSON } from '@lokascript/intent';\nimport type { SemanticNode, IRDiagnostic, CommandSchema, SemanticValue } from '@lokascript/intent';\nimport { intentRegistry } from './schema-registry.js';\nimport { sandboxed } from './sandbox.js';\n\n// ---------------------------------------------------------------------------\n// Runtime bridge — resolved lazily so the element works without @hyperfixi/core\n// ---------------------------------------------------------------------------\n\ninterface HyperfiziRuntime {\n evalLSENode(node: SemanticNode, element?: Element): Promise<unknown>;\n}\n\nfunction getRuntime(): HyperfiziRuntime | null {\n const w = globalThis as Record<string, unknown>;\n const api = w['hyperfixi'];\n if (api && typeof (api as Record<string, unknown>)['evalLSENode'] === 'function') {\n return api as HyperfiziRuntime;\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Trigger resolution\n// ---------------------------------------------------------------------------\n\ntype TriggerSpec =\n | { kind: 'load' }\n | { kind: 'manual' }\n | { kind: 'intersect' }\n | { kind: 'submit' }\n | { kind: 'event'; eventName: string };\n\nfunction parseTriggerValue(value: string): TriggerSpec {\n const v = value.trim().toLowerCase();\n if (v === '' || v === 'load') return { kind: 'load' };\n if (v === 'manual') return { kind: 'manual' };\n if (v === 'intersect') return { kind: 'intersect' };\n if (v === 'submit') return { kind: 'submit' };\n return { kind: 'event', eventName: v };\n}\n\n// ---------------------------------------------------------------------------\n// <lse-intent>\n// ---------------------------------------------------------------------------\n\ninterface PreparedNode {\n node: SemanticNode;\n raw: Record<string, unknown>;\n}\n\nexport class LSEIntentElement extends HTMLElement {\n static observedAttributes = ['src', 'disabled', 'timeout', 'trigger'];\n\n private _node: SemanticNode | null = null;\n private _diagnostics: IRDiagnostic[] = [];\n private _abortController: AbortController | null = null;\n private _triggerCleanup: (() => void) | null = null;\n private _initInFlight = false;\n private _initPending = false;\n\n // ── Lifecycle ──────────────────────────────────────────────────────────────\n\n connectedCallback(): void {\n void this._initialize();\n }\n\n disconnectedCallback(): void {\n this._abortController?.abort();\n this._abortController = null;\n this._tearDownTrigger();\n }\n\n attributeChangedCallback(name: string, _old: string | null, _new: string | null): void {\n if (name !== 'src' && name !== 'disabled' && name !== 'trigger') return;\n // Only react to attribute changes when connected. Before connect, the\n // connectedCallback will run _initialize once with the final attribute\n // state. This avoids races between setAttribute() calls that happen\n // before the element is appended to the document.\n if (!this.isConnected) return;\n void this._initialize();\n }\n\n // ── Public accessors ───────────────────────────────────────────────────────\n\n /** The parsed SemanticNode, or null if not yet loaded / invalid. */\n get node(): SemanticNode | null {\n return this._node;\n }\n\n /** Diagnostics from the last validation attempt. Returns a snapshot copy. */\n get diagnostics(): readonly IRDiagnostic[] {\n return [...this._diagnostics];\n }\n\n /**\n * Re-run preparation and execution explicitly. Useful after updating the\n * inline JSON, and required when `trigger=\"manual\"`. Always executes\n * regardless of trigger mode.\n */\n async refresh(): Promise<void> {\n const prepared = await this._prepare();\n if (!prepared) return;\n await this._execute(prepared.node);\n }\n\n // ── Private: initialization and trigger dispatch ───────────────────────────\n\n /**\n * Serialize concurrent `_initialize` calls. If one is already running,\n * mark a re-init as pending and return. The in-flight init will re-run\n * once after it finishes so the final attribute/JSON state is reflected.\n */\n private async _initialize(): Promise<void> {\n if (this._initInFlight) {\n this._initPending = true;\n return;\n }\n this._initInFlight = true;\n try {\n await this._doInitialize();\n while (this._initPending) {\n this._initPending = false;\n await this._doInitialize();\n }\n } finally {\n this._initInFlight = false;\n }\n }\n\n private async _doInitialize(): Promise<void> {\n // Always tear down any existing trigger wiring before re-initializing.\n this._tearDownTrigger();\n\n if (this.hasAttribute('disabled')) return;\n\n const prepared = await this._prepare();\n if (!prepared) return;\n\n // The element may have been disconnected while _prepare() was awaiting\n // a fetch or JSON parse. If so, don't wire triggers — disconnectedCallback\n // already ran cleanup.\n if (!this.isConnected) return;\n\n const spec = this._resolveTriggerSpec(prepared.raw);\n\n switch (spec.kind) {\n case 'load':\n await this._execute(prepared.node);\n return;\n case 'manual':\n return;\n case 'intersect':\n this._wireIntersect(prepared.node);\n return;\n case 'submit':\n this._wireSubmit(prepared.node);\n return;\n case 'event':\n this._wireEvent(prepared.node, spec.eventName);\n return;\n }\n }\n\n private _resolveTriggerSpec(raw: Record<string, unknown>): TriggerSpec {\n // 1. Explicit trigger attribute wins.\n const attr = this.getAttribute('trigger');\n if (attr !== null) {\n return parseTriggerValue(attr);\n }\n\n // 2. Wire-format trigger.event sugar in the JSON.\n const triggerObj = raw['trigger'];\n if (triggerObj && typeof triggerObj === 'object' && !Array.isArray(triggerObj)) {\n const eventName = (triggerObj as Record<string, unknown>)['event'];\n if (typeof eventName === 'string' && eventName.length > 0) {\n return parseTriggerValue(eventName);\n }\n }\n\n // 3. Default.\n return { kind: 'load' };\n }\n\n // ── Private: trigger wiring ────────────────────────────────────────────────\n\n private _tearDownTrigger(): void {\n if (this._triggerCleanup) {\n this._triggerCleanup();\n this._triggerCleanup = null;\n }\n }\n\n private _wireEvent(node: SemanticNode, eventName: string): void {\n const handler = (_ev: Event): void => {\n void this._execute(node);\n };\n this.addEventListener(eventName, handler);\n this._triggerCleanup = () => this.removeEventListener(eventName, handler);\n }\n\n private _wireSubmit(node: SemanticNode): void {\n const form = this.closest('form');\n if (!form) {\n this._diagnostics.push({\n severity: 'warning',\n code: 'NO_ANCESTOR_FORM',\n message: 'trigger=\"submit\" requires an ancestor <form>; none found.',\n });\n return;\n }\n const handler = (ev: Event): void => {\n // preventDefault synchronously so the form does not navigate while\n // execution is in flight. If execution fails, the diagnostics event\n // still fires but the form stays on the page.\n ev.preventDefault();\n void this._execute(node);\n };\n form.addEventListener('submit', handler);\n this._triggerCleanup = () => form.removeEventListener('submit', handler);\n }\n\n private _wireIntersect(node: SemanticNode): void {\n if (typeof IntersectionObserver === 'undefined') {\n this._diagnostics.push({\n severity: 'warning',\n code: 'NO_INTERSECTION_OBSERVER',\n message:\n 'trigger=\"intersect\" requires IntersectionObserver; not available in this environment.',\n });\n return;\n }\n const observer = new IntersectionObserver(entries => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n // One-shot: disconnect after first intersection.\n observer.disconnect();\n void this._execute(node);\n return;\n }\n }\n });\n observer.observe(this);\n this._triggerCleanup = () => observer.disconnect();\n }\n\n // ── Private: prepare + execute ─────────────────────────────────────────────\n\n /**\n * Read the JSON, validate, deserialize, and emit `lse:validated`. Returns\n * the prepared node and raw JSON on success, or null on failure (in which\n * case `lse:error` has already been emitted).\n */\n private async _prepare(): Promise<PreparedNode | null> {\n const raw = await this._readJSON();\n if (raw === null) return null;\n\n // Validate wire format\n const wireDiags = validateProtocolJSON(raw);\n const hasErrors = wireDiags.some(d => d.severity === 'error');\n\n if (hasErrors) {\n this._diagnostics = wireDiags;\n this._node = null;\n this._showError();\n this._emit('lse:error', { diagnostics: wireDiags, error: null });\n return null;\n }\n\n // Deserialize to SemanticNode\n let node: SemanticNode;\n try {\n node = fromProtocolJSON(raw as unknown as Parameters<typeof fromProtocolJSON>[0]);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this._diagnostics = [\n { severity: 'error', code: 'DESERIALIZE_ERROR', message: error.message },\n ];\n this._node = null;\n this._showError();\n this._emit('lse:error', { diagnostics: this._diagnostics, error });\n return null;\n }\n\n // Optional schema validation\n const schema = intentRegistry.get(node.action);\n const schemaDiags: IRDiagnostic[] = schema ? this._validateSchema(node, schema) : [];\n\n this._node = node;\n this._diagnostics = [...wireDiags, ...schemaDiags];\n this._emit('lse:validated', { node, diagnostics: [...this._diagnostics] });\n\n return { node, raw };\n }\n\n /**\n * Execute a prepared node via the hyperfixi runtime. Emits `lse:executed`\n * on success or `lse:error` on failure. Safe to call multiple times.\n *\n * **Event-handler unwrap.** If the prepared node is an event-handler (from\n * either verbose wire format or compact `trigger` sugar), the element's\n * own `trigger` attribute has already wired the DOM event listener — the\n * wire-format event metadata is redundant at this point. We unwrap the\n * event-handler and execute each body command directly, rather than passing\n * the event-handler node to `evalLSENode` (which would attempt to re-wire\n * a listener at runtime, effectively discarding the body).\n */\n private async _execute(node: SemanticNode): Promise<void> {\n const runtime = getRuntime();\n if (!runtime) {\n // Runtime not loaded — emit a warning but don't error.\n this._diagnostics.push({\n severity: 'warning',\n code: 'NO_RUNTIME',\n message:\n 'hyperfixi runtime not found. Load hyperfixi.js before intent-element.iife.js to enable execution.',\n });\n return;\n }\n\n // Unwrap event-handler nodes into their body commands. See the comment\n // above and examples/llm-native-todo-demo/README.md for the motivation.\n const executables: SemanticNode[] =\n node.kind === 'event-handler'\n ? [...((node as { body?: SemanticNode[] }).body ?? [])]\n : [node];\n\n const rawTimeout = this.getAttribute('timeout');\n const timeoutMs = rawTimeout !== null ? parseInt(rawTimeout, 10) || 5000 : 5000;\n\n // Execute body commands sequentially. A single failure stops the sequence\n // and emits `lse:error`; all successful commands before the failure have\n // already mutated the DOM.\n const results: unknown[] = [];\n for (const cmd of executables) {\n const result = await sandboxed(() => runtime.evalLSENode(cmd, this), timeoutMs);\n if (result.ok) {\n results.push(result.result);\n } else {\n this._diagnostics.push({\n severity: 'error',\n code: result.timedOut ? 'EXECUTION_TIMEOUT' : 'EXECUTION_ERROR',\n message: result.error?.message ?? 'Unknown execution error',\n });\n this._showError();\n this._emit('lse:error', { diagnostics: [...this._diagnostics], error: result.error });\n return;\n }\n }\n\n this._emit('lse:executed', {\n node,\n // For single-body cases keep the legacy `result` shape (first/only value);\n // for multi-body cases expose the full array via `results` too.\n result: results.length === 1 ? results[0] : results,\n results,\n });\n }\n\n private async _readJSON(): Promise<Record<string, unknown> | null> {\n // 1. src attribute — fetch remote JSON\n const src = this.getAttribute('src');\n if (src) {\n this._abortController?.abort();\n this._abortController = new AbortController();\n try {\n const response = await fetch(src, { signal: this._abortController.signal });\n if (!response.ok) {\n this._emitFetchError(src, response.status);\n return null;\n }\n return (await response.json()) as Record<string, unknown>;\n } catch (err) {\n // AbortError means the element was disconnected — silently ignore\n if (err instanceof Error && err.name === 'AbortError') return null;\n this._emitFetchError(src, 0, err instanceof Error ? err : undefined);\n return null;\n }\n }\n\n // 2. Inline <script type=\"application/lse+json\"> child\n const script = this.querySelector('script[type=\"application/lse+json\"]');\n if (script) {\n try {\n return JSON.parse(script.textContent ?? '') as Record<string, unknown>;\n } catch {\n const diag: IRDiagnostic = {\n severity: 'error',\n code: 'INVALID_JSON',\n message: 'Inline JSON is not valid JSON',\n };\n this._diagnostics = [diag];\n this._showError();\n this._emit('lse:error', { diagnostics: [diag], error: null });\n return null;\n }\n }\n\n return null;\n }\n\n private _validateSchema(node: SemanticNode, schema: CommandSchema): IRDiagnostic[] {\n const diags: IRDiagnostic[] = [];\n for (const roleSpec of schema.roles) {\n const value = node.roles.get(roleSpec.role) as SemanticValue | undefined;\n\n if (roleSpec.required && !value) {\n diags.push({\n severity: 'error',\n code: 'MISSING_REQUIRED_ROLE',\n message: `Required role \"${roleSpec.role}\" is missing for command \"${node.action}\"`,\n });\n continue;\n }\n\n if (value && roleSpec.expectedTypes && roleSpec.expectedTypes.length > 0) {\n if (\n !roleSpec.expectedTypes.includes(value.type as (typeof roleSpec.expectedTypes)[number])\n ) {\n diags.push({\n severity: 'warning',\n code: 'UNEXPECTED_ROLE_TYPE',\n message: `Role \"${roleSpec.role}\" has type \"${value.type}\" but expected one of: ${roleSpec.expectedTypes.join(', ')}`,\n });\n }\n }\n }\n return diags;\n }\n\n private _showError(): void {\n const errorSlot = this.querySelector('[slot=\"error\"]');\n if (errorSlot instanceof HTMLElement) {\n errorSlot.style.display = '';\n }\n }\n\n private _emit(type: string, detail: Record<string, unknown>): void {\n this.dispatchEvent(new CustomEvent(type, { detail, bubbles: true, composed: true }));\n }\n\n private _emitFetchError(src: string, status: number, error?: Error): void {\n const message = status\n ? `Failed to fetch ${src}: HTTP ${status}`\n : `Failed to fetch ${src}${error ? ': ' + error.message : ''}`;\n const diag: IRDiagnostic = { severity: 'error', code: 'FETCH_ERROR', message };\n this._diagnostics = [diag];\n this._showError();\n this._emit('lse:error', { diagnostics: [diag], error: error ?? null });\n }\n}\n","/**\n * @hyperfixi/intent-element\n *\n * Browser custom element that validates LSE protocol JSON and executes it\n * via the hyperfixi runtime. Zero-dependency validation via @lokascript/intent;\n * execution delegated to window.hyperfixi.evalLSENode (peer dep).\n *\n * Auto-registers <lse-intent> when loaded as a browser script.\n *\n * @example\n * ```html\n * <script src=\"hyperfixi.js\"></script>\n * <script src=\"intent-element.iife.global.js\"></script>\n *\n * <lse-intent>\n * <script type=\"application/lse+json\">\n * {\"action\":\"toggle\",\"roles\":{\"patient\":{\"type\":\"selector\",\"value\":\".active\"}},\"trigger\":{\"event\":\"click\"}}\n * </script>\n * <button slot=\"trigger\">Toggle sidebar</button>\n * </lse-intent>\n * ```\n */\n\nexport { LSEIntentElement } from './lse-intent.js';\nexport { intentRegistry } from './schema-registry.js';\nexport type { SandboxResult } from './sandbox.js';\n\n// Auto-register the custom element when loaded in a browser context\nif (typeof customElements !== 'undefined' && !customElements.get('lse-intent')) {\n // Dynamic import to avoid circular reference from the export above\n import('./lse-intent.js').then(({ LSEIntentElement }) => {\n customElements.define('lse-intent', LSEIntentElement);\n });\n}\n"],"mappings":"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","names":["createLiteral","value","dataType","createSelector","selectorKind","createReference","createExpression","raw","createFlag","name","enabled","createCommandNode","action","roles","metadata","rolesMap","node","createEventHandlerNode","body","eventModifiers","createConditionalNode","thenBranch","elseBranch","createCompoundNode","statements","chainType","base","createLoopNode","loopVariant","loopVariable","indexVariable","createTryNode","catchBranch","finallyBranch","createAsyncNode","variant","asyncBody","createMatchNode","arms","defaultArm","fromProtocolJSON","json","role","val","valueFromProtocol","annotations","a","diagnostics","d","eventName","bodyRoles","bodyNode","applyV12Metadata","kind","loopBody","arm","validateProtocolJSON","VALID_KINDS","v","VALID_VALUE_TYPES","trigger","VALID_CHAIN_TYPES","VALID_ASYNC_VARIANTS","VALID_DIAGNOSTIC_LEVELS","IntentSchemaRegistry","intentRegistry","init_schema_registry","__esmMin","schema","schemas","action","url","response","body","s","sandboxed","fn","timeoutMs","timedOut","timeoutHandle","timeoutPromise","_","reject","result","err","init_sandbox","__esmMin","lse_intent_exports","__export","LSEIntentElement","getRuntime","api","parseTriggerValue","value","v","init_lse_intent","__esmMin","init_dist","init_schema_registry","init_sandbox","name","_old","_new","prepared","spec","raw","attr","triggerObj","eventName","node","handler","_ev","form","ev","observer","entries","entry","wireDiags","validateProtocolJSON","d","fromProtocolJSON","err","error","schema","intentRegistry","schemaDiags","runtime","executables","rawTimeout","timeoutMs","results","cmd","result","sandboxed","src","response","script","diag","diags","roleSpec","errorSlot","type","detail","status","src_exports","__export","LSEIntentElement","intentRegistry","init_lse_intent","init_schema_registry"]}
1
+ {"version":3,"sources":["../../intent/src/types.ts","../../intent/src/diagnostics.ts","../../intent/src/schema.ts","../../intent/src/inference/infer-roles.ts","../../intent/src/ir/references.ts","../../intent/src/ir/parser.ts","../../intent/src/ir/renderer.ts","../../intent/src/ir/protocol.ts","../src/schema-registry.ts","../src/sandbox.ts","../src/lse-intent.ts","../src/index.ts"],"sourcesContent":["/**\n * Universal UI Intent Types\n *\n * Canonical semantic representation for DSL commands. Language-neutral —\n * captures the MEANING of commands independent of surface syntax.\n *\n * These types are domain-agnostic and work for any DSL (hyperscript, SQL,\n * voice commands, animations, etc.) and any target framework (React, Vue,\n * Alpine, vanilla JS).\n */\n\nimport type { Diagnostic } from './diagnostics';\n\n// =============================================================================\n// Action & Role\n// =============================================================================\n\n/**\n * The command/operation in a DSL (e.g., 'toggle', 'fetch', 'select').\n */\nexport type ActionType = string;\n\n/**\n * The grammatical function of a part of a command (e.g., 'patient', 'destination').\n */\nexport type SemanticRole = string;\n\n// =============================================================================\n// Semantic Values (7 types)\n// =============================================================================\n\nexport type SemanticValue =\n LiteralValue | SelectorValue | ReferenceValue | PropertyPathValue | ExpressionValue | FlagValue;\n\n/** Expected value types for role tokens. */\nexport type ExpectedType = SemanticValue['type'];\n\nexport interface LiteralValue {\n readonly type: 'literal';\n readonly value: string | number | boolean;\n readonly dataType?: 'string' | 'number' | 'boolean' | 'duration';\n}\n\nexport interface SelectorValue {\n readonly type: 'selector';\n readonly value: string;\n readonly selectorKind?: 'id' | 'class' | 'attribute' | 'element' | 'complex' | 'identifier';\n}\n\nexport interface ReferenceValue {\n readonly type: 'reference';\n readonly value: string;\n}\n\nexport interface PropertyPathValue {\n readonly type: 'property-path';\n readonly object: SemanticValue;\n readonly property: string;\n}\n\nexport interface ExpressionValue {\n readonly type: 'expression';\n readonly raw: string;\n}\n\nexport interface FlagValue {\n readonly type: 'flag';\n readonly name: string;\n readonly enabled: boolean;\n}\n\n// =============================================================================\n// Annotations & Protocol Diagnostics (v1.2)\n// =============================================================================\n\n/** A metadata annotation on a node (v1.2). */\nexport interface Annotation {\n readonly name: string;\n readonly value?: string;\n}\n\n/**\n * A type constraint diagnostic attached to a node (v1.2).\n * Named `ProtocolDiagnostic` to avoid collision with the `Diagnostic` type.\n *\n * Uses `severity` (not `level`) to match the `Diagnostic` type convention.\n * The wire-format equivalent, `ProtocolDiagnosticJSON`, uses `level` per the\n * LSE protocol spec — this is intentional and kept separate.\n */\nexport interface ProtocolDiagnostic {\n readonly severity: 'error' | 'warning' | 'info';\n readonly role: string;\n readonly message: string;\n readonly code: string;\n}\n\n/** Async coordination variant (v1.2). */\nexport type AsyncVariant = 'all' | 'race';\n\n/** A single arm in a match command (v1.2). */\nexport interface MatchArm {\n readonly pattern: SemanticValue;\n readonly body: SemanticNode[];\n}\n\n// =============================================================================\n// Semantic Nodes (5 kinds)\n// =============================================================================\n\n/** Base interface for all semantic nodes. */\nexport interface SemanticNode {\n readonly kind: 'command' | 'event-handler' | 'conditional' | 'compound' | 'loop';\n readonly action: ActionType;\n readonly roles: ReadonlyMap<SemanticRole, SemanticValue>;\n readonly metadata?: SemanticMetadata;\n readonly annotations?: readonly Annotation[];\n readonly diagnostics?: readonly Diagnostic[];\n}\n\nexport interface SemanticMetadata {\n readonly sourceLanguage?: string;\n readonly sourceText?: string;\n readonly sourcePosition?: SourcePosition;\n readonly patternId?: string;\n readonly confidence?: number;\n}\n\nexport interface SourcePosition {\n readonly start: number;\n readonly end: number;\n readonly line?: number;\n readonly column?: number;\n}\n\n/** A single DSL command. */\nexport interface CommandSemanticNode extends SemanticNode {\n readonly kind: 'command';\n readonly body?: readonly SemanticNode[];\n readonly catchBranch?: readonly SemanticNode[];\n readonly finallyBranch?: readonly SemanticNode[];\n readonly asyncVariant?: AsyncVariant;\n readonly asyncBody?: readonly SemanticNode[];\n readonly arms?: readonly MatchArm[];\n readonly defaultArm?: readonly SemanticNode[];\n}\n\n/** Trigger-based commands (e.g., \"on click\"). */\nexport interface EventHandlerSemanticNode extends SemanticNode {\n readonly kind: 'event-handler';\n readonly body: SemanticNode[];\n readonly eventModifiers?: EventModifiers;\n readonly additionalEvents?: readonly SemanticValue[];\n readonly parameterNames?: readonly string[];\n}\n\nexport interface EventModifiers {\n readonly once?: boolean;\n readonly debounce?: number;\n readonly throttle?: number;\n readonly queue?: 'first' | 'last' | 'all' | 'none';\n readonly from?: SemanticValue;\n}\n\n/** Conditional: \"if [condition] then [body] else [body]\". */\nexport interface ConditionalSemanticNode extends SemanticNode {\n readonly kind: 'conditional';\n readonly thenBranch: SemanticNode[];\n readonly elseBranch?: SemanticNode[];\n}\n\n/** Multiple chained statements. */\nexport interface CompoundSemanticNode extends SemanticNode {\n readonly kind: 'compound';\n readonly statements: SemanticNode[];\n readonly chainType: 'then' | 'and' | 'async' | 'sequential' | 'pipe';\n}\n\n/** Loop variant discriminant. */\nexport type LoopVariant = 'forever' | 'times' | 'for' | 'while' | 'until';\n\n/** Repeat/for/while loops. */\nexport interface LoopSemanticNode extends SemanticNode {\n readonly kind: 'loop';\n readonly loopVariant: LoopVariant;\n readonly body: SemanticNode[];\n readonly loopVariable?: string;\n readonly indexVariable?: string;\n}\n\n/** Versioned envelope for multi-node LSE documents (v1.2). */\nexport interface LSEEnvelope {\n readonly lseVersion: string;\n readonly features?: readonly string[];\n readonly nodes: readonly SemanticNode[];\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\nexport function createLiteral(\n value: string | number | boolean,\n dataType?: 'string' | 'number' | 'boolean' | 'duration'\n): LiteralValue {\n return dataType ? { type: 'literal', value, dataType } : { type: 'literal', value };\n}\n\nexport function createSelector(\n value: string,\n selectorKind?: SelectorValue['selectorKind']\n): SelectorValue {\n return selectorKind ? { type: 'selector', value, selectorKind } : { type: 'selector', value };\n}\n\nexport function createReference(value: string): ReferenceValue {\n return { type: 'reference', value };\n}\n\nexport function createPropertyPath(object: SemanticValue, property: string): PropertyPathValue {\n return { type: 'property-path', object, property };\n}\n\nexport function createExpression(raw: string): ExpressionValue {\n return { type: 'expression', raw };\n}\n\nexport function createFlag(name: string, enabled: boolean = true): FlagValue {\n return { type: 'flag', name, enabled };\n}\n\nexport function createCommandNode(\n action: ActionType,\n roles: Record<SemanticRole, SemanticValue> | Map<SemanticRole, SemanticValue>,\n metadata?: SemanticMetadata\n): CommandSemanticNode {\n const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));\n const node: CommandSemanticNode = { kind: 'command', action, roles: rolesMap };\n return metadata ? { ...node, metadata } : node;\n}\n\nexport function createEventHandlerNode(\n action: ActionType,\n roles: Record<SemanticRole, SemanticValue> | Map<SemanticRole, SemanticValue>,\n body: SemanticNode[],\n metadata?: SemanticMetadata,\n eventModifiers?: EventModifiers\n): EventHandlerSemanticNode {\n const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));\n return {\n kind: 'event-handler',\n action,\n roles: rolesMap,\n body,\n ...(eventModifiers && { eventModifiers }),\n ...(metadata && { metadata }),\n };\n}\n\nexport function createConditionalNode(\n action: ActionType,\n roles: Record<SemanticRole, SemanticValue> | Map<SemanticRole, SemanticValue>,\n thenBranch: SemanticNode[],\n elseBranch?: SemanticNode[],\n metadata?: SemanticMetadata\n): ConditionalSemanticNode {\n const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));\n return {\n kind: 'conditional',\n action,\n roles: rolesMap,\n thenBranch,\n ...(elseBranch && { elseBranch }),\n ...(metadata && { metadata }),\n };\n}\n\nexport function createCompoundNode(\n statements: SemanticNode[],\n chainType: CompoundSemanticNode['chainType'] = 'sequential',\n metadata?: SemanticMetadata\n): CompoundSemanticNode {\n const base = {\n kind: 'compound' as const,\n action: 'compound' as const,\n roles: new Map(),\n statements,\n chainType,\n };\n return metadata ? { ...base, metadata } : base;\n}\n\nexport function createLoopNode(\n action: ActionType,\n roles: Record<SemanticRole, SemanticValue> | Map<SemanticRole, SemanticValue>,\n loopVariant: LoopVariant,\n body: SemanticNode[],\n loopVariable?: string,\n indexVariable?: string,\n metadata?: SemanticMetadata\n): LoopSemanticNode {\n const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));\n return {\n kind: 'loop',\n action,\n roles: rolesMap,\n loopVariant,\n body,\n ...(loopVariable && { loopVariable }),\n ...(indexVariable && { indexVariable }),\n ...(metadata && { metadata }),\n };\n}\n\nexport function createTryNode(\n body: SemanticNode[],\n catchBranch?: SemanticNode[],\n finallyBranch?: SemanticNode[],\n metadata?: SemanticMetadata\n): CommandSemanticNode {\n return {\n kind: 'command',\n action: 'try',\n roles: new Map(),\n body,\n ...(catchBranch && catchBranch.length > 0 ? { catchBranch } : {}),\n ...(finallyBranch && finallyBranch.length > 0 ? { finallyBranch } : {}),\n ...(metadata ? { metadata } : {}),\n };\n}\n\nexport function createAsyncNode(\n variant: AsyncVariant,\n asyncBody: SemanticNode[],\n metadata?: SemanticMetadata\n): CommandSemanticNode {\n return {\n kind: 'command',\n action: variant,\n roles: new Map(),\n asyncVariant: variant,\n asyncBody,\n ...(metadata ? { metadata } : {}),\n };\n}\n\nexport function createMatchNode(\n roles: Record<SemanticRole, SemanticValue> | Map<SemanticRole, SemanticValue>,\n arms: MatchArm[],\n defaultArm?: SemanticNode[],\n metadata?: SemanticMetadata\n): CommandSemanticNode {\n const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));\n return {\n kind: 'command',\n action: 'match',\n roles: rolesMap,\n arms,\n ...(defaultArm && defaultArm.length > 0 ? { defaultArm } : {}),\n ...(metadata ? { metadata } : {}),\n };\n}\n\n// =============================================================================\n// Value Extraction Helpers\n// =============================================================================\n\n/**\n * Extract a string representation from any SemanticValue.\n */\nexport function extractValue(value: SemanticValue): string {\n if (value.type === 'flag') return value.name;\n if ('raw' in value && value.raw !== undefined) return String(value.raw);\n if ('value' in value && value.value !== undefined) return String(value.value);\n if (value.type === 'property-path') return `${extractValue(value.object)}.${value.property}`;\n return '';\n}\n\n/**\n * Get the typed `SemanticValue` for a named role on a SemanticNode.\n * Returns `undefined` if the role is missing.\n *\n * Use this when you need the structured value (e.g. to branch on\n * `value.type`, read `value.selectorKind`, etc.). See {@link extractRoleValue}\n * for the string-convenience companion.\n */\nexport function getRoleValue(node: SemanticNode, role: string): SemanticValue | undefined {\n return node.roles.get(role);\n}\n\n/**\n * Extract a string representation from a named role on a SemanticNode.\n * Returns empty string if the role is missing.\n *\n * String-convenience companion to {@link getRoleValue}. Use this when you\n * just want a stringified role value; use `getRoleValue` when you need to\n * inspect the typed `SemanticValue` (e.g. to branch on `value.type`).\n */\nexport function extractRoleValue(node: SemanticNode, role: string): string {\n const value = node.roles.get(role);\n if (!value) return '';\n return extractValue(value);\n}\n","/**\n * Structured Error Diagnostics\n *\n * Generic diagnostic types for reporting errors, warnings, and hints\n * during parsing, validation, and compilation. These provide richer feedback\n * than simple error strings, enabling:\n *\n * - Severity levels (error, warning, info)\n * - Machine-readable error codes\n * - Source location tracking\n * - Actionable suggestions\n * - Programmatic fix lookup\n */\n\n// =============================================================================\n// Diagnostic Types\n// =============================================================================\n\n/**\n * Severity level for a diagnostic.\n */\nexport type DiagnosticSeverity = 'error' | 'warning' | 'info';\n\n/**\n * A structured diagnostic message.\n *\n * Diagnostics provide rich error information beyond simple strings.\n * They're used for parser errors, validation warnings, and hints.\n */\nexport interface Diagnostic {\n /** Human-readable message */\n readonly message: string;\n\n /** Severity level */\n readonly severity: DiagnosticSeverity;\n\n /** Machine-readable code for programmatic handling (e.g., 'parse-error', 'missing-role') */\n readonly code?: string;\n\n /**\n * The role this diagnostic is about (e.g. `patient`). The wire format's\n * `Diagnostic` shape requires it, so it must survive a protocol-JSON round-trip.\n */\n readonly role?: string;\n\n /** 1-based line number in source */\n readonly line?: number;\n\n /** 0-based column offset in line */\n readonly column?: number;\n\n /** Original source text that triggered the diagnostic */\n readonly source?: string;\n\n /** Actionable suggestions for fixing the issue */\n readonly suggestions?: readonly string[];\n}\n\n/**\n * Result of a diagnostic-aware validation.\n */\nexport interface DiagnosticResult {\n /** Whether validation passed (no errors, though warnings/info may exist) */\n readonly ok: boolean;\n\n /** All diagnostics, including errors, warnings, and info */\n readonly diagnostics: readonly Diagnostic[];\n\n /** Convenience: count of each severity level */\n readonly summary: DiagnosticSummary;\n}\n\n/**\n * Summary counts by severity.\n */\nexport interface DiagnosticSummary {\n readonly errors: number;\n readonly warnings: number;\n readonly infos: number;\n}\n\n// =============================================================================\n// Diagnostic Collector\n// =============================================================================\n\n/**\n * Collects diagnostics during a parsing/validation/compilation pass.\n *\n * @example\n * ```typescript\n * const collector = createDiagnosticCollector();\n * collector.error('Unknown command: foo', { code: 'unknown-command', line: 3 });\n * const result = collector.toResult();\n * ```\n */\nexport interface DiagnosticCollector {\n /** Message-first form: `error(message, options?)` */\n error(message: string, options?: DiagnosticOptions): void;\n /** Code-first form: `error(code, message, options?)` — useful at call sites where the code is the stable key */\n error(code: string, message: string, options?: DiagnosticOptions): void;\n warning(message: string, options?: DiagnosticOptions): void;\n info(message: string, options?: DiagnosticOptions): void;\n add(diagnostic: Diagnostic): void;\n hasErrors(): boolean;\n getDiagnostics(): readonly Diagnostic[];\n toResult(): DiagnosticResult;\n}\n\n/**\n * Options for adding a diagnostic via the collector's convenience methods.\n */\nexport interface DiagnosticOptions {\n readonly code?: string;\n readonly line?: number;\n readonly column?: number;\n readonly source?: string;\n readonly suggestions?: readonly string[];\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\n/**\n * Create a new diagnostic collector.\n */\nexport function createDiagnosticCollector(): DiagnosticCollector {\n const diagnostics: Diagnostic[] = [];\n\n function addWithSeverity(\n severity: DiagnosticSeverity,\n message: string,\n options?: DiagnosticOptions\n ): void {\n const diag: Diagnostic = {\n message,\n severity,\n ...(options?.code != null && { code: options.code }),\n ...(options?.line != null && { line: options.line }),\n ...(options?.column != null && { column: options.column }),\n ...(options?.source != null && { source: options.source }),\n ...(options?.suggestions != null &&\n options.suggestions.length > 0 && { suggestions: options.suggestions }),\n };\n diagnostics.push(diag);\n }\n\n return {\n error(\n messageOrCode: string,\n messageOrOptions?: string | DiagnosticOptions,\n options?: DiagnosticOptions\n ) {\n if (typeof messageOrOptions === 'string') {\n // Code-first form: error(code, message, options?)\n addWithSeverity('error', messageOrOptions, { ...options, code: messageOrCode });\n } else {\n // Message-first form: error(message, options?)\n addWithSeverity('error', messageOrCode, messageOrOptions);\n }\n },\n warning(message, options) {\n addWithSeverity('warning', message, options);\n },\n info(message, options) {\n addWithSeverity('info', message, options);\n },\n add(diagnostic) {\n diagnostics.push(diagnostic);\n },\n hasErrors() {\n return diagnostics.some(d => d.severity === 'error');\n },\n getDiagnostics() {\n return diagnostics;\n },\n toResult(): DiagnosticResult {\n let errors = 0;\n let warnings = 0;\n let infos = 0;\n for (const d of diagnostics) {\n if (d.severity === 'error') errors++;\n else if (d.severity === 'warning') warnings++;\n else infos++;\n }\n return {\n ok: errors === 0,\n diagnostics,\n summary: { errors, warnings, infos },\n };\n },\n };\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/**\n * Create a diagnostic from an Error object.\n */\nexport function fromError(\n error: unknown,\n options?: Omit<DiagnosticOptions, 'code'> & { code?: string }\n): Diagnostic {\n const message = error instanceof Error ? error.message : String(error);\n return {\n message,\n severity: 'error',\n ...(options?.code != null && { code: options.code }),\n ...(options?.line != null && { line: options.line }),\n ...(options?.column != null && { column: options.column }),\n ...(options?.source != null && { source: options.source }),\n ...(options?.suggestions != null &&\n options.suggestions.length > 0 && { suggestions: options.suggestions }),\n };\n}\n\n/**\n * Filter diagnostics by severity.\n */\nexport function filterBySeverity(\n diagnostics: readonly Diagnostic[],\n severity: DiagnosticSeverity\n): readonly Diagnostic[] {\n return diagnostics.filter(d => d.severity === severity);\n}\n","/**\n * Command Schema Types\n *\n * Defines the structure of DSL commands for pattern generation and validation.\n * Language-neutral — describes semantic structure, not surface syntax.\n */\n\nimport type { ActionType, SemanticRole, SemanticValue, ExpectedType } from './types';\n\nexport interface CommandSchema {\n readonly action: ActionType;\n readonly description: string;\n readonly roles: RoleSpec[];\n readonly primaryRole: SemanticRole;\n readonly category: string;\n readonly hasBody?: boolean;\n readonly notes?: string;\n /**\n * Identifier tokens that may appear in the core parser's positional `args`\n * but are not bound to any role — e.g., scroll's `top|bottom|smoothly`.\n * These are skipped when scanning args for role values.\n */\n readonly argSkipTokens?: ReadonlyArray<string>;\n /**\n * Which role consumes the core parser's `target` field (the trailing\n * `on X` / `to X` / `from X` captured separately from args). Defaults to\n * `'destination'`. Set to `'source'` for commands like `remove .x from #y`.\n */\n readonly targetRole?: SemanticRole;\n}\n\nexport interface RoleSpec {\n readonly role: SemanticRole;\n readonly description: string;\n readonly required: boolean;\n readonly expectedTypes: Array<ExpectedType>;\n readonly default?: SemanticValue;\n /**\n * Ordering priority for SVO (Subject-Verb-Object) languages.\n * **Higher values appear EARLIER** in the surface form (descending sort).\n *\n * Example — `select <columns> from <source> where <condition>`:\n * `columns: 2, source: 1, condition: 0`\n *\n * Pick values so the most \"central\" role is highest and optional / trailing\n * clauses are lowest. When adding a role between two existing ones, bump all\n * higher positions rather than using fractional numbers.\n */\n readonly svoPosition?: number;\n /**\n * Ordering priority for SOV (Subject-Object-Verb) languages.\n * **Higher values appear EARLIER** in the surface form (descending sort).\n *\n * Same convention as {@link svoPosition} — the verb is appended last by the\n * pattern generator, so role positions order the pre-verb material.\n */\n readonly sovPosition?: number;\n /**\n * Per-language primary marker keyword for this role.\n *\n * - Non-empty string (e.g., `'to'`) — the canonical marker.\n * - Empty string `''` — explicitly no marker (bare positional arg).\n * - Multi-word strings (e.g., `'partials in'`) — match consecutive identifier tokens.\n * - `undefined` — the language profile's default marker for the role applies.\n *\n * For roles that accept multiple alternate markers (e.g., `put`'s\n * `into|before|after`), declare the alternates separately in {@link markerVariants}.\n */\n readonly markerOverride?: Record<string, string>;\n /**\n * Additional alternate marker keywords for this role, beyond {@link markerOverride}.\n * Used by schema-driven role inference to recognize any of the listed markers as\n * signaling this role; the matched marker is recorded in {@link methodCarrier} if set.\n *\n * Pattern-generation consumers typically read only `markerOverride`; this field is\n * specifically for bridge-side inference (see `inferRolesFromSchema`).\n */\n readonly markerVariants?: Record<string, readonly string[]>;\n /**\n * Per-language marker to use when RENDERING, overriding {@link markerOverride}\n * and the profile default. Empty string `''` means \"render this role bare\" —\n * for a marker that exists to help the parser but is not written by hand\n * (SQL's `insert` takes its values with no `set` before them, though the\n * profile gives the `values` role that marker).\n *\n * The key `'*'` is a blanket statement about the PROFILE default marker, so\n * it ranks below a per-language {@link markerOverride}: `{ '*': '' }` renders\n * the role bare in every language except those with an explicit override.\n * Full precedence: `renderOverride[lang]` → `markerOverride[lang]` →\n * `renderOverride['*']` → profile default.\n */\n readonly renderOverride?: Record<string, string>;\n readonly markerPosition?: 'before' | 'after';\n /**\n * Per-language override of {@link markerPosition}, for languages whose marker\n * sits on the other side of its value than their word order implies.\n *\n * Resolution when rendering: this → {@link markerPosition} → the profile's\n * marker position for the role → the word-order default (SOV: after, else before).\n */\n readonly markerPositionOverride?: Record<string, 'before' | 'after'>;\n /**\n * Which side of the verb this role renders on in SOV languages.\n *\n * SOV puts roles before the verb by default (`preVerb`). `postVerb` moves this\n * role after it — e.g. summarize's format renders `content 要約 として format`,\n * not `content として format 要約`. Ignored for non-SOV languages.\n */\n readonly sovSlot?: 'preVerb' | 'postVerb';\n /**\n * Wrap this role's value in double quotes when rendering, if it contains\n * whitespace and is not already quoted. For free-text roles (a question, a\n * prompt) whose value would otherwise be re-parsed as several tokens.\n */\n readonly quoteMultiword?: boolean;\n readonly greedy?: boolean;\n readonly selectorKinds?: ReadonlyArray<'id' | 'class' | 'attribute' | 'element' | 'complex'>;\n /**\n * When this role's marker has alternates (via {@link markerVariants}), the\n * matched marker keyword is recorded as a literal in the role named here.\n * Used by `put` to expose `into|before|after` as the `method` role.\n */\n readonly methodCarrier?: SemanticRole;\n}\n\n/**\n * Helper to create a command schema with sensible defaults.\n *\n * @throws TypeError when `schema` is missing `action` or `roles`, or when they\n * have the wrong shape. The types catch this for TypeScript callers; the\n * runtime check exists so JavaScript callers and hand-built objects fail with\n * a message naming the field instead of a `roles[0] of undefined` crash from\n * deep inside the package.\n */\nexport function defineCommand(\n schema: Partial<CommandSchema> & Pick<CommandSchema, 'action' | 'roles'>\n): CommandSchema {\n if (typeof schema !== 'object' || schema === null) {\n throw new TypeError(`defineCommand: expected a schema object, got ${describeValue(schema)}.`);\n }\n if (typeof schema.action !== 'string' || schema.action.length === 0) {\n throw new TypeError(\n `defineCommand: 'action' must be a non-empty string (got ${describeValue(schema.action)}).`\n );\n }\n if (!Array.isArray(schema.roles)) {\n throw new TypeError(\n `defineCommand(\"${schema.action}\"): 'roles' must be an array of RoleSpec ` +\n `(got ${describeValue(schema.roles)}). Build entries with defineRole().`\n );\n }\n for (const [index, role] of schema.roles.entries()) {\n if (typeof role !== 'object' || role === null || typeof role.role !== 'string') {\n throw new TypeError(\n `defineCommand(\"${schema.action}\"): roles[${index}] must be a RoleSpec with a string ` +\n `'role' field (got ${describeValue(role)}). Build it with defineRole().`\n );\n }\n }\n\n return {\n description: schema.description || `${schema.action} command`,\n category: schema.category || 'general',\n primaryRole: schema.primaryRole || schema.roles[0]?.role || 'patient',\n ...schema,\n action: schema.action,\n roles: schema.roles,\n } as CommandSchema;\n}\n\n/**\n * Helper to create a role spec with sensible defaults.\n */\nexport function defineRole(\n role: Partial<RoleSpec> & Pick<RoleSpec, 'role' | 'required' | 'expectedTypes'>\n): RoleSpec {\n return {\n description: role.description || `${role.role} role`,\n ...role,\n role: role.role,\n required: role.required,\n expectedTypes: role.expectedTypes,\n };\n}\n\n/**\n * Look up a role spec by name within a command schema.\n * Returns `undefined` if no role with that name exists.\n *\n * @example\n * const spec = getRoleSpec(toggleSchema, 'patient');\n * if (spec?.required) { ... }\n */\nexport function getRoleSpec(schema: CommandSchema, role: string): RoleSpec | undefined {\n // Contract is already \"undefined when not found\", so a malformed schema\n // answers the same question rather than throwing.\n if (!schema || !Array.isArray(schema.roles)) return undefined;\n return schema.roles.find(r => r.role === role);\n}\n\n/** Render a value for an error message without dumping large objects. */\nfunction describeValue(value: unknown): string {\n if (value === null) return 'null';\n if (Array.isArray(value)) return `an array of length ${value.length}`;\n if (typeof value === 'string') return `the string ${JSON.stringify(value)}`;\n if (typeof value === 'object') return 'an object';\n return typeof value === 'undefined' ? 'undefined' : String(value);\n}\n","/**\n * Schema-driven role inference.\n *\n * Maps a parser's positional args + modifiers + target → a role-keyed map,\n * driven by a `CommandSchema`. Used by IR bridges (core's `from-core.ts`,\n * framework's `from-interchange.ts`) to convert positional ASTs into the\n * role-based semantic IR without hand-rolling per-command switch cases.\n *\n * The function is generic over input/output value types via a small `ValueAdapter`\n * interface so callers with different node shapes (InterchangeNode, structural\n * INode, SemanticValue) can all share the same role-mapping logic.\n *\n * The schema-driven inference operates on the **English form** only (the surface\n * form the core hyperscript parser produces); per-language profile defaults\n * are not consulted here.\n */\nimport type { CommandSchema, RoleSpec } from '../schema';\nimport type { SemanticRole } from '../types';\n\n/**\n * Adapter interface that abstracts over the caller's node shape.\n *\n * @typeParam T - The input value type (e.g., `InterchangeNode`, `INode`).\n * @typeParam V - The output role-value type (e.g., `InterchangeNode`, `SemanticValue`).\n */\nexport interface ValueAdapter<T, V> {\n /**\n * If `node` is an identifier, return its keyword string; otherwise return\n * `undefined`. Used to match marker keywords and skip-tokens against args.\n */\n getIdentifierName(node: T): string | undefined;\n /**\n * Convert an input node to the output role-value type.\n */\n convertValue(node: T): V;\n /**\n * Wrap a raw string as a literal output value (used for `methodCarrier`\n * literals and for string-shaped modifier values).\n */\n createLiteralValue(text: string): V;\n}\n\n/**\n * Infer semantic roles from a `CommandSchema` plus the parser's positional\n * args, modifier object, and trailing-target node.\n *\n * Returns the role map (possibly empty). Returns `null` only if the schema is\n * not applicable (currently never — the function always returns a map).\n *\n * @example\n * ```ts\n * const roles = inferRolesFromSchema(toggleSchema, args, modifiers, target, adapter);\n * // → { patient: <args[0]>, destination: <target> }\n * ```\n */\nexport function inferRolesFromSchema<T, V>(\n schema: CommandSchema,\n args: readonly T[],\n modifiers: Readonly<Record<string, unknown>> | undefined,\n target: T | undefined,\n adapter: ValueAdapter<T, V>\n): Record<SemanticRole, V> {\n const roles: Record<SemanticRole, V> = {};\n const skipTokens = new Set(schema.argSkipTokens ?? []);\n const usedArgIndices = new Set<number>();\n\n // Pass 1: marker-keyed roles (modifier hit, then marker-in-args).\n // Iterate in declaration order so methodCarrier writes happen alongside their owning role.\n for (const role of schema.roles) {\n const markers = englishMarkers(role);\n if (markers.length === 0) continue; // No marker → handled in pass 2\n\n // Try modifiers first.\n let matched = tryModifierMarkers(role, markers, modifiers, adapter);\n\n // Fall back to scanning args for marker-in-args (e.g., scroll's `to`).\n if (!matched) {\n matched = tryArgMarkers(role, markers, args, skipTokens, usedArgIndices, adapter);\n }\n\n if (matched) {\n roles[role.role] = matched.value;\n if (role.methodCarrier) {\n roles[role.methodCarrier] = adapter.createLiteralValue(matched.matchedMarker);\n }\n }\n }\n\n // Pass 2: target field → targetRole (default 'destination').\n // Done before the positional pass so the target-filled role isn't also\n // considered for a positional arg slot.\n if (target !== undefined) {\n const tgtRole = schema.targetRole ?? 'destination';\n if (roles[tgtRole] === undefined) {\n roles[tgtRole] = adapter.convertValue(target);\n }\n }\n\n // Pass 3: positional (no-marker, not-yet-filled) roles, ordered by\n // svoPosition ASCENDING (semantic's convention — lower number = earlier\n // in surface form, matches `sortRolesByWordOrder` in @lokascript/semantic).\n const positionalRoles = schema.roles\n .filter(r => englishMarkers(r).length === 0 && roles[r.role] === undefined)\n .sort((a, b) => (a.svoPosition ?? 99) - (b.svoPosition ?? 99));\n\n let cursor = 0;\n for (const role of positionalRoles) {\n while (cursor < args.length) {\n if (usedArgIndices.has(cursor)) {\n cursor++;\n continue;\n }\n const name = adapter.getIdentifierName(args[cursor]);\n if (name !== undefined && skipTokens.has(name)) {\n cursor++;\n continue;\n }\n roles[role.role] = adapter.convertValue(args[cursor]);\n usedArgIndices.add(cursor);\n cursor++;\n break;\n }\n }\n\n return roles;\n}\n\n/**\n * Return the role's English marker(s). Empty array means \"positional\"\n * (either explicit empty string `''` or no `markerOverride.en` at all).\n */\nfunction englishMarkers(role: RoleSpec): readonly string[] {\n const primary = role.markerOverride?.en;\n const variants = role.markerVariants?.en ?? [];\n\n const out: string[] = [];\n if (typeof primary === 'string' && primary !== '') out.push(primary);\n for (const v of variants) if (v !== '') out.push(v);\n return out;\n}\n\ninterface MarkerMatch<V> {\n readonly value: V;\n readonly matchedMarker: string;\n}\n\n/**\n * Look up each candidate marker as a key in the modifiers object. Returns the\n * first hit. Modifier values may be nodes (objects with `type`) or strings.\n */\nfunction tryModifierMarkers<T, V>(\n _role: RoleSpec,\n markers: readonly string[],\n modifiers: Readonly<Record<string, unknown>> | undefined,\n adapter: ValueAdapter<T, V>\n): MarkerMatch<V> | undefined {\n if (!modifiers) return undefined;\n for (const m of markers) {\n if (!(m in modifiers)) continue;\n const raw = modifiers[m];\n if (raw === undefined || raw === null) continue;\n if (typeof raw === 'string') {\n return { value: adapter.createLiteralValue(raw), matchedMarker: m };\n }\n if (typeof raw === 'object' && 'type' in (raw as object)) {\n return { value: adapter.convertValue(raw as T), matchedMarker: m };\n }\n }\n return undefined;\n}\n\n/**\n * Scan args for a marker token (or multi-word marker phrase) followed by a\n * non-skip value arg. Mutates `usedArgIndices` to record consumed positions.\n *\n * Longest-match-first ordering ensures e.g. `partials in` matches before a\n * bare `in` would.\n */\nfunction tryArgMarkers<T, V>(\n _role: RoleSpec,\n markers: readonly string[],\n args: readonly T[],\n skipTokens: ReadonlySet<string>,\n usedArgIndices: Set<number>,\n adapter: ValueAdapter<T, V>\n): MarkerMatch<V> | undefined {\n const sorted = [...markers].sort((a, b) => b.split(/\\s+/).length - a.split(/\\s+/).length);\n\n for (const marker of sorted) {\n const words = marker.split(/\\s+/).filter(w => w.length > 0);\n if (words.length === 0) continue;\n\n for (let i = 0; i + words.length <= args.length; i++) {\n // Check that `args[i..i+words.length)` are unused identifiers matching `words`.\n let allMatch = true;\n for (let w = 0; w < words.length; w++) {\n if (usedArgIndices.has(i + w)) {\n allMatch = false;\n break;\n }\n const name = adapter.getIdentifierName(args[i + w]);\n if (name !== words[w]) {\n allMatch = false;\n break;\n }\n }\n if (!allMatch) continue;\n\n // Find the next non-used, non-skip arg as the role value.\n for (let v = i + words.length; v < args.length; v++) {\n if (usedArgIndices.has(v)) continue;\n const name = adapter.getIdentifierName(args[v]);\n if (name !== undefined && skipTokens.has(name)) continue;\n\n for (let w = 0; w < words.length; w++) usedArgIndices.add(i + w);\n usedArgIndices.add(v);\n return { value: adapter.convertValue(args[v]), matchedMarker: marker };\n }\n }\n }\n\n return undefined;\n}\n","/**\n * Reference Validation\n *\n * Configurable validation for reference values (me, you, it, result, etc.).\n * Domain DSLs can provide custom reference sets.\n */\n\n/** Default set of valid references for hyperscript-style DSLs. */\nexport const DEFAULT_REFERENCES: ReadonlySet<string> = new Set([\n 'me',\n 'you',\n 'it',\n 'result',\n 'event',\n 'target',\n 'body',\n // DOM context globals — as reference-typed as `body`, and already advertised as\n // references by core's REFERENCE_KEYWORDS and the parser's PROPERTY_ACCESS_BASES.\n // Without them here, a foreign surface (ja ドキュメント, es documento) lexes as a\n // keyword but fails isValidReference, degrades to a literal, and leaks verbatim.\n 'document',\n 'window',\n 'detail',\n]);\n\n/**\n * Check if a value is a valid reference name.\n *\n * @param value - The string to check\n * @param referenceSet - Custom reference set (defaults to DEFAULT_REFERENCES)\n */\nexport function isValidReference(\n value: string,\n referenceSet: ReadonlySet<string> = DEFAULT_REFERENCES\n): boolean {\n return referenceSet.has(value);\n}\n","/**\n * Explicit Syntax Parser\n *\n * Parses the universal [command role:value ...] bracket syntax.\n * Domain-agnostic — schema validation is injectable via ParseExplicitOptions.\n *\n * Three parsing levels matching the ABNF grammar:\n * parseDocument() → parseCompound() → parseExplicit()\n * (multi-line) (chain ops) (single bracket)\n */\n\nimport type {\n SemanticNode,\n SemanticValue,\n SemanticRole,\n ActionType,\n LoopVariant,\n Annotation,\n LSEEnvelope,\n CompoundSemanticNode,\n} from '../types';\nimport {\n createCommandNode,\n createEventHandlerNode,\n createConditionalNode,\n createLoopNode,\n createCompoundNode,\n createSelector,\n createLiteral,\n createReference,\n createFlag,\n} from '../types';\nimport type { Diagnostic } from '../diagnostics';\nimport type { ParseExplicitOptions } from './types';\nimport { isValidReference, DEFAULT_REFERENCES } from './references';\n\n/**\n * Structural role names whose bracket-enclosed values are nested commands.\n *\n * In these roles a value starting with `[` is ALWAYS a nested command; write an\n * attribute selector as a selector literal (`<[data-active]/>`). Prior to v2.0 the\n * parser guessed from the inner content, which silently dropped zero-argument\n * commands — `[halt]` is both a valid command and a valid attribute selector.\n */\nexport const STRUCTURAL_ROLES = new Set([\n 'body',\n 'then',\n 'else',\n 'condition',\n 'loop-body',\n 'variable',\n 'catch',\n 'finally',\n]);\n\n/**\n * Loop commands, and the attributes the renderer writes beside a loop head's\n * roles (`[repeat quantity:3 loop-variant:times loop-body:[…]]`). The\n * attributes describe the LOOP, not a role of its command's schema, so schema\n * validation must not reject them: it did, so the renderer's own loop output\n * could not be parsed back once a schema was supplied.\n */\nconst LOOP_COMMANDS = new Set(['repeat', 'for']);\nconst LOOP_ATTRIBUTES = new Set(['loop-variant', 'index-variable']);\n\n// =============================================================================\n// Explicit Syntax Parser\n// =============================================================================\n\nexport function parseExplicit(input: string, options: ParseExplicitOptions = {}): SemanticNode {\n const trimmed = input.trim();\n const collect = options.collectDiagnostics === true;\n const diagnostics: Diagnostic[] = [];\n\n if (!trimmed.startsWith('[') || !trimmed.endsWith(']')) {\n throw new Error('Explicit syntax must be wrapped in brackets: [command role:value ...]');\n }\n\n const content = trimmed.slice(1, -1).trim();\n if (!content) {\n throw new Error('Empty explicit statement');\n }\n\n const tokens = tokenizeExplicit(content);\n if (tokens.length === 0) {\n throw new Error('No command specified in explicit statement');\n }\n\n const command = tokens[0].toLowerCase() as ActionType;\n const roles = new Map<SemanticRole, SemanticValue>();\n const refSet = options.referenceSet ?? DEFAULT_REFERENCES;\n\n const schema = options.schemaLookup?.getSchema(command);\n // Build a Set for O(1) membership checks across all role tokens in this parse call.\n // For single lookups outside a parse loop, use getRoleSpec() from schema.ts instead.\n const validRoleNames = schema ? new Set(schema.roles.map(r => r.role)) : null;\n\n for (let i = 1; i < tokens.length; i++) {\n const token = tokens[i];\n\n if (token.startsWith('+') || token.startsWith('~')) {\n const enabled = token.startsWith('+');\n const flagName = token.slice(1);\n if (!flagName) {\n if (collect) {\n diagnostics.push({\n severity: 'error',\n code: 'EMPTY_FLAG',\n message: `Empty flag name: \"${token}\"`,\n source: 'schema',\n });\n continue;\n }\n throw new Error(`Empty flag name: \"${token}\"`);\n }\n if (validRoleNames && !validRoleNames.has(flagName as SemanticRole)) {\n if (collect) {\n diagnostics.push({\n severity: 'error',\n code: 'UNKNOWN_FLAG',\n message: `Unknown flag \"${flagName}\" for command \"${command}\"`,\n source: 'schema',\n suggestions: [...validRoleNames].map(r => `+${r}`),\n });\n roles.set(flagName as SemanticRole, createFlag(flagName, enabled));\n continue;\n }\n throw new Error(\n `Unknown flag \"${flagName}\" for command \"${command}\". Valid roles: ${[...validRoleNames].join(', ')}`\n );\n }\n roles.set(flagName as SemanticRole, createFlag(flagName, enabled));\n continue;\n }\n\n const colonIndex = token.indexOf(':');\n\n if (colonIndex === -1) {\n if (collect) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_ROLE_FORMAT',\n message: `Invalid role format: \"${token}\". Expected role:value or +flag`,\n source: 'schema',\n });\n continue;\n }\n throw new Error(`Invalid role format: \"${token}\". Expected role:value or +flag`);\n }\n\n const roleName = token.slice(0, colonIndex);\n\n if (\n validRoleNames &&\n !STRUCTURAL_ROLES.has(roleName) &&\n !(LOOP_COMMANDS.has(command) && LOOP_ATTRIBUTES.has(roleName)) &&\n !validRoleNames.has(roleName as SemanticRole)\n ) {\n if (collect) {\n diagnostics.push({\n severity: 'error',\n code: 'UNKNOWN_ROLE',\n message: `Unknown role \"${roleName}\" for command \"${command}\"`,\n source: 'schema',\n suggestions: [...validRoleNames],\n });\n } else {\n throw new Error(\n `Unknown role \"${roleName}\" for command \"${command}\". Valid roles: ${[...validRoleNames].join(', ')}`\n );\n }\n }\n\n const role = roleName as SemanticRole;\n const valueStr = token.slice(colonIndex + 1);\n\n // In a structural role, `[...]` is always a nested command.\n if (STRUCTURAL_ROLES.has(roleName) && valueStr.startsWith('[')) {\n const nestedEnd = findMatchingBracket(token, colonIndex + 1);\n const nestedSyntax = token.slice(colonIndex + 1, nestedEnd + 1);\n roles.set(role, { type: 'expression', raw: nestedSyntax });\n continue;\n }\n\n const value = parseExplicitValue(valueStr, refSet);\n roles.set(role, value);\n }\n\n if (schema && command !== 'on') {\n for (const roleSpec of schema.roles) {\n if (roleSpec.required && !roles.has(roleSpec.role as SemanticRole) && !roleSpec.default) {\n if (collect) {\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_REQUIRED_ROLE',\n message: `Missing required role \"${roleSpec.role}\" for command \"${command}\": ${roleSpec.description}`,\n source: 'schema',\n });\n } else {\n throw new Error(\n `Missing required role \"${roleSpec.role}\" for command \"${command}\": ${roleSpec.description}`\n );\n }\n }\n }\n }\n\n const attachDiagnostics = (node: SemanticNode): SemanticNode => {\n if (diagnostics.length === 0) return node;\n return { ...node, diagnostics };\n };\n\n switch (command) {\n case 'on': {\n const eventValue = roles.get('event');\n if (!eventValue) {\n if (collect) {\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_EVENT_ROLE',\n message: 'Event handler requires event role: [on event:click ...]',\n source: 'schema',\n });\n return attachDiagnostics(\n createEventHandlerNode('on', roles, [], { sourceLanguage: 'explicit' })\n );\n }\n throw new Error('Event handler requires event role: [on event:click ...]');\n }\n const body = extractStructuralBody(roles, 'body', options);\n roles.delete('body' as SemanticRole);\n return attachDiagnostics(\n createEventHandlerNode('on', roles, body, { sourceLanguage: 'explicit' })\n );\n }\n\n case 'if': {\n const thenBranch = extractStructuralBody(roles, 'then', options);\n const elseBranch = extractStructuralBody(roles, 'else', options);\n roles.delete('then' as SemanticRole);\n roles.delete('else' as SemanticRole);\n return attachDiagnostics(\n createConditionalNode(\n command,\n roles,\n thenBranch,\n elseBranch.length > 0 ? elseBranch : undefined,\n { sourceLanguage: 'explicit' }\n )\n );\n }\n\n // `for item in …` is a loop too (the semantic parser builds `for` loops).\n case 'repeat':\n case 'for': {\n const loopBody = extractStructuralBody(roles, 'loop-body', options);\n const loopVariantValue = roles.get('loop-variant' as SemanticRole);\n const variableValue = roles.get('variable' as SemanticRole);\n const indexVariableValue = roles.get('index-variable' as SemanticRole);\n\n roles.delete('loop-body' as SemanticRole);\n roles.delete('loop-variant' as SemanticRole);\n roles.delete('variable' as SemanticRole);\n roles.delete('index-variable' as SemanticRole);\n\n let variant: LoopVariant;\n if (\n loopVariantValue &&\n loopVariantValue.type === 'literal' &&\n typeof loopVariantValue.value === 'string'\n ) {\n variant = loopVariantValue.value as LoopVariant;\n } else if (roles.has('quantity' as SemanticRole)) {\n variant = 'times';\n } else if (roles.has('source' as SemanticRole)) {\n variant = 'for';\n } else if (roles.has('condition' as SemanticRole)) {\n variant = 'while';\n } else {\n variant = 'forever';\n }\n\n const loopVariable =\n variableValue?.type === 'literal' && typeof variableValue.value === 'string'\n ? variableValue.value\n : variableValue?.type === 'expression'\n ? variableValue.raw\n : undefined;\n const indexVariable =\n indexVariableValue?.type === 'literal' && typeof indexVariableValue.value === 'string'\n ? indexVariableValue.value\n : undefined;\n\n return attachDiagnostics(\n createLoopNode(command, roles, variant, loopBody, loopVariable, indexVariable, {\n sourceLanguage: 'explicit',\n })\n );\n }\n\n default:\n return attachDiagnostics(createCommandNode(command, roles, { sourceLanguage: 'explicit' }));\n }\n}\n\nexport function isExplicitSyntax(input: string): boolean {\n const trimmed = input.trim();\n return trimmed.startsWith('[') && trimmed.endsWith(']');\n}\n\n// =============================================================================\n// Compound Statement Parsing\n// =============================================================================\n\nconst CHAIN_KEYWORDS = new Set(['then', 'and', 'async', 'sequential']);\n\nfunction splitCompoundSegments(input: string): {\n segments: string[];\n chainType: CompoundSemanticNode['chainType'] | null;\n} {\n const segments: string[] = [];\n let chainType: CompoundSemanticNode['chainType'] | null = null;\n let current = '';\n let bracketDepth = 0;\n let inString = false;\n let stringChar = '';\n\n for (let i = 0; i < input.length; i++) {\n const char = input[i];\n\n if (inString) {\n current += char;\n if (char === stringChar && input[i - 1] !== '\\\\') inString = false;\n continue;\n }\n\n if (char === '\"' || char === \"'\") {\n inString = true;\n stringChar = char;\n current += char;\n continue;\n }\n\n if (char === '[') {\n bracketDepth++;\n current += char;\n continue;\n }\n if (char === ']') {\n bracketDepth--;\n current += char;\n if (bracketDepth === 0) {\n const rest = input.slice(i + 1);\n const pipeMatch = rest.match(/^\\s*\\|>\\s*/);\n if (pipeMatch) {\n segments.push(current.trim());\n current = '';\n chainType ??= 'pipe';\n i += pipeMatch[0].length;\n continue;\n }\n const keywordMatch = rest.match(/^\\s+(then|and|async|sequential)\\s+/);\n if (keywordMatch && CHAIN_KEYWORDS.has(keywordMatch[1])) {\n segments.push(current.trim());\n current = '';\n chainType ??= keywordMatch[1] as CompoundSemanticNode['chainType'];\n i += keywordMatch[0].length;\n continue;\n }\n }\n continue;\n }\n\n current += char;\n }\n\n if (current.trim()) segments.push(current.trim());\n return { segments, chainType };\n}\n\nexport function parseCompound(input: string, options?: ParseExplicitOptions): SemanticNode {\n const trimmed = input.trim();\n const { annotations, remainder } = extractAnnotations(trimmed);\n const { segments, chainType } = splitCompoundSegments(remainder);\n\n let node: SemanticNode;\n if (segments.length <= 1) {\n node = parseExplicit(segments[0] || remainder, options);\n } else {\n const statements = segments.map(seg => parseExplicit(seg, options));\n node = createCompoundNode(statements, chainType ?? 'sequential');\n }\n\n if (annotations.length > 0) return { ...node, annotations };\n return node;\n}\n\n// =============================================================================\n// Annotation Parsing\n// =============================================================================\n\nfunction extractAnnotations(input: string): { annotations: Annotation[]; remainder: string } {\n const annotations: Annotation[] = [];\n let pos = 0;\n\n while (pos < input.length) {\n while (pos < input.length && (input[pos] === ' ' || input[pos] === '\\t')) pos++;\n if (pos >= input.length || input[pos] !== '@') break;\n\n pos++;\n const nameStart = pos;\n while (pos < input.length && /[A-Za-z0-9\\-_]/.test(input[pos])) pos++;\n const name = input.slice(nameStart, pos);\n if (!name) break;\n\n let value: string | undefined;\n if (pos < input.length && input[pos] === '(') {\n pos++;\n const valueStart = pos;\n if (pos < input.length && (input[pos] === '\"' || input[pos] === \"'\")) {\n const quote = input[pos];\n pos++;\n while (pos < input.length && input[pos] !== quote) {\n if (input[pos] === '\\\\') pos++;\n pos++;\n }\n pos++;\n value = input.slice(valueStart + 1, pos - 1);\n } else {\n while (pos < input.length && input[pos] !== ')') pos++;\n value = input.slice(valueStart, pos);\n }\n if (pos < input.length && input[pos] === ')') pos++;\n }\n\n annotations.push(value !== undefined ? { name, value } : { name });\n }\n\n return { annotations, remainder: input.slice(pos).trim() };\n}\n\n// =============================================================================\n// Document Parsing\n// =============================================================================\n\nconst VERSION_HEADER_RE = /^#!lse\\s+(\\d+\\.\\d+(?:\\.\\d+)?)\\s*$/;\n\nexport function parseDocument(input: string, options?: ParseExplicitOptions): LSEEnvelope {\n const lines = input.split('\\n');\n let lseVersion = '1.0';\n const nodes: SemanticNode[] = [];\n let startLine = 0;\n\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i].trim();\n if (!trimmed) continue;\n const versionMatch = trimmed.match(VERSION_HEADER_RE);\n if (versionMatch) {\n lseVersion = versionMatch[1];\n startLine = i + 1;\n }\n break;\n }\n\n for (let i = startLine; i < lines.length; i++) {\n const trimmed = lines[i].trim();\n if (!trimmed) continue;\n if (trimmed.startsWith('//') || (trimmed.startsWith('#') && !trimmed.startsWith('#!')))\n continue;\n nodes.push(parseCompound(trimmed, options));\n }\n\n return { lseVersion, nodes };\n}\n\n// =============================================================================\n// Detection Helpers\n// =============================================================================\n\nexport function isCompoundSyntax(input: string): boolean {\n const { segments } = splitCompoundSegments(input.trim());\n return segments.length > 1;\n}\n\nexport function isDocumentSyntax(input: string): boolean {\n const trimmed = input.trim();\n if (trimmed.includes('\\n')) return true;\n if (VERSION_HEADER_RE.test(trimmed)) return true;\n if (trimmed.startsWith('//') || (trimmed.startsWith('#') && !trimmed.startsWith('#!')))\n return true;\n return false;\n}\n\n// =============================================================================\n// Internal Helpers\n// =============================================================================\n\nfunction extractStructuralBody(\n roles: Map<SemanticRole, SemanticValue>,\n roleName: string,\n options: ParseExplicitOptions\n): SemanticNode[] {\n const value = roles.get(roleName as SemanticRole);\n if (!value) return [];\n if (value.type === 'expression') return [parseExplicit(value.raw, options)];\n return [];\n}\n\n/** Whether a combinator appears outside any quoted span. */\nfunction hasUnquotedCombinator(value: string): boolean {\n let quote = '';\n for (const ch of value) {\n if (quote) {\n if (ch === quote) quote = '';\n continue;\n }\n if (ch === '\"' || ch === \"'\") {\n quote = ch;\n continue;\n }\n if (ch === ' ' || ch === '>' || ch === '+' || ch === '~' || ch === ',') return true;\n }\n return false;\n}\n\n/**\n * Infers a selector's kind. Combinators are tested first, so `.a > .b` is\n * `complex` rather than `class`. A combinator inside a quoted span does not\n * count, so `[aria-label=\"Close menu\"]` is `attribute`.\n */\nfunction inferSelectorKind(\n value: string\n): 'id' | 'class' | 'attribute' | 'element' | 'complex' | undefined {\n if (!value) return undefined;\n if (hasUnquotedCombinator(value)) return 'complex';\n if (value.startsWith('#')) return 'id';\n if (value.startsWith('.')) return 'class';\n if (value.startsWith('[')) return 'attribute';\n if (value.startsWith('*')) return 'element';\n return undefined;\n}\n\n/** Expands the escape sequences defined by the grammar: \\\\ \\\" \\' \\n \\r \\t */\nfunction unescapeString(s: string): string {\n if (!s.includes('\\\\')) return s;\n\n let out = '';\n for (let i = 0; i < s.length; i++) {\n if (s[i] === '\\\\' && i + 1 < s.length) {\n const next = s[i + 1];\n const mapped =\n next === '\\\\'\n ? '\\\\'\n : next === '\"'\n ? '\"'\n : next === \"'\"\n ? \"'\"\n : next === 'n'\n ? '\\n'\n : next === 'r'\n ? '\\r'\n : next === 't'\n ? '\\t'\n : '';\n if (mapped) {\n out += mapped;\n i++;\n continue;\n }\n }\n out += s[i];\n }\n return out;\n}\n\n/**\n * Splits on spaces at bracket-depth 0, outside quoted strings, and outside\n * selector literals (`<...selector.../>`). The branch order is normative: the\n * string check precedes the selector check, so a `/>` inside a quoted string does\n * not close the literal. A `<` opens a selector literal only at the start of a\n * value — when the token buffer is empty or ends with `:`.\n */\nfunction tokenizeExplicit(content: string): string[] {\n const tokens: string[] = [];\n let current = '';\n let inString = false;\n let stringChar = '';\n let inSelector = false;\n let bracketDepth = 0;\n\n for (let i = 0; i < content.length; i++) {\n const char = content[i];\n if (inString) {\n current += char;\n if (char === stringChar) {\n // Even backslash count means the quote closes the string.\n let backslashes = 0;\n for (let j = i - 1; j >= 0 && content[j] === '\\\\'; j--) backslashes++;\n if (backslashes % 2 === 0) inString = false;\n }\n continue;\n }\n if (char === '\"' || char === \"'\") {\n inString = true;\n stringChar = char;\n current += char;\n continue;\n }\n if (inSelector) {\n current += char;\n if (char === '>' && current.endsWith('/>')) inSelector = false;\n continue;\n }\n if (char === '<' && (current === '' || current.endsWith(':'))) {\n inSelector = true;\n current += char;\n continue;\n }\n if (char === '[') {\n bracketDepth++;\n current += char;\n continue;\n }\n if (char === ']') {\n bracketDepth--;\n current += char;\n continue;\n }\n if (char === ' ' && bracketDepth === 0) {\n if (current) {\n tokens.push(current);\n current = '';\n }\n continue;\n }\n current += char;\n }\n\n if (current) tokens.push(current);\n return tokens;\n}\n\nfunction parseExplicitValue(valueStr: string, referenceSet: ReadonlySet<string>): SemanticValue {\n // Selector literal — the delimiters force selector interpretation.\n if (valueStr.startsWith('<')) {\n if (!valueStr.endsWith('/>')) {\n throw new Error(`Unterminated selector literal: \"${valueStr}\". Expected a closing \"/>\"`);\n }\n const inner = valueStr.slice(1, -2);\n if (!inner) throw new Error(`Empty selector literal: \"${valueStr}\"`);\n return createSelector(inner, inferSelectorKind(inner));\n }\n if (\n valueStr.startsWith('#') ||\n valueStr.startsWith('.') ||\n valueStr.startsWith('[') ||\n valueStr.startsWith('@') ||\n valueStr.startsWith('*')\n ) {\n return createSelector(valueStr, inferSelectorKind(valueStr));\n }\n if (valueStr.startsWith('\"') || valueStr.startsWith(\"'\")) {\n return createLiteral(unescapeString(valueStr.slice(1, -1)), 'string');\n }\n if (valueStr === 'true') return createLiteral(true, 'boolean');\n if (valueStr === 'false') return createLiteral(false, 'boolean');\n const lowerRef = valueStr.toLowerCase();\n if (isValidReference(lowerRef, referenceSet)) return createReference(lowerRef);\n const numMatch = valueStr.match(/^(-?\\d+(?:\\.\\d+)?)(ms|s|m|h)?$/);\n if (numMatch) {\n const num = parseFloat(numMatch[1]);\n const suffix = numMatch[2];\n if (suffix) return createLiteral(valueStr, 'duration');\n return createLiteral(num, 'number');\n }\n return createLiteral(valueStr);\n}\n\nfunction findMatchingBracket(str: string, start: number): number {\n let depth = 0;\n for (let i = start; i < str.length; i++) {\n if (str[i] === '[') depth++;\n else if (str[i] === ']') {\n depth--;\n if (depth === 0) return i;\n }\n }\n return str.length - 1;\n}\n","/**\n * Explicit Syntax Renderer\n *\n * Serializes SemanticNode to the universal [command role:value ...] bracket syntax.\n * Zero dependencies beyond core types — no language-specific logic.\n */\n\nimport type {\n SemanticNode,\n SemanticValue,\n CommandSemanticNode,\n CompoundSemanticNode,\n EventHandlerSemanticNode,\n ConditionalSemanticNode,\n LoopSemanticNode,\n LSEEnvelope,\n} from '../types';\n\n/**\n * Render a semantic node as explicit bracket syntax.\n *\n * @example\n * ```typescript\n * renderExplicit(node) // \"[toggle patient:.active destination:#button]\"\n * renderExplicit(annotatedNode) // \"@timeout(5s) [fetch source:\\\"/api/users\\\"]\"\n * ```\n */\nexport function renderExplicit(node: SemanticNode): string {\n let result: string;\n\n if (node.kind === 'compound') {\n const compoundNode = node as CompoundSemanticNode;\n result = compoundNode.statements\n .map(stmt => renderExplicit(stmt))\n .join(` ${compoundNode.chainType} `);\n } else {\n result = renderBracketCommand(node);\n }\n\n if (node.annotations && node.annotations.length > 0) {\n const annParts = node.annotations.map(ann =>\n ann.value !== undefined ? `@${ann.name}(${ann.value})` : `@${ann.name}`\n );\n return annParts.join(' ') + ' ' + result;\n }\n\n return result;\n}\n\n/**\n * Render an LSEEnvelope as a multi-line document string.\n */\nexport function renderDocument(envelope: LSEEnvelope): string {\n const lines: string[] = [];\n if (envelope.lseVersion && envelope.lseVersion !== '1.0') {\n lines.push(`#!lse ${envelope.lseVersion}`);\n }\n for (const node of envelope.nodes) {\n lines.push(renderExplicit(node));\n }\n return lines.join('\\n');\n}\n\n// =============================================================================\n// Internal Helpers\n// =============================================================================\n\nfunction renderBracketCommand(node: SemanticNode): string {\n const parts: string[] = [node.action];\n\n for (const [role, value] of node.roles) {\n if (value.type === 'flag') {\n parts.push(value.enabled ? `+${value.name}` : `~${value.name}`);\n } else {\n parts.push(`${role}:${valueToString(value)}`);\n }\n }\n\n if (node.kind === 'event-handler') {\n const eventNode = node as EventHandlerSemanticNode;\n if (eventNode.body && eventNode.body.length > 0) {\n parts.push(`body:${eventNode.body.map(n => renderExplicit(n)).join(' ')}`);\n }\n }\n\n if (node.kind === 'conditional') {\n const condNode = node as ConditionalSemanticNode;\n if (condNode.thenBranch && condNode.thenBranch.length > 0) {\n parts.push(`then:${condNode.thenBranch.map(n => renderExplicit(n)).join(' ')}`);\n }\n if (condNode.elseBranch && condNode.elseBranch.length > 0) {\n parts.push(`else:${condNode.elseBranch.map(n => renderExplicit(n)).join(' ')}`);\n }\n }\n\n if (node.kind === 'loop') {\n const loopNode = node as LoopSemanticNode;\n if (loopNode.loopVariant) parts.push(`loop-variant:${loopNode.loopVariant}`);\n if (loopNode.loopVariable) parts.push(`variable:${loopNode.loopVariable}`);\n if (loopNode.indexVariable) parts.push(`index-variable:${loopNode.indexVariable}`);\n if (loopNode.body && loopNode.body.length > 0) {\n parts.push(`loop-body:${loopNode.body.map(n => renderExplicit(n)).join(' ')}`);\n }\n }\n\n if (node.kind === 'command') {\n const cmd = node as CommandSemanticNode;\n if (cmd.body && cmd.body.length > 0)\n parts.push(`body:${cmd.body.map(n => renderExplicit(n)).join(' ')}`);\n if (cmd.catchBranch && cmd.catchBranch.length > 0)\n parts.push(`catch:${cmd.catchBranch.map(n => renderExplicit(n)).join(' ')}`);\n if (cmd.finallyBranch && cmd.finallyBranch.length > 0)\n parts.push(`finally:${cmd.finallyBranch.map(n => renderExplicit(n)).join(' ')}`);\n if (cmd.asyncVariant) parts.push(`async-variant:${cmd.asyncVariant}`);\n if (cmd.asyncBody && cmd.asyncBody.length > 0)\n parts.push(`async-body:${cmd.asyncBody.map(n => renderExplicit(n)).join(' ')}`);\n }\n\n return `[${parts.join(' ')}]`;\n}\n\n/**\n * Whether a selector must be wrapped in `<.../>` to survive a re-parse.\n *\n * A bare token re-classifies as a selector only if it starts with one of\n * `# . @ *`. A space would split the token, and a leading `[` in a structural\n * role would re-parse as a nested command — so both must wrap.\n */\nfunction needsSelectorWrap(value: string): boolean {\n if (value.includes(' ')) return true;\n const first = value[0];\n return !(first === '#' || first === '.' || first === '@' || first === '*');\n}\n\nfunction valueToString(value: SemanticValue): string {\n switch (value.type) {\n case 'literal':\n if (typeof value.value === 'string') {\n // JSON.stringify escapes backslashes and quotes; a raw `\"${v}\"` would emit\n // an unparseable literal for a value containing a quote.\n if (value.dataType === 'string' || /\\s/.test(value.value))\n return JSON.stringify(value.value);\n return value.value;\n }\n return String(value.value);\n case 'selector':\n if (!value.value) return value.value;\n return needsSelectorWrap(value.value) ? `<${value.value}/>` : value.value;\n case 'reference':\n return value.value;\n case 'property-path':\n return `${valueToString(value.object)}'s ${value.property}`;\n case 'expression':\n return value.raw;\n case 'flag':\n return value.name;\n }\n}\n","/**\n * Protocol JSON Serialization\n *\n * Converts between TypeScript SemanticNode (internal representation) and\n * the protocol wire format defined in protocol/spec/wire-format.md.\n *\n * Use toProtocolJSON() to serialize for tool-to-tool interchange.\n * Use fromProtocolJSON() to deserialize from protocol-conformant sources.\n */\n\nimport type {\n SemanticNode,\n SemanticValue,\n SemanticRole,\n CommandSemanticNode,\n EventHandlerSemanticNode,\n CompoundSemanticNode,\n ConditionalSemanticNode,\n LoopSemanticNode,\n LoopVariant,\n AsyncVariant,\n LSEEnvelope,\n} from '../types';\nimport {\n createCommandNode,\n createEventHandlerNode,\n createCompoundNode,\n createConditionalNode,\n createLoopNode,\n createTryNode,\n createAsyncNode,\n createMatchNode,\n createSelector,\n createLiteral,\n createReference,\n createExpression,\n createFlag,\n extractValue,\n} from '../types';\nimport type {\n ProtocolNodeJSON,\n ProtocolValueJSON,\n ProtocolDiagnosticJSON,\n ProtocolChainType,\n IRDiagnostic,\n LSEEnvelopeJSON,\n} from './types';\n\n// =============================================================================\n// Serialization: SemanticNode → ProtocolNodeJSON\n// =============================================================================\n\nexport function toProtocolJSON(node: SemanticNode): ProtocolNodeJSON {\n const roles = rolesToProtocol(node.roles);\n let result: ProtocolNodeJSON;\n\n switch (node.kind) {\n case 'command': {\n const cmd = node as CommandSemanticNode;\n result = { kind: 'command', action: node.action, roles };\n if (cmd.body && cmd.body.length > 0) result.body = cmd.body.map(toProtocolJSON);\n if (cmd.catchBranch && cmd.catchBranch.length > 0)\n result.catchBranch = cmd.catchBranch.map(toProtocolJSON);\n if (cmd.finallyBranch && cmd.finallyBranch.length > 0)\n result.finallyBranch = cmd.finallyBranch.map(toProtocolJSON);\n if (cmd.asyncVariant) result.asyncVariant = cmd.asyncVariant;\n if (cmd.asyncBody && cmd.asyncBody.length > 0)\n result.asyncBody = cmd.asyncBody.map(toProtocolJSON);\n if (cmd.arms && cmd.arms.length > 0) {\n result.arms = cmd.arms.map(arm => ({\n pattern: valueToProtocol(arm.pattern),\n body: arm.body.map(toProtocolJSON),\n }));\n }\n if (cmd.defaultArm && cmd.defaultArm.length > 0)\n result.defaultArm = cmd.defaultArm.map(toProtocolJSON);\n break;\n }\n case 'event-handler': {\n const eh = node as EventHandlerSemanticNode;\n\n // Emit compact form with `trigger` sugar when it's lossless. The compact\n // form is `{action: bodyAction, roles: bodyRoles, trigger: {event, modifiers?}}`\n // and deserializes back to an identical event-handler node via the\n // `if (json.trigger)` branch in fromProtocolJSON. The compact form is\n // also the only form the runtime dispatch path currently executes\n // correctly for most commands — see examples/llm-native-todo-demo.\n if (canEmitCompactTrigger(eh)) {\n const body = eh.body[0] as CommandSemanticNode;\n const eventValue = eh.roles.get('event' as SemanticRole);\n // Safe because canEmitCompactTrigger verified type + value.\n const eventName = (eventValue as { value: string }).value;\n\n const trigger: { event: string; modifiers?: Record<string, unknown> } = {\n event: eventName,\n };\n const triggerModifiers = serializeEventModifiers(eh.eventModifiers);\n if (triggerModifiers) trigger.modifiers = triggerModifiers;\n\n result = {\n action: body.action,\n roles: rolesToProtocol(body.roles),\n trigger,\n };\n break;\n }\n\n // Fallback: verbose form (multi-command body, nested control flow,\n // additional events, parameter names, annotations on body, etc.)\n result = {\n kind: 'event-handler',\n action: node.action,\n roles,\n body: eh.body.map(toProtocolJSON),\n };\n break;\n }\n case 'compound': {\n const c = node as CompoundSemanticNode;\n result = {\n kind: 'compound',\n action: 'compound',\n statements: c.statements.map(toProtocolJSON),\n chainType: c.chainType,\n } as ProtocolNodeJSON;\n break;\n }\n case 'conditional': {\n const cond = node as ConditionalSemanticNode;\n result = { kind: 'command', action: node.action, roles };\n result.thenBranch = cond.thenBranch.map(toProtocolJSON);\n if (cond.elseBranch && cond.elseBranch.length > 0)\n result.elseBranch = cond.elseBranch.map(toProtocolJSON);\n break;\n }\n case 'loop': {\n const loop = node as LoopSemanticNode;\n result = { kind: 'command', action: node.action, roles };\n result.loopVariant = loop.loopVariant;\n result.loopBody = loop.body.map(toProtocolJSON);\n if (loop.loopVariable) result.loopVariable = loop.loopVariable;\n if (loop.indexVariable) result.indexVariable = loop.indexVariable;\n break;\n }\n }\n\n if (node.annotations && node.annotations.length > 0) {\n result.annotations = node.annotations.map(a =>\n a.value !== undefined ? { name: a.name, value: a.value } : { name: a.name }\n );\n }\n\n if (node.diagnostics && node.diagnostics.length > 0) {\n result.diagnostics = node.diagnostics.map(d => {\n const json: ProtocolDiagnosticJSON = { level: d.severity, message: d.message };\n // `role` is part of the wire format's Diagnostic shape; dropping it here\n // silently lost which role a type-constraint diagnostic was about.\n if (d.role) json.role = d.role;\n if (d.code) json.code = d.code;\n if (d.source) json.source = d.source;\n if (d.suggestions && d.suggestions.length > 0) json.suggestions = [...d.suggestions];\n return json;\n });\n }\n\n return result;\n}\n\nfunction rolesToProtocol(\n roles: ReadonlyMap<SemanticRole, SemanticValue>\n): Record<string, ProtocolValueJSON> {\n const result: Record<string, ProtocolValueJSON> = {};\n for (const [role, value] of roles) result[role] = valueToProtocol(value);\n return result;\n}\n\nfunction valueToProtocol(value: SemanticValue): ProtocolValueJSON {\n switch (value.type) {\n case 'selector': {\n const PROTOCOL_SELECTOR_KINDS = new Set([\n 'id',\n 'class',\n 'attribute',\n 'element',\n 'complex',\n ] as const);\n const sk = 'selectorKind' in value ? value.selectorKind : undefined;\n const sv: ProtocolValueJSON = { type: 'selector', value: value.value };\n if (sk && PROTOCOL_SELECTOR_KINDS.has(sk as 'id'))\n sv.selectorKind = sk as 'id' | 'class' | 'attribute' | 'element' | 'complex';\n return sv;\n }\n case 'literal':\n return value.dataType\n ? { type: 'literal', value: value.value, dataType: value.dataType }\n : { type: 'literal', value: value.value };\n case 'reference':\n return { type: 'reference', value: value.value };\n case 'expression':\n return { type: 'expression', raw: value.raw };\n case 'property-path':\n return { type: 'expression', raw: extractValue(value) };\n case 'flag':\n return { type: 'flag', name: value.name, enabled: value.enabled };\n }\n}\n\n/**\n * Decide whether an event-handler node can be emitted in the compact\n * `{action, roles, trigger}` form without losing information.\n *\n * The compact form is lossless when the event-handler wraps exactly one plain\n * command, uses a simple literal event name, and carries no metadata that\n * compact form can't represent (body-level annotations, additional events,\n * parameter names, etc.). Round-trip: `fromProtocolJSON(compact)` rebuilds an\n * equivalent event-handler via the `if (json.trigger)` branch.\n */\nfunction canEmitCompactTrigger(eh: EventHandlerSemanticNode): boolean {\n // Must wrap exactly one body node and that node must be a plain command.\n if (!eh.body || eh.body.length !== 1) return false;\n const body = eh.body[0];\n if (body.kind !== 'command') return false;\n\n // The body command must not carry its own control flow or v1.2 extensions.\n const cmd = body as CommandSemanticNode;\n if (cmd.body && cmd.body.length > 0) return false;\n if (cmd.catchBranch && cmd.catchBranch.length > 0) return false;\n if (cmd.finallyBranch && cmd.finallyBranch.length > 0) return false;\n if (cmd.asyncVariant) return false;\n if (cmd.asyncBody && cmd.asyncBody.length > 0) return false;\n if (cmd.arms && cmd.arms.length > 0) return false;\n if (cmd.defaultArm && cmd.defaultArm.length > 0) return false;\n\n // Event handler must expose a simple literal string event name.\n const eventValue = eh.roles.get('event' as SemanticRole);\n if (!eventValue || eventValue.type !== 'literal') return false;\n if (typeof (eventValue as { value: unknown }).value !== 'string') return false;\n\n // The body command must NOT have its own `event` role — in compact form\n // `roles.event` is reserved for the trigger and would collide on re-parse.\n if (cmd.roles.has('event' as SemanticRole)) return false;\n\n // Event handler must not carry features compact form cannot represent.\n if (eh.additionalEvents && eh.additionalEvents.length > 0) return false;\n if (eh.parameterNames && eh.parameterNames.length > 0) return false;\n\n // Annotations/diagnostics: compact form collapses event-handler and body\n // command into one node, so only EH-level annotations round-trip. If either\n // the EH or the body command carries annotations/diagnostics, fall back\n // to verbose form to preserve them losslessly.\n if (eh.annotations && eh.annotations.length > 0) return false;\n if (eh.diagnostics && eh.diagnostics.length > 0) return false;\n if (cmd.annotations && cmd.annotations.length > 0) return false;\n if (cmd.diagnostics && cmd.diagnostics.length > 0) return false;\n\n return true;\n}\n\n/**\n * Serialize EventModifiers to the shape expected by `trigger.modifiers` in the\n * wire format. Returns undefined when there are no modifiers, so the caller\n * can omit the field entirely.\n *\n * `from` is a SemanticValue reference and is not round-trip-safe through the\n * trigger sugar path; if present, it signals that canEmitCompactTrigger\n * should have rejected the node. We still emit the simple keys here so\n * partial information survives if the caller bypasses the guard.\n */\nfunction serializeEventModifiers(\n mods: EventHandlerSemanticNode['eventModifiers']\n): Record<string, unknown> | undefined {\n if (!mods) return undefined;\n const out: Record<string, unknown> = {};\n if (mods.once) out.once = true;\n if (typeof mods.debounce === 'number') out.debounce = mods.debounce;\n if (typeof mods.throttle === 'number') out.throttle = mods.throttle;\n if (mods.queue) out.queue = mods.queue;\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\n// =============================================================================\n// Deserialization: ProtocolNodeJSON → SemanticNode\n// =============================================================================\n\nexport function fromProtocolJSON(json: ProtocolNodeJSON): SemanticNode {\n const roles = new Map<SemanticRole, SemanticValue>(\n Object.entries(json.roles ?? {}).map(([role, val]) => [role, valueFromProtocol(val)])\n );\n\n const annotations = json.annotations?.map(a =>\n a.value !== undefined ? { name: a.name, value: a.value } : { name: a.name }\n );\n const diagnostics = json.diagnostics?.map(d => ({\n severity: d.level as 'error' | 'warning' | 'info',\n message: d.message,\n ...(d.role ? { role: d.role } : {}),\n ...(d.code ? { code: d.code } : {}),\n ...(d.source ? { source: d.source } : {}),\n ...(d.suggestions && d.suggestions.length > 0 ? { suggestions: d.suggestions } : {}),\n }));\n\n if (json.trigger) {\n const eventName = json.trigger.event;\n roles.set('event' as SemanticRole, createLiteral(eventName, 'string'));\n const bodyRoles = new Map(roles);\n bodyRoles.delete('event' as SemanticRole);\n const bodyNode = createCommandNode(json.action, bodyRoles);\n const node = createEventHandlerNode(\n 'on',\n roles,\n [bodyNode],\n { sourceLanguage: 'json' },\n (json.trigger.modifiers as Record<string, unknown> | undefined) ?? {}\n );\n return applyV12Metadata(node, annotations, diagnostics);\n }\n\n const kind = json.kind ?? 'command';\n\n if (kind === 'event-handler') {\n const body = (json.body ?? []).map(fromProtocolJSON);\n return applyV12Metadata(\n createEventHandlerNode(json.action, roles, body),\n annotations,\n diagnostics\n );\n }\n\n if (kind === 'compound') {\n const statements = (json.statements ?? []).map(fromProtocolJSON);\n return applyV12Metadata(\n createCompoundNode(statements, (json.chainType ?? 'sequential') as ProtocolChainType),\n annotations,\n diagnostics\n );\n }\n\n if (json.thenBranch && json.thenBranch.length > 0) {\n const thenBranch = json.thenBranch.map(fromProtocolJSON);\n const elseBranch = json.elseBranch ? json.elseBranch.map(fromProtocolJSON) : undefined;\n return applyV12Metadata(\n createConditionalNode(json.action, roles, thenBranch, elseBranch),\n annotations,\n diagnostics\n );\n }\n\n if (json.loopVariant) {\n const loopBody = (json.loopBody ?? []).map(fromProtocolJSON);\n return applyV12Metadata(\n createLoopNode(\n json.action,\n roles,\n json.loopVariant as LoopVariant,\n loopBody,\n json.loopVariable,\n json.indexVariable\n ),\n annotations,\n diagnostics\n );\n }\n\n if (json.catchBranch || json.finallyBranch) {\n const body = (json.body ?? []).map(fromProtocolJSON);\n const catchBranch = json.catchBranch ? json.catchBranch.map(fromProtocolJSON) : undefined;\n const finallyBranch = json.finallyBranch ? json.finallyBranch.map(fromProtocolJSON) : undefined;\n return applyV12Metadata(\n createTryNode(body, catchBranch, finallyBranch),\n annotations,\n diagnostics\n );\n }\n\n if (json.asyncVariant) {\n const asyncBody = (json.asyncBody ?? []).map(fromProtocolJSON);\n return applyV12Metadata(\n createAsyncNode(json.asyncVariant as AsyncVariant, asyncBody),\n annotations,\n diagnostics\n );\n }\n\n if (json.arms && json.arms.length > 0) {\n const arms = json.arms.map(arm => ({\n pattern: valueFromProtocol(arm.pattern),\n body: arm.body.map(fromProtocolJSON),\n }));\n const defaultArm = json.defaultArm ? json.defaultArm.map(fromProtocolJSON) : undefined;\n return applyV12Metadata(createMatchNode(roles, arms, defaultArm), annotations, diagnostics);\n }\n\n if (json.body && json.body.length > 0 && kind === 'command') {\n return applyV12Metadata(\n createTryNode(json.body.map(fromProtocolJSON)),\n annotations,\n diagnostics\n );\n }\n\n return applyV12Metadata(createCommandNode(json.action, roles), annotations, diagnostics);\n}\n\nfunction applyV12Metadata(\n node: SemanticNode,\n annotations: Array<{ name: string; value?: string }> | undefined,\n diagnostics:\n | Array<{\n severity: 'error' | 'warning' | 'info';\n message: string;\n code?: string;\n source?: string;\n suggestions?: string[];\n }>\n | undefined\n): SemanticNode {\n if ((!annotations || annotations.length === 0) && (!diagnostics || diagnostics.length === 0))\n return node;\n return {\n ...node,\n ...(annotations && annotations.length > 0 ? { annotations } : {}),\n ...(diagnostics && diagnostics.length > 0 ? { diagnostics } : {}),\n };\n}\n\nfunction valueFromProtocol(value: ProtocolValueJSON): SemanticValue {\n switch (value.type) {\n case 'selector':\n return createSelector(String(value.value ?? ''), value.selectorKind);\n case 'literal':\n return createLiteral(\n value.value !== undefined ? value.value : '',\n value.dataType as 'string' | 'number' | 'boolean' | 'duration' | undefined\n );\n case 'reference':\n return createReference(String(value.value ?? ''));\n case 'expression':\n return createExpression(value.raw ?? String(value.value ?? ''));\n case 'flag':\n return createFlag(\n value.name ?? String(value.value ?? ''),\n value.enabled ?? (typeof value.value === 'boolean' ? value.value : true)\n );\n case 'property-path':\n return createExpression(String(value.value ?? value.raw ?? ''));\n }\n}\n\n// =============================================================================\n// Envelope (v1.2)\n// =============================================================================\n\nexport function toEnvelopeJSON(envelope: LSEEnvelope): LSEEnvelopeJSON {\n const result: LSEEnvelopeJSON = {\n lseVersion: envelope.lseVersion,\n nodes: envelope.nodes.map(toProtocolJSON),\n };\n if (envelope.features && envelope.features.length > 0) result.features = [...envelope.features];\n return result;\n}\n\nexport function fromEnvelopeJSON(json: LSEEnvelopeJSON): LSEEnvelope {\n return {\n lseVersion: json.lseVersion,\n ...(json.features && json.features.length > 0 ? { features: json.features } : {}),\n nodes: json.nodes.map(fromProtocolJSON),\n };\n}\n\nexport function isEnvelope(json: unknown): json is LSEEnvelopeJSON {\n return (\n typeof json === 'object' &&\n json !== null &&\n 'lseVersion' in json &&\n 'nodes' in json &&\n Array.isArray((json as Record<string, unknown>).nodes)\n );\n}\n\n// =============================================================================\n// Validation\n// =============================================================================\n\nconst VALID_KINDS = new Set(['command', 'event-handler', 'compound']);\nconst VALID_VALUE_TYPES = new Set([\n 'selector',\n 'literal',\n 'reference',\n 'expression',\n 'property-path',\n 'flag',\n]);\nconst VALID_CHAIN_TYPES = new Set(['then', 'and', 'async', 'sequential', 'pipe']);\nconst VALID_ASYNC_VARIANTS = new Set(['all', 'race']);\nconst VALID_DIAGNOSTIC_LEVELS = new Set(['error', 'warning', 'info']);\n\nexport function validateProtocolJSON(json: unknown): IRDiagnostic[] {\n const diagnostics: IRDiagnostic[] = [];\n\n if (typeof json !== 'object' || json === null) {\n return [{ severity: 'error', code: 'INVALID_ROOT', message: 'Root must be a JSON object' }];\n }\n\n const node = json as Record<string, unknown>;\n const kind = 'kind' in node ? String(node.kind) : 'command';\n\n if ('kind' in node && !VALID_KINDS.has(kind)) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_KIND',\n message: `Invalid kind \"${node.kind}\". Must be one of: command, event-handler, compound`,\n });\n }\n\n if (!('action' in node)) {\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_ACTION',\n message: 'Missing required field: action',\n });\n } else if (typeof node.action !== 'string' || node.action.length === 0) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_ACTION',\n message: 'action must be a non-empty string',\n });\n }\n\n if (!('roles' in node) && kind !== 'compound') {\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_ROLES',\n message: 'Missing required field: roles',\n });\n } else if (\n 'roles' in node &&\n (typeof node.roles !== 'object' || node.roles === null || Array.isArray(node.roles))\n ) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_ROLES',\n message: 'roles must be a plain object',\n });\n } else if ('roles' in node && node.roles) {\n for (const [role, value] of Object.entries(node.roles as Record<string, unknown>)) {\n if (typeof value !== 'object' || value === null) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_ROLE_VALUE',\n message: `Role \"${role}\" value must be an object`,\n });\n continue;\n }\n const v = value as Record<string, unknown>;\n if (!('type' in v) || !VALID_VALUE_TYPES.has(String(v.type))) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_VALUE_TYPE',\n message: `Role \"${role}\" has invalid type \"${v.type}\"`,\n });\n }\n if (v.type === 'flag') {\n if (typeof v.name !== 'string' && !('value' in v))\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_FLAG_NAME',\n message: `Flag role \"${role}\" missing required field: name (or value)`,\n });\n if (typeof v.enabled !== 'boolean' && !('value' in v))\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_FLAG_ENABLED',\n message: `Flag role \"${role}\" missing required field: enabled (or value)`,\n });\n }\n if (v.type === 'expression' && typeof v.raw !== 'string' && !('value' in v)) {\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_EXPRESSION_RAW',\n message: `Expression role \"${role}\" missing required field: raw (or value)`,\n });\n }\n }\n }\n\n if ('trigger' in node && node.trigger) {\n const trigger = node.trigger as Record<string, unknown>;\n if (typeof trigger !== 'object' || trigger === null) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_TRIGGER',\n message: 'trigger must be an object',\n });\n } else if (!trigger.event || typeof trigger.event !== 'string') {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_TRIGGER',\n message: 'trigger.event is required and must be a non-empty string',\n });\n }\n }\n\n if (kind === 'event-handler' && !('trigger' in node)) {\n if (!('body' in node) || !Array.isArray(node.body)) {\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_BODY',\n message: 'event-handler node requires a body array',\n });\n }\n }\n\n if (kind === 'compound') {\n if (!('statements' in node) || !Array.isArray(node.statements)) {\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_STATEMENTS',\n message: 'compound node requires a statements array',\n });\n }\n if ('chainType' in node && !VALID_CHAIN_TYPES.has(String(node.chainType))) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_CHAIN_TYPE',\n message: `Invalid chainType \"${node.chainType}\"`,\n });\n }\n }\n\n if ('diagnostics' in node && !Array.isArray(node.diagnostics)) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_DIAGNOSTICS',\n message: 'diagnostics must be an array',\n });\n }\n\n if ('annotations' in node) {\n if (!Array.isArray(node.annotations)) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_ANNOTATIONS',\n message: 'annotations must be an array',\n });\n } else {\n for (let i = 0; i < (node.annotations as unknown[]).length; i++) {\n const a = (node.annotations as unknown[])[i] as Record<string, unknown> | null;\n if (!a || typeof a !== 'object') {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_ANNOTATION_ENTRY',\n message: `annotations[${i}] must be an object`,\n });\n continue;\n }\n if (typeof a.name !== 'string' || a.name.length === 0)\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_ANNOTATION_NAME',\n message: `annotations[${i}] missing required field: name`,\n });\n }\n }\n }\n\n if ('asyncVariant' in node && !VALID_ASYNC_VARIANTS.has(String(node.asyncVariant))) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_ASYNC_VARIANT',\n message: `Invalid asyncVariant \"${node.asyncVariant}\". Must be \"all\" or \"race\"`,\n });\n }\n\n if ('arms' in node) {\n if (!Array.isArray(node.arms)) {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_ARMS',\n message: 'arms must be an array',\n });\n } else {\n for (let i = 0; i < (node.arms as unknown[]).length; i++) {\n const arm = (node.arms as unknown[])[i] as Record<string, unknown> | null;\n if (!arm || typeof arm !== 'object') {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_ARM_ENTRY',\n message: `arms[${i}] must be an object`,\n });\n continue;\n }\n if (!arm.pattern || typeof arm.pattern !== 'object')\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_ARM_PATTERN',\n message: `arms[${i}] missing required field: pattern`,\n });\n if (!Array.isArray(arm.body))\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_ARM_BODY',\n message: `arms[${i}] missing required field: body (array)`,\n });\n }\n }\n }\n\n if ('diagnostics' in node && Array.isArray(node.diagnostics)) {\n for (let i = 0; i < (node.diagnostics as unknown[]).length; i++) {\n const d = (node.diagnostics as unknown[])[i] as Record<string, unknown> | null;\n if (!d || typeof d !== 'object') {\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_DIAGNOSTIC_ENTRY',\n message: `diagnostics[${i}] must be an object`,\n });\n continue;\n }\n if (!VALID_DIAGNOSTIC_LEVELS.has(String(d.level)))\n diagnostics.push({\n severity: 'error',\n code: 'INVALID_DIAGNOSTIC_LEVEL',\n message: `diagnostics[${i}].level must be \"error\", \"warning\", or \"info\"`,\n });\n if (typeof d.message !== 'string')\n diagnostics.push({\n severity: 'error',\n code: 'MISSING_DIAGNOSTIC_MESSAGE',\n message: `diagnostics[${i}] missing required field: message`,\n });\n }\n }\n\n return diagnostics;\n}\n","/**\n * Client-side schema registry for <lse-intent>.\n *\n * Schemas registered here are used to validate protocol JSON before execution.\n * Register once per page; the registry is a singleton per module.\n */\n\nimport type { CommandSchema } from '@lokascript/intent';\n\nclass IntentSchemaRegistry {\n private schemas = new Map<string, CommandSchema>();\n\n /** Register a command schema. Overwrites any existing schema for the same action. */\n register(schema: CommandSchema): void {\n this.schemas.set(schema.action, schema);\n }\n\n /** Register multiple schemas at once. */\n registerAll(schemas: CommandSchema[]): void {\n for (const schema of schemas) this.register(schema);\n }\n\n /** Look up a schema by action name. Returns undefined if not registered. */\n get(action: string): CommandSchema | undefined {\n return this.schemas.get(action);\n }\n\n /** Check whether a schema is registered for the given action. */\n has(action: string): boolean {\n return this.schemas.has(action);\n }\n\n /** Remove a schema. */\n unregister(action: string): void {\n this.schemas.delete(action);\n }\n\n /** Clear all registered schemas. */\n clear(): void {\n this.schemas.clear();\n }\n\n /** Number of registered schemas. */\n get size(): number {\n return this.schemas.size;\n }\n\n /**\n * Fetch and register schemas from a JSON endpoint.\n * The endpoint must return an array of CommandSchema objects.\n * Throws if the response is not OK, not an array, or contains malformed schemas.\n */\n async loadFrom(url: string): Promise<void> {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(\n `Failed to load schemas from ${url}: ${response.status} ${response.statusText}`\n );\n }\n const body: unknown = await response.json();\n if (!Array.isArray(body)) {\n throw new Error(`Expected array of schemas from ${url}, got ${typeof body}`);\n }\n for (let i = 0; i < body.length; i++) {\n const s = body[i] as Record<string, unknown>;\n if (typeof s?.action !== 'string' || !Array.isArray(s?.roles)) {\n throw new Error(\n `Schema at index ${i} from ${url} is missing required fields \"action\" (string) and \"roles\" (array)`\n );\n }\n }\n this.registerAll(body as CommandSchema[]);\n }\n}\n\n/** Singleton registry — shared across all <lse-intent> elements on the page. */\nexport const intentRegistry = new IntentSchemaRegistry();\n","/**\n * Sandboxed execution wrapper for <lse-intent>.\n *\n * Wraps the hyperfixi runtime call with:\n * - try/catch for execution errors\n * - configurable timeout (default 5s)\n * - structured error reporting\n */\n\nexport interface SandboxResult {\n ok: boolean;\n result?: unknown;\n error?: Error;\n timedOut?: boolean;\n}\n\nconst DEFAULT_TIMEOUT_MS = 5000;\n\n/**\n * Execute `fn` with a timeout. If it exceeds `timeoutMs`, the promise\n * rejects with an error whose `timedOut` flag is set.\n */\nexport async function sandboxed(\n fn: () => Promise<unknown>,\n timeoutMs = DEFAULT_TIMEOUT_MS\n): Promise<SandboxResult> {\n let timedOut = false;\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutHandle = setTimeout(() => {\n timedOut = true;\n reject(new Error(`LSE execution timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n });\n\n try {\n const result = await Promise.race([fn(), timeoutPromise]);\n clearTimeout(timeoutHandle);\n return { ok: true, result };\n } catch (err) {\n clearTimeout(timeoutHandle);\n const error = err instanceof Error ? err : new Error(String(err));\n return { ok: false, error, timedOut };\n }\n}\n","/**\n * <lse-intent> Custom Element\n *\n * Accepts LSE protocol JSON via an inline <script type=\"application/lse+json\">\n * child (or a `src` attribute), validates it, and executes it via the hyperfixi\n * runtime according to a declarative trigger model. Degrades gracefully when\n * the runtime is unavailable.\n *\n * ## Trigger modes\n *\n * The `trigger` attribute controls when validation + execution fires:\n *\n * - `load` (default) — fire immediately on `connectedCallback`\n * - `click` — fire on click anywhere inside the element\n * - `submit` — fire on submit of the closest ancestor `<form>`; preventDefault is called synchronously\n * - `intersect` — fire when the element scrolls into view (IntersectionObserver; one-shot)\n * - `manual` — never fire automatically; caller must invoke `.refresh()`\n * - (any other value) — treated as a DOM event name, wired via addEventListener on the element\n *\n * If no `trigger` attribute is set, the element inspects the JSON wire-format\n * `trigger.event` sugar field (protocol/spec/wire-format.md lines 441–474) and\n * uses it as the event name. If neither is present, defaults to `load`.\n *\n * @example\n * ```html\n * <!-- Load on connect (default) -->\n * <lse-intent>\n * <script type=\"application/lse+json\">\n * {\"action\":\"toggle\",\"roles\":{\"patient\":{\"type\":\"selector\",\"value\":\".active\"}}}\n * </script>\n * </lse-intent>\n *\n * <!-- Fire on click -->\n * <lse-intent trigger=\"click\">\n * <button slot=\"trigger\">Toggle</button>\n * <script type=\"application/lse+json\">\n * {\"action\":\"toggle\",\"roles\":{\"patient\":{\"type\":\"selector\",\"value\":\".active\"}}}\n * </script>\n * </lse-intent>\n *\n * <!-- Auto-wire from JSON trigger.event sugar -->\n * <lse-intent>\n * <button>Toggle</button>\n * <script type=\"application/lse+json\">\n * {\"action\":\"toggle\",\"roles\":{\"patient\":{\"type\":\"selector\",\"value\":\".active\"}},\"trigger\":{\"event\":\"click\"}}\n * </script>\n * </lse-intent>\n *\n * <!-- Manual (caller drives refresh) -->\n * <lse-intent trigger=\"manual\" id=\"my-intent\">...</lse-intent>\n * <script>document.getElementById('my-intent').refresh()</script>\n *\n * <!-- Fetch from URL, fire on intersect -->\n * <lse-intent src=\"/intents/toggle-sidebar.json\" trigger=\"intersect\"></lse-intent>\n * ```\n *\n * Events dispatched on the element:\n * - `lse:validated` — after schema check, before execution (detail: { node, diagnostics })\n * - `lse:executed` — after successful execution (detail: { node, result })\n * - `lse:error` — on validation or execution failure (detail: { diagnostics, error })\n */\n\nimport { fromProtocolJSON, validateProtocolJSON } from '@lokascript/intent';\nimport type { SemanticNode, IRDiagnostic, CommandSchema, SemanticValue } from '@lokascript/intent';\nimport { intentRegistry } from './schema-registry.js';\nimport { sandboxed } from './sandbox.js';\n\n// ---------------------------------------------------------------------------\n// Runtime bridge — resolved lazily so the element works without @hyperfixi/core\n// ---------------------------------------------------------------------------\n\ninterface HyperfiziRuntime {\n evalLSENode(node: SemanticNode, element?: Element): Promise<unknown>;\n}\n\nfunction getRuntime(): HyperfiziRuntime | null {\n const w = globalThis as Record<string, unknown>;\n const api = w['hyperfixi'];\n if (api && typeof (api as Record<string, unknown>)['evalLSENode'] === 'function') {\n return api as HyperfiziRuntime;\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Trigger resolution\n// ---------------------------------------------------------------------------\n\ntype TriggerSpec =\n | { kind: 'load' }\n | { kind: 'manual' }\n | { kind: 'intersect' }\n | { kind: 'submit' }\n | { kind: 'event'; eventName: string };\n\nfunction parseTriggerValue(value: string): TriggerSpec {\n const v = value.trim().toLowerCase();\n if (v === '' || v === 'load') return { kind: 'load' };\n if (v === 'manual') return { kind: 'manual' };\n if (v === 'intersect') return { kind: 'intersect' };\n if (v === 'submit') return { kind: 'submit' };\n return { kind: 'event', eventName: v };\n}\n\n// ---------------------------------------------------------------------------\n// <lse-intent>\n// ---------------------------------------------------------------------------\n\ninterface PreparedNode {\n node: SemanticNode;\n raw: Record<string, unknown>;\n}\n\nexport class LSEIntentElement extends HTMLElement {\n static observedAttributes = ['src', 'disabled', 'timeout', 'trigger'];\n\n private _node: SemanticNode | null = null;\n private _diagnostics: IRDiagnostic[] = [];\n private _abortController: AbortController | null = null;\n private _triggerCleanup: (() => void) | null = null;\n private _initInFlight = false;\n private _initPending = false;\n\n // ── Lifecycle ──────────────────────────────────────────────────────────────\n\n connectedCallback(): void {\n void this._initialize();\n }\n\n disconnectedCallback(): void {\n this._abortController?.abort();\n this._abortController = null;\n this._tearDownTrigger();\n }\n\n attributeChangedCallback(name: string, _old: string | null, _new: string | null): void {\n if (name !== 'src' && name !== 'disabled' && name !== 'trigger') return;\n // Only react to attribute changes when connected. Before connect, the\n // connectedCallback will run _initialize once with the final attribute\n // state. This avoids races between setAttribute() calls that happen\n // before the element is appended to the document.\n if (!this.isConnected) return;\n void this._initialize();\n }\n\n // ── Public accessors ───────────────────────────────────────────────────────\n\n /** The parsed SemanticNode, or null if not yet loaded / invalid. */\n get node(): SemanticNode | null {\n return this._node;\n }\n\n /** Diagnostics from the last validation attempt. Returns a snapshot copy. */\n get diagnostics(): readonly IRDiagnostic[] {\n return [...this._diagnostics];\n }\n\n /**\n * Re-run preparation and execution explicitly. Useful after updating the\n * inline JSON, and required when `trigger=\"manual\"`. Always executes\n * regardless of trigger mode.\n */\n async refresh(): Promise<void> {\n const prepared = await this._prepare();\n if (!prepared) return;\n await this._execute(prepared.node);\n }\n\n // ── Private: initialization and trigger dispatch ───────────────────────────\n\n /**\n * Serialize concurrent `_initialize` calls. If one is already running,\n * mark a re-init as pending and return. The in-flight init will re-run\n * once after it finishes so the final attribute/JSON state is reflected.\n */\n private async _initialize(): Promise<void> {\n if (this._initInFlight) {\n this._initPending = true;\n return;\n }\n this._initInFlight = true;\n try {\n await this._doInitialize();\n while (this._initPending) {\n this._initPending = false;\n await this._doInitialize();\n }\n } finally {\n this._initInFlight = false;\n }\n }\n\n private async _doInitialize(): Promise<void> {\n // Always tear down any existing trigger wiring before re-initializing.\n this._tearDownTrigger();\n\n if (this.hasAttribute('disabled')) return;\n\n const prepared = await this._prepare();\n if (!prepared) return;\n\n // The element may have been disconnected while _prepare() was awaiting\n // a fetch or JSON parse. If so, don't wire triggers — disconnectedCallback\n // already ran cleanup.\n if (!this.isConnected) return;\n\n const spec = this._resolveTriggerSpec(prepared.raw);\n\n switch (spec.kind) {\n case 'load':\n await this._execute(prepared.node);\n return;\n case 'manual':\n return;\n case 'intersect':\n this._wireIntersect(prepared.node);\n return;\n case 'submit':\n this._wireSubmit(prepared.node);\n return;\n case 'event':\n this._wireEvent(prepared.node, spec.eventName);\n return;\n }\n }\n\n private _resolveTriggerSpec(raw: Record<string, unknown>): TriggerSpec {\n // 1. Explicit trigger attribute wins.\n const attr = this.getAttribute('trigger');\n if (attr !== null) {\n return parseTriggerValue(attr);\n }\n\n // 2. Wire-format trigger.event sugar in the JSON.\n const triggerObj = raw['trigger'];\n if (triggerObj && typeof triggerObj === 'object' && !Array.isArray(triggerObj)) {\n const eventName = (triggerObj as Record<string, unknown>)['event'];\n if (typeof eventName === 'string' && eventName.length > 0) {\n return parseTriggerValue(eventName);\n }\n }\n\n // 3. Default.\n return { kind: 'load' };\n }\n\n // ── Private: trigger wiring ────────────────────────────────────────────────\n\n private _tearDownTrigger(): void {\n if (this._triggerCleanup) {\n this._triggerCleanup();\n this._triggerCleanup = null;\n }\n }\n\n private _wireEvent(node: SemanticNode, eventName: string): void {\n const handler = (_ev: Event): void => {\n void this._execute(node);\n };\n this.addEventListener(eventName, handler);\n this._triggerCleanup = () => this.removeEventListener(eventName, handler);\n }\n\n private _wireSubmit(node: SemanticNode): void {\n const form = this.closest('form');\n if (!form) {\n this._diagnostics.push({\n severity: 'warning',\n code: 'NO_ANCESTOR_FORM',\n message: 'trigger=\"submit\" requires an ancestor <form>; none found.',\n });\n return;\n }\n const handler = (ev: Event): void => {\n // preventDefault synchronously so the form does not navigate while\n // execution is in flight. If execution fails, the diagnostics event\n // still fires but the form stays on the page.\n ev.preventDefault();\n void this._execute(node);\n };\n form.addEventListener('submit', handler);\n this._triggerCleanup = () => form.removeEventListener('submit', handler);\n }\n\n private _wireIntersect(node: SemanticNode): void {\n if (typeof IntersectionObserver === 'undefined') {\n this._diagnostics.push({\n severity: 'warning',\n code: 'NO_INTERSECTION_OBSERVER',\n message:\n 'trigger=\"intersect\" requires IntersectionObserver; not available in this environment.',\n });\n return;\n }\n const observer = new IntersectionObserver(entries => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n // One-shot: disconnect after first intersection.\n observer.disconnect();\n void this._execute(node);\n return;\n }\n }\n });\n observer.observe(this);\n this._triggerCleanup = () => observer.disconnect();\n }\n\n // ── Private: prepare + execute ─────────────────────────────────────────────\n\n /**\n * Read the JSON, validate, deserialize, and emit `lse:validated`. Returns\n * the prepared node and raw JSON on success, or null on failure (in which\n * case `lse:error` has already been emitted).\n */\n private async _prepare(): Promise<PreparedNode | null> {\n const raw = await this._readJSON();\n if (raw === null) return null;\n\n // Validate wire format\n const wireDiags = validateProtocolJSON(raw);\n const hasErrors = wireDiags.some(d => d.severity === 'error');\n\n if (hasErrors) {\n this._diagnostics = wireDiags;\n this._node = null;\n this._showError();\n this._emit('lse:error', { diagnostics: wireDiags, error: null });\n return null;\n }\n\n // Deserialize to SemanticNode\n let node: SemanticNode;\n try {\n node = fromProtocolJSON(raw as unknown as Parameters<typeof fromProtocolJSON>[0]);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this._diagnostics = [\n { severity: 'error', code: 'DESERIALIZE_ERROR', message: error.message },\n ];\n this._node = null;\n this._showError();\n this._emit('lse:error', { diagnostics: this._diagnostics, error });\n return null;\n }\n\n // Optional schema validation\n const schema = intentRegistry.get(node.action);\n const schemaDiags: IRDiagnostic[] = schema ? this._validateSchema(node, schema) : [];\n\n this._node = node;\n this._diagnostics = [...wireDiags, ...schemaDiags];\n this._emit('lse:validated', { node, diagnostics: [...this._diagnostics] });\n\n return { node, raw };\n }\n\n /**\n * Execute a prepared node via the hyperfixi runtime. Emits `lse:executed`\n * on success or `lse:error` on failure. Safe to call multiple times.\n *\n * **Event-handler unwrap.** If the prepared node is an event-handler (from\n * either verbose wire format or compact `trigger` sugar), the element's\n * own `trigger` attribute has already wired the DOM event listener — the\n * wire-format event metadata is redundant at this point. We unwrap the\n * event-handler and execute each body command directly, rather than passing\n * the event-handler node to `evalLSENode` (which would attempt to re-wire\n * a listener at runtime, effectively discarding the body).\n */\n private async _execute(node: SemanticNode): Promise<void> {\n const runtime = getRuntime();\n if (!runtime) {\n // Runtime not loaded — emit a warning but don't error.\n this._diagnostics.push({\n severity: 'warning',\n code: 'NO_RUNTIME',\n message:\n 'hyperfixi runtime not found. Load hyperfixi.js before intent-element.iife.js to enable execution.',\n });\n return;\n }\n\n // Unwrap event-handler nodes into their body commands. See the comment\n // above and examples/llm-native-todo-demo/README.md for the motivation.\n const executables: SemanticNode[] =\n node.kind === 'event-handler'\n ? [...((node as { body?: SemanticNode[] }).body ?? [])]\n : [node];\n\n const rawTimeout = this.getAttribute('timeout');\n const timeoutMs = rawTimeout !== null ? parseInt(rawTimeout, 10) || 5000 : 5000;\n\n // Execute body commands sequentially. A single failure stops the sequence\n // and emits `lse:error`; all successful commands before the failure have\n // already mutated the DOM.\n const results: unknown[] = [];\n for (const cmd of executables) {\n const result = await sandboxed(() => runtime.evalLSENode(cmd, this), timeoutMs);\n if (result.ok) {\n results.push(result.result);\n } else {\n this._diagnostics.push({\n severity: 'error',\n code: result.timedOut ? 'EXECUTION_TIMEOUT' : 'EXECUTION_ERROR',\n message: result.error?.message ?? 'Unknown execution error',\n });\n this._showError();\n this._emit('lse:error', { diagnostics: [...this._diagnostics], error: result.error });\n return;\n }\n }\n\n this._emit('lse:executed', {\n node,\n // For single-body cases keep the legacy `result` shape (first/only value);\n // for multi-body cases expose the full array via `results` too.\n result: results.length === 1 ? results[0] : results,\n results,\n });\n }\n\n private async _readJSON(): Promise<Record<string, unknown> | null> {\n // 1. src attribute — fetch remote JSON\n const src = this.getAttribute('src');\n if (src) {\n this._abortController?.abort();\n this._abortController = new AbortController();\n try {\n const response = await fetch(src, { signal: this._abortController.signal });\n if (!response.ok) {\n this._emitFetchError(src, response.status);\n return null;\n }\n return (await response.json()) as Record<string, unknown>;\n } catch (err) {\n // AbortError means the element was disconnected — silently ignore\n if (err instanceof Error && err.name === 'AbortError') return null;\n this._emitFetchError(src, 0, err instanceof Error ? err : undefined);\n return null;\n }\n }\n\n // 2. Inline <script type=\"application/lse+json\"> child\n const script = this.querySelector('script[type=\"application/lse+json\"]');\n if (script) {\n try {\n return JSON.parse(script.textContent ?? '') as Record<string, unknown>;\n } catch {\n const diag: IRDiagnostic = {\n severity: 'error',\n code: 'INVALID_JSON',\n message: 'Inline JSON is not valid JSON',\n };\n this._diagnostics = [diag];\n this._showError();\n this._emit('lse:error', { diagnostics: [diag], error: null });\n return null;\n }\n }\n\n return null;\n }\n\n private _validateSchema(node: SemanticNode, schema: CommandSchema): IRDiagnostic[] {\n const diags: IRDiagnostic[] = [];\n for (const roleSpec of schema.roles) {\n const value = node.roles.get(roleSpec.role) as SemanticValue | undefined;\n\n if (roleSpec.required && !value) {\n diags.push({\n severity: 'error',\n code: 'MISSING_REQUIRED_ROLE',\n message: `Required role \"${roleSpec.role}\" is missing for command \"${node.action}\"`,\n });\n continue;\n }\n\n if (value && roleSpec.expectedTypes && roleSpec.expectedTypes.length > 0) {\n if (\n !roleSpec.expectedTypes.includes(value.type as (typeof roleSpec.expectedTypes)[number])\n ) {\n diags.push({\n severity: 'warning',\n code: 'UNEXPECTED_ROLE_TYPE',\n message: `Role \"${roleSpec.role}\" has type \"${value.type}\" but expected one of: ${roleSpec.expectedTypes.join(', ')}`,\n });\n }\n }\n }\n return diags;\n }\n\n private _showError(): void {\n const errorSlot = this.querySelector('[slot=\"error\"]');\n if (errorSlot instanceof HTMLElement) {\n errorSlot.style.display = '';\n }\n }\n\n private _emit(type: string, detail: Record<string, unknown>): void {\n this.dispatchEvent(new CustomEvent(type, { detail, bubbles: true, composed: true }));\n }\n\n private _emitFetchError(src: string, status: number, error?: Error): void {\n const message = status\n ? `Failed to fetch ${src}: HTTP ${status}`\n : `Failed to fetch ${src}${error ? ': ' + error.message : ''}`;\n const diag: IRDiagnostic = { severity: 'error', code: 'FETCH_ERROR', message };\n this._diagnostics = [diag];\n this._showError();\n this._emit('lse:error', { diagnostics: [diag], error: error ?? null });\n }\n}\n","/**\n * @hyperfixi/intent-element\n *\n * Browser custom element that validates LSE protocol JSON and executes it\n * via the hyperfixi runtime. Zero-dependency validation via @lokascript/intent;\n * execution delegated to window.hyperfixi.evalLSENode (peer dep).\n *\n * Auto-registers <lse-intent> when loaded as a browser script.\n *\n * @example\n * ```html\n * <script src=\"hyperfixi.js\"></script>\n * <script src=\"intent-element.iife.global.js\"></script>\n *\n * <lse-intent>\n * <script type=\"application/lse+json\">\n * {\"action\":\"toggle\",\"roles\":{\"patient\":{\"type\":\"selector\",\"value\":\".active\"}},\"trigger\":{\"event\":\"click\"}}\n * </script>\n * <button slot=\"trigger\">Toggle sidebar</button>\n * </lse-intent>\n * ```\n */\n\nexport { LSEIntentElement } from './lse-intent.js';\nexport { intentRegistry } from './schema-registry.js';\nexport type { SandboxResult } from './sandbox.js';\n\n// Auto-register the custom element when loaded in a browser context\nif (typeof customElements !== 'undefined' && !customElements.get('lse-intent')) {\n // Dynamic import to avoid circular reference from the export above\n import('./lse-intent.js').then(({ LSEIntentElement }) => {\n customElements.define('lse-intent', LSEIntentElement);\n });\n}\n"],"mappings":"6eAwMO,SAASA,EACdC,EACAC,EACc,CACd,OAAOA,EAAW,CAAE,KAAM,UAAW,MAAAD,EAAO,SAAAC,CAAS,EAAI,CAAE,KAAM,UAAW,MAAAD,CAAM,CACpF,CAEO,SAASE,EACdF,EACAG,EACe,CACf,OAAOA,EAAe,CAAE,KAAM,WAAY,MAAAH,EAAO,aAAAG,CAAa,EAAI,CAAE,KAAM,WAAY,MAAAH,CAAM,CAC9F,CAEO,SAASI,EAAgBJ,EAA+B,CAC7D,MAAO,CAAE,KAAM,YAAa,MAAAA,CAAM,CACpC,CAMO,SAASK,EAAiBC,EAA8B,CAC7D,MAAO,CAAE,KAAM,aAAc,IAAAA,CAAI,CACnC,CAEO,SAASC,EAAWC,EAAcC,EAAmB,GAAiB,CAC3E,MAAO,CAAE,KAAM,OAAQ,KAAAD,EAAM,QAAAC,CAAQ,CACvC,CAEO,SAASC,EACdC,EACAC,EACAC,EACqB,CACrB,IAAMC,EAAWF,aAAiB,IAAMA,EAAQ,IAAI,IAAI,OAAO,QAAQA,CAAK,CAAC,EACvEG,EAA4B,CAAE,KAAM,UAAW,OAAAJ,EAAQ,MAAOG,CAAS,EAC7E,OAAOD,EAAW,CAAE,GAAGE,EAAM,SAAAF,CAAS,EAAIE,CAC5C,CAEO,SAASC,EACdL,EACAC,EACAK,EACAJ,EACAK,EAC0B,CAC1B,IAAMJ,EAAWF,aAAiB,IAAMA,EAAQ,IAAI,IAAI,OAAO,QAAQA,CAAK,CAAC,EAC7E,MAAO,CACL,KAAM,gBACN,OAAAD,EACA,MAAOG,EACP,KAAAG,EACA,GAAIC,GAAkB,CAAE,eAAAA,CAAe,EACvC,GAAIL,GAAY,CAAE,SAAAA,CAAS,CAC7B,CACF,CAEO,SAASM,EACdR,EACAC,EACAQ,EACAC,EACAR,EACyB,CACzB,IAAMC,EAAWF,aAAiB,IAAMA,EAAQ,IAAI,IAAI,OAAO,QAAQA,CAAK,CAAC,EAC7E,MAAO,CACL,KAAM,cACN,OAAAD,EACA,MAAOG,EACP,WAAAM,EACA,GAAIC,GAAc,CAAE,WAAAA,CAAW,EAC/B,GAAIR,GAAY,CAAE,SAAAA,CAAS,CAC7B,CACF,CAEO,SAASS,EACdC,EACAC,EAA+C,aAC/CX,EACsB,CACtB,IAAMY,EAAO,CACX,KAAM,WACN,OAAQ,WACR,MAAO,IAAI,IACX,WAAAF,EACA,UAAAC,CACF,EACA,OAAOX,EAAW,CAAE,GAAGY,EAAM,SAAAZ,CAAS,EAAIY,CAC5C,CAEO,SAASC,EACdf,EACAC,EACAe,EACAV,EACAW,EACAC,EACAhB,EACkB,CAClB,IAAMC,EAAWF,aAAiB,IAAMA,EAAQ,IAAI,IAAI,OAAO,QAAQA,CAAK,CAAC,EAC7E,MAAO,CACL,KAAM,OACN,OAAAD,EACA,MAAOG,EACP,YAAAa,EACA,KAAAV,EACA,GAAIW,GAAgB,CAAE,aAAAA,CAAa,EACnC,GAAIC,GAAiB,CAAE,cAAAA,CAAc,EACrC,GAAIhB,GAAY,CAAE,SAAAA,CAAS,CAC7B,CACF,CAEO,SAASiB,EACdb,EACAc,EACAC,EACAnB,EACqB,CACrB,MAAO,CACL,KAAM,UACN,OAAQ,MACR,MAAO,IAAI,IACX,KAAAI,EACA,GAAIc,GAAeA,EAAY,OAAS,EAAI,CAAE,YAAAA,CAAY,EAAI,CAAC,EAC/D,GAAIC,GAAiBA,EAAc,OAAS,EAAI,CAAE,cAAAA,CAAc,EAAI,CAAC,EACrE,GAAInB,EAAW,CAAE,SAAAA,CAAS,EAAI,CAAC,CACjC,CACF,CAEO,SAASoB,EACdC,EACAC,EACAtB,EACqB,CACrB,MAAO,CACL,KAAM,UACN,OAAQqB,EACR,MAAO,IAAI,IACX,aAAcA,EACd,UAAAC,EACA,GAAItB,EAAW,CAAE,SAAAA,CAAS,EAAI,CAAC,CACjC,CACF,CAEO,SAASuB,EACdxB,EACAyB,EACAC,EACAzB,EACqB,CAErB,MAAO,CACL,KAAM,UACN,OAAQ,QACR,MAJeD,aAAiB,IAAMA,EAAQ,IAAI,IAAI,OAAO,QAAQA,CAAK,CAAC,EAK3E,KAAAyB,EACA,GAAIC,GAAcA,EAAW,OAAS,EAAI,CAAE,WAAAA,CAAW,EAAI,CAAC,EAC5D,GAAIzB,EAAW,CAAE,SAAAA,CAAS,EAAI,CAAC,CACjC,CACF,CO5EO,SAAS0B,EAAiBC,EAAsC,CACrE,IAAM5B,EAAQ,IAAI,IAChB,OAAO,QAAQ4B,EAAK,OAAS,CAAC,CAAC,EAAE,IAAI,CAAC,CAACC,EAAMC,CAAG,IAAM,CAACD,EAAME,EAAkBD,CAAG,CAAC,CAAC,CACtF,EAEME,EAAcJ,EAAK,aAAa,IAAIK,GACxCA,EAAE,QAAU,OAAY,CAAE,KAAMA,EAAE,KAAM,MAAOA,EAAE,KAAM,EAAI,CAAE,KAAMA,EAAE,IAAK,CAC5E,EACMC,EAAcN,EAAK,aAAa,IAAIO,IAAM,CAC9C,SAAUA,EAAE,MACZ,QAASA,EAAE,QACX,GAAIA,EAAE,KAAO,CAAE,KAAMA,EAAE,IAAK,EAAI,CAAC,EACjC,GAAIA,EAAE,KAAO,CAAE,KAAMA,EAAE,IAAK,EAAI,CAAC,EACjC,GAAIA,EAAE,OAAS,CAAE,OAAQA,EAAE,MAAO,EAAI,CAAC,EACvC,GAAIA,EAAE,aAAeA,EAAE,YAAY,OAAS,EAAI,CAAE,YAAaA,EAAE,WAAY,EAAI,CAAC,CACpF,EAAE,EAEF,GAAIP,EAAK,QAAS,CAChB,IAAMQ,EAAYR,EAAK,QAAQ,MAC/B5B,EAAM,IAAI,QAAyBb,EAAciD,EAAW,QAAQ,CAAC,EACrE,IAAMC,EAAY,IAAI,IAAIrC,CAAK,EAC/BqC,EAAU,OAAO,OAAuB,EACxC,IAAMC,EAAWxC,EAAkB8B,EAAK,OAAQS,CAAS,EACnDlC,EAAOC,EACX,KACAJ,EACA,CAACsC,CAAQ,EACT,CAAE,eAAgB,MAAO,EACxBV,EAAK,QAAQ,WAAqD,CAAC,CACtE,EACA,OAAOW,EAAiBpC,EAAM6B,EAAaE,CAAW,CACxD,CAEA,IAAMM,EAAOZ,EAAK,MAAQ,UAE1B,GAAIY,IAAS,gBAAiB,CAC5B,IAAMnC,GAAQuB,EAAK,MAAQ,CAAC,GAAG,IAAID,CAAgB,EACnD,OAAOY,EACLnC,EAAuBwB,EAAK,OAAQ5B,EAAOK,CAAI,EAC/C2B,EACAE,CACF,CACF,CAEA,GAAIM,IAAS,WAAY,CACvB,IAAM7B,GAAciB,EAAK,YAAc,CAAC,GAAG,IAAID,CAAgB,EAC/D,OAAOY,EACL7B,EAAmBC,EAAaiB,EAAK,WAAa,YAAkC,EACpFI,EACAE,CACF,CACF,CAEA,GAAIN,EAAK,YAAcA,EAAK,WAAW,OAAS,EAAG,CACjD,IAAMpB,EAAaoB,EAAK,WAAW,IAAID,CAAgB,EACjDlB,EAAamB,EAAK,WAAaA,EAAK,WAAW,IAAID,CAAgB,EAAI,OAC7E,OAAOY,EACLhC,EAAsBqB,EAAK,OAAQ5B,EAAOQ,EAAYC,CAAU,EAChEuB,EACAE,CACF,CACF,CAEA,GAAIN,EAAK,YAAa,CACpB,IAAMa,GAAYb,EAAK,UAAY,CAAC,GAAG,IAAID,CAAgB,EAC3D,OAAOY,EACLzB,EACEc,EAAK,OACL5B,EACA4B,EAAK,YACLa,EACAb,EAAK,aACLA,EAAK,aACP,EACAI,EACAE,CACF,CACF,CAEA,GAAIN,EAAK,aAAeA,EAAK,cAAe,CAC1C,IAAMvB,GAAQuB,EAAK,MAAQ,CAAC,GAAG,IAAID,CAAgB,EAC7CR,EAAcS,EAAK,YAAcA,EAAK,YAAY,IAAID,CAAgB,EAAI,OAC1EP,EAAgBQ,EAAK,cAAgBA,EAAK,cAAc,IAAID,CAAgB,EAAI,OACtF,OAAOY,EACLrB,EAAcb,EAAMc,EAAaC,CAAa,EAC9CY,EACAE,CACF,CACF,CAEA,GAAIN,EAAK,aAAc,CACrB,IAAML,GAAaK,EAAK,WAAa,CAAC,GAAG,IAAID,CAAgB,EAC7D,OAAOY,EACLlB,EAAgBO,EAAK,aAA8BL,CAAS,EAC5DS,EACAE,CACF,CACF,CAEA,GAAIN,EAAK,MAAQA,EAAK,KAAK,OAAS,EAAG,CACrC,IAAMH,EAAOG,EAAK,KAAK,IAAIc,IAAQ,CACjC,QAASX,EAAkBW,EAAI,OAAO,EACtC,KAAMA,EAAI,KAAK,IAAIf,CAAgB,CACrC,EAAE,EACID,EAAaE,EAAK,WAAaA,EAAK,WAAW,IAAID,CAAgB,EAAI,OAC7E,OAAOY,EAAiBf,EAAgBxB,EAAOyB,EAAMC,CAAU,EAAGM,EAAaE,CAAW,CAC5F,CAEA,OAAIN,EAAK,MAAQA,EAAK,KAAK,OAAS,GAAKY,IAAS,UACzCD,EACLrB,EAAcU,EAAK,KAAK,IAAID,CAAgB,CAAC,EAC7CK,EACAE,CACF,EAGKK,EAAiBzC,EAAkB8B,EAAK,OAAQ5B,CAAK,EAAGgC,EAAaE,CAAW,CACzF,CAEA,SAASK,EACPpC,EACA6B,EACAE,EASc,CACd,OAAK,CAACF,GAAeA,EAAY,SAAW,KAAO,CAACE,GAAeA,EAAY,SAAW,GACjF/B,EACF,CACL,GAAGA,EACH,GAAI6B,GAAeA,EAAY,OAAS,EAAI,CAAE,YAAAA,CAAY,EAAI,CAAC,EAC/D,GAAIE,GAAeA,EAAY,OAAS,EAAI,CAAE,YAAAA,CAAY,EAAI,CAAC,CACjE,CACF,CAEA,SAASH,EAAkB3C,EAAyC,CAClE,OAAQA,EAAM,KAAM,CAClB,IAAK,WACH,OAAOE,EAAe,OAAOF,EAAM,OAAS,EAAE,EAAGA,EAAM,YAAY,EACrE,IAAK,UACH,OAAOD,EACLC,EAAM,QAAU,OAAYA,EAAM,MAAQ,GAC1CA,EAAM,QACR,EACF,IAAK,YACH,OAAOI,EAAgB,OAAOJ,EAAM,OAAS,EAAE,CAAC,EAClD,IAAK,aACH,OAAOK,EAAiBL,EAAM,KAAO,OAAOA,EAAM,OAAS,EAAE,CAAC,EAChE,IAAK,OACH,OAAOO,EACLP,EAAM,MAAQ,OAAOA,EAAM,OAAS,EAAE,EACtCA,EAAM,UAAY,OAAOA,EAAM,OAAU,UAAYA,EAAM,MAAQ,GACrE,EACF,IAAK,gBACH,OAAOK,EAAiB,OAAOL,EAAM,OAASA,EAAM,KAAO,EAAE,CAAC,CAClE,CACF,CAkDO,SAASuD,EAAqBf,EAA+B,CAClE,IAAMM,EAA8B,CAAC,EAErC,GAAI,OAAON,GAAS,UAAYA,IAAS,KACvC,MAAO,CAAC,CAAE,SAAU,QAAS,KAAM,eAAgB,QAAS,4BAA6B,CAAC,EAG5F,IAAMzB,EAAOyB,EACPY,EAAO,SAAUrC,EAAO,OAAOA,EAAK,IAAI,EAAI,UAwBlD,GAtBI,SAAUA,GAAQ,CAACyC,EAAY,IAAIJ,CAAI,GACzCN,EAAY,KAAK,CACf,SAAU,QACV,KAAM,eACN,QAAS,iBAAiB/B,EAAK,IAAI,qDACrC,CAAC,EAGG,WAAYA,GAMP,OAAOA,EAAK,QAAW,UAAYA,EAAK,OAAO,SAAW,IACnE+B,EAAY,KAAK,CACf,SAAU,QACV,KAAM,iBACN,QAAS,mCACX,CAAC,EAVDA,EAAY,KAAK,CACf,SAAU,QACV,KAAM,iBACN,QAAS,gCACX,CAAC,EASC,EAAE,UAAW/B,IAASqC,IAAS,WACjCN,EAAY,KAAK,CACf,SAAU,QACV,KAAM,gBACN,QAAS,+BACX,CAAC,UAED,UAAW/B,IACV,OAAOA,EAAK,OAAU,UAAYA,EAAK,QAAU,MAAQ,MAAM,QAAQA,EAAK,KAAK,GAElF+B,EAAY,KAAK,CACf,SAAU,QACV,KAAM,gBACN,QAAS,8BACX,CAAC,UACQ,UAAW/B,GAAQA,EAAK,MACjC,OAAW,CAAC0B,EAAMzC,CAAK,IAAK,OAAO,QAAQe,EAAK,KAAgC,EAAG,CACjF,GAAI,OAAOf,GAAU,UAAYA,IAAU,KAAM,CAC/C8C,EAAY,KAAK,CACf,SAAU,QACV,KAAM,qBACN,QAAS,SAASL,CAAI,2BACxB,CAAC,EACD,QACF,CACA,IAAMgB,EAAIzD,GACN,EAAE,SAAUyD,IAAM,CAACC,EAAkB,IAAI,OAAOD,EAAE,IAAI,CAAC,IACzDX,EAAY,KAAK,CACf,SAAU,QACV,KAAM,qBACN,QAAS,SAASL,CAAI,uBAAuBgB,EAAE,IAAI,GACrD,CAAC,EAECA,EAAE,OAAS,SACT,OAAOA,EAAE,MAAS,UAAY,EAAE,UAAWA,IAC7CX,EAAY,KAAK,CACf,SAAU,QACV,KAAM,oBACN,QAAS,cAAcL,CAAI,2CAC7B,CAAC,EACC,OAAOgB,EAAE,SAAY,WAAa,EAAE,UAAWA,IACjDX,EAAY,KAAK,CACf,SAAU,QACV,KAAM,uBACN,QAAS,cAAcL,CAAI,8CAC7B,CAAC,GAEDgB,EAAE,OAAS,cAAgB,OAAOA,EAAE,KAAQ,UAAY,EAAE,UAAWA,IACvEX,EAAY,KAAK,CACf,SAAU,QACV,KAAM,yBACN,QAAS,oBAAoBL,CAAI,0CACnC,CAAC,CAEL,CAGF,GAAI,YAAa1B,GAAQA,EAAK,QAAS,CACrC,IAAM4C,EAAU5C,EAAK,QACjB,OAAO4C,GAAY,UAAYA,IAAY,KAC7Cb,EAAY,KAAK,CACf,SAAU,QACV,KAAM,kBACN,QAAS,2BACX,CAAC,GACQ,CAACa,EAAQ,OAAS,OAAOA,EAAQ,OAAU,WACpDb,EAAY,KAAK,CACf,SAAU,QACV,KAAM,kBACN,QAAS,0DACX,CAAC,CAEL,CAqCA,GAnCIM,IAAS,iBAAmB,EAAE,YAAarC,KACzC,EAAE,SAAUA,IAAS,CAAC,MAAM,QAAQA,EAAK,IAAI,IAC/C+B,EAAY,KAAK,CACf,SAAU,QACV,KAAM,eACN,QAAS,0CACX,CAAC,EAIDM,IAAS,cACP,EAAE,eAAgBrC,IAAS,CAAC,MAAM,QAAQA,EAAK,UAAU,IAC3D+B,EAAY,KAAK,CACf,SAAU,QACV,KAAM,qBACN,QAAS,2CACX,CAAC,EAEC,cAAe/B,GAAQ,CAAC6C,EAAkB,IAAI,OAAO7C,EAAK,SAAS,CAAC,GACtE+B,EAAY,KAAK,CACf,SAAU,QACV,KAAM,qBACN,QAAS,sBAAsB/B,EAAK,SAAS,GAC/C,CAAC,GAID,gBAAiBA,GAAQ,CAAC,MAAM,QAAQA,EAAK,WAAW,GAC1D+B,EAAY,KAAK,CACf,SAAU,QACV,KAAM,sBACN,QAAS,8BACX,CAAC,EAGC,gBAAiB/B,EACnB,GAAI,CAAC,MAAM,QAAQA,EAAK,WAAW,EACjC+B,EAAY,KAAK,CACf,SAAU,QACV,KAAM,sBACN,QAAS,8BACX,CAAC,MAED,SAAS,EAAI,EAAG,EAAK/B,EAAK,YAA0B,OAAQ,IAAK,CAC/D,IAAM8B,EAAK9B,EAAK,YAA0B,CAAC,EAC3C,GAAI,CAAC8B,GAAK,OAAOA,GAAM,SAAU,CAC/BC,EAAY,KAAK,CACf,SAAU,QACV,KAAM,2BACN,QAAS,eAAe,CAAC,qBAC3B,CAAC,EACD,QACF,EACI,OAAOD,EAAE,MAAS,UAAYA,EAAE,KAAK,SAAW,IAClDC,EAAY,KAAK,CACf,SAAU,QACV,KAAM,0BACN,QAAS,eAAe,CAAC,gCAC3B,CAAC,CACL,CAYJ,GARI,iBAAkB/B,GAAQ,CAAC8C,EAAqB,IAAI,OAAO9C,EAAK,YAAY,CAAC,GAC/E+B,EAAY,KAAK,CACf,SAAU,QACV,KAAM,wBACN,QAAS,yBAAyB/B,EAAK,YAAY,4BACrD,CAAC,EAGC,SAAUA,EACZ,GAAI,CAAC,MAAM,QAAQA,EAAK,IAAI,EAC1B+B,EAAY,KAAK,CACf,SAAU,QACV,KAAM,eACN,QAAS,uBACX,CAAC,MAED,SAAS,EAAI,EAAG,EAAK/B,EAAK,KAAmB,OAAQ,IAAK,CACxD,IAAMuC,EAAOvC,EAAK,KAAmB,CAAC,EACtC,GAAI,CAACuC,GAAO,OAAOA,GAAQ,SAAU,CACnCR,EAAY,KAAK,CACf,SAAU,QACV,KAAM,oBACN,QAAS,QAAQ,CAAC,qBACpB,CAAC,EACD,QACF,EACI,CAACQ,EAAI,SAAW,OAAOA,EAAI,SAAY,WACzCR,EAAY,KAAK,CACf,SAAU,QACV,KAAM,sBACN,QAAS,QAAQ,CAAC,mCACpB,CAAC,EACE,MAAM,QAAQQ,EAAI,IAAI,GACzBR,EAAY,KAAK,CACf,SAAU,QACV,KAAM,mBACN,QAAS,QAAQ,CAAC,wCACpB,CAAC,CACL,CAIJ,GAAI,gBAAiB/B,GAAQ,MAAM,QAAQA,EAAK,WAAW,EACzD,QAAS,EAAI,EAAG,EAAKA,EAAK,YAA0B,OAAQ,IAAK,CAC/D,IAAMgC,EAAKhC,EAAK,YAA0B,CAAC,EAC3C,GAAI,CAACgC,GAAK,OAAOA,GAAM,SAAU,CAC/BD,EAAY,KAAK,CACf,SAAU,QACV,KAAM,2BACN,QAAS,eAAe,CAAC,qBAC3B,CAAC,EACD,QACF,CACKgB,EAAwB,IAAI,OAAOf,EAAE,KAAK,CAAC,GAC9CD,EAAY,KAAK,CACf,SAAU,QACV,KAAM,2BACN,QAAS,eAAe,CAAC,+CAC3B,CAAC,EACC,OAAOC,EAAE,SAAY,UACvBD,EAAY,KAAK,CACf,SAAU,QACV,KAAM,6BACN,QAAS,eAAe,CAAC,mCAC3B,CAAC,CACL,CAGF,OAAOA,CACT,KA3PMU,EACAE,EAQAE,EACAC,EACAC,wBAXAN,EAAc,IAAI,IAAI,CAAC,UAAW,gBAAiB,UAAU,CAAC,EAC9DE,EAAoB,IAAI,IAAI,CAChC,WACA,UACA,YACA,aACA,gBACA,MACF,CAAC,EACKE,EAAoB,IAAI,IAAI,CAAC,OAAQ,MAAO,QAAS,aAAc,MAAM,CAAC,EAC1EC,EAAuB,IAAI,IAAI,CAAC,MAAO,MAAM,CAAC,EAC9CC,EAA0B,IAAI,IAAI,CAAC,QAAS,UAAW,MAAM,CAAC,IC9epE,IASMC,EAmEOC,EA5EbC,EAAAC,EAAA,kBASMH,EAAN,KAA2B,CAA3B,cACE,KAAQ,QAAU,IAAI,IAGtB,SAASI,EAA6B,CACpC,KAAK,QAAQ,IAAIA,EAAO,OAAQA,CAAM,CACxC,CAGA,YAAYC,EAAgC,CAC1C,QAAWD,KAAUC,EAAS,KAAK,SAASD,CAAM,CACpD,CAGA,IAAIE,EAA2C,CAC7C,OAAO,KAAK,QAAQ,IAAIA,CAAM,CAChC,CAGA,IAAIA,EAAyB,CAC3B,OAAO,KAAK,QAAQ,IAAIA,CAAM,CAChC,CAGA,WAAWA,EAAsB,CAC/B,KAAK,QAAQ,OAAOA,CAAM,CAC5B,CAGA,OAAc,CACZ,KAAK,QAAQ,MAAM,CACrB,CAGA,IAAI,MAAe,CACjB,OAAO,KAAK,QAAQ,IACtB,CAOA,MAAM,SAASC,EAA4B,CACzC,IAAMC,EAAW,MAAM,MAAMD,CAAG,EAChC,GAAI,CAACC,EAAS,GACZ,MAAM,IAAI,MACR,+BAA+BD,CAAG,KAAKC,EAAS,MAAM,IAAIA,EAAS,UAAU,EAC/E,EAEF,IAAMC,EAAgB,MAAMD,EAAS,KAAK,EAC1C,GAAI,CAAC,MAAM,QAAQC,CAAI,EACrB,MAAM,IAAI,MAAM,kCAAkCF,CAAG,SAAS,OAAOE,CAAI,EAAE,EAE7E,QAAS,EAAI,EAAG,EAAIA,EAAK,OAAQ,IAAK,CACpC,IAAMC,EAAID,EAAK,CAAC,EAChB,GAAI,OAAOC,GAAG,QAAW,UAAY,CAAC,MAAM,QAAQA,GAAG,KAAK,EAC1D,MAAM,IAAI,MACR,mBAAmB,CAAC,SAASH,CAAG,mEAClC,CAEJ,CACA,KAAK,YAAYE,CAAuB,CAC1C,CACF,EAGaR,EAAiB,IAAID,ICtDlC,eAAsBW,EACpBC,EACAC,EAAY,IACY,CACxB,IAAIC,EAAW,GACXC,EAEEC,EAAiB,IAAI,QAAe,CAACC,EAAGC,IAAW,CACvDH,EAAgB,WAAW,IAAM,CAC/BD,EAAW,GACXI,EAAO,IAAI,MAAM,iCAAiCL,CAAS,IAAI,CAAC,CAClE,EAAGA,CAAS,CACd,CAAC,EAED,GAAI,CACF,IAAMM,EAAS,MAAM,QAAQ,KAAK,CAACP,EAAG,EAAGI,CAAc,CAAC,EACxD,oBAAaD,CAAa,EACnB,CAAE,GAAI,GAAM,OAAAI,CAAO,CAC5B,OAASC,EAAK,CACZ,oBAAaL,CAAa,EAEnB,CAAE,GAAI,GAAO,MADNK,aAAe,MAAQA,EAAM,IAAI,MAAM,OAAOA,CAAG,CAAC,EACrC,SAAAN,CAAS,CACtC,CACF,CA7CA,IAAAO,EAAAC,EAAA,oBCAA,IAAAC,EAAA,GAAAC,EAAAD,EAAA,sBAAAE,IA2EA,SAASC,GAAsC,CAE7C,IAAMC,EADI,WACI,UACd,OAAIA,GAAO,OAAQA,EAAgC,aAAmB,WAC7DA,EAEF,IACT,CAaA,SAASC,EAAkBC,EAA4B,CACrD,IAAMC,EAAID,EAAM,KAAK,EAAE,YAAY,EACnC,OAAIC,IAAM,IAAMA,IAAM,OAAe,CAAE,KAAM,MAAO,EAChDA,IAAM,SAAiB,CAAE,KAAM,QAAS,EACxCA,IAAM,YAAoB,CAAE,KAAM,WAAY,EAC9CA,IAAM,SAAiB,CAAE,KAAM,QAAS,EACrC,CAAE,KAAM,QAAS,UAAWA,CAAE,CACvC,CAtGA,IAiHaL,EAjHbM,EAAAC,EAAA,kBA8DAC,IAEAC,IACAC,IAgDaV,EAAN,cAA+B,WAAY,CAA3C,kCAGL,KAAQ,MAA6B,KACrC,KAAQ,aAA+B,CAAC,EACxC,KAAQ,iBAA2C,KACnD,KAAQ,gBAAuC,KAC/C,KAAQ,cAAgB,GACxB,KAAQ,aAAe,GAIvB,mBAA0B,CACnB,KAAK,YAAY,CACxB,CAEA,sBAA6B,CAC3B,KAAK,kBAAkB,MAAM,EAC7B,KAAK,iBAAmB,KACxB,KAAK,iBAAiB,CACxB,CAEA,yBAAyBW,EAAcC,EAAqBC,EAA2B,CACjFF,IAAS,OAASA,IAAS,YAAcA,IAAS,WAKjD,KAAK,aACL,KAAK,YAAY,CACxB,CAKA,IAAI,MAA4B,CAC9B,OAAO,KAAK,KACd,CAGA,IAAI,aAAuC,CACzC,MAAO,CAAC,GAAG,KAAK,YAAY,CAC9B,CAOA,MAAM,SAAyB,CAC7B,IAAMG,EAAW,MAAM,KAAK,SAAS,EAChCA,GACL,MAAM,KAAK,SAASA,EAAS,IAAI,CACnC,CASA,MAAc,aAA6B,CACzC,GAAI,KAAK,cAAe,CACtB,KAAK,aAAe,GACpB,MACF,CACA,KAAK,cAAgB,GACrB,GAAI,CAEF,IADA,MAAM,KAAK,cAAc,EAClB,KAAK,cACV,KAAK,aAAe,GACpB,MAAM,KAAK,cAAc,CAE7B,QAAE,CACA,KAAK,cAAgB,EACvB,CACF,CAEA,MAAc,eAA+B,CAI3C,GAFA,KAAK,iBAAiB,EAElB,KAAK,aAAa,UAAU,EAAG,OAEnC,IAAMA,EAAW,MAAM,KAAK,SAAS,EAMrC,GALI,CAACA,GAKD,CAAC,KAAK,YAAa,OAEvB,IAAMC,EAAO,KAAK,oBAAoBD,EAAS,GAAG,EAElD,OAAQC,EAAK,KAAM,CACjB,IAAK,OACH,MAAM,KAAK,SAASD,EAAS,IAAI,EACjC,OACF,IAAK,SACH,OACF,IAAK,YACH,KAAK,eAAeA,EAAS,IAAI,EACjC,OACF,IAAK,SACH,KAAK,YAAYA,EAAS,IAAI,EAC9B,OACF,IAAK,QACH,KAAK,WAAWA,EAAS,KAAMC,EAAK,SAAS,EAC7C,MACJ,CACF,CAEQ,oBAAoBC,EAA2C,CAErE,IAAMC,EAAO,KAAK,aAAa,SAAS,EACxC,GAAIA,IAAS,KACX,OAAOd,EAAkBc,CAAI,EAI/B,IAAMC,EAAaF,EAAI,QACvB,GAAIE,GAAc,OAAOA,GAAe,UAAY,CAAC,MAAM,QAAQA,CAAU,EAAG,CAC9E,IAAMC,EAAaD,EAAuC,MAC1D,GAAI,OAAOC,GAAc,UAAYA,EAAU,OAAS,EACtD,OAAOhB,EAAkBgB,CAAS,CAEtC,CAGA,MAAO,CAAE,KAAM,MAAO,CACxB,CAIQ,kBAAyB,CAC3B,KAAK,kBACP,KAAK,gBAAgB,EACrB,KAAK,gBAAkB,KAE3B,CAEQ,WAAWC,EAAoBD,EAAyB,CAC9D,IAAME,EAAWC,GAAqB,CAC/B,KAAK,SAASF,CAAI,CACzB,EACA,KAAK,iBAAiBD,EAAWE,CAAO,EACxC,KAAK,gBAAkB,IAAM,KAAK,oBAAoBF,EAAWE,CAAO,CAC1E,CAEQ,YAAYD,EAA0B,CAC5C,IAAMG,EAAO,KAAK,QAAQ,MAAM,EAChC,GAAI,CAACA,EAAM,CACT,KAAK,aAAa,KAAK,CACrB,SAAU,UACV,KAAM,mBACN,QAAS,2DACX,CAAC,EACD,MACF,CACA,IAAMF,EAAWG,GAAoB,CAInCA,EAAG,eAAe,EACb,KAAK,SAASJ,CAAI,CACzB,EACAG,EAAK,iBAAiB,SAAUF,CAAO,EACvC,KAAK,gBAAkB,IAAME,EAAK,oBAAoB,SAAUF,CAAO,CACzE,CAEQ,eAAeD,EAA0B,CAC/C,GAAI,OAAO,qBAAyB,IAAa,CAC/C,KAAK,aAAa,KAAK,CACrB,SAAU,UACV,KAAM,2BACN,QACE,uFACJ,CAAC,EACD,MACF,CACA,IAAMK,EAAW,IAAI,qBAAqBC,GAAW,CACnD,QAAWC,KAASD,EAClB,GAAIC,EAAM,eAAgB,CAExBF,EAAS,WAAW,EACf,KAAK,SAASL,CAAI,EACvB,MACF,CAEJ,CAAC,EACDK,EAAS,QAAQ,IAAI,EACrB,KAAK,gBAAkB,IAAMA,EAAS,WAAW,CACnD,CASA,MAAc,UAAyC,CACrD,IAAMT,EAAM,MAAM,KAAK,UAAU,EACjC,GAAIA,IAAQ,KAAM,OAAO,KAGzB,IAAMY,EAAYC,EAAqBb,CAAG,EAG1C,GAFkBY,EAAU,KAAKE,GAAKA,EAAE,WAAa,OAAO,EAG1D,YAAK,aAAeF,EACpB,KAAK,MAAQ,KACb,KAAK,WAAW,EAChB,KAAK,MAAM,YAAa,CAAE,YAAaA,EAAW,MAAO,IAAK,CAAC,EACxD,KAIT,IAAIR,EACJ,GAAI,CACFA,EAAOW,EAAiBf,CAAwD,CAClF,OAASgB,EAAK,CACZ,IAAMC,EAAQD,aAAe,MAAQA,EAAM,IAAI,MAAM,OAAOA,CAAG,CAAC,EAChE,YAAK,aAAe,CAClB,CAAE,SAAU,QAAS,KAAM,oBAAqB,QAASC,EAAM,OAAQ,CACzE,EACA,KAAK,MAAQ,KACb,KAAK,WAAW,EAChB,KAAK,MAAM,YAAa,CAAE,YAAa,KAAK,aAAc,MAAAA,CAAM,CAAC,EAC1D,IACT,CAGA,IAAMC,EAASC,EAAe,IAAIf,EAAK,MAAM,EACvCgB,EAA8BF,EAAS,KAAK,gBAAgBd,EAAMc,CAAM,EAAI,CAAC,EAEnF,YAAK,MAAQd,EACb,KAAK,aAAe,CAAC,GAAGQ,EAAW,GAAGQ,CAAW,EACjD,KAAK,MAAM,gBAAiB,CAAE,KAAAhB,EAAM,YAAa,CAAC,GAAG,KAAK,YAAY,CAAE,CAAC,EAElE,CAAE,KAAAA,EAAM,IAAAJ,CAAI,CACrB,CAcA,MAAc,SAASI,EAAmC,CACxD,IAAMiB,EAAUpC,EAAW,EAC3B,GAAI,CAACoC,EAAS,CAEZ,KAAK,aAAa,KAAK,CACrB,SAAU,UACV,KAAM,aACN,QACE,mGACJ,CAAC,EACD,MACF,CAIA,IAAMC,EACJlB,EAAK,OAAS,gBACV,CAAC,GAAKA,EAAmC,MAAQ,CAAC,CAAE,EACpD,CAACA,CAAI,EAELmB,EAAa,KAAK,aAAa,SAAS,EACxCC,EAAYD,IAAe,MAAO,SAASA,EAAY,EAAE,GAAK,IAK9DE,EAAqB,CAAC,EAC5B,QAAWC,KAAOJ,EAAa,CAC7B,IAAMK,EAAS,MAAMC,EAAU,IAAMP,EAAQ,YAAYK,EAAK,IAAI,EAAGF,CAAS,EAC9E,GAAIG,EAAO,GACTF,EAAQ,KAAKE,EAAO,MAAM,MACrB,CACL,KAAK,aAAa,KAAK,CACrB,SAAU,QACV,KAAMA,EAAO,SAAW,oBAAsB,kBAC9C,QAASA,EAAO,OAAO,SAAW,yBACpC,CAAC,EACD,KAAK,WAAW,EAChB,KAAK,MAAM,YAAa,CAAE,YAAa,CAAC,GAAG,KAAK,YAAY,EAAG,MAAOA,EAAO,KAAM,CAAC,EACpF,MACF,CACF,CAEA,KAAK,MAAM,eAAgB,CACzB,KAAAvB,EAGA,OAAQqB,EAAQ,SAAW,EAAIA,EAAQ,CAAC,EAAIA,EAC5C,QAAAA,CACF,CAAC,CACH,CAEA,MAAc,WAAqD,CAEjE,IAAMI,EAAM,KAAK,aAAa,KAAK,EACnC,GAAIA,EAAK,CACP,KAAK,kBAAkB,MAAM,EAC7B,KAAK,iBAAmB,IAAI,gBAC5B,GAAI,CACF,IAAMC,EAAW,MAAM,MAAMD,EAAK,CAAE,OAAQ,KAAK,iBAAiB,MAAO,CAAC,EAC1E,OAAKC,EAAS,GAIN,MAAMA,EAAS,KAAK,GAH1B,KAAK,gBAAgBD,EAAKC,EAAS,MAAM,EAClC,KAGX,OAASd,EAAK,CAEZ,OAAIA,aAAe,OAASA,EAAI,OAAS,cACzC,KAAK,gBAAgBa,EAAK,EAAGb,aAAe,MAAQA,EAAM,MAAS,EAC5D,IACT,CACF,CAGA,IAAMe,EAAS,KAAK,cAAc,qCAAqC,EACvE,GAAIA,EACF,GAAI,CACF,OAAO,KAAK,MAAMA,EAAO,aAAe,EAAE,CAC5C,MAAQ,CACN,IAAMC,EAAqB,CACzB,SAAU,QACV,KAAM,eACN,QAAS,+BACX,EACA,YAAK,aAAe,CAACA,CAAI,EACzB,KAAK,WAAW,EAChB,KAAK,MAAM,YAAa,CAAE,YAAa,CAACA,CAAI,EAAG,MAAO,IAAK,CAAC,EACrD,IACT,CAGF,OAAO,IACT,CAEQ,gBAAgB5B,EAAoBc,EAAuC,CACjF,IAAMe,EAAwB,CAAC,EAC/B,QAAWC,KAAYhB,EAAO,MAAO,CACnC,IAAM9B,EAAQgB,EAAK,MAAM,IAAI8B,EAAS,IAAI,EAE1C,GAAIA,EAAS,UAAY,CAAC9C,EAAO,CAC/B6C,EAAM,KAAK,CACT,SAAU,QACV,KAAM,wBACN,QAAS,kBAAkBC,EAAS,IAAI,6BAA6B9B,EAAK,MAAM,GAClF,CAAC,EACD,QACF,CAEIhB,GAAS8C,EAAS,eAAiBA,EAAS,cAAc,OAAS,IAElEA,EAAS,cAAc,SAAS9C,EAAM,IAA+C,GAEtF6C,EAAM,KAAK,CACT,SAAU,UACV,KAAM,uBACN,QAAS,SAASC,EAAS,IAAI,eAAe9C,EAAM,IAAI,0BAA0B8C,EAAS,cAAc,KAAK,IAAI,CAAC,EACrH,CAAC,EAGP,CACA,OAAOD,CACT,CAEQ,YAAmB,CACzB,IAAME,EAAY,KAAK,cAAc,gBAAgB,EACjDA,aAAqB,cACvBA,EAAU,MAAM,QAAU,GAE9B,CAEQ,MAAMC,EAAcC,EAAuC,CACjE,KAAK,cAAc,IAAI,YAAYD,EAAM,CAAE,OAAAC,EAAQ,QAAS,GAAM,SAAU,EAAK,CAAC,CAAC,CACrF,CAEQ,gBAAgBR,EAAaS,EAAgBrB,EAAqB,CAIxE,IAAMe,EAAqB,CAAE,SAAU,QAAS,KAAM,cAAe,QAHrDM,EACZ,mBAAmBT,CAAG,UAAUS,CAAM,GACtC,mBAAmBT,CAAG,GAAGZ,EAAQ,KAAOA,EAAM,QAAU,EAAE,EACe,EAC7E,KAAK,aAAe,CAACe,CAAI,EACzB,KAAK,WAAW,EAChB,KAAK,MAAM,YAAa,CAAE,YAAa,CAACA,CAAI,EAAG,MAAOf,GAAS,IAAK,CAAC,CACvE,CACF,EA/YajC,EACJ,mBAAqB,CAAC,MAAO,WAAY,UAAW,SAAS,IClHtE,IAAAuD,EAAA,GAAAC,EAAAD,EAAA,sBAAAE,EAAA,mBAAAC,IAuBAC,IACAC,IAII,OAAO,eAAmB,KAAe,CAAC,eAAe,IAAI,YAAY,GAE3E,oCAA0B,KAAK,CAAC,CAAE,iBAAAH,CAAiB,IAAM,CACvD,eAAe,OAAO,aAAcA,CAAgB,CACtD,CAAC","names":["createLiteral","value","dataType","createSelector","selectorKind","createReference","createExpression","raw","createFlag","name","enabled","createCommandNode","action","roles","metadata","rolesMap","node","createEventHandlerNode","body","eventModifiers","createConditionalNode","thenBranch","elseBranch","createCompoundNode","statements","chainType","base","createLoopNode","loopVariant","loopVariable","indexVariable","createTryNode","catchBranch","finallyBranch","createAsyncNode","variant","asyncBody","createMatchNode","arms","defaultArm","fromProtocolJSON","json","role","val","valueFromProtocol","annotations","a","diagnostics","d","eventName","bodyRoles","bodyNode","applyV12Metadata","kind","loopBody","arm","validateProtocolJSON","VALID_KINDS","v","VALID_VALUE_TYPES","trigger","VALID_CHAIN_TYPES","VALID_ASYNC_VARIANTS","VALID_DIAGNOSTIC_LEVELS","IntentSchemaRegistry","intentRegistry","init_schema_registry","__esmMin","schema","schemas","action","url","response","body","s","sandboxed","fn","timeoutMs","timedOut","timeoutHandle","timeoutPromise","_","reject","result","err","init_sandbox","__esmMin","lse_intent_exports","__export","LSEIntentElement","getRuntime","api","parseTriggerValue","value","v","init_lse_intent","__esmMin","init_dist","init_schema_registry","init_sandbox","name","_old","_new","prepared","spec","raw","attr","triggerObj","eventName","node","handler","_ev","form","ev","observer","entries","entry","wireDiags","validateProtocolJSON","d","fromProtocolJSON","err","error","schema","intentRegistry","schemaDiags","runtime","executables","rawTimeout","timeoutMs","results","cmd","result","sandboxed","src","response","script","diag","diags","roleSpec","errorSlot","type","detail","status","src_exports","__export","LSEIntentElement","intentRegistry","init_lse_intent","init_schema_registry"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperfixi/intent-element",
3
- "version": "3.1.1",
3
+ "version": "3.2.0",
4
4
  "description": "<lse-intent> custom element — validates LSE protocol JSON and executes it via the hyperfixi runtime.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -44,10 +44,10 @@
44
44
  "author": "LokaScript Contributors",
45
45
  "license": "MIT",
46
46
  "dependencies": {
47
- "@lokascript/intent": "^3.1.1"
47
+ "@lokascript/intent": "^3.2.0"
48
48
  },
49
49
  "peerDependencies": {
50
- "@hyperfixi/core": "^3.1.1"
50
+ "@hyperfixi/core": "^3.2.0"
51
51
  },
52
52
  "peerDependenciesMeta": {
53
53
  "@hyperfixi/core": {
@@ -55,11 +55,11 @@
55
55
  }
56
56
  },
57
57
  "devDependencies": {
58
- "@hyperfixi/core": "^3.1.1",
59
- "@types/node": "^26.1.2",
58
+ "@hyperfixi/core": "^3.2.0",
59
+ "@types/node": "^26.6.3",
60
60
  "tsup": "^8.0.0",
61
61
  "typescript": "^5.0.0",
62
- "vitest": "^4.1.5"
62
+ "vitest": "^5.0.2"
63
63
  },
64
64
  "publishConfig": {
65
65
  "access": "public"