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/client.js +1473 -0
- package/index.js +27 -0
- package/lines.js +61 -0
- package/package.json +63 -0
- package/readme +132 -0
- package/src/config.js +2129 -0
- package/src/digraph.js +110 -0
- package/src/flatten.js +143 -0
- package/src/generators.js +282 -0
- package/src/helpers.js +216 -0
- package/src/semantics.js +293 -0
- package/src/unflatten.js +128 -0
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
|
+
}
|