@toa.io/core 1.0.0-alpha.28 → 1.0.0-alpha.282

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.
Files changed (98) hide show
  1. package/CHANGELOG.md +168 -0
  2. package/package.json +8 -10
  3. package/src/assignment.js +5 -8
  4. package/src/call.js +35 -9
  5. package/src/cascade.js +9 -11
  6. package/src/component.js +61 -21
  7. package/src/composition.js +3 -7
  8. package/src/connector.js +29 -22
  9. package/src/context.js +8 -6
  10. package/src/contract/contract.js +18 -0
  11. package/src/contract/index.js +2 -7
  12. package/src/contract/reply.js +36 -15
  13. package/src/contract/request.js +20 -10
  14. package/src/contract/schemas/index.js +9 -5
  15. package/src/contract/schemas/query.yaml +14 -1
  16. package/src/contract/schemas/source.yaml +23 -0
  17. package/src/discovery.js +15 -13
  18. package/src/effect.js +15 -0
  19. package/src/emission.js +2 -6
  20. package/src/entities/changeset.js +7 -14
  21. package/src/entities/entity.js +75 -29
  22. package/src/entities/factory.js +21 -14
  23. package/src/entities/index.js +1 -5
  24. package/src/entities/newid.js +9 -0
  25. package/src/entities/set.js +13 -4
  26. package/src/event.js +21 -7
  27. package/src/exceptions.js +53 -39
  28. package/src/exposition.js +5 -8
  29. package/src/guard.js +13 -0
  30. package/src/index.js +28 -47
  31. package/src/locator.js +2 -6
  32. package/src/observation.js +3 -15
  33. package/src/operation.js +40 -14
  34. package/src/outbox/index.js +1 -0
  35. package/src/outbox/outbox.js +296 -0
  36. package/src/query/criteria.js +3 -7
  37. package/src/query/options.js +5 -8
  38. package/src/query.js +36 -7
  39. package/src/receiver.js +76 -18
  40. package/src/reflection.js +2 -6
  41. package/src/remote.js +9 -12
  42. package/src/state.js +89 -50
  43. package/src/transition.js +13 -25
  44. package/src/transmission.js +15 -10
  45. package/src/unmanaged.js +7 -0
  46. package/test/call.fixtures.js +8 -12
  47. package/test/call.test.js +21 -9
  48. package/test/component.fixtures.js +5 -8
  49. package/test/component.test.js +22 -10
  50. package/test/connector.fixtures.js +10 -8
  51. package/test/connector.test.js +59 -38
  52. package/test/context.fixtures.js +8 -12
  53. package/test/context.test.js +18 -7
  54. package/test/contract/conditions.test.js +11 -9
  55. package/test/contract/contract.fixtures.js +12 -16
  56. package/test/contract/request.test.js +84 -34
  57. package/test/discovery.test.js +64 -0
  58. package/test/emission.fixtures.js +6 -10
  59. package/test/emission.test.js +29 -12
  60. package/test/entities/entity.fixtures.js +7 -11
  61. package/test/entities/entity.test.js +70 -49
  62. package/test/entities/factory.fixtures.js +22 -10
  63. package/test/entities/factory.test.js +29 -15
  64. package/test/entities/set.fixtures.js +6 -8
  65. package/test/entities/set.test.js +6 -5
  66. package/test/event.fixtures.js +9 -14
  67. package/test/event.test.js +57 -31
  68. package/test/locator.test.js +25 -24
  69. package/test/outbox.test.js +121 -0
  70. package/test/query.fixtures.js +1 -5
  71. package/test/query.test.js +42 -14
  72. package/test/receiver.fixtures.js +9 -12
  73. package/test/receiver.test.js +45 -22
  74. package/test/reflection.test.js +7 -6
  75. package/test/state.fixtures.js +25 -30
  76. package/test/state.test.js +72 -19
  77. package/test/transmission.fixtures.js +4 -6
  78. package/test/transmission.test.js +33 -15
  79. package/types/atomicity.d.ts +58 -0
  80. package/types/bindings.d.ts +8 -6
  81. package/types/bridges.ts +5 -2
  82. package/types/component.d.ts +7 -4
  83. package/types/context.d.ts +6 -3
  84. package/types/entity.d.ts +4 -4
  85. package/types/event.d.ts +1 -1
  86. package/types/extensions.d.ts +35 -10
  87. package/types/index.ts +16 -13
  88. package/types/message.d.ts +1 -0
  89. package/types/operations.d.ts +6 -0
  90. package/types/outbox.d.ts +53 -0
  91. package/types/query.d.ts +2 -0
  92. package/types/receiver.d.ts +2 -2
  93. package/types/reflection.d.ts +1 -1
  94. package/types/remote.d.ts +18 -0
  95. package/types/request.d.ts +16 -1
  96. package/types/state.d.ts +11 -8
  97. package/types/storages.d.ts +36 -11
  98. package/src/contract/conditions.js +0 -21
@@ -1,28 +1,53 @@
1
- import * as _core from './index'
2
- import * as _component from './component'
3
- import * as _context from './context'
4
- import * as _storages from './storages'
5
- import * as _bindings from './bindings'
1
+ import * as _core from './index.js'
2
+ import * as _component from './component.js'
3
+ import * as _context from './context.js'
4
+ import * as _storages from './storages.js'
5
+ import * as _bindings from './bindings.js'
6
+ import { Manifest } from '@toa.io/norm'
7
+
8
+ /**
9
+ * What the process hosting an extension provides to it: the counterpart of a component's
10
+ * `Context`. What is returned is a connector the extension depends on.
11
+ */
12
+ export interface Host {
13
+ /** a component of the context, by locator */
14
+ remote (locator: _core.Locator, source?: _core.Source): Promise<_core.Remote>
15
+
16
+ /** a channel of the messaging binding */
17
+ broadcast<T = unknown> (channel: string, group?: string): Promise<_bindings.Broadcast<T>>
18
+
19
+ /** components to run inside the extension's own process */
20
+ composition (paths: string[]): Promise<_core.Connector>
21
+
22
+ /** a consumer of an event of the context, `namespace.component.event` */
23
+ receive (label: string, receiver: _core.Receiver): Promise<_core.Connector>
24
+
25
+ /** what the replicas of one group decide together */
26
+ atom (group: string): _core.atomicity.Atom
27
+ }
6
28
 
7
- export interface Factory{
8
- tenant? (locator: _core.Locator, manifest: object): _core.Connector
29
+ export interface Factory {
30
+ tenant? (locator: _core.Locator, manifest: object, component: Manifest):
31
+ _core.Connector | Promise<_core.Connector>
9
32
 
10
33
  aspect? (locator: _core.Locator, manifest: object | null): Aspect | Aspect[]
11
34
 
12
- service? (name?: string): _core.Connector | null
35
+ service? (name?: string): _core.Connector | null | Promise<_core.Connector | null>
13
36
 
14
37
  component? (component: _component.Component): _component.Component
15
38
 
16
39
  context? (context: _context.Context): _context.Context
17
40
 
41
+ manage? (composition: _core.Connector): _core.Connector
42
+
18
43
  storage? (storage: _storages.Storage): _storages.Storage
19
44
 
20
- emitter? (emitter: _bindings.Emitter, label: string): _bindings.Emitter
45
+ emitter? (emitter: _bindings.Emitter, label: string, locator: _core.Locator): _bindings.Emitter
21
46
 
22
47
  receiver? (receiver: _core.Receiver, locator: _core.Locator): _core.Receiver
23
48
  }
24
49
 
25
- export interface Aspect extends _core.Connector{
50
+ export interface Aspect extends _core.Connector {
26
51
  name: string
27
52
 
28
53
  invoke (...args: any[]): any
package/types/index.ts CHANGED
@@ -1,14 +1,17 @@
1
- export * as bindings from './bindings'
2
- export * as extensions from './extensions'
3
- export * as storages from './storages'
4
- export * as bridges from './bridges'
5
- export * as operations from './operations'
1
+ export * as bindings from './bindings.js'
2
+ export * as extensions from './extensions.js'
3
+ export * as storages from './storages.js'
4
+ export * as outbox from './outbox.js'
5
+ export * as atomicity from './atomicity.js'
6
+ export * as bridges from './bridges.js'
7
+ export * as operations from './operations.js'
6
8
 
7
- export type { Component } from './component'
8
- export { Connector } from './connector'
9
- export type { Context } from './context'
10
- export type { Exception } from './exception'
11
- export { Locator } from './locator'
12
- export type { Receiver } from './receiver'
13
- export type { Message } from './message'
14
- export type { Request, Query, Reply } from './request'
9
+ export type { Component } from './component.js'
10
+ export type { Remote } from './remote.js'
11
+ export { Connector } from './connector.js'
12
+ export type { Context } from './context.js'
13
+ export type { Exception } from './exception.js'
14
+ export { Locator } from './locator.js'
15
+ export type { Receiver } from './receiver.js'
16
+ export type { Message } from './message.js'
17
+ export type { Request, Query, Reply, Source } from './request.js'
@@ -1,3 +1,4 @@
1
1
  export interface Message<T = any> {
2
2
  payload: T
3
+ telemetry?: string // W3C traceparent
3
4
  }
@@ -1,2 +1,8 @@
1
+ import { Request } from './request.js'
2
+
1
3
  export type type = 'transition' | 'observation' | 'assignment' | 'computation' | 'effect'
2
4
  export type scope = 'object' | 'objects' | 'changeset'
5
+
6
+ export class Operation {
7
+ invoke<T = any> (request: Request): Promise<T>
8
+ }
@@ -0,0 +1,53 @@
1
+ import * as _state from './state.js'
2
+ import * as _atomicity from './atomicity.js'
3
+
4
+ declare namespace toa.core {
5
+
6
+ namespace outbox {
7
+
8
+ /**
9
+ * The intent to publish, committed with the state change it belongs to. Everything about
10
+ * it is core's: the storage writes it and, where the images are the write's own, fills
11
+ * `event.origin` and `event.state` in.
12
+ */
13
+ interface Row {
14
+ id: string
15
+
16
+ /** which replica pumps this row; carries no other meaning, and no ordering */
17
+ lane: number
18
+
19
+ published: boolean
20
+
21
+ /** not before this */
22
+ pending: number
23
+
24
+ event: _state.Event
25
+ }
26
+
27
+ /** What a storage offers when it can commit a row atomically with the entity. */
28
+ interface Storage {
29
+ insert (row: Row, session?: unknown): Promise<void>
30
+
31
+ insertMany (rows: Row[], session?: unknown): Promise<void>
32
+
33
+ /**
34
+ * One page of what is due, still unpublished, and in one of the given lanes, in the
35
+ * order the rows were written. `after` continues from the last id of the page before.
36
+ */
37
+ pending (lanes: number[], now: number, limit: number, after?: string): Promise<Row[]>
38
+
39
+ settle (ids: string[]): Promise<void>
40
+ }
41
+
42
+ }
43
+
44
+ }
45
+
46
+ export type Row = toa.core.outbox.Row
47
+ export type Storage = toa.core.outbox.Storage
48
+
49
+ /**
50
+ * A lane is a slot of `atomicity`: which replica pumps a row, and nothing else. The outbox is
51
+ * the first thing to claim slots this way, not the last.
52
+ */
53
+ export type Atom = _atomicity.Atom
package/types/query.d.ts CHANGED
@@ -4,6 +4,8 @@ declare namespace toa.core {
4
4
  id?: string
5
5
  version?: number
6
6
  criteria?: Object
7
+ search?: string
8
+ sample?: number
7
9
  omit?: number
8
10
  limit?: number
9
11
  sort?: string[]
@@ -1,5 +1,5 @@
1
- import * as _message from './message'
2
- import * as _connector from './connector'
1
+ import * as _message from './message.js'
2
+ import * as _connector from './connector.js'
3
3
 
4
4
  declare namespace toa.core {
5
5
 
@@ -1,4 +1,4 @@
1
- import * as _core from './connector'
1
+ import * as _core from './connector.js'
2
2
 
3
3
  declare namespace toa.core {
4
4
 
@@ -0,0 +1,18 @@
1
+ import { Component } from './component.js'
2
+
3
+ export class Remote extends Component {
4
+ explain (endpoint: string): Promise<Explanation>
5
+ }
6
+
7
+ interface Explanation {
8
+ input: Schema | null
9
+ output: Schema | null
10
+ errors?: string[]
11
+ }
12
+
13
+ interface Schema {
14
+ type: string
15
+ properties: {
16
+ [key: string]: Schema
17
+ }
18
+ }
@@ -1,19 +1,34 @@
1
- import { Exception } from './exception'
1
+ import { Exception } from './exception.js'
2
2
 
3
3
  export interface Query {
4
4
  id?: string
5
+ ids?: Array<string>
5
6
  criteria?: string
7
+ search?: string
8
+ sample?: number
6
9
  omit?: number
7
10
  limit?: number
8
11
  sort?: Array<string>
9
12
  projection?: Array<string>
10
13
  version?: number
14
+ deleted?: boolean
11
15
  }
12
16
 
17
+ /**
18
+ * Origin of a call. Stamped by the caller, sanitized by the request contract.
19
+ */
20
+ export type Source =
21
+ | { namespace: string, component: string, operation: string }
22
+ | { namespace: string, component: string, event: string }
23
+ | { service: string }
24
+
13
25
  export interface Request {
14
26
  input?: any
15
27
  query?: Query
16
28
  authentic?: boolean
29
+ task?: boolean
30
+ telemetry?: string // W3C traceparent
31
+ source?: Source
17
32
  }
18
33
 
19
34
  export interface Reply {
package/types/state.d.ts CHANGED
@@ -1,14 +1,17 @@
1
- import * as _entity from './entity'
2
- import * as _storages from './storages'
1
+ import * as _entity from './entity.js'
2
+ import * as _storages from './storages.js'
3
3
 
4
4
  declare namespace toa.core {
5
5
 
6
6
  namespace transition {
7
7
 
8
8
  type Event = {
9
- origin: Object
9
+ /** the pre-image; null when the entity did not exist before */
10
+ origin: Object | null
10
11
  state: Object
11
- changeset: Object
12
+ /** out-of-band values an algorithm wrote into `state._trailers`; must be serializable */
13
+ trailers?: Object
14
+ input?: Object
12
15
  }
13
16
 
14
17
  }
@@ -16,17 +19,17 @@ declare namespace toa.core {
16
19
  interface State {
17
20
  init(id: string): _entity.Entity
18
21
 
19
- object(query: _storages.Query): Promise<_entity.Entity>
22
+ object(query: _storages.Query, mutable?: boolean): Promise<_entity.Entity>
20
23
 
21
- objects(query: _storages.Query): Promise<_entity.Entity[]>
24
+ objects(query: _storages.Query, mutable?: boolean): Promise<_entity.Entity[]>
22
25
 
23
26
  changeset(query: _storages.Query): _entity.Changeset
24
27
 
25
28
  none(): null
26
29
 
27
- commit(entity: _entity.Entity): Promise<boolean>
30
+ commit(entity: _entity.Entity, input?: Object): Promise<boolean>
28
31
 
29
- apply(changeset: _entity.Changeset): Promise<void>
32
+ apply(changeset: _entity.Changeset, input?: Object): Promise<_storages.Record>
30
33
  }
31
34
 
32
35
  }
@@ -1,7 +1,8 @@
1
1
  // noinspection ES6UnusedImports
2
2
 
3
- import { Locator } from './locator'
4
- import { Connector } from './connector'
3
+ import { Locator } from './locator.js'
4
+ import { Connector } from './connector.js'
5
+ import * as outbox from './outbox.js'
5
6
 
6
7
  declare namespace toa.core {
7
8
 
@@ -30,36 +31,60 @@ declare namespace toa.core {
30
31
  id?: string
31
32
  version?: number
32
33
  criteria?: ast.Node
34
+ search?: string
35
+ sample?: number
33
36
  options?: Object
34
37
  }
35
38
 
36
39
  interface Migration {
37
- disconnect(): Promise<void>
40
+ disconnect (): Promise<void>
38
41
 
39
- database(name: string): Promise<void>
42
+ database (name: string): Promise<void>
40
43
 
41
- table(database: string, locator: Locator, schema: Object, reset?: boolean): Promise<string>
44
+ table (database: string, locator: Locator, schema: Object, reset?: boolean): Promise<string>
45
+ }
46
+
47
+ interface Options {
48
+ /** whether this component publishes anything, and so needs an outbox */
49
+ outbox?: boolean
42
50
  }
43
51
 
44
52
  interface Factory {
45
- storage(locator: Locator, properties?: object): Storage
53
+ storage (locator: Locator, properties?: object, options?: Options): Storage
46
54
 
47
- migration?(driver?: string): Migration
55
+ migration? (driver?: string): Migration
48
56
  }
49
57
  }
50
58
 
51
59
  interface Storage extends Connector {
52
60
  // object observation
53
- get?(query: storages.Query): Promise<storages.Record | null>
61
+ get? (query: storages.Query): Promise<storages.Record | null>
54
62
 
55
63
  // objects observation
56
- find?(query: storages.Query): Promise<storages.Record[]>
64
+ find? (query: storages.Query): Promise<storages.Record[]>
57
65
 
58
66
  // commit
59
- store?(record: storages.Record): Promise<boolean>
67
+ store? (record: storages.Record, row?: outbox.Row): Promise<boolean>
68
+
69
+ // mass commit
70
+ massStore? (records: storages.Record[], rows?: outbox.Row[]): Promise<boolean>
60
71
 
61
72
  // assignment
62
- upsert?(query: storages.Query, changeset: Object, insert: storages.Record): Promise<storages.Record>
73
+ upsert? (query: storages.Query, changeset: Object, row?: outbox.Row): Promise<storages.Record>
74
+
75
+ // atomic get-or-create
76
+ ensure? (query: storages.Query, properties: Object, record: storages.Record, row?: outbox.Row): Promise<storages.Record>
77
+
78
+ /**
79
+ * Present only where a row can be committed atomically with the entity. Its absence is
80
+ * what makes the runtime fall back to publishing inline, so a storage that cannot do
81
+ * this must not offer it: a row written outside the transaction would be a second write
82
+ * with a crash window in front of it, which is the defect the outbox exists to close.
83
+ *
84
+ * The row schema is the connector's own — it also owns `pending` and `settle` — and
85
+ * never crosses this boundary.
86
+ */
87
+ outbox?: outbox.Storage
63
88
  }
64
89
 
65
90
  }
@@ -1,21 +0,0 @@
1
- 'use strict'
2
-
3
- const { SystemException } = require('../exceptions')
4
-
5
- class Conditions {
6
- #schema
7
-
8
- constructor (schema) {
9
- this.#schema = schema
10
- }
11
-
12
- fit (value) {
13
- const error = this.#schema.fit(value)
14
-
15
- if (error !== null) throw new this.constructor.Exception(error)
16
- }
17
-
18
- static Exception = SystemException
19
- }
20
-
21
- exports.Conditions = Conditions