@lokascript/framework 2.3.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/api/index.js +29 -516
  2. package/dist/api/index.js.map +1 -1
  3. package/dist/core/index.js +20 -128
  4. package/dist/core/index.js.map +1 -1
  5. package/dist/core/pattern-matching/index.js +19 -12
  6. package/dist/core/pattern-matching/index.js.map +1 -1
  7. package/dist/core/tokenization/index.js.map +1 -1
  8. package/dist/core/types.d.ts +7 -354
  9. package/dist/core/types.d.ts.map +1 -1
  10. package/dist/core/types.js +21 -129
  11. package/dist/core/types.js.map +1 -1
  12. package/dist/generation/diagnostics.d.ts +5 -115
  13. package/dist/generation/diagnostics.d.ts.map +1 -1
  14. package/dist/generation/index.js +20 -77
  15. package/dist/generation/index.js.map +1 -1
  16. package/dist/index.cjs +189 -1458
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.js +108 -1347
  19. package/dist/index.js.map +1 -1
  20. package/dist/interfaces/value-extractor.d.ts +20 -0
  21. package/dist/interfaces/value-extractor.d.ts.map +1 -1
  22. package/dist/ir/explicit-parser.d.ts +2 -83
  23. package/dist/ir/explicit-parser.d.ts.map +1 -1
  24. package/dist/ir/explicit-renderer.d.ts +2 -31
  25. package/dist/ir/explicit-renderer.d.ts.map +1 -1
  26. package/dist/ir/index.js +89 -1260
  27. package/dist/ir/index.js.map +1 -1
  28. package/dist/ir/protocol-json.d.ts +2 -70
  29. package/dist/ir/protocol-json.d.ts.map +1 -1
  30. package/dist/ir/references.d.ts +2 -16
  31. package/dist/ir/references.d.ts.map +1 -1
  32. package/dist/ir/types.d.ts +2 -148
  33. package/dist/ir/types.d.ts.map +1 -1
  34. package/dist/parsing/index.js +19 -12
  35. package/dist/parsing/index.js.map +1 -1
  36. package/dist/schema/command-schema.d.ts +3 -92
  37. package/dist/schema/command-schema.d.ts.map +1 -1
  38. package/dist/schema/index.js +3 -20
  39. package/dist/schema/index.js.map +1 -1
  40. package/dist/testing/index.js +1133 -755
  41. package/dist/testing/index.js.map +1 -1
  42. package/package.json +6 -2
  43. package/src/core/types.ts +71 -600
  44. package/src/generation/diagnostics.ts +12 -233
  45. package/src/interfaces/value-extractor.ts +32 -0
  46. package/src/ir/explicit-parser.ts +12 -792
  47. package/src/ir/explicit-renderer.ts +2 -195
  48. package/src/ir/from-interchange.test.ts +91 -4
  49. package/src/ir/from-interchange.ts +59 -0
  50. package/src/ir/protocol-json.test.ts +16 -7
  51. package/src/ir/protocol-json.ts +9 -748
  52. package/src/ir/references.ts +2 -30
  53. package/src/ir/types.ts +18 -190
  54. package/src/schema/command-schema.ts +3 -134
package/dist/index.js CHANGED
@@ -1,132 +1,23 @@
1
1
  // src/core/types.ts
2
- function createLiteral(value, dataType) {
3
- return dataType ? { type: "literal", value, dataType } : { type: "literal", value };
4
- }
5
- function createSelector(value, selectorKind) {
6
- return selectorKind ? { type: "selector", value, selectorKind } : { type: "selector", value };
7
- }
8
- function createReference(value) {
9
- return { type: "reference", value };
10
- }
11
- function createPropertyPath(object, property) {
12
- return { type: "property-path", object, property };
13
- }
14
- function createExpression(raw) {
15
- return { type: "expression", raw };
16
- }
17
- function createFlag(name, enabled = true) {
18
- return { type: "flag", name, enabled };
19
- }
20
- function createCommandNode(action, roles, metadata) {
21
- const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
22
- const node = {
23
- kind: "command",
24
- action,
25
- roles: rolesMap
26
- };
27
- if (metadata) {
28
- return { ...node, metadata };
29
- }
30
- return node;
31
- }
32
- function createEventHandlerNode(action, roles, body, metadata, eventModifiers) {
33
- const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
34
- const base = {
35
- kind: "event-handler",
36
- action,
37
- roles: rolesMap,
38
- body
39
- };
40
- return {
41
- ...base,
42
- ...eventModifiers && { eventModifiers },
43
- ...metadata && { metadata }
44
- };
45
- }
46
- function createConditionalNode(action, roles, thenBranch, elseBranch, metadata) {
47
- const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
48
- const base = {
49
- kind: "conditional",
50
- action,
51
- roles: rolesMap,
52
- thenBranch
53
- };
54
- return {
55
- ...base,
56
- ...elseBranch && { elseBranch },
57
- ...metadata && { metadata }
58
- };
59
- }
60
- function createCompoundNode(statements, chainType = "sequential", metadata) {
61
- const base = {
62
- kind: "compound",
63
- action: "compound",
64
- roles: /* @__PURE__ */ new Map(),
65
- statements,
66
- chainType
67
- };
68
- return metadata ? { ...base, metadata } : base;
69
- }
70
- function extractValue(value) {
71
- if (value.type === "flag") return value.name;
72
- if ("raw" in value && value.raw !== void 0) return String(value.raw);
73
- if ("value" in value && value.value !== void 0) return String(value.value);
74
- if (value.type === "property-path") return `${extractValue(value.object)}.${value.property}`;
75
- return "";
76
- }
77
- function extractRoleValue(node, role) {
78
- const value = node.roles.get(role);
79
- if (!value) return "";
80
- return extractValue(value);
81
- }
82
- function createLoopNode(action, roles, loopVariant, body, loopVariable, indexVariable, metadata) {
83
- const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
84
- const base = {
85
- kind: "loop",
86
- action,
87
- roles: rolesMap,
88
- loopVariant,
89
- body
90
- };
91
- return {
92
- ...base,
93
- ...loopVariable && { loopVariable },
94
- ...indexVariable && { indexVariable },
95
- ...metadata && { metadata }
96
- };
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
- }
2
+ import {
3
+ createLiteral,
4
+ createSelector,
5
+ createReference,
6
+ createPropertyPath,
7
+ createExpression,
8
+ createFlag,
9
+ createCommandNode,
10
+ createEventHandlerNode,
11
+ createConditionalNode,
12
+ createCompoundNode,
13
+ createLoopNode,
14
+ createTryNode,
15
+ createAsyncNode,
16
+ createMatchNode,
17
+ extractValue,
18
+ extractRoleValue,
19
+ getRoleValue
20
+ } from "@lokascript/intent";
130
21
 
131
22
  // src/core/logger.ts
132
23
  var DebugLogger = class {
@@ -1974,6 +1865,22 @@ var UnicodeIdentifierExtractor = class {
1974
1865
  return length > 0 ? { value: input.substring(position, position + length), length } : null;
1975
1866
  }
1976
1867
  };
1868
+ var LatinExtendedIdentifierExtractor = class {
1869
+ constructor() {
1870
+ this.name = "latin-extended-identifier";
1871
+ }
1872
+ canExtract(input, position) {
1873
+ return /\p{L}/u.test(input[position]);
1874
+ }
1875
+ extract(input, position) {
1876
+ let end = position;
1877
+ while (end < input.length && /[\p{L}\p{N}_-]/u.test(input[end])) {
1878
+ end++;
1879
+ }
1880
+ if (end === position) return null;
1881
+ return { value: input.slice(position, end), length: end - position };
1882
+ }
1883
+ };
1977
1884
  var WhitespaceExtractor = class {
1978
1885
  constructor() {
1979
1886
  this.name = "whitespace";
@@ -2010,621 +1917,21 @@ function createTokenizerContext(tokenizer) {
2010
1917
  }
2011
1918
 
2012
1919
  // 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
- }
1920
+ import { DEFAULT_REFERENCES, isValidReference as isValidReference2 } from "@lokascript/intent";
2025
1921
 
2026
1922
  // 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
- const collect = options.collectDiagnostics === true;
2052
- const diagnostics = [];
2053
- if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) {
2054
- throw new Error("Explicit syntax must be wrapped in brackets: [command role:value ...]");
2055
- }
2056
- const content = trimmed.slice(1, -1).trim();
2057
- if (!content) {
2058
- throw new Error("Empty explicit statement");
2059
- }
2060
- const tokens = tokenizeExplicit(content);
2061
- if (tokens.length === 0) {
2062
- throw new Error("No command specified in explicit statement");
2063
- }
2064
- const command = tokens[0].toLowerCase();
2065
- const roles = /* @__PURE__ */ new Map();
2066
- const refSet = options.referenceSet ?? DEFAULT_REFERENCES;
2067
- const schema = options.schemaLookup?.getSchema(command);
2068
- const validRoleNames = schema ? new Set(schema.roles.map((r) => r.role)) : null;
2069
- for (let i = 1; i < tokens.length; i++) {
2070
- const token = tokens[i];
2071
- if (token.startsWith("+") || token.startsWith("~")) {
2072
- const enabled = token.startsWith("+");
2073
- const flagName = token.slice(1);
2074
- if (!flagName) {
2075
- if (collect) {
2076
- diagnostics.push({
2077
- severity: "error",
2078
- code: "EMPTY_FLAG",
2079
- message: `Empty flag name: "${token}"`,
2080
- source: "schema"
2081
- });
2082
- continue;
2083
- }
2084
- throw new Error(`Empty flag name: "${token}"`);
2085
- }
2086
- if (validRoleNames && !validRoleNames.has(flagName)) {
2087
- const roleList = [...validRoleNames].join(", ");
2088
- if (collect) {
2089
- diagnostics.push({
2090
- severity: "error",
2091
- code: "UNKNOWN_FLAG",
2092
- message: `Unknown flag "${flagName}" for command "${command}"`,
2093
- source: "schema",
2094
- suggestions: [...validRoleNames].map((r) => `+${r}`)
2095
- });
2096
- roles.set(flagName, createFlag(flagName, enabled));
2097
- continue;
2098
- }
2099
- throw new Error(
2100
- `Unknown flag "${flagName}" for command "${command}". Valid roles: ${roleList}`
2101
- );
2102
- }
2103
- roles.set(flagName, createFlag(flagName, enabled));
2104
- continue;
2105
- }
2106
- const colonIndex = token.indexOf(":");
2107
- if (colonIndex === -1) {
2108
- if (collect) {
2109
- diagnostics.push({
2110
- severity: "error",
2111
- code: "INVALID_ROLE_FORMAT",
2112
- message: `Invalid role format: "${token}". Expected role:value or +flag`,
2113
- source: "schema"
2114
- });
2115
- continue;
2116
- }
2117
- throw new Error(`Invalid role format: "${token}". Expected role:value or +flag`);
2118
- }
2119
- const roleName = token.slice(0, colonIndex);
2120
- if (validRoleNames && !STRUCTURAL_ROLES.has(roleName) && !validRoleNames.has(roleName)) {
2121
- const roleList = [...validRoleNames].join(", ");
2122
- if (collect) {
2123
- diagnostics.push({
2124
- severity: "error",
2125
- code: "UNKNOWN_ROLE",
2126
- message: `Unknown role "${roleName}" for command "${command}"`,
2127
- source: "schema",
2128
- suggestions: [...validRoleNames]
2129
- });
2130
- } else {
2131
- throw new Error(
2132
- `Unknown role "${roleName}" for command "${command}". Valid roles: ${roleList}`
2133
- );
2134
- }
2135
- }
2136
- const role = roleName;
2137
- const valueStr = token.slice(colonIndex + 1);
2138
- if (STRUCTURAL_ROLES.has(roleName) && valueStr.startsWith("[") && isNestedCommand(valueStr)) {
2139
- const nestedEnd = findMatchingBracket(token, colonIndex + 1);
2140
- const nestedSyntax = token.slice(colonIndex + 1, nestedEnd + 1);
2141
- roles.set(role, { type: "expression", raw: nestedSyntax });
2142
- continue;
2143
- }
2144
- const value = parseExplicitValue(valueStr, refSet);
2145
- roles.set(role, value);
2146
- }
2147
- if (schema && command !== "on") {
2148
- for (const roleSpec of schema.roles) {
2149
- if (roleSpec.required && !roles.has(roleSpec.role) && !roleSpec.default) {
2150
- if (collect) {
2151
- diagnostics.push({
2152
- severity: "error",
2153
- code: "MISSING_REQUIRED_ROLE",
2154
- message: `Missing required role "${roleSpec.role}" for command "${command}": ${roleSpec.description}`,
2155
- source: "schema"
2156
- });
2157
- } else {
2158
- throw new Error(
2159
- `Missing required role "${roleSpec.role}" for command "${command}": ${roleSpec.description}`
2160
- );
2161
- }
2162
- }
2163
- }
2164
- }
2165
- const attachDiagnostics = (node) => {
2166
- if (diagnostics.length === 0) return node;
2167
- return { ...node, diagnostics };
2168
- };
2169
- switch (command) {
2170
- case "on": {
2171
- const eventValue = roles.get("event");
2172
- if (!eventValue) {
2173
- if (collect) {
2174
- diagnostics.push({
2175
- severity: "error",
2176
- code: "MISSING_EVENT_ROLE",
2177
- message: "Event handler requires event role: [on event:click ...]",
2178
- source: "schema"
2179
- });
2180
- return attachDiagnostics(
2181
- createEventHandlerNode("on", roles, [], {
2182
- sourceLanguage: "explicit"
2183
- })
2184
- );
2185
- }
2186
- throw new Error("Event handler requires event role: [on event:click ...]");
2187
- }
2188
- const body = extractStructuralBody(roles, "body", options);
2189
- roles.delete("body");
2190
- return attachDiagnostics(
2191
- createEventHandlerNode("on", roles, body, {
2192
- sourceLanguage: "explicit"
2193
- })
2194
- );
2195
- }
2196
- case "if": {
2197
- const thenBranch = extractStructuralBody(roles, "then", options);
2198
- const elseBranch = extractStructuralBody(roles, "else", options);
2199
- roles.delete("then");
2200
- roles.delete("else");
2201
- return attachDiagnostics(
2202
- createConditionalNode(
2203
- command,
2204
- roles,
2205
- thenBranch,
2206
- elseBranch.length > 0 ? elseBranch : void 0,
2207
- {
2208
- sourceLanguage: "explicit"
2209
- }
2210
- )
2211
- );
2212
- }
2213
- case "repeat": {
2214
- const loopBody = extractStructuralBody(roles, "loop-body", options);
2215
- const loopVariantValue = roles.get("loop-variant");
2216
- const variableValue = roles.get("variable");
2217
- const indexVariableValue = roles.get("index-variable");
2218
- roles.delete("loop-body");
2219
- roles.delete("loop-variant");
2220
- roles.delete("variable");
2221
- roles.delete("index-variable");
2222
- let variant;
2223
- if (loopVariantValue && loopVariantValue.type === "literal" && typeof loopVariantValue.value === "string") {
2224
- variant = loopVariantValue.value;
2225
- } else if (roles.has("quantity")) {
2226
- variant = "times";
2227
- } else if (roles.has("source")) {
2228
- variant = "for";
2229
- } else if (roles.has("condition")) {
2230
- variant = "while";
2231
- } else {
2232
- variant = "forever";
2233
- }
2234
- const loopVariable = variableValue?.type === "literal" && typeof variableValue.value === "string" ? variableValue.value : variableValue?.type === "expression" ? variableValue.raw : void 0;
2235
- const indexVariable = indexVariableValue?.type === "literal" && typeof indexVariableValue.value === "string" ? indexVariableValue.value : void 0;
2236
- return attachDiagnostics(
2237
- createLoopNode(command, roles, variant, loopBody, loopVariable, indexVariable, {
2238
- sourceLanguage: "explicit"
2239
- })
2240
- );
2241
- }
2242
- default:
2243
- return attachDiagnostics(
2244
- createCommandNode(command, roles, {
2245
- sourceLanguage: "explicit"
2246
- })
2247
- );
2248
- }
2249
- }
2250
- function isExplicitSyntax(input) {
2251
- const trimmed = input.trim();
2252
- return trimmed.startsWith("[") && trimmed.endsWith("]");
2253
- }
2254
- var CHAIN_KEYWORDS = /* @__PURE__ */ new Set(["then", "and", "async", "sequential"]);
2255
- function splitCompoundSegments(input) {
2256
- const segments = [];
2257
- let chainType = null;
2258
- let current = "";
2259
- let bracketDepth = 0;
2260
- let inString = false;
2261
- let stringChar = "";
2262
- for (let i = 0; i < input.length; i++) {
2263
- const char = input[i];
2264
- if (inString) {
2265
- current += char;
2266
- if (char === stringChar && input[i - 1] !== "\\") {
2267
- inString = false;
2268
- }
2269
- continue;
2270
- }
2271
- if (char === '"' || char === "'") {
2272
- inString = true;
2273
- stringChar = char;
2274
- current += char;
2275
- continue;
2276
- }
2277
- if (char === "[") {
2278
- bracketDepth++;
2279
- current += char;
2280
- continue;
2281
- }
2282
- if (char === "]") {
2283
- bracketDepth--;
2284
- current += char;
2285
- if (bracketDepth === 0) {
2286
- const rest = input.slice(i + 1);
2287
- const pipeMatch = rest.match(/^\s*\|>\s*/);
2288
- if (pipeMatch) {
2289
- segments.push(current.trim());
2290
- current = "";
2291
- chainType ?? (chainType = "pipe");
2292
- i += pipeMatch[0].length;
2293
- continue;
2294
- }
2295
- const keywordMatch = rest.match(/^\s+(then|and|async|sequential)\s+/);
2296
- if (keywordMatch && CHAIN_KEYWORDS.has(keywordMatch[1])) {
2297
- segments.push(current.trim());
2298
- current = "";
2299
- chainType ?? (chainType = keywordMatch[1]);
2300
- i += keywordMatch[0].length;
2301
- continue;
2302
- }
2303
- }
2304
- continue;
2305
- }
2306
- current += char;
2307
- }
2308
- if (current.trim()) {
2309
- segments.push(current.trim());
2310
- }
2311
- return { segments, chainType };
2312
- }
2313
- function parseCompound(input, options) {
2314
- const trimmed = input.trim();
2315
- const { annotations, remainder } = extractAnnotations(trimmed);
2316
- const { segments, chainType } = splitCompoundSegments(remainder);
2317
- let node;
2318
- if (segments.length <= 1) {
2319
- node = parseExplicit(segments[0] || remainder, options);
2320
- } else {
2321
- const statements = segments.map((seg) => parseExplicit(seg, options));
2322
- node = createCompoundNode(statements, chainType ?? "sequential");
2323
- }
2324
- if (annotations.length > 0) {
2325
- return { ...node, annotations };
2326
- }
2327
- return node;
2328
- }
2329
- function extractAnnotations(input) {
2330
- const annotations = [];
2331
- let pos = 0;
2332
- while (pos < input.length) {
2333
- while (pos < input.length && (input[pos] === " " || input[pos] === " ")) {
2334
- pos++;
2335
- }
2336
- if (pos >= input.length || input[pos] !== "@") {
2337
- break;
2338
- }
2339
- pos++;
2340
- const nameStart = pos;
2341
- while (pos < input.length && /[A-Za-z0-9\-_]/.test(input[pos])) {
2342
- pos++;
2343
- }
2344
- const name = input.slice(nameStart, pos);
2345
- if (!name) break;
2346
- let value;
2347
- if (pos < input.length && input[pos] === "(") {
2348
- pos++;
2349
- const valueStart = pos;
2350
- if (pos < input.length && (input[pos] === '"' || input[pos] === "'")) {
2351
- const quote = input[pos];
2352
- pos++;
2353
- while (pos < input.length && input[pos] !== quote) {
2354
- if (input[pos] === "\\") pos++;
2355
- pos++;
2356
- }
2357
- pos++;
2358
- value = input.slice(valueStart + 1, pos - 1);
2359
- } else {
2360
- while (pos < input.length && input[pos] !== ")") {
2361
- pos++;
2362
- }
2363
- value = input.slice(valueStart, pos);
2364
- }
2365
- if (pos < input.length && input[pos] === ")") {
2366
- pos++;
2367
- }
2368
- }
2369
- annotations.push(value !== void 0 ? { name, value } : { name });
2370
- }
2371
- return { annotations, remainder: input.slice(pos).trim() };
2372
- }
2373
- var VERSION_HEADER_RE = /^#!lse\s+(\d+\.\d+(?:\.\d+)?)\s*$/;
2374
- function parseDocument(input, options) {
2375
- const lines = input.split("\n");
2376
- let lseVersion = "1.0";
2377
- const nodes = [];
2378
- let startLine = 0;
2379
- for (let i = 0; i < lines.length; i++) {
2380
- const trimmed = lines[i].trim();
2381
- if (!trimmed) continue;
2382
- const versionMatch = trimmed.match(VERSION_HEADER_RE);
2383
- if (versionMatch) {
2384
- lseVersion = versionMatch[1];
2385
- startLine = i + 1;
2386
- }
2387
- break;
2388
- }
2389
- for (let i = startLine; i < lines.length; i++) {
2390
- const trimmed = lines[i].trim();
2391
- if (!trimmed) continue;
2392
- if (trimmed.startsWith("//") || trimmed.startsWith("#") && !trimmed.startsWith("#!")) {
2393
- continue;
2394
- }
2395
- nodes.push(parseCompound(trimmed, options));
2396
- }
2397
- return { lseVersion, nodes };
2398
- }
2399
- function isCompoundSyntax(input) {
2400
- const { segments } = splitCompoundSegments(input.trim());
2401
- return segments.length > 1;
2402
- }
2403
- function isDocumentSyntax(input) {
2404
- const trimmed = input.trim();
2405
- if (trimmed.includes("\n")) return true;
2406
- if (VERSION_HEADER_RE.test(trimmed)) return true;
2407
- if (trimmed.startsWith("//") || trimmed.startsWith("#") && !trimmed.startsWith("#!")) {
2408
- return true;
2409
- }
2410
- return false;
2411
- }
2412
- function extractStructuralBody(roles, roleName, options) {
2413
- const value = roles.get(roleName);
2414
- if (!value) return [];
2415
- if (value.type === "expression") {
2416
- return [parseExplicit(value.raw, options)];
2417
- }
2418
- return [];
2419
- }
2420
- function inferSelectorKind(value) {
2421
- if (!value) return void 0;
2422
- if (value.startsWith("#")) return "id";
2423
- if (value.startsWith(".")) return "class";
2424
- if (value.startsWith("[")) return "attribute";
2425
- if (value.startsWith("<") || value.startsWith("*")) return "element";
2426
- if (/[>+~ ]/.test(value) && value.length > 1) return "complex";
2427
- return void 0;
2428
- }
2429
- function tokenizeExplicit(content) {
2430
- const tokens = [];
2431
- let current = "";
2432
- let inString = false;
2433
- let stringChar = "";
2434
- let bracketDepth = 0;
2435
- for (let i = 0; i < content.length; i++) {
2436
- const char = content[i];
2437
- if (inString) {
2438
- current += char;
2439
- if (char === stringChar && content[i - 1] !== "\\") {
2440
- inString = false;
2441
- }
2442
- continue;
2443
- }
2444
- if (char === '"' || char === "'") {
2445
- inString = true;
2446
- stringChar = char;
2447
- current += char;
2448
- continue;
2449
- }
2450
- if (char === "[") {
2451
- bracketDepth++;
2452
- current += char;
2453
- continue;
2454
- }
2455
- if (char === "]") {
2456
- bracketDepth--;
2457
- current += char;
2458
- continue;
2459
- }
2460
- if (char === " " && bracketDepth === 0) {
2461
- if (current) {
2462
- tokens.push(current);
2463
- current = "";
2464
- }
2465
- continue;
2466
- }
2467
- current += char;
2468
- }
2469
- if (current) {
2470
- tokens.push(current);
2471
- }
2472
- return tokens;
2473
- }
2474
- function parseExplicitValue(valueStr, referenceSet) {
2475
- if (valueStr.startsWith("#") || valueStr.startsWith(".") || valueStr.startsWith("[") || valueStr.startsWith("@") || valueStr.startsWith("*")) {
2476
- return createSelector(valueStr, inferSelectorKind(valueStr));
2477
- }
2478
- if (valueStr.startsWith('"') || valueStr.startsWith("'")) {
2479
- const inner = valueStr.slice(1, -1);
2480
- return createLiteral(inner, "string");
2481
- }
2482
- if (valueStr === "true") return createLiteral(true, "boolean");
2483
- if (valueStr === "false") return createLiteral(false, "boolean");
2484
- const lowerRef = valueStr.toLowerCase();
2485
- if (isValidReference2(lowerRef, referenceSet)) {
2486
- return createReference(lowerRef);
2487
- }
2488
- const numMatch = valueStr.match(/^(-?\d+(?:\.\d+)?)(ms|s|m|h)?$/);
2489
- if (numMatch) {
2490
- const num = parseFloat(numMatch[1]);
2491
- const suffix = numMatch[2];
2492
- if (suffix) {
2493
- return createLiteral(valueStr, "duration");
2494
- }
2495
- return createLiteral(num, "number");
2496
- }
2497
- return createLiteral(valueStr);
2498
- }
2499
- function findMatchingBracket(str, start) {
2500
- let depth = 0;
2501
- for (let i = start; i < str.length; i++) {
2502
- if (str[i] === "[") depth++;
2503
- else if (str[i] === "]") {
2504
- depth--;
2505
- if (depth === 0) return i;
2506
- }
2507
- }
2508
- return str.length - 1;
2509
- }
1923
+ import {
1924
+ STRUCTURAL_ROLES,
1925
+ parseExplicit,
1926
+ isExplicitSyntax,
1927
+ parseCompound,
1928
+ parseDocument,
1929
+ isCompoundSyntax,
1930
+ isDocumentSyntax
1931
+ } from "@lokascript/intent";
2510
1932
 
2511
1933
  // src/ir/explicit-renderer.ts
2512
- function renderExplicit(node) {
2513
- let result;
2514
- if (node.kind === "compound") {
2515
- const compoundNode = node;
2516
- const renderedStatements = compoundNode.statements.map((stmt) => renderExplicit(stmt));
2517
- result = renderedStatements.join(` ${compoundNode.chainType} `);
2518
- } else {
2519
- result = renderBracketCommand(node);
2520
- }
2521
- if (node.annotations && node.annotations.length > 0) {
2522
- const annParts = node.annotations.map(
2523
- (ann) => ann.value !== void 0 ? `@${ann.name}(${ann.value})` : `@${ann.name}`
2524
- );
2525
- return annParts.join(" ") + " " + result;
2526
- }
2527
- return result;
2528
- }
2529
- function renderDocument(envelope) {
2530
- const lines = [];
2531
- if (envelope.lseVersion && envelope.lseVersion !== "1.0") {
2532
- lines.push(`#!lse ${envelope.lseVersion}`);
2533
- }
2534
- for (const node of envelope.nodes) {
2535
- lines.push(renderExplicit(node));
2536
- }
2537
- return lines.join("\n");
2538
- }
2539
- function renderBracketCommand(node) {
2540
- const parts = [node.action];
2541
- for (const [role, value] of node.roles) {
2542
- if (value.type === "flag") {
2543
- parts.push(value.enabled ? `+${value.name}` : `~${value.name}`);
2544
- } else {
2545
- parts.push(`${role}:${valueToString(value)}`);
2546
- }
2547
- }
2548
- if (node.kind === "event-handler") {
2549
- const eventNode = node;
2550
- if (eventNode.body && eventNode.body.length > 0) {
2551
- const bodyParts = eventNode.body.map((n) => renderExplicit(n));
2552
- parts.push(`body:${bodyParts.join(" ")}`);
2553
- }
2554
- }
2555
- if (node.kind === "conditional") {
2556
- const condNode = node;
2557
- if (condNode.thenBranch && condNode.thenBranch.length > 0) {
2558
- const thenParts = condNode.thenBranch.map((n) => renderExplicit(n));
2559
- parts.push(`then:${thenParts.join(" ")}`);
2560
- }
2561
- if (condNode.elseBranch && condNode.elseBranch.length > 0) {
2562
- const elseParts = condNode.elseBranch.map((n) => renderExplicit(n));
2563
- parts.push(`else:${elseParts.join(" ")}`);
2564
- }
2565
- }
2566
- if (node.kind === "loop") {
2567
- const loopNode = node;
2568
- if (loopNode.loopVariant) {
2569
- parts.push(`loop-variant:${loopNode.loopVariant}`);
2570
- }
2571
- if (loopNode.loopVariable) {
2572
- parts.push(`variable:${loopNode.loopVariable}`);
2573
- }
2574
- if (loopNode.indexVariable) {
2575
- parts.push(`index-variable:${loopNode.indexVariable}`);
2576
- }
2577
- if (loopNode.body && loopNode.body.length > 0) {
2578
- const bodyParts = loopNode.body.map((n) => renderExplicit(n));
2579
- parts.push(`loop-body:${bodyParts.join(" ")}`);
2580
- }
2581
- }
2582
- if (node.kind === "command") {
2583
- const cmd = node;
2584
- if (cmd.body && cmd.body.length > 0) {
2585
- const bodyParts = cmd.body.map((n) => renderExplicit(n));
2586
- parts.push(`body:${bodyParts.join(" ")}`);
2587
- }
2588
- if (cmd.catchBranch && cmd.catchBranch.length > 0) {
2589
- const catchParts = cmd.catchBranch.map((n) => renderExplicit(n));
2590
- parts.push(`catch:${catchParts.join(" ")}`);
2591
- }
2592
- if (cmd.finallyBranch && cmd.finallyBranch.length > 0) {
2593
- const finallyParts = cmd.finallyBranch.map((n) => renderExplicit(n));
2594
- parts.push(`finally:${finallyParts.join(" ")}`);
2595
- }
2596
- if (cmd.asyncVariant) {
2597
- parts.push(`async-variant:${cmd.asyncVariant}`);
2598
- }
2599
- if (cmd.asyncBody && cmd.asyncBody.length > 0) {
2600
- const asyncParts = cmd.asyncBody.map((n) => renderExplicit(n));
2601
- parts.push(`async-body:${asyncParts.join(" ")}`);
2602
- }
2603
- }
2604
- return `[${parts.join(" ")}]`;
2605
- }
2606
- function valueToString(value) {
2607
- switch (value.type) {
2608
- case "literal":
2609
- if (typeof value.value === "string") {
2610
- if (value.dataType === "string" || /\s/.test(value.value)) {
2611
- return `"${value.value}"`;
2612
- }
2613
- return value.value;
2614
- }
2615
- return String(value.value);
2616
- case "selector":
2617
- return value.value;
2618
- case "reference":
2619
- return value.value;
2620
- case "property-path":
2621
- return `${valueToString(value.object)}'s ${value.property}`;
2622
- case "expression":
2623
- return value.raw;
2624
- case "flag":
2625
- return value.name;
2626
- }
2627
- }
1934
+ import { renderExplicit, renderDocument } from "@lokascript/intent";
2628
1935
 
2629
1936
  // src/ir/json-schema.ts
2630
1937
  var VALID_VALUE_TYPES = /* @__PURE__ */ new Set([
@@ -2796,527 +2103,14 @@ function detectSelectorKind(selector) {
2796
2103
  }
2797
2104
 
2798
2105
  // src/ir/protocol-json.ts
2799
- function toProtocolJSON(node) {
2800
- const roles = rolesToProtocol(node.roles);
2801
- let result;
2802
- switch (node.kind) {
2803
- case "command": {
2804
- const cmd = node;
2805
- result = { kind: "command", action: node.action, roles };
2806
- if (cmd.body && cmd.body.length > 0) {
2807
- result.body = cmd.body.map(toProtocolJSON);
2808
- }
2809
- if (cmd.catchBranch && cmd.catchBranch.length > 0) {
2810
- result.catchBranch = cmd.catchBranch.map(toProtocolJSON);
2811
- }
2812
- if (cmd.finallyBranch && cmd.finallyBranch.length > 0) {
2813
- result.finallyBranch = cmd.finallyBranch.map(toProtocolJSON);
2814
- }
2815
- if (cmd.asyncVariant) {
2816
- result.asyncVariant = cmd.asyncVariant;
2817
- }
2818
- if (cmd.asyncBody && cmd.asyncBody.length > 0) {
2819
- result.asyncBody = cmd.asyncBody.map(toProtocolJSON);
2820
- }
2821
- if (cmd.arms && cmd.arms.length > 0) {
2822
- result.arms = cmd.arms.map((arm) => ({
2823
- pattern: valueToProtocol(arm.pattern),
2824
- body: arm.body.map(toProtocolJSON)
2825
- }));
2826
- }
2827
- if (cmd.defaultArm && cmd.defaultArm.length > 0) {
2828
- result.defaultArm = cmd.defaultArm.map(toProtocolJSON);
2829
- }
2830
- break;
2831
- }
2832
- case "event-handler": {
2833
- const eh = node;
2834
- result = {
2835
- kind: "event-handler",
2836
- action: node.action,
2837
- roles,
2838
- body: eh.body.map(toProtocolJSON)
2839
- };
2840
- break;
2841
- }
2842
- case "compound": {
2843
- const c = node;
2844
- result = {
2845
- kind: "compound",
2846
- action: "compound",
2847
- statements: c.statements.map(toProtocolJSON),
2848
- chainType: c.chainType
2849
- };
2850
- break;
2851
- }
2852
- case "conditional": {
2853
- const cond = node;
2854
- result = { kind: "command", action: node.action, roles };
2855
- result.thenBranch = cond.thenBranch.map(toProtocolJSON);
2856
- if (cond.elseBranch && cond.elseBranch.length > 0) {
2857
- result.elseBranch = cond.elseBranch.map(toProtocolJSON);
2858
- }
2859
- break;
2860
- }
2861
- case "loop": {
2862
- const loop = node;
2863
- result = { kind: "command", action: node.action, roles };
2864
- result.loopVariant = loop.loopVariant;
2865
- result.loopBody = loop.body.map(toProtocolJSON);
2866
- if (loop.loopVariable) result.loopVariable = loop.loopVariable;
2867
- if (loop.indexVariable) result.indexVariable = loop.indexVariable;
2868
- break;
2869
- }
2870
- }
2871
- if (node.annotations && node.annotations.length > 0) {
2872
- result.annotations = node.annotations.map(
2873
- (a) => a.value !== void 0 ? { name: a.name, value: a.value } : { name: a.name }
2874
- );
2875
- }
2876
- if (node.diagnostics && node.diagnostics.length > 0) {
2877
- result.diagnostics = node.diagnostics.map((d) => {
2878
- const json = {
2879
- level: d.severity,
2880
- message: d.message
2881
- };
2882
- if (d.code) json.code = d.code;
2883
- if (d.source) json.source = d.source;
2884
- if (d.suggestions && d.suggestions.length > 0) json.suggestions = [...d.suggestions];
2885
- return json;
2886
- });
2887
- }
2888
- return result;
2889
- }
2890
- function rolesToProtocol(roles) {
2891
- const result = {};
2892
- for (const [role, value] of roles) {
2893
- result[role] = valueToProtocol(value);
2894
- }
2895
- return result;
2896
- }
2897
- function valueToProtocol(value) {
2898
- switch (value.type) {
2899
- case "selector": {
2900
- const PROTOCOL_SELECTOR_KINDS = /* @__PURE__ */ new Set([
2901
- "id",
2902
- "class",
2903
- "attribute",
2904
- "element",
2905
- "complex"
2906
- ]);
2907
- const sk = "selectorKind" in value ? value.selectorKind : void 0;
2908
- const sv = { type: "selector", value: value.value };
2909
- if (sk && PROTOCOL_SELECTOR_KINDS.has(sk)) {
2910
- sv.selectorKind = sk;
2911
- }
2912
- return sv;
2913
- }
2914
- case "literal":
2915
- return value.dataType ? { type: "literal", value: value.value, dataType: value.dataType } : { type: "literal", value: value.value };
2916
- case "reference":
2917
- return { type: "reference", value: value.value };
2918
- case "expression":
2919
- return { type: "expression", raw: value.raw };
2920
- case "property-path":
2921
- return { type: "expression", raw: extractValue(value) };
2922
- case "flag":
2923
- return { type: "flag", name: value.name, enabled: value.enabled };
2924
- }
2925
- }
2926
- function fromProtocolJSON(json) {
2927
- const roles = new Map(
2928
- Object.entries(json.roles ?? {}).map(([role, val]) => [role, valueFromProtocol(val)])
2929
- );
2930
- const annotations = json.annotations?.map(
2931
- (a) => a.value !== void 0 ? { name: a.name, value: a.value } : { name: a.name }
2932
- );
2933
- const diagnostics = json.diagnostics?.map((d) => ({
2934
- severity: d.level,
2935
- message: d.message,
2936
- ...d.code ? { code: d.code } : {},
2937
- ...d.source ? { source: d.source } : {},
2938
- ...d.suggestions && d.suggestions.length > 0 ? { suggestions: d.suggestions } : {}
2939
- }));
2940
- if (json.trigger) {
2941
- const eventName = json.trigger.event;
2942
- roles.set("event", createLiteral(eventName, "string"));
2943
- const bodyRoles = new Map(roles);
2944
- bodyRoles.delete("event");
2945
- const bodyNode = createCommandNode(json.action, bodyRoles);
2946
- const node2 = createEventHandlerNode(
2947
- "on",
2948
- roles,
2949
- [bodyNode],
2950
- { sourceLanguage: "json" },
2951
- json.trigger.modifiers ?? {}
2952
- );
2953
- return applyV12Metadata(node2, annotations, diagnostics);
2954
- }
2955
- const kind = json.kind ?? "command";
2956
- if (kind === "event-handler") {
2957
- const body = (json.body ?? []).map(fromProtocolJSON);
2958
- const node2 = createEventHandlerNode(json.action, roles, body);
2959
- return applyV12Metadata(node2, annotations, diagnostics);
2960
- }
2961
- if (kind === "compound") {
2962
- const statements = (json.statements ?? []).map(fromProtocolJSON);
2963
- const node2 = createCompoundNode(
2964
- statements,
2965
- json.chainType ?? "sequential"
2966
- );
2967
- return applyV12Metadata(node2, annotations, diagnostics);
2968
- }
2969
- if (json.thenBranch && json.thenBranch.length > 0) {
2970
- const thenBranch = json.thenBranch.map(fromProtocolJSON);
2971
- const elseBranch = json.elseBranch ? json.elseBranch.map(fromProtocolJSON) : void 0;
2972
- const node2 = createConditionalNode(json.action, roles, thenBranch, elseBranch);
2973
- return applyV12Metadata(node2, annotations, diagnostics);
2974
- }
2975
- if (json.loopVariant) {
2976
- const loopBody = (json.loopBody ?? []).map(fromProtocolJSON);
2977
- const node2 = createLoopNode(
2978
- json.action,
2979
- roles,
2980
- json.loopVariant,
2981
- loopBody,
2982
- json.loopVariable,
2983
- json.indexVariable
2984
- );
2985
- return applyV12Metadata(node2, annotations, diagnostics);
2986
- }
2987
- if (json.catchBranch || json.finallyBranch) {
2988
- const body = (json.body ?? []).map(fromProtocolJSON);
2989
- const catchBranch = json.catchBranch ? json.catchBranch.map(fromProtocolJSON) : void 0;
2990
- const finallyBranch = json.finallyBranch ? json.finallyBranch.map(fromProtocolJSON) : void 0;
2991
- const node2 = createTryNode(body, catchBranch, finallyBranch);
2992
- return applyV12Metadata(node2, annotations, diagnostics);
2993
- }
2994
- if (json.asyncVariant) {
2995
- const asyncBody = (json.asyncBody ?? []).map(fromProtocolJSON);
2996
- const node2 = createAsyncNode(json.asyncVariant, asyncBody);
2997
- return applyV12Metadata(node2, annotations, diagnostics);
2998
- }
2999
- if (json.arms && json.arms.length > 0) {
3000
- const arms = json.arms.map((arm) => ({
3001
- pattern: valueFromProtocol(arm.pattern),
3002
- body: arm.body.map(fromProtocolJSON)
3003
- }));
3004
- const defaultArm = json.defaultArm ? json.defaultArm.map(fromProtocolJSON) : void 0;
3005
- const node2 = createMatchNode(roles, arms, defaultArm);
3006
- return applyV12Metadata(node2, annotations, diagnostics);
3007
- }
3008
- if (json.body && json.body.length > 0 && kind === "command") {
3009
- const body = json.body.map(fromProtocolJSON);
3010
- const node2 = createTryNode(body);
3011
- return applyV12Metadata(node2, annotations, diagnostics);
3012
- }
3013
- const node = createCommandNode(json.action, roles);
3014
- return applyV12Metadata(node, annotations, diagnostics);
3015
- }
3016
- function applyV12Metadata(node, annotations, diagnostics) {
3017
- if ((!annotations || annotations.length === 0) && (!diagnostics || diagnostics.length === 0)) {
3018
- return node;
3019
- }
3020
- return {
3021
- ...node,
3022
- ...annotations && annotations.length > 0 ? { annotations } : {},
3023
- ...diagnostics && diagnostics.length > 0 ? { diagnostics } : {}
3024
- };
3025
- }
3026
- function valueFromProtocol(value) {
3027
- switch (value.type) {
3028
- case "selector":
3029
- return createSelector(String(value.value ?? ""), value.selectorKind);
3030
- case "literal":
3031
- return createLiteral(
3032
- value.value !== void 0 ? value.value : "",
3033
- value.dataType
3034
- );
3035
- case "reference":
3036
- return createReference(String(value.value ?? ""));
3037
- case "expression":
3038
- return createExpression(value.raw ?? String(value.value ?? ""));
3039
- case "flag":
3040
- return createFlag(
3041
- value.name ?? String(value.value ?? ""),
3042
- value.enabled ?? (typeof value.value === "boolean" ? value.value : true)
3043
- );
3044
- case "property-path":
3045
- return createExpression(String(value.value ?? value.raw ?? ""));
3046
- }
3047
- }
3048
- function toEnvelopeJSON(envelope) {
3049
- const result = {
3050
- lseVersion: envelope.lseVersion,
3051
- nodes: envelope.nodes.map(toProtocolJSON)
3052
- };
3053
- if (envelope.features && envelope.features.length > 0) {
3054
- result.features = [...envelope.features];
3055
- }
3056
- return result;
3057
- }
3058
- function fromEnvelopeJSON(json) {
3059
- return {
3060
- lseVersion: json.lseVersion,
3061
- ...json.features && json.features.length > 0 ? { features: json.features } : {},
3062
- nodes: json.nodes.map(fromProtocolJSON)
3063
- };
3064
- }
3065
- function isEnvelope(json) {
3066
- return typeof json === "object" && json !== null && "lseVersion" in json && "nodes" in json && Array.isArray(json.nodes);
3067
- }
3068
- var VALID_KINDS = /* @__PURE__ */ new Set(["command", "event-handler", "compound"]);
3069
- var VALID_VALUE_TYPES2 = /* @__PURE__ */ new Set([
3070
- "selector",
3071
- "literal",
3072
- "reference",
3073
- "expression",
3074
- "property-path",
3075
- "flag"
3076
- ]);
3077
- var VALID_CHAIN_TYPES = /* @__PURE__ */ new Set(["then", "and", "async", "sequential", "pipe"]);
3078
- var VALID_ASYNC_VARIANTS = /* @__PURE__ */ new Set(["all", "race"]);
3079
- var VALID_DIAGNOSTIC_LEVELS = /* @__PURE__ */ new Set(["error", "warning", "info"]);
3080
- function validateProtocolJSON(json) {
3081
- const diagnostics = [];
3082
- if (typeof json !== "object" || json === null) {
3083
- return [{ severity: "error", code: "INVALID_ROOT", message: "Root must be a JSON object" }];
3084
- }
3085
- const node = json;
3086
- const kind = "kind" in node ? String(node.kind) : "command";
3087
- if ("kind" in node && !VALID_KINDS.has(kind)) {
3088
- diagnostics.push({
3089
- severity: "error",
3090
- code: "INVALID_KIND",
3091
- message: `Invalid kind "${node.kind}". Must be one of: command, event-handler, compound`
3092
- });
3093
- }
3094
- if (!("action" in node)) {
3095
- diagnostics.push({
3096
- severity: "error",
3097
- code: "MISSING_ACTION",
3098
- message: "Missing required field: action"
3099
- });
3100
- } else if (typeof node.action !== "string" || node.action.length === 0) {
3101
- diagnostics.push({
3102
- severity: "error",
3103
- code: "INVALID_ACTION",
3104
- message: "action must be a non-empty string"
3105
- });
3106
- }
3107
- if (!("roles" in node) && kind !== "compound") {
3108
- diagnostics.push({
3109
- severity: "error",
3110
- code: "MISSING_ROLES",
3111
- message: "Missing required field: roles"
3112
- });
3113
- } else if ("roles" in node && (typeof node.roles !== "object" || node.roles === null || Array.isArray(node.roles))) {
3114
- diagnostics.push({
3115
- severity: "error",
3116
- code: "INVALID_ROLES",
3117
- message: "roles must be a plain object"
3118
- });
3119
- } else if ("roles" in node && node.roles) {
3120
- for (const [role, value] of Object.entries(node.roles)) {
3121
- if (typeof value !== "object" || value === null) {
3122
- diagnostics.push({
3123
- severity: "error",
3124
- code: "INVALID_ROLE_VALUE",
3125
- message: `Role "${role}" value must be an object`
3126
- });
3127
- continue;
3128
- }
3129
- const v = value;
3130
- if (!("type" in v) || !VALID_VALUE_TYPES2.has(String(v.type))) {
3131
- diagnostics.push({
3132
- severity: "error",
3133
- code: "INVALID_VALUE_TYPE",
3134
- message: `Role "${role}" has invalid type "${v.type}"`
3135
- });
3136
- }
3137
- if (v.type === "flag") {
3138
- const hasName = typeof v.name === "string";
3139
- const hasEnabled = typeof v.enabled === "boolean";
3140
- const hasValue = "value" in v;
3141
- if (!hasName && !hasValue) {
3142
- diagnostics.push({
3143
- severity: "error",
3144
- code: "MISSING_FLAG_NAME",
3145
- message: `Flag role "${role}" missing required field: name (or value)`
3146
- });
3147
- }
3148
- if (!hasEnabled && !hasValue) {
3149
- diagnostics.push({
3150
- severity: "error",
3151
- code: "MISSING_FLAG_ENABLED",
3152
- message: `Flag role "${role}" missing required field: enabled (or value)`
3153
- });
3154
- }
3155
- }
3156
- if (v.type === "expression" && typeof v.raw !== "string" && !("value" in v)) {
3157
- diagnostics.push({
3158
- severity: "error",
3159
- code: "MISSING_EXPRESSION_RAW",
3160
- message: `Expression role "${role}" missing required field: raw (or value)`
3161
- });
3162
- }
3163
- }
3164
- }
3165
- if ("trigger" in node && node.trigger) {
3166
- const trigger = node.trigger;
3167
- if (typeof trigger !== "object" || trigger === null) {
3168
- diagnostics.push({
3169
- severity: "error",
3170
- code: "INVALID_TRIGGER",
3171
- message: "trigger must be an object"
3172
- });
3173
- } else if (!trigger.event || typeof trigger.event !== "string") {
3174
- diagnostics.push({
3175
- severity: "error",
3176
- code: "INVALID_TRIGGER",
3177
- message: "trigger.event is required and must be a non-empty string"
3178
- });
3179
- }
3180
- }
3181
- if (kind === "event-handler" && !("trigger" in node)) {
3182
- if (!("body" in node) || !Array.isArray(node.body)) {
3183
- diagnostics.push({
3184
- severity: "error",
3185
- code: "MISSING_BODY",
3186
- message: "event-handler node requires a body array"
3187
- });
3188
- }
3189
- }
3190
- if (kind === "compound") {
3191
- if (!("statements" in node) || !Array.isArray(node.statements)) {
3192
- diagnostics.push({
3193
- severity: "error",
3194
- code: "MISSING_STATEMENTS",
3195
- message: "compound node requires a statements array"
3196
- });
3197
- }
3198
- if ("chainType" in node && !VALID_CHAIN_TYPES.has(String(node.chainType))) {
3199
- diagnostics.push({
3200
- severity: "error",
3201
- code: "INVALID_CHAIN_TYPE",
3202
- message: `Invalid chainType "${node.chainType}". Must be one of: then, and, async, sequential, pipe`
3203
- });
3204
- }
3205
- }
3206
- if ("diagnostics" in node) {
3207
- if (!Array.isArray(node.diagnostics)) {
3208
- diagnostics.push({
3209
- severity: "error",
3210
- code: "INVALID_DIAGNOSTICS",
3211
- message: "diagnostics must be an array"
3212
- });
3213
- } else {
3214
- for (let i = 0; i < node.diagnostics.length; i++) {
3215
- const d = node.diagnostics[i];
3216
- if (!d || typeof d !== "object") {
3217
- diagnostics.push({
3218
- severity: "error",
3219
- code: "INVALID_DIAGNOSTIC_ENTRY",
3220
- message: `diagnostics[${i}] must be an object`
3221
- });
3222
- continue;
3223
- }
3224
- if (!VALID_DIAGNOSTIC_LEVELS.has(String(d.level))) {
3225
- diagnostics.push({
3226
- severity: "error",
3227
- code: "INVALID_DIAGNOSTIC_LEVEL",
3228
- message: `diagnostics[${i}].level must be "error", "warning", or "info"`
3229
- });
3230
- }
3231
- if (typeof d.message !== "string") {
3232
- diagnostics.push({
3233
- severity: "error",
3234
- code: "MISSING_DIAGNOSTIC_MESSAGE",
3235
- message: `diagnostics[${i}] missing required field: message`
3236
- });
3237
- }
3238
- if ("code" in d && typeof d.code !== "string") {
3239
- diagnostics.push({
3240
- severity: "warning",
3241
- code: "INVALID_DIAGNOSTIC_CODE",
3242
- message: `diagnostics[${i}].code must be a string if present`
3243
- });
3244
- }
3245
- }
3246
- }
3247
- }
3248
- if ("annotations" in node) {
3249
- if (!Array.isArray(node.annotations)) {
3250
- diagnostics.push({
3251
- severity: "error",
3252
- code: "INVALID_ANNOTATIONS",
3253
- message: "annotations must be an array"
3254
- });
3255
- } else {
3256
- for (let i = 0; i < node.annotations.length; i++) {
3257
- const a = node.annotations[i];
3258
- if (!a || typeof a !== "object") {
3259
- diagnostics.push({
3260
- severity: "error",
3261
- code: "INVALID_ANNOTATION_ENTRY",
3262
- message: `annotations[${i}] must be an object`
3263
- });
3264
- continue;
3265
- }
3266
- if (typeof a.name !== "string" || a.name.length === 0) {
3267
- diagnostics.push({
3268
- severity: "error",
3269
- code: "MISSING_ANNOTATION_NAME",
3270
- message: `annotations[${i}] missing required field: name`
3271
- });
3272
- }
3273
- }
3274
- }
3275
- }
3276
- if ("asyncVariant" in node && !VALID_ASYNC_VARIANTS.has(String(node.asyncVariant))) {
3277
- diagnostics.push({
3278
- severity: "error",
3279
- code: "INVALID_ASYNC_VARIANT",
3280
- message: `Invalid asyncVariant "${node.asyncVariant}". Must be "all" or "race"`
3281
- });
3282
- }
3283
- if ("arms" in node) {
3284
- if (!Array.isArray(node.arms)) {
3285
- diagnostics.push({
3286
- severity: "error",
3287
- code: "INVALID_ARMS",
3288
- message: "arms must be an array"
3289
- });
3290
- } else {
3291
- for (let i = 0; i < node.arms.length; i++) {
3292
- const arm = node.arms[i];
3293
- if (!arm || typeof arm !== "object") {
3294
- diagnostics.push({
3295
- severity: "error",
3296
- code: "INVALID_ARM_ENTRY",
3297
- message: `arms[${i}] must be an object`
3298
- });
3299
- continue;
3300
- }
3301
- if (!arm.pattern || typeof arm.pattern !== "object") {
3302
- diagnostics.push({
3303
- severity: "error",
3304
- code: "MISSING_ARM_PATTERN",
3305
- message: `arms[${i}] missing required field: pattern`
3306
- });
3307
- }
3308
- if (!Array.isArray(arm.body)) {
3309
- diagnostics.push({
3310
- severity: "error",
3311
- code: "MISSING_ARM_BODY",
3312
- message: `arms[${i}] missing required field: body (array)`
3313
- });
3314
- }
3315
- }
3316
- }
3317
- }
3318
- return diagnostics;
3319
- }
2106
+ import {
2107
+ toProtocolJSON,
2108
+ fromProtocolJSON,
2109
+ validateProtocolJSON,
2110
+ toEnvelopeJSON,
2111
+ fromEnvelopeJSON,
2112
+ isEnvelope
2113
+ } from "@lokascript/intent";
3320
2114
 
3321
2115
  // src/ir/from-interchange.ts
3322
2116
  function fromInterchangeNode(node) {
@@ -3455,7 +2249,7 @@ function convertValue(node) {
3455
2249
  case "literal":
3456
2250
  return convertLiteral(node);
3457
2251
  case "selector":
3458
- return createSelector(String(node.value ?? ""), inferSelectorKind2(String(node.value ?? "")));
2252
+ return createSelector(String(node.value ?? ""), inferSelectorKind(String(node.value ?? "")));
3459
2253
  case "identifier":
3460
2254
  return convertIdentifier(node);
3461
2255
  case "variable":
@@ -3557,6 +2351,33 @@ function renderExpr(node) {
3557
2351
  return "";
3558
2352
  }
3559
2353
  }
2354
+ var SCROLL_MARKERS = /* @__PURE__ */ new Set([
2355
+ "to",
2356
+ "of",
2357
+ "the",
2358
+ "top",
2359
+ "bottom",
2360
+ "middle",
2361
+ "center",
2362
+ "nearest",
2363
+ "left",
2364
+ "right",
2365
+ "smoothly",
2366
+ "instantly"
2367
+ ]);
2368
+ var URL_MARKERS = /* @__PURE__ */ new Set(["url"]);
2369
+ var PARTIALS_IN_MARKERS = /* @__PURE__ */ new Set(["partials", "in"]);
2370
+ function firstNonMarkerArg(args, markers) {
2371
+ for (const a of args) {
2372
+ if (a.type === "identifier") {
2373
+ const ident = a;
2374
+ const word = typeof ident.name === "string" ? ident.name : ident.value;
2375
+ if (typeof word === "string" && markers.has(word)) continue;
2376
+ }
2377
+ return a;
2378
+ }
2379
+ return void 0;
2380
+ }
3560
2381
  function inferRoles(name, args, modifiers, target) {
3561
2382
  const roles = {};
3562
2383
  switch (name) {
@@ -3636,6 +2457,25 @@ function inferRoles(name, args, modifiers, target) {
3636
2457
  if (args[0]) roles.patient = convertValue(args[0]);
3637
2458
  break;
3638
2459
  }
2460
+ // scroll [to] [position] target [smoothly|instantly] → destination=target
2461
+ case "scroll": {
2462
+ const t = firstNonMarkerArg(args, SCROLL_MARKERS);
2463
+ if (t) roles.destination = convertValue(t);
2464
+ break;
2465
+ }
2466
+ // push|replace url <X> → patient=X
2467
+ case "push":
2468
+ case "replace": {
2469
+ const t = firstNonMarkerArg(args, URL_MARKERS);
2470
+ if (t) roles.patient = convertValue(t);
2471
+ break;
2472
+ }
2473
+ // process partials in <X> → patient=X
2474
+ case "process": {
2475
+ const t = firstNonMarkerArg(args, PARTIALS_IN_MARKERS);
2476
+ if (t) roles.patient = convertValue(t);
2477
+ break;
2478
+ }
3639
2479
  default: {
3640
2480
  if (args[0]) roles.patient = convertValue(args[0]);
3641
2481
  if (target) roles.destination = convertValue(target);
@@ -3644,7 +2484,7 @@ function inferRoles(name, args, modifiers, target) {
3644
2484
  }
3645
2485
  return roles;
3646
2486
  }
3647
- function inferSelectorKind2(value) {
2487
+ function inferSelectorKind(value) {
3648
2488
  if (!value) return void 0;
3649
2489
  if (value.startsWith("#")) return "id";
3650
2490
  if (value.startsWith(".")) return "class";
@@ -3655,7 +2495,7 @@ function inferSelectorKind2(value) {
3655
2495
  }
3656
2496
  function inferValueFromString(value) {
3657
2497
  if (DEFAULT_REFERENCES.has(value)) return createReference(value);
3658
- const kind = inferSelectorKind2(value);
2498
+ const kind = inferSelectorKind(value);
3659
2499
  if (kind) return createSelector(value, kind);
3660
2500
  return createLiteral(value);
3661
2501
  }
@@ -5473,25 +4313,7 @@ async function registryToAOTBackends(registry) {
5473
4313
  }
5474
4314
 
5475
4315
  // src/schema/command-schema.ts
5476
- function defineCommand(schema) {
5477
- return {
5478
- description: schema.description || `${schema.action} command`,
5479
- category: schema.category || "general",
5480
- primaryRole: schema.primaryRole || schema.roles[0]?.role || "patient",
5481
- ...schema,
5482
- action: schema.action,
5483
- roles: schema.roles
5484
- };
5485
- }
5486
- function defineRole(role) {
5487
- return {
5488
- description: role.description || `${role.role} role`,
5489
- ...role,
5490
- role: role.role,
5491
- required: role.required,
5492
- expectedTypes: role.expectedTypes
5493
- };
5494
- }
4316
+ import { defineCommand, defineRole, getRoleSpec } from "@lokascript/intent";
5495
4317
 
5496
4318
  // src/generation/renderer.ts
5497
4319
  function lookupKeyword(keywords, action, language) {
@@ -5584,71 +4406,7 @@ function createSchemaRenderer(schemas, profiles) {
5584
4406
  }
5585
4407
 
5586
4408
  // src/generation/diagnostics.ts
5587
- function createDiagnosticCollector() {
5588
- const diagnostics = [];
5589
- function addWithSeverity(severity, message, options) {
5590
- const diag = {
5591
- message,
5592
- severity,
5593
- ...options?.code != null && { code: options.code },
5594
- ...options?.line != null && { line: options.line },
5595
- ...options?.column != null && { column: options.column },
5596
- ...options?.source != null && { source: options.source },
5597
- ...options?.suggestions != null && options.suggestions.length > 0 && { suggestions: options.suggestions }
5598
- };
5599
- diagnostics.push(diag);
5600
- }
5601
- return {
5602
- error(message, options) {
5603
- addWithSeverity("error", message, options);
5604
- },
5605
- warning(message, options) {
5606
- addWithSeverity("warning", message, options);
5607
- },
5608
- info(message, options) {
5609
- addWithSeverity("info", message, options);
5610
- },
5611
- add(diagnostic) {
5612
- diagnostics.push(diagnostic);
5613
- },
5614
- hasErrors() {
5615
- return diagnostics.some((d) => d.severity === "error");
5616
- },
5617
- getDiagnostics() {
5618
- return diagnostics;
5619
- },
5620
- toResult() {
5621
- let errors = 0;
5622
- let warnings = 0;
5623
- let infos = 0;
5624
- for (const d of diagnostics) {
5625
- if (d.severity === "error") errors++;
5626
- else if (d.severity === "warning") warnings++;
5627
- else infos++;
5628
- }
5629
- return {
5630
- ok: errors === 0,
5631
- diagnostics,
5632
- summary: { errors, warnings, infos }
5633
- };
5634
- }
5635
- };
5636
- }
5637
- function fromError(error, options) {
5638
- const message = error instanceof Error ? error.message : String(error);
5639
- return {
5640
- message,
5641
- severity: "error",
5642
- ...options?.code != null && { code: options.code },
5643
- ...options?.line != null && { line: options.line },
5644
- ...options?.column != null && { column: options.column },
5645
- ...options?.source != null && { source: options.source },
5646
- ...options?.suggestions != null && options.suggestions.length > 0 && { suggestions: options.suggestions }
5647
- };
5648
- }
5649
- function filterBySeverity(diagnostics, severity) {
5650
- return diagnostics.filter((d) => d.severity === severity);
5651
- }
4409
+ import { createDiagnosticCollector, fromError, filterBySeverity } from "@lokascript/intent";
5652
4410
 
5653
4411
  // src/core/tokenization/token-utils.ts
5654
4412
  var TokenStreamImpl = class {
@@ -7303,6 +6061,7 @@ export {
7303
6061
  IdentifierExtractor,
7304
6062
  InMemoryDictionary,
7305
6063
  InMemoryProfileProvider,
6064
+ LatinExtendedIdentifierExtractor,
7306
6065
  NullDictionary,
7307
6066
  NullProfileProvider,
7308
6067
  NumberExtractor,
@@ -7367,6 +6126,8 @@ export {
7367
6126
  getAllPossessiveKeywords,
7368
6127
  getDefaultExtractors,
7369
6128
  getPossessiveReference,
6129
+ getRoleSpec,
6130
+ getRoleValue,
7370
6131
  insertMarkers,
7371
6132
  isAsciiIdentifierChar,
7372
6133
  isAsciiLetter,