theprogrammablemind_4wp 9.5.1 → 9.6.0
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/client.js +100 -57
- package/lines.js +7 -0
- package/package.json +5 -4
- package/runtime.js +0 -1
- package/src/config.js +330 -76
- package/src/configHelpers.js +68 -35
- package/src/fragments.js +83 -0
- package/src/generators.js +20 -14
- package/src/helpers.js +107 -3
- package/src/project2.js +54 -4
- package/src/semantics.js +21 -16
package/src/configHelpers.js
CHANGED
|
@@ -97,11 +97,11 @@ const setupArgs = (args, config, logs, hierarchy, uuidForScoping) => {
|
|
|
97
97
|
if (args.uuid) {
|
|
98
98
|
args.objects = args.getObjects(args.uuid)
|
|
99
99
|
}
|
|
100
|
-
if (!hierarchy) {
|
|
101
|
-
hierarchy = config.
|
|
100
|
+
if (!hierarchy && config.getHierarchy) {
|
|
101
|
+
hierarchy = config.getHierarchy()
|
|
102
102
|
}
|
|
103
103
|
// callId
|
|
104
|
-
args.calls = new InitCalls(args.isInstance ? `${args.isInstance}#${config.
|
|
104
|
+
args.calls = new InitCalls(args.isInstance ? `${args.isInstance}#${config.getName()}` : config.getName())
|
|
105
105
|
if (global.theprogrammablemind && global.theprogrammablemind.loadForTesting) {
|
|
106
106
|
args.calls = new InitCalls(Object.keys(global.theprogrammablemind.loadForTesting)[0])
|
|
107
107
|
}
|
|
@@ -112,14 +112,18 @@ const setupArgs = (args, config, logs, hierarchy, uuidForScoping) => {
|
|
|
112
112
|
throw new ErrorReason(context)
|
|
113
113
|
}
|
|
114
114
|
args.kms = config.getConfigs()
|
|
115
|
-
args.config = config
|
|
115
|
+
args.config = config.getPseudoConfig(uuidForScoping, config)
|
|
116
116
|
args.hierarchy = hierarchy
|
|
117
117
|
args.isA = isA(hierarchy)
|
|
118
118
|
// args.listable = listable(hierarchy)
|
|
119
119
|
// args.asList = asList
|
|
120
120
|
args.retry = () => { throw new RetryError() }
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
// mappings are optional
|
|
122
|
+
args.fragments = (query, mappings) => {
|
|
123
|
+
return config.fragment(args, query, mappings)
|
|
124
|
+
}
|
|
125
|
+
args.fragmentMapper = (values, fromModelQuery, toModelQuery) => {
|
|
126
|
+
return config.fragmentMapper(args, values, fromModelQuery, toModelQuery)
|
|
123
127
|
}
|
|
124
128
|
args.breakOnSemantics = false
|
|
125
129
|
args.theDebugger = {
|
|
@@ -133,20 +137,35 @@ const setupArgs = (args, config, logs, hierarchy, uuidForScoping) => {
|
|
|
133
137
|
args.addPattern = (pattern, def) => config.addPattern(pattern, args.uuid)
|
|
134
138
|
args.addGenerator = (generator) => config.addGenerator(generator, args.uuid, config.name)
|
|
135
139
|
|
|
140
|
+
if (config.getTestConfig()?.testModuleName) {
|
|
141
|
+
args.testModuleName = config.getTestConfig().testModuleName
|
|
142
|
+
}
|
|
136
143
|
args.addAssumedScoped = (args, assumed) => {
|
|
137
144
|
const addAssumed = (args, ...moreAssumed) => {
|
|
138
145
|
return { ...args, assumed: Object.assign({}, assumed, (args.assumed || {}), ...moreAssumed) }
|
|
139
146
|
}
|
|
140
147
|
|
|
141
|
-
args.s = (c) => config.getSemantics(logs).apply(args, c)
|
|
142
|
-
args.g = (c,
|
|
143
|
-
|
|
148
|
+
args.s = (c, options = {}) => config.getSemantics(logs).apply(args, c, options)
|
|
149
|
+
args.g = (c, rest = {}) => {
|
|
150
|
+
// if (JSON.stringify(rest) !== '{}' && !(rest.assumed || rest.options)) {
|
|
151
|
+
// debugger
|
|
152
|
+
// }
|
|
153
|
+
const { assumed = {}, options = {} } = rest
|
|
154
|
+
return config.getGenerators(logs).apply(addAssumed(args, assumed), c, assumed, options)
|
|
144
155
|
}
|
|
145
|
-
args.gp = (c,
|
|
146
|
-
|
|
156
|
+
args.gp = (c, rest = {}) => {
|
|
157
|
+
// if (JSON.stringify(rest) !== '{}' && !(rest.assumed || rest.options)) {
|
|
158
|
+
// debugger
|
|
159
|
+
// }
|
|
160
|
+
const { assumed = {}, options = {} } = rest
|
|
161
|
+
return config.getGenerators(logs).apply(addAssumed(args, assumed, { paraphrase: true, isResponse: false, response: false }), c, { paraphrase: true, isResponse: false, response: false }, options)
|
|
147
162
|
}
|
|
148
|
-
args.gr = (c,
|
|
149
|
-
|
|
163
|
+
args.gr = (c, rest = {}) => {
|
|
164
|
+
// if (JSON.stringify(rest) !== '{}' && !(rest.assumed || rest.options)) {
|
|
165
|
+
// debugger
|
|
166
|
+
// }
|
|
167
|
+
const { assumed = {}, options = {} } = rest
|
|
168
|
+
return config.getGenerators(logs).apply(addAssumed(args, assumed, { paraphrase: false, isResponse: true }), { ...c, paraphrase: false, isResponse: true }, options)
|
|
150
169
|
}
|
|
151
170
|
args.e = (c) => {
|
|
152
171
|
if (!c) {
|
|
@@ -179,14 +198,8 @@ const setupArgs = (args, config, logs, hierarchy, uuidForScoping) => {
|
|
|
179
198
|
}
|
|
180
199
|
config.getAddedArgs(args)
|
|
181
200
|
|
|
182
|
-
Object.assign(args, args.getUUIDScoped(uuidForScoping || config.
|
|
201
|
+
Object.assign(args, args.getUUIDScoped(uuidForScoping || config.getUUID()))
|
|
183
202
|
args.apis = args.apis || ((name) => config.getConfig(name).api)
|
|
184
|
-
/*
|
|
185
|
-
if (uuidForScoping) {
|
|
186
|
-
Object.assign(args, args.getUUIDScoped(uuidForScoping))
|
|
187
|
-
}
|
|
188
|
-
*/
|
|
189
|
-
// sets args for all the API. that make a copy so the args must be fully setup by here except for scoped
|
|
190
203
|
config.setArgs(args)
|
|
191
204
|
}
|
|
192
205
|
|
|
@@ -199,6 +212,22 @@ const getObjects = (objects) => {
|
|
|
199
212
|
}
|
|
200
213
|
}
|
|
201
214
|
|
|
215
|
+
const run = async (config, handler) => {
|
|
216
|
+
// map to hash
|
|
217
|
+
config = config || {}
|
|
218
|
+
if (config.config) {
|
|
219
|
+
config = config
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const hierarchy = new DigraphInternal((config.config || {}).hierarchy || [])
|
|
223
|
+
|
|
224
|
+
const objects = config.config.objects.namespaced[config.uuid]
|
|
225
|
+
const logs = []
|
|
226
|
+
const args = {}
|
|
227
|
+
setupArgs(args, config, logs, hierarchy)
|
|
228
|
+
return handler(args)
|
|
229
|
+
}
|
|
230
|
+
|
|
202
231
|
const processContext = async (context, { objects = {}, config, logs = [] }) => {
|
|
203
232
|
const generators = config.getGenerators(logs)
|
|
204
233
|
const semantics = config.getSemantics(logs)
|
|
@@ -291,7 +320,7 @@ const setupContexts = (rawContexts) => {
|
|
|
291
320
|
return contexts
|
|
292
321
|
}
|
|
293
322
|
|
|
294
|
-
const processContextsB = async ({ config, hierarchy, semantics, generators, json, isTest, isProcess, isModule, rebuildingTemplate, isInstance, instance, query, data, retries, url, commandLineArgs, forTemplate }) => {
|
|
323
|
+
const processContextsB = async ({ config, hierarchy, semantics, generators, json, isTest, isProcess, isModule, rebuildingTemplate, isInstance, instance, query, data, retries, url, commandLineArgs, forTemplate, contextIdCounter }) => {
|
|
295
324
|
// TODO fix this name to contextsPrime
|
|
296
325
|
const contextsPrime = []
|
|
297
326
|
const generatedPrime = []
|
|
@@ -311,14 +340,13 @@ const processContextsB = async ({ config, hierarchy, semantics, generators, json
|
|
|
311
340
|
args.insert = (context) => toDo.unshift(context)
|
|
312
341
|
let overlap, lastRange
|
|
313
342
|
config.debugLoops = commandLineArgs && commandLineArgs.debugLoops
|
|
314
|
-
let context_id_counter = 0
|
|
315
343
|
while (toDo.length > 0) {
|
|
316
344
|
const context = toDo.shift()
|
|
317
345
|
args.calls.next()
|
|
318
346
|
let contextPrime = context
|
|
319
347
|
context.topLevel = true
|
|
320
|
-
|
|
321
|
-
context.context_id =
|
|
348
|
+
contextIdCounter += 1
|
|
349
|
+
context.context_id = contextIdCounter
|
|
322
350
|
try {
|
|
323
351
|
if (json.has_errors) {
|
|
324
352
|
throw new Error('There are errors in the logs. Run with the -d flag and grep for Error')
|
|
@@ -328,6 +356,9 @@ const processContextsB = async ({ config, hierarchy, semantics, generators, json
|
|
|
328
356
|
const semantics = config.getSemantics(json.logs)
|
|
329
357
|
try {
|
|
330
358
|
contextPrime = await semantics.apply(args, context)
|
|
359
|
+
// contextPrime.greg = 'yes'
|
|
360
|
+
// console.log("context_id", context.context_id)
|
|
361
|
+
// console.log("semantics.apply", JSON.stringify(contextPrime, null, 2))
|
|
331
362
|
} catch (e) {
|
|
332
363
|
if (e.message == 'Maximum call stack size exceeded') {
|
|
333
364
|
const mostCalled = semantics.getMostCalled()
|
|
@@ -368,21 +399,21 @@ const processContextsB = async ({ config, hierarchy, semantics, generators, json
|
|
|
368
399
|
const generated = contextPrime.isResponse ? await config.getGenerators(json.logs).apply({ ...args, assumed }, contextPrime, assumed) : ''
|
|
369
400
|
let generatedParenthesized = []
|
|
370
401
|
if (generateParenthesized) {
|
|
371
|
-
config.
|
|
402
|
+
config.setParenthesized(true)
|
|
372
403
|
generatedParenthesized = contextPrime.isResponse ? await config.getGenerators(json.logs).apply({ ...args, assumed }, contextPrime, assumed) : ''
|
|
373
|
-
config.
|
|
404
|
+
config.setParenthesized(false)
|
|
374
405
|
}
|
|
375
406
|
// assumed = { paraphrase: true, response: false };
|
|
376
407
|
assumed = { paraphrase: true, isResponse: false, response: false }
|
|
377
408
|
if (generateParenthesized) {
|
|
378
|
-
config.
|
|
409
|
+
config.setParenthesized(false)
|
|
379
410
|
}
|
|
380
411
|
const paraphrases = await config.getGenerators(json.logs).apply({ ...args, assumed }, contextPrime, assumed)
|
|
381
412
|
let paraphrasesParenthesized = []
|
|
382
413
|
if (generateParenthesized) {
|
|
383
|
-
config.
|
|
414
|
+
config.setParenthesized(true)
|
|
384
415
|
paraphrasesParenthesized = await config.getGenerators(json.logs).apply({ ...args, assumed }, contextPrime, assumed)
|
|
385
|
-
config.
|
|
416
|
+
config.setParenthesized(false)
|
|
386
417
|
}
|
|
387
418
|
contextsPrime.push(contextPrime)
|
|
388
419
|
generatedPrime.push(generated)
|
|
@@ -421,7 +452,7 @@ const processContextsB = async ({ config, hierarchy, semantics, generators, json
|
|
|
421
452
|
throw e
|
|
422
453
|
}
|
|
423
454
|
}
|
|
424
|
-
return { contextsPrime, generatedPrime, paraphrasesPrime, paraphrasesParenthesizedPrime, generatedParenthesizedPrime, responsesPrime }
|
|
455
|
+
return { contextsPrime, generatedPrime, paraphrasesPrime, paraphrasesParenthesizedPrime, generatedParenthesizedPrime, responsesPrime, updatedContextIdCounter: contextIdCounter }
|
|
425
456
|
}
|
|
426
457
|
|
|
427
458
|
// instance template loadTemplate
|
|
@@ -429,11 +460,6 @@ const loadInstance = async (config, instance) => {
|
|
|
429
460
|
const transitoryMode = global.transitoryMode
|
|
430
461
|
global.transitoryMode = false
|
|
431
462
|
|
|
432
|
-
/*
|
|
433
|
-
if (config.name == 'people' && instance.name == 'people') {
|
|
434
|
-
debugger
|
|
435
|
-
}
|
|
436
|
-
*/
|
|
437
463
|
const rl = instance.resultss.length
|
|
438
464
|
if (rl > 0) {
|
|
439
465
|
config.addAssociations(instance.resultss[instance.resultss.length - 1].rtf_associations || [])
|
|
@@ -479,6 +505,9 @@ const loadInstance = async (config, instance) => {
|
|
|
479
505
|
args.isModule = true
|
|
480
506
|
args.isProcess = false
|
|
481
507
|
}
|
|
508
|
+
if (instance.name == config.testConfig.testModuleName) {
|
|
509
|
+
args.isTesting = true
|
|
510
|
+
}
|
|
482
511
|
await results.apply(args)
|
|
483
512
|
} else if (results.isFragment) {
|
|
484
513
|
} else {
|
|
@@ -490,6 +519,9 @@ const loadInstance = async (config, instance) => {
|
|
|
490
519
|
args.instance = ''
|
|
491
520
|
args.isProcess = !config.isModule
|
|
492
521
|
args.isModule = !!config.isModule
|
|
522
|
+
if (instance.name == config.testConfig.testModuleName) {
|
|
523
|
+
args.isTesting = true
|
|
524
|
+
}
|
|
493
525
|
await processContextsB(args)
|
|
494
526
|
if (results.skipSemantics) {
|
|
495
527
|
config.config.skipSemantics = null
|
|
@@ -505,6 +537,7 @@ module.exports = {
|
|
|
505
537
|
// listable,
|
|
506
538
|
setupArgs,
|
|
507
539
|
processContext,
|
|
540
|
+
run,
|
|
508
541
|
getObjects,
|
|
509
542
|
gs,
|
|
510
543
|
processContextsB,
|
package/src/fragments.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const _ = require('lodash')
|
|
2
|
+
const helpers = require('./helpers')
|
|
3
|
+
|
|
4
|
+
function fragmentInstantiator (args, contexts) {
|
|
5
|
+
return new Object({
|
|
6
|
+
contexts: () => {
|
|
7
|
+
return _.cloneDeep(contexts)
|
|
8
|
+
},
|
|
9
|
+
instantiate: async (mappings) => {
|
|
10
|
+
const instantiated = _.cloneDeep(contexts)
|
|
11
|
+
const todo = [{ context: instantiated, path: [] }]
|
|
12
|
+
args = { ...args }
|
|
13
|
+
while (todo.length > 0) {
|
|
14
|
+
const { context, path } = todo.pop()
|
|
15
|
+
args.context = context
|
|
16
|
+
args.path = path
|
|
17
|
+
for (const mapping of mappings) {
|
|
18
|
+
if (await mapping.match(args)) {
|
|
19
|
+
await mapping.apply(args)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
for (const key of Object.keys(context)) {
|
|
23
|
+
// if (['number', 'string', 'boolean'].includes(typeof (context[key]))) {
|
|
24
|
+
if (!helpers.isCompound(context[key])) {
|
|
25
|
+
continue
|
|
26
|
+
}
|
|
27
|
+
if (context[key].instantiated) {
|
|
28
|
+
continue
|
|
29
|
+
}
|
|
30
|
+
todo.push({ context: context[key], path: [...path, key] })
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (contexts.length == 1 && instantiated.length == 1) {
|
|
34
|
+
return instantiated[0]
|
|
35
|
+
} else {
|
|
36
|
+
return instantiated
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function fragmentMapperInstantiator(values, modelFrom, modelTo) {
|
|
43
|
+
const paths = {}
|
|
44
|
+
for (const value of values) {
|
|
45
|
+
paths[value] = { value }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
{
|
|
49
|
+
const fi = fragmentInstantiator({paths}, modelFrom)
|
|
50
|
+
await fi.instantiate([
|
|
51
|
+
{
|
|
52
|
+
match: ({context, path}) => values.includes(context.value),
|
|
53
|
+
apply: ({context, path}) => paths[context.value].from = path
|
|
54
|
+
},
|
|
55
|
+
])
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
{
|
|
59
|
+
const fi = fragmentInstantiator({paths}, modelTo)
|
|
60
|
+
await fi.instantiate([
|
|
61
|
+
{
|
|
62
|
+
match: ({context, path}) => values.includes(context.value),
|
|
63
|
+
apply: ({context, path}) => paths[context.value].to = path
|
|
64
|
+
},
|
|
65
|
+
])
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
instantiate: (actualFrom) => {
|
|
69
|
+
const actualTo = structuredClone(modelTo)
|
|
70
|
+
for (const value in paths) {
|
|
71
|
+
const { from, to } = paths[value]
|
|
72
|
+
const actualValue = helpers.getByPath(actualFrom, from, null)
|
|
73
|
+
helpers.setByPath(actualTo, to, actualValue)
|
|
74
|
+
}
|
|
75
|
+
return actualTo
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = {
|
|
81
|
+
fragmentInstantiator,
|
|
82
|
+
fragmentMapperInstantiator,
|
|
83
|
+
}
|
package/src/generators.js
CHANGED
|
@@ -26,11 +26,13 @@ class Generator {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/*
|
|
29
30
|
getAPIs (config) {
|
|
30
31
|
if (config && config._api && config._api.multiApi) {
|
|
31
32
|
return config._api.apis
|
|
32
33
|
}
|
|
33
34
|
}
|
|
35
|
+
*/
|
|
34
36
|
|
|
35
37
|
toLabel () {
|
|
36
38
|
const where = this.where ? `where: "${this.where}"` : ''
|
|
@@ -59,7 +61,7 @@ class Generator {
|
|
|
59
61
|
context,
|
|
60
62
|
callId,
|
|
61
63
|
api: this.getAPI(config),
|
|
62
|
-
apis:
|
|
64
|
+
apis: config.getAPIs(),
|
|
63
65
|
}
|
|
64
66
|
const args = Object.assign({}, baseArgs, moreArgs, (baseArgs.getUUIDScoped || (() => { return {} }))(this.uuid))
|
|
65
67
|
// return this.match(args)
|
|
@@ -78,11 +80,11 @@ class Generator {
|
|
|
78
80
|
if (!log) {
|
|
79
81
|
throw new Error('generators.apply argument log is required')
|
|
80
82
|
}
|
|
81
|
-
if (baseArgs.call && config && baseArgs.calls.stack.length > config.
|
|
82
|
-
throw new Error(`Max depth of ${config.
|
|
83
|
+
if (baseArgs.call && config && baseArgs.calls.stack.length > config.getMaxDepth()) {
|
|
84
|
+
throw new Error(`Max depth of ${config.getMaxDepth()} for calls has been exceeded. maxDepth can be set on the config object. To see the calls run with the --dl or set the debugLoops property on the config`)
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
if (config && config.
|
|
87
|
+
if (config && config.getDebugLoops()) {
|
|
86
88
|
console.log('apply', this.toLabel())
|
|
87
89
|
}
|
|
88
90
|
// this.getAPI(config)
|
|
@@ -113,7 +115,7 @@ class Generator {
|
|
|
113
115
|
config,
|
|
114
116
|
response,
|
|
115
117
|
api: this.getAPI(config),
|
|
116
|
-
apis:
|
|
118
|
+
apis: config.getAPIs()
|
|
117
119
|
}
|
|
118
120
|
const args = Object.assign({}, baseArgs, moreArgs, (baseArgs.getUUIDScoped || (() => { return {} }))(this.uuid))
|
|
119
121
|
if (this.property === 'generatorp') {
|
|
@@ -182,10 +184,10 @@ class Generators {
|
|
|
182
184
|
const generator = this.generators[igenerator]
|
|
183
185
|
if (await generator.matches(args, objects, context, hierarchy, config, options)) {
|
|
184
186
|
const log = (message) => { this.logs.push(message) }
|
|
185
|
-
// this.logs.push(`Generators: applied ${generator.toString()}\n to\n ${
|
|
187
|
+
// this.logs.push(`Generators: applied ${generator.toString()}\n to\n ${stringify(context)}`)
|
|
186
188
|
let errorMessage = 'The apply function did not return a value'
|
|
187
189
|
try {
|
|
188
|
-
generated = await generator.apply(args, objects, context, hierarchy, config, response, log)
|
|
190
|
+
generated = await generator.apply(args, objects, context, hierarchy, config, response, log, options)
|
|
189
191
|
} catch (e) {
|
|
190
192
|
// the next if handle this
|
|
191
193
|
generated = null
|
|
@@ -202,7 +204,7 @@ class Generators {
|
|
|
202
204
|
}
|
|
203
205
|
}
|
|
204
206
|
if (!generated && generated !== '') {
|
|
205
|
-
const widths = [10, 10
|
|
207
|
+
const widths = Lines.addRemainder([10, 10])
|
|
206
208
|
const lines = new Lines(widths)
|
|
207
209
|
lines.setElement(0, 0, 'Generator')
|
|
208
210
|
const source = `${generator.km}/#${generator.index}`
|
|
@@ -212,7 +214,7 @@ class Generators {
|
|
|
212
214
|
lines.newRow()
|
|
213
215
|
lines.setElement(0, 1, 'TO')
|
|
214
216
|
lines.setElement(0, 2, `context_id: ${context.context_id}`)
|
|
215
|
-
lines.setElement(1, 2, JSON.stringify(helpers.sortJson(context, { depth:
|
|
217
|
+
lines.setElement(1, 2, JSON.stringify(helpers.sortJson(context, { depth: 10 }), null, 2))
|
|
216
218
|
lines.newRow()
|
|
217
219
|
lines.setElement(0, 1, 'STACK')
|
|
218
220
|
lines.setElement(0, 2, stack)
|
|
@@ -229,8 +231,8 @@ class Generators {
|
|
|
229
231
|
args.calls.pop()
|
|
230
232
|
throw { error: [message], logs: this.logs }
|
|
231
233
|
}
|
|
232
|
-
if (
|
|
233
|
-
const widths = [10, 10
|
|
234
|
+
if (config.getDebug()) {
|
|
235
|
+
const widths = Lines.addRemainder([10, 10])
|
|
234
236
|
const lines = new Lines(widths)
|
|
235
237
|
lines.setElement(0, 0, 'Generator')
|
|
236
238
|
if (generator.index > -1 && generator.km) {
|
|
@@ -262,8 +264,8 @@ class Generators {
|
|
|
262
264
|
}
|
|
263
265
|
}
|
|
264
266
|
args.calls.pop()
|
|
265
|
-
if (!applied &&
|
|
266
|
-
const widths = [10, 10
|
|
267
|
+
if (!applied && config.getDebug()) {
|
|
268
|
+
const widths = Lines.addRemainder([10, 10])
|
|
267
269
|
const lines = new Lines(widths)
|
|
268
270
|
lines.setElement(0, 0, 'Generator')
|
|
269
271
|
lines.setElement(0, 2, 'No generator applied')
|
|
@@ -275,7 +277,11 @@ class Generators {
|
|
|
275
277
|
lines.setElement(0, 2, JSON.stringify(context, null, 2))
|
|
276
278
|
this.logs.push(lines.toString())
|
|
277
279
|
}
|
|
278
|
-
|
|
280
|
+
let parenthesized = false
|
|
281
|
+
if (config && config.getParenthesized()) {
|
|
282
|
+
parenthesized = true
|
|
283
|
+
}
|
|
284
|
+
return parenthesized ? '(' + generated + ')' : generated
|
|
279
285
|
}
|
|
280
286
|
}
|
|
281
287
|
|
package/src/helpers.js
CHANGED
|
@@ -292,8 +292,53 @@ const hashCode = (str) => {
|
|
|
292
292
|
return hash
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
-
|
|
296
|
-
|
|
295
|
+
/**
|
|
296
|
+
* Recursively sorts object keys alphabetically.
|
|
297
|
+
* Fully handles arrays (including objects inside arrays) and preserves Date/other non-plain objects.
|
|
298
|
+
*
|
|
299
|
+
* @param {any} input - The value to sort (object, array, primitive, etc.)
|
|
300
|
+
* @param {Object} [options]
|
|
301
|
+
* - ignoreCase: boolean (default false) – case-insensitive sort
|
|
302
|
+
* - reverse: boolean (default false) – reverse alphabetical order
|
|
303
|
+
* @returns {any} New sorted value
|
|
304
|
+
*/
|
|
305
|
+
function sortJson(input, options = {}) {
|
|
306
|
+
const { ignoreCase = false, reverse = false } = options;
|
|
307
|
+
|
|
308
|
+
// Helper: is this a plain object {} (not Array, Date, Map, null, etc.)
|
|
309
|
+
function isPlainObject(value) {
|
|
310
|
+
return value !== null && typeof value === 'object' && value.constructor === Object;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Handle arrays: map over elements and recurse
|
|
314
|
+
if (Array.isArray(input)) {
|
|
315
|
+
return input.map(item => sortJson(item, options));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// If not a plain object, return unchanged (preserves Date, Map, Set, primitives, etc.)
|
|
319
|
+
if (!isPlainObject(input)) {
|
|
320
|
+
return input;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Sorter for keys
|
|
324
|
+
const sorter = (a, b) => {
|
|
325
|
+
const A = ignoreCase ? a.toLowerCase() : a;
|
|
326
|
+
const B = ignoreCase ? b.toLowerCase() : b;
|
|
327
|
+
return reverse ? B.localeCompare(A) : A.localeCompare(B);
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
// Build new sorted object
|
|
331
|
+
const sorted = {};
|
|
332
|
+
|
|
333
|
+
Object.keys(input)
|
|
334
|
+
.sort(sorter)
|
|
335
|
+
.forEach(key => {
|
|
336
|
+
const value = input[key];
|
|
337
|
+
// Always recurse: handles nested objects and arrays properly
|
|
338
|
+
sorted[key] = sortJson(value, options);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
return sorted;
|
|
297
342
|
}
|
|
298
343
|
|
|
299
344
|
const validProps = (valids, object, type) => {
|
|
@@ -436,6 +481,63 @@ const stableId = (tag) => {
|
|
|
436
481
|
return id
|
|
437
482
|
}
|
|
438
483
|
|
|
484
|
+
function getByPath(obj, path, defaultValue) {
|
|
485
|
+
let current = obj;
|
|
486
|
+
for (const key of path) {
|
|
487
|
+
if (current === null || current === undefined) return defaultValue;
|
|
488
|
+
if (typeof current !== 'object') return defaultValue;
|
|
489
|
+
current = current[key];
|
|
490
|
+
}
|
|
491
|
+
return current === undefined ? defaultValue : current;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Set a value in an object by path array.
|
|
496
|
+
* Automatically creates missing objects {} or arrays [] as needed.
|
|
497
|
+
*
|
|
498
|
+
* @param {Object} obj - The root object to modify
|
|
499
|
+
* @param {Array<string|number>} path - Array of keys/indices
|
|
500
|
+
* @param {*} value - Value to set
|
|
501
|
+
* @returns {*} The set value (for chaining)
|
|
502
|
+
*/
|
|
503
|
+
function setByPath(obj, path, value) {
|
|
504
|
+
if (!Array.isArray(path) || path.length === 0) {
|
|
505
|
+
throw new Error('Path must be a non-empty array');
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
let current = obj;
|
|
509
|
+
|
|
510
|
+
for (let i = 0; i < path.length; i++) {
|
|
511
|
+
const key = path[i];
|
|
512
|
+
const isLast = i === path.length - 1;
|
|
513
|
+
|
|
514
|
+
if (isLast) {
|
|
515
|
+
// Final step — just assign
|
|
516
|
+
current[key] = value;
|
|
517
|
+
} else {
|
|
518
|
+
// Not last — ensure next level exists
|
|
519
|
+
const nextKey = path[i + 1];
|
|
520
|
+
|
|
521
|
+
if (current[key] == null) {
|
|
522
|
+
// Auto-create: array if next key is number, otherwise object
|
|
523
|
+
current[key] = typeof nextKey === 'number' || String(nextKey >>> 0) === nextKey
|
|
524
|
+
? []
|
|
525
|
+
: {};
|
|
526
|
+
} else if (Array.isArray(current[key]) && typeof nextKey !== 'number') {
|
|
527
|
+
// Safety: if current is array but next key isn't a valid index → convert to object
|
|
528
|
+
current[key] = { ...current[key] };
|
|
529
|
+
} else if (!Array.isArray(current[key]) && typeof nextKey === 'number') {
|
|
530
|
+
// If next expects array but current is object → convert
|
|
531
|
+
current[key] = Object.values(current[key]);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
current = current[key];
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return value;
|
|
539
|
+
}
|
|
540
|
+
|
|
439
541
|
module.exports = {
|
|
440
542
|
stableId,
|
|
441
543
|
ecatch,
|
|
@@ -462,5 +564,7 @@ module.exports = {
|
|
|
462
564
|
where,
|
|
463
565
|
w,
|
|
464
566
|
suggestAssociationsFix,
|
|
465
|
-
suggestAssociationsFixFromSummaries
|
|
567
|
+
suggestAssociationsFixFromSummaries,
|
|
568
|
+
getByPath,
|
|
569
|
+
setByPath,
|
|
466
570
|
}
|
package/src/project2.js
CHANGED
|
@@ -1,7 +1,32 @@
|
|
|
1
|
-
function
|
|
1
|
+
function areFirstNEqual(arr1, arr2, n) {
|
|
2
|
+
if (n <= 0) return true;
|
|
3
|
+
if (arr1.length < n || arr2.length < n) return false;
|
|
4
|
+
|
|
5
|
+
for (let i = 0; i < n; i++) {
|
|
6
|
+
if (arr1[i] !== arr2[i]) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function project(source, filters, path=[]) {
|
|
2
14
|
if (['string', 'number'].includes(typeof source)) {
|
|
3
15
|
return source
|
|
4
16
|
}
|
|
17
|
+
if (Array.isArray(source)) {
|
|
18
|
+
const result = []
|
|
19
|
+
for (const value of source) {
|
|
20
|
+
result.push(project(value, filters, [...path, '*']))
|
|
21
|
+
}
|
|
22
|
+
return result
|
|
23
|
+
}
|
|
24
|
+
function isPlainObject(obj) {
|
|
25
|
+
return Object.prototype.toString.call(obj) === '[object Object]';
|
|
26
|
+
}
|
|
27
|
+
if (!isPlainObject(source) && !Array.isArray(source)) {
|
|
28
|
+
return source
|
|
29
|
+
}
|
|
5
30
|
|
|
6
31
|
if (Object.keys(source).length === 0 && filters.length === 0) {
|
|
7
32
|
return {};
|
|
@@ -11,7 +36,7 @@ function project(source, filters) {
|
|
|
11
36
|
const filter = filters.find(f => f.match({ context: source }));
|
|
12
37
|
if (!filter) {
|
|
13
38
|
if (Array.isArray(source)) {
|
|
14
|
-
return source.map((element) => project(element, filters))
|
|
39
|
+
return source.map((element) => project(element, filters, [...path, '*']))
|
|
15
40
|
}
|
|
16
41
|
return {};
|
|
17
42
|
}
|
|
@@ -22,6 +47,7 @@ function project(source, filters) {
|
|
|
22
47
|
// update
|
|
23
48
|
const updatedProperties = []
|
|
24
49
|
for (const property of properties) {
|
|
50
|
+
// property that contains a list of properties to be checked
|
|
25
51
|
if (property.properties) {
|
|
26
52
|
for (const moreProperty of source[property.properties] || []) {
|
|
27
53
|
updatedProperties.push(moreProperty)
|
|
@@ -35,10 +61,34 @@ function project(source, filters) {
|
|
|
35
61
|
// Build the result object
|
|
36
62
|
const result = {};
|
|
37
63
|
properties.forEach(prop => {
|
|
38
|
-
if (source.hasOwnProperty(prop)) {
|
|
64
|
+
if (prop.path && (prop.path.length === path.length + 1) && areFirstNEqual(path, prop.path, path.length) && source.hasOwnProperty(prop.path[path.length])) {
|
|
65
|
+
const endProp = prop.path[path.length]
|
|
66
|
+
if (Array.isArray(source[endProp])) {
|
|
67
|
+
result[endProp] = []
|
|
68
|
+
for (const key in source[endProp]) {
|
|
69
|
+
result[endProp].push(project(source[endProp][key], filters, [...path, endProp, key]))
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
result[endProp] = {}
|
|
73
|
+
for (const key in source[endProp]) {
|
|
74
|
+
result[endProp][key] = project(source[endProp][key], filters, [...path, endProp, key])
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
} else if (source.hasOwnProperty(prop)) {
|
|
39
78
|
// If the property is an object and not null, recursively project it
|
|
40
79
|
if (typeof source[prop] === 'object' && source[prop] !== null) {
|
|
41
|
-
result[prop] = project(source[prop], filters);
|
|
80
|
+
result[prop] = project(source[prop], filters, [...path, prop]);
|
|
81
|
+
} else {
|
|
82
|
+
// Copy primitive or null properties directly
|
|
83
|
+
result[prop] = source[prop];
|
|
84
|
+
}
|
|
85
|
+
} else if (prop.property && source.hasOwnProperty(prop.property)) {
|
|
86
|
+
// If the property is an object and not null, recursively project it
|
|
87
|
+
if (typeof source[prop.property] === 'object' && source[prop.property] !== null) {
|
|
88
|
+
result[prop.property] = {}
|
|
89
|
+
for (const key of prop.check) {
|
|
90
|
+
result[prop.property][key] = project(source[prop.property][key], filters, [...path, prop.property, key]);
|
|
91
|
+
}
|
|
42
92
|
} else {
|
|
43
93
|
// Copy primitive or null properties directly
|
|
44
94
|
result[prop] = source[prop];
|