theprogrammablemind 7.1.4-beta.3

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/src/config.js ADDED
@@ -0,0 +1,2129 @@
1
+ // lookup = (name) => returns <config>
2
+ const { Semantics, normalizeGenerator } = require('./semantics')
3
+ const { Generators } = require('./generators')
4
+ // const { uuid: uuidv4 } = require('uuidv4')
5
+ const client = require('../client')
6
+ const Digraph = require('./digraph')
7
+ const helpers = require('./helpers')
8
+ const deepEqual = require('deep-equal')
9
+ const _ = require('lodash')
10
+
11
+ const debugBreak = () => {
12
+ // debugger
13
+ }
14
+
15
+ const bags = [
16
+ 'generators',
17
+ 'semantics'
18
+ ]
19
+
20
+ const indent = (string, indent) => {
21
+ return string.replace(/^/gm, ' '.repeat(indent));
22
+ }
23
+
24
+ if (process.env.DEBUG_HIERARCHY) {
25
+ global.entodictonDebugHierarchy = JSON.parse(process.env.DEBUG_HIERARCHY)
26
+ }
27
+
28
+ if (process.env.DEBUG_BRIDGE) {
29
+ // id/level
30
+ global.entodictonDebugBridge = process.env.DEBUG_BRIDGE.split('/')
31
+ if (global.entodictonDebugBridge.length !== 2) {
32
+ console.log('Expected DEBUG_BRIDGE to be of the form "id/level"');
33
+ }
34
+ global.entodictonDebugBridge[1] = int(global.entodictonDebugBridge[1])
35
+ }
36
+
37
+ const hierarchyCanonical = (element) => {
38
+ if (element.child && element.parent) {
39
+ return element
40
+ } else {
41
+ return { child: element[0], parent: element[1] }
42
+ }
43
+ }
44
+
45
+ const isValidDef = (word, def, config) => {
46
+ if (!def.id) {
47
+ throw `In the KM "${config.name}", for the word ${word} the following definition is missing the "id" property: ${JSON.stringify(def)}`
48
+ }
49
+ /*
50
+ if (!def.initial) {
51
+ throw `In the KM "${config.name}", for the word ${word} the following definition is missing the "initial" property: ${JSON.stringify(def)}`
52
+ }
53
+ */
54
+ }
55
+
56
+ const hierarchyToCanonical = (edge) => {
57
+ if (Array.isArray(edge)) {
58
+ return { child: edge[0], parent: edge[1] }
59
+ }
60
+ return edge
61
+ }
62
+
63
+ const addWord = (baseConfig, config) => ({ word, id, initial }) => {
64
+ if (!baseConfig.config.words) {
65
+ baseConfig.config.words = {}
66
+ }
67
+ const words = baseConfig.config.words
68
+ const def = { id, initial, uuid: config.uuid }
69
+ if (words[word]) {
70
+ if (!words[word].some((e) => helpers.safeEquals(e, def))) {
71
+ words[word].unshift(def)
72
+ }
73
+ } else {
74
+ words[word] = [def]
75
+ }
76
+ }
77
+
78
+ const normalizeConfig = (config) => {
79
+ if (config) {
80
+ for (const bag of bags) {
81
+ if (config[bag]) {
82
+ config[bag] = config[bag].map(normalizeGenerator)
83
+ for (let i = 0; i < config[bag].length; ++i) {
84
+ config[bag][i].index = i
85
+ config[bag][i].km = config.name
86
+ }
87
+ }
88
+ }
89
+ }
90
+ }
91
+
92
+ /*
93
+ function isLetter (char) {
94
+ return (/[a-zA-Z]/).test(char)
95
+ }
96
+ */
97
+
98
+ function configDup (config, options) {
99
+ if (config instanceof Config) {
100
+ return config.copy(options)
101
+ }
102
+ return _.cloneDeep(config)
103
+ }
104
+
105
+ function setWordsUUIDs (words, uuid) {
106
+ for (const key in words) {
107
+ words[key] = words[key].map((o) => Object.assign(o, { uuid }))
108
+ }
109
+ }
110
+
111
+ function applyUUID (config, uuid) {
112
+ if (config instanceof Config) {
113
+ config = config.config
114
+ }
115
+
116
+ if (config.namespaces) {
117
+ const keys = Object.keys(config.namespaces)
118
+ if (keys.length > 1) {
119
+ debugBreak()
120
+ // throw 'wtf 23'
121
+ }
122
+ if (config.namespaces[keys[0]]) {
123
+ config.namespaces[uuid] = config.namespaces[keys[0]]
124
+ }
125
+ }
126
+ if (config.operators) {
127
+ config.operators = config.operators.map((o) => Object.assign(o, { uuid }))
128
+ }
129
+ if (config.bridges) {
130
+ config.bridges = config.bridges.map((o) => Object.assign(o, { uuid }))
131
+ }
132
+ if (config.words) {
133
+ setWordsUUIDs(config.words, uuid)
134
+ }
135
+ for (const property of bags) {
136
+ if (config[property]) {
137
+ config[property].forEach((bag) => { bag.uuid = uuid })
138
+ }
139
+ }
140
+ }
141
+
142
+ /*
143
+ function namespaceValid (namespace) {
144
+ if (Array.isArray(namespace)) {
145
+ return true
146
+ }
147
+ if (!namespace) {
148
+ return true
149
+ }
150
+ return false
151
+ }
152
+ */
153
+
154
+ class KM {
155
+ toNS (id) {
156
+ if (this._namespace.length === 0) {
157
+ return id
158
+ }
159
+ return `${this._namespace.join('#')}#${id}`
160
+ }
161
+
162
+ getName (config) {
163
+ if (config instanceof Config) {
164
+ return config.config.name
165
+ } else {
166
+ return config.name
167
+ }
168
+ }
169
+
170
+ get name () {
171
+ return this.toNS(this._name)
172
+ }
173
+
174
+ constructor ({ config, namespace = [], uuid, isSelf = false }) {
175
+ if (uuid) {
176
+ this._uuid = uuid
177
+ this._class = 'KM'
178
+ this._config = config
179
+ this._namespace = namespace
180
+ this._name = this.getName(config)
181
+ this._isSelf = true // new version the entry is for the containiner class but can't be that due to circularity
182
+ } else {
183
+ // this._uuid = uuidv4();
184
+ if (config instanceof Config) {
185
+ config.valid()
186
+ this._name = config.config.name
187
+ // config.uuid = this._uuid
188
+ this._uuid = config._uuid
189
+ } else {
190
+ // this._uuid = uuidv4()
191
+ this._name = config.name
192
+ this._uuid = getCounter(this._name)
193
+ }
194
+ this._class = 'KM'
195
+ this._config = config
196
+ // applyUUID(this._config, this._uuid)
197
+ if (this._config instanceof Config) {
198
+ this._config.valid()
199
+ }
200
+ this._namespace = namespace
201
+ this._isSelf = false // old version the entry is for the containiner class but can't be that due to circularity
202
+ }
203
+ }
204
+
205
+ valid () {
206
+ if (this._config.initializerFn && !(this._config instanceof Config)) {
207
+ debugBreak()
208
+ return false
209
+ }
210
+ if (this._namespace && !Array.isArray(this._namespace)) {
211
+ debugBreak()
212
+ return false
213
+ }
214
+ if (this._config instanceof Config) {
215
+ if (!this._config.valid()) {
216
+ debugBreak()
217
+ return false
218
+ }
219
+ }
220
+ return true
221
+ }
222
+
223
+ copy2 (options) {
224
+ // greg -> add a flag to say don't init the api's
225
+ const config = configDup(this._config, options)
226
+ const km = new KM({
227
+ config,
228
+ name: this._name,
229
+ _uuid: config._uuid,
230
+ namespace: this._namespace,
231
+ isSelf: this._isSelf
232
+ })
233
+ return km // copy2()
234
+ }
235
+
236
+ copy () {
237
+ const km = new KM({
238
+ name: this._name,
239
+ config: configDup(this._config),
240
+ // _uuid: uuidv4(),
241
+ _uuid: getCounter(this._config._name),
242
+ namespace: this._namespace,
243
+ isSelf: this._isSelf
244
+ })
245
+ if (km.config instanceof Config) {
246
+ km.config.valid()
247
+ }
248
+ applyUUID(km._config, km._uuid)
249
+ return km // copy()
250
+ }
251
+
252
+ get api () {
253
+ return this._config.api
254
+ }
255
+
256
+ toString () {
257
+ return `KM(${this._name}, ${JSON.stringify(this.config)}, ${this._namespace} isSelf(${this._isSelf}))`
258
+ }
259
+
260
+ get uuid () {
261
+ return this._uuid
262
+ }
263
+
264
+ get config () {
265
+ return this._config
266
+ }
267
+
268
+ set config (config) {
269
+ this._config = config
270
+ }
271
+
272
+ get isSelf () {
273
+ return this._isSelf
274
+ }
275
+
276
+ get namespace () {
277
+ return this._namespace
278
+ }
279
+
280
+ set namespace (namespace) {
281
+ this._namespace = namespace
282
+ }
283
+ }
284
+
285
+ const multiApiImpl = (initializer) => {
286
+ return new Object({
287
+ multiApi: true,
288
+
289
+ // multi functions
290
+ add: (config, multiApi, api) => {
291
+ initializer(config, api)
292
+ const name = api.getName()
293
+ multiApi.apis[name] = api
294
+ api.objects = config.get('objects')
295
+ api.config = () => config
296
+ multiApi.current = name
297
+ },
298
+
299
+ initialize: ({ config, api: multiApi }) => {
300
+ for (const apiName in multiApi.apis) {
301
+ const api = multiApi.apis[apiName]
302
+ initializer(config, api)
303
+ }
304
+ },
305
+
306
+ set objects (value) {
307
+ for (const key in Object.keys(this.apis)) {
308
+ this.apis[key].objects = value
309
+ }
310
+ },
311
+
312
+ // "product1": apiInstance(testData1),
313
+ apis: {
314
+ },
315
+
316
+ // api functions
317
+ api: (multiApi) => multiApi.apis[multiApi.current]
318
+ })
319
+ }
320
+
321
+ let configCounter = 1
322
+
323
+ const getCounter = (maybeName = '') => {
324
+ const counter = configCounter
325
+ configCounter += 1
326
+ return `${maybeName}${counter}`
327
+ }
328
+
329
+ class Config {
330
+ defaultConfig () {
331
+ this.config = {
332
+ operators: [], // TODO
333
+ bridges: [], // Done
334
+ hierarchy: [], // Done
335
+ name: '',
336
+ namespaces: {
337
+ // "config id" : { namespace: [""], ids: set(ids) }
338
+ },
339
+ eqClasses: [],
340
+ priorities: [], // Done
341
+ version: '3',
342
+ debug: false,
343
+ associations: { // Done
344
+ negative: [],
345
+ positive: []
346
+ },
347
+ objects: {
348
+ // this is where the namespaced configs have their objects
349
+ namespaced: {}
350
+ },
351
+ description: '',
352
+ words: {}, // Done
353
+ floaters: [],
354
+ implicits: [],
355
+ flatten: [],
356
+ contexts: [], // TODO future
357
+
358
+ expected_generated: [],
359
+ expected_results: [],
360
+ skipSemantics: false
361
+ }
362
+ for (const bag of bags) {
363
+ this.config[bag] = []
364
+ }
365
+ // this.wasInitialized = false
366
+ }
367
+
368
+ // applies only to config sent to the server
369
+
370
+ watching() {
371
+ const props = [
372
+ 'operators',
373
+ 'bridges',
374
+ 'hierarchy',
375
+ 'namespaces',
376
+ 'eqClasses',
377
+ 'priorities',
378
+ 'associations',
379
+ 'words',
380
+ 'floaters',
381
+ 'implicits',
382
+ 'flatten',
383
+ ];
384
+ const watching = {}
385
+ for (let prop of props) {
386
+ watching[prop] = this.config[prop]
387
+ }
388
+ return JSON.stringify(watching)
389
+ }
390
+
391
+ watch() {
392
+ this.watchStart = this.watching();
393
+ }
394
+
395
+ wasChanged() {
396
+ return this.watchStart !== this.watching()
397
+ }
398
+
399
+ exists(marker) {
400
+ for (let bridge of this.config.bridges) {
401
+ if (bridge.id == marker) {
402
+ return true;
403
+ }
404
+ }
405
+ }
406
+
407
+ getSemantics (logs = []) {
408
+ return new Semantics(this.config.semantics || [], logs)
409
+ }
410
+
411
+ getGenerators (logs = []) {
412
+ return new Generators(this.config.generators || [], logs)
413
+ }
414
+
415
+ warningNotEvaluated (log, value) {
416
+ const description = 'WARNING: for semantics, implement an evaluations handler, set "value" property of the operator to the value.'
417
+ const match = `({context}) => context.marker == '${value.marker}' && context.evaluate && <other conditions as you like>`
418
+ const apply = `({context}) => <do stuff...>; context.value = <value>`
419
+ const input = indent(JSON.stringify(value, null, 2), 2)
420
+ const message = `${description}\nThe semantic would be\n match: ${match}\n apply: ${apply}\nThe input context would be:\n${input}\n`
421
+ log.push(indent(message, 4))
422
+ }
423
+
424
+ // value is in response field
425
+ // TODO maybe generalize out query+evaluate along the lines of set value and set reference
426
+ /*
427
+ getEvaluator (s, log, context) {
428
+ const instance = s({ ...context, evaluate: true })
429
+ if (!instance.evalue && !instance.verbatim && !instance.value) {
430
+ this.warningNotEvaluated(log, context);
431
+ }
432
+ if (!instance.evalue) {
433
+ instance.evalue = instance.value
434
+ instance.edefault = true
435
+ }
436
+ delete instance.evaluate
437
+ instance.instance = true;
438
+ return instance
439
+ }
440
+ */
441
+ getEvaluator (s, log, context) {
442
+ const instance = s({ ...context, evaluate: true })
443
+ if (!instance.evalue && !instance.verbatim && !instance.value) {
444
+ this.warningNotEvaluated(log, context);
445
+ }
446
+ if (!instance.evalue) {
447
+ instance.evalue = instance.value
448
+ instance.edefault = true
449
+ }
450
+ delete instance.evaluate
451
+ instance.instance = true;
452
+ return instance
453
+ }
454
+
455
+
456
+ fragmentInstantiator (contexts) {
457
+ return new Object({
458
+ contexts: () => contexts,
459
+ instantiate: (mappings) => {
460
+ const instantiated = _.cloneDeep(contexts)
461
+ // const todo = [...instantiated]
462
+ // const todo = [...instantiated]
463
+ const todo = _.clone(instantiated)
464
+ while (todo.length > 0) {
465
+ const context = todo.pop()
466
+ for (const mapping of mappings) {
467
+ if (mapping.match({ context })) {
468
+ mapping.apply({ context })
469
+ }
470
+ }
471
+ for (const key of Object.keys(context)) {
472
+ // if (['number', 'string', 'boolean'].includes(typeof (context[key]))) {
473
+ if (!helpers.isCompound(context[key])) {
474
+ continue
475
+ }
476
+ if (context[key].instantiated) {
477
+ continue;
478
+ }
479
+ todo.push(context[key])
480
+ }
481
+ }
482
+ return instantiated
483
+ }
484
+ })
485
+ }
486
+
487
+ fragment (query) {
488
+ for (const instance of (this.instances || [])) {
489
+ for (const fragment of (instance.fragments || [])) {
490
+ if (fragment.query === query) {
491
+ return this.fragmentInstantiator(fragment.contexts)
492
+ }
493
+ }
494
+ }
495
+ }
496
+
497
+ needsRebuild(template, instance, options = { rebuild: false }) {
498
+ if (options.rebuild) {
499
+ return true
500
+ }
501
+ const toCanonical = (f) => {
502
+ if (typeof f == 'string') {
503
+ return { query: f }
504
+ } else {
505
+ return f
506
+ }
507
+ }
508
+ const instanceFragments = (instance.fragments || []).map((fragment) => fragment.key || fragment.query).map( toCanonical )
509
+ const templateFragments = (template.fragments || []).concat(this.dynamicFragments).map( toCanonical )
510
+ const sameFragments = deepEqual(templateFragments, instanceFragments)
511
+ const sameQueries = deepEqual((template.queries || []), (instance.queries || []))
512
+ return !(instance && sameQueries && sameFragments)
513
+ }
514
+
515
+ load (template, instance, options = { rebuild: false } ) {
516
+ this.logs.push(`loading template for ${this.name}`)
517
+ if (instance && instance.associations && !options.rebuild) {
518
+ this.addAssociations(instance.associations)
519
+ }
520
+ if (options.rebuild) {
521
+ // TODO fix beforeQuery
522
+ template = { fragments: [], queries: [], ...template }
523
+ template.fragments = template.fragments.concat(this.dynamicFragments)
524
+ client.build({ config: this, target: this.name, beforeQuery: () => {}, template, ...options })
525
+ } else {
526
+ // no change
527
+ this.initInstances.push(instance)
528
+ this.instances.push(instance)
529
+ client.processInstance(this, instance)
530
+ }
531
+ }
532
+
533
+ addFragments(fragments) {
534
+ // only run this if not loading as module write error if loading as module and different
535
+ this.dynamicFragments = this.dynamicFragments.concat(fragments)
536
+ }
537
+
538
+ objects () {
539
+ return this.config.objects.namespaced[this._uuid]
540
+ }
541
+
542
+ addAssociationsFromTests(tests = []) {
543
+ for (let test of tests) {
544
+ this.addAssociations(test.associations || []);
545
+ }
546
+ }
547
+
548
+
549
+ addAssociations (associations) {
550
+ for (let association of associations) {
551
+ this.addAssociation(association)
552
+ }
553
+ }
554
+
555
+ addAssociation (association) {
556
+ if (!this.config.associations) {
557
+ this.config.associations = {
558
+ negative: [],
559
+ positive: []
560
+ }
561
+ }
562
+ if (global.entodictonDebugAssociation) {
563
+ if (deepEqual(global.entodictonDebugAssociation, association)) {
564
+ debugger; // debug association hit
565
+ }
566
+ }
567
+ this.config.associations.positive.push(association)
568
+ }
569
+
570
+ // TODO add more error checking to these like addHierarchy has
571
+ addPriorities (priorities) {
572
+ if (!this.config.priorities) {
573
+ this.config.priorities = []
574
+ }
575
+ this.config.priorities.push(priorities)
576
+ }
577
+
578
+ addHierarchy (child, parent) {
579
+ if (child && parent || !child || Array.isArray(child) || (typeof child == 'string' && !parent)) {
580
+ this.addHierarchyChildParent(child, parent)
581
+ // this.addHierarchyProperties ({ child, parent })
582
+ } else {
583
+ this.addHierarchyProperties (child)
584
+ }
585
+ }
586
+
587
+ addHierarchyProperties (properties) {
588
+ if (typeof properties.child !== 'string') {
589
+ throw `addHierarchy expected child property to be a string. got ${JSON.stringify(child)}`
590
+ }
591
+ if (typeof properties.parent !== 'string') {
592
+ throw `addHierarchy expected parent properties to be a string. got ${JSON.stringify(parent)}`
593
+ }
594
+ if (!this.config.hierarchy) {
595
+ this.config.hierarchy = []
596
+ }
597
+ // this.config.hierarchy.push([properties.child, properties.parent])
598
+ this.config.hierarchy.push(properties)
599
+ }
600
+
601
+ addHierarchyChildParent (child, parent) {
602
+ if (typeof child !== 'string') {
603
+ throw `addHierarchy expected child to be a string. got ${JSON.stringify(child)}`
604
+ }
605
+ if (typeof parent !== 'string') {
606
+ throw `addHierarchy expected parent to be a string. got ${JSON.stringify(parent)}`
607
+ }
608
+ if (!this.config.hierarchy) {
609
+ this.config.hierarchy = []
610
+ }
611
+ if (global.entodictonDebugHierarchy) {
612
+ if (deepEqual(global.entodictonDebugHierarchy, [child, parent])) {
613
+ debugger; // debug hierarchy hit
614
+ }
615
+ }
616
+ if (this.config.hierarchy.find( (element) => {
617
+ const hc = hierarchyCanonical(element)
618
+ if (child == hc.child && parent == hc.parent) {
619
+ return true
620
+ }
621
+ })) {
622
+ return
623
+ }
624
+
625
+ this.config.hierarchy.push([child, parent])
626
+ }
627
+
628
+ getBridge (id, level) {
629
+ return this.config.bridges.find( (bridge) => bridge.id == id && bridge.level == level )
630
+ }
631
+
632
+ addBridge (bridge) {
633
+ if (!this.config.bridges) {
634
+ this.config.bridges = []
635
+ }
636
+ const bridges = this.config.bridges
637
+ const def = Object.assign({}, bridge, { uuid: this._uuid })
638
+
639
+ if (global.entodictonDebugBridge) {
640
+ if (global.entodictonDebugBridge[0] == bridge.id && global.entodictonDebugBridge[1] == bridge.level) {
641
+ debugger; // debug hierarchy hit
642
+ }
643
+ }
644
+
645
+ if (bridge.allowDups) {
646
+ if (bridges.find( (b) => b.id == bridge.id && b.level == bridge.level && b.bridge == bridge.bridge )) {
647
+ return;
648
+ }
649
+ }
650
+ if (global.transitoryMode) {
651
+ def.transitoryMode = true
652
+ }
653
+ bridges.push(def)
654
+ this.checkBridges();
655
+ }
656
+
657
+ addGenerator (match, apply) {
658
+ let generator = match
659
+ if ((typeof match === 'function') && (typeof apply === 'function')) {
660
+ generator = { match, apply }
661
+ }
662
+
663
+ if (!(typeof generator.match === 'function')) {
664
+ throw 'addGenerator: Expected matcher to be a function'
665
+ }
666
+ if (!(typeof generator.apply === 'function')) {
667
+ throw 'addGenerator: Expected action to be a function'
668
+ }
669
+
670
+ if (!this.config.generators) {
671
+ this.config.generators = []
672
+ }
673
+
674
+ if (!generator.where) {
675
+ generator.where = client.where(3)
676
+ }
677
+
678
+ const generators = this.config.generators
679
+ Object.assign(generator, { uuid: this._uuid, km: this.name, index: generators.length })
680
+ // used to be unshift
681
+ generators.unshift(generator)
682
+ }
683
+
684
+ addSemantic (match, apply) {
685
+ let semantic = match
686
+ if ((typeof match === 'function') && (typeof apply === 'function')) {
687
+ semantic = { match, apply }
688
+ }
689
+
690
+ if (!(typeof semantic.match === 'function')) {
691
+ throw 'addSemantic: Expected match to be a function'
692
+ }
693
+ if (!(typeof semantic.apply === 'function')) {
694
+ throw 'addSemantic: Expected apply to be a function'
695
+ }
696
+
697
+ if (!this.config.semantics) {
698
+ this.config.semantics = []
699
+ }
700
+
701
+ if (!semantic.where) {
702
+ semantic.where = client.where(3)
703
+ }
704
+
705
+ const semantics = this.config.semantics
706
+ Object.assign(semantic, { uuid: this._uuid, km: this.name, index: semantics.length })
707
+ semantics.unshift(semantic)
708
+ }
709
+
710
+ addOperator (objectOrPattern) {
711
+ if (!this.config.operators) {
712
+ this.config.operators = []
713
+ }
714
+ const operators = this.config.operators
715
+
716
+ let operator;
717
+ if (typeof objectOrPattern === 'string') {
718
+ operator = { pattern: objectOrPattern, uuid: this._uuid }
719
+ } else {
720
+ operator = Object.assign({}, objectOrPattern, { uuid: this._uuid })
721
+ }
722
+
723
+ if (operator.allowDups) {
724
+ if (operators.find( (o) => o.pattern == operator.pattern )) {
725
+ return;
726
+ }
727
+ }
728
+
729
+ operators.unshift(operator)
730
+ this.checkOperators()
731
+ }
732
+
733
+ addWord (word, def) {
734
+ this.addWordInternal(this.config, word, def)
735
+ }
736
+
737
+ addWordInternal (config, word, def) {
738
+ if (!config.words) {
739
+ config.words = {}
740
+ }
741
+ const words = config.words
742
+ def = Object.assign({}, def, { uuid: this._uuid })
743
+ if (words[word]) {
744
+ if (!words[word].some((e) => helpers.safeEquals(e, def))) {
745
+ words[word].unshift(def)
746
+ }
747
+ } else {
748
+ words[word] = [def]
749
+ }
750
+ }
751
+
752
+ getAPI (uuid) {
753
+ if (this._uuid === uuid) {
754
+ return this.api
755
+ }
756
+ for (const km of this.configs) {
757
+ if (km._uuid === uuid) {
758
+ return km.config.api
759
+ }
760
+ }
761
+ }
762
+
763
+ getServer() {
764
+ return this._server
765
+ }
766
+
767
+ getAPIKey() {
768
+ return this._key
769
+ }
770
+
771
+ server (server, apiKey) {
772
+ if (server) {
773
+ this._server = server
774
+ }
775
+ if (apiKey) {
776
+ this._key = apiKey
777
+ }
778
+
779
+ if (this._api && this._api.multiApi) {
780
+ for (const key of Object.keys(this._api.apis)) {
781
+ const api = this._api.apis[key]
782
+ if (api.server) {
783
+ api.server(server, apiKey)
784
+ } else {
785
+ api.credentials = { server, key: apiKey }
786
+ }
787
+ }
788
+ }
789
+
790
+ for (const km of (this.configs || [])) {
791
+ const config = km._config
792
+ if (config.server) {
793
+ km._config.server(server, apiKey)
794
+ }
795
+ }
796
+ }
797
+
798
+ getParams () {
799
+ const params = {
800
+ objects: this.config.objects,
801
+ config: this
802
+ }
803
+ for (const bag of bags) {
804
+ params[bag] = this.config[bag]
805
+ }
806
+ return params
807
+ }
808
+
809
+ processContexts (contexts, params) {
810
+ client.processContexts(contexts, this.getParams())
811
+ }
812
+
813
+ processContext (context) {
814
+ return client.processContext(context, this.getParams())
815
+ }
816
+
817
+ process (query, options) {
818
+ return client.process(this, query, options)
819
+ }
820
+
821
+ initDefaults () {
822
+ const init = (config) => {
823
+ if (!config.objects) {
824
+ config.objects = {
825
+ namespaced: {
826
+ }
827
+ }
828
+ }
829
+
830
+ if (!config.associations) {
831
+ config.associations = {}
832
+ }
833
+
834
+ if (!config.associations.positive) {
835
+ config.associations.positive = []
836
+ }
837
+
838
+ if (!config.associations.negative) {
839
+ config.associations.negative = []
840
+ }
841
+ }
842
+ init(this.config)
843
+ init(this.initConfig)
844
+ }
845
+
846
+ km (name) {
847
+ return this.getConfig(name)
848
+ }
849
+
850
+ // set the args in the api's
851
+ setArgs (args) {
852
+ const setArgs = (config) => {
853
+ if (!config._api) {
854
+ return
855
+ }
856
+ if (config._api.multiApi) {
857
+ for (const api in config._api.multiApi) {
858
+ config._api.multiApi[api].args = args
859
+ }
860
+ } else {
861
+ config._api.args = args
862
+ }
863
+ }
864
+
865
+ setArgs(this)
866
+ for (const config of this.configs) {
867
+ if (config.config instanceof Config) {
868
+ setArgs(config.config)
869
+ }
870
+ }
871
+ }
872
+
873
+ getConfigs () {
874
+ const configs = {};
875
+ configs[this.name] = this
876
+ for (const config of this.configs) {
877
+ if (config.config instanceof Config) {
878
+ configs[config.config.name] = config.config
879
+ }
880
+ }
881
+ return configs;
882
+ }
883
+
884
+ getConfig (name) {
885
+ if (this.name === name) {
886
+ return this
887
+ }
888
+ for (const config of this.configs) {
889
+ if (config.config instanceof Config) {
890
+ if (config.config.nsToString(config.config.name) === name) {
891
+ return config.config
892
+ }
893
+ }
894
+ }
895
+ }
896
+
897
+ removeDevelopmentElements(config) {
898
+ if (!config) {
899
+ return
900
+ }
901
+ config.operators = config.operators || []
902
+ config.bridges = config.bridges || []
903
+ config.words = config.words || {}
904
+ config.generators = config.generators || []
905
+ config.semantics = config.semantics || []
906
+
907
+ config.operators = config.operators.filter( (element) => !element.development )
908
+ config.bridges = config.bridges.filter( (element) => !element.development )
909
+ config.generators = config.generators.filter( (element) => !element.development )
910
+ config.semantics = config.semantics.filter( (element) => !element.development )
911
+ config.hierarchy = (config.hierarchy || []).filter( (element) => !element.development )
912
+ for (const word in config.words) {
913
+ const defs = config.words[word] || []
914
+ config.words[word] = defs.filter( (def) => !def.development )
915
+ if (config.words[word].length == 0) {
916
+ delete config.words[word]
917
+ }
918
+ }
919
+ }
920
+
921
+ // configs = [ { config, namespace } ... ]
922
+ constructor (config, module) {
923
+ if (config instanceof Config) {
924
+ throw 'Excepted the config argument to be a hash not a Config object'
925
+ }
926
+
927
+ this.addedArgss = []
928
+ const isProcess = require.main === module
929
+ this.isModule = !isProcess
930
+ if (this.isModule) {
931
+ this.removeDevelopmentElements(config)
932
+ }
933
+ this.initInstances = []
934
+ this.instances = []
935
+ this.logs = []
936
+ this.dynamicFragments = []
937
+ // when running queries any bridges added are marked as transitory so that the associated will ignore those op's
938
+ this.transitoryMode = false
939
+
940
+ // check for duplicate bridges
941
+ if (config && config.bridges) {
942
+ let duplicated = new Set()
943
+ const seen = new Set()
944
+ for (const bridge of config.bridges) {
945
+ const id = `${bridge.id}/${bridge.level}`
946
+ if (seen.has(id)) {
947
+ duplicated.add(id)
948
+ } else {
949
+ seen.add(id)
950
+ }
951
+ }
952
+ duplicated = Array.from(duplicated)
953
+ if (duplicated.length > 0) {
954
+ throw `In the KM ${config.name}, the following operators are duplicated in the bridges: ${duplicated}`
955
+ }
956
+ }
957
+
958
+ if (config && config.words) {
959
+ for (const word of Object.keys(config.words)) {
960
+ for (const def of config.words[word]) {
961
+ isValidDef(word, def, config)
962
+ }
963
+ }
964
+ }
965
+
966
+ normalizeConfig(config)
967
+
968
+ // set the default server so stuff just works
969
+ this.server('http://184.67.27.82:3000', '6804954f-e56d-471f-bbb8-08e3c54d9321')
970
+
971
+ this.defaultConfig()
972
+ this.initializerFn = ({ currentConfig }) => {
973
+ if (currentConfig instanceof Config) {
974
+ currentConfig.wasInitialized = true
975
+ }
976
+ }
977
+ if (config) {
978
+ this.name = config.name
979
+ }
980
+ this.motivations = []
981
+ this.loadOrder = new Digraph()
982
+ this.wasInitialized = false
983
+ this.configs = []
984
+ this._api = undefined
985
+ this._namespace = []
986
+ this._eqClasses = []
987
+ // this._uuid = uuidv4()
988
+ this._uuid = getCounter(this.name)
989
+ if (config) {
990
+ config = _.cloneDeep(config)
991
+ this.config = config
992
+ if (!this.config.generators) {
993
+ this.config.generators = []
994
+ }
995
+ if (!this.config.semantics) {
996
+ this.config.semantics = []
997
+ }
998
+ if (!this.config.words) {
999
+ // this.config.words = {}
1000
+ }
1001
+ for (let bridge of (this.config.bridges || [])) {
1002
+ /*
1003
+ if (bridge.generator) {
1004
+ this.config.generators.push({
1005
+ match: ({context}) => bridge.id == context.marker,
1006
+ apply: (args) => bridge.generator(args),
1007
+ })
1008
+ }
1009
+ */
1010
+ if (bridge.isA) {
1011
+ for (let parent of bridge.isA) {
1012
+ this.addHierarchy(bridge.id, parent)
1013
+ }
1014
+ }
1015
+ if (bridge.before) {
1016
+ for (let after of bridge.before) {
1017
+ this.addPriorities([after, [bridge.id, bridge.level]])
1018
+ }
1019
+ }
1020
+ if (bridge.words) {
1021
+ for (let def of bridge.words) {
1022
+ if (typeof def == 'string') {
1023
+ this.addWordInternal(this.config, def, {"id": bridge.id, "initial": "{}" })
1024
+ } else {
1025
+ const word = def.word
1026
+ def = { initial: JSON.stringify(def), id: bridge.id, word: undefined }
1027
+ this.addWordInternal(this.config, word, def)
1028
+ }
1029
+ }
1030
+ }
1031
+ if (bridge.generator) {
1032
+ this.config.generators.unshift(bridge.generator)
1033
+ }
1034
+ if (bridge.generators) {
1035
+ const generators = [...bridge.generators]
1036
+ generators.reverse()
1037
+ for (let generator of generators) {
1038
+ this.config.generators.unshift(generator)
1039
+ }
1040
+ }
1041
+ if (bridge.generatorp) {
1042
+ this.config.generators.unshift({
1043
+ where: bridge.generatorp.where || client.where(3),
1044
+ match: ({context}) => bridge.id == context.marker && context.paraphrase,
1045
+ apply: (args) => bridge.generatorp(args),
1046
+ })
1047
+ }
1048
+ if (bridge.generatorr) {
1049
+ this.config.generators.unshift({
1050
+ // TODO merge response and isResponse
1051
+ where: bridge.generatorr.where || client.where(3),
1052
+ match: ({context}) => bridge.id == context.marker && !context.paraphrase && (context.response || context.isResponse),
1053
+ apply: (args) => bridge.generatorr(args),
1054
+ })
1055
+ }
1056
+ if (bridge.evaluator) {
1057
+ this.config.semantics.unshift({
1058
+ where: bridge.evaluator.where || client.where(3),
1059
+ match: ({context}) => bridge.id == context.marker && context.evaluate,
1060
+ apply: (args) => bridge.evaluator(args),
1061
+ })
1062
+ }
1063
+ if (bridge.semantic) {
1064
+ this.config.semantics.unshift({
1065
+ where: bridge.semantic.where || client.where(3),
1066
+ match: ({context}) => bridge.id == context.marker,
1067
+ apply: (args) => bridge.semantic(args),
1068
+ })
1069
+ }
1070
+ }
1071
+ if (config.operators) {
1072
+ config.operators = config.operators.map((operator) => {
1073
+ if (typeof operator === 'string') {
1074
+ return { pattern: operator }
1075
+ } else {
1076
+ return operator
1077
+ }
1078
+ })
1079
+ }
1080
+ }
1081
+ this.initConfig = _.cloneDeep(this.config)
1082
+ this.configs.push(new KM({ config: this.config, uuid: this._uuid }))
1083
+
1084
+ /*
1085
+ if (config) {
1086
+ this.configs.push(new KM({config, isSelf: true}))
1087
+ this.addInternal(Object.assign({}, config), false)
1088
+ } else {
1089
+ this.configs.push( new KM({config: this.config, isSelf: true}) )
1090
+ }
1091
+ */
1092
+
1093
+ this.setUUIDs()
1094
+ this.initDefaults()
1095
+ if (!this.config.objects.namespaced) {
1096
+ this.config.objects.namespaced = {}
1097
+ }
1098
+ this.get('objects').namespaced[this._uuid] = {}
1099
+ this.valid()
1100
+ }
1101
+
1102
+ addArgs(moreArgs) {
1103
+ this.addedArgss.push(moreArgs)
1104
+ }
1105
+
1106
+ getAddedArgs(args) {
1107
+ for (let addedArgs of this.addedArgss) {
1108
+ addedArgs = addedArgs(args)
1109
+ Object.assign(args, addedArgs)
1110
+ }
1111
+ }
1112
+
1113
+ set multiApi (initializer) {
1114
+ this.api = multiApiImpl(initializer)
1115
+ }
1116
+
1117
+ get multiApi () {
1118
+ if (this._api.multiApi) {
1119
+ return this._api
1120
+ } else {
1121
+ return null
1122
+ }
1123
+ }
1124
+
1125
+ get api () {
1126
+ if (this._api && this._api.multiApi) {
1127
+ return this._api.api(this._api)
1128
+ } else {
1129
+ return this._api
1130
+ }
1131
+ }
1132
+
1133
+ addAPI(api) {
1134
+ if (this._api && this._api.multiApi) {
1135
+ this._api.add(this, this._api, api)
1136
+ } else {
1137
+ throw "Can only add apis to a multi-api";
1138
+ }
1139
+ }
1140
+
1141
+ set api (value) {
1142
+ if (this._api && this._api.multiApi) {
1143
+ this._api.add(this, this._api, value)
1144
+ } else {
1145
+ this._api = _.cloneDeep(value)
1146
+ if (this._api) {
1147
+ this._api.objects = this.config.objects
1148
+ this._api.config = () => this
1149
+ this._api.uuid = this._uuid
1150
+ }
1151
+ }
1152
+ }
1153
+
1154
+ set afterTest (value) {
1155
+ this._afterTest = value
1156
+ }
1157
+
1158
+ get uuid () {
1159
+ return this._uuid
1160
+ }
1161
+
1162
+ set uuid (uuid) {
1163
+ const map = { [this._uuid]: uuid }
1164
+ this._uuid = uuid
1165
+ configCounter += 1
1166
+ this.mapUUIDs(map)
1167
+ }
1168
+
1169
+ // remove all added modules and initialize with the init config
1170
+ resetToOne () {
1171
+ /*
1172
+ this.config = _.cloneDeep(this.initConfig)
1173
+ this.configs = [this.configs[0]]
1174
+ this.setUUIDs()
1175
+ this.initDefaults()
1176
+ */
1177
+ this.configs = [this.configs[0]]
1178
+ this.defaultConfig()
1179
+ if (!this.initializeFn) {
1180
+ // TODO move this to the default initializer
1181
+ Object.assign(this.config.objects, _.cloneDeep(this.initConfig.objects || {}))
1182
+ }
1183
+ this.initializeConfigFromConfigs({ others: [], objects: this.config.objects.namespaced, moreNames: [], callInitializers: false })
1184
+ const map = {}
1185
+ for (let i = 0; i < this.configs.length; ++i) {
1186
+ map[this.configs[i].uuid] = this.configs[i].uuid
1187
+ }
1188
+ this.mapUUIDs(map)
1189
+ // this.valid() init was not run because the kms are not all setup yet
1190
+ }
1191
+
1192
+ // motivation === { match, apply, uuid }
1193
+ addMotivation (motivation) {
1194
+ this.motivations.push(motivation)
1195
+ }
1196
+
1197
+ resetMotivations () {
1198
+ this.motivations = []
1199
+ }
1200
+
1201
+ doMotivations (args, context) {
1202
+ args = Object.assign({}, args, { context })
1203
+ args.objects = args.getObjects(this.uuid)
1204
+ const motivations = this.motivations
1205
+ this.motivations = []
1206
+ let done = false
1207
+ for (const motivation of motivations) {
1208
+ if (!done && motivation.match(args)) {
1209
+ motivation.apply(args)
1210
+ if (args.context.controlKeepMotivation || motivation.repeat) {
1211
+ this.motivations.push(motivation)
1212
+ }
1213
+ done = true
1214
+ } else {
1215
+ this.motivations.push(motivation)
1216
+ }
1217
+ }
1218
+ return done
1219
+ }
1220
+
1221
+ // TODO add more details
1222
+ equal(config) {
1223
+ if (JSON.stringify(this.config) != JSON.stringify(config.config)) {
1224
+ debugger;
1225
+ return false;
1226
+ }
1227
+ return true
1228
+ }
1229
+
1230
+ dump(fn) {
1231
+ const fs = require('fs')
1232
+ fs.writeFileSync(fn, JSON.stringify(this.config, 0, 2))
1233
+ }
1234
+
1235
+ copy (options = {}) {
1236
+ this.valid()
1237
+ const cp = new Config()
1238
+ cp.logs = []
1239
+ cp.transitoryMode = this.transitoryMode
1240
+ cp.configs = this.configs.map((km) => km.copy2(Object.assign({}, options, { callInitializers: false })))
1241
+ cp._uuid = cp.configs[0]._uuid
1242
+ cp.initializerFn = this.initializerFn
1243
+ cp.initAfterApi = this.initAfterApi
1244
+ cp._api = _.cloneDeep(this._api)
1245
+ cp._namespace = this._namespace
1246
+ cp._eqClasses = this._eqClasses
1247
+ cp.name = this.name
1248
+ cp.description = this.description
1249
+ cp.tests = this.tests
1250
+ cp.motivations = this.motivations
1251
+ cp.isModule = this.isModule
1252
+ cp.initInstances = this.initInstances.slice()
1253
+ cp.instances = this.instances.slice()
1254
+
1255
+ cp.initConfig = _.cloneDeep(this.initConfig)
1256
+ cp.defaultConfig()
1257
+ // cp.wasInitialized = false; // since default config GREG
1258
+ cp.resetWasInitialized()
1259
+ if (!this.initializeFn) {
1260
+ // TODO move this to the default initializer
1261
+ Object.assign(cp.config.objects, _.cloneDeep(this.initConfig.objects || {}))
1262
+ }
1263
+ // cp.initializeConfigFromConfigs({ others: [], objects: cp.config.objects.namespaced, moreNames: [], ...options })
1264
+ cp.initializeConfigFromConfigs(Object.assign({ others: [], objects: cp.config.objects.namespaced, moreNames: [] }, options))
1265
+ const map = {}
1266
+ for (let i = 0; i < this.configs.length; ++i) {
1267
+ map[this.configs[i].uuid] = cp.configs[i].uuid
1268
+ }
1269
+ cp.mapUUIDs(map)
1270
+
1271
+ if (cp._api) {
1272
+ cp._api.objects = cp.config.objects
1273
+ cp._api.config = () => (cp instanceof Config) ? cp : cp.config
1274
+ cp._api.uuid = cp._uuid
1275
+ }
1276
+
1277
+ if (!cp.config.objects) {
1278
+ cp.config.objects = { namespaced: {} }
1279
+ } else if (!cp.config.objects.namespaced) {
1280
+ cp.config.objects.namespaced = {}
1281
+ }
1282
+ cp.configs.forEach((km) => {
1283
+ // const namespace = km.namespace
1284
+ cp.config.objects.namespaced[km._uuid] = {}
1285
+ })
1286
+
1287
+ if (options.callInitializers) {
1288
+ cp.rebuild(options)
1289
+ }
1290
+ cp.valid()
1291
+ return cp
1292
+ }
1293
+
1294
+ setUUIDs () {
1295
+ this.config.bridges && this.config.bridges.forEach((bridge) => { bridge.uuid = this._uuid })
1296
+ this.config.words && setWordsUUIDs(this.config.words, this._uuid)
1297
+ this.config.operators && this.config.operators.forEach((operator) => { operator.uuid = this._uuid })
1298
+ const ids = Array.from(new Set((this.config.bridges && this.config.bridges.map((bridge) => bridge.id)) || []))
1299
+ ids.sort()
1300
+ this.config.namespaces = {}
1301
+ // if (true || ids.length > 0) {
1302
+ this.config.namespaces[this.uuid] = { ids, namespace: this._namespace }
1303
+ for (const bag of bags) {
1304
+ if (this.config[bag]) {
1305
+ this.config[bag].forEach((bag) => { bag.uuid = this._uuid })
1306
+ }
1307
+ }
1308
+ }
1309
+
1310
+ mapUUIDs (map) {
1311
+ if (this._eqClasses) {
1312
+ this._eqClasses = this._eqClasses.map((eqclass) => eqclass.map((uuid) => map[uuid] || uuid))
1313
+ }
1314
+
1315
+ if (this.config.namespaces) {
1316
+ const ns = {}
1317
+ for (const uuid of Object.keys(this.config.namespaces)) {
1318
+ const uuidPrime = map[uuid] || uuid
1319
+ ns[uuidPrime] = this.config.namespaces[uuid]
1320
+ }
1321
+ this.config.namespaces = ns
1322
+ }
1323
+
1324
+ if (this.config.words) {
1325
+ const words = this.config.words
1326
+ for (const key in words) {
1327
+ words[key].forEach((o) => {
1328
+ if (o.uuid) {
1329
+ if (map[o.uuid]) {
1330
+ o.uuid = map[o.uuid]
1331
+ }
1332
+ } else {
1333
+ o.uuid = this._uuid
1334
+ }
1335
+ })
1336
+ }
1337
+ }
1338
+
1339
+ if (this.config.bridges) {
1340
+ this.config.bridges.forEach((bridge) => {
1341
+ if (bridge.uuid) {
1342
+ if (map[bridge.uuid]) {
1343
+ bridge.uuid = map[bridge.uuid]
1344
+ }
1345
+ } else {
1346
+ bridge.uuid = this._uuid
1347
+ }
1348
+ })
1349
+ }
1350
+
1351
+ if (this.config.operators) {
1352
+ this.config.operators.forEach((operator) => {
1353
+ if (operator.uuid) {
1354
+ if (map[operator.uuid]) {
1355
+ operator.uuid = map[operator.uuid]
1356
+ }
1357
+ } else {
1358
+ operator.uuid = this._uuid
1359
+ }
1360
+ })
1361
+ }
1362
+
1363
+ for (const bag of bags) {
1364
+ if (this.config[bag]) {
1365
+ this.config[bag].forEach((bag) => {
1366
+ if (bag.uuid) {
1367
+ if (map[bag.uuid]) {
1368
+ bag.uuid = map[bag.uuid]
1369
+ }
1370
+ } else {
1371
+ bag.uuid = this._uuid
1372
+ }
1373
+ })
1374
+ }
1375
+ }
1376
+ }
1377
+
1378
+ initializeFromConfigs () {
1379
+ this.configs.forEach(({ config, namespace, uuid }) => {
1380
+ /*
1381
+ let objects = this.get('objects')
1382
+ if (namespace) {
1383
+ objects = {}
1384
+ this.get('objects')['namespaced'][namespace] = objects
1385
+ }
1386
+ */
1387
+ const objects = {}
1388
+ const km = (name) => this.getConfig(name)
1389
+ if (config instanceof Config) {
1390
+ const aw = addWord(this, config)
1391
+ this.get('objects').namespaced[config._uuid] = objects
1392
+ if (config._api) {
1393
+ config._api.objects = objects
1394
+ config._api.config = () => this
1395
+ }
1396
+ config.initializerFn({ addWord: aw, km, config, baseConfig: this, currentConfig: config, objects, namespace, uuid, api: config.api })
1397
+ } else {
1398
+ const aw = addWord(this, this)
1399
+ this.get('objects').namespaced[this._uuid] = objects
1400
+ if (config._api) {
1401
+ config._api.objects = objects
1402
+ config._api.config = () => this
1403
+ }
1404
+ this.initializerFn({ addWord: aw, km, config: this, baseConfig: this, currentConfig: this, objects, namespace, uuid, api: this.api })
1405
+ }
1406
+ })
1407
+ this.instances.forEach((instance) => client.processInstance(this, instance))
1408
+ }
1409
+
1410
+ initialize ({ force = true } = {}) {
1411
+ if (force || !this.wasInitialized) {
1412
+ const aw = addWord(this, this)
1413
+ const km = (name) => this.getConfig(name)
1414
+ // this.initializerFn({ addWord: aw, km, config: this, baseConfig: this, currentConfig: this, objects: this.get('objects'), uuid: this._uuid, namespace: '', api: this.api })
1415
+ const objects = this.config.objects.namespaced[this._uuid]
1416
+ this.initializerFn({ addWord: aw, km, config: this, baseConfig: this, currentConfig: this, objects, uuid: this._uuid, namespace: '', api: this.api })
1417
+ this.wasInitialized = true
1418
+ }
1419
+ }
1420
+
1421
+ initializer (fn, { initAfterApi = false } = {}) {
1422
+ this.wasInitialized = false
1423
+ this.initAfterApi = initAfterApi
1424
+ this.initializerFn = (args) => {
1425
+ const transitoryMode = global.transitoryMode
1426
+ global.transitoryMode = false
1427
+ // const baseConfig = args.baseConfig
1428
+ const currentConfig = args.currentConfig
1429
+
1430
+ if (currentConfig.api) {
1431
+ currentConfig.api.objects = args.objects
1432
+ currentConfig.api.config = () => this
1433
+ currentConfig.api.uuid = currentConfig._uuid
1434
+ }
1435
+ // this.instances.forEach( (instance) => client.processInstance(this, instance) )
1436
+ fn(args)
1437
+ currentConfig.wasInitialized = true
1438
+ global.transitoryMode = transitoryMode
1439
+ }
1440
+ // this.initializeFromConfigs()
1441
+ this.rebuild()
1442
+ }
1443
+
1444
+ nsToString (id) {
1445
+ return `${this._namespace.concat(id).join('#')}`
1446
+ }
1447
+
1448
+ validBag (bag) {
1449
+ if (bag.match === null) {
1450
+ return false
1451
+ }
1452
+ if (bag.apply === null) {
1453
+ return false
1454
+ }
1455
+ if (bag.uuid === null) {
1456
+ return false
1457
+ }
1458
+ return true
1459
+ }
1460
+
1461
+ valid () {
1462
+ const uuids = this.configs.map((config) => config._uuid)
1463
+
1464
+ if (!this.logs) {
1465
+ debugBreak()
1466
+ return false
1467
+ }
1468
+ for (const bag of bags) {
1469
+ if (this.config[bag]) {
1470
+ const problem = this.config[bag].some((bag) => {
1471
+ if (!this.validBag(bag)) {
1472
+ debugBreak()
1473
+ return true
1474
+ }
1475
+ if (!uuids.includes(bag.uuid)) {
1476
+ debugBreak()
1477
+ return true
1478
+ }
1479
+ return false
1480
+ })
1481
+
1482
+ if (problem) {
1483
+ return false
1484
+ }
1485
+ }
1486
+ }
1487
+
1488
+ if (this._uuid !== this.configs[0].uuid) {
1489
+ debugBreak()
1490
+ return false
1491
+ }
1492
+ if (this.configs[0]._config instanceof Config) {
1493
+ debugBreak()
1494
+ return false
1495
+ }
1496
+
1497
+ if (!this.config.objects) {
1498
+ debugBreak()
1499
+ return false
1500
+ }
1501
+
1502
+ for (const key in this.config.words) {
1503
+ const values = this.config.words[key]
1504
+ if (values.some((word) => (Object.keys(word).includes('uuid') && !word.uuid))) {
1505
+ debugBreak()
1506
+ return false
1507
+ }
1508
+ }
1509
+
1510
+ const kmsUuids = this.configs.map((km) => km.uuid)
1511
+ const bridgesUuids = (this.config.bridges && this.config.bridges.map((bridge) => bridge.uuid).filter((uuid) => uuid)) || []
1512
+ let result = true
1513
+ bridgesUuids.forEach((buuid) => {
1514
+ if (!kmsUuids.includes(buuid)) {
1515
+ debugBreak()
1516
+ result = false
1517
+ }
1518
+ })
1519
+
1520
+ if (this.get('namespaces')) {
1521
+ const namespaceUuids = Object.keys(this.get('namespaces'))
1522
+ namespaceUuids.forEach((uuid) => {
1523
+ if (!kmsUuids.includes(uuid)) {
1524
+ debugBreak() // in namespaces
1525
+ result = false
1526
+ }
1527
+ })
1528
+ if (!result) {
1529
+ debugBreak()
1530
+ return false
1531
+ }
1532
+ kmsUuids.forEach((uuid) => {
1533
+ if (!namespaceUuids.includes(uuid)) {
1534
+ debugBreak() // in namespaces
1535
+ result = false
1536
+ }
1537
+ })
1538
+ if (!result) {
1539
+ debugBreak()
1540
+ return false
1541
+ }
1542
+ }
1543
+
1544
+ if (Object.keys(this.config.namespaces || {}) === ['undefined']) {
1545
+ debugBreak()
1546
+ result = false
1547
+ }
1548
+ if (!result) {
1549
+ debugBreak() // not valid
1550
+ return false
1551
+ }
1552
+
1553
+ result = true
1554
+ this.configs.forEach((km) => {
1555
+ if (!km.valid()) {
1556
+ result = false
1557
+ }
1558
+ })
1559
+ if (!result) {
1560
+ debugBreak() // not valid
1561
+ return false
1562
+ }
1563
+ return result
1564
+ }
1565
+
1566
+ resetWasInitialized () {
1567
+ this.wasInitialized = false
1568
+ this.configs.forEach((km) => {
1569
+ const config = km.config
1570
+ if (config instanceof Config) {
1571
+ config.wasInitialized = false
1572
+ }
1573
+ })
1574
+ }
1575
+
1576
+ rebuild ({ isModule: mainIsModule = false } = {}) {
1577
+ const debug = this.config.debug;
1578
+ this.config = _.cloneDeep(this.initConfig)
1579
+ if (debug) {
1580
+ this.config.debug = debug
1581
+ }
1582
+ // already set
1583
+ // this.isModule = this.isModule || mainIsModule
1584
+ mainIsModule = this.isModule
1585
+ this.config.objects.namespaced = {}
1586
+ this.resetWasInitialized()
1587
+
1588
+ // reorder configs base on load ordering
1589
+ {
1590
+ const ordering = this.loadOrder.order(this.configs.map((km) => km.name || km.uuid))
1591
+ const oconfigs = []
1592
+ for (const nameOrUUID of ordering) {
1593
+ for (const km of this.configs) {
1594
+ if (km.name === nameOrUUID || km.uuid === nameOrUUID) {
1595
+ oconfigs.push(km)
1596
+ }
1597
+ }
1598
+ }
1599
+ this.configs = [...oconfigs]
1600
+ }
1601
+
1602
+ const inits = []
1603
+ const initAfterApis = []
1604
+ this.configs.forEach((km) => {
1605
+ const namespace = km.namespace
1606
+ this.config.objects.namespaced[km._uuid] = {}
1607
+ const namespacedObjects = this.config.objects.namespaced[km._uuid]
1608
+ this.setupNamespace(km)
1609
+ const aw = addWord(km.config, km.config)
1610
+ let config = km.config
1611
+
1612
+ if (config.addedArgss) {
1613
+ this.addedArgss = this.addedArgss.concat(config.addedArgss)
1614
+ }
1615
+
1616
+ let isSelf = false
1617
+ let isModule = true
1618
+ if (!(config instanceof Config)) {
1619
+ config = this
1620
+ isSelf = true
1621
+ isModule = mainIsModule
1622
+ }
1623
+ if (!isSelf) {
1624
+ config.config = _.cloneDeep(config.initConfig)
1625
+ }
1626
+ config.wasInitialized = false
1627
+ // TODO change name of config: to baseConfig:
1628
+ const kmFn = (name) => this.getConfig(name)
1629
+ const args = { isModule, addWord: aw, km: kmFn, config, baseConfig: this, currentConfig: config, uuid: config._uuid, objects: namespacedObjects, namespace, api: config.api }
1630
+ config.initializerFn(args)
1631
+ if (config.initAfterApi) {
1632
+ initAfterApis.push({ config, args })
1633
+ }
1634
+ // greg
1635
+ if (config._api) {
1636
+ if (config._api.initialize) {
1637
+ // reverse the list
1638
+ inits.unshift( () => config._api.initialize({ config: this, km: kmFn, api: config._api }) )
1639
+ // config._api.initialize({ config, api: config._api })
1640
+ }
1641
+ config._api.objects = namespacedObjects
1642
+ config._api.config = () => this
1643
+ config._api.uuid = config._uuid
1644
+ }
1645
+ config.setUUIDs()
1646
+ config.applyNamespace(config.config, namespace, config.uuid)
1647
+ if (!isSelf) {
1648
+ this.addInternal(config, true, false, false, true)
1649
+ }
1650
+ km.valid()
1651
+ })
1652
+ // console.log('inits from config', inits)
1653
+ for (const init of inits) {
1654
+ init()
1655
+ }
1656
+ for (let init of initAfterApis) {
1657
+ // init.args.isAfterApi = true
1658
+ init.config.initializerFn({ ...init.args, isAfterApi: true })
1659
+ }
1660
+ this.instances.forEach((instance) => client.processInstance(this, instance))
1661
+ this.valid()
1662
+ this.checkBridges()
1663
+ }
1664
+
1665
+ // name: namespace name
1666
+ // others
1667
+ // if undefined namespace applies to first loaded config
1668
+ // if list of name then namespace applied only to those configs
1669
+ // if true then namespace applies to all loaded configs
1670
+ namespace (moreNames, others) {
1671
+ const config = this.copy()
1672
+ if (!Array.isArray(moreNames)) {
1673
+ moreNames = [moreNames]
1674
+ }
1675
+ config.defaultConfig()
1676
+
1677
+ if (Array.isArray(others)) {
1678
+ config.configs.forEach((km) => {
1679
+ let config = km.config
1680
+ let updateNamespace = false
1681
+ if (!(km.config instanceof Config)) {
1682
+ config = this
1683
+ updateNamespace = true
1684
+ }
1685
+ if (others.includes(config.config.name)) {
1686
+ km.namespace = moreNames.concat(km.namespace)
1687
+ if (updateNamespace) {
1688
+ config._namespace = moreNames.concat(config._namespace)
1689
+ }
1690
+ }
1691
+ })
1692
+ } else if (others) {
1693
+ config.configs.forEach((km) => {
1694
+ km.namespace = moreNames.concat(km.namespace)
1695
+ })
1696
+ config._namespace = moreNames.concat(config._namespace)
1697
+ } else {
1698
+ const km = config.configs[0]
1699
+ km.namespace = moreNames.concat(km.namespace)
1700
+ config._namespace = moreNames.concat(config._namespace)
1701
+ }
1702
+ config.rebuild()
1703
+ return config
1704
+ }
1705
+
1706
+ setupNamespace (km) {
1707
+ let config = km.config
1708
+ if (!(config instanceof Config)) {
1709
+ config = this.initConfig
1710
+ }
1711
+ const bridges = ((config instanceof Config) ? config.config.bridges : config.bridges) || []
1712
+ const ids = Array.from(new Set(bridges.map((bridge) => bridge.id)))
1713
+ this.config.namespaces = this.config.namespaces || {}
1714
+
1715
+ this.config.namespaces[km.uuid] = {
1716
+ ids,
1717
+ namespace: km.namespace
1718
+ }
1719
+ }
1720
+
1721
+ // config.initializerFn({ config: this, objects, namespace })
1722
+ // this goes through all the configs and sets up this.config
1723
+ initializeConfigFromConfigs ({ others, objects, moreNames, callInitializers = true }) {
1724
+ // setup the namespace in the configs
1725
+ let first = true
1726
+ this.configs.forEach((km) => {
1727
+ let config = km.config
1728
+ if (!(config instanceof Config)) {
1729
+ config = this.initConfig
1730
+ }
1731
+ if (!km.namespace) {
1732
+ km.namespace = []
1733
+ }
1734
+ if (config instanceof Config) {
1735
+ // km.namespace = config._namespace
1736
+ config = config.initConfig
1737
+ }
1738
+
1739
+ const configPrime = _.cloneDeep(config)
1740
+
1741
+ if (Array.isArray(others)) {
1742
+ if (others.indexOf(config.name) >= 0) {
1743
+ km.namespace = moreNames.concat(km.namespace)
1744
+ }
1745
+ } else if (others === true || (!others && first)) {
1746
+ km.namespace = moreNames.concat(km.namespace)
1747
+ }
1748
+
1749
+ this.applyNamespace(configPrime, km.namespace, km.uuid)
1750
+ first = false
1751
+ this.addInternal(configPrime, false, true)
1752
+ applyUUID(configPrime, km.uuid)
1753
+ })
1754
+
1755
+ // setup the namespaces mapping for passing to the server side
1756
+
1757
+ this.configs.forEach((km) => {
1758
+ // const namespace = km.namespace
1759
+ // if (true || namespace.length > 0) { this.setupNamespace(km) }
1760
+ this.setupNamespace(km)
1761
+ })
1762
+
1763
+ // initialize the configs in context
1764
+
1765
+ if (callInitializers) {
1766
+ this.configs.forEach(({ namespace, uuid, config, isSelf }) => {
1767
+ if (isSelf) {
1768
+ config = this
1769
+ }
1770
+ let nsobjects = objects
1771
+ if (namespace) {
1772
+ nsobjects = this.pathToNamespace(objects, namespace)
1773
+ }
1774
+ const km = (name) => this.getConfig(name)
1775
+ if (config instanceof Config) {
1776
+ const aw = addWord(this, config)
1777
+ config.initializerFn({ isModule: this.isModule, addWord: aw, baseConfig: this, km, currentConfig: config, config, objects: nsobjects, namespace, uuid, api: config.api })
1778
+ } else {
1779
+ const aw = addWord(this, this)
1780
+ this.initializerFn({ isModule: this.isModule, addWord: aw, baseConfig: this, km, currentConfig: this, config: this, objects: nsobjects, namespace, uuid, api: this.api })
1781
+ }
1782
+ })
1783
+ }
1784
+ }
1785
+
1786
+ applyNamespace (config, namespace, uuid) {
1787
+ const toNS = (id) => {
1788
+ if (namespace.length === 0) {
1789
+ return id
1790
+ }
1791
+ return `${namespace.join('#')}#${id}`
1792
+ }
1793
+ /*
1794
+ if (config.name) {
1795
+ config.name = toNS(config.name)
1796
+ }
1797
+ */
1798
+ if (config.associations) {
1799
+ const map = (associations) => {
1800
+ return associations.map((association) => {
1801
+ return association.map((id) => [toNS(id[0]), id[1]])
1802
+ })
1803
+ }
1804
+ const associations = config.associations
1805
+ if (associations.positive) {
1806
+ associations.positive = map(associations.positive)
1807
+ }
1808
+ if (associations.negative) {
1809
+ associations.negative = map(associations.negative)
1810
+ }
1811
+ }
1812
+
1813
+ if (config.words) {
1814
+ const words = {}
1815
+ for (const word in config.words) {
1816
+ words[word] = config.words[word].map((word) => {
1817
+ return Object.assign({}, word, { id: toNS(word.id), uuid })
1818
+ })
1819
+ }
1820
+ config.words = words
1821
+ }
1822
+
1823
+ if (config.hierarchy) {
1824
+ let hierarchy = config.hierarchy
1825
+ hierarchy = hierarchy.map((h) => {
1826
+ if (Array.isArray(h)) {
1827
+ return h.map((id) => toNS(id))
1828
+ } else {
1829
+ h.child = toNS(h.child)
1830
+ h.parent = toNS(h.parent)
1831
+ return Object.assign({}, h, { child: toNS(h.child), parent: toNS(h.parent) })
1832
+ }
1833
+ })
1834
+ config.hierarchy = hierarchy
1835
+ }
1836
+
1837
+ if (config.priorities) {
1838
+ let priorities = config.priorities
1839
+ priorities = priorities.map((p) => {
1840
+ return p.map((id) => {
1841
+ return [toNS(id[0]), id[1]]
1842
+ })
1843
+ })
1844
+ config.priorities = priorities
1845
+ }
1846
+
1847
+ for (const bag of bags) {
1848
+ if (config[bag]) {
1849
+ config[bag] = config[bag].map((b) => {
1850
+ return Object.assign({}, b, { uuid })
1851
+ })
1852
+ }
1853
+ }
1854
+ }
1855
+
1856
+ checkOperators () {
1857
+ if (!this.config.operators) {
1858
+ return
1859
+ }
1860
+
1861
+ const seen = {}
1862
+ for (let operator of this.config.operators) {
1863
+ if (seen[operator.pattern]) {
1864
+ const key = `${operator.pattern} (namespace: ${operator.uuid})`
1865
+ throw new Error(`Operator '${key}' is defined more than once in the operators`)
1866
+ }
1867
+ seen[operator.pattern] = true
1868
+ }
1869
+ }
1870
+
1871
+ checkBridges () {
1872
+ if (!this.config.bridges) {
1873
+ return
1874
+ }
1875
+
1876
+ /*
1877
+ "namespaces": {
1878
+ "634a678b-8d92-4464-bf65-943a82f404d8": {
1879
+ "ids": [ "tankConcept" ],
1880
+ "namespace": [ "ns1" ]
1881
+ },
1882
+ console.log('before check', JSON.stringify(this._eqClasses, null, 2))
1883
+ */
1884
+
1885
+ const errors = []
1886
+ /*
1887
+ const namespaces = this.config.namespaces
1888
+ const nsToIds = {}
1889
+ for (const uuid in namespaces) {
1890
+ const namespace = namespaces[uuid].namespace
1891
+ const ns = namespace.join('#')
1892
+ if (!nsToIds[ns]) {
1893
+ nsToIds[ns] = new Set()
1894
+ }
1895
+ const ids = nsToIds[ns]
1896
+ for (const id of namespaces[uuid].ids) {
1897
+ if (ids.has(id)) {
1898
+ if (ns === '') {
1899
+ const dups = this.config.bridges.filter( (bridge) => bridge.id == id )
1900
+ errors.push(`Id '${id}' is defined more than once in the bridges of the base namespace of the KM ${this.name}, Dups are ${JSON.stringify(dups)}`)
1901
+ } else {
1902
+ errors.push(`Id '${id}' is defined more than once in the bridges of the ${ns} namespace of the KM ${this.name}`)
1903
+ }
1904
+ } else {
1905
+ ids.add(id)
1906
+ }
1907
+ }
1908
+ }
1909
+ */
1910
+
1911
+ const keyIsPresent = {}
1912
+ for (const bridge of this.config.bridges) {
1913
+ const key = `${bridge.id}/${bridge.level} (namespace: ${bridge.uuid})`
1914
+ if (keyIsPresent[key]) {
1915
+ errors.push(new Error(`Id '${key}' is defined more than once in bridges of the base namespace of the KM ${this.name}`))
1916
+ } else {
1917
+ keyIsPresent[key] = key
1918
+ }
1919
+ }
1920
+
1921
+ if (errors.length > 0) {
1922
+ throw errors
1923
+ }
1924
+ };
1925
+
1926
+ get (property) {
1927
+ return this.config[property]
1928
+ }
1929
+
1930
+ set (property, value) {
1931
+ if (!this.config.hasOwnProperty(property)) {
1932
+ throw `Setting invalid property ${property}`
1933
+ }
1934
+ this.config[property] = value
1935
+ // this.configs[0][property] = value
1936
+ }
1937
+
1938
+ pathToNamespace (objects, path) {
1939
+ let ns = objects
1940
+ path = Object.assign([], path)
1941
+ while (path.length > 0) {
1942
+ const next = path.shift()
1943
+ if (!ns[next]) {
1944
+ ns[next] = {}
1945
+ }
1946
+ ns = ns[next]
1947
+ }
1948
+ return ns
1949
+ }
1950
+
1951
+ addEqClass (existing, other) {
1952
+ for (const eqClass of this._eqClasses) {
1953
+ if (eqClass.includes(other._uuid)) {
1954
+ eqClass.push(other._uuid)
1955
+ }
1956
+ }
1957
+ }
1958
+
1959
+ add (more) {
1960
+ if (more === this) {
1961
+ throw 'Cannot add an object to itself.'
1962
+ }
1963
+ if (!(more instanceof Config)) {
1964
+ more = new Config(more)
1965
+ }
1966
+
1967
+ this.valid()
1968
+ more.valid()
1969
+ // copy so i don't have to copy later
1970
+ more = more.copy()
1971
+ more.server(this._server, this._key)
1972
+
1973
+ this.loadOrder.addList(more.configs.map((km) => km.name || km.uuid))
1974
+
1975
+ // get the new ones
1976
+ // remove the dups
1977
+ // run the initialize one all new ones
1978
+ // this.configs = this.configs.concat(new KM({config: more}));
1979
+ // only set for the first one the rest have it set
1980
+ const namespace = this._namespace.concat(more._namespace)
1981
+ const moreKMs1 = [new KM({ config: more, uuid: more.uuid, namespace })]
1982
+ const moreKMs2 = more.configs.slice(1).map((km) => {
1983
+ return km
1984
+ // const cp = km.copy()
1985
+ // cp.namespace = namespace
1986
+ // return cp;
1987
+ })
1988
+ const moreKMs = moreKMs1.concat(moreKMs2)
1989
+ const eqClass = moreKMs.map((km) => km.uuid)
1990
+ this._eqClasses.push(eqClass)
1991
+ // look for dups and combine them with the eqclasses
1992
+ for (const moreKM of moreKMs) {
1993
+ const existingKM = this.configs.find((km) => moreKM._name && km.name === moreKM.name)
1994
+ if (existingKM) {
1995
+ this.addEqClass(existingKM, moreKM)
1996
+ } else {
1997
+ this.configs.push(moreKM)
1998
+ }
1999
+ }
2000
+ more.resetToOne()
2001
+ this.config.eqClasses = this._eqClasses
2002
+ // greg
2003
+ // setup instances
2004
+ this.instances = []
2005
+ this.configs.forEach((km) => {
2006
+ this.instances = (km._config.instances || this.initInstances.slice()).concat(this.instances)
2007
+ })
2008
+
2009
+ this.rebuild()
2010
+ this.valid()
2011
+ return this
2012
+ }
2013
+
2014
+ // TODO get rid of useOldVersion arg
2015
+ addInternal (more, useOldVersion = true, skipObjects = false, includeNamespaces = true, allowNameToBeNull = false) {
2016
+ if (more instanceof Config) {
2017
+ more.initialize({ force: false })
2018
+ if (useOldVersion) {
2019
+ more = more.config
2020
+ } else {
2021
+ // more = more.initConfig
2022
+ more = _.cloneDeep(more.initConfig)
2023
+ }
2024
+ }
2025
+ for (const key of Object.keys(more)) {
2026
+ const value = more[key]
2027
+ // TODO remove name and description on the config bag
2028
+ const noOverwrite = ['name', 'namespaced']
2029
+ if (!this.config[key]) {
2030
+ if (allowNameToBeNull) {
2031
+ if (noOverwrite.includes(key)) {
2032
+ continue
2033
+ }
2034
+ } else if (this.config[key] && noOverwrite.includes(key)) {
2035
+ continue
2036
+ }
2037
+ this.config[key] = value
2038
+ continue
2039
+ }
2040
+ if (key === 'words') {
2041
+ const configWords = this.config.words
2042
+ const moreWords = more.words
2043
+ for (const word of Object.keys(moreWords)) {
2044
+ if (!configWords[word]) {
2045
+ configWords[word] = []
2046
+ }
2047
+ configWords[word] = configWords[word].concat(moreWords[word])
2048
+ }
2049
+ } else if (key === 'name') {
2050
+ /*
2051
+ if (this.config[key] === '') {
2052
+ this.config[key] = more[key]
2053
+ }
2054
+ */
2055
+ } else if (key === 'namespaces') {
2056
+ if (includeNamespaces) {
2057
+ Object.assign(this.config[key], more[key])
2058
+ }
2059
+ } else if (key === 'debug') {
2060
+ this.config[key] = this.config[key] || more[key]
2061
+ } else if (key === 'description') {
2062
+ this.config[key] += ' ' + more[key].trim()
2063
+ } else if (key === 'objects') {
2064
+ if (!skipObjects) {
2065
+ // namespaced is special
2066
+ const namespaced = this.config.objects.namespaced
2067
+ Object.assign(this.config[key], more[key])
2068
+ this.config.objects.namespaced = namespaced
2069
+ }
2070
+ } else if (key === 'associations') {
2071
+ const configAssociations = this.config.associations
2072
+ const moreAssociations = more.associations
2073
+ if (moreAssociations.positive) {
2074
+ configAssociations.positive = configAssociations.positive.concat(moreAssociations.positive)
2075
+ }
2076
+ if (moreAssociations.negative) {
2077
+ configAssociations.negative = configAssociations.negative.concat(moreAssociations.negative)
2078
+ }
2079
+ } else if (Array.isArray(value)) {
2080
+ // handle allowDups
2081
+ if (key == 'operators') {
2082
+ // TODO what about other props
2083
+ const isDup = (op1, op2) => op1.pattern == op2.pattern
2084
+ for (const newOne of more[key]) {
2085
+ for (let iOldOne = 0; iOldOne < this.config[key].length; ++iOldOne) {
2086
+ const oldOne = this.config[key][iOldOne];
2087
+ if (isDup(newOne, oldOne)) {
2088
+ if (oldOne.allowDups) {
2089
+ // the old one takes precedence to match what would happen during the original load
2090
+ this.config[key].splice(iOldOne, 1)
2091
+ break;
2092
+ }
2093
+ }
2094
+ }
2095
+ }
2096
+ }
2097
+ if (key == 'bridges') {
2098
+ // TODO what about other props
2099
+ const idDup = (b1, b2) => b1.id == b2.id && b1.level == b2.level && b1.bridge == b2.bridge
2100
+ for (const newOne of more[key]) {
2101
+ for (let iOldOne = 0; iOldOne < this.config[key].length; ++iOldOne) {
2102
+ const oldOne = this.config[key][iOldOne];
2103
+ if (newOne.id == oldOne.id) {
2104
+ if (oldOne.allowDups) {
2105
+ // the old one takes precedence to match what would happen during the original load
2106
+ this.config[key].splice(iOldOne, 1)
2107
+ break;
2108
+ }
2109
+ }
2110
+ }
2111
+ }
2112
+ }
2113
+ // console.log('key', key, 'XXX')
2114
+ // console.log('more', JSON.stringify(more, null, 2))
2115
+ this.config[key] = this.config[key].concat(more[key])
2116
+ } else {
2117
+ if (!(key in this.config)) {
2118
+ throw `Unexpected property in config ${key}`
2119
+ }
2120
+ this.config[key] = more[key]
2121
+ }
2122
+ }
2123
+ return this
2124
+ }
2125
+ }
2126
+
2127
+ module.exports = {
2128
+ Config
2129
+ }