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