@lokascript/framework 2.3.1 → 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 +1066 -712
  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/ir/index.js CHANGED
@@ -1,744 +1,40 @@
1
1
  // src/ir/references.ts
2
- var DEFAULT_REFERENCES = /* @__PURE__ */ new Set([
3
- "me",
4
- "you",
5
- "it",
6
- "result",
7
- "event",
8
- "target",
9
- "body"
10
- ]);
11
- function isValidReference(value, referenceSet = DEFAULT_REFERENCES) {
12
- return referenceSet.has(value);
13
- }
14
-
15
- // src/core/types.ts
16
- function createLiteral(value, dataType) {
17
- return dataType ? { type: "literal", value, dataType } : { type: "literal", value };
18
- }
19
- function createSelector(value, selectorKind) {
20
- return selectorKind ? { type: "selector", value, selectorKind } : { type: "selector", value };
21
- }
22
- function createReference(value) {
23
- return { type: "reference", value };
24
- }
25
- function createPropertyPath(object, property) {
26
- return { type: "property-path", object, property };
27
- }
28
- function createExpression(raw) {
29
- return { type: "expression", raw };
30
- }
31
- function createFlag(name, enabled = true) {
32
- return { type: "flag", name, enabled };
33
- }
34
- function createCommandNode(action, roles, metadata) {
35
- const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
36
- const node = {
37
- kind: "command",
38
- action,
39
- roles: rolesMap
40
- };
41
- if (metadata) {
42
- return { ...node, metadata };
43
- }
44
- return node;
45
- }
46
- function createEventHandlerNode(action, roles, body, metadata, eventModifiers) {
47
- const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
48
- const base = {
49
- kind: "event-handler",
50
- action,
51
- roles: rolesMap,
52
- body
53
- };
54
- return {
55
- ...base,
56
- ...eventModifiers && { eventModifiers },
57
- ...metadata && { metadata }
58
- };
59
- }
60
- function createConditionalNode(action, roles, thenBranch, elseBranch, metadata) {
61
- const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
62
- const base = {
63
- kind: "conditional",
64
- action,
65
- roles: rolesMap,
66
- thenBranch
67
- };
68
- return {
69
- ...base,
70
- ...elseBranch && { elseBranch },
71
- ...metadata && { metadata }
72
- };
73
- }
74
- function createCompoundNode(statements, chainType = "sequential", metadata) {
75
- const base = {
76
- kind: "compound",
77
- action: "compound",
78
- roles: /* @__PURE__ */ new Map(),
79
- statements,
80
- chainType
81
- };
82
- return metadata ? { ...base, metadata } : base;
83
- }
84
- function extractValue(value) {
85
- if (value.type === "flag") return value.name;
86
- if ("raw" in value && value.raw !== void 0) return String(value.raw);
87
- if ("value" in value && value.value !== void 0) return String(value.value);
88
- if (value.type === "property-path") return `${extractValue(value.object)}.${value.property}`;
89
- return "";
90
- }
91
- function createLoopNode(action, roles, loopVariant, body, loopVariable, indexVariable, metadata) {
92
- const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
93
- const base = {
94
- kind: "loop",
95
- action,
96
- roles: rolesMap,
97
- loopVariant,
98
- body
99
- };
100
- return {
101
- ...base,
102
- ...loopVariable && { loopVariable },
103
- ...indexVariable && { indexVariable },
104
- ...metadata && { metadata }
105
- };
106
- }
107
- function createTryNode(body, catchBranch, finallyBranch, metadata) {
108
- return {
109
- kind: "command",
110
- action: "try",
111
- roles: /* @__PURE__ */ new Map(),
112
- body,
113
- ...catchBranch && catchBranch.length > 0 ? { catchBranch } : {},
114
- ...finallyBranch && finallyBranch.length > 0 ? { finallyBranch } : {},
115
- ...metadata ? { metadata } : {}
116
- };
117
- }
118
- function createAsyncNode(variant, asyncBody, metadata) {
119
- return {
120
- kind: "command",
121
- action: variant,
122
- roles: /* @__PURE__ */ new Map(),
123
- asyncVariant: variant,
124
- asyncBody,
125
- ...metadata ? { metadata } : {}
126
- };
127
- }
128
- function createMatchNode(roles, arms, defaultArm, metadata) {
129
- const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
130
- return {
131
- kind: "command",
132
- action: "match",
133
- roles: rolesMap,
134
- arms,
135
- ...defaultArm && defaultArm.length > 0 ? { defaultArm } : {},
136
- ...metadata ? { metadata } : {}
137
- };
138
- }
2
+ import { DEFAULT_REFERENCES, isValidReference } from "@lokascript/intent";
139
3
 
140
4
  // src/ir/explicit-parser.ts
141
- var STRUCTURAL_ROLES = /* @__PURE__ */ new Set([
142
- "body",
143
- "then",
144
- "else",
145
- "condition",
146
- "loop-body",
147
- "variable",
148
- "catch",
149
- // v1.2: try/catch/finally
150
- "finally"
151
- // v1.2: try/catch/finally
152
- ]);
153
- function isNestedCommand(value) {
154
- const inner = value.slice(1, -1);
155
- let depth = 0;
156
- for (const ch of inner) {
157
- if (ch === "[") depth++;
158
- else if (ch === "]") depth--;
159
- else if (depth === 0 && (ch === " " || ch === ":")) return true;
160
- }
161
- return false;
162
- }
163
- function parseExplicit(input, options = {}) {
164
- const trimmed = input.trim();
165
- const collect = options.collectDiagnostics === true;
166
- const diagnostics = [];
167
- if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) {
168
- throw new Error("Explicit syntax must be wrapped in brackets: [command role:value ...]");
169
- }
170
- const content = trimmed.slice(1, -1).trim();
171
- if (!content) {
172
- throw new Error("Empty explicit statement");
173
- }
174
- const tokens = tokenizeExplicit(content);
175
- if (tokens.length === 0) {
176
- throw new Error("No command specified in explicit statement");
177
- }
178
- const command = tokens[0].toLowerCase();
179
- const roles = /* @__PURE__ */ new Map();
180
- const refSet = options.referenceSet ?? DEFAULT_REFERENCES;
181
- const schema = options.schemaLookup?.getSchema(command);
182
- const validRoleNames = schema ? new Set(schema.roles.map((r) => r.role)) : null;
183
- for (let i = 1; i < tokens.length; i++) {
184
- const token = tokens[i];
185
- if (token.startsWith("+") || token.startsWith("~")) {
186
- const enabled = token.startsWith("+");
187
- const flagName = token.slice(1);
188
- if (!flagName) {
189
- if (collect) {
190
- diagnostics.push({
191
- severity: "error",
192
- code: "EMPTY_FLAG",
193
- message: `Empty flag name: "${token}"`,
194
- source: "schema"
195
- });
196
- continue;
197
- }
198
- throw new Error(`Empty flag name: "${token}"`);
199
- }
200
- if (validRoleNames && !validRoleNames.has(flagName)) {
201
- const roleList = [...validRoleNames].join(", ");
202
- if (collect) {
203
- diagnostics.push({
204
- severity: "error",
205
- code: "UNKNOWN_FLAG",
206
- message: `Unknown flag "${flagName}" for command "${command}"`,
207
- source: "schema",
208
- suggestions: [...validRoleNames].map((r) => `+${r}`)
209
- });
210
- roles.set(flagName, createFlag(flagName, enabled));
211
- continue;
212
- }
213
- throw new Error(
214
- `Unknown flag "${flagName}" for command "${command}". Valid roles: ${roleList}`
215
- );
216
- }
217
- roles.set(flagName, createFlag(flagName, enabled));
218
- continue;
219
- }
220
- const colonIndex = token.indexOf(":");
221
- if (colonIndex === -1) {
222
- if (collect) {
223
- diagnostics.push({
224
- severity: "error",
225
- code: "INVALID_ROLE_FORMAT",
226
- message: `Invalid role format: "${token}". Expected role:value or +flag`,
227
- source: "schema"
228
- });
229
- continue;
230
- }
231
- throw new Error(`Invalid role format: "${token}". Expected role:value or +flag`);
232
- }
233
- const roleName = token.slice(0, colonIndex);
234
- if (validRoleNames && !STRUCTURAL_ROLES.has(roleName) && !validRoleNames.has(roleName)) {
235
- const roleList = [...validRoleNames].join(", ");
236
- if (collect) {
237
- diagnostics.push({
238
- severity: "error",
239
- code: "UNKNOWN_ROLE",
240
- message: `Unknown role "${roleName}" for command "${command}"`,
241
- source: "schema",
242
- suggestions: [...validRoleNames]
243
- });
244
- } else {
245
- throw new Error(
246
- `Unknown role "${roleName}" for command "${command}". Valid roles: ${roleList}`
247
- );
248
- }
249
- }
250
- const role = roleName;
251
- const valueStr = token.slice(colonIndex + 1);
252
- if (STRUCTURAL_ROLES.has(roleName) && valueStr.startsWith("[") && isNestedCommand(valueStr)) {
253
- const nestedEnd = findMatchingBracket(token, colonIndex + 1);
254
- const nestedSyntax = token.slice(colonIndex + 1, nestedEnd + 1);
255
- roles.set(role, { type: "expression", raw: nestedSyntax });
256
- continue;
257
- }
258
- const value = parseExplicitValue(valueStr, refSet);
259
- roles.set(role, value);
260
- }
261
- if (schema && command !== "on") {
262
- for (const roleSpec of schema.roles) {
263
- if (roleSpec.required && !roles.has(roleSpec.role) && !roleSpec.default) {
264
- if (collect) {
265
- diagnostics.push({
266
- severity: "error",
267
- code: "MISSING_REQUIRED_ROLE",
268
- message: `Missing required role "${roleSpec.role}" for command "${command}": ${roleSpec.description}`,
269
- source: "schema"
270
- });
271
- } else {
272
- throw new Error(
273
- `Missing required role "${roleSpec.role}" for command "${command}": ${roleSpec.description}`
274
- );
275
- }
276
- }
277
- }
278
- }
279
- const attachDiagnostics = (node) => {
280
- if (diagnostics.length === 0) return node;
281
- return { ...node, diagnostics };
282
- };
283
- switch (command) {
284
- case "on": {
285
- const eventValue = roles.get("event");
286
- if (!eventValue) {
287
- if (collect) {
288
- diagnostics.push({
289
- severity: "error",
290
- code: "MISSING_EVENT_ROLE",
291
- message: "Event handler requires event role: [on event:click ...]",
292
- source: "schema"
293
- });
294
- return attachDiagnostics(
295
- createEventHandlerNode("on", roles, [], {
296
- sourceLanguage: "explicit"
297
- })
298
- );
299
- }
300
- throw new Error("Event handler requires event role: [on event:click ...]");
301
- }
302
- const body = extractStructuralBody(roles, "body", options);
303
- roles.delete("body");
304
- return attachDiagnostics(
305
- createEventHandlerNode("on", roles, body, {
306
- sourceLanguage: "explicit"
307
- })
308
- );
309
- }
310
- case "if": {
311
- const thenBranch = extractStructuralBody(roles, "then", options);
312
- const elseBranch = extractStructuralBody(roles, "else", options);
313
- roles.delete("then");
314
- roles.delete("else");
315
- return attachDiagnostics(
316
- createConditionalNode(
317
- command,
318
- roles,
319
- thenBranch,
320
- elseBranch.length > 0 ? elseBranch : void 0,
321
- {
322
- sourceLanguage: "explicit"
323
- }
324
- )
325
- );
326
- }
327
- case "repeat": {
328
- const loopBody = extractStructuralBody(roles, "loop-body", options);
329
- const loopVariantValue = roles.get("loop-variant");
330
- const variableValue = roles.get("variable");
331
- const indexVariableValue = roles.get("index-variable");
332
- roles.delete("loop-body");
333
- roles.delete("loop-variant");
334
- roles.delete("variable");
335
- roles.delete("index-variable");
336
- let variant;
337
- if (loopVariantValue && loopVariantValue.type === "literal" && typeof loopVariantValue.value === "string") {
338
- variant = loopVariantValue.value;
339
- } else if (roles.has("quantity")) {
340
- variant = "times";
341
- } else if (roles.has("source")) {
342
- variant = "for";
343
- } else if (roles.has("condition")) {
344
- variant = "while";
345
- } else {
346
- variant = "forever";
347
- }
348
- const loopVariable = variableValue?.type === "literal" && typeof variableValue.value === "string" ? variableValue.value : variableValue?.type === "expression" ? variableValue.raw : void 0;
349
- const indexVariable = indexVariableValue?.type === "literal" && typeof indexVariableValue.value === "string" ? indexVariableValue.value : void 0;
350
- return attachDiagnostics(
351
- createLoopNode(command, roles, variant, loopBody, loopVariable, indexVariable, {
352
- sourceLanguage: "explicit"
353
- })
354
- );
355
- }
356
- default:
357
- return attachDiagnostics(
358
- createCommandNode(command, roles, {
359
- sourceLanguage: "explicit"
360
- })
361
- );
362
- }
363
- }
364
- function isExplicitSyntax(input) {
365
- const trimmed = input.trim();
366
- return trimmed.startsWith("[") && trimmed.endsWith("]");
367
- }
368
- var CHAIN_KEYWORDS = /* @__PURE__ */ new Set(["then", "and", "async", "sequential"]);
369
- function splitCompoundSegments(input) {
370
- const segments = [];
371
- let chainType = null;
372
- let current = "";
373
- let bracketDepth = 0;
374
- let inString = false;
375
- let stringChar = "";
376
- for (let i = 0; i < input.length; i++) {
377
- const char = input[i];
378
- if (inString) {
379
- current += char;
380
- if (char === stringChar && input[i - 1] !== "\\") {
381
- inString = false;
382
- }
383
- continue;
384
- }
385
- if (char === '"' || char === "'") {
386
- inString = true;
387
- stringChar = char;
388
- current += char;
389
- continue;
390
- }
391
- if (char === "[") {
392
- bracketDepth++;
393
- current += char;
394
- continue;
395
- }
396
- if (char === "]") {
397
- bracketDepth--;
398
- current += char;
399
- if (bracketDepth === 0) {
400
- const rest = input.slice(i + 1);
401
- const pipeMatch = rest.match(/^\s*\|>\s*/);
402
- if (pipeMatch) {
403
- segments.push(current.trim());
404
- current = "";
405
- chainType ?? (chainType = "pipe");
406
- i += pipeMatch[0].length;
407
- continue;
408
- }
409
- const keywordMatch = rest.match(/^\s+(then|and|async|sequential)\s+/);
410
- if (keywordMatch && CHAIN_KEYWORDS.has(keywordMatch[1])) {
411
- segments.push(current.trim());
412
- current = "";
413
- chainType ?? (chainType = keywordMatch[1]);
414
- i += keywordMatch[0].length;
415
- continue;
416
- }
417
- }
418
- continue;
419
- }
420
- current += char;
421
- }
422
- if (current.trim()) {
423
- segments.push(current.trim());
424
- }
425
- return { segments, chainType };
426
- }
427
- function parseCompound(input, options) {
428
- const trimmed = input.trim();
429
- const { annotations, remainder } = extractAnnotations(trimmed);
430
- const { segments, chainType } = splitCompoundSegments(remainder);
431
- let node;
432
- if (segments.length <= 1) {
433
- node = parseExplicit(segments[0] || remainder, options);
434
- } else {
435
- const statements = segments.map((seg) => parseExplicit(seg, options));
436
- node = createCompoundNode(statements, chainType ?? "sequential");
437
- }
438
- if (annotations.length > 0) {
439
- return { ...node, annotations };
440
- }
441
- return node;
442
- }
443
- function extractAnnotations(input) {
444
- const annotations = [];
445
- let pos = 0;
446
- while (pos < input.length) {
447
- while (pos < input.length && (input[pos] === " " || input[pos] === " ")) {
448
- pos++;
449
- }
450
- if (pos >= input.length || input[pos] !== "@") {
451
- break;
452
- }
453
- pos++;
454
- const nameStart = pos;
455
- while (pos < input.length && /[A-Za-z0-9\-_]/.test(input[pos])) {
456
- pos++;
457
- }
458
- const name = input.slice(nameStart, pos);
459
- if (!name) break;
460
- let value;
461
- if (pos < input.length && input[pos] === "(") {
462
- pos++;
463
- const valueStart = pos;
464
- if (pos < input.length && (input[pos] === '"' || input[pos] === "'")) {
465
- const quote = input[pos];
466
- pos++;
467
- while (pos < input.length && input[pos] !== quote) {
468
- if (input[pos] === "\\") pos++;
469
- pos++;
470
- }
471
- pos++;
472
- value = input.slice(valueStart + 1, pos - 1);
473
- } else {
474
- while (pos < input.length && input[pos] !== ")") {
475
- pos++;
476
- }
477
- value = input.slice(valueStart, pos);
478
- }
479
- if (pos < input.length && input[pos] === ")") {
480
- pos++;
481
- }
482
- }
483
- annotations.push(value !== void 0 ? { name, value } : { name });
484
- }
485
- return { annotations, remainder: input.slice(pos).trim() };
486
- }
487
- var VERSION_HEADER_RE = /^#!lse\s+(\d+\.\d+(?:\.\d+)?)\s*$/;
488
- function parseDocument(input, options) {
489
- const lines = input.split("\n");
490
- let lseVersion = "1.0";
491
- const nodes = [];
492
- let startLine = 0;
493
- for (let i = 0; i < lines.length; i++) {
494
- const trimmed = lines[i].trim();
495
- if (!trimmed) continue;
496
- const versionMatch = trimmed.match(VERSION_HEADER_RE);
497
- if (versionMatch) {
498
- lseVersion = versionMatch[1];
499
- startLine = i + 1;
500
- }
501
- break;
502
- }
503
- for (let i = startLine; i < lines.length; i++) {
504
- const trimmed = lines[i].trim();
505
- if (!trimmed) continue;
506
- if (trimmed.startsWith("//") || trimmed.startsWith("#") && !trimmed.startsWith("#!")) {
507
- continue;
508
- }
509
- nodes.push(parseCompound(trimmed, options));
510
- }
511
- return { lseVersion, nodes };
512
- }
513
- function isCompoundSyntax(input) {
514
- const { segments } = splitCompoundSegments(input.trim());
515
- return segments.length > 1;
516
- }
517
- function isDocumentSyntax(input) {
518
- const trimmed = input.trim();
519
- if (trimmed.includes("\n")) return true;
520
- if (VERSION_HEADER_RE.test(trimmed)) return true;
521
- if (trimmed.startsWith("//") || trimmed.startsWith("#") && !trimmed.startsWith("#!")) {
522
- return true;
523
- }
524
- return false;
525
- }
526
- function extractStructuralBody(roles, roleName, options) {
527
- const value = roles.get(roleName);
528
- if (!value) return [];
529
- if (value.type === "expression") {
530
- return [parseExplicit(value.raw, options)];
531
- }
532
- return [];
533
- }
534
- function inferSelectorKind(value) {
535
- if (!value) return void 0;
536
- if (value.startsWith("#")) return "id";
537
- if (value.startsWith(".")) return "class";
538
- if (value.startsWith("[")) return "attribute";
539
- if (value.startsWith("<") || value.startsWith("*")) return "element";
540
- if (/[>+~ ]/.test(value) && value.length > 1) return "complex";
541
- return void 0;
542
- }
543
- function tokenizeExplicit(content) {
544
- const tokens = [];
545
- let current = "";
546
- let inString = false;
547
- let stringChar = "";
548
- let bracketDepth = 0;
549
- for (let i = 0; i < content.length; i++) {
550
- const char = content[i];
551
- if (inString) {
552
- current += char;
553
- if (char === stringChar && content[i - 1] !== "\\") {
554
- inString = false;
555
- }
556
- continue;
557
- }
558
- if (char === '"' || char === "'") {
559
- inString = true;
560
- stringChar = char;
561
- current += char;
562
- continue;
563
- }
564
- if (char === "[") {
565
- bracketDepth++;
566
- current += char;
567
- continue;
568
- }
569
- if (char === "]") {
570
- bracketDepth--;
571
- current += char;
572
- continue;
573
- }
574
- if (char === " " && bracketDepth === 0) {
575
- if (current) {
576
- tokens.push(current);
577
- current = "";
578
- }
579
- continue;
580
- }
581
- current += char;
582
- }
583
- if (current) {
584
- tokens.push(current);
585
- }
586
- return tokens;
587
- }
588
- function parseExplicitValue(valueStr, referenceSet) {
589
- if (valueStr.startsWith("#") || valueStr.startsWith(".") || valueStr.startsWith("[") || valueStr.startsWith("@") || valueStr.startsWith("*")) {
590
- return createSelector(valueStr, inferSelectorKind(valueStr));
591
- }
592
- if (valueStr.startsWith('"') || valueStr.startsWith("'")) {
593
- const inner = valueStr.slice(1, -1);
594
- return createLiteral(inner, "string");
595
- }
596
- if (valueStr === "true") return createLiteral(true, "boolean");
597
- if (valueStr === "false") return createLiteral(false, "boolean");
598
- const lowerRef = valueStr.toLowerCase();
599
- if (isValidReference(lowerRef, referenceSet)) {
600
- return createReference(lowerRef);
601
- }
602
- const numMatch = valueStr.match(/^(-?\d+(?:\.\d+)?)(ms|s|m|h)?$/);
603
- if (numMatch) {
604
- const num = parseFloat(numMatch[1]);
605
- const suffix = numMatch[2];
606
- if (suffix) {
607
- return createLiteral(valueStr, "duration");
608
- }
609
- return createLiteral(num, "number");
610
- }
611
- return createLiteral(valueStr);
612
- }
613
- function findMatchingBracket(str, start) {
614
- let depth = 0;
615
- for (let i = start; i < str.length; i++) {
616
- if (str[i] === "[") depth++;
617
- else if (str[i] === "]") {
618
- depth--;
619
- if (depth === 0) return i;
620
- }
621
- }
622
- return str.length - 1;
623
- }
5
+ import {
6
+ STRUCTURAL_ROLES,
7
+ parseExplicit,
8
+ isExplicitSyntax,
9
+ parseCompound,
10
+ parseDocument,
11
+ isCompoundSyntax,
12
+ isDocumentSyntax
13
+ } from "@lokascript/intent";
624
14
 
625
15
  // src/ir/explicit-renderer.ts
626
- function renderExplicit(node) {
627
- let result;
628
- if (node.kind === "compound") {
629
- const compoundNode = node;
630
- const renderedStatements = compoundNode.statements.map((stmt) => renderExplicit(stmt));
631
- result = renderedStatements.join(` ${compoundNode.chainType} `);
632
- } else {
633
- result = renderBracketCommand(node);
634
- }
635
- if (node.annotations && node.annotations.length > 0) {
636
- const annParts = node.annotations.map(
637
- (ann) => ann.value !== void 0 ? `@${ann.name}(${ann.value})` : `@${ann.name}`
638
- );
639
- return annParts.join(" ") + " " + result;
640
- }
641
- return result;
642
- }
643
- function renderDocument(envelope) {
644
- const lines = [];
645
- if (envelope.lseVersion && envelope.lseVersion !== "1.0") {
646
- lines.push(`#!lse ${envelope.lseVersion}`);
647
- }
648
- for (const node of envelope.nodes) {
649
- lines.push(renderExplicit(node));
650
- }
651
- return lines.join("\n");
652
- }
653
- function renderBracketCommand(node) {
654
- const parts = [node.action];
655
- for (const [role, value] of node.roles) {
656
- if (value.type === "flag") {
657
- parts.push(value.enabled ? `+${value.name}` : `~${value.name}`);
658
- } else {
659
- parts.push(`${role}:${valueToString(value)}`);
660
- }
661
- }
662
- if (node.kind === "event-handler") {
663
- const eventNode = node;
664
- if (eventNode.body && eventNode.body.length > 0) {
665
- const bodyParts = eventNode.body.map((n) => renderExplicit(n));
666
- parts.push(`body:${bodyParts.join(" ")}`);
667
- }
668
- }
669
- if (node.kind === "conditional") {
670
- const condNode = node;
671
- if (condNode.thenBranch && condNode.thenBranch.length > 0) {
672
- const thenParts = condNode.thenBranch.map((n) => renderExplicit(n));
673
- parts.push(`then:${thenParts.join(" ")}`);
674
- }
675
- if (condNode.elseBranch && condNode.elseBranch.length > 0) {
676
- const elseParts = condNode.elseBranch.map((n) => renderExplicit(n));
677
- parts.push(`else:${elseParts.join(" ")}`);
678
- }
679
- }
680
- if (node.kind === "loop") {
681
- const loopNode = node;
682
- if (loopNode.loopVariant) {
683
- parts.push(`loop-variant:${loopNode.loopVariant}`);
684
- }
685
- if (loopNode.loopVariable) {
686
- parts.push(`variable:${loopNode.loopVariable}`);
687
- }
688
- if (loopNode.indexVariable) {
689
- parts.push(`index-variable:${loopNode.indexVariable}`);
690
- }
691
- if (loopNode.body && loopNode.body.length > 0) {
692
- const bodyParts = loopNode.body.map((n) => renderExplicit(n));
693
- parts.push(`loop-body:${bodyParts.join(" ")}`);
694
- }
695
- }
696
- if (node.kind === "command") {
697
- const cmd = node;
698
- if (cmd.body && cmd.body.length > 0) {
699
- const bodyParts = cmd.body.map((n) => renderExplicit(n));
700
- parts.push(`body:${bodyParts.join(" ")}`);
701
- }
702
- if (cmd.catchBranch && cmd.catchBranch.length > 0) {
703
- const catchParts = cmd.catchBranch.map((n) => renderExplicit(n));
704
- parts.push(`catch:${catchParts.join(" ")}`);
705
- }
706
- if (cmd.finallyBranch && cmd.finallyBranch.length > 0) {
707
- const finallyParts = cmd.finallyBranch.map((n) => renderExplicit(n));
708
- parts.push(`finally:${finallyParts.join(" ")}`);
709
- }
710
- if (cmd.asyncVariant) {
711
- parts.push(`async-variant:${cmd.asyncVariant}`);
712
- }
713
- if (cmd.asyncBody && cmd.asyncBody.length > 0) {
714
- const asyncParts = cmd.asyncBody.map((n) => renderExplicit(n));
715
- parts.push(`async-body:${asyncParts.join(" ")}`);
716
- }
717
- }
718
- return `[${parts.join(" ")}]`;
719
- }
720
- function valueToString(value) {
721
- switch (value.type) {
722
- case "literal":
723
- if (typeof value.value === "string") {
724
- if (value.dataType === "string" || /\s/.test(value.value)) {
725
- return `"${value.value}"`;
726
- }
727
- return value.value;
728
- }
729
- return String(value.value);
730
- case "selector":
731
- return value.value;
732
- case "reference":
733
- return value.value;
734
- case "property-path":
735
- return `${valueToString(value.object)}'s ${value.property}`;
736
- case "expression":
737
- return value.raw;
738
- case "flag":
739
- return value.name;
740
- }
741
- }
16
+ import { renderExplicit, renderDocument } from "@lokascript/intent";
17
+
18
+ // src/core/types.ts
19
+ import {
20
+ createLiteral,
21
+ createSelector,
22
+ createReference,
23
+ createPropertyPath,
24
+ createExpression,
25
+ createFlag,
26
+ createCommandNode,
27
+ createEventHandlerNode,
28
+ createConditionalNode,
29
+ createCompoundNode,
30
+ createLoopNode,
31
+ createTryNode,
32
+ createAsyncNode,
33
+ createMatchNode,
34
+ extractValue,
35
+ extractRoleValue,
36
+ getRoleValue
37
+ } from "@lokascript/intent";
742
38
 
743
39
  // src/ir/json-schema.ts
744
40
  var VALID_VALUE_TYPES = /* @__PURE__ */ new Set([
@@ -910,527 +206,14 @@ function detectSelectorKind(selector) {
910
206
  }
911
207
 
912
208
  // src/ir/protocol-json.ts
913
- function toProtocolJSON(node) {
914
- const roles = rolesToProtocol(node.roles);
915
- let result;
916
- switch (node.kind) {
917
- case "command": {
918
- const cmd = node;
919
- result = { kind: "command", action: node.action, roles };
920
- if (cmd.body && cmd.body.length > 0) {
921
- result.body = cmd.body.map(toProtocolJSON);
922
- }
923
- if (cmd.catchBranch && cmd.catchBranch.length > 0) {
924
- result.catchBranch = cmd.catchBranch.map(toProtocolJSON);
925
- }
926
- if (cmd.finallyBranch && cmd.finallyBranch.length > 0) {
927
- result.finallyBranch = cmd.finallyBranch.map(toProtocolJSON);
928
- }
929
- if (cmd.asyncVariant) {
930
- result.asyncVariant = cmd.asyncVariant;
931
- }
932
- if (cmd.asyncBody && cmd.asyncBody.length > 0) {
933
- result.asyncBody = cmd.asyncBody.map(toProtocolJSON);
934
- }
935
- if (cmd.arms && cmd.arms.length > 0) {
936
- result.arms = cmd.arms.map((arm) => ({
937
- pattern: valueToProtocol(arm.pattern),
938
- body: arm.body.map(toProtocolJSON)
939
- }));
940
- }
941
- if (cmd.defaultArm && cmd.defaultArm.length > 0) {
942
- result.defaultArm = cmd.defaultArm.map(toProtocolJSON);
943
- }
944
- break;
945
- }
946
- case "event-handler": {
947
- const eh = node;
948
- result = {
949
- kind: "event-handler",
950
- action: node.action,
951
- roles,
952
- body: eh.body.map(toProtocolJSON)
953
- };
954
- break;
955
- }
956
- case "compound": {
957
- const c = node;
958
- result = {
959
- kind: "compound",
960
- action: "compound",
961
- statements: c.statements.map(toProtocolJSON),
962
- chainType: c.chainType
963
- };
964
- break;
965
- }
966
- case "conditional": {
967
- const cond = node;
968
- result = { kind: "command", action: node.action, roles };
969
- result.thenBranch = cond.thenBranch.map(toProtocolJSON);
970
- if (cond.elseBranch && cond.elseBranch.length > 0) {
971
- result.elseBranch = cond.elseBranch.map(toProtocolJSON);
972
- }
973
- break;
974
- }
975
- case "loop": {
976
- const loop = node;
977
- result = { kind: "command", action: node.action, roles };
978
- result.loopVariant = loop.loopVariant;
979
- result.loopBody = loop.body.map(toProtocolJSON);
980
- if (loop.loopVariable) result.loopVariable = loop.loopVariable;
981
- if (loop.indexVariable) result.indexVariable = loop.indexVariable;
982
- break;
983
- }
984
- }
985
- if (node.annotations && node.annotations.length > 0) {
986
- result.annotations = node.annotations.map(
987
- (a) => a.value !== void 0 ? { name: a.name, value: a.value } : { name: a.name }
988
- );
989
- }
990
- if (node.diagnostics && node.diagnostics.length > 0) {
991
- result.diagnostics = node.diagnostics.map((d) => {
992
- const json = {
993
- level: d.severity,
994
- message: d.message
995
- };
996
- if (d.code) json.code = d.code;
997
- if (d.source) json.source = d.source;
998
- if (d.suggestions && d.suggestions.length > 0) json.suggestions = [...d.suggestions];
999
- return json;
1000
- });
1001
- }
1002
- return result;
1003
- }
1004
- function rolesToProtocol(roles) {
1005
- const result = {};
1006
- for (const [role, value] of roles) {
1007
- result[role] = valueToProtocol(value);
1008
- }
1009
- return result;
1010
- }
1011
- function valueToProtocol(value) {
1012
- switch (value.type) {
1013
- case "selector": {
1014
- const PROTOCOL_SELECTOR_KINDS = /* @__PURE__ */ new Set([
1015
- "id",
1016
- "class",
1017
- "attribute",
1018
- "element",
1019
- "complex"
1020
- ]);
1021
- const sk = "selectorKind" in value ? value.selectorKind : void 0;
1022
- const sv = { type: "selector", value: value.value };
1023
- if (sk && PROTOCOL_SELECTOR_KINDS.has(sk)) {
1024
- sv.selectorKind = sk;
1025
- }
1026
- return sv;
1027
- }
1028
- case "literal":
1029
- return value.dataType ? { type: "literal", value: value.value, dataType: value.dataType } : { type: "literal", value: value.value };
1030
- case "reference":
1031
- return { type: "reference", value: value.value };
1032
- case "expression":
1033
- return { type: "expression", raw: value.raw };
1034
- case "property-path":
1035
- return { type: "expression", raw: extractValue(value) };
1036
- case "flag":
1037
- return { type: "flag", name: value.name, enabled: value.enabled };
1038
- }
1039
- }
1040
- function fromProtocolJSON(json) {
1041
- const roles = new Map(
1042
- Object.entries(json.roles ?? {}).map(([role, val]) => [role, valueFromProtocol(val)])
1043
- );
1044
- const annotations = json.annotations?.map(
1045
- (a) => a.value !== void 0 ? { name: a.name, value: a.value } : { name: a.name }
1046
- );
1047
- const diagnostics = json.diagnostics?.map((d) => ({
1048
- severity: d.level,
1049
- message: d.message,
1050
- ...d.code ? { code: d.code } : {},
1051
- ...d.source ? { source: d.source } : {},
1052
- ...d.suggestions && d.suggestions.length > 0 ? { suggestions: d.suggestions } : {}
1053
- }));
1054
- if (json.trigger) {
1055
- const eventName = json.trigger.event;
1056
- roles.set("event", createLiteral(eventName, "string"));
1057
- const bodyRoles = new Map(roles);
1058
- bodyRoles.delete("event");
1059
- const bodyNode = createCommandNode(json.action, bodyRoles);
1060
- const node2 = createEventHandlerNode(
1061
- "on",
1062
- roles,
1063
- [bodyNode],
1064
- { sourceLanguage: "json" },
1065
- json.trigger.modifiers ?? {}
1066
- );
1067
- return applyV12Metadata(node2, annotations, diagnostics);
1068
- }
1069
- const kind = json.kind ?? "command";
1070
- if (kind === "event-handler") {
1071
- const body = (json.body ?? []).map(fromProtocolJSON);
1072
- const node2 = createEventHandlerNode(json.action, roles, body);
1073
- return applyV12Metadata(node2, annotations, diagnostics);
1074
- }
1075
- if (kind === "compound") {
1076
- const statements = (json.statements ?? []).map(fromProtocolJSON);
1077
- const node2 = createCompoundNode(
1078
- statements,
1079
- json.chainType ?? "sequential"
1080
- );
1081
- return applyV12Metadata(node2, annotations, diagnostics);
1082
- }
1083
- if (json.thenBranch && json.thenBranch.length > 0) {
1084
- const thenBranch = json.thenBranch.map(fromProtocolJSON);
1085
- const elseBranch = json.elseBranch ? json.elseBranch.map(fromProtocolJSON) : void 0;
1086
- const node2 = createConditionalNode(json.action, roles, thenBranch, elseBranch);
1087
- return applyV12Metadata(node2, annotations, diagnostics);
1088
- }
1089
- if (json.loopVariant) {
1090
- const loopBody = (json.loopBody ?? []).map(fromProtocolJSON);
1091
- const node2 = createLoopNode(
1092
- json.action,
1093
- roles,
1094
- json.loopVariant,
1095
- loopBody,
1096
- json.loopVariable,
1097
- json.indexVariable
1098
- );
1099
- return applyV12Metadata(node2, annotations, diagnostics);
1100
- }
1101
- if (json.catchBranch || json.finallyBranch) {
1102
- const body = (json.body ?? []).map(fromProtocolJSON);
1103
- const catchBranch = json.catchBranch ? json.catchBranch.map(fromProtocolJSON) : void 0;
1104
- const finallyBranch = json.finallyBranch ? json.finallyBranch.map(fromProtocolJSON) : void 0;
1105
- const node2 = createTryNode(body, catchBranch, finallyBranch);
1106
- return applyV12Metadata(node2, annotations, diagnostics);
1107
- }
1108
- if (json.asyncVariant) {
1109
- const asyncBody = (json.asyncBody ?? []).map(fromProtocolJSON);
1110
- const node2 = createAsyncNode(json.asyncVariant, asyncBody);
1111
- return applyV12Metadata(node2, annotations, diagnostics);
1112
- }
1113
- if (json.arms && json.arms.length > 0) {
1114
- const arms = json.arms.map((arm) => ({
1115
- pattern: valueFromProtocol(arm.pattern),
1116
- body: arm.body.map(fromProtocolJSON)
1117
- }));
1118
- const defaultArm = json.defaultArm ? json.defaultArm.map(fromProtocolJSON) : void 0;
1119
- const node2 = createMatchNode(roles, arms, defaultArm);
1120
- return applyV12Metadata(node2, annotations, diagnostics);
1121
- }
1122
- if (json.body && json.body.length > 0 && kind === "command") {
1123
- const body = json.body.map(fromProtocolJSON);
1124
- const node2 = createTryNode(body);
1125
- return applyV12Metadata(node2, annotations, diagnostics);
1126
- }
1127
- const node = createCommandNode(json.action, roles);
1128
- return applyV12Metadata(node, annotations, diagnostics);
1129
- }
1130
- function applyV12Metadata(node, annotations, diagnostics) {
1131
- if ((!annotations || annotations.length === 0) && (!diagnostics || diagnostics.length === 0)) {
1132
- return node;
1133
- }
1134
- return {
1135
- ...node,
1136
- ...annotations && annotations.length > 0 ? { annotations } : {},
1137
- ...diagnostics && diagnostics.length > 0 ? { diagnostics } : {}
1138
- };
1139
- }
1140
- function valueFromProtocol(value) {
1141
- switch (value.type) {
1142
- case "selector":
1143
- return createSelector(String(value.value ?? ""), value.selectorKind);
1144
- case "literal":
1145
- return createLiteral(
1146
- value.value !== void 0 ? value.value : "",
1147
- value.dataType
1148
- );
1149
- case "reference":
1150
- return createReference(String(value.value ?? ""));
1151
- case "expression":
1152
- return createExpression(value.raw ?? String(value.value ?? ""));
1153
- case "flag":
1154
- return createFlag(
1155
- value.name ?? String(value.value ?? ""),
1156
- value.enabled ?? (typeof value.value === "boolean" ? value.value : true)
1157
- );
1158
- case "property-path":
1159
- return createExpression(String(value.value ?? value.raw ?? ""));
1160
- }
1161
- }
1162
- function toEnvelopeJSON(envelope) {
1163
- const result = {
1164
- lseVersion: envelope.lseVersion,
1165
- nodes: envelope.nodes.map(toProtocolJSON)
1166
- };
1167
- if (envelope.features && envelope.features.length > 0) {
1168
- result.features = [...envelope.features];
1169
- }
1170
- return result;
1171
- }
1172
- function fromEnvelopeJSON(json) {
1173
- return {
1174
- lseVersion: json.lseVersion,
1175
- ...json.features && json.features.length > 0 ? { features: json.features } : {},
1176
- nodes: json.nodes.map(fromProtocolJSON)
1177
- };
1178
- }
1179
- function isEnvelope(json) {
1180
- return typeof json === "object" && json !== null && "lseVersion" in json && "nodes" in json && Array.isArray(json.nodes);
1181
- }
1182
- var VALID_KINDS = /* @__PURE__ */ new Set(["command", "event-handler", "compound"]);
1183
- var VALID_VALUE_TYPES2 = /* @__PURE__ */ new Set([
1184
- "selector",
1185
- "literal",
1186
- "reference",
1187
- "expression",
1188
- "property-path",
1189
- "flag"
1190
- ]);
1191
- var VALID_CHAIN_TYPES = /* @__PURE__ */ new Set(["then", "and", "async", "sequential", "pipe"]);
1192
- var VALID_ASYNC_VARIANTS = /* @__PURE__ */ new Set(["all", "race"]);
1193
- var VALID_DIAGNOSTIC_LEVELS = /* @__PURE__ */ new Set(["error", "warning", "info"]);
1194
- function validateProtocolJSON(json) {
1195
- const diagnostics = [];
1196
- if (typeof json !== "object" || json === null) {
1197
- return [{ severity: "error", code: "INVALID_ROOT", message: "Root must be a JSON object" }];
1198
- }
1199
- const node = json;
1200
- const kind = "kind" in node ? String(node.kind) : "command";
1201
- if ("kind" in node && !VALID_KINDS.has(kind)) {
1202
- diagnostics.push({
1203
- severity: "error",
1204
- code: "INVALID_KIND",
1205
- message: `Invalid kind "${node.kind}". Must be one of: command, event-handler, compound`
1206
- });
1207
- }
1208
- if (!("action" in node)) {
1209
- diagnostics.push({
1210
- severity: "error",
1211
- code: "MISSING_ACTION",
1212
- message: "Missing required field: action"
1213
- });
1214
- } else if (typeof node.action !== "string" || node.action.length === 0) {
1215
- diagnostics.push({
1216
- severity: "error",
1217
- code: "INVALID_ACTION",
1218
- message: "action must be a non-empty string"
1219
- });
1220
- }
1221
- if (!("roles" in node) && kind !== "compound") {
1222
- diagnostics.push({
1223
- severity: "error",
1224
- code: "MISSING_ROLES",
1225
- message: "Missing required field: roles"
1226
- });
1227
- } else if ("roles" in node && (typeof node.roles !== "object" || node.roles === null || Array.isArray(node.roles))) {
1228
- diagnostics.push({
1229
- severity: "error",
1230
- code: "INVALID_ROLES",
1231
- message: "roles must be a plain object"
1232
- });
1233
- } else if ("roles" in node && node.roles) {
1234
- for (const [role, value] of Object.entries(node.roles)) {
1235
- if (typeof value !== "object" || value === null) {
1236
- diagnostics.push({
1237
- severity: "error",
1238
- code: "INVALID_ROLE_VALUE",
1239
- message: `Role "${role}" value must be an object`
1240
- });
1241
- continue;
1242
- }
1243
- const v = value;
1244
- if (!("type" in v) || !VALID_VALUE_TYPES2.has(String(v.type))) {
1245
- diagnostics.push({
1246
- severity: "error",
1247
- code: "INVALID_VALUE_TYPE",
1248
- message: `Role "${role}" has invalid type "${v.type}"`
1249
- });
1250
- }
1251
- if (v.type === "flag") {
1252
- const hasName = typeof v.name === "string";
1253
- const hasEnabled = typeof v.enabled === "boolean";
1254
- const hasValue = "value" in v;
1255
- if (!hasName && !hasValue) {
1256
- diagnostics.push({
1257
- severity: "error",
1258
- code: "MISSING_FLAG_NAME",
1259
- message: `Flag role "${role}" missing required field: name (or value)`
1260
- });
1261
- }
1262
- if (!hasEnabled && !hasValue) {
1263
- diagnostics.push({
1264
- severity: "error",
1265
- code: "MISSING_FLAG_ENABLED",
1266
- message: `Flag role "${role}" missing required field: enabled (or value)`
1267
- });
1268
- }
1269
- }
1270
- if (v.type === "expression" && typeof v.raw !== "string" && !("value" in v)) {
1271
- diagnostics.push({
1272
- severity: "error",
1273
- code: "MISSING_EXPRESSION_RAW",
1274
- message: `Expression role "${role}" missing required field: raw (or value)`
1275
- });
1276
- }
1277
- }
1278
- }
1279
- if ("trigger" in node && node.trigger) {
1280
- const trigger = node.trigger;
1281
- if (typeof trigger !== "object" || trigger === null) {
1282
- diagnostics.push({
1283
- severity: "error",
1284
- code: "INVALID_TRIGGER",
1285
- message: "trigger must be an object"
1286
- });
1287
- } else if (!trigger.event || typeof trigger.event !== "string") {
1288
- diagnostics.push({
1289
- severity: "error",
1290
- code: "INVALID_TRIGGER",
1291
- message: "trigger.event is required and must be a non-empty string"
1292
- });
1293
- }
1294
- }
1295
- if (kind === "event-handler" && !("trigger" in node)) {
1296
- if (!("body" in node) || !Array.isArray(node.body)) {
1297
- diagnostics.push({
1298
- severity: "error",
1299
- code: "MISSING_BODY",
1300
- message: "event-handler node requires a body array"
1301
- });
1302
- }
1303
- }
1304
- if (kind === "compound") {
1305
- if (!("statements" in node) || !Array.isArray(node.statements)) {
1306
- diagnostics.push({
1307
- severity: "error",
1308
- code: "MISSING_STATEMENTS",
1309
- message: "compound node requires a statements array"
1310
- });
1311
- }
1312
- if ("chainType" in node && !VALID_CHAIN_TYPES.has(String(node.chainType))) {
1313
- diagnostics.push({
1314
- severity: "error",
1315
- code: "INVALID_CHAIN_TYPE",
1316
- message: `Invalid chainType "${node.chainType}". Must be one of: then, and, async, sequential, pipe`
1317
- });
1318
- }
1319
- }
1320
- if ("diagnostics" in node) {
1321
- if (!Array.isArray(node.diagnostics)) {
1322
- diagnostics.push({
1323
- severity: "error",
1324
- code: "INVALID_DIAGNOSTICS",
1325
- message: "diagnostics must be an array"
1326
- });
1327
- } else {
1328
- for (let i = 0; i < node.diagnostics.length; i++) {
1329
- const d = node.diagnostics[i];
1330
- if (!d || typeof d !== "object") {
1331
- diagnostics.push({
1332
- severity: "error",
1333
- code: "INVALID_DIAGNOSTIC_ENTRY",
1334
- message: `diagnostics[${i}] must be an object`
1335
- });
1336
- continue;
1337
- }
1338
- if (!VALID_DIAGNOSTIC_LEVELS.has(String(d.level))) {
1339
- diagnostics.push({
1340
- severity: "error",
1341
- code: "INVALID_DIAGNOSTIC_LEVEL",
1342
- message: `diagnostics[${i}].level must be "error", "warning", or "info"`
1343
- });
1344
- }
1345
- if (typeof d.message !== "string") {
1346
- diagnostics.push({
1347
- severity: "error",
1348
- code: "MISSING_DIAGNOSTIC_MESSAGE",
1349
- message: `diagnostics[${i}] missing required field: message`
1350
- });
1351
- }
1352
- if ("code" in d && typeof d.code !== "string") {
1353
- diagnostics.push({
1354
- severity: "warning",
1355
- code: "INVALID_DIAGNOSTIC_CODE",
1356
- message: `diagnostics[${i}].code must be a string if present`
1357
- });
1358
- }
1359
- }
1360
- }
1361
- }
1362
- if ("annotations" in node) {
1363
- if (!Array.isArray(node.annotations)) {
1364
- diagnostics.push({
1365
- severity: "error",
1366
- code: "INVALID_ANNOTATIONS",
1367
- message: "annotations must be an array"
1368
- });
1369
- } else {
1370
- for (let i = 0; i < node.annotations.length; i++) {
1371
- const a = node.annotations[i];
1372
- if (!a || typeof a !== "object") {
1373
- diagnostics.push({
1374
- severity: "error",
1375
- code: "INVALID_ANNOTATION_ENTRY",
1376
- message: `annotations[${i}] must be an object`
1377
- });
1378
- continue;
1379
- }
1380
- if (typeof a.name !== "string" || a.name.length === 0) {
1381
- diagnostics.push({
1382
- severity: "error",
1383
- code: "MISSING_ANNOTATION_NAME",
1384
- message: `annotations[${i}] missing required field: name`
1385
- });
1386
- }
1387
- }
1388
- }
1389
- }
1390
- if ("asyncVariant" in node && !VALID_ASYNC_VARIANTS.has(String(node.asyncVariant))) {
1391
- diagnostics.push({
1392
- severity: "error",
1393
- code: "INVALID_ASYNC_VARIANT",
1394
- message: `Invalid asyncVariant "${node.asyncVariant}". Must be "all" or "race"`
1395
- });
1396
- }
1397
- if ("arms" in node) {
1398
- if (!Array.isArray(node.arms)) {
1399
- diagnostics.push({
1400
- severity: "error",
1401
- code: "INVALID_ARMS",
1402
- message: "arms must be an array"
1403
- });
1404
- } else {
1405
- for (let i = 0; i < node.arms.length; i++) {
1406
- const arm = node.arms[i];
1407
- if (!arm || typeof arm !== "object") {
1408
- diagnostics.push({
1409
- severity: "error",
1410
- code: "INVALID_ARM_ENTRY",
1411
- message: `arms[${i}] must be an object`
1412
- });
1413
- continue;
1414
- }
1415
- if (!arm.pattern || typeof arm.pattern !== "object") {
1416
- diagnostics.push({
1417
- severity: "error",
1418
- code: "MISSING_ARM_PATTERN",
1419
- message: `arms[${i}] missing required field: pattern`
1420
- });
1421
- }
1422
- if (!Array.isArray(arm.body)) {
1423
- diagnostics.push({
1424
- severity: "error",
1425
- code: "MISSING_ARM_BODY",
1426
- message: `arms[${i}] missing required field: body (array)`
1427
- });
1428
- }
1429
- }
1430
- }
1431
- }
1432
- return diagnostics;
1433
- }
209
+ import {
210
+ toProtocolJSON,
211
+ fromProtocolJSON,
212
+ validateProtocolJSON,
213
+ toEnvelopeJSON,
214
+ fromEnvelopeJSON,
215
+ isEnvelope
216
+ } from "@lokascript/intent";
1434
217
 
1435
218
  // src/ir/from-interchange.ts
1436
219
  function fromInterchangeNode(node) {
@@ -1569,7 +352,7 @@ function convertValue(node) {
1569
352
  case "literal":
1570
353
  return convertLiteral(node);
1571
354
  case "selector":
1572
- return createSelector(String(node.value ?? ""), inferSelectorKind2(String(node.value ?? "")));
355
+ return createSelector(String(node.value ?? ""), inferSelectorKind(String(node.value ?? "")));
1573
356
  case "identifier":
1574
357
  return convertIdentifier(node);
1575
358
  case "variable":
@@ -1671,6 +454,33 @@ function renderExpr(node) {
1671
454
  return "";
1672
455
  }
1673
456
  }
457
+ var SCROLL_MARKERS = /* @__PURE__ */ new Set([
458
+ "to",
459
+ "of",
460
+ "the",
461
+ "top",
462
+ "bottom",
463
+ "middle",
464
+ "center",
465
+ "nearest",
466
+ "left",
467
+ "right",
468
+ "smoothly",
469
+ "instantly"
470
+ ]);
471
+ var URL_MARKERS = /* @__PURE__ */ new Set(["url"]);
472
+ var PARTIALS_IN_MARKERS = /* @__PURE__ */ new Set(["partials", "in"]);
473
+ function firstNonMarkerArg(args, markers) {
474
+ for (const a of args) {
475
+ if (a.type === "identifier") {
476
+ const ident = a;
477
+ const word = typeof ident.name === "string" ? ident.name : ident.value;
478
+ if (typeof word === "string" && markers.has(word)) continue;
479
+ }
480
+ return a;
481
+ }
482
+ return void 0;
483
+ }
1674
484
  function inferRoles(name, args, modifiers, target) {
1675
485
  const roles = {};
1676
486
  switch (name) {
@@ -1750,6 +560,25 @@ function inferRoles(name, args, modifiers, target) {
1750
560
  if (args[0]) roles.patient = convertValue(args[0]);
1751
561
  break;
1752
562
  }
563
+ // scroll [to] [position] target [smoothly|instantly] → destination=target
564
+ case "scroll": {
565
+ const t = firstNonMarkerArg(args, SCROLL_MARKERS);
566
+ if (t) roles.destination = convertValue(t);
567
+ break;
568
+ }
569
+ // push|replace url <X> → patient=X
570
+ case "push":
571
+ case "replace": {
572
+ const t = firstNonMarkerArg(args, URL_MARKERS);
573
+ if (t) roles.patient = convertValue(t);
574
+ break;
575
+ }
576
+ // process partials in <X> → patient=X
577
+ case "process": {
578
+ const t = firstNonMarkerArg(args, PARTIALS_IN_MARKERS);
579
+ if (t) roles.patient = convertValue(t);
580
+ break;
581
+ }
1753
582
  default: {
1754
583
  if (args[0]) roles.patient = convertValue(args[0]);
1755
584
  if (target) roles.destination = convertValue(target);
@@ -1758,7 +587,7 @@ function inferRoles(name, args, modifiers, target) {
1758
587
  }
1759
588
  return roles;
1760
589
  }
1761
- function inferSelectorKind2(value) {
590
+ function inferSelectorKind(value) {
1762
591
  if (!value) return void 0;
1763
592
  if (value.startsWith("#")) return "id";
1764
593
  if (value.startsWith(".")) return "class";
@@ -1769,7 +598,7 @@ function inferSelectorKind2(value) {
1769
598
  }
1770
599
  function inferValueFromString(value) {
1771
600
  if (DEFAULT_REFERENCES.has(value)) return createReference(value);
1772
- const kind = inferSelectorKind2(value);
601
+ const kind = inferSelectorKind(value);
1773
602
  if (kind) return createSelector(value, kind);
1774
603
  return createLiteral(value);
1775
604
  }