@lokascript/framework 2.0.0 → 2.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.
Files changed (138) hide show
  1. package/dist/aot/index.d.ts +1 -0
  2. package/dist/aot/index.d.ts.map +1 -1
  3. package/dist/aot/registry-bridge.d.ts +27 -0
  4. package/dist/aot/registry-bridge.d.ts.map +1 -0
  5. package/dist/api/create-dsl.d.ts.map +1 -1
  6. package/dist/api/dispatcher.d.ts +22 -0
  7. package/dist/api/dispatcher.d.ts.map +1 -1
  8. package/dist/api/domain-registry.d.ts +41 -0
  9. package/dist/api/domain-registry.d.ts.map +1 -1
  10. package/dist/api/index.js +1438 -21
  11. package/dist/api/index.js.map +1 -1
  12. package/dist/core/index.js +247 -10
  13. package/dist/core/index.js.map +1 -1
  14. package/dist/core/pattern-matching/index.js +74 -10
  15. package/dist/core/pattern-matching/index.js.map +1 -1
  16. package/dist/core/pattern-matching/pattern-matcher.d.ts.map +1 -1
  17. package/dist/core/tokenization/index.js +133 -0
  18. package/dist/core/tokenization/index.js.map +1 -1
  19. package/dist/core/tokenization/morphology/base-normalizer.d.ts +94 -0
  20. package/dist/core/tokenization/morphology/base-normalizer.d.ts.map +1 -0
  21. package/dist/core/tokenization/morphology/index.d.ts +3 -1
  22. package/dist/core/tokenization/morphology/index.d.ts.map +1 -1
  23. package/dist/core/types.d.ts +81 -2
  24. package/dist/core/types.d.ts.map +1 -1
  25. package/dist/core/types.js +40 -0
  26. package/dist/core/types.js.map +1 -1
  27. package/dist/feedback/confidence-gate.d.ts +28 -0
  28. package/dist/feedback/confidence-gate.d.ts.map +1 -0
  29. package/dist/feedback/feedback-formatter.d.ts +21 -0
  30. package/dist/feedback/feedback-formatter.d.ts.map +1 -0
  31. package/dist/feedback/index.d.ts +11 -0
  32. package/dist/feedback/index.d.ts.map +1 -0
  33. package/dist/feedback/pattern-tracker.d.ts +47 -0
  34. package/dist/feedback/pattern-tracker.d.ts.map +1 -0
  35. package/dist/feedback/types.d.ts +113 -0
  36. package/dist/feedback/types.d.ts.map +1 -0
  37. package/dist/generation/index.js +2 -1
  38. package/dist/generation/index.js.map +1 -1
  39. package/dist/index.cjs +3264 -132
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.d.ts +8 -2
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +3223 -132
  44. package/dist/index.js.map +1 -1
  45. package/dist/ir/explicit-parser.d.ts +86 -0
  46. package/dist/ir/explicit-parser.d.ts.map +1 -0
  47. package/dist/ir/explicit-renderer.d.ts +34 -0
  48. package/dist/ir/explicit-renderer.d.ts.map +1 -0
  49. package/dist/ir/from-interchange.d.ts +51 -0
  50. package/dist/ir/from-interchange.d.ts.map +1 -0
  51. package/dist/ir/index.d.ts +31 -0
  52. package/dist/ir/index.d.ts.map +1 -0
  53. package/dist/ir/index.js +1934 -0
  54. package/dist/ir/index.js.map +1 -0
  55. package/dist/ir/json-schema.d.ts +40 -0
  56. package/dist/ir/json-schema.d.ts.map +1 -0
  57. package/dist/ir/protocol-json.d.ts +73 -0
  58. package/dist/ir/protocol-json.d.ts.map +1 -0
  59. package/dist/ir/references.d.ts +19 -0
  60. package/dist/ir/references.d.ts.map +1 -0
  61. package/dist/ir/to-runtime-ast.d.ts +53 -0
  62. package/dist/ir/to-runtime-ast.d.ts.map +1 -0
  63. package/dist/ir/types.d.ts +151 -0
  64. package/dist/ir/types.d.ts.map +1 -0
  65. package/dist/parsing/index.js +74 -10
  66. package/dist/parsing/index.js.map +1 -1
  67. package/dist/prompts/index.d.ts +9 -0
  68. package/dist/prompts/index.d.ts.map +1 -0
  69. package/dist/prompts/prompt-generator.d.ts +49 -0
  70. package/dist/prompts/prompt-generator.d.ts.map +1 -0
  71. package/dist/prompts/types.d.ts +64 -0
  72. package/dist/prompts/types.d.ts.map +1 -0
  73. package/dist/schema/command-schema.d.ts +17 -0
  74. package/dist/schema/command-schema.d.ts.map +1 -1
  75. package/dist/schema/index.js.map +1 -1
  76. package/dist/testing/index.js +8673 -8260
  77. package/dist/testing/index.js.map +1 -1
  78. package/dist/training/index.d.ts +11 -0
  79. package/dist/training/index.d.ts.map +1 -0
  80. package/dist/training/jsonl-writer.d.ts +42 -0
  81. package/dist/training/jsonl-writer.d.ts.map +1 -0
  82. package/dist/training/schema-synthesizer.d.ts +20 -0
  83. package/dist/training/schema-synthesizer.d.ts.map +1 -0
  84. package/dist/training/types.d.ts +73 -0
  85. package/dist/training/types.d.ts.map +1 -0
  86. package/package.json +5 -1
  87. package/src/aot/index.ts +1 -0
  88. package/src/aot/registry-bridge.test.ts +104 -0
  89. package/src/aot/registry-bridge.ts +66 -0
  90. package/src/api/create-dsl.test.ts +306 -0
  91. package/src/api/create-dsl.ts +21 -0
  92. package/src/api/dispatcher.test.ts +66 -0
  93. package/src/api/dispatcher.ts +77 -9
  94. package/src/api/domain-registry.ts +120 -4
  95. package/src/core/pattern-matching/pattern-matcher.test.ts +131 -0
  96. package/src/core/pattern-matching/pattern-matcher.ts +102 -19
  97. package/src/core/tokenization/morphology/base-normalizer.ts +249 -0
  98. package/src/core/tokenization/morphology/index.ts +3 -1
  99. package/src/core/types.ts +147 -2
  100. package/src/feedback/confidence-gate.test.ts +111 -0
  101. package/src/feedback/confidence-gate.ts +70 -0
  102. package/src/feedback/feedback-formatter.test.ts +198 -0
  103. package/src/feedback/feedback-formatter.ts +298 -0
  104. package/src/feedback/index.ts +24 -0
  105. package/src/feedback/pattern-tracker.test.ts +198 -0
  106. package/src/feedback/pattern-tracker.ts +140 -0
  107. package/src/feedback/types.ts +149 -0
  108. package/src/generation/pattern-generator.ts +2 -2
  109. package/src/index.ts +29 -0
  110. package/src/integration/llm-lse-integration.test.ts +457 -0
  111. package/src/ir/conformance.test.ts +120 -0
  112. package/src/ir/explicit-parser.test.ts +612 -0
  113. package/src/ir/explicit-parser.ts +793 -0
  114. package/src/ir/explicit-renderer.test.ts +229 -0
  115. package/src/ir/explicit-renderer.ts +198 -0
  116. package/src/ir/from-interchange.test.ts +853 -0
  117. package/src/ir/from-interchange.ts +594 -0
  118. package/src/ir/index.ts +77 -0
  119. package/src/ir/json-schema.test.ts +281 -0
  120. package/src/ir/json-schema.ts +299 -0
  121. package/src/ir/protocol-json.test.ts +1187 -0
  122. package/src/ir/protocol-json.ts +751 -0
  123. package/src/ir/references.test.ts +38 -0
  124. package/src/ir/references.ts +33 -0
  125. package/src/ir/round-trip.test.ts +557 -0
  126. package/src/ir/to-runtime-ast.ts +203 -0
  127. package/src/ir/types.ts +191 -0
  128. package/src/prompts/index.ts +15 -0
  129. package/src/prompts/prompt-generator.test.ts +386 -0
  130. package/src/prompts/prompt-generator.ts +531 -0
  131. package/src/prompts/types.ts +89 -0
  132. package/src/schema/command-schema.ts +19 -0
  133. package/src/training/index.ts +13 -0
  134. package/src/training/jsonl-writer.test.ts +138 -0
  135. package/src/training/jsonl-writer.ts +67 -0
  136. package/src/training/schema-synthesizer.test.ts +250 -0
  137. package/src/training/schema-synthesizer.ts +306 -0
  138. package/src/training/types.ts +103 -0
@@ -0,0 +1,1934 @@
1
+ // src/ir/references.ts
2
+ var DEFAULT_REFERENCES = /* @__PURE__ */ new Set([
3
+ "me",
4
+ "you",
5
+ "it",
6
+ "result",
7
+ "event",
8
+ "target",
9
+ "body"
10
+ ]);
11
+ function isValidReference(value, referenceSet = DEFAULT_REFERENCES) {
12
+ return referenceSet.has(value);
13
+ }
14
+
15
+ // src/core/types.ts
16
+ function createLiteral(value, dataType) {
17
+ return dataType ? { type: "literal", value, dataType } : { type: "literal", value };
18
+ }
19
+ function createSelector(value, selectorKind) {
20
+ return selectorKind ? { type: "selector", value, selectorKind } : { type: "selector", value };
21
+ }
22
+ function createReference(value) {
23
+ return { type: "reference", value };
24
+ }
25
+ function createPropertyPath(object, property) {
26
+ return { type: "property-path", object, property };
27
+ }
28
+ function createExpression(raw) {
29
+ return { type: "expression", raw };
30
+ }
31
+ function createFlag(name, enabled = true) {
32
+ return { type: "flag", name, enabled };
33
+ }
34
+ function createCommandNode(action, roles, metadata) {
35
+ const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
36
+ const node = {
37
+ kind: "command",
38
+ action,
39
+ roles: rolesMap
40
+ };
41
+ if (metadata) {
42
+ return { ...node, metadata };
43
+ }
44
+ return node;
45
+ }
46
+ function createEventHandlerNode(action, roles, body, metadata, eventModifiers) {
47
+ const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
48
+ const base = {
49
+ kind: "event-handler",
50
+ action,
51
+ roles: rolesMap,
52
+ body
53
+ };
54
+ return {
55
+ ...base,
56
+ ...eventModifiers && { eventModifiers },
57
+ ...metadata && { metadata }
58
+ };
59
+ }
60
+ function createConditionalNode(action, roles, thenBranch, elseBranch, metadata) {
61
+ const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
62
+ const base = {
63
+ kind: "conditional",
64
+ action,
65
+ roles: rolesMap,
66
+ thenBranch
67
+ };
68
+ return {
69
+ ...base,
70
+ ...elseBranch && { elseBranch },
71
+ ...metadata && { metadata }
72
+ };
73
+ }
74
+ function createCompoundNode(statements, chainType = "sequential", metadata) {
75
+ const base = {
76
+ kind: "compound",
77
+ action: "compound",
78
+ roles: /* @__PURE__ */ new Map(),
79
+ statements,
80
+ chainType
81
+ };
82
+ return metadata ? { ...base, metadata } : base;
83
+ }
84
+ function extractValue(value) {
85
+ if (value.type === "flag") return value.name;
86
+ if ("raw" in value && value.raw !== void 0) return String(value.raw);
87
+ if ("value" in value && value.value !== void 0) return String(value.value);
88
+ if (value.type === "property-path") return `${extractValue(value.object)}.${value.property}`;
89
+ return "";
90
+ }
91
+ function createLoopNode(action, roles, loopVariant, body, loopVariable, indexVariable, metadata) {
92
+ const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
93
+ const base = {
94
+ kind: "loop",
95
+ action,
96
+ roles: rolesMap,
97
+ loopVariant,
98
+ body
99
+ };
100
+ return {
101
+ ...base,
102
+ ...loopVariable && { loopVariable },
103
+ ...indexVariable && { indexVariable },
104
+ ...metadata && { metadata }
105
+ };
106
+ }
107
+ function createTryNode(body, catchBranch, finallyBranch, metadata) {
108
+ return {
109
+ kind: "command",
110
+ action: "try",
111
+ roles: /* @__PURE__ */ new Map(),
112
+ body,
113
+ ...catchBranch && catchBranch.length > 0 ? { catchBranch } : {},
114
+ ...finallyBranch && finallyBranch.length > 0 ? { finallyBranch } : {},
115
+ ...metadata ? { metadata } : {}
116
+ };
117
+ }
118
+ function createAsyncNode(variant, asyncBody, metadata) {
119
+ return {
120
+ kind: "command",
121
+ action: variant,
122
+ roles: /* @__PURE__ */ new Map(),
123
+ asyncVariant: variant,
124
+ asyncBody,
125
+ ...metadata ? { metadata } : {}
126
+ };
127
+ }
128
+ function createMatchNode(roles, arms, defaultArm, metadata) {
129
+ const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
130
+ return {
131
+ kind: "command",
132
+ action: "match",
133
+ roles: rolesMap,
134
+ arms,
135
+ ...defaultArm && defaultArm.length > 0 ? { defaultArm } : {},
136
+ ...metadata ? { metadata } : {}
137
+ };
138
+ }
139
+
140
+ // src/ir/explicit-parser.ts
141
+ var STRUCTURAL_ROLES = /* @__PURE__ */ new Set([
142
+ "body",
143
+ "then",
144
+ "else",
145
+ "condition",
146
+ "loop-body",
147
+ "variable",
148
+ "catch",
149
+ // v1.2: try/catch/finally
150
+ "finally"
151
+ // v1.2: try/catch/finally
152
+ ]);
153
+ function isNestedCommand(value) {
154
+ const inner = value.slice(1, -1);
155
+ let depth = 0;
156
+ for (const ch of inner) {
157
+ if (ch === "[") depth++;
158
+ else if (ch === "]") depth--;
159
+ else if (depth === 0 && (ch === " " || ch === ":")) return true;
160
+ }
161
+ return false;
162
+ }
163
+ function parseExplicit(input, options = {}) {
164
+ const trimmed = input.trim();
165
+ const collect = options.collectDiagnostics === true;
166
+ const diagnostics = [];
167
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) {
168
+ throw new Error("Explicit syntax must be wrapped in brackets: [command role:value ...]");
169
+ }
170
+ const content = trimmed.slice(1, -1).trim();
171
+ if (!content) {
172
+ throw new Error("Empty explicit statement");
173
+ }
174
+ const tokens = tokenizeExplicit(content);
175
+ if (tokens.length === 0) {
176
+ throw new Error("No command specified in explicit statement");
177
+ }
178
+ const command = tokens[0].toLowerCase();
179
+ const roles = /* @__PURE__ */ new Map();
180
+ const refSet = options.referenceSet ?? DEFAULT_REFERENCES;
181
+ const schema = options.schemaLookup?.getSchema(command);
182
+ const validRoleNames = schema ? new Set(schema.roles.map((r) => r.role)) : null;
183
+ for (let i = 1; i < tokens.length; i++) {
184
+ const token = tokens[i];
185
+ if (token.startsWith("+") || token.startsWith("~")) {
186
+ const enabled = token.startsWith("+");
187
+ const flagName = token.slice(1);
188
+ if (!flagName) {
189
+ if (collect) {
190
+ diagnostics.push({
191
+ severity: "error",
192
+ code: "EMPTY_FLAG",
193
+ message: `Empty flag name: "${token}"`,
194
+ source: "schema"
195
+ });
196
+ continue;
197
+ }
198
+ throw new Error(`Empty flag name: "${token}"`);
199
+ }
200
+ if (validRoleNames && !validRoleNames.has(flagName)) {
201
+ const roleList = [...validRoleNames].join(", ");
202
+ if (collect) {
203
+ diagnostics.push({
204
+ severity: "error",
205
+ code: "UNKNOWN_FLAG",
206
+ message: `Unknown flag "${flagName}" for command "${command}"`,
207
+ source: "schema",
208
+ suggestions: [...validRoleNames].map((r) => `+${r}`)
209
+ });
210
+ roles.set(flagName, createFlag(flagName, enabled));
211
+ continue;
212
+ }
213
+ throw new Error(
214
+ `Unknown flag "${flagName}" for command "${command}". Valid roles: ${roleList}`
215
+ );
216
+ }
217
+ roles.set(flagName, createFlag(flagName, enabled));
218
+ continue;
219
+ }
220
+ const colonIndex = token.indexOf(":");
221
+ if (colonIndex === -1) {
222
+ if (collect) {
223
+ diagnostics.push({
224
+ severity: "error",
225
+ code: "INVALID_ROLE_FORMAT",
226
+ message: `Invalid role format: "${token}". Expected role:value or +flag`,
227
+ source: "schema"
228
+ });
229
+ continue;
230
+ }
231
+ throw new Error(`Invalid role format: "${token}". Expected role:value or +flag`);
232
+ }
233
+ const roleName = token.slice(0, colonIndex);
234
+ if (validRoleNames && !STRUCTURAL_ROLES.has(roleName) && !validRoleNames.has(roleName)) {
235
+ const roleList = [...validRoleNames].join(", ");
236
+ if (collect) {
237
+ diagnostics.push({
238
+ severity: "error",
239
+ code: "UNKNOWN_ROLE",
240
+ message: `Unknown role "${roleName}" for command "${command}"`,
241
+ source: "schema",
242
+ suggestions: [...validRoleNames]
243
+ });
244
+ } else {
245
+ throw new Error(
246
+ `Unknown role "${roleName}" for command "${command}". Valid roles: ${roleList}`
247
+ );
248
+ }
249
+ }
250
+ const role = roleName;
251
+ const valueStr = token.slice(colonIndex + 1);
252
+ if (STRUCTURAL_ROLES.has(roleName) && valueStr.startsWith("[") && isNestedCommand(valueStr)) {
253
+ const nestedEnd = findMatchingBracket(token, colonIndex + 1);
254
+ const nestedSyntax = token.slice(colonIndex + 1, nestedEnd + 1);
255
+ roles.set(role, { type: "expression", raw: nestedSyntax });
256
+ continue;
257
+ }
258
+ const value = parseExplicitValue(valueStr, refSet);
259
+ roles.set(role, value);
260
+ }
261
+ if (schema && command !== "on") {
262
+ for (const roleSpec of schema.roles) {
263
+ if (roleSpec.required && !roles.has(roleSpec.role) && !roleSpec.default) {
264
+ if (collect) {
265
+ diagnostics.push({
266
+ severity: "error",
267
+ code: "MISSING_REQUIRED_ROLE",
268
+ message: `Missing required role "${roleSpec.role}" for command "${command}": ${roleSpec.description}`,
269
+ source: "schema"
270
+ });
271
+ } else {
272
+ throw new Error(
273
+ `Missing required role "${roleSpec.role}" for command "${command}": ${roleSpec.description}`
274
+ );
275
+ }
276
+ }
277
+ }
278
+ }
279
+ const attachDiagnostics = (node) => {
280
+ if (diagnostics.length === 0) return node;
281
+ return { ...node, diagnostics };
282
+ };
283
+ switch (command) {
284
+ case "on": {
285
+ const eventValue = roles.get("event");
286
+ if (!eventValue) {
287
+ if (collect) {
288
+ diagnostics.push({
289
+ severity: "error",
290
+ code: "MISSING_EVENT_ROLE",
291
+ message: "Event handler requires event role: [on event:click ...]",
292
+ source: "schema"
293
+ });
294
+ return attachDiagnostics(
295
+ createEventHandlerNode("on", roles, [], {
296
+ sourceLanguage: "explicit"
297
+ })
298
+ );
299
+ }
300
+ throw new Error("Event handler requires event role: [on event:click ...]");
301
+ }
302
+ const body = extractStructuralBody(roles, "body", options);
303
+ roles.delete("body");
304
+ return attachDiagnostics(
305
+ createEventHandlerNode("on", roles, body, {
306
+ sourceLanguage: "explicit"
307
+ })
308
+ );
309
+ }
310
+ case "if": {
311
+ const thenBranch = extractStructuralBody(roles, "then", options);
312
+ const elseBranch = extractStructuralBody(roles, "else", options);
313
+ roles.delete("then");
314
+ roles.delete("else");
315
+ return attachDiagnostics(
316
+ createConditionalNode(
317
+ command,
318
+ roles,
319
+ thenBranch,
320
+ elseBranch.length > 0 ? elseBranch : void 0,
321
+ {
322
+ sourceLanguage: "explicit"
323
+ }
324
+ )
325
+ );
326
+ }
327
+ case "repeat": {
328
+ const loopBody = extractStructuralBody(roles, "loop-body", options);
329
+ const loopVariantValue = roles.get("loop-variant");
330
+ const variableValue = roles.get("variable");
331
+ const indexVariableValue = roles.get("index-variable");
332
+ roles.delete("loop-body");
333
+ roles.delete("loop-variant");
334
+ roles.delete("variable");
335
+ roles.delete("index-variable");
336
+ let variant;
337
+ if (loopVariantValue && loopVariantValue.type === "literal" && typeof loopVariantValue.value === "string") {
338
+ variant = loopVariantValue.value;
339
+ } else if (roles.has("quantity")) {
340
+ variant = "times";
341
+ } else if (roles.has("source")) {
342
+ variant = "for";
343
+ } else if (roles.has("condition")) {
344
+ variant = "while";
345
+ } else {
346
+ variant = "forever";
347
+ }
348
+ const loopVariable = variableValue?.type === "literal" && typeof variableValue.value === "string" ? variableValue.value : variableValue?.type === "expression" ? variableValue.raw : void 0;
349
+ const indexVariable = indexVariableValue?.type === "literal" && typeof indexVariableValue.value === "string" ? indexVariableValue.value : void 0;
350
+ return attachDiagnostics(
351
+ createLoopNode(command, roles, variant, loopBody, loopVariable, indexVariable, {
352
+ sourceLanguage: "explicit"
353
+ })
354
+ );
355
+ }
356
+ default:
357
+ return attachDiagnostics(
358
+ createCommandNode(command, roles, {
359
+ sourceLanguage: "explicit"
360
+ })
361
+ );
362
+ }
363
+ }
364
+ function isExplicitSyntax(input) {
365
+ const trimmed = input.trim();
366
+ return trimmed.startsWith("[") && trimmed.endsWith("]");
367
+ }
368
+ var CHAIN_KEYWORDS = /* @__PURE__ */ new Set(["then", "and", "async", "sequential"]);
369
+ function splitCompoundSegments(input) {
370
+ const segments = [];
371
+ let chainType = null;
372
+ let current = "";
373
+ let bracketDepth = 0;
374
+ let inString = false;
375
+ let stringChar = "";
376
+ for (let i = 0; i < input.length; i++) {
377
+ const char = input[i];
378
+ if (inString) {
379
+ current += char;
380
+ if (char === stringChar && input[i - 1] !== "\\") {
381
+ inString = false;
382
+ }
383
+ continue;
384
+ }
385
+ if (char === '"' || char === "'") {
386
+ inString = true;
387
+ stringChar = char;
388
+ current += char;
389
+ continue;
390
+ }
391
+ if (char === "[") {
392
+ bracketDepth++;
393
+ current += char;
394
+ continue;
395
+ }
396
+ if (char === "]") {
397
+ bracketDepth--;
398
+ current += char;
399
+ if (bracketDepth === 0) {
400
+ const rest = input.slice(i + 1);
401
+ const pipeMatch = rest.match(/^\s*\|>\s*/);
402
+ if (pipeMatch) {
403
+ segments.push(current.trim());
404
+ current = "";
405
+ chainType ?? (chainType = "pipe");
406
+ i += pipeMatch[0].length;
407
+ continue;
408
+ }
409
+ const keywordMatch = rest.match(/^\s+(then|and|async|sequential)\s+/);
410
+ if (keywordMatch && CHAIN_KEYWORDS.has(keywordMatch[1])) {
411
+ segments.push(current.trim());
412
+ current = "";
413
+ chainType ?? (chainType = keywordMatch[1]);
414
+ i += keywordMatch[0].length;
415
+ continue;
416
+ }
417
+ }
418
+ continue;
419
+ }
420
+ current += char;
421
+ }
422
+ if (current.trim()) {
423
+ segments.push(current.trim());
424
+ }
425
+ return { segments, chainType };
426
+ }
427
+ function parseCompound(input, options) {
428
+ const trimmed = input.trim();
429
+ const { annotations, remainder } = extractAnnotations(trimmed);
430
+ const { segments, chainType } = splitCompoundSegments(remainder);
431
+ let node;
432
+ if (segments.length <= 1) {
433
+ node = parseExplicit(segments[0] || remainder, options);
434
+ } else {
435
+ const statements = segments.map((seg) => parseExplicit(seg, options));
436
+ node = createCompoundNode(statements, chainType ?? "sequential");
437
+ }
438
+ if (annotations.length > 0) {
439
+ return { ...node, annotations };
440
+ }
441
+ return node;
442
+ }
443
+ function extractAnnotations(input) {
444
+ const annotations = [];
445
+ let pos = 0;
446
+ while (pos < input.length) {
447
+ while (pos < input.length && (input[pos] === " " || input[pos] === " ")) {
448
+ pos++;
449
+ }
450
+ if (pos >= input.length || input[pos] !== "@") {
451
+ break;
452
+ }
453
+ pos++;
454
+ const nameStart = pos;
455
+ while (pos < input.length && /[A-Za-z0-9\-_]/.test(input[pos])) {
456
+ pos++;
457
+ }
458
+ const name = input.slice(nameStart, pos);
459
+ if (!name) break;
460
+ let value;
461
+ if (pos < input.length && input[pos] === "(") {
462
+ pos++;
463
+ const valueStart = pos;
464
+ if (pos < input.length && (input[pos] === '"' || input[pos] === "'")) {
465
+ const quote = input[pos];
466
+ pos++;
467
+ while (pos < input.length && input[pos] !== quote) {
468
+ if (input[pos] === "\\") pos++;
469
+ pos++;
470
+ }
471
+ pos++;
472
+ value = input.slice(valueStart + 1, pos - 1);
473
+ } else {
474
+ while (pos < input.length && input[pos] !== ")") {
475
+ pos++;
476
+ }
477
+ value = input.slice(valueStart, pos);
478
+ }
479
+ if (pos < input.length && input[pos] === ")") {
480
+ pos++;
481
+ }
482
+ }
483
+ annotations.push(value !== void 0 ? { name, value } : { name });
484
+ }
485
+ return { annotations, remainder: input.slice(pos).trim() };
486
+ }
487
+ var VERSION_HEADER_RE = /^#!lse\s+(\d+\.\d+(?:\.\d+)?)\s*$/;
488
+ function parseDocument(input, options) {
489
+ const lines = input.split("\n");
490
+ let lseVersion = "1.0";
491
+ const nodes = [];
492
+ let startLine = 0;
493
+ for (let i = 0; i < lines.length; i++) {
494
+ const trimmed = lines[i].trim();
495
+ if (!trimmed) continue;
496
+ const versionMatch = trimmed.match(VERSION_HEADER_RE);
497
+ if (versionMatch) {
498
+ lseVersion = versionMatch[1];
499
+ startLine = i + 1;
500
+ }
501
+ break;
502
+ }
503
+ for (let i = startLine; i < lines.length; i++) {
504
+ const trimmed = lines[i].trim();
505
+ if (!trimmed) continue;
506
+ if (trimmed.startsWith("//") || trimmed.startsWith("#") && !trimmed.startsWith("#!")) {
507
+ continue;
508
+ }
509
+ nodes.push(parseCompound(trimmed, options));
510
+ }
511
+ return { lseVersion, nodes };
512
+ }
513
+ function isCompoundSyntax(input) {
514
+ const { segments } = splitCompoundSegments(input.trim());
515
+ return segments.length > 1;
516
+ }
517
+ function isDocumentSyntax(input) {
518
+ const trimmed = input.trim();
519
+ if (trimmed.includes("\n")) return true;
520
+ if (VERSION_HEADER_RE.test(trimmed)) return true;
521
+ if (trimmed.startsWith("//") || trimmed.startsWith("#") && !trimmed.startsWith("#!")) {
522
+ return true;
523
+ }
524
+ return false;
525
+ }
526
+ function extractStructuralBody(roles, roleName, options) {
527
+ const value = roles.get(roleName);
528
+ if (!value) return [];
529
+ if (value.type === "expression") {
530
+ return [parseExplicit(value.raw, options)];
531
+ }
532
+ return [];
533
+ }
534
+ function inferSelectorKind(value) {
535
+ if (!value) return void 0;
536
+ if (value.startsWith("#")) return "id";
537
+ if (value.startsWith(".")) return "class";
538
+ if (value.startsWith("[")) return "attribute";
539
+ if (value.startsWith("<") || value.startsWith("*")) return "element";
540
+ if (/[>+~ ]/.test(value) && value.length > 1) return "complex";
541
+ return void 0;
542
+ }
543
+ function tokenizeExplicit(content) {
544
+ const tokens = [];
545
+ let current = "";
546
+ let inString = false;
547
+ let stringChar = "";
548
+ let bracketDepth = 0;
549
+ for (let i = 0; i < content.length; i++) {
550
+ const char = content[i];
551
+ if (inString) {
552
+ current += char;
553
+ if (char === stringChar && content[i - 1] !== "\\") {
554
+ inString = false;
555
+ }
556
+ continue;
557
+ }
558
+ if (char === '"' || char === "'") {
559
+ inString = true;
560
+ stringChar = char;
561
+ current += char;
562
+ continue;
563
+ }
564
+ if (char === "[") {
565
+ bracketDepth++;
566
+ current += char;
567
+ continue;
568
+ }
569
+ if (char === "]") {
570
+ bracketDepth--;
571
+ current += char;
572
+ continue;
573
+ }
574
+ if (char === " " && bracketDepth === 0) {
575
+ if (current) {
576
+ tokens.push(current);
577
+ current = "";
578
+ }
579
+ continue;
580
+ }
581
+ current += char;
582
+ }
583
+ if (current) {
584
+ tokens.push(current);
585
+ }
586
+ return tokens;
587
+ }
588
+ function parseExplicitValue(valueStr, referenceSet) {
589
+ if (valueStr.startsWith("#") || valueStr.startsWith(".") || valueStr.startsWith("[") || valueStr.startsWith("@") || valueStr.startsWith("*")) {
590
+ return createSelector(valueStr, inferSelectorKind(valueStr));
591
+ }
592
+ if (valueStr.startsWith('"') || valueStr.startsWith("'")) {
593
+ const inner = valueStr.slice(1, -1);
594
+ return createLiteral(inner, "string");
595
+ }
596
+ if (valueStr === "true") return createLiteral(true, "boolean");
597
+ if (valueStr === "false") return createLiteral(false, "boolean");
598
+ const lowerRef = valueStr.toLowerCase();
599
+ if (isValidReference(lowerRef, referenceSet)) {
600
+ return createReference(lowerRef);
601
+ }
602
+ const numMatch = valueStr.match(/^(-?\d+(?:\.\d+)?)(ms|s|m|h)?$/);
603
+ if (numMatch) {
604
+ const num = parseFloat(numMatch[1]);
605
+ const suffix = numMatch[2];
606
+ if (suffix) {
607
+ return createLiteral(valueStr, "duration");
608
+ }
609
+ return createLiteral(num, "number");
610
+ }
611
+ return createLiteral(valueStr);
612
+ }
613
+ function findMatchingBracket(str, start) {
614
+ let depth = 0;
615
+ for (let i = start; i < str.length; i++) {
616
+ if (str[i] === "[") depth++;
617
+ else if (str[i] === "]") {
618
+ depth--;
619
+ if (depth === 0) return i;
620
+ }
621
+ }
622
+ return str.length - 1;
623
+ }
624
+
625
+ // src/ir/explicit-renderer.ts
626
+ function renderExplicit(node) {
627
+ let result;
628
+ if (node.kind === "compound") {
629
+ const compoundNode = node;
630
+ const renderedStatements = compoundNode.statements.map((stmt) => renderExplicit(stmt));
631
+ result = renderedStatements.join(` ${compoundNode.chainType} `);
632
+ } else {
633
+ result = renderBracketCommand(node);
634
+ }
635
+ if (node.annotations && node.annotations.length > 0) {
636
+ const annParts = node.annotations.map(
637
+ (ann) => ann.value !== void 0 ? `@${ann.name}(${ann.value})` : `@${ann.name}`
638
+ );
639
+ return annParts.join(" ") + " " + result;
640
+ }
641
+ return result;
642
+ }
643
+ function renderDocument(envelope) {
644
+ const lines = [];
645
+ if (envelope.lseVersion && envelope.lseVersion !== "1.0") {
646
+ lines.push(`#!lse ${envelope.lseVersion}`);
647
+ }
648
+ for (const node of envelope.nodes) {
649
+ lines.push(renderExplicit(node));
650
+ }
651
+ return lines.join("\n");
652
+ }
653
+ function renderBracketCommand(node) {
654
+ const parts = [node.action];
655
+ for (const [role, value] of node.roles) {
656
+ if (value.type === "flag") {
657
+ parts.push(value.enabled ? `+${value.name}` : `~${value.name}`);
658
+ } else {
659
+ parts.push(`${role}:${valueToString(value)}`);
660
+ }
661
+ }
662
+ if (node.kind === "event-handler") {
663
+ const eventNode = node;
664
+ if (eventNode.body && eventNode.body.length > 0) {
665
+ const bodyParts = eventNode.body.map((n) => renderExplicit(n));
666
+ parts.push(`body:${bodyParts.join(" ")}`);
667
+ }
668
+ }
669
+ if (node.kind === "conditional") {
670
+ const condNode = node;
671
+ if (condNode.thenBranch && condNode.thenBranch.length > 0) {
672
+ const thenParts = condNode.thenBranch.map((n) => renderExplicit(n));
673
+ parts.push(`then:${thenParts.join(" ")}`);
674
+ }
675
+ if (condNode.elseBranch && condNode.elseBranch.length > 0) {
676
+ const elseParts = condNode.elseBranch.map((n) => renderExplicit(n));
677
+ parts.push(`else:${elseParts.join(" ")}`);
678
+ }
679
+ }
680
+ if (node.kind === "loop") {
681
+ const loopNode = node;
682
+ if (loopNode.loopVariant) {
683
+ parts.push(`loop-variant:${loopNode.loopVariant}`);
684
+ }
685
+ if (loopNode.loopVariable) {
686
+ parts.push(`variable:${loopNode.loopVariable}`);
687
+ }
688
+ if (loopNode.indexVariable) {
689
+ parts.push(`index-variable:${loopNode.indexVariable}`);
690
+ }
691
+ if (loopNode.body && loopNode.body.length > 0) {
692
+ const bodyParts = loopNode.body.map((n) => renderExplicit(n));
693
+ parts.push(`loop-body:${bodyParts.join(" ")}`);
694
+ }
695
+ }
696
+ if (node.kind === "command") {
697
+ const cmd = node;
698
+ if (cmd.body && cmd.body.length > 0) {
699
+ const bodyParts = cmd.body.map((n) => renderExplicit(n));
700
+ parts.push(`body:${bodyParts.join(" ")}`);
701
+ }
702
+ if (cmd.catchBranch && cmd.catchBranch.length > 0) {
703
+ const catchParts = cmd.catchBranch.map((n) => renderExplicit(n));
704
+ parts.push(`catch:${catchParts.join(" ")}`);
705
+ }
706
+ if (cmd.finallyBranch && cmd.finallyBranch.length > 0) {
707
+ const finallyParts = cmd.finallyBranch.map((n) => renderExplicit(n));
708
+ parts.push(`finally:${finallyParts.join(" ")}`);
709
+ }
710
+ if (cmd.asyncVariant) {
711
+ parts.push(`async-variant:${cmd.asyncVariant}`);
712
+ }
713
+ if (cmd.asyncBody && cmd.asyncBody.length > 0) {
714
+ const asyncParts = cmd.asyncBody.map((n) => renderExplicit(n));
715
+ parts.push(`async-body:${asyncParts.join(" ")}`);
716
+ }
717
+ }
718
+ return `[${parts.join(" ")}]`;
719
+ }
720
+ function valueToString(value) {
721
+ switch (value.type) {
722
+ case "literal":
723
+ if (typeof value.value === "string") {
724
+ if (value.dataType === "string" || /\s/.test(value.value)) {
725
+ return `"${value.value}"`;
726
+ }
727
+ return value.value;
728
+ }
729
+ return String(value.value);
730
+ case "selector":
731
+ return value.value;
732
+ case "reference":
733
+ return value.value;
734
+ case "property-path":
735
+ return `${valueToString(value.object)}'s ${value.property}`;
736
+ case "expression":
737
+ return value.raw;
738
+ case "flag":
739
+ return value.name;
740
+ }
741
+ }
742
+
743
+ // src/ir/json-schema.ts
744
+ var VALID_VALUE_TYPES = /* @__PURE__ */ new Set([
745
+ "selector",
746
+ "literal",
747
+ "reference",
748
+ "expression",
749
+ "property-path",
750
+ "flag"
751
+ ]);
752
+ function validateSemanticJSON(input) {
753
+ const diagnostics = [];
754
+ if (!input.action || typeof input.action !== "string") {
755
+ diagnostics.push({
756
+ severity: "error",
757
+ code: "INVALID_ACTION",
758
+ message: 'Field "action" is required and must be a string.',
759
+ suggestion: 'Provide a command name like "toggle", "add", "set", etc.'
760
+ });
761
+ return diagnostics;
762
+ }
763
+ if (input.roles && typeof input.roles === "object") {
764
+ for (const [role, value] of Object.entries(input.roles)) {
765
+ if (!value || typeof value !== "object") {
766
+ diagnostics.push({
767
+ severity: "error",
768
+ code: "INVALID_ROLE_VALUE",
769
+ message: `Role "${role}" must be an object with "type" and "value" fields.`,
770
+ suggestion: 'Use: { "type": "selector", "value": ".active" }'
771
+ });
772
+ continue;
773
+ }
774
+ const v = value;
775
+ if (!VALID_VALUE_TYPES.has(v.type)) {
776
+ diagnostics.push({
777
+ severity: "error",
778
+ code: "INVALID_VALUE_TYPE",
779
+ message: `Role "${role}" has invalid type "${v.type}".`,
780
+ suggestion: "Valid types: selector, literal, reference, expression, property-path."
781
+ });
782
+ }
783
+ if (v.value === void 0 || v.value === null) {
784
+ diagnostics.push({
785
+ severity: "error",
786
+ code: "MISSING_VALUE",
787
+ message: `Role "${role}" is missing the "value" field.`
788
+ });
789
+ }
790
+ }
791
+ }
792
+ if (input.trigger) {
793
+ if (!input.trigger.event || typeof input.trigger.event !== "string") {
794
+ diagnostics.push({
795
+ severity: "error",
796
+ code: "INVALID_TRIGGER",
797
+ message: 'Trigger "event" is required and must be a string.',
798
+ suggestion: 'Use an event name like "click", "mouseover", "keydown".'
799
+ });
800
+ }
801
+ }
802
+ return diagnostics;
803
+ }
804
+ function jsonToSemanticNode(input) {
805
+ const roles = /* @__PURE__ */ new Map();
806
+ if (input.roles) {
807
+ for (const [role, value] of Object.entries(input.roles)) {
808
+ roles.set(role, convertJSONValue(value));
809
+ }
810
+ }
811
+ if (input.trigger) {
812
+ roles.set("event", createLiteral(input.trigger.event, "string"));
813
+ const bodyRoles = new Map(roles);
814
+ bodyRoles.delete("event");
815
+ const bodyNode = createCommandNode(input.action, bodyRoles);
816
+ return createEventHandlerNode(
817
+ "on",
818
+ roles,
819
+ [bodyNode],
820
+ { sourceLanguage: "json" },
821
+ input.trigger.modifiers ?? {}
822
+ );
823
+ }
824
+ return createCommandNode(input.action, roles, {
825
+ sourceLanguage: "json"
826
+ });
827
+ }
828
+ function semanticNodeToJSON(node) {
829
+ if (node.kind === "compound") {
830
+ const compound = node;
831
+ if (compound.statements.length > 0) {
832
+ return semanticNodeToJSON(compound.statements[0]);
833
+ }
834
+ return { action: "compound", roles: {} };
835
+ }
836
+ if (node.kind === "event-handler") {
837
+ const eventNode = node;
838
+ const bodyAction = eventNode.body?.[0]?.action ?? node.action;
839
+ const roles2 = {};
840
+ if (eventNode.body?.[0]) {
841
+ for (const [role, value] of eventNode.body[0].roles) {
842
+ roles2[role] = semanticValueToJSON(value);
843
+ }
844
+ }
845
+ const eventValue = node.roles.get("event");
846
+ const eventName = eventValue ? "value" in eventValue ? String(eventValue.value) : "raw" in eventValue ? eventValue.raw : "unknown" : "unknown";
847
+ const result = {
848
+ action: bodyAction === "on" ? eventNode.body?.[0]?.action ?? "on" : bodyAction,
849
+ roles: roles2,
850
+ trigger: {
851
+ event: eventName
852
+ }
853
+ };
854
+ if (eventNode.eventModifiers && Object.keys(eventNode.eventModifiers).length > 0) {
855
+ result.trigger.modifiers = eventNode.eventModifiers;
856
+ }
857
+ return result;
858
+ }
859
+ const roles = {};
860
+ for (const [role, value] of node.roles) {
861
+ roles[role] = semanticValueToJSON(value);
862
+ }
863
+ return { action: node.action, roles };
864
+ }
865
+ function convertJSONValue(value) {
866
+ switch (value.type) {
867
+ case "selector":
868
+ return createSelector(String(value.value), detectSelectorKind(String(value.value)));
869
+ case "literal":
870
+ return createLiteral(
871
+ value.value,
872
+ typeof value.value === "number" ? "number" : typeof value.value === "boolean" ? "boolean" : "string"
873
+ );
874
+ case "reference":
875
+ return createReference(String(value.value));
876
+ case "expression":
877
+ return createExpression(String(value.value));
878
+ case "property-path":
879
+ return createExpression(String(value.value));
880
+ case "flag":
881
+ return createFlag(String(value.value), true);
882
+ default:
883
+ return createLiteral(String(value.value), "string");
884
+ }
885
+ }
886
+ function semanticValueToJSON(value) {
887
+ switch (value.type) {
888
+ case "selector":
889
+ return { type: "selector", value: value.value };
890
+ case "literal":
891
+ return { type: "literal", value: value.value };
892
+ case "reference":
893
+ return { type: "reference", value: value.value };
894
+ case "expression":
895
+ return { type: "expression", value: value.raw };
896
+ case "property-path":
897
+ return {
898
+ type: "property-path",
899
+ value: `${semanticValueToJSON(value.object).value}.${value.property}`
900
+ };
901
+ case "flag":
902
+ return { type: "flag", value: value.enabled };
903
+ }
904
+ }
905
+ function detectSelectorKind(selector) {
906
+ if (selector.startsWith("#")) return "id";
907
+ if (selector.startsWith(".")) return "class";
908
+ if (selector.startsWith("[")) return "attribute";
909
+ return "complex";
910
+ }
911
+
912
+ // src/ir/protocol-json.ts
913
+ function toProtocolJSON(node) {
914
+ const roles = rolesToProtocol(node.roles);
915
+ let result;
916
+ switch (node.kind) {
917
+ case "command": {
918
+ const cmd = node;
919
+ result = { kind: "command", action: node.action, roles };
920
+ if (cmd.body && cmd.body.length > 0) {
921
+ result.body = cmd.body.map(toProtocolJSON);
922
+ }
923
+ if (cmd.catchBranch && cmd.catchBranch.length > 0) {
924
+ result.catchBranch = cmd.catchBranch.map(toProtocolJSON);
925
+ }
926
+ if (cmd.finallyBranch && cmd.finallyBranch.length > 0) {
927
+ result.finallyBranch = cmd.finallyBranch.map(toProtocolJSON);
928
+ }
929
+ if (cmd.asyncVariant) {
930
+ result.asyncVariant = cmd.asyncVariant;
931
+ }
932
+ if (cmd.asyncBody && cmd.asyncBody.length > 0) {
933
+ result.asyncBody = cmd.asyncBody.map(toProtocolJSON);
934
+ }
935
+ if (cmd.arms && cmd.arms.length > 0) {
936
+ result.arms = cmd.arms.map((arm) => ({
937
+ pattern: valueToProtocol(arm.pattern),
938
+ body: arm.body.map(toProtocolJSON)
939
+ }));
940
+ }
941
+ if (cmd.defaultArm && cmd.defaultArm.length > 0) {
942
+ result.defaultArm = cmd.defaultArm.map(toProtocolJSON);
943
+ }
944
+ break;
945
+ }
946
+ case "event-handler": {
947
+ const eh = node;
948
+ result = {
949
+ kind: "event-handler",
950
+ action: node.action,
951
+ roles,
952
+ body: eh.body.map(toProtocolJSON)
953
+ };
954
+ break;
955
+ }
956
+ case "compound": {
957
+ const c = node;
958
+ result = {
959
+ kind: "compound",
960
+ action: "compound",
961
+ statements: c.statements.map(toProtocolJSON),
962
+ chainType: c.chainType
963
+ };
964
+ break;
965
+ }
966
+ case "conditional": {
967
+ const cond = node;
968
+ result = { kind: "command", action: node.action, roles };
969
+ result.thenBranch = cond.thenBranch.map(toProtocolJSON);
970
+ if (cond.elseBranch && cond.elseBranch.length > 0) {
971
+ result.elseBranch = cond.elseBranch.map(toProtocolJSON);
972
+ }
973
+ break;
974
+ }
975
+ case "loop": {
976
+ const loop = node;
977
+ result = { kind: "command", action: node.action, roles };
978
+ result.loopVariant = loop.loopVariant;
979
+ result.loopBody = loop.body.map(toProtocolJSON);
980
+ if (loop.loopVariable) result.loopVariable = loop.loopVariable;
981
+ if (loop.indexVariable) result.indexVariable = loop.indexVariable;
982
+ break;
983
+ }
984
+ }
985
+ if (node.annotations && node.annotations.length > 0) {
986
+ result.annotations = node.annotations.map(
987
+ (a) => a.value !== void 0 ? { name: a.name, value: a.value } : { name: a.name }
988
+ );
989
+ }
990
+ if (node.diagnostics && node.diagnostics.length > 0) {
991
+ result.diagnostics = node.diagnostics.map((d) => {
992
+ const json = {
993
+ level: d.severity,
994
+ message: d.message
995
+ };
996
+ if (d.code) json.code = d.code;
997
+ if (d.source) json.source = d.source;
998
+ if (d.suggestions && d.suggestions.length > 0) json.suggestions = [...d.suggestions];
999
+ return json;
1000
+ });
1001
+ }
1002
+ return result;
1003
+ }
1004
+ function rolesToProtocol(roles) {
1005
+ const result = {};
1006
+ for (const [role, value] of roles) {
1007
+ result[role] = valueToProtocol(value);
1008
+ }
1009
+ return result;
1010
+ }
1011
+ function valueToProtocol(value) {
1012
+ switch (value.type) {
1013
+ case "selector": {
1014
+ const PROTOCOL_SELECTOR_KINDS = /* @__PURE__ */ new Set([
1015
+ "id",
1016
+ "class",
1017
+ "attribute",
1018
+ "element",
1019
+ "complex"
1020
+ ]);
1021
+ const sk = "selectorKind" in value ? value.selectorKind : void 0;
1022
+ const sv = { type: "selector", value: value.value };
1023
+ if (sk && PROTOCOL_SELECTOR_KINDS.has(sk)) {
1024
+ sv.selectorKind = sk;
1025
+ }
1026
+ return sv;
1027
+ }
1028
+ case "literal":
1029
+ return value.dataType ? { type: "literal", value: value.value, dataType: value.dataType } : { type: "literal", value: value.value };
1030
+ case "reference":
1031
+ return { type: "reference", value: value.value };
1032
+ case "expression":
1033
+ return { type: "expression", raw: value.raw };
1034
+ case "property-path":
1035
+ return { type: "expression", raw: extractValue(value) };
1036
+ case "flag":
1037
+ return { type: "flag", name: value.name, enabled: value.enabled };
1038
+ }
1039
+ }
1040
+ function fromProtocolJSON(json) {
1041
+ const roles = new Map(
1042
+ Object.entries(json.roles ?? {}).map(([role, val]) => [role, valueFromProtocol(val)])
1043
+ );
1044
+ const annotations = json.annotations?.map(
1045
+ (a) => a.value !== void 0 ? { name: a.name, value: a.value } : { name: a.name }
1046
+ );
1047
+ const diagnostics = json.diagnostics?.map((d) => ({
1048
+ severity: d.level,
1049
+ message: d.message,
1050
+ ...d.code ? { code: d.code } : {},
1051
+ ...d.source ? { source: d.source } : {},
1052
+ ...d.suggestions && d.suggestions.length > 0 ? { suggestions: d.suggestions } : {}
1053
+ }));
1054
+ if (json.trigger) {
1055
+ const eventName = json.trigger.event;
1056
+ roles.set("event", createLiteral(eventName, "string"));
1057
+ const bodyRoles = new Map(roles);
1058
+ bodyRoles.delete("event");
1059
+ const bodyNode = createCommandNode(json.action, bodyRoles);
1060
+ const node2 = createEventHandlerNode(
1061
+ "on",
1062
+ roles,
1063
+ [bodyNode],
1064
+ { sourceLanguage: "json" },
1065
+ json.trigger.modifiers ?? {}
1066
+ );
1067
+ return applyV12Metadata(node2, annotations, diagnostics);
1068
+ }
1069
+ const kind = json.kind ?? "command";
1070
+ if (kind === "event-handler") {
1071
+ const body = (json.body ?? []).map(fromProtocolJSON);
1072
+ const node2 = createEventHandlerNode(json.action, roles, body);
1073
+ return applyV12Metadata(node2, annotations, diagnostics);
1074
+ }
1075
+ if (kind === "compound") {
1076
+ const statements = (json.statements ?? []).map(fromProtocolJSON);
1077
+ const node2 = createCompoundNode(
1078
+ statements,
1079
+ json.chainType ?? "sequential"
1080
+ );
1081
+ return applyV12Metadata(node2, annotations, diagnostics);
1082
+ }
1083
+ if (json.thenBranch && json.thenBranch.length > 0) {
1084
+ const thenBranch = json.thenBranch.map(fromProtocolJSON);
1085
+ const elseBranch = json.elseBranch ? json.elseBranch.map(fromProtocolJSON) : void 0;
1086
+ const node2 = createConditionalNode(json.action, roles, thenBranch, elseBranch);
1087
+ return applyV12Metadata(node2, annotations, diagnostics);
1088
+ }
1089
+ if (json.loopVariant) {
1090
+ const loopBody = (json.loopBody ?? []).map(fromProtocolJSON);
1091
+ const node2 = createLoopNode(
1092
+ json.action,
1093
+ roles,
1094
+ json.loopVariant,
1095
+ loopBody,
1096
+ json.loopVariable,
1097
+ json.indexVariable
1098
+ );
1099
+ return applyV12Metadata(node2, annotations, diagnostics);
1100
+ }
1101
+ if (json.catchBranch || json.finallyBranch) {
1102
+ const body = (json.body ?? []).map(fromProtocolJSON);
1103
+ const catchBranch = json.catchBranch ? json.catchBranch.map(fromProtocolJSON) : void 0;
1104
+ const finallyBranch = json.finallyBranch ? json.finallyBranch.map(fromProtocolJSON) : void 0;
1105
+ const node2 = createTryNode(body, catchBranch, finallyBranch);
1106
+ return applyV12Metadata(node2, annotations, diagnostics);
1107
+ }
1108
+ if (json.asyncVariant) {
1109
+ const asyncBody = (json.asyncBody ?? []).map(fromProtocolJSON);
1110
+ const node2 = createAsyncNode(json.asyncVariant, asyncBody);
1111
+ return applyV12Metadata(node2, annotations, diagnostics);
1112
+ }
1113
+ if (json.arms && json.arms.length > 0) {
1114
+ const arms = json.arms.map((arm) => ({
1115
+ pattern: valueFromProtocol(arm.pattern),
1116
+ body: arm.body.map(fromProtocolJSON)
1117
+ }));
1118
+ const defaultArm = json.defaultArm ? json.defaultArm.map(fromProtocolJSON) : void 0;
1119
+ const node2 = createMatchNode(roles, arms, defaultArm);
1120
+ return applyV12Metadata(node2, annotations, diagnostics);
1121
+ }
1122
+ if (json.body && json.body.length > 0 && kind === "command") {
1123
+ const body = json.body.map(fromProtocolJSON);
1124
+ const node2 = createTryNode(body);
1125
+ return applyV12Metadata(node2, annotations, diagnostics);
1126
+ }
1127
+ const node = createCommandNode(json.action, roles);
1128
+ return applyV12Metadata(node, annotations, diagnostics);
1129
+ }
1130
+ function applyV12Metadata(node, annotations, diagnostics) {
1131
+ if ((!annotations || annotations.length === 0) && (!diagnostics || diagnostics.length === 0)) {
1132
+ return node;
1133
+ }
1134
+ return {
1135
+ ...node,
1136
+ ...annotations && annotations.length > 0 ? { annotations } : {},
1137
+ ...diagnostics && diagnostics.length > 0 ? { diagnostics } : {}
1138
+ };
1139
+ }
1140
+ function valueFromProtocol(value) {
1141
+ switch (value.type) {
1142
+ case "selector":
1143
+ return createSelector(String(value.value ?? ""), value.selectorKind);
1144
+ case "literal":
1145
+ return createLiteral(
1146
+ value.value !== void 0 ? value.value : "",
1147
+ value.dataType
1148
+ );
1149
+ case "reference":
1150
+ return createReference(String(value.value ?? ""));
1151
+ case "expression":
1152
+ return createExpression(value.raw ?? String(value.value ?? ""));
1153
+ case "flag":
1154
+ return createFlag(
1155
+ value.name ?? String(value.value ?? ""),
1156
+ value.enabled ?? (typeof value.value === "boolean" ? value.value : true)
1157
+ );
1158
+ case "property-path":
1159
+ return createExpression(String(value.value ?? value.raw ?? ""));
1160
+ }
1161
+ }
1162
+ function toEnvelopeJSON(envelope) {
1163
+ const result = {
1164
+ lseVersion: envelope.lseVersion,
1165
+ nodes: envelope.nodes.map(toProtocolJSON)
1166
+ };
1167
+ if (envelope.features && envelope.features.length > 0) {
1168
+ result.features = [...envelope.features];
1169
+ }
1170
+ return result;
1171
+ }
1172
+ function fromEnvelopeJSON(json) {
1173
+ return {
1174
+ lseVersion: json.lseVersion,
1175
+ ...json.features && json.features.length > 0 ? { features: json.features } : {},
1176
+ nodes: json.nodes.map(fromProtocolJSON)
1177
+ };
1178
+ }
1179
+ function isEnvelope(json) {
1180
+ return typeof json === "object" && json !== null && "lseVersion" in json && "nodes" in json && Array.isArray(json.nodes);
1181
+ }
1182
+ var VALID_KINDS = /* @__PURE__ */ new Set(["command", "event-handler", "compound"]);
1183
+ var VALID_VALUE_TYPES2 = /* @__PURE__ */ new Set([
1184
+ "selector",
1185
+ "literal",
1186
+ "reference",
1187
+ "expression",
1188
+ "property-path",
1189
+ "flag"
1190
+ ]);
1191
+ var VALID_CHAIN_TYPES = /* @__PURE__ */ new Set(["then", "and", "async", "sequential", "pipe"]);
1192
+ var VALID_ASYNC_VARIANTS = /* @__PURE__ */ new Set(["all", "race"]);
1193
+ var VALID_DIAGNOSTIC_LEVELS = /* @__PURE__ */ new Set(["error", "warning", "info"]);
1194
+ function validateProtocolJSON(json) {
1195
+ const diagnostics = [];
1196
+ if (typeof json !== "object" || json === null) {
1197
+ return [{ severity: "error", code: "INVALID_ROOT", message: "Root must be a JSON object" }];
1198
+ }
1199
+ const node = json;
1200
+ const kind = "kind" in node ? String(node.kind) : "command";
1201
+ if ("kind" in node && !VALID_KINDS.has(kind)) {
1202
+ diagnostics.push({
1203
+ severity: "error",
1204
+ code: "INVALID_KIND",
1205
+ message: `Invalid kind "${node.kind}". Must be one of: command, event-handler, compound`
1206
+ });
1207
+ }
1208
+ if (!("action" in node)) {
1209
+ diagnostics.push({
1210
+ severity: "error",
1211
+ code: "MISSING_ACTION",
1212
+ message: "Missing required field: action"
1213
+ });
1214
+ } else if (typeof node.action !== "string" || node.action.length === 0) {
1215
+ diagnostics.push({
1216
+ severity: "error",
1217
+ code: "INVALID_ACTION",
1218
+ message: "action must be a non-empty string"
1219
+ });
1220
+ }
1221
+ if (!("roles" in node) && kind !== "compound") {
1222
+ diagnostics.push({
1223
+ severity: "error",
1224
+ code: "MISSING_ROLES",
1225
+ message: "Missing required field: roles"
1226
+ });
1227
+ } else if ("roles" in node && (typeof node.roles !== "object" || node.roles === null || Array.isArray(node.roles))) {
1228
+ diagnostics.push({
1229
+ severity: "error",
1230
+ code: "INVALID_ROLES",
1231
+ message: "roles must be a plain object"
1232
+ });
1233
+ } else if ("roles" in node && node.roles) {
1234
+ for (const [role, value] of Object.entries(node.roles)) {
1235
+ if (typeof value !== "object" || value === null) {
1236
+ diagnostics.push({
1237
+ severity: "error",
1238
+ code: "INVALID_ROLE_VALUE",
1239
+ message: `Role "${role}" value must be an object`
1240
+ });
1241
+ continue;
1242
+ }
1243
+ const v = value;
1244
+ if (!("type" in v) || !VALID_VALUE_TYPES2.has(String(v.type))) {
1245
+ diagnostics.push({
1246
+ severity: "error",
1247
+ code: "INVALID_VALUE_TYPE",
1248
+ message: `Role "${role}" has invalid type "${v.type}"`
1249
+ });
1250
+ }
1251
+ if (v.type === "flag") {
1252
+ const hasName = typeof v.name === "string";
1253
+ const hasEnabled = typeof v.enabled === "boolean";
1254
+ const hasValue = "value" in v;
1255
+ if (!hasName && !hasValue) {
1256
+ diagnostics.push({
1257
+ severity: "error",
1258
+ code: "MISSING_FLAG_NAME",
1259
+ message: `Flag role "${role}" missing required field: name (or value)`
1260
+ });
1261
+ }
1262
+ if (!hasEnabled && !hasValue) {
1263
+ diagnostics.push({
1264
+ severity: "error",
1265
+ code: "MISSING_FLAG_ENABLED",
1266
+ message: `Flag role "${role}" missing required field: enabled (or value)`
1267
+ });
1268
+ }
1269
+ }
1270
+ if (v.type === "expression" && typeof v.raw !== "string" && !("value" in v)) {
1271
+ diagnostics.push({
1272
+ severity: "error",
1273
+ code: "MISSING_EXPRESSION_RAW",
1274
+ message: `Expression role "${role}" missing required field: raw (or value)`
1275
+ });
1276
+ }
1277
+ }
1278
+ }
1279
+ if ("trigger" in node && node.trigger) {
1280
+ const trigger = node.trigger;
1281
+ if (typeof trigger !== "object" || trigger === null) {
1282
+ diagnostics.push({
1283
+ severity: "error",
1284
+ code: "INVALID_TRIGGER",
1285
+ message: "trigger must be an object"
1286
+ });
1287
+ } else if (!trigger.event || typeof trigger.event !== "string") {
1288
+ diagnostics.push({
1289
+ severity: "error",
1290
+ code: "INVALID_TRIGGER",
1291
+ message: "trigger.event is required and must be a non-empty string"
1292
+ });
1293
+ }
1294
+ }
1295
+ if (kind === "event-handler" && !("trigger" in node)) {
1296
+ if (!("body" in node) || !Array.isArray(node.body)) {
1297
+ diagnostics.push({
1298
+ severity: "error",
1299
+ code: "MISSING_BODY",
1300
+ message: "event-handler node requires a body array"
1301
+ });
1302
+ }
1303
+ }
1304
+ if (kind === "compound") {
1305
+ if (!("statements" in node) || !Array.isArray(node.statements)) {
1306
+ diagnostics.push({
1307
+ severity: "error",
1308
+ code: "MISSING_STATEMENTS",
1309
+ message: "compound node requires a statements array"
1310
+ });
1311
+ }
1312
+ if ("chainType" in node && !VALID_CHAIN_TYPES.has(String(node.chainType))) {
1313
+ diagnostics.push({
1314
+ severity: "error",
1315
+ code: "INVALID_CHAIN_TYPE",
1316
+ message: `Invalid chainType "${node.chainType}". Must be one of: then, and, async, sequential, pipe`
1317
+ });
1318
+ }
1319
+ }
1320
+ if ("diagnostics" in node) {
1321
+ if (!Array.isArray(node.diagnostics)) {
1322
+ diagnostics.push({
1323
+ severity: "error",
1324
+ code: "INVALID_DIAGNOSTICS",
1325
+ message: "diagnostics must be an array"
1326
+ });
1327
+ } else {
1328
+ for (let i = 0; i < node.diagnostics.length; i++) {
1329
+ const d = node.diagnostics[i];
1330
+ if (!d || typeof d !== "object") {
1331
+ diagnostics.push({
1332
+ severity: "error",
1333
+ code: "INVALID_DIAGNOSTIC_ENTRY",
1334
+ message: `diagnostics[${i}] must be an object`
1335
+ });
1336
+ continue;
1337
+ }
1338
+ if (!VALID_DIAGNOSTIC_LEVELS.has(String(d.level))) {
1339
+ diagnostics.push({
1340
+ severity: "error",
1341
+ code: "INVALID_DIAGNOSTIC_LEVEL",
1342
+ message: `diagnostics[${i}].level must be "error", "warning", or "info"`
1343
+ });
1344
+ }
1345
+ if (typeof d.message !== "string") {
1346
+ diagnostics.push({
1347
+ severity: "error",
1348
+ code: "MISSING_DIAGNOSTIC_MESSAGE",
1349
+ message: `diagnostics[${i}] missing required field: message`
1350
+ });
1351
+ }
1352
+ if ("code" in d && typeof d.code !== "string") {
1353
+ diagnostics.push({
1354
+ severity: "warning",
1355
+ code: "INVALID_DIAGNOSTIC_CODE",
1356
+ message: `diagnostics[${i}].code must be a string if present`
1357
+ });
1358
+ }
1359
+ }
1360
+ }
1361
+ }
1362
+ if ("annotations" in node) {
1363
+ if (!Array.isArray(node.annotations)) {
1364
+ diagnostics.push({
1365
+ severity: "error",
1366
+ code: "INVALID_ANNOTATIONS",
1367
+ message: "annotations must be an array"
1368
+ });
1369
+ } else {
1370
+ for (let i = 0; i < node.annotations.length; i++) {
1371
+ const a = node.annotations[i];
1372
+ if (!a || typeof a !== "object") {
1373
+ diagnostics.push({
1374
+ severity: "error",
1375
+ code: "INVALID_ANNOTATION_ENTRY",
1376
+ message: `annotations[${i}] must be an object`
1377
+ });
1378
+ continue;
1379
+ }
1380
+ if (typeof a.name !== "string" || a.name.length === 0) {
1381
+ diagnostics.push({
1382
+ severity: "error",
1383
+ code: "MISSING_ANNOTATION_NAME",
1384
+ message: `annotations[${i}] missing required field: name`
1385
+ });
1386
+ }
1387
+ }
1388
+ }
1389
+ }
1390
+ if ("asyncVariant" in node && !VALID_ASYNC_VARIANTS.has(String(node.asyncVariant))) {
1391
+ diagnostics.push({
1392
+ severity: "error",
1393
+ code: "INVALID_ASYNC_VARIANT",
1394
+ message: `Invalid asyncVariant "${node.asyncVariant}". Must be "all" or "race"`
1395
+ });
1396
+ }
1397
+ if ("arms" in node) {
1398
+ if (!Array.isArray(node.arms)) {
1399
+ diagnostics.push({
1400
+ severity: "error",
1401
+ code: "INVALID_ARMS",
1402
+ message: "arms must be an array"
1403
+ });
1404
+ } else {
1405
+ for (let i = 0; i < node.arms.length; i++) {
1406
+ const arm = node.arms[i];
1407
+ if (!arm || typeof arm !== "object") {
1408
+ diagnostics.push({
1409
+ severity: "error",
1410
+ code: "INVALID_ARM_ENTRY",
1411
+ message: `arms[${i}] must be an object`
1412
+ });
1413
+ continue;
1414
+ }
1415
+ if (!arm.pattern || typeof arm.pattern !== "object") {
1416
+ diagnostics.push({
1417
+ severity: "error",
1418
+ code: "MISSING_ARM_PATTERN",
1419
+ message: `arms[${i}] missing required field: pattern`
1420
+ });
1421
+ }
1422
+ if (!Array.isArray(arm.body)) {
1423
+ diagnostics.push({
1424
+ severity: "error",
1425
+ code: "MISSING_ARM_BODY",
1426
+ message: `arms[${i}] missing required field: body (array)`
1427
+ });
1428
+ }
1429
+ }
1430
+ }
1431
+ }
1432
+ return diagnostics;
1433
+ }
1434
+
1435
+ // src/ir/from-interchange.ts
1436
+ function fromInterchangeNode(node) {
1437
+ if (!node || typeof node !== "object") {
1438
+ return createCommandNode("unknown", {});
1439
+ }
1440
+ switch (node.type) {
1441
+ case "event":
1442
+ return convertEvent(node);
1443
+ case "command":
1444
+ return convertCommand(node);
1445
+ case "if":
1446
+ return convertIf(node);
1447
+ case "repeat":
1448
+ return convertRepeat(node);
1449
+ case "foreach":
1450
+ return convertForEach(node);
1451
+ case "while":
1452
+ return convertWhile(node);
1453
+ // Expression nodes — wrap as `get` command with patient role
1454
+ default:
1455
+ return createCommandNode("get", { patient: convertValue(node) });
1456
+ }
1457
+ }
1458
+ function convertEvent(node) {
1459
+ const event = String(node.event ?? "click");
1460
+ const bodyNodes = Array.isArray(node.body) ? node.body : [];
1461
+ const body = bodyNodes.map(fromInterchangeNode);
1462
+ const roles = {
1463
+ event: createLiteral(event)
1464
+ };
1465
+ if (node.target && typeof node.target === "object") {
1466
+ roles.source = convertValue(node.target);
1467
+ }
1468
+ const modifiers = node.modifiers;
1469
+ let eventModifiers;
1470
+ if (modifiers) {
1471
+ eventModifiers = bridgeEventModifiers(modifiers);
1472
+ if (modifiers.prevent) roles.prevent = createLiteral(true);
1473
+ if (modifiers.stop) roles.stop = createLiteral(true);
1474
+ if (modifiers.passive) roles.passive = createLiteral(true);
1475
+ if (modifiers.capture) roles.capture = createLiteral(true);
1476
+ }
1477
+ return createEventHandlerNode("on", roles, body, void 0, eventModifiers);
1478
+ }
1479
+ function convertCommand(node) {
1480
+ const name = String(node.name ?? "unknown");
1481
+ const args = Array.isArray(node.args) ? node.args : [];
1482
+ const target = node.target && typeof node.target === "object" ? node.target : void 0;
1483
+ const existingRoles = node.roles;
1484
+ const modifiers = node.modifiers;
1485
+ let roles;
1486
+ if (existingRoles && typeof existingRoles === "object" && Object.keys(existingRoles).length > 0) {
1487
+ roles = {};
1488
+ for (const [role, value] of Object.entries(existingRoles)) {
1489
+ if (value && typeof value === "object" && "type" in value) {
1490
+ roles[role] = convertValue(value);
1491
+ }
1492
+ }
1493
+ } else {
1494
+ roles = inferRoles(name, args, modifiers, target);
1495
+ }
1496
+ return createCommandNode(name, roles);
1497
+ }
1498
+ function convertIf(node) {
1499
+ const condition = node.condition && typeof node.condition === "object" ? convertValue(node.condition) : createExpression("true");
1500
+ const thenBody = Array.isArray(node.thenBranch) ? node.thenBranch : [];
1501
+ const thenBranch = thenBody.map(fromInterchangeNode);
1502
+ let elseBranch;
1503
+ const elseIfBranches = node.elseIfBranches;
1504
+ if (elseIfBranches && elseIfBranches.length > 0) {
1505
+ elseBranch = [chainElseIfs(elseIfBranches, node.elseBranch)];
1506
+ } else if (Array.isArray(node.elseBranch) && node.elseBranch.length > 0) {
1507
+ elseBranch = node.elseBranch.map(fromInterchangeNode);
1508
+ }
1509
+ const roles = { condition };
1510
+ return createConditionalNode("if", roles, thenBranch, elseBranch);
1511
+ }
1512
+ function chainElseIfs(branches, finalElse) {
1513
+ const [first, ...rest] = branches;
1514
+ const condition = convertValue(first.condition);
1515
+ const thenBranch = first.body.map(fromInterchangeNode);
1516
+ let elseBranch;
1517
+ if (rest.length > 0) {
1518
+ elseBranch = [chainElseIfs(rest, finalElse)];
1519
+ } else if (finalElse && finalElse.length > 0) {
1520
+ elseBranch = finalElse.map(fromInterchangeNode);
1521
+ }
1522
+ return createConditionalNode("if", { condition }, thenBranch, elseBranch);
1523
+ }
1524
+ function convertRepeat(node) {
1525
+ const bodyNodes = Array.isArray(node.body) ? node.body : [];
1526
+ const body = bodyNodes.map(fromInterchangeNode);
1527
+ const roles = {};
1528
+ let variant;
1529
+ if (node.count !== void 0 && node.count !== null) {
1530
+ variant = "times";
1531
+ if (typeof node.count === "number") {
1532
+ roles.quantity = createLiteral(node.count, "number");
1533
+ } else if (typeof node.count === "object") {
1534
+ roles.quantity = convertValue(node.count);
1535
+ }
1536
+ } else if (node.whileCondition && typeof node.whileCondition === "object") {
1537
+ variant = "while";
1538
+ roles.condition = convertValue(node.whileCondition);
1539
+ } else if (typeof node.untilEvent === "string") {
1540
+ variant = "until";
1541
+ roles.condition = createLiteral(node.untilEvent);
1542
+ } else {
1543
+ variant = "forever";
1544
+ }
1545
+ return createLoopNode("repeat", roles, variant, body);
1546
+ }
1547
+ function convertForEach(node) {
1548
+ const bodyNodes = Array.isArray(node.body) ? node.body : [];
1549
+ const body = bodyNodes.map(fromInterchangeNode);
1550
+ const roles = {};
1551
+ if (node.collection && typeof node.collection === "object") {
1552
+ roles.source = convertValue(node.collection);
1553
+ }
1554
+ const itemName = typeof node.itemName === "string" ? node.itemName : void 0;
1555
+ const indexName = typeof node.indexName === "string" ? node.indexName : void 0;
1556
+ return createLoopNode("repeat", roles, "for", body, itemName, indexName);
1557
+ }
1558
+ function convertWhile(node) {
1559
+ const bodyNodes = Array.isArray(node.body) ? node.body : [];
1560
+ const body = bodyNodes.map(fromInterchangeNode);
1561
+ const roles = {};
1562
+ if (node.condition && typeof node.condition === "object") {
1563
+ roles.condition = convertValue(node.condition);
1564
+ }
1565
+ return createLoopNode("repeat", roles, "while", body);
1566
+ }
1567
+ function convertValue(node) {
1568
+ switch (node.type) {
1569
+ case "literal":
1570
+ return convertLiteral(node);
1571
+ case "selector":
1572
+ return createSelector(String(node.value ?? ""), inferSelectorKind2(String(node.value ?? "")));
1573
+ case "identifier":
1574
+ return convertIdentifier(node);
1575
+ case "variable":
1576
+ return convertVariable(node);
1577
+ case "possessive":
1578
+ return createPropertyPath(
1579
+ node.object && typeof node.object === "object" ? convertValue(node.object) : createReference("me"),
1580
+ String(node.property ?? "")
1581
+ );
1582
+ // Complex expressions → collapse to raw string
1583
+ case "binary":
1584
+ case "unary":
1585
+ case "member":
1586
+ case "call":
1587
+ case "positional":
1588
+ return createExpression(renderExpr(node));
1589
+ default:
1590
+ if ("value" in node) return createLiteral(String(node.value ?? ""));
1591
+ return createExpression(renderExpr(node));
1592
+ }
1593
+ }
1594
+ function convertLiteral(node) {
1595
+ const value = node.value;
1596
+ if (value === null || value === void 0) return createLiteral("null");
1597
+ const dataType = typeof value === "number" ? "number" : typeof value === "boolean" ? "boolean" : void 0;
1598
+ return createLiteral(value, dataType);
1599
+ }
1600
+ function convertIdentifier(node) {
1601
+ const value = String(node.value ?? "");
1602
+ if (DEFAULT_REFERENCES.has(value)) {
1603
+ return createReference(value);
1604
+ }
1605
+ return createLiteral(value);
1606
+ }
1607
+ function convertVariable(node) {
1608
+ const name = String(node.name ?? "");
1609
+ const scope = String(node.scope ?? "local");
1610
+ switch (scope) {
1611
+ case "element":
1612
+ return createExpression(`:${name}`);
1613
+ case "global":
1614
+ return createExpression(`$${name}`);
1615
+ default:
1616
+ return createExpression(name);
1617
+ }
1618
+ }
1619
+ function renderExpr(node) {
1620
+ if (!node || typeof node !== "object") return "";
1621
+ switch (node.type) {
1622
+ case "literal": {
1623
+ const v = node.value;
1624
+ if (v === null || v === void 0) return "null";
1625
+ if (typeof v === "string") return /\s/.test(v) ? `"${v}"` : v;
1626
+ return String(v);
1627
+ }
1628
+ case "identifier":
1629
+ return String(node.value ?? "");
1630
+ case "selector":
1631
+ return String(node.value ?? "");
1632
+ case "variable": {
1633
+ const name = String(node.name ?? "");
1634
+ const scope = String(node.scope ?? "local");
1635
+ if (scope === "element") return `:${name}`;
1636
+ if (scope === "global") return `$${name}`;
1637
+ return name;
1638
+ }
1639
+ case "binary": {
1640
+ const left = node.left && typeof node.left === "object" ? renderExpr(node.left) : "";
1641
+ const right = node.right && typeof node.right === "object" ? renderExpr(node.right) : "";
1642
+ return `${left} ${node.operator ?? ""} ${right}`;
1643
+ }
1644
+ case "unary": {
1645
+ const operand = node.operand && typeof node.operand === "object" ? renderExpr(node.operand) : "";
1646
+ return `${node.operator ?? ""} ${operand}`;
1647
+ }
1648
+ case "member": {
1649
+ const obj = node.object && typeof node.object === "object" ? renderExpr(node.object) : "";
1650
+ if (node.computed && node.property && typeof node.property === "object") {
1651
+ return `${obj}[${renderExpr(node.property)}]`;
1652
+ }
1653
+ const prop = typeof node.property === "string" ? node.property : node.property && typeof node.property === "object" ? renderExpr(node.property) : "";
1654
+ return `${obj}.${prop}`;
1655
+ }
1656
+ case "possessive": {
1657
+ const obj = node.object && typeof node.object === "object" ? renderExpr(node.object) : "";
1658
+ return `${obj}'s ${node.property ?? ""}`;
1659
+ }
1660
+ case "call": {
1661
+ const callee = node.callee && typeof node.callee === "object" ? renderExpr(node.callee) : "";
1662
+ const args = Array.isArray(node.args) ? node.args.map((a) => renderExpr(a)).join(", ") : "";
1663
+ return `${callee}(${args})`;
1664
+ }
1665
+ case "positional": {
1666
+ const target = node.target && typeof node.target === "object" ? renderExpr(node.target) : "";
1667
+ return target ? `${node.position ?? ""} ${target}` : String(node.position ?? "");
1668
+ }
1669
+ default:
1670
+ if ("value" in node) return String(node.value ?? "");
1671
+ return "";
1672
+ }
1673
+ }
1674
+ function inferRoles(name, args, modifiers, target) {
1675
+ const roles = {};
1676
+ switch (name) {
1677
+ case "set": {
1678
+ if (args[0]) roles.destination = convertValue(args[0]);
1679
+ const toVal = modifiers?.to;
1680
+ if (toVal && typeof toVal === "object" && "type" in toVal) {
1681
+ roles.patient = convertValue(toVal);
1682
+ } else if (args[1]) {
1683
+ roles.patient = convertValue(args[1]);
1684
+ }
1685
+ break;
1686
+ }
1687
+ case "put": {
1688
+ if (args[0]) roles.patient = convertValue(args[0]);
1689
+ if (modifiers) {
1690
+ for (const key of ["into", "before", "after"]) {
1691
+ const val = modifiers[key];
1692
+ if (val && typeof val === "object" && "type" in val) {
1693
+ roles.destination = convertValue(val);
1694
+ roles.method = createLiteral(key);
1695
+ break;
1696
+ }
1697
+ }
1698
+ }
1699
+ if (!roles.destination && target) {
1700
+ roles.destination = convertValue(target);
1701
+ }
1702
+ break;
1703
+ }
1704
+ case "increment":
1705
+ case "decrement": {
1706
+ if (args[0]) roles.destination = convertValue(args[0]);
1707
+ const byVal = modifiers?.by;
1708
+ if (byVal && typeof byVal === "object" && "type" in byVal) {
1709
+ roles.quantity = convertValue(byVal);
1710
+ } else if (args[1]) {
1711
+ roles.quantity = convertValue(args[1]);
1712
+ }
1713
+ break;
1714
+ }
1715
+ case "fetch": {
1716
+ if (args[0]) roles.source = convertValue(args[0]);
1717
+ const asVal = modifiers?.as;
1718
+ if (asVal && typeof asVal === "object" && "type" in asVal) {
1719
+ roles.responseType = convertValue(asVal);
1720
+ } else if (typeof asVal === "string") {
1721
+ roles.responseType = createLiteral(asVal);
1722
+ }
1723
+ break;
1724
+ }
1725
+ case "wait":
1726
+ case "settle": {
1727
+ if (args[0]) roles.duration = convertValue(args[0]);
1728
+ break;
1729
+ }
1730
+ case "toggle":
1731
+ case "add":
1732
+ case "show":
1733
+ case "hide": {
1734
+ if (args[0]) roles.patient = convertValue(args[0]);
1735
+ if (target) roles.destination = convertValue(target);
1736
+ break;
1737
+ }
1738
+ case "remove": {
1739
+ if (args[0]) roles.patient = convertValue(args[0]);
1740
+ if (target) roles.source = convertValue(target);
1741
+ break;
1742
+ }
1743
+ case "send":
1744
+ case "trigger": {
1745
+ if (args[0]) roles.patient = convertValue(args[0]);
1746
+ if (target) roles.destination = convertValue(target);
1747
+ break;
1748
+ }
1749
+ case "log": {
1750
+ if (args[0]) roles.patient = convertValue(args[0]);
1751
+ break;
1752
+ }
1753
+ default: {
1754
+ if (args[0]) roles.patient = convertValue(args[0]);
1755
+ if (target) roles.destination = convertValue(target);
1756
+ break;
1757
+ }
1758
+ }
1759
+ return roles;
1760
+ }
1761
+ function inferSelectorKind2(value) {
1762
+ if (!value) return void 0;
1763
+ if (value.startsWith("#")) return "id";
1764
+ if (value.startsWith(".")) return "class";
1765
+ if (value.startsWith("[")) return "attribute";
1766
+ if (value.startsWith("<") || value.startsWith("*")) return "element";
1767
+ if (/[>\+~ ]/.test(value) && value.length > 1) return "complex";
1768
+ return void 0;
1769
+ }
1770
+ function inferValueFromString(value) {
1771
+ if (DEFAULT_REFERENCES.has(value)) return createReference(value);
1772
+ const kind = inferSelectorKind2(value);
1773
+ if (kind) return createSelector(value, kind);
1774
+ return createLiteral(value);
1775
+ }
1776
+ function bridgeEventModifiers(mods) {
1777
+ const once = mods.once === true ? true : void 0;
1778
+ const debounce = typeof mods.debounce === "number" ? mods.debounce : void 0;
1779
+ const throttle = typeof mods.throttle === "number" ? mods.throttle : void 0;
1780
+ const from = typeof mods.from === "string" ? inferValueFromString(mods.from) : void 0;
1781
+ if (once === void 0 && debounce === void 0 && throttle === void 0 && from === void 0) {
1782
+ return void 0;
1783
+ }
1784
+ return {
1785
+ ...once !== void 0 ? { once } : {},
1786
+ ...debounce !== void 0 ? { debounce } : {},
1787
+ ...throttle !== void 0 ? { throttle } : {},
1788
+ ...from !== void 0 ? { from } : {}
1789
+ };
1790
+ }
1791
+
1792
+ // src/ir/to-runtime-ast.ts
1793
+ function semanticNodeToRuntimeAST(node) {
1794
+ switch (node.kind) {
1795
+ case "event-handler":
1796
+ return convertEventHandler(node);
1797
+ case "conditional":
1798
+ return convertConditional(node);
1799
+ case "loop":
1800
+ return convertLoop(node);
1801
+ case "compound": {
1802
+ const compound = node;
1803
+ const commands = (compound.statements ?? []).map(semanticNodeToRuntimeAST);
1804
+ return {
1805
+ type: "CommandSequence",
1806
+ commands
1807
+ };
1808
+ }
1809
+ case "command":
1810
+ default:
1811
+ return convertCommand2(node);
1812
+ }
1813
+ }
1814
+ function convertEventHandler(eh) {
1815
+ const eventValue = eh.roles.get("event");
1816
+ const eventName = eventValue && "value" in eventValue ? String(eventValue.value) : "click";
1817
+ const modifiers = {};
1818
+ if (eh.eventModifiers) {
1819
+ if (eh.eventModifiers.once) modifiers.once = true;
1820
+ if (eh.eventModifiers.debounce) modifiers.debounce = eh.eventModifiers.debounce;
1821
+ if (eh.eventModifiers.throttle) modifiers.throttle = eh.eventModifiers.throttle;
1822
+ if (eh.eventModifiers.queue) modifiers.queue = eh.eventModifiers.queue;
1823
+ }
1824
+ return {
1825
+ type: "event",
1826
+ event: eventName,
1827
+ modifiers,
1828
+ body: (eh.body ?? []).map(semanticNodeToRuntimeAST)
1829
+ };
1830
+ }
1831
+ function convertCommand2(node) {
1832
+ const roles = {};
1833
+ const args = [];
1834
+ for (const [roleName, value] of node.roles) {
1835
+ const astValue = semanticValueToAST(value);
1836
+ roles[roleName] = astValue;
1837
+ args.push(astValue);
1838
+ }
1839
+ return {
1840
+ type: "command",
1841
+ name: node.action,
1842
+ args,
1843
+ roles
1844
+ };
1845
+ }
1846
+ function convertConditional(node) {
1847
+ const conditionValue = node.roles.get("condition");
1848
+ return {
1849
+ type: "command",
1850
+ name: "if",
1851
+ args: conditionValue ? [semanticValueToAST(conditionValue)] : [],
1852
+ condition: conditionValue ? semanticValueToAST(conditionValue) : void 0,
1853
+ thenBranch: (node.thenBranch ?? []).map(semanticNodeToRuntimeAST),
1854
+ elseBranch: (node.elseBranch ?? []).map(semanticNodeToRuntimeAST)
1855
+ };
1856
+ }
1857
+ function convertLoop(node) {
1858
+ const countValue = node.roles.get("patient") || node.roles.get("count");
1859
+ return {
1860
+ type: "command",
1861
+ name: "repeat",
1862
+ args: countValue ? [semanticValueToAST(countValue)] : [],
1863
+ loopVariant: node.loopVariant ?? "forever",
1864
+ body: (node.body ?? []).map(semanticNodeToRuntimeAST),
1865
+ loopVariable: node.loopVariable,
1866
+ indexVariable: node.indexVariable
1867
+ };
1868
+ }
1869
+ function semanticValueToAST(value) {
1870
+ switch (value.type) {
1871
+ case "selector":
1872
+ return { type: "selector", value: value.value };
1873
+ case "reference":
1874
+ return { type: "identifier", value: value.value };
1875
+ case "literal": {
1876
+ const lit = value;
1877
+ if (lit.dataType === "duration") {
1878
+ const str = String(lit.value);
1879
+ const match = /^(\d+(?:\.\d+)?)(ms|s)$/.exec(str);
1880
+ if (match) {
1881
+ const ms = match[2] === "s" ? parseFloat(match[1]) * 1e3 : parseFloat(match[1]);
1882
+ return { type: "literal", value: ms };
1883
+ }
1884
+ }
1885
+ return { type: "literal", value: lit.value };
1886
+ }
1887
+ case "property-path": {
1888
+ const pp = value;
1889
+ const objectAST = semanticValueToAST(pp.object);
1890
+ return {
1891
+ type: "member",
1892
+ object: objectAST,
1893
+ property: pp.property
1894
+ };
1895
+ }
1896
+ case "expression": {
1897
+ const expr = value;
1898
+ return { type: "expression", value: expr.raw };
1899
+ }
1900
+ case "flag": {
1901
+ const flag = value;
1902
+ return { type: "literal", value: flag.enabled };
1903
+ }
1904
+ default:
1905
+ return { type: "literal", value: String(value.value ?? "") };
1906
+ }
1907
+ }
1908
+ export {
1909
+ DEFAULT_REFERENCES,
1910
+ convertValue,
1911
+ fromEnvelopeJSON,
1912
+ fromInterchangeNode,
1913
+ fromProtocolJSON,
1914
+ isCompoundSyntax,
1915
+ isDocumentSyntax,
1916
+ isEnvelope,
1917
+ isExplicitSyntax,
1918
+ isValidReference,
1919
+ jsonToSemanticNode,
1920
+ parseCompound,
1921
+ parseDocument,
1922
+ parseExplicit,
1923
+ renderDocument,
1924
+ renderExplicit,
1925
+ renderExpr,
1926
+ semanticNodeToJSON,
1927
+ semanticNodeToRuntimeAST,
1928
+ semanticValueToAST,
1929
+ toEnvelopeJSON,
1930
+ toProtocolJSON,
1931
+ validateProtocolJSON,
1932
+ validateSemanticJSON
1933
+ };
1934
+ //# sourceMappingURL=index.js.map