theprogrammablemind_4wp 9.5.1-beta.1 → 9.5.1-beta.11

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) {
@@ -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 = 132
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.1",
76
+ "version": "9.5.1-beta.11",
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
  }
@@ -904,6 +903,10 @@ class Config {
904
903
  return config_toServer(config)
905
904
  }
906
905
 
906
+ async run(handler) {
907
+ return configHelpers.run(this, handler)
908
+ }
909
+
907
910
  async fixtures () {
908
911
  if (this.testConfig?.fixtures) {
909
912
  const args = {}
@@ -1109,59 +1112,42 @@ class Config {
1109
1112
  return instance
1110
1113
  }
1111
1114
 
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) {
1115
+ getFragment(query) {
1146
1116
  for (const instance of (this.instances || [])) {
1147
1117
  for (const fragment of (instance.fragments || [])) {
1148
1118
  if (fragment.query === query) {
1149
- return this.fragmentInstantiator(args, fragment.contexts)
1119
+ return fragment
1150
1120
  }
1151
1121
  }
1152
1122
  for (const fragment of (instance.resultss || [])) {
1153
1123
  if (fragment.isFragment && fragment.query === query) {
1154
- return this.fragmentInstantiator(args, fragment.contexts)
1124
+ return fragment
1155
1125
  }
1156
1126
  }
1157
1127
  for (const fragment of (this.fragmentsBeingBuilt || [])) {
1158
1128
  if (fragment.query === query) {
1159
- return this.fragmentInstantiator(args, fragment.contexts)
1129
+ return fragment
1160
1130
  }
1161
1131
  }
1162
1132
  }
1163
1133
  }
1164
1134
 
1135
+ fragment (args, query) {
1136
+ const fragment = this.getFragment(query)
1137
+ if (fragment) {
1138
+ return fragmentInstantiator(args, fragment.contexts)
1139
+ }
1140
+ }
1141
+
1142
+ fragmentMapper (args, values, fromModelQuery, toModelQuery) {
1143
+ const fromModelFragment = this.getFragment(fromModelQuery)
1144
+ console.dir(fromModelFragment)
1145
+ const toModelFragment = this.getFragment(toModelQuery)
1146
+ console.dir(toModelFragment)
1147
+ const mapper = fragmentMapperInstantiator(values, fromModelFragment.contexts, toModelFragment.contexts)
1148
+ return mapper
1149
+ }
1150
+
1165
1151
  // { rebuild: false, isModule: false }
1166
1152
  needsRebuild (template, instance, options) {
1167
1153
  if (options.rebuild) {
@@ -1866,6 +1852,16 @@ class Config {
1866
1852
  }
1867
1853
  }
1868
1854
 
1855
+ getObjects () {
1856
+ const configs = {}
1857
+ const ns = this.config.objects.namespaced
1858
+ configs[this.name] = this
1859
+ for (const config of this.configs) {
1860
+ configs[config._name] = ns[config._uuid]
1861
+ }
1862
+ return configs
1863
+ }
1864
+
1869
1865
  getConfig (name) {
1870
1866
  if (this.name === name) {
1871
1867
  return this
@@ -1913,12 +1909,15 @@ class Config {
1913
1909
  } else if (this.scope == 'development') {
1914
1910
  new_result = !(element.scope === 'testing')
1915
1911
  }
1912
+ /*
1916
1913
  if (global.GORDO && old_result !== new_result) {
1917
1914
  global.GORDO = false
1918
1915
  console.log("THERE WAS A DIFFERENCE ------------------------------------------------")
1919
1916
  debugger // greg23old
1920
1917
  }
1921
- return old_result
1918
+ */
1919
+ // return old_result
1920
+ return new_result
1922
1921
  }
1923
1922
 
1924
1923
  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,6 +136,9 @@ 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) }
@@ -199,6 +205,23 @@ const getObjects = (objects) => {
199
205
  }
200
206
  }
201
207
 
208
+ const run = async (config, handler) => {
209
+ // map to hash
210
+ config = config || {}
211
+ if (config.config) {
212
+ config = config
213
+ }
214
+
215
+ const hierarchy = new DigraphInternal((config.config || {}).hierarchy || [])
216
+
217
+ debugger
218
+ const objects = config.config.objects.namespaced[config.uuid]
219
+ const logs = []
220
+ const args = {}
221
+ setupArgs(args, config, logs, hierarchy)
222
+ return handler(args)
223
+ }
224
+
202
225
  const processContext = async (context, { objects = {}, config, logs = [] }) => {
203
226
  const generators = config.getGenerators(logs)
204
227
  const semantics = config.getSemantics(logs)
@@ -291,7 +314,7 @@ const setupContexts = (rawContexts) => {
291
314
  return contexts
292
315
  }
293
316
 
294
- const processContextsB = async ({ config, hierarchy, semantics, generators, json, isTest, isProcess, isModule, rebuildingTemplate, isInstance, instance, query, data, retries, url, commandLineArgs, forTemplate }) => {
317
+ const processContextsB = async ({ config, hierarchy, semantics, generators, json, isTest, isProcess, isModule, rebuildingTemplate, isInstance, instance, query, data, retries, url, commandLineArgs, forTemplate, contextIdCounter }) => {
295
318
  // TODO fix this name to contextsPrime
296
319
  const contextsPrime = []
297
320
  const generatedPrime = []
@@ -311,14 +334,13 @@ const processContextsB = async ({ config, hierarchy, semantics, generators, json
311
334
  args.insert = (context) => toDo.unshift(context)
312
335
  let overlap, lastRange
313
336
  config.debugLoops = commandLineArgs && commandLineArgs.debugLoops
314
- let context_id_counter = 0
315
337
  while (toDo.length > 0) {
316
338
  const context = toDo.shift()
317
339
  args.calls.next()
318
340
  let contextPrime = context
319
341
  context.topLevel = true
320
- context_id_counter += 1
321
- context.context_id = context_id_counter
342
+ contextIdCounter += 1
343
+ context.context_id = contextIdCounter
322
344
  try {
323
345
  if (json.has_errors) {
324
346
  throw new Error('There are errors in the logs. Run with the -d flag and grep for Error')
@@ -328,6 +350,9 @@ const processContextsB = async ({ config, hierarchy, semantics, generators, json
328
350
  const semantics = config.getSemantics(json.logs)
329
351
  try {
330
352
  contextPrime = await semantics.apply(args, context)
353
+ // contextPrime.greg = 'yes'
354
+ // console.log("context_id", context.context_id)
355
+ // console.log("semantics.apply", JSON.stringify(contextPrime, null, 2))
331
356
  } catch (e) {
332
357
  if (e.message == 'Maximum call stack size exceeded') {
333
358
  const mostCalled = semantics.getMostCalled()
@@ -421,7 +446,7 @@ const processContextsB = async ({ config, hierarchy, semantics, generators, json
421
446
  throw e
422
447
  }
423
448
  }
424
- return { contextsPrime, generatedPrime, paraphrasesPrime, paraphrasesParenthesizedPrime, generatedParenthesizedPrime, responsesPrime }
449
+ return { contextsPrime, generatedPrime, paraphrasesPrime, paraphrasesParenthesizedPrime, generatedParenthesizedPrime, responsesPrime, updatedContextIdCounter: contextIdCounter }
425
450
  }
426
451
 
427
452
  // instance template loadTemplate
@@ -479,6 +504,9 @@ const loadInstance = async (config, instance) => {
479
504
  args.isModule = true
480
505
  args.isProcess = false
481
506
  }
507
+ if (instance.name == config.testConfig.testModuleName) {
508
+ args.isTesting = true
509
+ }
482
510
  await results.apply(args)
483
511
  } else if (results.isFragment) {
484
512
  } else {
@@ -490,6 +518,9 @@ const loadInstance = async (config, instance) => {
490
518
  args.instance = ''
491
519
  args.isProcess = !config.isModule
492
520
  args.isModule = !!config.isModule
521
+ if (instance.name == config.testConfig.testModuleName) {
522
+ args.isTesting = true
523
+ }
493
524
  await processContextsB(args)
494
525
  if (results.skipSemantics) {
495
526
  config.config.skipSemantics = null
@@ -505,6 +536,7 @@ module.exports = {
505
536
  // listable,
506
537
  setupArgs,
507
538
  processContext,
539
+ run,
508
540
  getObjects,
509
541
  gs,
510
542
  processContextsB,
@@ -0,0 +1,77 @@
1
+ const _ = require('lodash')
2
+ const helpers = require('./helpers')
3
+
4
+ function fragmentInstantiator (args, contexts) {
5
+ return new Object({
6
+ contexts: () => contexts,
7
+ instantiate: async (mappings) => {
8
+ const instantiated = _.cloneDeep(contexts)
9
+ const todo = [{ context: instantiated, path: [] }]
10
+ args = { ...args }
11
+ while (todo.length > 0) {
12
+ const { context, path } = todo.pop()
13
+ args.context = context
14
+ args.path = path
15
+ for (const mapping of mappings) {
16
+ if (await mapping.match(args)) {
17
+ await mapping.apply(args)
18
+ }
19
+ }
20
+ for (const key of Object.keys(context)) {
21
+ // if (['number', 'string', 'boolean'].includes(typeof (context[key]))) {
22
+ if (!helpers.isCompound(context[key])) {
23
+ continue
24
+ }
25
+ if (context[key].instantiated) {
26
+ continue
27
+ }
28
+ todo.push({ context: context[key], path: [...path, key] })
29
+ }
30
+ }
31
+ return instantiated
32
+ }
33
+ })
34
+ }
35
+
36
+ async function fragmentMapperInstantiator(values, modelFrom, modelTo) {
37
+ const paths = {}
38
+ for (const value of values) {
39
+ paths[value] = { value }
40
+ }
41
+
42
+ {
43
+ const fi = fragmentInstantiator({paths}, modelFrom)
44
+ await fi.instantiate([
45
+ {
46
+ match: ({context, path}) => values.includes(context.value),
47
+ apply: ({context, path}) => paths[context.value].from = path
48
+ },
49
+ ])
50
+ }
51
+
52
+ {
53
+ const fi = fragmentInstantiator({paths}, modelTo)
54
+ await fi.instantiate([
55
+ {
56
+ match: ({context, path}) => values.includes(context.value),
57
+ apply: ({context, path}) => paths[context.value].to = path
58
+ },
59
+ ])
60
+ }
61
+ return {
62
+ instantiate: (actualFrom) => {
63
+ const actualTo = structuredClone(modelTo)
64
+ for (const value in paths) {
65
+ const { from, to } = paths[value]
66
+ const actualValue = helpers.getByPath(actualFrom, from, null)
67
+ helpers.setByPath(actualTo, to, actualValue)
68
+ }
69
+ return actualTo
70
+ }
71
+ }
72
+ }
73
+
74
+ module.exports = {
75
+ fragmentInstantiator,
76
+ fragmentMapperInstantiator,
77
+ }
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 ${JSON.stringify(context)}`)
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)
@@ -202,7 +203,7 @@ class Generators {
202
203
  }
203
204
  }
204
205
  if (!generated && generated !== '') {
205
- const widths = [10, 10, 90]
206
+ const widths = Lines.addRemainder([10, 10])
206
207
  const lines = new Lines(widths)
207
208
  lines.setElement(0, 0, 'Generator')
208
209
  const source = `${generator.km}/#${generator.index}`
@@ -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, JSON.stringify(helpers.sortJson(context, { depth: 25 }), null, 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,14 +224,14 @@ 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 ${JSON.stringify(context, null, 2)}.\n${errorMessage}'`
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()
230
231
  throw { error: [message], logs: this.logs }
231
232
  }
232
233
  if (((config || {}).config || {}).debug) {
233
- const widths = [10, 10, 90]
234
+ const widths = Lines.addRemainder([10, 10])
234
235
  const lines = new Lines(widths)
235
236
  lines.setElement(0, 0, 'Generator')
236
237
  if (generator.index > -1 && generator.km) {
@@ -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, JSON.stringify(helpers.sortJson(context, { depth: 25 }), null, 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
@@ -263,7 +264,7 @@ class Generators {
263
264
  }
264
265
  args.calls.pop()
265
266
  if (!applied && ((config || {}).config || {}).debug) {
266
- const widths = [10, 10, 90]
267
+ const widths = Lines.addRemainder([10, 10])
267
268
  const lines = new Lines(widths)
268
269
  lines.setElement(0, 0, 'Generator')
269
270
  lines.setElement(0, 2, 'No generator applied')
@@ -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, JSON.stringify(context, null, 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)
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
@@ -218,7 +218,7 @@ class Semantics {
218
218
  errorMessage = e.toString()
219
219
  }
220
220
 
221
- const widths = [10, 10, 90]
221
+ const widths = Lines.addRemainder([10, 10])
222
222
  const lines = new Lines(widths)
223
223
  lines.setElement(0, 0, 'Semantic')
224
224
  const source = `${semantic.km}/#${semantic.index}`
@@ -248,7 +248,7 @@ class Semantics {
248
248
  args.calls.touch(contextPrime)
249
249
  // this.logs.push(`Semantics: applied ${semantic.toString()}\n to\n ${JSON.stringify(context)}\n the result was ${JSON.stringify(contextPrime)}\n`)
250
250
  if (((config || {}).config || {}).debug) {
251
- const widths = [10, 10, 132]
251
+ const widths = Lines.addRemainder([10, 10])
252
252
  const lines = new Lines(widths)
253
253
  lines.setElement(0, 0, 'Semantic')
254
254
  if (semantic.index > -1 && semantic.km) {
@@ -295,7 +295,7 @@ class Semantics {
295
295
  }
296
296
  args.calls.pop()
297
297
  if (!applied && ((config || {}).config || {}).debug) {
298
- const widths = [10, 10, 90]
298
+ const widths = Lines.addRemainder([10, 10])
299
299
  const lines = new Lines(widths)
300
300
  lines.setElement(0, 0, 'Semantic')
301
301
  lines.setElement(0, 2, 'No semantic applied')