theprogrammablemind_4wp 9.5.1-beta.7 → 9.5.1-beta.8

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 (2) hide show
  1. package/package.json +2 -2
  2. package/src/fragments.js +126 -0
package/package.json CHANGED
@@ -45,7 +45,7 @@
45
45
  "runtime.js",
46
46
  "src/helpers.js",
47
47
  "src/flatten.js",
48
- "src/fragment.js",
48
+ "src/fragments.js",
49
49
  "src/unflatten.js",
50
50
  "src/config.js",
51
51
  "src/configHelpers.js",
@@ -72,6 +72,6 @@
72
72
  "sort-json": "^2.0.0",
73
73
  "uuid": "^8.3.2"
74
74
  },
75
- "version": "9.5.1-beta.7",
75
+ "version": "9.5.1-beta.8",
76
76
  "license": "UNLICENSED"
77
77
  }
@@ -0,0 +1,126 @@
1
+ const _ = require('lodash')
2
+ const helpers = require('./helpers')
3
+
4
+ function fragmentInstantiator(args, contexts) {
5
+ return new Object({
6
+ contexts: () => contexts,
7
+
8
+ instantiate: async (mappings) => {
9
+ const root = _.cloneDeep(contexts);
10
+
11
+ // To prevent infinite loops on circular references
12
+ const visited = new WeakSet();
13
+
14
+ // Stack item: { node, path }
15
+ // path is an array like: [], ['user'], ['users', 0], ['users', 0, 'address']
16
+ const todo = [{ node: root, path: [] }];
17
+
18
+ args = { ...args };
19
+
20
+ while (todo.length > 0) {
21
+ const { node, path } = todo.pop();
22
+
23
+ // Skip non-compound values early
24
+ if (!helpers.isCompound(node)) continue;
25
+
26
+ // Prevent reprocessing (including circular refs)
27
+ if (visited.has(node)) continue;
28
+ visited.add(node);
29
+
30
+ // Attach clean, frozen path array (non-enumerable)
31
+ Object.defineProperty(node, '__path', {
32
+ value: Object.freeze(path.slice()), // immutable copy
33
+ writable: false,
34
+ enumerable: false,
35
+ configurable: false
36
+ });
37
+
38
+ // Helpful string version
39
+ Object.defineProperty(node, '__pathString', {
40
+ value: path.map(p => String(p)).join('.'),
41
+ enumerable: false
42
+ });
43
+
44
+ // Pass rich context to mappings
45
+ args.context = node;
46
+ args.path = path; // e.g. ['users', 0, 'profile']
47
+ args.pathString = path.map(p => String(p)).join('.'); // "users.0.profile"
48
+
49
+ // Apply mappings
50
+ for (const mapping of mappings) {
51
+ if (await mapping.match(args)) {
52
+ await mapping.apply(args);
53
+ }
54
+ }
55
+
56
+ // Traverse children — build path correctly for objects AND arrays
57
+ if (Array.isArray(node)) {
58
+ node.forEach((child, index) => {
59
+ if (helpers.isCompound(child)) {
60
+ todo.push({
61
+ node: child,
62
+ path: [...path, index] // index as number!
63
+ });
64
+ }
65
+ });
66
+ } else {
67
+ // Plain object
68
+ for (const key of Object.keys(node)) {
69
+ const child = node[key];
70
+ if (helpers.isCompound(child)) {
71
+ todo.push({
72
+ node: child,
73
+ path: [...path, key] // key as string
74
+ });
75
+ }
76
+ }
77
+ }
78
+ }
79
+
80
+ return root;
81
+ }
82
+ });
83
+ }
84
+
85
+ async function fragmentMapperInstantiator(values, modelFrom, modelTo) {
86
+ const paths = {}
87
+ for (const value of values) {
88
+ paths[value] = { value }
89
+ }
90
+
91
+ {
92
+ const fi = fragmentInstantiator({paths}, modelFrom)
93
+ await fi.instantiate([
94
+ {
95
+ match: ({context, path}) => values.includes(context.value),
96
+ apply: ({context, path}) => paths[context.value].from = path
97
+ },
98
+ ])
99
+ }
100
+
101
+ {
102
+ const fi = fragmentInstantiator({paths}, modelTo)
103
+ await fi.instantiate([
104
+ {
105
+ match: ({context, path}) => values.includes(context.value),
106
+ apply: ({context, path}) => paths[context.value].to = path
107
+ },
108
+ ])
109
+ }
110
+ return {
111
+ instantiate: (actualFrom) => {
112
+ const actualTo = structuredClone(modelTo)
113
+ for (const value in paths) {
114
+ const { from, to } = paths[value]
115
+ const actualValue = helpers.getByPath(actualFrom, from, null)
116
+ helpers.setByPath(actualTo, to, actualValue)
117
+ }
118
+ return actualTo
119
+ }
120
+ }
121
+ }
122
+
123
+ module.exports = {
124
+ fragmentInstantiator,
125
+ fragmentMapperInstantiator,
126
+ }