@lokascript/framework 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aot/index.d.ts +1 -0
- package/dist/aot/index.d.ts.map +1 -1
- package/dist/aot/registry-bridge.d.ts +27 -0
- package/dist/aot/registry-bridge.d.ts.map +1 -0
- package/dist/api/create-dsl.d.ts.map +1 -1
- package/dist/api/dispatcher.d.ts +22 -0
- package/dist/api/dispatcher.d.ts.map +1 -1
- package/dist/api/domain-registry.d.ts +41 -0
- package/dist/api/domain-registry.d.ts.map +1 -1
- package/dist/api/index.js +1351 -21
- package/dist/api/index.js.map +1 -1
- package/dist/core/index.js +114 -10
- package/dist/core/index.js.map +1 -1
- package/dist/core/pattern-matching/index.js +74 -10
- package/dist/core/pattern-matching/index.js.map +1 -1
- package/dist/core/pattern-matching/pattern-matcher.d.ts.map +1 -1
- package/dist/core/types.d.ts +80 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +40 -0
- package/dist/core/types.js.map +1 -1
- package/dist/feedback/confidence-gate.d.ts +28 -0
- package/dist/feedback/confidence-gate.d.ts.map +1 -0
- package/dist/feedback/feedback-formatter.d.ts +21 -0
- package/dist/feedback/feedback-formatter.d.ts.map +1 -0
- package/dist/feedback/index.d.ts +11 -0
- package/dist/feedback/index.d.ts.map +1 -0
- package/dist/feedback/pattern-tracker.d.ts +47 -0
- package/dist/feedback/pattern-tracker.d.ts.map +1 -0
- package/dist/feedback/types.d.ts +113 -0
- package/dist/feedback/types.d.ts.map +1 -0
- package/dist/generation/index.js +2 -1
- package/dist/generation/index.js.map +1 -1
- package/dist/index.cjs +2685 -71
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2652 -71
- package/dist/index.js.map +1 -1
- package/dist/ir/explicit-parser.d.ts +31 -0
- package/dist/ir/explicit-parser.d.ts.map +1 -0
- package/dist/ir/explicit-renderer.d.ts +17 -0
- package/dist/ir/explicit-renderer.d.ts.map +1 -0
- package/dist/ir/from-interchange.d.ts +51 -0
- package/dist/ir/from-interchange.d.ts.map +1 -0
- package/dist/ir/index.d.ts +29 -0
- package/dist/ir/index.d.ts.map +1 -0
- package/dist/ir/index.js +1557 -0
- package/dist/ir/index.js.map +1 -0
- package/dist/ir/json-schema.d.ts +40 -0
- package/dist/ir/json-schema.d.ts.map +1 -0
- package/dist/ir/protocol-json.d.ts +73 -0
- package/dist/ir/protocol-json.d.ts.map +1 -0
- package/dist/ir/references.d.ts +19 -0
- package/dist/ir/references.d.ts.map +1 -0
- package/dist/ir/types.d.ts +141 -0
- package/dist/ir/types.d.ts.map +1 -0
- package/dist/parsing/index.js +74 -10
- package/dist/parsing/index.js.map +1 -1
- package/dist/prompts/index.d.ts +9 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/prompt-generator.d.ts +49 -0
- package/dist/prompts/prompt-generator.d.ts.map +1 -0
- package/dist/prompts/types.d.ts +64 -0
- package/dist/prompts/types.d.ts.map +1 -0
- package/dist/schema/command-schema.d.ts +17 -0
- package/dist/schema/command-schema.d.ts.map +1 -1
- package/dist/schema/index.js.map +1 -1
- package/dist/testing/index.js +3 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/training/index.d.ts +11 -0
- package/dist/training/index.d.ts.map +1 -0
- package/dist/training/jsonl-writer.d.ts +42 -0
- package/dist/training/jsonl-writer.d.ts.map +1 -0
- package/dist/training/schema-synthesizer.d.ts +20 -0
- package/dist/training/schema-synthesizer.d.ts.map +1 -0
- package/dist/training/types.d.ts +73 -0
- package/dist/training/types.d.ts.map +1 -0
- package/package.json +5 -1
- package/src/aot/index.ts +1 -0
- package/src/aot/registry-bridge.test.ts +104 -0
- package/src/aot/registry-bridge.ts +66 -0
- package/src/api/create-dsl.test.ts +306 -0
- package/src/api/create-dsl.ts +21 -0
- package/src/api/dispatcher.test.ts +66 -0
- package/src/api/dispatcher.ts +77 -9
- package/src/api/domain-registry.ts +120 -4
- package/src/core/pattern-matching/pattern-matcher.test.ts +131 -0
- package/src/core/pattern-matching/pattern-matcher.ts +102 -19
- package/src/core/types.ts +145 -2
- package/src/feedback/confidence-gate.test.ts +111 -0
- package/src/feedback/confidence-gate.ts +70 -0
- package/src/feedback/feedback-formatter.test.ts +198 -0
- package/src/feedback/feedback-formatter.ts +298 -0
- package/src/feedback/index.ts +24 -0
- package/src/feedback/pattern-tracker.test.ts +198 -0
- package/src/feedback/pattern-tracker.ts +140 -0
- package/src/feedback/types.ts +149 -0
- package/src/generation/pattern-generator.ts +2 -2
- package/src/index.ts +22 -0
- package/src/integration/llm-lse-integration.test.ts +457 -0
- package/src/ir/explicit-parser.test.ts +306 -0
- package/src/ir/explicit-parser.ts +419 -0
- package/src/ir/explicit-renderer.test.ts +144 -0
- package/src/ir/explicit-renderer.ts +146 -0
- package/src/ir/from-interchange.test.ts +853 -0
- package/src/ir/from-interchange.ts +594 -0
- package/src/ir/index.ts +66 -0
- package/src/ir/json-schema.test.ts +281 -0
- package/src/ir/json-schema.ts +299 -0
- package/src/ir/protocol-json.test.ts +1185 -0
- package/src/ir/protocol-json.ts +745 -0
- package/src/ir/references.test.ts +38 -0
- package/src/ir/references.ts +33 -0
- package/src/ir/round-trip.test.ts +557 -0
- package/src/ir/types.ts +181 -0
- package/src/prompts/index.ts +15 -0
- package/src/prompts/prompt-generator.test.ts +386 -0
- package/src/prompts/prompt-generator.ts +531 -0
- package/src/prompts/types.ts +89 -0
- package/src/schema/command-schema.ts +19 -0
- package/src/training/index.ts +13 -0
- package/src/training/jsonl-writer.test.ts +138 -0
- package/src/training/jsonl-writer.ts +67 -0
- package/src/training/schema-synthesizer.test.ts +250 -0
- package/src/training/schema-synthesizer.ts +306 -0
- package/src/training/types.ts +103 -0
package/dist/index.js
CHANGED
|
@@ -14,6 +14,9 @@ function createPropertyPath(object, property) {
|
|
|
14
14
|
function createExpression(raw) {
|
|
15
15
|
return { type: "expression", raw };
|
|
16
16
|
}
|
|
17
|
+
function createFlag(name, enabled = true) {
|
|
18
|
+
return { type: "flag", name, enabled };
|
|
19
|
+
}
|
|
17
20
|
function createCommandNode(action, roles, metadata) {
|
|
18
21
|
const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
|
|
19
22
|
const node = {
|
|
@@ -65,6 +68,7 @@ function createCompoundNode(statements, chainType = "sequential", metadata) {
|
|
|
65
68
|
return metadata ? { ...base, metadata } : base;
|
|
66
69
|
}
|
|
67
70
|
function extractValue(value) {
|
|
71
|
+
if (value.type === "flag") return value.name;
|
|
68
72
|
if ("raw" in value && value.raw !== void 0) return String(value.raw);
|
|
69
73
|
if ("value" in value && value.value !== void 0) return String(value.value);
|
|
70
74
|
if (value.type === "property-path") return `${extractValue(value.object)}.${value.property}`;
|
|
@@ -91,6 +95,38 @@ function createLoopNode(action, roles, loopVariant, body, loopVariable, indexVar
|
|
|
91
95
|
...metadata && { metadata }
|
|
92
96
|
};
|
|
93
97
|
}
|
|
98
|
+
function createTryNode(body, catchBranch, finallyBranch, metadata) {
|
|
99
|
+
return {
|
|
100
|
+
kind: "command",
|
|
101
|
+
action: "try",
|
|
102
|
+
roles: /* @__PURE__ */ new Map(),
|
|
103
|
+
body,
|
|
104
|
+
...catchBranch && catchBranch.length > 0 ? { catchBranch } : {},
|
|
105
|
+
...finallyBranch && finallyBranch.length > 0 ? { finallyBranch } : {},
|
|
106
|
+
...metadata ? { metadata } : {}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function createAsyncNode(variant, asyncBody, metadata) {
|
|
110
|
+
return {
|
|
111
|
+
kind: "command",
|
|
112
|
+
action: variant,
|
|
113
|
+
roles: /* @__PURE__ */ new Map(),
|
|
114
|
+
asyncVariant: variant,
|
|
115
|
+
asyncBody,
|
|
116
|
+
...metadata ? { metadata } : {}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function createMatchNode(roles, arms, defaultArm, metadata) {
|
|
120
|
+
const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
|
|
121
|
+
return {
|
|
122
|
+
kind: "command",
|
|
123
|
+
action: "match",
|
|
124
|
+
roles: rolesMap,
|
|
125
|
+
arms,
|
|
126
|
+
...defaultArm && defaultArm.length > 0 ? { defaultArm } : {},
|
|
127
|
+
...metadata ? { metadata } : {}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
94
130
|
|
|
95
131
|
// src/core/logger.ts
|
|
96
132
|
var DebugLogger = class {
|
|
@@ -346,7 +382,7 @@ var _PatternMatcher = class _PatternMatcher {
|
|
|
346
382
|
* a token and the immediately following pattern token fails, the matcher
|
|
347
383
|
* resets to before the optional role and retries the failed token.
|
|
348
384
|
*/
|
|
349
|
-
matchTokenSequence(tokens, patternTokens, captured) {
|
|
385
|
+
matchTokenSequence(tokens, patternTokens, captured, parentStopMarkers) {
|
|
350
386
|
const firstPatternToken = patternTokens[0];
|
|
351
387
|
const patternExpectsConjunction = firstPatternToken?.type === "literal" && (firstPatternToken.value === "and" || firstPatternToken.value === "then" || firstPatternToken.alternatives?.includes("and") || firstPatternToken.alternatives?.includes("then"));
|
|
352
388
|
if (this.currentProfile?.code === "ar" && !patternExpectsConjunction) {
|
|
@@ -370,6 +406,9 @@ var _PatternMatcher = class _PatternMatcher {
|
|
|
370
406
|
);
|
|
371
407
|
if (patternToken.type === "role" && patternToken.greedy) {
|
|
372
408
|
const stopMarkers = this.collectStopMarkers(patternTokens, i + 1);
|
|
409
|
+
if (parentStopMarkers) {
|
|
410
|
+
for (const m of parentStopMarkers) stopMarkers.add(m);
|
|
411
|
+
}
|
|
373
412
|
const values = [];
|
|
374
413
|
while (!tokens.isAtEnd()) {
|
|
375
414
|
const nextToken = tokens.peek();
|
|
@@ -389,8 +428,58 @@ var _PatternMatcher = class _PatternMatcher {
|
|
|
389
428
|
return false;
|
|
390
429
|
}
|
|
391
430
|
}
|
|
431
|
+
if (patternToken.type === "group") {
|
|
432
|
+
const siblingStopMarkers = this.collectStopMarkers(patternTokens, i + 1);
|
|
433
|
+
if (parentStopMarkers) {
|
|
434
|
+
for (const m of parentStopMarkers) siblingStopMarkers.add(m);
|
|
435
|
+
}
|
|
436
|
+
const groupMatched = this.matchGroupToken(
|
|
437
|
+
tokens,
|
|
438
|
+
patternToken,
|
|
439
|
+
captured,
|
|
440
|
+
siblingStopMarkers
|
|
441
|
+
);
|
|
442
|
+
if (groupMatched) {
|
|
443
|
+
prevOptionalMark = null;
|
|
444
|
+
prevOptionalRole = null;
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
if (this.isOptional(patternToken)) {
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
392
452
|
const isOptionalRole = patternToken.type === "role" && patternToken.optional === true;
|
|
393
453
|
const markBefore = isOptionalRole ? tokens.mark() : null;
|
|
454
|
+
const isRoleToken = patternToken.type === "role";
|
|
455
|
+
if (isRoleToken && !isOptionalRole) {
|
|
456
|
+
const nextPT = i + 1 < patternTokens.length ? patternTokens[i + 1] : null;
|
|
457
|
+
if (nextPT?.type === "literal") {
|
|
458
|
+
const currentToken = tokens.peek();
|
|
459
|
+
if (currentToken && this.getMatchType(currentToken, nextPT.value) !== "none") {
|
|
460
|
+
this.logger.debug(
|
|
461
|
+
" >> MARKER-STEAL GUARD: current token matches next literal, skipping role"
|
|
462
|
+
);
|
|
463
|
+
this.logger.debug(" >> Token match FAILED");
|
|
464
|
+
if (this.isOptional(patternToken)) {
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
if (prevOptionalMark && prevOptionalRole) {
|
|
468
|
+
this.logger.debug(" >> BACKTRACKING: undoing optional role", prevOptionalRole);
|
|
469
|
+
tokens.reset(prevOptionalMark);
|
|
470
|
+
captured.delete(prevOptionalRole);
|
|
471
|
+
prevOptionalMark = null;
|
|
472
|
+
prevOptionalRole = null;
|
|
473
|
+
const retryMatched = this.matchPatternToken(tokens, patternToken, captured);
|
|
474
|
+
this.logger.debug(" >> Backtrack retry result:", retryMatched);
|
|
475
|
+
if (retryMatched) {
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
394
483
|
const matched = this.matchPatternToken(tokens, patternToken, captured);
|
|
395
484
|
this.logger.debug(" >> Match result:", matched);
|
|
396
485
|
if (matched) {
|
|
@@ -511,7 +600,7 @@ var _PatternMatcher = class _PatternMatcher {
|
|
|
511
600
|
const possessiveValue = this.tryMatchPossessiveExpression(tokens);
|
|
512
601
|
if (possessiveValue) {
|
|
513
602
|
if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
|
|
514
|
-
if (!
|
|
603
|
+
if (!isTypeCompatible(possessiveValue.type, patternToken.expectedTypes)) {
|
|
515
604
|
return patternToken.optional || false;
|
|
516
605
|
}
|
|
517
606
|
}
|
|
@@ -521,7 +610,7 @@ var _PatternMatcher = class _PatternMatcher {
|
|
|
521
610
|
const methodCallValue = this.tryMatchMethodCallExpression(tokens);
|
|
522
611
|
if (methodCallValue) {
|
|
523
612
|
if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
|
|
524
|
-
if (!
|
|
613
|
+
if (!isTypeCompatible(methodCallValue.type, patternToken.expectedTypes)) {
|
|
525
614
|
return patternToken.optional || false;
|
|
526
615
|
}
|
|
527
616
|
}
|
|
@@ -541,7 +630,7 @@ var _PatternMatcher = class _PatternMatcher {
|
|
|
541
630
|
const propertyAccessValue = this.tryMatchPropertyAccessExpression(tokens);
|
|
542
631
|
if (propertyAccessValue) {
|
|
543
632
|
if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
|
|
544
|
-
if (!
|
|
633
|
+
if (!isTypeCompatible(propertyAccessValue.type, patternToken.expectedTypes)) {
|
|
545
634
|
return patternToken.optional || false;
|
|
546
635
|
}
|
|
547
636
|
}
|
|
@@ -861,10 +950,15 @@ var _PatternMatcher = class _PatternMatcher {
|
|
|
861
950
|
* (e.g., a value like 'hello') to sit between groups without blocking later
|
|
862
951
|
* marker matches.
|
|
863
952
|
*/
|
|
864
|
-
matchGroupToken(tokens, patternToken, captured) {
|
|
953
|
+
matchGroupToken(tokens, patternToken, captured, parentStopMarkers) {
|
|
865
954
|
const mark = tokens.mark();
|
|
866
955
|
const capturedBefore = new Set(captured.keys());
|
|
867
|
-
const success = this.matchTokenSequence(
|
|
956
|
+
const success = this.matchTokenSequence(
|
|
957
|
+
tokens,
|
|
958
|
+
patternToken.tokens,
|
|
959
|
+
captured,
|
|
960
|
+
parentStopMarkers
|
|
961
|
+
);
|
|
868
962
|
if (success) return true;
|
|
869
963
|
tokens.reset(mark);
|
|
870
964
|
for (const role of captured.keys()) {
|
|
@@ -886,7 +980,12 @@ var _PatternMatcher = class _PatternMatcher {
|
|
|
886
980
|
"- skipping intervening tokens"
|
|
887
981
|
);
|
|
888
982
|
for (let s = 0; s < offset; s++) tokens.advance();
|
|
889
|
-
const retrySuccess = this.matchTokenSequence(
|
|
983
|
+
const retrySuccess = this.matchTokenSequence(
|
|
984
|
+
tokens,
|
|
985
|
+
patternToken.tokens,
|
|
986
|
+
captured,
|
|
987
|
+
parentStopMarkers
|
|
988
|
+
);
|
|
890
989
|
if (retrySuccess) return true;
|
|
891
990
|
tokens.reset(mark);
|
|
892
991
|
for (const role of captured.keys()) {
|
|
@@ -953,7 +1052,7 @@ var _PatternMatcher = class _PatternMatcher {
|
|
|
953
1052
|
break;
|
|
954
1053
|
}
|
|
955
1054
|
}
|
|
956
|
-
break;
|
|
1055
|
+
if (!pt.optional) break;
|
|
957
1056
|
}
|
|
958
1057
|
}
|
|
959
1058
|
return markers;
|
|
@@ -963,8 +1062,9 @@ var _PatternMatcher = class _PatternMatcher {
|
|
|
963
1062
|
*/
|
|
964
1063
|
isStopMarker(token, stopMarkers) {
|
|
965
1064
|
if (stopMarkers.size === 0) return false;
|
|
966
|
-
|
|
967
|
-
|
|
1065
|
+
if (stopMarkers.has(token.value.toLowerCase())) return true;
|
|
1066
|
+
if (token.normalized && stopMarkers.has(token.normalized.toLowerCase())) return true;
|
|
1067
|
+
return false;
|
|
968
1068
|
}
|
|
969
1069
|
/**
|
|
970
1070
|
* Convert a language token to a semantic value.
|
|
@@ -1369,7 +1469,7 @@ function addRoleWithMarker(tokens, roleSpec, profile) {
|
|
|
1369
1469
|
if (marker) {
|
|
1370
1470
|
const markerInfo = profile.roleMarkers?.[roleSpec.role];
|
|
1371
1471
|
const defaultPosition = profile.wordOrder === "SOV" ? "after" : "before";
|
|
1372
|
-
const position = markerInfo?.position ?? defaultPosition;
|
|
1472
|
+
const position = roleSpec.markerPosition ?? markerInfo?.position ?? defaultPosition;
|
|
1373
1473
|
if (isOptional) {
|
|
1374
1474
|
if (position === "before") {
|
|
1375
1475
|
tokens.push({
|
|
@@ -1909,6 +2009,1419 @@ function createTokenizerContext(tokenizer) {
|
|
|
1909
2009
|
return ctx;
|
|
1910
2010
|
}
|
|
1911
2011
|
|
|
2012
|
+
// src/ir/references.ts
|
|
2013
|
+
var DEFAULT_REFERENCES = /* @__PURE__ */ new Set([
|
|
2014
|
+
"me",
|
|
2015
|
+
"you",
|
|
2016
|
+
"it",
|
|
2017
|
+
"result",
|
|
2018
|
+
"event",
|
|
2019
|
+
"target",
|
|
2020
|
+
"body"
|
|
2021
|
+
]);
|
|
2022
|
+
function isValidReference2(value, referenceSet = DEFAULT_REFERENCES) {
|
|
2023
|
+
return referenceSet.has(value);
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
// src/ir/explicit-parser.ts
|
|
2027
|
+
var STRUCTURAL_ROLES = /* @__PURE__ */ new Set([
|
|
2028
|
+
"body",
|
|
2029
|
+
"then",
|
|
2030
|
+
"else",
|
|
2031
|
+
"condition",
|
|
2032
|
+
"loop-body",
|
|
2033
|
+
"variable",
|
|
2034
|
+
"catch",
|
|
2035
|
+
// v1.2: try/catch/finally
|
|
2036
|
+
"finally"
|
|
2037
|
+
// v1.2: try/catch/finally
|
|
2038
|
+
]);
|
|
2039
|
+
function isNestedCommand(value) {
|
|
2040
|
+
const inner = value.slice(1, -1);
|
|
2041
|
+
let depth = 0;
|
|
2042
|
+
for (const ch of inner) {
|
|
2043
|
+
if (ch === "[") depth++;
|
|
2044
|
+
else if (ch === "]") depth--;
|
|
2045
|
+
else if (depth === 0 && (ch === " " || ch === ":")) return true;
|
|
2046
|
+
}
|
|
2047
|
+
return false;
|
|
2048
|
+
}
|
|
2049
|
+
function parseExplicit(input, options = {}) {
|
|
2050
|
+
const trimmed = input.trim();
|
|
2051
|
+
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) {
|
|
2052
|
+
throw new Error("Explicit syntax must be wrapped in brackets: [command role:value ...]");
|
|
2053
|
+
}
|
|
2054
|
+
const content = trimmed.slice(1, -1).trim();
|
|
2055
|
+
if (!content) {
|
|
2056
|
+
throw new Error("Empty explicit statement");
|
|
2057
|
+
}
|
|
2058
|
+
const tokens = tokenizeExplicit(content);
|
|
2059
|
+
if (tokens.length === 0) {
|
|
2060
|
+
throw new Error("No command specified in explicit statement");
|
|
2061
|
+
}
|
|
2062
|
+
const command = tokens[0].toLowerCase();
|
|
2063
|
+
const roles = /* @__PURE__ */ new Map();
|
|
2064
|
+
const refSet = options.referenceSet ?? DEFAULT_REFERENCES;
|
|
2065
|
+
const schema = options.schemaLookup?.getSchema(command);
|
|
2066
|
+
const validRoleNames = schema ? new Set(schema.roles.map((r) => r.role)) : null;
|
|
2067
|
+
for (let i = 1; i < tokens.length; i++) {
|
|
2068
|
+
const token = tokens[i];
|
|
2069
|
+
if (token.startsWith("+") || token.startsWith("~")) {
|
|
2070
|
+
const enabled = token.startsWith("+");
|
|
2071
|
+
const flagName = token.slice(1);
|
|
2072
|
+
if (!flagName) {
|
|
2073
|
+
throw new Error(`Empty flag name: "${token}"`);
|
|
2074
|
+
}
|
|
2075
|
+
if (validRoleNames && !validRoleNames.has(flagName)) {
|
|
2076
|
+
const roleList = [...validRoleNames].join(", ");
|
|
2077
|
+
throw new Error(
|
|
2078
|
+
`Unknown flag "${flagName}" for command "${command}". Valid roles: ${roleList}`
|
|
2079
|
+
);
|
|
2080
|
+
}
|
|
2081
|
+
roles.set(flagName, createFlag(flagName, enabled));
|
|
2082
|
+
continue;
|
|
2083
|
+
}
|
|
2084
|
+
const colonIndex = token.indexOf(":");
|
|
2085
|
+
if (colonIndex === -1) {
|
|
2086
|
+
throw new Error(`Invalid role format: "${token}". Expected role:value or +flag`);
|
|
2087
|
+
}
|
|
2088
|
+
const roleName = token.slice(0, colonIndex);
|
|
2089
|
+
if (validRoleNames && !STRUCTURAL_ROLES.has(roleName) && !validRoleNames.has(roleName)) {
|
|
2090
|
+
const roleList = [...validRoleNames].join(", ");
|
|
2091
|
+
throw new Error(
|
|
2092
|
+
`Unknown role "${roleName}" for command "${command}". Valid roles: ${roleList}`
|
|
2093
|
+
);
|
|
2094
|
+
}
|
|
2095
|
+
const role = roleName;
|
|
2096
|
+
const valueStr = token.slice(colonIndex + 1);
|
|
2097
|
+
if (STRUCTURAL_ROLES.has(roleName) && valueStr.startsWith("[") && isNestedCommand(valueStr)) {
|
|
2098
|
+
const nestedEnd = findMatchingBracket(token, colonIndex + 1);
|
|
2099
|
+
const nestedSyntax = token.slice(colonIndex + 1, nestedEnd + 1);
|
|
2100
|
+
roles.set(role, { type: "expression", raw: nestedSyntax });
|
|
2101
|
+
continue;
|
|
2102
|
+
}
|
|
2103
|
+
const value = parseExplicitValue(valueStr, refSet);
|
|
2104
|
+
roles.set(role, value);
|
|
2105
|
+
}
|
|
2106
|
+
if (schema && command !== "on") {
|
|
2107
|
+
for (const roleSpec of schema.roles) {
|
|
2108
|
+
if (roleSpec.required && !roles.has(roleSpec.role) && !roleSpec.default) {
|
|
2109
|
+
throw new Error(
|
|
2110
|
+
`Missing required role "${roleSpec.role}" for command "${command}": ${roleSpec.description}`
|
|
2111
|
+
);
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
switch (command) {
|
|
2116
|
+
case "on": {
|
|
2117
|
+
const eventValue = roles.get("event");
|
|
2118
|
+
if (!eventValue) {
|
|
2119
|
+
throw new Error("Event handler requires event role: [on event:click ...]");
|
|
2120
|
+
}
|
|
2121
|
+
const body = extractStructuralBody(roles, "body", options);
|
|
2122
|
+
roles.delete("body");
|
|
2123
|
+
return createEventHandlerNode("on", roles, body, {
|
|
2124
|
+
sourceLanguage: "explicit"
|
|
2125
|
+
});
|
|
2126
|
+
}
|
|
2127
|
+
case "if": {
|
|
2128
|
+
const thenBranch = extractStructuralBody(roles, "then", options);
|
|
2129
|
+
const elseBranch = extractStructuralBody(roles, "else", options);
|
|
2130
|
+
roles.delete("then");
|
|
2131
|
+
roles.delete("else");
|
|
2132
|
+
return createConditionalNode(
|
|
2133
|
+
command,
|
|
2134
|
+
roles,
|
|
2135
|
+
thenBranch,
|
|
2136
|
+
elseBranch.length > 0 ? elseBranch : void 0,
|
|
2137
|
+
{
|
|
2138
|
+
sourceLanguage: "explicit"
|
|
2139
|
+
}
|
|
2140
|
+
);
|
|
2141
|
+
}
|
|
2142
|
+
case "repeat": {
|
|
2143
|
+
const loopBody = extractStructuralBody(roles, "loop-body", options);
|
|
2144
|
+
const loopVariantValue = roles.get("loop-variant");
|
|
2145
|
+
const variableValue = roles.get("variable");
|
|
2146
|
+
const indexVariableValue = roles.get("index-variable");
|
|
2147
|
+
roles.delete("loop-body");
|
|
2148
|
+
roles.delete("loop-variant");
|
|
2149
|
+
roles.delete("variable");
|
|
2150
|
+
roles.delete("index-variable");
|
|
2151
|
+
let variant;
|
|
2152
|
+
if (loopVariantValue && loopVariantValue.type === "literal" && typeof loopVariantValue.value === "string") {
|
|
2153
|
+
variant = loopVariantValue.value;
|
|
2154
|
+
} else if (roles.has("quantity")) {
|
|
2155
|
+
variant = "times";
|
|
2156
|
+
} else if (roles.has("source")) {
|
|
2157
|
+
variant = "for";
|
|
2158
|
+
} else if (roles.has("condition")) {
|
|
2159
|
+
variant = "while";
|
|
2160
|
+
} else {
|
|
2161
|
+
variant = "forever";
|
|
2162
|
+
}
|
|
2163
|
+
const loopVariable = variableValue?.type === "literal" && typeof variableValue.value === "string" ? variableValue.value : variableValue?.type === "expression" ? variableValue.raw : void 0;
|
|
2164
|
+
const indexVariable = indexVariableValue?.type === "literal" && typeof indexVariableValue.value === "string" ? indexVariableValue.value : void 0;
|
|
2165
|
+
return createLoopNode(command, roles, variant, loopBody, loopVariable, indexVariable, {
|
|
2166
|
+
sourceLanguage: "explicit"
|
|
2167
|
+
});
|
|
2168
|
+
}
|
|
2169
|
+
default:
|
|
2170
|
+
return createCommandNode(command, roles, {
|
|
2171
|
+
sourceLanguage: "explicit"
|
|
2172
|
+
});
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
function isExplicitSyntax(input) {
|
|
2176
|
+
const trimmed = input.trim();
|
|
2177
|
+
return trimmed.startsWith("[") && trimmed.endsWith("]");
|
|
2178
|
+
}
|
|
2179
|
+
function extractStructuralBody(roles, roleName, options) {
|
|
2180
|
+
const value = roles.get(roleName);
|
|
2181
|
+
if (!value) return [];
|
|
2182
|
+
if (value.type === "expression") {
|
|
2183
|
+
return [parseExplicit(value.raw, options)];
|
|
2184
|
+
}
|
|
2185
|
+
return [];
|
|
2186
|
+
}
|
|
2187
|
+
function inferSelectorKind(value) {
|
|
2188
|
+
if (!value) return void 0;
|
|
2189
|
+
if (value.startsWith("#")) return "id";
|
|
2190
|
+
if (value.startsWith(".")) return "class";
|
|
2191
|
+
if (value.startsWith("[")) return "attribute";
|
|
2192
|
+
if (value.startsWith("<") || value.startsWith("*")) return "element";
|
|
2193
|
+
if (/[>+~ ]/.test(value) && value.length > 1) return "complex";
|
|
2194
|
+
return void 0;
|
|
2195
|
+
}
|
|
2196
|
+
function tokenizeExplicit(content) {
|
|
2197
|
+
const tokens = [];
|
|
2198
|
+
let current = "";
|
|
2199
|
+
let inString = false;
|
|
2200
|
+
let stringChar = "";
|
|
2201
|
+
let bracketDepth = 0;
|
|
2202
|
+
for (let i = 0; i < content.length; i++) {
|
|
2203
|
+
const char = content[i];
|
|
2204
|
+
if (inString) {
|
|
2205
|
+
current += char;
|
|
2206
|
+
if (char === stringChar && content[i - 1] !== "\\") {
|
|
2207
|
+
inString = false;
|
|
2208
|
+
}
|
|
2209
|
+
continue;
|
|
2210
|
+
}
|
|
2211
|
+
if (char === '"' || char === "'") {
|
|
2212
|
+
inString = true;
|
|
2213
|
+
stringChar = char;
|
|
2214
|
+
current += char;
|
|
2215
|
+
continue;
|
|
2216
|
+
}
|
|
2217
|
+
if (char === "[") {
|
|
2218
|
+
bracketDepth++;
|
|
2219
|
+
current += char;
|
|
2220
|
+
continue;
|
|
2221
|
+
}
|
|
2222
|
+
if (char === "]") {
|
|
2223
|
+
bracketDepth--;
|
|
2224
|
+
current += char;
|
|
2225
|
+
continue;
|
|
2226
|
+
}
|
|
2227
|
+
if (char === " " && bracketDepth === 0) {
|
|
2228
|
+
if (current) {
|
|
2229
|
+
tokens.push(current);
|
|
2230
|
+
current = "";
|
|
2231
|
+
}
|
|
2232
|
+
continue;
|
|
2233
|
+
}
|
|
2234
|
+
current += char;
|
|
2235
|
+
}
|
|
2236
|
+
if (current) {
|
|
2237
|
+
tokens.push(current);
|
|
2238
|
+
}
|
|
2239
|
+
return tokens;
|
|
2240
|
+
}
|
|
2241
|
+
function parseExplicitValue(valueStr, referenceSet) {
|
|
2242
|
+
if (valueStr.startsWith("#") || valueStr.startsWith(".") || valueStr.startsWith("[") || valueStr.startsWith("@") || valueStr.startsWith("*")) {
|
|
2243
|
+
return createSelector(valueStr, inferSelectorKind(valueStr));
|
|
2244
|
+
}
|
|
2245
|
+
if (valueStr.startsWith('"') || valueStr.startsWith("'")) {
|
|
2246
|
+
const inner = valueStr.slice(1, -1);
|
|
2247
|
+
return createLiteral(inner, "string");
|
|
2248
|
+
}
|
|
2249
|
+
if (valueStr === "true") return createLiteral(true, "boolean");
|
|
2250
|
+
if (valueStr === "false") return createLiteral(false, "boolean");
|
|
2251
|
+
const lowerRef = valueStr.toLowerCase();
|
|
2252
|
+
if (isValidReference2(lowerRef, referenceSet)) {
|
|
2253
|
+
return createReference(lowerRef);
|
|
2254
|
+
}
|
|
2255
|
+
const numMatch = valueStr.match(/^(-?\d+(?:\.\d+)?)(ms|s|m|h)?$/);
|
|
2256
|
+
if (numMatch) {
|
|
2257
|
+
const num = parseFloat(numMatch[1]);
|
|
2258
|
+
const suffix = numMatch[2];
|
|
2259
|
+
if (suffix) {
|
|
2260
|
+
return createLiteral(valueStr, "duration");
|
|
2261
|
+
}
|
|
2262
|
+
return createLiteral(num, "number");
|
|
2263
|
+
}
|
|
2264
|
+
return createLiteral(valueStr);
|
|
2265
|
+
}
|
|
2266
|
+
function findMatchingBracket(str, start) {
|
|
2267
|
+
let depth = 0;
|
|
2268
|
+
for (let i = start; i < str.length; i++) {
|
|
2269
|
+
if (str[i] === "[") depth++;
|
|
2270
|
+
else if (str[i] === "]") {
|
|
2271
|
+
depth--;
|
|
2272
|
+
if (depth === 0) return i;
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
return str.length - 1;
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2278
|
+
// src/ir/explicit-renderer.ts
|
|
2279
|
+
function renderExplicit(node) {
|
|
2280
|
+
if (node.kind === "compound") {
|
|
2281
|
+
const compoundNode = node;
|
|
2282
|
+
const renderedStatements = compoundNode.statements.map((stmt) => renderExplicit(stmt));
|
|
2283
|
+
return renderedStatements.join(` ${compoundNode.chainType} `);
|
|
2284
|
+
}
|
|
2285
|
+
const parts = [node.action];
|
|
2286
|
+
for (const [role, value] of node.roles) {
|
|
2287
|
+
if (value.type === "flag") {
|
|
2288
|
+
parts.push(value.enabled ? `+${value.name}` : `~${value.name}`);
|
|
2289
|
+
} else {
|
|
2290
|
+
parts.push(`${role}:${valueToString(value)}`);
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
if (node.kind === "event-handler") {
|
|
2294
|
+
const eventNode = node;
|
|
2295
|
+
if (eventNode.body && eventNode.body.length > 0) {
|
|
2296
|
+
const bodyParts = eventNode.body.map((n) => renderExplicit(n));
|
|
2297
|
+
parts.push(`body:${bodyParts.join(" ")}`);
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
if (node.kind === "conditional") {
|
|
2301
|
+
const condNode = node;
|
|
2302
|
+
if (condNode.thenBranch && condNode.thenBranch.length > 0) {
|
|
2303
|
+
const thenParts = condNode.thenBranch.map((n) => renderExplicit(n));
|
|
2304
|
+
parts.push(`then:${thenParts.join(" ")}`);
|
|
2305
|
+
}
|
|
2306
|
+
if (condNode.elseBranch && condNode.elseBranch.length > 0) {
|
|
2307
|
+
const elseParts = condNode.elseBranch.map((n) => renderExplicit(n));
|
|
2308
|
+
parts.push(`else:${elseParts.join(" ")}`);
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
if (node.kind === "loop") {
|
|
2312
|
+
const loopNode = node;
|
|
2313
|
+
if (loopNode.loopVariant) {
|
|
2314
|
+
parts.push(`loop-variant:${loopNode.loopVariant}`);
|
|
2315
|
+
}
|
|
2316
|
+
if (loopNode.loopVariable) {
|
|
2317
|
+
parts.push(`variable:${loopNode.loopVariable}`);
|
|
2318
|
+
}
|
|
2319
|
+
if (loopNode.indexVariable) {
|
|
2320
|
+
parts.push(`index-variable:${loopNode.indexVariable}`);
|
|
2321
|
+
}
|
|
2322
|
+
if (loopNode.body && loopNode.body.length > 0) {
|
|
2323
|
+
const bodyParts = loopNode.body.map((n) => renderExplicit(n));
|
|
2324
|
+
parts.push(`loop-body:${bodyParts.join(" ")}`);
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
if (node.kind === "command") {
|
|
2328
|
+
const cmd = node;
|
|
2329
|
+
if (cmd.body && cmd.body.length > 0) {
|
|
2330
|
+
const bodyParts = cmd.body.map((n) => renderExplicit(n));
|
|
2331
|
+
parts.push(`body:${bodyParts.join(" ")}`);
|
|
2332
|
+
}
|
|
2333
|
+
if (cmd.catchBranch && cmd.catchBranch.length > 0) {
|
|
2334
|
+
const catchParts = cmd.catchBranch.map((n) => renderExplicit(n));
|
|
2335
|
+
parts.push(`catch:${catchParts.join(" ")}`);
|
|
2336
|
+
}
|
|
2337
|
+
if (cmd.finallyBranch && cmd.finallyBranch.length > 0) {
|
|
2338
|
+
const finallyParts = cmd.finallyBranch.map((n) => renderExplicit(n));
|
|
2339
|
+
parts.push(`finally:${finallyParts.join(" ")}`);
|
|
2340
|
+
}
|
|
2341
|
+
if (cmd.asyncVariant) {
|
|
2342
|
+
parts.push(`async-variant:${cmd.asyncVariant}`);
|
|
2343
|
+
}
|
|
2344
|
+
if (cmd.asyncBody && cmd.asyncBody.length > 0) {
|
|
2345
|
+
const asyncParts = cmd.asyncBody.map((n) => renderExplicit(n));
|
|
2346
|
+
parts.push(`async-body:${asyncParts.join(" ")}`);
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
return `[${parts.join(" ")}]`;
|
|
2350
|
+
}
|
|
2351
|
+
function valueToString(value) {
|
|
2352
|
+
switch (value.type) {
|
|
2353
|
+
case "literal":
|
|
2354
|
+
if (typeof value.value === "string") {
|
|
2355
|
+
if (value.dataType === "string" || /\s/.test(value.value)) {
|
|
2356
|
+
return `"${value.value}"`;
|
|
2357
|
+
}
|
|
2358
|
+
return value.value;
|
|
2359
|
+
}
|
|
2360
|
+
return String(value.value);
|
|
2361
|
+
case "selector":
|
|
2362
|
+
return value.value;
|
|
2363
|
+
case "reference":
|
|
2364
|
+
return value.value;
|
|
2365
|
+
case "property-path":
|
|
2366
|
+
return `${valueToString(value.object)}'s ${value.property}`;
|
|
2367
|
+
case "expression":
|
|
2368
|
+
return value.raw;
|
|
2369
|
+
case "flag":
|
|
2370
|
+
return value.name;
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
// src/ir/json-schema.ts
|
|
2375
|
+
var VALID_VALUE_TYPES = /* @__PURE__ */ new Set([
|
|
2376
|
+
"selector",
|
|
2377
|
+
"literal",
|
|
2378
|
+
"reference",
|
|
2379
|
+
"expression",
|
|
2380
|
+
"property-path",
|
|
2381
|
+
"flag"
|
|
2382
|
+
]);
|
|
2383
|
+
function validateSemanticJSON(input) {
|
|
2384
|
+
const diagnostics = [];
|
|
2385
|
+
if (!input.action || typeof input.action !== "string") {
|
|
2386
|
+
diagnostics.push({
|
|
2387
|
+
severity: "error",
|
|
2388
|
+
code: "INVALID_ACTION",
|
|
2389
|
+
message: 'Field "action" is required and must be a string.',
|
|
2390
|
+
suggestion: 'Provide a command name like "toggle", "add", "set", etc.'
|
|
2391
|
+
});
|
|
2392
|
+
return diagnostics;
|
|
2393
|
+
}
|
|
2394
|
+
if (input.roles && typeof input.roles === "object") {
|
|
2395
|
+
for (const [role, value] of Object.entries(input.roles)) {
|
|
2396
|
+
if (!value || typeof value !== "object") {
|
|
2397
|
+
diagnostics.push({
|
|
2398
|
+
severity: "error",
|
|
2399
|
+
code: "INVALID_ROLE_VALUE",
|
|
2400
|
+
message: `Role "${role}" must be an object with "type" and "value" fields.`,
|
|
2401
|
+
suggestion: 'Use: { "type": "selector", "value": ".active" }'
|
|
2402
|
+
});
|
|
2403
|
+
continue;
|
|
2404
|
+
}
|
|
2405
|
+
const v = value;
|
|
2406
|
+
if (!VALID_VALUE_TYPES.has(v.type)) {
|
|
2407
|
+
diagnostics.push({
|
|
2408
|
+
severity: "error",
|
|
2409
|
+
code: "INVALID_VALUE_TYPE",
|
|
2410
|
+
message: `Role "${role}" has invalid type "${v.type}".`,
|
|
2411
|
+
suggestion: "Valid types: selector, literal, reference, expression, property-path."
|
|
2412
|
+
});
|
|
2413
|
+
}
|
|
2414
|
+
if (v.value === void 0 || v.value === null) {
|
|
2415
|
+
diagnostics.push({
|
|
2416
|
+
severity: "error",
|
|
2417
|
+
code: "MISSING_VALUE",
|
|
2418
|
+
message: `Role "${role}" is missing the "value" field.`
|
|
2419
|
+
});
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
if (input.trigger) {
|
|
2424
|
+
if (!input.trigger.event || typeof input.trigger.event !== "string") {
|
|
2425
|
+
diagnostics.push({
|
|
2426
|
+
severity: "error",
|
|
2427
|
+
code: "INVALID_TRIGGER",
|
|
2428
|
+
message: 'Trigger "event" is required and must be a string.',
|
|
2429
|
+
suggestion: 'Use an event name like "click", "mouseover", "keydown".'
|
|
2430
|
+
});
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
return diagnostics;
|
|
2434
|
+
}
|
|
2435
|
+
function jsonToSemanticNode(input) {
|
|
2436
|
+
const roles = /* @__PURE__ */ new Map();
|
|
2437
|
+
if (input.roles) {
|
|
2438
|
+
for (const [role, value] of Object.entries(input.roles)) {
|
|
2439
|
+
roles.set(role, convertJSONValue(value));
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
if (input.trigger) {
|
|
2443
|
+
roles.set("event", createLiteral(input.trigger.event, "string"));
|
|
2444
|
+
const bodyRoles = new Map(roles);
|
|
2445
|
+
bodyRoles.delete("event");
|
|
2446
|
+
const bodyNode = createCommandNode(input.action, bodyRoles);
|
|
2447
|
+
return createEventHandlerNode(
|
|
2448
|
+
"on",
|
|
2449
|
+
roles,
|
|
2450
|
+
[bodyNode],
|
|
2451
|
+
{ sourceLanguage: "json" },
|
|
2452
|
+
input.trigger.modifiers ?? {}
|
|
2453
|
+
);
|
|
2454
|
+
}
|
|
2455
|
+
return createCommandNode(input.action, roles, {
|
|
2456
|
+
sourceLanguage: "json"
|
|
2457
|
+
});
|
|
2458
|
+
}
|
|
2459
|
+
function semanticNodeToJSON(node) {
|
|
2460
|
+
if (node.kind === "compound") {
|
|
2461
|
+
const compound = node;
|
|
2462
|
+
if (compound.statements.length > 0) {
|
|
2463
|
+
return semanticNodeToJSON(compound.statements[0]);
|
|
2464
|
+
}
|
|
2465
|
+
return { action: "compound", roles: {} };
|
|
2466
|
+
}
|
|
2467
|
+
if (node.kind === "event-handler") {
|
|
2468
|
+
const eventNode = node;
|
|
2469
|
+
const bodyAction = eventNode.body?.[0]?.action ?? node.action;
|
|
2470
|
+
const roles2 = {};
|
|
2471
|
+
if (eventNode.body?.[0]) {
|
|
2472
|
+
for (const [role, value] of eventNode.body[0].roles) {
|
|
2473
|
+
roles2[role] = semanticValueToJSON(value);
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
const eventValue = node.roles.get("event");
|
|
2477
|
+
const eventName = eventValue ? "value" in eventValue ? String(eventValue.value) : "raw" in eventValue ? eventValue.raw : "unknown" : "unknown";
|
|
2478
|
+
const result = {
|
|
2479
|
+
action: bodyAction === "on" ? eventNode.body?.[0]?.action ?? "on" : bodyAction,
|
|
2480
|
+
roles: roles2,
|
|
2481
|
+
trigger: {
|
|
2482
|
+
event: eventName
|
|
2483
|
+
}
|
|
2484
|
+
};
|
|
2485
|
+
if (eventNode.eventModifiers && Object.keys(eventNode.eventModifiers).length > 0) {
|
|
2486
|
+
result.trigger.modifiers = eventNode.eventModifiers;
|
|
2487
|
+
}
|
|
2488
|
+
return result;
|
|
2489
|
+
}
|
|
2490
|
+
const roles = {};
|
|
2491
|
+
for (const [role, value] of node.roles) {
|
|
2492
|
+
roles[role] = semanticValueToJSON(value);
|
|
2493
|
+
}
|
|
2494
|
+
return { action: node.action, roles };
|
|
2495
|
+
}
|
|
2496
|
+
function convertJSONValue(value) {
|
|
2497
|
+
switch (value.type) {
|
|
2498
|
+
case "selector":
|
|
2499
|
+
return createSelector(String(value.value), detectSelectorKind(String(value.value)));
|
|
2500
|
+
case "literal":
|
|
2501
|
+
return createLiteral(
|
|
2502
|
+
value.value,
|
|
2503
|
+
typeof value.value === "number" ? "number" : typeof value.value === "boolean" ? "boolean" : "string"
|
|
2504
|
+
);
|
|
2505
|
+
case "reference":
|
|
2506
|
+
return createReference(String(value.value));
|
|
2507
|
+
case "expression":
|
|
2508
|
+
return createExpression(String(value.value));
|
|
2509
|
+
case "property-path":
|
|
2510
|
+
return createExpression(String(value.value));
|
|
2511
|
+
case "flag":
|
|
2512
|
+
return createFlag(String(value.value), true);
|
|
2513
|
+
default:
|
|
2514
|
+
return createLiteral(String(value.value), "string");
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
function semanticValueToJSON(value) {
|
|
2518
|
+
switch (value.type) {
|
|
2519
|
+
case "selector":
|
|
2520
|
+
return { type: "selector", value: value.value };
|
|
2521
|
+
case "literal":
|
|
2522
|
+
return { type: "literal", value: value.value };
|
|
2523
|
+
case "reference":
|
|
2524
|
+
return { type: "reference", value: value.value };
|
|
2525
|
+
case "expression":
|
|
2526
|
+
return { type: "expression", value: value.raw };
|
|
2527
|
+
case "property-path":
|
|
2528
|
+
return {
|
|
2529
|
+
type: "property-path",
|
|
2530
|
+
value: `${semanticValueToJSON(value.object).value}.${value.property}`
|
|
2531
|
+
};
|
|
2532
|
+
case "flag":
|
|
2533
|
+
return { type: "flag", value: value.enabled };
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
function detectSelectorKind(selector) {
|
|
2537
|
+
if (selector.startsWith("#")) return "id";
|
|
2538
|
+
if (selector.startsWith(".")) return "class";
|
|
2539
|
+
if (selector.startsWith("[")) return "attribute";
|
|
2540
|
+
return "complex";
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2543
|
+
// src/ir/protocol-json.ts
|
|
2544
|
+
function toProtocolJSON(node) {
|
|
2545
|
+
const roles = rolesToProtocol(node.roles);
|
|
2546
|
+
let result;
|
|
2547
|
+
switch (node.kind) {
|
|
2548
|
+
case "command": {
|
|
2549
|
+
const cmd = node;
|
|
2550
|
+
result = { kind: "command", action: node.action, roles };
|
|
2551
|
+
if (cmd.body && cmd.body.length > 0) {
|
|
2552
|
+
result.body = cmd.body.map(toProtocolJSON);
|
|
2553
|
+
}
|
|
2554
|
+
if (cmd.catchBranch && cmd.catchBranch.length > 0) {
|
|
2555
|
+
result.catchBranch = cmd.catchBranch.map(toProtocolJSON);
|
|
2556
|
+
}
|
|
2557
|
+
if (cmd.finallyBranch && cmd.finallyBranch.length > 0) {
|
|
2558
|
+
result.finallyBranch = cmd.finallyBranch.map(toProtocolJSON);
|
|
2559
|
+
}
|
|
2560
|
+
if (cmd.asyncVariant) {
|
|
2561
|
+
result.asyncVariant = cmd.asyncVariant;
|
|
2562
|
+
}
|
|
2563
|
+
if (cmd.asyncBody && cmd.asyncBody.length > 0) {
|
|
2564
|
+
result.asyncBody = cmd.asyncBody.map(toProtocolJSON);
|
|
2565
|
+
}
|
|
2566
|
+
if (cmd.arms && cmd.arms.length > 0) {
|
|
2567
|
+
result.arms = cmd.arms.map((arm) => ({
|
|
2568
|
+
pattern: valueToProtocol(arm.pattern),
|
|
2569
|
+
body: arm.body.map(toProtocolJSON)
|
|
2570
|
+
}));
|
|
2571
|
+
}
|
|
2572
|
+
if (cmd.defaultArm && cmd.defaultArm.length > 0) {
|
|
2573
|
+
result.defaultArm = cmd.defaultArm.map(toProtocolJSON);
|
|
2574
|
+
}
|
|
2575
|
+
break;
|
|
2576
|
+
}
|
|
2577
|
+
case "event-handler": {
|
|
2578
|
+
const eh = node;
|
|
2579
|
+
result = {
|
|
2580
|
+
kind: "event-handler",
|
|
2581
|
+
action: node.action,
|
|
2582
|
+
roles,
|
|
2583
|
+
body: eh.body.map(toProtocolJSON)
|
|
2584
|
+
};
|
|
2585
|
+
break;
|
|
2586
|
+
}
|
|
2587
|
+
case "compound": {
|
|
2588
|
+
const c = node;
|
|
2589
|
+
result = {
|
|
2590
|
+
kind: "compound",
|
|
2591
|
+
action: "compound",
|
|
2592
|
+
statements: c.statements.map(toProtocolJSON),
|
|
2593
|
+
chainType: c.chainType
|
|
2594
|
+
};
|
|
2595
|
+
break;
|
|
2596
|
+
}
|
|
2597
|
+
case "conditional": {
|
|
2598
|
+
const cond = node;
|
|
2599
|
+
result = { kind: "command", action: node.action, roles };
|
|
2600
|
+
result.thenBranch = cond.thenBranch.map(toProtocolJSON);
|
|
2601
|
+
if (cond.elseBranch && cond.elseBranch.length > 0) {
|
|
2602
|
+
result.elseBranch = cond.elseBranch.map(toProtocolJSON);
|
|
2603
|
+
}
|
|
2604
|
+
break;
|
|
2605
|
+
}
|
|
2606
|
+
case "loop": {
|
|
2607
|
+
const loop = node;
|
|
2608
|
+
result = { kind: "command", action: node.action, roles };
|
|
2609
|
+
result.loopVariant = loop.loopVariant;
|
|
2610
|
+
result.loopBody = loop.body.map(toProtocolJSON);
|
|
2611
|
+
if (loop.loopVariable) result.loopVariable = loop.loopVariable;
|
|
2612
|
+
if (loop.indexVariable) result.indexVariable = loop.indexVariable;
|
|
2613
|
+
break;
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
if (node.annotations && node.annotations.length > 0) {
|
|
2617
|
+
result.annotations = node.annotations.map(
|
|
2618
|
+
(a) => a.value !== void 0 ? { name: a.name, value: a.value } : { name: a.name }
|
|
2619
|
+
);
|
|
2620
|
+
}
|
|
2621
|
+
if (node.diagnostics && node.diagnostics.length > 0) {
|
|
2622
|
+
result.diagnostics = node.diagnostics.map((d) => ({
|
|
2623
|
+
level: d.level,
|
|
2624
|
+
role: d.role,
|
|
2625
|
+
message: d.message,
|
|
2626
|
+
code: d.code
|
|
2627
|
+
}));
|
|
2628
|
+
}
|
|
2629
|
+
return result;
|
|
2630
|
+
}
|
|
2631
|
+
function rolesToProtocol(roles) {
|
|
2632
|
+
const result = {};
|
|
2633
|
+
for (const [role, value] of roles) {
|
|
2634
|
+
result[role] = valueToProtocol(value);
|
|
2635
|
+
}
|
|
2636
|
+
return result;
|
|
2637
|
+
}
|
|
2638
|
+
function valueToProtocol(value) {
|
|
2639
|
+
switch (value.type) {
|
|
2640
|
+
case "selector": {
|
|
2641
|
+
const PROTOCOL_SELECTOR_KINDS = /* @__PURE__ */ new Set([
|
|
2642
|
+
"id",
|
|
2643
|
+
"class",
|
|
2644
|
+
"attribute",
|
|
2645
|
+
"element",
|
|
2646
|
+
"complex"
|
|
2647
|
+
]);
|
|
2648
|
+
const sk = "selectorKind" in value ? value.selectorKind : void 0;
|
|
2649
|
+
const sv = { type: "selector", value: value.value };
|
|
2650
|
+
if (sk && PROTOCOL_SELECTOR_KINDS.has(sk)) {
|
|
2651
|
+
sv.selectorKind = sk;
|
|
2652
|
+
}
|
|
2653
|
+
return sv;
|
|
2654
|
+
}
|
|
2655
|
+
case "literal":
|
|
2656
|
+
return value.dataType ? { type: "literal", value: value.value, dataType: value.dataType } : { type: "literal", value: value.value };
|
|
2657
|
+
case "reference":
|
|
2658
|
+
return { type: "reference", value: value.value };
|
|
2659
|
+
case "expression":
|
|
2660
|
+
return { type: "expression", raw: value.raw };
|
|
2661
|
+
case "property-path":
|
|
2662
|
+
return { type: "expression", raw: extractValue(value) };
|
|
2663
|
+
case "flag":
|
|
2664
|
+
return { type: "flag", name: value.name, enabled: value.enabled };
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
function fromProtocolJSON(json) {
|
|
2668
|
+
const roles = new Map(
|
|
2669
|
+
Object.entries(json.roles ?? {}).map(([role, val]) => [role, valueFromProtocol(val)])
|
|
2670
|
+
);
|
|
2671
|
+
const annotations = json.annotations?.map(
|
|
2672
|
+
(a) => a.value !== void 0 ? { name: a.name, value: a.value } : { name: a.name }
|
|
2673
|
+
);
|
|
2674
|
+
const diagnostics = json.diagnostics?.map((d) => ({
|
|
2675
|
+
level: d.level,
|
|
2676
|
+
role: d.role,
|
|
2677
|
+
message: d.message,
|
|
2678
|
+
code: d.code
|
|
2679
|
+
}));
|
|
2680
|
+
if (json.trigger) {
|
|
2681
|
+
const eventName = json.trigger.event;
|
|
2682
|
+
roles.set("event", createLiteral(eventName, "string"));
|
|
2683
|
+
const bodyRoles = new Map(roles);
|
|
2684
|
+
bodyRoles.delete("event");
|
|
2685
|
+
const bodyNode = createCommandNode(json.action, bodyRoles);
|
|
2686
|
+
const node2 = createEventHandlerNode(
|
|
2687
|
+
"on",
|
|
2688
|
+
roles,
|
|
2689
|
+
[bodyNode],
|
|
2690
|
+
{ sourceLanguage: "json" },
|
|
2691
|
+
json.trigger.modifiers ?? {}
|
|
2692
|
+
);
|
|
2693
|
+
return applyV12Metadata(node2, annotations, diagnostics);
|
|
2694
|
+
}
|
|
2695
|
+
const kind = json.kind ?? "command";
|
|
2696
|
+
if (kind === "event-handler") {
|
|
2697
|
+
const body = (json.body ?? []).map(fromProtocolJSON);
|
|
2698
|
+
const node2 = createEventHandlerNode(json.action, roles, body);
|
|
2699
|
+
return applyV12Metadata(node2, annotations, diagnostics);
|
|
2700
|
+
}
|
|
2701
|
+
if (kind === "compound") {
|
|
2702
|
+
const statements = (json.statements ?? []).map(fromProtocolJSON);
|
|
2703
|
+
const node2 = createCompoundNode(
|
|
2704
|
+
statements,
|
|
2705
|
+
json.chainType ?? "sequential"
|
|
2706
|
+
);
|
|
2707
|
+
return applyV12Metadata(node2, annotations, diagnostics);
|
|
2708
|
+
}
|
|
2709
|
+
if (json.thenBranch && json.thenBranch.length > 0) {
|
|
2710
|
+
const thenBranch = json.thenBranch.map(fromProtocolJSON);
|
|
2711
|
+
const elseBranch = json.elseBranch ? json.elseBranch.map(fromProtocolJSON) : void 0;
|
|
2712
|
+
const node2 = createConditionalNode(json.action, roles, thenBranch, elseBranch);
|
|
2713
|
+
return applyV12Metadata(node2, annotations, diagnostics);
|
|
2714
|
+
}
|
|
2715
|
+
if (json.loopVariant) {
|
|
2716
|
+
const loopBody = (json.loopBody ?? []).map(fromProtocolJSON);
|
|
2717
|
+
const node2 = createLoopNode(
|
|
2718
|
+
json.action,
|
|
2719
|
+
roles,
|
|
2720
|
+
json.loopVariant,
|
|
2721
|
+
loopBody,
|
|
2722
|
+
json.loopVariable,
|
|
2723
|
+
json.indexVariable
|
|
2724
|
+
);
|
|
2725
|
+
return applyV12Metadata(node2, annotations, diagnostics);
|
|
2726
|
+
}
|
|
2727
|
+
if (json.catchBranch || json.finallyBranch) {
|
|
2728
|
+
const body = (json.body ?? []).map(fromProtocolJSON);
|
|
2729
|
+
const catchBranch = json.catchBranch ? json.catchBranch.map(fromProtocolJSON) : void 0;
|
|
2730
|
+
const finallyBranch = json.finallyBranch ? json.finallyBranch.map(fromProtocolJSON) : void 0;
|
|
2731
|
+
const node2 = createTryNode(body, catchBranch, finallyBranch);
|
|
2732
|
+
return applyV12Metadata(node2, annotations, diagnostics);
|
|
2733
|
+
}
|
|
2734
|
+
if (json.asyncVariant) {
|
|
2735
|
+
const asyncBody = (json.asyncBody ?? []).map(fromProtocolJSON);
|
|
2736
|
+
const node2 = createAsyncNode(json.asyncVariant, asyncBody);
|
|
2737
|
+
return applyV12Metadata(node2, annotations, diagnostics);
|
|
2738
|
+
}
|
|
2739
|
+
if (json.arms && json.arms.length > 0) {
|
|
2740
|
+
const arms = json.arms.map((arm) => ({
|
|
2741
|
+
pattern: valueFromProtocol(arm.pattern),
|
|
2742
|
+
body: arm.body.map(fromProtocolJSON)
|
|
2743
|
+
}));
|
|
2744
|
+
const defaultArm = json.defaultArm ? json.defaultArm.map(fromProtocolJSON) : void 0;
|
|
2745
|
+
const node2 = createMatchNode(roles, arms, defaultArm);
|
|
2746
|
+
return applyV12Metadata(node2, annotations, diagnostics);
|
|
2747
|
+
}
|
|
2748
|
+
if (json.body && json.body.length > 0 && kind === "command") {
|
|
2749
|
+
const body = json.body.map(fromProtocolJSON);
|
|
2750
|
+
const node2 = createTryNode(body);
|
|
2751
|
+
return applyV12Metadata(node2, annotations, diagnostics);
|
|
2752
|
+
}
|
|
2753
|
+
const node = createCommandNode(json.action, roles);
|
|
2754
|
+
return applyV12Metadata(node, annotations, diagnostics);
|
|
2755
|
+
}
|
|
2756
|
+
function applyV12Metadata(node, annotations, diagnostics) {
|
|
2757
|
+
if ((!annotations || annotations.length === 0) && (!diagnostics || diagnostics.length === 0)) {
|
|
2758
|
+
return node;
|
|
2759
|
+
}
|
|
2760
|
+
return {
|
|
2761
|
+
...node,
|
|
2762
|
+
...annotations && annotations.length > 0 ? { annotations } : {},
|
|
2763
|
+
...diagnostics && diagnostics.length > 0 ? { diagnostics } : {}
|
|
2764
|
+
};
|
|
2765
|
+
}
|
|
2766
|
+
function valueFromProtocol(value) {
|
|
2767
|
+
switch (value.type) {
|
|
2768
|
+
case "selector":
|
|
2769
|
+
return createSelector(String(value.value ?? ""), value.selectorKind);
|
|
2770
|
+
case "literal":
|
|
2771
|
+
return createLiteral(
|
|
2772
|
+
value.value !== void 0 ? value.value : "",
|
|
2773
|
+
value.dataType
|
|
2774
|
+
);
|
|
2775
|
+
case "reference":
|
|
2776
|
+
return createReference(String(value.value ?? ""));
|
|
2777
|
+
case "expression":
|
|
2778
|
+
return createExpression(value.raw ?? String(value.value ?? ""));
|
|
2779
|
+
case "flag":
|
|
2780
|
+
return createFlag(
|
|
2781
|
+
value.name ?? String(value.value ?? ""),
|
|
2782
|
+
value.enabled ?? (typeof value.value === "boolean" ? value.value : true)
|
|
2783
|
+
);
|
|
2784
|
+
case "property-path":
|
|
2785
|
+
return createExpression(String(value.value ?? value.raw ?? ""));
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
function toEnvelopeJSON(envelope) {
|
|
2789
|
+
const result = {
|
|
2790
|
+
lseVersion: envelope.lseVersion,
|
|
2791
|
+
nodes: envelope.nodes.map(toProtocolJSON)
|
|
2792
|
+
};
|
|
2793
|
+
if (envelope.features && envelope.features.length > 0) {
|
|
2794
|
+
result.features = [...envelope.features];
|
|
2795
|
+
}
|
|
2796
|
+
return result;
|
|
2797
|
+
}
|
|
2798
|
+
function fromEnvelopeJSON(json) {
|
|
2799
|
+
return {
|
|
2800
|
+
lseVersion: json.lseVersion,
|
|
2801
|
+
...json.features && json.features.length > 0 ? { features: json.features } : {},
|
|
2802
|
+
nodes: json.nodes.map(fromProtocolJSON)
|
|
2803
|
+
};
|
|
2804
|
+
}
|
|
2805
|
+
function isEnvelope(json) {
|
|
2806
|
+
return typeof json === "object" && json !== null && "lseVersion" in json && "nodes" in json && Array.isArray(json.nodes);
|
|
2807
|
+
}
|
|
2808
|
+
var VALID_KINDS = /* @__PURE__ */ new Set(["command", "event-handler", "compound"]);
|
|
2809
|
+
var VALID_VALUE_TYPES2 = /* @__PURE__ */ new Set([
|
|
2810
|
+
"selector",
|
|
2811
|
+
"literal",
|
|
2812
|
+
"reference",
|
|
2813
|
+
"expression",
|
|
2814
|
+
"property-path",
|
|
2815
|
+
"flag"
|
|
2816
|
+
]);
|
|
2817
|
+
var VALID_CHAIN_TYPES = /* @__PURE__ */ new Set(["then", "and", "async", "sequential", "pipe"]);
|
|
2818
|
+
var VALID_ASYNC_VARIANTS = /* @__PURE__ */ new Set(["all", "race"]);
|
|
2819
|
+
var VALID_DIAGNOSTIC_LEVELS = /* @__PURE__ */ new Set(["error", "warning"]);
|
|
2820
|
+
function validateProtocolJSON(json) {
|
|
2821
|
+
const diagnostics = [];
|
|
2822
|
+
if (typeof json !== "object" || json === null) {
|
|
2823
|
+
return [{ severity: "error", code: "INVALID_ROOT", message: "Root must be a JSON object" }];
|
|
2824
|
+
}
|
|
2825
|
+
const node = json;
|
|
2826
|
+
const kind = "kind" in node ? String(node.kind) : "command";
|
|
2827
|
+
if ("kind" in node && !VALID_KINDS.has(kind)) {
|
|
2828
|
+
diagnostics.push({
|
|
2829
|
+
severity: "error",
|
|
2830
|
+
code: "INVALID_KIND",
|
|
2831
|
+
message: `Invalid kind "${node.kind}". Must be one of: command, event-handler, compound`
|
|
2832
|
+
});
|
|
2833
|
+
}
|
|
2834
|
+
if (!("action" in node)) {
|
|
2835
|
+
diagnostics.push({
|
|
2836
|
+
severity: "error",
|
|
2837
|
+
code: "MISSING_ACTION",
|
|
2838
|
+
message: "Missing required field: action"
|
|
2839
|
+
});
|
|
2840
|
+
} else if (typeof node.action !== "string" || node.action.length === 0) {
|
|
2841
|
+
diagnostics.push({
|
|
2842
|
+
severity: "error",
|
|
2843
|
+
code: "INVALID_ACTION",
|
|
2844
|
+
message: "action must be a non-empty string"
|
|
2845
|
+
});
|
|
2846
|
+
}
|
|
2847
|
+
if (!("roles" in node) && kind !== "compound") {
|
|
2848
|
+
diagnostics.push({
|
|
2849
|
+
severity: "error",
|
|
2850
|
+
code: "MISSING_ROLES",
|
|
2851
|
+
message: "Missing required field: roles"
|
|
2852
|
+
});
|
|
2853
|
+
} else if ("roles" in node && (typeof node.roles !== "object" || node.roles === null || Array.isArray(node.roles))) {
|
|
2854
|
+
diagnostics.push({
|
|
2855
|
+
severity: "error",
|
|
2856
|
+
code: "INVALID_ROLES",
|
|
2857
|
+
message: "roles must be a plain object"
|
|
2858
|
+
});
|
|
2859
|
+
} else if ("roles" in node && node.roles) {
|
|
2860
|
+
for (const [role, value] of Object.entries(node.roles)) {
|
|
2861
|
+
if (typeof value !== "object" || value === null) {
|
|
2862
|
+
diagnostics.push({
|
|
2863
|
+
severity: "error",
|
|
2864
|
+
code: "INVALID_ROLE_VALUE",
|
|
2865
|
+
message: `Role "${role}" value must be an object`
|
|
2866
|
+
});
|
|
2867
|
+
continue;
|
|
2868
|
+
}
|
|
2869
|
+
const v = value;
|
|
2870
|
+
if (!("type" in v) || !VALID_VALUE_TYPES2.has(String(v.type))) {
|
|
2871
|
+
diagnostics.push({
|
|
2872
|
+
severity: "error",
|
|
2873
|
+
code: "INVALID_VALUE_TYPE",
|
|
2874
|
+
message: `Role "${role}" has invalid type "${v.type}"`
|
|
2875
|
+
});
|
|
2876
|
+
}
|
|
2877
|
+
if (v.type === "flag") {
|
|
2878
|
+
const hasName = typeof v.name === "string";
|
|
2879
|
+
const hasEnabled = typeof v.enabled === "boolean";
|
|
2880
|
+
const hasValue = "value" in v;
|
|
2881
|
+
if (!hasName && !hasValue) {
|
|
2882
|
+
diagnostics.push({
|
|
2883
|
+
severity: "error",
|
|
2884
|
+
code: "MISSING_FLAG_NAME",
|
|
2885
|
+
message: `Flag role "${role}" missing required field: name (or value)`
|
|
2886
|
+
});
|
|
2887
|
+
}
|
|
2888
|
+
if (!hasEnabled && !hasValue) {
|
|
2889
|
+
diagnostics.push({
|
|
2890
|
+
severity: "error",
|
|
2891
|
+
code: "MISSING_FLAG_ENABLED",
|
|
2892
|
+
message: `Flag role "${role}" missing required field: enabled (or value)`
|
|
2893
|
+
});
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
if (v.type === "expression" && typeof v.raw !== "string" && !("value" in v)) {
|
|
2897
|
+
diagnostics.push({
|
|
2898
|
+
severity: "error",
|
|
2899
|
+
code: "MISSING_EXPRESSION_RAW",
|
|
2900
|
+
message: `Expression role "${role}" missing required field: raw (or value)`
|
|
2901
|
+
});
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2905
|
+
if ("trigger" in node && node.trigger) {
|
|
2906
|
+
const trigger = node.trigger;
|
|
2907
|
+
if (typeof trigger !== "object" || trigger === null) {
|
|
2908
|
+
diagnostics.push({
|
|
2909
|
+
severity: "error",
|
|
2910
|
+
code: "INVALID_TRIGGER",
|
|
2911
|
+
message: "trigger must be an object"
|
|
2912
|
+
});
|
|
2913
|
+
} else if (!trigger.event || typeof trigger.event !== "string") {
|
|
2914
|
+
diagnostics.push({
|
|
2915
|
+
severity: "error",
|
|
2916
|
+
code: "INVALID_TRIGGER",
|
|
2917
|
+
message: "trigger.event is required and must be a non-empty string"
|
|
2918
|
+
});
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
if (kind === "event-handler" && !("trigger" in node)) {
|
|
2922
|
+
if (!("body" in node) || !Array.isArray(node.body)) {
|
|
2923
|
+
diagnostics.push({
|
|
2924
|
+
severity: "error",
|
|
2925
|
+
code: "MISSING_BODY",
|
|
2926
|
+
message: "event-handler node requires a body array"
|
|
2927
|
+
});
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
if (kind === "compound") {
|
|
2931
|
+
if (!("statements" in node) || !Array.isArray(node.statements)) {
|
|
2932
|
+
diagnostics.push({
|
|
2933
|
+
severity: "error",
|
|
2934
|
+
code: "MISSING_STATEMENTS",
|
|
2935
|
+
message: "compound node requires a statements array"
|
|
2936
|
+
});
|
|
2937
|
+
}
|
|
2938
|
+
if ("chainType" in node && !VALID_CHAIN_TYPES.has(String(node.chainType))) {
|
|
2939
|
+
diagnostics.push({
|
|
2940
|
+
severity: "error",
|
|
2941
|
+
code: "INVALID_CHAIN_TYPE",
|
|
2942
|
+
message: `Invalid chainType "${node.chainType}". Must be one of: then, and, async, sequential, pipe`
|
|
2943
|
+
});
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
if ("diagnostics" in node) {
|
|
2947
|
+
if (!Array.isArray(node.diagnostics)) {
|
|
2948
|
+
diagnostics.push({
|
|
2949
|
+
severity: "error",
|
|
2950
|
+
code: "INVALID_DIAGNOSTICS",
|
|
2951
|
+
message: "diagnostics must be an array"
|
|
2952
|
+
});
|
|
2953
|
+
} else {
|
|
2954
|
+
for (let i = 0; i < node.diagnostics.length; i++) {
|
|
2955
|
+
const d = node.diagnostics[i];
|
|
2956
|
+
if (!d || typeof d !== "object") {
|
|
2957
|
+
diagnostics.push({
|
|
2958
|
+
severity: "error",
|
|
2959
|
+
code: "INVALID_DIAGNOSTIC_ENTRY",
|
|
2960
|
+
message: `diagnostics[${i}] must be an object`
|
|
2961
|
+
});
|
|
2962
|
+
continue;
|
|
2963
|
+
}
|
|
2964
|
+
if (!VALID_DIAGNOSTIC_LEVELS.has(String(d.level))) {
|
|
2965
|
+
diagnostics.push({
|
|
2966
|
+
severity: "error",
|
|
2967
|
+
code: "INVALID_DIAGNOSTIC_LEVEL",
|
|
2968
|
+
message: `diagnostics[${i}].level must be "error" or "warning"`
|
|
2969
|
+
});
|
|
2970
|
+
}
|
|
2971
|
+
if (typeof d.role !== "string") {
|
|
2972
|
+
diagnostics.push({
|
|
2973
|
+
severity: "error",
|
|
2974
|
+
code: "MISSING_DIAGNOSTIC_ROLE",
|
|
2975
|
+
message: `diagnostics[${i}] missing required field: role`
|
|
2976
|
+
});
|
|
2977
|
+
}
|
|
2978
|
+
if (typeof d.message !== "string") {
|
|
2979
|
+
diagnostics.push({
|
|
2980
|
+
severity: "error",
|
|
2981
|
+
code: "MISSING_DIAGNOSTIC_MESSAGE",
|
|
2982
|
+
message: `diagnostics[${i}] missing required field: message`
|
|
2983
|
+
});
|
|
2984
|
+
}
|
|
2985
|
+
if (typeof d.code !== "string") {
|
|
2986
|
+
diagnostics.push({
|
|
2987
|
+
severity: "error",
|
|
2988
|
+
code: "MISSING_DIAGNOSTIC_CODE",
|
|
2989
|
+
message: `diagnostics[${i}] missing required field: code`
|
|
2990
|
+
});
|
|
2991
|
+
}
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
if ("annotations" in node) {
|
|
2996
|
+
if (!Array.isArray(node.annotations)) {
|
|
2997
|
+
diagnostics.push({
|
|
2998
|
+
severity: "error",
|
|
2999
|
+
code: "INVALID_ANNOTATIONS",
|
|
3000
|
+
message: "annotations must be an array"
|
|
3001
|
+
});
|
|
3002
|
+
} else {
|
|
3003
|
+
for (let i = 0; i < node.annotations.length; i++) {
|
|
3004
|
+
const a = node.annotations[i];
|
|
3005
|
+
if (!a || typeof a !== "object") {
|
|
3006
|
+
diagnostics.push({
|
|
3007
|
+
severity: "error",
|
|
3008
|
+
code: "INVALID_ANNOTATION_ENTRY",
|
|
3009
|
+
message: `annotations[${i}] must be an object`
|
|
3010
|
+
});
|
|
3011
|
+
continue;
|
|
3012
|
+
}
|
|
3013
|
+
if (typeof a.name !== "string" || a.name.length === 0) {
|
|
3014
|
+
diagnostics.push({
|
|
3015
|
+
severity: "error",
|
|
3016
|
+
code: "MISSING_ANNOTATION_NAME",
|
|
3017
|
+
message: `annotations[${i}] missing required field: name`
|
|
3018
|
+
});
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
if ("asyncVariant" in node && !VALID_ASYNC_VARIANTS.has(String(node.asyncVariant))) {
|
|
3024
|
+
diagnostics.push({
|
|
3025
|
+
severity: "error",
|
|
3026
|
+
code: "INVALID_ASYNC_VARIANT",
|
|
3027
|
+
message: `Invalid asyncVariant "${node.asyncVariant}". Must be "all" or "race"`
|
|
3028
|
+
});
|
|
3029
|
+
}
|
|
3030
|
+
if ("arms" in node) {
|
|
3031
|
+
if (!Array.isArray(node.arms)) {
|
|
3032
|
+
diagnostics.push({
|
|
3033
|
+
severity: "error",
|
|
3034
|
+
code: "INVALID_ARMS",
|
|
3035
|
+
message: "arms must be an array"
|
|
3036
|
+
});
|
|
3037
|
+
} else {
|
|
3038
|
+
for (let i = 0; i < node.arms.length; i++) {
|
|
3039
|
+
const arm = node.arms[i];
|
|
3040
|
+
if (!arm || typeof arm !== "object") {
|
|
3041
|
+
diagnostics.push({
|
|
3042
|
+
severity: "error",
|
|
3043
|
+
code: "INVALID_ARM_ENTRY",
|
|
3044
|
+
message: `arms[${i}] must be an object`
|
|
3045
|
+
});
|
|
3046
|
+
continue;
|
|
3047
|
+
}
|
|
3048
|
+
if (!arm.pattern || typeof arm.pattern !== "object") {
|
|
3049
|
+
diagnostics.push({
|
|
3050
|
+
severity: "error",
|
|
3051
|
+
code: "MISSING_ARM_PATTERN",
|
|
3052
|
+
message: `arms[${i}] missing required field: pattern`
|
|
3053
|
+
});
|
|
3054
|
+
}
|
|
3055
|
+
if (!Array.isArray(arm.body)) {
|
|
3056
|
+
diagnostics.push({
|
|
3057
|
+
severity: "error",
|
|
3058
|
+
code: "MISSING_ARM_BODY",
|
|
3059
|
+
message: `arms[${i}] missing required field: body (array)`
|
|
3060
|
+
});
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
return diagnostics;
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3068
|
+
// src/ir/from-interchange.ts
|
|
3069
|
+
function fromInterchangeNode(node) {
|
|
3070
|
+
if (!node || typeof node !== "object") {
|
|
3071
|
+
return createCommandNode("unknown", {});
|
|
3072
|
+
}
|
|
3073
|
+
switch (node.type) {
|
|
3074
|
+
case "event":
|
|
3075
|
+
return convertEvent(node);
|
|
3076
|
+
case "command":
|
|
3077
|
+
return convertCommand(node);
|
|
3078
|
+
case "if":
|
|
3079
|
+
return convertIf(node);
|
|
3080
|
+
case "repeat":
|
|
3081
|
+
return convertRepeat(node);
|
|
3082
|
+
case "foreach":
|
|
3083
|
+
return convertForEach(node);
|
|
3084
|
+
case "while":
|
|
3085
|
+
return convertWhile(node);
|
|
3086
|
+
// Expression nodes — wrap as `get` command with patient role
|
|
3087
|
+
default:
|
|
3088
|
+
return createCommandNode("get", { patient: convertValue(node) });
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
function convertEvent(node) {
|
|
3092
|
+
const event = String(node.event ?? "click");
|
|
3093
|
+
const bodyNodes = Array.isArray(node.body) ? node.body : [];
|
|
3094
|
+
const body = bodyNodes.map(fromInterchangeNode);
|
|
3095
|
+
const roles = {
|
|
3096
|
+
event: createLiteral(event)
|
|
3097
|
+
};
|
|
3098
|
+
if (node.target && typeof node.target === "object") {
|
|
3099
|
+
roles.source = convertValue(node.target);
|
|
3100
|
+
}
|
|
3101
|
+
const modifiers = node.modifiers;
|
|
3102
|
+
let eventModifiers;
|
|
3103
|
+
if (modifiers) {
|
|
3104
|
+
eventModifiers = bridgeEventModifiers(modifiers);
|
|
3105
|
+
if (modifiers.prevent) roles.prevent = createLiteral(true);
|
|
3106
|
+
if (modifiers.stop) roles.stop = createLiteral(true);
|
|
3107
|
+
if (modifiers.passive) roles.passive = createLiteral(true);
|
|
3108
|
+
if (modifiers.capture) roles.capture = createLiteral(true);
|
|
3109
|
+
}
|
|
3110
|
+
return createEventHandlerNode("on", roles, body, void 0, eventModifiers);
|
|
3111
|
+
}
|
|
3112
|
+
function convertCommand(node) {
|
|
3113
|
+
const name = String(node.name ?? "unknown");
|
|
3114
|
+
const args = Array.isArray(node.args) ? node.args : [];
|
|
3115
|
+
const target = node.target && typeof node.target === "object" ? node.target : void 0;
|
|
3116
|
+
const existingRoles = node.roles;
|
|
3117
|
+
const modifiers = node.modifiers;
|
|
3118
|
+
let roles;
|
|
3119
|
+
if (existingRoles && typeof existingRoles === "object" && Object.keys(existingRoles).length > 0) {
|
|
3120
|
+
roles = {};
|
|
3121
|
+
for (const [role, value] of Object.entries(existingRoles)) {
|
|
3122
|
+
if (value && typeof value === "object" && "type" in value) {
|
|
3123
|
+
roles[role] = convertValue(value);
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
} else {
|
|
3127
|
+
roles = inferRoles(name, args, modifiers, target);
|
|
3128
|
+
}
|
|
3129
|
+
return createCommandNode(name, roles);
|
|
3130
|
+
}
|
|
3131
|
+
function convertIf(node) {
|
|
3132
|
+
const condition = node.condition && typeof node.condition === "object" ? convertValue(node.condition) : createExpression("true");
|
|
3133
|
+
const thenBody = Array.isArray(node.thenBranch) ? node.thenBranch : [];
|
|
3134
|
+
const thenBranch = thenBody.map(fromInterchangeNode);
|
|
3135
|
+
let elseBranch;
|
|
3136
|
+
const elseIfBranches = node.elseIfBranches;
|
|
3137
|
+
if (elseIfBranches && elseIfBranches.length > 0) {
|
|
3138
|
+
elseBranch = [chainElseIfs(elseIfBranches, node.elseBranch)];
|
|
3139
|
+
} else if (Array.isArray(node.elseBranch) && node.elseBranch.length > 0) {
|
|
3140
|
+
elseBranch = node.elseBranch.map(fromInterchangeNode);
|
|
3141
|
+
}
|
|
3142
|
+
const roles = { condition };
|
|
3143
|
+
return createConditionalNode("if", roles, thenBranch, elseBranch);
|
|
3144
|
+
}
|
|
3145
|
+
function chainElseIfs(branches, finalElse) {
|
|
3146
|
+
const [first, ...rest] = branches;
|
|
3147
|
+
const condition = convertValue(first.condition);
|
|
3148
|
+
const thenBranch = first.body.map(fromInterchangeNode);
|
|
3149
|
+
let elseBranch;
|
|
3150
|
+
if (rest.length > 0) {
|
|
3151
|
+
elseBranch = [chainElseIfs(rest, finalElse)];
|
|
3152
|
+
} else if (finalElse && finalElse.length > 0) {
|
|
3153
|
+
elseBranch = finalElse.map(fromInterchangeNode);
|
|
3154
|
+
}
|
|
3155
|
+
return createConditionalNode("if", { condition }, thenBranch, elseBranch);
|
|
3156
|
+
}
|
|
3157
|
+
function convertRepeat(node) {
|
|
3158
|
+
const bodyNodes = Array.isArray(node.body) ? node.body : [];
|
|
3159
|
+
const body = bodyNodes.map(fromInterchangeNode);
|
|
3160
|
+
const roles = {};
|
|
3161
|
+
let variant;
|
|
3162
|
+
if (node.count !== void 0 && node.count !== null) {
|
|
3163
|
+
variant = "times";
|
|
3164
|
+
if (typeof node.count === "number") {
|
|
3165
|
+
roles.quantity = createLiteral(node.count, "number");
|
|
3166
|
+
} else if (typeof node.count === "object") {
|
|
3167
|
+
roles.quantity = convertValue(node.count);
|
|
3168
|
+
}
|
|
3169
|
+
} else if (node.whileCondition && typeof node.whileCondition === "object") {
|
|
3170
|
+
variant = "while";
|
|
3171
|
+
roles.condition = convertValue(node.whileCondition);
|
|
3172
|
+
} else if (typeof node.untilEvent === "string") {
|
|
3173
|
+
variant = "until";
|
|
3174
|
+
roles.condition = createLiteral(node.untilEvent);
|
|
3175
|
+
} else {
|
|
3176
|
+
variant = "forever";
|
|
3177
|
+
}
|
|
3178
|
+
return createLoopNode("repeat", roles, variant, body);
|
|
3179
|
+
}
|
|
3180
|
+
function convertForEach(node) {
|
|
3181
|
+
const bodyNodes = Array.isArray(node.body) ? node.body : [];
|
|
3182
|
+
const body = bodyNodes.map(fromInterchangeNode);
|
|
3183
|
+
const roles = {};
|
|
3184
|
+
if (node.collection && typeof node.collection === "object") {
|
|
3185
|
+
roles.source = convertValue(node.collection);
|
|
3186
|
+
}
|
|
3187
|
+
const itemName = typeof node.itemName === "string" ? node.itemName : void 0;
|
|
3188
|
+
const indexName = typeof node.indexName === "string" ? node.indexName : void 0;
|
|
3189
|
+
return createLoopNode("repeat", roles, "for", body, itemName, indexName);
|
|
3190
|
+
}
|
|
3191
|
+
function convertWhile(node) {
|
|
3192
|
+
const bodyNodes = Array.isArray(node.body) ? node.body : [];
|
|
3193
|
+
const body = bodyNodes.map(fromInterchangeNode);
|
|
3194
|
+
const roles = {};
|
|
3195
|
+
if (node.condition && typeof node.condition === "object") {
|
|
3196
|
+
roles.condition = convertValue(node.condition);
|
|
3197
|
+
}
|
|
3198
|
+
return createLoopNode("repeat", roles, "while", body);
|
|
3199
|
+
}
|
|
3200
|
+
function convertValue(node) {
|
|
3201
|
+
switch (node.type) {
|
|
3202
|
+
case "literal":
|
|
3203
|
+
return convertLiteral(node);
|
|
3204
|
+
case "selector":
|
|
3205
|
+
return createSelector(String(node.value ?? ""), inferSelectorKind2(String(node.value ?? "")));
|
|
3206
|
+
case "identifier":
|
|
3207
|
+
return convertIdentifier(node);
|
|
3208
|
+
case "variable":
|
|
3209
|
+
return convertVariable(node);
|
|
3210
|
+
case "possessive":
|
|
3211
|
+
return createPropertyPath(
|
|
3212
|
+
node.object && typeof node.object === "object" ? convertValue(node.object) : createReference("me"),
|
|
3213
|
+
String(node.property ?? "")
|
|
3214
|
+
);
|
|
3215
|
+
// Complex expressions → collapse to raw string
|
|
3216
|
+
case "binary":
|
|
3217
|
+
case "unary":
|
|
3218
|
+
case "member":
|
|
3219
|
+
case "call":
|
|
3220
|
+
case "positional":
|
|
3221
|
+
return createExpression(renderExpr(node));
|
|
3222
|
+
default:
|
|
3223
|
+
if ("value" in node) return createLiteral(String(node.value ?? ""));
|
|
3224
|
+
return createExpression(renderExpr(node));
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
function convertLiteral(node) {
|
|
3228
|
+
const value = node.value;
|
|
3229
|
+
if (value === null || value === void 0) return createLiteral("null");
|
|
3230
|
+
const dataType = typeof value === "number" ? "number" : typeof value === "boolean" ? "boolean" : void 0;
|
|
3231
|
+
return createLiteral(value, dataType);
|
|
3232
|
+
}
|
|
3233
|
+
function convertIdentifier(node) {
|
|
3234
|
+
const value = String(node.value ?? "");
|
|
3235
|
+
if (DEFAULT_REFERENCES.has(value)) {
|
|
3236
|
+
return createReference(value);
|
|
3237
|
+
}
|
|
3238
|
+
return createLiteral(value);
|
|
3239
|
+
}
|
|
3240
|
+
function convertVariable(node) {
|
|
3241
|
+
const name = String(node.name ?? "");
|
|
3242
|
+
const scope = String(node.scope ?? "local");
|
|
3243
|
+
switch (scope) {
|
|
3244
|
+
case "element":
|
|
3245
|
+
return createExpression(`:${name}`);
|
|
3246
|
+
case "global":
|
|
3247
|
+
return createExpression(`$${name}`);
|
|
3248
|
+
default:
|
|
3249
|
+
return createExpression(name);
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
function renderExpr(node) {
|
|
3253
|
+
if (!node || typeof node !== "object") return "";
|
|
3254
|
+
switch (node.type) {
|
|
3255
|
+
case "literal": {
|
|
3256
|
+
const v = node.value;
|
|
3257
|
+
if (v === null || v === void 0) return "null";
|
|
3258
|
+
if (typeof v === "string") return /\s/.test(v) ? `"${v}"` : v;
|
|
3259
|
+
return String(v);
|
|
3260
|
+
}
|
|
3261
|
+
case "identifier":
|
|
3262
|
+
return String(node.value ?? "");
|
|
3263
|
+
case "selector":
|
|
3264
|
+
return String(node.value ?? "");
|
|
3265
|
+
case "variable": {
|
|
3266
|
+
const name = String(node.name ?? "");
|
|
3267
|
+
const scope = String(node.scope ?? "local");
|
|
3268
|
+
if (scope === "element") return `:${name}`;
|
|
3269
|
+
if (scope === "global") return `$${name}`;
|
|
3270
|
+
return name;
|
|
3271
|
+
}
|
|
3272
|
+
case "binary": {
|
|
3273
|
+
const left = node.left && typeof node.left === "object" ? renderExpr(node.left) : "";
|
|
3274
|
+
const right = node.right && typeof node.right === "object" ? renderExpr(node.right) : "";
|
|
3275
|
+
return `${left} ${node.operator ?? ""} ${right}`;
|
|
3276
|
+
}
|
|
3277
|
+
case "unary": {
|
|
3278
|
+
const operand = node.operand && typeof node.operand === "object" ? renderExpr(node.operand) : "";
|
|
3279
|
+
return `${node.operator ?? ""} ${operand}`;
|
|
3280
|
+
}
|
|
3281
|
+
case "member": {
|
|
3282
|
+
const obj = node.object && typeof node.object === "object" ? renderExpr(node.object) : "";
|
|
3283
|
+
if (node.computed && node.property && typeof node.property === "object") {
|
|
3284
|
+
return `${obj}[${renderExpr(node.property)}]`;
|
|
3285
|
+
}
|
|
3286
|
+
const prop = typeof node.property === "string" ? node.property : node.property && typeof node.property === "object" ? renderExpr(node.property) : "";
|
|
3287
|
+
return `${obj}.${prop}`;
|
|
3288
|
+
}
|
|
3289
|
+
case "possessive": {
|
|
3290
|
+
const obj = node.object && typeof node.object === "object" ? renderExpr(node.object) : "";
|
|
3291
|
+
return `${obj}'s ${node.property ?? ""}`;
|
|
3292
|
+
}
|
|
3293
|
+
case "call": {
|
|
3294
|
+
const callee = node.callee && typeof node.callee === "object" ? renderExpr(node.callee) : "";
|
|
3295
|
+
const args = Array.isArray(node.args) ? node.args.map((a) => renderExpr(a)).join(", ") : "";
|
|
3296
|
+
return `${callee}(${args})`;
|
|
3297
|
+
}
|
|
3298
|
+
case "positional": {
|
|
3299
|
+
const target = node.target && typeof node.target === "object" ? renderExpr(node.target) : "";
|
|
3300
|
+
return target ? `${node.position ?? ""} ${target}` : String(node.position ?? "");
|
|
3301
|
+
}
|
|
3302
|
+
default:
|
|
3303
|
+
if ("value" in node) return String(node.value ?? "");
|
|
3304
|
+
return "";
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
function inferRoles(name, args, modifiers, target) {
|
|
3308
|
+
const roles = {};
|
|
3309
|
+
switch (name) {
|
|
3310
|
+
case "set": {
|
|
3311
|
+
if (args[0]) roles.destination = convertValue(args[0]);
|
|
3312
|
+
const toVal = modifiers?.to;
|
|
3313
|
+
if (toVal && typeof toVal === "object" && "type" in toVal) {
|
|
3314
|
+
roles.patient = convertValue(toVal);
|
|
3315
|
+
} else if (args[1]) {
|
|
3316
|
+
roles.patient = convertValue(args[1]);
|
|
3317
|
+
}
|
|
3318
|
+
break;
|
|
3319
|
+
}
|
|
3320
|
+
case "put": {
|
|
3321
|
+
if (args[0]) roles.patient = convertValue(args[0]);
|
|
3322
|
+
if (modifiers) {
|
|
3323
|
+
for (const key of ["into", "before", "after"]) {
|
|
3324
|
+
const val = modifiers[key];
|
|
3325
|
+
if (val && typeof val === "object" && "type" in val) {
|
|
3326
|
+
roles.destination = convertValue(val);
|
|
3327
|
+
roles.method = createLiteral(key);
|
|
3328
|
+
break;
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
if (!roles.destination && target) {
|
|
3333
|
+
roles.destination = convertValue(target);
|
|
3334
|
+
}
|
|
3335
|
+
break;
|
|
3336
|
+
}
|
|
3337
|
+
case "increment":
|
|
3338
|
+
case "decrement": {
|
|
3339
|
+
if (args[0]) roles.destination = convertValue(args[0]);
|
|
3340
|
+
const byVal = modifiers?.by;
|
|
3341
|
+
if (byVal && typeof byVal === "object" && "type" in byVal) {
|
|
3342
|
+
roles.quantity = convertValue(byVal);
|
|
3343
|
+
} else if (args[1]) {
|
|
3344
|
+
roles.quantity = convertValue(args[1]);
|
|
3345
|
+
}
|
|
3346
|
+
break;
|
|
3347
|
+
}
|
|
3348
|
+
case "fetch": {
|
|
3349
|
+
if (args[0]) roles.source = convertValue(args[0]);
|
|
3350
|
+
const asVal = modifiers?.as;
|
|
3351
|
+
if (asVal && typeof asVal === "object" && "type" in asVal) {
|
|
3352
|
+
roles.responseType = convertValue(asVal);
|
|
3353
|
+
} else if (typeof asVal === "string") {
|
|
3354
|
+
roles.responseType = createLiteral(asVal);
|
|
3355
|
+
}
|
|
3356
|
+
break;
|
|
3357
|
+
}
|
|
3358
|
+
case "wait":
|
|
3359
|
+
case "settle": {
|
|
3360
|
+
if (args[0]) roles.duration = convertValue(args[0]);
|
|
3361
|
+
break;
|
|
3362
|
+
}
|
|
3363
|
+
case "toggle":
|
|
3364
|
+
case "add":
|
|
3365
|
+
case "show":
|
|
3366
|
+
case "hide": {
|
|
3367
|
+
if (args[0]) roles.patient = convertValue(args[0]);
|
|
3368
|
+
if (target) roles.destination = convertValue(target);
|
|
3369
|
+
break;
|
|
3370
|
+
}
|
|
3371
|
+
case "remove": {
|
|
3372
|
+
if (args[0]) roles.patient = convertValue(args[0]);
|
|
3373
|
+
if (target) roles.source = convertValue(target);
|
|
3374
|
+
break;
|
|
3375
|
+
}
|
|
3376
|
+
case "send":
|
|
3377
|
+
case "trigger": {
|
|
3378
|
+
if (args[0]) roles.patient = convertValue(args[0]);
|
|
3379
|
+
if (target) roles.destination = convertValue(target);
|
|
3380
|
+
break;
|
|
3381
|
+
}
|
|
3382
|
+
case "log": {
|
|
3383
|
+
if (args[0]) roles.patient = convertValue(args[0]);
|
|
3384
|
+
break;
|
|
3385
|
+
}
|
|
3386
|
+
default: {
|
|
3387
|
+
if (args[0]) roles.patient = convertValue(args[0]);
|
|
3388
|
+
if (target) roles.destination = convertValue(target);
|
|
3389
|
+
break;
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
3392
|
+
return roles;
|
|
3393
|
+
}
|
|
3394
|
+
function inferSelectorKind2(value) {
|
|
3395
|
+
if (!value) return void 0;
|
|
3396
|
+
if (value.startsWith("#")) return "id";
|
|
3397
|
+
if (value.startsWith(".")) return "class";
|
|
3398
|
+
if (value.startsWith("[")) return "attribute";
|
|
3399
|
+
if (value.startsWith("<") || value.startsWith("*")) return "element";
|
|
3400
|
+
if (/[>\+~ ]/.test(value) && value.length > 1) return "complex";
|
|
3401
|
+
return void 0;
|
|
3402
|
+
}
|
|
3403
|
+
function inferValueFromString(value) {
|
|
3404
|
+
if (DEFAULT_REFERENCES.has(value)) return createReference(value);
|
|
3405
|
+
const kind = inferSelectorKind2(value);
|
|
3406
|
+
if (kind) return createSelector(value, kind);
|
|
3407
|
+
return createLiteral(value);
|
|
3408
|
+
}
|
|
3409
|
+
function bridgeEventModifiers(mods) {
|
|
3410
|
+
const once = mods.once === true ? true : void 0;
|
|
3411
|
+
const debounce = typeof mods.debounce === "number" ? mods.debounce : void 0;
|
|
3412
|
+
const throttle = typeof mods.throttle === "number" ? mods.throttle : void 0;
|
|
3413
|
+
const from = typeof mods.from === "string" ? inferValueFromString(mods.from) : void 0;
|
|
3414
|
+
if (once === void 0 && debounce === void 0 && throttle === void 0 && from === void 0) {
|
|
3415
|
+
return void 0;
|
|
3416
|
+
}
|
|
3417
|
+
return {
|
|
3418
|
+
...once !== void 0 ? { once } : {},
|
|
3419
|
+
...debounce !== void 0 ? { debounce } : {},
|
|
3420
|
+
...throttle !== void 0 ? { throttle } : {},
|
|
3421
|
+
...from !== void 0 ? { from } : {}
|
|
3422
|
+
};
|
|
3423
|
+
}
|
|
3424
|
+
|
|
1912
3425
|
// src/api/create-dsl.ts
|
|
1913
3426
|
var DSLRegistry = class {
|
|
1914
3427
|
constructor(config) {
|
|
@@ -1946,12 +3459,23 @@ var MultilingualDSLImpl = class {
|
|
|
1946
3459
|
if (config.codeGenerator) {
|
|
1947
3460
|
this.codeGenerator = config.codeGenerator;
|
|
1948
3461
|
}
|
|
3462
|
+
const schemaMap = new Map(config.schemas.map((s) => [s.action, s]));
|
|
3463
|
+
this.schemaLookup = {
|
|
3464
|
+
getSchema: (action) => schemaMap.get(action)
|
|
3465
|
+
};
|
|
1949
3466
|
}
|
|
1950
3467
|
parse(input, language) {
|
|
1951
3468
|
const result = this.parseWithConfidence(input, language);
|
|
1952
3469
|
return result.node;
|
|
1953
3470
|
}
|
|
1954
3471
|
parseWithConfidence(input, language) {
|
|
3472
|
+
if (isExplicitSyntax(input)) {
|
|
3473
|
+
const node = parseExplicit(input, { schemaLookup: this.schemaLookup });
|
|
3474
|
+
if (!this.schemaLookup.getSchema(node.action)) {
|
|
3475
|
+
throw new Error(`No schema for action "${node.action}" in this DSL`);
|
|
3476
|
+
}
|
|
3477
|
+
return { node, confidence: 1 };
|
|
3478
|
+
}
|
|
1955
3479
|
const tokenizer = this.registry.getTokenizer(language);
|
|
1956
3480
|
if (!tokenizer) {
|
|
1957
3481
|
throw new Error(`No tokenizer registered for language: ${language}`);
|
|
@@ -1987,75 +3511,802 @@ var MultilingualDSLImpl = class {
|
|
|
1987
3511
|
valid: true,
|
|
1988
3512
|
node: result.node
|
|
1989
3513
|
};
|
|
1990
|
-
} catch (error) {
|
|
3514
|
+
} catch (error) {
|
|
3515
|
+
return {
|
|
3516
|
+
valid: false,
|
|
3517
|
+
errors: [error instanceof Error ? error.message : String(error)]
|
|
3518
|
+
};
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
translate(input, fromLanguage, toLanguage) {
|
|
3522
|
+
if (isExplicitSyntax(input)) return input;
|
|
3523
|
+
return this.transformer.transform(input, fromLanguage, toLanguage);
|
|
3524
|
+
}
|
|
3525
|
+
compile(input, language) {
|
|
3526
|
+
if (!this.codeGenerator) {
|
|
3527
|
+
return {
|
|
3528
|
+
ok: false,
|
|
3529
|
+
errors: ["No code generator configured for this DSL"]
|
|
3530
|
+
};
|
|
3531
|
+
}
|
|
3532
|
+
try {
|
|
3533
|
+
const result = this.parseWithConfidence(input, language);
|
|
3534
|
+
const code = this.codeGenerator.generate(result.node);
|
|
3535
|
+
return {
|
|
3536
|
+
ok: true,
|
|
3537
|
+
code,
|
|
3538
|
+
node: result.node,
|
|
3539
|
+
metadata: {
|
|
3540
|
+
parser: "semantic",
|
|
3541
|
+
confidence: result.confidence
|
|
3542
|
+
}
|
|
3543
|
+
};
|
|
3544
|
+
} catch (error) {
|
|
3545
|
+
return {
|
|
3546
|
+
ok: false,
|
|
3547
|
+
errors: [error instanceof Error ? error.message : String(error)]
|
|
3548
|
+
};
|
|
3549
|
+
}
|
|
3550
|
+
}
|
|
3551
|
+
getSupportedLanguages() {
|
|
3552
|
+
return this.registry.getSupportedLanguages();
|
|
3553
|
+
}
|
|
3554
|
+
};
|
|
3555
|
+
function createDefaultDictionary(config) {
|
|
3556
|
+
const translations = {};
|
|
3557
|
+
for (const lang of config.languages) {
|
|
3558
|
+
const keywords = {};
|
|
3559
|
+
for (const [canonical, keywordDef] of Object.entries(lang.patternProfile.keywords)) {
|
|
3560
|
+
keywords[canonical] = keywordDef.primary;
|
|
3561
|
+
}
|
|
3562
|
+
translations[lang.code] = keywords;
|
|
3563
|
+
}
|
|
3564
|
+
return new InMemoryDictionary(translations);
|
|
3565
|
+
}
|
|
3566
|
+
function createDefaultProfileProvider(config) {
|
|
3567
|
+
const profiles = {};
|
|
3568
|
+
for (const lang of config.languages) {
|
|
3569
|
+
if (lang.grammarProfile) {
|
|
3570
|
+
profiles[lang.code] = lang.grammarProfile;
|
|
3571
|
+
}
|
|
3572
|
+
}
|
|
3573
|
+
return new InMemoryProfileProvider(profiles);
|
|
3574
|
+
}
|
|
3575
|
+
function createMultilingualDSL(config) {
|
|
3576
|
+
const dictionary = config.dictionary ?? createDefaultDictionary(config);
|
|
3577
|
+
const profileProvider = config.profileProvider ?? createDefaultProfileProvider(config);
|
|
3578
|
+
const transformer = new GrammarTransformer({
|
|
3579
|
+
dictionary,
|
|
3580
|
+
profileProvider
|
|
3581
|
+
});
|
|
3582
|
+
const registry = new DSLRegistry(config);
|
|
3583
|
+
return new MultilingualDSLImpl(config, registry, transformer);
|
|
3584
|
+
}
|
|
3585
|
+
|
|
3586
|
+
// src/prompts/prompt-generator.ts
|
|
3587
|
+
function generatePrompt(config) {
|
|
3588
|
+
const outputFormat = config.outputFormat ?? "both";
|
|
3589
|
+
const examplesPerCommand = config.examplesPerCommand ?? 2;
|
|
3590
|
+
const sections = [
|
|
3591
|
+
buildProtocolSection(),
|
|
3592
|
+
buildValueTypeSection(),
|
|
3593
|
+
buildCommandsSection(config.schemas, outputFormat, examplesPerCommand),
|
|
3594
|
+
buildOutputFormatSection(outputFormat),
|
|
3595
|
+
buildErrorRecoverySection()
|
|
3596
|
+
];
|
|
3597
|
+
let finalSections = sections;
|
|
3598
|
+
if (config.maxTokens && config.maxTokens > 0) {
|
|
3599
|
+
finalSections = truncateToTokenBudget(sections, config.maxTokens);
|
|
3600
|
+
}
|
|
3601
|
+
const text = finalSections.map((s) => `## ${s.title}
|
|
3602
|
+
|
|
3603
|
+
${s.content}`).join("\n\n---\n\n");
|
|
3604
|
+
const totalRoles = config.schemas.reduce((sum, s) => sum + s.roles.length, 0);
|
|
3605
|
+
const approximateTokens = estimateTokens(text);
|
|
3606
|
+
const metadata = {
|
|
3607
|
+
domain: config.domain,
|
|
3608
|
+
commandCount: config.schemas.length,
|
|
3609
|
+
roleCount: totalRoles,
|
|
3610
|
+
approximateTokens
|
|
3611
|
+
};
|
|
3612
|
+
return { text, sections: finalSections, metadata };
|
|
3613
|
+
}
|
|
3614
|
+
function generateExamples(schema, count = 2) {
|
|
3615
|
+
const combos = generateRoleCombinations(schema);
|
|
3616
|
+
const examples = [];
|
|
3617
|
+
for (let i = 0; i < Math.min(count, combos.length); i++) {
|
|
3618
|
+
const combo = combos[i];
|
|
3619
|
+
const roles = /* @__PURE__ */ new Map();
|
|
3620
|
+
for (const role of combo) {
|
|
3621
|
+
const sample = sampleValue(role);
|
|
3622
|
+
roles.set(role.role, sample);
|
|
3623
|
+
}
|
|
3624
|
+
const nodeRoles = /* @__PURE__ */ new Map();
|
|
3625
|
+
for (const [roleName, sample] of roles) {
|
|
3626
|
+
nodeRoles.set(roleName, toSemanticValue(sample));
|
|
3627
|
+
}
|
|
3628
|
+
const node = createCommandNode(schema.action, nodeRoles);
|
|
3629
|
+
const explicit = renderExplicit(node);
|
|
3630
|
+
const jsonRoles = {};
|
|
3631
|
+
for (const [roleName, sample] of roles) {
|
|
3632
|
+
jsonRoles[roleName] = sample;
|
|
3633
|
+
}
|
|
3634
|
+
const json = { action: schema.action, roles: jsonRoles };
|
|
3635
|
+
examples.push({ explicit, json });
|
|
3636
|
+
}
|
|
3637
|
+
return examples;
|
|
3638
|
+
}
|
|
3639
|
+
function generateProtocolReference() {
|
|
3640
|
+
const protocol = buildProtocolSection();
|
|
3641
|
+
const valueTypes = buildValueTypeSection();
|
|
3642
|
+
return `# LokaScript Explicit Syntax (LSE) Quick Reference
|
|
3643
|
+
|
|
3644
|
+
${protocol.content}
|
|
3645
|
+
|
|
3646
|
+
${valueTypes.content}`;
|
|
3647
|
+
}
|
|
3648
|
+
function buildProtocolSection() {
|
|
3649
|
+
const content = `LokaScript Explicit Syntax (LSE) is a bracket-based format for imperative commands.
|
|
3650
|
+
|
|
3651
|
+
### Syntax
|
|
3652
|
+
|
|
3653
|
+
\`\`\`
|
|
3654
|
+
[command role1:value1 role2:value2 +flag1 ~flag2]
|
|
3655
|
+
\`\`\`
|
|
3656
|
+
|
|
3657
|
+
- **Command**: The first token inside brackets (lowercased)
|
|
3658
|
+
- **Role pair**: \`name:value\` \u2014 a named semantic role with a typed value (no space around colon)
|
|
3659
|
+
- **Enabled flag**: \`+name\` \u2014 boolean attribute present
|
|
3660
|
+
- **Disabled flag**: \`~name\` \u2014 boolean attribute negated
|
|
3661
|
+
- **Nested body**: \`body:[command ...]\` \u2014 a bracket command inside a role value
|
|
3662
|
+
|
|
3663
|
+
### Rules
|
|
3664
|
+
|
|
3665
|
+
1. Commands are always lowercased
|
|
3666
|
+
2. Role names preserve their original case
|
|
3667
|
+
3. No spaces around the colon in role:value pairs
|
|
3668
|
+
4. Strings with spaces must be quoted: \`patient:"hello world"\`
|
|
3669
|
+
5. Selectors start with \`#\`, \`.\`, \`[\`, \`@\`, or \`*\`
|
|
3670
|
+
6. Output must be valid bracket syntax: \`[action role:value ...]\``;
|
|
3671
|
+
return {
|
|
3672
|
+
id: "protocol",
|
|
3673
|
+
title: "LSE Protocol",
|
|
3674
|
+
content,
|
|
3675
|
+
approximateTokens: estimateTokens(content)
|
|
3676
|
+
};
|
|
3677
|
+
}
|
|
3678
|
+
function buildValueTypeSection() {
|
|
3679
|
+
const content = `Values in LSE are classified by their syntactic form (first match wins):
|
|
3680
|
+
|
|
3681
|
+
| Type | Syntax | Example |
|
|
3682
|
+
|------|--------|---------|
|
|
3683
|
+
| Selector | Starts with \`#\` \`.\` \`[\` \`@\` \`*\` | \`#button\`, \`.active\`, \`[data-id]\` |
|
|
3684
|
+
| String | Quoted with \`"\` or \`'\` | \`"hello world"\`, \`'json'\` |
|
|
3685
|
+
| Boolean | Exact: \`true\` / \`false\` | \`visible:true\` |
|
|
3686
|
+
| Reference | Built-in name | \`me\`, \`you\`, \`it\`, \`result\`, \`event\`, \`target\`, \`body\` |
|
|
3687
|
+
| Duration | Number + suffix | \`500ms\`, \`2s\`, \`1m\`, \`1h\` |
|
|
3688
|
+
| Number | Digits with optional decimal | \`5\`, \`3.14\`, \`-1\` |
|
|
3689
|
+
| Plain | Fallback (any non-whitespace) | \`/api/users\`, \`json\`, \`production\` |
|
|
3690
|
+
|
|
3691
|
+
**Important**: Classification is by prefix, not by intent. \`#true\` is a selector (not boolean). \`event\` as a value is a reference (not plain text).`;
|
|
3692
|
+
return {
|
|
3693
|
+
id: "value-types",
|
|
3694
|
+
title: "Value Types",
|
|
3695
|
+
content,
|
|
3696
|
+
approximateTokens: estimateTokens(content)
|
|
3697
|
+
};
|
|
3698
|
+
}
|
|
3699
|
+
function buildCommandsSection(schemas, outputFormat, examplesPerCommand) {
|
|
3700
|
+
const parts = [];
|
|
3701
|
+
for (const schema of schemas) {
|
|
3702
|
+
parts.push(formatCommandDoc(schema, outputFormat, examplesPerCommand));
|
|
3703
|
+
}
|
|
3704
|
+
const content = parts.join("\n\n");
|
|
3705
|
+
return {
|
|
3706
|
+
id: "commands",
|
|
3707
|
+
title: "Available Commands",
|
|
3708
|
+
content,
|
|
3709
|
+
approximateTokens: estimateTokens(content)
|
|
3710
|
+
};
|
|
3711
|
+
}
|
|
3712
|
+
function buildOutputFormatSection(outputFormat) {
|
|
3713
|
+
let content;
|
|
3714
|
+
if (outputFormat === "explicit") {
|
|
3715
|
+
content = `Output valid LSE bracket syntax. Each command must be wrapped in brackets:
|
|
3716
|
+
|
|
3717
|
+
\`\`\`
|
|
3718
|
+
[command role:value ...]
|
|
3719
|
+
\`\`\`
|
|
3720
|
+
|
|
3721
|
+
Do NOT output JSON. Only use bracket syntax.`;
|
|
3722
|
+
} else if (outputFormat === "json") {
|
|
3723
|
+
content = `Output valid JSON in the LLM-simplified format:
|
|
3724
|
+
|
|
3725
|
+
\`\`\`json
|
|
3726
|
+
{
|
|
3727
|
+
"action": "command-name",
|
|
3728
|
+
"roles": {
|
|
3729
|
+
"roleName": { "type": "valueType", "value": "theValue" }
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
\`\`\`
|
|
3733
|
+
|
|
3734
|
+
Valid value types: \`selector\`, \`literal\`, \`reference\`, \`expression\`.
|
|
3735
|
+
Do NOT output bracket syntax. Only use JSON.`;
|
|
3736
|
+
} else {
|
|
3737
|
+
content = `You may output EITHER format:
|
|
3738
|
+
|
|
3739
|
+
**Bracket syntax** (preferred for single commands):
|
|
3740
|
+
\`\`\`
|
|
3741
|
+
[command role:value ...]
|
|
3742
|
+
\`\`\`
|
|
3743
|
+
|
|
3744
|
+
**JSON format** (preferred when structured data is needed):
|
|
3745
|
+
\`\`\`json
|
|
3746
|
+
{
|
|
3747
|
+
"action": "command-name",
|
|
3748
|
+
"roles": {
|
|
3749
|
+
"roleName": { "type": "valueType", "value": "theValue" }
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3752
|
+
\`\`\`
|
|
3753
|
+
|
|
3754
|
+
Both formats are equally valid. Use whichever is more natural for the context.`;
|
|
3755
|
+
}
|
|
3756
|
+
return {
|
|
3757
|
+
id: "output-format",
|
|
3758
|
+
title: "Output Format",
|
|
3759
|
+
content,
|
|
3760
|
+
approximateTokens: estimateTokens(content)
|
|
3761
|
+
};
|
|
3762
|
+
}
|
|
3763
|
+
function buildErrorRecoverySection() {
|
|
3764
|
+
const content = `If you're unsure about a command or role:
|
|
3765
|
+
|
|
3766
|
+
1. **Unknown command**: Use the closest matching command from the Available Commands section. Do not invent commands.
|
|
3767
|
+
2. **Unknown role**: Use only the roles listed for each command. Do not add roles not in the schema.
|
|
3768
|
+
3. **Ambiguous value type**: When in doubt, use \`expression\` type (it's the most permissive).
|
|
3769
|
+
4. **Missing required role**: Always include all required roles. Check the Required/Optional labels.
|
|
3770
|
+
5. **Selector vs plain value**: If a value starts with \`#\`, \`.\`, \`[\`, \`@\`, or \`*\`, it's a selector. URLs like \`/api/data\` are plain values.
|
|
3771
|
+
|
|
3772
|
+
When generating LSE, prefer explicit role labeling over positional guessing. Every value should have a role name.`;
|
|
3773
|
+
return {
|
|
3774
|
+
id: "error-recovery",
|
|
3775
|
+
title: "Error Recovery",
|
|
3776
|
+
content,
|
|
3777
|
+
approximateTokens: estimateTokens(content)
|
|
3778
|
+
};
|
|
3779
|
+
}
|
|
3780
|
+
function formatCommandDoc(schema, outputFormat, examplesPerCommand) {
|
|
3781
|
+
const lines = [];
|
|
3782
|
+
lines.push(`### \`${schema.action}\` \u2014 ${schema.description}`);
|
|
3783
|
+
lines.push("");
|
|
3784
|
+
const required = schema.roles.filter((r) => r.required);
|
|
3785
|
+
const optional = schema.roles.filter((r) => !r.required);
|
|
3786
|
+
if (required.length > 0) {
|
|
3787
|
+
lines.push("**Required roles:**");
|
|
3788
|
+
for (const role of required) {
|
|
3789
|
+
lines.push(`- \`${role.role}\`: ${role.description} (type: ${formatExpectedTypes(role)})`);
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
if (optional.length > 0) {
|
|
3793
|
+
lines.push("**Optional roles:**");
|
|
3794
|
+
for (const role of optional) {
|
|
3795
|
+
lines.push(`- \`${role.role}\`: ${role.description} (type: ${formatExpectedTypes(role)})`);
|
|
3796
|
+
}
|
|
3797
|
+
}
|
|
3798
|
+
const examples = generateExamples(schema, examplesPerCommand);
|
|
3799
|
+
if (examples.length > 0) {
|
|
3800
|
+
lines.push("");
|
|
3801
|
+
if (outputFormat === "explicit" || outputFormat === "both") {
|
|
3802
|
+
lines.push("**Bracket syntax:**");
|
|
3803
|
+
for (const ex of examples) {
|
|
3804
|
+
lines.push(`\`\`\`
|
|
3805
|
+
${ex.explicit}
|
|
3806
|
+
\`\`\``);
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
if (outputFormat === "json" || outputFormat === "both") {
|
|
3810
|
+
lines.push("**JSON format:**");
|
|
3811
|
+
for (const ex of examples) {
|
|
3812
|
+
lines.push(`\`\`\`json
|
|
3813
|
+
${JSON.stringify(ex.json, null, 2)}
|
|
3814
|
+
\`\`\``);
|
|
3815
|
+
}
|
|
3816
|
+
}
|
|
3817
|
+
}
|
|
3818
|
+
return lines.join("\n");
|
|
3819
|
+
}
|
|
3820
|
+
function formatExpectedTypes(role) {
|
|
3821
|
+
return role.expectedTypes.join(" | ");
|
|
3822
|
+
}
|
|
3823
|
+
function generateRoleCombinations(schema) {
|
|
3824
|
+
const required = schema.roles.filter((r) => r.required);
|
|
3825
|
+
const optional = schema.roles.filter((r) => !r.required);
|
|
3826
|
+
const combos = [];
|
|
3827
|
+
if (required.length > 0) {
|
|
3828
|
+
combos.push(required);
|
|
3829
|
+
}
|
|
3830
|
+
for (const opt of optional) {
|
|
3831
|
+
combos.push([...required, opt]);
|
|
3832
|
+
}
|
|
3833
|
+
if (optional.length >= 2) {
|
|
3834
|
+
combos.push([...required, ...optional]);
|
|
3835
|
+
}
|
|
3836
|
+
if (combos.length === 0) {
|
|
3837
|
+
combos.push(schema.roles.slice());
|
|
3838
|
+
}
|
|
3839
|
+
return combos;
|
|
3840
|
+
}
|
|
3841
|
+
function sampleValue(role) {
|
|
3842
|
+
const primaryType = role.expectedTypes[0] || "expression";
|
|
3843
|
+
switch (primaryType) {
|
|
3844
|
+
case "selector":
|
|
3845
|
+
return sampleSelector(role.role);
|
|
3846
|
+
case "literal":
|
|
3847
|
+
return sampleLiteral(role.role);
|
|
3848
|
+
case "reference":
|
|
3849
|
+
return { type: "reference", value: "me" };
|
|
3850
|
+
case "expression":
|
|
3851
|
+
return sampleExpression(role.role);
|
|
3852
|
+
case "flag":
|
|
3853
|
+
return { type: "flag", value: true };
|
|
3854
|
+
default:
|
|
3855
|
+
return { type: "literal", value: "example" };
|
|
3856
|
+
}
|
|
3857
|
+
}
|
|
3858
|
+
function sampleSelector(roleName) {
|
|
3859
|
+
const selectors = {
|
|
3860
|
+
patient: ".active",
|
|
3861
|
+
destination: "#output",
|
|
3862
|
+
source: "#input",
|
|
3863
|
+
target: "#target"
|
|
3864
|
+
};
|
|
3865
|
+
return { type: "selector", value: selectors[roleName] || `#${roleName}` };
|
|
3866
|
+
}
|
|
3867
|
+
function sampleLiteral(roleName) {
|
|
3868
|
+
const literals = {
|
|
3869
|
+
duration: "30s",
|
|
3870
|
+
interval: "5s",
|
|
3871
|
+
delay: "500ms",
|
|
3872
|
+
timeout: "10s",
|
|
3873
|
+
quantity: 5,
|
|
3874
|
+
limit: 10,
|
|
3875
|
+
style: "json",
|
|
3876
|
+
manner: "rolling",
|
|
3877
|
+
format: "html"
|
|
3878
|
+
};
|
|
3879
|
+
return { type: "literal", value: literals[roleName] || roleName };
|
|
3880
|
+
}
|
|
3881
|
+
function sampleExpression(roleName) {
|
|
3882
|
+
const expressions = {
|
|
3883
|
+
source: "/api/data",
|
|
3884
|
+
destination: "#output",
|
|
3885
|
+
patient: ".active",
|
|
3886
|
+
instrument: "toUpperCase",
|
|
3887
|
+
condition: "age > 18",
|
|
3888
|
+
style: "json",
|
|
3889
|
+
duration: "5s",
|
|
3890
|
+
url: "/api/users"
|
|
3891
|
+
};
|
|
3892
|
+
return { type: "expression", value: expressions[roleName] || `/api/${roleName}` };
|
|
3893
|
+
}
|
|
3894
|
+
function toSemanticValue(sample) {
|
|
3895
|
+
switch (sample.type) {
|
|
3896
|
+
case "selector":
|
|
3897
|
+
return {
|
|
3898
|
+
type: "selector",
|
|
3899
|
+
value: String(sample.value),
|
|
3900
|
+
selectorKind: detectSelectorKind2(String(sample.value))
|
|
3901
|
+
};
|
|
3902
|
+
case "literal":
|
|
1991
3903
|
return {
|
|
1992
|
-
|
|
1993
|
-
|
|
3904
|
+
type: "literal",
|
|
3905
|
+
value: sample.value,
|
|
3906
|
+
dataType: typeof sample.value === "number" ? "number" : typeof sample.value === "boolean" ? "boolean" : "string"
|
|
1994
3907
|
};
|
|
3908
|
+
case "reference":
|
|
3909
|
+
return { type: "reference", value: String(sample.value) };
|
|
3910
|
+
case "expression":
|
|
3911
|
+
return { type: "expression", value: String(sample.value), raw: String(sample.value) };
|
|
3912
|
+
case "flag":
|
|
3913
|
+
return { type: "flag", value: sample.value };
|
|
3914
|
+
default:
|
|
3915
|
+
return { type: "literal", value: String(sample.value), dataType: "string" };
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
function detectSelectorKind2(selector) {
|
|
3919
|
+
if (selector.startsWith("#")) return "id";
|
|
3920
|
+
if (selector.startsWith(".")) return "class";
|
|
3921
|
+
if (selector.startsWith("[")) return "attribute";
|
|
3922
|
+
return "complex";
|
|
3923
|
+
}
|
|
3924
|
+
function estimateTokens(text) {
|
|
3925
|
+
return Math.ceil(text.length / 4);
|
|
3926
|
+
}
|
|
3927
|
+
function truncateToTokenBudget(sections, maxTokens) {
|
|
3928
|
+
const result = [];
|
|
3929
|
+
let budget = maxTokens;
|
|
3930
|
+
for (const section of sections) {
|
|
3931
|
+
if (section.approximateTokens <= budget) {
|
|
3932
|
+
result.push(section);
|
|
3933
|
+
budget -= section.approximateTokens;
|
|
3934
|
+
} else {
|
|
3935
|
+
const charBudget = Math.max(budget * 4, 40);
|
|
3936
|
+
const truncatedContent = section.content.slice(0, charBudget) + "\n\n*(truncated)*";
|
|
3937
|
+
result.push({
|
|
3938
|
+
...section,
|
|
3939
|
+
content: truncatedContent,
|
|
3940
|
+
approximateTokens: estimateTokens(truncatedContent)
|
|
3941
|
+
});
|
|
3942
|
+
break;
|
|
1995
3943
|
}
|
|
1996
3944
|
}
|
|
1997
|
-
|
|
1998
|
-
|
|
3945
|
+
return result;
|
|
3946
|
+
}
|
|
3947
|
+
|
|
3948
|
+
// src/training/schema-synthesizer.ts
|
|
3949
|
+
function synthesizeFromSchemas(schemas, config) {
|
|
3950
|
+
const maxPerCommand = config.maxPairsPerCommand ?? 5;
|
|
3951
|
+
const minQuality = config.minQuality ?? 0.5;
|
|
3952
|
+
const shuffleRoles = config.shuffleRoles ?? true;
|
|
3953
|
+
const languages = config.languages ?? ["en"];
|
|
3954
|
+
const pairs = [];
|
|
3955
|
+
let idCounter = 0;
|
|
3956
|
+
for (const schema of schemas) {
|
|
3957
|
+
const combos = generateRoleCombinations2(schema);
|
|
3958
|
+
for (const language of languages) {
|
|
3959
|
+
let commandPairCount = 0;
|
|
3960
|
+
for (const combo of combos) {
|
|
3961
|
+
if (commandPairCount >= maxPerCommand) break;
|
|
3962
|
+
const pair = buildTrainingPair(
|
|
3963
|
+
schema,
|
|
3964
|
+
combo,
|
|
3965
|
+
language,
|
|
3966
|
+
config.domain,
|
|
3967
|
+
`${config.domain}-synth-${idCounter++}`
|
|
3968
|
+
);
|
|
3969
|
+
if (pair.quality >= minQuality) {
|
|
3970
|
+
pairs.push(pair);
|
|
3971
|
+
commandPairCount++;
|
|
3972
|
+
}
|
|
3973
|
+
if (shuffleRoles && combo.length >= 2 && commandPairCount < maxPerCommand) {
|
|
3974
|
+
const shuffled = [...combo].reverse();
|
|
3975
|
+
const shuffledPair = buildTrainingPair(
|
|
3976
|
+
schema,
|
|
3977
|
+
shuffled,
|
|
3978
|
+
language,
|
|
3979
|
+
config.domain,
|
|
3980
|
+
`${config.domain}-synth-${idCounter++}`
|
|
3981
|
+
);
|
|
3982
|
+
if (shuffledPair.quality >= minQuality) {
|
|
3983
|
+
pairs.push(shuffledPair);
|
|
3984
|
+
commandPairCount++;
|
|
3985
|
+
}
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3988
|
+
}
|
|
1999
3989
|
}
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
3990
|
+
const metadata = {
|
|
3991
|
+
domain: config.domain,
|
|
3992
|
+
commandCount: schemas.length,
|
|
3993
|
+
pairCount: pairs.length,
|
|
3994
|
+
bySource: { synthetic: pairs.length },
|
|
3995
|
+
languages
|
|
3996
|
+
};
|
|
3997
|
+
return { pairs, metadata };
|
|
3998
|
+
}
|
|
3999
|
+
function generateRoleCombinations2(schema) {
|
|
4000
|
+
const required = schema.roles.filter((r) => r.required);
|
|
4001
|
+
const optional = schema.roles.filter((r) => !r.required);
|
|
4002
|
+
const combos = [];
|
|
4003
|
+
if (required.length > 0) {
|
|
4004
|
+
combos.push(required);
|
|
4005
|
+
}
|
|
4006
|
+
for (const opt of optional) {
|
|
4007
|
+
combos.push([...required, opt]);
|
|
4008
|
+
}
|
|
4009
|
+
if (optional.length >= 2) {
|
|
4010
|
+
combos.push([...required, ...optional]);
|
|
4011
|
+
}
|
|
4012
|
+
if (combos.length === 0) {
|
|
4013
|
+
combos.push(schema.roles.slice());
|
|
4014
|
+
}
|
|
4015
|
+
return combos;
|
|
4016
|
+
}
|
|
4017
|
+
function buildTrainingPair(schema, roles, language, domain, id) {
|
|
4018
|
+
const sampledRoles = /* @__PURE__ */ new Map();
|
|
4019
|
+
for (const role of roles) {
|
|
4020
|
+
sampledRoles.set(role.role, sampleValue2(role));
|
|
4021
|
+
}
|
|
4022
|
+
const nodeRoles = /* @__PURE__ */ new Map();
|
|
4023
|
+
for (const [name, sample] of sampledRoles) {
|
|
4024
|
+
nodeRoles.set(name, toSemanticValue2(sample));
|
|
4025
|
+
}
|
|
4026
|
+
const node = createCommandNode(schema.action, nodeRoles);
|
|
4027
|
+
const explicit = renderExplicit(node);
|
|
4028
|
+
const jsonRoles = {};
|
|
4029
|
+
for (const [name, sample] of sampledRoles) {
|
|
4030
|
+
jsonRoles[name] = sample;
|
|
4031
|
+
}
|
|
4032
|
+
const json = { action: schema.action, roles: jsonRoles };
|
|
4033
|
+
const natural = buildNaturalForm(schema, sampledRoles, language);
|
|
4034
|
+
const requiredCount = schema.roles.filter((r) => r.required).length;
|
|
4035
|
+
const filledRequired = roles.filter((r) => r.required).length;
|
|
4036
|
+
const quality = requiredCount > 0 ? filledRequired / requiredCount : 0.8;
|
|
4037
|
+
return {
|
|
4038
|
+
id,
|
|
4039
|
+
natural,
|
|
4040
|
+
language,
|
|
4041
|
+
explicit,
|
|
4042
|
+
json,
|
|
4043
|
+
domain,
|
|
4044
|
+
action: schema.action,
|
|
4045
|
+
source: "synthetic",
|
|
4046
|
+
quality
|
|
4047
|
+
};
|
|
4048
|
+
}
|
|
4049
|
+
function buildNaturalForm(schema, roles, language) {
|
|
4050
|
+
const parts = [schema.action];
|
|
4051
|
+
for (const roleSpec of schema.roles) {
|
|
4052
|
+
const sample = roles.get(roleSpec.role);
|
|
4053
|
+
if (!sample) continue;
|
|
4054
|
+
const marker = roleSpec.markerOverride?.[language];
|
|
4055
|
+
if (marker) {
|
|
4056
|
+
parts.push(marker);
|
|
2006
4057
|
}
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
4058
|
+
parts.push(String(sample.value));
|
|
4059
|
+
}
|
|
4060
|
+
return parts.join(" ");
|
|
4061
|
+
}
|
|
4062
|
+
function sampleValue2(role) {
|
|
4063
|
+
const primaryType = role.expectedTypes[0] || "expression";
|
|
4064
|
+
switch (primaryType) {
|
|
4065
|
+
case "selector":
|
|
4066
|
+
return sampleSelector2(role.role);
|
|
4067
|
+
case "literal":
|
|
4068
|
+
return sampleLiteral2(role.role);
|
|
4069
|
+
case "reference":
|
|
4070
|
+
return { type: "reference", value: "me" };
|
|
4071
|
+
case "expression":
|
|
4072
|
+
return sampleExpression2(role.role);
|
|
4073
|
+
case "flag":
|
|
4074
|
+
return { type: "flag", value: true };
|
|
4075
|
+
default:
|
|
4076
|
+
return { type: "literal", value: "example" };
|
|
4077
|
+
}
|
|
4078
|
+
}
|
|
4079
|
+
function sampleSelector2(roleName) {
|
|
4080
|
+
const selectors = {
|
|
4081
|
+
patient: ".active",
|
|
4082
|
+
destination: "#output",
|
|
4083
|
+
source: "#input",
|
|
4084
|
+
target: "#target"
|
|
4085
|
+
};
|
|
4086
|
+
return { type: "selector", value: selectors[roleName] || `#${roleName}` };
|
|
4087
|
+
}
|
|
4088
|
+
function sampleLiteral2(roleName) {
|
|
4089
|
+
const literals = {
|
|
4090
|
+
duration: "30s",
|
|
4091
|
+
interval: "5s",
|
|
4092
|
+
delay: "500ms",
|
|
4093
|
+
timeout: "10s",
|
|
4094
|
+
quantity: 5,
|
|
4095
|
+
limit: 10,
|
|
4096
|
+
style: "json",
|
|
4097
|
+
manner: "rolling",
|
|
4098
|
+
format: "html"
|
|
4099
|
+
};
|
|
4100
|
+
return { type: "literal", value: literals[roleName] || roleName };
|
|
4101
|
+
}
|
|
4102
|
+
function sampleExpression2(roleName) {
|
|
4103
|
+
const expressions = {
|
|
4104
|
+
source: "/api/data",
|
|
4105
|
+
destination: "#output",
|
|
4106
|
+
patient: ".active",
|
|
4107
|
+
instrument: "toUpperCase",
|
|
4108
|
+
condition: "age > 18",
|
|
4109
|
+
style: "json",
|
|
4110
|
+
duration: "5s",
|
|
4111
|
+
url: "/api/users"
|
|
4112
|
+
};
|
|
4113
|
+
return { type: "expression", value: expressions[roleName] || `/api/${roleName}` };
|
|
4114
|
+
}
|
|
4115
|
+
function toSemanticValue2(sample) {
|
|
4116
|
+
switch (sample.type) {
|
|
4117
|
+
case "selector":
|
|
2010
4118
|
return {
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
metadata: {
|
|
2015
|
-
parser: "semantic",
|
|
2016
|
-
confidence: result.confidence
|
|
2017
|
-
}
|
|
4119
|
+
type: "selector",
|
|
4120
|
+
value: String(sample.value),
|
|
4121
|
+
selectorKind: detectSelectorKind3(String(sample.value))
|
|
2018
4122
|
};
|
|
2019
|
-
|
|
4123
|
+
case "literal":
|
|
2020
4124
|
return {
|
|
2021
|
-
|
|
2022
|
-
|
|
4125
|
+
type: "literal",
|
|
4126
|
+
value: sample.value,
|
|
4127
|
+
dataType: typeof sample.value === "number" ? "number" : typeof sample.value === "boolean" ? "boolean" : "string"
|
|
2023
4128
|
};
|
|
4129
|
+
case "reference":
|
|
4130
|
+
return { type: "reference", value: String(sample.value) };
|
|
4131
|
+
case "expression":
|
|
4132
|
+
return { type: "expression", value: String(sample.value), raw: String(sample.value) };
|
|
4133
|
+
case "flag":
|
|
4134
|
+
return { type: "flag", value: sample.value };
|
|
4135
|
+
default:
|
|
4136
|
+
return { type: "literal", value: String(sample.value), dataType: "string" };
|
|
4137
|
+
}
|
|
4138
|
+
}
|
|
4139
|
+
function detectSelectorKind3(selector) {
|
|
4140
|
+
if (selector.startsWith("#")) return "id";
|
|
4141
|
+
if (selector.startsWith(".")) return "class";
|
|
4142
|
+
if (selector.startsWith("[")) return "attribute";
|
|
4143
|
+
return "complex";
|
|
4144
|
+
}
|
|
4145
|
+
|
|
4146
|
+
// src/feedback/feedback-formatter.ts
|
|
4147
|
+
function buildFeedback(input, inputFormat, diagnostics, schemaLookup, action) {
|
|
4148
|
+
const hasErrors = diagnostics.some((d) => d.severity === "error");
|
|
4149
|
+
const feedbackDiagnostics = diagnostics.map((d) => toFeedbackDiagnostic(d));
|
|
4150
|
+
const hints = generateHints(feedbackDiagnostics, schemaLookup, action);
|
|
4151
|
+
let schemaInfo;
|
|
4152
|
+
let correctedExample;
|
|
4153
|
+
if (action && schemaLookup) {
|
|
4154
|
+
const schema = schemaLookup.getSchema(action);
|
|
4155
|
+
if (schema) {
|
|
4156
|
+
schemaInfo = extractSchemaInfo(schema);
|
|
4157
|
+
if (hasErrors) {
|
|
4158
|
+
correctedExample = generateCorrectedExample(schema);
|
|
4159
|
+
}
|
|
2024
4160
|
}
|
|
2025
4161
|
}
|
|
2026
|
-
|
|
2027
|
-
|
|
4162
|
+
return {
|
|
4163
|
+
accepted: !hasErrors,
|
|
4164
|
+
input: { format: inputFormat, text: input },
|
|
4165
|
+
diagnostics: feedbackDiagnostics,
|
|
4166
|
+
hints,
|
|
4167
|
+
...schemaInfo && { schema: schemaInfo },
|
|
4168
|
+
...correctedExample && { correctedExample }
|
|
4169
|
+
};
|
|
4170
|
+
}
|
|
4171
|
+
function toFeedbackDiagnostic(d) {
|
|
4172
|
+
const fixType = classifyFixType(d);
|
|
4173
|
+
return {
|
|
4174
|
+
severity: d.severity,
|
|
4175
|
+
code: d.code || "unknown",
|
|
4176
|
+
message: d.message,
|
|
4177
|
+
...d.suggestions?.[0] && { suggestion: d.suggestions[0] },
|
|
4178
|
+
...fixType && { fixType }
|
|
4179
|
+
};
|
|
4180
|
+
}
|
|
4181
|
+
function classifyFixType(d) {
|
|
4182
|
+
const code = (d.code || "").toLowerCase();
|
|
4183
|
+
const msg = d.message.toLowerCase();
|
|
4184
|
+
if (code.includes("missing-role") || code.includes("missing_role") || msg.includes("required role") || msg.includes("missing role")) {
|
|
4185
|
+
return "missing_role";
|
|
2028
4186
|
}
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
4187
|
+
if (code.includes("invalid-type") || code.includes("invalid_type") || code.includes("type-mismatch") || msg.includes("invalid type") || msg.includes("expected type")) {
|
|
4188
|
+
return "invalid_type";
|
|
4189
|
+
}
|
|
4190
|
+
if (code.includes("unknown-command") || code.includes("unknown_command") || msg.includes("unknown command") || msg.includes("not recognized") && msg.includes("command")) {
|
|
4191
|
+
return "unknown_command";
|
|
4192
|
+
}
|
|
4193
|
+
if (code.includes("unknown-role") || code.includes("unknown_role") || msg.includes("unknown role") || msg.includes("not recognized") && msg.includes("role")) {
|
|
4194
|
+
return "unknown_role";
|
|
4195
|
+
}
|
|
4196
|
+
if (code.includes("parse") || code.includes("syntax") || msg.includes("syntax error") || msg.includes("parse error") || msg.includes("unexpected")) {
|
|
4197
|
+
return "syntax_error";
|
|
4198
|
+
}
|
|
4199
|
+
return void 0;
|
|
4200
|
+
}
|
|
4201
|
+
function generateHints(diagnostics, schemaLookup, action) {
|
|
4202
|
+
const hints = [];
|
|
4203
|
+
const schema = action && schemaLookup ? schemaLookup.getSchema(action) : void 0;
|
|
4204
|
+
for (const d of diagnostics) {
|
|
4205
|
+
if (d.severity !== "error") continue;
|
|
4206
|
+
switch (d.fixType) {
|
|
4207
|
+
case "missing_role": {
|
|
4208
|
+
if (schema) {
|
|
4209
|
+
const required = schema.roles.filter((r) => r.required).map((r) => `'${r.role}' (${r.description}, type: ${r.expectedTypes.join("|")})`).join(", ");
|
|
4210
|
+
hints.push(`Required roles for '${action}': ${required}`);
|
|
4211
|
+
} else {
|
|
4212
|
+
hints.push("Check that all required roles are present.");
|
|
4213
|
+
}
|
|
4214
|
+
break;
|
|
4215
|
+
}
|
|
4216
|
+
case "unknown_command": {
|
|
4217
|
+
hints.push(
|
|
4218
|
+
`Command not recognized. Ensure you're using a valid command from the domain schema.`
|
|
4219
|
+
);
|
|
4220
|
+
break;
|
|
4221
|
+
}
|
|
4222
|
+
case "unknown_role": {
|
|
4223
|
+
if (schema) {
|
|
4224
|
+
const validRoles = schema.roles.map((r) => `'${r.role}'`).join(", ");
|
|
4225
|
+
hints.push(`Valid roles for '${action}': ${validRoles}`);
|
|
4226
|
+
} else {
|
|
4227
|
+
hints.push("Use only roles defined in the command schema.");
|
|
4228
|
+
}
|
|
4229
|
+
break;
|
|
4230
|
+
}
|
|
4231
|
+
case "invalid_type": {
|
|
4232
|
+
hints.push(
|
|
4233
|
+
"Check value types: selectors start with #.[@*, strings use quotes, numbers are plain digits."
|
|
4234
|
+
);
|
|
4235
|
+
break;
|
|
4236
|
+
}
|
|
4237
|
+
case "syntax_error": {
|
|
4238
|
+
hints.push("Ensure bracket syntax: [command role:value ...]. No spaces around the colon.");
|
|
4239
|
+
break;
|
|
4240
|
+
}
|
|
4241
|
+
default: {
|
|
4242
|
+
if (d.suggestion) {
|
|
4243
|
+
hints.push(d.suggestion);
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
2036
4246
|
}
|
|
2037
|
-
translations[lang.code] = keywords;
|
|
2038
4247
|
}
|
|
2039
|
-
return new
|
|
4248
|
+
return [...new Set(hints)];
|
|
2040
4249
|
}
|
|
2041
|
-
function
|
|
2042
|
-
const
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
4250
|
+
function extractSchemaInfo(schema) {
|
|
4251
|
+
const requiredRoles = [];
|
|
4252
|
+
const optionalRoles = [];
|
|
4253
|
+
const roleDescriptions = {};
|
|
4254
|
+
for (const role of schema.roles) {
|
|
4255
|
+
if (role.required) {
|
|
4256
|
+
requiredRoles.push(role.role);
|
|
4257
|
+
} else {
|
|
4258
|
+
optionalRoles.push(role.role);
|
|
2046
4259
|
}
|
|
4260
|
+
roleDescriptions[role.role] = role.description;
|
|
2047
4261
|
}
|
|
2048
|
-
return
|
|
4262
|
+
return {
|
|
4263
|
+
action: schema.action,
|
|
4264
|
+
requiredRoles,
|
|
4265
|
+
optionalRoles,
|
|
4266
|
+
roleDescriptions
|
|
4267
|
+
};
|
|
2049
4268
|
}
|
|
2050
|
-
function
|
|
2051
|
-
const
|
|
2052
|
-
const
|
|
2053
|
-
const
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
4269
|
+
function generateCorrectedExample(schema) {
|
|
4270
|
+
const roles = /* @__PURE__ */ new Map();
|
|
4271
|
+
const jsonRoles = {};
|
|
4272
|
+
for (const role of schema.roles) {
|
|
4273
|
+
if (!role.required) continue;
|
|
4274
|
+
const sample = sampleDefaultValue(role.role, role.expectedTypes[0] || "expression");
|
|
4275
|
+
roles.set(role.role, toSemanticValue3(sample));
|
|
4276
|
+
jsonRoles[role.role] = sample;
|
|
4277
|
+
}
|
|
4278
|
+
const node = createCommandNode(schema.action, roles);
|
|
4279
|
+
const explicit = renderExplicit(node);
|
|
4280
|
+
const json = { action: schema.action, roles: jsonRoles };
|
|
4281
|
+
return { explicit, json };
|
|
4282
|
+
}
|
|
4283
|
+
function sampleDefaultValue(roleName, expectedType) {
|
|
4284
|
+
switch (expectedType) {
|
|
4285
|
+
case "selector":
|
|
4286
|
+
return { type: "selector", value: `#${roleName}` };
|
|
4287
|
+
case "expression":
|
|
4288
|
+
return { type: "expression", value: `<${roleName}-value>` };
|
|
4289
|
+
case "literal":
|
|
4290
|
+
return { type: "literal", value: `<${roleName}-value>` };
|
|
4291
|
+
case "reference":
|
|
4292
|
+
return { type: "reference", value: "me" };
|
|
4293
|
+
default:
|
|
4294
|
+
return { type: "expression", value: `<${roleName}-value>` };
|
|
4295
|
+
}
|
|
4296
|
+
}
|
|
4297
|
+
function toSemanticValue3(sample) {
|
|
4298
|
+
switch (sample.type) {
|
|
4299
|
+
case "selector":
|
|
4300
|
+
return { type: "selector", value: sample.value, selectorKind: "id" };
|
|
4301
|
+
case "expression":
|
|
4302
|
+
return { type: "expression", value: sample.value, raw: sample.value };
|
|
4303
|
+
case "literal":
|
|
4304
|
+
return { type: "literal", value: sample.value, dataType: "string" };
|
|
4305
|
+
case "reference":
|
|
4306
|
+
return { type: "reference", value: sample.value };
|
|
4307
|
+
default:
|
|
4308
|
+
return { type: "literal", value: sample.value, dataType: "string" };
|
|
4309
|
+
}
|
|
2059
4310
|
}
|
|
2060
4311
|
|
|
2061
4312
|
// src/api/domain-registry.ts
|
|
@@ -2075,6 +4326,17 @@ var DomainRegistry = class {
|
|
|
2075
4326
|
}
|
|
2076
4327
|
this.descriptors.set(descriptor.name, descriptor);
|
|
2077
4328
|
}
|
|
4329
|
+
/**
|
|
4330
|
+
* Attach schemas to an already-registered domain.
|
|
4331
|
+
* Useful for lazy loading schemas after initial registration.
|
|
4332
|
+
*/
|
|
4333
|
+
setSchemas(name, schemas) {
|
|
4334
|
+
const existing = this.descriptors.get(name);
|
|
4335
|
+
if (!existing) {
|
|
4336
|
+
throw new Error(`Domain not registered: ${name}`);
|
|
4337
|
+
}
|
|
4338
|
+
this.descriptors.set(name, { ...existing, schemas });
|
|
4339
|
+
}
|
|
2078
4340
|
/**
|
|
2079
4341
|
* Get all registered domain names.
|
|
2080
4342
|
*/
|
|
@@ -2144,6 +4406,71 @@ var DomainRegistry = class {
|
|
|
2144
4406
|
return this.getDSL(descriptor);
|
|
2145
4407
|
}
|
|
2146
4408
|
// ---------------------------------------------------------------------------
|
|
4409
|
+
// LLM ↔ LSE Convenience Methods
|
|
4410
|
+
// ---------------------------------------------------------------------------
|
|
4411
|
+
/**
|
|
4412
|
+
* Generate an LLM system prompt for a domain from its schemas.
|
|
4413
|
+
* Returns null if the domain is not registered or has no schemas.
|
|
4414
|
+
*/
|
|
4415
|
+
generatePrompt(domain, config) {
|
|
4416
|
+
const descriptor = this.descriptors.get(domain);
|
|
4417
|
+
if (!descriptor?.schemas || descriptor.schemas.length === 0) return null;
|
|
4418
|
+
return generatePrompt({
|
|
4419
|
+
domain,
|
|
4420
|
+
description: descriptor.description,
|
|
4421
|
+
schemas: descriptor.schemas,
|
|
4422
|
+
...config
|
|
4423
|
+
});
|
|
4424
|
+
}
|
|
4425
|
+
/**
|
|
4426
|
+
* Synthesize training data for a domain from its schemas.
|
|
4427
|
+
* Returns null if the domain is not registered or has no schemas.
|
|
4428
|
+
*/
|
|
4429
|
+
generateTrainingData(domain, config) {
|
|
4430
|
+
const descriptor = this.descriptors.get(domain);
|
|
4431
|
+
if (!descriptor?.schemas || descriptor.schemas.length === 0) return null;
|
|
4432
|
+
return synthesizeFromSchemas(descriptor.schemas, {
|
|
4433
|
+
domain,
|
|
4434
|
+
...config
|
|
4435
|
+
});
|
|
4436
|
+
}
|
|
4437
|
+
/**
|
|
4438
|
+
* Build structured feedback for a failed LSE input in a domain.
|
|
4439
|
+
* Returns null if the domain is not registered or has no schemas.
|
|
4440
|
+
*/
|
|
4441
|
+
buildFeedback(domain, input, inputFormat, diagnostics) {
|
|
4442
|
+
const descriptor = this.descriptors.get(domain);
|
|
4443
|
+
if (!descriptor?.schemas || descriptor.schemas.length === 0) return null;
|
|
4444
|
+
const schemaLookup = this.createSchemaLookup(descriptor.schemas);
|
|
4445
|
+
let action;
|
|
4446
|
+
try {
|
|
4447
|
+
if (inputFormat === "explicit") {
|
|
4448
|
+
const node = parseExplicit(input, { schemaLookup });
|
|
4449
|
+
action = node.action;
|
|
4450
|
+
}
|
|
4451
|
+
} catch {
|
|
4452
|
+
}
|
|
4453
|
+
return buildFeedback(input, inputFormat, diagnostics, schemaLookup, action);
|
|
4454
|
+
}
|
|
4455
|
+
/**
|
|
4456
|
+
* Get schemas for a domain (if available).
|
|
4457
|
+
*/
|
|
4458
|
+
getSchemas(domain) {
|
|
4459
|
+
const descriptor = this.descriptors.get(domain);
|
|
4460
|
+
if (!descriptor?.schemas) return null;
|
|
4461
|
+
return descriptor.schemas;
|
|
4462
|
+
}
|
|
4463
|
+
/**
|
|
4464
|
+
* Create a SchemaLookup from a domain's schemas.
|
|
4465
|
+
*/
|
|
4466
|
+
createSchemaLookup(schemas) {
|
|
4467
|
+
const map = /* @__PURE__ */ new Map();
|
|
4468
|
+
for (const s of schemas) {
|
|
4469
|
+
map.set(s.action, s);
|
|
4470
|
+
}
|
|
4471
|
+
return { getSchema: (action) => map.get(action) };
|
|
4472
|
+
}
|
|
4473
|
+
// ---------------------------------------------------------------------------
|
|
2147
4474
|
// Private: DSL/renderer lifecycle
|
|
2148
4475
|
// ---------------------------------------------------------------------------
|
|
2149
4476
|
async getDSL(descriptor) {
|
|
@@ -2243,7 +4570,7 @@ function generateToolDefinitions(desc) {
|
|
|
2243
4570
|
case "parse":
|
|
2244
4571
|
tools.push({
|
|
2245
4572
|
name: `parse_${desc.name}`,
|
|
2246
|
-
description: `Parse a ${desc.description} input into a semantic representation. Supports: ${langList}.`,
|
|
4573
|
+
description: `Parse a ${desc.description} input into a semantic representation (action, roles, confidence). Supports: ${langList}.`,
|
|
2247
4574
|
inputSchema: {
|
|
2248
4575
|
type: "object",
|
|
2249
4576
|
properties: {
|
|
@@ -2264,7 +4591,7 @@ function generateToolDefinitions(desc) {
|
|
|
2264
4591
|
case "compile":
|
|
2265
4592
|
tools.push({
|
|
2266
4593
|
name: `compile_${desc.name}`,
|
|
2267
|
-
description: `Compile a ${desc.description} input to target code. Supports: ${langList}.`,
|
|
4594
|
+
description: `Compile a ${desc.description} input to ${desc.outputDescription ?? "target code"}. Supports: ${langList}.`,
|
|
2268
4595
|
inputSchema: {
|
|
2269
4596
|
type: "object",
|
|
2270
4597
|
properties: {
|
|
@@ -2285,7 +4612,7 @@ function generateToolDefinitions(desc) {
|
|
|
2285
4612
|
case "validate":
|
|
2286
4613
|
tools.push({
|
|
2287
4614
|
name: `validate_${desc.name}`,
|
|
2288
|
-
description: `
|
|
4615
|
+
description: `Check ${desc.description} syntax for errors. Returns {valid, errors[]}. Supports: ${langList}.`,
|
|
2289
4616
|
inputSchema: {
|
|
2290
4617
|
type: "object",
|
|
2291
4618
|
properties: {
|
|
@@ -2306,7 +4633,7 @@ function generateToolDefinitions(desc) {
|
|
|
2306
4633
|
case "translate":
|
|
2307
4634
|
tools.push({
|
|
2308
4635
|
name: `translate_${desc.name}`,
|
|
2309
|
-
description: `Translate ${desc.description} input between natural languages
|
|
4636
|
+
description: `Translate ${desc.description} input between natural languages (requires 'from' and 'to' codes). Supports: ${langList}.`,
|
|
2310
4637
|
inputSchema: {
|
|
2311
4638
|
type: "object",
|
|
2312
4639
|
properties: {
|
|
@@ -2372,9 +4699,9 @@ var CrossDomainDispatcher = class {
|
|
|
2372
4699
|
if (domainNames.length === 0) return null;
|
|
2373
4700
|
const candidates = [];
|
|
2374
4701
|
for (const name of domainNames) {
|
|
2375
|
-
const dsl = await this.registry.getDSLForDomain(name);
|
|
2376
|
-
if (!dsl) continue;
|
|
2377
4702
|
try {
|
|
4703
|
+
const dsl = await this.registry.getDSLForDomain(name);
|
|
4704
|
+
if (!dsl) continue;
|
|
2378
4705
|
const { node, confidence } = dsl.parseWithConfidence(input, language);
|
|
2379
4706
|
if (confidence >= this.minConfidence) {
|
|
2380
4707
|
candidates.push({ domain: name, node, confidence, dsl });
|
|
@@ -2383,10 +4710,16 @@ var CrossDomainDispatcher = class {
|
|
|
2383
4710
|
}
|
|
2384
4711
|
}
|
|
2385
4712
|
if (candidates.length === 0) return null;
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
4713
|
+
if (this.priority.length > 0) {
|
|
4714
|
+
candidates.sort((a, b) => {
|
|
4715
|
+
const pa = this.getPriorityIndex(a.domain);
|
|
4716
|
+
const pb = this.getPriorityIndex(b.domain);
|
|
4717
|
+
if (pa !== pb) return pa - pb;
|
|
4718
|
+
return b.confidence - a.confidence;
|
|
4719
|
+
});
|
|
4720
|
+
} else {
|
|
4721
|
+
candidates.sort((a, b) => b.confidence - a.confidence);
|
|
4722
|
+
}
|
|
2390
4723
|
return candidates[0];
|
|
2391
4724
|
}
|
|
2392
4725
|
/**
|
|
@@ -2430,6 +4763,40 @@ var CrossDomainDispatcher = class {
|
|
|
2430
4763
|
const result = detected.dsl.compile(input, language);
|
|
2431
4764
|
return { ...result, domain: detected.domain };
|
|
2432
4765
|
}
|
|
4766
|
+
/**
|
|
4767
|
+
* Compile multi-line input where each line may belong to a different domain.
|
|
4768
|
+
* Detects the domain for each line and compiles it, returning per-line results.
|
|
4769
|
+
* Empty lines and comment lines are skipped.
|
|
4770
|
+
*/
|
|
4771
|
+
async compileComposite(input, language = "en") {
|
|
4772
|
+
const lines = input.split("\n");
|
|
4773
|
+
const statements = [];
|
|
4774
|
+
const errors = [];
|
|
4775
|
+
for (let i = 0; i < lines.length; i++) {
|
|
4776
|
+
const line = lines[i];
|
|
4777
|
+
if (isSkippableLine(line)) continue;
|
|
4778
|
+
const trimmed = line.trim();
|
|
4779
|
+
const detected = await this.detect(trimmed, language);
|
|
4780
|
+
if (detected) {
|
|
4781
|
+
const result = detected.dsl.compile(trimmed, language);
|
|
4782
|
+
statements.push({
|
|
4783
|
+
line: i + 1,
|
|
4784
|
+
input: trimmed,
|
|
4785
|
+
domain: detected.domain,
|
|
4786
|
+
ok: result.ok,
|
|
4787
|
+
code: result.code,
|
|
4788
|
+
errors: result.errors
|
|
4789
|
+
});
|
|
4790
|
+
} else {
|
|
4791
|
+
errors.push({
|
|
4792
|
+
line: i + 1,
|
|
4793
|
+
input: trimmed,
|
|
4794
|
+
message: "No domain matched this input"
|
|
4795
|
+
});
|
|
4796
|
+
}
|
|
4797
|
+
}
|
|
4798
|
+
return { statements, errors };
|
|
4799
|
+
}
|
|
2433
4800
|
/**
|
|
2434
4801
|
* Validate input against all domains, returning the best match.
|
|
2435
4802
|
*/
|
|
@@ -2711,6 +5078,30 @@ ${body}
|
|
|
2711
5078
|
}
|
|
2712
5079
|
};
|
|
2713
5080
|
|
|
5081
|
+
// src/aot/registry-bridge.ts
|
|
5082
|
+
async function registryToAOTBackends(registry) {
|
|
5083
|
+
const backends = [];
|
|
5084
|
+
for (const name of registry.getDomainNames()) {
|
|
5085
|
+
const descriptor = registry.getDescriptor(name);
|
|
5086
|
+
if (!descriptor?.scanConfig) continue;
|
|
5087
|
+
const dsl = await registry.getDSLForDomain(name);
|
|
5088
|
+
if (!dsl) continue;
|
|
5089
|
+
const scanConfig = {
|
|
5090
|
+
domain: name,
|
|
5091
|
+
attributes: [...descriptor.scanConfig.attributes],
|
|
5092
|
+
...descriptor.scanConfig.scriptTypes ? { scriptTypes: [...descriptor.scanConfig.scriptTypes] } : {},
|
|
5093
|
+
defaultLanguage: descriptor.scanConfig.defaultLanguage ?? "en"
|
|
5094
|
+
};
|
|
5095
|
+
const codeGenerator = {
|
|
5096
|
+
generate(node) {
|
|
5097
|
+
return JSON.stringify({ action: node.action, roles: Object.fromEntries(node.roles) });
|
|
5098
|
+
}
|
|
5099
|
+
};
|
|
5100
|
+
backends.push({ domain: name, dsl, codeGenerator, scanConfig });
|
|
5101
|
+
}
|
|
5102
|
+
return backends;
|
|
5103
|
+
}
|
|
5104
|
+
|
|
2714
5105
|
// src/schema/command-schema.ts
|
|
2715
5106
|
function defineCommand(schema) {
|
|
2716
5107
|
return {
|
|
@@ -4002,6 +6393,163 @@ function normalized(stem, confidence, metadata) {
|
|
|
4002
6393
|
return { stem, confidence };
|
|
4003
6394
|
}
|
|
4004
6395
|
|
|
6396
|
+
// src/training/jsonl-writer.ts
|
|
6397
|
+
function toJSONL(pairs) {
|
|
6398
|
+
return pairs.map((pair) => JSON.stringify(toJSONLRow(pair))).join("\n");
|
|
6399
|
+
}
|
|
6400
|
+
function toJSONLRow(pair) {
|
|
6401
|
+
return {
|
|
6402
|
+
id: pair.id,
|
|
6403
|
+
prompt: pair.natural,
|
|
6404
|
+
completion: pair.explicit,
|
|
6405
|
+
metadata: {
|
|
6406
|
+
domain: pair.domain,
|
|
6407
|
+
action: pair.action,
|
|
6408
|
+
language: pair.language,
|
|
6409
|
+
source: pair.source,
|
|
6410
|
+
quality: pair.quality,
|
|
6411
|
+
...pair.confidence !== void 0 && { confidence: pair.confidence }
|
|
6412
|
+
}
|
|
6413
|
+
};
|
|
6414
|
+
}
|
|
6415
|
+
function parseJSONL(text) {
|
|
6416
|
+
return text.split("\n").filter((line) => line.trim().length > 0).map((line) => JSON.parse(line));
|
|
6417
|
+
}
|
|
6418
|
+
|
|
6419
|
+
// src/feedback/confidence-gate.ts
|
|
6420
|
+
function needsDisambiguation(confidence, low = 0.5, high = 0.7) {
|
|
6421
|
+
return confidence >= low && confidence < high;
|
|
6422
|
+
}
|
|
6423
|
+
function buildDisambiguation(input, candidates) {
|
|
6424
|
+
const sortedCandidates = [...candidates].sort((a, b) => b.confidence - a.confidence);
|
|
6425
|
+
const disambiguationCandidates = sortedCandidates.map((c, i) => ({
|
|
6426
|
+
action: c.action,
|
|
6427
|
+
confidence: c.confidence,
|
|
6428
|
+
explicit: renderExplicit(c.node),
|
|
6429
|
+
description: c.description || `${c.action} (option ${String.fromCharCode(97 + i)})`
|
|
6430
|
+
}));
|
|
6431
|
+
const optionLines = disambiguationCandidates.map(
|
|
6432
|
+
(c, i) => ` (${String.fromCharCode(97 + i)}) ${c.explicit} \u2014 ${c.description} (conf: ${c.confidence.toFixed(2)})`
|
|
6433
|
+
);
|
|
6434
|
+
const question = [
|
|
6435
|
+
`Your input parsed with confidence ${sortedCandidates[0]?.confidence.toFixed(2) ?? "?"}.`,
|
|
6436
|
+
...optionLines,
|
|
6437
|
+
"Which did you mean?"
|
|
6438
|
+
].join("\n");
|
|
6439
|
+
return {
|
|
6440
|
+
input,
|
|
6441
|
+
candidates: disambiguationCandidates,
|
|
6442
|
+
question
|
|
6443
|
+
};
|
|
6444
|
+
}
|
|
6445
|
+
|
|
6446
|
+
// src/feedback/pattern-tracker.ts
|
|
6447
|
+
var PatternTracker = class {
|
|
6448
|
+
constructor(maxEvents = 1e3) {
|
|
6449
|
+
this.events = [];
|
|
6450
|
+
this.maxEvents = maxEvents;
|
|
6451
|
+
}
|
|
6452
|
+
/**
|
|
6453
|
+
* Record a pattern event.
|
|
6454
|
+
*/
|
|
6455
|
+
record(event) {
|
|
6456
|
+
this.events.push(event);
|
|
6457
|
+
if (this.events.length > this.maxEvents) {
|
|
6458
|
+
this.events = this.events.slice(this.events.length - this.maxEvents);
|
|
6459
|
+
}
|
|
6460
|
+
}
|
|
6461
|
+
/**
|
|
6462
|
+
* Get hit rate by command action.
|
|
6463
|
+
*/
|
|
6464
|
+
getHitRateByCommand() {
|
|
6465
|
+
return this.computeHitRate((e) => e.action);
|
|
6466
|
+
}
|
|
6467
|
+
/**
|
|
6468
|
+
* Get hit rate by language.
|
|
6469
|
+
*/
|
|
6470
|
+
getHitRateByLanguage() {
|
|
6471
|
+
return this.computeHitRate((e) => e.language);
|
|
6472
|
+
}
|
|
6473
|
+
/**
|
|
6474
|
+
* Get the most frequent failure diagnostic codes.
|
|
6475
|
+
*/
|
|
6476
|
+
getTopFailures(limit = 10) {
|
|
6477
|
+
const codeMap = /* @__PURE__ */ new Map();
|
|
6478
|
+
for (const event of this.events) {
|
|
6479
|
+
if (event.outcome !== "rejected" || !event.diagnosticCodes) continue;
|
|
6480
|
+
for (const code of event.diagnosticCodes) {
|
|
6481
|
+
const entry = codeMap.get(code) || { count: 0, actions: /* @__PURE__ */ new Set() };
|
|
6482
|
+
entry.count++;
|
|
6483
|
+
entry.actions.add(event.action);
|
|
6484
|
+
codeMap.set(code, entry);
|
|
6485
|
+
}
|
|
6486
|
+
}
|
|
6487
|
+
return Array.from(codeMap.entries()).map(([code, { count, actions }]) => ({
|
|
6488
|
+
code,
|
|
6489
|
+
count,
|
|
6490
|
+
actions: Array.from(actions)
|
|
6491
|
+
})).sort((a, b) => b.count - a.count).slice(0, limit);
|
|
6492
|
+
}
|
|
6493
|
+
/**
|
|
6494
|
+
* Get a complete summary of tracking data.
|
|
6495
|
+
*/
|
|
6496
|
+
getSummary() {
|
|
6497
|
+
const byOutcome = {};
|
|
6498
|
+
for (const event of this.events) {
|
|
6499
|
+
byOutcome[event.outcome] = (byOutcome[event.outcome] || 0) + 1;
|
|
6500
|
+
}
|
|
6501
|
+
return {
|
|
6502
|
+
totalEvents: this.events.length,
|
|
6503
|
+
byOutcome,
|
|
6504
|
+
byCommand: this.getHitRateByCommand(),
|
|
6505
|
+
byLanguage: this.getHitRateByLanguage(),
|
|
6506
|
+
topFailures: this.getTopFailures()
|
|
6507
|
+
};
|
|
6508
|
+
}
|
|
6509
|
+
/**
|
|
6510
|
+
* Export all events as JSONL.
|
|
6511
|
+
*/
|
|
6512
|
+
exportJSONL() {
|
|
6513
|
+
return this.events.map((e) => JSON.stringify(e)).join("\n");
|
|
6514
|
+
}
|
|
6515
|
+
/**
|
|
6516
|
+
* Get the number of recorded events.
|
|
6517
|
+
*/
|
|
6518
|
+
get size() {
|
|
6519
|
+
return this.events.length;
|
|
6520
|
+
}
|
|
6521
|
+
/**
|
|
6522
|
+
* Clear all events.
|
|
6523
|
+
*/
|
|
6524
|
+
clear() {
|
|
6525
|
+
this.events = [];
|
|
6526
|
+
}
|
|
6527
|
+
// ---------------------------------------------------------------------------
|
|
6528
|
+
// Private
|
|
6529
|
+
// ---------------------------------------------------------------------------
|
|
6530
|
+
computeHitRate(keyFn) {
|
|
6531
|
+
const map = /* @__PURE__ */ new Map();
|
|
6532
|
+
for (const event of this.events) {
|
|
6533
|
+
const key = keyFn(event);
|
|
6534
|
+
const entry = map.get(key) || { attempts: 0, successes: 0 };
|
|
6535
|
+
entry.attempts++;
|
|
6536
|
+
if (event.outcome === "accepted") {
|
|
6537
|
+
entry.successes++;
|
|
6538
|
+
}
|
|
6539
|
+
map.set(key, entry);
|
|
6540
|
+
}
|
|
6541
|
+
const result = {};
|
|
6542
|
+
for (const [key, { attempts, successes }] of map) {
|
|
6543
|
+
result[key] = {
|
|
6544
|
+
attempts,
|
|
6545
|
+
successes,
|
|
6546
|
+
rate: attempts > 0 ? successes / attempts : 0
|
|
6547
|
+
};
|
|
6548
|
+
}
|
|
6549
|
+
return result;
|
|
6550
|
+
}
|
|
6551
|
+
};
|
|
6552
|
+
|
|
4005
6553
|
// src/parsing/multi-statement.ts
|
|
4006
6554
|
function createMultiStatementParser(dsl, config) {
|
|
4007
6555
|
return new MultiStatementParserImpl(dsl, config);
|
|
@@ -4245,6 +6793,7 @@ export {
|
|
|
4245
6793
|
CrossDomainDispatcher,
|
|
4246
6794
|
DEFAULT_OPERATORS,
|
|
4247
6795
|
DEFAULT_PUNCTUATION,
|
|
6796
|
+
DEFAULT_REFERENCES,
|
|
4248
6797
|
DomainAwareScanner,
|
|
4249
6798
|
DomainRegistry,
|
|
4250
6799
|
GrammarTransformer,
|
|
@@ -4256,24 +6805,31 @@ export {
|
|
|
4256
6805
|
NumberExtractor,
|
|
4257
6806
|
OperatorExtractor,
|
|
4258
6807
|
PatternMatcher,
|
|
6808
|
+
PatternTracker,
|
|
4259
6809
|
PunctuationExtractor,
|
|
4260
6810
|
StringLiteralExtractor,
|
|
4261
6811
|
TokenStreamImpl,
|
|
4262
6812
|
UnicodeIdentifierExtractor,
|
|
4263
6813
|
WhitespaceExtractor,
|
|
4264
6814
|
accumulateBlocks,
|
|
6815
|
+
buildDisambiguation,
|
|
6816
|
+
buildFeedback,
|
|
4265
6817
|
buildPhrase,
|
|
4266
6818
|
buildTablesFromProfiles,
|
|
4267
6819
|
combineClassifiers,
|
|
6820
|
+
convertValue,
|
|
6821
|
+
createAsyncNode,
|
|
4268
6822
|
createCommandNode,
|
|
4269
6823
|
createCompoundNode,
|
|
4270
6824
|
createConditionalNode,
|
|
4271
6825
|
createDiagnosticCollector,
|
|
4272
6826
|
createEventHandlerNode,
|
|
4273
6827
|
createExpression,
|
|
6828
|
+
createFlag,
|
|
4274
6829
|
createLatinCharClassifiers,
|
|
4275
6830
|
createLiteral,
|
|
4276
6831
|
createLoopNode,
|
|
6832
|
+
createMatchNode,
|
|
4277
6833
|
createMultiStatementParser,
|
|
4278
6834
|
createMultilingualDSL,
|
|
4279
6835
|
createPosition,
|
|
@@ -4284,6 +6840,7 @@ export {
|
|
|
4284
6840
|
createSimpleTokenizer,
|
|
4285
6841
|
createToken,
|
|
4286
6842
|
createTokenizerContext,
|
|
6843
|
+
createTryNode,
|
|
4287
6844
|
createUnicodeRangeClassifier,
|
|
4288
6845
|
defineCommand,
|
|
4289
6846
|
defineRole,
|
|
@@ -4295,9 +6852,15 @@ export {
|
|
|
4295
6852
|
extractUrl,
|
|
4296
6853
|
extractValue,
|
|
4297
6854
|
filterBySeverity,
|
|
6855
|
+
fromEnvelopeJSON,
|
|
4298
6856
|
fromError,
|
|
6857
|
+
fromInterchangeNode,
|
|
6858
|
+
fromProtocolJSON,
|
|
6859
|
+
generateExamples,
|
|
4299
6860
|
generatePattern,
|
|
4300
6861
|
generatePatternVariants,
|
|
6862
|
+
generatePrompt,
|
|
6863
|
+
generateProtocolReference,
|
|
4301
6864
|
getAllPossessiveKeywords,
|
|
4302
6865
|
getDefaultExtractors,
|
|
4303
6866
|
getPossessiveReference,
|
|
@@ -4310,6 +6873,8 @@ export {
|
|
|
4310
6873
|
isClassName,
|
|
4311
6874
|
isContextAwareExtractor,
|
|
4312
6875
|
isDigit,
|
|
6876
|
+
isEnvelope,
|
|
6877
|
+
isExplicitSyntax,
|
|
4313
6878
|
isIdSelector,
|
|
4314
6879
|
isNumericValue,
|
|
4315
6880
|
isPossessiveKeyword,
|
|
@@ -4319,17 +6884,33 @@ export {
|
|
|
4319
6884
|
isSelectorStart,
|
|
4320
6885
|
isTypeCompatible,
|
|
4321
6886
|
isUrlStart,
|
|
6887
|
+
isValidReference2 as isValidReference,
|
|
4322
6888
|
isVariableRef,
|
|
4323
6889
|
isWhitespace,
|
|
4324
6890
|
joinTokens,
|
|
6891
|
+
jsonToSemanticNode,
|
|
4325
6892
|
lookupKeyword,
|
|
4326
6893
|
lookupMarker,
|
|
4327
6894
|
matchBest,
|
|
4328
6895
|
matchPattern,
|
|
6896
|
+
needsDisambiguation,
|
|
4329
6897
|
noChange,
|
|
4330
6898
|
normalized,
|
|
6899
|
+
parseExplicit,
|
|
6900
|
+
parseJSONL,
|
|
4331
6901
|
patternMatcher,
|
|
6902
|
+
registryToAOTBackends,
|
|
6903
|
+
renderExplicit,
|
|
6904
|
+
renderExpr,
|
|
4332
6905
|
reorderRoles,
|
|
6906
|
+
semanticNodeToJSON,
|
|
6907
|
+
synthesizeFromSchemas,
|
|
6908
|
+
toEnvelopeJSON,
|
|
6909
|
+
toJSONL,
|
|
6910
|
+
toJSONLRow,
|
|
6911
|
+
toProtocolJSON,
|
|
6912
|
+
validateProtocolJSON,
|
|
6913
|
+
validateSemanticJSON,
|
|
4333
6914
|
validateValueType,
|
|
4334
6915
|
withDefaultExtractors
|
|
4335
6916
|
};
|