theprogrammablemind 9.5.1-beta.2 → 9.5.1-beta.21

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 CHANGED
@@ -316,6 +316,7 @@ const _process = async (config, query, { initializer, commandLineArgs, credentia
316
316
  }
317
317
 
318
318
  let startCounter = 0
319
+ let contextIdCounter = 0
319
320
  while (true) {
320
321
  if (queries.length === 0) {
321
322
  break
@@ -356,8 +357,9 @@ const _process = async (config, query, { initializer, commandLineArgs, credentia
356
357
  }
357
358
  const summary = { summaries: json.summaries, length: json.contexts.length }
358
359
  summaries.push(summary)
359
- const { contextsPrime, generatedPrime, paraphrasesPrime, paraphrasesParenthesizedPrime, generatedParenthesizedPrime, responsesPrime } =
360
- await processContextsB({ isTest, isProcess, isModule, rebuildingTemplate, config, hierarchy, json, commandLineArgs /*, generators, semantics */ })
360
+ const { updatedContextIdCounter, contextsPrime, generatedPrime, paraphrasesPrime, paraphrasesParenthesizedPrime, generatedParenthesizedPrime, responsesPrime } =
361
+ await processContextsB({ contextIdCounter, isTest, isProcess, isModule, rebuildingTemplate, config, hierarchy, json, commandLineArgs /*, generators, semantics */ })
362
+ contextIdCounter = updatedContextIdCounter
361
363
  if (isTest) {
362
364
  const end = runtime.performance.performance.now()
363
365
  clientSideTime = end - start
@@ -475,6 +477,7 @@ const runTest = async (config, expected, { args, verbose, testConfig, debug, tim
475
477
  return
476
478
  }
477
479
  // initialize in between test so state is not preserved since the test was adding without state
480
+ config.testConfig.testModuleName = testConfig.testModuleName
478
481
  await config.rebuild()
479
482
  const errorHandler = (error) => {
480
483
  if (error.metadata) {
@@ -913,7 +916,7 @@ const defaultInnerProcess = (config, errorHandler, responses) => {
913
916
  console.log('')
914
917
  const screen_width = process.stdout.columns
915
918
  // || 0 for when running without a console
916
- const widths = [70, 10, Math.max(80, screen_width - 71 || 0)]
919
+ const widths = Lines.addRemainder([70, 10])
917
920
  const lines = new Lines(widths)
918
921
  lines.setElement(0, 0, '--- The paraphrases are ----------')
919
922
  lines.setElement(0, 2, '--- The response strings are ----------')
@@ -2008,9 +2011,15 @@ const ensureTestFile = (module, name, type) => {
2008
2011
  }
2009
2012
 
2010
2013
  const knowledgeModule = async (...args) => {
2011
- await knowledgeModuleImpl(...args).catch((e) => {
2014
+ await knowledgeModuleImpl(...args).catch(async (e) => {
2012
2015
  console.error(e)
2013
- process.exit(-1)
2016
+ function sleep(ms) {
2017
+ return new Promise((resolve) => {
2018
+ setTimeout(resolve, ms);
2019
+ });
2020
+ }
2021
+ await sleep(1) // get the stderr to flush
2022
+ await process.exit(-1); // tiny trick: empty write forces flush of console.error buffer
2014
2023
  })
2015
2024
  }
2016
2025
 
package/lines.js CHANGED
@@ -5,6 +5,13 @@ class Lines {
5
5
  this.rows = []
6
6
  }
7
7
 
8
+ static SCREEN_WIDTH = 164
9
+
10
+ static addRemainder(widths) {
11
+ const sum = widths.reduce((a, b) => a + b)
12
+ return [...widths, Lines.SCREEN_WIDTH - sum]
13
+ }
14
+
8
15
  addLine () {
9
16
  this.lines.push(this.widths.map((width) => ''.padEnd(width)))
10
17
  }
package/package.json CHANGED
@@ -3,14 +3,14 @@
3
3
  "@eslint/js": "^9.21.0",
4
4
  "@typescript-eslint/eslint-plugin": "^4.28.4",
5
5
  "@typescript-eslint/parser": "^4.28.4",
6
+ "argparse": "^2.0.1",
6
7
  "eslint": "^7.32.0",
7
8
  "eslint-config-standard": "^16.0.3",
8
9
  "eslint-plugin-import": "^2.23.4",
9
10
  "eslint-plugin-node": "^11.1.0",
10
11
  "eslint-plugin-promise": "^5.1.0",
11
12
  "globals": "^16.0.0",
12
- "jest": "^29.7.0",
13
- "argparse": "^2.0.1"
13
+ "jest": "^30.2.0"
14
14
  },
15
15
  "scripts": {
16
16
  "to:debug": "node inspect node_modules/.bin/jest --runInBand -t NEO23",
@@ -45,6 +45,7 @@
45
45
  "runtime.js",
46
46
  "src/helpers.js",
47
47
  "src/flatten.js",
48
+ "src/fragments.js",
48
49
  "src/unflatten.js",
49
50
  "src/config.js",
50
51
  "src/configHelpers.js",
@@ -61,6 +62,7 @@
61
62
  "dependencies": {
62
63
  "base-64": "^1.0.0",
63
64
  "deep-equal": "^2.0.4",
65
+ "flatted": "^3.3.3",
64
66
  "fs": "0.0.1-security",
65
67
  "json-diff": "^1.0.3",
66
68
  "json-stable-stringify": "^1.0.1",
@@ -71,6 +73,6 @@
71
73
  "sort-json": "^2.0.0",
72
74
  "uuid": "^8.3.2"
73
75
  },
74
- "version": "9.5.1-beta.2",
76
+ "version": "9.5.1-beta.21",
75
77
  "license": "UNLICENSED"
76
78
  }
package/src/config.js CHANGED
@@ -10,6 +10,7 @@ const { ecatch } = require('./helpers')
10
10
  const runtime = require('../runtime')
11
11
  const _ = require('lodash')
12
12
  const db = require('./debug')
13
+ const { fragmentInstantiator, fragmentMapperInstantiator } = require('./fragments')
13
14
 
14
15
  const debugBreak = () => {
15
16
  // debugger
@@ -289,11 +290,9 @@ const priority_valid = (cp) => {
289
290
  const handleBridgeProps = (config, bridge, { addFirst, uuid } = {}) => {
290
291
  ecatch(`While processing the bridge for ${bridge.id}#${bridge.level}`,
291
292
  () => {
292
- /*
293
- if (bridge.development && config.isModule) {
293
+ if (bridge.scope && config.isModule) {
294
294
  return
295
295
  }
296
- */
297
296
  if (false && !bridge.bridge) {
298
297
  bridge.bridge = '{ ...next(operator) }'
299
298
  }
@@ -403,11 +402,15 @@ const handleBridgeProps = (config, bridge, { addFirst, uuid } = {}) => {
403
402
  if (bridge.generatorp) {
404
403
  const match = bridge.generatorp.match || (() => true)
405
404
  const apply = typeof bridge.generatorp === 'function' ? bridge.generatorp : bridge.generatorp.apply || bridge.generatorp
406
- const level = bridge.generatorp.level >= 0 ? bridge.generatorp.level : bridge.level + 1
405
+ let level = bridge.generatorp.level >= 0 ? bridge.generatorp.level : bridge.level + 1
406
+ if (!bridge.bridge) {
407
+ level = 0
408
+ }
407
409
 
408
410
  const generator = {
409
411
  where: bridge.generatorp.where || bridge.where || helpers.where(4),
410
- match: async (args) => bridge.id === args.context.marker && args.context.level === level && args.context.paraphrase && await match(args),
412
+ // match: async (args) => bridge.id === args.context.marker && args.context.level === level && args.context.paraphrase && await match(args),
413
+ match: async (args) => args.isA(args.context.marker, bridge.id) && args.context.level === level && args.context.paraphrase && await match(args),
411
414
  apply: (args) => apply(args),
412
415
  applyWrapped: apply,
413
416
  property: 'generatorp'
@@ -424,7 +427,8 @@ const handleBridgeProps = (config, bridge, { addFirst, uuid } = {}) => {
424
427
  const level = bridge.generatorr.level >= 0 ? bridge.generatorr.level : bridge.level + 1
425
428
  const generator = {
426
429
  where: bridge.generatorr.where || bridge.where || helpers.where(4),
427
- match: async (args) => bridge.id === args.context.marker && args.context.level === level && !args.context.paraphrase && (args.context.response || args.context.isResponse) && await match(args),
430
+ // match: async (args) => bridge.id === args.context.marker && args.context.level === level && !args.context.paraphrase && (args.context.response || args.context.isResponse) && await match(args),
431
+ match: async (args) => args.isA(args.context.marker, bridge.id) && args.context.level === level && !args.context.paraphrase && (args.context.response || args.context.isResponse) && await match(args),
428
432
  apply: (args) => apply(args),
429
433
  applyWrapped: apply,
430
434
  property: 'generatorr'
@@ -904,6 +908,10 @@ class Config {
904
908
  return config_toServer(config)
905
909
  }
906
910
 
911
+ async run(handler) {
912
+ return configHelpers.run(this, handler)
913
+ }
914
+
907
915
  async fixtures () {
908
916
  if (this.testConfig?.fixtures) {
909
917
  const args = {}
@@ -1109,59 +1117,42 @@ class Config {
1109
1117
  return instance
1110
1118
  }
1111
1119
 
1112
- fragmentInstantiator (args, contexts) {
1113
- return new Object({
1114
- contexts: () => contexts,
1115
- instantiate: async (mappings) => {
1116
- const instantiated = _.cloneDeep(contexts)
1117
- // const todo = [...instantiated]
1118
- // const todo = [...instantiated]
1119
- const todo = _.clone(instantiated)
1120
- args = { ...args }
1121
- while (todo.length > 0) {
1122
- const context = todo.pop()
1123
- args.context = context
1124
- for (const mapping of mappings) {
1125
- if (await mapping.match(args)) {
1126
- await mapping.apply(args)
1127
- }
1128
- }
1129
- for (const key of Object.keys(context)) {
1130
- // if (['number', 'string', 'boolean'].includes(typeof (context[key]))) {
1131
- if (!helpers.isCompound(context[key])) {
1132
- continue
1133
- }
1134
- if (context[key].instantiated) {
1135
- continue
1136
- }
1137
- todo.push(context[key])
1138
- }
1139
- }
1140
- return instantiated
1141
- }
1142
- })
1143
- }
1144
-
1145
- fragment (args, query) {
1120
+ getFragment(query) {
1146
1121
  for (const instance of (this.instances || [])) {
1147
1122
  for (const fragment of (instance.fragments || [])) {
1148
1123
  if (fragment.query === query) {
1149
- return this.fragmentInstantiator(args, fragment.contexts)
1124
+ return fragment
1150
1125
  }
1151
1126
  }
1152
1127
  for (const fragment of (instance.resultss || [])) {
1153
1128
  if (fragment.isFragment && fragment.query === query) {
1154
- return this.fragmentInstantiator(args, fragment.contexts)
1129
+ return fragment
1155
1130
  }
1156
1131
  }
1157
1132
  for (const fragment of (this.fragmentsBeingBuilt || [])) {
1158
1133
  if (fragment.query === query) {
1159
- return this.fragmentInstantiator(args, fragment.contexts)
1134
+ return fragment
1160
1135
  }
1161
1136
  }
1162
1137
  }
1163
1138
  }
1164
1139
 
1140
+ fragment (args, query) {
1141
+ const fragment = this.getFragment(query)
1142
+ if (fragment) {
1143
+ return fragmentInstantiator(args, fragment.contexts)
1144
+ }
1145
+ }
1146
+
1147
+ fragmentMapper (args, values, fromModelQuery, toModelQuery) {
1148
+ const fromModelFragment = this.getFragment(fromModelQuery)
1149
+ console.dir(fromModelFragment)
1150
+ const toModelFragment = this.getFragment(toModelQuery)
1151
+ console.dir(toModelFragment)
1152
+ const mapper = fragmentMapperInstantiator(values, fromModelFragment.contexts, toModelFragment.contexts)
1153
+ return mapper
1154
+ }
1155
+
1165
1156
  // { rebuild: false, isModule: false }
1166
1157
  needsRebuild (template, instance, options) {
1167
1158
  if (options.rebuild) {
@@ -1520,6 +1511,11 @@ class Config {
1520
1511
  }
1521
1512
  }
1522
1513
 
1514
+ updateBridge(id, updater) {
1515
+ const bridge = this.config.bridges.find((b) => b.id === id)
1516
+ updater({ config: this, bridge })
1517
+ }
1518
+
1523
1519
  addBridge (bridge, uuid) {
1524
1520
  if (!this.config.bridges) {
1525
1521
  this.config.bridges = []
@@ -1821,7 +1817,7 @@ class Config {
1821
1817
 
1822
1818
  // set the args in the api's
1823
1819
  setArgs (args) {
1824
- const setArgs = (config) => {
1820
+ const setConfigArgs = (config) => {
1825
1821
  if (!config._api) {
1826
1822
  return
1827
1823
  }
@@ -1834,10 +1830,10 @@ class Config {
1834
1830
  }
1835
1831
  }
1836
1832
 
1837
- setArgs(this)
1833
+ setConfigArgs(this)
1838
1834
  for (const config of this.configs) {
1839
1835
  if (config.config instanceof Config) {
1840
- setArgs(config.config)
1836
+ setConfigArgs(config.config)
1841
1837
  }
1842
1838
  }
1843
1839
  }
@@ -1866,6 +1862,16 @@ class Config {
1866
1862
  }
1867
1863
  }
1868
1864
 
1865
+ getObjects () {
1866
+ const configs = {}
1867
+ const ns = this.config.objects.namespaced
1868
+ configs[this.name] = this
1869
+ for (const config of this.configs) {
1870
+ configs[config._name] = ns[config._uuid]
1871
+ }
1872
+ return configs
1873
+ }
1874
+
1869
1875
  getConfig (name) {
1870
1876
  if (this.name === name) {
1871
1877
  return this
@@ -1913,12 +1919,15 @@ class Config {
1913
1919
  } else if (this.scope == 'development') {
1914
1920
  new_result = !(element.scope === 'testing')
1915
1921
  }
1922
+ /*
1916
1923
  if (global.GORDO && old_result !== new_result) {
1917
1924
  global.GORDO = false
1918
1925
  console.log("THERE WAS A DIFFERENCE ------------------------------------------------")
1919
1926
  debugger // greg23old
1920
1927
  }
1921
- return old_result
1928
+ */
1929
+ // return old_result
1930
+ return new_result
1922
1931
  }
1923
1932
 
1924
1933
  config.operators = config.operators.filter((element) => keep(element))
@@ -121,6 +121,9 @@ const setupArgs = (args, config, logs, hierarchy, uuidForScoping) => {
121
121
  args.fragments = (query) => {
122
122
  return config.fragment(args, query)
123
123
  }
124
+ args.fragmentMapper = (values, fromModelQuery, toModelQuery) => {
125
+ return config.fragmentMapper(args, values, fromModelQuery, toModelQuery)
126
+ }
124
127
  args.breakOnSemantics = false
125
128
  args.theDebugger = {
126
129
  breakOnSemantics: (value) => args.breakOnSemantics = value
@@ -133,20 +136,35 @@ const setupArgs = (args, config, logs, hierarchy, uuidForScoping) => {
133
136
  args.addPattern = (pattern, def) => config.addPattern(pattern, args.uuid)
134
137
  args.addGenerator = (generator) => config.addGenerator(generator, args.uuid, config.name)
135
138
 
139
+ if (config.testConfig?.testModuleName) {
140
+ args.testModuleName = config.testConfig.testModuleName
141
+ }
136
142
  args.addAssumedScoped = (args, assumed) => {
137
143
  const addAssumed = (args, ...moreAssumed) => {
138
144
  return { ...args, assumed: Object.assign({}, assumed, (args.assumed || {}), ...moreAssumed) }
139
145
  }
140
146
 
141
- args.s = (c) => config.getSemantics(logs).apply(args, c)
142
- args.g = (c, a = {}) => {
143
- return config.getGenerators(logs).apply(addAssumed(args, a), c, a)
147
+ args.s = (c, options = {}) => config.getSemantics(logs).apply(args, c, options)
148
+ args.g = (c, rest = {}) => {
149
+ // if (JSON.stringify(rest) !== '{}' && !(rest.assumed || rest.options)) {
150
+ // debugger
151
+ // }
152
+ const { assumed = {}, options = {} } = rest
153
+ return config.getGenerators(logs).apply(addAssumed(args, assumed), c, assumed, options)
144
154
  }
145
- args.gp = (c, a = {}) => {
146
- return config.getGenerators(logs).apply(addAssumed(args, a, { paraphrase: true, isResponse: false, response: false }), c, { paraphrase: true, isResponse: false, response: false })
155
+ args.gp = (c, rest = {}) => {
156
+ // if (JSON.stringify(rest) !== '{}' && !(rest.assumed || rest.options)) {
157
+ // debugger
158
+ // }
159
+ const { assumed = {}, options = {} } = rest
160
+ return config.getGenerators(logs).apply(addAssumed(args, assumed, { paraphrase: true, isResponse: false, response: false }), c, { paraphrase: true, isResponse: false, response: false }, options)
147
161
  }
148
- args.gr = (c, a = {}) => {
149
- return config.getGenerators(logs).apply(addAssumed(args, a, { paraphrase: false, isResponse: true }), { ...c, paraphrase: false, isResponse: true })
162
+ args.gr = (c, rest = {}) => {
163
+ // if (JSON.stringify(rest) !== '{}' && !(rest.assumed || rest.options)) {
164
+ // debugger
165
+ // }
166
+ const { assumed = {}, options = {} } = rest
167
+ return config.getGenerators(logs).apply(addAssumed(args, assumed, { paraphrase: false, isResponse: true }), { ...c, paraphrase: false, isResponse: true }, options)
150
168
  }
151
169
  args.e = (c) => {
152
170
  if (!c) {
@@ -181,6 +199,7 @@ const setupArgs = (args, config, logs, hierarchy, uuidForScoping) => {
181
199
 
182
200
  Object.assign(args, args.getUUIDScoped(uuidForScoping || config.uuid))
183
201
  args.apis = args.apis || ((name) => config.getConfig(name).api)
202
+ // args.apis = (name) => config.getAPIs(name)
184
203
  /*
185
204
  if (uuidForScoping) {
186
205
  Object.assign(args, args.getUUIDScoped(uuidForScoping))
@@ -199,6 +218,22 @@ const getObjects = (objects) => {
199
218
  }
200
219
  }
201
220
 
221
+ const run = async (config, handler) => {
222
+ // map to hash
223
+ config = config || {}
224
+ if (config.config) {
225
+ config = config
226
+ }
227
+
228
+ const hierarchy = new DigraphInternal((config.config || {}).hierarchy || [])
229
+
230
+ const objects = config.config.objects.namespaced[config.uuid]
231
+ const logs = []
232
+ const args = {}
233
+ setupArgs(args, config, logs, hierarchy)
234
+ return handler(args)
235
+ }
236
+
202
237
  const processContext = async (context, { objects = {}, config, logs = [] }) => {
203
238
  const generators = config.getGenerators(logs)
204
239
  const semantics = config.getSemantics(logs)
@@ -291,7 +326,7 @@ const setupContexts = (rawContexts) => {
291
326
  return contexts
292
327
  }
293
328
 
294
- const processContextsB = async ({ config, hierarchy, semantics, generators, json, isTest, isProcess, isModule, rebuildingTemplate, isInstance, instance, query, data, retries, url, commandLineArgs, forTemplate }) => {
329
+ const processContextsB = async ({ config, hierarchy, semantics, generators, json, isTest, isProcess, isModule, rebuildingTemplate, isInstance, instance, query, data, retries, url, commandLineArgs, forTemplate, contextIdCounter }) => {
295
330
  // TODO fix this name to contextsPrime
296
331
  const contextsPrime = []
297
332
  const generatedPrime = []
@@ -311,14 +346,13 @@ const processContextsB = async ({ config, hierarchy, semantics, generators, json
311
346
  args.insert = (context) => toDo.unshift(context)
312
347
  let overlap, lastRange
313
348
  config.debugLoops = commandLineArgs && commandLineArgs.debugLoops
314
- let context_id_counter = 0
315
349
  while (toDo.length > 0) {
316
350
  const context = toDo.shift()
317
351
  args.calls.next()
318
352
  let contextPrime = context
319
353
  context.topLevel = true
320
- context_id_counter += 1
321
- context.context_id = context_id_counter
354
+ contextIdCounter += 1
355
+ context.context_id = contextIdCounter
322
356
  try {
323
357
  if (json.has_errors) {
324
358
  throw new Error('There are errors in the logs. Run with the -d flag and grep for Error')
@@ -328,6 +362,9 @@ const processContextsB = async ({ config, hierarchy, semantics, generators, json
328
362
  const semantics = config.getSemantics(json.logs)
329
363
  try {
330
364
  contextPrime = await semantics.apply(args, context)
365
+ // contextPrime.greg = 'yes'
366
+ // console.log("context_id", context.context_id)
367
+ // console.log("semantics.apply", JSON.stringify(contextPrime, null, 2))
331
368
  } catch (e) {
332
369
  if (e.message == 'Maximum call stack size exceeded') {
333
370
  const mostCalled = semantics.getMostCalled()
@@ -421,7 +458,7 @@ const processContextsB = async ({ config, hierarchy, semantics, generators, json
421
458
  throw e
422
459
  }
423
460
  }
424
- return { contextsPrime, generatedPrime, paraphrasesPrime, paraphrasesParenthesizedPrime, generatedParenthesizedPrime, responsesPrime }
461
+ return { contextsPrime, generatedPrime, paraphrasesPrime, paraphrasesParenthesizedPrime, generatedParenthesizedPrime, responsesPrime, updatedContextIdCounter: contextIdCounter }
425
462
  }
426
463
 
427
464
  // instance template loadTemplate
@@ -479,6 +516,9 @@ const loadInstance = async (config, instance) => {
479
516
  args.isModule = true
480
517
  args.isProcess = false
481
518
  }
519
+ if (instance.name == config.testConfig.testModuleName) {
520
+ args.isTesting = true
521
+ }
482
522
  await results.apply(args)
483
523
  } else if (results.isFragment) {
484
524
  } else {
@@ -490,6 +530,9 @@ const loadInstance = async (config, instance) => {
490
530
  args.instance = ''
491
531
  args.isProcess = !config.isModule
492
532
  args.isModule = !!config.isModule
533
+ if (instance.name == config.testConfig.testModuleName) {
534
+ args.isTesting = true
535
+ }
493
536
  await processContextsB(args)
494
537
  if (results.skipSemantics) {
495
538
  config.config.skipSemantics = null
@@ -505,6 +548,7 @@ module.exports = {
505
548
  // listable,
506
549
  setupArgs,
507
550
  processContext,
551
+ run,
508
552
  getObjects,
509
553
  gs,
510
554
  processContextsB,
@@ -0,0 +1,79 @@
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
+ return instantiated
34
+ }
35
+ })
36
+ }
37
+
38
+ async function fragmentMapperInstantiator(values, modelFrom, modelTo) {
39
+ const paths = {}
40
+ for (const value of values) {
41
+ paths[value] = { value }
42
+ }
43
+
44
+ {
45
+ const fi = fragmentInstantiator({paths}, modelFrom)
46
+ await fi.instantiate([
47
+ {
48
+ match: ({context, path}) => values.includes(context.value),
49
+ apply: ({context, path}) => paths[context.value].from = path
50
+ },
51
+ ])
52
+ }
53
+
54
+ {
55
+ const fi = fragmentInstantiator({paths}, modelTo)
56
+ await fi.instantiate([
57
+ {
58
+ match: ({context, path}) => values.includes(context.value),
59
+ apply: ({context, path}) => paths[context.value].to = path
60
+ },
61
+ ])
62
+ }
63
+ return {
64
+ instantiate: (actualFrom) => {
65
+ const actualTo = structuredClone(modelTo)
66
+ for (const value in paths) {
67
+ const { from, to } = paths[value]
68
+ const actualValue = helpers.getByPath(actualFrom, from, null)
69
+ helpers.setByPath(actualTo, to, actualValue)
70
+ }
71
+ return actualTo
72
+ }
73
+ }
74
+ }
75
+
76
+ module.exports = {
77
+ fragmentInstantiator,
78
+ fragmentMapperInstantiator,
79
+ }
package/src/generators.js CHANGED
@@ -182,10 +182,10 @@ class Generators {
182
182
  const generator = this.generators[igenerator]
183
183
  if (await generator.matches(args, objects, context, hierarchy, config, options)) {
184
184
  const log = (message) => { this.logs.push(message) }
185
- // this.logs.push(`Generators: applied ${generator.toString()}\n to\n ${JSON.stringify(context)}`)
185
+ // this.logs.push(`Generators: applied ${generator.toString()}\n to\n ${stringify(context)}`)
186
186
  let errorMessage = 'The apply function did not return a value'
187
187
  try {
188
- generated = await generator.apply(args, objects, context, hierarchy, config, response, log)
188
+ generated = await generator.apply(args, objects, context, hierarchy, config, response, log, options)
189
189
  } catch (e) {
190
190
  // the next if handle this
191
191
  generated = null
@@ -202,7 +202,7 @@ class Generators {
202
202
  }
203
203
  }
204
204
  if (!generated && generated !== '') {
205
- const widths = [10, 10, 90]
205
+ const widths = Lines.addRemainder([10, 10])
206
206
  const lines = new Lines(widths)
207
207
  lines.setElement(0, 0, 'Generator')
208
208
  const source = `${generator.km}/#${generator.index}`
@@ -212,7 +212,7 @@ class Generators {
212
212
  lines.newRow()
213
213
  lines.setElement(0, 1, 'TO')
214
214
  lines.setElement(0, 2, `context_id: ${context.context_id}`)
215
- lines.setElement(1, 2, JSON.stringify(helpers.sortJson(context, { depth: 25 }), null, 2))
215
+ lines.setElement(1, 2, JSON.stringify(helpers.sortJson(context, { depth: 10 }), null, 2))
216
216
  lines.newRow()
217
217
  lines.setElement(0, 1, 'STACK')
218
218
  lines.setElement(0, 2, stack)
@@ -230,7 +230,7 @@ class Generators {
230
230
  throw { error: [message], logs: this.logs }
231
231
  }
232
232
  if (((config || {}).config || {}).debug) {
233
- const widths = [10, 10, 90]
233
+ const widths = Lines.addRemainder([10, 10])
234
234
  const lines = new Lines(widths)
235
235
  lines.setElement(0, 0, 'Generator')
236
236
  if (generator.index > -1 && generator.km) {
@@ -263,7 +263,7 @@ class Generators {
263
263
  }
264
264
  args.calls.pop()
265
265
  if (!applied && ((config || {}).config || {}).debug) {
266
- const widths = [10, 10, 90]
266
+ const widths = Lines.addRemainder([10, 10])
267
267
  const lines = new Lines(widths)
268
268
  lines.setElement(0, 0, 'Generator')
269
269
  lines.setElement(0, 2, 'No generator applied')
package/src/helpers.js CHANGED
@@ -436,6 +436,63 @@ const stableId = (tag) => {
436
436
  return id
437
437
  }
438
438
 
439
+ function getByPath(obj, path, defaultValue) {
440
+ let current = obj;
441
+ for (const key of path) {
442
+ if (current === null || current === undefined) return defaultValue;
443
+ if (typeof current !== 'object') return defaultValue;
444
+ current = current[key];
445
+ }
446
+ return current === undefined ? defaultValue : current;
447
+ }
448
+
449
+ /**
450
+ * Set a value in an object by path array.
451
+ * Automatically creates missing objects {} or arrays [] as needed.
452
+ *
453
+ * @param {Object} obj - The root object to modify
454
+ * @param {Array<string|number>} path - Array of keys/indices
455
+ * @param {*} value - Value to set
456
+ * @returns {*} The set value (for chaining)
457
+ */
458
+ function setByPath(obj, path, value) {
459
+ if (!Array.isArray(path) || path.length === 0) {
460
+ throw new Error('Path must be a non-empty array');
461
+ }
462
+
463
+ let current = obj;
464
+
465
+ for (let i = 0; i < path.length; i++) {
466
+ const key = path[i];
467
+ const isLast = i === path.length - 1;
468
+
469
+ if (isLast) {
470
+ // Final step — just assign
471
+ current[key] = value;
472
+ } else {
473
+ // Not last — ensure next level exists
474
+ const nextKey = path[i + 1];
475
+
476
+ if (current[key] == null) {
477
+ // Auto-create: array if next key is number, otherwise object
478
+ current[key] = typeof nextKey === 'number' || String(nextKey >>> 0) === nextKey
479
+ ? []
480
+ : {};
481
+ } else if (Array.isArray(current[key]) && typeof nextKey !== 'number') {
482
+ // Safety: if current is array but next key isn't a valid index → convert to object
483
+ current[key] = { ...current[key] };
484
+ } else if (!Array.isArray(current[key]) && typeof nextKey === 'number') {
485
+ // If next expects array but current is object → convert
486
+ current[key] = Object.values(current[key]);
487
+ }
488
+
489
+ current = current[key];
490
+ }
491
+ }
492
+
493
+ return value;
494
+ }
495
+
439
496
  module.exports = {
440
497
  stableId,
441
498
  ecatch,
@@ -463,4 +520,6 @@ module.exports = {
463
520
  w,
464
521
  suggestAssociationsFix,
465
522
  suggestAssociationsFixFromSummaries,
523
+ getByPath,
524
+ setByPath,
466
525
  }
package/src/semantics.js CHANGED
@@ -70,6 +70,7 @@ class Semantic {
70
70
  }
71
71
 
72
72
  async matches (args, context, options = {}) {
73
+ args = {...args}
73
74
  this.fixUpArgs(args, context)
74
75
  const matches = await this.matcher(args)
75
76
  if (matches && (options.debug || {}).match || args.callId === this.callId) {
@@ -81,6 +82,7 @@ class Semantic {
81
82
  }
82
83
 
83
84
  async apply (args, context, s, options = {}) {
85
+ args = {...args}
84
86
  const { config } = args
85
87
  if (config && config.debugLoops) {
86
88
  console.log('apply', this.toLabel())
@@ -218,7 +220,7 @@ class Semantics {
218
220
  errorMessage = e.toString()
219
221
  }
220
222
 
221
- const widths = [10, 10, 90]
223
+ const widths = Lines.addRemainder([10, 10])
222
224
  const lines = new Lines(widths)
223
225
  lines.setElement(0, 0, 'Semantic')
224
226
  const source = `${semantic.km}/#${semantic.index}`
@@ -248,7 +250,7 @@ class Semantics {
248
250
  args.calls.touch(contextPrime)
249
251
  // this.logs.push(`Semantics: applied ${semantic.toString()}\n to\n ${JSON.stringify(context)}\n the result was ${JSON.stringify(contextPrime)}\n`)
250
252
  if (((config || {}).config || {}).debug) {
251
- const widths = [10, 10, 132]
253
+ const widths = Lines.addRemainder([10, 10])
252
254
  const lines = new Lines(widths)
253
255
  lines.setElement(0, 0, 'Semantic')
254
256
  if (semantic.index > -1 && semantic.km) {
@@ -295,7 +297,7 @@ class Semantics {
295
297
  }
296
298
  args.calls.pop()
297
299
  if (!applied && ((config || {}).config || {}).debug) {
298
- const widths = [10, 10, 90]
300
+ const widths = Lines.addRemainder([10, 10])
299
301
  const lines = new Lines(widths)
300
302
  lines.setElement(0, 0, 'Semantic')
301
303
  lines.setElement(0, 2, 'No semantic applied')