theprogrammablemind 7.6.0 → 7.7.0-beta.1

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
@@ -173,22 +173,44 @@ const setupArgs = (args, config, logs, hierarchy) => {
173
173
  args.theDebugger = {
174
174
  breakOnSemantics: (value) => args.breakOnSemantics = value
175
175
  }
176
- args.s = (c) => config.getSemantics(logs).apply(args, c)
177
- args.g = (c) => config.getGenerators(logs).apply(args, c)
178
- args.gp = (c) => config.getGenerators(logs).apply(args, { ...c, paraphrase: true, isResponse: false, response: false})
179
- args.gr = (c) => config.getGenerators(logs).apply(args, { ...c, paraphrase: false, isResponse: true })
180
176
  if (!logs) {
181
177
  debugger
182
178
  }
183
- args.e = (c) => config.getEvaluator(args.s, args.calls, logs, c)
184
179
  args.log = (message) => logs.push(message)
185
- args.gs = gs(args.g)
186
- args.gsp = gs(args.gp)
187
- args.gsr = gs(args.gr)
180
+
181
+ args.addAssumedScoped = (args, assumed) => {
182
+ const addAssumed = (args, ...moreAssumed) => {
183
+ return { ...args, assumed: Object.assign({}, (args.assumed || {}), ...moreAssumed) }
184
+ }
185
+
186
+ args.s = (c) => config.getSemantics(logs).apply(args, c)
187
+ args.g = (c, a = {}) => {
188
+ return config.getGenerators(logs).apply(addAssumed(args, a), c, a)
189
+ }
190
+ args.gp = (c, a = {}) => {
191
+ return config.getGenerators(logs).apply(addAssumed(args, a, {paraphrase: true, isResponse: false, response: false}), c, {paraphrase: true, isResponse: false, response: false})
192
+ }
193
+ args.gr = (c, a = {}) => {
194
+ return config.getGenerators(logs).apply(addAssumed(args, a, {paraphrase: false, isResponse: true}), { ...c, paraphrase: false, isResponse: true })
195
+ }
196
+ args.e = (c) => {
197
+ return config.getEvaluator(args.s, args.calls, logs, c)
198
+ }
199
+ args.gs = gs(args.g)
200
+ args.gsp = gs(args.gp)
201
+ args.gsr = gs(args.gr)
202
+ }
203
+ // for semantics
204
+ args.addAssumedScoped(args, {})
188
205
  config.getAddedArgs(args)
189
206
  }
190
207
 
191
208
  const gs = (g) => (contexts, separator, lastSeparator) => {
209
+ if (!Array.isArray(contexts)) {
210
+ debugger
211
+ throw new Error("Expected a list")
212
+ }
213
+
192
214
  let s = ''
193
215
  if (!separator) {
194
216
  separator = ' '
@@ -276,8 +298,9 @@ const processContext = (context, { objects = {}, config, logs = [] }) => {
276
298
  setupArgs(args, config, logs, hierarchy)
277
299
 
278
300
  context = semantics.apply(args, context)
279
- const generated = generators.apply(args, context)[0]
280
- const paraphrases = generators.apply(args, context, { paraphrase: true, response: false, isResponse: false })[0]
301
+ const generated = generators.apply(args, context)
302
+ const assumed = { paraphrase: true, response: false, isResponse: false }
303
+ const paraphrases = generators.apply({...args, assumed}, context, { paraphrase: true, response: false, isResponse: false })
281
304
  let responses = []
282
305
  if (context.isResponse) {
283
306
  responses = generated
@@ -447,37 +470,25 @@ const processContextsB = ({ config, hierarchy, semantics, generators, json, isTe
447
470
  continue
448
471
  }
449
472
  let assumed = { isResponse: true };
450
- const generated = contextPrime.isResponse ? config.getGenerators(json.logs).apply(args, contextPrime, assumed)[0] : ''
473
+ const generated = contextPrime.isResponse ? config.getGenerators(json.logs).apply({...args, assumed}, contextPrime, assumed) : ''
451
474
  let generatedParenthesized = []
452
475
  if (generateParenthesized) {
453
476
  config.parenthesized = true
454
- generatedParenthesized = contextPrime.isResponse ? config.getGenerators(json.logs).apply(args, contextPrime, assumed)[0] : ''
477
+ generatedParenthesized = contextPrime.isResponse ? config.getGenerators(json.logs).apply({...args, assumed}, contextPrime, assumed) : ''
455
478
  config.parenthesized = false
456
479
  }
457
480
  // assumed = { paraphrase: true, response: false };
458
- assumed = { paraphrase: true };
459
- args.g = (c) => config.getGenerators(json.logs).apply(args, c, assumed)
460
- args.gp = (c) => config.getGenerators(json.logs).apply(args, {...c, paraphrase: true, isResponse: false, response: false }, assumed)
461
- args.gr = (c) => config.getGenerators(json.logs).apply(args, {...c, paraphrase: false }, assumed)
462
- args.gs = gs(args.g)
463
- args.gsp = gs(args.gsp)
464
- args.gsr = gs(args.gr)
481
+ assumed = { paraphrase: true, isResponse: false, response: false };
465
482
  if (generateParenthesized) {
466
483
  config.parenthesized = false
467
484
  }
468
- const paraphrases = config.getGenerators(json.logs).apply(args, contextPrime, assumed)[0]
485
+ const paraphrases = config.getGenerators(json.logs).apply({...args, assumed}, contextPrime, assumed)
469
486
  let paraphrasesParenthesized = []
470
487
  if (generateParenthesized) {
471
488
  config.parenthesized = true
472
- paraphrasesParenthesized = config.getGenerators(json.logs).apply(args, contextPrime, assumed)[0]
489
+ paraphrasesParenthesized = config.getGenerators(json.logs).apply({...args, assumed}, contextPrime, assumed)
473
490
  config.parenthesized = false
474
491
  }
475
- args.g = (c) => config.getGenerators(json.logs).apply(args, c)
476
- args.gp = (c) => config.getGenerators(json.logs).apply(args, {...c, paraphrase: true, isResponse: false, response: false })
477
- args.gr = (c) => config.getGenerators(json.logs).apply(args, {...c, paraphrase: false })
478
- args.gs = gs(args.g)
479
- args.gsp = gs(args.gp)
480
- args.gsr = gs(args.gr)
481
492
  contextsPrime.push(contextPrime)
482
493
  generatedPrime.push(generated)
483
494
  paraphrasesPrime.push(paraphrases)
@@ -614,7 +625,12 @@ const loadInstance = (config, instance) => {
614
625
  if (results.skipSemantics) {
615
626
  config.config.skipSemantics = results.skipSemantics
616
627
  }
617
- processContextsB({ config, hierarchy, json: results/*, generators, semantics */, commandLineArgs: {}, isInstance: `instance${i}`, instance: instance.queries[i] })
628
+ const args = { config, hierarchy, json: results, commandLineArgs: {} }
629
+ if (instance.queries) {
630
+ args.isInstance = `instance${i}`
631
+ args.instance = instance.queries[i]
632
+ }
633
+ processContextsB(args)
618
634
  if (results.skipSemantics) {
619
635
  config.config.skipSemantics = null
620
636
  }
@@ -660,6 +676,7 @@ const _process = async (config, query, { initializer, commandLineArgs, credentia
660
676
  opChoices: []
661
677
  },
662
678
  times: 0.0,
679
+ clientSideTimes: 0.0,
663
680
  trace: '',
664
681
  contexts: [],
665
682
  generated: [],
@@ -701,8 +718,16 @@ const _process = async (config, query, { initializer, commandLineArgs, credentia
701
718
  if (json.status !== 200) {
702
719
  throw json
703
720
  } else {
721
+ let clientSideTime
722
+ if (isTest) {
723
+ start = runtime.performance.performance.now()
724
+ }
704
725
  const { contextsPrime, generatedPrime, paraphrasesPrime, paraphrasesParenthesizedPrime, generatedParenthesizedPrime, responsesPrime } =
705
726
  processContextsB({ isTest, config, hierarchy, json, commandLineArgs /*, generators, semantics */ })
727
+ if (isTest) {
728
+ end = runtime.performance.performance.now()
729
+ clientSideTime = end - start
730
+ }
706
731
  response.associations = json.associations
707
732
  response.learned_contextual_priorities = json.learned_contextual_priorities
708
733
  response.hierarchy = json.hierarchy
@@ -713,6 +738,7 @@ const _process = async (config, query, { initializer, commandLineArgs, credentia
713
738
  // appendNoDups(response.metadata.priorities, json.metadata.priorities)
714
739
  appendNoDups(response.metadata.opChoices, json.metadata.opChoices)
715
740
  response.times += json.times
741
+ response.clientSideTimes += clientSideTime
716
742
  response.trace = response.trace.concat(json.trace)
717
743
  response.version = json.version
718
744
  response.explain_priorities = json.explain_priorities
@@ -778,10 +804,6 @@ const runTest = async (config, expected, { args, verbose, testConfig, debug }) =
778
804
  const test = expected.query
779
805
  // initialize in between test so state is not preserved since the test was adding without state
780
806
  config.rebuild()
781
- if (!args.dontAddAssociations) {
782
- config.addAssociationsFromTests(config.tests)
783
- }
784
- // config.addAssocationsFromTests(
785
807
  const errorHandler = (error) => {
786
808
  if (error.metadata) {
787
809
  const priorities = analyzeMetaData(expected.metadata, error.metadata)
@@ -806,10 +828,10 @@ const runTest = async (config, expected, { args, verbose, testConfig, debug }) =
806
828
  defaultInnerProcess(config, errorHandler, result)
807
829
  }
808
830
  if (verbose) {
809
- const widths = [100, 20]
831
+ const widths = [100, 60]
810
832
  const lines = new Lines(widths)
811
833
  lines.setElement(0, 0, test)
812
- lines.setElement(0, 1, `time on server ${result.times.toFixed(2)}`)
834
+ lines.setElement(0, 1, `time on server: ${result.times.toFixed(2)} client: ${(result.clientSideTimes/1000).toFixed(2)}`)
813
835
  lines.log()
814
836
  }
815
837
  const expected_objects = sortJson(convertToStable(expected.objects), { depth: 25 })
@@ -1173,7 +1195,7 @@ const defaultInnerProcess = (config, errorHandler, responses) => {
1173
1195
  }
1174
1196
 
1175
1197
  if (responses.explain_priorities) {
1176
- console.log("Explain Priorities")
1198
+ console.log("Explain Priorities (listed from lower priority to higher priority)")
1177
1199
  for ([inputss, outpus, reason] of responses.explain_priorities) {
1178
1200
  console.log(` ${JSON.stringify(inputss)} reason: ${reason}`)
1179
1201
  }
@@ -1232,7 +1254,7 @@ const defaultProcess = ({ config, errorHandler }) => async (promise) => {
1232
1254
  }
1233
1255
 
1234
1256
  // builtTemplate saveInstance
1235
- const rebuildTemplate = async ({ config, target, template, errorHandler = defaultErrorHandler }) => {
1257
+ const rebuildTemplate = async ({ config, target, previousResultss, startOfChanges, template, errorHandler = defaultErrorHandler }) => {
1236
1258
  const accumulators = {
1237
1259
  resultss: [],
1238
1260
  fragments: [],
@@ -1245,7 +1267,7 @@ const rebuildTemplate = async ({ config, target, template, errorHandler = defaul
1245
1267
  finish()
1246
1268
  return
1247
1269
  }
1248
- const { property, hierarchy, query: queryOrExtraConfig, initializer, skipSemantics } = queries.shift()
1270
+ const { property, hierarchy, query: queryOrExtraConfig, previousResults, initializer, skipSemantics } = queries.shift()
1249
1271
  // queries are strings or { query: "blah", development: true/false }
1250
1272
  if (typeof queryOrExtraConfig === 'string' || queryOrExtraConfig.query) {
1251
1273
  let query = queryOrExtraConfig;
@@ -1267,17 +1289,26 @@ const rebuildTemplate = async ({ config, target, template, errorHandler = defaul
1267
1289
  }
1268
1290
  }
1269
1291
  try {
1270
- const results = await _process(config, query.query, {initializer, rebuildingTemplate: true})
1292
+ let results
1293
+ let prMessage = ''
1294
+ if (previousResults && previousResults.query == query.query) {
1295
+ results = previousResults
1296
+ prMessage = ' Using previous results. use -rtf for a hard rebuild of everything on the server side.'
1297
+ loadInstance(config, { resultss: [results] })
1298
+ } else {
1299
+ results = await _process(config, query.query, {initializer, rebuildingTemplate: true})
1300
+ }
1271
1301
  if (config.config.debug) {
1272
1302
  // TODO pass in the error handler like the other ones
1273
1303
  defaultInnerProcess(config, defaultErrorHandler, results)
1274
1304
  }
1275
1305
  if (results.contexts.length > 1) {
1276
1306
  console.log(`query "${query.query}". There is ${results.contexts.length} contexts in the results. Make sure its producing the results that you expect.`)
1307
+ throw new Error(`query "${query.query}". There is ${results.contexts.length} contexts in the results. Make sure its producing the results that you expect.`)
1277
1308
  } else if (results.paraphrases[0] != query.query) {
1278
- console.log(`query "${query.query}". The paraphrase is different from the query "${results.paraphrases[0]}".`)
1309
+ console.log(`query "${query.query}". The paraphrase is different from the query "${results.paraphrases[0]}".${prMessage}`)
1279
1310
  } else {
1280
- console.log(`query ${query.query}`)
1311
+ console.log(`query "${query.query}".${prMessage}`)
1281
1312
  }
1282
1313
  global.transitoryMode = transitoryMode
1283
1314
  config.config.skipSemantics = null
@@ -1299,14 +1330,18 @@ const rebuildTemplate = async ({ config, target, template, errorHandler = defaul
1299
1330
  // it will just get added to the config
1300
1331
  const extraConfig = queryOrExtraConfig
1301
1332
  console.log('config', extraConfig)
1302
- try {
1303
- config.addInternal(_.cloneDeep(extraConfig), { handleCalculatedProps: true } )
1304
- } catch ( e ) {
1305
- const where = extraConfig.where ? ` ${extraConfig.where}` : ''
1306
- throw new Error(`Error processing extra config${where}: ${e.stack}}`)
1333
+ if (extraConfig.stop) {
1334
+ await looper([])
1335
+ } else {
1336
+ try {
1337
+ config.addInternal(_.cloneDeep(extraConfig), { handleCalculatedProps: true } )
1338
+ } catch ( e ) {
1339
+ const where = extraConfig.where ? ` ${extraConfig.where}` : ''
1340
+ throw new Error(`Error processing extra config${where}: ${e.stack}}`)
1341
+ }
1342
+ accumulators[property].push({ extraConfig: true, ...extraConfig })
1343
+ await looper(queries)
1307
1344
  }
1308
- accumulators[property].push({ extraConfig: true, ...extraConfig })
1309
- await looper(queries)
1310
1345
  }
1311
1346
  }
1312
1347
 
@@ -1327,6 +1362,7 @@ const rebuildTemplate = async ({ config, target, template, errorHandler = defaul
1327
1362
  } else {
1328
1363
  delete result.load_cache_time
1329
1364
  delete result.times
1365
+ delete result.clientSideTimes
1330
1366
  delete result.memory_free_percent
1331
1367
  delete result.logs
1332
1368
  delete result.version
@@ -1360,13 +1396,41 @@ const rebuildTemplate = async ({ config, target, template, errorHandler = defaul
1360
1396
  }
1361
1397
  let todo = []
1362
1398
  todo = todo.concat((template.initializers || []).map((query) => { return { initializer: true, property: 'resultss', query, skipSemantics: false || query.skipSemantics } }))
1363
- todo = todo.concat((template.queries || []).map((query) => { return { property: 'resultss', query, skipSemantics: false || query.skipSemantics} }))
1399
+ todo = todo.concat((template.queries || []).map((query, index) => {
1400
+ let pr
1401
+ if (index < startOfChanges) {
1402
+ pr = previousResultss[index]
1403
+ }
1404
+ return { property: 'resultss', query, previousResults: pr, skipSemantics: false || query.skipSemantics}
1405
+ }))
1364
1406
  todo = todo.concat((template.fragments || []).map((query) => { return Object.assign({}, toProperties(query), { property: 'fragments', skipSemantics: false }) }))
1365
1407
  todo = todo.concat((template.semantics || []).map((definition) => { return { property: 'semantics', query: `${definition.from}\n${definition.to}`, skipSemantics: true } }))
1366
1408
  await looper(Object.assign([], todo))
1367
1409
  }
1368
1410
 
1369
- const knowledgeModule = async ({
1411
+ const checkTemplate = (template) => {
1412
+ return
1413
+ if (!template) {
1414
+ return
1415
+ }
1416
+ if (template.checks) {
1417
+ throw new Error("The 'checks' property should be in the 'test' property not the 'template' property")
1418
+ }
1419
+ }
1420
+
1421
+ const checkTest = (testConfig) => {
1422
+ if (!testConfig) {
1423
+ return
1424
+ }
1425
+ if (!testConfig.name) {
1426
+ throw new Error("The 'test' property is missing the 'name' property that contains the name of the '<km>.test.json' file")
1427
+ }
1428
+ if (!testConfig.contents) {
1429
+ throw new Error("The 'test' property is missing the 'contents' property that contains contents of the '<km>.test.json' file")
1430
+ }
1431
+ }
1432
+
1433
+ const knowledgeModuleImpl = async ({
1370
1434
  module: moduleFromJSFile,
1371
1435
  description,
1372
1436
  section,
@@ -1399,9 +1463,10 @@ const knowledgeModule = async ({
1399
1463
  if (!description) {
1400
1464
  throw new Error("'description' is a required parameter. The value should the description of the knowledge module.")
1401
1465
  }
1402
- if (!test) {
1466
+ if (!testConfig) {
1403
1467
  throw new Error("'test' is a required parameter. The value should the path to the file used to store the tests of the knowledge module and the contents of the file in the form { name: <filePath>, contexts: <json> }.")
1404
1468
  }
1469
+ checkTest(testConfig)
1405
1470
 
1406
1471
  const isProcess = require.main === moduleFromJSFile
1407
1472
 
@@ -1452,13 +1517,14 @@ const knowledgeModule = async ({
1452
1517
  parser.add_argument('-tnp', '--testNoParenthesized', { action: 'store_true', help: 'Don\' check parenthesized differences for the tests' })
1453
1518
  parser.add_argument('-n', '--count', { help: 'Number of times to run the tests. Default is one. Use this to check for flakey test. If possible the system will print out a message with the word "hint" suggesting how to fix the problem' })
1454
1519
  // parser.add_argument('-b', '--build', { help: 'Specify the template file name of the form <kmName>. There should be a file called <baseKmName>.<kmName>.template.json with the queries to run. For example { queries: [...] }. The template file will be run and generate an instantiation called <baseKmName>.<kmName>.instance.json and a file called <kmName>.js that will load the template file (this is file generated only if not already existing) and a test file called <KmName>.tests.json. This can then be loaded into an instance of the current knowledge module to setup initial conditions.' })
1455
- parser.add_argument('-rt', '--rebuildTemplate', { action: 'store_true', help: 'Force a template rebuild' })
1520
+ parser.add_argument('-rt', '--rebuildTemplate', { action: 'store_true', help: 'Force a template rebuild. Using optimization where if the query/config has not changed it will use the previous value. One there is a change all subsequence query/configs will be run.' })
1521
+ parser.add_argument('-rtf', '--rebuildTemplateFull', { action: 'store_true', help: 'Force a template rebuild. Skip the optimization' })
1456
1522
  parser.add_argument('-l', '--loop', { action: 'store_true', help: 'Run a loop so that multiply queries may be run' })
1457
1523
  parser.add_argument('-i', '--info', { action: 'store_true', help: 'Print meta-data for the module' })
1458
1524
  parser.add_argument('-v', '--vimdiff', { action: 'store_true', help: 'For failures run vimdiff' })
1459
1525
  parser.add_argument('-g', '--greg', { action: 'store_true', help: 'Set the server to be localhost so I can debug stuff' })
1460
1526
  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]]' })
1461
- parser.add_argument('-r', '--retrain', { action: 'store_true', help: 'Get the server to retrain the neural nets' })
1527
+ parser.add_argument('-r', '--reset', { action: 'store_true', help: 'Get the server to bypass the cache and rebuild everything' })
1462
1528
  parser.add_argument('-q', '--query', { help: 'Run the specified query' })
1463
1529
  parser.add_argument('-ip ', '--server', { help: 'Server to run against' })
1464
1530
  parser.add_argument('-qp ', '--queryParams', { help: 'Query params for the server call' })
@@ -1466,7 +1532,6 @@ const knowledgeModule = async ({
1466
1532
  parser.add_argument('--parenthesized', { action: 'store_true', help: 'Show the generated phrases with parenthesis.' })
1467
1533
  parser.add_argument('-c', '--clean', { help: 'Remove data from the test files. a === association' })
1468
1534
  parser.add_argument('-od', '--objectDiff', { action: 'store_true', help: 'When showing the objects use a colour diff' })
1469
- parser.add_argument('-daa', '--dontAddAssociations', { action: 'store_true', help: 'Do not add associations from the tests.' })
1470
1535
  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, g === generators, s === semantics, l === load t=tests ordering p === priorities a == associations j == JSON sent to server. for example --print wb' })
1471
1536
  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.' })
1472
1537
  parser.add_argument('-sd', '--saveDeveloper', { action: 'store_true', help: 'Same as -s but the query will not show up in the info command.' })
@@ -1484,6 +1549,10 @@ const knowledgeModule = async ({
1484
1549
  const args = parser.parse_args()
1485
1550
  args.count = args.count || 1
1486
1551
 
1552
+ if (args.rebuildTemplateFull) {
1553
+ args.rebuildTemplate = true
1554
+ }
1555
+
1487
1556
  if (args.parenthesized) {
1488
1557
  config.parenthesized = true
1489
1558
  }
@@ -1671,24 +1740,40 @@ const knowledgeModule = async ({
1671
1740
  }
1672
1741
  }
1673
1742
 
1743
+ checkTemplate(template)
1744
+
1674
1745
  if (template) {
1675
- const needsRebuild = config.needsRebuild(template.template, template.instance, options)
1676
- if (needsRebuild) {
1746
+ let needsRebuild
1747
+ if (args.rebuildTemplate && !args.rebuildTemplateFull) {
1748
+ // get the startOfChanges for the partial rebuild
1749
+ needsRebuild = config.needsRebuild(template.template, template.instance, { ...options, rebuild: false })
1750
+ } else {
1751
+ // do a check or full rebuild
1752
+ needsRebuild = config.needsRebuild(template.template, template.instance, options)
1753
+ }
1754
+
1755
+ if (needsRebuild.needsRebuild) {
1756
+ if (needsRebuild.previousResultss) {
1757
+ console.log("Rebuild using the optimization to use previous results until a change is hit. For a full rebuild use -rtf")
1758
+ }
1677
1759
  console.log(`This module "${config.name}" needs rebuilding all other arguments will be ignored. Try again after the template is rebuilt.`)
1678
1760
  options.rebuild = true
1679
1761
  config.config.rebuild = true
1680
1762
  }
1681
- config.load(template.template, template.instance, { rebuild: needsRebuild })
1682
- printConfig()
1683
- if (needsRebuild) {
1763
+ try {
1764
+ config.load(template.template, template.instance, { rebuild: needsRebuild.needsRebuild || options.rebuild, previousResultss: needsRebuild.previousResultss, startOfChanges: needsRebuild.startOfChanges })
1765
+ } catch( e ) {
1766
+ console.error(`Error loading template for ${config.name}. ${e.error ? e.error : e}`)
1767
+ runtime.process.exit(-1)
1768
+ }
1769
+ if (!args.query) {
1770
+ printConfig()
1771
+ }
1772
+ if (needsRebuild.needsRebuild) {
1684
1773
  return
1685
1774
  }
1686
1775
  }
1687
1776
 
1688
- if (!args.save && !args.rebuildTemplate && !args.dontAddAssociations) {
1689
- config.addAssociationsFromTests(config.tests);
1690
- }
1691
-
1692
1777
  if (args.retrain) {
1693
1778
  config.config.retrain = true
1694
1779
  }
@@ -1696,7 +1781,6 @@ const knowledgeModule = async ({
1696
1781
  if (args.test || args.testVerbose || args.testAllVerbose || args.save) {
1697
1782
  global.transitoryMode = true
1698
1783
  }
1699
-
1700
1784
  if (!args.query && !args.test && !args.info && (args.save || args.saveDeveloper)) {
1701
1785
  global.transitoryMode = true
1702
1786
  saveTests(config, test, testConfig, args.saveDeveloper)
@@ -1718,7 +1802,6 @@ const knowledgeModule = async ({
1718
1802
  useTestConfig = config.getConfigs()[args.testModuleName].getTestConfig()
1719
1803
  useTestConfig.testModuleName = args.testModuleName
1720
1804
  test = useTestConfig.name
1721
-
1722
1805
  }
1723
1806
  runTests(config, test, { args, debug: args.debug, testConfig: useTestConfig, verbose: args.testVerbose || args.testAllVerbose, stopAtFirstError: !args.testAllVerbose }).then((results) => {
1724
1807
  let newError = false
@@ -1762,17 +1845,21 @@ const knowledgeModule = async ({
1762
1845
  console.log(` actual ${label} `, actual)
1763
1846
  newError = true
1764
1847
  headerShown = true
1848
+ if (args.vimdiff) {
1849
+ vimdiff(result.actual.paraphrasesParenthesized, result.expected.paraphrasesParenthesized)
1850
+ }
1765
1851
  }
1766
1852
  }
1767
1853
  show('paraphrases', result.expected.paraphrases, result.actual.paraphrases)
1768
1854
  if (!args.testNoParenthesized) {
1769
1855
  show('paraphrases parenthesized', result.expected.paraphrasesParenthesized, result.actual.paraphrasesParenthesized)
1770
1856
  }
1857
+ /*
1858
+ }
1771
1859
  show('responses', result.expected.responses, result.actual.responses)
1772
1860
  if (!args.testNoParenthesized) {
1773
1861
  show('responses parenthesized', result.expected.generatedParenthesized, result.actual.generatedParenthesized)
1774
1862
  }
1775
- /*
1776
1863
  if (JSON.stringify(result.expected.paraphrases) !== JSON.stringify(result.actual.paraphrases)) {
1777
1864
  if (!headerShown) {
1778
1865
  console.log(' Failure')
@@ -1798,10 +1885,10 @@ const knowledgeModule = async ({
1798
1885
  }
1799
1886
  const widths = [4, 18, 72]
1800
1887
  const lines = new Lines(widths)
1801
- lines.setElement(1, 1, 'expected checked')
1888
+ lines.setElement(1, 1, 'expected checked objects')
1802
1889
  lines.setElement(2, 2, JSON.stringify(result.expected.checked, null, 2))
1803
1890
  lines.log()
1804
- lines.setElement(1, 1, 'actual checked')
1891
+ lines.setElement(1, 1, 'actual checked objects')
1805
1892
  lines.setElement(2, 2, JSON.stringify(result.actual.checked, null, 2))
1806
1893
  lines.log()
1807
1894
  if (args.vimdiff) {
@@ -1816,10 +1903,10 @@ const knowledgeModule = async ({
1816
1903
  }
1817
1904
  const widths = [4, 18, 72]
1818
1905
  const lines = new Lines(widths)
1819
- lines.setElement(1, 1, 'expected checkedContexts', true)
1906
+ lines.setElement(1, 1, 'expected checked contexts', true)
1820
1907
  lines.setElement(2, 2, JSON.stringify(result.expected.checkedContexts, null, 2))
1821
1908
  lines.log()
1822
- lines.setElement(1, 1, 'actual checkedContexts', true)
1909
+ lines.setElement(1, 1, 'actual checked contexts', true)
1823
1910
  lines.setElement(2, 2, JSON.stringify(result.actual.checkedContexts, null, 2))
1824
1911
  lines.log()
1825
1912
  if (args.vimdiff) {
@@ -1836,16 +1923,18 @@ const knowledgeModule = async ({
1836
1923
  }
1837
1924
  }
1838
1925
  }
1839
- if (!headerShown) {
1926
+ if (hasError) {
1927
+ if (!headerShown) {
1928
+ if (!(useTestConfig.check && useTestConfig.check.length > 0)) {
1929
+ console.log('There are failures due to things other than paraphrases, responses and checked properties being different. They are not shown because you ran -tv or -tva which only shows difference in paraphrase and results. Usually what I do is -s and do a diff to make sure there are no other problems. If the paraphrases or results were different they would have shown here.')
1930
+ }
1931
+ }
1840
1932
  if (!(useTestConfig.check && useTestConfig.check.length > 0)) {
1841
- console.log('There are failures due to things other than paraphrases, responses and checked properties being different. They are not shown because you ran -tv or -tva which only shows difference in paraphrase and results. Usually what I do is -s and do a diff to make sure there are no other problems. If the paraphrases or results were different they would have shown here.')
1933
+ console.log('use -v arg to write files expected.json and actual.json in the current directory for detailed comparison. Or do -s and then git diff the changes.')
1934
+ // console.log(JSON.stringify(contexts))
1935
+ console.log('**************************** THERE WERE ERRORS ************************')
1842
1936
  }
1843
1937
  }
1844
- if (!(useTestConfig.check && useTestConfig.check.length > 0)) {
1845
- console.log('use -v arg to write files expected.json and actual.json in the current directory for detailed comparison. Or do -s and then git diff the changes.')
1846
- // console.log(JSON.stringify(contexts))
1847
- console.log('**************************** ERRORS ************************')
1848
- }
1849
1938
  }
1850
1939
  // const contexts = { failures: results }
1851
1940
  l(n - 1, hasError || newError)
@@ -1929,7 +2018,7 @@ const knowledgeModule = async ({
1929
2018
  }
1930
2019
 
1931
2020
  if (template) {
1932
- if (config.needsRebuild(template.template, template.instance, { isModule: !isProcess })) {
2021
+ if (config.needsRebuild(template.template, template.instance, { isModule: !isProcess }).needsRebuild) {
1933
2022
  const error = `This module "${config.name}" cannot be used because the instance file needs rebuilding. Run on the command line with no arguments or the -rt argument to rebuild.`
1934
2023
  throw new Error(error)
1935
2024
  }
@@ -1939,8 +2028,6 @@ const knowledgeModule = async ({
1939
2028
  errorHandler(e)
1940
2029
  }
1941
2030
  }
1942
-
1943
- config.addAssociationsFromTests(config.tests);
1944
2031
  }
1945
2032
 
1946
2033
  createConfigExport = () => {
@@ -1951,7 +2038,6 @@ const knowledgeModule = async ({
1951
2038
  }
1952
2039
  moduleFromJSFile.exports = createConfigExport
1953
2040
  }
1954
-
1955
2041
  }
1956
2042
 
1957
2043
  /*
@@ -1999,6 +2085,10 @@ function w(func) {
1999
2085
  return func
2000
2086
  }
2001
2087
 
2088
+ const knowledgeModule = async (...args) => {
2089
+ await knowledgeModuleImpl(...args).catch((e) => console.error(e))
2090
+ }
2091
+
2002
2092
  module.exports = {
2003
2093
  process: _process,
2004
2094
  where,
package/package.json CHANGED
@@ -12,6 +12,7 @@
12
12
  "scripts": {
13
13
  "to:debug": "node inspect node_modules/.bin/jest --runInBand -t NEO23",
14
14
  "test:debug": "node inspect node_modules/.bin/jest --runInBand --config ./jest.config.json",
15
+ "td": "node inspect node_modules/.bin/jest --runInBand --config ./jest.config.json",
15
16
  "tod": "node inspect node_modules/.bin/jest --runInBand -t NEO23",
16
17
  "lint:fix": "eslint \"**/*.js\" --fix",
17
18
  "lint": "eslint \"**/*.js\"",
@@ -64,6 +65,6 @@
64
65
  "json-stable-stringify": "^1.0.1",
65
66
  "node-fetch": "^2.6.1"
66
67
  },
67
- "version": "7.6.0",
68
+ "version": "7.7.0-beta.1",
68
69
  "license": "ISC"
69
70
  }
package/runtime.js CHANGED
@@ -15,4 +15,5 @@ module.exports = {
15
15
  jsonDiff: require('json-diff'),
16
16
  sortJson: require('sort-json'),
17
17
  util: require('util'),
18
+ performance: require('perf_hooks'),
18
19
  }
package/src/config.js CHANGED
@@ -27,7 +27,7 @@ const config_toServer = (config) => {
27
27
 
28
28
  const debugPriority = (priority) => {
29
29
  if (global.entodictonDebugPriority) {
30
- if (helpers.safeEquals(entodictonDebugPriority, priority)) {
30
+ if (helpers.subPriority(entodictonDebugPriority, priority)) {
31
31
  debugger; // debug hierarchy hit
32
32
  }
33
33
  }
@@ -965,7 +965,7 @@ class Config {
965
965
  }
966
966
 
967
967
  // { rebuild: false, isModule: false }
968
- needsRebuild(template, instance, options) {
968
+ needsRebuild (template, instance, options) {
969
969
  if (options.rebuild) {
970
970
  return true
971
971
  }
@@ -1058,7 +1058,21 @@ class Config {
1058
1058
  return elements.map( toCanonicalQuery )
1059
1059
  }
1060
1060
 
1061
- const sameQueries = helpers.safeEquals(toCanonicalQueries(template.queries || []).map(helpers.updateQueries), toCanonicalQueries(instance.queries || []))
1061
+ const templateQueries = toCanonicalQueries(template.queries || []).map(helpers.updateQueries)
1062
+ const instanceQueries = toCanonicalQueries(instance.queries || [])
1063
+ let sameQueries = true
1064
+ let startOfChanges;
1065
+ for (let iq = 0; iq < templateQueries.length; ++iq) {
1066
+ if (!helpers.safeEquals(templateQueries[iq], instanceQueries[iq])) {
1067
+ sameQueries = false
1068
+ startOfChanges = iq
1069
+ }
1070
+ }
1071
+ // things were deleted case
1072
+ if (templateQueries.length < instanceQueries.length) {
1073
+ startOfChanges = instanceQueries.length
1074
+ }
1075
+ // const sameQueries = helpers.safeEquals(toCanonicalQueries(template.queries || []).map(helpers.updateQueries), toCanonicalQueries(instance.queries || []))
1062
1076
 
1063
1077
  const debug = false
1064
1078
  if (debug) {
@@ -1067,16 +1081,20 @@ class Config {
1067
1081
  debugger
1068
1082
  debugger
1069
1083
  }
1070
- console.log("instance", instance)
1084
+ // console.log("instance", instance)
1071
1085
  console.log("sameQueries", sameQueries)
1072
1086
  console.log("sameFragments", sameFragments)
1073
- console.log("templateFragments", templateFragments)
1074
- console.log("instanceFragments", instanceFragments)
1075
- console.log('template.queries', JSON.stringify(toCanonicalQueries(template.queries || []).map(helpers.updateQueries), null, 2))
1076
- console.log("instance.queries", JSON.stringify(toCanonicalQueries(instance.queries || []), null, 2))
1087
+ // console.log("templateFragments", templateFragments)
1088
+ // console.log("instanceFragments", instanceFragments)
1089
+ // console.log('template.queries', JSON.stringify(toCanonicalQueries(template.queries || []).map(helpers.updateQueries), null, 2))
1090
+ // console.log("instance.queries", JSON.stringify(toCanonicalQueries(instance.queries || []), null, 2))
1077
1091
  }
1078
1092
  }
1079
- return !(instance && sameQueries && sameFragments)
1093
+ if (startOfChanges || instance.resultss) {
1094
+ return { needsRebuild: !(instance && sameQueries && sameFragments), startOfChanges, previousResultss: instance.resultss }
1095
+ } else {
1096
+ return { needsRebuild: !(instance && sameQueries && sameFragments) }
1097
+ }
1080
1098
  }
1081
1099
 
1082
1100
  validifyTemplate (template) {
@@ -1097,7 +1115,7 @@ class Config {
1097
1115
  }
1098
1116
 
1099
1117
  // loadTemplate
1100
- load (template, instance, options = { rebuild: false } ) {
1118
+ load (template, instance, options = { rebuild: false, previousResultss: undefined, startOfChanges: undefined } ) {
1101
1119
  this.validifyTemplate(template)
1102
1120
  instance.template = template
1103
1121
  this.logs.push(`loading template for ${this.name}`)
@@ -1105,7 +1123,7 @@ class Config {
1105
1123
  // TODO fix beforeQuery
1106
1124
  template = { fragments: [], queries: [], ...template }
1107
1125
  template.fragments = template.fragments.concat(this.dynamicFragments)
1108
- client.rebuildTemplate({ config: this, target: this.name, beforeQuery: () => {}, template, ...options })
1126
+ client.rebuildTemplate({ config: this, target: this.name, previousResultss: options.previousResultss, startOfChanges: options.startOfChanges, beforeQuery: () => {}, template, ...options })
1109
1127
  } else {
1110
1128
  // no change
1111
1129
  // this.initInstances.push({ ...instance, name: config.name })
@@ -1137,12 +1155,6 @@ class Config {
1137
1155
  return this.config.objects.namespaced[this._uuid]
1138
1156
  }
1139
1157
 
1140
- addAssociationsFromTests(tests = []) {
1141
- for (let test of tests) {
1142
- this.addAssociations(test.associations || []);
1143
- }
1144
- }
1145
-
1146
1158
  addAssociations (associations) {
1147
1159
  for (let association of associations) {
1148
1160
  this.addAssociation(association)
package/src/flatten.js CHANGED
@@ -81,6 +81,9 @@ const flatten = (markers, value) => {
81
81
  return flattenListHelper(markers, value)
82
82
  }
83
83
 
84
+ if (value.flatten === false) {
85
+ return [[value], false]
86
+ }
84
87
  const marker = value.marker
85
88
  let properties = value
86
89
 
package/src/generators.js CHANGED
@@ -73,7 +73,8 @@ class Generator {
73
73
  return matches
74
74
  }
75
75
 
76
- apply (baseArgs, objects, g, gs, context, hierarchy, config, response, log, options = {}) {
76
+ // apply (baseArgs, objects, g, gs, context, hierarchy, config, response, log, options = {}) {
77
+ apply (baseArgs, objects, context, hierarchy, config, response, log, options = {}) {
77
78
  if (!log) {
78
79
  throw new Error('generators.apply argument log is required')
79
80
  }
@@ -103,12 +104,12 @@ class Generator {
103
104
  log,
104
105
  global:
105
106
  objects,
106
- g,
107
+ // g,
107
108
  n,
108
109
  hierarchy,
109
110
  context,
110
111
  uuid: this.uuid,
111
- gs,
112
+ // gs,
112
113
  config,
113
114
  response,
114
115
  api: this.getAPI(config),
@@ -165,136 +166,124 @@ class Generators {
165
166
  this.logs = logs
166
167
  };
167
168
 
168
- // assumed - properties added to contexts before the generators are called. For setting paraphrase property
169
- apply (args, contexts, assumed = {}, options = {}) {
169
+ // assumed - properties added to context before the generators are called. For setting paraphrase property
170
+ apply (args, context, assumed = {}, options = {}) {
171
+ if (Array.isArray(context)) {
172
+ throw new Error("Expected a context not an array")
173
+ }
174
+ if (typeof context !== 'object') {
175
+ return String(context)
176
+ }
177
+
170
178
  const config = args.config
171
179
  const objects = args.objects
172
180
  const hierarchy = args.hierarchy
173
181
  const response = args.response
174
- if (Array.isArray(contexts)) {
175
- // no-op
176
- } else if (typeof contexts === 'object') {
177
- contexts = [contexts]
178
- } else {
179
- return String(contexts)
180
- }
181
-
182
- const contextsPrime = []
183
- for (const icontext in contexts) {
184
- let context = contexts[icontext]
185
- context = Object.assign({}, context, assumed)
186
- // let context_prime = JSON.stringify(context);
187
- let generated = context
188
- let applied = false
189
- const stack = args.calls.push()
190
- for (let igenerator = 0; igenerator < this.generators.length; ++igenerator) {
191
- const generator = this.generators[igenerator]
192
- if (generator.matches(args, objects, context, hierarchy, config, options)) {
193
- const g = (context, options) => {
194
- const r = this.apply(args, context, Object.assign({}, ((options||{}).assumed || {}), assumed), options)
195
- if (Array.isArray(r)) {
196
- return r.join(' ')
197
- } else {
198
- return r
199
- }
200
- }
201
- const log = (message) => { this.logs.push(message) }
202
- // this.logs.push(`Generators: applied ${generator.toString()}\n to\n ${JSON.stringify(context)}`)
203
- let errorMessage = 'The apply function did not return a value'
204
- try {
205
- generated= generator.apply(args, objects, g, args.gs, context, hierarchy, config, response, log)
206
- } catch( e ) {
207
- // the next if handle this
208
- generated = null
209
- e.retryCall = () => generator.apply(args, objects, g, args.gs, context, hierarchy, config, response, log)
210
- const help = 'The error has a retryCall property that will recall the function that failed.'
211
- if (e.stack && e.message) {
212
- const info = `${generator.notes ? generator.notes : ''}${generator.where ? generator.where : ''}`
213
- errorMessage = `Error applying generator '${info}. Error is ${e.toString()} stack is ${e.stack}. Generator is ${generator.toString()}. ${help}`
214
- } else if (e.error) {
215
- const info = `${generator.notes ? generator.notes : ''}${generator.where ? generator.where : ''}`
216
- errorMessage = `Error applying generator '${info}. Error is ${e.error.join()}. Generator is ${generator.toString()}. ${help}`
217
- } else {
218
- errorMessage = e.toString()
219
- }
182
+
183
+ // args = { ...args, ...args.getAssumedScoped(assumed) }
184
+ args.addAssumedScoped(args, assumed)
185
+ context = Object.assign({}, context, args.assumed)
186
+ let generated = ''
187
+ let applied = false
188
+ const stack = args.calls.push()
189
+ for (let igenerator = 0; igenerator < this.generators.length; ++igenerator) {
190
+ const generator = this.generators[igenerator]
191
+ if (generator.matches(args, objects, context, hierarchy, config, options)) {
192
+ const log = (message) => { this.logs.push(message) }
193
+ // this.logs.push(`Generators: applied ${generator.toString()}\n to\n ${JSON.stringify(context)}`)
194
+ let errorMessage = 'The apply function did not return a value'
195
+ try {
196
+ generated= generator.apply(args, objects, context, hierarchy, config, response, log)
197
+ } catch( e ) {
198
+ // the next if handle this
199
+ generated = null
200
+ e.retryCall = () => generator.apply(args, objects, context, hierarchy, config, response, log)
201
+ const help = 'The error has a retryCall property that will recall the function that failed.'
202
+ if (e.stack && e.message) {
203
+ const info = `${generator.notes ? generator.notes : ''}${generator.where ? generator.where : ''}`
204
+ errorMessage = `Error applying generator '${info}. Error is ${e.toString()} stack is ${e.stack}. Generator is ${generator.toString()}. ${help}`
205
+ } else if (e.error) {
206
+ const info = `${generator.notes ? generator.notes : ''}${generator.where ? generator.where : ''}`
207
+ errorMessage = `Error applying generator '${info}. Error is ${e.error.join()}. Generator is ${generator.toString()}. ${help}`
208
+ } else {
209
+ errorMessage = e.toString()
220
210
  }
221
- if (!generated && generated !== '') {
222
- const widths = [10, 10, 90]
223
- const lines = new Lines(widths)
224
- lines.setElement(0, 0, 'Generator')
225
- const source = `${generator.km}/#${generator.index}`
226
- lines.setElement(0, 2, `ERROR while applying (${source}) ${generator.toLabel()}`)
227
- lines.newRow()
228
- lines.setElement(0, 2, generator.toString())
229
- lines.newRow()
230
- lines.setElement(0, 1, 'TO')
231
- lines.setElement(0, 2, `(HASHCODE ${helpers.hashCode(JSON.stringify(helpers.sortJson(context, { depth: 25 })))})`)
232
- lines.setElement(1, 2, JSON.stringify(helpers.sortJson(context, { depth: 25 }), null, 2))
233
- lines.newRow()
234
- lines.setElement(0, 1, 'STACK')
235
- lines.setElement(0, 2, stack)
236
- lines.newRow()
237
- lines.setElement(0, 1, 'DEBUG')
238
- lines.setElement(0, 2, `To debug this use args.callId == '${args.calls.current()}'`)
239
- lines.newRow()
240
- lines.setElement(0, 1, 'ERROR')
241
- lines.setElement(0, 2, errorMessage)
242
- this.logs.push(lines.toString())
243
- const message = `ERROR while applying (${source}) ${generator.toLabel()}\n to\n ${JSON.stringify(context, null, 2)}.\n${errorMessage}'`
244
- // this.logs.push(message)
245
- // return [message]
246
- args.calls.pop()
247
- throw { error: [message], logs: this.logs }
248
- }
249
- if (((config || {}).config || {}).debug) {
250
- const widths = [10, 10, 90]
251
- const lines = new Lines(widths)
252
- lines.setElement(0, 0, 'Generator')
253
- if (generator.index > -1 && generator.km) {
254
- // lines.setElement(0, 2, `KM '${generator.km}' ordinal: ${generator.index}`)
255
- lines.setElement(0, 2, generator.toLabel())
256
- }
257
- lines.newRow()
258
- lines.setElement(0, 1, 'APPLIED')
211
+ }
212
+ if (!generated && generated !== '') {
213
+ const widths = [10, 10, 90]
214
+ const lines = new Lines(widths)
215
+ lines.setElement(0, 0, 'Generator')
216
+ const source = `${generator.km}/#${generator.index}`
217
+ lines.setElement(0, 2, `ERROR while applying (${source}) ${generator.toLabel()}`)
218
+ lines.newRow()
219
+ lines.setElement(0, 2, generator.toString())
220
+ lines.newRow()
221
+ lines.setElement(0, 1, 'TO')
222
+ lines.setElement(0, 2, `(HASHCODE ${helpers.hashCode(JSON.stringify(helpers.sortJson(context, { depth: 25 })))})`)
223
+ lines.setElement(1, 2, JSON.stringify(helpers.sortJson(context, { depth: 25 }), null, 2))
224
+ lines.newRow()
225
+ lines.setElement(0, 1, 'STACK')
226
+ lines.setElement(0, 2, stack)
227
+ lines.newRow()
228
+ lines.setElement(0, 1, 'DEBUG')
229
+ lines.setElement(0, 2, `To debug this use args.callId == '${args.calls.current()}'`)
230
+ lines.newRow()
231
+ lines.setElement(0, 1, 'ERROR')
232
+ lines.setElement(0, 2, errorMessage)
233
+ this.logs.push(lines.toString())
234
+ const message = `ERROR while applying (${source}) ${generator.toLabel()}\n to\n ${JSON.stringify(context, null, 2)}.\n${errorMessage}'`
235
+ // this.logs.push(message)
236
+ // return [message]
237
+ args.calls.pop()
238
+ throw { error: [message], logs: this.logs }
239
+ }
240
+ if (((config || {}).config || {}).debug) {
241
+ const widths = [10, 10, 90]
242
+ const lines = new Lines(widths)
243
+ lines.setElement(0, 0, 'Generator')
244
+ if (generator.index > -1 && generator.km) {
245
+ // lines.setElement(0, 2, `KM '${generator.km}' ordinal: ${generator.index}`)
259
246
  lines.setElement(0, 2, generator.toLabel())
260
- lines.newRow()
261
- lines.setElement(0, 2, generator.toString())
262
- lines.newRow()
263
- lines.setElement(0, 1, 'RESULT')
264
- lines.setElement(0, 2, generated)
265
- lines.newRow()
266
- lines.setElement(0, 1, 'STACK')
267
- lines.setElement(0, 2, stack)
268
- lines.newRow()
269
- lines.setElement(0, 1, 'DEBUG')
270
- lines.setElement(0, 2, `To debug this use args.callId == '${args.calls.current()}'`)
271
- lines.newRow()
272
- lines.setElement(0, 1, 'TO')
273
- lines.setElement(0, 2, `(HASHCODE ${helpers.hashCode(JSON.stringify(helpers.sortJson(context, { depth: 25 })))})`)
274
- lines.setElement(1, 2, JSON.stringify(helpers.sortJson(context, { depth: 25 }), null, 2))
275
- this.logs.push(lines.toString())
276
247
  }
277
- applied = true
278
- break
248
+ lines.newRow()
249
+ lines.setElement(0, 1, 'APPLIED')
250
+ lines.setElement(0, 2, generator.toLabel())
251
+ lines.newRow()
252
+ lines.setElement(0, 2, generator.toString())
253
+ lines.newRow()
254
+ lines.setElement(0, 1, 'RESULT')
255
+ lines.setElement(0, 2, generated)
256
+ lines.newRow()
257
+ lines.setElement(0, 1, 'STACK')
258
+ lines.setElement(0, 2, stack)
259
+ lines.newRow()
260
+ lines.setElement(0, 1, 'DEBUG')
261
+ lines.setElement(0, 2, `To debug this use args.callId == '${args.calls.current()}'`)
262
+ lines.newRow()
263
+ lines.setElement(0, 1, 'TO')
264
+ lines.setElement(0, 2, `(HASHCODE ${helpers.hashCode(JSON.stringify(helpers.sortJson(context, { depth: 25 })))})`)
265
+ lines.setElement(1, 2, JSON.stringify(helpers.sortJson(context, { depth: 25 }), null, 2))
266
+ this.logs.push(lines.toString())
279
267
  }
268
+ applied = true
269
+ break
280
270
  }
281
- args.calls.pop()
282
- if (!applied && ((config || {}).config || {}).debug) {
283
- const widths = [10, 10, 90]
284
- const lines = new Lines(widths)
285
- lines.setElement(0, 0, 'Generator')
286
- lines.setElement(0, 2, 'No generator applied')
287
- lines.newRow()
288
- lines.setElement(0, 1, 'STACK')
289
- lines.setElement(0, 2, stack)
290
- lines.newRow()
291
- lines.setElement(0, 1, 'TO')
292
- lines.setElement(0, 2, JSON.stringify(context, null, 2))
293
- this.logs.push(lines.toString())
294
- }
295
- contextsPrime.push((config || {}).parenthesized ? "(" + generated + ")" : generated)
296
271
  }
297
- return contextsPrime
272
+ args.calls.pop()
273
+ if (!applied && ((config || {}).config || {}).debug) {
274
+ const widths = [10, 10, 90]
275
+ const lines = new Lines(widths)
276
+ lines.setElement(0, 0, 'Generator')
277
+ lines.setElement(0, 2, 'No generator applied')
278
+ lines.newRow()
279
+ lines.setElement(0, 1, 'STACK')
280
+ lines.setElement(0, 2, stack)
281
+ lines.newRow()
282
+ lines.setElement(0, 1, 'TO')
283
+ lines.setElement(0, 2, JSON.stringify(context, null, 2))
284
+ this.logs.push(lines.toString())
285
+ }
286
+ return ((config || {}).parenthesized ? "(" + generated + ")" : generated)
298
287
  }
299
288
  }
300
289
 
package/src/helpers.js CHANGED
@@ -334,6 +334,45 @@ const ecatch = (where, call) => {
334
334
  }
335
335
  }
336
336
 
337
+ const equalKey = (key1, key2) => {
338
+ return key1[0] == key2[0] && key1[1] == key2[1]
339
+ }
340
+
341
+ // matches for { context: ..., [ordered], choose: ... } exactely OR
342
+ // [ <id1>, <id2> ] - where id1 is chosen
343
+ const subPriority = (sub, sup) => {
344
+ if (Array.isArray(sub)) {
345
+ const subChoosen = sub[0]
346
+ const subOther = sub[1]
347
+ const hasChoosen = sup.choose.find( (index) => equalKey(sup.context[index], subChoosen)) != undefined
348
+ const hasOtherChosen = sup.choose.find( (index) => equalKey(sup.context[index], subOther)) != undefined
349
+ const hasOther = sup.context.find( (other) => equalKey(other, subOther) ) !== undefined
350
+ return !!(hasChoosen && hasOther) && !hasOtherChosen
351
+ }
352
+
353
+ if (!safeEquals([...sub.choose].sort(), [...sup.choose].sort())) {
354
+ return false
355
+ }
356
+
357
+ const choose = (priority) => {
358
+ const chosen = []
359
+ for (const i of priority.choose) {
360
+ chosen.push(priority.context[i])
361
+ }
362
+ return chosen
363
+ }
364
+ const chosen1 = choose(sub)
365
+ const chosen2 = choose(sup)
366
+ const sameId = (id1, id2) => id1[0] == id2[0] && id1[1] == id2[1]
367
+ // same length so only need one way
368
+ const missing1 = chosen1.find( (id1) => !chosen2.find( (id2) => sameId(id1, id2)) )
369
+ if (missing1) {
370
+ return false
371
+ }
372
+
373
+ return true
374
+ }
375
+
337
376
  module.exports = {
338
377
  ecatch,
339
378
  functionsToStrings,
@@ -354,5 +393,6 @@ module.exports = {
354
393
  isCompound,
355
394
  InitCalls,
356
395
  hashCode,
357
- sortJson
396
+ sortJson,
397
+ subPriority,
358
398
  }
package/src/project.js CHANGED
@@ -16,6 +16,9 @@ const project = (object, filter) => {
16
16
  return object.map( element => project(element, filter) )
17
17
  } else {
18
18
  for (let properties of filter) {
19
+ if (typeof properties == 'function') {
20
+ properties = properties(object)
21
+ }
19
22
  if (typeof properties == 'object') {
20
23
  if (properties.propertyLists) {
21
24
  for (const propertyList in properties.propertyLists) {
@@ -45,7 +48,6 @@ const project = (object, filter) => {
45
48
  if (properties.property) {
46
49
  const property = properties.property
47
50
  if (properties.isPropertyList) {
48
- debugger
49
51
  if (!Array.isArray(object[property])) {
50
52
  return projection
51
53
  }