@toa.io/core 1.0.0-alpha.3 → 1.0.0-alpha.30

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/core",
3
- "version": "1.0.0-alpha.3",
3
+ "version": "1.0.0-alpha.30",
4
4
  "description": "Toa Core",
5
5
  "author": "temich <tema.gurtovoy@gmail.com>",
6
6
  "homepage": "https://github.com/toa-io/toa#readme",
@@ -21,13 +21,13 @@
21
21
  },
22
22
  "dependencies": {
23
23
  "@rsql/parser": "1.2.4",
24
- "@toa.io/console": "1.0.0-alpha.3",
25
- "@toa.io/generic": "1.0.0-alpha.3",
26
- "@toa.io/yaml": "1.0.0-alpha.3",
24
+ "@toa.io/console": "1.0.0-alpha.30",
25
+ "@toa.io/generic": "1.0.0-alpha.30",
26
+ "@toa.io/yaml": "1.0.0-alpha.30",
27
27
  "error-value": "0.3.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "clone-deep": "4.0.1"
31
31
  },
32
- "gitHead": "e36ac7871fc14d15863aaf8f9bbdeace8bdfa9f0"
32
+ "gitHead": "29cc19358f35fbe9229dced979724f7a26538013"
33
33
  }
package/src/assignment.js CHANGED
@@ -9,13 +9,22 @@ class Assignment extends Operation {
9
9
  }
10
10
 
11
11
  async commit (store) {
12
- const { scope, state, reply } = store
12
+ const {
13
+ scope,
14
+ state,
15
+ reply
16
+ } = store
13
17
 
14
18
  if (reply.error !== undefined) return
15
19
 
16
20
  scope.set(state)
17
21
 
18
- await this.scope.apply(scope)
22
+ const output = await this.scope.apply(scope)
23
+
24
+ // assignment returns new state by default
25
+ if (store.reply.output === undefined) {
26
+ store.reply.output = output
27
+ }
19
28
  }
20
29
  }
21
30
 
package/src/cascade.js CHANGED
@@ -1,16 +1,15 @@
1
1
  'use strict'
2
2
 
3
- const { merge } = require('@toa.io/generic')
4
3
  const { Connector } = require('./connector')
5
4
 
6
5
  class Cascade extends Connector {
7
- #bridges
6
+ // #bridges
8
7
  #last
9
8
 
10
9
  constructor (bridges) {
11
10
  super()
12
11
 
13
- this.#bridges = bridges
12
+ // this.#bridges = bridges
14
13
  this.#last = bridges[bridges.length - 1]
15
14
 
16
15
  this.depends(bridges)
@@ -18,14 +18,13 @@ properties:
18
18
  minItems: 1
19
19
  items:
20
20
  type: string
21
- pattern: ^[a-zA-Z]+([-a-zA-Z0-9]*[a-zA-Z0-9]+)?(:(asc|desc))?$
21
+ pattern: ^\w{1,32}(?::(?:asc|desc))?$
22
22
  projection:
23
23
  type: array
24
24
  uniqueItems: true
25
25
  minItems: 1
26
26
  items:
27
27
  type: string
28
- pattern: ^([a-zA-Z]+([-a-zA-Z0-9]*[a-zA-Z0-9]+)?)$
29
28
  not:
30
29
  const: id
31
30
  additionalProperties: false
@@ -1,6 +1,5 @@
1
1
  'use strict'
2
2
 
3
- const { merge, overwrite, newid } = require('@toa.io/generic')
4
3
  const { EntityContractException } = require('../exceptions')
5
4
 
6
5
  class Changeset {
@@ -26,21 +25,13 @@ class Changeset {
26
25
 
27
26
  if (error !== null) throw new EntityContractException(error)
28
27
 
28
+ value._updated = Date.now()
29
+
29
30
  this.#state = value
30
31
  }
31
32
 
32
33
  export () {
33
- const changeset = this.#state
34
- const result = { changeset }
35
- const insert = merge({ id: newid() }, changeset)
36
- const error = this.#schema.fit(insert)
37
-
38
- if (error === null) {
39
- delete insert.id
40
- result.insert = overwrite(insert, changeset)
41
- }
42
-
43
- return result
34
+ return this.#state
44
35
  }
45
36
  }
46
37
 
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  const { difference, newid } = require('@toa.io/generic')
4
-
5
4
  const { EntityContractException } = require('../exceptions')
6
5
 
7
6
  class Entity {
@@ -29,7 +28,8 @@ class Entity {
29
28
  set (value) {
30
29
  const error = this.#schema.fit(value)
31
30
 
32
- if (error !== null) throw new EntityContractException(error)
31
+ if (error !== null)
32
+ throw new EntityContractException(error)
33
33
 
34
34
  this.#set(value)
35
35
  }
@@ -38,18 +38,34 @@ class Entity {
38
38
  return {
39
39
  origin: this.#origin,
40
40
  state: this.#state,
41
- changeset: this.#origin === null ? this.#state : difference(this.#origin, this.#state)
41
+ changeset: this.#origin === null ? this.#state : difference(this.#origin, this.#state),
42
+ trailers: this.#state._trailers
42
43
  }
43
44
  }
44
45
 
45
46
  #init (id) {
46
- const value = { ...this.#schema.defaults({ id }), _version: 0 }
47
+ const value = {
48
+ ...this.#schema.defaults({ id }),
49
+ _version: 0,
50
+ _created: Date.now()
51
+ }
47
52
 
48
53
  this.#set(value)
49
54
  }
50
55
 
51
56
  #set (value) {
52
- Object.defineProperty(value, 'id', { writable: false, configurable: false })
57
+ if (!('_trailers' in value))
58
+ Object.defineProperty(value, '_trailers', {
59
+ writable: false,
60
+ configurable: false,
61
+ enumerable: false,
62
+ value: {}
63
+ })
64
+
65
+ if (this.#state !== undefined) {
66
+ value._updated = Date.now()
67
+ value._version++
68
+ }
53
69
 
54
70
  this.#state = value
55
71
  }
package/src/exceptions.js CHANGED
@@ -19,6 +19,7 @@ const codes = {
19
19
  StatePrecondition: 303,
20
20
  StateConcurrency: 304,
21
21
  StateInitialization: 305,
22
+ Duplicate: 306,
22
23
 
23
24
  Communication: 400,
24
25
  Transmission: 401
@@ -88,7 +89,7 @@ for (const [name, code] of Object.entries(codes)) {
88
89
  if (exports[classname] === undefined) {
89
90
  exports[classname] = class extends Exception {
90
91
  constructor (message) {
91
- super(code, message || classname)
92
+ super(code, message ?? classname)
92
93
  }
93
94
  }
94
95
  }
package/src/locator.js CHANGED
@@ -12,6 +12,7 @@ class Locator {
12
12
  id
13
13
  label
14
14
  uppercase
15
+ lowercase
15
16
 
16
17
  /**
17
18
  * @param {string} name
@@ -26,6 +27,7 @@ class Locator {
26
27
  this.id = concat(namespace, '.') + name
27
28
  this.label = (concat(namespace, '-') + name).toLowerCase()
28
29
  this.uppercase = (concat(namespace, '_') + name).toUpperCase()
30
+ this.lowercase = (concat(namespace, '_') + name).toLowerCase()
29
31
  }
30
32
 
31
33
  hostname (prefix) {
@@ -39,8 +41,11 @@ class Locator {
39
41
  static parse (string) {
40
42
  const [namespace, name] = string.split(DOT)
41
43
 
42
- if (name === undefined) return new Locator(namespace)
43
- else return new Locator(name, namespace)
44
+ if (name === undefined) {
45
+ return new Locator(namespace)
46
+ } else {
47
+ return new Locator(name, namespace)
48
+ }
44
49
  }
45
50
  }
46
51
 
@@ -33,9 +33,9 @@ const coerce = (node, properties) => {
33
33
  }
34
34
 
35
35
  const COERCE = {
36
- number: Number,
37
- integer: parseInt,
38
- boolean: Boolean
36
+ number: Number.parseFloat,
37
+ integer: Number.parseInt,
38
+ boolean: (value) => value === 'true'
39
39
  }
40
40
 
41
41
  exports.criteria = criteria
package/src/state.js CHANGED
@@ -4,27 +4,20 @@ const { empty } = require('@toa.io/generic')
4
4
 
5
5
  const {
6
6
  StatePreconditionException,
7
- StateNotFoundException,
8
- StateInitializationException
7
+ StateNotFoundException
9
8
  } = require('./exceptions')
10
9
 
11
- /**
12
- * @implements {toa.core.State}
13
- */
14
10
  class State {
15
- /** @type {toa.core.Storage} */
11
+ #associated
16
12
  #storage
17
-
18
- /** @type {toa.core.entity.Factory} */
19
13
  #entity
20
14
  #emission
21
- #dependent
22
15
 
23
- constructor (storage, entity, emission, dependent) {
16
+ constructor (storage, entity, emission, associated) {
24
17
  this.#storage = storage
25
18
  this.#entity = entity
26
19
  this.#emission = emission
27
- this.#dependent = dependent === true
20
+ this.#associated = associated === true
28
21
  }
29
22
 
30
23
  init (id) {
@@ -35,12 +28,16 @@ class State {
35
28
  const record = await this.#storage.get(query)
36
29
 
37
30
  if (record === null) {
38
- if (this.#dependent && query.id !== undefined && query.version === undefined) return this.init(query.id)
39
- else if (query.version !== undefined) throw new StatePreconditionException()
31
+ if (this.#associated && query.id !== undefined && query.version === undefined) {
32
+ return this.init(query.id)
33
+ } else if (query.version !== undefined) throw new StatePreconditionException()
40
34
  }
41
35
 
42
- if (record === null) return null
43
- else return this.#entity.object(record)
36
+ if (record === null) {
37
+ return null
38
+ } else {
39
+ return this.#entity.object(record)
40
+ }
44
41
  }
45
42
 
46
43
  async objects (query) {
@@ -68,30 +65,34 @@ class State {
68
65
  ok = await this.#storage.store(object)
69
66
 
70
67
  // #20
71
- await this.#emission.emit(event)
68
+ if (ok === true) {
69
+ await this.#emission.emit(event)
70
+ }
72
71
  }
73
72
 
74
73
  return ok
75
74
  }
76
75
 
77
76
  async apply (state) {
78
- const { changeset, insert } = state.export()
79
-
80
- let upsert
81
-
82
- if (this.#dependent && state.query.id !== undefined && state.query.version === undefined) {
83
- upsert = insert
84
- }
77
+ const changeset = state.export()
85
78
 
86
- const result = await this.#storage.upsert(state.query, changeset, upsert)
79
+ const result = await this.#storage.upsert(state.query, changeset)
87
80
 
88
81
  if (result === null) {
89
- if (state.query.version !== undefined) throw new StatePreconditionException()
90
- else throw new StateNotFoundException()
82
+ if (state.query.version !== undefined) {
83
+ throw new StatePreconditionException()
84
+ } else {
85
+ throw new StateNotFoundException()
86
+ }
87
+ } else {
88
+ // same as above
89
+ await this.#emission.emit({
90
+ changeset,
91
+ state: result
92
+ })
91
93
  }
92
94
 
93
- // same as above
94
- await this.#emission.emit({ changeset, state: result })
95
+ return result
95
96
  }
96
97
  }
97
98
 
package/src/transition.js CHANGED
@@ -3,7 +3,10 @@
3
3
  const { retry } = require('@toa.io/generic')
4
4
 
5
5
  const { Operation } = require('./operation')
6
- const { StateConcurrencyException, StateNotFoundException } = require('./exceptions')
6
+ const {
7
+ StateConcurrencyException,
8
+ StateNotFoundException
9
+ } = require('./exceptions')
7
10
 
8
11
  class Transition extends Operation {
9
12
  #concurrency
@@ -23,7 +26,9 @@ class Transition extends Operation {
23
26
 
24
27
  store.scope = request.query ? await this.query(request.query) : this.scope.init()
25
28
 
26
- if (store.scope === null) throw new StateNotFoundException()
29
+ if (store.scope === null) {
30
+ throw new StateNotFoundException()
31
+ }
27
32
 
28
33
  store.state = store.scope.get()
29
34
  }
@@ -35,11 +40,13 @@ class Transition extends Operation {
35
40
 
36
41
  scope.set(state)
37
42
 
38
- const ok = await this.scope.commit(scope)
43
+ const result = await this.scope.commit(scope)
39
44
 
40
- if (ok !== true) {
41
- if (this.#concurrency === 'retry') retry()
42
- else throw new StateConcurrencyException()
45
+ if (result === false) {
46
+ if (this.#concurrency === 'retry')
47
+ return retry()
48
+ else
49
+ throw new StateConcurrencyException()
43
50
  }
44
51
  }
45
52
 
@@ -16,10 +16,13 @@ const state = () => ({
16
16
  _created: generate(),
17
17
  _updated: generate(),
18
18
  _deleted: generate(),
19
- _version: generate()
19
+ _version: 0
20
20
  })
21
21
 
22
- const failed = () => ({ ...state(), fail: true })
22
+ const failed = () => ({
23
+ ...state(),
24
+ fail: true
25
+ })
23
26
 
24
27
  exports.schema = schema
25
28
  exports.state = state
@@ -28,9 +28,12 @@ describe('argument', () => {
28
28
  it('should provide initial state if no argument passed', () => {
29
29
  const entity = new Entity(fixtures.schema)
30
30
  const defaults = fixtures.schema.defaults.mock.results[0].value
31
- const expected = { ...defaults, _version: 0 }
31
+ const expected = {
32
+ ...defaults,
33
+ _version: 0
34
+ }
32
35
 
33
- expect(entity.get()).toStrictEqual(expected)
36
+ expect(entity.get()).toStrictEqual(expect.objectContaining(expected))
34
37
  })
35
38
 
36
39
  it('should set state', () => {
@@ -51,26 +54,12 @@ it('should provide event', () => {
51
54
 
52
55
  const event = entity.event()
53
56
 
54
- expect(event).toEqual({
57
+ expect(event).toEqual(expect.objectContaining({
55
58
  state,
56
59
  origin,
57
- changeset: { foo: 'new value' }
58
- })
59
- })
60
-
61
- it('should define `id` as readonly', async () => {
62
- const origin = fixtures.state()
63
- const entity = new Entity(fixtures.schema, origin)
64
- const state = entity.get()
65
-
66
- expect(() => (state.id = 1)).toThrow('assign to read only property')
67
- })
68
-
69
- it('should seal id', async () => {
70
- const origin = fixtures.state()
71
- const entity = new Entity(fixtures.schema, origin)
72
- const state = entity.get()
73
- const redefine = () => Object.defineProperty(state, 'id', { writable: true })
74
-
75
- expect(redefine).toThrow('redefine property')
60
+ changeset: expect.objectContaining({
61
+ foo: 'new value',
62
+ _version: 1
63
+ })
64
+ }))
76
65
  })
@@ -1,10 +1,11 @@
1
- export class Locator {
1
+ export class Locator{
2
2
  public readonly name: string
3
3
  public readonly namespace: string
4
4
 
5
5
  public readonly id: string
6
6
  public readonly label: string
7
7
  public readonly uppercase: string
8
+ public readonly lowercase: string
8
9
 
9
10
  constructor (name: string, namespace?: string)
10
11