theprogrammablemind_4wp 7.6.0 → 7.7.0-beta.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -14,4 +14,5 @@ module.exports = {
14
14
  jsonDiff: "Should not be called in the browser",
15
15
  sortJson: "Should not be called in the browser",
16
16
  util: "Should not be called in the browser",
17
+ performance: "Should not be called in the browser",
17
18
  }
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
  }