@live-change/framework 0.9.137 → 0.9.138

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.
@@ -0,0 +1,59 @@
1
+ import { AccessSpecification } from "../processors/accessMethod.js"
2
+ import PropertyDefinition, { PropertyDefinitionSpecification } from "./PropertyDefinition.js"
3
+ import type { ContextBase, QueryParameters } from "./types.js"
4
+
5
+ type QueryCode = string | ((input: any, output: any, parameters: QueryParameters) => Promise<any>)
6
+
7
+ export interface QueryDefinitionSpecification {
8
+ name: string
9
+ properties: Record<string, PropertyDefinitionSpecification>
10
+ returns?: PropertyDefinitionSpecification,
11
+ code: QueryCode,
12
+ sourceName: string,
13
+ update: boolean,
14
+
15
+ validation?: (parameters: QueryParameters, context: ContextBase) => Promise<any>
16
+ waitForEvents?: boolean,
17
+ timeout?: number,
18
+ requestTimeout?: number
19
+ }
20
+
21
+ class QueryDefinition<T extends QueryDefinitionSpecification> {
22
+ [key: string]: any
23
+
24
+ constructor(definition: T) {
25
+ this.properties = {}
26
+ // @ts-ignore
27
+ for(let key in definition) this[key] = definition[key]
28
+ if(definition.properties) {
29
+ for (let propName in definition.properties) {
30
+ const propDefn = definition.properties[propName]
31
+ this.createAndAddProperty(propName, propDefn)
32
+ }
33
+ }
34
+ if(definition.returns) {
35
+ this.returns = new PropertyDefinition(definition.returns)
36
+ }
37
+ }
38
+
39
+ createAndAddProperty(name, definition) {
40
+ const property = new PropertyDefinition(definition)
41
+ this.properties[name] = property
42
+ }
43
+
44
+ toJSON() {
45
+ let properties = {}
46
+ for(let propName in this.properties) {
47
+ properties[propName] = this.properties[propName].toJSON()
48
+ }
49
+ let returns = this.returns ? this.returns.toJSON() : null
50
+ return {
51
+ ... this,
52
+ properties,
53
+ returns
54
+ }
55
+ }
56
+
57
+ }
58
+
59
+ export default QueryDefinition
@@ -8,6 +8,7 @@ import ViewDefinition, { ViewDefinitionSpecification } from "./ViewDefinition.js
8
8
  import EventDefinition from "./EventDefinition.js"
9
9
  import defaultValidators from '../utils/validators.js'
10
10
  import { crudChanges, definitionToJSON } from "../utils.js"
11
+ import QueryDefinition, { QueryDefinitionSpecification } from "./QueryDefinition.js"
11
12
 
12
13
  function createModelProxy(definition, model) {
13
14
  return new Proxy(model, {
@@ -72,6 +73,21 @@ function createForeignIndexProxy(definition, model) {
72
73
  })
73
74
  }
74
75
 
76
+ function createQueryProxy(definition, query) {
77
+ return new Proxy(query, {
78
+ get(target, prop, receiver) {
79
+ const runtime = definition._runtime
80
+ if(runtime) {
81
+ const queryRuntime = runtime.queries[query.name]
82
+ if(queryRuntime[prop]) {
83
+ return Reflect.get(queryRuntime, prop, receiver)
84
+ }
85
+ }
86
+ return Reflect.get(target, prop, receiver)
87
+ }
88
+ })
89
+ }
90
+
75
91
 
76
92
 
77
93
  export interface ServiceDefinitionSpecification {
@@ -99,6 +115,7 @@ class ServiceDefinition<T extends ServiceDefinitionSpecification> {
99
115
  this.endpoints = []
100
116
  this.validators = { ...defaultValidators }
101
117
  this.clientSideFilters = []
118
+ this.queries = {}
102
119
  // @ts-ignore
103
120
  for(let key in definition) this[key] = definition[key]
104
121
  }
@@ -193,6 +210,13 @@ class ServiceDefinition<T extends ServiceDefinitionSpecification> {
193
210
  this.clientSideFilters.push(filter)
194
211
  }
195
212
 
213
+ query<T extends QueryDefinitionSpecification>(definition: T) {
214
+ if(this.queries[definition.name]) throw new Error('query ' + definition.name + ' already exists')
215
+ const query = new QueryDefinition<T>(definition)
216
+ this.queries[query.name] = query
217
+ return createQueryProxy(this, query)
218
+ }
219
+
196
220
  toJSON() {
197
221
  let models = {}
198
222
  for(let key in this.models) models[key] = this.models[key].toJSON()
@@ -235,9 +259,11 @@ class ServiceDefinition<T extends ServiceDefinitionSpecification> {
235
259
  let oldModule = JSON.parse(JSON.stringify(oldModuleParam))
236
260
  let changes: Record<string, any>[] = []
237
261
  changes.push(...crudChanges(oldModule.models || {}, this.models || {},
238
- "Model", "model", { }))
262
+ "Model", "model", { }))
239
263
  changes.push(...crudChanges(oldModule.indexes || {}, this.indexes || {},
240
264
  "Index", "index", { }))
265
+ changes.push(...crudChanges(oldModule.queries || {}, this.queries || {},
266
+ "Query", "query", { }))
241
267
  return changes
242
268
  }
243
269
  }
@@ -19,6 +19,7 @@ export interface ViewContext extends ContextBase {
19
19
  export type ActionParameters = Record<string, any>
20
20
  export type TriggerParameters = Record<string, any>
21
21
  export type EventParameters = Record<string, any>
22
+ export type QueryParameters = Record<string, any>
22
23
 
23
24
  export interface TriggerSettings {
24
25
  service?: string,
@@ -1,5 +1,6 @@
1
1
  import LcDao from "@live-change/dao"
2
2
  import Dao from "./Dao.js"
3
+ import { originalCredentialsSymbol } from "@live-change/dao"
3
4
 
4
5
  import { waitForSignal } from './utils.js'
5
6
 
@@ -31,12 +32,14 @@ class LiveDao extends LcDao.DaoProxy {
31
32
 
32
33
  computeCredentials() {
33
34
  let credentials = JSON.parse(JSON.stringify(this.initialCredentials))
35
+ const originalCredentials = JSON.parse(JSON.stringify(this.initialCredentials))
34
36
  const keys = Object.keys(credentials).filter(key => key.endsWith("Key"))
35
37
  for(const credentialsObserver of this.credentialsObservations) {
36
38
  credentials = {
37
39
  ...credentials,
38
40
  ...credentialsObserver.credentials,
39
- roles: [...credentials.roles, ...(credentialsObserver.credentials.roles || [])]
41
+ roles: [...credentials.roles, ...(credentialsObserver.credentials.roles || [])],
42
+ [originalCredentialsSymbol]: originalCredentials
40
43
  }
41
44
  }
42
45
  for(const key of keys) {
@@ -0,0 +1,101 @@
1
+ import { prepareParameters, processReturn, preFilterParameters } from "./params.js"
2
+
3
+ class Query {
4
+
5
+ constructor(definition, service) {
6
+ this.definition = definition
7
+ this.service = service
8
+ this.queryKey = `${service.name}.${definition.name}`
9
+ this.isObjectQuery = definition.returns?.type !== Array && definition.returns?.type !== 'Array'
10
+ && definition.returns?.type !== 'array'
11
+ this.codeString = typeof definition.code === 'function' ? `(${definition.code.toString()})` : definition.code
12
+ }
13
+
14
+ async run(parameters, context) {
15
+ if(!this.definition.update) throw new Error("Only update queries can be run")
16
+
17
+ if(!parameters) parameters = {}
18
+ if(!context) context = {
19
+ client: {
20
+ session: null,
21
+ user: null,
22
+ ip: null,
23
+ roles: []
24
+ },
25
+ service: this.service,
26
+ }
27
+
28
+ let preparedParams = await prepareParameters(parameters, this.definition.properties, this.service)
29
+
30
+ const resultPromise = this.service.app.dao.requestWithSettings({
31
+ timeout: this.definition.timeout,
32
+ }, ['database', 'runQuery'], this.service.app.databaseName, 'queries', this.queryKey, preparedParams)
33
+
34
+ resultPromise.catch(error => {
35
+ console.error(`Query ${this.definition.name} error `, error.stack || error)
36
+ })
37
+ return resultPromise
38
+ }
39
+
40
+ get(parameters, context) {
41
+ if(this.definition.update) throw new Error("Only non-update queries can be get")
42
+
43
+ if(!parameters) parameters = {}
44
+ if(!context) context = {
45
+ client: {
46
+ session: null,
47
+ user: null,
48
+ ip: null,
49
+ roles: []
50
+ },
51
+ service: this.service,
52
+ }
53
+
54
+ const resultPromise = this.service.app.dao.get([
55
+ 'database', this.definition.isObjectQuery ? 'runQueryObject' : 'runQuery',
56
+ this.service.app.databaseName, 'queries', this.queryKey, parameters
57
+ ])
58
+ resultPromise.catch(error => {
59
+ console.error(`Query ${this.definition.name} error `, error.stack || error)
60
+ })
61
+ return resultPromise
62
+ }
63
+
64
+ observable(parameters, context) {
65
+ if(this.definition.update) throw new Error("Only non-update queries can be observable")
66
+ if(!parameters) parameters = {}
67
+ if(!context) context = {
68
+ client: {
69
+ session: null,
70
+ user: null,
71
+ ip: null,
72
+ roles: []
73
+ },
74
+ service: this.service,
75
+ }
76
+
77
+ const resultPromise = this.service.app.dao.observable([
78
+ 'database', this.definition.isObjectQuery ? 'runQueryObject' : 'runQuery',
79
+ this.service.app.databaseName, 'queries', this.queryKey, parameters
80
+ ])
81
+ resultPromise.catch(error => {
82
+ console.error(`Query ${this.definition.name} error `, error.stack || error)
83
+ })
84
+ return resultPromise
85
+ }
86
+
87
+ toJSON() {
88
+ return {
89
+ name: this.definition.name,
90
+ properties: this.definition.properties,
91
+ returns: this.definition.returns,
92
+ code: this.codeString,
93
+ sourceName: this.definition.sourceName,
94
+ update: this.definition.update,
95
+ timeout: this.definition.timeout,
96
+ requestTimeout: this.definition.requestTimeout,
97
+ }
98
+ }
99
+ }
100
+
101
+ export default Query
@@ -70,6 +70,11 @@ class Service {
70
70
  }
71
71
 
72
72
  this.authenticators = this.definition.authenticators
73
+
74
+ this.queries = {}
75
+ for(let queryName in this.definition.queries) {
76
+ this.queries[queryName] = new Query( this.definition.queries[queryName], this )
77
+ }
73
78
  }
74
79
 
75
80
  async start(config) {
@@ -10,6 +10,8 @@ async function update(changes, service, app, force) {
10
10
  const dao = app.dao
11
11
  const database = app.databaseName
12
12
 
13
+ dao.request(['database', 'createTable'], database, 'queries').catch(e => 'ok')
14
+
13
15
  if(!app.noCache) {
14
16
  dao.request(['database', 'createTable'], database, 'cache').catch(e => 'ok')
15
17
  dao.request(['database', 'createIndex'], database, 'cache_byTimestamp', `${
@@ -110,10 +112,6 @@ async function update(changes, service, app, force) {
110
112
 
111
113
  debug("CREATE INDEX", indexName, index)
112
114
 
113
- const options = {
114
- multi: index.multi || false
115
- }
116
-
117
115
  if(index.function) {
118
116
  const functionCode = `(${index.function})`
119
117
  ;(globalThis.compiledFunctionsCandidates = globalThis.compiledFunctionsCandidates || {})[functionCode] = index.function
@@ -322,6 +320,42 @@ async function update(changes, service, app, force) {
322
320
  }
323
321
  })`, { table, property: change.name })*/
324
322
  } break;
323
+ case "createQuery": {
324
+ const query = change.query
325
+ const queryKey = service.name + '.' + query.name
326
+ await dao.request(['database', 'put'], database, 'queries', {
327
+ id: queryKey,
328
+ code: typeof query.code === 'function' ? `(${query.code.toString()})` : query.code,
329
+ sourceName: query.sourceName,
330
+ update: query.update,
331
+ timeout: query.timeout,
332
+ properties: query.properties,
333
+ returns: query.returns
334
+ })
335
+ debug("QUERY CREATED!", query.name)
336
+ } break;
337
+ case "renameQuery": {
338
+ const query = change.to
339
+ const queryKey = service.name + '.' + query.name
340
+ const oldQueryKey = service.name + '.' + change.from.name
341
+ const oldQuery = await dao.get(['database', 'get', database, 'queries', oldQueryKey])
342
+ await dao.request(['database', 'put'], database, 'queries', {
343
+ id: queryKey,
344
+ code: typeof oldQuery.code === 'function' ? `(${oldQuery.code.toString()})` : oldQuery.code,
345
+ sourceName: oldQuery.sourceName,
346
+ update: oldQuery.update,
347
+ timeout: oldQuery.timeout,
348
+ properties: oldQuery.properties,
349
+ returns: oldQuery.returns
350
+ })
351
+ await dao.request(['database', 'delete'], database, 'queries', oldQueryKey)
352
+ debug("QUERY RENAMED!", query.name)
353
+ } break;
354
+ case "deleteQuery": {
355
+ const queryKey = service.name + '.' + change.name
356
+ await dao.request(['database', 'delete'], database, 'queries', queryKey)
357
+ debug("QUERY DELETED!", change.name)
358
+ } break;
325
359
  default:
326
360
  }
327
361
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@live-change/framework",
3
- "version": "0.9.137",
3
+ "version": "0.9.138",
4
4
  "description": "Live Change Framework - ultimate solution for real time mobile/web apps",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -22,11 +22,11 @@
22
22
  },
23
23
  "homepage": "https://github.com/live-change/live-change-stack",
24
24
  "devDependencies": {
25
- "@live-change/dao": "^0.9.137",
26
- "@live-change/uid": "^0.9.137",
25
+ "@live-change/dao": "^0.9.138",
26
+ "@live-change/uid": "^0.9.138",
27
27
  "typedoc": "0.28.3",
28
28
  "typedoc-plugin-markdown": "^4.6.3",
29
29
  "typedoc-plugin-rename-defaults": "^0.7.3"
30
30
  },
31
- "gitHead": "762fcddea43fa160b99ee72eb29219a5e0048498"
31
+ "gitHead": "af56bfe0ca7899934a4c563ead788318832336de"
32
32
  }