theprogrammablemind_4wp 9.5.1-beta.9 → 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 CHANGED
@@ -12,10 +12,9 @@ const _ = require('lodash')
12
12
  const stringify = require('json-stable-stringify')
13
13
  const Lines = require('./lines')
14
14
  const flattens = require('./src/flatten')
15
- const { appendNoDups, updateQueries, safeNoDups, stableId, where, suggestAssociationsFix, suggestAssociationsFixFromSummaries, validProps } = require('./src/helpers')
15
+ const { sortJson, appendNoDups, updateQueries, safeNoDups, stableId, where, suggestAssociationsFix, suggestAssociationsFixFromSummaries, validProps } = require('./src/helpers')
16
16
  const runtime = require('./runtime')
17
- const sortJson = runtime.sortJson
18
- const debug = require('./src/debug')
17
+ const db = require('./src/debug')
19
18
 
20
19
  const getConfig_getObjectsCheck = (config, testConfig) => {
21
20
  let testConfigName = config.name
@@ -46,33 +45,33 @@ const getSuggestionMessage = (suggestion) => {
46
45
  return `Try adding this to the associations: { context: ${JSON.stringify(getSuggestion(suggestion))}, choose: <indexOfMainElement> },\n If that does not work look at the logs and check when the operators become wrong during an interation. Deduce the change based on the previous iteration and what operator was applied.`
47
46
  }
48
47
 
49
- const getConfig_getContextCheck = (testConfig) => {
50
- return (testConfig.checks && testConfig.checks.context) || []
51
- }
52
-
53
48
  const pickContext = (contextChecks) => (context) => {
54
49
  return project2(context, contextChecks)
55
50
  }
56
51
 
57
52
  const pickObjects = (config, testConfig, getObjects) => {
58
- /*
59
- let testConfigName = config.name
60
- if (testConfig.testModuleName) {
61
- objects = getObjects(config.config.objects)(config.getConfigs()[testConfig.testModuleName].uuid)
62
- testConfigName = testConfig.testModuleName
63
- }
64
- */
65
53
  const checks = getConfig_getObjectsCheck(config, testConfig)
54
+ const contextChecks = config.getContextChecks()
66
55
  const projection = {}
67
56
  for (const km in checks) {
68
57
  const objects = getObjects(km)
69
58
  if (!objects) {
70
59
  throw new Error(`In the checks for ${config.name} the KM ${km} does not exist`)
71
60
  }
72
- if (checks[km] && checks[km].find((check) => check.match && check.apply)) {
73
- projection[km] = project2(objects, checks[km])
61
+
62
+ if (false) {
63
+ if (checks[km] && checks[km].find((check) => check.match && check.apply)) {
64
+ projection[km] = project2(objects, checks[km])
65
+ } else {
66
+ projection[km] = project(objects, checks[km])
67
+ }
74
68
  } else {
75
- projection[km] = project(objects, checks[km])
69
+ const lastIndex = contextChecks.length - 1
70
+ const last = contextChecks[lastIndex]
71
+ contextChecks[lastIndex] = { match: last.match, apply: () => [...new Set([...(checks[km] || []), ...last.apply()])] }
72
+
73
+ // projection[km] = project2(objects, [...checks[km], ...contextChecks])
74
+ projection[km] = project2(objects, contextChecks)
76
75
  }
77
76
  }
78
77
  return projection
@@ -237,9 +236,10 @@ const doWithRetries = async (n, url, queryParams, data) => {
237
236
  body: JSON.stringify(data),
238
237
  timeout: 1000 * 60 * 5, // it does not respect this timeout so that's why I have the retries
239
238
  headers: {
240
- mode: 'no-cors',
239
+ // mode: 'no-cors',
241
240
  'Content-Type': 'application/json'
242
- }
241
+ },
242
+ credentials: 'same-origin',
243
243
  })
244
244
  if (result.ok) {
245
245
  return JSON.parse(JSON.stringify(await result.json()))
@@ -841,7 +841,7 @@ const defaultInnerProcess = (config, errorHandler, responses) => {
841
841
  }
842
842
  console.log(responses.trace)
843
843
 
844
- if (true) {
844
+ if (false) {
845
845
  if (global.beforeObjects) {
846
846
  console.log('objects', runtime.jsonDiff.diffString(global.beforeObjects, config.get('objects')))
847
847
  } else {
@@ -916,7 +916,7 @@ const defaultInnerProcess = (config, errorHandler, responses) => {
916
916
  console.log('')
917
917
  const screen_width = process.stdout.columns
918
918
  // || 0 for when running without a console
919
- const widths = [70, 10, Math.max(80, screen_width - 71 || 0)]
919
+ const widths = Lines.addRemainder([70, 10])
920
920
  const lines = new Lines(widths)
921
921
  lines.setElement(0, 0, '--- The paraphrases are ----------')
922
922
  lines.setElement(0, 2, '--- The response strings are ----------')
@@ -973,7 +973,7 @@ const rebuildTemplate = async ({ config, instance, target, previousResultss, reb
973
973
  if (instance.fragments) {
974
974
  pr = instance.fragments[index]
975
975
  }
976
- return Object.assign({}, toProperties(query), { property: 'fragments', previousResults: pr, skipSemantics: false })
976
+ return Object.assign({}, toProperties(query), { property: 'fragments', previousResults: pr, skipSemantics: true })
977
977
  }
978
978
 
979
979
  const looper = async (configs) => {
@@ -1327,13 +1327,32 @@ const knowledgeModuleImpl = async ({
1327
1327
  parser.add_argument('-cl', '--checkForLoop', { nargs: '?', help: 'Check for loops in the priorities, Optional argument is list of operator keys to consider. For example [["banana", 0], ["food", 1]]' })
1328
1328
  parser.add_argument('-r', '--reset', { action: 'store_true', help: 'Get the server to bypass the cache and rebuild everything' })
1329
1329
  parser.add_argument('-q', '--query', { help: 'Run the specified query' })
1330
+ parser.add_argument('-f', '--filter', { help: 'for -pd only the data for the knowledge modules that start with this string will be shown' })
1330
1331
  parser.add_argument('-ip ', '--server', { help: 'Server to run against' })
1331
1332
  parser.add_argument('-qp ', '--queryParams', { help: 'Query params for the server call' })
1332
1333
  parser.add_argument('-dt', '--deleteTest', { help: 'Delete the specified query from the tests file.' })
1333
1334
  parser.add_argument('--parenthesized', { action: 'store_true', help: 'Show the generated phrases with parenthesis.' })
1334
1335
  parser.add_argument('-c', '--clean', { help: 'Remove data from the test files. a === association' })
1335
1336
  parser.add_argument('-od', '--objectDiff', { action: 'store_true', help: 'When showing the objects use a colour diff' })
1336
- parser.add_argument('-p', '--print', { help: 'Print the specified elements c === config, w === words, b === bridges, o === operators d === objects (d for data), h === hierarchy, ha === hierarchy ancestors g === generators, s === semantics, l === load t=tests ordering p === priorities a === associations j === JSON sent to server. for example --print wb' })
1337
+ parser.add_argument('-p', '--print', { help:
1338
+ `Print the specified elements
1339
+ a === associations
1340
+ b === bridges,
1341
+ c === config,
1342
+ cc === test checks,
1343
+ d === objects (d for data),
1344
+ g === generators,
1345
+ h === hierarchy,
1346
+ ha === hierarchy ancestors,
1347
+ j === JSON sent to server,
1348
+ l === load ordering,
1349
+ o === operators,
1350
+ p === priorities,
1351
+ s === semantics,
1352
+ t === tests ordering,
1353
+ w === words,
1354
+ for example --print wb' })
1355
+ ` })
1337
1356
  parser.add_argument('-s', '--save', { action: 'store_true', help: 'When running with the --query flag this will save the current run to the test file. When running without the --query flag all tests will be run and resaved.' })
1338
1357
  parser.add_argument('-fr', '--failRebuild', { action: 'store_true', help: 'If a rebuild is required fail out.' })
1339
1358
  parser.add_argument('-sd', '--saveDeveloper', { action: 'store_true', help: 'Same as -s but the query will not show up in the info command.' })
@@ -1494,6 +1513,12 @@ const knowledgeModuleImpl = async ({
1494
1513
  counter += 1
1495
1514
  }
1496
1515
  }
1516
+ if (hasArg('cc')) {
1517
+ for (const cc of config.getContextChecks()) {
1518
+ const printable = { ...cc, match: cc.match.toString(), apply: cc.apply.toString() }
1519
+ console.log(JSON.stringify(printable, null, 2))
1520
+ }
1521
+ }
1497
1522
  if (hasArg('c')) {
1498
1523
  const { data } = setupProcessB({ config })
1499
1524
  console.log('Config as sent to server')
@@ -1557,8 +1582,19 @@ const knowledgeModuleImpl = async ({
1557
1582
  }
1558
1583
 
1559
1584
  if (hasArg('d')) {
1560
- console.log('objects (data) ================')
1561
- console.log(JSON.stringify(config.config.objects, null, 2))
1585
+ if (args.filter) {
1586
+ console.log(`objects (data) filtered by ${args.filter} ================`)
1587
+ const projection = { namespaced: {} }
1588
+ for (const key of Object.keys(config.config.objects.namespaced)) {
1589
+ if (key.startsWith(args.filter)) {
1590
+ projection.namespaced[key] = config.config.objects.namespaced[key]
1591
+ }
1592
+ }
1593
+ console.log(JSON.stringify(projection, null, 2))
1594
+ } else {
1595
+ console.log('objects (data) ================')
1596
+ console.log(JSON.stringify(config.config.objects, null, 2))
1597
+ }
1562
1598
  }
1563
1599
 
1564
1600
  if (hasArg('p')) {
@@ -2011,9 +2047,15 @@ const ensureTestFile = (module, name, type) => {
2011
2047
  }
2012
2048
 
2013
2049
  const knowledgeModule = async (...args) => {
2014
- await knowledgeModuleImpl(...args).catch((e) => {
2050
+ await knowledgeModuleImpl(...args).catch(async (e) => {
2015
2051
  console.error(e)
2016
- process.exit(-1)
2052
+ function sleep(ms) {
2053
+ return new Promise((resolve) => {
2054
+ setTimeout(resolve, ms);
2055
+ });
2056
+ }
2057
+ await sleep(1) // get the stderr to flush
2058
+ await process.exit(-1); // tiny trick: empty write forces flush of console.error buffer
2017
2059
  })
2018
2060
  }
2019
2061
 
@@ -2038,5 +2080,6 @@ module.exports = {
2038
2080
  gs,
2039
2081
  flattens,
2040
2082
  writeTest,
2041
- getConfigForTest
2083
+ getConfigForTest,
2084
+ debug: db,
2042
2085
  }
package/lines.js CHANGED
@@ -5,7 +5,7 @@ class Lines {
5
5
  this.rows = []
6
6
  }
7
7
 
8
- static SCREEN_WIDTH = 132
8
+ static SCREEN_WIDTH = 164
9
9
 
10
10
  static addRemainder(widths) {
11
11
  const sum = widths.reduce((a, b) => a + b)
package/package.json CHANGED
@@ -70,9 +70,8 @@
70
70
  "node-fetch": "^2.6.1",
71
71
  "readline": "^1.3.0",
72
72
  "scriptjs": "^2.5.9",
73
- "sort-json": "^2.0.0",
74
73
  "uuid": "^8.3.2"
75
74
  },
76
- "version": "9.5.1-beta.9",
75
+ "version": "9.6.0",
77
76
  "license": "UNLICENSED"
78
77
  }
package/runtime.js CHANGED
@@ -12,7 +12,6 @@ module.exports = {
12
12
  ArgumentParser: 'Should not be called in the browser',
13
13
  readline: 'Should not be called in the browser',
14
14
  jsonDiff: 'Should not be called in the browser',
15
- sortJson: 'Should not be called in the browser',
16
15
  util: 'Should not be called in the browser',
17
16
  performance: 'Should not be called in the browser'
18
17
  }
package/src/config.js CHANGED
@@ -10,7 +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 } = require('./fragments')
13
+ const { fragmentInstantiator, fragmentMapperInstantiator } = require('./fragments')
14
14
 
15
15
  const debugBreak = () => {
16
16
  // debugger
@@ -21,8 +21,6 @@ const bags = [
21
21
  'semantics'
22
22
  ]
23
23
 
24
- global.GORDO = true
25
-
26
24
  const indent = (string, indent) => {
27
25
  return string.replace(/^/gm, ' '.repeat(indent))
28
26
  }
@@ -337,6 +335,8 @@ const handleBridgeProps = (config, bridge, { addFirst, uuid } = {}) => {
337
335
  config.addPriority({ context: [[bridge.id, bridge.level], after], choose: [0] })
338
336
  }
339
337
  }
338
+
339
+ // done in setTestConfig
340
340
  if (bridge.check) {
341
341
  if (!config.testConfig) {
342
342
  config.testConfig = {}
@@ -347,12 +347,15 @@ const handleBridgeProps = (config, bridge, { addFirst, uuid } = {}) => {
347
347
  if (!config.testConfig?.checks?.context) {
348
348
  config.testConfig.checks.context = []
349
349
  }
350
- config.testConfig.checks.context.push({
350
+ config.contextChecksFromBridges.push({
351
+ 'bridge.id': bridge.id,
352
+ 'bridge.check': bridge.check,
351
353
  match: ({context}) => context.marker == bridge.id,
352
354
  exported: true,
353
355
  apply: ({context}) => bridge.check,
354
356
  })
355
357
  }
358
+
356
359
  if (bridge.after) {
357
360
  for (let before of bridge.after) {
358
361
  if (typeof before === 'string') {
@@ -402,11 +405,15 @@ const handleBridgeProps = (config, bridge, { addFirst, uuid } = {}) => {
402
405
  if (bridge.generatorp) {
403
406
  const match = bridge.generatorp.match || (() => true)
404
407
  const apply = typeof bridge.generatorp === 'function' ? bridge.generatorp : bridge.generatorp.apply || bridge.generatorp
405
- const level = bridge.generatorp.level >= 0 ? bridge.generatorp.level : bridge.level + 1
408
+ let level = bridge.generatorp.level >= 0 ? bridge.generatorp.level : bridge.level + 1
409
+ if (!bridge.bridge) {
410
+ level = 0
411
+ }
406
412
 
407
413
  const generator = {
408
414
  where: bridge.generatorp.where || bridge.where || helpers.where(4),
409
- match: async (args) => bridge.id === args.context.marker && args.context.level === level && args.context.paraphrase && await match(args),
415
+ // match: async (args) => bridge.id === args.context.marker && args.context.level === level && args.context.paraphrase && await match(args),
416
+ match: async (args) => args.isA(args.context.marker, bridge.id) && args.context.level === level && args.context.paraphrase && await match(args),
410
417
  apply: (args) => apply(args),
411
418
  applyWrapped: apply,
412
419
  property: 'generatorp'
@@ -423,7 +430,8 @@ const handleBridgeProps = (config, bridge, { addFirst, uuid } = {}) => {
423
430
  const level = bridge.generatorr.level >= 0 ? bridge.generatorr.level : bridge.level + 1
424
431
  const generator = {
425
432
  where: bridge.generatorr.where || bridge.where || helpers.where(4),
426
- match: async (args) => bridge.id === args.context.marker && args.context.level === level && !args.context.paraphrase && (args.context.response || args.context.isResponse) && await match(args),
433
+ // match: async (args) => bridge.id === args.context.marker && args.context.level === level && !args.context.paraphrase && (args.context.response || args.context.isResponse) && await match(args),
434
+ 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),
427
435
  apply: (args) => apply(args),
428
436
  applyWrapped: apply,
429
437
  property: 'generatorr'
@@ -510,8 +518,43 @@ const handleCalculatedProps = (baseConfig, moreConfig, { addFirst, uuid } = {})
510
518
  if (moreConfig.bridges) {
511
519
  moreConfig.bridges = moreConfig.bridges.map((bridge) => {
512
520
  bridge = { ...bridge }
513
- const valid = ['after', 'conditional', 'associations', 'before', 'bridge', 'check', 'disabled', 'scope', 'skipable', 'return_type_selector', 'evaluator', 'evaluators', 'generatorp', 'generatorr', 'generatorpr', 'generators', 'operator', 'id', 'convolution', 'inverted', 'isA', 'children', 'parents',
514
- 'level', 'optional', 'selector', 'separators', 'semantic', 'semantics', 'words', /Bridge$/, 'localHierarchy', 'levelSpecificHierarchy', 'where', 'uuid']
521
+ const valid = [
522
+ 'after',
523
+ 'associations',
524
+ 'before',
525
+ 'bridge',
526
+ 'check',
527
+ 'children',
528
+ 'conditional',
529
+ 'convolution',
530
+ 'disabled',
531
+ 'enhanced_associations',
532
+ 'evaluator',
533
+ 'evaluators',
534
+ 'generatorp',
535
+ 'generatorpr',
536
+ 'generatorr',
537
+ 'generators',
538
+ 'id',
539
+ 'inverted',
540
+ 'isA',
541
+ 'level',
542
+ 'levelSpecificHierarchy',
543
+ 'localHierarchy',
544
+ 'operator',
545
+ 'optional',
546
+ 'parents',
547
+ 'return_type_selector',
548
+ 'scope',
549
+ 'selector',
550
+ 'semantic',
551
+ 'semantics',
552
+ 'separators',
553
+ 'skipable',
554
+ 'uuid',
555
+ 'where',
556
+ 'words', /Bridge$/,
557
+ ]
515
558
  helpers.validProps(valid, bridge, 'bridge')
516
559
  handleBridgeProps(baseConfig, bridge, { addFirst, uuid })
517
560
  return bridge
@@ -674,7 +717,7 @@ function setWordsUUIDs (words, uuid) {
674
717
  for (const key in literals) {
675
718
  literals[key] = literals[key].map((o) => Object.assign(o, { uuid }))
676
719
  }
677
- const patterns = words.patterns
720
+ const patterns = words.patterns || []
678
721
  for (const pattern of patterns) {
679
722
  pattern.defs.map((def) => Object.assign(def, { uuid }))
680
723
  }
@@ -903,6 +946,10 @@ class Config {
903
946
  return config_toServer(config)
904
947
  }
905
948
 
949
+ async run(handler) {
950
+ return configHelpers.run(this, handler)
951
+ }
952
+
906
953
  async fixtures () {
907
954
  if (this.testConfig?.fixtures) {
908
955
  const args = {}
@@ -936,6 +983,7 @@ class Config {
936
983
 
937
984
  getPseudoConfig (uuid, config) {
938
985
  return {
986
+ pseudo: true,
939
987
  description: 'this is a pseudo config that has limited functionality due to being available in the initializer and fixtures function context',
940
988
  addAssociation: (...args) => this.addAssociation(...args),
941
989
  addAssociations: (...args) => this.addAssociations(...args),
@@ -949,6 +997,22 @@ class Config {
949
997
  removeSemantic: (...args) => this.removeSemantic(...args, uuid, config.name),
950
998
  addWord: (...args) => this.addWord(...args, uuid),
951
999
  addPattern: (...args) => this.addPattern(...args, uuid),
1000
+ updateBridge: (...args) => this.updateBridge(...args),
1001
+ processContext: (...args) => this.processContext(...args),
1002
+
1003
+ semantics: () => this.config.semantics,
1004
+ getConfigs: () => this.configs,
1005
+ getTestConfig: () => this.getTestConfig(),
1006
+ getTests: () => this.getTests(),
1007
+ getName: () => this.getName(),
1008
+ getDescription: () => this.getDescription(),
1009
+ getDebug: () => this.getDebug(),
1010
+ getDebugLoops: () => this.getDebugLoops(),
1011
+ getMaxDepth: () => this.getMaxDepth(),
1012
+ getAPIs: (...args) => this.getAPIs(...args),
1013
+ getAPI: (...args) => this.getAPI(...args),
1014
+ getUUID: (...args) => this.getUUID(...args),
1015
+ nsToString: (...args) => this.nsToString(...args),
952
1016
 
953
1017
  getHierarchy: (...args) => this.config.hierarchy,
954
1018
  getBridges: (...args) => this.config.bridges,
@@ -958,10 +1022,53 @@ class Config {
958
1022
  fragment: (...args) => this.fragment(...args),
959
1023
  server: (...args) => this.server(...args),
960
1024
  exists: (...args) => this.exists(...args),
961
- addAPI: (...args) => this.addAPI(...args)
1025
+ addAPI: (...args) => this.addAPI(...args),
1026
+
1027
+ getParenthesized: () => this.getParenthesized(),
1028
+ setParenthesized: (...args) => this.setParenthesized(...args),
962
1029
  }
963
1030
  }
964
1031
 
1032
+ getUUID() {
1033
+ return this.uuid
1034
+ }
1035
+
1036
+ getTestConfig() {
1037
+ return this.testConfig
1038
+ }
1039
+
1040
+ getMaxDepth() {
1041
+ return this.config.maxDepth
1042
+ }
1043
+
1044
+ getDebug() {
1045
+ return this.config.debug
1046
+ }
1047
+
1048
+ getDebugLoops() {
1049
+ return this.config.debugLoops
1050
+ }
1051
+
1052
+ getName() {
1053
+ return this.name
1054
+ }
1055
+
1056
+ getDescription() {
1057
+ return this.description
1058
+ }
1059
+
1060
+ getTests() {
1061
+ return this.tests
1062
+ }
1063
+
1064
+ getParenthesized(value) {
1065
+ return this.parenthesized
1066
+ }
1067
+
1068
+ setParenthesized(value) {
1069
+ this.parenthesized = value
1070
+ }
1071
+
965
1072
  inDevelopmentMode (call) {
966
1073
  config.developmentModeOn += 1
967
1074
  try {
@@ -978,7 +1085,27 @@ class Config {
978
1085
  }
979
1086
 
980
1087
  setTestConfig (testConfig) {
981
- this.testConfig = testConfig
1088
+ this.testConfig = { ...testConfig }
1089
+ if (!this.testConfig.checks) {
1090
+ this.testConfig.checks = {}
1091
+ }
1092
+ if (!this.testConfig?.checks?.context) {
1093
+ this.testConfig.checks.context = []
1094
+ }
1095
+ this.testConfig.checks.context = this.contextChecksFromBridges.concat(this.testConfig.checks.context)
1096
+ /*
1097
+ const currentTestConfig = testConfig // add bridge has added check.context's
1098
+
1099
+ if (!this.testConfig.checks) {
1100
+ debugger
1101
+ // this.testConfig.checks =
1102
+ }
1103
+
1104
+ // set during bridge setup
1105
+ if (currentTestConfig.checks?.context) {
1106
+ this.testConfig.checks.context = currentTestConfig.checks.context.concat(this.testConfig.checks.context)
1107
+ }
1108
+ */
982
1109
  }
983
1110
 
984
1111
  getTestConfig () {
@@ -1108,26 +1235,52 @@ class Config {
1108
1235
  return instance
1109
1236
  }
1110
1237
 
1111
- fragment (args, query) {
1238
+ getFragment(query) {
1112
1239
  for (const instance of (this.instances || [])) {
1113
1240
  for (const fragment of (instance.fragments || [])) {
1114
1241
  if (fragment.query === query) {
1115
- return fragmentInstantiator(args, fragment.contexts)
1242
+ return fragment
1116
1243
  }
1117
1244
  }
1118
1245
  for (const fragment of (instance.resultss || [])) {
1119
1246
  if (fragment.isFragment && fragment.query === query) {
1120
- return fragmentInstantiator(args, fragment.contexts)
1247
+ return fragment
1121
1248
  }
1122
1249
  }
1123
1250
  for (const fragment of (this.fragmentsBeingBuilt || [])) {
1124
1251
  if (fragment.query === query) {
1125
- return fragmentInstantiator(args, fragment.contexts)
1252
+ return fragment
1126
1253
  }
1127
1254
  }
1128
1255
  }
1129
1256
  }
1130
1257
 
1258
+ // mappings are optional
1259
+ async fragment (args, query, mappings) {
1260
+ const fragment = this.getFragment(query)
1261
+ if (fragment) {
1262
+ let fi = fragmentInstantiator(args, fragment.contexts)
1263
+ if (mappings) {
1264
+ fi = await fi
1265
+ return fi.instantiate([{
1266
+ match: ({context}) => !!mappings[context.value], // is the value in the mappings
1267
+ apply: ({context}) => Object.assign(context, mappings[context.value]),
1268
+ }])
1269
+ } else {
1270
+ return fi
1271
+ }
1272
+ }
1273
+ }
1274
+
1275
+ fragmentMapper (args, values, fromModelQuery, toModelQuery) {
1276
+ const fromModelFragment = this.getFragment(fromModelQuery)
1277
+ console.dir(fromModelFragment)
1278
+ const toModelFragment = this.getFragment(toModelQuery)
1279
+ console.dir(toModelFragment)
1280
+ const mapper = fragmentMapperInstantiator(values, fromModelFragment.contexts, toModelFragment.contexts)
1281
+ return mapper
1282
+ }
1283
+
1131
1284
  // { rebuild: false, isModule: false }
1132
1285
  needsRebuild (template, instance, options) {
1133
1286
  if (options.rebuild) {
@@ -1486,6 +1639,11 @@ class Config {
1486
1639
  }
1487
1640
  }
1488
1641
 
1642
+ updateBridge(id, updater) {
1643
+ const bridge = this.config.bridges.find((b) => b.id === id)
1644
+ updater({ config: this, bridge })
1645
+ }
1646
+
1489
1647
  addBridge (bridge, uuid) {
1490
1648
  if (!this.config.bridges) {
1491
1649
  this.config.bridges = []
@@ -1670,7 +1828,9 @@ class Config {
1670
1828
 
1671
1829
  getAPIs (uuid) {
1672
1830
  let config
1673
- if (this._uuid === uuid) {
1831
+ if (!uuid) {
1832
+ config = this
1833
+ } else if (this._uuid === uuid) {
1674
1834
  config = this
1675
1835
  } else {
1676
1836
  for (const km of this.configs) {
@@ -1685,6 +1845,14 @@ class Config {
1685
1845
  }
1686
1846
  }
1687
1847
 
1848
+ /*
1849
+ getAPIs (config) {
1850
+ if (config && config._api && config._api.multiApi) {
1851
+ return config._api.apis
1852
+ }
1853
+ }
1854
+ */
1855
+
1688
1856
  getServer () {
1689
1857
  return this._server
1690
1858
  }
@@ -1787,7 +1955,7 @@ class Config {
1787
1955
 
1788
1956
  // set the args in the api's
1789
1957
  setArgs (args) {
1790
- const setArgs = (config) => {
1958
+ const setConfigArgs = (config) => {
1791
1959
  if (!config._api) {
1792
1960
  return
1793
1961
  }
@@ -1800,10 +1968,10 @@ class Config {
1800
1968
  }
1801
1969
  }
1802
1970
 
1803
- setArgs(this)
1971
+ setConfigArgs(this)
1804
1972
  for (const config of this.configs) {
1805
1973
  if (config.config instanceof Config) {
1806
- setArgs(config.config)
1974
+ setConfigArgs(config.config)
1807
1975
  }
1808
1976
  }
1809
1977
  }
@@ -1832,6 +2000,16 @@ class Config {
1832
2000
  }
1833
2001
  }
1834
2002
 
2003
+ getObjects () {
2004
+ const configs = {}
2005
+ const ns = this.config.objects.namespaced
2006
+ configs[this.name] = this
2007
+ for (const config of this.configs) {
2008
+ configs[config._name] = ns[config._uuid]
2009
+ }
2010
+ return configs
2011
+ }
2012
+
1835
2013
  getConfig (name) {
1836
2014
  if (this.name === name) {
1837
2015
  return this
@@ -1879,14 +2057,6 @@ class Config {
1879
2057
  } else if (this.scope == 'development') {
1880
2058
  new_result = !(element.scope === 'testing')
1881
2059
  }
1882
- /*
1883
- if (global.GORDO && old_result !== new_result) {
1884
- global.GORDO = false
1885
- console.log("THERE WAS A DIFFERENCE ------------------------------------------------")
1886
- debugger // greg23old
1887
- }
1888
- */
1889
- // return old_result
1890
2060
  return new_result
1891
2061
  }
1892
2062
 
@@ -1992,6 +2162,8 @@ class Config {
1992
2162
  config.priorities = config.priorities || []
1993
2163
  }
1994
2164
 
2165
+ this.contextChecksFromBridges = []
2166
+
1995
2167
  this._enable = []
1996
2168
  this._apiKMs = apiKMs
1997
2169
 
@@ -2051,7 +2223,7 @@ class Config {
2051
2223
 
2052
2224
  // set the default server so stuff just works
2053
2225
  // this.server('https://184.67.27.82:3000', '6804954f-e56d-471f-bbb8-08e3c54d9321')
2054
- this.server('https://thinktelligence.com:3000', '6804954f-e56d-471f-bbb8-08e3c54d9321')
2226
+ this.server('https://thinktelligence.com/entodicton', '6804954f-e56d-471f-bbb8-08e3c54d9321')
2055
2227
 
2056
2228
  this.defaultConfig()
2057
2229
  this.initializerFn = ({ currentConfig }) => {
@@ -2318,6 +2490,7 @@ class Config {
2318
2490
  // update uuid here set the uuid in the objects and add error checking
2319
2491
  cp.initializerFn = this.initializerFn
2320
2492
  cp.terminatorFn = this.terminatorFn
2493
+ cp.contextChecksFromBridges = this.contextChecksFromBridges
2321
2494
  cp._apiKMs = this._apiKMs
2322
2495
  cp._apiConstructor = this._apiConstructor
2323
2496
  if (cp._apiConstructor) {
@@ -2678,19 +2851,37 @@ class Config {
2678
2851
 
2679
2852
  getContextChecks() {
2680
2853
  const allChecks = []
2681
- for (const name of this.loadOrdering) {
2682
- const checks = this.kms[name].testConfig?.checks?.context || []
2683
- for (const check of checks) {
2684
- if (check.exported) {
2685
- allChecks.push(check)
2854
+ let defaults = () => []
2855
+ if (this.loadOrdering) {
2856
+ for (const name of this.loadOrdering) {
2857
+ const checks = this.kms[name].testConfig?.checks?.context || []
2858
+ const oldDefaults = defaults
2859
+ for (const check of checks) {
2860
+ if (!check.match) {
2861
+ const oldDefaults = defaults
2862
+ defaults = () => helpers.safeNoDups([...check.apply(), ...oldDefaults()])
2863
+ continue
2864
+ }
2865
+ if (check.exported) {
2866
+ allChecks.push(check)
2867
+ }
2686
2868
  }
2687
2869
  }
2688
2870
  }
2689
2871
  for (const check of this.testConfig?.checks?.context || []) {
2872
+ if (!check.match) {
2873
+ const oldDefaults = defaults
2874
+ defaults = () => [...new Set([...check.apply(), ...oldDefaults()])]
2875
+ continue
2876
+ }
2690
2877
  if (!check.exported) {
2691
2878
  allChecks.push(check)
2692
2879
  }
2693
2880
  }
2881
+ allChecks.push({
2882
+ match: () => true,
2883
+ apply: defaults
2884
+ })
2694
2885
  return allChecks
2695
2886
  }
2696
2887
 
@@ -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.hierarchy
100
+ if (!hierarchy && config.getHierarchy) {
101
+ hierarchy = config.getHierarchy()
102
102
  }
103
103
  // callId
104
- args.calls = new InitCalls(args.isInstance ? `${args.isInstance}#${config.name}` : config.name)
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
- args.fragments = (query) => {
122
- return config.fragment(args, query)
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,23 +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
 
136
- if (config.testConfig?.testModuleName) {
137
- args.testModuleName = config.testConfig.testModuleName
140
+ if (config.getTestConfig()?.testModuleName) {
141
+ args.testModuleName = config.getTestConfig().testModuleName
138
142
  }
139
143
  args.addAssumedScoped = (args, assumed) => {
140
144
  const addAssumed = (args, ...moreAssumed) => {
141
145
  return { ...args, assumed: Object.assign({}, assumed, (args.assumed || {}), ...moreAssumed) }
142
146
  }
143
147
 
144
- args.s = (c) => config.getSemantics(logs).apply(args, c)
145
- args.g = (c, a = {}) => {
146
- return config.getGenerators(logs).apply(addAssumed(args, a), c, a)
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)
147
155
  }
148
- args.gp = (c, a = {}) => {
149
- return config.getGenerators(logs).apply(addAssumed(args, a, { paraphrase: true, isResponse: false, response: false }), c, { paraphrase: true, isResponse: false, response: false })
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)
150
162
  }
151
- args.gr = (c, a = {}) => {
152
- return config.getGenerators(logs).apply(addAssumed(args, a, { paraphrase: false, isResponse: true }), { ...c, paraphrase: false, isResponse: true })
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)
153
169
  }
154
170
  args.e = (c) => {
155
171
  if (!c) {
@@ -182,14 +198,8 @@ const setupArgs = (args, config, logs, hierarchy, uuidForScoping) => {
182
198
  }
183
199
  config.getAddedArgs(args)
184
200
 
185
- Object.assign(args, args.getUUIDScoped(uuidForScoping || config.uuid))
201
+ Object.assign(args, args.getUUIDScoped(uuidForScoping || config.getUUID()))
186
202
  args.apis = args.apis || ((name) => config.getConfig(name).api)
187
- /*
188
- if (uuidForScoping) {
189
- Object.assign(args, args.getUUIDScoped(uuidForScoping))
190
- }
191
- */
192
- // sets args for all the API. that make a copy so the args must be fully setup by here except for scoped
193
203
  config.setArgs(args)
194
204
  }
195
205
 
@@ -202,6 +212,22 @@ const getObjects = (objects) => {
202
212
  }
203
213
  }
204
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
+
205
231
  const processContext = async (context, { objects = {}, config, logs = [] }) => {
206
232
  const generators = config.getGenerators(logs)
207
233
  const semantics = config.getSemantics(logs)
@@ -373,21 +399,21 @@ const processContextsB = async ({ config, hierarchy, semantics, generators, json
373
399
  const generated = contextPrime.isResponse ? await config.getGenerators(json.logs).apply({ ...args, assumed }, contextPrime, assumed) : ''
374
400
  let generatedParenthesized = []
375
401
  if (generateParenthesized) {
376
- config.parenthesized = true
402
+ config.setParenthesized(true)
377
403
  generatedParenthesized = contextPrime.isResponse ? await config.getGenerators(json.logs).apply({ ...args, assumed }, contextPrime, assumed) : ''
378
- config.parenthesized = false
404
+ config.setParenthesized(false)
379
405
  }
380
406
  // assumed = { paraphrase: true, response: false };
381
407
  assumed = { paraphrase: true, isResponse: false, response: false }
382
408
  if (generateParenthesized) {
383
- config.parenthesized = false
409
+ config.setParenthesized(false)
384
410
  }
385
411
  const paraphrases = await config.getGenerators(json.logs).apply({ ...args, assumed }, contextPrime, assumed)
386
412
  let paraphrasesParenthesized = []
387
413
  if (generateParenthesized) {
388
- config.parenthesized = true
414
+ config.setParenthesized(true)
389
415
  paraphrasesParenthesized = await config.getGenerators(json.logs).apply({ ...args, assumed }, contextPrime, assumed)
390
- config.parenthesized = false
416
+ config.setParenthesized(false)
391
417
  }
392
418
  contextsPrime.push(contextPrime)
393
419
  generatedPrime.push(generated)
@@ -434,11 +460,6 @@ const loadInstance = async (config, instance) => {
434
460
  const transitoryMode = global.transitoryMode
435
461
  global.transitoryMode = false
436
462
 
437
- /*
438
- if (config.name == 'people' && instance.name == 'people') {
439
- debugger
440
- }
441
- */
442
463
  const rl = instance.resultss.length
443
464
  if (rl > 0) {
444
465
  config.addAssociations(instance.resultss[instance.resultss.length - 1].rtf_associations || [])
@@ -516,6 +537,7 @@ module.exports = {
516
537
  // listable,
517
538
  setupArgs,
518
539
  processContext,
540
+ run,
519
541
  getObjects,
520
542
  gs,
521
543
  processContextsB,
package/src/fragments.js CHANGED
@@ -1,85 +1,42 @@
1
1
  const _ = require('lodash')
2
2
  const helpers = require('./helpers')
3
3
 
4
- function fragmentInstantiator(args, contexts) {
5
- return new Object({
6
- contexts: () => contexts,
7
-
4
+ function fragmentInstantiator (args, contexts) {
5
+ return new Object({
6
+ contexts: () => {
7
+ return _.cloneDeep(contexts)
8
+ },
8
9
  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
-
10
+ const instantiated = _.cloneDeep(contexts)
11
+ const todo = [{ context: instantiated, path: [] }]
12
+ args = { ...args }
20
13
  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
14
+ const { context, path } = todo.pop()
15
+ args.context = context
16
+ args.path = path
50
17
  for (const mapping of mappings) {
51
18
  if (await mapping.match(args)) {
52
- await mapping.apply(args);
19
+ await mapping.apply(args)
53
20
  }
54
21
  }
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
- }
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
76
26
  }
27
+ if (context[key].instantiated) {
28
+ continue
29
+ }
30
+ todo.push({ context: context[key], path: [...path, key] })
77
31
  }
78
32
  }
79
-
80
- return root;
33
+ if (contexts.length == 1 && instantiated.length == 1) {
34
+ return instantiated[0]
35
+ } else {
36
+ return instantiated
37
+ }
81
38
  }
82
- });
39
+ })
83
40
  }
84
41
 
85
42
  async function fragmentMapperInstantiator(values, modelFrom, modelTo) {
package/src/generators.js CHANGED
@@ -1,4 +1,3 @@
1
- const { stringify } = require('flatted');
2
1
  const { args: contextArgs, normalizeGenerator } = require('./helpers')
3
2
  const Lines = require('../lines')
4
3
  const helpers = require('./helpers')
@@ -27,11 +26,13 @@ class Generator {
27
26
  }
28
27
  }
29
28
 
29
+ /*
30
30
  getAPIs (config) {
31
31
  if (config && config._api && config._api.multiApi) {
32
32
  return config._api.apis
33
33
  }
34
34
  }
35
+ */
35
36
 
36
37
  toLabel () {
37
38
  const where = this.where ? `where: "${this.where}"` : ''
@@ -60,7 +61,7 @@ class Generator {
60
61
  context,
61
62
  callId,
62
63
  api: this.getAPI(config),
63
- apis: this.getAPIs(config)
64
+ apis: config.getAPIs(),
64
65
  }
65
66
  const args = Object.assign({}, baseArgs, moreArgs, (baseArgs.getUUIDScoped || (() => { return {} }))(this.uuid))
66
67
  // return this.match(args)
@@ -79,11 +80,11 @@ class Generator {
79
80
  if (!log) {
80
81
  throw new Error('generators.apply argument log is required')
81
82
  }
82
- if (baseArgs.call && config && baseArgs.calls.stack.length > config.maxDepth) {
83
- throw new Error(`Max depth of ${config.maxDepth} 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
+ 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`)
84
85
  }
85
86
 
86
- if (config && config.debugLoops) {
87
+ if (config && config.getDebugLoops()) {
87
88
  console.log('apply', this.toLabel())
88
89
  }
89
90
  // this.getAPI(config)
@@ -114,7 +115,7 @@ class Generator {
114
115
  config,
115
116
  response,
116
117
  api: this.getAPI(config),
117
- apis: this.getAPIs(config)
118
+ apis: config.getAPIs()
118
119
  }
119
120
  const args = Object.assign({}, baseArgs, moreArgs, (baseArgs.getUUIDScoped || (() => { return {} }))(this.uuid))
120
121
  if (this.property === 'generatorp') {
@@ -186,7 +187,7 @@ class Generators {
186
187
  // this.logs.push(`Generators: applied ${generator.toString()}\n to\n ${stringify(context)}`)
187
188
  let errorMessage = 'The apply function did not return a value'
188
189
  try {
189
- generated = await generator.apply(args, objects, context, hierarchy, config, response, log)
190
+ generated = await generator.apply(args, objects, context, hierarchy, config, response, log, options)
190
191
  } catch (e) {
191
192
  // the next if handle this
192
193
  generated = null
@@ -213,7 +214,7 @@ class Generators {
213
214
  lines.newRow()
214
215
  lines.setElement(0, 1, 'TO')
215
216
  lines.setElement(0, 2, `context_id: ${context.context_id}`)
216
- lines.setElement(1, 2, stringify(helpers.sortJson(context, { depth: 10 }), null, 2))
217
+ lines.setElement(1, 2, JSON.stringify(helpers.sortJson(context, { depth: 10 }), null, 2))
217
218
  lines.newRow()
218
219
  lines.setElement(0, 1, 'STACK')
219
220
  lines.setElement(0, 2, stack)
@@ -224,13 +225,13 @@ class Generators {
224
225
  lines.setElement(0, 1, 'ERROR')
225
226
  lines.setElement(0, 2, errorMessage)
226
227
  this.logs.push(lines.toString())
227
- const message = `ERROR while applying (${source}) ${generator.toLabel()}\n to\n ${stringify(context, null, 2)}.\n${errorMessage}'`
228
+ const message = `ERROR while applying (${source}) ${generator.toLabel()}\n to\n ${JSON.stringify(context, null, 2)}.\n${errorMessage}'`
228
229
  // this.logs.push(message)
229
230
  // return [message]
230
231
  args.calls.pop()
231
232
  throw { error: [message], logs: this.logs }
232
233
  }
233
- if (((config || {}).config || {}).debug) {
234
+ if (config.getDebug()) {
234
235
  const widths = Lines.addRemainder([10, 10])
235
236
  const lines = new Lines(widths)
236
237
  lines.setElement(0, 0, 'Generator')
@@ -255,7 +256,7 @@ class Generators {
255
256
  lines.newRow()
256
257
  lines.setElement(0, 1, 'TO')
257
258
  lines.setElement(0, 2, `context_id: ${context.context_id}`)
258
- lines.setElement(1, 2, stringify(helpers.sortJson(context, { depth: 25 }), null, 2))
259
+ lines.setElement(1, 2, JSON.stringify(helpers.sortJson(context, { depth: 25 }), null, 2))
259
260
  this.logs.push(lines.toString())
260
261
  }
261
262
  applied = true
@@ -263,7 +264,7 @@ class Generators {
263
264
  }
264
265
  }
265
266
  args.calls.pop()
266
- if (!applied && ((config || {}).config || {}).debug) {
267
+ if (!applied && config.getDebug()) {
267
268
  const widths = Lines.addRemainder([10, 10])
268
269
  const lines = new Lines(widths)
269
270
  lines.setElement(0, 0, 'Generator')
@@ -273,10 +274,14 @@ class Generators {
273
274
  lines.setElement(0, 2, stack)
274
275
  lines.newRow()
275
276
  lines.setElement(0, 1, 'TO')
276
- lines.setElement(0, 2, stringify(context, null, 2))
277
+ lines.setElement(0, 2, JSON.stringify(context, null, 2))
277
278
  this.logs.push(lines.toString())
278
279
  }
279
- return ((config || {}).parenthesized ? '(' + generated + ')' : generated)
280
+ let parenthesized = false
281
+ if (config && config.getParenthesized()) {
282
+ parenthesized = true
283
+ }
284
+ return parenthesized ? '(' + generated + ')' : generated
280
285
  }
281
286
  }
282
287
 
package/src/helpers.js CHANGED
@@ -292,8 +292,53 @@ const hashCode = (str) => {
292
292
  return hash
293
293
  }
294
294
 
295
- const sortJson = (json) => {
296
- return json
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) => {
package/src/project2.js CHANGED
@@ -1,7 +1,32 @@
1
- function project(source, filters) {
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];
package/src/semantics.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const { args: contextArgs, normalizeGenerator, normalizeSemantic } = require('./helpers')
2
2
  const Lines = require('../lines')
3
3
  const helpers = require('./helpers')
4
+ const debug = require('./debug')
4
5
 
5
6
  class Semantic {
6
7
  // constructor ({match, apply, uuid, index, km, notes}) {
@@ -43,11 +44,13 @@ class Semantic {
43
44
  }
44
45
  }
45
46
 
47
+ /*
46
48
  getAPIs (config) {
47
49
  if (config && config._api && config._api.multiApi) {
48
50
  return config._api.apis
49
51
  }
50
52
  }
53
+ */
51
54
 
52
55
  fixUpArgs (args, context) {
53
56
  args.uuid = this.uuid
@@ -57,7 +60,7 @@ class Semantic {
57
60
  args.global = objects
58
61
  const config = args.config
59
62
  args.api = this.getAPI(config)
60
- args.apis = this.getAPIs(config)
63
+ args.apis = config.getAPIs()
61
64
  args.args = contextArgs(context, args.hierarchy)
62
65
  args.context = context
63
66
  let n = (id) => id
@@ -70,6 +73,7 @@ class Semantic {
70
73
  }
71
74
 
72
75
  async matches (args, context, options = {}) {
76
+ args = {...args}
73
77
  this.fixUpArgs(args, context)
74
78
  const matches = await this.matcher(args)
75
79
  if (matches && (options.debug || {}).match || args.callId === this.callId) {
@@ -81,12 +85,13 @@ class Semantic {
81
85
  }
82
86
 
83
87
  async apply (args, context, s, options = {}) {
88
+ args = {...args}
84
89
  const { config } = args
85
- if (config && config.debugLoops) {
90
+ if (config && config.getDebugLoops()) {
86
91
  console.log('apply', this.toLabel())
87
92
  }
88
- if (args.calls && config && args.calls.stack.length > config.maxDepth) {
89
- throw new Error(`Max depth of ${config.maxDepth} 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`)
93
+ if (args.calls && config && args.calls.stack.length > config.getMaxDepth()) {
94
+ 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`)
90
95
  }
91
96
 
92
97
  const contextPrime = Object.assign({}, context)
@@ -157,6 +162,7 @@ class Semantics {
157
162
  }
158
163
  args = { ...args }
159
164
  const config = args.config
165
+ const debug = config.getDebug()
160
166
  let contextPrime = Object.assign({}, context)
161
167
  const s = (context, options) => this.apply(args, context, options)
162
168
  let applied = false
@@ -197,7 +203,6 @@ class Semantics {
197
203
  continue
198
204
  }
199
205
  if (!contextPrime.controlKeepMotivation && semantic.oneShot) {
200
- // semantic.tied_ids.forEach((tied_id) => args.config.removeSemantic(tied_id))
201
206
  args.config.removeSemantic(semantic)
202
207
  }
203
208
  for (const { listener } of deferred) {
@@ -247,7 +252,7 @@ class Semantics {
247
252
  }
248
253
  args.calls.touch(contextPrime)
249
254
  // this.logs.push(`Semantics: applied ${semantic.toString()}\n to\n ${JSON.stringify(context)}\n the result was ${JSON.stringify(contextPrime)}\n`)
250
- if (((config || {}).config || {}).debug) {
255
+ if (config.getDebug()) {
251
256
  const widths = Lines.addRemainder([10, 10])
252
257
  const lines = new Lines(widths)
253
258
  lines.setElement(0, 0, 'Semantic')
@@ -294,7 +299,7 @@ class Semantics {
294
299
  counter += 1
295
300
  }
296
301
  args.calls.pop()
297
- if (!applied && ((config || {}).config || {}).debug) {
302
+ if (!applied && debug) {
298
303
  const widths = Lines.addRemainder([10, 10])
299
304
  const lines = new Lines(widths)
300
305
  lines.setElement(0, 0, 'Semantic')