theprogrammablemind 7.5.0 → 7.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/client.js CHANGED
@@ -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.2",
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
  }