@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
package/dist/index.cjs CHANGED
@@ -21,10 +21,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  AOTOrchestrator: () => AOTOrchestrator,
24
+ BaseMorphologicalNormalizer: () => BaseMorphologicalNormalizer,
24
25
  BaseTokenizer: () => BaseTokenizer,
25
26
  CrossDomainDispatcher: () => CrossDomainDispatcher,
26
27
  DEFAULT_OPERATORS: () => DEFAULT_OPERATORS,
27
28
  DEFAULT_PUNCTUATION: () => DEFAULT_PUNCTUATION,
29
+ DEFAULT_REFERENCES: () => DEFAULT_REFERENCES,
28
30
  DomainAwareScanner: () => DomainAwareScanner,
29
31
  DomainRegistry: () => DomainRegistry,
30
32
  GrammarTransformer: () => GrammarTransformer,
@@ -36,24 +38,31 @@ __export(index_exports, {
36
38
  NumberExtractor: () => NumberExtractor,
37
39
  OperatorExtractor: () => OperatorExtractor,
38
40
  PatternMatcher: () => PatternMatcher,
41
+ PatternTracker: () => PatternTracker,
39
42
  PunctuationExtractor: () => PunctuationExtractor,
40
43
  StringLiteralExtractor: () => StringLiteralExtractor,
41
44
  TokenStreamImpl: () => TokenStreamImpl,
42
45
  UnicodeIdentifierExtractor: () => UnicodeIdentifierExtractor,
43
46
  WhitespaceExtractor: () => WhitespaceExtractor,
44
47
  accumulateBlocks: () => accumulateBlocks,
48
+ buildDisambiguation: () => buildDisambiguation,
49
+ buildFeedback: () => buildFeedback,
45
50
  buildPhrase: () => buildPhrase,
46
51
  buildTablesFromProfiles: () => buildTablesFromProfiles,
47
52
  combineClassifiers: () => combineClassifiers,
53
+ convertValue: () => convertValue,
54
+ createAsyncNode: () => createAsyncNode,
48
55
  createCommandNode: () => createCommandNode,
49
56
  createCompoundNode: () => createCompoundNode,
50
57
  createConditionalNode: () => createConditionalNode,
51
58
  createDiagnosticCollector: () => createDiagnosticCollector,
52
59
  createEventHandlerNode: () => createEventHandlerNode,
53
60
  createExpression: () => createExpression,
61
+ createFlag: () => createFlag,
54
62
  createLatinCharClassifiers: () => createLatinCharClassifiers,
55
63
  createLiteral: () => createLiteral,
56
64
  createLoopNode: () => createLoopNode,
65
+ createMatchNode: () => createMatchNode,
57
66
  createMultiStatementParser: () => createMultiStatementParser,
58
67
  createMultilingualDSL: () => createMultilingualDSL,
59
68
  createPosition: () => createPosition,
@@ -64,6 +73,7 @@ __export(index_exports, {
64
73
  createSimpleTokenizer: () => createSimpleTokenizer,
65
74
  createToken: () => createToken,
66
75
  createTokenizerContext: () => createTokenizerContext,
76
+ createTryNode: () => createTryNode,
67
77
  createUnicodeRangeClassifier: () => createUnicodeRangeClassifier,
68
78
  defineCommand: () => defineCommand,
69
79
  defineRole: () => defineRole,
@@ -75,9 +85,15 @@ __export(index_exports, {
75
85
  extractUrl: () => extractUrl,
76
86
  extractValue: () => extractValue,
77
87
  filterBySeverity: () => filterBySeverity,
88
+ fromEnvelopeJSON: () => fromEnvelopeJSON,
78
89
  fromError: () => fromError,
90
+ fromInterchangeNode: () => fromInterchangeNode,
91
+ fromProtocolJSON: () => fromProtocolJSON,
92
+ generateExamples: () => generateExamples,
79
93
  generatePattern: () => generatePattern,
80
94
  generatePatternVariants: () => generatePatternVariants,
95
+ generatePrompt: () => generatePrompt,
96
+ generateProtocolReference: () => generateProtocolReference,
81
97
  getAllPossessiveKeywords: () => getAllPossessiveKeywords,
82
98
  getDefaultExtractors: () => getDefaultExtractors,
83
99
  getPossessiveReference: () => getPossessiveReference,
@@ -88,8 +104,12 @@ __export(index_exports, {
88
104
  isCSSPropertyRef: () => isCSSPropertyRef,
89
105
  isCSSSelector: () => isCSSSelector,
90
106
  isClassName: () => isClassName,
107
+ isCompoundSyntax: () => isCompoundSyntax,
91
108
  isContextAwareExtractor: () => isContextAwareExtractor,
92
109
  isDigit: () => isDigit,
110
+ isDocumentSyntax: () => isDocumentSyntax,
111
+ isEnvelope: () => isEnvelope,
112
+ isExplicitSyntax: () => isExplicitSyntax,
93
113
  isIdSelector: () => isIdSelector,
94
114
  isNumericValue: () => isNumericValue,
95
115
  isPossessiveKeyword: () => isPossessiveKeyword,
@@ -99,17 +119,38 @@ __export(index_exports, {
99
119
  isSelectorStart: () => isSelectorStart,
100
120
  isTypeCompatible: () => isTypeCompatible,
101
121
  isUrlStart: () => isUrlStart,
122
+ isValidReference: () => isValidReference2,
102
123
  isVariableRef: () => isVariableRef,
103
124
  isWhitespace: () => isWhitespace,
104
125
  joinTokens: () => joinTokens,
126
+ jsonToSemanticNode: () => jsonToSemanticNode,
105
127
  lookupKeyword: () => lookupKeyword,
106
128
  lookupMarker: () => lookupMarker,
107
129
  matchBest: () => matchBest,
108
130
  matchPattern: () => matchPattern,
131
+ needsDisambiguation: () => needsDisambiguation,
109
132
  noChange: () => noChange,
110
133
  normalized: () => normalized,
134
+ parseCompound: () => parseCompound,
135
+ parseDocument: () => parseDocument,
136
+ parseExplicit: () => parseExplicit,
137
+ parseJSONL: () => parseJSONL,
111
138
  patternMatcher: () => patternMatcher,
139
+ registryToAOTBackends: () => registryToAOTBackends,
140
+ renderDocument: () => renderDocument,
141
+ renderExplicit: () => renderExplicit,
142
+ renderExpr: () => renderExpr,
112
143
  reorderRoles: () => reorderRoles,
144
+ semanticNodeToJSON: () => semanticNodeToJSON,
145
+ semanticNodeToRuntimeAST: () => semanticNodeToRuntimeAST,
146
+ semanticValueToAST: () => semanticValueToAST,
147
+ synthesizeFromSchemas: () => synthesizeFromSchemas,
148
+ toEnvelopeJSON: () => toEnvelopeJSON,
149
+ toJSONL: () => toJSONL,
150
+ toJSONLRow: () => toJSONLRow,
151
+ toProtocolJSON: () => toProtocolJSON,
152
+ validateProtocolJSON: () => validateProtocolJSON,
153
+ validateSemanticJSON: () => validateSemanticJSON,
113
154
  validateValueType: () => validateValueType,
114
155
  withDefaultExtractors: () => withDefaultExtractors
115
156
  });
@@ -131,6 +172,9 @@ function createPropertyPath(object, property) {
131
172
  function createExpression(raw) {
132
173
  return { type: "expression", raw };
133
174
  }
175
+ function createFlag(name, enabled = true) {
176
+ return { type: "flag", name, enabled };
177
+ }
134
178
  function createCommandNode(action, roles, metadata) {
135
179
  const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
136
180
  const node = {
@@ -182,6 +226,7 @@ function createCompoundNode(statements, chainType = "sequential", metadata) {
182
226
  return metadata ? { ...base, metadata } : base;
183
227
  }
184
228
  function extractValue(value) {
229
+ if (value.type === "flag") return value.name;
185
230
  if ("raw" in value && value.raw !== void 0) return String(value.raw);
186
231
  if ("value" in value && value.value !== void 0) return String(value.value);
187
232
  if (value.type === "property-path") return `${extractValue(value.object)}.${value.property}`;
@@ -208,6 +253,38 @@ function createLoopNode(action, roles, loopVariant, body, loopVariable, indexVar
208
253
  ...metadata && { metadata }
209
254
  };
210
255
  }
256
+ function createTryNode(body, catchBranch, finallyBranch, metadata) {
257
+ return {
258
+ kind: "command",
259
+ action: "try",
260
+ roles: /* @__PURE__ */ new Map(),
261
+ body,
262
+ ...catchBranch && catchBranch.length > 0 ? { catchBranch } : {},
263
+ ...finallyBranch && finallyBranch.length > 0 ? { finallyBranch } : {},
264
+ ...metadata ? { metadata } : {}
265
+ };
266
+ }
267
+ function createAsyncNode(variant, asyncBody, metadata) {
268
+ return {
269
+ kind: "command",
270
+ action: variant,
271
+ roles: /* @__PURE__ */ new Map(),
272
+ asyncVariant: variant,
273
+ asyncBody,
274
+ ...metadata ? { metadata } : {}
275
+ };
276
+ }
277
+ function createMatchNode(roles, arms, defaultArm, metadata) {
278
+ const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
279
+ return {
280
+ kind: "command",
281
+ action: "match",
282
+ roles: rolesMap,
283
+ arms,
284
+ ...defaultArm && defaultArm.length > 0 ? { defaultArm } : {},
285
+ ...metadata ? { metadata } : {}
286
+ };
287
+ }
211
288
 
212
289
  // src/core/logger.ts
213
290
  var DebugLogger = class {
@@ -463,7 +540,7 @@ var _PatternMatcher = class _PatternMatcher {
463
540
  * a token and the immediately following pattern token fails, the matcher
464
541
  * resets to before the optional role and retries the failed token.
465
542
  */
466
- matchTokenSequence(tokens, patternTokens, captured) {
543
+ matchTokenSequence(tokens, patternTokens, captured, parentStopMarkers) {
467
544
  const firstPatternToken = patternTokens[0];
468
545
  const patternExpectsConjunction = firstPatternToken?.type === "literal" && (firstPatternToken.value === "and" || firstPatternToken.value === "then" || firstPatternToken.alternatives?.includes("and") || firstPatternToken.alternatives?.includes("then"));
469
546
  if (this.currentProfile?.code === "ar" && !patternExpectsConjunction) {
@@ -487,6 +564,9 @@ var _PatternMatcher = class _PatternMatcher {
487
564
  );
488
565
  if (patternToken.type === "role" && patternToken.greedy) {
489
566
  const stopMarkers = this.collectStopMarkers(patternTokens, i + 1);
567
+ if (parentStopMarkers) {
568
+ for (const m of parentStopMarkers) stopMarkers.add(m);
569
+ }
490
570
  const values = [];
491
571
  while (!tokens.isAtEnd()) {
492
572
  const nextToken = tokens.peek();
@@ -506,8 +586,58 @@ var _PatternMatcher = class _PatternMatcher {
506
586
  return false;
507
587
  }
508
588
  }
589
+ if (patternToken.type === "group") {
590
+ const siblingStopMarkers = this.collectStopMarkers(patternTokens, i + 1);
591
+ if (parentStopMarkers) {
592
+ for (const m of parentStopMarkers) siblingStopMarkers.add(m);
593
+ }
594
+ const groupMatched = this.matchGroupToken(
595
+ tokens,
596
+ patternToken,
597
+ captured,
598
+ siblingStopMarkers
599
+ );
600
+ if (groupMatched) {
601
+ prevOptionalMark = null;
602
+ prevOptionalRole = null;
603
+ continue;
604
+ }
605
+ if (this.isOptional(patternToken)) {
606
+ continue;
607
+ }
608
+ return false;
609
+ }
509
610
  const isOptionalRole = patternToken.type === "role" && patternToken.optional === true;
510
611
  const markBefore = isOptionalRole ? tokens.mark() : null;
612
+ const isRoleToken = patternToken.type === "role";
613
+ if (isRoleToken && !isOptionalRole) {
614
+ const nextPT = i + 1 < patternTokens.length ? patternTokens[i + 1] : null;
615
+ if (nextPT?.type === "literal") {
616
+ const currentToken = tokens.peek();
617
+ if (currentToken && this.getMatchType(currentToken, nextPT.value) !== "none") {
618
+ this.logger.debug(
619
+ " >> MARKER-STEAL GUARD: current token matches next literal, skipping role"
620
+ );
621
+ this.logger.debug(" >> Token match FAILED");
622
+ if (this.isOptional(patternToken)) {
623
+ continue;
624
+ }
625
+ if (prevOptionalMark && prevOptionalRole) {
626
+ this.logger.debug(" >> BACKTRACKING: undoing optional role", prevOptionalRole);
627
+ tokens.reset(prevOptionalMark);
628
+ captured.delete(prevOptionalRole);
629
+ prevOptionalMark = null;
630
+ prevOptionalRole = null;
631
+ const retryMatched = this.matchPatternToken(tokens, patternToken, captured);
632
+ this.logger.debug(" >> Backtrack retry result:", retryMatched);
633
+ if (retryMatched) {
634
+ continue;
635
+ }
636
+ }
637
+ return false;
638
+ }
639
+ }
640
+ }
511
641
  const matched = this.matchPatternToken(tokens, patternToken, captured);
512
642
  this.logger.debug(" >> Match result:", matched);
513
643
  if (matched) {
@@ -628,7 +758,7 @@ var _PatternMatcher = class _PatternMatcher {
628
758
  const possessiveValue = this.tryMatchPossessiveExpression(tokens);
629
759
  if (possessiveValue) {
630
760
  if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
631
- if (!patternToken.expectedTypes.includes(possessiveValue.type) && !patternToken.expectedTypes.includes("expression")) {
761
+ if (!isTypeCompatible(possessiveValue.type, patternToken.expectedTypes)) {
632
762
  return patternToken.optional || false;
633
763
  }
634
764
  }
@@ -638,7 +768,7 @@ var _PatternMatcher = class _PatternMatcher {
638
768
  const methodCallValue = this.tryMatchMethodCallExpression(tokens);
639
769
  if (methodCallValue) {
640
770
  if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
641
- if (!patternToken.expectedTypes.includes(methodCallValue.type) && !patternToken.expectedTypes.includes("expression")) {
771
+ if (!isTypeCompatible(methodCallValue.type, patternToken.expectedTypes)) {
642
772
  return patternToken.optional || false;
643
773
  }
644
774
  }
@@ -658,7 +788,7 @@ var _PatternMatcher = class _PatternMatcher {
658
788
  const propertyAccessValue = this.tryMatchPropertyAccessExpression(tokens);
659
789
  if (propertyAccessValue) {
660
790
  if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
661
- if (!patternToken.expectedTypes.includes(propertyAccessValue.type) && !patternToken.expectedTypes.includes("expression")) {
791
+ if (!isTypeCompatible(propertyAccessValue.type, patternToken.expectedTypes)) {
662
792
  return patternToken.optional || false;
663
793
  }
664
794
  }
@@ -978,10 +1108,15 @@ var _PatternMatcher = class _PatternMatcher {
978
1108
  * (e.g., a value like 'hello') to sit between groups without blocking later
979
1109
  * marker matches.
980
1110
  */
981
- matchGroupToken(tokens, patternToken, captured) {
1111
+ matchGroupToken(tokens, patternToken, captured, parentStopMarkers) {
982
1112
  const mark = tokens.mark();
983
1113
  const capturedBefore = new Set(captured.keys());
984
- const success = this.matchTokenSequence(tokens, patternToken.tokens, captured);
1114
+ const success = this.matchTokenSequence(
1115
+ tokens,
1116
+ patternToken.tokens,
1117
+ captured,
1118
+ parentStopMarkers
1119
+ );
985
1120
  if (success) return true;
986
1121
  tokens.reset(mark);
987
1122
  for (const role of captured.keys()) {
@@ -1003,7 +1138,12 @@ var _PatternMatcher = class _PatternMatcher {
1003
1138
  "- skipping intervening tokens"
1004
1139
  );
1005
1140
  for (let s = 0; s < offset; s++) tokens.advance();
1006
- const retrySuccess = this.matchTokenSequence(tokens, patternToken.tokens, captured);
1141
+ const retrySuccess = this.matchTokenSequence(
1142
+ tokens,
1143
+ patternToken.tokens,
1144
+ captured,
1145
+ parentStopMarkers
1146
+ );
1007
1147
  if (retrySuccess) return true;
1008
1148
  tokens.reset(mark);
1009
1149
  for (const role of captured.keys()) {
@@ -1070,7 +1210,7 @@ var _PatternMatcher = class _PatternMatcher {
1070
1210
  break;
1071
1211
  }
1072
1212
  }
1073
- break;
1213
+ if (!pt.optional) break;
1074
1214
  }
1075
1215
  }
1076
1216
  return markers;
@@ -1080,8 +1220,9 @@ var _PatternMatcher = class _PatternMatcher {
1080
1220
  */
1081
1221
  isStopMarker(token, stopMarkers) {
1082
1222
  if (stopMarkers.size === 0) return false;
1083
- const value = (token.normalized || token.value).toLowerCase();
1084
- return stopMarkers.has(value);
1223
+ if (stopMarkers.has(token.value.toLowerCase())) return true;
1224
+ if (token.normalized && stopMarkers.has(token.normalized.toLowerCase())) return true;
1225
+ return false;
1085
1226
  }
1086
1227
  /**
1087
1228
  * Convert a language token to a semantic value.
@@ -1486,7 +1627,7 @@ function addRoleWithMarker(tokens, roleSpec, profile) {
1486
1627
  if (marker) {
1487
1628
  const markerInfo = profile.roleMarkers?.[roleSpec.role];
1488
1629
  const defaultPosition = profile.wordOrder === "SOV" ? "after" : "before";
1489
- const position = markerInfo?.position ?? defaultPosition;
1630
+ const position = roleSpec.markerPosition ?? markerInfo?.position ?? defaultPosition;
1490
1631
  if (isOptional) {
1491
1632
  if (position === "before") {
1492
1633
  tokens.push({
@@ -2026,153 +2167,2674 @@ function createTokenizerContext(tokenizer) {
2026
2167
  return ctx;
2027
2168
  }
2028
2169
 
2029
- // src/api/create-dsl.ts
2030
- var DSLRegistry = class {
2031
- constructor(config) {
2032
- this.patterns = /* @__PURE__ */ new Map();
2033
- this.tokenizers = /* @__PURE__ */ new Map();
2034
- this.schemas = config.schemas;
2035
- for (const lang of config.languages) {
2036
- this.registerLanguage(lang);
2170
+ // src/ir/references.ts
2171
+ var DEFAULT_REFERENCES = /* @__PURE__ */ new Set([
2172
+ "me",
2173
+ "you",
2174
+ "it",
2175
+ "result",
2176
+ "event",
2177
+ "target",
2178
+ "body"
2179
+ ]);
2180
+ function isValidReference2(value, referenceSet = DEFAULT_REFERENCES) {
2181
+ return referenceSet.has(value);
2182
+ }
2183
+
2184
+ // src/ir/explicit-parser.ts
2185
+ var STRUCTURAL_ROLES = /* @__PURE__ */ new Set([
2186
+ "body",
2187
+ "then",
2188
+ "else",
2189
+ "condition",
2190
+ "loop-body",
2191
+ "variable",
2192
+ "catch",
2193
+ // v1.2: try/catch/finally
2194
+ "finally"
2195
+ // v1.2: try/catch/finally
2196
+ ]);
2197
+ function isNestedCommand(value) {
2198
+ const inner = value.slice(1, -1);
2199
+ let depth = 0;
2200
+ for (const ch of inner) {
2201
+ if (ch === "[") depth++;
2202
+ else if (ch === "]") depth--;
2203
+ else if (depth === 0 && (ch === " " || ch === ":")) return true;
2204
+ }
2205
+ return false;
2206
+ }
2207
+ function parseExplicit(input, options = {}) {
2208
+ const trimmed = input.trim();
2209
+ const collect = options.collectDiagnostics === true;
2210
+ const diagnostics = [];
2211
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) {
2212
+ throw new Error("Explicit syntax must be wrapped in brackets: [command role:value ...]");
2213
+ }
2214
+ const content = trimmed.slice(1, -1).trim();
2215
+ if (!content) {
2216
+ throw new Error("Empty explicit statement");
2217
+ }
2218
+ const tokens = tokenizeExplicit(content);
2219
+ if (tokens.length === 0) {
2220
+ throw new Error("No command specified in explicit statement");
2221
+ }
2222
+ const command = tokens[0].toLowerCase();
2223
+ const roles = /* @__PURE__ */ new Map();
2224
+ const refSet = options.referenceSet ?? DEFAULT_REFERENCES;
2225
+ const schema = options.schemaLookup?.getSchema(command);
2226
+ const validRoleNames = schema ? new Set(schema.roles.map((r) => r.role)) : null;
2227
+ for (let i = 1; i < tokens.length; i++) {
2228
+ const token = tokens[i];
2229
+ if (token.startsWith("+") || token.startsWith("~")) {
2230
+ const enabled = token.startsWith("+");
2231
+ const flagName = token.slice(1);
2232
+ if (!flagName) {
2233
+ if (collect) {
2234
+ diagnostics.push({
2235
+ severity: "error",
2236
+ code: "EMPTY_FLAG",
2237
+ message: `Empty flag name: "${token}"`,
2238
+ source: "schema"
2239
+ });
2240
+ continue;
2241
+ }
2242
+ throw new Error(`Empty flag name: "${token}"`);
2243
+ }
2244
+ if (validRoleNames && !validRoleNames.has(flagName)) {
2245
+ const roleList = [...validRoleNames].join(", ");
2246
+ if (collect) {
2247
+ diagnostics.push({
2248
+ severity: "error",
2249
+ code: "UNKNOWN_FLAG",
2250
+ message: `Unknown flag "${flagName}" for command "${command}"`,
2251
+ source: "schema",
2252
+ suggestions: [...validRoleNames].map((r) => `+${r}`)
2253
+ });
2254
+ roles.set(flagName, createFlag(flagName, enabled));
2255
+ continue;
2256
+ }
2257
+ throw new Error(
2258
+ `Unknown flag "${flagName}" for command "${command}". Valid roles: ${roleList}`
2259
+ );
2260
+ }
2261
+ roles.set(flagName, createFlag(flagName, enabled));
2262
+ continue;
2263
+ }
2264
+ const colonIndex = token.indexOf(":");
2265
+ if (colonIndex === -1) {
2266
+ if (collect) {
2267
+ diagnostics.push({
2268
+ severity: "error",
2269
+ code: "INVALID_ROLE_FORMAT",
2270
+ message: `Invalid role format: "${token}". Expected role:value or +flag`,
2271
+ source: "schema"
2272
+ });
2273
+ continue;
2274
+ }
2275
+ throw new Error(`Invalid role format: "${token}". Expected role:value or +flag`);
2276
+ }
2277
+ const roleName = token.slice(0, colonIndex);
2278
+ if (validRoleNames && !STRUCTURAL_ROLES.has(roleName) && !validRoleNames.has(roleName)) {
2279
+ const roleList = [...validRoleNames].join(", ");
2280
+ if (collect) {
2281
+ diagnostics.push({
2282
+ severity: "error",
2283
+ code: "UNKNOWN_ROLE",
2284
+ message: `Unknown role "${roleName}" for command "${command}"`,
2285
+ source: "schema",
2286
+ suggestions: [...validRoleNames]
2287
+ });
2288
+ } else {
2289
+ throw new Error(
2290
+ `Unknown role "${roleName}" for command "${command}". Valid roles: ${roleList}`
2291
+ );
2292
+ }
2293
+ }
2294
+ const role = roleName;
2295
+ const valueStr = token.slice(colonIndex + 1);
2296
+ if (STRUCTURAL_ROLES.has(roleName) && valueStr.startsWith("[") && isNestedCommand(valueStr)) {
2297
+ const nestedEnd = findMatchingBracket(token, colonIndex + 1);
2298
+ const nestedSyntax = token.slice(colonIndex + 1, nestedEnd + 1);
2299
+ roles.set(role, { type: "expression", raw: nestedSyntax });
2300
+ continue;
2301
+ }
2302
+ const value = parseExplicitValue(valueStr, refSet);
2303
+ roles.set(role, value);
2304
+ }
2305
+ if (schema && command !== "on") {
2306
+ for (const roleSpec of schema.roles) {
2307
+ if (roleSpec.required && !roles.has(roleSpec.role) && !roleSpec.default) {
2308
+ if (collect) {
2309
+ diagnostics.push({
2310
+ severity: "error",
2311
+ code: "MISSING_REQUIRED_ROLE",
2312
+ message: `Missing required role "${roleSpec.role}" for command "${command}": ${roleSpec.description}`,
2313
+ source: "schema"
2314
+ });
2315
+ } else {
2316
+ throw new Error(
2317
+ `Missing required role "${roleSpec.role}" for command "${command}": ${roleSpec.description}`
2318
+ );
2319
+ }
2320
+ }
2037
2321
  }
2038
2322
  }
2039
- registerLanguage(lang) {
2040
- this.tokenizers.set(lang.code, lang.tokenizer);
2041
- const patterns = [];
2042
- for (const schema of this.schemas) {
2043
- const pattern = generatePattern(schema, lang.patternProfile);
2044
- patterns.push(pattern);
2323
+ const attachDiagnostics = (node) => {
2324
+ if (diagnostics.length === 0) return node;
2325
+ return { ...node, diagnostics };
2326
+ };
2327
+ switch (command) {
2328
+ case "on": {
2329
+ const eventValue = roles.get("event");
2330
+ if (!eventValue) {
2331
+ if (collect) {
2332
+ diagnostics.push({
2333
+ severity: "error",
2334
+ code: "MISSING_EVENT_ROLE",
2335
+ message: "Event handler requires event role: [on event:click ...]",
2336
+ source: "schema"
2337
+ });
2338
+ return attachDiagnostics(
2339
+ createEventHandlerNode("on", roles, [], {
2340
+ sourceLanguage: "explicit"
2341
+ })
2342
+ );
2343
+ }
2344
+ throw new Error("Event handler requires event role: [on event:click ...]");
2345
+ }
2346
+ const body = extractStructuralBody(roles, "body", options);
2347
+ roles.delete("body");
2348
+ return attachDiagnostics(
2349
+ createEventHandlerNode("on", roles, body, {
2350
+ sourceLanguage: "explicit"
2351
+ })
2352
+ );
2045
2353
  }
2046
- this.patterns.set(lang.code, patterns);
2354
+ case "if": {
2355
+ const thenBranch = extractStructuralBody(roles, "then", options);
2356
+ const elseBranch = extractStructuralBody(roles, "else", options);
2357
+ roles.delete("then");
2358
+ roles.delete("else");
2359
+ return attachDiagnostics(
2360
+ createConditionalNode(
2361
+ command,
2362
+ roles,
2363
+ thenBranch,
2364
+ elseBranch.length > 0 ? elseBranch : void 0,
2365
+ {
2366
+ sourceLanguage: "explicit"
2367
+ }
2368
+ )
2369
+ );
2370
+ }
2371
+ case "repeat": {
2372
+ const loopBody = extractStructuralBody(roles, "loop-body", options);
2373
+ const loopVariantValue = roles.get("loop-variant");
2374
+ const variableValue = roles.get("variable");
2375
+ const indexVariableValue = roles.get("index-variable");
2376
+ roles.delete("loop-body");
2377
+ roles.delete("loop-variant");
2378
+ roles.delete("variable");
2379
+ roles.delete("index-variable");
2380
+ let variant;
2381
+ if (loopVariantValue && loopVariantValue.type === "literal" && typeof loopVariantValue.value === "string") {
2382
+ variant = loopVariantValue.value;
2383
+ } else if (roles.has("quantity")) {
2384
+ variant = "times";
2385
+ } else if (roles.has("source")) {
2386
+ variant = "for";
2387
+ } else if (roles.has("condition")) {
2388
+ variant = "while";
2389
+ } else {
2390
+ variant = "forever";
2391
+ }
2392
+ const loopVariable = variableValue?.type === "literal" && typeof variableValue.value === "string" ? variableValue.value : variableValue?.type === "expression" ? variableValue.raw : void 0;
2393
+ const indexVariable = indexVariableValue?.type === "literal" && typeof indexVariableValue.value === "string" ? indexVariableValue.value : void 0;
2394
+ return attachDiagnostics(
2395
+ createLoopNode(command, roles, variant, loopBody, loopVariable, indexVariable, {
2396
+ sourceLanguage: "explicit"
2397
+ })
2398
+ );
2399
+ }
2400
+ default:
2401
+ return attachDiagnostics(
2402
+ createCommandNode(command, roles, {
2403
+ sourceLanguage: "explicit"
2404
+ })
2405
+ );
2047
2406
  }
2048
- getPatterns(language) {
2049
- return this.patterns.get(language) || [];
2407
+ }
2408
+ function isExplicitSyntax(input) {
2409
+ const trimmed = input.trim();
2410
+ return trimmed.startsWith("[") && trimmed.endsWith("]");
2411
+ }
2412
+ var CHAIN_KEYWORDS = /* @__PURE__ */ new Set(["then", "and", "async", "sequential"]);
2413
+ function splitCompoundSegments(input) {
2414
+ const segments = [];
2415
+ let chainType = null;
2416
+ let current = "";
2417
+ let bracketDepth = 0;
2418
+ let inString = false;
2419
+ let stringChar = "";
2420
+ for (let i = 0; i < input.length; i++) {
2421
+ const char = input[i];
2422
+ if (inString) {
2423
+ current += char;
2424
+ if (char === stringChar && input[i - 1] !== "\\") {
2425
+ inString = false;
2426
+ }
2427
+ continue;
2428
+ }
2429
+ if (char === '"' || char === "'") {
2430
+ inString = true;
2431
+ stringChar = char;
2432
+ current += char;
2433
+ continue;
2434
+ }
2435
+ if (char === "[") {
2436
+ bracketDepth++;
2437
+ current += char;
2438
+ continue;
2439
+ }
2440
+ if (char === "]") {
2441
+ bracketDepth--;
2442
+ current += char;
2443
+ if (bracketDepth === 0) {
2444
+ const rest = input.slice(i + 1);
2445
+ const pipeMatch = rest.match(/^\s*\|>\s*/);
2446
+ if (pipeMatch) {
2447
+ segments.push(current.trim());
2448
+ current = "";
2449
+ chainType ?? (chainType = "pipe");
2450
+ i += pipeMatch[0].length;
2451
+ continue;
2452
+ }
2453
+ const keywordMatch = rest.match(/^\s+(then|and|async|sequential)\s+/);
2454
+ if (keywordMatch && CHAIN_KEYWORDS.has(keywordMatch[1])) {
2455
+ segments.push(current.trim());
2456
+ current = "";
2457
+ chainType ?? (chainType = keywordMatch[1]);
2458
+ i += keywordMatch[0].length;
2459
+ continue;
2460
+ }
2461
+ }
2462
+ continue;
2463
+ }
2464
+ current += char;
2050
2465
  }
2051
- getTokenizer(language) {
2052
- return this.tokenizers.get(language);
2466
+ if (current.trim()) {
2467
+ segments.push(current.trim());
2053
2468
  }
2054
- getSupportedLanguages() {
2055
- return Array.from(this.patterns.keys());
2469
+ return { segments, chainType };
2470
+ }
2471
+ function parseCompound(input, options) {
2472
+ const trimmed = input.trim();
2473
+ const { annotations, remainder } = extractAnnotations(trimmed);
2474
+ const { segments, chainType } = splitCompoundSegments(remainder);
2475
+ let node;
2476
+ if (segments.length <= 1) {
2477
+ node = parseExplicit(segments[0] || remainder, options);
2478
+ } else {
2479
+ const statements = segments.map((seg) => parseExplicit(seg, options));
2480
+ node = createCompoundNode(statements, chainType ?? "sequential");
2056
2481
  }
2057
- };
2058
- var MultilingualDSLImpl = class {
2059
- constructor(config, registry, transformer) {
2060
- this.registry = registry;
2061
- this.matcher = new PatternMatcher();
2062
- this.transformer = transformer;
2063
- if (config.codeGenerator) {
2064
- this.codeGenerator = config.codeGenerator;
2482
+ if (annotations.length > 0) {
2483
+ return { ...node, annotations };
2484
+ }
2485
+ return node;
2486
+ }
2487
+ function extractAnnotations(input) {
2488
+ const annotations = [];
2489
+ let pos = 0;
2490
+ while (pos < input.length) {
2491
+ while (pos < input.length && (input[pos] === " " || input[pos] === " ")) {
2492
+ pos++;
2065
2493
  }
2494
+ if (pos >= input.length || input[pos] !== "@") {
2495
+ break;
2496
+ }
2497
+ pos++;
2498
+ const nameStart = pos;
2499
+ while (pos < input.length && /[A-Za-z0-9\-_]/.test(input[pos])) {
2500
+ pos++;
2501
+ }
2502
+ const name = input.slice(nameStart, pos);
2503
+ if (!name) break;
2504
+ let value;
2505
+ if (pos < input.length && input[pos] === "(") {
2506
+ pos++;
2507
+ const valueStart = pos;
2508
+ if (pos < input.length && (input[pos] === '"' || input[pos] === "'")) {
2509
+ const quote = input[pos];
2510
+ pos++;
2511
+ while (pos < input.length && input[pos] !== quote) {
2512
+ if (input[pos] === "\\") pos++;
2513
+ pos++;
2514
+ }
2515
+ pos++;
2516
+ value = input.slice(valueStart + 1, pos - 1);
2517
+ } else {
2518
+ while (pos < input.length && input[pos] !== ")") {
2519
+ pos++;
2520
+ }
2521
+ value = input.slice(valueStart, pos);
2522
+ }
2523
+ if (pos < input.length && input[pos] === ")") {
2524
+ pos++;
2525
+ }
2526
+ }
2527
+ annotations.push(value !== void 0 ? { name, value } : { name });
2066
2528
  }
2067
- parse(input, language) {
2068
- const result = this.parseWithConfidence(input, language);
2069
- return result.node;
2529
+ return { annotations, remainder: input.slice(pos).trim() };
2530
+ }
2531
+ var VERSION_HEADER_RE = /^#!lse\s+(\d+\.\d+(?:\.\d+)?)\s*$/;
2532
+ function parseDocument(input, options) {
2533
+ const lines = input.split("\n");
2534
+ let lseVersion = "1.0";
2535
+ const nodes = [];
2536
+ let startLine = 0;
2537
+ for (let i = 0; i < lines.length; i++) {
2538
+ const trimmed = lines[i].trim();
2539
+ if (!trimmed) continue;
2540
+ const versionMatch = trimmed.match(VERSION_HEADER_RE);
2541
+ if (versionMatch) {
2542
+ lseVersion = versionMatch[1];
2543
+ startLine = i + 1;
2544
+ }
2545
+ break;
2546
+ }
2547
+ for (let i = startLine; i < lines.length; i++) {
2548
+ const trimmed = lines[i].trim();
2549
+ if (!trimmed) continue;
2550
+ if (trimmed.startsWith("//") || trimmed.startsWith("#") && !trimmed.startsWith("#!")) {
2551
+ continue;
2552
+ }
2553
+ nodes.push(parseCompound(trimmed, options));
2554
+ }
2555
+ return { lseVersion, nodes };
2556
+ }
2557
+ function isCompoundSyntax(input) {
2558
+ const { segments } = splitCompoundSegments(input.trim());
2559
+ return segments.length > 1;
2560
+ }
2561
+ function isDocumentSyntax(input) {
2562
+ const trimmed = input.trim();
2563
+ if (trimmed.includes("\n")) return true;
2564
+ if (VERSION_HEADER_RE.test(trimmed)) return true;
2565
+ if (trimmed.startsWith("//") || trimmed.startsWith("#") && !trimmed.startsWith("#!")) {
2566
+ return true;
2070
2567
  }
2071
- parseWithConfidence(input, language) {
2072
- const tokenizer = this.registry.getTokenizer(language);
2073
- if (!tokenizer) {
2074
- throw new Error(`No tokenizer registered for language: ${language}`);
2568
+ return false;
2569
+ }
2570
+ function extractStructuralBody(roles, roleName, options) {
2571
+ const value = roles.get(roleName);
2572
+ if (!value) return [];
2573
+ if (value.type === "expression") {
2574
+ return [parseExplicit(value.raw, options)];
2575
+ }
2576
+ return [];
2577
+ }
2578
+ function inferSelectorKind(value) {
2579
+ if (!value) return void 0;
2580
+ if (value.startsWith("#")) return "id";
2581
+ if (value.startsWith(".")) return "class";
2582
+ if (value.startsWith("[")) return "attribute";
2583
+ if (value.startsWith("<") || value.startsWith("*")) return "element";
2584
+ if (/[>+~ ]/.test(value) && value.length > 1) return "complex";
2585
+ return void 0;
2586
+ }
2587
+ function tokenizeExplicit(content) {
2588
+ const tokens = [];
2589
+ let current = "";
2590
+ let inString = false;
2591
+ let stringChar = "";
2592
+ let bracketDepth = 0;
2593
+ for (let i = 0; i < content.length; i++) {
2594
+ const char = content[i];
2595
+ if (inString) {
2596
+ current += char;
2597
+ if (char === stringChar && content[i - 1] !== "\\") {
2598
+ inString = false;
2599
+ }
2600
+ continue;
2601
+ }
2602
+ if (char === '"' || char === "'") {
2603
+ inString = true;
2604
+ stringChar = char;
2605
+ current += char;
2606
+ continue;
2607
+ }
2608
+ if (char === "[") {
2609
+ bracketDepth++;
2610
+ current += char;
2611
+ continue;
2612
+ }
2613
+ if (char === "]") {
2614
+ bracketDepth--;
2615
+ current += char;
2616
+ continue;
2617
+ }
2618
+ if (char === " " && bracketDepth === 0) {
2619
+ if (current) {
2620
+ tokens.push(current);
2621
+ current = "";
2622
+ }
2623
+ continue;
2075
2624
  }
2076
- const tokens = tokenizer.tokenize(input);
2077
- const patterns = this.registry.getPatterns(language);
2078
- const profile = {
2079
- code: language
2080
- };
2081
- for (const pattern of patterns) {
2082
- const match = this.matcher.matchPattern(tokens, pattern, profile);
2083
- if (match) {
2084
- const node = {
2085
- kind: "command",
2086
- action: pattern.command,
2087
- roles: match.captured,
2088
- metadata: {
2089
- sourceLanguage: language,
2090
- sourceText: input,
2091
- patternId: pattern.id,
2092
- confidence: match.confidence
2093
- }
2094
- };
2095
- return { node, confidence: match.confidence };
2625
+ current += char;
2626
+ }
2627
+ if (current) {
2628
+ tokens.push(current);
2629
+ }
2630
+ return tokens;
2631
+ }
2632
+ function parseExplicitValue(valueStr, referenceSet) {
2633
+ if (valueStr.startsWith("#") || valueStr.startsWith(".") || valueStr.startsWith("[") || valueStr.startsWith("@") || valueStr.startsWith("*")) {
2634
+ return createSelector(valueStr, inferSelectorKind(valueStr));
2635
+ }
2636
+ if (valueStr.startsWith('"') || valueStr.startsWith("'")) {
2637
+ const inner = valueStr.slice(1, -1);
2638
+ return createLiteral(inner, "string");
2639
+ }
2640
+ if (valueStr === "true") return createLiteral(true, "boolean");
2641
+ if (valueStr === "false") return createLiteral(false, "boolean");
2642
+ const lowerRef = valueStr.toLowerCase();
2643
+ if (isValidReference2(lowerRef, referenceSet)) {
2644
+ return createReference(lowerRef);
2645
+ }
2646
+ const numMatch = valueStr.match(/^(-?\d+(?:\.\d+)?)(ms|s|m|h)?$/);
2647
+ if (numMatch) {
2648
+ const num = parseFloat(numMatch[1]);
2649
+ const suffix = numMatch[2];
2650
+ if (suffix) {
2651
+ return createLiteral(valueStr, "duration");
2652
+ }
2653
+ return createLiteral(num, "number");
2654
+ }
2655
+ return createLiteral(valueStr);
2656
+ }
2657
+ function findMatchingBracket(str, start) {
2658
+ let depth = 0;
2659
+ for (let i = start; i < str.length; i++) {
2660
+ if (str[i] === "[") depth++;
2661
+ else if (str[i] === "]") {
2662
+ depth--;
2663
+ if (depth === 0) return i;
2664
+ }
2665
+ }
2666
+ return str.length - 1;
2667
+ }
2668
+
2669
+ // src/ir/explicit-renderer.ts
2670
+ function renderExplicit(node) {
2671
+ let result;
2672
+ if (node.kind === "compound") {
2673
+ const compoundNode = node;
2674
+ const renderedStatements = compoundNode.statements.map((stmt) => renderExplicit(stmt));
2675
+ result = renderedStatements.join(` ${compoundNode.chainType} `);
2676
+ } else {
2677
+ result = renderBracketCommand(node);
2678
+ }
2679
+ if (node.annotations && node.annotations.length > 0) {
2680
+ const annParts = node.annotations.map(
2681
+ (ann) => ann.value !== void 0 ? `@${ann.name}(${ann.value})` : `@${ann.name}`
2682
+ );
2683
+ return annParts.join(" ") + " " + result;
2684
+ }
2685
+ return result;
2686
+ }
2687
+ function renderDocument(envelope) {
2688
+ const lines = [];
2689
+ if (envelope.lseVersion && envelope.lseVersion !== "1.0") {
2690
+ lines.push(`#!lse ${envelope.lseVersion}`);
2691
+ }
2692
+ for (const node of envelope.nodes) {
2693
+ lines.push(renderExplicit(node));
2694
+ }
2695
+ return lines.join("\n");
2696
+ }
2697
+ function renderBracketCommand(node) {
2698
+ const parts = [node.action];
2699
+ for (const [role, value] of node.roles) {
2700
+ if (value.type === "flag") {
2701
+ parts.push(value.enabled ? `+${value.name}` : `~${value.name}`);
2702
+ } else {
2703
+ parts.push(`${role}:${valueToString(value)}`);
2704
+ }
2705
+ }
2706
+ if (node.kind === "event-handler") {
2707
+ const eventNode = node;
2708
+ if (eventNode.body && eventNode.body.length > 0) {
2709
+ const bodyParts = eventNode.body.map((n) => renderExplicit(n));
2710
+ parts.push(`body:${bodyParts.join(" ")}`);
2711
+ }
2712
+ }
2713
+ if (node.kind === "conditional") {
2714
+ const condNode = node;
2715
+ if (condNode.thenBranch && condNode.thenBranch.length > 0) {
2716
+ const thenParts = condNode.thenBranch.map((n) => renderExplicit(n));
2717
+ parts.push(`then:${thenParts.join(" ")}`);
2718
+ }
2719
+ if (condNode.elseBranch && condNode.elseBranch.length > 0) {
2720
+ const elseParts = condNode.elseBranch.map((n) => renderExplicit(n));
2721
+ parts.push(`else:${elseParts.join(" ")}`);
2722
+ }
2723
+ }
2724
+ if (node.kind === "loop") {
2725
+ const loopNode = node;
2726
+ if (loopNode.loopVariant) {
2727
+ parts.push(`loop-variant:${loopNode.loopVariant}`);
2728
+ }
2729
+ if (loopNode.loopVariable) {
2730
+ parts.push(`variable:${loopNode.loopVariable}`);
2731
+ }
2732
+ if (loopNode.indexVariable) {
2733
+ parts.push(`index-variable:${loopNode.indexVariable}`);
2734
+ }
2735
+ if (loopNode.body && loopNode.body.length > 0) {
2736
+ const bodyParts = loopNode.body.map((n) => renderExplicit(n));
2737
+ parts.push(`loop-body:${bodyParts.join(" ")}`);
2738
+ }
2739
+ }
2740
+ if (node.kind === "command") {
2741
+ const cmd = node;
2742
+ if (cmd.body && cmd.body.length > 0) {
2743
+ const bodyParts = cmd.body.map((n) => renderExplicit(n));
2744
+ parts.push(`body:${bodyParts.join(" ")}`);
2745
+ }
2746
+ if (cmd.catchBranch && cmd.catchBranch.length > 0) {
2747
+ const catchParts = cmd.catchBranch.map((n) => renderExplicit(n));
2748
+ parts.push(`catch:${catchParts.join(" ")}`);
2749
+ }
2750
+ if (cmd.finallyBranch && cmd.finallyBranch.length > 0) {
2751
+ const finallyParts = cmd.finallyBranch.map((n) => renderExplicit(n));
2752
+ parts.push(`finally:${finallyParts.join(" ")}`);
2753
+ }
2754
+ if (cmd.asyncVariant) {
2755
+ parts.push(`async-variant:${cmd.asyncVariant}`);
2756
+ }
2757
+ if (cmd.asyncBody && cmd.asyncBody.length > 0) {
2758
+ const asyncParts = cmd.asyncBody.map((n) => renderExplicit(n));
2759
+ parts.push(`async-body:${asyncParts.join(" ")}`);
2760
+ }
2761
+ }
2762
+ return `[${parts.join(" ")}]`;
2763
+ }
2764
+ function valueToString(value) {
2765
+ switch (value.type) {
2766
+ case "literal":
2767
+ if (typeof value.value === "string") {
2768
+ if (value.dataType === "string" || /\s/.test(value.value)) {
2769
+ return `"${value.value}"`;
2770
+ }
2771
+ return value.value;
2772
+ }
2773
+ return String(value.value);
2774
+ case "selector":
2775
+ return value.value;
2776
+ case "reference":
2777
+ return value.value;
2778
+ case "property-path":
2779
+ return `${valueToString(value.object)}'s ${value.property}`;
2780
+ case "expression":
2781
+ return value.raw;
2782
+ case "flag":
2783
+ return value.name;
2784
+ }
2785
+ }
2786
+
2787
+ // src/ir/json-schema.ts
2788
+ var VALID_VALUE_TYPES = /* @__PURE__ */ new Set([
2789
+ "selector",
2790
+ "literal",
2791
+ "reference",
2792
+ "expression",
2793
+ "property-path",
2794
+ "flag"
2795
+ ]);
2796
+ function validateSemanticJSON(input) {
2797
+ const diagnostics = [];
2798
+ if (!input.action || typeof input.action !== "string") {
2799
+ diagnostics.push({
2800
+ severity: "error",
2801
+ code: "INVALID_ACTION",
2802
+ message: 'Field "action" is required and must be a string.',
2803
+ suggestion: 'Provide a command name like "toggle", "add", "set", etc.'
2804
+ });
2805
+ return diagnostics;
2806
+ }
2807
+ if (input.roles && typeof input.roles === "object") {
2808
+ for (const [role, value] of Object.entries(input.roles)) {
2809
+ if (!value || typeof value !== "object") {
2810
+ diagnostics.push({
2811
+ severity: "error",
2812
+ code: "INVALID_ROLE_VALUE",
2813
+ message: `Role "${role}" must be an object with "type" and "value" fields.`,
2814
+ suggestion: 'Use: { "type": "selector", "value": ".active" }'
2815
+ });
2816
+ continue;
2817
+ }
2818
+ const v = value;
2819
+ if (!VALID_VALUE_TYPES.has(v.type)) {
2820
+ diagnostics.push({
2821
+ severity: "error",
2822
+ code: "INVALID_VALUE_TYPE",
2823
+ message: `Role "${role}" has invalid type "${v.type}".`,
2824
+ suggestion: "Valid types: selector, literal, reference, expression, property-path."
2825
+ });
2826
+ }
2827
+ if (v.value === void 0 || v.value === null) {
2828
+ diagnostics.push({
2829
+ severity: "error",
2830
+ code: "MISSING_VALUE",
2831
+ message: `Role "${role}" is missing the "value" field.`
2832
+ });
2096
2833
  }
2097
2834
  }
2098
- throw new Error(`No pattern matched for input: ${input}`);
2099
2835
  }
2100
- validate(input, language) {
2101
- try {
2102
- const result = this.parseWithConfidence(input, language);
2103
- return {
2104
- valid: true,
2105
- node: result.node
2106
- };
2107
- } catch (error) {
2108
- return {
2109
- valid: false,
2110
- errors: [error instanceof Error ? error.message : String(error)]
2111
- };
2836
+ if (input.trigger) {
2837
+ if (!input.trigger.event || typeof input.trigger.event !== "string") {
2838
+ diagnostics.push({
2839
+ severity: "error",
2840
+ code: "INVALID_TRIGGER",
2841
+ message: 'Trigger "event" is required and must be a string.',
2842
+ suggestion: 'Use an event name like "click", "mouseover", "keydown".'
2843
+ });
2112
2844
  }
2113
2845
  }
2114
- translate(input, fromLanguage, toLanguage) {
2115
- return this.transformer.transform(input, fromLanguage, toLanguage);
2846
+ return diagnostics;
2847
+ }
2848
+ function jsonToSemanticNode(input) {
2849
+ const roles = /* @__PURE__ */ new Map();
2850
+ if (input.roles) {
2851
+ for (const [role, value] of Object.entries(input.roles)) {
2852
+ roles.set(role, convertJSONValue(value));
2853
+ }
2854
+ }
2855
+ if (input.trigger) {
2856
+ roles.set("event", createLiteral(input.trigger.event, "string"));
2857
+ const bodyRoles = new Map(roles);
2858
+ bodyRoles.delete("event");
2859
+ const bodyNode = createCommandNode(input.action, bodyRoles);
2860
+ return createEventHandlerNode(
2861
+ "on",
2862
+ roles,
2863
+ [bodyNode],
2864
+ { sourceLanguage: "json" },
2865
+ input.trigger.modifiers ?? {}
2866
+ );
2116
2867
  }
2117
- compile(input, language) {
2868
+ return createCommandNode(input.action, roles, {
2869
+ sourceLanguage: "json"
2870
+ });
2871
+ }
2872
+ function semanticNodeToJSON(node) {
2873
+ if (node.kind === "compound") {
2874
+ const compound = node;
2875
+ if (compound.statements.length > 0) {
2876
+ return semanticNodeToJSON(compound.statements[0]);
2877
+ }
2878
+ return { action: "compound", roles: {} };
2879
+ }
2880
+ if (node.kind === "event-handler") {
2881
+ const eventNode = node;
2882
+ const bodyAction = eventNode.body?.[0]?.action ?? node.action;
2883
+ const roles2 = {};
2884
+ if (eventNode.body?.[0]) {
2885
+ for (const [role, value] of eventNode.body[0].roles) {
2886
+ roles2[role] = semanticValueToJSON(value);
2887
+ }
2888
+ }
2889
+ const eventValue = node.roles.get("event");
2890
+ const eventName = eventValue ? "value" in eventValue ? String(eventValue.value) : "raw" in eventValue ? eventValue.raw : "unknown" : "unknown";
2891
+ const result = {
2892
+ action: bodyAction === "on" ? eventNode.body?.[0]?.action ?? "on" : bodyAction,
2893
+ roles: roles2,
2894
+ trigger: {
2895
+ event: eventName
2896
+ }
2897
+ };
2898
+ if (eventNode.eventModifiers && Object.keys(eventNode.eventModifiers).length > 0) {
2899
+ result.trigger.modifiers = eventNode.eventModifiers;
2900
+ }
2901
+ return result;
2902
+ }
2903
+ const roles = {};
2904
+ for (const [role, value] of node.roles) {
2905
+ roles[role] = semanticValueToJSON(value);
2906
+ }
2907
+ return { action: node.action, roles };
2908
+ }
2909
+ function convertJSONValue(value) {
2910
+ switch (value.type) {
2911
+ case "selector":
2912
+ return createSelector(String(value.value), detectSelectorKind(String(value.value)));
2913
+ case "literal":
2914
+ return createLiteral(
2915
+ value.value,
2916
+ typeof value.value === "number" ? "number" : typeof value.value === "boolean" ? "boolean" : "string"
2917
+ );
2918
+ case "reference":
2919
+ return createReference(String(value.value));
2920
+ case "expression":
2921
+ return createExpression(String(value.value));
2922
+ case "property-path":
2923
+ return createExpression(String(value.value));
2924
+ case "flag":
2925
+ return createFlag(String(value.value), true);
2926
+ default:
2927
+ return createLiteral(String(value.value), "string");
2928
+ }
2929
+ }
2930
+ function semanticValueToJSON(value) {
2931
+ switch (value.type) {
2932
+ case "selector":
2933
+ return { type: "selector", value: value.value };
2934
+ case "literal":
2935
+ return { type: "literal", value: value.value };
2936
+ case "reference":
2937
+ return { type: "reference", value: value.value };
2938
+ case "expression":
2939
+ return { type: "expression", value: value.raw };
2940
+ case "property-path":
2941
+ return {
2942
+ type: "property-path",
2943
+ value: `${semanticValueToJSON(value.object).value}.${value.property}`
2944
+ };
2945
+ case "flag":
2946
+ return { type: "flag", value: value.enabled };
2947
+ }
2948
+ }
2949
+ function detectSelectorKind(selector) {
2950
+ if (selector.startsWith("#")) return "id";
2951
+ if (selector.startsWith(".")) return "class";
2952
+ if (selector.startsWith("[")) return "attribute";
2953
+ return "complex";
2954
+ }
2955
+
2956
+ // src/ir/protocol-json.ts
2957
+ function toProtocolJSON(node) {
2958
+ const roles = rolesToProtocol(node.roles);
2959
+ let result;
2960
+ switch (node.kind) {
2961
+ case "command": {
2962
+ const cmd = node;
2963
+ result = { kind: "command", action: node.action, roles };
2964
+ if (cmd.body && cmd.body.length > 0) {
2965
+ result.body = cmd.body.map(toProtocolJSON);
2966
+ }
2967
+ if (cmd.catchBranch && cmd.catchBranch.length > 0) {
2968
+ result.catchBranch = cmd.catchBranch.map(toProtocolJSON);
2969
+ }
2970
+ if (cmd.finallyBranch && cmd.finallyBranch.length > 0) {
2971
+ result.finallyBranch = cmd.finallyBranch.map(toProtocolJSON);
2972
+ }
2973
+ if (cmd.asyncVariant) {
2974
+ result.asyncVariant = cmd.asyncVariant;
2975
+ }
2976
+ if (cmd.asyncBody && cmd.asyncBody.length > 0) {
2977
+ result.asyncBody = cmd.asyncBody.map(toProtocolJSON);
2978
+ }
2979
+ if (cmd.arms && cmd.arms.length > 0) {
2980
+ result.arms = cmd.arms.map((arm) => ({
2981
+ pattern: valueToProtocol(arm.pattern),
2982
+ body: arm.body.map(toProtocolJSON)
2983
+ }));
2984
+ }
2985
+ if (cmd.defaultArm && cmd.defaultArm.length > 0) {
2986
+ result.defaultArm = cmd.defaultArm.map(toProtocolJSON);
2987
+ }
2988
+ break;
2989
+ }
2990
+ case "event-handler": {
2991
+ const eh = node;
2992
+ result = {
2993
+ kind: "event-handler",
2994
+ action: node.action,
2995
+ roles,
2996
+ body: eh.body.map(toProtocolJSON)
2997
+ };
2998
+ break;
2999
+ }
3000
+ case "compound": {
3001
+ const c = node;
3002
+ result = {
3003
+ kind: "compound",
3004
+ action: "compound",
3005
+ statements: c.statements.map(toProtocolJSON),
3006
+ chainType: c.chainType
3007
+ };
3008
+ break;
3009
+ }
3010
+ case "conditional": {
3011
+ const cond = node;
3012
+ result = { kind: "command", action: node.action, roles };
3013
+ result.thenBranch = cond.thenBranch.map(toProtocolJSON);
3014
+ if (cond.elseBranch && cond.elseBranch.length > 0) {
3015
+ result.elseBranch = cond.elseBranch.map(toProtocolJSON);
3016
+ }
3017
+ break;
3018
+ }
3019
+ case "loop": {
3020
+ const loop = node;
3021
+ result = { kind: "command", action: node.action, roles };
3022
+ result.loopVariant = loop.loopVariant;
3023
+ result.loopBody = loop.body.map(toProtocolJSON);
3024
+ if (loop.loopVariable) result.loopVariable = loop.loopVariable;
3025
+ if (loop.indexVariable) result.indexVariable = loop.indexVariable;
3026
+ break;
3027
+ }
3028
+ }
3029
+ if (node.annotations && node.annotations.length > 0) {
3030
+ result.annotations = node.annotations.map(
3031
+ (a) => a.value !== void 0 ? { name: a.name, value: a.value } : { name: a.name }
3032
+ );
3033
+ }
3034
+ if (node.diagnostics && node.diagnostics.length > 0) {
3035
+ result.diagnostics = node.diagnostics.map((d) => {
3036
+ const json = {
3037
+ level: d.severity,
3038
+ message: d.message
3039
+ };
3040
+ if (d.code) json.code = d.code;
3041
+ if (d.source) json.source = d.source;
3042
+ if (d.suggestions && d.suggestions.length > 0) json.suggestions = [...d.suggestions];
3043
+ return json;
3044
+ });
3045
+ }
3046
+ return result;
3047
+ }
3048
+ function rolesToProtocol(roles) {
3049
+ const result = {};
3050
+ for (const [role, value] of roles) {
3051
+ result[role] = valueToProtocol(value);
3052
+ }
3053
+ return result;
3054
+ }
3055
+ function valueToProtocol(value) {
3056
+ switch (value.type) {
3057
+ case "selector": {
3058
+ const PROTOCOL_SELECTOR_KINDS = /* @__PURE__ */ new Set([
3059
+ "id",
3060
+ "class",
3061
+ "attribute",
3062
+ "element",
3063
+ "complex"
3064
+ ]);
3065
+ const sk = "selectorKind" in value ? value.selectorKind : void 0;
3066
+ const sv = { type: "selector", value: value.value };
3067
+ if (sk && PROTOCOL_SELECTOR_KINDS.has(sk)) {
3068
+ sv.selectorKind = sk;
3069
+ }
3070
+ return sv;
3071
+ }
3072
+ case "literal":
3073
+ return value.dataType ? { type: "literal", value: value.value, dataType: value.dataType } : { type: "literal", value: value.value };
3074
+ case "reference":
3075
+ return { type: "reference", value: value.value };
3076
+ case "expression":
3077
+ return { type: "expression", raw: value.raw };
3078
+ case "property-path":
3079
+ return { type: "expression", raw: extractValue(value) };
3080
+ case "flag":
3081
+ return { type: "flag", name: value.name, enabled: value.enabled };
3082
+ }
3083
+ }
3084
+ function fromProtocolJSON(json) {
3085
+ const roles = new Map(
3086
+ Object.entries(json.roles ?? {}).map(([role, val]) => [role, valueFromProtocol(val)])
3087
+ );
3088
+ const annotations = json.annotations?.map(
3089
+ (a) => a.value !== void 0 ? { name: a.name, value: a.value } : { name: a.name }
3090
+ );
3091
+ const diagnostics = json.diagnostics?.map((d) => ({
3092
+ severity: d.level,
3093
+ message: d.message,
3094
+ ...d.code ? { code: d.code } : {},
3095
+ ...d.source ? { source: d.source } : {},
3096
+ ...d.suggestions && d.suggestions.length > 0 ? { suggestions: d.suggestions } : {}
3097
+ }));
3098
+ if (json.trigger) {
3099
+ const eventName = json.trigger.event;
3100
+ roles.set("event", createLiteral(eventName, "string"));
3101
+ const bodyRoles = new Map(roles);
3102
+ bodyRoles.delete("event");
3103
+ const bodyNode = createCommandNode(json.action, bodyRoles);
3104
+ const node2 = createEventHandlerNode(
3105
+ "on",
3106
+ roles,
3107
+ [bodyNode],
3108
+ { sourceLanguage: "json" },
3109
+ json.trigger.modifiers ?? {}
3110
+ );
3111
+ return applyV12Metadata(node2, annotations, diagnostics);
3112
+ }
3113
+ const kind = json.kind ?? "command";
3114
+ if (kind === "event-handler") {
3115
+ const body = (json.body ?? []).map(fromProtocolJSON);
3116
+ const node2 = createEventHandlerNode(json.action, roles, body);
3117
+ return applyV12Metadata(node2, annotations, diagnostics);
3118
+ }
3119
+ if (kind === "compound") {
3120
+ const statements = (json.statements ?? []).map(fromProtocolJSON);
3121
+ const node2 = createCompoundNode(
3122
+ statements,
3123
+ json.chainType ?? "sequential"
3124
+ );
3125
+ return applyV12Metadata(node2, annotations, diagnostics);
3126
+ }
3127
+ if (json.thenBranch && json.thenBranch.length > 0) {
3128
+ const thenBranch = json.thenBranch.map(fromProtocolJSON);
3129
+ const elseBranch = json.elseBranch ? json.elseBranch.map(fromProtocolJSON) : void 0;
3130
+ const node2 = createConditionalNode(json.action, roles, thenBranch, elseBranch);
3131
+ return applyV12Metadata(node2, annotations, diagnostics);
3132
+ }
3133
+ if (json.loopVariant) {
3134
+ const loopBody = (json.loopBody ?? []).map(fromProtocolJSON);
3135
+ const node2 = createLoopNode(
3136
+ json.action,
3137
+ roles,
3138
+ json.loopVariant,
3139
+ loopBody,
3140
+ json.loopVariable,
3141
+ json.indexVariable
3142
+ );
3143
+ return applyV12Metadata(node2, annotations, diagnostics);
3144
+ }
3145
+ if (json.catchBranch || json.finallyBranch) {
3146
+ const body = (json.body ?? []).map(fromProtocolJSON);
3147
+ const catchBranch = json.catchBranch ? json.catchBranch.map(fromProtocolJSON) : void 0;
3148
+ const finallyBranch = json.finallyBranch ? json.finallyBranch.map(fromProtocolJSON) : void 0;
3149
+ const node2 = createTryNode(body, catchBranch, finallyBranch);
3150
+ return applyV12Metadata(node2, annotations, diagnostics);
3151
+ }
3152
+ if (json.asyncVariant) {
3153
+ const asyncBody = (json.asyncBody ?? []).map(fromProtocolJSON);
3154
+ const node2 = createAsyncNode(json.asyncVariant, asyncBody);
3155
+ return applyV12Metadata(node2, annotations, diagnostics);
3156
+ }
3157
+ if (json.arms && json.arms.length > 0) {
3158
+ const arms = json.arms.map((arm) => ({
3159
+ pattern: valueFromProtocol(arm.pattern),
3160
+ body: arm.body.map(fromProtocolJSON)
3161
+ }));
3162
+ const defaultArm = json.defaultArm ? json.defaultArm.map(fromProtocolJSON) : void 0;
3163
+ const node2 = createMatchNode(roles, arms, defaultArm);
3164
+ return applyV12Metadata(node2, annotations, diagnostics);
3165
+ }
3166
+ if (json.body && json.body.length > 0 && kind === "command") {
3167
+ const body = json.body.map(fromProtocolJSON);
3168
+ const node2 = createTryNode(body);
3169
+ return applyV12Metadata(node2, annotations, diagnostics);
3170
+ }
3171
+ const node = createCommandNode(json.action, roles);
3172
+ return applyV12Metadata(node, annotations, diagnostics);
3173
+ }
3174
+ function applyV12Metadata(node, annotations, diagnostics) {
3175
+ if ((!annotations || annotations.length === 0) && (!diagnostics || diagnostics.length === 0)) {
3176
+ return node;
3177
+ }
3178
+ return {
3179
+ ...node,
3180
+ ...annotations && annotations.length > 0 ? { annotations } : {},
3181
+ ...diagnostics && diagnostics.length > 0 ? { diagnostics } : {}
3182
+ };
3183
+ }
3184
+ function valueFromProtocol(value) {
3185
+ switch (value.type) {
3186
+ case "selector":
3187
+ return createSelector(String(value.value ?? ""), value.selectorKind);
3188
+ case "literal":
3189
+ return createLiteral(
3190
+ value.value !== void 0 ? value.value : "",
3191
+ value.dataType
3192
+ );
3193
+ case "reference":
3194
+ return createReference(String(value.value ?? ""));
3195
+ case "expression":
3196
+ return createExpression(value.raw ?? String(value.value ?? ""));
3197
+ case "flag":
3198
+ return createFlag(
3199
+ value.name ?? String(value.value ?? ""),
3200
+ value.enabled ?? (typeof value.value === "boolean" ? value.value : true)
3201
+ );
3202
+ case "property-path":
3203
+ return createExpression(String(value.value ?? value.raw ?? ""));
3204
+ }
3205
+ }
3206
+ function toEnvelopeJSON(envelope) {
3207
+ const result = {
3208
+ lseVersion: envelope.lseVersion,
3209
+ nodes: envelope.nodes.map(toProtocolJSON)
3210
+ };
3211
+ if (envelope.features && envelope.features.length > 0) {
3212
+ result.features = [...envelope.features];
3213
+ }
3214
+ return result;
3215
+ }
3216
+ function fromEnvelopeJSON(json) {
3217
+ return {
3218
+ lseVersion: json.lseVersion,
3219
+ ...json.features && json.features.length > 0 ? { features: json.features } : {},
3220
+ nodes: json.nodes.map(fromProtocolJSON)
3221
+ };
3222
+ }
3223
+ function isEnvelope(json) {
3224
+ return typeof json === "object" && json !== null && "lseVersion" in json && "nodes" in json && Array.isArray(json.nodes);
3225
+ }
3226
+ var VALID_KINDS = /* @__PURE__ */ new Set(["command", "event-handler", "compound"]);
3227
+ var VALID_VALUE_TYPES2 = /* @__PURE__ */ new Set([
3228
+ "selector",
3229
+ "literal",
3230
+ "reference",
3231
+ "expression",
3232
+ "property-path",
3233
+ "flag"
3234
+ ]);
3235
+ var VALID_CHAIN_TYPES = /* @__PURE__ */ new Set(["then", "and", "async", "sequential", "pipe"]);
3236
+ var VALID_ASYNC_VARIANTS = /* @__PURE__ */ new Set(["all", "race"]);
3237
+ var VALID_DIAGNOSTIC_LEVELS = /* @__PURE__ */ new Set(["error", "warning", "info"]);
3238
+ function validateProtocolJSON(json) {
3239
+ const diagnostics = [];
3240
+ if (typeof json !== "object" || json === null) {
3241
+ return [{ severity: "error", code: "INVALID_ROOT", message: "Root must be a JSON object" }];
3242
+ }
3243
+ const node = json;
3244
+ const kind = "kind" in node ? String(node.kind) : "command";
3245
+ if ("kind" in node && !VALID_KINDS.has(kind)) {
3246
+ diagnostics.push({
3247
+ severity: "error",
3248
+ code: "INVALID_KIND",
3249
+ message: `Invalid kind "${node.kind}". Must be one of: command, event-handler, compound`
3250
+ });
3251
+ }
3252
+ if (!("action" in node)) {
3253
+ diagnostics.push({
3254
+ severity: "error",
3255
+ code: "MISSING_ACTION",
3256
+ message: "Missing required field: action"
3257
+ });
3258
+ } else if (typeof node.action !== "string" || node.action.length === 0) {
3259
+ diagnostics.push({
3260
+ severity: "error",
3261
+ code: "INVALID_ACTION",
3262
+ message: "action must be a non-empty string"
3263
+ });
3264
+ }
3265
+ if (!("roles" in node) && kind !== "compound") {
3266
+ diagnostics.push({
3267
+ severity: "error",
3268
+ code: "MISSING_ROLES",
3269
+ message: "Missing required field: roles"
3270
+ });
3271
+ } else if ("roles" in node && (typeof node.roles !== "object" || node.roles === null || Array.isArray(node.roles))) {
3272
+ diagnostics.push({
3273
+ severity: "error",
3274
+ code: "INVALID_ROLES",
3275
+ message: "roles must be a plain object"
3276
+ });
3277
+ } else if ("roles" in node && node.roles) {
3278
+ for (const [role, value] of Object.entries(node.roles)) {
3279
+ if (typeof value !== "object" || value === null) {
3280
+ diagnostics.push({
3281
+ severity: "error",
3282
+ code: "INVALID_ROLE_VALUE",
3283
+ message: `Role "${role}" value must be an object`
3284
+ });
3285
+ continue;
3286
+ }
3287
+ const v = value;
3288
+ if (!("type" in v) || !VALID_VALUE_TYPES2.has(String(v.type))) {
3289
+ diagnostics.push({
3290
+ severity: "error",
3291
+ code: "INVALID_VALUE_TYPE",
3292
+ message: `Role "${role}" has invalid type "${v.type}"`
3293
+ });
3294
+ }
3295
+ if (v.type === "flag") {
3296
+ const hasName = typeof v.name === "string";
3297
+ const hasEnabled = typeof v.enabled === "boolean";
3298
+ const hasValue = "value" in v;
3299
+ if (!hasName && !hasValue) {
3300
+ diagnostics.push({
3301
+ severity: "error",
3302
+ code: "MISSING_FLAG_NAME",
3303
+ message: `Flag role "${role}" missing required field: name (or value)`
3304
+ });
3305
+ }
3306
+ if (!hasEnabled && !hasValue) {
3307
+ diagnostics.push({
3308
+ severity: "error",
3309
+ code: "MISSING_FLAG_ENABLED",
3310
+ message: `Flag role "${role}" missing required field: enabled (or value)`
3311
+ });
3312
+ }
3313
+ }
3314
+ if (v.type === "expression" && typeof v.raw !== "string" && !("value" in v)) {
3315
+ diagnostics.push({
3316
+ severity: "error",
3317
+ code: "MISSING_EXPRESSION_RAW",
3318
+ message: `Expression role "${role}" missing required field: raw (or value)`
3319
+ });
3320
+ }
3321
+ }
3322
+ }
3323
+ if ("trigger" in node && node.trigger) {
3324
+ const trigger = node.trigger;
3325
+ if (typeof trigger !== "object" || trigger === null) {
3326
+ diagnostics.push({
3327
+ severity: "error",
3328
+ code: "INVALID_TRIGGER",
3329
+ message: "trigger must be an object"
3330
+ });
3331
+ } else if (!trigger.event || typeof trigger.event !== "string") {
3332
+ diagnostics.push({
3333
+ severity: "error",
3334
+ code: "INVALID_TRIGGER",
3335
+ message: "trigger.event is required and must be a non-empty string"
3336
+ });
3337
+ }
3338
+ }
3339
+ if (kind === "event-handler" && !("trigger" in node)) {
3340
+ if (!("body" in node) || !Array.isArray(node.body)) {
3341
+ diagnostics.push({
3342
+ severity: "error",
3343
+ code: "MISSING_BODY",
3344
+ message: "event-handler node requires a body array"
3345
+ });
3346
+ }
3347
+ }
3348
+ if (kind === "compound") {
3349
+ if (!("statements" in node) || !Array.isArray(node.statements)) {
3350
+ diagnostics.push({
3351
+ severity: "error",
3352
+ code: "MISSING_STATEMENTS",
3353
+ message: "compound node requires a statements array"
3354
+ });
3355
+ }
3356
+ if ("chainType" in node && !VALID_CHAIN_TYPES.has(String(node.chainType))) {
3357
+ diagnostics.push({
3358
+ severity: "error",
3359
+ code: "INVALID_CHAIN_TYPE",
3360
+ message: `Invalid chainType "${node.chainType}". Must be one of: then, and, async, sequential, pipe`
3361
+ });
3362
+ }
3363
+ }
3364
+ if ("diagnostics" in node) {
3365
+ if (!Array.isArray(node.diagnostics)) {
3366
+ diagnostics.push({
3367
+ severity: "error",
3368
+ code: "INVALID_DIAGNOSTICS",
3369
+ message: "diagnostics must be an array"
3370
+ });
3371
+ } else {
3372
+ for (let i = 0; i < node.diagnostics.length; i++) {
3373
+ const d = node.diagnostics[i];
3374
+ if (!d || typeof d !== "object") {
3375
+ diagnostics.push({
3376
+ severity: "error",
3377
+ code: "INVALID_DIAGNOSTIC_ENTRY",
3378
+ message: `diagnostics[${i}] must be an object`
3379
+ });
3380
+ continue;
3381
+ }
3382
+ if (!VALID_DIAGNOSTIC_LEVELS.has(String(d.level))) {
3383
+ diagnostics.push({
3384
+ severity: "error",
3385
+ code: "INVALID_DIAGNOSTIC_LEVEL",
3386
+ message: `diagnostics[${i}].level must be "error", "warning", or "info"`
3387
+ });
3388
+ }
3389
+ if (typeof d.message !== "string") {
3390
+ diagnostics.push({
3391
+ severity: "error",
3392
+ code: "MISSING_DIAGNOSTIC_MESSAGE",
3393
+ message: `diagnostics[${i}] missing required field: message`
3394
+ });
3395
+ }
3396
+ if ("code" in d && typeof d.code !== "string") {
3397
+ diagnostics.push({
3398
+ severity: "warning",
3399
+ code: "INVALID_DIAGNOSTIC_CODE",
3400
+ message: `diagnostics[${i}].code must be a string if present`
3401
+ });
3402
+ }
3403
+ }
3404
+ }
3405
+ }
3406
+ if ("annotations" in node) {
3407
+ if (!Array.isArray(node.annotations)) {
3408
+ diagnostics.push({
3409
+ severity: "error",
3410
+ code: "INVALID_ANNOTATIONS",
3411
+ message: "annotations must be an array"
3412
+ });
3413
+ } else {
3414
+ for (let i = 0; i < node.annotations.length; i++) {
3415
+ const a = node.annotations[i];
3416
+ if (!a || typeof a !== "object") {
3417
+ diagnostics.push({
3418
+ severity: "error",
3419
+ code: "INVALID_ANNOTATION_ENTRY",
3420
+ message: `annotations[${i}] must be an object`
3421
+ });
3422
+ continue;
3423
+ }
3424
+ if (typeof a.name !== "string" || a.name.length === 0) {
3425
+ diagnostics.push({
3426
+ severity: "error",
3427
+ code: "MISSING_ANNOTATION_NAME",
3428
+ message: `annotations[${i}] missing required field: name`
3429
+ });
3430
+ }
3431
+ }
3432
+ }
3433
+ }
3434
+ if ("asyncVariant" in node && !VALID_ASYNC_VARIANTS.has(String(node.asyncVariant))) {
3435
+ diagnostics.push({
3436
+ severity: "error",
3437
+ code: "INVALID_ASYNC_VARIANT",
3438
+ message: `Invalid asyncVariant "${node.asyncVariant}". Must be "all" or "race"`
3439
+ });
3440
+ }
3441
+ if ("arms" in node) {
3442
+ if (!Array.isArray(node.arms)) {
3443
+ diagnostics.push({
3444
+ severity: "error",
3445
+ code: "INVALID_ARMS",
3446
+ message: "arms must be an array"
3447
+ });
3448
+ } else {
3449
+ for (let i = 0; i < node.arms.length; i++) {
3450
+ const arm = node.arms[i];
3451
+ if (!arm || typeof arm !== "object") {
3452
+ diagnostics.push({
3453
+ severity: "error",
3454
+ code: "INVALID_ARM_ENTRY",
3455
+ message: `arms[${i}] must be an object`
3456
+ });
3457
+ continue;
3458
+ }
3459
+ if (!arm.pattern || typeof arm.pattern !== "object") {
3460
+ diagnostics.push({
3461
+ severity: "error",
3462
+ code: "MISSING_ARM_PATTERN",
3463
+ message: `arms[${i}] missing required field: pattern`
3464
+ });
3465
+ }
3466
+ if (!Array.isArray(arm.body)) {
3467
+ diagnostics.push({
3468
+ severity: "error",
3469
+ code: "MISSING_ARM_BODY",
3470
+ message: `arms[${i}] missing required field: body (array)`
3471
+ });
3472
+ }
3473
+ }
3474
+ }
3475
+ }
3476
+ return diagnostics;
3477
+ }
3478
+
3479
+ // src/ir/from-interchange.ts
3480
+ function fromInterchangeNode(node) {
3481
+ if (!node || typeof node !== "object") {
3482
+ return createCommandNode("unknown", {});
3483
+ }
3484
+ switch (node.type) {
3485
+ case "event":
3486
+ return convertEvent(node);
3487
+ case "command":
3488
+ return convertCommand(node);
3489
+ case "if":
3490
+ return convertIf(node);
3491
+ case "repeat":
3492
+ return convertRepeat(node);
3493
+ case "foreach":
3494
+ return convertForEach(node);
3495
+ case "while":
3496
+ return convertWhile(node);
3497
+ // Expression nodes — wrap as `get` command with patient role
3498
+ default:
3499
+ return createCommandNode("get", { patient: convertValue(node) });
3500
+ }
3501
+ }
3502
+ function convertEvent(node) {
3503
+ const event = String(node.event ?? "click");
3504
+ const bodyNodes = Array.isArray(node.body) ? node.body : [];
3505
+ const body = bodyNodes.map(fromInterchangeNode);
3506
+ const roles = {
3507
+ event: createLiteral(event)
3508
+ };
3509
+ if (node.target && typeof node.target === "object") {
3510
+ roles.source = convertValue(node.target);
3511
+ }
3512
+ const modifiers = node.modifiers;
3513
+ let eventModifiers;
3514
+ if (modifiers) {
3515
+ eventModifiers = bridgeEventModifiers(modifiers);
3516
+ if (modifiers.prevent) roles.prevent = createLiteral(true);
3517
+ if (modifiers.stop) roles.stop = createLiteral(true);
3518
+ if (modifiers.passive) roles.passive = createLiteral(true);
3519
+ if (modifiers.capture) roles.capture = createLiteral(true);
3520
+ }
3521
+ return createEventHandlerNode("on", roles, body, void 0, eventModifiers);
3522
+ }
3523
+ function convertCommand(node) {
3524
+ const name = String(node.name ?? "unknown");
3525
+ const args = Array.isArray(node.args) ? node.args : [];
3526
+ const target = node.target && typeof node.target === "object" ? node.target : void 0;
3527
+ const existingRoles = node.roles;
3528
+ const modifiers = node.modifiers;
3529
+ let roles;
3530
+ if (existingRoles && typeof existingRoles === "object" && Object.keys(existingRoles).length > 0) {
3531
+ roles = {};
3532
+ for (const [role, value] of Object.entries(existingRoles)) {
3533
+ if (value && typeof value === "object" && "type" in value) {
3534
+ roles[role] = convertValue(value);
3535
+ }
3536
+ }
3537
+ } else {
3538
+ roles = inferRoles(name, args, modifiers, target);
3539
+ }
3540
+ return createCommandNode(name, roles);
3541
+ }
3542
+ function convertIf(node) {
3543
+ const condition = node.condition && typeof node.condition === "object" ? convertValue(node.condition) : createExpression("true");
3544
+ const thenBody = Array.isArray(node.thenBranch) ? node.thenBranch : [];
3545
+ const thenBranch = thenBody.map(fromInterchangeNode);
3546
+ let elseBranch;
3547
+ const elseIfBranches = node.elseIfBranches;
3548
+ if (elseIfBranches && elseIfBranches.length > 0) {
3549
+ elseBranch = [chainElseIfs(elseIfBranches, node.elseBranch)];
3550
+ } else if (Array.isArray(node.elseBranch) && node.elseBranch.length > 0) {
3551
+ elseBranch = node.elseBranch.map(fromInterchangeNode);
3552
+ }
3553
+ const roles = { condition };
3554
+ return createConditionalNode("if", roles, thenBranch, elseBranch);
3555
+ }
3556
+ function chainElseIfs(branches, finalElse) {
3557
+ const [first, ...rest] = branches;
3558
+ const condition = convertValue(first.condition);
3559
+ const thenBranch = first.body.map(fromInterchangeNode);
3560
+ let elseBranch;
3561
+ if (rest.length > 0) {
3562
+ elseBranch = [chainElseIfs(rest, finalElse)];
3563
+ } else if (finalElse && finalElse.length > 0) {
3564
+ elseBranch = finalElse.map(fromInterchangeNode);
3565
+ }
3566
+ return createConditionalNode("if", { condition }, thenBranch, elseBranch);
3567
+ }
3568
+ function convertRepeat(node) {
3569
+ const bodyNodes = Array.isArray(node.body) ? node.body : [];
3570
+ const body = bodyNodes.map(fromInterchangeNode);
3571
+ const roles = {};
3572
+ let variant;
3573
+ if (node.count !== void 0 && node.count !== null) {
3574
+ variant = "times";
3575
+ if (typeof node.count === "number") {
3576
+ roles.quantity = createLiteral(node.count, "number");
3577
+ } else if (typeof node.count === "object") {
3578
+ roles.quantity = convertValue(node.count);
3579
+ }
3580
+ } else if (node.whileCondition && typeof node.whileCondition === "object") {
3581
+ variant = "while";
3582
+ roles.condition = convertValue(node.whileCondition);
3583
+ } else if (typeof node.untilEvent === "string") {
3584
+ variant = "until";
3585
+ roles.condition = createLiteral(node.untilEvent);
3586
+ } else {
3587
+ variant = "forever";
3588
+ }
3589
+ return createLoopNode("repeat", roles, variant, body);
3590
+ }
3591
+ function convertForEach(node) {
3592
+ const bodyNodes = Array.isArray(node.body) ? node.body : [];
3593
+ const body = bodyNodes.map(fromInterchangeNode);
3594
+ const roles = {};
3595
+ if (node.collection && typeof node.collection === "object") {
3596
+ roles.source = convertValue(node.collection);
3597
+ }
3598
+ const itemName = typeof node.itemName === "string" ? node.itemName : void 0;
3599
+ const indexName = typeof node.indexName === "string" ? node.indexName : void 0;
3600
+ return createLoopNode("repeat", roles, "for", body, itemName, indexName);
3601
+ }
3602
+ function convertWhile(node) {
3603
+ const bodyNodes = Array.isArray(node.body) ? node.body : [];
3604
+ const body = bodyNodes.map(fromInterchangeNode);
3605
+ const roles = {};
3606
+ if (node.condition && typeof node.condition === "object") {
3607
+ roles.condition = convertValue(node.condition);
3608
+ }
3609
+ return createLoopNode("repeat", roles, "while", body);
3610
+ }
3611
+ function convertValue(node) {
3612
+ switch (node.type) {
3613
+ case "literal":
3614
+ return convertLiteral(node);
3615
+ case "selector":
3616
+ return createSelector(String(node.value ?? ""), inferSelectorKind2(String(node.value ?? "")));
3617
+ case "identifier":
3618
+ return convertIdentifier(node);
3619
+ case "variable":
3620
+ return convertVariable(node);
3621
+ case "possessive":
3622
+ return createPropertyPath(
3623
+ node.object && typeof node.object === "object" ? convertValue(node.object) : createReference("me"),
3624
+ String(node.property ?? "")
3625
+ );
3626
+ // Complex expressions → collapse to raw string
3627
+ case "binary":
3628
+ case "unary":
3629
+ case "member":
3630
+ case "call":
3631
+ case "positional":
3632
+ return createExpression(renderExpr(node));
3633
+ default:
3634
+ if ("value" in node) return createLiteral(String(node.value ?? ""));
3635
+ return createExpression(renderExpr(node));
3636
+ }
3637
+ }
3638
+ function convertLiteral(node) {
3639
+ const value = node.value;
3640
+ if (value === null || value === void 0) return createLiteral("null");
3641
+ const dataType = typeof value === "number" ? "number" : typeof value === "boolean" ? "boolean" : void 0;
3642
+ return createLiteral(value, dataType);
3643
+ }
3644
+ function convertIdentifier(node) {
3645
+ const value = String(node.value ?? "");
3646
+ if (DEFAULT_REFERENCES.has(value)) {
3647
+ return createReference(value);
3648
+ }
3649
+ return createLiteral(value);
3650
+ }
3651
+ function convertVariable(node) {
3652
+ const name = String(node.name ?? "");
3653
+ const scope = String(node.scope ?? "local");
3654
+ switch (scope) {
3655
+ case "element":
3656
+ return createExpression(`:${name}`);
3657
+ case "global":
3658
+ return createExpression(`$${name}`);
3659
+ default:
3660
+ return createExpression(name);
3661
+ }
3662
+ }
3663
+ function renderExpr(node) {
3664
+ if (!node || typeof node !== "object") return "";
3665
+ switch (node.type) {
3666
+ case "literal": {
3667
+ const v = node.value;
3668
+ if (v === null || v === void 0) return "null";
3669
+ if (typeof v === "string") return /\s/.test(v) ? `"${v}"` : v;
3670
+ return String(v);
3671
+ }
3672
+ case "identifier":
3673
+ return String(node.value ?? "");
3674
+ case "selector":
3675
+ return String(node.value ?? "");
3676
+ case "variable": {
3677
+ const name = String(node.name ?? "");
3678
+ const scope = String(node.scope ?? "local");
3679
+ if (scope === "element") return `:${name}`;
3680
+ if (scope === "global") return `$${name}`;
3681
+ return name;
3682
+ }
3683
+ case "binary": {
3684
+ const left = node.left && typeof node.left === "object" ? renderExpr(node.left) : "";
3685
+ const right = node.right && typeof node.right === "object" ? renderExpr(node.right) : "";
3686
+ return `${left} ${node.operator ?? ""} ${right}`;
3687
+ }
3688
+ case "unary": {
3689
+ const operand = node.operand && typeof node.operand === "object" ? renderExpr(node.operand) : "";
3690
+ return `${node.operator ?? ""} ${operand}`;
3691
+ }
3692
+ case "member": {
3693
+ const obj = node.object && typeof node.object === "object" ? renderExpr(node.object) : "";
3694
+ if (node.computed && node.property && typeof node.property === "object") {
3695
+ return `${obj}[${renderExpr(node.property)}]`;
3696
+ }
3697
+ const prop = typeof node.property === "string" ? node.property : node.property && typeof node.property === "object" ? renderExpr(node.property) : "";
3698
+ return `${obj}.${prop}`;
3699
+ }
3700
+ case "possessive": {
3701
+ const obj = node.object && typeof node.object === "object" ? renderExpr(node.object) : "";
3702
+ return `${obj}'s ${node.property ?? ""}`;
3703
+ }
3704
+ case "call": {
3705
+ const callee = node.callee && typeof node.callee === "object" ? renderExpr(node.callee) : "";
3706
+ const args = Array.isArray(node.args) ? node.args.map((a) => renderExpr(a)).join(", ") : "";
3707
+ return `${callee}(${args})`;
3708
+ }
3709
+ case "positional": {
3710
+ const target = node.target && typeof node.target === "object" ? renderExpr(node.target) : "";
3711
+ return target ? `${node.position ?? ""} ${target}` : String(node.position ?? "");
3712
+ }
3713
+ default:
3714
+ if ("value" in node) return String(node.value ?? "");
3715
+ return "";
3716
+ }
3717
+ }
3718
+ function inferRoles(name, args, modifiers, target) {
3719
+ const roles = {};
3720
+ switch (name) {
3721
+ case "set": {
3722
+ if (args[0]) roles.destination = convertValue(args[0]);
3723
+ const toVal = modifiers?.to;
3724
+ if (toVal && typeof toVal === "object" && "type" in toVal) {
3725
+ roles.patient = convertValue(toVal);
3726
+ } else if (args[1]) {
3727
+ roles.patient = convertValue(args[1]);
3728
+ }
3729
+ break;
3730
+ }
3731
+ case "put": {
3732
+ if (args[0]) roles.patient = convertValue(args[0]);
3733
+ if (modifiers) {
3734
+ for (const key of ["into", "before", "after"]) {
3735
+ const val = modifiers[key];
3736
+ if (val && typeof val === "object" && "type" in val) {
3737
+ roles.destination = convertValue(val);
3738
+ roles.method = createLiteral(key);
3739
+ break;
3740
+ }
3741
+ }
3742
+ }
3743
+ if (!roles.destination && target) {
3744
+ roles.destination = convertValue(target);
3745
+ }
3746
+ break;
3747
+ }
3748
+ case "increment":
3749
+ case "decrement": {
3750
+ if (args[0]) roles.destination = convertValue(args[0]);
3751
+ const byVal = modifiers?.by;
3752
+ if (byVal && typeof byVal === "object" && "type" in byVal) {
3753
+ roles.quantity = convertValue(byVal);
3754
+ } else if (args[1]) {
3755
+ roles.quantity = convertValue(args[1]);
3756
+ }
3757
+ break;
3758
+ }
3759
+ case "fetch": {
3760
+ if (args[0]) roles.source = convertValue(args[0]);
3761
+ const asVal = modifiers?.as;
3762
+ if (asVal && typeof asVal === "object" && "type" in asVal) {
3763
+ roles.responseType = convertValue(asVal);
3764
+ } else if (typeof asVal === "string") {
3765
+ roles.responseType = createLiteral(asVal);
3766
+ }
3767
+ break;
3768
+ }
3769
+ case "wait":
3770
+ case "settle": {
3771
+ if (args[0]) roles.duration = convertValue(args[0]);
3772
+ break;
3773
+ }
3774
+ case "toggle":
3775
+ case "add":
3776
+ case "show":
3777
+ case "hide": {
3778
+ if (args[0]) roles.patient = convertValue(args[0]);
3779
+ if (target) roles.destination = convertValue(target);
3780
+ break;
3781
+ }
3782
+ case "remove": {
3783
+ if (args[0]) roles.patient = convertValue(args[0]);
3784
+ if (target) roles.source = convertValue(target);
3785
+ break;
3786
+ }
3787
+ case "send":
3788
+ case "trigger": {
3789
+ if (args[0]) roles.patient = convertValue(args[0]);
3790
+ if (target) roles.destination = convertValue(target);
3791
+ break;
3792
+ }
3793
+ case "log": {
3794
+ if (args[0]) roles.patient = convertValue(args[0]);
3795
+ break;
3796
+ }
3797
+ default: {
3798
+ if (args[0]) roles.patient = convertValue(args[0]);
3799
+ if (target) roles.destination = convertValue(target);
3800
+ break;
3801
+ }
3802
+ }
3803
+ return roles;
3804
+ }
3805
+ function inferSelectorKind2(value) {
3806
+ if (!value) return void 0;
3807
+ if (value.startsWith("#")) return "id";
3808
+ if (value.startsWith(".")) return "class";
3809
+ if (value.startsWith("[")) return "attribute";
3810
+ if (value.startsWith("<") || value.startsWith("*")) return "element";
3811
+ if (/[>\+~ ]/.test(value) && value.length > 1) return "complex";
3812
+ return void 0;
3813
+ }
3814
+ function inferValueFromString(value) {
3815
+ if (DEFAULT_REFERENCES.has(value)) return createReference(value);
3816
+ const kind = inferSelectorKind2(value);
3817
+ if (kind) return createSelector(value, kind);
3818
+ return createLiteral(value);
3819
+ }
3820
+ function bridgeEventModifiers(mods) {
3821
+ const once = mods.once === true ? true : void 0;
3822
+ const debounce = typeof mods.debounce === "number" ? mods.debounce : void 0;
3823
+ const throttle = typeof mods.throttle === "number" ? mods.throttle : void 0;
3824
+ const from = typeof mods.from === "string" ? inferValueFromString(mods.from) : void 0;
3825
+ if (once === void 0 && debounce === void 0 && throttle === void 0 && from === void 0) {
3826
+ return void 0;
3827
+ }
3828
+ return {
3829
+ ...once !== void 0 ? { once } : {},
3830
+ ...debounce !== void 0 ? { debounce } : {},
3831
+ ...throttle !== void 0 ? { throttle } : {},
3832
+ ...from !== void 0 ? { from } : {}
3833
+ };
3834
+ }
3835
+
3836
+ // src/ir/to-runtime-ast.ts
3837
+ function semanticNodeToRuntimeAST(node) {
3838
+ switch (node.kind) {
3839
+ case "event-handler":
3840
+ return convertEventHandler(node);
3841
+ case "conditional":
3842
+ return convertConditional(node);
3843
+ case "loop":
3844
+ return convertLoop(node);
3845
+ case "compound": {
3846
+ const compound = node;
3847
+ const commands = (compound.statements ?? []).map(semanticNodeToRuntimeAST);
3848
+ return {
3849
+ type: "CommandSequence",
3850
+ commands
3851
+ };
3852
+ }
3853
+ case "command":
3854
+ default:
3855
+ return convertCommand2(node);
3856
+ }
3857
+ }
3858
+ function convertEventHandler(eh) {
3859
+ const eventValue = eh.roles.get("event");
3860
+ const eventName = eventValue && "value" in eventValue ? String(eventValue.value) : "click";
3861
+ const modifiers = {};
3862
+ if (eh.eventModifiers) {
3863
+ if (eh.eventModifiers.once) modifiers.once = true;
3864
+ if (eh.eventModifiers.debounce) modifiers.debounce = eh.eventModifiers.debounce;
3865
+ if (eh.eventModifiers.throttle) modifiers.throttle = eh.eventModifiers.throttle;
3866
+ if (eh.eventModifiers.queue) modifiers.queue = eh.eventModifiers.queue;
3867
+ }
3868
+ return {
3869
+ type: "event",
3870
+ event: eventName,
3871
+ modifiers,
3872
+ body: (eh.body ?? []).map(semanticNodeToRuntimeAST)
3873
+ };
3874
+ }
3875
+ function convertCommand2(node) {
3876
+ const roles = {};
3877
+ const args = [];
3878
+ for (const [roleName, value] of node.roles) {
3879
+ const astValue = semanticValueToAST(value);
3880
+ roles[roleName] = astValue;
3881
+ args.push(astValue);
3882
+ }
3883
+ return {
3884
+ type: "command",
3885
+ name: node.action,
3886
+ args,
3887
+ roles
3888
+ };
3889
+ }
3890
+ function convertConditional(node) {
3891
+ const conditionValue = node.roles.get("condition");
3892
+ return {
3893
+ type: "command",
3894
+ name: "if",
3895
+ args: conditionValue ? [semanticValueToAST(conditionValue)] : [],
3896
+ condition: conditionValue ? semanticValueToAST(conditionValue) : void 0,
3897
+ thenBranch: (node.thenBranch ?? []).map(semanticNodeToRuntimeAST),
3898
+ elseBranch: (node.elseBranch ?? []).map(semanticNodeToRuntimeAST)
3899
+ };
3900
+ }
3901
+ function convertLoop(node) {
3902
+ const countValue = node.roles.get("patient") || node.roles.get("count");
3903
+ return {
3904
+ type: "command",
3905
+ name: "repeat",
3906
+ args: countValue ? [semanticValueToAST(countValue)] : [],
3907
+ loopVariant: node.loopVariant ?? "forever",
3908
+ body: (node.body ?? []).map(semanticNodeToRuntimeAST),
3909
+ loopVariable: node.loopVariable,
3910
+ indexVariable: node.indexVariable
3911
+ };
3912
+ }
3913
+ function semanticValueToAST(value) {
3914
+ switch (value.type) {
3915
+ case "selector":
3916
+ return { type: "selector", value: value.value };
3917
+ case "reference":
3918
+ return { type: "identifier", value: value.value };
3919
+ case "literal": {
3920
+ const lit = value;
3921
+ if (lit.dataType === "duration") {
3922
+ const str = String(lit.value);
3923
+ const match = /^(\d+(?:\.\d+)?)(ms|s)$/.exec(str);
3924
+ if (match) {
3925
+ const ms = match[2] === "s" ? parseFloat(match[1]) * 1e3 : parseFloat(match[1]);
3926
+ return { type: "literal", value: ms };
3927
+ }
3928
+ }
3929
+ return { type: "literal", value: lit.value };
3930
+ }
3931
+ case "property-path": {
3932
+ const pp = value;
3933
+ const objectAST = semanticValueToAST(pp.object);
3934
+ return {
3935
+ type: "member",
3936
+ object: objectAST,
3937
+ property: pp.property
3938
+ };
3939
+ }
3940
+ case "expression": {
3941
+ const expr = value;
3942
+ return { type: "expression", value: expr.raw };
3943
+ }
3944
+ case "flag": {
3945
+ const flag = value;
3946
+ return { type: "literal", value: flag.enabled };
3947
+ }
3948
+ default:
3949
+ return { type: "literal", value: String(value.value ?? "") };
3950
+ }
3951
+ }
3952
+
3953
+ // src/api/create-dsl.ts
3954
+ var DSLRegistry = class {
3955
+ constructor(config) {
3956
+ this.patterns = /* @__PURE__ */ new Map();
3957
+ this.tokenizers = /* @__PURE__ */ new Map();
3958
+ this.schemas = config.schemas;
3959
+ for (const lang of config.languages) {
3960
+ this.registerLanguage(lang);
3961
+ }
3962
+ }
3963
+ registerLanguage(lang) {
3964
+ this.tokenizers.set(lang.code, lang.tokenizer);
3965
+ const patterns = [];
3966
+ for (const schema of this.schemas) {
3967
+ const pattern = generatePattern(schema, lang.patternProfile);
3968
+ patterns.push(pattern);
3969
+ }
3970
+ this.patterns.set(lang.code, patterns);
3971
+ }
3972
+ getPatterns(language) {
3973
+ return this.patterns.get(language) || [];
3974
+ }
3975
+ getTokenizer(language) {
3976
+ return this.tokenizers.get(language);
3977
+ }
3978
+ getSupportedLanguages() {
3979
+ return Array.from(this.patterns.keys());
3980
+ }
3981
+ };
3982
+ var MultilingualDSLImpl = class {
3983
+ constructor(config, registry, transformer) {
3984
+ this.registry = registry;
3985
+ this.matcher = new PatternMatcher();
3986
+ this.transformer = transformer;
3987
+ if (config.codeGenerator) {
3988
+ this.codeGenerator = config.codeGenerator;
3989
+ }
3990
+ const schemaMap = new Map(config.schemas.map((s) => [s.action, s]));
3991
+ this.schemaLookup = {
3992
+ getSchema: (action) => schemaMap.get(action)
3993
+ };
3994
+ }
3995
+ parse(input, language) {
3996
+ const result = this.parseWithConfidence(input, language);
3997
+ return result.node;
3998
+ }
3999
+ parseWithConfidence(input, language) {
4000
+ if (isExplicitSyntax(input)) {
4001
+ const node = parseExplicit(input, { schemaLookup: this.schemaLookup });
4002
+ if (!this.schemaLookup.getSchema(node.action)) {
4003
+ throw new Error(`No schema for action "${node.action}" in this DSL`);
4004
+ }
4005
+ return { node, confidence: 1 };
4006
+ }
4007
+ const tokenizer = this.registry.getTokenizer(language);
4008
+ if (!tokenizer) {
4009
+ throw new Error(`No tokenizer registered for language: ${language}`);
4010
+ }
4011
+ const tokens = tokenizer.tokenize(input);
4012
+ const patterns = this.registry.getPatterns(language);
4013
+ const profile = {
4014
+ code: language
4015
+ };
4016
+ for (const pattern of patterns) {
4017
+ const match = this.matcher.matchPattern(tokens, pattern, profile);
4018
+ if (match) {
4019
+ const node = {
4020
+ kind: "command",
4021
+ action: pattern.command,
4022
+ roles: match.captured,
4023
+ metadata: {
4024
+ sourceLanguage: language,
4025
+ sourceText: input,
4026
+ patternId: pattern.id,
4027
+ confidence: match.confidence
4028
+ }
4029
+ };
4030
+ return { node, confidence: match.confidence };
4031
+ }
4032
+ }
4033
+ throw new Error(`No pattern matched for input: ${input}`);
4034
+ }
4035
+ validate(input, language) {
4036
+ try {
4037
+ const result = this.parseWithConfidence(input, language);
4038
+ return {
4039
+ valid: true,
4040
+ node: result.node
4041
+ };
4042
+ } catch (error) {
4043
+ return {
4044
+ valid: false,
4045
+ errors: [error instanceof Error ? error.message : String(error)]
4046
+ };
4047
+ }
4048
+ }
4049
+ translate(input, fromLanguage, toLanguage) {
4050
+ if (isExplicitSyntax(input)) return input;
4051
+ return this.transformer.transform(input, fromLanguage, toLanguage);
4052
+ }
4053
+ compile(input, language) {
2118
4054
  if (!this.codeGenerator) {
2119
4055
  return {
2120
4056
  ok: false,
2121
4057
  errors: ["No code generator configured for this DSL"]
2122
4058
  };
2123
4059
  }
2124
- try {
2125
- const result = this.parseWithConfidence(input, language);
2126
- const code = this.codeGenerator.generate(result.node);
4060
+ try {
4061
+ const result = this.parseWithConfidence(input, language);
4062
+ const code = this.codeGenerator.generate(result.node);
4063
+ return {
4064
+ ok: true,
4065
+ code,
4066
+ node: result.node,
4067
+ metadata: {
4068
+ parser: "semantic",
4069
+ confidence: result.confidence
4070
+ }
4071
+ };
4072
+ } catch (error) {
4073
+ return {
4074
+ ok: false,
4075
+ errors: [error instanceof Error ? error.message : String(error)]
4076
+ };
4077
+ }
4078
+ }
4079
+ getSupportedLanguages() {
4080
+ return this.registry.getSupportedLanguages();
4081
+ }
4082
+ };
4083
+ function createDefaultDictionary(config) {
4084
+ const translations = {};
4085
+ for (const lang of config.languages) {
4086
+ const keywords = {};
4087
+ for (const [canonical, keywordDef] of Object.entries(lang.patternProfile.keywords)) {
4088
+ keywords[canonical] = keywordDef.primary;
4089
+ }
4090
+ translations[lang.code] = keywords;
4091
+ }
4092
+ return new InMemoryDictionary(translations);
4093
+ }
4094
+ function createDefaultProfileProvider(config) {
4095
+ const profiles = {};
4096
+ for (const lang of config.languages) {
4097
+ if (lang.grammarProfile) {
4098
+ profiles[lang.code] = lang.grammarProfile;
4099
+ }
4100
+ }
4101
+ return new InMemoryProfileProvider(profiles);
4102
+ }
4103
+ function createMultilingualDSL(config) {
4104
+ const dictionary = config.dictionary ?? createDefaultDictionary(config);
4105
+ const profileProvider = config.profileProvider ?? createDefaultProfileProvider(config);
4106
+ const transformer = new GrammarTransformer({
4107
+ dictionary,
4108
+ profileProvider
4109
+ });
4110
+ const registry = new DSLRegistry(config);
4111
+ return new MultilingualDSLImpl(config, registry, transformer);
4112
+ }
4113
+
4114
+ // src/prompts/prompt-generator.ts
4115
+ function generatePrompt(config) {
4116
+ const outputFormat = config.outputFormat ?? "both";
4117
+ const examplesPerCommand = config.examplesPerCommand ?? 2;
4118
+ const sections = [
4119
+ buildProtocolSection(),
4120
+ buildValueTypeSection(),
4121
+ buildCommandsSection(config.schemas, outputFormat, examplesPerCommand),
4122
+ buildOutputFormatSection(outputFormat),
4123
+ buildErrorRecoverySection()
4124
+ ];
4125
+ let finalSections = sections;
4126
+ if (config.maxTokens && config.maxTokens > 0) {
4127
+ finalSections = truncateToTokenBudget(sections, config.maxTokens);
4128
+ }
4129
+ const text = finalSections.map((s) => `## ${s.title}
4130
+
4131
+ ${s.content}`).join("\n\n---\n\n");
4132
+ const totalRoles = config.schemas.reduce((sum, s) => sum + s.roles.length, 0);
4133
+ const approximateTokens = estimateTokens(text);
4134
+ const metadata = {
4135
+ domain: config.domain,
4136
+ commandCount: config.schemas.length,
4137
+ roleCount: totalRoles,
4138
+ approximateTokens
4139
+ };
4140
+ return { text, sections: finalSections, metadata };
4141
+ }
4142
+ function generateExamples(schema, count = 2) {
4143
+ const combos = generateRoleCombinations(schema);
4144
+ const examples = [];
4145
+ for (let i = 0; i < Math.min(count, combos.length); i++) {
4146
+ const combo = combos[i];
4147
+ const roles = /* @__PURE__ */ new Map();
4148
+ for (const role of combo) {
4149
+ const sample = sampleValue(role);
4150
+ roles.set(role.role, sample);
4151
+ }
4152
+ const nodeRoles = /* @__PURE__ */ new Map();
4153
+ for (const [roleName, sample] of roles) {
4154
+ nodeRoles.set(roleName, toSemanticValue(sample));
4155
+ }
4156
+ const node = createCommandNode(schema.action, nodeRoles);
4157
+ const explicit = renderExplicit(node);
4158
+ const jsonRoles = {};
4159
+ for (const [roleName, sample] of roles) {
4160
+ jsonRoles[roleName] = sample;
4161
+ }
4162
+ const json = { action: schema.action, roles: jsonRoles };
4163
+ examples.push({ explicit, json });
4164
+ }
4165
+ return examples;
4166
+ }
4167
+ function generateProtocolReference() {
4168
+ const protocol = buildProtocolSection();
4169
+ const valueTypes = buildValueTypeSection();
4170
+ return `# LokaScript Explicit Syntax (LSE) Quick Reference
4171
+
4172
+ ${protocol.content}
4173
+
4174
+ ${valueTypes.content}`;
4175
+ }
4176
+ function buildProtocolSection() {
4177
+ const content = `LokaScript Explicit Syntax (LSE) is a bracket-based format for imperative commands.
4178
+
4179
+ ### Syntax
4180
+
4181
+ \`\`\`
4182
+ [command role1:value1 role2:value2 +flag1 ~flag2]
4183
+ \`\`\`
4184
+
4185
+ - **Command**: The first token inside brackets (lowercased)
4186
+ - **Role pair**: \`name:value\` \u2014 a named semantic role with a typed value (no space around colon)
4187
+ - **Enabled flag**: \`+name\` \u2014 boolean attribute present
4188
+ - **Disabled flag**: \`~name\` \u2014 boolean attribute negated
4189
+ - **Nested body**: \`body:[command ...]\` \u2014 a bracket command inside a role value
4190
+
4191
+ ### Rules
4192
+
4193
+ 1. Commands are always lowercased
4194
+ 2. Role names preserve their original case
4195
+ 3. No spaces around the colon in role:value pairs
4196
+ 4. Strings with spaces must be quoted: \`patient:"hello world"\`
4197
+ 5. Selectors start with \`#\`, \`.\`, \`[\`, \`@\`, or \`*\`
4198
+ 6. Output must be valid bracket syntax: \`[action role:value ...]\``;
4199
+ return {
4200
+ id: "protocol",
4201
+ title: "LSE Protocol",
4202
+ content,
4203
+ approximateTokens: estimateTokens(content)
4204
+ };
4205
+ }
4206
+ function buildValueTypeSection() {
4207
+ const content = `Values in LSE are classified by their syntactic form (first match wins):
4208
+
4209
+ | Type | Syntax | Example |
4210
+ |------|--------|---------|
4211
+ | Selector | Starts with \`#\` \`.\` \`[\` \`@\` \`*\` | \`#button\`, \`.active\`, \`[data-id]\` |
4212
+ | String | Quoted with \`"\` or \`'\` | \`"hello world"\`, \`'json'\` |
4213
+ | Boolean | Exact: \`true\` / \`false\` | \`visible:true\` |
4214
+ | Reference | Built-in name | \`me\`, \`you\`, \`it\`, \`result\`, \`event\`, \`target\`, \`body\` |
4215
+ | Duration | Number + suffix | \`500ms\`, \`2s\`, \`1m\`, \`1h\` |
4216
+ | Number | Digits with optional decimal | \`5\`, \`3.14\`, \`-1\` |
4217
+ | Plain | Fallback (any non-whitespace) | \`/api/users\`, \`json\`, \`production\` |
4218
+
4219
+ **Important**: Classification is by prefix, not by intent. \`#true\` is a selector (not boolean). \`event\` as a value is a reference (not plain text).`;
4220
+ return {
4221
+ id: "value-types",
4222
+ title: "Value Types",
4223
+ content,
4224
+ approximateTokens: estimateTokens(content)
4225
+ };
4226
+ }
4227
+ function buildCommandsSection(schemas, outputFormat, examplesPerCommand) {
4228
+ const parts = [];
4229
+ for (const schema of schemas) {
4230
+ parts.push(formatCommandDoc(schema, outputFormat, examplesPerCommand));
4231
+ }
4232
+ const content = parts.join("\n\n");
4233
+ return {
4234
+ id: "commands",
4235
+ title: "Available Commands",
4236
+ content,
4237
+ approximateTokens: estimateTokens(content)
4238
+ };
4239
+ }
4240
+ function buildOutputFormatSection(outputFormat) {
4241
+ let content;
4242
+ if (outputFormat === "explicit") {
4243
+ content = `Output valid LSE bracket syntax. Each command must be wrapped in brackets:
4244
+
4245
+ \`\`\`
4246
+ [command role:value ...]
4247
+ \`\`\`
4248
+
4249
+ Do NOT output JSON. Only use bracket syntax.`;
4250
+ } else if (outputFormat === "json") {
4251
+ content = `Output valid JSON in the LLM-simplified format:
4252
+
4253
+ \`\`\`json
4254
+ {
4255
+ "action": "command-name",
4256
+ "roles": {
4257
+ "roleName": { "type": "valueType", "value": "theValue" }
4258
+ }
4259
+ }
4260
+ \`\`\`
4261
+
4262
+ Valid value types: \`selector\`, \`literal\`, \`reference\`, \`expression\`.
4263
+ Do NOT output bracket syntax. Only use JSON.`;
4264
+ } else {
4265
+ content = `You may output EITHER format:
4266
+
4267
+ **Bracket syntax** (preferred for single commands):
4268
+ \`\`\`
4269
+ [command role:value ...]
4270
+ \`\`\`
4271
+
4272
+ **JSON format** (preferred when structured data is needed):
4273
+ \`\`\`json
4274
+ {
4275
+ "action": "command-name",
4276
+ "roles": {
4277
+ "roleName": { "type": "valueType", "value": "theValue" }
4278
+ }
4279
+ }
4280
+ \`\`\`
4281
+
4282
+ Both formats are equally valid. Use whichever is more natural for the context.`;
4283
+ }
4284
+ return {
4285
+ id: "output-format",
4286
+ title: "Output Format",
4287
+ content,
4288
+ approximateTokens: estimateTokens(content)
4289
+ };
4290
+ }
4291
+ function buildErrorRecoverySection() {
4292
+ const content = `If you're unsure about a command or role:
4293
+
4294
+ 1. **Unknown command**: Use the closest matching command from the Available Commands section. Do not invent commands.
4295
+ 2. **Unknown role**: Use only the roles listed for each command. Do not add roles not in the schema.
4296
+ 3. **Ambiguous value type**: When in doubt, use \`expression\` type (it's the most permissive).
4297
+ 4. **Missing required role**: Always include all required roles. Check the Required/Optional labels.
4298
+ 5. **Selector vs plain value**: If a value starts with \`#\`, \`.\`, \`[\`, \`@\`, or \`*\`, it's a selector. URLs like \`/api/data\` are plain values.
4299
+
4300
+ When generating LSE, prefer explicit role labeling over positional guessing. Every value should have a role name.`;
4301
+ return {
4302
+ id: "error-recovery",
4303
+ title: "Error Recovery",
4304
+ content,
4305
+ approximateTokens: estimateTokens(content)
4306
+ };
4307
+ }
4308
+ function formatCommandDoc(schema, outputFormat, examplesPerCommand) {
4309
+ const lines = [];
4310
+ lines.push(`### \`${schema.action}\` \u2014 ${schema.description}`);
4311
+ lines.push("");
4312
+ const required = schema.roles.filter((r) => r.required);
4313
+ const optional = schema.roles.filter((r) => !r.required);
4314
+ if (required.length > 0) {
4315
+ lines.push("**Required roles:**");
4316
+ for (const role of required) {
4317
+ lines.push(`- \`${role.role}\`: ${role.description} (type: ${formatExpectedTypes(role)})`);
4318
+ }
4319
+ }
4320
+ if (optional.length > 0) {
4321
+ lines.push("**Optional roles:**");
4322
+ for (const role of optional) {
4323
+ lines.push(`- \`${role.role}\`: ${role.description} (type: ${formatExpectedTypes(role)})`);
4324
+ }
4325
+ }
4326
+ const examples = generateExamples(schema, examplesPerCommand);
4327
+ if (examples.length > 0) {
4328
+ lines.push("");
4329
+ if (outputFormat === "explicit" || outputFormat === "both") {
4330
+ lines.push("**Bracket syntax:**");
4331
+ for (const ex of examples) {
4332
+ lines.push(`\`\`\`
4333
+ ${ex.explicit}
4334
+ \`\`\``);
4335
+ }
4336
+ }
4337
+ if (outputFormat === "json" || outputFormat === "both") {
4338
+ lines.push("**JSON format:**");
4339
+ for (const ex of examples) {
4340
+ lines.push(`\`\`\`json
4341
+ ${JSON.stringify(ex.json, null, 2)}
4342
+ \`\`\``);
4343
+ }
4344
+ }
4345
+ }
4346
+ return lines.join("\n");
4347
+ }
4348
+ function formatExpectedTypes(role) {
4349
+ return role.expectedTypes.join(" | ");
4350
+ }
4351
+ function generateRoleCombinations(schema) {
4352
+ const required = schema.roles.filter((r) => r.required);
4353
+ const optional = schema.roles.filter((r) => !r.required);
4354
+ const combos = [];
4355
+ if (required.length > 0) {
4356
+ combos.push(required);
4357
+ }
4358
+ for (const opt of optional) {
4359
+ combos.push([...required, opt]);
4360
+ }
4361
+ if (optional.length >= 2) {
4362
+ combos.push([...required, ...optional]);
4363
+ }
4364
+ if (combos.length === 0) {
4365
+ combos.push(schema.roles.slice());
4366
+ }
4367
+ return combos;
4368
+ }
4369
+ function sampleValue(role) {
4370
+ const primaryType = role.expectedTypes[0] || "expression";
4371
+ switch (primaryType) {
4372
+ case "selector":
4373
+ return sampleSelector(role.role);
4374
+ case "literal":
4375
+ return sampleLiteral(role.role);
4376
+ case "reference":
4377
+ return { type: "reference", value: "me" };
4378
+ case "expression":
4379
+ return sampleExpression(role.role);
4380
+ case "flag":
4381
+ return { type: "flag", value: true };
4382
+ default:
4383
+ return { type: "literal", value: "example" };
4384
+ }
4385
+ }
4386
+ function sampleSelector(roleName) {
4387
+ const selectors = {
4388
+ patient: ".active",
4389
+ destination: "#output",
4390
+ source: "#input",
4391
+ target: "#target"
4392
+ };
4393
+ return { type: "selector", value: selectors[roleName] || `#${roleName}` };
4394
+ }
4395
+ function sampleLiteral(roleName) {
4396
+ const literals = {
4397
+ duration: "30s",
4398
+ interval: "5s",
4399
+ delay: "500ms",
4400
+ timeout: "10s",
4401
+ quantity: 5,
4402
+ limit: 10,
4403
+ style: "json",
4404
+ manner: "rolling",
4405
+ format: "html"
4406
+ };
4407
+ return { type: "literal", value: literals[roleName] || roleName };
4408
+ }
4409
+ function sampleExpression(roleName) {
4410
+ const expressions = {
4411
+ source: "/api/data",
4412
+ destination: "#output",
4413
+ patient: ".active",
4414
+ instrument: "toUpperCase",
4415
+ condition: "age > 18",
4416
+ style: "json",
4417
+ duration: "5s",
4418
+ url: "/api/users"
4419
+ };
4420
+ return { type: "expression", value: expressions[roleName] || `/api/${roleName}` };
4421
+ }
4422
+ function toSemanticValue(sample) {
4423
+ switch (sample.type) {
4424
+ case "selector":
4425
+ return {
4426
+ type: "selector",
4427
+ value: String(sample.value),
4428
+ selectorKind: detectSelectorKind2(String(sample.value))
4429
+ };
4430
+ case "literal":
4431
+ return {
4432
+ type: "literal",
4433
+ value: sample.value,
4434
+ dataType: typeof sample.value === "number" ? "number" : typeof sample.value === "boolean" ? "boolean" : "string"
4435
+ };
4436
+ case "reference":
4437
+ return { type: "reference", value: String(sample.value) };
4438
+ case "expression":
4439
+ return { type: "expression", value: String(sample.value), raw: String(sample.value) };
4440
+ case "flag":
4441
+ return { type: "flag", value: sample.value };
4442
+ default:
4443
+ return { type: "literal", value: String(sample.value), dataType: "string" };
4444
+ }
4445
+ }
4446
+ function detectSelectorKind2(selector) {
4447
+ if (selector.startsWith("#")) return "id";
4448
+ if (selector.startsWith(".")) return "class";
4449
+ if (selector.startsWith("[")) return "attribute";
4450
+ return "complex";
4451
+ }
4452
+ function estimateTokens(text) {
4453
+ return Math.ceil(text.length / 4);
4454
+ }
4455
+ function truncateToTokenBudget(sections, maxTokens) {
4456
+ const result = [];
4457
+ let budget = maxTokens;
4458
+ for (const section of sections) {
4459
+ if (section.approximateTokens <= budget) {
4460
+ result.push(section);
4461
+ budget -= section.approximateTokens;
4462
+ } else {
4463
+ const charBudget = Math.max(budget * 4, 40);
4464
+ const truncatedContent = section.content.slice(0, charBudget) + "\n\n*(truncated)*";
4465
+ result.push({
4466
+ ...section,
4467
+ content: truncatedContent,
4468
+ approximateTokens: estimateTokens(truncatedContent)
4469
+ });
4470
+ break;
4471
+ }
4472
+ }
4473
+ return result;
4474
+ }
4475
+
4476
+ // src/training/schema-synthesizer.ts
4477
+ function synthesizeFromSchemas(schemas, config) {
4478
+ const maxPerCommand = config.maxPairsPerCommand ?? 5;
4479
+ const minQuality = config.minQuality ?? 0.5;
4480
+ const shuffleRoles = config.shuffleRoles ?? true;
4481
+ const languages = config.languages ?? ["en"];
4482
+ const pairs = [];
4483
+ let idCounter = 0;
4484
+ for (const schema of schemas) {
4485
+ const combos = generateRoleCombinations2(schema);
4486
+ for (const language of languages) {
4487
+ let commandPairCount = 0;
4488
+ for (const combo of combos) {
4489
+ if (commandPairCount >= maxPerCommand) break;
4490
+ const pair = buildTrainingPair(
4491
+ schema,
4492
+ combo,
4493
+ language,
4494
+ config.domain,
4495
+ `${config.domain}-synth-${idCounter++}`
4496
+ );
4497
+ if (pair.quality >= minQuality) {
4498
+ pairs.push(pair);
4499
+ commandPairCount++;
4500
+ }
4501
+ if (shuffleRoles && combo.length >= 2 && commandPairCount < maxPerCommand) {
4502
+ const shuffled = [...combo].reverse();
4503
+ const shuffledPair = buildTrainingPair(
4504
+ schema,
4505
+ shuffled,
4506
+ language,
4507
+ config.domain,
4508
+ `${config.domain}-synth-${idCounter++}`
4509
+ );
4510
+ if (shuffledPair.quality >= minQuality) {
4511
+ pairs.push(shuffledPair);
4512
+ commandPairCount++;
4513
+ }
4514
+ }
4515
+ }
4516
+ }
4517
+ }
4518
+ const metadata = {
4519
+ domain: config.domain,
4520
+ commandCount: schemas.length,
4521
+ pairCount: pairs.length,
4522
+ bySource: { synthetic: pairs.length },
4523
+ languages
4524
+ };
4525
+ return { pairs, metadata };
4526
+ }
4527
+ function generateRoleCombinations2(schema) {
4528
+ const required = schema.roles.filter((r) => r.required);
4529
+ const optional = schema.roles.filter((r) => !r.required);
4530
+ const combos = [];
4531
+ if (required.length > 0) {
4532
+ combos.push(required);
4533
+ }
4534
+ for (const opt of optional) {
4535
+ combos.push([...required, opt]);
4536
+ }
4537
+ if (optional.length >= 2) {
4538
+ combos.push([...required, ...optional]);
4539
+ }
4540
+ if (combos.length === 0) {
4541
+ combos.push(schema.roles.slice());
4542
+ }
4543
+ return combos;
4544
+ }
4545
+ function buildTrainingPair(schema, roles, language, domain, id) {
4546
+ const sampledRoles = /* @__PURE__ */ new Map();
4547
+ for (const role of roles) {
4548
+ sampledRoles.set(role.role, sampleValue2(role));
4549
+ }
4550
+ const nodeRoles = /* @__PURE__ */ new Map();
4551
+ for (const [name, sample] of sampledRoles) {
4552
+ nodeRoles.set(name, toSemanticValue2(sample));
4553
+ }
4554
+ const node = createCommandNode(schema.action, nodeRoles);
4555
+ const explicit = renderExplicit(node);
4556
+ const jsonRoles = {};
4557
+ for (const [name, sample] of sampledRoles) {
4558
+ jsonRoles[name] = sample;
4559
+ }
4560
+ const json = { action: schema.action, roles: jsonRoles };
4561
+ const natural = buildNaturalForm(schema, sampledRoles, language);
4562
+ const requiredCount = schema.roles.filter((r) => r.required).length;
4563
+ const filledRequired = roles.filter((r) => r.required).length;
4564
+ const quality = requiredCount > 0 ? filledRequired / requiredCount : 0.8;
4565
+ return {
4566
+ id,
4567
+ natural,
4568
+ language,
4569
+ explicit,
4570
+ json,
4571
+ domain,
4572
+ action: schema.action,
4573
+ source: "synthetic",
4574
+ quality
4575
+ };
4576
+ }
4577
+ function buildNaturalForm(schema, roles, language) {
4578
+ const parts = [schema.action];
4579
+ for (const roleSpec of schema.roles) {
4580
+ const sample = roles.get(roleSpec.role);
4581
+ if (!sample) continue;
4582
+ const marker = roleSpec.markerOverride?.[language];
4583
+ if (marker) {
4584
+ parts.push(marker);
4585
+ }
4586
+ parts.push(String(sample.value));
4587
+ }
4588
+ return parts.join(" ");
4589
+ }
4590
+ function sampleValue2(role) {
4591
+ const primaryType = role.expectedTypes[0] || "expression";
4592
+ switch (primaryType) {
4593
+ case "selector":
4594
+ return sampleSelector2(role.role);
4595
+ case "literal":
4596
+ return sampleLiteral2(role.role);
4597
+ case "reference":
4598
+ return { type: "reference", value: "me" };
4599
+ case "expression":
4600
+ return sampleExpression2(role.role);
4601
+ case "flag":
4602
+ return { type: "flag", value: true };
4603
+ default:
4604
+ return { type: "literal", value: "example" };
4605
+ }
4606
+ }
4607
+ function sampleSelector2(roleName) {
4608
+ const selectors = {
4609
+ patient: ".active",
4610
+ destination: "#output",
4611
+ source: "#input",
4612
+ target: "#target"
4613
+ };
4614
+ return { type: "selector", value: selectors[roleName] || `#${roleName}` };
4615
+ }
4616
+ function sampleLiteral2(roleName) {
4617
+ const literals = {
4618
+ duration: "30s",
4619
+ interval: "5s",
4620
+ delay: "500ms",
4621
+ timeout: "10s",
4622
+ quantity: 5,
4623
+ limit: 10,
4624
+ style: "json",
4625
+ manner: "rolling",
4626
+ format: "html"
4627
+ };
4628
+ return { type: "literal", value: literals[roleName] || roleName };
4629
+ }
4630
+ function sampleExpression2(roleName) {
4631
+ const expressions = {
4632
+ source: "/api/data",
4633
+ destination: "#output",
4634
+ patient: ".active",
4635
+ instrument: "toUpperCase",
4636
+ condition: "age > 18",
4637
+ style: "json",
4638
+ duration: "5s",
4639
+ url: "/api/users"
4640
+ };
4641
+ return { type: "expression", value: expressions[roleName] || `/api/${roleName}` };
4642
+ }
4643
+ function toSemanticValue2(sample) {
4644
+ switch (sample.type) {
4645
+ case "selector":
2127
4646
  return {
2128
- ok: true,
2129
- code,
2130
- node: result.node,
2131
- metadata: {
2132
- parser: "semantic",
2133
- confidence: result.confidence
2134
- }
4647
+ type: "selector",
4648
+ value: String(sample.value),
4649
+ selectorKind: detectSelectorKind3(String(sample.value))
2135
4650
  };
2136
- } catch (error) {
4651
+ case "literal":
2137
4652
  return {
2138
- ok: false,
2139
- errors: [error instanceof Error ? error.message : String(error)]
4653
+ type: "literal",
4654
+ value: sample.value,
4655
+ dataType: typeof sample.value === "number" ? "number" : typeof sample.value === "boolean" ? "boolean" : "string"
2140
4656
  };
4657
+ case "reference":
4658
+ return { type: "reference", value: String(sample.value) };
4659
+ case "expression":
4660
+ return { type: "expression", value: String(sample.value), raw: String(sample.value) };
4661
+ case "flag":
4662
+ return { type: "flag", value: sample.value };
4663
+ default:
4664
+ return { type: "literal", value: String(sample.value), dataType: "string" };
4665
+ }
4666
+ }
4667
+ function detectSelectorKind3(selector) {
4668
+ if (selector.startsWith("#")) return "id";
4669
+ if (selector.startsWith(".")) return "class";
4670
+ if (selector.startsWith("[")) return "attribute";
4671
+ return "complex";
4672
+ }
4673
+
4674
+ // src/feedback/feedback-formatter.ts
4675
+ function buildFeedback(input, inputFormat, diagnostics, schemaLookup, action) {
4676
+ const hasErrors = diagnostics.some((d) => d.severity === "error");
4677
+ const feedbackDiagnostics = diagnostics.map((d) => toFeedbackDiagnostic(d));
4678
+ const hints = generateHints(feedbackDiagnostics, schemaLookup, action);
4679
+ let schemaInfo;
4680
+ let correctedExample;
4681
+ if (action && schemaLookup) {
4682
+ const schema = schemaLookup.getSchema(action);
4683
+ if (schema) {
4684
+ schemaInfo = extractSchemaInfo(schema);
4685
+ if (hasErrors) {
4686
+ correctedExample = generateCorrectedExample(schema);
4687
+ }
2141
4688
  }
2142
4689
  }
2143
- getSupportedLanguages() {
2144
- return this.registry.getSupportedLanguages();
4690
+ return {
4691
+ accepted: !hasErrors,
4692
+ input: { format: inputFormat, text: input },
4693
+ diagnostics: feedbackDiagnostics,
4694
+ hints,
4695
+ ...schemaInfo && { schema: schemaInfo },
4696
+ ...correctedExample && { correctedExample }
4697
+ };
4698
+ }
4699
+ function toFeedbackDiagnostic(d) {
4700
+ const fixType = classifyFixType(d);
4701
+ return {
4702
+ severity: d.severity,
4703
+ code: d.code || "unknown",
4704
+ message: d.message,
4705
+ ...d.suggestions?.[0] && { suggestion: d.suggestions[0] },
4706
+ ...fixType && { fixType }
4707
+ };
4708
+ }
4709
+ function classifyFixType(d) {
4710
+ const code = (d.code || "").toLowerCase();
4711
+ const msg = d.message.toLowerCase();
4712
+ if (code.includes("missing-role") || code.includes("missing_role") || msg.includes("required role") || msg.includes("missing role")) {
4713
+ return "missing_role";
2145
4714
  }
2146
- };
2147
- function createDefaultDictionary(config) {
2148
- const translations = {};
2149
- for (const lang of config.languages) {
2150
- const keywords = {};
2151
- for (const [canonical, keywordDef] of Object.entries(lang.patternProfile.keywords)) {
2152
- keywords[canonical] = keywordDef.primary;
4715
+ if (code.includes("invalid-type") || code.includes("invalid_type") || code.includes("type-mismatch") || msg.includes("invalid type") || msg.includes("expected type")) {
4716
+ return "invalid_type";
4717
+ }
4718
+ if (code.includes("unknown-command") || code.includes("unknown_command") || msg.includes("unknown command") || msg.includes("not recognized") && msg.includes("command")) {
4719
+ return "unknown_command";
4720
+ }
4721
+ if (code.includes("unknown-role") || code.includes("unknown_role") || msg.includes("unknown role") || msg.includes("not recognized") && msg.includes("role")) {
4722
+ return "unknown_role";
4723
+ }
4724
+ if (code.includes("parse") || code.includes("syntax") || msg.includes("syntax error") || msg.includes("parse error") || msg.includes("unexpected")) {
4725
+ return "syntax_error";
4726
+ }
4727
+ return void 0;
4728
+ }
4729
+ function generateHints(diagnostics, schemaLookup, action) {
4730
+ const hints = [];
4731
+ const schema = action && schemaLookup ? schemaLookup.getSchema(action) : void 0;
4732
+ for (const d of diagnostics) {
4733
+ if (d.severity !== "error") continue;
4734
+ switch (d.fixType) {
4735
+ case "missing_role": {
4736
+ if (schema) {
4737
+ const required = schema.roles.filter((r) => r.required).map((r) => `'${r.role}' (${r.description}, type: ${r.expectedTypes.join("|")})`).join(", ");
4738
+ hints.push(`Required roles for '${action}': ${required}`);
4739
+ } else {
4740
+ hints.push("Check that all required roles are present.");
4741
+ }
4742
+ break;
4743
+ }
4744
+ case "unknown_command": {
4745
+ hints.push(
4746
+ `Command not recognized. Ensure you're using a valid command from the domain schema.`
4747
+ );
4748
+ break;
4749
+ }
4750
+ case "unknown_role": {
4751
+ if (schema) {
4752
+ const validRoles = schema.roles.map((r) => `'${r.role}'`).join(", ");
4753
+ hints.push(`Valid roles for '${action}': ${validRoles}`);
4754
+ } else {
4755
+ hints.push("Use only roles defined in the command schema.");
4756
+ }
4757
+ break;
4758
+ }
4759
+ case "invalid_type": {
4760
+ hints.push(
4761
+ "Check value types: selectors start with #.[@*, strings use quotes, numbers are plain digits."
4762
+ );
4763
+ break;
4764
+ }
4765
+ case "syntax_error": {
4766
+ hints.push("Ensure bracket syntax: [command role:value ...]. No spaces around the colon.");
4767
+ break;
4768
+ }
4769
+ default: {
4770
+ if (d.suggestion) {
4771
+ hints.push(d.suggestion);
4772
+ }
4773
+ }
2153
4774
  }
2154
- translations[lang.code] = keywords;
2155
4775
  }
2156
- return new InMemoryDictionary(translations);
4776
+ return [...new Set(hints)];
2157
4777
  }
2158
- function createDefaultProfileProvider(config) {
2159
- const profiles = {};
2160
- for (const lang of config.languages) {
2161
- if (lang.grammarProfile) {
2162
- profiles[lang.code] = lang.grammarProfile;
4778
+ function extractSchemaInfo(schema) {
4779
+ const requiredRoles = [];
4780
+ const optionalRoles = [];
4781
+ const roleDescriptions = {};
4782
+ for (const role of schema.roles) {
4783
+ if (role.required) {
4784
+ requiredRoles.push(role.role);
4785
+ } else {
4786
+ optionalRoles.push(role.role);
2163
4787
  }
4788
+ roleDescriptions[role.role] = role.description;
2164
4789
  }
2165
- return new InMemoryProfileProvider(profiles);
4790
+ return {
4791
+ action: schema.action,
4792
+ requiredRoles,
4793
+ optionalRoles,
4794
+ roleDescriptions
4795
+ };
2166
4796
  }
2167
- function createMultilingualDSL(config) {
2168
- const dictionary = config.dictionary ?? createDefaultDictionary(config);
2169
- const profileProvider = config.profileProvider ?? createDefaultProfileProvider(config);
2170
- const transformer = new GrammarTransformer({
2171
- dictionary,
2172
- profileProvider
2173
- });
2174
- const registry = new DSLRegistry(config);
2175
- return new MultilingualDSLImpl(config, registry, transformer);
4797
+ function generateCorrectedExample(schema) {
4798
+ const roles = /* @__PURE__ */ new Map();
4799
+ const jsonRoles = {};
4800
+ for (const role of schema.roles) {
4801
+ if (!role.required) continue;
4802
+ const sample = sampleDefaultValue(role.role, role.expectedTypes[0] || "expression");
4803
+ roles.set(role.role, toSemanticValue3(sample));
4804
+ jsonRoles[role.role] = sample;
4805
+ }
4806
+ const node = createCommandNode(schema.action, roles);
4807
+ const explicit = renderExplicit(node);
4808
+ const json = { action: schema.action, roles: jsonRoles };
4809
+ return { explicit, json };
4810
+ }
4811
+ function sampleDefaultValue(roleName, expectedType) {
4812
+ switch (expectedType) {
4813
+ case "selector":
4814
+ return { type: "selector", value: `#${roleName}` };
4815
+ case "expression":
4816
+ return { type: "expression", value: `<${roleName}-value>` };
4817
+ case "literal":
4818
+ return { type: "literal", value: `<${roleName}-value>` };
4819
+ case "reference":
4820
+ return { type: "reference", value: "me" };
4821
+ default:
4822
+ return { type: "expression", value: `<${roleName}-value>` };
4823
+ }
4824
+ }
4825
+ function toSemanticValue3(sample) {
4826
+ switch (sample.type) {
4827
+ case "selector":
4828
+ return { type: "selector", value: sample.value, selectorKind: "id" };
4829
+ case "expression":
4830
+ return { type: "expression", value: sample.value, raw: sample.value };
4831
+ case "literal":
4832
+ return { type: "literal", value: sample.value, dataType: "string" };
4833
+ case "reference":
4834
+ return { type: "reference", value: sample.value };
4835
+ default:
4836
+ return { type: "literal", value: sample.value, dataType: "string" };
4837
+ }
2176
4838
  }
2177
4839
 
2178
4840
  // src/api/domain-registry.ts
@@ -2192,6 +4854,17 @@ var DomainRegistry = class {
2192
4854
  }
2193
4855
  this.descriptors.set(descriptor.name, descriptor);
2194
4856
  }
4857
+ /**
4858
+ * Attach schemas to an already-registered domain.
4859
+ * Useful for lazy loading schemas after initial registration.
4860
+ */
4861
+ setSchemas(name, schemas) {
4862
+ const existing = this.descriptors.get(name);
4863
+ if (!existing) {
4864
+ throw new Error(`Domain not registered: ${name}`);
4865
+ }
4866
+ this.descriptors.set(name, { ...existing, schemas });
4867
+ }
2195
4868
  /**
2196
4869
  * Get all registered domain names.
2197
4870
  */
@@ -2261,6 +4934,71 @@ var DomainRegistry = class {
2261
4934
  return this.getDSL(descriptor);
2262
4935
  }
2263
4936
  // ---------------------------------------------------------------------------
4937
+ // LLM ↔ LSE Convenience Methods
4938
+ // ---------------------------------------------------------------------------
4939
+ /**
4940
+ * Generate an LLM system prompt for a domain from its schemas.
4941
+ * Returns null if the domain is not registered or has no schemas.
4942
+ */
4943
+ generatePrompt(domain, config) {
4944
+ const descriptor = this.descriptors.get(domain);
4945
+ if (!descriptor?.schemas || descriptor.schemas.length === 0) return null;
4946
+ return generatePrompt({
4947
+ domain,
4948
+ description: descriptor.description,
4949
+ schemas: descriptor.schemas,
4950
+ ...config
4951
+ });
4952
+ }
4953
+ /**
4954
+ * Synthesize training data for a domain from its schemas.
4955
+ * Returns null if the domain is not registered or has no schemas.
4956
+ */
4957
+ generateTrainingData(domain, config) {
4958
+ const descriptor = this.descriptors.get(domain);
4959
+ if (!descriptor?.schemas || descriptor.schemas.length === 0) return null;
4960
+ return synthesizeFromSchemas(descriptor.schemas, {
4961
+ domain,
4962
+ ...config
4963
+ });
4964
+ }
4965
+ /**
4966
+ * Build structured feedback for a failed LSE input in a domain.
4967
+ * Returns null if the domain is not registered or has no schemas.
4968
+ */
4969
+ buildFeedback(domain, input, inputFormat, diagnostics) {
4970
+ const descriptor = this.descriptors.get(domain);
4971
+ if (!descriptor?.schemas || descriptor.schemas.length === 0) return null;
4972
+ const schemaLookup = this.createSchemaLookup(descriptor.schemas);
4973
+ let action;
4974
+ try {
4975
+ if (inputFormat === "explicit") {
4976
+ const node = parseExplicit(input, { schemaLookup });
4977
+ action = node.action;
4978
+ }
4979
+ } catch {
4980
+ }
4981
+ return buildFeedback(input, inputFormat, diagnostics, schemaLookup, action);
4982
+ }
4983
+ /**
4984
+ * Get schemas for a domain (if available).
4985
+ */
4986
+ getSchemas(domain) {
4987
+ const descriptor = this.descriptors.get(domain);
4988
+ if (!descriptor?.schemas) return null;
4989
+ return descriptor.schemas;
4990
+ }
4991
+ /**
4992
+ * Create a SchemaLookup from a domain's schemas.
4993
+ */
4994
+ createSchemaLookup(schemas) {
4995
+ const map = /* @__PURE__ */ new Map();
4996
+ for (const s of schemas) {
4997
+ map.set(s.action, s);
4998
+ }
4999
+ return { getSchema: (action) => map.get(action) };
5000
+ }
5001
+ // ---------------------------------------------------------------------------
2264
5002
  // Private: DSL/renderer lifecycle
2265
5003
  // ---------------------------------------------------------------------------
2266
5004
  async getDSL(descriptor) {
@@ -2360,7 +5098,7 @@ function generateToolDefinitions(desc) {
2360
5098
  case "parse":
2361
5099
  tools.push({
2362
5100
  name: `parse_${desc.name}`,
2363
- description: `Parse a ${desc.description} input into a semantic representation. Supports: ${langList}.`,
5101
+ description: `Parse a ${desc.description} input into a semantic representation (action, roles, confidence). Supports: ${langList}.`,
2364
5102
  inputSchema: {
2365
5103
  type: "object",
2366
5104
  properties: {
@@ -2381,7 +5119,7 @@ function generateToolDefinitions(desc) {
2381
5119
  case "compile":
2382
5120
  tools.push({
2383
5121
  name: `compile_${desc.name}`,
2384
- description: `Compile a ${desc.description} input to target code. Supports: ${langList}.`,
5122
+ description: `Compile a ${desc.description} input to ${desc.outputDescription ?? "target code"}. Supports: ${langList}.`,
2385
5123
  inputSchema: {
2386
5124
  type: "object",
2387
5125
  properties: {
@@ -2402,7 +5140,7 @@ function generateToolDefinitions(desc) {
2402
5140
  case "validate":
2403
5141
  tools.push({
2404
5142
  name: `validate_${desc.name}`,
2405
- description: `Validate ${desc.description} syntax. Returns whether it parses successfully and any errors. Supports: ${langList}.`,
5143
+ description: `Check ${desc.description} syntax for errors. Returns {valid, errors[]}. Supports: ${langList}.`,
2406
5144
  inputSchema: {
2407
5145
  type: "object",
2408
5146
  properties: {
@@ -2423,7 +5161,7 @@ function generateToolDefinitions(desc) {
2423
5161
  case "translate":
2424
5162
  tools.push({
2425
5163
  name: `translate_${desc.name}`,
2426
- description: `Translate ${desc.description} input between natural languages. Parses in source language and renders in target language.`,
5164
+ description: `Translate ${desc.description} input between natural languages (requires 'from' and 'to' codes). Supports: ${langList}.`,
2427
5165
  inputSchema: {
2428
5166
  type: "object",
2429
5167
  properties: {
@@ -2489,9 +5227,9 @@ var CrossDomainDispatcher = class {
2489
5227
  if (domainNames.length === 0) return null;
2490
5228
  const candidates = [];
2491
5229
  for (const name of domainNames) {
2492
- const dsl = await this.registry.getDSLForDomain(name);
2493
- if (!dsl) continue;
2494
5230
  try {
5231
+ const dsl = await this.registry.getDSLForDomain(name);
5232
+ if (!dsl) continue;
2495
5233
  const { node, confidence } = dsl.parseWithConfidence(input, language);
2496
5234
  if (confidence >= this.minConfidence) {
2497
5235
  candidates.push({ domain: name, node, confidence, dsl });
@@ -2500,10 +5238,16 @@ var CrossDomainDispatcher = class {
2500
5238
  }
2501
5239
  }
2502
5240
  if (candidates.length === 0) return null;
2503
- candidates.sort((a, b) => {
2504
- if (b.confidence !== a.confidence) return b.confidence - a.confidence;
2505
- return this.getPriorityIndex(a.domain) - this.getPriorityIndex(b.domain);
2506
- });
5241
+ if (this.priority.length > 0) {
5242
+ candidates.sort((a, b) => {
5243
+ const pa = this.getPriorityIndex(a.domain);
5244
+ const pb = this.getPriorityIndex(b.domain);
5245
+ if (pa !== pb) return pa - pb;
5246
+ return b.confidence - a.confidence;
5247
+ });
5248
+ } else {
5249
+ candidates.sort((a, b) => b.confidence - a.confidence);
5250
+ }
2507
5251
  return candidates[0];
2508
5252
  }
2509
5253
  /**
@@ -2547,6 +5291,40 @@ var CrossDomainDispatcher = class {
2547
5291
  const result = detected.dsl.compile(input, language);
2548
5292
  return { ...result, domain: detected.domain };
2549
5293
  }
5294
+ /**
5295
+ * Compile multi-line input where each line may belong to a different domain.
5296
+ * Detects the domain for each line and compiles it, returning per-line results.
5297
+ * Empty lines and comment lines are skipped.
5298
+ */
5299
+ async compileComposite(input, language = "en") {
5300
+ const lines = input.split("\n");
5301
+ const statements = [];
5302
+ const errors = [];
5303
+ for (let i = 0; i < lines.length; i++) {
5304
+ const line = lines[i];
5305
+ if (isSkippableLine(line)) continue;
5306
+ const trimmed = line.trim();
5307
+ const detected = await this.detect(trimmed, language);
5308
+ if (detected) {
5309
+ const result = detected.dsl.compile(trimmed, language);
5310
+ statements.push({
5311
+ line: i + 1,
5312
+ input: trimmed,
5313
+ domain: detected.domain,
5314
+ ok: result.ok,
5315
+ code: result.code,
5316
+ errors: result.errors
5317
+ });
5318
+ } else {
5319
+ errors.push({
5320
+ line: i + 1,
5321
+ input: trimmed,
5322
+ message: "No domain matched this input"
5323
+ });
5324
+ }
5325
+ }
5326
+ return { statements, errors };
5327
+ }
2550
5328
  /**
2551
5329
  * Validate input against all domains, returning the best match.
2552
5330
  */
@@ -2828,6 +5606,30 @@ ${body}
2828
5606
  }
2829
5607
  };
2830
5608
 
5609
+ // src/aot/registry-bridge.ts
5610
+ async function registryToAOTBackends(registry) {
5611
+ const backends = [];
5612
+ for (const name of registry.getDomainNames()) {
5613
+ const descriptor = registry.getDescriptor(name);
5614
+ if (!descriptor?.scanConfig) continue;
5615
+ const dsl = await registry.getDSLForDomain(name);
5616
+ if (!dsl) continue;
5617
+ const scanConfig = {
5618
+ domain: name,
5619
+ attributes: [...descriptor.scanConfig.attributes],
5620
+ ...descriptor.scanConfig.scriptTypes ? { scriptTypes: [...descriptor.scanConfig.scriptTypes] } : {},
5621
+ defaultLanguage: descriptor.scanConfig.defaultLanguage ?? "en"
5622
+ };
5623
+ const codeGenerator = {
5624
+ generate(node) {
5625
+ return JSON.stringify({ action: node.action, roles: Object.fromEntries(node.roles) });
5626
+ }
5627
+ };
5628
+ backends.push({ domain: name, dsl, codeGenerator, scanConfig });
5629
+ }
5630
+ return backends;
5631
+ }
5632
+
2831
5633
  // src/schema/command-schema.ts
2832
5634
  function defineCommand(schema) {
2833
5635
  return {
@@ -4119,6 +6921,295 @@ function normalized(stem, confidence, metadata) {
4119
6921
  return { stem, confidence };
4120
6922
  }
4121
6923
 
6924
+ // src/core/tokenization/morphology/base-normalizer.ts
6925
+ var BaseMorphologicalNormalizer = class {
6926
+ constructor(config) {
6927
+ this.language = config.language;
6928
+ this.config = {
6929
+ minWordLength: 3,
6930
+ minStemLength: 2,
6931
+ ...config
6932
+ };
6933
+ }
6934
+ /**
6935
+ * Standard normalization pipeline. Override for custom behavior.
6936
+ */
6937
+ normalize(word) {
6938
+ const lower = word.toLowerCase();
6939
+ if (this.isAlreadyNormalized(lower)) {
6940
+ return noChange(word);
6941
+ }
6942
+ if (this.config.reflexiveSuffixes) {
6943
+ const reflexive = this.tryReflexiveNormalization(lower);
6944
+ if (reflexive) return reflexive;
6945
+ }
6946
+ if (this.config.endings) {
6947
+ const conjugation = this.tryConjugationEndings(lower);
6948
+ if (conjugation) return conjugation;
6949
+ }
6950
+ if (this.config.suffixRules) {
6951
+ const suffix = this.trySuffixRules(lower);
6952
+ if (suffix) return suffix;
6953
+ }
6954
+ if (this.config.prefixRules) {
6955
+ const prefix = this.tryPrefixRules(lower);
6956
+ if (prefix) return prefix;
6957
+ }
6958
+ return noChange(word);
6959
+ }
6960
+ /**
6961
+ * Check if word is already in dictionary form (e.g., ends in -ar/-er/-ir).
6962
+ * Override for language-specific checks.
6963
+ */
6964
+ isAlreadyNormalized(word) {
6965
+ if (this.config.infinitiveEndings) {
6966
+ return this.config.infinitiveEndings.some((e) => word.endsWith(e));
6967
+ }
6968
+ return false;
6969
+ }
6970
+ /**
6971
+ * Try to strip reflexive suffixes and normalize the remainder.
6972
+ * Common in Romance languages (Spanish, Portuguese, French).
6973
+ */
6974
+ tryReflexiveNormalization(word) {
6975
+ const suffixes = this.config.reflexiveSuffixes;
6976
+ if (!suffixes) return null;
6977
+ for (const suffix of suffixes) {
6978
+ if (!word.endsWith(suffix)) continue;
6979
+ const remainder = word.slice(0, -suffix.length);
6980
+ if (this.isAlreadyNormalized(remainder)) {
6981
+ return normalized(remainder, 0.88, {
6982
+ removedSuffixes: [suffix],
6983
+ conjugationType: "reflexive"
6984
+ });
6985
+ }
6986
+ const inner = this.tryConjugationEndings(remainder) || this.trySuffixRules(remainder);
6987
+ if (inner && inner.stem !== remainder) {
6988
+ return normalized(inner.stem, inner.confidence * 0.95, {
6989
+ removedSuffixes: [suffix, ...inner.metadata?.removedSuffixes || []],
6990
+ conjugationType: "reflexive"
6991
+ });
6992
+ }
6993
+ }
6994
+ return null;
6995
+ }
6996
+ /**
6997
+ * Try conjugation endings (verb class endings like -ar/-er/-ir patterns).
6998
+ * Endings must be pre-sorted longest-first.
6999
+ */
7000
+ tryConjugationEndings(word) {
7001
+ const endings = this.config.endings;
7002
+ if (!endings) return null;
7003
+ const minStem = this.config.minStemLength ?? 2;
7004
+ for (const rule of endings) {
7005
+ if (!word.endsWith(rule.ending)) continue;
7006
+ const stemBase = word.slice(0, -rule.ending.length);
7007
+ if (stemBase.length < minStem) continue;
7008
+ const infinitive = stemBase + rule.stem;
7009
+ return normalized(infinitive, rule.confidence, {
7010
+ removedSuffixes: [rule.ending],
7011
+ conjugationType: rule.type
7012
+ });
7013
+ }
7014
+ return null;
7015
+ }
7016
+ /**
7017
+ * Try SuffixRule-style normalization.
7018
+ * Rules must be pre-sorted longest-first.
7019
+ */
7020
+ trySuffixRules(word) {
7021
+ const rules = this.config.suffixRules;
7022
+ if (!rules) return null;
7023
+ const defaultMinStem = this.config.minStemLength ?? 2;
7024
+ for (const rule of rules) {
7025
+ if (!word.endsWith(rule.pattern)) continue;
7026
+ const stem = word.slice(0, -rule.pattern.length);
7027
+ const minStem = rule.minStemLength ?? defaultMinStem;
7028
+ if (stem.length < minStem) continue;
7029
+ const result = stem + (rule.replacement || "");
7030
+ return normalized(result, rule.confidence, {
7031
+ removedSuffixes: [rule.pattern],
7032
+ ...rule.conjugationType && { conjugationType: rule.conjugationType }
7033
+ });
7034
+ }
7035
+ return null;
7036
+ }
7037
+ /**
7038
+ * Try PrefixRule-style normalization.
7039
+ */
7040
+ tryPrefixRules(word) {
7041
+ const rules = this.config.prefixRules;
7042
+ if (!rules) return null;
7043
+ for (const rule of rules) {
7044
+ if (!word.startsWith(rule.pattern)) continue;
7045
+ const remainder = word.slice(rule.pattern.length);
7046
+ const minRemaining = rule.minRemaining ?? this.config.minStemLength ?? 2;
7047
+ if (remainder.length < minRemaining) continue;
7048
+ return normalized(remainder, 1 - rule.confidencePenalty, {
7049
+ removedPrefixes: [rule.pattern]
7050
+ });
7051
+ }
7052
+ return null;
7053
+ }
7054
+ };
7055
+
7056
+ // src/training/jsonl-writer.ts
7057
+ function toJSONL(pairs) {
7058
+ return pairs.map((pair) => JSON.stringify(toJSONLRow(pair))).join("\n");
7059
+ }
7060
+ function toJSONLRow(pair) {
7061
+ return {
7062
+ id: pair.id,
7063
+ prompt: pair.natural,
7064
+ completion: pair.explicit,
7065
+ metadata: {
7066
+ domain: pair.domain,
7067
+ action: pair.action,
7068
+ language: pair.language,
7069
+ source: pair.source,
7070
+ quality: pair.quality,
7071
+ ...pair.confidence !== void 0 && { confidence: pair.confidence }
7072
+ }
7073
+ };
7074
+ }
7075
+ function parseJSONL(text) {
7076
+ return text.split("\n").filter((line) => line.trim().length > 0).map((line) => JSON.parse(line));
7077
+ }
7078
+
7079
+ // src/feedback/confidence-gate.ts
7080
+ function needsDisambiguation(confidence, low = 0.5, high = 0.7) {
7081
+ return confidence >= low && confidence < high;
7082
+ }
7083
+ function buildDisambiguation(input, candidates) {
7084
+ const sortedCandidates = [...candidates].sort((a, b) => b.confidence - a.confidence);
7085
+ const disambiguationCandidates = sortedCandidates.map((c, i) => ({
7086
+ action: c.action,
7087
+ confidence: c.confidence,
7088
+ explicit: renderExplicit(c.node),
7089
+ description: c.description || `${c.action} (option ${String.fromCharCode(97 + i)})`
7090
+ }));
7091
+ const optionLines = disambiguationCandidates.map(
7092
+ (c, i) => ` (${String.fromCharCode(97 + i)}) ${c.explicit} \u2014 ${c.description} (conf: ${c.confidence.toFixed(2)})`
7093
+ );
7094
+ const question = [
7095
+ `Your input parsed with confidence ${sortedCandidates[0]?.confidence.toFixed(2) ?? "?"}.`,
7096
+ ...optionLines,
7097
+ "Which did you mean?"
7098
+ ].join("\n");
7099
+ return {
7100
+ input,
7101
+ candidates: disambiguationCandidates,
7102
+ question
7103
+ };
7104
+ }
7105
+
7106
+ // src/feedback/pattern-tracker.ts
7107
+ var PatternTracker = class {
7108
+ constructor(maxEvents = 1e3) {
7109
+ this.events = [];
7110
+ this.maxEvents = maxEvents;
7111
+ }
7112
+ /**
7113
+ * Record a pattern event.
7114
+ */
7115
+ record(event) {
7116
+ this.events.push(event);
7117
+ if (this.events.length > this.maxEvents) {
7118
+ this.events = this.events.slice(this.events.length - this.maxEvents);
7119
+ }
7120
+ }
7121
+ /**
7122
+ * Get hit rate by command action.
7123
+ */
7124
+ getHitRateByCommand() {
7125
+ return this.computeHitRate((e) => e.action);
7126
+ }
7127
+ /**
7128
+ * Get hit rate by language.
7129
+ */
7130
+ getHitRateByLanguage() {
7131
+ return this.computeHitRate((e) => e.language);
7132
+ }
7133
+ /**
7134
+ * Get the most frequent failure diagnostic codes.
7135
+ */
7136
+ getTopFailures(limit = 10) {
7137
+ const codeMap = /* @__PURE__ */ new Map();
7138
+ for (const event of this.events) {
7139
+ if (event.outcome !== "rejected" || !event.diagnosticCodes) continue;
7140
+ for (const code of event.diagnosticCodes) {
7141
+ const entry = codeMap.get(code) || { count: 0, actions: /* @__PURE__ */ new Set() };
7142
+ entry.count++;
7143
+ entry.actions.add(event.action);
7144
+ codeMap.set(code, entry);
7145
+ }
7146
+ }
7147
+ return Array.from(codeMap.entries()).map(([code, { count, actions }]) => ({
7148
+ code,
7149
+ count,
7150
+ actions: Array.from(actions)
7151
+ })).sort((a, b) => b.count - a.count).slice(0, limit);
7152
+ }
7153
+ /**
7154
+ * Get a complete summary of tracking data.
7155
+ */
7156
+ getSummary() {
7157
+ const byOutcome = {};
7158
+ for (const event of this.events) {
7159
+ byOutcome[event.outcome] = (byOutcome[event.outcome] || 0) + 1;
7160
+ }
7161
+ return {
7162
+ totalEvents: this.events.length,
7163
+ byOutcome,
7164
+ byCommand: this.getHitRateByCommand(),
7165
+ byLanguage: this.getHitRateByLanguage(),
7166
+ topFailures: this.getTopFailures()
7167
+ };
7168
+ }
7169
+ /**
7170
+ * Export all events as JSONL.
7171
+ */
7172
+ exportJSONL() {
7173
+ return this.events.map((e) => JSON.stringify(e)).join("\n");
7174
+ }
7175
+ /**
7176
+ * Get the number of recorded events.
7177
+ */
7178
+ get size() {
7179
+ return this.events.length;
7180
+ }
7181
+ /**
7182
+ * Clear all events.
7183
+ */
7184
+ clear() {
7185
+ this.events = [];
7186
+ }
7187
+ // ---------------------------------------------------------------------------
7188
+ // Private
7189
+ // ---------------------------------------------------------------------------
7190
+ computeHitRate(keyFn) {
7191
+ const map = /* @__PURE__ */ new Map();
7192
+ for (const event of this.events) {
7193
+ const key = keyFn(event);
7194
+ const entry = map.get(key) || { attempts: 0, successes: 0 };
7195
+ entry.attempts++;
7196
+ if (event.outcome === "accepted") {
7197
+ entry.successes++;
7198
+ }
7199
+ map.set(key, entry);
7200
+ }
7201
+ const result = {};
7202
+ for (const [key, { attempts, successes }] of map) {
7203
+ result[key] = {
7204
+ attempts,
7205
+ successes,
7206
+ rate: attempts > 0 ? successes / attempts : 0
7207
+ };
7208
+ }
7209
+ return result;
7210
+ }
7211
+ };
7212
+
4122
7213
  // src/parsing/multi-statement.ts
4123
7214
  function createMultiStatementParser(dsl, config) {
4124
7215
  return new MultiStatementParserImpl(dsl, config);
@@ -4359,10 +7450,12 @@ function accumulateIndented(statements, config) {
4359
7450
  // Annotate the CommonJS export names for ESM import in node:
4360
7451
  0 && (module.exports = {
4361
7452
  AOTOrchestrator,
7453
+ BaseMorphologicalNormalizer,
4362
7454
  BaseTokenizer,
4363
7455
  CrossDomainDispatcher,
4364
7456
  DEFAULT_OPERATORS,
4365
7457
  DEFAULT_PUNCTUATION,
7458
+ DEFAULT_REFERENCES,
4366
7459
  DomainAwareScanner,
4367
7460
  DomainRegistry,
4368
7461
  GrammarTransformer,
@@ -4374,24 +7467,31 @@ function accumulateIndented(statements, config) {
4374
7467
  NumberExtractor,
4375
7468
  OperatorExtractor,
4376
7469
  PatternMatcher,
7470
+ PatternTracker,
4377
7471
  PunctuationExtractor,
4378
7472
  StringLiteralExtractor,
4379
7473
  TokenStreamImpl,
4380
7474
  UnicodeIdentifierExtractor,
4381
7475
  WhitespaceExtractor,
4382
7476
  accumulateBlocks,
7477
+ buildDisambiguation,
7478
+ buildFeedback,
4383
7479
  buildPhrase,
4384
7480
  buildTablesFromProfiles,
4385
7481
  combineClassifiers,
7482
+ convertValue,
7483
+ createAsyncNode,
4386
7484
  createCommandNode,
4387
7485
  createCompoundNode,
4388
7486
  createConditionalNode,
4389
7487
  createDiagnosticCollector,
4390
7488
  createEventHandlerNode,
4391
7489
  createExpression,
7490
+ createFlag,
4392
7491
  createLatinCharClassifiers,
4393
7492
  createLiteral,
4394
7493
  createLoopNode,
7494
+ createMatchNode,
4395
7495
  createMultiStatementParser,
4396
7496
  createMultilingualDSL,
4397
7497
  createPosition,
@@ -4402,6 +7502,7 @@ function accumulateIndented(statements, config) {
4402
7502
  createSimpleTokenizer,
4403
7503
  createToken,
4404
7504
  createTokenizerContext,
7505
+ createTryNode,
4405
7506
  createUnicodeRangeClassifier,
4406
7507
  defineCommand,
4407
7508
  defineRole,
@@ -4413,9 +7514,15 @@ function accumulateIndented(statements, config) {
4413
7514
  extractUrl,
4414
7515
  extractValue,
4415
7516
  filterBySeverity,
7517
+ fromEnvelopeJSON,
4416
7518
  fromError,
7519
+ fromInterchangeNode,
7520
+ fromProtocolJSON,
7521
+ generateExamples,
4417
7522
  generatePattern,
4418
7523
  generatePatternVariants,
7524
+ generatePrompt,
7525
+ generateProtocolReference,
4419
7526
  getAllPossessiveKeywords,
4420
7527
  getDefaultExtractors,
4421
7528
  getPossessiveReference,
@@ -4426,8 +7533,12 @@ function accumulateIndented(statements, config) {
4426
7533
  isCSSPropertyRef,
4427
7534
  isCSSSelector,
4428
7535
  isClassName,
7536
+ isCompoundSyntax,
4429
7537
  isContextAwareExtractor,
4430
7538
  isDigit,
7539
+ isDocumentSyntax,
7540
+ isEnvelope,
7541
+ isExplicitSyntax,
4431
7542
  isIdSelector,
4432
7543
  isNumericValue,
4433
7544
  isPossessiveKeyword,
@@ -4437,17 +7548,38 @@ function accumulateIndented(statements, config) {
4437
7548
  isSelectorStart,
4438
7549
  isTypeCompatible,
4439
7550
  isUrlStart,
7551
+ isValidReference,
4440
7552
  isVariableRef,
4441
7553
  isWhitespace,
4442
7554
  joinTokens,
7555
+ jsonToSemanticNode,
4443
7556
  lookupKeyword,
4444
7557
  lookupMarker,
4445
7558
  matchBest,
4446
7559
  matchPattern,
7560
+ needsDisambiguation,
4447
7561
  noChange,
4448
7562
  normalized,
7563
+ parseCompound,
7564
+ parseDocument,
7565
+ parseExplicit,
7566
+ parseJSONL,
4449
7567
  patternMatcher,
7568
+ registryToAOTBackends,
7569
+ renderDocument,
7570
+ renderExplicit,
7571
+ renderExpr,
4450
7572
  reorderRoles,
7573
+ semanticNodeToJSON,
7574
+ semanticNodeToRuntimeAST,
7575
+ semanticValueToAST,
7576
+ synthesizeFromSchemas,
7577
+ toEnvelopeJSON,
7578
+ toJSONL,
7579
+ toJSONLRow,
7580
+ toProtocolJSON,
7581
+ validateProtocolJSON,
7582
+ validateSemanticJSON,
4451
7583
  validateValueType,
4452
7584
  withDefaultExtractors
4453
7585
  });