@cocreate/utils 1.44.2 → 1.44.3
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/package.json +1 -1
- package/src/ObjectId.js +59 -37
- package/src/ast/index.js +404 -0
- package/src/{operators.js → ast/operators.js} +247 -250
- package/src/cloudProviders.js +150 -0
- package/src/createUpdate.js +1 -29
- package/src/dataQuery.js +2 -2
- package/src/getClientInfo.js +357 -0
- package/src/getClockOffset.js +46 -0
- package/src/getCountryLocation.js +278 -0
- package/src/index.js +47 -35
- package/src/ulid.js +72 -0
- package/src/uuid.js +67 -0
- package/src/core.js +0 -6
- package/src/operators copy.js +0 -687
- package/src/operators.ast.js +0 -287
- package/src/operators.v1.js +0 -687
- package/src/safeParse.js +0 -171
- package/src/uid.js +0 -16
|
@@ -1,29 +1,48 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (C) 2026 CoCreate and Contributors.
|
|
3
|
+
*
|
|
4
|
+
* This program is free software: you can redistribute it and/or modify
|
|
5
|
+
* it under the terms of the GNU Affero General Public License as published
|
|
6
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
* (at your option) any later version.
|
|
8
|
+
*
|
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU Affero General Public License for more details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*
|
|
17
|
+
********************************************************************************/
|
|
18
|
+
|
|
19
|
+
import { getValueFromObject } from "../getValueFromObject.js";
|
|
20
|
+
import { queryElements } from "../queryElements.js";
|
|
21
|
+
import { ObjectId } from "../ObjectId.js";
|
|
22
|
+
import { uuid } from "../uuid.js";
|
|
23
|
+
|
|
24
|
+
// Late-binding references dynamically set at boot time to decouple circular import loops
|
|
25
|
+
let astSyncRef = null;
|
|
26
|
+
let astAsyncRef = null;
|
|
27
|
+
|
|
28
|
+
export function setASTEvaluators(syncFn, asyncFn) {
|
|
29
|
+
astSyncRef = syncFn;
|
|
30
|
+
astAsyncRef = asyncFn;
|
|
31
|
+
}
|
|
7
32
|
|
|
8
33
|
const mathConstants = { PI: Math.PI, E: Math.E };
|
|
9
34
|
const mathFunctions = {
|
|
10
35
|
abs: Math.abs, ceil: Math.ceil, floor: Math.floor, round: Math.round,
|
|
11
36
|
max: Math.max, min: Math.min, pow: Math.pow, sqrt: Math.sqrt,
|
|
12
37
|
log: Math.log, sin: Math.sin, cos: Math.cos, tan: Math.tan,
|
|
13
|
-
Number: (v) => Number(v)
|
|
38
|
+
Number: (v) => Number(v)
|
|
14
39
|
};
|
|
15
40
|
|
|
16
|
-
/**
|
|
17
|
-
* Reference class used by safeParse to track object properties for assignments.
|
|
18
|
-
* Prevents dot-notation from just returning values when we need to assign to them (e.g., el.value = 5)
|
|
19
|
-
* Upgraded to handle array contexts: if the object is an array, properties map across all items natively.
|
|
20
|
-
*/
|
|
21
41
|
class Ref {
|
|
22
42
|
constructor(obj, prop) {
|
|
23
43
|
this.obj = obj;
|
|
24
44
|
this.prop = prop;
|
|
25
45
|
|
|
26
|
-
// Ensure we recognize standard arrays AND DOM collections (NodeList/HTMLCollection)
|
|
27
46
|
let isList = Array.isArray(obj) ||
|
|
28
47
|
(typeof NodeList !== "undefined" && obj instanceof NodeList) ||
|
|
29
48
|
(typeof HTMLCollection !== "undefined" && obj instanceof HTMLCollection);
|
|
@@ -36,12 +55,9 @@ class Ref {
|
|
|
36
55
|
}
|
|
37
56
|
get() {
|
|
38
57
|
if (this.isArrayContext) {
|
|
39
|
-
|
|
40
|
-
let res = this.obj.map(item => item ? item[this.prop] : undefined);
|
|
41
|
-
return res;
|
|
58
|
+
return this.obj.map(item => item ? item[this.prop] : undefined);
|
|
42
59
|
}
|
|
43
|
-
|
|
44
|
-
return res;
|
|
60
|
+
return this.obj ? this.obj[this.prop] : undefined;
|
|
45
61
|
}
|
|
46
62
|
set(val) {
|
|
47
63
|
if (this.isArrayContext) {
|
|
@@ -57,10 +73,7 @@ function unref(val) {
|
|
|
57
73
|
return val instanceof Ref ? val.get() : val;
|
|
58
74
|
}
|
|
59
75
|
|
|
60
|
-
|
|
61
|
-
* Parses math, logic, ternaries, dot notation, and property assignments securely.
|
|
62
|
-
*/
|
|
63
|
-
function safeParse(expression, registry = new Map()) {
|
|
76
|
+
export function safeParse(expression, registry = new Map()) {
|
|
64
77
|
if (typeof expression !== "string") return expression;
|
|
65
78
|
|
|
66
79
|
let currentExpr = expression.trim();
|
|
@@ -83,7 +96,7 @@ function safeParse(expression, registry = new Map()) {
|
|
|
83
96
|
consume();
|
|
84
97
|
let right = unref(parseAssignment());
|
|
85
98
|
if (left instanceof Ref) {
|
|
86
|
-
return left.set(right);
|
|
99
|
+
return left.set(right);
|
|
87
100
|
}
|
|
88
101
|
return right;
|
|
89
102
|
}
|
|
@@ -194,12 +207,11 @@ function safeParse(expression, registry = new Map()) {
|
|
|
194
207
|
return mathFunctions[token](...args);
|
|
195
208
|
}
|
|
196
209
|
|
|
197
|
-
// --- Context & Object Registry Traversal ---
|
|
198
210
|
let path = token.split(".");
|
|
199
211
|
let baseToken = path[0];
|
|
200
212
|
let val;
|
|
201
213
|
|
|
202
|
-
if (registry.has(baseToken)) {
|
|
214
|
+
if (registry && typeof registry.get === 'function' && registry.has(baseToken)) {
|
|
203
215
|
val = registry.get(baseToken);
|
|
204
216
|
} else if (typeof window !== "undefined" && window[baseToken]) {
|
|
205
217
|
val = window[baseToken];
|
|
@@ -208,7 +220,6 @@ function safeParse(expression, registry = new Map()) {
|
|
|
208
220
|
}
|
|
209
221
|
|
|
210
222
|
if (path.length === 1) {
|
|
211
|
-
// Support explicit call for root variables
|
|
212
223
|
if (peek() === "(" && typeof val === "function") {
|
|
213
224
|
consume();
|
|
214
225
|
let args = [];
|
|
@@ -227,7 +238,6 @@ function safeParse(expression, registry = new Map()) {
|
|
|
227
238
|
|
|
228
239
|
for (let i = 1; i < path.length - 1; i++) {
|
|
229
240
|
if (val !== null && val !== undefined) {
|
|
230
|
-
// Support deep traversal across arrays
|
|
231
241
|
let isList = Array.isArray(val) || (typeof NodeList !== "undefined" && val instanceof NodeList) || (typeof HTMLCollection !== "undefined" && val instanceof HTMLCollection);
|
|
232
242
|
if (isList && !(path[i] in val)) {
|
|
233
243
|
val = Array.from(val).map(item => item ? item[path[i]] : undefined);
|
|
@@ -241,7 +251,6 @@ function safeParse(expression, registry = new Map()) {
|
|
|
241
251
|
|
|
242
252
|
let ref = new Ref(val, path[path.length - 1]);
|
|
243
253
|
|
|
244
|
-
// Support explicit method calls (e.g., obj.method(arg))
|
|
245
254
|
if (peek() === "(") {
|
|
246
255
|
consume();
|
|
247
256
|
let args = [];
|
|
@@ -254,24 +263,15 @@ function safeParse(expression, registry = new Map()) {
|
|
|
254
263
|
}
|
|
255
264
|
if (peek() === ")") consume();
|
|
256
265
|
|
|
257
|
-
// Map the method call across all items if it's an array context
|
|
258
266
|
if (ref.isArrayContext) {
|
|
259
|
-
|
|
267
|
+
return ref.obj.map(item => {
|
|
260
268
|
let func = item ? item[ref.prop] : undefined;
|
|
261
|
-
|
|
262
|
-
console.warn(`Operator Engine: '${ref.prop}' is not a valid function on the target element.`);
|
|
263
|
-
}
|
|
264
|
-
let res = typeof func === "function" ? func.apply(item, args) : undefined;
|
|
265
|
-
return res;
|
|
269
|
+
return typeof func === "function" ? func.apply(item, args) : undefined;
|
|
266
270
|
});
|
|
267
|
-
return results; // Return the natively mapped Array so user can process it!
|
|
268
271
|
} else {
|
|
269
272
|
let func = ref.obj ? ref.obj[ref.prop] : undefined;
|
|
270
273
|
if (typeof func === "function") {
|
|
271
|
-
|
|
272
|
-
return res;
|
|
273
|
-
} else {
|
|
274
|
-
console.warn(`Operator Engine: Method '${ref.prop}' does not exist on the target object.`);
|
|
274
|
+
return func.apply(ref.obj, args);
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
277
|
}
|
|
@@ -280,45 +280,43 @@ function safeParse(expression, registry = new Map()) {
|
|
|
280
280
|
}
|
|
281
281
|
|
|
282
282
|
try {
|
|
283
|
-
|
|
284
|
-
return unref(result);
|
|
283
|
+
return unref(parse());
|
|
285
284
|
} catch (error) {
|
|
286
285
|
console.warn(`safeParse error: ${error.message} (Expr: "${expression}")`, error);
|
|
287
286
|
return null;
|
|
288
287
|
}
|
|
289
288
|
}
|
|
290
289
|
|
|
291
|
-
|
|
292
|
-
// --- CORE OPERATOR ENGINE ---
|
|
293
|
-
|
|
294
|
-
const customOperators = new Map(
|
|
290
|
+
export const customOperators = new Map(
|
|
295
291
|
Object.entries({
|
|
296
|
-
$organization_id: () => localStorage.getItem("organization_id"),
|
|
297
|
-
$user_id: () => localStorage.getItem("user_id"),
|
|
298
|
-
$client_id: () => localStorage.getItem("clientId"),
|
|
299
|
-
$session_id: () => localStorage.getItem("session_id"),
|
|
300
|
-
$this: function() { return this; },
|
|
301
|
-
$value: function() { return this && typeof this.getValue === 'function' ? this.getValue() : ""; },
|
|
302
|
-
$innerWidth: () => window.innerWidth,
|
|
303
|
-
$innerHeight: () => window.innerHeight,
|
|
304
|
-
$href: () => window.location.href.replace(/\/$/, ""),
|
|
305
|
-
$origin: () => window.location.origin,
|
|
306
|
-
$protocol: () => window.location.protocol,
|
|
307
|
-
$hostname: () => window.location.hostname,
|
|
308
|
-
$host: () => window.location.host,
|
|
309
|
-
$port: () => window.location.port,
|
|
310
|
-
$pathname: () => window.location.pathname.replace(/\/$/, ""),
|
|
311
|
-
$hash: () => window.location.hash,
|
|
312
|
-
$subdomain: () =>
|
|
292
|
+
$organization_id: () => typeof localStorage !== 'undefined' ? localStorage.getItem("organization_id") : "",
|
|
293
|
+
$user_id: () => typeof localStorage !== 'undefined' ? localStorage.getItem("user_id") : "",
|
|
294
|
+
$client_id: () => typeof localStorage !== 'undefined' ? localStorage.getItem("clientId") : "",
|
|
295
|
+
$session_id: () => typeof localStorage !== 'undefined' ? localStorage.getItem("session_id") : "",
|
|
296
|
+
$this: function() { return this.element; },
|
|
297
|
+
$value: function() { return this.element && typeof this.element.getValue === 'function' ? this.element.getValue() : ""; },
|
|
298
|
+
$innerWidth: () => typeof window !== 'undefined' ? window.innerWidth : 0,
|
|
299
|
+
$innerHeight: () => typeof window !== 'undefined' ? window.innerHeight : 0,
|
|
300
|
+
$href: () => typeof window !== 'undefined' ? window.location.href.replace(/\/$/, "") : "",
|
|
301
|
+
$origin: () => typeof window !== 'undefined' ? window.location.origin : "",
|
|
302
|
+
$protocol: () => typeof window !== 'undefined' ? window.location.protocol : "",
|
|
303
|
+
$hostname: () => typeof window !== 'undefined' ? window.location.hostname : "",
|
|
304
|
+
$host: () => typeof window !== 'undefined' ? window.location.host : "",
|
|
305
|
+
$port: () => typeof window !== 'undefined' ? window.location.port : "",
|
|
306
|
+
$pathname: () => typeof window !== 'undefined' ? window.location.pathname.replace(/\/$/, "") : "",
|
|
307
|
+
$hash: () => typeof window !== 'undefined' ? window.location.hash : "",
|
|
308
|
+
$subdomain: () => {
|
|
309
|
+
if (typeof window === 'undefined') return "";
|
|
310
|
+
const parts = window.location.hostname.split('.');
|
|
311
|
+
return parts.length > 2 ? parts[0] : "";
|
|
312
|
+
},
|
|
313
313
|
$object_id: () => ObjectId().toString(),
|
|
314
314
|
"ObjectId()": () => ObjectId().toString(),
|
|
315
315
|
|
|
316
|
-
// Unwrap query results correctly: Returns only the single first item
|
|
317
316
|
$query: function(selector) {
|
|
318
|
-
let element = this;
|
|
317
|
+
let element = this.element;
|
|
319
318
|
if (typeof selector === 'string') {
|
|
320
319
|
selector = selector.trim();
|
|
321
|
-
// Strip quotes out of the selector in case they were passed (e.g., $query('#id1'))
|
|
322
320
|
if ((selector.startsWith("'") && selector.endsWith("'")) ||
|
|
323
321
|
(selector.startsWith('"') && selector.endsWith('"'))) {
|
|
324
322
|
selector = selector.slice(1, -1);
|
|
@@ -327,43 +325,67 @@ const customOperators = new Map(
|
|
|
327
325
|
try {
|
|
328
326
|
let results = queryElements({ element, selector });
|
|
329
327
|
if (!results || results.length === 0) return undefined;
|
|
330
|
-
return results[0];
|
|
328
|
+
return results[0];
|
|
331
329
|
} catch (error) {
|
|
332
330
|
console.warn(`Operator Engine: Invalid $query selector => "${selector}"`, error);
|
|
333
331
|
return undefined;
|
|
334
332
|
}
|
|
335
333
|
},
|
|
336
|
-
// Unwrap query results correctly: Always returns an array
|
|
337
334
|
$queryAll: function(selector) {
|
|
338
|
-
let element = this;
|
|
335
|
+
let element = this.element;
|
|
339
336
|
if (typeof selector === 'string') {
|
|
340
337
|
selector = selector.trim();
|
|
341
|
-
// Strip quotes out of the selector in case they were passed (e.g., $queryAll('.items'))
|
|
342
338
|
if ((selector.startsWith("'") && selector.endsWith("'")) ||
|
|
343
339
|
(selector.startsWith('"') && selector.endsWith('"'))) {
|
|
344
340
|
selector = selector.slice(1, -1);
|
|
345
341
|
}
|
|
346
|
-
|
|
347
|
-
// Safely ignore queries targeting $document to prevent querySelector crashes
|
|
348
342
|
if (selector.includes('$document')) return [];
|
|
349
343
|
}
|
|
350
344
|
try {
|
|
351
345
|
let results = queryElements({ element, selector });
|
|
352
346
|
if (!results || results.length === 0) return [];
|
|
353
|
-
// Always cast to array so Ref mapping handles methods uniformly
|
|
354
347
|
return Array.from(results);
|
|
355
348
|
} catch (error) {
|
|
356
349
|
console.warn(`Operator Engine: Invalid $queryAll selector => "${selector}"`, error);
|
|
357
350
|
return [];
|
|
358
351
|
}
|
|
359
352
|
},
|
|
360
|
-
$eval: function(expression) {
|
|
353
|
+
$eval: function(expression) {
|
|
354
|
+
return safeParse(expression, this.registry);
|
|
355
|
+
},
|
|
356
|
+
"$": function(expression) {
|
|
357
|
+
return safeParse(expression, this.registry);
|
|
358
|
+
},
|
|
359
|
+
$ast: function(expression) {
|
|
360
|
+
if (!astSyncRef) {
|
|
361
|
+
console.warn("[Operator Engine] AST Evaluator reference not registered.");
|
|
362
|
+
return expression;
|
|
363
|
+
}
|
|
364
|
+
return astSyncRef({
|
|
365
|
+
executeAST: expression,
|
|
366
|
+
node: this.element,
|
|
367
|
+
registry: this.registry
|
|
368
|
+
});
|
|
369
|
+
},
|
|
370
|
+
$astAsync: async function(expression) {
|
|
371
|
+
if (!astAsyncRef) {
|
|
372
|
+
console.warn("[Operator Engine] AST Async Evaluator reference not registered.");
|
|
373
|
+
return expression;
|
|
374
|
+
}
|
|
375
|
+
return await astAsyncRef({
|
|
376
|
+
executeAST: expression,
|
|
377
|
+
node: this.element,
|
|
378
|
+
registry: this.registry
|
|
379
|
+
});
|
|
380
|
+
},
|
|
361
381
|
$relativePath: () => {
|
|
382
|
+
if (typeof window === 'undefined') return "./";
|
|
362
383
|
let currentPath = window.location.pathname.replace(/\/[^\/]*$/, "");
|
|
363
384
|
let depth = currentPath.split("/").filter(Boolean).length;
|
|
364
385
|
return depth > 0 ? "../".repeat(depth) : "./";
|
|
365
386
|
},
|
|
366
387
|
$path: () => {
|
|
388
|
+
if (typeof window === 'undefined') return "";
|
|
367
389
|
let path = window.location.pathname;
|
|
368
390
|
if (path.split("/").pop().includes(".")) path = path.replace(/\/[^\/]+$/, "/");
|
|
369
391
|
return path === "/" ? "" : path;
|
|
@@ -373,7 +395,7 @@ const customOperators = new Map(
|
|
|
373
395
|
return getValueFromObject(obj, key);
|
|
374
396
|
},
|
|
375
397
|
$setValue: function(...args) {
|
|
376
|
-
return typeof this.setValue === 'function' ? this.setValue(...args) || "" : "";
|
|
398
|
+
return this.element && typeof this.element.setValue === 'function' ? this.element.setValue(...args) || "" : "";
|
|
377
399
|
},
|
|
378
400
|
$true: () => true,
|
|
379
401
|
$false: () => false,
|
|
@@ -389,7 +411,14 @@ const customOperators = new Map(
|
|
|
389
411
|
if (isNaN(number)) return String(numCandidate ?? "");
|
|
390
412
|
return new Intl.NumberFormat(locale, options).format(number);
|
|
391
413
|
},
|
|
392
|
-
$
|
|
414
|
+
$uuid: function(prefix) { return uuid(prefix) || ""; },
|
|
415
|
+
|
|
416
|
+
$add: function(value) {
|
|
417
|
+
return Array.isArray(value) ? value.flat().reduce((a, b) => Number(a) + Number(b), 0) : Number(value) || 0;
|
|
418
|
+
},
|
|
419
|
+
$concat: function(value) {
|
|
420
|
+
return Array.isArray(value) ? value.flat().map(arg => typeof arg === 'object' ? JSON.stringify(arg) : arg).join('') : String(value ?? "");
|
|
421
|
+
}
|
|
393
422
|
})
|
|
394
423
|
);
|
|
395
424
|
|
|
@@ -409,7 +438,6 @@ const findInnermostOperator = (expression) => {
|
|
|
409
438
|
let stack = [];
|
|
410
439
|
let inSQ = false, inDQ = false;
|
|
411
440
|
|
|
412
|
-
// 1. Capture all parenthesis pairs accurately (The Parenthesis-First "Animals" boundary)
|
|
413
441
|
for (let i = 0; i < expression.length; i++) {
|
|
414
442
|
const char = expression[i];
|
|
415
443
|
if (char === '\\') { i++; continue; }
|
|
@@ -427,7 +455,6 @@ const findInnermostOperator = (expression) => {
|
|
|
427
455
|
}
|
|
428
456
|
}
|
|
429
457
|
|
|
430
|
-
// Sort by depth descending (innermost first), then by start descending (right-most first)
|
|
431
458
|
parens.sort((a, b) => b.depth - a.depth || b.start - a.start);
|
|
432
459
|
|
|
433
460
|
function stripParentheses(str) {
|
|
@@ -447,35 +474,29 @@ const findInnermostOperator = (expression) => {
|
|
|
447
474
|
return result;
|
|
448
475
|
}
|
|
449
476
|
|
|
450
|
-
// 2. Look to the left of the innermost parentheses to find the keyword operator
|
|
451
477
|
for (const p of parens) {
|
|
452
|
-
// We safely isolate ONLY what is inside the parens, bypassing the need for string replacements
|
|
453
478
|
const insideParens = expression.substring(p.start + 1, p.end).trim();
|
|
454
479
|
const leftPart = expression.substring(0, p.start);
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
const match = leftPart.match(/(?:^|[^\w\$])(\$[\w\-]+|ObjectId\b)\s*$/);
|
|
480
|
+
|
|
481
|
+
const match = leftPart.match(/(?:^|[^\w\$])(\$[\w\-]*|ObjectId\b)\s*$/);
|
|
458
482
|
|
|
459
483
|
if (match) {
|
|
460
484
|
let opName = match[1] === 'ObjectId' ? 'ObjectId()' : match[1];
|
|
461
|
-
if (customOperators.has(opName) || /^\$[\w\-]
|
|
462
|
-
// Pinpoint exactly where the operator string began
|
|
485
|
+
if (customOperators.has(opName) || /^\$[\w\-]*$/.test(opName)) {
|
|
463
486
|
const spacesMatch = leftPart.match(/\s*$/);
|
|
464
487
|
const spacesAtEnd = spacesMatch ? spacesMatch[0].length : 0;
|
|
465
488
|
const fullMatchStart = match.index + match[0].length - match[1].length - spacesAtEnd;
|
|
466
489
|
|
|
467
|
-
|
|
490
|
+
return {
|
|
468
491
|
operator: opName,
|
|
469
|
-
args: insideParens,
|
|
492
|
+
args: insideParens,
|
|
470
493
|
fullMatch: expression.substring(fullMatchStart, p.end + 1),
|
|
471
494
|
rawContent: expression.substring(fullMatchStart, p.end + 1)
|
|
472
495
|
};
|
|
473
|
-
return resultBlock;
|
|
474
496
|
}
|
|
475
497
|
}
|
|
476
498
|
}
|
|
477
499
|
|
|
478
|
-
// 3. Fallback: Find bare operators (like $this) that have no parentheses attached
|
|
479
500
|
const bareRegex = /(?:^|[^\w\$])(\$[\w\-]+|ObjectId\b)/g;
|
|
480
501
|
let bareMatch;
|
|
481
502
|
let bareCandidates = [];
|
|
@@ -483,8 +504,6 @@ const findInnermostOperator = (expression) => {
|
|
|
483
504
|
while ((bareMatch = bareRegex.exec(expression)) !== null) {
|
|
484
505
|
let possibleOp = bareMatch[1];
|
|
485
506
|
let opName = possibleOp === 'ObjectId' ? 'ObjectId()' : possibleOp;
|
|
486
|
-
|
|
487
|
-
// Ensure it isnt followed by a `(` (because if it was, it would have been caught above)
|
|
488
507
|
let remainder = expression.substring(bareMatch.index + bareMatch[0].length);
|
|
489
508
|
if (!/^\s*\(/.test(remainder)) {
|
|
490
509
|
if (customOperators.has(opName) || /^\$[\w\-]+$/.test(opName)) {
|
|
@@ -504,136 +523,11 @@ const findInnermostOperator = (expression) => {
|
|
|
504
523
|
return bareCandidates[0];
|
|
505
524
|
}
|
|
506
525
|
|
|
507
|
-
|
|
508
|
-
return defaultFallback;
|
|
526
|
+
return { operator: null, args: stripParentheses(expression), rawContent: expression.trim() };
|
|
509
527
|
};
|
|
510
528
|
|
|
511
|
-
function processOperators(element, value, exclude = [], parent, params = [], objectRegistry = new Map()) {
|
|
512
|
-
if (typeof value !== "string" || (!value.includes("$") && !value.includes("ObjectId()"))) return value;
|
|
513
|
-
let processedValue = value, hasPromise = false, unresolvedTokens = new Map();
|
|
514
|
-
while (processedValue.includes("$") || processedValue.includes("ObjectId()")) {
|
|
515
|
-
const paramMatch = processedValue.match(/^\$\$PARAM_(\d+)\$\$/);
|
|
516
|
-
if (paramMatch && Array.isArray(params) && params.length > 0) {
|
|
517
|
-
const index = parseInt(paramMatch[1], 10);
|
|
518
|
-
if (index < params.length) { processedValue = processedValue.replace(paramMatch[0], params[index]); continue; }
|
|
519
|
-
}
|
|
520
|
-
const { operator, args, rawContent, fullMatch } = findInnermostOperator(processedValue);
|
|
521
|
-
if (!operator || (operator === "$param" && !args)) break;
|
|
522
|
-
const textToReplace = fullMatch || rawContent;
|
|
523
|
-
if (exclude.includes(operator)) {
|
|
524
|
-
const token = `__UNRESOLVED_${unresolvedTokens.size}__`;
|
|
525
|
-
unresolvedTokens.set(token, textToReplace);
|
|
526
|
-
processedValue = processedValue.replace(textToReplace, token);
|
|
527
|
-
continue;
|
|
528
|
-
}
|
|
529
|
-
let resolvedValue = resolveOperator(element, operator, args, parent, params, objectRegistry);
|
|
530
|
-
if (resolvedValue === undefined) {
|
|
531
|
-
const token = `__UNRESOLVED_${unresolvedTokens.size}__`;
|
|
532
|
-
unresolvedTokens.set(token, textToReplace);
|
|
533
|
-
processedValue = processedValue.replace(textToReplace, token);
|
|
534
|
-
continue;
|
|
535
|
-
}
|
|
536
|
-
if (resolvedValue instanceof Promise) {
|
|
537
|
-
const paramIndex = params.length;
|
|
538
|
-
params.push(resolvedValue);
|
|
539
|
-
processedValue = processedValue.replace(textToReplace, `$$PARAM_${paramIndex}$$`);
|
|
540
|
-
hasPromise = true;
|
|
541
|
-
break;
|
|
542
|
-
}
|
|
543
|
-
if (params.some((p) => p instanceof Promise)) { hasPromise = true; break; }
|
|
544
|
-
let replacement = "";
|
|
545
|
-
if (operator === "$param") params.push(resolvedValue);
|
|
546
|
-
else if (resolvedValue !== null && (typeof resolvedValue === "object" || typeof resolvedValue === "function")) {
|
|
547
|
-
const token = `__OBJ_${objectRegistry.size}__`;
|
|
548
|
-
objectRegistry.set(token, resolvedValue);
|
|
549
|
-
replacement = token;
|
|
550
|
-
} else replacement = resolvedValue ?? "";
|
|
551
|
-
if (processedValue === textToReplace) { processedValue = replacement; break; }
|
|
552
|
-
processedValue = processedValue.replace(textToReplace, replacement);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
// Restore unresolved exclusions (Reverse-order replaces ensure perfectly unnested loops)
|
|
556
|
-
if (typeof processedValue === "string") {
|
|
557
|
-
let keys = Array.from(unresolvedTokens.keys()).reverse();
|
|
558
|
-
for (const token of keys) {
|
|
559
|
-
processedValue = processedValue.replaceAll(token, unresolvedTokens.get(token));
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
// Native method and property chaining evaluation
|
|
563
|
-
// 1. If the ENTIRE string is a pure method/property chain (e.g. __OBJ_0__.getValue() )
|
|
564
|
-
const exactMethodMatch = processedValue.match(/^__OBJ_\d+__(?:\.[a-zA-Z0-9_]+)+(?:\(.*\))?$/);
|
|
565
|
-
if (exactMethodMatch) {
|
|
566
|
-
const parsed = safeParse(processedValue, objectRegistry);
|
|
567
|
-
// DO NOT LEAK tokens to the UI. Fallback to "" instead of the processedValue placeholder if it fails.
|
|
568
|
-
return parsed !== null && parsed !== undefined ? parsed : "";
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
// 2. Unpack pure object variables
|
|
572
|
-
const exactMatch = processedValue.match(/^__OBJ_(\d+)__$/);
|
|
573
|
-
if (exactMatch && objectRegistry.has(processedValue)) {
|
|
574
|
-
processedValue = objectRegistry.get(processedValue);
|
|
575
|
-
} else {
|
|
576
|
-
// 3. Inline interpolation for methods/properties (e.g., "User clicked __OBJ_0__.getValue()")
|
|
577
|
-
const objRegex = /__OBJ_\d+__(?:\.[a-zA-Z0-9_]+)+(?:\([^)]*\))?/g;
|
|
578
|
-
if (processedValue.includes("__OBJ_")) {
|
|
579
|
-
processedValue = processedValue.replace(objRegex, (match) => {
|
|
580
|
-
const parsed = safeParse(match, objectRegistry);
|
|
581
|
-
// DO NOT LEAK tokens to the UI. Fallback to "" instead of the match placeholder if it fails.
|
|
582
|
-
return parsed !== null && parsed !== undefined ? parsed : "";
|
|
583
|
-
});
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
if (hasPromise) return { value: processedValue, params, objectRegistry };
|
|
589
|
-
return processedValue;
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
async function processOperatorsAsync(element, value, exclude = [], parent, params = [], objectRegistry = new Map()) {
|
|
593
|
-
let result = processOperators(element, value, exclude, parent, params, objectRegistry);
|
|
594
|
-
while (typeof result === "object" && result.params) {
|
|
595
|
-
const resolvedParams = await Promise.all(result.params);
|
|
596
|
-
result = processOperators(element, result.value, exclude, parent, resolvedParams, result.objectRegistry || objectRegistry);
|
|
597
|
-
}
|
|
598
|
-
return result;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
function resolveOperator(element, operator, args, parent, params, objectRegistry) {
|
|
602
|
-
if (params.some((p) => p instanceof Promise)) return "";
|
|
603
|
-
if (args && typeof args === "string" && args.includes("$")) {
|
|
604
|
-
args = processOperators(element, args, [], operator, params, objectRegistry);
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
let targetElements = element ? [element] : [];
|
|
608
|
-
if (args && typeof args === "string") {
|
|
609
|
-
const objMatch = args.match(/^__OBJ_(\d+)__$/);
|
|
610
|
-
if (objMatch && objectRegistry.has(args)) {
|
|
611
|
-
let registeredVal = objectRegistry.get(args);
|
|
612
|
-
// Ensure unrolled array natively
|
|
613
|
-
let isList = Array.isArray(registeredVal) || (typeof NodeList !== "undefined" && registeredVal instanceof NodeList) || (typeof HTMLCollection !== "undefined" && registeredVal instanceof HTMLCollection);
|
|
614
|
-
targetElements = isList ? Array.from(registeredVal) : [registeredVal];
|
|
615
|
-
} else if (!customOperators.has(operator) && (operator === "$query")) {
|
|
616
|
-
let qResults = queryElements({ element, selector: args });
|
|
617
|
-
if (!qResults) {
|
|
618
|
-
targetElements = [];
|
|
619
|
-
} else {
|
|
620
|
-
let isList = Array.isArray(qResults) || (typeof NodeList !== "undefined" && qResults instanceof NodeList) || (typeof HTMLCollection !== "undefined" && qResults instanceof HTMLCollection);
|
|
621
|
-
targetElements = isList ? Array.from(qResults) : [qResults];
|
|
622
|
-
}
|
|
623
|
-
if (!targetElements.length) return undefined;
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
let value = processValues(targetElements, operator, args, parent, objectRegistry);
|
|
628
|
-
if (value && typeof value === "string" && value.includes("$")) {
|
|
629
|
-
value = processOperators(element, value, [], parent, params, objectRegistry);
|
|
630
|
-
}
|
|
631
|
-
return value;
|
|
632
|
-
}
|
|
633
|
-
|
|
634
529
|
function processValues(elements, operator, args, parent, objectRegistry) {
|
|
635
530
|
let customOp = customOperators.get(operator);
|
|
636
|
-
|
|
637
531
|
let results = [];
|
|
638
532
|
let hasValidProperty = customOp ? true : false;
|
|
639
533
|
|
|
@@ -645,13 +539,11 @@ function processValues(elements, operator, args, parent, objectRegistry) {
|
|
|
645
539
|
if (propName in el) {
|
|
646
540
|
hasValidProperty = true;
|
|
647
541
|
rawValue = el[propName];
|
|
648
|
-
}
|
|
649
|
-
else {
|
|
542
|
+
} else {
|
|
650
543
|
continue;
|
|
651
544
|
}
|
|
652
545
|
}
|
|
653
546
|
|
|
654
|
-
// --- 1. RECONSTRUCT THE "GHOST" STRUCTURE (Tokens -> Actual JS Types) ---
|
|
655
547
|
let preparedArgs = [];
|
|
656
548
|
const originalWasArray = Array.isArray(args);
|
|
657
549
|
const hasArgs = originalWasArray || args !== undefined;
|
|
@@ -660,9 +552,8 @@ function processValues(elements, operator, args, parent, objectRegistry) {
|
|
|
660
552
|
preparedArgs = Array.from(args);
|
|
661
553
|
} else if (typeof args === "string") {
|
|
662
554
|
if (args === "") {
|
|
663
|
-
preparedArgs = [""];
|
|
555
|
+
preparedArgs = [""];
|
|
664
556
|
} else {
|
|
665
|
-
// Smart parse for comma-separated arguments respecting strings and brackets
|
|
666
557
|
let parsedArgs = [];
|
|
667
558
|
let currentArg = '';
|
|
668
559
|
let inSQ = false, inDQ = false;
|
|
@@ -678,7 +569,6 @@ function processValues(elements, operator, args, parent, objectRegistry) {
|
|
|
678
569
|
if (char === "'" && !inDQ) inSQ = !inSQ;
|
|
679
570
|
if (char === '"' && !inSQ) inDQ = !inDQ;
|
|
680
571
|
|
|
681
|
-
// Keep track of brackets and parens grouping so we don't accidentally split them inside
|
|
682
572
|
if (!inSQ && !inDQ) {
|
|
683
573
|
if (char === '(' || char === '[' || char === '{') depth++;
|
|
684
574
|
else if (char === ')' || char === ']' || char === '}') depth--;
|
|
@@ -693,25 +583,18 @@ function processValues(elements, operator, args, parent, objectRegistry) {
|
|
|
693
583
|
}
|
|
694
584
|
if (currentArg.trim() !== "") parsedArgs.push(currentArg.trim());
|
|
695
585
|
|
|
696
|
-
// Reconstruct actual JavaScript types
|
|
697
586
|
preparedArgs = parsedArgs.map(arg => {
|
|
698
|
-
// 1. Direct object token match (brings back exactly the array, obj, or variable!)
|
|
699
587
|
if (/^__OBJ_\d+__$/.test(arg) && objectRegistry && objectRegistry.has(arg)) {
|
|
700
588
|
return objectRegistry.get(arg);
|
|
701
589
|
}
|
|
702
|
-
|
|
703
|
-
// 2. Numbers
|
|
704
590
|
if (!isNaN(arg) && arg.trim() !== "") {
|
|
705
591
|
return Number(arg);
|
|
706
592
|
}
|
|
707
|
-
|
|
708
|
-
// 3. Booleans and Null
|
|
709
593
|
if (arg === "true") return true;
|
|
710
594
|
if (arg === "false") return false;
|
|
711
595
|
if (arg === "null") return null;
|
|
712
596
|
if (arg === "undefined") return undefined;
|
|
713
597
|
|
|
714
|
-
// 4. Strings with quotes (strip outer quotes reliably)
|
|
715
598
|
if ((arg.startsWith("'") && arg.endsWith("'")) || (arg.startsWith('"') && arg.endsWith('"'))) {
|
|
716
599
|
let unquoted = arg.slice(1, -1);
|
|
717
600
|
if (unquoted.includes("__OBJ_")) {
|
|
@@ -726,7 +609,6 @@ function processValues(elements, operator, args, parent, objectRegistry) {
|
|
|
726
609
|
return unquoted;
|
|
727
610
|
}
|
|
728
611
|
|
|
729
|
-
// 5. Inline token replacements for mixed strings (e.g., "Welcome __OBJ_1__")
|
|
730
612
|
if (typeof arg === "string" && arg.includes("__OBJ_")) {
|
|
731
613
|
return arg.replace(/__OBJ_\d+__/g, (match) => {
|
|
732
614
|
if (objectRegistry && objectRegistry.has(match)) {
|
|
@@ -736,8 +618,6 @@ function processValues(elements, operator, args, parent, objectRegistry) {
|
|
|
736
618
|
return match;
|
|
737
619
|
});
|
|
738
620
|
}
|
|
739
|
-
|
|
740
|
-
// Fallback literal string
|
|
741
621
|
return arg;
|
|
742
622
|
});
|
|
743
623
|
}
|
|
@@ -745,7 +625,6 @@ function processValues(elements, operator, args, parent, objectRegistry) {
|
|
|
745
625
|
preparedArgs = [args];
|
|
746
626
|
}
|
|
747
627
|
|
|
748
|
-
// Extra safety: Make sure tokens passed directly as Array items are mapped
|
|
749
628
|
if (originalWasArray) {
|
|
750
629
|
preparedArgs = preparedArgs.map(a => {
|
|
751
630
|
if (typeof a === 'string' && /^__OBJ_\d+__$/.test(a) && objectRegistry && objectRegistry.has(a)) {
|
|
@@ -755,7 +634,6 @@ function processValues(elements, operator, args, parent, objectRegistry) {
|
|
|
755
634
|
});
|
|
756
635
|
}
|
|
757
636
|
|
|
758
|
-
// Create the resolvedArgs shape the engine natively expects
|
|
759
637
|
let resolvedArgs;
|
|
760
638
|
if (originalWasArray || preparedArgs.length > 1) {
|
|
761
639
|
resolvedArgs = preparedArgs;
|
|
@@ -765,9 +643,9 @@ function processValues(elements, operator, args, parent, objectRegistry) {
|
|
|
765
643
|
resolvedArgs = undefined;
|
|
766
644
|
}
|
|
767
645
|
|
|
768
|
-
// --- 2. EXECUTE OR ASSIGN ---
|
|
769
646
|
if (typeof rawValue === "function") {
|
|
770
647
|
try {
|
|
648
|
+
let execCtx = { element: el, registry: objectRegistry };
|
|
771
649
|
if (isConstructor(rawValue, propName)) {
|
|
772
650
|
if (Array.isArray(resolvedArgs)) {
|
|
773
651
|
rawValue = new rawValue(...resolvedArgs);
|
|
@@ -778,22 +656,21 @@ function processValues(elements, operator, args, parent, objectRegistry) {
|
|
|
778
656
|
}
|
|
779
657
|
} else {
|
|
780
658
|
if (Array.isArray(resolvedArgs)) {
|
|
781
|
-
rawValue = rawValue.apply(
|
|
659
|
+
rawValue = rawValue.apply(execCtx, resolvedArgs);
|
|
782
660
|
} else if (resolvedArgs !== undefined) {
|
|
783
|
-
rawValue = rawValue.call(
|
|
661
|
+
rawValue = rawValue.call(execCtx, resolvedArgs);
|
|
784
662
|
} else {
|
|
785
|
-
rawValue = rawValue.call(
|
|
663
|
+
rawValue = rawValue.call(execCtx);
|
|
786
664
|
}
|
|
787
665
|
}
|
|
788
666
|
} catch (err) {
|
|
789
667
|
console.warn(`Operator Engine: Failed to execute method or operator "${operator}"`, err);
|
|
790
|
-
continue;
|
|
668
|
+
continue;
|
|
791
669
|
}
|
|
792
670
|
} else if (hasArgs && propName) {
|
|
793
|
-
// PROPERTY ASSIGNMENT
|
|
794
671
|
try {
|
|
795
672
|
el[propName] = resolvedArgs;
|
|
796
|
-
rawValue = el[propName];
|
|
673
|
+
rawValue = el[propName];
|
|
797
674
|
} catch (err) {
|
|
798
675
|
console.warn(`Operator Engine: Failed to assign property "${propName}"`, err);
|
|
799
676
|
continue;
|
|
@@ -806,17 +683,137 @@ function processValues(elements, operator, args, parent, objectRegistry) {
|
|
|
806
683
|
}
|
|
807
684
|
|
|
808
685
|
if (!hasValidProperty) return undefined;
|
|
809
|
-
if (results.length === 0) return "";
|
|
810
|
-
|
|
811
|
-
// Return the array wrapper (even for length 1) to ensure .map array methods remain accessible!
|
|
686
|
+
if (results.length === 0) return "";
|
|
812
687
|
if (results.length === 1) return results[0];
|
|
813
688
|
return results;
|
|
814
689
|
}
|
|
815
690
|
|
|
816
|
-
function
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
691
|
+
export function unpackTokens(val, registry) {
|
|
692
|
+
if (typeof val === "string") {
|
|
693
|
+
const exactMatch = val.match(/^__OBJ_(\d+)__$/);
|
|
694
|
+
if (exactMatch && registry.has(val)) {
|
|
695
|
+
return registry.get(val);
|
|
696
|
+
}
|
|
697
|
+
if (val.includes("__OBJ_")) {
|
|
698
|
+
return val.replace(/__OBJ_\d+__(?:\.[a-zA-Z0-9_]+)*(?:\([^)]*\))?/g, (match) => {
|
|
699
|
+
const parsed = safeParse(match, registry);
|
|
700
|
+
if (parsed !== null && parsed !== undefined) {
|
|
701
|
+
return typeof parsed === "object" ? JSON.stringify(parsed) : String(parsed);
|
|
702
|
+
}
|
|
703
|
+
const baseTokenMatch = match.match(/^__OBJ_\d+__/);
|
|
704
|
+
if (baseTokenMatch && registry.has(baseTokenMatch[0])) {
|
|
705
|
+
const baseVal = registry.get(baseTokenMatch[0]);
|
|
706
|
+
return typeof baseVal === "object" ? JSON.stringify(baseVal) : String(baseVal);
|
|
707
|
+
}
|
|
708
|
+
return "";
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
return val;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
function resolveOperator(element, operator, args, parent, params, objectRegistry) {
|
|
716
|
+
if (params.some((p) => p instanceof Promise)) return "";
|
|
717
|
+
if (args && typeof args === "string" && args.includes("$")) {
|
|
718
|
+
args = processOperators(element, args, [], operator, params, objectRegistry);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
let targetElements = element ? [element] : [];
|
|
722
|
+
if (args && typeof args === "string") {
|
|
723
|
+
const objMatch = args.match(/^__OBJ_(\d+)__$/);
|
|
724
|
+
if (objMatch && objectRegistry.has(args)) {
|
|
725
|
+
let registeredVal = objectRegistry.get(args);
|
|
726
|
+
let isList = Array.isArray(registeredVal) || (typeof NodeList !== "undefined" && registeredVal instanceof NodeList) || (typeof HTMLCollection !== "undefined" && registeredVal instanceof HTMLCollection);
|
|
727
|
+
targetElements = isList ? Array.from(registeredVal) : [registeredVal];
|
|
728
|
+
} else if (!customOperators.has(operator) && (operator === "$query")) {
|
|
729
|
+
let qResults = queryElements({ element, selector: args });
|
|
730
|
+
if (!qResults) {
|
|
731
|
+
targetElements = [];
|
|
732
|
+
} else {
|
|
733
|
+
let isList = Array.isArray(qResults) || (typeof NodeList !== "undefined" && qResults instanceof NodeList) || (typeof HTMLCollection !== "undefined" && qResults instanceof HTMLCollection);
|
|
734
|
+
targetElements = isList ? Array.from(qResults) : [qResults];
|
|
735
|
+
}
|
|
736
|
+
if (!targetElements.length) return undefined;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
let value = processValues(targetElements, operator, args, parent, objectRegistry);
|
|
741
|
+
if (value && typeof value === "string" && value.includes("$")) {
|
|
742
|
+
value = processOperators(element, value, [], parent, params, objectRegistry);
|
|
743
|
+
}
|
|
744
|
+
return value;
|
|
820
745
|
}
|
|
821
746
|
|
|
822
|
-
export
|
|
747
|
+
export function processOperators(element, value, exclude = [], parent, params = [], objectRegistry = new Map()) {
|
|
748
|
+
if (typeof value !== "string" || (!value.includes("$") && !value.includes("ObjectId()"))) return value;
|
|
749
|
+
let processedValue = value, hasPromise = false, unresolvedTokens = new Map();
|
|
750
|
+
|
|
751
|
+
while (processedValue.includes("$") || processedValue.includes("ObjectId()")) {
|
|
752
|
+
const paramMatch = processedValue.match(/^\$$PARAM_(\d+)\$\$/);
|
|
753
|
+
if (paramMatch && Array.isArray(params) && params.length > 0) {
|
|
754
|
+
const index = parseInt(paramMatch[1], 10);
|
|
755
|
+
if (index < params.length) {
|
|
756
|
+
processedValue = processedValue.replace(paramMatch[0], params[index]);
|
|
757
|
+
continue;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
const { operator, args, rawContent, fullMatch } = findInnermostOperator(processedValue);
|
|
762
|
+
if (!operator || (operator === "$param" && !args)) break;
|
|
763
|
+
|
|
764
|
+
const textToReplace = fullMatch || rawContent;
|
|
765
|
+
if (exclude.includes(operator)) {
|
|
766
|
+
const token = `__UNRESOLVED_${unresolvedTokens.size}__`;
|
|
767
|
+
unresolvedTokens.set(token, textToReplace);
|
|
768
|
+
processedValue = processedValue.replace(textToReplace, token);
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
let resolvedValue = resolveOperator(element, operator, args, parent, params, objectRegistry);
|
|
773
|
+
if (resolvedValue === undefined) {
|
|
774
|
+
const token = `__UNRESOLVED_${unresolvedTokens.size}__`;
|
|
775
|
+
unresolvedTokens.set(token, textToReplace);
|
|
776
|
+
processedValue = processedValue.replace(textToReplace, token);
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
if (resolvedValue instanceof Promise) {
|
|
780
|
+
const paramIndex = params.length;
|
|
781
|
+
params.push(resolvedValue);
|
|
782
|
+
processedValue = processedValue.replace(textToReplace, `$$PARAM_${paramIndex}$$`);
|
|
783
|
+
hasPromise = true;
|
|
784
|
+
break;
|
|
785
|
+
}
|
|
786
|
+
if (params.some((p) => p instanceof Promise)) { hasPromise = true; break; }
|
|
787
|
+
|
|
788
|
+
let replacement = "";
|
|
789
|
+
if (operator === "$param") params.push(resolvedValue);
|
|
790
|
+
else if (resolvedValue !== null && (typeof resolvedValue === "object" || typeof resolvedValue === "function")) {
|
|
791
|
+
const token = `__OBJ_${objectRegistry.size}__`;
|
|
792
|
+
objectRegistry.set(token, resolvedValue);
|
|
793
|
+
replacement = token;
|
|
794
|
+
} else replacement = resolvedValue ?? "";
|
|
795
|
+
|
|
796
|
+
if (processedValue === textToReplace) { processedValue = replacement; break; }
|
|
797
|
+
processedValue = processedValue.replace(textToReplace, replacement);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
if (typeof processedValue === "string") {
|
|
801
|
+
let keys = Array.from(unresolvedTokens.keys()).reverse();
|
|
802
|
+
for (const token of keys) {
|
|
803
|
+
processedValue = processedValue.replaceAll(token, unresolvedTokens.get(token));
|
|
804
|
+
}
|
|
805
|
+
processedValue = unpackTokens(processedValue, objectRegistry);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
if (hasPromise) return { value: processedValue, params, objectRegistry };
|
|
809
|
+
return processedValue;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
export async function processOperatorsAsync(element, value, exclude = [], parent, params = [], objectRegistry = new Map()) {
|
|
813
|
+
let result = processOperators(element, value, exclude, parent, params, objectRegistry);
|
|
814
|
+
while (typeof result === "object" && result.params) {
|
|
815
|
+
const resolvedParams = await Promise.all(result.params);
|
|
816
|
+
result = processOperators(element, result.value, exclude, parent, resolvedParams, result.objectRegistry || objectRegistry);
|
|
817
|
+
}
|
|
818
|
+
return result;
|
|
819
|
+
}
|