@cocreate/utils 1.42.0 → 1.42.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/cjs/ObjectId.js +54 -0
  2. package/dist/cjs/attributes.js +64 -0
  3. package/dist/cjs/checkValue.js +26 -0
  4. package/dist/cjs/clickedElement.js +48 -0
  5. package/dist/cjs/core.js +33 -0
  6. package/dist/cjs/createUpdate.js +188 -0
  7. package/dist/cjs/cssPath.js +60 -0
  8. package/dist/cjs/dataQuery.js +280 -0
  9. package/dist/cjs/dom.js +29 -0
  10. package/dist/cjs/domParser.js +44 -0
  11. package/dist/cjs/dotNotationToObject.js +103 -0
  12. package/dist/cjs/escapeHtml.js +25 -0
  13. package/dist/cjs/getRelativePath.js +39 -0
  14. package/dist/cjs/getValueFromObject.js +41 -0
  15. package/dist/cjs/index.js +112 -0
  16. package/dist/cjs/init-browser.js +4 -0
  17. package/dist/cjs/isValidDate.js +32 -0
  18. package/dist/cjs/objectToDotNotation.js +53 -0
  19. package/dist/cjs/objectToSearchParams.js +42 -0
  20. package/dist/cjs/operators copy.js +562 -0
  21. package/dist/cjs/operators.js +480 -0
  22. package/dist/cjs/parseTextToHtml.js +27 -0
  23. package/dist/cjs/queryElements.js +155 -0
  24. package/dist/cjs/safeParse.js +169 -0
  25. package/dist/cjs/uid.js +34 -0
  26. package/dist/esm/ObjectId.js +35 -0
  27. package/dist/esm/attributes.js +45 -0
  28. package/dist/esm/checkValue.js +7 -0
  29. package/dist/esm/clickedElement.js +29 -0
  30. package/dist/esm/core.js +14 -0
  31. package/dist/esm/createUpdate.js +185 -0
  32. package/dist/esm/cssPath.js +41 -0
  33. package/dist/esm/dataQuery.js +261 -0
  34. package/dist/esm/dom.js +10 -0
  35. package/dist/esm/domParser.js +25 -0
  36. package/dist/esm/dotNotationToObject.js +84 -0
  37. package/dist/esm/escapeHtml.js +6 -0
  38. package/dist/esm/getRelativePath.js +20 -0
  39. package/dist/esm/getValueFromObject.js +22 -0
  40. package/dist/esm/index.js +93 -0
  41. package/dist/esm/init-browser.js +4 -0
  42. package/dist/esm/isValidDate.js +13 -0
  43. package/dist/esm/objectToDotNotation.js +34 -0
  44. package/dist/esm/objectToSearchParams.js +23 -0
  45. package/dist/esm/operators copy.js +543 -0
  46. package/dist/esm/operators.js +461 -0
  47. package/dist/esm/package.json +3 -0
  48. package/dist/esm/parseTextToHtml.js +8 -0
  49. package/dist/esm/queryElements.js +136 -0
  50. package/dist/esm/safeParse.js +150 -0
  51. package/dist/esm/uid.js +15 -0
  52. package/package.json +9 -111
  53. package/src/index.js +3 -3
  54. package/src/operators copy.js +687 -0
  55. package/src/operators.js +407 -526
  56. package/.github/FUNDING.yml +0 -3
  57. package/.github/workflows/automated.yml +0 -44
  58. package/.github/workflows/manual.yml +0 -44
  59. package/CHANGELOG.md +0 -2075
  60. package/CoCreate.config.js +0 -23
  61. package/demo/index.html +0 -23
  62. package/docs/index.html +0 -331
  63. package/prettier.config.js +0 -16
  64. package/release.config.js +0 -22
  65. package/webpack.config.js +0 -65
@@ -0,0 +1,687 @@
1
+ import { ObjectId } from "./ObjectId.js";
2
+ import { uid } from "./uid.js";
3
+ import { queryElements } from "./queryElements.js";
4
+ import { getValueFromObject } from "./getValueFromObject.js";
5
+
6
+ // --- AST EVALUATION ENGINE (safeParse) ---
7
+
8
+ const mathConstants = { PI: Math.PI, E: Math.E };
9
+ const mathFunctions = {
10
+ abs: Math.abs, ceil: Math.ceil, floor: Math.floor, round: Math.round,
11
+ max: Math.max, min: Math.min, pow: Math.pow, sqrt: Math.sqrt,
12
+ log: Math.log, sin: Math.sin, cos: Math.cos, tan: Math.tan
13
+ };
14
+
15
+ /**
16
+ * Reference class used by safeParse to track object properties for assignments.
17
+ * Prevents dot-notation from just returning values when we need to assign to them (e.g., el.value = 5)
18
+ */
19
+ class Ref {
20
+ constructor(obj, prop) {
21
+ this.obj = obj;
22
+ this.prop = prop;
23
+ }
24
+ get() { return this.obj ? this.obj[this.prop] : undefined; }
25
+ set(val) {
26
+ if (this.obj) this.obj[this.prop] = val;
27
+ return val;
28
+ }
29
+ }
30
+
31
+ function unref(val) {
32
+ return val instanceof Ref ? val.get() : val;
33
+ }
34
+
35
+ /**
36
+ * Parses math, logic, ternaries, dot notation, and property assignments securely.
37
+ */
38
+ function safeParse(expression, registry = new Map()) {
39
+ if (typeof expression !== "string") return expression;
40
+
41
+ let currentExpr = expression.trim();
42
+ if (!currentExpr) return null;
43
+
44
+ const tokenizerRegex = /('[^']*'|"[^"]*"|\d+(?:\.\d+)?|>=|<=|===|!==|==|!=|&&|\|\||[a-zA-Z_][a-zA-Z0-9_\.]*|[\+\-\*\/\%\(\)\?\:\>\<\!\,\=])/g;
45
+ const tokens = currentExpr.match(tokenizerRegex) || [];
46
+ let pos = 0;
47
+
48
+ function peek() { return tokens[pos]; }
49
+ function consume() { return tokens[pos++]; }
50
+
51
+ function parse() {
52
+ return parseAssignment();
53
+ }
54
+
55
+ function parseAssignment() {
56
+ let left = parseTernary();
57
+ if (peek() === "=") {
58
+ consume();
59
+ let right = unref(parseAssignment());
60
+ if (left instanceof Ref) {
61
+ return left.set(right); // Assign the value to the actual object property
62
+ }
63
+ return right; // Fallback if LHS wasn't a valid reference
64
+ }
65
+ return left;
66
+ }
67
+
68
+ function parseTernary() {
69
+ let left = parseLogical();
70
+ if (peek() === "?") {
71
+ consume();
72
+ let trueExpr = parseTernary();
73
+ if (peek() === ":") {
74
+ consume();
75
+ let falseExpr = parseTernary();
76
+ return unref(left) ? unref(trueExpr) : unref(falseExpr);
77
+ }
78
+ }
79
+ return left;
80
+ }
81
+
82
+ function parseLogical() {
83
+ let left = parseComparison();
84
+ while (peek() === "&&" || peek() === "||") {
85
+ let op = consume();
86
+ let right = parseComparison();
87
+ if (op === "&&") left = unref(left) && unref(right);
88
+ if (op === "||") left = unref(left) || unref(right);
89
+ }
90
+ return left;
91
+ }
92
+
93
+ function parseComparison() {
94
+ let left = parseAdditive();
95
+ while ([">", "<", ">=", "<=", "===", "!==", "==", "!="].includes(peek())) {
96
+ let op = consume();
97
+ let right = parseAdditive();
98
+ let l = unref(left), r = unref(right);
99
+ if (op === ">") left = l > r;
100
+ if (op === "<") left = l < r;
101
+ if (op === ">=") left = l >= r;
102
+ if (op === "<=") left = l <= r;
103
+ if (op === "===") left = l === r;
104
+ if (op === "!==") left = l !== r;
105
+ if (op === "==") left = l == r;
106
+ if (op === "!=") left = l != r;
107
+ }
108
+ return left;
109
+ }
110
+
111
+ function parseAdditive() {
112
+ let left = parseMultiplicative();
113
+ while (["+", "-"].includes(peek())) {
114
+ let op = consume();
115
+ let right = parseMultiplicative();
116
+ if (op === "+") left = unref(left) + unref(right);
117
+ if (op === "-") left = unref(left) - unref(right);
118
+ }
119
+ return left;
120
+ }
121
+
122
+ function parseMultiplicative() {
123
+ let left = parsePrimary();
124
+ while (["*", "/", "%"].includes(peek())) {
125
+ let op = consume();
126
+ let right = parsePrimary();
127
+ if (op === "*") left = unref(left) * unref(right);
128
+ if (op === "/") left = unref(left) / unref(right);
129
+ if (op === "%") left = unref(left) % unref(right);
130
+ }
131
+ return left;
132
+ }
133
+
134
+ function parsePrimary() {
135
+ let token = consume();
136
+ if (!token) return undefined;
137
+
138
+ if (/^\d/.test(token)) return parseFloat(token);
139
+
140
+ if (token.startsWith("'") || token.startsWith('"')) {
141
+ return token.slice(1, -1); // Strip quotes
142
+ }
143
+
144
+ if (token === "true") return true;
145
+ if (token === "false") return false;
146
+
147
+ if (token === "(") {
148
+ let expr = unref(parse());
149
+ if (peek() === ")") consume();
150
+ return expr;
151
+ }
152
+
153
+ if (token === "-") return -unref(parsePrimary());
154
+ if (token === "!") return !unref(parsePrimary());
155
+
156
+ if (mathConstants.hasOwnProperty(token)) return mathConstants[token];
157
+
158
+ if (peek() === "(" && mathFunctions.hasOwnProperty(token)) {
159
+ consume();
160
+ let args = [];
161
+ if (peek() !== ")") {
162
+ args.push(unref(parse()));
163
+ while (peek() === ",") {
164
+ consume();
165
+ args.push(unref(parse()));
166
+ }
167
+ }
168
+ if (peek() === ")") consume();
169
+ return mathFunctions[token](...args);
170
+ }
171
+
172
+ // --- Context & Object Registry Traversal ---
173
+ let path = token.split(".");
174
+ let baseToken = path[0];
175
+ let val;
176
+
177
+ // Check if the token is a tracked DOM Element/Object from processOperators
178
+ if (registry.has(baseToken)) {
179
+ val = registry.get(baseToken);
180
+ } else if (typeof window !== "undefined" && window[baseToken]) {
181
+ val = window[baseToken];
182
+ } else {
183
+ val = undefined;
184
+ }
185
+
186
+ if (path.length === 1) return val; // No dot notation, return the raw object
187
+
188
+ // Traverse down the object path, stopping before the last property
189
+ for (let i = 1; i < path.length - 1; i++) {
190
+ if (val !== null && val !== undefined) {
191
+ val = val[path[i]];
192
+ } else {
193
+ return undefined;
194
+ }
195
+ }
196
+
197
+ // Return a Ref object for the final property to enable Assignment (LHS)
198
+ return new Ref(val, path[path.length - 1]);
199
+ }
200
+
201
+ try {
202
+ const result = parse();
203
+ return unref(result);
204
+ } catch (error) {
205
+ console.warn(`safeParse error: ${error.message} (Expr: "${expression}")`, error);
206
+ return null;
207
+ }
208
+ }
209
+
210
+
211
+ // --- CORE OPERATOR ENGINE ---
212
+
213
+ // Operators handled directly for simple, synchronous value retrieval
214
+ const customOperators = new Map(
215
+ Object.entries({
216
+ $organization_id: () => localStorage.getItem("organization_id"),
217
+ $user_id: () => localStorage.getItem("user_id"),
218
+ $clientId: () => localStorage.getItem("clientId"),
219
+ $session_id: () => localStorage.getItem("session_id"),
220
+ $value: (element) => element.getValue() || "",
221
+ $innerWidth: () => window.innerWidth,
222
+ $innerHeight: () => window.innerHeight,
223
+ $href: () => window.location.href.replace(/\/$/, ""),
224
+ $origin: () => window.location.origin,
225
+ $protocol: () => window.location.protocol,
226
+ $hostname: () => window.location.hostname,
227
+ $host: () => window.location.host,
228
+ $port: () => window.location.port,
229
+ $pathname: () => window.location.pathname.replace(/\/$/, ""),
230
+ $hash: () => window.location.hash,
231
+ $subdomain: () => getSubdomain() || "",
232
+ $object_id: () => ObjectId().toString(),
233
+ "ObjectId()": () => ObjectId().toString(),
234
+ $query: (element, args) => queryElements({ element, selector: args }),
235
+
236
+ // ✨ THE NEW AST PORTAL ✨
237
+ $eval: (element, args, context) => safeParse(args, context.registry),
238
+
239
+ $relativePath: () => {
240
+ let currentPath = window.location.pathname.replace(/\/[^\/]*$/, "");
241
+ let depth = currentPath.split("/").filter(Boolean).length;
242
+ return depth > 0 ? "../".repeat(depth) : "./";
243
+ },
244
+ $path: () => {
245
+ let path = window.location.pathname;
246
+ if (path.split("/").pop().includes(".")) {
247
+ path = path.replace(/\/[^\/]+$/, "/");
248
+ }
249
+ return path === "/" ? "" : path;
250
+ },
251
+ $param: (element, args) => args,
252
+ $getObjectValue: (element, args) => {
253
+ if (Array.isArray(args) && args.length >= 2) {
254
+ return getValueFromObject(args[0], args[1]);
255
+ }
256
+ return "";
257
+ },
258
+ $setValue: (element, args) => element.setValue(...args) || "",
259
+ $true: () => true,
260
+ $false: () => false,
261
+ $parse: (element, args) => {
262
+ let value = args || "";
263
+ try { return JSON.parse(value); }
264
+ catch (e) { return value; }
265
+ },
266
+ $numberFormat: (element, args) => {
267
+ let number = parseFloat(args[0]);
268
+ if (!Array.isArray(args)) args = [args];
269
+
270
+ const locale = args[0] || undefined;
271
+ const options = args[1] || {};
272
+ const numCandidate = args[2] !== undefined ? args[2] : args[0];
273
+
274
+ number = parseFloat(numCandidate);
275
+ if (isNaN(number)) return String(numCandidate ?? "");
276
+ return new Intl.NumberFormat(locale, options).format(number);
277
+ },
278
+ $uid: (element, args) => uid(args[0]) || "",
279
+ })
280
+ );
281
+
282
+ /**
283
+ * Helper to determine if a function should be called with 'new'.
284
+ * Uses heuristics like ES6 class syntax, lack of prototype (arrow function), or PascalCase naming.
285
+ */
286
+ const isConstructor = (func, name) => {
287
+ try {
288
+ if (typeof func !== 'function') return false;
289
+ if (/^\s*class\s+/.test(func.toString())) return true;
290
+ if (!func.prototype) return false;
291
+ const n = name || func.name;
292
+ if (n && /^[A-Z]/.test(n)) return true;
293
+ } catch(e) {}
294
+ return false;
295
+ };
296
+
297
+ /**
298
+ * Helper function to check if a string path starts with a potential bare operator.
299
+ */
300
+ const findBareOperatorInPath = (path) => {
301
+ const trimmedPath = path.trim();
302
+ const match = trimmedPath.match(/^(\$[\w\-]+)/);
303
+
304
+ if (match) {
305
+ const key = match[1];
306
+ const remaining = trimmedPath.substring(key.length);
307
+ if (remaining.length === 0 || /^\s|\[|\./.test(remaining)) {
308
+ return key;
309
+ }
310
+ }
311
+ return null;
312
+ }
313
+
314
+ /**
315
+ * Finds the innermost function call (operator + its balanced parentheses argument)
316
+ */
317
+ const findInnermostFunctionCall = (expression) => {
318
+ let balance = 0;
319
+ let deepestStart = -1;
320
+ let deepestEnd = -1;
321
+ let deepestBalance = -1;
322
+ let inSingleQuote = false;
323
+ let inDoubleQuote = false;
324
+
325
+ for (let i = 0; i < expression.length; i++) {
326
+ const char = expression[i];
327
+
328
+ if (char === '"' && !inSingleQuote) {
329
+ inDoubleQuote = !inDoubleQuote;
330
+ continue;
331
+ } else if (char === "'" && !inDoubleQuote) {
332
+ inSingleQuote = !inDoubleQuote;
333
+ continue;
334
+ }
335
+
336
+ if (inSingleQuote || inDoubleQuote) continue;
337
+
338
+ if (char === '(') {
339
+ balance++;
340
+ if (balance > deepestBalance) {
341
+ deepestBalance = balance;
342
+ deepestStart = i;
343
+ deepestEnd = -1;
344
+ }
345
+ } else if (char === ')') {
346
+ if (balance === deepestBalance) {
347
+ deepestEnd = i;
348
+ }
349
+ balance--;
350
+ }
351
+ }
352
+
353
+ if (deepestStart === -1 || deepestEnd === -1 || deepestEnd <= deepestStart) {
354
+ return null;
355
+ }
356
+
357
+ const rawArgs = expression.substring(deepestStart + 1, deepestEnd).trim();
358
+
359
+ let operatorStart = -1;
360
+ let nonWhitespaceFound = false;
361
+
362
+ for (let i = deepestStart - 1; i >= 0; i--) {
363
+ const char = expression[i];
364
+
365
+ if (!nonWhitespaceFound) {
366
+ if (/\s/.test(char)) continue;
367
+ nonWhitespaceFound = true;
368
+ }
369
+
370
+ let isOperatorChar = /[\w\-\$]/.test(char);
371
+
372
+ if (!isOperatorChar) {
373
+ operatorStart = i + 1;
374
+ break;
375
+ }
376
+ operatorStart = i;
377
+ }
378
+
379
+ if (operatorStart === -1) operatorStart = 0;
380
+ const operatorNameCandidate = expression.substring(operatorStart, deepestStart).trim();
381
+
382
+ if (/^\$[\w\-]+$/.test(operatorNameCandidate) || customOperators.has(operatorNameCandidate)) {
383
+ const fullMatch = expression.substring(operatorStart, deepestEnd + 1);
384
+ return { operator: operatorNameCandidate, args: rawArgs, fullMatch: fullMatch };
385
+ }
386
+ return null;
387
+ };
388
+
389
+ /**
390
+ * Main function to find the innermost operator.
391
+ */
392
+ const findInnermostOperator = (expression) => {
393
+ function stripParentheses(str) {
394
+ let result = str;
395
+ if (result.startsWith("(")) result = result.substring(1);
396
+ if (result.endsWith(")")) result = result.substring(0, result.length - 1);
397
+ return result;
398
+ }
399
+ let args;
400
+
401
+ const functionCall = findInnermostFunctionCall(expression);
402
+ if (functionCall) {
403
+ args = stripParentheses(functionCall.args);
404
+ return {
405
+ operator: functionCall.operator,
406
+ args,
407
+ rawContent: functionCall.args,
408
+ fullMatch: functionCall.fullMatch
409
+ };
410
+ }
411
+
412
+ const rawContent = expression.trim();
413
+ const innermostOperator = findBareOperatorInPath(rawContent);
414
+
415
+ if (innermostOperator) {
416
+ const operatorArgs = rawContent.substring(innermostOperator.length).trim();
417
+ args = stripParentheses(operatorArgs);
418
+ return { operator: innermostOperator, args, rawContent: rawContent };
419
+ }
420
+
421
+ args = stripParentheses(rawContent);
422
+ return { operator: null, args, rawContent: rawContent };
423
+ };
424
+
425
+ /**
426
+ * Synchronously processes a string, finding and replacing operators recursively.
427
+ * Uses an objectRegistry to safely store complex DOM elements and functions during parsing.
428
+ */
429
+ function processOperators(
430
+ element,
431
+ value,
432
+ exclude = [],
433
+ parent,
434
+ params = [],
435
+ objectRegistry = new Map() // Pass the registry through recursive layers
436
+ ) {
437
+ if (typeof value !== "string" || (!value.includes("$") && !value.includes("ObjectId()"))) {
438
+ return value;
439
+ }
440
+
441
+ let processedValue = value;
442
+ let hasPromise = false;
443
+ let unresolvedTokens = new Map();
444
+
445
+ while (processedValue.includes("$") || processedValue.includes("ObjectId()")) {
446
+
447
+ const paramMatch = processedValue.match(/^\$\$PARAM_(\d+)\$\$/);
448
+ if (paramMatch && Array.isArray(params) && params.length > 0) {
449
+ const index = parseInt(paramMatch[1], 10);
450
+ if (index < params.length) {
451
+ const resolvedTokenValue = params[index];
452
+ processedValue = processedValue.replace(paramMatch[0], resolvedTokenValue);
453
+ continue;
454
+ }
455
+ }
456
+
457
+ const { operator, args, rawContent, fullMatch } = findInnermostOperator(processedValue);
458
+
459
+ if (!operator) break;
460
+ if (operator === "$param" && !args) break;
461
+
462
+ const textToReplace = fullMatch || rawContent;
463
+
464
+ if (exclude.includes(operator)) {
465
+ const token = `__UNRESOLVED_${unresolvedTokens.size}__`;
466
+ unresolvedTokens.set(token, textToReplace);
467
+ processedValue = processedValue.replace(textToReplace, token);
468
+ continue;
469
+ }
470
+
471
+ // Execute operator, passing the objectRegistry down contextually
472
+ let resolvedValue = resolveOperator(element, operator, args, parent, params, objectRegistry);
473
+
474
+ if (resolvedValue === undefined) {
475
+ const token = `__UNRESOLVED_${unresolvedTokens.size}__`;
476
+ unresolvedTokens.set(token, textToReplace);
477
+ processedValue = processedValue.replace(textToReplace, token);
478
+ continue;
479
+ }
480
+
481
+ if (resolvedValue instanceof Promise) {
482
+ const paramIndex = params.length;
483
+ params.push(resolvedValue);
484
+ processedValue = processedValue.replace(textToReplace, `$$PARAM_${paramIndex}$$`);
485
+ hasPromise = true;
486
+ break;
487
+ }
488
+
489
+ if (params.some((p) => p instanceof Promise)) {
490
+ hasPromise = true;
491
+ break;
492
+ }
493
+
494
+ let replacement = "";
495
+ if (operator === "$param") {
496
+ params.push(resolvedValue);
497
+ } else if (resolvedValue !== null && (typeof resolvedValue === "object" || typeof resolvedValue === "function")) {
498
+ // ✨ THE TOKEN REGISTRY ✨
499
+ // Instead of stringifying Elements/Arrays/Functions, we store them safely
500
+ // and leave a token so safeParse can grab them later!
501
+ const token = `__OBJ_${objectRegistry.size}__`;
502
+ objectRegistry.set(token, resolvedValue);
503
+ replacement = token;
504
+ } else {
505
+ replacement = resolvedValue ?? "";
506
+ }
507
+
508
+ if (processedValue === textToReplace) {
509
+ processedValue = replacement;
510
+ break;
511
+ }
512
+
513
+ processedValue = processedValue.replace(textToReplace, replacement);
514
+
515
+ if (!processedValue.includes("$") && !processedValue.includes("ObjectId()")) {
516
+ break;
517
+ }
518
+ }
519
+
520
+ // Restore any unresolvable operator syntax
521
+ for (const [token, originalText] of unresolvedTokens.entries()) {
522
+ processedValue = processedValue.replace(token, originalText);
523
+ }
524
+
525
+ // FINAL UNWRAP: If the final evaluated string is literally just a single object token,
526
+ // unwrap it and return the actual raw Object/Element instead of the string!
527
+ if (typeof processedValue === "string") {
528
+ const exactMatch = processedValue.match(/^__OBJ_(\d+)__$/);
529
+ if (exactMatch && objectRegistry.has(processedValue)) {
530
+ processedValue = objectRegistry.get(processedValue);
531
+ }
532
+ }
533
+
534
+ if (hasPromise) {
535
+ return { value: processedValue, params, objectRegistry };
536
+ }
537
+
538
+ if (params.length) {
539
+ if (typeof processedValue === 'string' && processedValue.trim() === "") {
540
+ return params;
541
+ }
542
+ }
543
+
544
+ return processedValue;
545
+ }
546
+
547
+ async function processOperatorsAsync(
548
+ element,
549
+ value,
550
+ exclude = [],
551
+ parent,
552
+ params = [],
553
+ objectRegistry = new Map()
554
+ ) {
555
+ let result = processOperators(element, value, exclude, parent, params, objectRegistry);
556
+
557
+ while (typeof result === "object" && result.params) {
558
+ const resolvedParams = await Promise.all(result.params);
559
+ result = processOperators(
560
+ element,
561
+ result.value,
562
+ exclude,
563
+ parent,
564
+ resolvedParams,
565
+ result.objectRegistry || objectRegistry
566
+ );
567
+ }
568
+
569
+ if (result instanceof Promise) return await result;
570
+ return result;
571
+ }
572
+
573
+ /**
574
+ * Synchronously determines and executes the action for processing a single operator token.
575
+ */
576
+ function resolveOperator(element, operator, args, parent, params, objectRegistry) {
577
+ if (params.some((p) => p instanceof Promise)) return "";
578
+
579
+ if (args && typeof args === "string" && args.includes("$")) {
580
+ args = processOperators(element, args, [], operator, params, objectRegistry);
581
+ }
582
+
583
+ if (params.some((p) => p instanceof Promise)) return operator;
584
+
585
+ let targetElements = element ? [element] : [];
586
+
587
+ if (args && typeof args === "string" && !customOperators.has(operator)) {
588
+ targetElements = queryElements({ element, selector: args });
589
+ if (!targetElements.length) return undefined;
590
+ }
591
+
592
+ let value = processValues(targetElements, operator, args, parent, objectRegistry);
593
+
594
+ if (value && typeof value === "string" && value.includes("$")) {
595
+ value = processOperators(element, value, [], parent, params, objectRegistry);
596
+ }
597
+
598
+ return value;
599
+ }
600
+
601
+ /**
602
+ * Synchronously processes and aggregates values from a set of elements based on a specified operator.
603
+ */
604
+ function processValues(elements, operator, args, parent, objectRegistry) {
605
+ let customOp = customOperators.get(operator);
606
+ let aggregatedString = "";
607
+ let hasValidProperty = false;
608
+
609
+ // Pass the active registry down so custom operators (like $eval) can utilize it
610
+ const context = { registry: objectRegistry, element: elements[0] };
611
+
612
+ if (customOp) hasValidProperty = true;
613
+
614
+ for (const el of elements) {
615
+ if (!el) continue;
616
+
617
+ let rawValue = customOp;
618
+ const propName = customOp ? null : operator.substring(1);
619
+
620
+ if (!customOp) {
621
+ if (propName in el) {
622
+ hasValidProperty = true;
623
+ rawValue = el[propName];
624
+ } else {
625
+ continue;
626
+ }
627
+ }
628
+
629
+ if (typeof rawValue === "function") {
630
+ if (customOp) {
631
+ // Execute standard Custom Operators (always pass element and context)
632
+ if (Array.isArray(args)) {
633
+ rawValue = rawValue(el, ...args, context);
634
+ } else {
635
+ rawValue = rawValue(el, args, context);
636
+ }
637
+ } else {
638
+ // Execute native Element properties/methods dynamically using robust evaluation
639
+ if (isConstructor(rawValue, propName)) {
640
+ if (Array.isArray(args)) {
641
+ rawValue = new rawValue(...args);
642
+ } else if (args !== undefined && args !== "") {
643
+ rawValue = new rawValue(args);
644
+ } else {
645
+ rawValue = new rawValue();
646
+ }
647
+ } else {
648
+ if (Array.isArray(args)) {
649
+ rawValue = rawValue.apply(el, args); // Bind context correctly to element!
650
+ } else if (args !== undefined && args !== "") {
651
+ rawValue = rawValue.call(el, args);
652
+ } else {
653
+ rawValue = rawValue.call(el);
654
+ }
655
+ }
656
+ }
657
+ }
658
+
659
+ if (parent === "$param") {
660
+ if (rawValue !== undefined && rawValue !== null) return rawValue;
661
+ } else {
662
+ // Return raw objects/functions immediately so processOperators can tokenize them
663
+ if (
664
+ rawValue instanceof Promise ||
665
+ (typeof rawValue === "object" && rawValue !== null) ||
666
+ typeof rawValue === "function"
667
+ ) {
668
+ return rawValue;
669
+ }
670
+ aggregatedString += String(rawValue ?? "");
671
+ }
672
+ }
673
+
674
+ if (!hasValidProperty) return undefined;
675
+ return aggregatedString;
676
+ }
677
+
678
+ function getSubdomain() {
679
+ const hostname = window.location.hostname;
680
+ const parts = hostname.split(".");
681
+ if (parts.length > 2 && isNaN(parseInt(parts[parts.length - 1]))) {
682
+ return parts.slice(0, parts.length - 2).join(".");
683
+ }
684
+ return null;
685
+ }
686
+
687
+ export { processOperators, processOperatorsAsync, customOperators };