theprogrammablemind_4wp 7.5.0 → 7.5.1

Sign up to get free protection for your applications and to get access to all the features.
package/client.js CHANGED
@@ -78,7 +78,15 @@ const listable = (hierarchy) => (c, type) => {
78
78
  return false
79
79
  }
80
80
 
81
- const isA = (hierarchy) => (child, parent) => hierarchy.isA(child, parent)
81
+ const isA = (hierarchy) => (child, parent) => {
82
+ if (child.marker) {
83
+ child = child.marker
84
+ }
85
+ if (parent.marker) {
86
+ parent = parent.marker
87
+ }
88
+ return hierarchy.isA(child, parent)
89
+ }
82
90
 
83
91
  const asList = (context) => {
84
92
  if (context.marker === 'list') {
@@ -518,7 +526,7 @@ const processInstance = (config, instance) => {
518
526
  global.transitoryMode = transitoryMode
519
527
  }
520
528
 
521
- const _process = async (config, query, { credentials, writeTests, isTest, saveDeveloper, testConfig, testsFN, errorHandler = defaultErrorHandler } = {}) => {
529
+ const _process = async (config, query, { commandLineArgs, credentials, writeTests, isTest, saveDeveloper, testConfig, testsFN, errorHandler = defaultErrorHandler } = {}) => {
522
530
  if (credentials) {
523
531
  config.server(credentials.server, credentials.key)
524
532
  }
@@ -539,6 +547,9 @@ const _process = async (config, query, { credentials, writeTests, isTest, saveDe
539
547
  }
540
548
 
541
549
  let { data, /* generators, semantics, */ hierarchy } = setupProcessB({ config, allowDelta: true })
550
+ if (commandLineArgs && commandLineArgs.checkForLoop) {
551
+ data.checkForLoop = true
552
+ }
542
553
  let queries = query.split('\\n')
543
554
 
544
555
  try {
@@ -670,9 +681,12 @@ const runTest = async (config, expected, { verbose, afterTest, testConfig, debug
670
681
  defaultErrorHandler(error)
671
682
  }
672
683
 
673
- const objects = getObjects(config.config.objects)(config.uuid)
684
+ let objects = getObjects(config.config.objects)(config.uuid)
685
+ if (testConfig.testModuleName) {
686
+ objects = getObjects(config.config.objects)(config.getConfigs()[testConfig.testModuleName].uuid)
687
+ }
674
688
  config.beforeQuery({ query: test, isModule: false, objects })
675
- config.resetMotivations()
689
+ // config.resetMotivations()
676
690
  try {
677
691
  const result = await _process(config, test, { errorHandler, isTest: true })
678
692
  result.query = test
@@ -692,10 +706,23 @@ const runTest = async (config, expected, { verbose, afterTest, testConfig, debug
692
706
  const failed_responses = !matching(result.responses, expected.responses)
693
707
  const failed_contexts = !matching(result.contexts, expected.contexts)
694
708
  const failed_objects = !matching(actual_objects, expected_objects)
709
+
710
+ const pickEm = (objects) => {
711
+ const picked = {}
712
+ for (let prop of (testConfig.check || [])) {
713
+ picked[prop] = objects[prop]
714
+ }
715
+ return picked
716
+ }
717
+ const expected_checked = sortJson(pickEm(expected.objects.namespaced[0]), { depth: 25 })
718
+ const actual_checked = sortJson(pickEm(objects), { depth: 25 })
719
+ const failed_checked = !matching(actual_objects, expected_objects)
720
+
721
+ const failed_checks = !matching(actual_objects, expected_objects)
695
722
  const actual_config = sortJson(convertToStable(getConfigForTest(config, testConfig)), { depth: 25 })
696
723
  const expected_config = sortJson(convertToStable(expected.config), { depth: 25 })
697
724
  const failed_config = !matching(actual_config, expected_config)
698
- let failed = failed_paraphrases || failed_responses || failed_contexts || failed_objects || failed_config
725
+ let failed = failed_paraphrases || failed_responses || failed_contexts || failed_objects || failed_config || failed_checked
699
726
  if (!failed) {
700
727
  if (config.afterTest) {
701
728
  failed = config.afterTest({ query: test, expected, actual: result, config })
@@ -703,8 +730,8 @@ const runTest = async (config, expected, { verbose, afterTest, testConfig, debug
703
730
  return {
704
731
  utterance: test,
705
732
  errorFromAfterTest: failed,
706
- expected: { responses: expected.responses, paraphrases: expected.paraphrases, results: expected.contexts, objects: expected_objects, config: expected.config },
707
- actual: { responses: result.responses, paraphrases: result.paraphrases, results: result.contexts, objects: actual_objects, config: actual_config }
733
+ expected: { responses: expected.responses, paraphrases: expected.paraphrases, results: expected.contexts, checked: expected_checked, objects: expected_objects, config: expected.config },
734
+ actual: { responses: result.responses, paraphrases: result.paraphrases, results: result.contexts, checked: actual_checked, objects: actual_objects, config: actual_config }
708
735
  }
709
736
  }
710
737
  }
@@ -720,8 +747,8 @@ const runTest = async (config, expected, { verbose, afterTest, testConfig, debug
720
747
  if (failed) {
721
748
  return {
722
749
  utterance: test,
723
- expected: { responses: expected.responses, paraphrases: expected.paraphrases, results: expected.contexts, objects: expected_objects, config: expected.config },
724
- actual: { responses: result.responses, paraphrases: result.paraphrases, results: result.contexts, objects: actual_objects, config: actual_config }
750
+ expected: { responses: expected.responses, paraphrases: expected.paraphrases, results: expected.contexts, checked: expected_checked, objects: expected_objects, config: expected.config },
751
+ actual: { responses: result.responses, paraphrases: result.paraphrases, results: result.contexts, checked: actual_checked, objects: actual_objects, config: actual_config }
725
752
  }
726
753
  }
727
754
  } catch(error) {
@@ -770,7 +797,7 @@ const runTests = async (config, testFile, juicyBits) => {
770
797
  const saveTest = async (testFile, config, test, expected, testConfig, saveDeveloper) => {
771
798
  config.rebuild()
772
799
  const objects = getObjects(config.config.objects)(config.uuid)
773
- config.resetMotivations()
800
+ //config.resetMotivations()
774
801
  config.beforeQuery({ query: test, isModule: false, objects })
775
802
  console.log(test)
776
803
  const result = await _process(config, test, { isTest: true })
@@ -845,6 +872,7 @@ const submitBugToAPI = async (subscription_id, subscription_password, config) =>
845
872
  })
846
873
  }
847
874
 
875
+ /*
848
876
  const submitBug = async (subscription_id, subscription_password, config, utterance, retries = 2) => {
849
877
  // TODO remove these from the config
850
878
  const properties = ['expected_contexts', 'expected_generated']
@@ -881,6 +909,7 @@ const submitBug = async (subscription_id, subscription_password, config, utteran
881
909
  throw error
882
910
  })
883
911
  }
912
+ */
884
913
 
885
914
  const defaultErrorHandler = async (error) => {
886
915
  if (error.logs) {
@@ -1035,7 +1064,6 @@ const build = async ({ config, target, template, errorHandler = defaultErrorHand
1035
1064
  if (typeof queryOrExtraConfig === 'string') {
1036
1065
  query = { query }
1037
1066
  }
1038
- console.log('query', query.query)
1039
1067
  config.config.skipSemantics = skipSemantics
1040
1068
  const transitoryMode = global.transitoryMode
1041
1069
  if (property == 'fragments') {
@@ -1059,6 +1087,11 @@ const build = async ({ config, target, template, errorHandler = defaultErrorHand
1059
1087
  // TODO pass in the error handler like the other ones
1060
1088
  defaultInnerProcess(config, defaultErrorHandler, results)
1061
1089
  }
1090
+ if (results.contexts.length > 1) {
1091
+ console.log(`query ${query.query}. There is ${results.contexts.length} contexts in the results. Make sure its producing the results that you expect.`)
1092
+ } else {
1093
+ console.log(`query ${query.query}`)
1094
+ }
1062
1095
  global.transitoryMode = transitoryMode
1063
1096
  config.config.skipSemantics = null
1064
1097
  results.query = query.query
@@ -1239,7 +1272,7 @@ const knowledgeModule = async ({
1239
1272
  description: 'Entodicton knowledge module'
1240
1273
  })
1241
1274
 
1242
- parser.add_argument('-tfn', '--testFileName', { help: 'Override the test file for the module when the tests are being run' })
1275
+ parser.add_argument('-tmn', '--testModuleName', { help: 'When running tests instead of using the current modules tests use the specified modules tests' })
1243
1276
  parser.add_argument('-t', '--test', { action: 'store_true', help: 'Run the tests. Create tests by running with the --query + --save flag' })
1244
1277
  parser.add_argument('-tv', '--testVerbose', { action: 'store_true', help: 'Run the tests in verbose mode. Create tests by running with the --query or --loop with the --save flag' })
1245
1278
  parser.add_argument('-tva', '--testAllVerbose', { action: 'store_true', help: 'Run the tests in verbose mode. All the tests will be run instead of stopping at first failure. Create tests by running with the --query or --loop with the --save flag' })
@@ -1250,11 +1283,12 @@ const knowledgeModule = async ({
1250
1283
  parser.add_argument('-i', '--info', { action: 'store_true', help: 'Print meta-data for the module' })
1251
1284
  parser.add_argument('-v', '--vimdiff', { action: 'store_true', help: 'For failures run vimdiff' })
1252
1285
  parser.add_argument('-g', '--greg', { action: 'store_true', help: 'Set the server to be localhost so I can debug stuff' })
1286
+ parser.add_argument('-cl', '--checkForLoop', { action: 'store_true', help: 'Check for loops in the priorities' })
1253
1287
  parser.add_argument('-r', '--retrain', { action: 'store_true', help: 'Get the server to retrain the neural nets' })
1254
1288
  parser.add_argument('-q', '--query', { help: 'Run the specified query' })
1255
1289
  parser.add_argument('-ip ', '--server', { help: 'Server to run against' })
1256
1290
  parser.add_argument('-qp ', '--queryParams', { help: 'Query params for the server call' })
1257
- parser.add_argument('-td', '--testDelete', { help: 'Delete the specified query from the tests file.' })
1291
+ parser.add_argument('-dt', '--deleteTest', { help: 'Delete the specified query from the tests file.' })
1258
1292
  parser.add_argument('-c', '--clean', { help: 'Remove data from the test files. a === association' })
1259
1293
  parser.add_argument('-od', '--objectDiff', { action: 'store_true', help: 'When showing the objects use a colour diff' })
1260
1294
  parser.add_argument('-daa', '--dontAddAssociations', { action: 'store_true', help: 'Do not add associations from the tests.' })
@@ -1293,11 +1327,11 @@ const knowledgeModule = async ({
1293
1327
  return
1294
1328
  }
1295
1329
 
1296
- if (args.testDelete) {
1330
+ if (args.deleteTest) {
1297
1331
  let tests = JSON.parse(runtime.fs.readFileSync(testConfig.name))
1298
- tests = tests.filter( (test) => test.query !== args.testDelete );
1332
+ tests = tests.filter( (test) => test.query !== args.deleteTest );
1299
1333
  writeTestFile(testConfig.name, tests)
1300
- console.log(`Remove the test for "${args.testDelete}"`)
1334
+ console.log(`Remove the test for "${args.deleteTest}"`)
1301
1335
  return
1302
1336
  }
1303
1337
 
@@ -1453,7 +1487,15 @@ const knowledgeModule = async ({
1453
1487
  }
1454
1488
  return
1455
1489
  }
1456
- runTests(config, args.testFileName ? `${args.testFileName}.test.json` : test, { debug: args.debug, testConfig: testConfig, verbose: args.testVerbose || args.testAllVerbose, stopAtFirstError: !args.testAllVerbose }).then((results) => {
1490
+ let useTestConfig = testConfig
1491
+ if (args.testModuleName) {
1492
+ useTestConfig = config.getConfigs()[args.testModuleName].getTestConfig()
1493
+ useTestConfig.testModuleName = args.testModuleName
1494
+ test = useTestConfig.name
1495
+
1496
+ }
1497
+ // runTests(config, args.testFileName ? `${args.testFileName}.test.json` : test, { debug: args.debug, testConfig: testConfig, verbose: args.testVerbose || args.testAllVerbose, stopAtFirstError: !args.testAllVerbose }).then((results) => {
1498
+ runTests(config, test, { debug: args.debug, testConfig: useTestConfig, verbose: args.testVerbose || args.testAllVerbose, stopAtFirstError: !args.testAllVerbose }).then((results) => {
1457
1499
  if (results.length > 0 && args.vimdiff) {
1458
1500
  for (const result of results) {
1459
1501
  vimdiff(result.expected, result.actual)
@@ -1462,34 +1504,69 @@ const knowledgeModule = async ({
1462
1504
  let newError = false
1463
1505
  if (results.length > 0) {
1464
1506
  let headerShown = false
1465
- console.log('**************************** ERRORS ************************')
1507
+
1508
+ let hasError = false
1466
1509
  for (const result of results) {
1467
- console.log('Utterance: ', result.utterance)
1468
1510
  if (JSON.stringify(result.expected.paraphrases) !== JSON.stringify(result.actual.paraphrases)) {
1469
- if (!headerShown) {
1470
- console.log(' Failure')
1471
- }
1472
- console.log(' expected paraphrases', result.expected.paraphrases)
1473
- console.log(' actual paraphrases ', result.actual.paraphrases)
1474
- newError = true
1475
- headerShown = true
1511
+ hasError = true
1476
1512
  }
1477
1513
  if (JSON.stringify(result.expected.responses) !== JSON.stringify(result.actual.responses)) {
1478
- if (!headerShown) {
1479
- console.log(' Failure')
1514
+ hasError = true
1515
+ }
1516
+ if (JSON.stringify(result.expected.checked) !== JSON.stringify(result.actual.checked)) {
1517
+ hasError = true
1518
+ }
1519
+ }
1520
+
1521
+ if (hasError) {
1522
+ console.log('**************************** ERRORS ************************')
1523
+ for (const result of results) {
1524
+ console.log('Utterance: ', result.utterance)
1525
+ if (JSON.stringify(result.expected.paraphrases) !== JSON.stringify(result.actual.paraphrases)) {
1526
+ if (!headerShown) {
1527
+ console.log(' Failure')
1528
+ }
1529
+ console.log(' expected paraphrases', result.expected.paraphrases)
1530
+ console.log(' actual paraphrases ', result.actual.paraphrases)
1531
+ newError = true
1532
+ headerShown = true
1533
+ }
1534
+ if (JSON.stringify(result.expected.responses) !== JSON.stringify(result.actual.responses)) {
1535
+ if (!headerShown) {
1536
+ console.log(' Failure')
1537
+ }
1538
+ console.log(' expected responses ', result.expected.responses)
1539
+ console.log(' actual responses ', result.actual.responses)
1540
+ newError = true
1541
+ headerShown = true
1542
+ }
1543
+ if (JSON.stringify(result.expected.checked) !== JSON.stringify(result.actual.checked)) {
1544
+ if (!headerShown) {
1545
+ console.log(' Failure')
1546
+ }
1547
+ const widths = [4, 18, 72]
1548
+ const lines = new Lines(widths)
1549
+ lines.setElement(1, 1, 'expected checked')
1550
+ lines.setElement(2, 2, JSON.stringify(result.expected.checked, null, 2))
1551
+ lines.log()
1552
+ lines.setElement(1, 1, 'actual checked')
1553
+ lines.setElement(2, 2, JSON.stringify(result.actual.checked, null, 2))
1554
+ lines.log()
1555
+ newError = true
1556
+ headerShown = true
1480
1557
  }
1481
- console.log(' expected responses ', result.expected.responses)
1482
- console.log(' actual responses ', result.actual.responses)
1483
- newError = true
1484
- headerShown = true
1485
1558
  }
1486
1559
  }
1487
1560
  if (!headerShown) {
1488
- console.log('There are failures due to things other than paraphrases and response 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.')
1561
+ if (!(useTestConfig.check && useTestConfig.check.length > 0)) {
1562
+ 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.')
1563
+ }
1564
+ }
1565
+ if (!(useTestConfig.check && useTestConfig.check.length > 0)) {
1566
+ 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.')
1567
+ // console.log(JSON.stringify(contexts))
1568
+ console.log('**************************** ERRORS ************************')
1489
1569
  }
1490
- 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.')
1491
- // console.log(JSON.stringify(contexts))
1492
- console.log('**************************** ERRORS ************************')
1493
1570
  }
1494
1571
  // const contexts = { failures: results }
1495
1572
  l(n - 1, hasError || newError)
@@ -1540,13 +1617,14 @@ const knowledgeModule = async ({
1540
1617
  }
1541
1618
  config.beforeQuery({ query: args.query, isModule: false, objects })
1542
1619
  try {
1543
- processResults(_process(config, args.query, { dontAddAssociations: args.dontAddAssociations, writeTests: args.save || args.saveDeveloper, saveDeveloper: args.saveDeveloper, testConfig, testsFN: test }))
1620
+ processResults(_process(config, args.query, { commandLineArgs: args, dontAddAssociations: args.dontAddAssociations, writeTests: args.save || args.saveDeveloper, saveDeveloper: args.saveDeveloper, testConfig, testsFN: test }))
1544
1621
  } catch( error ) {
1545
1622
  console.log('Error', error);
1546
1623
  }
1547
1624
  }
1548
1625
  } else {
1549
1626
  config.addAssociationsFromTests(config.tests);
1627
+ config.setTestConfig(testConfig)
1550
1628
  //for (let query in config.tests) {
1551
1629
  // config.addAssociations(config.tests[query].associations || []);
1552
1630
  //}
@@ -1596,7 +1674,7 @@ module.exports = {
1596
1674
  process: _process,
1597
1675
  where,
1598
1676
  w,
1599
- submitBug,
1677
+ // submitBug,
1600
1678
  ensureTestFile,
1601
1679
  build,
1602
1680
  processContext,
package/lines.js CHANGED
@@ -12,6 +12,9 @@ class Lines {
12
12
  // will wrap to next line within the column
13
13
  setElement (row, column, value) {
14
14
  const values = value.toString().split('\n')
15
+ if (column >= this.widths.length) {
16
+ throw "Column out of range."
17
+ }
15
18
  const width = this.widths[column]
16
19
  let index = 0
17
20
  for (value of values) {
package/package.json CHANGED
@@ -61,6 +61,6 @@
61
61
  "json-stable-stringify": "^1.0.1",
62
62
  "node-fetch": "^2.6.1"
63
63
  },
64
- "version": "7.5.0",
64
+ "version": "7.5.1",
65
65
  "license": "ISC"
66
66
  }
package/src/config.js CHANGED
@@ -330,6 +330,17 @@ class Config {
330
330
  return `${maybeName}${counter}`
331
331
  }
332
332
 
333
+ setTestConfig(testConfig) {
334
+ if (this.name == 'ui') {
335
+ console.log('ui setting testConfig')
336
+ }
337
+ this.testConfig = testConfig
338
+ }
339
+
340
+ getTestConfig() {
341
+ return this.testConfig
342
+ }
343
+
333
344
  defaultConfig () {
334
345
  this.config = {
335
346
  operators: [], // TODO
@@ -412,11 +423,11 @@ class Config {
412
423
  }
413
424
 
414
425
  getSemantics (logs = []) {
415
- return new Semantics(this.config.semantics || [], logs, { km: this.name })
426
+ return new Semantics(this.config.semantics, logs, { km: this.name })
416
427
  }
417
428
 
418
429
  getGenerators (logs = []) {
419
- return new Generators(this.config.generators || [], logs, { km: this.name })
430
+ return new Generators(this.config.generators, logs, { km: this.name })
420
431
  }
421
432
 
422
433
  warningNotEvaluated (log, value) {
@@ -531,9 +542,23 @@ class Config {
531
542
  client.build({ config: this, target: this.name, beforeQuery: () => {}, template, ...options })
532
543
  } else {
533
544
  // no change
534
- this.initInstances.push(instance)
535
- this.instances.push(instance)
536
- client.processInstance(this, instance)
545
+ // this.initInstances.push({ ...instance, name: config.name })
546
+ const isEmpty = ( instance ) => {
547
+ const properties = [
548
+ "queries",
549
+ "resultss",
550
+ "fragments",
551
+ "semantics",
552
+ "associations",
553
+ ]
554
+ return !properties.find( (property) => instance[property].length > 0 )
555
+ }
556
+ if (!isEmpty(instance)) {
557
+ instance.name = this.name
558
+ this.initInstances.push(instance)
559
+ this.instances.push(instance)
560
+ client.processInstance(this, instance)
561
+ }
537
562
  }
538
563
  }
539
564
 
@@ -593,18 +618,21 @@ class Config {
593
618
  }
594
619
  }
595
620
 
596
- addHierarchyProperties (properties) {
597
- if (typeof properties.child !== 'string') {
621
+ addHierarchyProperties (edge) {
622
+ const { child, parent } = edge
623
+ if (typeof child !== 'string') {
598
624
  throw `addHierarchy expected child property to be a string. got ${JSON.stringify(child)}`
599
625
  }
600
- if (typeof properties.parent !== 'string') {
601
- throw `addHierarchy expected parent properties to be a string. got ${JSON.stringify(parent)}`
626
+ if (typeof parent !== 'string') {
627
+ throw `addHierarchy expected parent property to be a string. got ${JSON.stringify(parent)}`
602
628
  }
603
- if (!this.config.hierarchy) {
604
- this.config.hierarchy = []
629
+ if (global.entodictonDebugHierarchy) {
630
+ if (deepEqual(global.entodictonDebugHierarchy, [child, parent])) {
631
+ debugger; // debug hierarchy hit
632
+ }
605
633
  }
606
- // this.config.hierarchy.push([properties.child, properties.parent])
607
- this.config.hierarchy.push(properties)
634
+ this.config.hierarchy.push(edge)
635
+ // TODO greg11 this.hierarchy.addEdge(edge)
608
636
  this._delta.json.hierarchy.push([child, parent])
609
637
  }
610
638
 
@@ -615,14 +643,13 @@ class Config {
615
643
  if (typeof parent !== 'string') {
616
644
  throw `addHierarchy expected parent to be a string. got ${JSON.stringify(parent)}`
617
645
  }
618
- if (!this.config.hierarchy) {
619
- this.config.hierarchy = []
620
- }
646
+
621
647
  if (global.entodictonDebugHierarchy) {
622
648
  if (deepEqual(global.entodictonDebugHierarchy, [child, parent])) {
623
649
  debugger; // debug hierarchy hit
624
650
  }
625
651
  }
652
+
626
653
  if (this.config.hierarchy.find( (element) => {
627
654
  const hc = hierarchyCanonical(element)
628
655
  if (child == hc.child && parent == hc.parent) {
@@ -633,6 +660,7 @@ class Config {
633
660
  }
634
661
 
635
662
  this.config.hierarchy.push([child, parent])
663
+ // this.hierarchy.addEdge([child, parent])
636
664
  this._delta.json.hierarchy.push([child, parent])
637
665
  }
638
666
 
@@ -953,17 +981,11 @@ class Config {
953
981
  if (!config) {
954
982
  return
955
983
  }
956
- config.operators = config.operators || []
957
- config.bridges = config.bridges || []
958
- config.words = config.words || {}
959
- config.generators = config.generators || []
960
- config.semantics = config.semantics || []
961
-
962
984
  config.operators = config.operators.filter( (element) => !element.development )
963
985
  config.bridges = config.bridges.filter( (element) => !element.development )
964
986
  config.generators = config.generators.filter( (element) => !element.development )
965
987
  config.semantics = config.semantics.filter( (element) => !element.development )
966
- config.hierarchy = (config.hierarchy || []).filter( (element) => !element.development )
988
+ config.hierarchy = (config.hierarchy).filter( (element) => !element.development )
967
989
  for (const word in config.words) {
968
990
  const defs = config.words[word] || []
969
991
  config.words[word] = defs.filter( (def) => !def.development )
@@ -979,6 +1001,44 @@ class Config {
979
1001
  throw 'Excepted the config argument to be a hash not a Config object'
980
1002
  }
981
1003
 
1004
+ if (config) {
1005
+ const valid = [
1006
+ 'hierarchy',
1007
+ 'objects',
1008
+ 'bridges',
1009
+ 'operators',
1010
+ 'words',
1011
+ 'priorities',
1012
+ 'associations',
1013
+ 'name',
1014
+ 'version',
1015
+ 'generators',
1016
+ 'semantics',
1017
+ 'floaters',
1018
+ 'debug',
1019
+
1020
+ // TODO Fix these from the test app
1021
+ 'implicit',
1022
+ 'expected_generated',
1023
+ 'expected_results',
1024
+ 'skipSemantics',
1025
+ 'description',
1026
+ 'contexts',
1027
+ 'utterances',
1028
+ 'flatten',
1029
+ ]
1030
+ helpers.validProps(valid, config, 'config')
1031
+
1032
+ config.operators = config.operators || []
1033
+ config.bridges = config.bridges || []
1034
+ config.words = config.words || {}
1035
+ config.generators = config.generators || []
1036
+ config.semantics = config.semantics || []
1037
+ config.hierarchy = config.hierarchy || []
1038
+ config.associations = config.associations || { negative: [], positive: [] }
1039
+ config.priorities = config.priorities || []
1040
+ }
1041
+
982
1042
  this.allowDelta = false
983
1043
  this.resetDelta()
984
1044
 
@@ -1030,7 +1090,8 @@ class Config {
1030
1090
  normalizeConfig(config)
1031
1091
 
1032
1092
  // set the default server so stuff just works
1033
- this.server('http://184.67.27.82:3000', '6804954f-e56d-471f-bbb8-08e3c54d9321')
1093
+ // this.server('https://184.67.27.82:3000', '6804954f-e56d-471f-bbb8-08e3c54d9321')
1094
+ this.server('https://thinktelligence.com:3000', '6804954f-e56d-471f-bbb8-08e3c54d9321')
1034
1095
 
1035
1096
  this.defaultConfig()
1036
1097
  this.initializerFn = ({ currentConfig }) => {
@@ -1053,25 +1114,10 @@ class Config {
1053
1114
  if (config) {
1054
1115
  config = _.cloneDeep(config)
1055
1116
  this.config = config
1056
- if (!this.config.generators) {
1057
- this.config.generators = []
1058
- }
1059
- if (!this.config.semantics) {
1060
- this.config.semantics = []
1061
- }
1062
- if (!this.config.words) {
1063
- // this.config.words = {}
1064
- }
1065
- for (let bridge of (this.config.bridges || [])) {
1066
- const valid = [ 'before', 'bridge', 'development', 'evaluator', 'generatorp', 'generatorr', 'generators', 'hierarchy', 'id', 'inverted', 'isA',
1067
- 'level', 'optional', 'selector', 'semantic', 'uuid', 'words' ]
1068
- for (let prop of Object.keys(bridge)) {
1069
- if (!valid.includes(prop)) {
1070
- if (!prop.endsWith("Bridge")) {
1071
- throw `Unknown property "${prop}" in the bridge. Valid properties are ${valid}. The bridge is ${JSON.stringify(bridge)}`
1072
- }
1073
- }
1074
- }
1117
+ for (let bridge of this.config.bridges) {
1118
+ const valid = [ 'before', 'bridge', 'development', 'evaluator', 'generatorp', 'generatorr', 'generators', 'id', 'implicit', 'inverted', 'isA', 'children', 'parents',
1119
+ 'level', 'optional', 'selector', 'semantic', 'words', /Bridge$/, 'localHierarchy' ]
1120
+ helpers.validProps(valid, bridge, 'bridge')
1075
1121
  /*
1076
1122
  if (bridge.generator) {
1077
1123
  this.config.generators.push({
@@ -1080,6 +1126,16 @@ class Config {
1080
1126
  })
1081
1127
  }
1082
1128
  */
1129
+ if (bridge.children) {
1130
+ for (let child of bridge.children) {
1131
+ this.addHierarchy(child, bridge.id)
1132
+ }
1133
+ }
1134
+ if (bridge.parents) {
1135
+ for (let parent of bridge.parents) {
1136
+ this.addHierarchy(bridge.id, parent)
1137
+ }
1138
+ }
1083
1139
  if (bridge.isA) {
1084
1140
  for (let parent of bridge.isA) {
1085
1141
  this.addHierarchy(bridge.id, parent)
@@ -1087,6 +1143,9 @@ class Config {
1087
1143
  }
1088
1144
  if (bridge.before) {
1089
1145
  for (let after of bridge.before) {
1146
+ if (typeof after == 'string') {
1147
+ after = [after, 0]
1148
+ }
1090
1149
  this.addPriorities([after, [bridge.id, bridge.level]])
1091
1150
  }
1092
1151
  }
@@ -1151,6 +1210,7 @@ class Config {
1151
1210
  })
1152
1211
  }
1153
1212
  }
1213
+ this.hierarchy = new Digraph(this.config.hierarchy)
1154
1214
  this.initConfig = _.cloneDeep(this.config)
1155
1215
  this.configs.push(new KM({ config: this.config, getCounter: (name) => this.config.getCounter(name), uuid: this._uuid }))
1156
1216
 
@@ -1238,6 +1298,10 @@ class Config {
1238
1298
  }
1239
1299
 
1240
1300
  set api (value) {
1301
+ if (!value.initialize) {
1302
+ throw `Expected the API to have an initialize function for ${this.name}.`
1303
+ }
1304
+
1241
1305
  if (this._api && this._api.multiApi) {
1242
1306
  this._api.add(this, this._api, value)
1243
1307
  } else {
@@ -1350,12 +1414,13 @@ class Config {
1350
1414
  cp.name = this.name
1351
1415
  cp.description = this.description
1352
1416
  cp.tests = this.tests
1353
- cp.motivations = this.motivations
1417
+ cp.motivations = [...this.motivations]
1354
1418
  cp.isModule = this.isModule
1355
1419
  cp.loadedForTesting = this.loadedForTesting
1356
1420
  cp.initInstances = this.initInstances.slice()
1357
1421
  cp.instances = this.instances.slice()
1358
1422
  cp.configCounter = this.configCounter
1423
+ cp.testConfig = this.testConfig
1359
1424
 
1360
1425
  cp.initConfig = _.cloneDeep(this.initConfig)
1361
1426
  cp.defaultConfig()
@@ -1401,7 +1466,7 @@ class Config {
1401
1466
  this.config.bridges && this.config.bridges.forEach((bridge) => { bridge.uuid = this._uuid })
1402
1467
  this.config.words && setWordsUUIDs(this.config.words, this._uuid)
1403
1468
  this.config.operators && this.config.operators.forEach((operator) => { operator.uuid = this._uuid })
1404
- const ids = Array.from(new Set((this.config.bridges && this.config.bridges.map((bridge) => bridge.id)) || []))
1469
+ const ids = Array.from(new Set(this.config.bridges.map((bridge) => bridge.id)))
1405
1470
  ids.sort()
1406
1471
  this.config.namespaces = {}
1407
1472
  // if (true || ids.length > 0) {
@@ -1482,7 +1547,8 @@ class Config {
1482
1547
  }
1483
1548
 
1484
1549
  initializeFromConfigs () {
1485
- this.configs.forEach(({ config, namespace, uuid }) => {
1550
+ // [...this.configs].reverse().forEach(({ config, namespace, uuid }) => {
1551
+ [...this.configs].reverse().forEach(({ config, namespace, uuid }) => {
1486
1552
  /*
1487
1553
  let objects = this.get('objects')
1488
1554
  if (namespace) {
@@ -1617,6 +1683,13 @@ class Config {
1617
1683
  return false
1618
1684
  }
1619
1685
 
1686
+ /* TODO greg11
1687
+ if (!this.hierarchy) {
1688
+ debugBreak()
1689
+ return false
1690
+ }
1691
+ */
1692
+
1620
1693
  for (const key in this.config.words) {
1621
1694
  const values = this.config.words[key]
1622
1695
  if (values.some((word) => (Object.keys(word).includes('uuid') && !word.uuid))) {
@@ -1626,7 +1699,7 @@ class Config {
1626
1699
  }
1627
1700
 
1628
1701
  const kmsUuids = this.configs.map((km) => km.uuid)
1629
- const bridgesUuids = (this.config.bridges && this.config.bridges.map((bridge) => bridge.uuid).filter((uuid) => uuid)) || []
1702
+ const bridgesUuids = this.config.bridges.map((bridge) => bridge.uuid).filter((uuid) => uuid)
1630
1703
  let result = true
1631
1704
  bridgesUuids.forEach((buuid) => {
1632
1705
  if (!kmsUuids.includes(buuid)) {
@@ -1696,6 +1769,7 @@ class Config {
1696
1769
  this.resetDelta()
1697
1770
  const debug = this.config.debug;
1698
1771
  this.config = _.cloneDeep(this.initConfig)
1772
+ this.hierarchy = new Digraph(this.config.hierarchy)
1699
1773
  if (debug) {
1700
1774
  this.config.debug = debug
1701
1775
  }
@@ -1710,6 +1784,7 @@ class Config {
1710
1784
  }
1711
1785
  this.config.objects.namespaced = {}
1712
1786
  this.resetWasInitialized()
1787
+ this.resetMotivations()
1713
1788
 
1714
1789
  // reorder configs base on load ordering
1715
1790
  {
@@ -1725,8 +1800,17 @@ class Config {
1725
1800
  this.configs = [...oconfigs]
1726
1801
  }
1727
1802
 
1803
+ const addInternals = []
1728
1804
  const inits = []
1729
1805
  const initAfterApis = []
1806
+ if (false && this.config.hierarchy.find( (pair) => JSON.stringify(pair) === JSON.stringify(["equipable2","property"]))) {
1807
+ debugger // happened
1808
+ }
1809
+ if (false && this.name == 'countable') {
1810
+ debugger // in rebuild
1811
+ }
1812
+ const reverseIt = true
1813
+ const interleaved = true
1730
1814
  this.configs.forEach((km) => {
1731
1815
  const namespace = km.namespace
1732
1816
  this.config.objects.namespaced[km._uuid] = {}
@@ -1755,10 +1839,16 @@ class Config {
1755
1839
  config.wasInitialized = false
1756
1840
  // TODO change name of config: to baseConfig:
1757
1841
  const kmFn = (name) => this.getConfig(name)
1758
- const args = { isModule, addWord: aw, km: kmFn, config, baseConfig: this, currentConfig: config, uuid: config._uuid, objects: namespacedObjects, namespace, api: config.api }
1842
+ // const hierarchy = new Digraph((config.config || {}).hierarchy)
1843
+ const args = { isModule, addWord: aw, km: kmFn, hierarchy: this.hierarchy, config, baseConfig: this, currentConfig: config, uuid: config._uuid, objects: namespacedObjects, namespace, api: config.api }
1759
1844
  config.initializerFn(args)
1760
1845
  if (config.initAfterApi) {
1761
- initAfterApis.push({ config, args })
1846
+ // reverse the list
1847
+ initAfterApis.unshift({ config, args })
1848
+ } else {
1849
+ if (interleaved) {
1850
+ initAfterApis.unshift(null)
1851
+ }
1762
1852
  }
1763
1853
  // greg
1764
1854
  if (config._api) {
@@ -1766,27 +1856,112 @@ class Config {
1766
1856
  // reverse the list
1767
1857
  inits.unshift( () => config._api.initialize({ config: this, km: kmFn, api: config._api }) )
1768
1858
  // config._api.initialize({ config, api: config._api })
1859
+ } else {
1860
+ if (interleaved) {
1861
+ inits.unshift(null)
1862
+ }
1769
1863
  }
1770
1864
  config._api.objects = namespacedObjects
1771
1865
  config._api.config = () => this
1772
1866
  config._api.uuid = config._uuid
1867
+ } else {
1868
+ if (interleaved) {
1869
+ inits.unshift(null)
1870
+ }
1773
1871
  }
1774
1872
  config.setUUIDs()
1775
1873
  config.applyNamespace(config.config, namespace, config.uuid)
1776
1874
  if (!isSelf) {
1777
- this.addInternal(config, true, false, false, true)
1875
+ if (!reverseIt) {
1876
+ addInternals.push(config)
1877
+ } else {
1878
+ addInternals.unshift(config)
1879
+ }
1880
+ // this.addInternal(config, true, false, false, true)
1881
+ } else {
1882
+ if (interleaved) {
1883
+ addInternals.unshift(null)
1884
+ }
1778
1885
  }
1779
1886
  km.valid()
1780
1887
  })
1781
- // console.log('inits from config', inits)
1782
- for (const init of inits) {
1783
- init()
1888
+
1889
+ if (addInternals.length !== inits.length || addInternals.length !== initAfterApis.length) {
1890
+ debugger // bug
1784
1891
  }
1785
- for (let init of initAfterApis) {
1786
- // init.args.isAfterApi = true
1787
- init.config.initializerFn({ ...init.args, kms: this.getConfigs(), isAfterApi: true })
1892
+
1893
+ const generators = this.config.generators
1894
+ const semantics = this.config.semantics
1895
+ if (reverseIt) {
1896
+ this.config.generators = []
1897
+ this.config.semantics = []
1788
1898
  }
1789
- this.instances.forEach((instance) => client.processInstance(this, instance))
1899
+
1900
+ if (!interleaved) {
1901
+ for (const config of addInternals) {
1902
+ if (!reverseIt) {
1903
+ this.addInternal(config, true, false, false, true)
1904
+ } else {
1905
+ this.addInternalR(config, true, false, false, true)
1906
+ }
1907
+ }
1908
+ // console.log('inits from config', inits)
1909
+ for (const init of inits) {
1910
+ init()
1911
+ }
1912
+ for (let init of initAfterApis) {
1913
+ // init.args.isAfterApi = true
1914
+ init.config.initializerFn({ ...init.args, kms: this.getConfigs(), isAfterApi: true })
1915
+ }
1916
+ this.instances.forEach((instance) => client.processInstance(this, instance))
1917
+ } else {
1918
+ const base = {
1919
+ operators: this.config.operators,
1920
+ bridges: this.config.bridges,
1921
+ hierarchy: this.config.hierarchy,
1922
+ priorities: this.config.priorities,
1923
+ associations: this.config.associations,
1924
+ words: this.config.words
1925
+ }
1926
+
1927
+ this.config.operators = []
1928
+ this.config.bridges = []
1929
+ this.config.hierarchy = []
1930
+ this.config.priorities = []
1931
+ this.config.associations = { positive: [], negative: [] }
1932
+ this.config.words = {}
1933
+
1934
+ for (let i = 0; i < addInternals.length; ++i) {
1935
+ let name;
1936
+ if (addInternals[i]) {
1937
+ this.addInternalR(addInternals[i], true, false, false, true)
1938
+ name = addInternals[i].name
1939
+ } else{
1940
+ this.addInternalR(base, true, false, false, true)
1941
+ name = this.name
1942
+ }
1943
+ // console.log('name -------------', name)
1944
+ if (inits[i]) {
1945
+ inits[i]()
1946
+ }
1947
+ if (initAfterApis[i]) {
1948
+ const init = initAfterApis[i]
1949
+ init.config.initializerFn({ ...init.args, kms: this.getConfigs(), isAfterApi: true })
1950
+ }
1951
+ const instance = this.instances.find((instance) => instance.name == name)
1952
+ if (instance) {
1953
+ client.processInstance(this, instance)
1954
+ }
1955
+ this.hierarchy.edges = this.config.hierarchy
1956
+ }
1957
+ // this.instances.forEach((instance) => client.processInstance(this, instance))
1958
+ }
1959
+
1960
+ if (reverseIt) {
1961
+ this.config.generators = generators.concat(this.config.generators)
1962
+ this.config.semantics = semantics.concat(this.config.semantics)
1963
+ }
1964
+ this.hierarchy.edges = this.config.hierarchy
1790
1965
  this.valid()
1791
1966
  this.checkBridges()
1792
1967
  }
@@ -1952,8 +2127,7 @@ class Config {
1952
2127
  }
1953
2128
 
1954
2129
  if (config.hierarchy) {
1955
- let hierarchy = config.hierarchy
1956
- hierarchy = hierarchy.map((h) => {
2130
+ helpers.mapInPlace(config.hierarchy, (h) => {
1957
2131
  if (Array.isArray(h)) {
1958
2132
  return h.map((id) => toNS(id))
1959
2133
  } else {
@@ -1962,7 +2136,6 @@ class Config {
1962
2136
  return Object.assign({}, h, { child: toNS(h.child), parent: toNS(h.parent) })
1963
2137
  }
1964
2138
  })
1965
- config.hierarchy = hierarchy
1966
2139
  }
1967
2140
 
1968
2141
  if (config.priorities) {
@@ -2155,6 +2328,13 @@ class Config {
2155
2328
  this.configs.forEach((km) => {
2156
2329
  this.instances = (km._config.instances || this.initInstances.slice()).concat(this.instances)
2157
2330
  })
2331
+ let noDups = []
2332
+ for (let instance of this.instances) {
2333
+ if (!noDups.find( (existing) => existing.name == instance.name )) {
2334
+ noDups.push(instance)
2335
+ }
2336
+ }
2337
+ this.instances = noDups
2158
2338
 
2159
2339
  this.rebuild()
2160
2340
  this.valid()
@@ -2272,6 +2452,128 @@ class Config {
2272
2452
  }
2273
2453
  return this
2274
2454
  }
2455
+
2456
+ // assumes this is called in reverse order
2457
+ addInternalR (more, useOldVersion = true, skipObjects = false, includeNamespaces = true, allowNameToBeNull = false) {
2458
+ if (more instanceof Config) {
2459
+ more.initialize({ force: false })
2460
+ if (useOldVersion) {
2461
+ more = more.config
2462
+ } else {
2463
+ // more = more.initConfig
2464
+ more = _.cloneDeep(more.initConfig)
2465
+ }
2466
+ }
2467
+ for (const key of Object.keys(more)) {
2468
+ const value = more[key]
2469
+ // TODO remove name and description on the config bag
2470
+ const noOverwrite = ['name', 'namespaced']
2471
+ if (!this.config[key]) {
2472
+ if (allowNameToBeNull) {
2473
+ if (noOverwrite.includes(key)) {
2474
+ continue
2475
+ }
2476
+ } else if (this.config[key] && noOverwrite.includes(key)) {
2477
+ continue
2478
+ }
2479
+ this.config[key] = value
2480
+ continue
2481
+ }
2482
+ if (key === 'words') {
2483
+ const configWords = this.config.words
2484
+ const moreWords = more.words
2485
+ for (const word of Object.keys(moreWords)) {
2486
+ if (!configWords[word]) {
2487
+ configWords[word] = []
2488
+ }
2489
+ // configWords[word] = configWords[word].concat(moreWords[word])
2490
+ configWords[word] = moreWords[word].concat(configWords[word])
2491
+ }
2492
+ } else if (key === 'name') {
2493
+ /*
2494
+ if (this.config[key] === '') {
2495
+ this.config[key] = more[key]
2496
+ }
2497
+ */
2498
+ } else if (key === 'namespaces') {
2499
+ if (includeNamespaces) {
2500
+ Object.assign(this.config[key], more[key])
2501
+ }
2502
+ } else if (key === 'debug') {
2503
+ this.config[key] = this.config[key] || more[key]
2504
+ } else if (key === 'description') {
2505
+ // this.config[key] += ' ' + more[key].trim()
2506
+ this.config[key] = more[key].trim() + ' ' + this.config[key]
2507
+ } else if (key === 'objects') {
2508
+ if (!skipObjects) {
2509
+ // namespaced is special
2510
+ const namespaced = this.config.objects.namespaced
2511
+ Object.assign(this.config[key], more[key])
2512
+ this.config.objects.namespaced = namespaced
2513
+ }
2514
+ } else if (key === 'associations') {
2515
+ const configAssociations = this.config.associations
2516
+ const moreAssociations = more.associations
2517
+ if (moreAssociations.positive) {
2518
+ // configAssociations.positive = configAssociations.positive.concat(moreAssociations.positive)
2519
+ configAssociations.positive = moreAssociations.positive.concat(configAssociations.positive)
2520
+ }
2521
+ if (moreAssociations.negative) {
2522
+ // configAssociations.negative = configAssociations.negative.concat(moreAssociations.negative)
2523
+ configAssociations.negative = moreAssociations.negative.concat(configAssociations.negative)
2524
+ }
2525
+ } else if (Array.isArray(value)) {
2526
+ // handle allowDups
2527
+ if (key == 'operators') {
2528
+ // TODO what about other props
2529
+ const isDup = (op1, op2) => op1.pattern == op2.pattern
2530
+ for (const newOne of more[key]) {
2531
+ for (let iOldOne = 0; iOldOne < this.config[key].length; ++iOldOne) {
2532
+ const oldOne = this.config[key][iOldOne];
2533
+ if (isDup(newOne, oldOne)) {
2534
+ if (oldOne.allowDups) {
2535
+ // the old one takes precedence to match what would happen during the original load
2536
+ debugger
2537
+ this.config[key].splice(iOldOne, 1)
2538
+ break;
2539
+ }
2540
+ }
2541
+ }
2542
+ }
2543
+ }
2544
+ if (key == 'bridges') {
2545
+ // TODO what about other props
2546
+ const idDup = (b1, b2) => b1.id == b2.id && b1.level == b2.level && b1.bridge == b2.bridge
2547
+ for (const newOne of more[key]) {
2548
+ for (let iOldOne = 0; iOldOne < this.config[key].length; ++iOldOne) {
2549
+ const oldOne = this.config[key][iOldOne];
2550
+ if (newOne.id == oldOne.id) {
2551
+ if (oldOne.allowDups) {
2552
+ // the old one takes precedence to match what would happen during the original load
2553
+ this.config[key].splice(iOldOne, 1)
2554
+ break;
2555
+ }
2556
+ }
2557
+ }
2558
+ }
2559
+ }
2560
+ // console.log('key', key, 'XXX')
2561
+ // console.log('more', JSON.stringify(more, null, 2))
2562
+ // this.config[key] = this.config[key].concat(more[key])
2563
+ if (key == '2enerators') {
2564
+ debugger
2565
+ }
2566
+ // this.config[key] = this.config[key].concat(more[key])
2567
+ this.config[key] = more[key].concat(this.config[key])
2568
+ } else {
2569
+ if (!(key in this.config)) {
2570
+ throw `Unexpected property in config ${key}`
2571
+ }
2572
+ this.config[key] = more[key]
2573
+ }
2574
+ }
2575
+ return this
2576
+ }
2275
2577
  }
2276
2578
 
2277
2579
  module.exports = {
package/src/digraph.js CHANGED
@@ -9,21 +9,42 @@ const toA = (edge) => {
9
9
  class Digraph {
10
10
  // edges maybe either [child, parent] or { child, parent }
11
11
  constructor (edges = []) {
12
- this.edges = edges
12
+ // dont make a copy of edges. this is shared and that breaks stuff. TODO fix this
13
+ this._edges = edges
13
14
  }
14
15
 
15
- /*
16
- edges () {
17
- return this.edges
16
+ addEdges(edges) {
17
+ for (let edge of edges) {
18
+ this.addEdge(edge)
19
+ }
20
+ }
21
+
22
+ addEdge(edge) {
23
+ edge = toA(edge)
24
+ this._edges.push(edge)
25
+ }
26
+
27
+ get edges() {
28
+ return this._edges
29
+ }
30
+
31
+ set edges(edges) {
32
+ this._edges = edges
33
+ }
34
+
35
+ /*
36
+ set edges(edges) {
37
+ this._edges = edges.map( toA )
18
38
  }
19
39
  */
40
+
20
41
  acdcs (s, from, to) {
21
42
  const todo = [s]
22
43
  const seen = new Set([s])
23
44
  const acdcs = new Set([])
24
45
  while (todo.length > 0) {
25
46
  const n = todo.pop()
26
- this.edges.forEach((e) => {
47
+ this._edges.forEach((e) => {
27
48
  e = toA(e)
28
49
  if (e[from] === n) {
29
50
  acdcs.add(e[to])
@@ -84,12 +105,12 @@ class Digraph {
84
105
  }
85
106
 
86
107
  add (child, parent) {
87
- this.edges.push([child, parent])
108
+ this._edges.push([child, parent])
88
109
  }
89
110
 
90
111
  addList (l) {
91
112
  for (let i = 1; i < l.length; ++i) {
92
- this.edges.push([l[i - 1], l[i]])
113
+ this._edges.push([l[i - 1], l[i]])
93
114
  }
94
115
  }
95
116
 
package/src/generators.js CHANGED
@@ -195,10 +195,12 @@ class Generators {
195
195
  } catch( e ) {
196
196
  // the next if handle this
197
197
  generated = null
198
+ e.retryCall = () => generator.apply(args, objects, g, args.gs, context, hierarchy, config, response, log)
199
+ const help = 'The error has a retryCall property that will recall the function that failed.'
198
200
  if (e.stack && e.message) {
199
- errorMessage = `Error applying generator '${generator.notes}'. Error is ${e.toString()} stack is ${e.stack}. Generator is ${generator.toString()}`
201
+ errorMessage = `Error applying generator '${generator.notes}'. Error is ${e.toString()} stack is ${e.stack}. Generator is ${generator.toString()}. ${help}`
200
202
  } else if (e.error) {
201
- errorMessage = `Error applying generator '${generator.notes}'. Error is ${e.error.join()}. Generator is ${generator.toString()}`
203
+ errorMessage = `Error applying generator '${generator.notes}'. Error is ${e.error.join()}. Generator is ${generator.toString()}. ${help}`
202
204
  } else {
203
205
  errorMessage = e.toString()
204
206
  }
package/src/helpers.js CHANGED
@@ -217,4 +217,42 @@ const sortJson = (json) => {
217
217
  return json
218
218
  }
219
219
 
220
- module.exports = { args, safeEquals, appendNoDups, hashIndexesGet, hashIndexesSet, translationMapping, normalizeGenerator, normalizeSemantic, isArray, isObject, isCompound, InitCalls, hashCode, sortJson }
220
+ const validProps = (valids, object, type) => {
221
+ for (let prop of Object.keys(object)) {
222
+ let okay = false
223
+ for (valid of valids) {
224
+ if (prop.match(valid)) {
225
+ okay = true
226
+ break
227
+ }
228
+ }
229
+ if (!okay) {
230
+ throw `Unknown property "${prop}" in the ${type}. Valid properties are ${valids}. The ${type} is ${JSON.stringify(object)}`
231
+ }
232
+ }
233
+ }
234
+
235
+ const mapInPlace = (list, fn) => {
236
+ for (let i = 0; i < list.length; ++i) {
237
+ list[i] =fn(list[i])
238
+ }
239
+ }
240
+
241
+ module.exports = {
242
+ mapInPlace,
243
+ validProps,
244
+ args,
245
+ safeEquals,
246
+ appendNoDups,
247
+ hashIndexesGet,
248
+ hashIndexesSet,
249
+ translationMapping,
250
+ normalizeGenerator,
251
+ normalizeSemantic,
252
+ isArray,
253
+ isObject,
254
+ isCompound,
255
+ InitCalls,
256
+ hashCode,
257
+ sortJson
258
+ }
package/src/semantics.js CHANGED
@@ -189,10 +189,12 @@ class Semantics {
189
189
  } catch( e ) {
190
190
  contextPrime = null
191
191
  let errorMessage
192
+ e.retryCall = () => semantic.apply(args, context, s, log, options)
193
+ const help = 'The error has a retryCall property that will recall the function that failed.'
192
194
  if (e.stack && e.message) {
193
- errorMessage = `Error applying semantics '${semantic.notes}'. Error is ${e.toString()} stack is ${e.stack}. Semantic is ${semantic.toString()}`
195
+ errorMessage = `Error applying semantics '${semantic.notes}'. Error is ${e.toString()} stack is ${e.stack}. Semantic is ${semantic.toString()}. ${help}`
194
196
  } else if (e.error) {
195
- errorMessage = `Error applying semantics '${semantic.notes}'. Error is ${e.error.join()}. Semantic is ${semantic.toString()}`
197
+ errorMessage = `Error applying semantics '${semantic.notes}'. Error is ${e.error.join()}. Semantic is ${semantic.toString()}. ${help}`
196
198
  } else {
197
199
  errorMessage = e.toString();
198
200
  }