@live-change/framework 0.5.5 → 0.5.9

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/index.js CHANGED
@@ -1,12 +1,18 @@
1
1
  const App = require('./lib/App.js')
2
- App.app = () => {
2
+
3
+ module.exports = App
4
+
5
+ module.exports.app = () => {
3
6
  if(!global.liveChangeFrameworkApp) {
4
7
  global.liveChangeFrameworkApp = new App()
5
8
  }
6
9
  return global.liveChangeFrameworkApp
7
10
  }
8
11
 
9
- module.exports = App
12
+ module.exports.utils = require('./lib/utils.js')
13
+ module.exports.validation = require('./lib/utils/validation.js')
14
+ module.exports.rangeProperties = module.exports.utils.rangeProperties
15
+
10
16
 
11
17
  module.exports.ActionDefinition = require('./lib/definition/ActionDefinition.js')
12
18
  module.exports.EventDefinition = require('./lib/definition/EventDefinition.js')
package/lib/App.js CHANGED
@@ -9,8 +9,9 @@ const Service = require("./runtime/Service.js")
9
9
  const profileLog = require("./utils/profileLog.js")
10
10
 
11
11
  const Dao = require("./runtime/Dao.js")
12
+ const SessionDao = require("./runtime/SessionDao.js")
13
+ const LiveDao = require("./runtime/LiveDao.js")
12
14
  const ApiServer = require("./runtime/ApiServer.js")
13
- const SessionApiServer = require("./runtime/SessionApiServer.js")
14
15
 
15
16
  const reverseRelationProcessor = require("./processors/reverseRelation.js")
16
17
  const indexListProcessor = require("./processors/indexList.js")
@@ -73,20 +74,28 @@ class App {
73
74
 
74
75
  this.instanceId = randomString(4)
75
76
  this.uidGenerator = uidGenerator(this.instanceId)
77
+
78
+ this.activeTimeouts = new Set()
76
79
  }
77
80
 
78
81
  createServiceDefinition( definition ) {
79
- const config = this.config && this.config.services && this.config.services[definition.name]
82
+ const config = this.config && this.config.services && this.config.services.find
83
+ && this.config.services.find(c => c.name == definition.name)
80
84
  return new ServiceDefinition({ ...definition, config })
81
85
  }
82
86
 
83
87
  processServiceDefinition( sourceService, processors ) {
84
88
  if(!processors) processors = this.defaultProcessors
85
89
  processors = processors.slice()
86
- for(const plugin of sourceService.use) {
87
- processors.unshift(...plugin.processors)
90
+ function processUse(service) {
91
+ if(service && service.use) {
92
+ for(const plugin of service.use) {
93
+ processUse(plugin)
94
+ }
95
+ }
96
+ processors.unshift(...service.processors)
88
97
  }
89
- processors.unshift(...sourceService.processors)
98
+ processUse(sourceService)
90
99
  for(let processor of processors) processor(sourceService, this)
91
100
  }
92
101
 
@@ -145,9 +154,11 @@ class App {
145
154
  async createApiServer( config ) {
146
155
  return new ApiServer({ ...config, app: this })
147
156
  }
148
-
149
157
  async createSessionApiServer( config ) {
150
- return new SessionApiServer({ ...config, app: this })
158
+ return new ApiServer({ ...config, app: this }, SessionDao)
159
+ }
160
+ async createLiveApiServer( config ) {
161
+ return new ApiServer({ ...config, app: this }, LiveDao)
151
162
  }
152
163
 
153
164
  generateUid() {
@@ -248,9 +259,11 @@ class App {
248
259
  requestTimeout = this.requestTimeout
249
260
  }
250
261
  if(requestTimeout) {
251
- setTimeout(() => {
262
+ const timeout = setTimeout(() => {
263
+ this.activeTimeouts.delete(timeout)
252
264
  reject('timeout')
253
265
  }, requestTimeout)
266
+ this.activeTimeouts.add(timeout)
254
267
  }
255
268
  }).finally(() => {
256
269
  objectObservable.unobserve(observer)
@@ -374,6 +387,9 @@ class App {
374
387
  }
375
388
 
376
389
  async close() {
390
+ for(const timeout of this.activeTimeouts) {
391
+ clearTimeout(timeout)
392
+ }
377
393
  this.dao.dispose()
378
394
  }
379
395
 
@@ -381,4 +397,3 @@ class App {
381
397
 
382
398
 
383
399
  module.exports = App
384
- module.exports.rangeProperties = utils.rangeProperties
@@ -1,60 +1,9 @@
1
- function getValidator(validation, context) {
2
- if(typeof validation == 'string') {
3
- let validator = context.service.validators[validation]
4
- if(!validator) throw new Error(`Validator ${validation} not found`)
5
- return validator({}, context)
6
- } else {
7
- let validator = context.service.validators[validation.name]
8
- if(!validator) throw new Error(`Validator ${validation.name} not found`)
9
- return validator(validation, context)
10
- }
11
- }
12
-
13
- function getValidators(source, service) {
14
- let validators = {}
15
- const context = { source, service, getValidator: validation => getValidator(validation, context) }
16
- for(let propName in source.properties) {
17
- const prop = source.properties[propName]
18
- if(prop.validation) {
19
- const validations = Array.isArray(prop.validation) ? prop.validation : [prop.validation]
20
- for(let validation of validations) {
21
- const validator = getValidator(validation, context)
22
- if(validators[propName]) validators[propName].push(validator)
23
- else validators[propName] = [validator]
24
- }
25
- }
26
- }
27
- return validators
28
- }
29
-
30
- async function validate(props, validators, context) {
31
- console.log("VALIDATE PROPS", props, "WITH", validators)
32
- let propPromises = {}
33
- for(let propName in validators) {
34
- let propValidators = validators[propName]
35
- let promises = []
36
- for(let validator of propValidators) {
37
- //console.log("PROPS",props, propName)
38
- promises.push(validator(props[propName], { ...context, props, propName }))
39
- }
40
- propPromises[propName] = Promise.all(promises)
41
- }
42
- let propErrors = {}
43
- for(let propName in validators) {
44
- let errors = (await propPromises[propName]).filter(e=>!!e)
45
- console.log("EERRS",propName, errors)
46
- if(errors.length > 0) {
47
- console.log("ERRS", propName)
48
- propErrors[propName] = errors[0]
49
- }
50
- }
51
- console.log("PROP ERRORS", propErrors)
52
- if(Object.keys(propErrors).length > 0) throw { properties: propErrors }
53
- }
1
+ const { getValidators, validate } = require('../utils/validation.js')
54
2
 
55
3
  module.exports = function(service, app) {
56
4
  for(let actionName in service.actions) {
57
5
  const action = service.actions[actionName]
6
+ if(action.skipValidation) continue
58
7
  const validators = getValidators(action, service, action)
59
8
  if(Object.keys(validators).length > 0) {
60
9
  const oldExec = action.execute
@@ -68,6 +17,7 @@ module.exports = function(service, app) {
68
17
  }
69
18
  for(let viewName in service.views) {
70
19
  const view = service.views[viewName]
20
+ if(view.skipValidation) continue
71
21
  const validators = getValidators(view, service, view)
72
22
  if(Object.keys(validators).length > 0) {
73
23
  if (view.read && !view.fetch) {
@@ -42,7 +42,7 @@ class Action {
42
42
  parameters: await preFilterParameters(parameters, this.definition.properties)
43
43
  }
44
44
  if(parameters._commandId) command.id = parameters._commandId
45
- console.log("CALL COMMAND", JSON.stringify(command, null, " "))
45
+ //console.log("CALL COMMAND", JSON.stringify(command, null, " "))
46
46
  return this.service.app.command(command, this.definition.requestTimeout)
47
47
  }
48
48
  }
@@ -1,14 +1,16 @@
1
- const ReactiveDao = require("@live-change/dao")
1
+ const LcDao = require("@live-change/dao")
2
2
  const Dao = require("./Dao.js")
3
3
  const cookie = require('cookie')
4
4
 
5
5
  const { getIp } = require("./utils.js")
6
6
 
7
7
  class ApiServer {
8
- constructor(config) {
8
+ constructor(config, DaoConstructor = Dao) {
9
9
  this.config = config
10
- this.reactiveServer = new ReactiveDao.ReactiveServer(
11
- (credentials, connection) => {
10
+ this.DaoConstructor = DaoConstructor
11
+
12
+ this.reactiveServer = new LcDao.ReactiveServer(
13
+ (credentials, connection) => {
12
14
  const ip = getIp(connection)
13
15
  if(this.config.fastAuth) {
14
16
  if(typeof this.config.fastAuth == 'function') {
@@ -24,16 +26,28 @@ class ApiServer {
24
26
  }, config)
25
27
  }
26
28
 
27
- async daoFactory(credentialsp, ip) {
29
+ async daoFactory(credentialsp, ip) {
28
30
  let credentials = { ...credentialsp, ip, roles: [] }
29
- if(this.config.authentication) {
30
- let auth = Array.isArray(this.config.authentication)
31
- ? this.config.authentication : [this.config.authentication]
32
- for(let authenticator of auth) {
33
- if(authenticator) await authenticator(credentials, this.config)
31
+ if(this.config.authenticators) {
32
+ const auth = Array.isArray(this.config.authenticators)
33
+ ? 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
+ }
38
+ }
39
+ for(const service of this.config.services) {
40
+ console.log("SERIVCE AUTH", service.name, service.authenticators)
41
+ if(service.authenticators) {
42
+ for(const authenticator of service.authenticators) {
43
+ if(authenticator && authenticator.prepareCredentials)
44
+ await authenticator.prepareCredentials(credentials, this.config)
45
+ }
34
46
  }
35
47
  }
36
- return new Dao(this.config, { ...credentials })
48
+ const dao = new this.DaoConstructor(this.config, { ...credentials })
49
+ await dao.start()
50
+ return dao
37
51
  }
38
52
 
39
53
  handleConnection(connection) {
@@ -0,0 +1,103 @@
1
+ const LcDao = require("@live-change/dao")
2
+ const Dao = require("./Dao.js")
3
+
4
+ const { waitForSignal } = require('./utils.js')
5
+
6
+ class LiveDao extends LcDao.DaoProxy {
7
+ constructor(config, credentials) {
8
+ super(null)
9
+ this.config = config
10
+ this.initialCredentials = credentials
11
+
12
+ this.authenticators = []
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
+ }
29
+ }
30
+
31
+ this.currentDao = null
32
+ this.disposed = false
33
+ this.started = false
34
+ this.credentials = JSON.parse(JSON.stringify(this.initialCredentials))
35
+ }
36
+
37
+ async refreshCredentials() {
38
+ if(!this.started) return /// waiting for start
39
+ const newCredentials = this.computeCredentials()
40
+ if(JSON.stringify(newCredentials) != JSON.stringify(this.credentials)) {
41
+ this.credentials = newCredentials
42
+ this.buildDao()
43
+ }
44
+ }
45
+
46
+ computeCredentials() {
47
+ let credentials = JSON.parse(JSON.stringify(this.initialCredentials))
48
+ for(const credentialsObserver of this.credentialsObservations) {
49
+ credentials = {
50
+ ...credentials,
51
+ ...credentialsObservers.credentials,
52
+ roles: [...credentials.roles, ...credentialsObserver.credentials.roles]
53
+ }
54
+ }
55
+ return credentials
56
+ }
57
+
58
+ async start() {
59
+ this.credentialsObservations = this.authenticators.map(authenticator => {
60
+ const observable = authenticator.authenticatorObservable(this.initialCredentials)
61
+ const observer = {
62
+ set: (newCredentials) => {
63
+ state.credentials = newCredentials
64
+ this.refreshCredentials()
65
+ }
66
+ }
67
+ const promise = waitForSignal(observable)
68
+ const state = {
69
+ observable, observer, promise, credentials: {}
70
+ }
71
+ return state
72
+ })
73
+
74
+ await Promise.all(this.credentialsObservations.map(observation => observation.promise))
75
+
76
+ const newCredentials = this.computeCredentials()
77
+ if(JSON.stringify(newCredentials) != JSON.stringify(this.credentials)) {
78
+ this.credentials = newCredentials
79
+ }
80
+ this.buildDao()
81
+ this.started = true
82
+
83
+ if(!this.dao) throw new Error("dao not created?!")
84
+ }
85
+
86
+ buildDao() {
87
+ const oldDao = this.currentDao
88
+ this.currentDao = new Dao(this.config, { ...this.credentials })
89
+ this.setDao(this.currentDao)
90
+ if(oldDao) oldDao.dispose()
91
+ }
92
+ dispose() {
93
+ if(this.disposed) throw new Error("DAO dispose called twice!")
94
+ this.disposed = true
95
+ this.started = false
96
+ for(const observation of this.credentialsObservations) {
97
+ observation.observable.unobserver(observation.observer)
98
+ }
99
+ super.dispose()
100
+ }
101
+ }
102
+
103
+ module.exports = LiveDao
@@ -30,7 +30,7 @@ class ReaderModel {
30
30
  if(pathRange) {
31
31
  return this.indexRangePath(index, utils.prefixRange(pathRange, prefix, prefix))
32
32
  }
33
- return this.indexRangePath(index,{ gte: prefix+'_', lte: prefix+'_\xFF\xFF\xFF\xFF' })
33
+ return this.indexRangePath(index, { gte: prefix+':', lte: prefix+'_\xFF\xFF\xFF\xFF' })
34
34
  }
35
35
  return ['database', 'query', this.service.databaseName, `(${
36
36
  async (input, output, { tableName, indexName, range }) => {
@@ -86,7 +86,7 @@ class ReaderModel {
86
86
  if(pathRange) {
87
87
  return this.sortedIndexRangePath(index, utils.prefixRange(pathRange, prefix, prefix))
88
88
  }
89
- return this.sortedIndexRangePath(index,{ gte: str+'_', lte: str+'_\xFF\xFF\xFF\xFF' })
89
+ return this.sortedIndexRangePath(index, { gte: prefix+':', lte: prefix+'_\xFF\xFF\xFF\xFF' })
90
90
  }
91
91
  return ['database', 'query', this.service.databaseName, `(${
92
92
  async (input, output, { tableName, indexName, range }) => {
@@ -147,7 +147,7 @@ class ReaderModel {
147
147
  if(pathRange) {
148
148
  return this.indexObjectPath(index, utils.prefixRange(pathRange, prefix, prefix))
149
149
  }
150
- return this.indexObjectPath(index,{ gte: prefix+'_', lte: prefix+'_\xFF\xFF\xFF\xFF' })
150
+ return this.indexObjectPath(index,{ gte: prefix+':', lte: prefix+'_\xFF\xFF\xFF\xFF' })
151
151
  }
152
152
  return ['database', 'queryObject', this.service.databaseName, `(${
153
153
  async (input, output, { tableName, indexName, range }) => {
@@ -185,7 +185,7 @@ class ReaderModel {
185
185
  if(pathRange) {
186
186
  return this.indexRangeDelete(index, utils.prefixRange(pathRange, prefix, prefix))
187
187
  }
188
- return this.indexRangeDelete(index,{ gte: prefix+'_', lte: prefix+'_\xFF\xFF\xFF\xFF' })
188
+ return this.indexRangeDelete(index,{ gte: prefix+':', lte: prefix+'_\xFF\xFF\xFF\xFF' })
189
189
  }
190
190
  this.service.dao.request(['database', 'query'], this.service.databaseName, `${
191
191
  async (input, output, { tableName, indexName, range }) => {
@@ -203,7 +203,7 @@ class ReaderModel {
203
203
  if(pathRange) {
204
204
  return this.indexRangeUpdate(index, update, utils.prefixRange(pathRange, prefix, prefix))
205
205
  }
206
- return this.indexRangeUpdate(index, update,{ gte: prefix+'_', lte: prefix+'_\xFF\xFF\xFF\xFF' })
206
+ return this.indexRangeUpdate(index, update,{ gte: prefix+':', lte: prefix+'_\xFF\xFF\xFF\xFF' })
207
207
  }
208
208
  const operations = Array.isArray(update) ? update : [{ op:'merge', property: null, value: update }]
209
209
  this.service.dao.request(['database', 'query'], this.service.databaseName, `(${
@@ -48,7 +48,7 @@ class CommandQueue {
48
48
  })
49
49
  }
50
50
  async handleCommand(command) {
51
- console.log("COMMNAD HANDLE!", command)
51
+ //console.log("COMMNAD HANDLE!", command)
52
52
  if(command.state != 'new') return
53
53
  if(this.commandsStarted.has(command.id)) return
54
54
  this.commandsStarted.set(command.id, command)
@@ -0,0 +1,55 @@
1
+ function getValidator(validation, context) {
2
+ if(typeof validation == 'string') {
3
+ let validator = context.service.validators[validation]
4
+ if(!validator) throw new Error(`Validator ${validation} not found`)
5
+ return validator({}, context)
6
+ } else {
7
+ let validator = context.service.validators[validation.name]
8
+ if(!validator) throw new Error(`Validator ${validation.name} not found`)
9
+ return validator(validation, context)
10
+ }
11
+ }
12
+
13
+ function getValidators(source, service) {
14
+ let validators = {}
15
+ const context = { source, service, getValidator: validation => getValidator(validation, context) }
16
+ for(let propName in source.properties) {
17
+ const prop = source.properties[propName]
18
+ if(prop.validation) {
19
+ const validations = Array.isArray(prop.validation) ? prop.validation : [prop.validation]
20
+ for(let validation of validations) {
21
+ const validator = getValidator(validation, context)
22
+ if(validators[propName]) validators[propName].push(validator)
23
+ else validators[propName] = [validator]
24
+ }
25
+ }
26
+ }
27
+ return validators
28
+ }
29
+
30
+ async function validate(props, validators, context) {
31
+ //console.log("VALIDATE PROPS", props, "WITH", validators)
32
+ let propPromises = {}
33
+ for(let propName in validators) {
34
+ let propValidators = validators[propName]
35
+ let promises = []
36
+ for(let validator of propValidators) {
37
+ //console.log("PROPS",props, propName)
38
+ promises.push(validator(props[propName], { ...context, props, propName }))
39
+ }
40
+ propPromises[propName] = Promise.all(promises)
41
+ }
42
+ let propErrors = {}
43
+ for(let propName in validators) {
44
+ let errors = (await propPromises[propName]).filter(e=>!!e)
45
+ console.log("EERRS",propName, errors)
46
+ if(errors.length > 0) {
47
+ console.log("ERRS", propName)
48
+ propErrors[propName] = errors[0]
49
+ }
50
+ }
51
+ console.log("PROP ERRORS", propErrors)
52
+ if(Object.keys(propErrors).length > 0) throw { properties: propErrors }
53
+ }
54
+
55
+ module.exports = { getValidator, getValidators, validate }
package/lib/utils.js CHANGED
@@ -204,7 +204,7 @@ function prefixRange({ gt, lt, gte, lte, limit, reverse }, prefix, from = "") {
204
204
  gte: (typeof gte == 'string') ? getPrefix(gte) : (typeof gt == 'string' ? undefined : `${prefix}:`),
205
205
  lte: (typeof lte == 'string')
206
206
  ? getPrefix(lte)+"\xFF\xFF\xFF\xFF"
207
- : (typeof lt == 'string' ? undefined : `${prefix}:\xFF\xFF\xFF\xFF`),
207
+ : (typeof lt == 'string' ? undefined : `${prefix}_\xFF\xFF\xFF\xFF`),
208
208
  limit,
209
209
  reverse
210
210
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@live-change/framework",
3
- "version": "0.5.5",
3
+ "version": "0.5.9",
4
4
  "description": "Live Change Framework - ultimate solution for real time mobile/web apps",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,54 +0,0 @@
1
- const LcDao = require("@live-change/dao")
2
- const SessionDao = require("./SessionDao.js")
3
- const cookie = require('cookie')
4
-
5
- const { getIp } = require("./utils.js")
6
-
7
- class SessionApiServer {
8
- constructor(config) {
9
- this.config = config
10
- this.reactiveServer = new LcDao.ReactiveServer(
11
- (credentials, connection) => {
12
- const ip = getIp(connection)
13
- if(this.config.fastAuth) {
14
- if(typeof this.config.fastAuth == 'function') {
15
- credentials = this.config.fastAuth(connection)
16
- } else {
17
- const cookies = cookie.parse(connection.headers.cookie || '')
18
- const sessionKey = cookies.sessionKey
19
- if(!sessionKey) throw new Error('session key undefined')
20
- credentials = { sessionKey }
21
- }
22
- }
23
- return this.daoFactory(credentials, ip)
24
- }, config)
25
- }
26
-
27
- async daoFactory(credentialsp, ip) {
28
- let credentials = { ...credentialsp, ip, roles: [] }
29
- if(this.config.authenticators) {
30
- const auth = Array.isArray(this.config.authenticators)
31
- ? this.config.authenticators : [this.config.authenticators]
32
- for(const authenticator of auth) {
33
- if(authenticator) await authenticator(credentials, this.config)
34
- }
35
- }
36
- for(const service of this.config.services) {
37
- console.log("SERIVCE AUTH", service.name, service.authenticators)
38
- if(service.authenticators) {
39
- for(const authenticator of service.authenticators) {
40
- if(authenticator) await authenticator(credentials, this.config)
41
- }
42
- }
43
- }
44
- const dao = new SessionDao(this.config, { ...credentials })
45
- await dao.start()
46
- return dao
47
- }
48
-
49
- handleConnection(connection) {
50
- this.reactiveServer.handleConnection(connection)
51
- }
52
- }
53
-
54
- module.exports = SessionApiServer