@live-change/framework 0.5.11 → 0.5.15

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/lib/App.js CHANGED
@@ -34,6 +34,8 @@ const eventListener = require('./processes/eventListener.js')
34
34
 
35
35
  const utils = require('./utils.js')
36
36
 
37
+ const debug = require('debug')('framework')
38
+
37
39
  class App {
38
40
 
39
41
  constructor(config = {}) {
@@ -73,7 +75,7 @@ class App {
73
75
  this.databaseName = config?.db?.name || 'test'
74
76
 
75
77
  this.instanceId = randomString(4)
76
- this.uidGenerator = uidGenerator(this.instanceId)
78
+ this.uidGenerator = uidGenerator(this.instanceId, this.config.uidBorders)
77
79
 
78
80
  this.activeTimeouts = new Set()
79
81
  }
@@ -104,7 +106,7 @@ class App {
104
106
  }
105
107
 
106
108
  async applyChanges(changes, service, updaters, force) {
107
- console.log("APPLY CHANGES", JSON.stringify(changes, null, ' '))
109
+ debug("APPLY CHANGES", JSON.stringify(changes, null, ' '))
108
110
  updaters = updaters || this.defaultUpdaters
109
111
  for(let updater of updaters) {
110
112
  await updater(changes, service, this, force)
@@ -179,7 +181,7 @@ class App {
179
181
  })
180
182
  const routes = await this.dao.get(['database', 'tableRange', this.databaseName, 'triggerRoutes',
181
183
  { gte: data.type+'=', lte: data.type+'=\xFF\xFF\xFF\xFF' }])
182
- console.log("TRIGGER ROUTES", routes)
184
+ console.log("TRIGGER ROUTES", data.type, '=>', routes)
183
185
  let promises = []
184
186
  for(const route of routes) {
185
187
  promises.push(this.triggerService(route.service, { ...data }))
@@ -15,4 +15,12 @@ module.exports = function(service, definition, app) {
15
15
 
16
16
  delete definition.events
17
17
  delete definition.triggers
18
+ delete definition.authenticators
19
+ delete definition.processors
20
+ delete definition.validators
21
+ delete definition.processed
22
+ delete definition.foreignModels
23
+ delete definition.foreignIndexes
24
+ delete definition.config
25
+ delete definition.indexes
18
26
  }
@@ -84,6 +84,7 @@ class ServiceDefinition {
84
84
  this.use = []
85
85
  this.processors = []
86
86
  this.authenticators = []
87
+ this.beforeStartCallbacks = []
87
88
  this.validators = defaultValidators
88
89
  for(let key in definition) this[key] = definition[key]
89
90
  }
@@ -144,6 +145,10 @@ class ServiceDefinition {
144
145
  this.authenticators.push(authenticator)
145
146
  }
146
147
 
148
+ beforeStart(callback) {
149
+ this.beforeStartCallbacks.push(callback)
150
+ }
151
+
147
152
  toJSON() {
148
153
  let models = {}
149
154
  for(let key in this.models) models[key] = this.models[key].toJSON()
@@ -20,7 +20,8 @@ class Action {
20
20
  action: this,
21
21
  service: this.service,
22
22
  client: command.client,
23
- command
23
+ command,
24
+ trigger: (...args) => this.service.trigger(...args) /// TODO: collet call traces
24
25
  }, emit)
25
26
 
26
27
  resultPromise = resultPromise.then(async result => {
@@ -28,24 +28,24 @@ class ApiServer {
28
28
 
29
29
  async daoFactory(credentialsp, ip) {
30
30
  let credentials = { ...credentialsp, ip, roles: [] }
31
+ const allAuthenticators = []
31
32
  if(this.config.authenticators) {
32
33
  const auth = Array.isArray(this.config.authenticators)
33
34
  ? this.config.authenticators : [this.config.authenticators]
34
- for(const authenticator of auth) {
35
- if(authenticator && authenticator.prepareCredentials)
36
- await authenticator.prepareCredentials(credentials, this.config)
37
- }
35
+ allAuthenticators.push(...auth.filter(a => !!a))
38
36
  }
39
37
  for(const service of this.config.services) {
40
- console.log("SERIVCE AUTH", service.name, service.authenticators)
38
+ //console.log("SERIVCE AUTH", service.name, service.authenticators)
41
39
  if(service.authenticators) {
42
- for(const authenticator of service.authenticators) {
43
- if(authenticator && authenticator.prepareCredentials)
44
- await authenticator.prepareCredentials(credentials, this.config)
45
- }
40
+ allAuthenticators.push(...service.authenticators.filter(a => !!a))
46
41
  }
47
42
  }
48
- const dao = new this.DaoConstructor(this.config, { ...credentials })
43
+ for(const authenticator of allAuthenticators) {
44
+ if(authenticator.prepareCredentials) {
45
+ await authenticator.prepareCredentials(credentials, this.config)
46
+ }
47
+ }
48
+ const dao = new this.DaoConstructor({ ...this.config, authenticators: allAuthenticators }, { ...credentials })
49
49
  await dao.start()
50
50
  return dao
51
51
  }
@@ -11,21 +11,7 @@ class LiveDao extends LcDao.DaoProxy {
11
11
 
12
12
  this.authenticators = []
13
13
  if(this.config.authenticators) {
14
- const auth = Array.isArray(this.config.authenticators)
15
- ? this.config.authenticators : [this.config.authenticators]
16
- for(const authenticator of auth) {
17
- if(authenticator && authenticator.authenticatorObservable)
18
- this.authenticators.push(authenticator)
19
- }
20
- }
21
- for(const service of this.config.services) {
22
- console.log("SERIVCE AUTH", service.name, service.authenticators)
23
- if(service.authenticators) {
24
- for(const authenticator of service.authenticators) {
25
- if(authenticator && authenticator.authenticatorObservable)
26
- this.authenticators.push(authenticator)
27
- }
28
- }
14
+ this.authenticators = this.config.authenticators.filter(a => a.credentialsObservable)
29
15
  }
30
16
 
31
17
  this.currentDao = null
@@ -48,8 +34,8 @@ class LiveDao extends LcDao.DaoProxy {
48
34
  for(const credentialsObserver of this.credentialsObservations) {
49
35
  credentials = {
50
36
  ...credentials,
51
- ...credentialsObservers.credentials,
52
- roles: [...credentials.roles, ...credentialsObserver.credentials.roles]
37
+ ...credentialsObserver.credentials,
38
+ roles: [...credentials.roles, ...(credentialsObserver.credentials.roles || [])]
53
39
  }
54
40
  }
55
41
  return credentials
@@ -57,13 +43,21 @@ class LiveDao extends LcDao.DaoProxy {
57
43
 
58
44
  async start() {
59
45
  this.credentialsObservations = this.authenticators.map(authenticator => {
60
- const observable = authenticator.authenticatorObservable(this.initialCredentials)
46
+ const result = authenticator.credentialsObservable(this.initialCredentials)
47
+ const observable = result.then ? new LcDao.ObservablePromiseProxy(result) : result
61
48
  const observer = {
62
- set: (newCredentials) => {
63
- state.credentials = newCredentials
49
+ set: (data) => {
50
+ console.log("NEW CREDENTIALS", data)
51
+ if(data) {
52
+ const { id, ...newCredentials } = data
53
+ state.credentials = newCredentials
54
+ } else {
55
+ state.credentials = {}
56
+ }
64
57
  this.refreshCredentials()
65
58
  }
66
59
  }
60
+ observable.observe(observer)
67
61
  const promise = waitForSignal(observable)
68
62
  const state = {
69
63
  observable, observer, promise, credentials: {}
@@ -14,6 +14,31 @@ class ReaderModel {
14
14
  return ['database', 'tableObject', this.service.databaseName, this.tableName, id]
15
15
  }
16
16
 
17
+ limitedPath(id, fields) { // takes object or list of fields
18
+ if(Array.isArray(fields)) {
19
+ fields = utils.fieldListToFieldsObject(fields)
20
+ }
21
+ fields.id = true // id is required
22
+ return ['database', 'queryObject', this.service.databaseName, `(${
23
+ async (input, output, { tableName, id, fields }) => {
24
+ function mapper(obj, fields) {
25
+ if(!obj) return obj
26
+ const out = {}
27
+ for(const key in fields) {
28
+ if(typeof fields[key] == 'object') {
29
+ out[key] = mapper(obj[key], fields[key])
30
+ } else {
31
+ out[key] = obj[key]
32
+ }
33
+ }
34
+ return out
35
+ }
36
+ await input.table(tableName).object(id).onChange((obj, oldObj) =>
37
+ output.change(mapper(obj, fields), mapper(oldObj, fields)))
38
+ }
39
+ })`, { tableName: this.tableName, id, fields }]
40
+ }
41
+
17
42
  rangePath(range = {}) {
18
43
  if(typeof range != 'object') {
19
44
  const str = range.toString()
@@ -218,39 +243,30 @@ class ReaderModel {
218
243
  observable(id) {
219
244
  return this.service.dao.observable(this.path(id), ReactiveDao.ObservableValue)
220
245
  }
221
-
222
246
  async get(id) {
223
247
  return this.service.dao.get(this.path(id), ReactiveDao.ObservableValue)
224
248
  }
225
-
226
249
  rangeObservable(range) {
227
250
  return this.service.dao.observable(this.rangePath(range), ReactiveDao.ObservableList)
228
251
  }
229
-
230
252
  async rangeGet(range) {
231
253
  return this.service.dao.get(this.rangePath(range), ReactiveDao.ObservableList)
232
254
  }
233
-
234
255
  indexRangeObservable(index, range, pathRange = null) {
235
256
  return this.service.dao.observable(this.indexRangePath(index, range, pathRange), ReactiveDao.ObservableList)
236
257
  }
237
-
238
258
  async indexRangeGet(index, range, pathRange = null) {
239
259
  return this.service.dao.get(this.indexRangePath(index, range, pathRange), ReactiveDao.ObservableList)
240
260
  }
241
-
242
261
  indexObjectObservable(index, range, pathRange = null) {
243
262
  return this.service.dao.observable(this.indexObjectPath(index, range, pathRange))
244
263
  }
245
-
246
264
  async indexObjectGet(index, range, pathRange) {
247
265
  return this.service.dao.get(this.indexObjectPath(index, range, pathRange))
248
266
  }
249
-
250
267
  sortedIndexRangeObservable(index, range, pathRange = null) {
251
268
  return this.service.dao.observable(this.sortedIndexRangePath(index, range, pathRange), ReactiveDao.ObservableList)
252
269
  }
253
-
254
270
  async sortedIndexRangeGet(index, range, pathRange = null) {
255
271
  return this.service.dao.get(this.sortedIndexRangePath(index, range, pathRange), ReactiveDao.ObservableList)
256
272
  }
@@ -61,7 +61,6 @@ class Service {
61
61
  }
62
62
 
63
63
  this.authenticators = this.definition.authenticators
64
-
65
64
  }
66
65
 
67
66
  async start(config) {
@@ -76,7 +75,9 @@ class Service {
76
75
  let promises = config.processes.map(proc => proc(this, config))
77
76
  await Promise.all(promises)
78
77
 
79
- //if(config.startEventListener) this.startEventListener()
78
+ for(const beforeStartCallback of this.definition.beforeStartCallbacks) {
79
+ await beforeStartCallback(this)
80
+ }
80
81
 
81
82
  console.log("Service", this.definition.name, "started")
82
83
  }
@@ -16,7 +16,8 @@ class TriggerHandler {
16
16
  ...preparedParams
17
17
  }, {
18
18
  action: this,
19
- service: this.service
19
+ service: this.service,
20
+ trigger: (...args) => this.service.trigger(...args) /// TODO: collet call traces
20
21
  }, emit)
21
22
 
22
23
  resultPromise = resultPromise.then(async result => {
@@ -1,5 +1,5 @@
1
1
  const utils = require("../utils.js")
2
-
2
+ const debug = require('debug')('framework:updaters:db')
3
3
  async function update(changes, service, app, force) {
4
4
 
5
5
  const dao = app.dao
@@ -39,7 +39,7 @@ async function update(changes, service, app, force) {
39
39
  indexName = generateTableName(indexName)
40
40
  }
41
41
 
42
- console.log("CREATE INDEX", indexName)
42
+ debug("CREATE INDEX", indexName)
43
43
 
44
44
  const options = {
45
45
  multi: index.multi || false
@@ -99,19 +99,19 @@ async function update(changes, service, app, force) {
99
99
  }
100
100
  }
101
101
 
102
- console.log("INDEX CREATED!", index)
102
+ debug("INDEX CREATED!", index)
103
103
  }
104
104
 
105
- console.log("DATABASE UPDATER")
105
+ debug("DATABASE UPDATER")
106
106
 
107
107
  for(let change of changes) {
108
- console.log("PROCESSING CHANGE", change)
108
+ debug("PROCESSING CHANGE", change)
109
109
  switch(change.operation) {
110
110
  case "createModel": {
111
111
  const model = change.model
112
112
  const tableName = generateTableName(model.name)
113
113
  await dao.request(['database', 'createTable'], database, tableName)
114
- console.log("TABLE CREATED!", tableName)
114
+ debug("TABLE CREATED!", tableName)
115
115
  for(let indexName in model.indexes) {
116
116
  const index = model.indexes[indexName]
117
117
  await createIndex(tableName, indexName, index)
@@ -134,7 +134,7 @@ async function update(changes, service, app, force) {
134
134
  } break
135
135
  case "deleteModel": {
136
136
  const tableName = generateTableName(change.name)
137
- console.log("DELETE TABLE")
137
+ debug("DELETE TABLE")
138
138
  const model = change.model
139
139
  for(let indexName in model.indexes) {
140
140
  let indexName = change.name
@@ -188,9 +188,9 @@ async function update(changes, service, app, force) {
188
188
  const property = change.property
189
189
  let update = {}
190
190
  update[change.name] = property.defaultValue
191
- console.log("CREATE PROPERTY UPDATE", update)
191
+ debug("CREATE PROPERTY UPDATE", update)
192
192
  await dao.request(['database', 'query'], database, `(${
193
- async (input, output, { table, update }) =>
193
+ async (input, output, { table, update }) =>
194
194
  await input.table(table).onChange((obj, oldObj) => {
195
195
  if(obj) output.table(table).update(obj.id, [{ op: 'merge', value: update }])
196
196
  })
@@ -231,14 +231,14 @@ async function update(changes, service, app, force) {
231
231
  default:
232
232
  }
233
233
  }
234
- console.log("DATABASE UPDATED")
234
+ debug("DATABASE UPDATED")
235
235
 
236
- console.log("CHECKING DATABASE INTEGRITY...")
236
+ debug("CHECKING DATABASE INTEGRITY...")
237
237
  const indexes = await dao.get(['database', 'indexesList', database])
238
238
  for(const indexName in service.indexes) {
239
239
  const fullIndexName = generateTableName(indexName)
240
240
  if(!indexes.includes(fullIndexName)) {
241
- console.log("index", fullIndexName, "not found! creating...")
241
+ debug("index", fullIndexName, "not found! creating...")
242
242
  await createIndex(null, indexName, service.index[indexName])
243
243
  }
244
244
  }
@@ -248,8 +248,8 @@ async function update(changes, service, app, force) {
248
248
  for(const indexName in model.indexes) {
249
249
  const fullIndexName = tableName + '_' + indexName
250
250
  if(!indexes.includes(fullIndexName)) {
251
- console.log("I", indexes)
252
- console.log("table ", modelName, " index", fullIndexName, "not found! creating...")
251
+ debug("I", indexes)
252
+ debug("table ", modelName, " index", fullIndexName, "not found! creating...")
253
253
  await createIndex(generateTableName(modelName), indexName, model.indexes[indexName])
254
254
  }
255
255
  }
@@ -22,7 +22,7 @@ class CommandQueue {
22
22
  }
23
23
  }
24
24
  async start() {
25
- console.log("START QUEUE", this.tableName, this.indexName)
25
+ //console.log("START QUEUE", this.tableName, this.indexName)
26
26
  await this.connection.request(['database', 'createTable'], this.database, this.tableName).catch(e => 'ok')
27
27
  await this.connection.request(['database', 'createIndex'], this.database, this.indexName, `(${
28
28
  async function(input, output, { tableName }) {
@@ -29,7 +29,7 @@ class EventSourcing {
29
29
  await this.connection.request(['database', 'createLog'], this.database, this.logName).catch(e => 'ok')
30
30
  this.state = await this.connection.get(
31
31
  ['database', 'tableObject', this.database, 'eventConsumers', this.consumerId])
32
- console.log("GOT CONSUMER STATE", this.state)
32
+ //console.log("GOT CONSUMER STATE", this.state)
33
33
  if(!this.state) {
34
34
  this.state = {
35
35
  id: this.consumerId,
@@ -83,12 +83,12 @@ class EventSourcing {
83
83
  try {
84
84
  await this.doHandleEvent(event, mainEvent)
85
85
  done = true
86
- } catch(e) {
87
- if(e == 'timeout' && retry < maxRetry) {
86
+ } catch(e) {
87
+ if(e == 'timeout' && retry < maxRetry) {
88
88
  retry++
89
89
  const sleepTime = Math.pow(2, retry) * 100
90
90
  console.error(`Event \n${JSON.stringify(event, null, " ")}\n handling timeout, will retry `,
91
- retry, ' time after ', sleepTime, 'ms sleep')
91
+ retry, ' time after ', sleepTime, 'ms sleep')
92
92
  sleep(retry)
93
93
  } else {
94
94
  console.error(`EVENT \n${JSON.stringify(event, null, " ")}\n HANDLING ERROR`, e, ' => STOPPING!')
@@ -64,7 +64,7 @@ let validators = {
64
64
  }
65
65
  }
66
66
  validator.isRequired = (context) => {
67
- if(getField(context.props, prop) == to) {
67
+ if(getField(context, prop) == to) {
68
68
  for(let v of validators) {
69
69
  if(v.isRequired && v.isRequired(context)) return true
70
70
  }
package/lib/utils.js CHANGED
@@ -231,8 +231,23 @@ const rangeProperties = {
231
231
  }
232
232
  }
233
233
 
234
+ function fieldListToFieldsObject(fieldList) {
235
+ const fields = {}
236
+ for(const fieldName of fieldList) {
237
+ const parts = fieldName.split('.')
238
+ let at = fields
239
+ let i = 0
240
+ for(; i < parts.length - 1; i ++) {
241
+ if(!at[parts[i]]) at[parts[i]] = {}
242
+ at = at[parts[i]]
243
+ }
244
+ at[parts[i]] = true
245
+ }
246
+ return fields
247
+ }
248
+
234
249
  module.exports = {
235
250
  typeName, toJSON, setDifference, mapDifference, crudChanges,
236
251
  getProperty, setProperty, getField, setField, isObject, mergeDeep, generateDefault,
237
- prefixRange, rangeProperties
252
+ prefixRange, rangeProperties, fieldListToFieldsObject
238
253
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@live-change/framework",
3
- "version": "0.5.11",
3
+ "version": "0.5.15",
4
4
  "description": "Live Change Framework - ultimate solution for real time mobile/web apps",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -26,20 +26,12 @@
26
26
  "@live-change/db": "^0.3.64",
27
27
  "@live-change/db-store-level": "^0.1.15",
28
28
  "@live-change/db-store-lmdb": "^0.1.22",
29
- "@live-change/uid": "^0.1.3",
29
+ "@live-change/uid": "^0.1.4",
30
30
  "cookie": "^0.4.1",
31
- "encoding-down": "^7.1.0",
32
31
  "express": "^4.17.1",
33
- "level-rocksdb": "^5.0.0",
34
- "leveldown": "^6.1.0",
35
- "levelup": "^5.1.1",
36
- "memdown": "^6.1.1",
37
- "node-lmdb": "^0.8.0",
38
32
  "os-service": "^2.2.0",
39
33
  "rimraf": "^3.0.2",
40
- "rocksdb": "^5.2.0",
41
34
  "@live-change/sockjs": "^0.4.0-rc.1",
42
- "subleveldown": "^6.0.1",
43
35
  "tape": "^5.3.2",
44
36
  "websocket": "^1.0.34"
45
37
  }