@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.
- package/dist/api/index.js +29 -516
- package/dist/api/index.js.map +1 -1
- package/dist/core/index.js +20 -128
- package/dist/core/index.js.map +1 -1
- package/dist/core/pattern-matching/index.js +19 -12
- package/dist/core/pattern-matching/index.js.map +1 -1
- package/dist/core/tokenization/index.js.map +1 -1
- package/dist/core/types.d.ts +7 -354
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +21 -129
- package/dist/core/types.js.map +1 -1
- package/dist/generation/diagnostics.d.ts +5 -115
- package/dist/generation/diagnostics.d.ts.map +1 -1
- package/dist/generation/index.js +20 -77
- package/dist/generation/index.js.map +1 -1
- package/dist/index.cjs +189 -1458
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +108 -1347
- package/dist/index.js.map +1 -1
- package/dist/interfaces/value-extractor.d.ts +20 -0
- package/dist/interfaces/value-extractor.d.ts.map +1 -1
- package/dist/ir/explicit-parser.d.ts +2 -83
- package/dist/ir/explicit-parser.d.ts.map +1 -1
- package/dist/ir/explicit-renderer.d.ts +2 -31
- package/dist/ir/explicit-renderer.d.ts.map +1 -1
- package/dist/ir/index.js +89 -1260
- package/dist/ir/index.js.map +1 -1
- package/dist/ir/protocol-json.d.ts +2 -70
- package/dist/ir/protocol-json.d.ts.map +1 -1
- package/dist/ir/references.d.ts +2 -16
- package/dist/ir/references.d.ts.map +1 -1
- package/dist/ir/types.d.ts +2 -148
- package/dist/ir/types.d.ts.map +1 -1
- package/dist/parsing/index.js +19 -12
- package/dist/parsing/index.js.map +1 -1
- package/dist/schema/command-schema.d.ts +3 -92
- package/dist/schema/command-schema.d.ts.map +1 -1
- package/dist/schema/index.js +3 -20
- package/dist/schema/index.js.map +1 -1
- package/dist/testing/index.js +1133 -755
- package/dist/testing/index.js.map +1 -1
- package/package.json +6 -2
- package/src/core/types.ts +71 -600
- package/src/generation/diagnostics.ts +12 -233
- package/src/interfaces/value-extractor.ts +32 -0
- package/src/ir/explicit-parser.ts +12 -792
- package/src/ir/explicit-renderer.ts +2 -195
- package/src/ir/from-interchange.test.ts +91 -4
- package/src/ir/from-interchange.ts +59 -0
- package/src/ir/protocol-json.test.ts +16 -7
- package/src/ir/protocol-json.ts +9 -748
- package/src/ir/references.ts +2 -30
- package/src/ir/types.ts +18 -190
- 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
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
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
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
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 ?? ""),
|
|
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
|
|
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 =
|
|
601
|
+
const kind = inferSelectorKind(value);
|
|
1773
602
|
if (kind) return createSelector(value, kind);
|
|
1774
603
|
return createLiteral(value);
|
|
1775
604
|
}
|