@live-change/framework 0.7.18 → 0.7.19

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
@@ -3,10 +3,10 @@ const App = require('./lib/App.js')
3
3
  module.exports = App
4
4
 
5
5
  module.exports.app = () => {
6
- if(!global.liveChangeFrameworkApp) {
7
- global.liveChangeFrameworkApp = new App()
6
+ if(!globalThis.liveChangeFrameworkApp) {
7
+ globalThis.liveChangeFrameworkApp = new App()
8
8
  }
9
- return global.liveChangeFrameworkApp
9
+ return globalThis.liveChangeFrameworkApp
10
10
  }
11
11
 
12
12
  module.exports.utils = require('./lib/utils.js')
package/lib/App.js CHANGED
@@ -33,6 +33,8 @@ const triggerExecutor = require("./processes/triggerExecutor.js")
33
33
  const eventListener = require('./processes/eventListener.js')
34
34
 
35
35
  const utils = require('./utils.js')
36
+ const SplitEmitQueue = require("./utils/SplitEmitQueue.js");
37
+ const SingleEmitQueue = require("./utils/SingleEmitQueue.js");
36
38
 
37
39
  const debug = require('debug')('framework')
38
40
 
@@ -41,6 +43,9 @@ class App {
41
43
  constructor(config = {}) {
42
44
  this.config = config
43
45
  this.splitEvents = false
46
+ this.shortEvents = false
47
+ this.shortCommands = false
48
+ this.shortTriggers = false
44
49
 
45
50
  this.requestTimeout = config?.db?.requestTimeout || 10*1000
46
51
 
@@ -78,6 +83,9 @@ class App {
78
83
  this.uidGenerator = uidGenerator(this.instanceId, this.config.uidBorders)
79
84
 
80
85
  this.activeTimeouts = new Set()
86
+
87
+ this.startedServices = {}
88
+ this.triggerRoutes = {}
81
89
  }
82
90
 
83
91
  createServiceDefinition( definition ) {
@@ -142,6 +150,9 @@ class App {
142
150
  oldServiceJson = this.createServiceDefinition({name: service.name}).toJSON()
143
151
  }
144
152
  let changes = this.computeChanges(oldServiceJson, service)
153
+ //console.log("OLD SERVICE", JSON.stringify(oldServiceJson, null, ' '))
154
+ //console.log("NEW SERVICE", JSON.stringify(service.toJSON(), null, ' '))
155
+ //console.log("CHANGES", JSON.stringify(changes, null, ' '))
145
156
 
146
157
  /// TODO: chceck for overwriting renames, solve by addeding temporary names
147
158
 
@@ -164,6 +175,7 @@ class App {
164
175
  await service.start(config)
165
176
  console.log("service started", serviceDefinition.name, "!")
166
177
  await this.profileLog.end(profileOp)
178
+ this.startedServices[serviceDefinition.name] = service
167
179
  return service
168
180
  }
169
181
 
@@ -204,6 +216,10 @@ class App {
204
216
  }
205
217
 
206
218
  async trigger(data) {
219
+ if(this.shortTriggers) {
220
+ const triggers = this.triggerRoutes[data.route]
221
+ return await Promise.all(triggers.map(t => t.execute(data)))
222
+ }
207
223
  const profileOp = await this.profileLog.begin({
208
224
  operation: "callTrigger", triggerType: data.type, id: data.id, by: data.by
209
225
  })
@@ -222,7 +238,14 @@ class App {
222
238
  }
223
239
 
224
240
  async triggerService(service, data, returnArray = false) {
225
-
241
+ if(this.shortTriggers) {
242
+ const service = this.startedServices[service]
243
+ const triggers = service.triggers[data.type]
244
+ if(!triggers) return []
245
+ const result = await Promise.all(triggers.map(t => t.execute(data)))
246
+ if(!returnArray && Array.isArray(result) && result.length == 1) return result[0]
247
+ return result
248
+ }
226
249
  if(!data.id) data.id = this.generateUid()
227
250
  data.service = service
228
251
  data.state = 'new'
@@ -266,45 +289,111 @@ class App {
266
289
  if(!data.timestamp) data.timestamp = (new Date()).toISOString()
267
290
  data.state = 'new'
268
291
 
269
- const profileOp = await this.profileLog.begin({
270
- operation: "callCommand", commandType: data.type, service: data.service,
271
- commandId: data.id, by: data.by, client: data.client
272
- })
292
+ if(this.shortCommands) {
293
+ const command = data
294
+ const service = this.startedServices[data.service]
295
+ const action = service.actions[data.type]
296
+ const reportFinished = action.definition.waitForEvents ? 'command_' + command.id : undefined
297
+ const flags = {commandId: command.id, reportFinished}
298
+ const emit = (!this.splitEvents || this.shortEvents)
299
+ ? new SingleEmitQueue(service, flags)
300
+ : new SplitEmitQueue(service, flags)
301
+ const queuedBy = action.definition.queuedBy
302
+ if(queuedBy) {
303
+ const profileOp = await service.profileLog.begin({
304
+ operation: 'queueCommand', commandType: actionName,
305
+ commandId: command.id, client: command.client
306
+ })
307
+ const keyFunction = typeof queuedBy == 'function' ? queuedBy : (
308
+ Array.isArray(queuedBy) ? (c) => JSON.stringify(queuedBy.map(k => c[k])) :
309
+ (c) => JSON.stringify(c[queuedBy]))
310
+
311
+ const routine = () => service.profileLog.profile({
312
+ operation: 'runCommand', commandType: actionName,
313
+ commandId: command.id, client: command.client
314
+ }, async () => {
315
+ const result = await service.app.assertTime('command ' + action.definition.name,
316
+ action.definition.timeout || 10000,
317
+ () => action.runCommand(command, (...args) => emit.emit(...args)), command)
318
+ if(this.shortEvents) {
319
+ const bucket = {}
320
+ const eventsPromise = Promise.all(emit.emittedEvents.map(event => {
321
+ const service = this.startedServices[event.service]
322
+ const handler = service.events[event.type]
323
+ service.exentQueue.queue(() => handler.execute(event, bucket))
324
+ }))
325
+ if (action.definition.waitForEvents) await eventsPromise
326
+ } else {
327
+ const events = await emit.commit()
328
+ if (action.definition.waitForEvents)
329
+ await service.app.waitForEvents(reportFinished, events, action.definition.waitForEvents)
330
+ }
331
+ return result
332
+ })
333
+ routine.key = keyFunction(command)
334
+ const promise = service.keyBasedExecutionQueues.queue(routine)
335
+ await service.profileLog.endPromise(profileOp, promise)
336
+ return promise
337
+ } else {
338
+ const result = await service.app.assertTime('command ' + action.definition.name,
339
+ action.definition.timeout || 10000,
340
+ () => action.runCommand(command, (...args) => emit.emit(...args)), command)
341
+ if(this.shortEvents) {
342
+ const bucket = {}
343
+ console.log("emit", emit)
344
+ const eventsPromise = Promise.all(emit.emittedEvents.map(event => {
345
+ const service = this.startedServices[event.service]
346
+ const handler = service.events[event.type]
347
+ service.exentQueue.queue(() => handler.execute(event, bucket))
348
+ }))
349
+ if (action.definition.waitForEvents) await eventsPromise
350
+ } else {
351
+ const events = await emit.commit()
352
+ if (action.definition.waitForEvents)
353
+ await service.app.waitForEvents(reportFinished, events, action.definition.waitForEvents)
354
+ }
355
+ return result
356
+ }
357
+ } else { // queue and observe command execution
358
+ const profileOp = await this.profileLog.begin({
359
+ operation: "callCommand", commandType: data.type, service: data.service,
360
+ commandId: data.id, by: data.by, client: data.client
361
+ })
273
362
 
274
- const commandsTable = this.splitCommands ? `${data.service}_commands` : 'commands'
275
- const objectObservable = this.dao.observable(
363
+ const commandsTable = this.splitCommands ? `${data.service}_commands` : 'commands'
364
+ const objectObservable = this.dao.observable(
276
365
  ['database', 'tableObject', this.databaseName, commandsTable, data.id],
277
366
  ReactiveDao.ObservableValue
278
- )
279
- await this.dao.request(['database', 'update', this.databaseName, commandsTable, data.id, [
280
- { op: 'reverseMerge', value: data }
281
- ]])
282
- let observer
283
- const promise = new Promise((resolve, reject) => {
284
- observer = (signal, value) => {
285
- if(signal != 'set') return reject('unknownSignal')
286
- if(!value) return
287
- if(value.state == 'done') return resolve(value.result)
288
- if(value.state == 'failed') return reject(value.error)
289
- }
290
- objectObservable.observe(observer)
291
- if(!requestTimeout) {
292
- requestTimeout = this.requestTimeout
293
- }
294
- if(requestTimeout) {
295
- const timeout = setTimeout(() => {
296
- this.activeTimeouts.delete(timeout)
297
- reject('timeout')
298
- }, requestTimeout)
299
- this.activeTimeouts.add(timeout)
300
- }
301
- }).finally(() => {
302
- objectObservable.unobserve(observer)
303
- })
304
-
305
- await this.profileLog.endPromise(profileOp, promise)
367
+ )
368
+ await this.dao.request(['database', 'update', this.databaseName, commandsTable, data.id, [
369
+ {op: 'reverseMerge', value: data}
370
+ ]])
371
+ let observer
372
+ const promise = new Promise((resolve, reject) => {
373
+ observer = (signal, value) => {
374
+ if (signal != 'set') return reject('unknownSignal')
375
+ if (!value) return
376
+ if (value.state == 'done') return resolve(value.result)
377
+ if (value.state == 'failed') return reject(value.error)
378
+ }
379
+ objectObservable.observe(observer)
380
+ if (!requestTimeout) {
381
+ requestTimeout = this.requestTimeout
382
+ }
383
+ if (requestTimeout) {
384
+ const timeout = setTimeout(() => {
385
+ this.activeTimeouts.delete(timeout)
386
+ reject('timeout')
387
+ }, requestTimeout)
388
+ this.activeTimeouts.add(timeout)
389
+ }
390
+ }).finally(() => {
391
+ objectObservable.unobserve(observer)
392
+ })
306
393
 
307
- return promise
394
+ await this.profileLog.endPromise(profileOp, promise)
395
+ return promise
396
+ }
308
397
  }
309
398
 
310
399
  async emitEvents(service, events, flags = {}) {
@@ -28,6 +28,12 @@ class IndexDefinition {
28
28
  if(oldIndex.search && this.search && JSON.stringify(oldIndex.search) != JSON.stringify(this.search))
29
29
  changes.push({ operation: "indexSearchUpdated", index: this.name })
30
30
 
31
+ const oldStorage = oldIndex.storage || {}
32
+ const newStorage = this.storage || {}
33
+ if(JSON.stringify(oldStorage) != JSON.stringify(newStorage)) {
34
+ changes.push({ operation: "indexStorageChanged", index: this.name, oldStorage, storage: newStorage })
35
+ }
36
+
31
37
  return changes
32
38
  }
33
39
 
@@ -58,12 +58,21 @@ class ModelDefinition {
58
58
  changes.push(...utils.crudChanges(oldModel.properties || {}, json.properties || {},
59
59
  "Property", "property", { model: this.name }))
60
60
  changes.push(...utils.crudChanges(oldModel.indexes || {}, json.indexes || {},
61
- "Index", "index", { model: this.name }))
61
+ "Index", "index", { model: this.name, storage: this.storage }))
62
62
  if(oldModel.search && !this.search) changes.push({ operation: "searchDisabled", model: this.name })
63
63
  if(!oldModel.search && this.search) changes.push({ operation: "searchEnabled", model: this.name })
64
64
  if(oldModel.search && this.search && JSON.stringify(oldModel.search) != JSON.stringify(this.search))
65
65
  changes.push({ operation: "searchUpdated", model: this.name })
66
66
 
67
+ const oldStorage = oldModel.storage || {}
68
+ const newStorage = this.storage || {}
69
+ if(JSON.stringify(oldStorage) != JSON.stringify(newStorage)) {
70
+ changes.push({ operation: "storageChanged", model: this.name, oldStorage, storage: newStorage })
71
+ for(let indexName in this.indexes) {
72
+ changes.push({ operation: "indexStorageChanged", index: indexName, oldStorage, storage: newStorage })
73
+ }
74
+ }
75
+
67
76
  return changes
68
77
  }
69
78
 
@@ -7,9 +7,11 @@ async function startCommandExecutor(service, config) {
7
7
  if(!config.runCommands) return
8
8
 
9
9
  service.keyBasedExecutionQueues = service.keyBasedExecutionQueues || new KeyBasedExecutionQueues(r => r.key)
10
-
10
+
11
+ if(service.app.shortCommands) return
12
+
11
13
  service.commandQueue = new CommandQueue(service.dao, service.databaseName,
12
- service.app.splitCommands ? `${service.name}_commands` : 'commands', service.name)
14
+ service.app.splitCommands ? `${service.name}_commands` : 'commands', service.name, config.runCommands)
13
15
  for (let actionName in service.actions) {
14
16
  const action = service.actions[actionName]
15
17
  if (action.definition.queuedBy) {
@@ -24,9 +26,9 @@ async function startCommandExecutor(service, config) {
24
26
  })
25
27
  const reportFinished = action.definition.waitForEvents ? 'command_' + command.id : undefined
26
28
  const flags = {commandId: command.id, reportFinished}
27
- const emit = service.app.splitEvents
28
- ? new SplitEmitQueue(service, flags)
29
- : new SingleEmitQueue(service, flags)
29
+ const emit = (!service.app.splitEvents || this.shortEvents)
30
+ ? new SingleEmitQueue(service, flags)
31
+ : new SplitEmitQueue(service, flags)
30
32
  const routine = () => service.profileLog.profile({
31
33
  operation: 'runCommand', commandType: actionName,
32
34
  commandId: command.id, client: command.client
@@ -34,9 +36,19 @@ async function startCommandExecutor(service, config) {
34
36
  const result = await service.app.assertTime('command ' + action.definition.name,
35
37
  action.definition.timeout || 10000,
36
38
  () => action.runCommand(command, (...args) => emit.emit(...args)), command)
37
- const events = await emit.commit()
38
- if (action.definition.waitForEvents)
39
- await service.app.waitForEvents(reportFinished, events, action.definition.waitForEvents)
39
+ if(service.app.shortEvents) {
40
+ const bucket = {}
41
+ const eventsPromise = Promise.all(emit.emittedEvents.map(event => {
42
+ const handlerService = service.app.startedServices[event.service]
43
+ const handler = handlerService.events[event.type]
44
+ handlerService.exentQueue.queue(() => handler.execute(event, bucket))
45
+ }))
46
+ if (action.definition.waitForEvents) await eventsPromise
47
+ } else {
48
+ const events = await emit.commit()
49
+ if (action.definition.waitForEvents)
50
+ await service.app.waitForEvents(reportFinished, events, action.definition.waitForEvents)
51
+ }
40
52
  return result
41
53
  })
42
54
  routine.key = keyFunction(command)
@@ -52,15 +64,25 @@ async function startCommandExecutor(service, config) {
52
64
  }, async () => {
53
65
  const reportFinished = action.definition.waitForEvents ? 'command_' + command.id : undefined
54
66
  const flags = {commandId: command.id, reportFinished}
55
- const emit = service.app.splitEvents
56
- ? new SplitEmitQueue(service, flags)
57
- : new SingleEmitQueue(service, flags)
67
+ const emit = (!service.app.splitEvents || this.shortEvents)
68
+ ? new SingleEmitQueue(service, flags)
69
+ : new SplitEmitQueue(service, flags)
58
70
  const result = await service.app.assertTime('command ' + action.definition.name,
59
71
  action.definition.timeout || 10000,
60
72
  () => action.runCommand(command, (...args) => emit.emit(...args)), command)
61
- const events = await emit.commit()
62
- if (action.definition.waitForEvents)
63
- await service.app.waitForEvents(reportFinished, events, action.definition.waitForEvents)
73
+ if(service.app.shortEvents) {
74
+ const bucket = {}
75
+ const eventsPromise = Promise.all(emit.emittedEvents.map(event => {
76
+ const handlerService = service.app.startedServices[event.service]
77
+ const handler = handlerService.events[event.type]
78
+ handlerService.exentQueue.queue(() => handler.execute(event, bucket))
79
+ }))
80
+ if (action.definition.waitForEvents) await eventsPromise
81
+ } else {
82
+ const events = await emit.commit()
83
+ if (action.definition.waitForEvents)
84
+ await service.app.waitForEvents(reportFinished, events, action.definition.waitForEvents)
85
+ }
64
86
  return result
65
87
  })
66
88
  )
@@ -2,18 +2,17 @@ const EventSourcing = require('../utils/EventSourcing.js')
2
2
 
3
3
  async function startEventListener(service, config) {
4
4
  if(!config.handleEvents) return
5
-
5
+
6
6
  if(service.app.splitEvents) {
7
7
  service.eventSourcing = new EventSourcing(service.dao, service.databaseName,
8
8
  'events_'+service.name, service.name,
9
- { filter: (event) => event.service == service.name })
9
+ { filter: (event) => event.service == service.name }, config.handleEvents)
10
10
  } else {
11
11
  service.eventSourcing = new EventSourcing(service.dao, service.databaseName,
12
12
  'events', service.name,
13
- { filter: (event) => event.service == service.name })
13
+ { filter: (event) => event.service == service.name }, config.handleEvents)
14
14
  }
15
15
 
16
-
17
16
  for (let eventName in service.events) {
18
17
  const event = service.events[eventName]
19
18
  service.eventSourcing.addEventHandler(eventName, async (ev, bucket) => {
@@ -37,4 +36,4 @@ async function startEventListener(service, config) {
37
36
  service.eventSourcing.start()
38
37
  }
39
38
 
40
- module.exports = startEventListener
39
+ module.exports = startEventListener
@@ -8,10 +8,20 @@ async function startTriggerExecutor(service, config) {
8
8
 
9
9
  service.keyBasedExecutionQueues = service.keyBasedExecutionQueues || new KeyBasedExecutionQueues(r => r.key)
10
10
 
11
+ if(service.app.shortTriggers) {
12
+ for (let triggerName in service.triggers) {
13
+ service.app.triggerRoutes[triggerName] = service.app.triggerRoutes[triggerName] || []
14
+ for(let trigger of service.triggers[triggerName]) {
15
+ service.app.triggerRoutes[triggerName].push({ service, trigger })
16
+ }
17
+ }
18
+ return
19
+ }
20
+
11
21
  await service.dao.request(['database', 'createTable'], service.databaseName, 'triggerRoutes').catch(e => 'ok')
12
22
 
13
23
  service.triggerQueue = new CommandQueue(service.dao, service.databaseName,
14
- service.app.splitTriggers ? `${service.name}_triggers` : 'triggers', service.name )
24
+ service.app.splitTriggers ? `${service.name}_triggers` : 'triggers', service.name, config.runCommands)
15
25
  for (let triggerName in service.triggers) {
16
26
  const triggers = service.triggers[triggerName]
17
27
  await service.dao.request(['database', 'put'], service.databaseName, 'triggerRoutes',
@@ -35,7 +35,7 @@ class Action {
35
35
  }
36
36
 
37
37
  async callCommand(parameters, clientData) {
38
- if(!clientData.roles) throw new Error("no roles")
38
+ // if(!clientData.roles) throw new Error("no roles") - roles are not required in frontend
39
39
  const command = {
40
40
  type: this.definition.name,
41
41
  service: this.service.name,
@@ -48,4 +48,4 @@ class Action {
48
48
  }
49
49
  }
50
50
 
51
- module.exports = Action
51
+ module.exports = Action
@@ -8,7 +8,7 @@ function promiseMap(promise, fn) {
8
8
  }
9
9
 
10
10
  function prepareReactiveDaoDefinition(config, clientData) {
11
- if(!clientData.roles) throw new Error("no roles")
11
+ //if(!clientData.roles) throw new Error("no roles") - roles not required for local dao
12
12
  let dao = {}
13
13
  let staticDefinitions = []
14
14
  if(config.remote) {
@@ -43,7 +43,7 @@ function prepareReactiveDaoDefinition(config, clientData) {
43
43
  let methods = {}, values = {}
44
44
  for(let actionName in service.actions) {
45
45
  let action = service.actions[actionName]
46
- if(!clientData.roles) throw new Error("no roles")
46
+ //if(!clientData.roles) throw new Error("no roles") - roles not required for local dao
47
47
  methods[actionName] = (params) => action.callCommand(params, clientData)
48
48
  }
49
49
  for(let viewName in service.views) {
@@ -172,13 +172,13 @@ function prepareReactiveDaoDefinition(config, clientData) {
172
172
  return dao
173
173
  }
174
174
 
175
- class RTCMSDao extends ReactiveDao {
175
+ class Dao extends ReactiveDao {
176
176
  constructor(config, clientData) {
177
- console.log("CD", clientData)
177
+ //console.log("CD", clientData)
178
178
  super(clientData, prepareReactiveDaoDefinition(config, clientData))
179
179
  //console.log("Created dao with clientData",clientData)
180
- if( !clientData.roles ) throw new Error("NO ROLES!!")
180
+ // if( !clientData.roles ) throw new Error("NO ROLES!!") - roles not required for local dao
181
181
  }
182
182
  }
183
183
 
184
- module.exports = RTCMSDao
184
+ module.exports = Dao
@@ -1,4 +1,5 @@
1
1
  const ReaderModel = require("./ReaderModel.js")
2
+ const utils = require("../utils.js");
2
3
 
3
4
  class Model extends ReaderModel {
4
5
 
@@ -38,6 +39,64 @@ class Model extends ReaderModel {
38
39
  return res
39
40
  }
40
41
 
42
+ rangeDelete(range = {}, pathRange = null) {
43
+ if(typeof range != 'object' || Array.isArray(range)) {
44
+ const values = Array.isArray(range) ? range : [range]
45
+ const prefix = values.map(value => value === undefined ? '' : JSON.stringify(value)).join(':')
46
+ if(pathRange) {
47
+ return this.rangePath(utils.prefixRange(pathRange, prefix, prefix))
48
+ }
49
+ return this.rangePath({ gte: prefix+':', lte: prefix+'_\xFF\xFF\xFF\xFF' })
50
+ }
51
+ if(Array.isArray(range)) this.rangePath(range.join(','))
52
+ this.service.dao.request(['database', 'query'], this.service.databaseName, `(${
53
+ async (input, output, { tableName, range }) => {
54
+ await (await input.table(tableName)).range(range).onChange(async (obj, oldObj) => {
55
+ output.table(tableName).delete(obj.id)
56
+ })
57
+ }
58
+ })`, { tableName: this.tableName, range })
59
+ }
60
+
61
+ indexRangeDelete(index, range = {}, pathRange = null) {
62
+ if(typeof range != 'object' || Array.isArray(range)) {
63
+ const values = Array.isArray(range) ? range : [range]
64
+ const prefix = values.map(value => value === undefined ? '' : JSON.stringify(value)).join(':')
65
+ if(pathRange) {
66
+ return this.indexRangeDelete(index, utils.prefixRange(pathRange, prefix, prefix))
67
+ }
68
+ return this.indexRangeDelete(index,{ gte: prefix+':', lte: prefix+'_\xFF\xFF\xFF\xFF' })
69
+ }
70
+ this.service.dao.request(['database', 'query'], this.service.databaseName, `${
71
+ async (input, output, { tableName, indexName, range }) => {
72
+ await (await input.index(indexName)).range(range).onChange(async (ind, oldInd) => {
73
+ output.table(tableName).delete(ind.to)
74
+ })
75
+ }
76
+ })`, { indexName: this.tableName+'_'+index, tableName: this.tableName, range })
77
+ }
78
+
79
+ indexRangeUpdate(index, update, range = {}, pathRange = null) {
80
+ if(typeof range != 'object' || Array.isArray(range)) {
81
+ const values = Array.isArray(range) ? range : [range]
82
+ const prefix = values.map(value => value === undefined ? '' : JSON.stringify(value)).join(':')
83
+ if(pathRange) {
84
+ return this.indexRangeUpdate(index, update, utils.prefixRange(pathRange, prefix, prefix))
85
+ }
86
+ return this.indexRangeUpdate(index, update,{ gte: prefix+':', lte: prefix+'_\xFF\xFF\xFF\xFF' })
87
+ }
88
+ const operations = Array.isArray(update) ? update : [{ op:'merge', property: null, value: update }]
89
+ this.service.dao.request(['database', 'query'], this.service.databaseName, `(${
90
+ async (input, output, { tableName, indexName, range, operations }) => {
91
+ await (await input.index(indexName)).range(range).onChange(async (ind, oldInd) => {
92
+ output.table(tableName).update(ind.to, operations)
93
+ })
94
+ }
95
+ })`, { indexName: this.tableName+'_'+index, tableName: this.tableName, range, operations })
96
+ }
97
+
98
+
99
+
41
100
  }
42
101
 
43
102
  module.exports = Model
@@ -211,41 +211,17 @@ class ReaderModel {
211
211
  })`, { indexName: this.tableName+'_'+index, tableName: this.tableName, range }]
212
212
  }
213
213
 
214
- indexRangeDelete(index, range = {}, pathRange = null) {
214
+ countPath(range = {}, pathRange = null) {
215
215
  if(typeof range != 'object' || Array.isArray(range)) {
216
216
  const values = Array.isArray(range) ? range : [range]
217
217
  const prefix = values.map(value => value === undefined ? '' : JSON.stringify(value)).join(':')
218
218
  if(pathRange) {
219
- return this.indexRangeDelete(index, utils.prefixRange(pathRange, prefix, prefix))
219
+ return this.countPath(utils.prefixRange(pathRange, prefix, prefix))
220
220
  }
221
- return this.indexRangeDelete(index,{ gte: prefix+':', lte: prefix+'_\xFF\xFF\xFF\xFF' })
221
+ return this.countPath({ gte: prefix+':', lte: prefix+'_\xFF\xFF\xFF\xFF' })
222
222
  }
223
- this.service.dao.request(['database', 'query'], this.service.databaseName, `${
224
- async (input, output, { tableName, indexName, range }) => {
225
- await (await input.index(indexName)).range(range).onChange(async (ind, oldInd) => {
226
- output.table(tableName).delete(ind.to)
227
- })
228
- }
229
- })`, { indexName: this.tableName+'_'+index, tableName: this.tableName, range })
230
- }
231
-
232
- indexRangeUpdate(index, update, range = {}, pathRange = null) {
233
- if(typeof range != 'object' || Array.isArray(range)) {
234
- const values = Array.isArray(range) ? range : [range]
235
- const prefix = values.map(value => value === undefined ? '' : JSON.stringify(value)).join(':')
236
- if(pathRange) {
237
- return this.indexRangeUpdate(index, update, utils.prefixRange(pathRange, prefix, prefix))
238
- }
239
- return this.indexRangeUpdate(index, update,{ gte: prefix+':', lte: prefix+'_\xFF\xFF\xFF\xFF' })
240
- }
241
- const operations = Array.isArray(update) ? update : [{ op:'merge', property: null, value: update }]
242
- this.service.dao.request(['database', 'query'], this.service.databaseName, `(${
243
- async (input, output, { tableName, indexName, range, operations }) => {
244
- await (await input.index(indexName)).range(range).onChange(async (ind, oldInd) => {
245
- output.table(tableName).update(ind.to, operations)
246
- })
247
- }
248
- })`, { indexName: this.tableName+'_'+index, tableName: this.tableName, range, operations })
223
+ if(Array.isArray(range)) this.rangePath(range.join(','))
224
+ return ['database', 'tableCount', this.service.databaseName, this.tableName, range]
249
225
  }
250
226
 
251
227
  observable(id) {
@@ -278,6 +254,12 @@ class ReaderModel {
278
254
  async sortedIndexRangeGet(index, range, pathRange = null) {
279
255
  return this.service.dao.get(this.sortedIndexRangePath(index, range, pathRange), ReactiveDao.ObservableList)
280
256
  }
257
+ countObservable(range) {
258
+ return this.service.dao.observable(this.countPath(range), ReactiveDao.ObservableList)
259
+ }
260
+ async countGet(range) {
261
+ return this.service.dao.get(this.countPath(range), ReactiveDao.ObservableList)
262
+ }
281
263
 
282
264
  condition(id, condition = x => !!x, timeout = 10000) {
283
265
  return new Promise((resolve, reject) => {
@@ -5,6 +5,7 @@ const View = require("./View.js")
5
5
  const Action = require("./Action.js")
6
6
  const EventHandler = require("./EventHandler.js")
7
7
  const TriggerHandler = require("./TriggerHandler.js")
8
+ const ExecutionQueue = require("../utils/ExecutionQueue.js")
8
9
 
9
10
  class Service {
10
11
 
@@ -13,6 +14,8 @@ class Service {
13
14
  this.app = app
14
15
  this.name = definition.name
15
16
 
17
+ if(app.shortEvents) this.eventQueue = new ExecutionQueue()
18
+
16
19
  this.profileLog = app.profileLog
17
20
 
18
21
  this.dao = definition.daoFactory ? definition.daoFactory(app) : app.dao
@@ -5,23 +5,30 @@ async function update(changes, service, app, force) {
5
5
  const dao = app.dao
6
6
  const database = app.databaseName
7
7
 
8
- dao.request(['database', 'createTable'], database, 'eventConsumers').catch(e => 'ok')
9
- dao.request(['database', 'createTable'], database, 'triggerRoutes').catch(e => 'ok')
10
- dao.request(['database', 'createTable'], database, 'eventReports').catch(e => 'ok')
11
- if(app.splitEvents) {
12
- dao.request(['database', 'createLog'], database, service.name+'_events').catch(e => 'ok')
13
- } else {
14
- dao.request(['database', 'createLog'], database, 'events').catch(e => 'ok')
8
+ if(!app.shortEvents) {
9
+ dao.request(['database', 'createTable'], database, 'eventConsumers').catch(e => 'ok')
10
+ dao.request(['database', 'createTable'], database, 'eventReports').catch(e => 'ok')
11
+ if (app.splitEvents) {
12
+ dao.request(['database', 'createLog'], database, service.name + '_events').catch(e => 'ok')
13
+ } else {
14
+ dao.request(['database', 'createLog'], database, 'events').catch(e => 'ok')
15
+ }
15
16
  }
16
- if(app.splitCommands) {
17
- dao.request(['database', 'createTable'], database, service.name+'_commands').catch(e => 'ok')
18
- } else {
19
- dao.request(['database', 'createTable'], database, 'commands').catch(e => 'ok')
17
+
18
+ if(!app.shortCommands) {
19
+ if (app.splitCommands) {
20
+ dao.request(['database', 'createTable'], database, service.name + '_commands').catch(e => 'ok')
21
+ } else {
22
+ dao.request(['database', 'createTable'], database, 'commands').catch(e => 'ok')
23
+ }
20
24
  }
21
- if(app.splitTriggers) {
22
- dao.request(['database', 'createTable'], database, service.name+'_triggers').catch(e => 'ok')
23
- } else {
24
- dao.request(['database', 'createTable'], database, 'triggers').catch(e => 'ok')
25
+ if(!app.shortTriggers) {
26
+ dao.request(['database', 'createTable'], database, 'triggerRoutes').catch(e => 'ok')
27
+ if (app.splitTriggers) {
28
+ dao.request(['database', 'createTable'], database, service.name + '_triggers').catch(e => 'ok')
29
+ } else {
30
+ dao.request(['database', 'createTable'], database, 'triggers').catch(e => 'ok')
31
+ }
25
32
  }
26
33
 
27
34
  const generateTableName = (modelName) => {
@@ -47,7 +54,7 @@ async function update(changes, service, app, force) {
47
54
 
48
55
  if(index.function) {
49
56
  await dao.request(['database', 'createIndex'], database, indexName,
50
- `(${index.function})`, { ...(index.parameters || {}) })
57
+ `(${index.function})`, { ...(index.parameters || {}) }, index.storage ?? {})
51
58
  } else {
52
59
  if(!table) throw new Error("only function indexes are possible without table")
53
60
  if(index.multi) {
@@ -78,7 +85,7 @@ async function update(changes, service, app, force) {
78
85
  }
79
86
  })
80
87
  }
81
- })`, { property: index.property.split('.'), table })
88
+ })`, { property: index.property.split('.'), table }, index.storage ?? {})
82
89
  } else {
83
90
  if(!table) throw new Error("only function indexes are possible without table")
84
91
  const properties = (Array.isArray(index.property) ? index.property : [index.property]).map(p => p.split('.'))
@@ -95,7 +102,7 @@ async function update(changes, service, app, force) {
95
102
  await input.table(table).onChange((obj, oldObj) =>
96
103
  output.change(obj && mapper(obj), oldObj && mapper(oldObj)) )
97
104
  }
98
- })`, { properties, table })
105
+ })`, { properties, table }, index.storage ?? {})
99
106
  }
100
107
  }
101
108
 
@@ -110,7 +117,7 @@ async function update(changes, service, app, force) {
110
117
  case "createModel": {
111
118
  const model = change.model
112
119
  const tableName = generateTableName(model.name)
113
- await dao.request(['database', 'createTable'], database, tableName)
120
+ await dao.request(['database', 'createTable'], database, tableName, model.storage ?? {})
114
121
  debug("TABLE CREATED!", tableName)
115
122
  for(let indexName in model.indexes) {
116
123
  const index = model.indexes[indexName]
@@ -154,7 +161,7 @@ async function update(changes, service, app, force) {
154
161
  case "createIndex": {
155
162
  const table = change.model ? generateTableName(change.model) : null
156
163
  const index = change.index
157
- await createIndex(table, change.name, index)
164
+ await createIndex(table, change.name, index, change.model)
158
165
  } break
159
166
  case "renameIndex": {
160
167
  const table = change.model ? generateTableName(change.model) : null
@@ -1,12 +1,13 @@
1
1
  const ReactiveDao = require('@live-change/dao')
2
2
 
3
3
  class CommandQueue {
4
- constructor(connection, database, tableName, serviceName) {
4
+ constructor(connection, database, tableName, serviceName, config) {
5
5
  this.connection = connection
6
6
  this.database = database
7
7
  this.tableName = tableName
8
8
  this.indexName = tableName + '_new'
9
9
  this.serviceName = serviceName
10
+ this.config = config || {}
10
11
  this.observable = null
11
12
  this.disposed = false
12
13
  this.resolveStart = null
@@ -22,8 +23,10 @@ class CommandQueue {
22
23
  }
23
24
  }
24
25
  async start() {
26
+ console.log("START COMMAND QUEUE", this.database, this.tableName, this.indexName, this.config)
25
27
  //console.log("START QUEUE", this.tableName, this.indexName)
26
- await this.connection.request(['database', 'createTable'], this.database, this.tableName).catch(e => 'ok')
28
+ await this.connection.request(['database', 'createTable'], this.database, this.tableName,
29
+ this.config.storage ?? {}).catch(e => 'ok')
27
30
  await this.connection.request(['database', 'createIndex'], this.database, this.indexName, `(${
28
31
  async function(input, output, { tableName }) {
29
32
  await input.table(tableName).onChange(async (obj, oldObj) => {
@@ -33,7 +36,7 @@ class CommandQueue {
33
36
  await output.change(res, oldRes)
34
37
  })
35
38
  }
36
- })`, { tableName: this.tableName }).catch(e => 'ok')
39
+ })`, { tableName: this.tableName }, this.config.storage ?? {}).catch(e => 'ok')
37
40
  this.observable = this.connection.observable(
38
41
  ['database', 'indexRange', this.database, this.indexName, {
39
42
  gt: this.serviceName+'_',
@@ -25,8 +25,10 @@ class EventSourcing {
25
25
  await this.saveState()
26
26
  }
27
27
  async start() {
28
- await this.connection.request(['database', 'createTable'], this.database, 'eventConsumers').catch(e => 'ok')
29
- await this.connection.request(['database', 'createLog'], this.database, this.logName).catch(e => 'ok')
28
+ await this.connection.request(['database', 'createTable'], this.database, 'eventConsumers', this.config.storage ?? {})
29
+ .catch(e => 'ok')
30
+ await this.connection.request(['database', 'createLog'], this.database, this.logName, this.config.storage ?? {})
31
+ .catch(e => 'ok')
30
32
  this.state = await this.connection.get(
31
33
  ['database', 'tableObject', this.database, 'eventConsumers', this.consumerId])
32
34
  //console.log("GOT CONSUMER STATE", this.state)
@@ -30,7 +30,7 @@ class ExecutionQueue {
30
30
  runNext() {
31
31
  if(this.nextRoutines.length == 0) {
32
32
  this.running = false
33
- setTimeout(() => { if(!this.running) this.queues.delete(this.key) }, 500)
33
+ setTimeout(() => { if(!this.running && this.queues) this.queues.delete(this.key) }, 500)
34
34
  return;
35
35
  }
36
36
  this.running = true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@live-change/framework",
3
- "version": "0.7.18",
3
+ "version": "0.7.19",
4
4
  "description": "Live Change Framework - ultimate solution for real time mobile/web apps",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -21,13 +21,13 @@
21
21
  },
22
22
  "homepage": "https://github.com/live-change/live-change-framework",
23
23
  "devDependencies": {
24
- "@live-change/dao": "^0.5.10",
25
- "@live-change/dao-websocket": "^0.5.10",
26
- "@live-change/db": "^0.5.25",
27
- "@live-change/db-store-level": "^0.5.25",
28
- "@live-change/db-store-lmdb": "^0.5.25",
29
- "@live-change/sockjs": "^0.4.1",
30
- "@live-change/uid": "^0.7.18",
24
+ "@live-change/dao": "0.5.15",
25
+ "@live-change/dao-websocket": "0.5.15",
26
+ "@live-change/db": "0.6.3",
27
+ "@live-change/db-store-level": "0.6.3",
28
+ "@live-change/db-store-lmdb": "0.6.3",
29
+ "@live-change/sockjs": "0.4.1",
30
+ "@live-change/uid": "0.7.18",
31
31
  "cookie": "^0.4.1",
32
32
  "express": "^4.18.1",
33
33
  "os-service": "^2.2.0",
@@ -35,5 +35,5 @@
35
35
  "tape": "^5.3.2",
36
36
  "websocket": "^1.0.34"
37
37
  },
38
- "gitHead": "5916995dc07ad8f25ae983d20e45dc4365300386"
38
+ "gitHead": "bc09aeeb64724fdfcf4e944d1858a2a00f08edcc"
39
39
  }