theprogrammablemind_4wp 9.5.1-beta.7 → 9.5.1-beta.9
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 +3 -2
- package/src/fragments.js +126 -0
- package/src/generators.js +6 -5
package/package.json
CHANGED
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"runtime.js",
|
|
46
46
|
"src/helpers.js",
|
|
47
47
|
"src/flatten.js",
|
|
48
|
-
"src/
|
|
48
|
+
"src/fragments.js",
|
|
49
49
|
"src/unflatten.js",
|
|
50
50
|
"src/config.js",
|
|
51
51
|
"src/configHelpers.js",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"base-64": "^1.0.0",
|
|
64
64
|
"deep-equal": "^2.0.4",
|
|
65
|
+
"flatted": "^3.3.3",
|
|
65
66
|
"fs": "0.0.1-security",
|
|
66
67
|
"json-diff": "^1.0.3",
|
|
67
68
|
"json-stable-stringify": "^1.0.1",
|
|
@@ -72,6 +73,6 @@
|
|
|
72
73
|
"sort-json": "^2.0.0",
|
|
73
74
|
"uuid": "^8.3.2"
|
|
74
75
|
},
|
|
75
|
-
"version": "9.5.1-beta.
|
|
76
|
+
"version": "9.5.1-beta.9",
|
|
76
77
|
"license": "UNLICENSED"
|
|
77
78
|
}
|
package/src/fragments.js
ADDED
|
@@ -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
|
+
}
|
package/src/generators.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { stringify } = require('flatted');
|
|
1
2
|
const { args: contextArgs, normalizeGenerator } = require('./helpers')
|
|
2
3
|
const Lines = require('../lines')
|
|
3
4
|
const helpers = require('./helpers')
|
|
@@ -182,7 +183,7 @@ class Generators {
|
|
|
182
183
|
const generator = this.generators[igenerator]
|
|
183
184
|
if (await generator.matches(args, objects, context, hierarchy, config, options)) {
|
|
184
185
|
const log = (message) => { this.logs.push(message) }
|
|
185
|
-
// this.logs.push(`Generators: applied ${generator.toString()}\n to\n ${
|
|
186
|
+
// this.logs.push(`Generators: applied ${generator.toString()}\n to\n ${stringify(context)}`)
|
|
186
187
|
let errorMessage = 'The apply function did not return a value'
|
|
187
188
|
try {
|
|
188
189
|
generated = await generator.apply(args, objects, context, hierarchy, config, response, log)
|
|
@@ -212,7 +213,7 @@ class Generators {
|
|
|
212
213
|
lines.newRow()
|
|
213
214
|
lines.setElement(0, 1, 'TO')
|
|
214
215
|
lines.setElement(0, 2, `context_id: ${context.context_id}`)
|
|
215
|
-
lines.setElement(1, 2,
|
|
216
|
+
lines.setElement(1, 2, stringify(helpers.sortJson(context, { depth: 10 }), null, 2))
|
|
216
217
|
lines.newRow()
|
|
217
218
|
lines.setElement(0, 1, 'STACK')
|
|
218
219
|
lines.setElement(0, 2, stack)
|
|
@@ -223,7 +224,7 @@ class Generators {
|
|
|
223
224
|
lines.setElement(0, 1, 'ERROR')
|
|
224
225
|
lines.setElement(0, 2, errorMessage)
|
|
225
226
|
this.logs.push(lines.toString())
|
|
226
|
-
const message = `ERROR while applying (${source}) ${generator.toLabel()}\n to\n ${
|
|
227
|
+
const message = `ERROR while applying (${source}) ${generator.toLabel()}\n to\n ${stringify(context, null, 2)}.\n${errorMessage}'`
|
|
227
228
|
// this.logs.push(message)
|
|
228
229
|
// return [message]
|
|
229
230
|
args.calls.pop()
|
|
@@ -254,7 +255,7 @@ class Generators {
|
|
|
254
255
|
lines.newRow()
|
|
255
256
|
lines.setElement(0, 1, 'TO')
|
|
256
257
|
lines.setElement(0, 2, `context_id: ${context.context_id}`)
|
|
257
|
-
lines.setElement(1, 2,
|
|
258
|
+
lines.setElement(1, 2, stringify(helpers.sortJson(context, { depth: 25 }), null, 2))
|
|
258
259
|
this.logs.push(lines.toString())
|
|
259
260
|
}
|
|
260
261
|
applied = true
|
|
@@ -272,7 +273,7 @@ class Generators {
|
|
|
272
273
|
lines.setElement(0, 2, stack)
|
|
273
274
|
lines.newRow()
|
|
274
275
|
lines.setElement(0, 1, 'TO')
|
|
275
|
-
lines.setElement(0, 2,
|
|
276
|
+
lines.setElement(0, 2, stringify(context, null, 2))
|
|
276
277
|
this.logs.push(lines.toString())
|
|
277
278
|
}
|
|
278
279
|
return ((config || {}).parenthesized ? '(' + generated + ')' : generated)
|