@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
package/package.json
CHANGED
package/src/ObjectId.js
CHANGED
|
@@ -1,41 +1,63 @@
|
|
|
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 { getClockOffset } from "./getClockOffset.js";
|
|
20
|
+
|
|
1
21
|
let counter = 0;
|
|
2
22
|
|
|
3
23
|
export function ObjectId(inputId) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
24
|
+
if (inputId && /^[0-9a-fA-F]{24}$/.test(inputId)) {
|
|
25
|
+
return {
|
|
26
|
+
timestamp: inputId.substring(0, 8),
|
|
27
|
+
processId: inputId.substring(8, 20),
|
|
28
|
+
counter: inputId.substring(20),
|
|
29
|
+
toString: function () {
|
|
30
|
+
return this.timestamp + this.processId + this.counter;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
} else if (inputId) {
|
|
34
|
+
throw new Error("Invalid ObjectId provided.");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const now = Date.now() + getClockOffset();
|
|
38
|
+
const timestampHex = Math.floor(now / 1000)
|
|
39
|
+
.toString(16)
|
|
40
|
+
.padStart(8, "0");
|
|
41
|
+
|
|
42
|
+
const processIdHex = Math.floor(Math.random() * 0x100000000000)
|
|
43
|
+
.toString(16)
|
|
44
|
+
.padStart(12, "0");
|
|
45
|
+
|
|
46
|
+
counter = (counter + 1) % 10000;
|
|
47
|
+
if (counter < 2) {
|
|
48
|
+
counter = Math.floor(Math.random() * (5000 - 100 + 1)) + 100;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const counterHex = counter.toString(16).padStart(4, "0");
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
timestamp: timestampHex,
|
|
55
|
+
processId: processIdHex,
|
|
56
|
+
counter: counterHex,
|
|
57
|
+
toString: function () {
|
|
58
|
+
return this.timestamp + this.processId + this.counter;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
41
61
|
}
|
|
62
|
+
|
|
63
|
+
export default ObjectId;
|
package/src/ast/index.js
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
import { getValueFromObject } from "../getValueFromObject.js";
|
|
2
|
+
import { queryElements } from "../queryElements.js";
|
|
3
|
+
import {
|
|
4
|
+
processOperators,
|
|
5
|
+
processOperatorsAsync,
|
|
6
|
+
unpackTokens,
|
|
7
|
+
setASTEvaluators,
|
|
8
|
+
customOperators
|
|
9
|
+
} from "./operators.js";
|
|
10
|
+
|
|
11
|
+
export const isomorphicSandbox = {
|
|
12
|
+
Math,
|
|
13
|
+
Number,
|
|
14
|
+
String,
|
|
15
|
+
Boolean,
|
|
16
|
+
Date,
|
|
17
|
+
JSON,
|
|
18
|
+
Array,
|
|
19
|
+
Object,
|
|
20
|
+
parseInt,
|
|
21
|
+
parseFloat,
|
|
22
|
+
encodeURIComponent,
|
|
23
|
+
decodeURIComponent,
|
|
24
|
+
btoa: (str) => {
|
|
25
|
+
if (typeof btoa === 'function') return btoa(String(str));
|
|
26
|
+
return Buffer.from(String(str)).toString("base64");
|
|
27
|
+
},
|
|
28
|
+
atob: (b64) => {
|
|
29
|
+
if (typeof atob === 'function') return atob(b64);
|
|
30
|
+
return Buffer.from(String(b64), "base64").toString("utf-8");
|
|
31
|
+
},
|
|
32
|
+
concat: (...args) => args.flat().map(arg => typeof arg === 'object' ? JSON.stringify(arg) : arg).join(''),
|
|
33
|
+
|
|
34
|
+
// Core Asynchronous Flow Control Operators
|
|
35
|
+
await: {
|
|
36
|
+
all: async (promises) => {
|
|
37
|
+
if (promises && typeof promises === 'object' && !Array.isArray(promises)) {
|
|
38
|
+
const keys = Object.keys(promises);
|
|
39
|
+
const results = await Promise.all(keys.map(key => promises[key]));
|
|
40
|
+
return Object.fromEntries(keys.map((key, i) => [key, results[i]]));
|
|
41
|
+
}
|
|
42
|
+
return Promise.all(Array.isArray(promises) ? promises : [promises]);
|
|
43
|
+
},
|
|
44
|
+
any: async (promises) => {
|
|
45
|
+
if (promises && typeof promises === 'object' && !Array.isArray(promises)) {
|
|
46
|
+
return Promise.any(Object.values(promises));
|
|
47
|
+
}
|
|
48
|
+
return Promise.any(Array.isArray(promises) ? promises : [promises]);
|
|
49
|
+
},
|
|
50
|
+
race: async (promises) => {
|
|
51
|
+
if (promises && typeof promises === 'object' && !Array.isArray(promises)) {
|
|
52
|
+
return Promise.race(Object.values(promises));
|
|
53
|
+
}
|
|
54
|
+
return Promise.race(Array.isArray(promises) ? promises : [promises]);
|
|
55
|
+
},
|
|
56
|
+
allSettled: async (promises) => {
|
|
57
|
+
if (promises && typeof promises === 'object' && !Array.isArray(promises)) {
|
|
58
|
+
const keys = Object.keys(promises);
|
|
59
|
+
const results = await Promise.allSettled(keys.map(key => promises[key]));
|
|
60
|
+
return Object.fromEntries(keys.map((key, i) => [key, results[i]]));
|
|
61
|
+
}
|
|
62
|
+
return Promise.allSettled(Array.isArray(promises) ? promises : [promises]);
|
|
63
|
+
},
|
|
64
|
+
delay: (ms) => new Promise(resolve => setTimeout(resolve, Number(ms) || 0))
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const BrowserGlobals = {
|
|
69
|
+
get $organization_id() { return typeof localStorage !== 'undefined' ? localStorage.getItem("organization_id") : ""; },
|
|
70
|
+
get $user_id() { return typeof localStorage !== 'undefined' ? localStorage.getItem("user_id") : ""; },
|
|
71
|
+
get $client_id() { return typeof localStorage !== 'undefined' ? localStorage.getItem("clientId") : ""; },
|
|
72
|
+
get $session_id() { return typeof localStorage !== 'undefined' ? localStorage.getItem("session_id") : ""; },
|
|
73
|
+
get $innerWidth() { return typeof window !== 'undefined' ? window.innerWidth : 0; },
|
|
74
|
+
get $innerHeight() { return typeof window !== 'undefined' ? window.innerHeight : 0; },
|
|
75
|
+
get $href() { return typeof window !== 'undefined' ? window.location.href.replace(/\/$/, "") : ""; },
|
|
76
|
+
get $origin() { return typeof window !== 'undefined' ? window.location.origin : ""; },
|
|
77
|
+
get $protocol() { return typeof window !== 'undefined' ? window.location.protocol : ""; },
|
|
78
|
+
get $hostname() { return typeof window !== 'undefined' ? window.location.hostname : ""; },
|
|
79
|
+
get $host() { return typeof window !== 'undefined' ? window.location.host : ""; },
|
|
80
|
+
get $port() { return typeof window !== 'undefined' ? window.location.port : ""; },
|
|
81
|
+
get $pathname() { return typeof window !== 'undefined' ? window.location.pathname.replace(/\/$/, "") : ""; },
|
|
82
|
+
get $hash() { return typeof window !== 'undefined' ? window.location.hash : ""; },
|
|
83
|
+
get $subdomain() {
|
|
84
|
+
if (typeof window === 'undefined') return "";
|
|
85
|
+
const parts = window.location.hostname.split('.');
|
|
86
|
+
return parts.length > 2 ? parts[0] : "";
|
|
87
|
+
},
|
|
88
|
+
get $relativePath() {
|
|
89
|
+
if (typeof window === 'undefined') return "./";
|
|
90
|
+
const currentPath = window.location.pathname.replace(/\/[^\/]*$/, "");
|
|
91
|
+
const depth = currentPath.split("/").filter(Boolean).length;
|
|
92
|
+
return depth > 0 ? "../".repeat(depth) : "./";
|
|
93
|
+
},
|
|
94
|
+
get $path() {
|
|
95
|
+
if (typeof window === 'undefined') return "";
|
|
96
|
+
let path = window.location.pathname;
|
|
97
|
+
if (path.split("/").pop().includes(".")) path = path.replace(/\/[^\/]+$/, "/");
|
|
98
|
+
return path === "/" ? "" : path;
|
|
99
|
+
},
|
|
100
|
+
$parse: (value = "") => {
|
|
101
|
+
try { return JSON.parse(value); } catch (e) { return value; }
|
|
102
|
+
},
|
|
103
|
+
$query: function(selector) {
|
|
104
|
+
if (typeof document === 'undefined') return undefined;
|
|
105
|
+
let element = this?.element || null;
|
|
106
|
+
if (typeof selector === 'string') {
|
|
107
|
+
selector = selector.trim().replace(/^['"]|['']$/g, '');
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
const results = queryElements({ element, selector });
|
|
111
|
+
return results && results.length > 0 ? results[0] : undefined;
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.warn(`[AST Engine] Invalid $query selector => "${selector}"`, error);
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
$queryAll: function(selector) {
|
|
118
|
+
if (typeof document === 'undefined') return [];
|
|
119
|
+
let element = this?.element || null;
|
|
120
|
+
if (typeof selector === 'string') {
|
|
121
|
+
selector = selector.trim().replace(/^['"]|['']$/g, '');
|
|
122
|
+
if (selector.includes('$document')) return [];
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
const results = queryElements({ element, selector });
|
|
126
|
+
return results ? Array.from(results) : [];
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.warn(`[AST Engine] Invalid $queryAll selector => "${selector}"`, error);
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const isBrowser = typeof window !== 'undefined';
|
|
135
|
+
let baseSandboxScope = isBrowser ? Object.setPrototypeOf(BrowserGlobals, isomorphicSandbox) : isomorphicSandbox;
|
|
136
|
+
|
|
137
|
+
export function astSync({ executeAST, sandbox, node, registry } = {}) {
|
|
138
|
+
const targetNode = executeAST !== undefined ? executeAST : node;
|
|
139
|
+
if (targetNode === null || targetNode === undefined) return targetNode;
|
|
140
|
+
|
|
141
|
+
if (!registry) {
|
|
142
|
+
registry = sandbox instanceof Map ? sandbox : new Map(Object.entries(sandbox || {}));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Leaf String Evaluator: route to OperatorJS engine
|
|
146
|
+
if (typeof targetNode === "string") {
|
|
147
|
+
if (targetNode.includes("$") || targetNode.includes("ObjectId()")) {
|
|
148
|
+
let resolved = processOperators(node || null, targetNode, [], null, [], registry);
|
|
149
|
+
return unpackTokens(resolved, registry);
|
|
150
|
+
}
|
|
151
|
+
return targetNode;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// List Evaluator: maps items sequentially in a synchronous pipeline
|
|
155
|
+
if (Array.isArray(targetNode)) {
|
|
156
|
+
return targetNode.map(item => astSync({ executeAST: item, sandbox, node, registry }));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (typeof targetNode === "object") {
|
|
160
|
+
if (targetNode instanceof Date || targetNode instanceof RegExp) {
|
|
161
|
+
return targetNode;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const keys = Object.keys(targetNode);
|
|
165
|
+
|
|
166
|
+
// Context Function/Operator Evaluation for single key objects
|
|
167
|
+
if (keys.length === 1) {
|
|
168
|
+
const key = keys[0];
|
|
169
|
+
const isPrefixed = key.startsWith("$") && !["$array", "$filter", "$storage", "$database"].includes(key);
|
|
170
|
+
const prefixedKey = isPrefixed ? key : `$${key}`;
|
|
171
|
+
|
|
172
|
+
let opFunc = registry.get(key) || registry.get(prefixedKey) ||
|
|
173
|
+
customOperators.get(key) || customOperators.get(prefixedKey) ||
|
|
174
|
+
(sandbox && typeof sandbox === 'object' ? (sandbox[key] || sandbox[prefixedKey]) : undefined);
|
|
175
|
+
|
|
176
|
+
if (opFunc !== undefined) {
|
|
177
|
+
const evaluatedParam = astSync({ executeAST: targetNode[key], sandbox, node, registry });
|
|
178
|
+
let opCtx = { element: node, registry };
|
|
179
|
+
if (typeof opFunc === "function") {
|
|
180
|
+
const args = Array.isArray(evaluatedParam) ? evaluatedParam : [evaluatedParam];
|
|
181
|
+
|
|
182
|
+
// Pre-check for ES6 Class syntax
|
|
183
|
+
const isNativeClass = /^class\s|^class{/.test(Function.prototype.toString.call(opFunc));
|
|
184
|
+
if (isNativeClass) {
|
|
185
|
+
return new opFunc(...args);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Attempt invocation as function, fallback to constructor on TypeError
|
|
189
|
+
try {
|
|
190
|
+
return opFunc.call(opCtx, evaluatedParam);
|
|
191
|
+
} catch (e) {
|
|
192
|
+
if (e instanceof TypeError && /cannot be invoked without 'new'|class constructor/i.test(e.message)) {
|
|
193
|
+
return new opFunc(...args);
|
|
194
|
+
}
|
|
195
|
+
throw e;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return opFunc;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const resolvedObject = {};
|
|
203
|
+
for (const key of keys) {
|
|
204
|
+
let finalKey = key;
|
|
205
|
+
|
|
206
|
+
if (key.startsWith("$") && !["$array", "$filter", "$storage", "$database"].includes(key)) {
|
|
207
|
+
const resolvedKey = astSync({ executeAST: key, sandbox, node, registry });
|
|
208
|
+
if (typeof resolvedKey === "string" || typeof resolvedKey === "number") {
|
|
209
|
+
finalKey = String(resolvedKey);
|
|
210
|
+
}
|
|
211
|
+
} else if (key.includes('{{') && key.includes('}}')) {
|
|
212
|
+
finalKey = resolveInlinePlaceholdersSync(key, { sandbox, node, registry });
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
let finalValue = targetNode[key];
|
|
216
|
+
if (typeof finalValue === "string") {
|
|
217
|
+
if (finalValue.startsWith("$") && !["$array", "$filter", "$storage", "$database"].includes(finalValue)) {
|
|
218
|
+
finalValue = astSync({ executeAST: finalValue, sandbox, node, registry });
|
|
219
|
+
} else if (finalValue.includes('{{') && finalValue.includes('}}')) {
|
|
220
|
+
finalValue = resolveInlinePlaceholdersSync(finalValue, { sandbox, node, registry });
|
|
221
|
+
}
|
|
222
|
+
} else if (Array.isArray(finalValue) || (typeof finalValue === "object" && finalValue !== null)) {
|
|
223
|
+
finalValue = astSync({ executeAST: finalValue, sandbox, node, registry });
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (finalKey === 'return' || finalKey === '$return') {
|
|
227
|
+
return finalValue;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
resolvedObject[finalKey] = finalValue;
|
|
231
|
+
registry.set(finalKey, finalValue);
|
|
232
|
+
if (sandbox && typeof sandbox === 'object') {
|
|
233
|
+
sandbox[finalKey] = finalValue;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return resolvedObject;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return targetNode;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export async function astAsync({ executeAST, sandbox, node, registry } = {}) {
|
|
243
|
+
const targetNode = executeAST !== undefined ? executeAST : node;
|
|
244
|
+
if (targetNode === null || targetNode === undefined) return targetNode;
|
|
245
|
+
|
|
246
|
+
if (!registry) {
|
|
247
|
+
registry = sandbox instanceof Map ? sandbox : new Map(Object.entries(sandbox || {}));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Leaf String Evaluator: route to OperatorJS engine
|
|
251
|
+
if (typeof targetNode === "string") {
|
|
252
|
+
if (targetNode.includes("$") || targetNode.includes("ObjectId()")) {
|
|
253
|
+
let resolved = await processOperatorsAsync(node || null, targetNode, [], null, [], registry);
|
|
254
|
+
return unpackTokens(resolved, registry);
|
|
255
|
+
}
|
|
256
|
+
return targetNode;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// List Evaluator: maps items concurrently
|
|
260
|
+
if (Array.isArray(targetNode)) {
|
|
261
|
+
return await Promise.all(
|
|
262
|
+
targetNode.map(item => astAsync({ executeAST: item, sandbox, node, registry }))
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (typeof targetNode === "object") {
|
|
267
|
+
if (targetNode instanceof Date || targetNode instanceof RegExp) {
|
|
268
|
+
return targetNode;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const keys = Object.keys(targetNode);
|
|
272
|
+
|
|
273
|
+
// Context Function/Operator Evaluation for single key objects
|
|
274
|
+
if (keys.length === 1) {
|
|
275
|
+
const key = keys[0];
|
|
276
|
+
const isPrefixed = key.startsWith("$") && !["$array", "$filter", "$storage", "$database"].includes(key);
|
|
277
|
+
const prefixedKey = isPrefixed ? key : `$${key}`;
|
|
278
|
+
|
|
279
|
+
let opFunc = registry.get(key) || registry.get(prefixedKey) ||
|
|
280
|
+
customOperators.get(key) || customOperators.get(prefixedKey) ||
|
|
281
|
+
(sandbox && typeof sandbox === 'object' ? (sandbox[key] || sandbox[prefixedKey]) : undefined);
|
|
282
|
+
|
|
283
|
+
if (opFunc !== undefined) {
|
|
284
|
+
const evaluatedParam = await astAsync({ executeAST: targetNode[key], sandbox, node, registry });
|
|
285
|
+
let opCtx = { element: node, registry };
|
|
286
|
+
if (typeof opFunc === "function") {
|
|
287
|
+
const args = Array.isArray(evaluatedParam) ? evaluatedParam : [evaluatedParam];
|
|
288
|
+
|
|
289
|
+
// Pre-check for ES6 Class syntax
|
|
290
|
+
const isNativeClass = /^class\s|^class{/.test(Function.prototype.toString.call(opFunc));
|
|
291
|
+
if (isNativeClass) {
|
|
292
|
+
return new opFunc(...args);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Attempt invocation as function, fallback to constructor on TypeError
|
|
296
|
+
try {
|
|
297
|
+
return await opFunc.call(opCtx, evaluatedParam);
|
|
298
|
+
} catch (e) {
|
|
299
|
+
if (e instanceof TypeError && /cannot be invoked without 'new'|class constructor/i.test(e.message)) {
|
|
300
|
+
return new opFunc(...args);
|
|
301
|
+
}
|
|
302
|
+
throw e;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return opFunc;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const resolvedObject = {};
|
|
310
|
+
for (const key of keys) {
|
|
311
|
+
let finalKey = key;
|
|
312
|
+
|
|
313
|
+
if (key.startsWith("$") && !["$array", "$filter", "$storage", "$database"].includes(key)) {
|
|
314
|
+
const resolvedKey = await astAsync({ executeAST: key, sandbox, node, registry });
|
|
315
|
+
if (typeof resolvedKey === "string" || typeof resolvedKey === "number") {
|
|
316
|
+
finalKey = String(resolvedKey);
|
|
317
|
+
}
|
|
318
|
+
} else if (key.includes('{{') && key.includes('}}')) {
|
|
319
|
+
finalKey = await resolveInlinePlaceholdersAsync(key, { sandbox, node, registry });
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
let finalValue = targetNode[key];
|
|
323
|
+
if (typeof finalValue === "string") {
|
|
324
|
+
if (finalValue.startsWith("$") && !["$array", "$filter", "$storage", "$database"].includes(finalValue)) {
|
|
325
|
+
finalValue = await astAsync({ executeAST: finalValue, sandbox, node, registry });
|
|
326
|
+
} else if (finalValue.includes('{{') && finalValue.includes('}}')) {
|
|
327
|
+
finalValue = await resolveInlinePlaceholdersAsync(finalValue, { sandbox, node, registry });
|
|
328
|
+
}
|
|
329
|
+
} else if (Array.isArray(finalValue) || (typeof finalValue === "object" && finalValue !== null)) {
|
|
330
|
+
finalValue = await astAsync({ executeAST: finalValue, sandbox, node, registry });
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (finalKey === 'return' || finalKey === '$return') {
|
|
334
|
+
return finalValue;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
resolvedObject[finalKey] = finalValue;
|
|
338
|
+
registry.set(finalKey, finalValue);
|
|
339
|
+
if (sandbox && typeof sandbox === 'object') {
|
|
340
|
+
sandbox[finalKey] = finalValue;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return resolvedObject;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return targetNode;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function resolveInlinePlaceholdersSync(str, { sandbox, node, registry }) {
|
|
350
|
+
let result = str;
|
|
351
|
+
const regex = /\{\{\s*([\w\W]+?)\s*\}\}/g;
|
|
352
|
+
let match;
|
|
353
|
+
|
|
354
|
+
while ((match = regex.exec(str)) !== null) {
|
|
355
|
+
const placeholder = match[0];
|
|
356
|
+
const variablePath = match[1];
|
|
357
|
+
const evaluatedValue = astSync({ executeAST: variablePath, sandbox, node, registry });
|
|
358
|
+
const replacement = typeof evaluatedValue === 'object' ? JSON.stringify(evaluatedValue) : String(evaluatedValue);
|
|
359
|
+
result = result.replace(placeholder, replacement);
|
|
360
|
+
}
|
|
361
|
+
return result;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
async function resolveInlinePlaceholdersAsync(str, { sandbox, node, registry }) {
|
|
365
|
+
let result = str;
|
|
366
|
+
const regex = /\{\{\s*([\w\W]+?)\s*\}\}/g;
|
|
367
|
+
let match;
|
|
368
|
+
|
|
369
|
+
while ((match = regex.exec(str)) !== null) {
|
|
370
|
+
const placeholder = match[0];
|
|
371
|
+
const variablePath = match[1];
|
|
372
|
+
const evaluatedValue = await astAsync({ executeAST: variablePath, sandbox, node, registry });
|
|
373
|
+
const replacement = typeof evaluatedValue === 'object' ? JSON.stringify(evaluatedValue) : String(evaluatedValue);
|
|
374
|
+
result = result.replace(placeholder, replacement);
|
|
375
|
+
}
|
|
376
|
+
return result;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export async function evaluateAST({ node, executeAST, sandbox = {} } = {}) {
|
|
380
|
+
const targetNode = executeAST !== undefined ? executeAST : node;
|
|
381
|
+
|
|
382
|
+
if (typeof targetNode === "string") {
|
|
383
|
+
const tempRegistry = sandbox instanceof Map ? sandbox : new Map(Object.entries(sandbox));
|
|
384
|
+
let resolved = await processOperatorsAsync(node || null, targetNode, [], null, [], registry);
|
|
385
|
+
return unpackTokens(resolved, tempRegistry);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const activeScope = Object.create(baseSandboxScope);
|
|
389
|
+
Object.assign(activeScope, sandbox);
|
|
390
|
+
|
|
391
|
+
if (node) {
|
|
392
|
+
activeScope.element = node;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return await astAsync({
|
|
396
|
+
executeAST: targetNode,
|
|
397
|
+
sandbox: activeScope,
|
|
398
|
+
node: node
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
setASTEvaluators(astSync, astAsync);
|
|
403
|
+
|
|
404
|
+
export default astSync;
|