@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocreate/utils",
3
- "version": "1.44.2",
3
+ "version": "1.44.3",
4
4
  "description": "A simple utils component in vanilla javascript. Easily configured using HTML5 attributes and/or JavaScript API.",
5
5
  "keywords": [
6
6
  "utils",
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
- if (inputId && /^[0-9a-fA-F]{24}$/.test(inputId)) {
5
- return {
6
- timestamp: inputId.substring(0, 8),
7
- processId: inputId.substring(8, 20),
8
- counter: inputId.substring(20),
9
- toString: function () {
10
- return this.timestamp + this.processId + this.counter;
11
- }
12
- };
13
- } else if (inputId) {
14
- throw new Error("Invalid ObjectId provided.");
15
- }
16
-
17
- const timestampHex = Math.floor(
18
- new Date(new Date().toISOString()).getTime() / 1000
19
- )
20
- .toString(16)
21
- .padStart(8, "0");
22
- const processIdHex = Math.floor(Math.random() * 0x100000000000)
23
- .toString(16)
24
- .padStart(12, "0");
25
-
26
- counter = (counter + 1) % 10000;
27
- if (counter < 2) {
28
- counter = Math.floor(Math.random() * (5000 - 100 + 1)) + 100;
29
- }
30
-
31
- const counterHex = counter.toString(16).padStart(4, "0");
32
-
33
- return {
34
- timestamp: timestampHex,
35
- processId: processIdHex,
36
- counter: counterHex,
37
- toString: function () {
38
- return this.timestamp + this.processId + this.counter;
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;
@@ -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;