@hyperfixi/core 2.3.1 → 2.5.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/dom-processor.d.ts +8 -4
- package/dist/api/hyperscript-api.d.ts +5 -1
- package/dist/ast-utils/index.js +25320 -94
- package/dist/ast-utils/index.mjs +25320 -94
- package/dist/ast-utils/interchange/types.d.ts +7 -1
- package/dist/behaviors/index.js +54 -100
- package/dist/behaviors/index.mjs +54 -100
- package/dist/bundle-generator/index.js +19 -2
- package/dist/bundle-generator/index.mjs +19 -2
- package/dist/bundle-generator/parser-templates.d.ts +1 -1
- package/dist/bundle-generator/template-capabilities.d.ts +1 -1
- package/dist/chunks/bridge-Ce2mO-nk.js +2 -0
- package/dist/chunks/browser-modular-D5vPrb2X.js +2 -0
- package/dist/chunks/feature-eventsource-BpZvPy_K.js +2 -0
- package/dist/chunks/{feature-sockets-ClOH7vk7.js → feature-sockets-CrYvjZ4j.js} +2 -2
- package/dist/chunks/feature-webworker-BSYguEIW.js +2 -0
- package/dist/chunks/index-DsBHN4zW.js +2 -0
- package/dist/commands/advanced/async.d.ts +6 -2
- package/dist/commands/advanced/js.d.ts +1 -1
- package/dist/commands/animation/start-view-transition.d.ts +24 -0
- package/dist/commands/async/fetch.d.ts +6 -1
- package/dist/commands/control-flow/repeat.d.ts +2 -0
- package/dist/commands/data/clear.d.ts +23 -0
- package/dist/commands/data/set.d.ts +6 -0
- package/dist/commands/dom/close.d.ts +19 -0
- package/dist/commands/dom/empty.d.ts +19 -0
- package/dist/commands/dom/open.d.ts +21 -0
- package/dist/commands/dom/reset.d.ts +19 -0
- package/dist/commands/dom/select.d.ts +19 -0
- package/dist/commands/dom/swap.d.ts +7 -4
- package/dist/commands/events/trigger.d.ts +1 -1
- package/dist/commands/execution/blur.d.ts +19 -0
- package/dist/commands/execution/call.d.ts +1 -2
- package/dist/commands/execution/focus.d.ts +19 -0
- package/dist/commands/helpers/element-resolution.d.ts +2 -2
- package/dist/commands/helpers/event-waiting.d.ts +1 -1
- package/dist/commands/helpers/numeric-target-parser.d.ts +7 -0
- package/dist/commands/index.d.ts +34 -2
- package/dist/commands/index.js +19353 -4845
- package/dist/commands/index.mjs +19321 -4846
- package/dist/commands/navigation/go.d.ts +3 -0
- package/dist/commands/navigation/scroll-to.d.ts +26 -0
- package/dist/commands/utility/beep.d.ts +2 -2
- package/dist/commands/utility/breakpoint.d.ts +19 -0
- package/dist/commands/utility/pick.d.ts +11 -2
- package/dist/compatibility/browser-bundle-modular.d.ts +2 -2
- package/dist/compatibility/browser-bundle-multilingual.d.ts +1 -1
- package/dist/compatibility/browser-bundle-semantic-complete.d.ts +3 -3
- package/dist/compatibility/browser-bundle.d.ts +13 -6
- package/dist/compatibility/browser-modular.d.ts +1 -3
- package/dist/core/expression-evaluator.d.ts +4 -4
- package/dist/core/expression-registry.d.ts +8 -0
- package/dist/expressions/bundles/common-expressions.d.ts +2 -2
- package/dist/expressions/bundles/core-expressions.d.ts +2 -2
- package/dist/expressions/bundles/full-expressions.d.ts +2 -2
- package/dist/expressions/bundles/index.d.ts +3 -3
- package/dist/expressions/collection/index.d.ts +35 -0
- package/dist/expressions/conversion/impl/index.d.ts +1 -1
- package/dist/expressions/index.d.ts +4 -3
- package/dist/expressions/index.js +1117 -1590
- package/dist/expressions/index.mjs +1113 -1586
- package/dist/expressions/logical/index.d.ts +2 -0
- package/dist/expressions/mathematical/index.d.ts +11 -0
- package/dist/expressions/shared/index.d.ts +1 -1
- package/dist/expressions/shared/number-utils.d.ts +1 -0
- package/dist/htmx/htmx-attribute-processor.d.ts +37 -1
- package/dist/htmx/htmx-translator.d.ts +2 -0
- package/dist/htmx/i18n-hooks.d.ts +15 -0
- package/dist/htmx/i18n-orchestrator.d.ts +15 -0
- package/dist/htmx/lang-resolver.d.ts +3 -0
- package/dist/htmx/sse.d.ts +60 -0
- package/dist/htmx/ws.d.ts +59 -0
- package/dist/hyperfixi-browser-classic-i18n.js +2 -0
- package/dist/hyperfixi-browser-minimal.js +1 -0
- package/dist/hyperfixi-browser-standard.js +2 -0
- package/dist/hyperfixi-browser.js +2 -0
- package/dist/hyperfixi-classic-i18n.js +1 -1
- package/dist/hyperfixi-hx-v4.js +1 -0
- package/dist/hyperfixi-hx.js +1 -1
- package/dist/hyperfixi-hybrid-complete.js +1 -1
- package/dist/hyperfixi-hybrid-hx.js +1 -0
- package/dist/hyperfixi-minimal.js +1 -1
- package/dist/hyperfixi-multilingual.js +1 -1
- package/dist/hyperfixi-standard.js +1 -1
- package/dist/hyperfixi.js +1 -1
- package/dist/hyperfixi.mjs +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +43592 -45060
- package/dist/index.min.js +1 -1
- package/dist/index.mjs +43589 -45061
- package/dist/lib/index.d.ts +2 -2
- package/dist/lib/morph-adapter.d.ts +0 -13
- package/dist/lib/swap-executor.d.ts +0 -10
- package/dist/lib/view-transitions.d.ts +1 -30
- package/dist/lokascript-browser-classic-i18n.js +1 -1
- package/dist/lokascript-browser-minimal.js +1 -1
- package/dist/lokascript-browser-standard.js +1 -1
- package/dist/lokascript-browser.js +1 -1
- package/dist/lokascript-hybrid-complete.js +1 -1
- package/dist/lokascript-hybrid-hx.js +1 -1
- package/dist/lokascript-multilingual.js +1 -1
- package/dist/lsp-metadata.d.ts +9 -4
- package/dist/lsp-metadata.js +187 -3
- package/dist/lsp-metadata.mjs +185 -4
- package/dist/metadata.d.ts +1 -1
- package/dist/metadata.js +3 -3
- package/dist/metadata.mjs +3 -3
- package/dist/multilingual/bridge.d.ts +1 -1
- package/dist/multilingual/index.js +79 -22
- package/dist/multilingual/index.mjs +79 -22
- package/dist/parser/command-parsers/animation-commands.d.ts +1 -0
- package/dist/parser/command-parsers/utility-commands.d.ts +1 -0
- package/dist/parser/extensions.d.ts +51 -0
- package/dist/parser/full-parser.js +1223 -897
- package/dist/parser/full-parser.mjs +1223 -897
- package/dist/parser/helpers/ast-helpers.d.ts +1 -0
- package/dist/parser/helpers/parsing-helpers.d.ts +4 -0
- package/dist/parser/parser-types.d.ts +8 -28
- package/dist/parser/parser.d.ts +3 -7
- package/dist/parser/pratt-parser.d.ts +0 -3
- package/dist/parser/runtime.d.ts +4 -0
- package/dist/parser/semantic-integration.d.ts +17 -0
- package/dist/parser/types.d.ts +7 -1
- package/dist/reference/index.js +91 -0
- package/dist/reference/index.mjs +91 -0
- package/dist/registry/index.js +12867 -5876
- package/dist/registry/index.mjs +12867 -5876
- package/dist/registry/universal-types.d.ts +2 -1
- package/dist/runtime/command-adapter.d.ts +23 -16
- package/dist/runtime/plugin.d.ts +14 -0
- package/dist/runtime/runtime-base.d.ts +32 -7
- package/dist/runtime/runtime-factory.d.ts +3 -3
- package/dist/runtime/runtime.d.ts +2 -2
- package/dist/test-setup.d.ts +1 -0
- package/dist/types/base-types.d.ts +3 -0
- package/dist/types/feature-types.d.ts +1 -1
- package/dist/types/index.d.ts +2 -2
- package/package.json +29 -23
- package/vocab/htmx/ar.js +60 -0
- package/vocab/htmx/bn.js +49 -0
- package/vocab/htmx/de.js +60 -0
- package/vocab/htmx/en.js +21 -0
- package/vocab/htmx/es.js +60 -0
- package/vocab/htmx/fr.js +59 -0
- package/vocab/htmx/he.js +40 -0
- package/vocab/htmx/hi.js +60 -0
- package/vocab/htmx/id.js +57 -0
- package/vocab/htmx/it.js +58 -0
- package/vocab/htmx/ja.js +60 -0
- package/vocab/htmx/ko.js +60 -0
- package/vocab/htmx/ms.js +35 -0
- package/vocab/htmx/pl.js +60 -0
- package/vocab/htmx/pt.js +60 -0
- package/vocab/htmx/qu.js +60 -0
- package/vocab/htmx/ru.js +60 -0
- package/vocab/htmx/sw.js +59 -0
- package/vocab/htmx/th.js +49 -0
- package/vocab/htmx/tl.js +33 -0
- package/vocab/htmx/tr.js +60 -0
- package/vocab/htmx/uk.js +60 -0
- package/vocab/htmx/vi.js +51 -0
- package/vocab/htmx/zh.js +60 -0
- package/dist/bundles/test-minimal.d.ts +0 -3
- package/dist/bundles/test-standard.d.ts +0 -3
- package/dist/chunks/bridge-Clbh_xAj.js +0 -2
- package/dist/chunks/browser-modular-DIOxQqhV.js +0 -2
- package/dist/chunks/feature-eventsource-B5F2-H1r.js +0 -2
- package/dist/chunks/feature-webworker-3bAp0ac9.js +0 -2
- package/dist/chunks/index-DcxoRUBe.js +0 -2
- package/dist/compatibility/browser-bundle-minimal.d.ts +0 -8
- package/dist/compatibility/browser-bundle-standard.d.ts +0 -8
- package/dist/compatibility/hyperscript-tests/test-adapter.d.ts +0 -13
- package/dist/core/base-expression-evaluator.d.ts +0 -74
- package/dist/core/binary-expression-evaluator.d.ts +0 -7
- package/dist/core/call-expression-evaluator.d.ts +0 -7
- package/dist/core/configurable-expression-evaluator.d.ts +0 -5
- package/dist/core/lazy-expression-evaluator.d.ts +0 -22
- package/dist/core/parser.d.ts +0 -21
- package/dist/core/selector-evaluator.d.ts +0 -15
- package/dist/core/template-literal-evaluator.d.ts +0 -5
- package/dist/expressions/comparison/index.d.ts +0 -80
- package/dist/expressions/comparison/utils.d.ts +0 -2
- package/dist/expressions/conversion/impl/bridge.d.ts +0 -117
- package/dist/expressions/logical/impl/pattern-matching.d.ts +0 -58
- package/dist/expressions/positional/impl/bridge.d.ts +0 -95
- package/dist/expressions/property/index.d.ts +0 -55
- package/dist/expressions/references/impl/bridge.d.ts +0 -54
- package/dist/extensions/index.d.ts +0 -3
- package/dist/extensions/tailwind.d.ts +0 -22
- package/dist/mod.d.ts +0 -63
- package/dist/parser/expression-parser.d.ts +0 -6
- package/dist/runtime/runtime-experimental.d.ts +0 -18
- package/dist/scripts/code-generator.d.ts +0 -64
- package/dist/scripts/generate-missing-commands.d.ts +0 -4
|
@@ -1,78 +1,12 @@
|
|
|
1
|
-
function
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function checkDebugEnabled() {
|
|
9
|
-
if (typeof localStorage !== 'undefined') {
|
|
10
|
-
try {
|
|
11
|
-
const ls = localStorage;
|
|
12
|
-
const setting = ls.getItem('hyperfixi:debug') || ls.getItem('lokascript:debug');
|
|
13
|
-
if (setting === '*' || setting === 'true')
|
|
14
|
-
return true;
|
|
15
|
-
}
|
|
16
|
-
catch {
|
|
1
|
+
function createExpressionRegistry(...categories) {
|
|
2
|
+
const map = new Map();
|
|
3
|
+
for (const category of categories) {
|
|
4
|
+
for (const [name, impl] of Object.entries(category)) {
|
|
5
|
+
map.set(name, impl);
|
|
17
6
|
}
|
|
18
7
|
}
|
|
19
|
-
|
|
20
|
-
return true;
|
|
21
|
-
}
|
|
22
|
-
if (typeof process !== 'undefined' && process.env?.HYPERFIXI_DEBUG === 'true') {
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
return false;
|
|
8
|
+
return map;
|
|
26
9
|
}
|
|
27
|
-
const isDebugEnabled = checkDebugEnabled();
|
|
28
|
-
const DEBUG = {
|
|
29
|
-
commands: isDebugEnabled,
|
|
30
|
-
events: isDebugEnabled,
|
|
31
|
-
parsing: isDebugEnabled,
|
|
32
|
-
expressions: isDebugEnabled,
|
|
33
|
-
styles: isDebugEnabled,
|
|
34
|
-
runtime: isDebugEnabled,
|
|
35
|
-
loops: isDebugEnabled,
|
|
36
|
-
async: isDebugEnabled,
|
|
37
|
-
};
|
|
38
|
-
const debug = {
|
|
39
|
-
command: (...args) => {
|
|
40
|
-
if (DEBUG.commands)
|
|
41
|
-
console.log('🔧', ...args);
|
|
42
|
-
},
|
|
43
|
-
event: (...args) => {
|
|
44
|
-
if (DEBUG.events)
|
|
45
|
-
console.log('🎯', ...args);
|
|
46
|
-
},
|
|
47
|
-
parse: (...args) => {
|
|
48
|
-
if (DEBUG.parsing)
|
|
49
|
-
console.log('📝', ...args);
|
|
50
|
-
},
|
|
51
|
-
expr: (...args) => {
|
|
52
|
-
if (DEBUG.expressions)
|
|
53
|
-
console.log('🔍', ...args);
|
|
54
|
-
},
|
|
55
|
-
expressions: (...args) => {
|
|
56
|
-
if (DEBUG.expressions)
|
|
57
|
-
console.log('🔍', ...args);
|
|
58
|
-
},
|
|
59
|
-
style: (...args) => {
|
|
60
|
-
if (DEBUG.styles)
|
|
61
|
-
console.log('🎨', ...args);
|
|
62
|
-
},
|
|
63
|
-
runtime: (...args) => {
|
|
64
|
-
if (DEBUG.runtime)
|
|
65
|
-
console.log('🚀', ...args);
|
|
66
|
-
},
|
|
67
|
-
loop: (...args) => {
|
|
68
|
-
if (DEBUG.loops)
|
|
69
|
-
console.log('🔁', ...args);
|
|
70
|
-
},
|
|
71
|
-
async: (...args) => {
|
|
72
|
-
if (DEBUG.async)
|
|
73
|
-
console.log('⏳', ...args);
|
|
74
|
-
},
|
|
75
|
-
};
|
|
76
10
|
|
|
77
11
|
class ExpressionTypeRegistry {
|
|
78
12
|
constructor(config = {}) {
|
|
@@ -327,1473 +261,104 @@ class ExpressionTypeRegistry {
|
|
|
327
261
|
isType: (v) => typeof v === 'object' && v !== null && !Array.isArray(v) && !(v instanceof Element),
|
|
328
262
|
defaultValue: {},
|
|
329
263
|
description: 'Plain JavaScript object',
|
|
330
|
-
coerceFrom: {
|
|
331
|
-
String: v => {
|
|
332
|
-
try {
|
|
333
|
-
const parsed = JSON.parse(v);
|
|
334
|
-
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
|
|
335
|
-
return parsed;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
catch {
|
|
339
|
-
}
|
|
340
|
-
return null;
|
|
341
|
-
},
|
|
342
|
-
Array: v => {
|
|
343
|
-
const arr = v;
|
|
344
|
-
const obj = {};
|
|
345
|
-
arr.forEach((item, index) => {
|
|
346
|
-
obj[String(index)] = item;
|
|
347
|
-
});
|
|
348
|
-
return obj;
|
|
349
|
-
},
|
|
350
|
-
},
|
|
351
|
-
});
|
|
352
|
-
this.register({
|
|
353
|
-
name: 'Null',
|
|
354
|
-
hyperscriptType: 'null',
|
|
355
|
-
tsType: 'null',
|
|
356
|
-
isType: (v) => v === null,
|
|
357
|
-
defaultValue: null,
|
|
358
|
-
description: 'Null value',
|
|
359
|
-
});
|
|
360
|
-
this.register({
|
|
361
|
-
name: 'Undefined',
|
|
362
|
-
hyperscriptType: 'undefined',
|
|
363
|
-
tsType: 'undefined',
|
|
364
|
-
isType: (v) => v === undefined,
|
|
365
|
-
defaultValue: undefined,
|
|
366
|
-
description: 'Undefined value',
|
|
367
|
-
});
|
|
368
|
-
this.register({
|
|
369
|
-
name: 'Function',
|
|
370
|
-
hyperscriptType: 'function',
|
|
371
|
-
tsType: 'Function',
|
|
372
|
-
isType: (v) => typeof v === 'function',
|
|
373
|
-
defaultValue: null,
|
|
374
|
-
description: 'JavaScript function',
|
|
375
|
-
});
|
|
376
|
-
this.register({
|
|
377
|
-
name: 'Event',
|
|
378
|
-
hyperscriptType: 'event',
|
|
379
|
-
tsType: 'Event',
|
|
380
|
-
isType: (v) => v instanceof Event,
|
|
381
|
-
defaultValue: null,
|
|
382
|
-
description: 'DOM Event',
|
|
383
|
-
});
|
|
384
|
-
this.register({
|
|
385
|
-
name: 'NodeList',
|
|
386
|
-
hyperscriptType: 'element-list',
|
|
387
|
-
tsType: 'NodeList',
|
|
388
|
-
isType: (v) => v instanceof NodeList,
|
|
389
|
-
defaultValue: null,
|
|
390
|
-
description: 'DOM NodeList (typically from querySelectorAll)',
|
|
391
|
-
coerceFrom: {
|
|
392
|
-
Array: v => {
|
|
393
|
-
return null;
|
|
394
|
-
},
|
|
395
|
-
},
|
|
396
|
-
});
|
|
397
|
-
this.register({
|
|
398
|
-
name: 'Unknown',
|
|
399
|
-
hyperscriptType: 'unknown',
|
|
400
|
-
tsType: 'unknown',
|
|
401
|
-
isType: (_v) => true,
|
|
402
|
-
defaultValue: null,
|
|
403
|
-
description: 'Unknown/any value type',
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
const expressionTypeRegistry = new ExpressionTypeRegistry();
|
|
408
|
-
|
|
409
|
-
function isString(value) {
|
|
410
|
-
const stringType = expressionTypeRegistry.get('String');
|
|
411
|
-
return stringType ? stringType.isType(value) : typeof value === 'string';
|
|
412
|
-
}
|
|
413
|
-
function isNumber(value) {
|
|
414
|
-
const numberType = expressionTypeRegistry.get('Number');
|
|
415
|
-
return numberType ? numberType.isType(value) : typeof value === 'number';
|
|
416
|
-
}
|
|
417
|
-
function isBoolean(value) {
|
|
418
|
-
const boolType = expressionTypeRegistry.get('Boolean');
|
|
419
|
-
return boolType ? boolType.isType(value) : typeof value === 'boolean';
|
|
420
|
-
}
|
|
421
|
-
function isObject(value) {
|
|
422
|
-
const objectType = expressionTypeRegistry.get('Object');
|
|
423
|
-
return objectType ? objectType.isType(value) : typeof value === 'object' && value !== null;
|
|
424
|
-
}
|
|
425
|
-
function isFunction(value) {
|
|
426
|
-
const funcType = expressionTypeRegistry.get('Function');
|
|
427
|
-
return funcType ? funcType.isType(value) : typeof value === 'function';
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
function isInputElement(el) {
|
|
431
|
-
return (el !== null && typeof el === 'object' && 'tagName' in el && el.tagName === 'INPUT');
|
|
432
|
-
}
|
|
433
|
-
function isFormElement(el) {
|
|
434
|
-
if (el === null || typeof el !== 'object' || !('tagName' in el)) {
|
|
435
|
-
return false;
|
|
436
|
-
}
|
|
437
|
-
const tag = el.tagName;
|
|
438
|
-
return tag === 'INPUT' || tag === 'SELECT' || tag === 'TEXTAREA';
|
|
439
|
-
}
|
|
440
|
-
function isHTMLElement$1(el) {
|
|
441
|
-
return (el !== null &&
|
|
442
|
-
typeof el === 'object' &&
|
|
443
|
-
'nodeType' in el &&
|
|
444
|
-
el.nodeType === 1 &&
|
|
445
|
-
'style' in el);
|
|
446
|
-
}
|
|
447
|
-
function isOptionElement(el) {
|
|
448
|
-
return (el !== null &&
|
|
449
|
-
typeof el !== 'undefined' &&
|
|
450
|
-
typeof el === 'object' &&
|
|
451
|
-
'tagName' in el &&
|
|
452
|
-
el.tagName === 'OPTION');
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
function isElement(value) {
|
|
456
|
-
return value instanceof Element;
|
|
457
|
-
}
|
|
458
|
-
function isHTMLElement(value) {
|
|
459
|
-
return value instanceof HTMLElement;
|
|
460
|
-
}
|
|
461
|
-
const BOOLEAN_ATTRIBUTES = new Set([
|
|
462
|
-
'disabled',
|
|
463
|
-
'readonly',
|
|
464
|
-
'required',
|
|
465
|
-
'checked',
|
|
466
|
-
'selected',
|
|
467
|
-
'hidden',
|
|
468
|
-
'open',
|
|
469
|
-
'autofocus',
|
|
470
|
-
'autoplay',
|
|
471
|
-
'controls',
|
|
472
|
-
'loop',
|
|
473
|
-
'muted',
|
|
474
|
-
'multiple',
|
|
475
|
-
'reversed',
|
|
476
|
-
'defer',
|
|
477
|
-
'async',
|
|
478
|
-
'novalidate',
|
|
479
|
-
'formnovalidate',
|
|
480
|
-
'ismap',
|
|
481
|
-
]);
|
|
482
|
-
function accessAttribute(element, attrName) {
|
|
483
|
-
if (BOOLEAN_ATTRIBUTES.has(attrName.toLowerCase())) {
|
|
484
|
-
return element.hasAttribute(attrName);
|
|
485
|
-
}
|
|
486
|
-
return element.getAttribute(attrName);
|
|
487
|
-
}
|
|
488
|
-
function hasAttribute(element, attrName) {
|
|
489
|
-
return element.hasAttribute(attrName);
|
|
490
|
-
}
|
|
491
|
-
const SPECIAL_DOM_PROPERTIES = {
|
|
492
|
-
id: el => el.id,
|
|
493
|
-
classname: el => el.className,
|
|
494
|
-
class: el => el.className,
|
|
495
|
-
tagname: el => el.tagName.toLowerCase(),
|
|
496
|
-
innertext: el => el.textContent?.trim(),
|
|
497
|
-
innerHTML: el => el.innerHTML,
|
|
498
|
-
outerhtml: el => el.outerHTML,
|
|
499
|
-
value: el => (isFormElement(el) ? el.value : undefined),
|
|
500
|
-
checked: el => (isInputElement(el) ? el.checked : undefined),
|
|
501
|
-
disabled: el => ('disabled' in el ? el.disabled : undefined),
|
|
502
|
-
selected: el => (isOptionElement(el) ? el.selected : undefined),
|
|
503
|
-
tabindex: el => (isHTMLElement$1(el) ? el.tabIndex : undefined),
|
|
504
|
-
hidden: el => (isHTMLElement$1(el) ? el.hidden : undefined),
|
|
505
|
-
style: el => getComputedStyle(el),
|
|
506
|
-
children: el => Array.from(el.children),
|
|
507
|
-
parent: el => el.parentElement,
|
|
508
|
-
firstchild: el => el.firstElementChild,
|
|
509
|
-
lastchild: el => el.lastElementChild,
|
|
510
|
-
nextsibling: el => el.nextElementSibling,
|
|
511
|
-
previoussibling: el => el.previousElementSibling,
|
|
512
|
-
values: el => collectFormValues(el),
|
|
513
|
-
};
|
|
514
|
-
function collectFormValues(el) {
|
|
515
|
-
if (el instanceof HTMLFormElement)
|
|
516
|
-
return new FormData(el);
|
|
517
|
-
const fd = new FormData();
|
|
518
|
-
el.querySelectorAll('input, select, textarea').forEach((input) => {
|
|
519
|
-
const name = input.getAttribute('name');
|
|
520
|
-
if (name && 'value' in input)
|
|
521
|
-
fd.append(name, input.value);
|
|
522
|
-
});
|
|
523
|
-
return fd;
|
|
524
|
-
}
|
|
525
|
-
function getElementProperty(element, property) {
|
|
526
|
-
if (property.startsWith('computed-')) {
|
|
527
|
-
const cssProperty = property.slice('computed-'.length);
|
|
528
|
-
if (isHTMLElement(element)) {
|
|
529
|
-
const computedStyle = getComputedStyle(element);
|
|
530
|
-
return computedStyle.getPropertyValue(cssProperty);
|
|
531
|
-
}
|
|
532
|
-
return undefined;
|
|
533
|
-
}
|
|
534
|
-
if (property.startsWith('@')) {
|
|
535
|
-
const attrName = property.slice(1);
|
|
536
|
-
return accessAttribute(element, attrName);
|
|
537
|
-
}
|
|
538
|
-
const specialHandler = SPECIAL_DOM_PROPERTIES[property.toLowerCase()];
|
|
539
|
-
if (specialHandler) {
|
|
540
|
-
return specialHandler(element);
|
|
541
|
-
}
|
|
542
|
-
if (hasAttribute(element, property)) {
|
|
543
|
-
return accessAttribute(element, property);
|
|
544
|
-
}
|
|
545
|
-
const value = element[property];
|
|
546
|
-
if (isFunction(value)) {
|
|
547
|
-
return value.bind(element);
|
|
548
|
-
}
|
|
549
|
-
return value;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
function extractPropertyName(property) {
|
|
553
|
-
if (typeof property === 'string')
|
|
554
|
-
return property;
|
|
555
|
-
if (property && typeof property === 'object') {
|
|
556
|
-
if (typeof property.name === 'string')
|
|
557
|
-
return property.name;
|
|
558
|
-
if (typeof property.value === 'string')
|
|
559
|
-
return property.value;
|
|
560
|
-
}
|
|
561
|
-
return property;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
function getDoc(context) {
|
|
565
|
-
return context.me?.ownerDocument ?? (typeof document !== 'undefined' ? document : null);
|
|
566
|
-
}
|
|
567
|
-
function evaluateSelector(node, context) {
|
|
568
|
-
let selector = node.value;
|
|
569
|
-
if (selector.startsWith('<') && selector.endsWith('/>')) {
|
|
570
|
-
selector = selector.slice(1, -2).trim();
|
|
571
|
-
}
|
|
572
|
-
const doc = getDoc(context);
|
|
573
|
-
if (!doc) {
|
|
574
|
-
return [];
|
|
575
|
-
}
|
|
576
|
-
const elements = doc.querySelectorAll(selector);
|
|
577
|
-
const isEl = (el) => el && typeof el === 'object' && el.nodeType === 1 && typeof el.tagName === 'string';
|
|
578
|
-
return Array.from(elements).filter(isEl);
|
|
579
|
-
}
|
|
580
|
-
function evaluateCSSSelector(node, context) {
|
|
581
|
-
let selector = node.selector;
|
|
582
|
-
if (selector.startsWith('<') && selector.endsWith('/>')) {
|
|
583
|
-
selector = selector.slice(1, -2).trim();
|
|
584
|
-
}
|
|
585
|
-
const doc = getDoc(context);
|
|
586
|
-
if (!doc) {
|
|
587
|
-
return node.selectorType === 'id' ? null : [];
|
|
588
|
-
}
|
|
589
|
-
if (node.selectorType === 'id') {
|
|
590
|
-
const id = selector.startsWith('#') ? selector.slice(1) : selector;
|
|
591
|
-
return doc.getElementById(id);
|
|
592
|
-
}
|
|
593
|
-
else if (node.selectorType === 'class') {
|
|
594
|
-
const escapedSelector = selector.replace(/:/g, '\\:');
|
|
595
|
-
const elements = doc.querySelectorAll(escapedSelector);
|
|
596
|
-
return Array.from(elements).filter((el) => el instanceof HTMLElement);
|
|
597
|
-
}
|
|
598
|
-
const elements = doc.querySelectorAll(selector);
|
|
599
|
-
return Array.from(elements).filter((el) => el instanceof HTMLElement);
|
|
600
|
-
}
|
|
601
|
-
function evaluateIdSelector(node, context) {
|
|
602
|
-
const doc = getDoc(context);
|
|
603
|
-
if (!doc) {
|
|
604
|
-
return null;
|
|
605
|
-
}
|
|
606
|
-
const id = node.value.startsWith('#') ? node.value.slice(1) : node.value;
|
|
607
|
-
return doc.getElementById(id);
|
|
608
|
-
}
|
|
609
|
-
function evaluateQueryReference(node, context) {
|
|
610
|
-
let selector = node.selector;
|
|
611
|
-
if (selector.startsWith('<') && selector.endsWith('/>')) {
|
|
612
|
-
selector = selector.slice(1, -2).trim();
|
|
613
|
-
}
|
|
614
|
-
const doc = getDoc(context);
|
|
615
|
-
if (!doc) {
|
|
616
|
-
return [];
|
|
617
|
-
}
|
|
618
|
-
try {
|
|
619
|
-
return doc.querySelectorAll(selector);
|
|
620
|
-
}
|
|
621
|
-
catch {
|
|
622
|
-
return doc.createDocumentFragment().childNodes;
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
function lookupTemplateVariable(name, context) {
|
|
627
|
-
if (context.locals?.has(name))
|
|
628
|
-
return context.locals.get(name);
|
|
629
|
-
if (context.globals?.has(name))
|
|
630
|
-
return context.globals.get(name);
|
|
631
|
-
if (context.variables?.has(name))
|
|
632
|
-
return context.variables.get(name);
|
|
633
|
-
if (name === 'me')
|
|
634
|
-
return context.me;
|
|
635
|
-
if (name === 'it')
|
|
636
|
-
return context.it;
|
|
637
|
-
if (name === 'result')
|
|
638
|
-
return context.result;
|
|
639
|
-
return undefined;
|
|
640
|
-
}
|
|
641
|
-
async function resolveTemplateVariable(varName, context) {
|
|
642
|
-
if (varName.includes('.')) {
|
|
643
|
-
const parts = varName.split('.');
|
|
644
|
-
let value = lookupTemplateVariable(parts[0], context);
|
|
645
|
-
for (let i = 1; i < parts.length && value != null; i++) {
|
|
646
|
-
value = value[parts[i]];
|
|
647
|
-
}
|
|
648
|
-
return value;
|
|
649
|
-
}
|
|
650
|
-
return lookupTemplateVariable(varName, context);
|
|
651
|
-
}
|
|
652
|
-
async function resolveValue(name, context) {
|
|
653
|
-
debug.expressions(`RESOLVE: Looking for '${name}' in context`, {
|
|
654
|
-
hasInLocals: context.locals.has(name),
|
|
655
|
-
localsKeys: Array.from(context.locals.keys()),
|
|
656
|
-
value: context.locals.get(name),
|
|
657
|
-
});
|
|
658
|
-
if (context.locals.has(name)) {
|
|
659
|
-
const value = context.locals.get(name);
|
|
660
|
-
debug.expressions(`RESOLVE: Found '${name}' in locals:`, value);
|
|
661
|
-
return value;
|
|
662
|
-
}
|
|
663
|
-
if (context.globals && context.globals.has(name)) {
|
|
664
|
-
const value = context.globals.get(name);
|
|
665
|
-
debug.expressions(`RESOLVE: Found '${name}' in globals:`, value);
|
|
666
|
-
return value;
|
|
667
|
-
}
|
|
668
|
-
if (context.variables && context.variables.has(name)) {
|
|
669
|
-
const value = context.variables.get(name);
|
|
670
|
-
debug.expressions(`RESOLVE: Found '${name}' in variables (legacy):`, value);
|
|
671
|
-
return value;
|
|
672
|
-
}
|
|
673
|
-
const num = Number(name);
|
|
674
|
-
if (!isNaN(num)) {
|
|
675
|
-
return num;
|
|
676
|
-
}
|
|
677
|
-
if ((name.startsWith('"') && name.endsWith('"')) ||
|
|
678
|
-
(name.startsWith("'") && name.endsWith("'"))) {
|
|
679
|
-
return name.slice(1, -1);
|
|
680
|
-
}
|
|
681
|
-
if (name.includes('.')) {
|
|
682
|
-
const parts = name.split('.');
|
|
683
|
-
const baseName = parts[0];
|
|
684
|
-
let obj = null;
|
|
685
|
-
if (context.locals.has(baseName)) {
|
|
686
|
-
obj = context.locals.get(baseName);
|
|
687
|
-
}
|
|
688
|
-
else if (context.globals && context.globals.has(baseName)) {
|
|
689
|
-
obj = context.globals.get(baseName);
|
|
690
|
-
}
|
|
691
|
-
else if (context.variables && context.variables.has(baseName)) {
|
|
692
|
-
obj = context.variables.get(baseName);
|
|
693
|
-
}
|
|
694
|
-
if (obj !== null && obj !== undefined) {
|
|
695
|
-
for (let i = 1; i < parts.length; i++) {
|
|
696
|
-
if (obj === null || obj === undefined) {
|
|
697
|
-
debug.expressions(`RESOLVE: Property access failed at '${parts[i - 1]}'`);
|
|
698
|
-
return undefined;
|
|
699
|
-
}
|
|
700
|
-
obj = obj[parts[i]];
|
|
701
|
-
}
|
|
702
|
-
debug.expressions(`RESOLVE: Property access '${name}' =`, obj);
|
|
703
|
-
return obj;
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
return name;
|
|
707
|
-
}
|
|
708
|
-
async function evaluateSimpleExpression(exprCode, context) {
|
|
709
|
-
debug.expressions('EVAL: Evaluating expression:', exprCode);
|
|
710
|
-
const ternaryMatch = exprCode.match(/^(.+?)\s*\?(?![\.\[])\s*(.+?)\s*:\s*(.+)$/);
|
|
711
|
-
if (ternaryMatch) {
|
|
712
|
-
const [, conditionExpr, trueExpr, falseExpr] = ternaryMatch;
|
|
713
|
-
debug.expressions('EVAL: Parsed ternary:', { conditionExpr, trueExpr, falseExpr });
|
|
714
|
-
const conditionValue = await resolveValue(conditionExpr.trim(), context);
|
|
715
|
-
debug.expressions('EVAL: Ternary condition value:', conditionValue);
|
|
716
|
-
if (conditionValue) {
|
|
717
|
-
const trueValue = await resolveValue(trueExpr.trim(), context);
|
|
718
|
-
debug.expressions('EVAL: Ternary returned true branch:', trueValue);
|
|
719
|
-
return trueValue;
|
|
720
|
-
}
|
|
721
|
-
else {
|
|
722
|
-
const falseValue = await resolveValue(falseExpr.trim(), context);
|
|
723
|
-
debug.expressions('EVAL: Ternary returned false branch:', falseValue);
|
|
724
|
-
return falseValue;
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
const arithmeticMatch = exprCode.match(/^([a-zA-Z_$][a-zA-Z0-9_$]*|\d+(?:\.\d+)?)\s*([\+\-\*\/\%])\s*([a-zA-Z_$][a-zA-Z0-9_$]*|\d+(?:\.\d+)?)$/);
|
|
728
|
-
if (arithmeticMatch) {
|
|
729
|
-
const [, left, operator, right] = arithmeticMatch;
|
|
730
|
-
debug.expressions('EVAL: Parsed arithmetic:', { left, operator, right });
|
|
731
|
-
const leftValue = await resolveValue(left.trim(), context);
|
|
732
|
-
const rightValue = await resolveValue(right.trim(), context);
|
|
733
|
-
debug.expressions('EVAL: Resolved values:', { leftValue, rightValue });
|
|
734
|
-
const leftNum = Number(leftValue);
|
|
735
|
-
const rightNum = Number(rightValue);
|
|
736
|
-
if (!isNaN(leftNum) && !isNaN(rightNum)) {
|
|
737
|
-
let result;
|
|
738
|
-
switch (operator) {
|
|
739
|
-
case '+':
|
|
740
|
-
result = leftNum + rightNum;
|
|
741
|
-
break;
|
|
742
|
-
case '-':
|
|
743
|
-
result = leftNum - rightNum;
|
|
744
|
-
break;
|
|
745
|
-
case '*':
|
|
746
|
-
result = leftNum * rightNum;
|
|
747
|
-
break;
|
|
748
|
-
case '/':
|
|
749
|
-
result = leftNum / rightNum;
|
|
750
|
-
break;
|
|
751
|
-
case '%':
|
|
752
|
-
result = leftNum % rightNum;
|
|
753
|
-
break;
|
|
754
|
-
default:
|
|
755
|
-
result = leftNum;
|
|
756
|
-
}
|
|
757
|
-
debug.expressions('EVAL: Arithmetic result:', result);
|
|
758
|
-
return result;
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
const fallback = await resolveValue(exprCode.trim(), context);
|
|
762
|
-
debug.expressions('EVAL: Fallback result:', fallback);
|
|
763
|
-
return fallback;
|
|
764
|
-
}
|
|
765
|
-
async function evaluateTemplateLiteral(node, context) {
|
|
766
|
-
let template = node.value || '';
|
|
767
|
-
debug.expressions('TEMPLATE LITERAL: Evaluating', { template, node });
|
|
768
|
-
const varPattern = /\$([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)/g;
|
|
769
|
-
const matches = [];
|
|
770
|
-
let m;
|
|
771
|
-
while ((m = varPattern.exec(template)) !== null) {
|
|
772
|
-
if (template[m.index + 1] === '{')
|
|
773
|
-
continue;
|
|
774
|
-
matches.push({ match: m[0], varName: m[1], index: m.index });
|
|
775
|
-
}
|
|
776
|
-
for (let i = matches.length - 1; i >= 0; i--) {
|
|
777
|
-
const { match, varName, index } = matches[i];
|
|
778
|
-
const value = await resolveTemplateVariable(varName, context);
|
|
779
|
-
debug.expressions(`TEMPLATE: $${varName} resolved to`, value);
|
|
780
|
-
template =
|
|
781
|
-
template.slice(0, index) + String(value ?? '') + template.slice(index + match.length);
|
|
782
|
-
}
|
|
783
|
-
let result = '';
|
|
784
|
-
let j = 0;
|
|
785
|
-
while (j < template.length) {
|
|
786
|
-
const exprStart = template.indexOf('${', j);
|
|
787
|
-
if (exprStart === -1) {
|
|
788
|
-
result += template.slice(j);
|
|
789
|
-
break;
|
|
790
|
-
}
|
|
791
|
-
result += template.slice(j, exprStart);
|
|
792
|
-
let depth = 1;
|
|
793
|
-
let exprEnd = exprStart + 2;
|
|
794
|
-
while (exprEnd < template.length && depth > 0) {
|
|
795
|
-
if (template[exprEnd] === '{')
|
|
796
|
-
depth++;
|
|
797
|
-
if (template[exprEnd] === '}')
|
|
798
|
-
depth--;
|
|
799
|
-
if (depth > 0)
|
|
800
|
-
exprEnd++;
|
|
801
|
-
}
|
|
802
|
-
if (depth !== 0) {
|
|
803
|
-
throw new Error(`Unterminated template expression in: ${template}`);
|
|
804
|
-
}
|
|
805
|
-
const exprCode = template.slice(exprStart + 2, exprEnd);
|
|
806
|
-
debug.expressions('TEMPLATE: Evaluating expression:', exprCode);
|
|
807
|
-
let value;
|
|
808
|
-
const trimmed = exprCode.trim();
|
|
809
|
-
if (context.locals.has(trimmed)) {
|
|
810
|
-
value = context.locals.get(trimmed);
|
|
811
|
-
debug.expressions(`TEMPLATE: Found in locals: ${trimmed} =`, value);
|
|
812
|
-
}
|
|
813
|
-
else if (context.globals && context.globals.has(trimmed)) {
|
|
814
|
-
value = context.globals.get(trimmed);
|
|
815
|
-
debug.expressions(`TEMPLATE: Found in globals: ${trimmed} =`, value);
|
|
816
|
-
}
|
|
817
|
-
else if (context.variables && context.variables.has(trimmed)) {
|
|
818
|
-
value = context.variables.get(trimmed);
|
|
819
|
-
debug.expressions(`TEMPLATE: Found in variables (legacy): ${trimmed} =`, value);
|
|
820
|
-
}
|
|
821
|
-
else {
|
|
822
|
-
value = await evaluateSimpleExpression(exprCode, context);
|
|
823
|
-
debug.expressions(`TEMPLATE: Evaluated expression "${exprCode}" =`, value);
|
|
824
|
-
}
|
|
825
|
-
result += String(value);
|
|
826
|
-
j = exprEnd + 1;
|
|
827
|
-
}
|
|
828
|
-
return result;
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
function coerceArithmeticOperand(value) {
|
|
832
|
-
if (Array.isArray(value) && value.length === 1) {
|
|
833
|
-
value = value[0];
|
|
834
|
-
}
|
|
835
|
-
if (value !== null &&
|
|
836
|
-
typeof value === 'object' &&
|
|
837
|
-
typeof value.textContent === 'string') {
|
|
838
|
-
const text = value.value ?? value.textContent;
|
|
839
|
-
if (text !== null && text !== undefined) {
|
|
840
|
-
const trimmed = String(text).trim();
|
|
841
|
-
if (trimmed === '')
|
|
842
|
-
return 0;
|
|
843
|
-
const num = parseFloat(trimmed);
|
|
844
|
-
return isNaN(num) ? trimmed : num;
|
|
845
|
-
}
|
|
846
|
-
return 0;
|
|
847
|
-
}
|
|
848
|
-
return value;
|
|
849
|
-
}
|
|
850
|
-
async function evaluateBinaryExpression(node, context, evaluate, unwrap, expressionRegistry, evaluateSelector) {
|
|
851
|
-
const { operator, left, right } = node;
|
|
852
|
-
if (operator === 'in' && left.type === 'selector') {
|
|
853
|
-
let selector = left.value;
|
|
854
|
-
if (selector.startsWith('<') && selector.endsWith('/>')) {
|
|
855
|
-
selector = selector.slice(1, -2).trim();
|
|
856
|
-
}
|
|
857
|
-
const contextElement = unwrap(await evaluate(right, context));
|
|
858
|
-
if (!contextElement || typeof contextElement.querySelector !== 'function') {
|
|
859
|
-
throw new Error(`'in' operator requires a DOM element as the right operand (got: ${typeof contextElement})`);
|
|
860
|
-
}
|
|
861
|
-
const nodeList = contextElement.querySelectorAll(selector);
|
|
862
|
-
return Array.from(nodeList);
|
|
863
|
-
}
|
|
864
|
-
if (operator === 'in' && left.type === 'queryReference') {
|
|
865
|
-
let selector = left.selector;
|
|
866
|
-
if (selector.startsWith('<') && selector.endsWith('/>')) {
|
|
867
|
-
selector = selector.slice(1, -2).trim();
|
|
868
|
-
}
|
|
869
|
-
const contextElement = unwrap(await evaluate(right, context));
|
|
870
|
-
if (!contextElement || typeof contextElement.querySelector !== 'function') {
|
|
871
|
-
throw new Error(`'in' operator requires a DOM element as the right operand (got: ${typeof contextElement})`);
|
|
872
|
-
}
|
|
873
|
-
const nodeList = contextElement.querySelectorAll(selector);
|
|
874
|
-
return Array.from(nodeList);
|
|
875
|
-
}
|
|
876
|
-
if (operator === 'in') {
|
|
877
|
-
let positionalOp = null;
|
|
878
|
-
let selector = null;
|
|
879
|
-
if (left.type === 'positionalExpression') {
|
|
880
|
-
positionalOp = left.operator;
|
|
881
|
-
const selectorArg = left.argument;
|
|
882
|
-
if (selectorArg?.type === 'cssSelector') {
|
|
883
|
-
selector = selectorArg.selector;
|
|
884
|
-
}
|
|
885
|
-
else if (selectorArg?.type === 'selector') {
|
|
886
|
-
selector = selectorArg.value;
|
|
887
|
-
}
|
|
888
|
-
else if (selectorArg?.type === 'classSelector') {
|
|
889
|
-
selector = '.' + selectorArg.className;
|
|
890
|
-
}
|
|
891
|
-
else if (selectorArg?.type === 'idSelector') {
|
|
892
|
-
selector = '#' + selectorArg.id;
|
|
893
|
-
}
|
|
894
|
-
else if (selectorArg) {
|
|
895
|
-
selector = String(await evaluate(selectorArg, context));
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
else if (left.type === 'memberExpression' &&
|
|
899
|
-
left.object?.type === 'identifier' &&
|
|
900
|
-
(left.object.name === 'first' || left.object.name === 'last')) {
|
|
901
|
-
positionalOp = left.object.name;
|
|
902
|
-
if (left.property?.type === 'identifier' && left.property.name) {
|
|
903
|
-
selector = '.' + left.property.name;
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
else if (left.type === 'callExpression' &&
|
|
907
|
-
left.callee?.type === 'identifier' &&
|
|
908
|
-
(left.callee.name === 'first' || left.callee.name === 'last')) {
|
|
909
|
-
positionalOp = left.callee.name;
|
|
910
|
-
const selectorArg = left.arguments?.[0];
|
|
911
|
-
if (selectorArg?.type === 'selector') {
|
|
912
|
-
selector = selectorArg.value;
|
|
913
|
-
}
|
|
914
|
-
else if (selectorArg?.type === 'cssSelector') {
|
|
915
|
-
selector = selectorArg.selector;
|
|
916
|
-
}
|
|
917
|
-
else if (selectorArg?.type === 'classSelector') {
|
|
918
|
-
selector = '.' + selectorArg.className;
|
|
919
|
-
}
|
|
920
|
-
else if (selectorArg?.type === 'idSelector') {
|
|
921
|
-
selector = '#' + selectorArg.id;
|
|
922
|
-
}
|
|
923
|
-
else if (selectorArg) {
|
|
924
|
-
selector = String(await evaluate(selectorArg, context));
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
if (positionalOp && selector) {
|
|
928
|
-
const scopeElement = unwrap(await evaluate(right, context));
|
|
929
|
-
if (!scopeElement || typeof scopeElement.querySelectorAll !== 'function') {
|
|
930
|
-
return undefined;
|
|
931
|
-
}
|
|
932
|
-
const nodeList = scopeElement.querySelectorAll(selector);
|
|
933
|
-
const elements = Array.from(nodeList);
|
|
934
|
-
if (positionalOp === 'first') {
|
|
935
|
-
return elements.length > 0 ? elements[0] : undefined;
|
|
936
|
-
}
|
|
937
|
-
else if (positionalOp === 'last') {
|
|
938
|
-
return elements.length > 0 ? elements[elements.length - 1] : undefined;
|
|
939
|
-
}
|
|
940
|
-
return elements;
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
if (operator === 'matches' &&
|
|
944
|
-
(right.type === 'selector' || right.type === 'cssSelector' || right.type === 'classSelector')) {
|
|
945
|
-
const leftValue = await evaluate(left, context);
|
|
946
|
-
const selectorStr = right.value || right.selector;
|
|
947
|
-
if (leftValue && typeof leftValue.matches === 'function') {
|
|
948
|
-
try {
|
|
949
|
-
return leftValue.matches(selectorStr);
|
|
950
|
-
}
|
|
951
|
-
catch {
|
|
952
|
-
return false;
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
return false;
|
|
956
|
-
}
|
|
957
|
-
const leftValue = await evaluate(left, context);
|
|
958
|
-
const rightValue = await evaluate(right, context);
|
|
959
|
-
if (operator === 'has' || operator === 'have') {
|
|
960
|
-
if (leftValue instanceof Element) {
|
|
961
|
-
if ((right.type === 'cssSelector' && right.selectorType === 'class') ||
|
|
962
|
-
(right.type === 'selector' && right.value?.startsWith('.'))) {
|
|
963
|
-
const className = right.selector?.slice(1) || right.value?.slice(1) || '';
|
|
964
|
-
return leftValue.classList.contains(className);
|
|
965
|
-
}
|
|
966
|
-
if (right.type === 'attributeAccess' || right.type === 'attributeRef') {
|
|
967
|
-
const attrName = right.attributeName || right.name || right.value?.replace(/^@/, '') || '';
|
|
968
|
-
return leftValue.hasAttribute(attrName);
|
|
969
|
-
}
|
|
970
|
-
if (right.type === 'cssSelector' && right.selectorType === 'attribute') {
|
|
971
|
-
const attrName = (right.selector || right.value || '').replace(/^@/, '');
|
|
972
|
-
return leftValue.hasAttribute(attrName);
|
|
973
|
-
}
|
|
974
|
-
if ((right.type === 'cssSelector' && right.selectorType === 'id') ||
|
|
975
|
-
right.type === 'idSelector') {
|
|
976
|
-
const id = (right.selector || right.value || '').replace(/^#/, '');
|
|
977
|
-
return leftValue.querySelector(`#${CSS.escape(id)}`) !== null;
|
|
978
|
-
}
|
|
979
|
-
if (right.type === 'selector' || right.type === 'queryReference') {
|
|
980
|
-
let sel = right.value || right.selector || '';
|
|
981
|
-
if (sel.startsWith('<') && sel.endsWith('/>')) {
|
|
982
|
-
sel = sel.slice(1, -2).trim();
|
|
983
|
-
}
|
|
984
|
-
try {
|
|
985
|
-
return leftValue.querySelector(sel) !== null;
|
|
986
|
-
}
|
|
987
|
-
catch {
|
|
988
|
-
return false;
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
if (right.type === 'cssSelector') {
|
|
992
|
-
const sel = right.selector || '';
|
|
993
|
-
try {
|
|
994
|
-
return leftValue.querySelector(sel) !== null;
|
|
995
|
-
}
|
|
996
|
-
catch {
|
|
997
|
-
return false;
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
if (Array.isArray(leftValue)) {
|
|
1002
|
-
return leftValue.includes(rightValue);
|
|
1003
|
-
}
|
|
1004
|
-
if (typeof leftValue === 'string') {
|
|
1005
|
-
return leftValue.includes(String(rightValue));
|
|
1006
|
-
}
|
|
1007
|
-
return false;
|
|
1008
|
-
}
|
|
1009
|
-
switch (operator) {
|
|
1010
|
-
case '>':
|
|
1011
|
-
case 'is greater than':
|
|
1012
|
-
return leftValue > rightValue;
|
|
1013
|
-
case '<':
|
|
1014
|
-
case 'is less than':
|
|
1015
|
-
return leftValue < rightValue;
|
|
1016
|
-
case '>=':
|
|
1017
|
-
case 'is greater than or equal to':
|
|
1018
|
-
return leftValue >= rightValue;
|
|
1019
|
-
case '<=':
|
|
1020
|
-
case 'is less than or equal to':
|
|
1021
|
-
return leftValue <= rightValue;
|
|
1022
|
-
case '==':
|
|
1023
|
-
case 'equals':
|
|
1024
|
-
case 'is equal to':
|
|
1025
|
-
return leftValue == rightValue;
|
|
1026
|
-
case '===':
|
|
1027
|
-
case 'is really equal to':
|
|
1028
|
-
case 'really equals':
|
|
1029
|
-
return leftValue === rightValue;
|
|
1030
|
-
case '!=':
|
|
1031
|
-
case 'is not equal to':
|
|
1032
|
-
return leftValue != rightValue;
|
|
1033
|
-
case '!==':
|
|
1034
|
-
case 'is not really equal to':
|
|
1035
|
-
return leftValue !== rightValue;
|
|
1036
|
-
case '+': {
|
|
1037
|
-
const coercedLeft = coerceArithmeticOperand(leftValue);
|
|
1038
|
-
const coercedRight = coerceArithmeticOperand(rightValue);
|
|
1039
|
-
const shouldUseStringConcatenation = typeof coercedLeft === 'string' || typeof coercedRight === 'string';
|
|
1040
|
-
if (shouldUseStringConcatenation) {
|
|
1041
|
-
const stringConcatExpr = expressionRegistry.get('stringConcatenation');
|
|
1042
|
-
if (stringConcatExpr) {
|
|
1043
|
-
debug.expressions('Using string concatenation for:', {
|
|
1044
|
-
leftValue: coercedLeft,
|
|
1045
|
-
rightValue: coercedRight,
|
|
1046
|
-
});
|
|
1047
|
-
const result = await stringConcatExpr.evaluate(context, {
|
|
1048
|
-
left: coercedLeft,
|
|
1049
|
-
right: coercedRight,
|
|
1050
|
-
});
|
|
1051
|
-
return result.success ? result.value : coercedLeft + coercedRight;
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
else {
|
|
1055
|
-
const additionExpr = expressionRegistry.get('addition');
|
|
1056
|
-
if (additionExpr) {
|
|
1057
|
-
debug.expressions('Using numeric addition for:', {
|
|
1058
|
-
leftValue: coercedLeft,
|
|
1059
|
-
rightValue: coercedRight,
|
|
1060
|
-
});
|
|
1061
|
-
const result = await additionExpr.evaluate(context, {
|
|
1062
|
-
left: coercedLeft,
|
|
1063
|
-
right: coercedRight,
|
|
1064
|
-
});
|
|
1065
|
-
return result.success ? result.value : coercedLeft + coercedRight;
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
return coercedLeft + coercedRight;
|
|
1069
|
-
}
|
|
1070
|
-
case '-':
|
|
1071
|
-
return (coerceArithmeticOperand(leftValue) - coerceArithmeticOperand(rightValue));
|
|
1072
|
-
case '*':
|
|
1073
|
-
return (coerceArithmeticOperand(leftValue) * coerceArithmeticOperand(rightValue));
|
|
1074
|
-
case '/':
|
|
1075
|
-
return (coerceArithmeticOperand(leftValue) / coerceArithmeticOperand(rightValue));
|
|
1076
|
-
case '%':
|
|
1077
|
-
case 'mod':
|
|
1078
|
-
return (coerceArithmeticOperand(leftValue) % coerceArithmeticOperand(rightValue));
|
|
1079
|
-
case 'as':
|
|
1080
|
-
const typeName = typeof rightValue === 'string'
|
|
1081
|
-
? rightValue
|
|
1082
|
-
: right.type === 'identifier'
|
|
1083
|
-
? right.name
|
|
1084
|
-
: right.type === 'literal'
|
|
1085
|
-
? right.value
|
|
1086
|
-
: String(rightValue);
|
|
1087
|
-
const asExpr = expressionRegistry.get('as');
|
|
1088
|
-
return asExpr ? asExpr.evaluate(context, leftValue, typeName) : leftValue;
|
|
1089
|
-
case '&&':
|
|
1090
|
-
case 'and':
|
|
1091
|
-
return leftValue && rightValue;
|
|
1092
|
-
case '||':
|
|
1093
|
-
case 'or':
|
|
1094
|
-
return leftValue || rightValue;
|
|
1095
|
-
case 'is':
|
|
1096
|
-
return leftValue === rightValue;
|
|
1097
|
-
case 'is not':
|
|
1098
|
-
return leftValue !== rightValue;
|
|
1099
|
-
case 'is a':
|
|
1100
|
-
case 'is an':
|
|
1101
|
-
const checkTypeName = right.type === 'identifier' ? right.name.toLowerCase() : String(rightValue).toLowerCase();
|
|
1102
|
-
switch (checkTypeName) {
|
|
1103
|
-
case 'string':
|
|
1104
|
-
return typeof leftValue === 'string';
|
|
1105
|
-
case 'number':
|
|
1106
|
-
return typeof leftValue === 'number';
|
|
1107
|
-
case 'boolean':
|
|
1108
|
-
return typeof leftValue === 'boolean';
|
|
1109
|
-
case 'object':
|
|
1110
|
-
return typeof leftValue === 'object' && leftValue !== null;
|
|
1111
|
-
case 'array':
|
|
1112
|
-
return Array.isArray(leftValue);
|
|
1113
|
-
case 'function':
|
|
1114
|
-
return typeof leftValue === 'function';
|
|
1115
|
-
case 'null':
|
|
1116
|
-
return leftValue === null;
|
|
1117
|
-
case 'undefined':
|
|
1118
|
-
return leftValue === undefined;
|
|
1119
|
-
default:
|
|
1120
|
-
const typeNameStr = right.type === 'identifier' ? right.name : String(rightValue);
|
|
1121
|
-
return leftValue != null && leftValue.constructor?.name === typeNameStr;
|
|
1122
|
-
}
|
|
1123
|
-
case 'is not a':
|
|
1124
|
-
case 'is not an':
|
|
1125
|
-
const negCheckTypeName = right.type === 'identifier' ? right.name.toLowerCase() : String(rightValue).toLowerCase();
|
|
1126
|
-
switch (negCheckTypeName) {
|
|
1127
|
-
case 'string':
|
|
1128
|
-
return typeof leftValue !== 'string';
|
|
1129
|
-
case 'number':
|
|
1130
|
-
return typeof leftValue !== 'number';
|
|
1131
|
-
case 'boolean':
|
|
1132
|
-
return typeof leftValue !== 'boolean';
|
|
1133
|
-
case 'object':
|
|
1134
|
-
return !(typeof leftValue === 'object' && leftValue !== null);
|
|
1135
|
-
case 'array':
|
|
1136
|
-
return !Array.isArray(leftValue);
|
|
1137
|
-
case 'function':
|
|
1138
|
-
return typeof leftValue !== 'function';
|
|
1139
|
-
case 'null':
|
|
1140
|
-
return leftValue !== null;
|
|
1141
|
-
case 'undefined':
|
|
1142
|
-
return leftValue !== undefined;
|
|
1143
|
-
default:
|
|
1144
|
-
const negTypeNameStr = right.type === 'identifier' ? right.name : String(rightValue);
|
|
1145
|
-
return !(leftValue != null && leftValue.constructor?.name === negTypeNameStr);
|
|
1146
|
-
}
|
|
1147
|
-
case '=':
|
|
1148
|
-
if (left.type === 'identifier') {
|
|
1149
|
-
const variableName = left.name;
|
|
1150
|
-
if (variableName === 'result') {
|
|
1151
|
-
context.result = rightValue;
|
|
1152
|
-
}
|
|
1153
|
-
else if (variableName === 'it') {
|
|
1154
|
-
context.it = rightValue;
|
|
1155
|
-
}
|
|
1156
|
-
else if (variableName === 'you') {
|
|
1157
|
-
context.you = rightValue;
|
|
1158
|
-
}
|
|
1159
|
-
else {
|
|
1160
|
-
context.locals.set(variableName, rightValue);
|
|
1161
|
-
}
|
|
1162
|
-
return rightValue;
|
|
1163
|
-
}
|
|
1164
|
-
else {
|
|
1165
|
-
throw new Error('Left side of assignment must be an identifier');
|
|
1166
|
-
}
|
|
1167
|
-
case 'contains':
|
|
1168
|
-
if (Array.isArray(leftValue)) {
|
|
1169
|
-
return leftValue.includes(rightValue);
|
|
1170
|
-
}
|
|
1171
|
-
if (typeof leftValue === 'string') {
|
|
1172
|
-
return leftValue.includes(String(rightValue));
|
|
1173
|
-
}
|
|
1174
|
-
if (leftValue && typeof leftValue.matches === 'function') {
|
|
1175
|
-
return leftValue.matches(String(rightValue));
|
|
1176
|
-
}
|
|
1177
|
-
return false;
|
|
1178
|
-
case 'include':
|
|
1179
|
-
case 'includes':
|
|
1180
|
-
if (Array.isArray(leftValue)) {
|
|
1181
|
-
return leftValue.includes(rightValue);
|
|
1182
|
-
}
|
|
1183
|
-
if (typeof leftValue === 'string') {
|
|
1184
|
-
return leftValue.includes(String(rightValue));
|
|
1185
|
-
}
|
|
1186
|
-
if (leftValue && typeof leftValue.matches === 'function') {
|
|
1187
|
-
return leftValue.matches(String(rightValue));
|
|
1188
|
-
}
|
|
1189
|
-
return false;
|
|
1190
|
-
case 'match':
|
|
1191
|
-
case 'matches':
|
|
1192
|
-
if (leftValue && typeof leftValue.matches === 'function') {
|
|
1193
|
-
const selectorStr = typeof rightValue === 'string' ? rightValue : String(rightValue);
|
|
1194
|
-
try {
|
|
1195
|
-
return leftValue.matches(selectorStr);
|
|
1196
|
-
}
|
|
1197
|
-
catch {
|
|
1198
|
-
return false;
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
return false;
|
|
1202
|
-
case 'in':
|
|
1203
|
-
const selectorIn = typeof leftValue === 'string' ? leftValue : String(leftValue);
|
|
1204
|
-
const contextElementIn = rightValue;
|
|
1205
|
-
if (!contextElementIn || typeof contextElementIn.querySelector !== 'function') {
|
|
1206
|
-
if (Array.isArray(contextElementIn)) {
|
|
1207
|
-
return contextElementIn.includes(leftValue);
|
|
1208
|
-
}
|
|
1209
|
-
if (typeof contextElementIn === 'object' && contextElementIn !== null) {
|
|
1210
|
-
return selectorIn in contextElementIn;
|
|
1211
|
-
}
|
|
1212
|
-
throw new Error(`'in' operator requires a DOM element, array, or object as the right operand (got: ${typeof contextElementIn})`);
|
|
1213
|
-
}
|
|
1214
|
-
return contextElementIn.querySelector(selectorIn);
|
|
1215
|
-
case ' ':
|
|
1216
|
-
if (typeof leftValue === 'string' && typeof rightValue === 'string') {
|
|
1217
|
-
return { command: leftValue, selector: rightValue };
|
|
1218
|
-
}
|
|
1219
|
-
if (left.type === 'identifier' && right.type === 'selector') {
|
|
1220
|
-
return { command: left.name, selector: right.value };
|
|
1221
|
-
}
|
|
1222
|
-
return rightValue;
|
|
1223
|
-
default:
|
|
1224
|
-
throw new Error(`Unsupported binary operator: "${operator}" (length: ${operator.length})`);
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
|
-
async function evaluateCallExpression(node, context, evaluate, unwrap, expressionRegistry, evaluateSelector) {
|
|
1229
|
-
const { callee, arguments: args, isConstructor } = node;
|
|
1230
|
-
if (isConstructor) {
|
|
1231
|
-
const constructorName = callee.name;
|
|
1232
|
-
const Constructor = context.globals?.get(constructorName) || window[constructorName];
|
|
1233
|
-
if (typeof Constructor === 'function') {
|
|
1234
|
-
const evaluatedArgs = await Promise.all(args.map((arg) => evaluate(arg, context)));
|
|
1235
|
-
return new Constructor(...evaluatedArgs);
|
|
1236
|
-
}
|
|
1237
|
-
throw new Error(`Unknown constructor: ${constructorName}`);
|
|
1238
|
-
}
|
|
1239
|
-
if (callee.type === 'memberExpression' || callee.type === 'propertyAccess') {
|
|
1240
|
-
return evaluateMethodCall(callee, args, context, evaluate, unwrap);
|
|
1241
|
-
}
|
|
1242
|
-
const functionName = callee.name || callee;
|
|
1243
|
-
const expression = expressionRegistry.get(functionName);
|
|
1244
|
-
if (expression) {
|
|
1245
|
-
const selectorStringFunctions = ['closest', 'previous', 'next'];
|
|
1246
|
-
const collectionFunctions = ['first', 'last', 'random', 'at'];
|
|
1247
|
-
const needsSelectorString = selectorStringFunctions.includes(functionName);
|
|
1248
|
-
const needsCollection = collectionFunctions.includes(functionName);
|
|
1249
|
-
const evaluatedArgs = await Promise.all(args.map((arg) => {
|
|
1250
|
-
if (needsSelectorString &&
|
|
1251
|
-
arg &&
|
|
1252
|
-
arg.type === 'selector' &&
|
|
1253
|
-
typeof arg.value === 'string') {
|
|
1254
|
-
return arg.value;
|
|
1255
|
-
}
|
|
1256
|
-
if (needsSelectorString &&
|
|
1257
|
-
arg &&
|
|
1258
|
-
arg.type === 'identifier' &&
|
|
1259
|
-
typeof arg.name === 'string') {
|
|
1260
|
-
return arg.name;
|
|
1261
|
-
}
|
|
1262
|
-
if (needsCollection && arg && arg.type === 'selector' && typeof arg.value === 'string') {
|
|
1263
|
-
return evaluateSelector(arg, context);
|
|
1264
|
-
}
|
|
1265
|
-
return evaluate(arg, context);
|
|
1266
|
-
}));
|
|
1267
|
-
return expression.evaluate(context, ...evaluatedArgs);
|
|
1268
|
-
}
|
|
1269
|
-
const func = context.globals?.get(functionName) || window[functionName];
|
|
1270
|
-
if (typeof func === 'function') {
|
|
1271
|
-
const evaluatedArgs = await Promise.all(args.map((arg) => evaluate(arg, context)));
|
|
1272
|
-
return func(...evaluatedArgs);
|
|
1273
|
-
}
|
|
1274
|
-
throw new Error(`Unknown function: ${functionName}`);
|
|
1275
|
-
}
|
|
1276
|
-
async function evaluateMethodCall(callee, args, context, evaluate, unwrap) {
|
|
1277
|
-
try {
|
|
1278
|
-
const object = await evaluate(callee.object, context);
|
|
1279
|
-
const thisContext = unwrap(object);
|
|
1280
|
-
if (thisContext === null || thisContext === undefined) {
|
|
1281
|
-
throw new Error(`Cannot call method on null or undefined`);
|
|
1282
|
-
}
|
|
1283
|
-
const propertyName = extractPropertyName(callee.property);
|
|
1284
|
-
if (!propertyName || typeof propertyName !== 'string') {
|
|
1285
|
-
throw new Error(`Invalid method name: ${propertyName}`);
|
|
1286
|
-
}
|
|
1287
|
-
const func = thisContext[propertyName];
|
|
1288
|
-
if (typeof func !== 'function') {
|
|
1289
|
-
throw new Error(`Property "${propertyName}" is not a function on ${thisContext.constructor?.name || typeof thisContext}`);
|
|
1290
|
-
}
|
|
1291
|
-
const evaluatedArgs = await Promise.all(args.map((arg) => evaluate(arg, context)));
|
|
1292
|
-
const result = func.apply(thisContext, evaluatedArgs);
|
|
1293
|
-
return result;
|
|
1294
|
-
}
|
|
1295
|
-
catch (error) {
|
|
1296
|
-
if (error instanceof Error) {
|
|
1297
|
-
throw error;
|
|
1298
|
-
}
|
|
1299
|
-
throw new Error(`Failed to evaluate method call: ${error}`);
|
|
1300
|
-
}
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
class BaseExpressionEvaluator {
|
|
1304
|
-
constructor() {
|
|
1305
|
-
this.expressionRegistry = new Map();
|
|
1306
|
-
}
|
|
1307
|
-
registerCategory(expressions) {
|
|
1308
|
-
Object.entries(expressions).forEach(([name, impl]) => {
|
|
1309
|
-
this.expressionRegistry.set(name, impl);
|
|
1310
|
-
});
|
|
1311
|
-
}
|
|
1312
|
-
unwrapSelectorResult(value) {
|
|
1313
|
-
if (value instanceof NodeList && value.length > 0) {
|
|
1314
|
-
return value[0];
|
|
1315
|
-
}
|
|
1316
|
-
if (Array.isArray(value) && value.length > 0) {
|
|
1317
|
-
const firstElement = value[0];
|
|
1318
|
-
const hasRegexProps = 'index' in value && 'input' in value;
|
|
1319
|
-
if (hasRegexProps) {
|
|
1320
|
-
return value;
|
|
1321
|
-
}
|
|
1322
|
-
if (firstElement instanceof Element || firstElement instanceof Node) {
|
|
1323
|
-
return firstElement;
|
|
1324
|
-
}
|
|
1325
|
-
return value;
|
|
1326
|
-
}
|
|
1327
|
-
return value;
|
|
1328
|
-
}
|
|
1329
|
-
async evaluate(node, context) {
|
|
1330
|
-
if (!node) {
|
|
1331
|
-
debug.expressions('EVALUATOR: Received null/undefined node, returning null');
|
|
1332
|
-
return null;
|
|
1333
|
-
}
|
|
1334
|
-
if (!node.type) {
|
|
1335
|
-
console.error('EVALUATOR: Node missing type property:', node);
|
|
1336
|
-
throw new Error(`Node missing type property: ${JSON.stringify(node)}`);
|
|
1337
|
-
}
|
|
1338
|
-
switch (node.type) {
|
|
1339
|
-
case 'identifier':
|
|
1340
|
-
return this.evaluateIdentifier(node, context);
|
|
1341
|
-
case 'literal':
|
|
1342
|
-
return this.evaluateLiteral(node, context);
|
|
1343
|
-
case 'string':
|
|
1344
|
-
return node.value ?? '';
|
|
1345
|
-
case 'memberExpression':
|
|
1346
|
-
return this.evaluateMemberExpression(node, context);
|
|
1347
|
-
case 'binaryExpression':
|
|
1348
|
-
return this.evaluateBinaryExpression(node, context);
|
|
1349
|
-
case 'unaryExpression':
|
|
1350
|
-
return this.evaluateUnaryExpression(node, context);
|
|
1351
|
-
case 'callExpression':
|
|
1352
|
-
return this.evaluateCallExpression(node, context);
|
|
1353
|
-
case 'selector':
|
|
1354
|
-
return this.evaluateSelector(node, context);
|
|
1355
|
-
case 'dollarExpression':
|
|
1356
|
-
return this.evaluateDollarExpression(node, context);
|
|
1357
|
-
case 'possessiveExpression':
|
|
1358
|
-
return this.evaluatePossessiveExpression(node, context);
|
|
1359
|
-
case 'templateLiteral':
|
|
1360
|
-
return this.evaluateTemplateLiteral(node, context);
|
|
1361
|
-
case 'arrayLiteral':
|
|
1362
|
-
return this.evaluateArrayLiteral(node, context);
|
|
1363
|
-
case 'objectLiteral':
|
|
1364
|
-
return this.evaluateObjectLiteral(node, context);
|
|
1365
|
-
case 'conditionalExpression':
|
|
1366
|
-
return this.evaluateConditionalExpression(node, context);
|
|
1367
|
-
case 'cssSelector':
|
|
1368
|
-
return this.evaluateCSSSelector(node, context);
|
|
1369
|
-
case 'propertyAccess':
|
|
1370
|
-
return this.evaluatePropertyAccess(node, context);
|
|
1371
|
-
case 'propertyOfExpression':
|
|
1372
|
-
return this.evaluatePropertyOfExpression(node, context);
|
|
1373
|
-
case 'contextReference':
|
|
1374
|
-
return this.evaluateContextReference(node, context);
|
|
1375
|
-
case 'queryReference':
|
|
1376
|
-
return this.evaluateQueryReference(node, context);
|
|
1377
|
-
case 'idSelector':
|
|
1378
|
-
return this.evaluateIdSelector(node, context);
|
|
1379
|
-
case 'attributeAccess':
|
|
1380
|
-
return this.evaluateAttributeAccess(node, context);
|
|
1381
|
-
case 'asExpression':
|
|
1382
|
-
return this.evaluateAsExpression(node, context);
|
|
1383
|
-
default:
|
|
1384
|
-
throw new Error(`Unsupported AST node type for evaluation: ${node.type}`);
|
|
1385
|
-
}
|
|
1386
|
-
}
|
|
1387
|
-
async evaluateWithResult(node, context) {
|
|
1388
|
-
try {
|
|
1389
|
-
const value = await this.evaluate(node, context);
|
|
1390
|
-
return ok(value);
|
|
1391
|
-
}
|
|
1392
|
-
catch (e) {
|
|
1393
|
-
if (e instanceof Error) {
|
|
1394
|
-
const error = e;
|
|
1395
|
-
if (error.isHalt || error.message === 'HALT_EXECUTION') {
|
|
1396
|
-
return err({ type: 'halt' });
|
|
1397
|
-
}
|
|
1398
|
-
if (error.isExit || error.message === 'EXIT_COMMAND') {
|
|
1399
|
-
return err({ type: 'exit', returnValue: error.returnValue });
|
|
1400
|
-
}
|
|
1401
|
-
if (error.isBreak) {
|
|
1402
|
-
return err({ type: 'break' });
|
|
1403
|
-
}
|
|
1404
|
-
if (error.isContinue) {
|
|
1405
|
-
return err({ type: 'continue' });
|
|
1406
|
-
}
|
|
1407
|
-
if (error.isReturn) {
|
|
1408
|
-
return err({ type: 'return', returnValue: error.returnValue });
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1411
|
-
throw e;
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
async evaluateArrayLiteral(node, context) {
|
|
1415
|
-
const elements = node.elements || [];
|
|
1416
|
-
const evaluatedElements = [];
|
|
1417
|
-
for (const element of elements) {
|
|
1418
|
-
const value = await this.evaluate(element, context);
|
|
1419
|
-
evaluatedElements.push(value);
|
|
1420
|
-
}
|
|
1421
|
-
return evaluatedElements;
|
|
1422
|
-
}
|
|
1423
|
-
async evaluateObjectLiteral(node, context) {
|
|
1424
|
-
const properties = node.properties || [];
|
|
1425
|
-
const result = {};
|
|
1426
|
-
for (const property of properties) {
|
|
1427
|
-
let key;
|
|
1428
|
-
if (property.key.type === 'identifier') {
|
|
1429
|
-
key = property.key.name;
|
|
1430
|
-
}
|
|
1431
|
-
else if (property.key.type === 'literal') {
|
|
1432
|
-
key = String(property.key.value);
|
|
1433
|
-
}
|
|
1434
|
-
else {
|
|
1435
|
-
const evaluatedKey = await this.evaluate(property.key, context);
|
|
1436
|
-
key = String(evaluatedKey);
|
|
1437
|
-
}
|
|
1438
|
-
const value = await this.evaluate(property.value, context);
|
|
1439
|
-
result[key] = value;
|
|
1440
|
-
}
|
|
1441
|
-
return result;
|
|
1442
|
-
}
|
|
1443
|
-
async evaluateConditionalExpression(node, context) {
|
|
1444
|
-
const test = await this.evaluate(node.test, context);
|
|
1445
|
-
if (test) {
|
|
1446
|
-
return this.evaluate(node.consequent, context);
|
|
1447
|
-
}
|
|
1448
|
-
else if (node.alternate) {
|
|
1449
|
-
return this.evaluate(node.alternate, context);
|
|
1450
|
-
}
|
|
1451
|
-
return undefined;
|
|
1452
|
-
}
|
|
1453
|
-
async evaluateTemplateLiteral(node, context) {
|
|
1454
|
-
return evaluateTemplateLiteral(node, context);
|
|
1455
|
-
}
|
|
1456
|
-
async evaluateSimpleExpression(exprCode, context) {
|
|
1457
|
-
return evaluateSimpleExpression(exprCode, context);
|
|
1458
|
-
}
|
|
1459
|
-
async resolveValue(name, context) {
|
|
1460
|
-
return resolveValue(name, context);
|
|
1461
|
-
}
|
|
1462
|
-
async evaluateIdentifier(node, context) {
|
|
1463
|
-
const { name, scope } = node;
|
|
1464
|
-
if (name === 'me' || name === 'my' || name === 'I') {
|
|
1465
|
-
return context.me;
|
|
1466
|
-
}
|
|
1467
|
-
if (name === 'you' || name === 'your') {
|
|
1468
|
-
return context.you;
|
|
1469
|
-
}
|
|
1470
|
-
if (name === 'it' || name === 'its') {
|
|
1471
|
-
return context.it;
|
|
1472
|
-
}
|
|
1473
|
-
if (name === 'result') {
|
|
1474
|
-
return context.result;
|
|
1475
|
-
}
|
|
1476
|
-
if (name === 'event') {
|
|
1477
|
-
return context.event;
|
|
1478
|
-
}
|
|
1479
|
-
const expression = this.expressionRegistry.get(name);
|
|
1480
|
-
if (expression) {
|
|
1481
|
-
return expression.evaluate(context);
|
|
1482
|
-
}
|
|
1483
|
-
if (scope === 'local') {
|
|
1484
|
-
if (context.locals?.has(name)) {
|
|
1485
|
-
return context.locals.get(name);
|
|
1486
|
-
}
|
|
1487
|
-
return undefined;
|
|
1488
|
-
}
|
|
1489
|
-
if (scope === 'global') {
|
|
1490
|
-
if (context.globals?.has(name)) {
|
|
1491
|
-
return context.globals.get(name);
|
|
1492
|
-
}
|
|
1493
|
-
if (typeof window !== 'undefined' && name in window) {
|
|
1494
|
-
return window[name];
|
|
1495
|
-
}
|
|
1496
|
-
return undefined;
|
|
1497
|
-
}
|
|
1498
|
-
if (context.locals?.has(name)) {
|
|
1499
|
-
return context.locals.get(name);
|
|
1500
|
-
}
|
|
1501
|
-
if (context.globals?.has(name)) {
|
|
1502
|
-
return context.globals.get(name);
|
|
1503
|
-
}
|
|
1504
|
-
if (context.variables?.has(name)) {
|
|
1505
|
-
return context.variables.get(name);
|
|
1506
|
-
}
|
|
1507
|
-
if (typeof globalThis !== 'undefined' && name in globalThis) {
|
|
1508
|
-
return globalThis[name];
|
|
1509
|
-
}
|
|
1510
|
-
if (typeof window !== 'undefined' && name in window) {
|
|
1511
|
-
return window[name];
|
|
1512
|
-
}
|
|
1513
|
-
return undefined;
|
|
1514
|
-
}
|
|
1515
|
-
async evaluateLiteral(node, _context) {
|
|
1516
|
-
return node.value;
|
|
1517
|
-
}
|
|
1518
|
-
async evaluateContextReference(node, context) {
|
|
1519
|
-
const { contextType } = node;
|
|
1520
|
-
switch (contextType) {
|
|
1521
|
-
case 'me':
|
|
1522
|
-
return context.me;
|
|
1523
|
-
case 'it':
|
|
1524
|
-
return context.result;
|
|
1525
|
-
case 'you':
|
|
1526
|
-
return context.you;
|
|
1527
|
-
case 'event':
|
|
1528
|
-
return context.event;
|
|
1529
|
-
case 'body':
|
|
1530
|
-
return (context.meta?.ownerDocument?.body || document?.body);
|
|
1531
|
-
case 'detail':
|
|
1532
|
-
return context.event?.detail;
|
|
1533
|
-
case 'target':
|
|
1534
|
-
return context.you || context.event?.target;
|
|
1535
|
-
case 'sender':
|
|
1536
|
-
return context.event?.target;
|
|
1537
|
-
default:
|
|
1538
|
-
if (context.locals && context.locals.has(contextType)) {
|
|
1539
|
-
return context.locals.get(contextType);
|
|
1540
|
-
}
|
|
1541
|
-
debug.expressions(`Unknown context reference type: ${contextType}`);
|
|
1542
|
-
return undefined;
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
async evaluateMemberExpression(node, context) {
|
|
1546
|
-
const { object, property, computed } = node;
|
|
1547
|
-
if (object.type === 'identifier' && ['add', 'remove'].includes(object.name)) {
|
|
1548
|
-
const commandName = object.name;
|
|
1549
|
-
const className = extractPropertyName(property);
|
|
1550
|
-
if (!context.me) {
|
|
1551
|
-
throw new Error('Context element "me" is null');
|
|
1552
|
-
}
|
|
1553
|
-
if (commandName === 'add') {
|
|
1554
|
-
context.me.classList.add(className);
|
|
1555
|
-
}
|
|
1556
|
-
else if (commandName === 'remove') {
|
|
1557
|
-
context.me.classList.remove(className);
|
|
1558
|
-
}
|
|
1559
|
-
return;
|
|
1560
|
-
}
|
|
1561
|
-
let objectValue = await this.evaluate(object, context);
|
|
1562
|
-
if (computed) {
|
|
1563
|
-
const propertyValue = await this.evaluate(property, context);
|
|
1564
|
-
return objectValue[propertyValue];
|
|
1565
|
-
}
|
|
1566
|
-
else {
|
|
1567
|
-
const propertyName = extractPropertyName(property);
|
|
1568
|
-
const isArrayProp = Array.isArray(objectValue) &&
|
|
1569
|
-
typeof propertyName === 'string' &&
|
|
1570
|
-
(propertyName === 'length' || propertyName in Array.prototype);
|
|
1571
|
-
if (!isArrayProp) {
|
|
1572
|
-
objectValue = this.unwrapSelectorResult(objectValue);
|
|
1573
|
-
}
|
|
1574
|
-
if (typeof propertyName === 'string') {
|
|
1575
|
-
if (propertyName.startsWith('computed-')) {
|
|
1576
|
-
const styleProp = propertyName.substring('computed-'.length);
|
|
1577
|
-
if (objectValue && typeof window !== 'undefined' && objectValue instanceof Element) {
|
|
1578
|
-
const computedStyle = window.getComputedStyle(objectValue);
|
|
1579
|
-
return computedStyle.getPropertyValue(styleProp);
|
|
1580
|
-
}
|
|
1581
|
-
return undefined;
|
|
1582
|
-
}
|
|
1583
|
-
if (propertyName.startsWith('@')) {
|
|
1584
|
-
const attrName = propertyName.substring(1);
|
|
1585
|
-
if (objectValue && typeof objectValue.getAttribute === 'function') {
|
|
1586
|
-
return accessAttribute(objectValue, attrName);
|
|
1587
|
-
}
|
|
1588
|
-
return undefined;
|
|
1589
|
-
}
|
|
1590
|
-
if (objectValue && objectValue instanceof Element) {
|
|
1591
|
-
if (propertyName === 'previous' || propertyName === 'prev') {
|
|
1592
|
-
return objectValue.previousElementSibling;
|
|
1593
|
-
}
|
|
1594
|
-
if (propertyName === 'next') {
|
|
1595
|
-
return objectValue.nextElementSibling;
|
|
1596
|
-
}
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
1599
|
-
const value = objectValue?.[propertyName];
|
|
1600
|
-
if (typeof value === 'function') {
|
|
1601
|
-
return value.bind(objectValue);
|
|
1602
|
-
}
|
|
1603
|
-
return value;
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
coerceArithmeticOperand(value) {
|
|
1607
|
-
return coerceArithmeticOperand(value);
|
|
1608
|
-
}
|
|
1609
|
-
async evaluateBinaryExpression(node, context) {
|
|
1610
|
-
return evaluateBinaryExpression(node, context, this.evaluate.bind(this), this.unwrapSelectorResult.bind(this), this.expressionRegistry, this.evaluateSelector.bind(this));
|
|
1611
|
-
}
|
|
1612
|
-
async evaluateUnaryExpression(node, context) {
|
|
1613
|
-
const { operator, argument } = node;
|
|
1614
|
-
const operandValue = await this.evaluate(argument, context);
|
|
1615
|
-
switch (operator) {
|
|
1616
|
-
case 'not':
|
|
1617
|
-
case '!':
|
|
1618
|
-
const notExpr = this.expressionRegistry.get('not');
|
|
1619
|
-
return notExpr ? notExpr.evaluate(context, operandValue) : !operandValue;
|
|
1620
|
-
case 'no':
|
|
1621
|
-
const noExpr = this.expressionRegistry.get('no');
|
|
1622
|
-
return noExpr ? noExpr.evaluate(context, operandValue) : false;
|
|
1623
|
-
case 'some':
|
|
1624
|
-
if (operandValue === null || operandValue === undefined)
|
|
1625
|
-
return false;
|
|
1626
|
-
if (typeof operandValue === 'string')
|
|
1627
|
-
return operandValue.length > 0;
|
|
1628
|
-
if (Array.isArray(operandValue))
|
|
1629
|
-
return operandValue.length > 0;
|
|
1630
|
-
if (operandValue instanceof NodeList)
|
|
1631
|
-
return operandValue.length > 0;
|
|
1632
|
-
if (operandValue instanceof HTMLCollection)
|
|
1633
|
-
return operandValue.length > 0;
|
|
1634
|
-
if (typeof operandValue === 'object')
|
|
1635
|
-
return Object.keys(operandValue).length > 0;
|
|
1636
|
-
return true;
|
|
1637
|
-
case 'exists':
|
|
1638
|
-
const existsExpr = this.expressionRegistry.get('exists');
|
|
1639
|
-
return existsExpr ? existsExpr.evaluate(context, operandValue) : operandValue != null;
|
|
1640
|
-
case 'does not exist':
|
|
1641
|
-
const doesNotExistExpr = this.expressionRegistry.get('doesNotExist');
|
|
1642
|
-
return doesNotExistExpr
|
|
1643
|
-
? doesNotExistExpr.evaluate(context, operandValue)
|
|
1644
|
-
: operandValue == null;
|
|
1645
|
-
case '-':
|
|
1646
|
-
return -operandValue;
|
|
1647
|
-
case '+':
|
|
1648
|
-
return +operandValue;
|
|
1649
|
-
default:
|
|
1650
|
-
throw new Error(`Unsupported unary operator: ${operator}`);
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
async evaluateCallExpression(node, context) {
|
|
1654
|
-
return evaluateCallExpression(node, context, this.evaluate.bind(this), this.unwrapSelectorResult.bind(this), this.expressionRegistry, this.evaluateSelector.bind(this));
|
|
1655
|
-
}
|
|
1656
|
-
async evaluateMethodCall(callee, args, context) {
|
|
1657
|
-
return evaluateMethodCall(callee, args, context, this.evaluate.bind(this), this.unwrapSelectorResult.bind(this));
|
|
1658
|
-
}
|
|
1659
|
-
async evaluateSelector(node, context) {
|
|
1660
|
-
return evaluateSelector(node, context);
|
|
1661
|
-
}
|
|
1662
|
-
evaluateAttributeAccess(node, context) {
|
|
1663
|
-
if (context.me && typeof context.me.getAttribute === 'function') {
|
|
1664
|
-
return accessAttribute(context.me, node.attributeName);
|
|
1665
|
-
}
|
|
1666
|
-
return `@${node.attributeName}`;
|
|
1667
|
-
}
|
|
1668
|
-
async evaluateAsExpression(node, context) {
|
|
1669
|
-
const value = await this.evaluate(node.expression, context);
|
|
1670
|
-
const asExpr = this.expressionRegistry.get('as');
|
|
1671
|
-
if (asExpr) {
|
|
1672
|
-
return asExpr.evaluate(context, value, node.targetType);
|
|
1673
|
-
}
|
|
1674
|
-
throw new Error(`Conversion type 'as' not registered`);
|
|
1675
|
-
}
|
|
1676
|
-
getAvailableExpressions() {
|
|
1677
|
-
return Array.from(this.expressionRegistry.keys());
|
|
1678
|
-
}
|
|
1679
|
-
async evaluateDollarExpression(node, context) {
|
|
1680
|
-
const value = await this.evaluate(node.expression, context);
|
|
1681
|
-
if (node.expression.type === 'identifier') {
|
|
1682
|
-
const varName = node.expression.name;
|
|
1683
|
-
if (/^\d+$/.test(varName)) {
|
|
1684
|
-
return varName;
|
|
1685
|
-
}
|
|
1686
|
-
if (context.locals?.has(varName)) {
|
|
1687
|
-
return context.locals.get(varName);
|
|
1688
|
-
}
|
|
1689
|
-
if (varName === 'me' && context.me)
|
|
1690
|
-
return context.me;
|
|
1691
|
-
if (varName === 'you' && context.you)
|
|
1692
|
-
return context.you;
|
|
1693
|
-
if (varName === 'it' && context.it)
|
|
1694
|
-
return context.it;
|
|
1695
|
-
if (varName === 'result' && context.result)
|
|
1696
|
-
return context.result;
|
|
1697
|
-
if (typeof window !== 'undefined' && varName === 'window') {
|
|
1698
|
-
return window;
|
|
1699
|
-
}
|
|
1700
|
-
if (context.globals?.has(varName)) {
|
|
1701
|
-
return context.globals.get(varName);
|
|
1702
|
-
}
|
|
1703
|
-
return undefined;
|
|
1704
|
-
}
|
|
1705
|
-
return value;
|
|
1706
|
-
}
|
|
1707
|
-
async evaluatePossessiveExpression(node, context) {
|
|
1708
|
-
const { object, property } = node;
|
|
1709
|
-
const objectValue = this.unwrapSelectorResult(await this.evaluate(object, context));
|
|
1710
|
-
if (!objectValue) {
|
|
1711
|
-
return undefined;
|
|
1712
|
-
}
|
|
1713
|
-
const propertyName = extractPropertyName(property);
|
|
1714
|
-
if (typeof propertyName === 'string') {
|
|
1715
|
-
if (isElement(objectValue)) {
|
|
1716
|
-
return getElementProperty(objectValue, propertyName);
|
|
1717
|
-
}
|
|
1718
|
-
return objectValue[propertyName];
|
|
1719
|
-
}
|
|
1720
|
-
return objectValue[propertyName];
|
|
1721
|
-
}
|
|
1722
|
-
hasExpression(name) {
|
|
1723
|
-
return this.expressionRegistry.has(name);
|
|
1724
|
-
}
|
|
1725
|
-
async evaluateCSSSelector(node, context) {
|
|
1726
|
-
return evaluateCSSSelector(node, context);
|
|
1727
|
-
}
|
|
1728
|
-
async evaluateIdSelector(node, context) {
|
|
1729
|
-
return evaluateIdSelector(node, context);
|
|
1730
|
-
}
|
|
1731
|
-
async evaluateQueryReference(node, context) {
|
|
1732
|
-
return evaluateQueryReference(node, context);
|
|
1733
|
-
}
|
|
1734
|
-
async evaluatePropertyAccess(node, context) {
|
|
1735
|
-
const object = await this.evaluate(node.object, context);
|
|
1736
|
-
const propertyNode = node.property;
|
|
1737
|
-
if (object === null || object === undefined) {
|
|
1738
|
-
throw new Error(`Cannot access property "${propertyNode.name || propertyNode.value}" of ${object}`);
|
|
1739
|
-
}
|
|
1740
|
-
const propertyName = propertyNode.name || propertyNode.value;
|
|
1741
|
-
if (!propertyName) {
|
|
1742
|
-
throw new Error('Property name must be an identifier');
|
|
1743
|
-
}
|
|
1744
|
-
try {
|
|
1745
|
-
let value;
|
|
1746
|
-
if (isElement(object)) {
|
|
1747
|
-
value = getElementProperty(object, propertyName);
|
|
1748
|
-
}
|
|
1749
|
-
else {
|
|
1750
|
-
value = object[propertyName];
|
|
1751
|
-
}
|
|
1752
|
-
if (typeof value === 'function') {
|
|
1753
|
-
return value.bind(object);
|
|
1754
|
-
}
|
|
1755
|
-
return value;
|
|
1756
|
-
}
|
|
1757
|
-
catch (error) {
|
|
1758
|
-
throw new Error(`Error accessing property "${propertyName}": ${error}`);
|
|
1759
|
-
}
|
|
1760
|
-
}
|
|
1761
|
-
async evaluatePropertyOfExpression(node, context) {
|
|
1762
|
-
const propertyNode = node.property;
|
|
1763
|
-
const propertyName = propertyNode.name || propertyNode.value;
|
|
1764
|
-
if (!propertyName) {
|
|
1765
|
-
throw new Error('Property name must be an identifier in "the X of Y" pattern');
|
|
1766
|
-
}
|
|
1767
|
-
const target = this.unwrapSelectorResult(await this.evaluate(node.target, context));
|
|
1768
|
-
if (target === null || target === undefined) {
|
|
1769
|
-
throw new Error(`Cannot access property "${propertyName}" of ${target}`);
|
|
1770
|
-
}
|
|
1771
|
-
try {
|
|
1772
|
-
let value;
|
|
1773
|
-
if (isElement(target)) {
|
|
1774
|
-
value = getElementProperty(target, propertyName);
|
|
1775
|
-
}
|
|
1776
|
-
else {
|
|
1777
|
-
value = target[propertyName];
|
|
1778
|
-
}
|
|
1779
|
-
if (typeof value === 'function') {
|
|
1780
|
-
return value.bind(target);
|
|
1781
|
-
}
|
|
1782
|
-
return value;
|
|
1783
|
-
}
|
|
1784
|
-
catch (error) {
|
|
1785
|
-
throw new Error(`Failed to access property "${propertyName}" on target: ${error}`);
|
|
1786
|
-
}
|
|
264
|
+
coerceFrom: {
|
|
265
|
+
String: v => {
|
|
266
|
+
try {
|
|
267
|
+
const parsed = JSON.parse(v);
|
|
268
|
+
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
|
|
269
|
+
return parsed;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
}
|
|
274
|
+
return null;
|
|
275
|
+
},
|
|
276
|
+
Array: v => {
|
|
277
|
+
const arr = v;
|
|
278
|
+
const obj = {};
|
|
279
|
+
arr.forEach((item, index) => {
|
|
280
|
+
obj[String(index)] = item;
|
|
281
|
+
});
|
|
282
|
+
return obj;
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
this.register({
|
|
287
|
+
name: 'Null',
|
|
288
|
+
hyperscriptType: 'null',
|
|
289
|
+
tsType: 'null',
|
|
290
|
+
isType: (v) => v === null,
|
|
291
|
+
defaultValue: null,
|
|
292
|
+
description: 'Null value',
|
|
293
|
+
});
|
|
294
|
+
this.register({
|
|
295
|
+
name: 'Undefined',
|
|
296
|
+
hyperscriptType: 'undefined',
|
|
297
|
+
tsType: 'undefined',
|
|
298
|
+
isType: (v) => v === undefined,
|
|
299
|
+
defaultValue: undefined,
|
|
300
|
+
description: 'Undefined value',
|
|
301
|
+
});
|
|
302
|
+
this.register({
|
|
303
|
+
name: 'Function',
|
|
304
|
+
hyperscriptType: 'function',
|
|
305
|
+
tsType: 'Function',
|
|
306
|
+
isType: (v) => typeof v === 'function',
|
|
307
|
+
defaultValue: null,
|
|
308
|
+
description: 'JavaScript function',
|
|
309
|
+
});
|
|
310
|
+
this.register({
|
|
311
|
+
name: 'Event',
|
|
312
|
+
hyperscriptType: 'event',
|
|
313
|
+
tsType: 'Event',
|
|
314
|
+
isType: (v) => v instanceof Event,
|
|
315
|
+
defaultValue: null,
|
|
316
|
+
description: 'DOM Event',
|
|
317
|
+
});
|
|
318
|
+
this.register({
|
|
319
|
+
name: 'NodeList',
|
|
320
|
+
hyperscriptType: 'element-list',
|
|
321
|
+
tsType: 'NodeList',
|
|
322
|
+
isType: (v) => v instanceof NodeList,
|
|
323
|
+
defaultValue: null,
|
|
324
|
+
description: 'DOM NodeList (typically from querySelectorAll)',
|
|
325
|
+
coerceFrom: {
|
|
326
|
+
Array: v => {
|
|
327
|
+
return null;
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
this.register({
|
|
332
|
+
name: 'Unknown',
|
|
333
|
+
hyperscriptType: 'unknown',
|
|
334
|
+
tsType: 'unknown',
|
|
335
|
+
isType: (_v) => true,
|
|
336
|
+
defaultValue: null,
|
|
337
|
+
description: 'Unknown/any value type',
|
|
338
|
+
});
|
|
1787
339
|
}
|
|
1788
340
|
}
|
|
341
|
+
const expressionTypeRegistry = new ExpressionTypeRegistry();
|
|
1789
342
|
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
343
|
+
function isString(value) {
|
|
344
|
+
const stringType = expressionTypeRegistry.get('String');
|
|
345
|
+
return stringType ? stringType.isType(value) : typeof value === 'string';
|
|
346
|
+
}
|
|
347
|
+
function isNumber(value) {
|
|
348
|
+
const numberType = expressionTypeRegistry.get('Number');
|
|
349
|
+
return numberType ? numberType.isType(value) : typeof value === 'number';
|
|
350
|
+
}
|
|
351
|
+
function isBoolean(value) {
|
|
352
|
+
const boolType = expressionTypeRegistry.get('Boolean');
|
|
353
|
+
return boolType ? boolType.isType(value) : typeof value === 'boolean';
|
|
354
|
+
}
|
|
355
|
+
function isObject(value) {
|
|
356
|
+
const objectType = expressionTypeRegistry.get('Object');
|
|
357
|
+
return objectType ? objectType.isType(value) : typeof value === 'object' && value !== null;
|
|
358
|
+
}
|
|
359
|
+
function isFunction(value) {
|
|
360
|
+
const funcType = expressionTypeRegistry.get('Function');
|
|
361
|
+
return funcType ? funcType.isType(value) : typeof value === 'function';
|
|
1797
362
|
}
|
|
1798
363
|
|
|
1799
364
|
const NUMBER_WORDS = {
|
|
@@ -2744,7 +1309,7 @@ const meExpression = {
|
|
|
2744
1309
|
category: 'Reference',
|
|
2745
1310
|
evaluatesTo: 'Element',
|
|
2746
1311
|
async evaluate(context) {
|
|
2747
|
-
return context.me
|
|
1312
|
+
return context.me ?? null;
|
|
2748
1313
|
},
|
|
2749
1314
|
validate: validateNoArgs,
|
|
2750
1315
|
};
|
|
@@ -2753,7 +1318,7 @@ const youExpression = {
|
|
|
2753
1318
|
category: 'Reference',
|
|
2754
1319
|
evaluatesTo: 'Element',
|
|
2755
1320
|
async evaluate(context) {
|
|
2756
|
-
return context.you
|
|
1321
|
+
return context.you ?? null;
|
|
2757
1322
|
},
|
|
2758
1323
|
validate: validateNoArgs,
|
|
2759
1324
|
};
|
|
@@ -3432,6 +1997,39 @@ function toNumberOrNull(value) {
|
|
|
3432
1997
|
}
|
|
3433
1998
|
return null;
|
|
3434
1999
|
}
|
|
2000
|
+
function ensureFinite(num, operation) {
|
|
2001
|
+
if (!Number.isFinite(num)) {
|
|
2002
|
+
if (Number.isNaN(num)) {
|
|
2003
|
+
throw new Error(`${operation} resulted in non-finite value: NaN`);
|
|
2004
|
+
}
|
|
2005
|
+
throw new Error(`${operation} resulted in non-finite value: ${num > 0 ? 'Infinity' : '-Infinity'}`);
|
|
2006
|
+
}
|
|
2007
|
+
return num;
|
|
2008
|
+
}
|
|
2009
|
+
function canBeNumeric(value) {
|
|
2010
|
+
try {
|
|
2011
|
+
toNumber(value, '_');
|
|
2012
|
+
return true;
|
|
2013
|
+
}
|
|
2014
|
+
catch {
|
|
2015
|
+
return false;
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
function safeDivide(left, right, allowInfinity = true) {
|
|
2019
|
+
if (right === 0) {
|
|
2020
|
+
if (allowInfinity) {
|
|
2021
|
+
return left === 0 ? NaN : left > 0 ? Infinity : -Infinity;
|
|
2022
|
+
}
|
|
2023
|
+
throw new Error('Division by zero');
|
|
2024
|
+
}
|
|
2025
|
+
return left / right;
|
|
2026
|
+
}
|
|
2027
|
+
function safeModulo(left, right) {
|
|
2028
|
+
if (right === 0) {
|
|
2029
|
+
throw new Error('Modulo by zero');
|
|
2030
|
+
}
|
|
2031
|
+
return left % right;
|
|
2032
|
+
}
|
|
3435
2033
|
|
|
3436
2034
|
function compareValues(left, right, operator) {
|
|
3437
2035
|
if (operator === '===') {
|
|
@@ -3541,6 +2139,44 @@ function coerceToComparable(left, right) {
|
|
|
3541
2139
|
return [String(left), String(right)];
|
|
3542
2140
|
}
|
|
3543
2141
|
|
|
2142
|
+
function validResult() {
|
|
2143
|
+
return {
|
|
2144
|
+
isValid: true,
|
|
2145
|
+
errors: [],
|
|
2146
|
+
suggestions: [],
|
|
2147
|
+
};
|
|
2148
|
+
}
|
|
2149
|
+
function invalidResult(errors, suggestions = []) {
|
|
2150
|
+
return {
|
|
2151
|
+
isValid: false,
|
|
2152
|
+
errors,
|
|
2153
|
+
suggestions,
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2156
|
+
function createError(type, message, suggestions = [], severity = 'error') {
|
|
2157
|
+
return { type, message, suggestions, severity };
|
|
2158
|
+
}
|
|
2159
|
+
function validateBinaryInput(input) {
|
|
2160
|
+
if (input === null || input === undefined) {
|
|
2161
|
+
return invalidResult([createError('missing-argument', 'Input is null or undefined')], ['Provide an object with left and right operands']);
|
|
2162
|
+
}
|
|
2163
|
+
if (typeof input !== 'object') {
|
|
2164
|
+
return invalidResult([createError('type-mismatch', `Expected object, got ${typeof input}`)], ['Provide an object with left and right operands']);
|
|
2165
|
+
}
|
|
2166
|
+
const obj = input;
|
|
2167
|
+
if (!('left' in obj) || !('right' in obj)) {
|
|
2168
|
+
const missing = [];
|
|
2169
|
+
if (!('left' in obj))
|
|
2170
|
+
missing.push('left');
|
|
2171
|
+
if (!('right' in obj))
|
|
2172
|
+
missing.push('right');
|
|
2173
|
+
return invalidResult([
|
|
2174
|
+
createError('missing-argument', `Missing ${missing.join(' and ')} operand${missing.length > 1 ? 's' : ''}`),
|
|
2175
|
+
], ['Provide { left: value, right: value }']);
|
|
2176
|
+
}
|
|
2177
|
+
return validResult();
|
|
2178
|
+
}
|
|
2179
|
+
|
|
3544
2180
|
function trackEvaluation(expression, context, args, result, startTime, success = true, error) {
|
|
3545
2181
|
if (context &&
|
|
3546
2182
|
typeof context === 'object' &&
|
|
@@ -4189,10 +2825,6 @@ const noExpression = {
|
|
|
4189
2825
|
result = value.length === 0;
|
|
4190
2826
|
else if (isString(value))
|
|
4191
2827
|
result = false;
|
|
4192
|
-
else if (value instanceof Node || value instanceof Element)
|
|
4193
|
-
result = false;
|
|
4194
|
-
else if (isObject(value))
|
|
4195
|
-
result = Object.keys(value).length === 0;
|
|
4196
2828
|
else
|
|
4197
2829
|
result = false;
|
|
4198
2830
|
if (tracking)
|
|
@@ -4368,6 +3000,33 @@ const endsWithExpression = {
|
|
|
4368
3000
|
return validateArgCount(args, 2, 'endsWith', 'str, suffix');
|
|
4369
3001
|
},
|
|
4370
3002
|
};
|
|
3003
|
+
const betweenExpression = {
|
|
3004
|
+
name: 'between',
|
|
3005
|
+
category: 'Logical',
|
|
3006
|
+
evaluatesTo: 'Boolean',
|
|
3007
|
+
operators: ['is between', 'between'],
|
|
3008
|
+
async evaluate(context, value, min, max) {
|
|
3009
|
+
const tracking = context.evaluationHistory;
|
|
3010
|
+
const startTime = tracking ? Date.now() : 0;
|
|
3011
|
+
let lo = min;
|
|
3012
|
+
let hi = max;
|
|
3013
|
+
if (lo != null && hi != null && lo > hi) {
|
|
3014
|
+
const swap = lo;
|
|
3015
|
+
lo = hi;
|
|
3016
|
+
hi = swap;
|
|
3017
|
+
}
|
|
3018
|
+
const result = lo != null &&
|
|
3019
|
+
hi != null &&
|
|
3020
|
+
value >= lo &&
|
|
3021
|
+
value <= hi;
|
|
3022
|
+
if (tracking)
|
|
3023
|
+
trackEvaluation(this, context, [value, min, max], result, startTime);
|
|
3024
|
+
return result;
|
|
3025
|
+
},
|
|
3026
|
+
validate(args) {
|
|
3027
|
+
return validateArgCount(args, 3, 'between', 'value, min, max');
|
|
3028
|
+
},
|
|
3029
|
+
};
|
|
4371
3030
|
const matchesExpression = {
|
|
4372
3031
|
name: 'matches',
|
|
4373
3032
|
category: 'Logical',
|
|
@@ -4564,11 +3223,438 @@ const logicalExpressions = {
|
|
|
4564
3223
|
doesNotContain: doesNotContainExpression,
|
|
4565
3224
|
startsWith: startsWithExpression,
|
|
4566
3225
|
endsWith: endsWithExpression,
|
|
3226
|
+
between: betweenExpression,
|
|
4567
3227
|
matches: matchesExpression,
|
|
4568
3228
|
has: hasExpression,
|
|
4569
3229
|
doesNotHave: doesNotHaveExpression,
|
|
4570
3230
|
};
|
|
4571
3231
|
|
|
3232
|
+
function mergeFragments(...fragments) {
|
|
3233
|
+
const merged = new Map();
|
|
3234
|
+
for (const fragment of fragments) {
|
|
3235
|
+
for (const [key, entry] of fragment) {
|
|
3236
|
+
const existing = merged.get(key);
|
|
3237
|
+
if (existing) {
|
|
3238
|
+
merged.set(key, {
|
|
3239
|
+
prefix: entry.prefix ?? existing.prefix,
|
|
3240
|
+
infix: entry.infix ?? existing.infix,
|
|
3241
|
+
});
|
|
3242
|
+
}
|
|
3243
|
+
else {
|
|
3244
|
+
merged.set(key, { ...entry });
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
return merged;
|
|
3249
|
+
}
|
|
3250
|
+
function leftAssoc(bp, handler) {
|
|
3251
|
+
return {
|
|
3252
|
+
infix: {
|
|
3253
|
+
bp: [bp, bp + 1],
|
|
3254
|
+
handler: handler ??
|
|
3255
|
+
((left, token, ctx) => {
|
|
3256
|
+
const right = ctx.parseExpr(bp + 1);
|
|
3257
|
+
return {
|
|
3258
|
+
type: 'binaryExpression',
|
|
3259
|
+
operator: token.value,
|
|
3260
|
+
left,
|
|
3261
|
+
right,
|
|
3262
|
+
start: left.start,
|
|
3263
|
+
end: right.end ?? token.end,
|
|
3264
|
+
line: left.line ?? token.line,
|
|
3265
|
+
column: left.column ?? token.column,
|
|
3266
|
+
};
|
|
3267
|
+
}),
|
|
3268
|
+
},
|
|
3269
|
+
};
|
|
3270
|
+
}
|
|
3271
|
+
function rightAssoc(bp, handler) {
|
|
3272
|
+
return {
|
|
3273
|
+
infix: {
|
|
3274
|
+
bp: [bp + 1, bp],
|
|
3275
|
+
handler: ((left, token, ctx) => {
|
|
3276
|
+
const right = ctx.parseExpr(bp);
|
|
3277
|
+
return {
|
|
3278
|
+
type: 'binaryExpression',
|
|
3279
|
+
operator: token.value,
|
|
3280
|
+
left,
|
|
3281
|
+
right,
|
|
3282
|
+
start: left.start,
|
|
3283
|
+
end: right.end ?? token.end,
|
|
3284
|
+
line: left.line ?? token.line,
|
|
3285
|
+
column: left.column ?? token.column,
|
|
3286
|
+
};
|
|
3287
|
+
}),
|
|
3288
|
+
},
|
|
3289
|
+
};
|
|
3290
|
+
}
|
|
3291
|
+
function prefix(bp, handler) {
|
|
3292
|
+
return {
|
|
3293
|
+
prefix: {
|
|
3294
|
+
bp,
|
|
3295
|
+
handler: handler ??
|
|
3296
|
+
((token, ctx) => {
|
|
3297
|
+
const operand = ctx.parseExpr(bp);
|
|
3298
|
+
return {
|
|
3299
|
+
type: 'unaryExpression',
|
|
3300
|
+
operator: token.value,
|
|
3301
|
+
operand,
|
|
3302
|
+
argument: operand,
|
|
3303
|
+
prefix: true,
|
|
3304
|
+
start: token.start,
|
|
3305
|
+
end: operand.end ?? token.end,
|
|
3306
|
+
line: token.line,
|
|
3307
|
+
column: token.column,
|
|
3308
|
+
};
|
|
3309
|
+
}),
|
|
3310
|
+
},
|
|
3311
|
+
};
|
|
3312
|
+
}
|
|
3313
|
+
const CORE_FRAGMENT = new Map([
|
|
3314
|
+
['or', leftAssoc(10)],
|
|
3315
|
+
['||', leftAssoc(10)],
|
|
3316
|
+
['and', leftAssoc(20)],
|
|
3317
|
+
['&&', leftAssoc(20)],
|
|
3318
|
+
['==', leftAssoc(30)],
|
|
3319
|
+
['!=', leftAssoc(30)],
|
|
3320
|
+
['<', leftAssoc(30)],
|
|
3321
|
+
['>', leftAssoc(30)],
|
|
3322
|
+
['<=', leftAssoc(30)],
|
|
3323
|
+
['>=', leftAssoc(30)],
|
|
3324
|
+
['is', leftAssoc(30)],
|
|
3325
|
+
['matches', leftAssoc(30)],
|
|
3326
|
+
['contains', leftAssoc(30)],
|
|
3327
|
+
['starts with', leftAssoc(30)],
|
|
3328
|
+
['ends with', leftAssoc(30)],
|
|
3329
|
+
['does not start with', leftAssoc(30)],
|
|
3330
|
+
['does not end with', leftAssoc(30)],
|
|
3331
|
+
[
|
|
3332
|
+
'is between',
|
|
3333
|
+
leftAssoc(30, (left, _token, ctx) => {
|
|
3334
|
+
const min = ctx.parseExpr(31);
|
|
3335
|
+
const next = ctx.peek();
|
|
3336
|
+
if (!next || next.value.toLowerCase() !== 'and') {
|
|
3337
|
+
throw new Error(`between requires 'and' between min and max operands, got: ${next?.value ?? '<end>'}`);
|
|
3338
|
+
}
|
|
3339
|
+
ctx.advance();
|
|
3340
|
+
const max = ctx.parseExpr(31);
|
|
3341
|
+
return {
|
|
3342
|
+
type: 'betweenExpression',
|
|
3343
|
+
value: left,
|
|
3344
|
+
min,
|
|
3345
|
+
max,
|
|
3346
|
+
negated: false,
|
|
3347
|
+
start: left.start,
|
|
3348
|
+
end: max.end,
|
|
3349
|
+
};
|
|
3350
|
+
}),
|
|
3351
|
+
],
|
|
3352
|
+
[
|
|
3353
|
+
'is not between',
|
|
3354
|
+
leftAssoc(30, (left, _token, ctx) => {
|
|
3355
|
+
const min = ctx.parseExpr(31);
|
|
3356
|
+
const next = ctx.peek();
|
|
3357
|
+
if (!next || next.value.toLowerCase() !== 'and') {
|
|
3358
|
+
throw new Error(`between requires 'and' between min and max operands, got: ${next?.value ?? '<end>'}`);
|
|
3359
|
+
}
|
|
3360
|
+
ctx.advance();
|
|
3361
|
+
const max = ctx.parseExpr(31);
|
|
3362
|
+
return {
|
|
3363
|
+
type: 'betweenExpression',
|
|
3364
|
+
value: left,
|
|
3365
|
+
min,
|
|
3366
|
+
max,
|
|
3367
|
+
negated: true,
|
|
3368
|
+
start: left.start,
|
|
3369
|
+
end: max.end,
|
|
3370
|
+
};
|
|
3371
|
+
}),
|
|
3372
|
+
],
|
|
3373
|
+
[
|
|
3374
|
+
'ignoring case',
|
|
3375
|
+
{
|
|
3376
|
+
infix: {
|
|
3377
|
+
bp: [25, 26],
|
|
3378
|
+
handler: (left, _token, _ctx) => {
|
|
3379
|
+
left.ignoringCase = true;
|
|
3380
|
+
return left;
|
|
3381
|
+
},
|
|
3382
|
+
},
|
|
3383
|
+
},
|
|
3384
|
+
],
|
|
3385
|
+
[
|
|
3386
|
+
'where',
|
|
3387
|
+
leftAssoc(28, (left, _token, ctx) => {
|
|
3388
|
+
const predicate = ctx.parseExpr(29);
|
|
3389
|
+
return {
|
|
3390
|
+
type: 'collectionExpression',
|
|
3391
|
+
operator: 'where',
|
|
3392
|
+
collection: left,
|
|
3393
|
+
right: predicate,
|
|
3394
|
+
start: left.start,
|
|
3395
|
+
end: predicate.end,
|
|
3396
|
+
};
|
|
3397
|
+
}),
|
|
3398
|
+
],
|
|
3399
|
+
[
|
|
3400
|
+
'sorted by',
|
|
3401
|
+
leftAssoc(28, (left, _token, ctx) => {
|
|
3402
|
+
const keyExpr = ctx.parseExpr(29);
|
|
3403
|
+
let order = 'asc';
|
|
3404
|
+
const next = ctx.peek();
|
|
3405
|
+
if (next && typeof next.value === 'string') {
|
|
3406
|
+
const v = next.value.toLowerCase();
|
|
3407
|
+
if (v === 'asc' || v === 'ascending') {
|
|
3408
|
+
ctx.advance();
|
|
3409
|
+
order = 'asc';
|
|
3410
|
+
}
|
|
3411
|
+
else if (v === 'desc' || v === 'descending') {
|
|
3412
|
+
ctx.advance();
|
|
3413
|
+
order = 'desc';
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3416
|
+
return {
|
|
3417
|
+
type: 'collectionExpression',
|
|
3418
|
+
operator: 'sorted by',
|
|
3419
|
+
collection: left,
|
|
3420
|
+
right: keyExpr,
|
|
3421
|
+
order,
|
|
3422
|
+
start: left.start,
|
|
3423
|
+
end: keyExpr.end,
|
|
3424
|
+
};
|
|
3425
|
+
}),
|
|
3426
|
+
],
|
|
3427
|
+
[
|
|
3428
|
+
'mapped to',
|
|
3429
|
+
leftAssoc(28, (left, _token, ctx) => {
|
|
3430
|
+
const expr = ctx.parseExpr(29);
|
|
3431
|
+
return {
|
|
3432
|
+
type: 'collectionExpression',
|
|
3433
|
+
operator: 'mapped to',
|
|
3434
|
+
collection: left,
|
|
3435
|
+
right: expr,
|
|
3436
|
+
start: left.start,
|
|
3437
|
+
end: expr.end,
|
|
3438
|
+
};
|
|
3439
|
+
}),
|
|
3440
|
+
],
|
|
3441
|
+
[
|
|
3442
|
+
'split by',
|
|
3443
|
+
leftAssoc(28, (left, _token, ctx) => {
|
|
3444
|
+
const sep = ctx.parseExpr(29);
|
|
3445
|
+
return {
|
|
3446
|
+
type: 'collectionExpression',
|
|
3447
|
+
operator: 'split by',
|
|
3448
|
+
collection: left,
|
|
3449
|
+
right: sep,
|
|
3450
|
+
start: left.start,
|
|
3451
|
+
end: sep.end,
|
|
3452
|
+
};
|
|
3453
|
+
}),
|
|
3454
|
+
],
|
|
3455
|
+
[
|
|
3456
|
+
'joined by',
|
|
3457
|
+
leftAssoc(28, (left, _token, ctx) => {
|
|
3458
|
+
const sep = ctx.parseExpr(29);
|
|
3459
|
+
return {
|
|
3460
|
+
type: 'collectionExpression',
|
|
3461
|
+
operator: 'joined by',
|
|
3462
|
+
collection: left,
|
|
3463
|
+
right: sep,
|
|
3464
|
+
start: left.start,
|
|
3465
|
+
end: sep.end,
|
|
3466
|
+
};
|
|
3467
|
+
}),
|
|
3468
|
+
],
|
|
3469
|
+
['+', { ...leftAssoc(40), ...prefix(80) }],
|
|
3470
|
+
['-', { ...leftAssoc(40), ...prefix(80) }],
|
|
3471
|
+
['*', leftAssoc(50)],
|
|
3472
|
+
['/', leftAssoc(50)],
|
|
3473
|
+
['%', leftAssoc(50)],
|
|
3474
|
+
['mod', leftAssoc(50)],
|
|
3475
|
+
['^', rightAssoc(60)],
|
|
3476
|
+
['**', rightAssoc(60)],
|
|
3477
|
+
[
|
|
3478
|
+
'as',
|
|
3479
|
+
leftAssoc(70, (left, token, ctx) => {
|
|
3480
|
+
const targetType = ctx.parseExpr(71);
|
|
3481
|
+
const peeked = ctx.peek();
|
|
3482
|
+
if (peeked &&
|
|
3483
|
+
peeked.value === ':' &&
|
|
3484
|
+
targetType &&
|
|
3485
|
+
targetType.type === 'identifier') {
|
|
3486
|
+
ctx.advance();
|
|
3487
|
+
const suffix = ctx.advance();
|
|
3488
|
+
if (suffix && suffix.value !== undefined) {
|
|
3489
|
+
targetType.name = `${targetType.name}:${suffix.value}`;
|
|
3490
|
+
targetType.end = suffix.end;
|
|
3491
|
+
}
|
|
3492
|
+
}
|
|
3493
|
+
return {
|
|
3494
|
+
type: 'asExpression',
|
|
3495
|
+
expression: left,
|
|
3496
|
+
targetType,
|
|
3497
|
+
start: left.start,
|
|
3498
|
+
end: targetType.end ?? token.end,
|
|
3499
|
+
line: left.line ?? token.line,
|
|
3500
|
+
column: left.column ?? token.column,
|
|
3501
|
+
};
|
|
3502
|
+
}),
|
|
3503
|
+
],
|
|
3504
|
+
['not', prefix(80)],
|
|
3505
|
+
['!', prefix(80)],
|
|
3506
|
+
['no', prefix(80)],
|
|
3507
|
+
]);
|
|
3508
|
+
function makeTypeCheckHandler(negated) {
|
|
3509
|
+
return (left, _token, ctx) => {
|
|
3510
|
+
const typeToken = ctx.advance();
|
|
3511
|
+
if (!typeToken || !typeToken.value) {
|
|
3512
|
+
throw new Error(`Type check requires a type name after 'is ${negated ? 'not ' : ''}a/an', got: <end>`);
|
|
3513
|
+
}
|
|
3514
|
+
let nullOk = true;
|
|
3515
|
+
const peeked = ctx.peek();
|
|
3516
|
+
if (peeked && peeked.value === '!') {
|
|
3517
|
+
ctx.advance();
|
|
3518
|
+
nullOk = false;
|
|
3519
|
+
}
|
|
3520
|
+
return {
|
|
3521
|
+
type: 'typeCheckExpression',
|
|
3522
|
+
value: left,
|
|
3523
|
+
typeName: typeToken.value,
|
|
3524
|
+
nullOk,
|
|
3525
|
+
negated,
|
|
3526
|
+
start: left.start,
|
|
3527
|
+
end: typeToken.end,
|
|
3528
|
+
};
|
|
3529
|
+
};
|
|
3530
|
+
}
|
|
3531
|
+
const PARSER_COMPARISON_FRAGMENT = new Map([
|
|
3532
|
+
['===', leftAssoc(30)],
|
|
3533
|
+
['!==', leftAssoc(30)],
|
|
3534
|
+
['is not', leftAssoc(30)],
|
|
3535
|
+
['am', leftAssoc(30)],
|
|
3536
|
+
['is in', leftAssoc(30)],
|
|
3537
|
+
['is not in', leftAssoc(30)],
|
|
3538
|
+
['precedes', leftAssoc(30)],
|
|
3539
|
+
['does not precede', leftAssoc(30)],
|
|
3540
|
+
['follows', leftAssoc(30)],
|
|
3541
|
+
['does not follow', leftAssoc(30)],
|
|
3542
|
+
['is a', leftAssoc(30, makeTypeCheckHandler(false))],
|
|
3543
|
+
['is an', leftAssoc(30, makeTypeCheckHandler(false))],
|
|
3544
|
+
['is not a', leftAssoc(30, makeTypeCheckHandler(true))],
|
|
3545
|
+
['is not an', leftAssoc(30, makeTypeCheckHandler(true))],
|
|
3546
|
+
['has', leftAssoc(30)],
|
|
3547
|
+
['have', leftAssoc(30)],
|
|
3548
|
+
['match', leftAssoc(30)],
|
|
3549
|
+
['include', leftAssoc(30)],
|
|
3550
|
+
['includes', leftAssoc(30)],
|
|
3551
|
+
['equals', leftAssoc(30)],
|
|
3552
|
+
['does not contain', leftAssoc(30)],
|
|
3553
|
+
['does not include', leftAssoc(30)],
|
|
3554
|
+
['is equal to', leftAssoc(30)],
|
|
3555
|
+
['is not equal to', leftAssoc(30)],
|
|
3556
|
+
['is really equal to', leftAssoc(30)],
|
|
3557
|
+
['is not really equal to', leftAssoc(30)],
|
|
3558
|
+
['really equals', leftAssoc(30)],
|
|
3559
|
+
['is greater than', leftAssoc(30)],
|
|
3560
|
+
['is less than', leftAssoc(30)],
|
|
3561
|
+
['is greater than or equal to', leftAssoc(30)],
|
|
3562
|
+
['is less than or equal to', leftAssoc(30)],
|
|
3563
|
+
['in', leftAssoc(30)],
|
|
3564
|
+
['of', leftAssoc(30)],
|
|
3565
|
+
['really', leftAssoc(30)],
|
|
3566
|
+
[
|
|
3567
|
+
'exists',
|
|
3568
|
+
{
|
|
3569
|
+
prefix: {
|
|
3570
|
+
bp: 80,
|
|
3571
|
+
handler: (token, ctx) => ({
|
|
3572
|
+
type: 'unaryExpression',
|
|
3573
|
+
operator: token.value,
|
|
3574
|
+
operand: ctx.parseExpr(80),
|
|
3575
|
+
start: token.start,
|
|
3576
|
+
}),
|
|
3577
|
+
},
|
|
3578
|
+
infix: {
|
|
3579
|
+
bp: [30, 31],
|
|
3580
|
+
handler: (left, token) => ({
|
|
3581
|
+
type: 'unaryExpression',
|
|
3582
|
+
operator: token.value,
|
|
3583
|
+
operand: left,
|
|
3584
|
+
prefix: false,
|
|
3585
|
+
start: left.start,
|
|
3586
|
+
}),
|
|
3587
|
+
},
|
|
3588
|
+
},
|
|
3589
|
+
],
|
|
3590
|
+
[
|
|
3591
|
+
'does not exist',
|
|
3592
|
+
{
|
|
3593
|
+
infix: {
|
|
3594
|
+
bp: [30, 31],
|
|
3595
|
+
handler: (left, token) => ({
|
|
3596
|
+
type: 'unaryExpression',
|
|
3597
|
+
operator: token.value,
|
|
3598
|
+
operand: left,
|
|
3599
|
+
prefix: false,
|
|
3600
|
+
start: left.start,
|
|
3601
|
+
}),
|
|
3602
|
+
},
|
|
3603
|
+
},
|
|
3604
|
+
],
|
|
3605
|
+
[
|
|
3606
|
+
'is empty',
|
|
3607
|
+
{
|
|
3608
|
+
infix: {
|
|
3609
|
+
bp: [30, 31],
|
|
3610
|
+
handler: (left, token) => ({
|
|
3611
|
+
type: 'unaryExpression',
|
|
3612
|
+
operator: token.value,
|
|
3613
|
+
operand: left,
|
|
3614
|
+
prefix: false,
|
|
3615
|
+
start: left.start,
|
|
3616
|
+
}),
|
|
3617
|
+
},
|
|
3618
|
+
},
|
|
3619
|
+
],
|
|
3620
|
+
[
|
|
3621
|
+
'is not empty',
|
|
3622
|
+
{
|
|
3623
|
+
infix: {
|
|
3624
|
+
bp: [30, 31],
|
|
3625
|
+
handler: (left, token) => ({
|
|
3626
|
+
type: 'unaryExpression',
|
|
3627
|
+
operator: token.value,
|
|
3628
|
+
operand: left,
|
|
3629
|
+
prefix: false,
|
|
3630
|
+
start: left.start,
|
|
3631
|
+
}),
|
|
3632
|
+
},
|
|
3633
|
+
},
|
|
3634
|
+
],
|
|
3635
|
+
['some', prefix(80)],
|
|
3636
|
+
]);
|
|
3637
|
+
const ASSIGNMENT_FRAGMENT = new Map([
|
|
3638
|
+
['=', rightAssoc(5)],
|
|
3639
|
+
]);
|
|
3640
|
+
mergeFragments(CORE_FRAGMENT, PARSER_COMPARISON_FRAGMENT, ASSIGNMENT_FRAGMENT);
|
|
3641
|
+
|
|
3642
|
+
const LOCAL_READ_HOOKS = new Set();
|
|
3643
|
+
function notifyLocalRead(name, context) {
|
|
3644
|
+
if (LOCAL_READ_HOOKS.size === 0)
|
|
3645
|
+
return;
|
|
3646
|
+
for (const hook of LOCAL_READ_HOOKS) {
|
|
3647
|
+
try {
|
|
3648
|
+
hook(name, context);
|
|
3649
|
+
}
|
|
3650
|
+
catch (err) {
|
|
3651
|
+
if (typeof console !== 'undefined') {
|
|
3652
|
+
console.error('[hyperfixi] localReadHook threw:', err);
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
}
|
|
3656
|
+
}
|
|
3657
|
+
|
|
4572
3658
|
const StringLiteralInputSchema = v
|
|
4573
3659
|
.object({
|
|
4574
3660
|
value: v.string().describe('String literal value'),
|
|
@@ -4682,6 +3768,7 @@ class StringLiteralExpression extends BaseExpressionImpl {
|
|
|
4682
3768
|
if (varName === 'result' && context.result)
|
|
4683
3769
|
return context.result;
|
|
4684
3770
|
if (context.locals?.has(varName)) {
|
|
3771
|
+
notifyLocalRead(varName, context);
|
|
4685
3772
|
return context.locals.get(varName);
|
|
4686
3773
|
}
|
|
4687
3774
|
if (context.globals?.has(varName)) {
|
|
@@ -4791,7 +3878,7 @@ class BooleanLiteralExpression extends BaseExpressionImpl {
|
|
|
4791
3878
|
}
|
|
4792
3879
|
}
|
|
4793
3880
|
}
|
|
4794
|
-
class AdditionExpression extends BaseExpressionImpl {
|
|
3881
|
+
let AdditionExpression$1 = class AdditionExpression extends BaseExpressionImpl {
|
|
4795
3882
|
constructor() {
|
|
4796
3883
|
super(...arguments);
|
|
4797
3884
|
this.name = 'addition';
|
|
@@ -4838,7 +3925,7 @@ class AdditionExpression extends BaseExpressionImpl {
|
|
|
4838
3925
|
]);
|
|
4839
3926
|
}
|
|
4840
3927
|
}
|
|
4841
|
-
}
|
|
3928
|
+
};
|
|
4842
3929
|
class StringConcatenationExpression extends BaseExpressionImpl {
|
|
4843
3930
|
constructor() {
|
|
4844
3931
|
super(...arguments);
|
|
@@ -4901,7 +3988,7 @@ class StringConcatenationExpression extends BaseExpressionImpl {
|
|
|
4901
3988
|
}
|
|
4902
3989
|
}
|
|
4903
3990
|
}
|
|
4904
|
-
class MultiplicationExpression extends BaseExpressionImpl {
|
|
3991
|
+
let MultiplicationExpression$1 = class MultiplicationExpression extends BaseExpressionImpl {
|
|
4905
3992
|
constructor() {
|
|
4906
3993
|
super(...arguments);
|
|
4907
3994
|
this.name = 'multiplication';
|
|
@@ -4948,7 +4035,7 @@ class MultiplicationExpression extends BaseExpressionImpl {
|
|
|
4948
4035
|
]);
|
|
4949
4036
|
}
|
|
4950
4037
|
}
|
|
4951
|
-
}
|
|
4038
|
+
};
|
|
4952
4039
|
function createStringLiteralExpression() {
|
|
4953
4040
|
return new StringLiteralExpression();
|
|
4954
4041
|
}
|
|
@@ -4958,30 +4045,26 @@ function createNumberLiteralExpression() {
|
|
|
4958
4045
|
function createBooleanLiteralExpression() {
|
|
4959
4046
|
return new BooleanLiteralExpression();
|
|
4960
4047
|
}
|
|
4961
|
-
function createAdditionExpression() {
|
|
4962
|
-
return new AdditionExpression();
|
|
4048
|
+
function createAdditionExpression$1() {
|
|
4049
|
+
return new AdditionExpression$1();
|
|
4963
4050
|
}
|
|
4964
4051
|
function createStringConcatenationExpression() {
|
|
4965
4052
|
return new StringConcatenationExpression();
|
|
4966
4053
|
}
|
|
4967
|
-
function createMultiplicationExpression() {
|
|
4968
|
-
return new MultiplicationExpression();
|
|
4054
|
+
function createMultiplicationExpression$1() {
|
|
4055
|
+
return new MultiplicationExpression$1();
|
|
4969
4056
|
}
|
|
4970
4057
|
const specialExpressions = {
|
|
4971
4058
|
stringLiteral: createStringLiteralExpression(),
|
|
4972
4059
|
numberLiteral: createNumberLiteralExpression(),
|
|
4973
4060
|
booleanLiteral: createBooleanLiteralExpression(),
|
|
4974
|
-
addition: createAdditionExpression(),
|
|
4061
|
+
addition: createAdditionExpression$1(),
|
|
4975
4062
|
stringConcatenation: createStringConcatenationExpression(),
|
|
4976
|
-
multiplication: createMultiplicationExpression(),
|
|
4063
|
+
multiplication: createMultiplicationExpression$1(),
|
|
4977
4064
|
};
|
|
4978
4065
|
|
|
4979
|
-
function
|
|
4980
|
-
return
|
|
4981
|
-
referencesExpressions,
|
|
4982
|
-
logicalExpressions,
|
|
4983
|
-
specialExpressions,
|
|
4984
|
-
]);
|
|
4066
|
+
function createCoreRegistry() {
|
|
4067
|
+
return createExpressionRegistry(referencesExpressions, logicalExpressions, specialExpressions);
|
|
4985
4068
|
}
|
|
4986
4069
|
|
|
4987
4070
|
function converterError(name, code, message, suggestions, type = 'runtime-error') {
|
|
@@ -5096,11 +4179,21 @@ const enhancedConverters = {
|
|
|
5096
4179
|
}
|
|
5097
4180
|
},
|
|
5098
4181
|
JSON: (value, _context) => {
|
|
4182
|
+
if (!isString(value))
|
|
4183
|
+
return success(value, 'object');
|
|
4184
|
+
try {
|
|
4185
|
+
return success(JSON.parse(value), 'object');
|
|
4186
|
+
}
|
|
4187
|
+
catch (error) {
|
|
4188
|
+
return converterError('JSON', 'JSON_PARSE_FAILED', errorMsg('Failed to parse value as JSON', error), ['Check JSON syntax and escaping', 'Use `as JSONString` to stringify a value instead'], 'syntax-error');
|
|
4189
|
+
}
|
|
4190
|
+
},
|
|
4191
|
+
JSONString: (value, _context) => {
|
|
5099
4192
|
try {
|
|
5100
4193
|
return success(JSON.stringify(value), 'string');
|
|
5101
4194
|
}
|
|
5102
4195
|
catch (error) {
|
|
5103
|
-
return converterError('
|
|
4196
|
+
return converterError('JSONString', 'JSON_STRINGIFY_FAILED', errorMsg('Failed to stringify value as JSON', error), [
|
|
5104
4197
|
'Check for circular references',
|
|
5105
4198
|
'Ensure all properties are serializable',
|
|
5106
4199
|
'Remove functions and undefined values',
|
|
@@ -5154,6 +4247,35 @@ const enhancedConverters = {
|
|
|
5154
4247
|
]);
|
|
5155
4248
|
}
|
|
5156
4249
|
},
|
|
4250
|
+
FormEncoded: (value, context) => {
|
|
4251
|
+
try {
|
|
4252
|
+
let values = value;
|
|
4253
|
+
if (value instanceof HTMLElement) {
|
|
4254
|
+
const extracted = enhancedConverters.Values(value, context);
|
|
4255
|
+
if (!extracted.success)
|
|
4256
|
+
return extracted;
|
|
4257
|
+
values = extracted.value;
|
|
4258
|
+
}
|
|
4259
|
+
if (!isObject(values))
|
|
4260
|
+
return success('', 'string');
|
|
4261
|
+
const params = new URLSearchParams();
|
|
4262
|
+
for (const [k, v] of Object.entries(values)) {
|
|
4263
|
+
if (v === undefined || v === null)
|
|
4264
|
+
continue;
|
|
4265
|
+
if (Array.isArray(v)) {
|
|
4266
|
+
for (const item of v)
|
|
4267
|
+
params.append(k, String(item));
|
|
4268
|
+
}
|
|
4269
|
+
else {
|
|
4270
|
+
params.append(k, String(v));
|
|
4271
|
+
}
|
|
4272
|
+
}
|
|
4273
|
+
return success(params.toString(), 'string');
|
|
4274
|
+
}
|
|
4275
|
+
catch (error) {
|
|
4276
|
+
return converterError('FormEncoded', 'FORM_ENCODE_FAILED', errorMsg('Failed to URL-encode values', error), ['Ensure input is a form element or plain object of scalar/array values']);
|
|
4277
|
+
}
|
|
4278
|
+
},
|
|
5157
4279
|
};
|
|
5158
4280
|
const AsExpressionInputSchema = v.object({
|
|
5159
4281
|
value: v.any(),
|
|
@@ -5186,10 +4308,15 @@ class AsExpression extends BaseExpressionImpl {
|
|
|
5186
4308
|
expectedOutput: { name: 'John', age: '25' },
|
|
5187
4309
|
},
|
|
5188
4310
|
{
|
|
5189
|
-
input: '[1,2,3] as
|
|
5190
|
-
description: 'Convert array to JSON string',
|
|
4311
|
+
input: '[1,2,3] as JSONString',
|
|
4312
|
+
description: 'Convert array to JSON string (upstream 0.9.90: use JSONString, not JSON)',
|
|
5191
4313
|
expectedOutput: '[1,2,3]',
|
|
5192
4314
|
},
|
|
4315
|
+
{
|
|
4316
|
+
input: '\'{"a":1}\' as JSON',
|
|
4317
|
+
description: 'Parse a JSON string (upstream 0.9.90: as JSON now parses)',
|
|
4318
|
+
expectedOutput: { a: 1 },
|
|
4319
|
+
},
|
|
5193
4320
|
{
|
|
5194
4321
|
input: '"2023-12-25" as Date',
|
|
5195
4322
|
description: 'Parse date string',
|
|
@@ -5254,8 +4381,8 @@ class AsExpression extends BaseExpressionImpl {
|
|
|
5254
4381
|
},
|
|
5255
4382
|
{
|
|
5256
4383
|
title: 'Array to JSON serialization',
|
|
5257
|
-
code: 'put items as
|
|
5258
|
-
explanation: 'Convert array to JSON string for storage',
|
|
4384
|
+
code: 'put items as JSONString into storage',
|
|
4385
|
+
explanation: 'Convert array to JSON string for storage (upstream 0.9.90: use `as JSONString`)',
|
|
5259
4386
|
output: '"[1,2,3]"',
|
|
5260
4387
|
},
|
|
5261
4388
|
{
|
|
@@ -5326,6 +4453,8 @@ class AsExpression extends BaseExpressionImpl {
|
|
|
5326
4453
|
obj: 'Object',
|
|
5327
4454
|
date: 'Date',
|
|
5328
4455
|
json: 'JSON',
|
|
4456
|
+
jsonstring: 'JSONString',
|
|
4457
|
+
formencoded: 'FormEncoded',
|
|
5329
4458
|
};
|
|
5330
4459
|
const aliasedType = typeAliases[lowerType];
|
|
5331
4460
|
if (aliasedType) {
|
|
@@ -5672,14 +4801,24 @@ const defaultConversions = {
|
|
|
5672
4801
|
const [year, month, day] = value.split('-').map(Number);
|
|
5673
4802
|
return new Date(year, month - 1, day);
|
|
5674
4803
|
}
|
|
5675
|
-
const date = new Date(value);
|
|
5676
|
-
return date;
|
|
4804
|
+
const date = new Date(value);
|
|
4805
|
+
return date;
|
|
4806
|
+
},
|
|
4807
|
+
JSON: (value) => {
|
|
4808
|
+
if (!isString(value))
|
|
4809
|
+
return value;
|
|
4810
|
+
try {
|
|
4811
|
+
return JSON.parse(value);
|
|
4812
|
+
}
|
|
4813
|
+
catch {
|
|
4814
|
+
return value;
|
|
4815
|
+
}
|
|
5677
4816
|
},
|
|
5678
|
-
|
|
4817
|
+
JSONString: (value) => {
|
|
5679
4818
|
try {
|
|
5680
4819
|
return JSON.stringify(value);
|
|
5681
4820
|
}
|
|
5682
|
-
catch
|
|
4821
|
+
catch {
|
|
5683
4822
|
return '{}';
|
|
5684
4823
|
}
|
|
5685
4824
|
},
|
|
@@ -5739,9 +4878,27 @@ const defaultConversions = {
|
|
|
5739
4878
|
}
|
|
5740
4879
|
return {};
|
|
5741
4880
|
},
|
|
4881
|
+
FormEncoded: (value, context) => {
|
|
4882
|
+
const values = value instanceof HTMLElement ? defaultConversions.Values(value, context) : value;
|
|
4883
|
+
if (!isObject(values))
|
|
4884
|
+
return '';
|
|
4885
|
+
const params = new URLSearchParams();
|
|
4886
|
+
for (const [k, v] of Object.entries(values)) {
|
|
4887
|
+
if (v === undefined || v === null)
|
|
4888
|
+
continue;
|
|
4889
|
+
if (Array.isArray(v)) {
|
|
4890
|
+
for (const item of v)
|
|
4891
|
+
params.append(k, String(item));
|
|
4892
|
+
}
|
|
4893
|
+
else {
|
|
4894
|
+
params.append(k, String(v));
|
|
4895
|
+
}
|
|
4896
|
+
}
|
|
4897
|
+
return params.toString();
|
|
4898
|
+
},
|
|
5742
4899
|
'Values:Form': (value, context) => {
|
|
5743
4900
|
const values = defaultConversions.Values(value, context);
|
|
5744
|
-
return
|
|
4901
|
+
return defaultConversions.FormEncoded(values, context);
|
|
5745
4902
|
},
|
|
5746
4903
|
'Values:JSON': (value, context) => {
|
|
5747
4904
|
const values = defaultConversions.Values(value, context);
|
|
@@ -5857,6 +5014,8 @@ const asExpression = {
|
|
|
5857
5014
|
obj: 'Object',
|
|
5858
5015
|
date: 'Date',
|
|
5859
5016
|
json: 'JSON',
|
|
5017
|
+
jsonstring: 'JSONString',
|
|
5018
|
+
formencoded: 'FormEncoded',
|
|
5860
5019
|
};
|
|
5861
5020
|
const aliasedType = typeAliases[lowerType];
|
|
5862
5021
|
if (aliasedType) {
|
|
@@ -6595,14 +5754,127 @@ const positionalExpressions = {
|
|
|
6595
5754
|
previousWithin: previousWithinExpression,
|
|
6596
5755
|
};
|
|
6597
5756
|
|
|
6598
|
-
function
|
|
6599
|
-
return
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
5757
|
+
function createCommonRegistry() {
|
|
5758
|
+
return createExpressionRegistry(referencesExpressions, logicalExpressions, specialExpressions, conversionExpressions, positionalExpressions);
|
|
5759
|
+
}
|
|
5760
|
+
|
|
5761
|
+
function isInputElement(el) {
|
|
5762
|
+
return (el !== null && typeof el === 'object' && 'tagName' in el && el.tagName === 'INPUT');
|
|
5763
|
+
}
|
|
5764
|
+
function isFormElement(el) {
|
|
5765
|
+
if (el === null || typeof el !== 'object' || !('tagName' in el)) {
|
|
5766
|
+
return false;
|
|
5767
|
+
}
|
|
5768
|
+
const tag = el.tagName;
|
|
5769
|
+
return tag === 'INPUT' || tag === 'SELECT' || tag === 'TEXTAREA';
|
|
5770
|
+
}
|
|
5771
|
+
function isHTMLElement$1(el) {
|
|
5772
|
+
return (el !== null &&
|
|
5773
|
+
typeof el === 'object' &&
|
|
5774
|
+
'nodeType' in el &&
|
|
5775
|
+
el.nodeType === 1 &&
|
|
5776
|
+
'style' in el);
|
|
5777
|
+
}
|
|
5778
|
+
function isOptionElement(el) {
|
|
5779
|
+
return (el !== null &&
|
|
5780
|
+
typeof el !== 'undefined' &&
|
|
5781
|
+
typeof el === 'object' &&
|
|
5782
|
+
'tagName' in el &&
|
|
5783
|
+
el.tagName === 'OPTION');
|
|
5784
|
+
}
|
|
5785
|
+
|
|
5786
|
+
function isHTMLElement(value) {
|
|
5787
|
+
return value instanceof HTMLElement;
|
|
5788
|
+
}
|
|
5789
|
+
const BOOLEAN_ATTRIBUTES = new Set([
|
|
5790
|
+
'disabled',
|
|
5791
|
+
'readonly',
|
|
5792
|
+
'required',
|
|
5793
|
+
'checked',
|
|
5794
|
+
'selected',
|
|
5795
|
+
'hidden',
|
|
5796
|
+
'open',
|
|
5797
|
+
'autofocus',
|
|
5798
|
+
'autoplay',
|
|
5799
|
+
'controls',
|
|
5800
|
+
'loop',
|
|
5801
|
+
'muted',
|
|
5802
|
+
'multiple',
|
|
5803
|
+
'reversed',
|
|
5804
|
+
'defer',
|
|
5805
|
+
'async',
|
|
5806
|
+
'novalidate',
|
|
5807
|
+
'formnovalidate',
|
|
5808
|
+
'ismap',
|
|
5809
|
+
]);
|
|
5810
|
+
function accessAttribute(element, attrName) {
|
|
5811
|
+
if (BOOLEAN_ATTRIBUTES.has(attrName.toLowerCase())) {
|
|
5812
|
+
return element.hasAttribute(attrName);
|
|
5813
|
+
}
|
|
5814
|
+
return element.getAttribute(attrName);
|
|
5815
|
+
}
|
|
5816
|
+
function hasAttribute(element, attrName) {
|
|
5817
|
+
return element.hasAttribute(attrName);
|
|
5818
|
+
}
|
|
5819
|
+
const SPECIAL_DOM_PROPERTIES = {
|
|
5820
|
+
id: el => el.id,
|
|
5821
|
+
classname: el => el.className,
|
|
5822
|
+
class: el => el.className,
|
|
5823
|
+
tagname: el => el.tagName.toLowerCase(),
|
|
5824
|
+
innertext: el => el.textContent?.trim(),
|
|
5825
|
+
innerHTML: el => el.innerHTML,
|
|
5826
|
+
outerhtml: el => el.outerHTML,
|
|
5827
|
+
value: el => (isFormElement(el) ? el.value : undefined),
|
|
5828
|
+
checked: el => (isInputElement(el) ? el.checked : undefined),
|
|
5829
|
+
disabled: el => ('disabled' in el ? el.disabled : undefined),
|
|
5830
|
+
selected: el => (isOptionElement(el) ? el.selected : undefined),
|
|
5831
|
+
tabindex: el => (isHTMLElement$1(el) ? el.tabIndex : undefined),
|
|
5832
|
+
hidden: el => (isHTMLElement$1(el) ? el.hidden : undefined),
|
|
5833
|
+
style: el => getComputedStyle(el),
|
|
5834
|
+
children: el => Array.from(el.children),
|
|
5835
|
+
parent: el => el.parentElement,
|
|
5836
|
+
firstchild: el => el.firstElementChild,
|
|
5837
|
+
lastchild: el => el.lastElementChild,
|
|
5838
|
+
nextsibling: el => el.nextElementSibling,
|
|
5839
|
+
previoussibling: el => el.previousElementSibling,
|
|
5840
|
+
values: el => collectFormValues(el),
|
|
5841
|
+
};
|
|
5842
|
+
function collectFormValues(el) {
|
|
5843
|
+
if (el instanceof HTMLFormElement)
|
|
5844
|
+
return new FormData(el);
|
|
5845
|
+
const fd = new FormData();
|
|
5846
|
+
el.querySelectorAll('input, select, textarea').forEach((input) => {
|
|
5847
|
+
const name = input.getAttribute('name');
|
|
5848
|
+
if (name && 'value' in input)
|
|
5849
|
+
fd.append(name, input.value);
|
|
5850
|
+
});
|
|
5851
|
+
return fd;
|
|
5852
|
+
}
|
|
5853
|
+
function getElementProperty(element, property) {
|
|
5854
|
+
if (property.startsWith('computed-')) {
|
|
5855
|
+
const cssProperty = property.slice('computed-'.length);
|
|
5856
|
+
if (isHTMLElement(element)) {
|
|
5857
|
+
const computedStyle = getComputedStyle(element);
|
|
5858
|
+
return computedStyle.getPropertyValue(cssProperty);
|
|
5859
|
+
}
|
|
5860
|
+
return undefined;
|
|
5861
|
+
}
|
|
5862
|
+
if (property.startsWith('@')) {
|
|
5863
|
+
const attrName = property.slice(1);
|
|
5864
|
+
return accessAttribute(element, attrName);
|
|
5865
|
+
}
|
|
5866
|
+
const specialHandler = SPECIAL_DOM_PROPERTIES[property.toLowerCase()];
|
|
5867
|
+
if (specialHandler) {
|
|
5868
|
+
return specialHandler(element);
|
|
5869
|
+
}
|
|
5870
|
+
if (hasAttribute(element, property)) {
|
|
5871
|
+
return accessAttribute(element, property);
|
|
5872
|
+
}
|
|
5873
|
+
const value = element[property];
|
|
5874
|
+
if (isFunction(value)) {
|
|
5875
|
+
return value.bind(element);
|
|
5876
|
+
}
|
|
5877
|
+
return value;
|
|
6606
5878
|
}
|
|
6607
5879
|
|
|
6608
5880
|
const UNSAFE_PROPS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
@@ -6844,16 +6116,271 @@ const propertiesExpressions = {
|
|
|
6844
6116
|
idReference: idReferenceExpression,
|
|
6845
6117
|
};
|
|
6846
6118
|
|
|
6847
|
-
function
|
|
6848
|
-
return
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6119
|
+
function createFullRegistry() {
|
|
6120
|
+
return createExpressionRegistry(referencesExpressions, logicalExpressions, specialExpressions, conversionExpressions, positionalExpressions, propertiesExpressions);
|
|
6121
|
+
}
|
|
6122
|
+
|
|
6123
|
+
class AdditionExpression {
|
|
6124
|
+
constructor() {
|
|
6125
|
+
this.name = 'addition';
|
|
6126
|
+
this.category = 'Special';
|
|
6127
|
+
this.syntax = 'left + right';
|
|
6128
|
+
this.outputType = 'number';
|
|
6129
|
+
this.metadata = { category: 'Special', complexity: 'simple' };
|
|
6130
|
+
}
|
|
6131
|
+
async evaluate(_context, input) {
|
|
6132
|
+
const validation = this.validate(input);
|
|
6133
|
+
if (!validation.isValid) {
|
|
6134
|
+
return { success: false, errors: validation.errors, suggestions: validation.suggestions };
|
|
6135
|
+
}
|
|
6136
|
+
try {
|
|
6137
|
+
const { left, right } = input;
|
|
6138
|
+
const leftNum = toNumber(left, 'left operand');
|
|
6139
|
+
const rightNum = toNumber(right, 'right operand');
|
|
6140
|
+
const result = ensureFinite(leftNum + rightNum, 'addition');
|
|
6141
|
+
return { success: true, value: result, type: 'number' };
|
|
6142
|
+
}
|
|
6143
|
+
catch (error) {
|
|
6144
|
+
return {
|
|
6145
|
+
success: false,
|
|
6146
|
+
errors: [
|
|
6147
|
+
createError('runtime-error', `Addition failed: ${error instanceof Error ? error.message : String(error)}`),
|
|
6148
|
+
],
|
|
6149
|
+
suggestions: ['Ensure both operands are numeric'],
|
|
6150
|
+
};
|
|
6151
|
+
}
|
|
6152
|
+
}
|
|
6153
|
+
validate(input) {
|
|
6154
|
+
const result = validateBinaryInput(input);
|
|
6155
|
+
if (!result.isValid)
|
|
6156
|
+
return result;
|
|
6157
|
+
const { left, right } = input;
|
|
6158
|
+
if (!canBeNumeric(left)) {
|
|
6159
|
+
return {
|
|
6160
|
+
isValid: false,
|
|
6161
|
+
errors: [
|
|
6162
|
+
createError('type-mismatch', `Left operand cannot be converted to number: ${String(left)}`),
|
|
6163
|
+
],
|
|
6164
|
+
suggestions: ['Provide a numeric value for left operand'],
|
|
6165
|
+
};
|
|
6166
|
+
}
|
|
6167
|
+
if (!canBeNumeric(right)) {
|
|
6168
|
+
return {
|
|
6169
|
+
isValid: false,
|
|
6170
|
+
errors: [
|
|
6171
|
+
createError('type-mismatch', `Right operand cannot be converted to number: ${String(right)}`),
|
|
6172
|
+
],
|
|
6173
|
+
suggestions: ['Provide a numeric value for right operand'],
|
|
6174
|
+
};
|
|
6175
|
+
}
|
|
6176
|
+
return { isValid: true, errors: [], suggestions: [] };
|
|
6177
|
+
}
|
|
6178
|
+
}
|
|
6179
|
+
class SubtractionExpression {
|
|
6180
|
+
constructor() {
|
|
6181
|
+
this.name = 'subtraction';
|
|
6182
|
+
this.category = 'Special';
|
|
6183
|
+
this.syntax = 'left - right';
|
|
6184
|
+
this.outputType = 'number';
|
|
6185
|
+
this.metadata = { category: 'Special', complexity: 'simple' };
|
|
6186
|
+
}
|
|
6187
|
+
async evaluate(_context, input) {
|
|
6188
|
+
const validation = this.validate(input);
|
|
6189
|
+
if (!validation.isValid) {
|
|
6190
|
+
return { success: false, errors: validation.errors, suggestions: validation.suggestions };
|
|
6191
|
+
}
|
|
6192
|
+
try {
|
|
6193
|
+
const { left, right } = input;
|
|
6194
|
+
const leftNum = toNumber(left, 'left operand');
|
|
6195
|
+
const rightNum = toNumber(right, 'right operand');
|
|
6196
|
+
const result = ensureFinite(leftNum - rightNum, 'subtraction');
|
|
6197
|
+
return { success: true, value: result, type: 'number' };
|
|
6198
|
+
}
|
|
6199
|
+
catch (error) {
|
|
6200
|
+
return {
|
|
6201
|
+
success: false,
|
|
6202
|
+
errors: [
|
|
6203
|
+
createError('runtime-error', `Subtraction failed: ${error instanceof Error ? error.message : String(error)}`),
|
|
6204
|
+
],
|
|
6205
|
+
suggestions: ['Ensure both operands are numeric'],
|
|
6206
|
+
};
|
|
6207
|
+
}
|
|
6208
|
+
}
|
|
6209
|
+
validate(input) {
|
|
6210
|
+
const addExpr = new AdditionExpression();
|
|
6211
|
+
return addExpr.validate(input);
|
|
6212
|
+
}
|
|
6213
|
+
}
|
|
6214
|
+
class MultiplicationExpression {
|
|
6215
|
+
constructor() {
|
|
6216
|
+
this.name = 'multiplication';
|
|
6217
|
+
this.category = 'Special';
|
|
6218
|
+
this.syntax = 'left * right';
|
|
6219
|
+
this.outputType = 'number';
|
|
6220
|
+
this.metadata = { category: 'Special', complexity: 'simple' };
|
|
6221
|
+
}
|
|
6222
|
+
async evaluate(_context, input) {
|
|
6223
|
+
const validation = this.validate(input);
|
|
6224
|
+
if (!validation.isValid) {
|
|
6225
|
+
return { success: false, errors: validation.errors, suggestions: validation.suggestions };
|
|
6226
|
+
}
|
|
6227
|
+
try {
|
|
6228
|
+
const { left, right } = input;
|
|
6229
|
+
const leftNum = toNumber(left, 'left operand');
|
|
6230
|
+
const rightNum = toNumber(right, 'right operand');
|
|
6231
|
+
const result = ensureFinite(leftNum * rightNum, 'multiplication');
|
|
6232
|
+
return { success: true, value: result, type: 'number' };
|
|
6233
|
+
}
|
|
6234
|
+
catch (error) {
|
|
6235
|
+
return {
|
|
6236
|
+
success: false,
|
|
6237
|
+
errors: [
|
|
6238
|
+
createError('runtime-error', `Multiplication failed: ${error instanceof Error ? error.message : String(error)}`),
|
|
6239
|
+
],
|
|
6240
|
+
suggestions: ['Ensure both operands are numeric'],
|
|
6241
|
+
};
|
|
6242
|
+
}
|
|
6243
|
+
}
|
|
6244
|
+
validate(input) {
|
|
6245
|
+
const addExpr = new AdditionExpression();
|
|
6246
|
+
return addExpr.validate(input);
|
|
6247
|
+
}
|
|
6248
|
+
}
|
|
6249
|
+
class DivisionExpression {
|
|
6250
|
+
constructor() {
|
|
6251
|
+
this.name = 'division';
|
|
6252
|
+
this.category = 'Special';
|
|
6253
|
+
this.syntax = 'left / right';
|
|
6254
|
+
this.outputType = 'number';
|
|
6255
|
+
this.metadata = { category: 'Special', complexity: 'simple' };
|
|
6256
|
+
}
|
|
6257
|
+
async evaluate(_context, input) {
|
|
6258
|
+
const validation = this.validate(input);
|
|
6259
|
+
if (!validation.isValid) {
|
|
6260
|
+
return { success: false, errors: validation.errors, suggestions: validation.suggestions };
|
|
6261
|
+
}
|
|
6262
|
+
try {
|
|
6263
|
+
const { left, right } = input;
|
|
6264
|
+
const leftNum = toNumber(left, 'left operand');
|
|
6265
|
+
const rightNum = toNumber(right, 'right operand');
|
|
6266
|
+
const result = safeDivide(leftNum, rightNum, true);
|
|
6267
|
+
return { success: true, value: result, type: 'number' };
|
|
6268
|
+
}
|
|
6269
|
+
catch (error) {
|
|
6270
|
+
return {
|
|
6271
|
+
success: false,
|
|
6272
|
+
errors: [
|
|
6273
|
+
createError('runtime-error', `Division failed: ${error instanceof Error ? error.message : String(error)}`),
|
|
6274
|
+
],
|
|
6275
|
+
suggestions: ['Ensure both operands are numeric'],
|
|
6276
|
+
};
|
|
6277
|
+
}
|
|
6278
|
+
}
|
|
6279
|
+
validate(input) {
|
|
6280
|
+
const addExpr = new AdditionExpression();
|
|
6281
|
+
return addExpr.validate(input);
|
|
6282
|
+
}
|
|
6283
|
+
}
|
|
6284
|
+
class ModuloExpression {
|
|
6285
|
+
constructor() {
|
|
6286
|
+
this.name = 'modulo';
|
|
6287
|
+
this.category = 'Special';
|
|
6288
|
+
this.syntax = 'left mod right';
|
|
6289
|
+
this.outputType = 'number';
|
|
6290
|
+
this.metadata = { category: 'Special', complexity: 'simple' };
|
|
6291
|
+
}
|
|
6292
|
+
async evaluate(_context, input) {
|
|
6293
|
+
const validation = this.validate(input);
|
|
6294
|
+
if (!validation.isValid) {
|
|
6295
|
+
return { success: false, errors: validation.errors, suggestions: validation.suggestions };
|
|
6296
|
+
}
|
|
6297
|
+
try {
|
|
6298
|
+
const { left, right } = input;
|
|
6299
|
+
const leftNum = toNumber(left, 'left operand');
|
|
6300
|
+
const rightNum = toNumber(right, 'right operand');
|
|
6301
|
+
const result = safeModulo(leftNum, rightNum);
|
|
6302
|
+
return { success: true, value: result, type: 'number' };
|
|
6303
|
+
}
|
|
6304
|
+
catch (error) {
|
|
6305
|
+
return {
|
|
6306
|
+
success: false,
|
|
6307
|
+
errors: [
|
|
6308
|
+
createError('runtime-error', `Modulo failed: ${error instanceof Error ? error.message : String(error)}`),
|
|
6309
|
+
],
|
|
6310
|
+
suggestions: ['Ensure both operands are numeric and divisor is not zero'],
|
|
6311
|
+
};
|
|
6312
|
+
}
|
|
6313
|
+
}
|
|
6314
|
+
validate(input) {
|
|
6315
|
+
const addExpr = new AdditionExpression();
|
|
6316
|
+
return addExpr.validate(input);
|
|
6317
|
+
}
|
|
6318
|
+
}
|
|
6319
|
+
class PowerExpression {
|
|
6320
|
+
constructor() {
|
|
6321
|
+
this.name = 'power';
|
|
6322
|
+
this.category = 'Special';
|
|
6323
|
+
this.syntax = 'left ** right';
|
|
6324
|
+
this.outputType = 'number';
|
|
6325
|
+
this.metadata = { category: 'Special', complexity: 'simple' };
|
|
6326
|
+
}
|
|
6327
|
+
async evaluate(_context, input) {
|
|
6328
|
+
const validation = this.validate(input);
|
|
6329
|
+
if (!validation.isValid) {
|
|
6330
|
+
return { success: false, errors: validation.errors, suggestions: validation.suggestions };
|
|
6331
|
+
}
|
|
6332
|
+
try {
|
|
6333
|
+
const { left, right } = input;
|
|
6334
|
+
const leftNum = toNumber(left, 'left operand');
|
|
6335
|
+
const rightNum = toNumber(right, 'right operand');
|
|
6336
|
+
const result = ensureFinite(Math.pow(leftNum, rightNum), 'power');
|
|
6337
|
+
return { success: true, value: result, type: 'number' };
|
|
6338
|
+
}
|
|
6339
|
+
catch (error) {
|
|
6340
|
+
return {
|
|
6341
|
+
success: false,
|
|
6342
|
+
errors: [
|
|
6343
|
+
createError('runtime-error', `Power failed: ${error instanceof Error ? error.message : String(error)}`),
|
|
6344
|
+
],
|
|
6345
|
+
suggestions: ['Ensure both operands are numeric'],
|
|
6346
|
+
};
|
|
6347
|
+
}
|
|
6348
|
+
}
|
|
6349
|
+
validate(input) {
|
|
6350
|
+
const addExpr = new AdditionExpression();
|
|
6351
|
+
return addExpr.validate(input);
|
|
6352
|
+
}
|
|
6353
|
+
}
|
|
6354
|
+
function createAdditionExpression() {
|
|
6355
|
+
return new AdditionExpression();
|
|
6356
|
+
}
|
|
6357
|
+
function createSubtractionExpression() {
|
|
6358
|
+
return new SubtractionExpression();
|
|
6359
|
+
}
|
|
6360
|
+
function createMultiplicationExpression() {
|
|
6361
|
+
return new MultiplicationExpression();
|
|
6362
|
+
}
|
|
6363
|
+
function createDivisionExpression() {
|
|
6364
|
+
return new DivisionExpression();
|
|
6365
|
+
}
|
|
6366
|
+
function createModuloExpression() {
|
|
6367
|
+
return new ModuloExpression();
|
|
6368
|
+
}
|
|
6369
|
+
function createPowerExpression() {
|
|
6370
|
+
return new PowerExpression();
|
|
6371
|
+
}
|
|
6372
|
+
const mathematicalExpressions = {
|
|
6373
|
+
addition: createAdditionExpression(),
|
|
6374
|
+
subtraction: createSubtractionExpression(),
|
|
6375
|
+
multiplication: createMultiplicationExpression(),
|
|
6376
|
+
division: createDivisionExpression(),
|
|
6377
|
+
modulo: createModuloExpression(),
|
|
6378
|
+
power: createPowerExpression(),
|
|
6379
|
+
};
|
|
6380
|
+
|
|
6381
|
+
function createFullExpressionRegistry() {
|
|
6382
|
+
return createExpressionRegistry(referencesExpressions, logicalExpressions, conversionExpressions, positionalExpressions, propertiesExpressions, specialExpressions, mathematicalExpressions);
|
|
6856
6383
|
}
|
|
6857
6384
|
|
|
6858
|
-
export {
|
|
6385
|
+
export { conversionExpressions as conversion, conversionExpressions, createCommonRegistry, createCoreRegistry, createExpressionRegistry, createFullExpressionRegistry, createFullRegistry, logicalExpressions as logical, logicalExpressions, positionalExpressions as positional, positionalExpressions, propertiesExpressions as properties, propertiesExpressions, referencesExpressions as references, referencesExpressions, specialExpressions as special, specialExpressions };
|
|
6859
6386
|
//# sourceMappingURL=index.mjs.map
|