@domql/utils 3.7.5 → 3.7.6

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/README.md CHANGED
@@ -70,6 +70,50 @@ if (key === 'childProps') {
70
70
 
71
71
  When set on `element.props`, prevents the element from inheriting `childProps` from its parent via `inheritParentProps`. Used by fragment elements to avoid double-application of childProps (since fragments explicitly forward childProps to their children).
72
72
 
73
+ ## Scope (`scope.js`)
74
+
75
+ ### `createScope(element, parent)`
76
+
77
+ Creates the scope for an element with prototype-chain inheritance:
78
+
79
+ ```
80
+ el.scope → parent.scope → grandparent.scope → ... → root.scope → context.globalScope
81
+ ```
82
+
83
+ **Behavior:**
84
+ - **No own scope** — element inherits `parent.scope` directly (same reference)
85
+ - **Own scope defined** (`scope: { myVar: 1 }`) — prototype is set to parent's scope, chaining up to `globalScope`
86
+ - **No parent/root scope** — new scope created with `Object.create(context.globalScope)`
87
+ - **No context** — plain `{}`
88
+
89
+ ### `globalScope`
90
+
91
+ Automatically initialized as `context.globalScope = {}` if context exists. Sits at the bottom of every scope prototype chain, making its properties accessible from any element via `el.scope.X`.
92
+
93
+ ```js
94
+ // In context or set by the serialization pipeline:
95
+ context.globalScope = {
96
+ API_URL: 'https://api.example.com',
97
+ helpers: { capitalize: (s) => s[0].toUpperCase() + s.slice(1) }
98
+ }
99
+
100
+ // Accessible from any element — no import needed:
101
+ onClick: (e, el) => fetch(el.scope.API_URL)
102
+
103
+ // Also directly accessible:
104
+ el.context.globalScope.API_URL
105
+ ```
106
+
107
+ When a component defines its own scope, properties shadow parent/global values but the chain remains walkable:
108
+
109
+ ```js
110
+ // Parent: scope = { theme: 'dark' }
111
+ // Child: scope = { count: 0 }
112
+ // el.scope.count → 0 (own)
113
+ // el.scope.theme → 'dark' (parent, via prototype)
114
+ // el.scope.API_URL → '...' (globalScope, via prototype chain)
115
+ ```
116
+
73
117
  ## Key Sets (`keys.js`)
74
118
 
75
119
  - **`DOMQ_PROPERTIES`** — Framework-level keys that stay at element root
@@ -26,6 +26,7 @@ __export(object_exports, {
26
26
  deepDestringifyFunctions: () => deepDestringifyFunctions,
27
27
  deepMerge: () => deepMerge,
28
28
  deepStringifyFunctions: () => deepStringifyFunctions,
29
+ destringifyGlobalScope: () => destringifyGlobalScope,
29
30
  detectInfiniteLoop: () => detectInfiniteLoop,
30
31
  excludeKeysFromObject: () => excludeKeysFromObject,
31
32
  exec: () => exec,
@@ -337,6 +338,35 @@ const deepDestringifyFunctions = (obj, destringified = {}) => {
337
338
  }
338
339
  return destringified;
339
340
  };
341
+ const destringifyGlobalScope = (gs) => {
342
+ if (!gs || typeof gs !== "object") return gs;
343
+ const result = {};
344
+ const fnEntries = [];
345
+ for (const key of Object.keys(gs)) {
346
+ const val = gs[key];
347
+ if ((0, import_types.isString)(val) && hasFunction(val)) {
348
+ fnEntries.push([key, val]);
349
+ } else {
350
+ result[key] = val;
351
+ }
352
+ }
353
+ for (const [key, fnStr] of fnEntries) {
354
+ try {
355
+ const varDecls = Object.keys(result).map((k) => `var ${k} = __gs__[${JSON.stringify(k)}];`).join("\n");
356
+ result[key] = import_globals.window.eval(
357
+ `(function(__gs__) { ${varDecls}
358
+ return (${fnStr}); })`
359
+ )(result);
360
+ } catch (e) {
361
+ try {
362
+ result[key] = import_globals.window.eval(`(${fnStr})`);
363
+ } catch (_) {
364
+ result[key] = fnStr;
365
+ }
366
+ }
367
+ }
368
+ return result;
369
+ };
340
370
  const stringToObject = (str, opts = { verbose: true }) => {
341
371
  try {
342
372
  return str ? import_globals.window.eval("(" + str + ")") : {};
package/dist/cjs/scope.js CHANGED
@@ -23,5 +23,22 @@ __export(scope_exports, {
23
23
  module.exports = __toCommonJS(scope_exports);
24
24
  const createScope = (element, parent) => {
25
25
  const { __ref: ref } = element;
26
- if (!element.scope) element.scope = parent.scope || ref.root?.scope || {};
26
+ const context = element.context || parent.context || ref.root?.context;
27
+ if (context && !context.globalScope) context.globalScope = {};
28
+ const parentScope = parent.scope || ref.root?.scope;
29
+ const globalScope = context?.globalScope;
30
+ if (!element.scope) {
31
+ if (parentScope) {
32
+ element.scope = parentScope;
33
+ } else if (globalScope) {
34
+ element.scope = Object.create(globalScope);
35
+ } else {
36
+ element.scope = {};
37
+ }
38
+ } else if (typeof element.scope === "object" && element.scope !== null) {
39
+ if (Object.getPrototypeOf(element.scope) === Object.prototype) {
40
+ const proto = parentScope || globalScope;
41
+ if (proto) Object.setPrototypeOf(element.scope, proto);
42
+ }
43
+ }
27
44
  };
@@ -293,6 +293,35 @@ const deepDestringifyFunctions = (obj, destringified = {}) => {
293
293
  }
294
294
  return destringified;
295
295
  };
296
+ const destringifyGlobalScope = (gs) => {
297
+ if (!gs || typeof gs !== "object") return gs;
298
+ const result = {};
299
+ const fnEntries = [];
300
+ for (const key of Object.keys(gs)) {
301
+ const val = gs[key];
302
+ if (isString(val) && hasFunction(val)) {
303
+ fnEntries.push([key, val]);
304
+ } else {
305
+ result[key] = val;
306
+ }
307
+ }
308
+ for (const [key, fnStr] of fnEntries) {
309
+ try {
310
+ const varDecls = Object.keys(result).map((k) => `var ${k} = __gs__[${JSON.stringify(k)}];`).join("\n");
311
+ result[key] = window.eval(
312
+ `(function(__gs__) { ${varDecls}
313
+ return (${fnStr}); })`
314
+ )(result);
315
+ } catch (e) {
316
+ try {
317
+ result[key] = window.eval(`(${fnStr})`);
318
+ } catch (_) {
319
+ result[key] = fnStr;
320
+ }
321
+ }
322
+ }
323
+ return result;
324
+ };
296
325
  const stringToObject = (str, opts = { verbose: true }) => {
297
326
  try {
298
327
  return str ? window.eval("(" + str + ")") : {};
@@ -546,6 +575,7 @@ export {
546
575
  deepDestringifyFunctions,
547
576
  deepMerge,
548
577
  deepStringifyFunctions,
578
+ destringifyGlobalScope,
549
579
  detectInfiniteLoop,
550
580
  excludeKeysFromObject,
551
581
  exec,
package/dist/esm/scope.js CHANGED
@@ -1,6 +1,23 @@
1
1
  const createScope = (element, parent) => {
2
2
  const { __ref: ref } = element;
3
- if (!element.scope) element.scope = parent.scope || ref.root?.scope || {};
3
+ const context = element.context || parent.context || ref.root?.context;
4
+ if (context && !context.globalScope) context.globalScope = {};
5
+ const parentScope = parent.scope || ref.root?.scope;
6
+ const globalScope = context?.globalScope;
7
+ if (!element.scope) {
8
+ if (parentScope) {
9
+ element.scope = parentScope;
10
+ } else if (globalScope) {
11
+ element.scope = Object.create(globalScope);
12
+ } else {
13
+ element.scope = {};
14
+ }
15
+ } else if (typeof element.scope === "object" && element.scope !== null) {
16
+ if (Object.getPrototypeOf(element.scope) === Object.prototype) {
17
+ const proto = parentScope || globalScope;
18
+ if (proto) Object.setPrototypeOf(element.scope, proto);
19
+ }
20
+ }
4
21
  };
5
22
  export {
6
23
  createScope
@@ -1631,6 +1631,7 @@ var DomqlUtils = (() => {
1631
1631
  deepMergeExtends: () => deepMergeExtends,
1632
1632
  deepStringifyFunctions: () => deepStringifyFunctions,
1633
1633
  defineSetter: () => defineSetter,
1634
+ destringifyGlobalScope: () => destringifyGlobalScope,
1634
1635
  detectInfiniteLoop: () => detectInfiniteLoop,
1635
1636
  document: () => document2,
1636
1637
  encodeNewlines: () => encodeNewlines,
@@ -2556,6 +2557,35 @@ var DomqlUtils = (() => {
2556
2557
  }
2557
2558
  return destringified;
2558
2559
  };
2560
+ var destringifyGlobalScope = (gs) => {
2561
+ if (!gs || typeof gs !== "object") return gs;
2562
+ const result = {};
2563
+ const fnEntries = [];
2564
+ for (const key of Object.keys(gs)) {
2565
+ const val = gs[key];
2566
+ if (isString(val) && hasFunction(val)) {
2567
+ fnEntries.push([key, val]);
2568
+ } else {
2569
+ result[key] = val;
2570
+ }
2571
+ }
2572
+ for (const [key, fnStr] of fnEntries) {
2573
+ try {
2574
+ const varDecls = Object.keys(result).map((k) => `var ${k} = __gs__[${JSON.stringify(k)}];`).join("\n");
2575
+ result[key] = window2.eval(
2576
+ `(function(__gs__) { ${varDecls}
2577
+ return (${fnStr}); })`
2578
+ )(result);
2579
+ } catch (e) {
2580
+ try {
2581
+ result[key] = window2.eval(`(${fnStr})`);
2582
+ } catch (_) {
2583
+ result[key] = fnStr;
2584
+ }
2585
+ }
2586
+ }
2587
+ return result;
2588
+ };
2559
2589
  var stringToObject = (str, opts = { verbose: true }) => {
2560
2590
  try {
2561
2591
  return str ? window2.eval("(" + str + ")") : {};
@@ -4078,7 +4108,24 @@ var DomqlUtils = (() => {
4078
4108
  // scope.js
4079
4109
  var createScope = (element, parent) => {
4080
4110
  const { __ref: ref } = element;
4081
- if (!element.scope) element.scope = parent.scope || ref.root?.scope || {};
4111
+ const context = element.context || parent.context || ref.root?.context;
4112
+ if (context && !context.globalScope) context.globalScope = {};
4113
+ const parentScope = parent.scope || ref.root?.scope;
4114
+ const globalScope = context?.globalScope;
4115
+ if (!element.scope) {
4116
+ if (parentScope) {
4117
+ element.scope = parentScope;
4118
+ } else if (globalScope) {
4119
+ element.scope = Object.create(globalScope);
4120
+ } else {
4121
+ element.scope = {};
4122
+ }
4123
+ } else if (typeof element.scope === "object" && element.scope !== null) {
4124
+ if (Object.getPrototypeOf(element.scope) === Object.prototype) {
4125
+ const proto = parentScope || globalScope;
4126
+ if (proto) Object.setPrototypeOf(element.scope, proto);
4127
+ }
4128
+ }
4082
4129
  };
4083
4130
 
4084
4131
  // triggerEvent.js
package/object.js CHANGED
@@ -348,6 +348,49 @@ export const deepDestringifyFunctions = (obj, destringified = {}) => {
348
348
  return destringified
349
349
  }
350
350
 
351
+ /**
352
+ * Destringify a globalScope object so that function strings become real functions.
353
+ * All globalScope values are made available as local variables when eval'ing each
354
+ * function, so helpers can reference constants and other helpers naturally.
355
+ */
356
+ export const destringifyGlobalScope = (gs) => {
357
+ if (!gs || typeof gs !== 'object') return gs
358
+
359
+ // First pass: collect non-function values (constants, arrays, objects)
360
+ const result = {}
361
+ const fnEntries = []
362
+ for (const key of Object.keys(gs)) {
363
+ const val = gs[key]
364
+ if (isString(val) && hasFunction(val)) {
365
+ fnEntries.push([key, val])
366
+ } else {
367
+ result[key] = val
368
+ }
369
+ }
370
+
371
+ // Second pass: eval functions in a closure with all values in scope
372
+ for (const [key, fnStr] of fnEntries) {
373
+ try {
374
+ // Build a closure that exposes all current globalScope values
375
+ const varDecls = Object.keys(result)
376
+ .map(k => `var ${k} = __gs__[${JSON.stringify(k)}];`)
377
+ .join('\n')
378
+ result[key] = window.eval(
379
+ `(function(__gs__) { ${varDecls}\n return (${fnStr}); })`
380
+ )(result)
381
+ } catch (e) {
382
+ // Fallback: try plain eval
383
+ try {
384
+ result[key] = window.eval(`(${fnStr})`)
385
+ } catch (_) {
386
+ result[key] = fnStr
387
+ }
388
+ }
389
+ }
390
+
391
+ return result
392
+ }
393
+
351
394
  export const stringToObject = (str, opts = { verbose: true }) => {
352
395
  try {
353
396
  return str ? window.eval('(' + str + ')') : {} // eslint-disable-line
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@domql/utils",
3
- "version": "3.7.5",
3
+ "version": "3.7.6",
4
4
  "license": "CC-BY-NC-4.0",
5
5
  "type": "module",
6
6
  "module": "./dist/esm/index.js",
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "gitHead": "9fc1b79b41cdc725ca6b24aec64920a599634681",
40
40
  "peerDependencies": {
41
- "@symbo.ls/fetch": "^3.7.5"
41
+ "@symbo.ls/fetch": "^3.7.6"
42
42
  },
43
43
  "peerDependenciesMeta": {
44
44
  "@symbo.ls/fetch": {
package/scope.js CHANGED
@@ -1,8 +1,32 @@
1
1
  'use strict'
2
2
 
3
3
  // Create scope - shared object across the elements to the own or the nearest parent
4
+ // Prototype chain: own scope → parent scope → ... → root scope → globalScope
4
5
  export const createScope = (element, parent) => {
5
6
  const { __ref: ref } = element
6
- // If the element doesn't have a scope, initialize it using the parent's scope or the root's scope.
7
- if (!element.scope) element.scope = parent.scope || ref.root?.scope || {}
7
+ const context = element.context || parent.context || ref.root?.context
8
+
9
+ // Ensure globalScope exists on context
10
+ if (context && !context.globalScope) context.globalScope = {}
11
+
12
+ const parentScope = parent.scope || ref.root?.scope
13
+ const globalScope = context?.globalScope
14
+
15
+ if (!element.scope) {
16
+ // No own scope defined — inherit parent scope directly
17
+ if (parentScope) {
18
+ element.scope = parentScope
19
+ } else if (globalScope) {
20
+ // No parent scope either — create root-level scope with globalScope as prototype
21
+ element.scope = Object.create(globalScope)
22
+ } else {
23
+ element.scope = {}
24
+ }
25
+ } else if (typeof element.scope === 'object' && element.scope !== null) {
26
+ // Element defines its own scope — chain prototype to parent scope (or globalScope)
27
+ if (Object.getPrototypeOf(element.scope) === Object.prototype) {
28
+ const proto = parentScope || globalScope
29
+ if (proto) Object.setPrototypeOf(element.scope, proto)
30
+ }
31
+ }
8
32
  }