@toa.io/core 1.0.0-alpha.284 → 1.0.0-alpha.287

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 (290) hide show
  1. package/CHANGELOG.md +1 -185
  2. package/package.json +19 -7
  3. package/source/assignment.ts +27 -0
  4. package/{src/call.js → source/call.ts} +16 -21
  5. package/{src/cascade.js → source/cascade.ts} +6 -8
  6. package/{src/component.js → source/component.ts} +27 -25
  7. package/{src/composition.js → source/composition.ts} +9 -3
  8. package/{src/connector.js → source/connector.ts} +36 -55
  9. package/source/context.ts +67 -0
  10. package/source/contract/contract.ts +23 -0
  11. package/{src/contract/reply.js → source/contract/reply.ts} +26 -19
  12. package/source/contract/request.ts +85 -0
  13. package/source/contract/schemas.ts +39 -0
  14. package/{src/discovery.js → source/discovery.ts} +15 -8
  15. package/source/effect.ts +14 -0
  16. package/{src/emission.js → source/emission.ts} +5 -3
  17. package/source/entities/changeset.ts +37 -0
  18. package/source/entities/entity.ts +137 -0
  19. package/source/entities/factory.ts +52 -0
  20. package/source/entities/index.ts +4 -0
  21. package/{src/entities/newid.js → source/entities/newid.ts} +1 -1
  22. package/{src/entities/set.js → source/entities/set.ts} +8 -5
  23. package/source/event.ts +57 -0
  24. package/{src/exceptions.js → source/exceptions.ts} +52 -33
  25. package/source/exposition.ts +33 -0
  26. package/source/guard.ts +16 -0
  27. package/{src/index.js → source/index.ts} +8 -0
  28. package/{src/locator.js → source/locator.ts} +17 -30
  29. package/source/observation.ts +16 -0
  30. package/source/operation.ts +138 -0
  31. package/{src/outbox/outbox.js → source/outbox/outbox.ts} +87 -53
  32. package/source/query/criteria.ts +90 -0
  33. package/source/query/options.ts +35 -0
  34. package/{src/query.js → source/query.ts} +14 -17
  35. package/source/receiver.ts +120 -0
  36. package/source/reflection.ts +20 -0
  37. package/source/remote.ts +20 -0
  38. package/source/state.ts +179 -0
  39. package/source/transition.ts +79 -0
  40. package/{src/transmission.js → source/transmission.ts} +8 -8
  41. package/source/types/atomicity.ts +63 -0
  42. package/source/types/bindings.ts +59 -0
  43. package/source/types/bridges.ts +54 -0
  44. package/source/types/extensions.ts +89 -0
  45. package/{types → source/types}/index.ts +9 -16
  46. package/{types/message.d.ts → source/types/message.ts} +2 -1
  47. package/source/types/operations.ts +31 -0
  48. package/source/types/outbox.ts +35 -0
  49. package/source/types/receiver.ts +10 -0
  50. package/{types/request.d.ts → source/types/request.ts} +8 -9
  51. package/source/types/state.ts +9 -0
  52. package/source/types/storages.ts +129 -0
  53. package/source/unmanaged.ts +8 -0
  54. package/test/call.fixtures.js +3 -1
  55. package/test/call.test.js +28 -7
  56. package/test/component.test.js +10 -5
  57. package/test/connector.fixtures.js +7 -7
  58. package/test/connector.test.js +15 -2
  59. package/test/context.fixtures.js +1 -1
  60. package/test/context.test.js +4 -4
  61. package/test/contract/conditions.test.js +6 -2
  62. package/test/contract/contract.fixtures.js +3 -4
  63. package/test/contract/request.test.js +68 -34
  64. package/test/discovery.test.js +10 -3
  65. package/test/emission.test.js +16 -8
  66. package/test/entities/entity.fixtures.js +5 -6
  67. package/test/entities/entity.test.js +54 -20
  68. package/test/entities/factory.fixtures.js +11 -10
  69. package/test/entities/factory.test.js +47 -10
  70. package/test/entities/set.test.js +1 -1
  71. package/test/event.test.js +29 -11
  72. package/test/locator.test.js +10 -4
  73. package/test/outbox.test.js +49 -15
  74. package/test/query.test.js +69 -7
  75. package/test/receiver.fixtures.js +2 -2
  76. package/test/receiver.test.js +53 -27
  77. package/test/reflection.test.js +3 -3
  78. package/test/state.fixtures.js +10 -3
  79. package/test/state.test.js +77 -14
  80. package/test/transmission.test.js +25 -12
  81. package/transpiled/assignment.d.ts +6 -0
  82. package/transpiled/assignment.js +20 -0
  83. package/transpiled/assignment.js.map +1 -0
  84. package/transpiled/call.d.ts +10 -0
  85. package/transpiled/call.js +52 -0
  86. package/transpiled/call.js.map +1 -0
  87. package/transpiled/cascade.d.ts +7 -0
  88. package/transpiled/cascade.js +28 -0
  89. package/transpiled/cascade.js.map +1 -0
  90. package/transpiled/component.d.ts +15 -0
  91. package/transpiled/component.js +61 -0
  92. package/transpiled/component.js.map +1 -0
  93. package/transpiled/composition.d.ts +6 -0
  94. package/transpiled/composition.js +23 -0
  95. package/transpiled/composition.js.map +1 -0
  96. package/transpiled/connector.d.ts +47 -0
  97. package/transpiled/connector.js +143 -0
  98. package/transpiled/connector.js.map +1 -0
  99. package/transpiled/context.d.ts +18 -0
  100. package/transpiled/context.js +41 -0
  101. package/transpiled/context.js.map +1 -0
  102. package/transpiled/contract/contract.d.ts +10 -0
  103. package/transpiled/contract/contract.js +14 -0
  104. package/transpiled/contract/contract.js.map +1 -0
  105. package/transpiled/contract/index.d.ts +2 -0
  106. package/transpiled/contract/index.js +3 -0
  107. package/transpiled/contract/index.js.map +1 -0
  108. package/transpiled/contract/reply.d.ts +7 -0
  109. package/transpiled/contract/reply.js +48 -0
  110. package/transpiled/contract/reply.js.map +1 -0
  111. package/transpiled/contract/request.d.ts +24 -0
  112. package/transpiled/contract/request.js +59 -0
  113. package/transpiled/contract/request.js.map +1 -0
  114. package/transpiled/contract/schemas.d.ts +4 -0
  115. package/transpiled/contract/schemas.js +36 -0
  116. package/transpiled/contract/schemas.js.map +1 -0
  117. package/transpiled/discovery.d.ts +12 -0
  118. package/transpiled/discovery.js +34 -0
  119. package/transpiled/discovery.js.map +1 -0
  120. package/transpiled/effect.d.ts +5 -0
  121. package/transpiled/effect.js +11 -0
  122. package/transpiled/effect.js.map +1 -0
  123. package/transpiled/emission.d.ts +8 -0
  124. package/transpiled/emission.js +14 -0
  125. package/transpiled/emission.js.map +1 -0
  126. package/transpiled/entities/changeset.d.ts +10 -0
  127. package/transpiled/entities/changeset.js +27 -0
  128. package/transpiled/entities/changeset.js.map +1 -0
  129. package/transpiled/entities/entity.d.ts +19 -0
  130. package/transpiled/entities/entity.js +109 -0
  131. package/transpiled/entities/entity.js.map +1 -0
  132. package/transpiled/entities/factory.d.ts +22 -0
  133. package/transpiled/entities/factory.js +34 -0
  134. package/transpiled/entities/factory.js.map +1 -0
  135. package/transpiled/entities/index.d.ts +4 -0
  136. package/transpiled/entities/index.js +5 -0
  137. package/transpiled/entities/index.js.map +1 -0
  138. package/transpiled/entities/newid.d.ts +1 -0
  139. package/transpiled/entities/newid.js +7 -0
  140. package/transpiled/entities/newid.js.map +1 -0
  141. package/transpiled/entities/set.d.ts +10 -0
  142. package/transpiled/entities/set.js +19 -0
  143. package/transpiled/entities/set.js.map +1 -0
  144. package/transpiled/event.d.ts +14 -0
  145. package/transpiled/event.js +38 -0
  146. package/transpiled/event.js.map +1 -0
  147. package/transpiled/exceptions.d.ts +61 -0
  148. package/transpiled/exceptions.js +93 -0
  149. package/transpiled/exceptions.js.map +1 -0
  150. package/transpiled/exposition.d.ts +18 -0
  151. package/transpiled/exposition.js +18 -0
  152. package/transpiled/exposition.js.map +1 -0
  153. package/transpiled/guard.d.ts +7 -0
  154. package/transpiled/guard.js +12 -0
  155. package/transpiled/guard.js.map +1 -0
  156. package/transpiled/index.d.ts +35 -0
  157. package/transpiled/index.js +29 -0
  158. package/transpiled/index.js.map +1 -0
  159. package/transpiled/locator.d.ts +12 -0
  160. package/transpiled/locator.js +32 -0
  161. package/transpiled/locator.js.map +1 -0
  162. package/transpiled/observation.d.ts +5 -0
  163. package/transpiled/observation.js +12 -0
  164. package/transpiled/observation.js.map +1 -0
  165. package/transpiled/operation.d.ts +49 -0
  166. package/transpiled/operation.js +77 -0
  167. package/transpiled/operation.js.map +1 -0
  168. package/transpiled/outbox/index.d.ts +1 -0
  169. package/transpiled/outbox/index.js +2 -0
  170. package/transpiled/outbox/index.js.map +1 -0
  171. package/transpiled/outbox/outbox.d.ts +46 -0
  172. package/transpiled/outbox/outbox.js +267 -0
  173. package/transpiled/outbox/outbox.js.map +1 -0
  174. package/transpiled/query/criteria.d.ts +6 -0
  175. package/transpiled/query/criteria.js +74 -0
  176. package/transpiled/query/criteria.js.map +1 -0
  177. package/transpiled/query/options.d.ts +3 -0
  178. package/transpiled/query/options.js +27 -0
  179. package/transpiled/query/options.js.map +1 -0
  180. package/transpiled/query.d.ts +9 -0
  181. package/transpiled/query.js +57 -0
  182. package/transpiled/query.js.map +1 -0
  183. package/transpiled/receiver.d.ts +20 -0
  184. package/transpiled/receiver.js +92 -0
  185. package/transpiled/receiver.js.map +1 -0
  186. package/transpiled/reflection.d.ts +9 -0
  187. package/transpiled/reflection.js +13 -0
  188. package/transpiled/reflection.js.map +1 -0
  189. package/transpiled/remote.d.ts +10 -0
  190. package/transpiled/remote.js +12 -0
  191. package/transpiled/remote.js.map +1 -0
  192. package/transpiled/state.d.ts +33 -0
  193. package/transpiled/state.js +126 -0
  194. package/transpiled/state.js.map +1 -0
  195. package/transpiled/transition.d.ts +14 -0
  196. package/transpiled/transition.js +51 -0
  197. package/transpiled/transition.js.map +1 -0
  198. package/transpiled/transmission.d.ts +8 -0
  199. package/transpiled/transmission.js +30 -0
  200. package/transpiled/transmission.js.map +1 -0
  201. package/transpiled/types/atomicity.d.ts +56 -0
  202. package/transpiled/types/atomicity.js +2 -0
  203. package/transpiled/types/atomicity.js.map +1 -0
  204. package/transpiled/types/bindings.d.ts +42 -0
  205. package/transpiled/types/bindings.js +2 -0
  206. package/transpiled/types/bindings.js.map +1 -0
  207. package/transpiled/types/bridges.d.ts +38 -0
  208. package/transpiled/types/bridges.js +2 -0
  209. package/transpiled/types/bridges.js.map +1 -0
  210. package/transpiled/types/extensions.d.ts +62 -0
  211. package/transpiled/types/extensions.js +2 -0
  212. package/transpiled/types/extensions.js.map +1 -0
  213. package/transpiled/types/index.d.ts +13 -0
  214. package/transpiled/types/index.js +8 -0
  215. package/transpiled/types/index.js.map +1 -0
  216. package/transpiled/types/message.d.ts +5 -0
  217. package/transpiled/types/message.js +2 -0
  218. package/transpiled/types/message.js.map +1 -0
  219. package/transpiled/types/operations.d.ts +15 -0
  220. package/transpiled/types/operations.js +2 -0
  221. package/transpiled/types/operations.js.map +1 -0
  222. package/transpiled/types/outbox.d.ts +28 -0
  223. package/transpiled/types/outbox.js +2 -0
  224. package/transpiled/types/outbox.js.map +1 -0
  225. package/transpiled/types/receiver.d.ts +9 -0
  226. package/transpiled/types/receiver.js +2 -0
  227. package/transpiled/types/receiver.js.map +1 -0
  228. package/transpiled/types/request.d.ts +55 -0
  229. package/transpiled/types/request.js +2 -0
  230. package/transpiled/types/request.js.map +1 -0
  231. package/transpiled/types/state.d.ts +9 -0
  232. package/transpiled/types/state.js +2 -0
  233. package/transpiled/types/state.js.map +1 -0
  234. package/transpiled/types/storages.d.ts +106 -0
  235. package/transpiled/types/storages.js +2 -0
  236. package/transpiled/types/storages.js.map +1 -0
  237. package/transpiled/unmanaged.d.ts +5 -0
  238. package/transpiled/unmanaged.js +7 -0
  239. package/transpiled/unmanaged.js.map +1 -0
  240. package/tsconfig.json +10 -0
  241. package/tsconfig.tsbuildinfo +1 -0
  242. package/src/assignment.js +0 -28
  243. package/src/context.js +0 -57
  244. package/src/contract/contract.js +0 -18
  245. package/src/contract/request.js +0 -77
  246. package/src/contract/schemas/error.yaml +0 -9
  247. package/src/contract/schemas/index.js +0 -11
  248. package/src/contract/schemas/query.yaml +0 -43
  249. package/src/contract/schemas/source.yaml +0 -23
  250. package/src/effect.js +0 -15
  251. package/src/entities/changeset.js +0 -36
  252. package/src/entities/entity.js +0 -119
  253. package/src/entities/factory.js +0 -40
  254. package/src/entities/index.js +0 -1
  255. package/src/event.js +0 -54
  256. package/src/exposition.js +0 -24
  257. package/src/guard.js +0 -13
  258. package/src/observation.js +0 -8
  259. package/src/operation.js +0 -95
  260. package/src/query/criteria.js +0 -44
  261. package/src/query/options.js +0 -37
  262. package/src/receiver.js +0 -123
  263. package/src/reflection.js +0 -24
  264. package/src/remote.js +0 -14
  265. package/src/state.js +0 -138
  266. package/src/transition.js +0 -61
  267. package/src/unmanaged.js +0 -7
  268. package/types/atomicity.d.ts +0 -79
  269. package/types/bindings.d.ts +0 -42
  270. package/types/bridges.ts +0 -33
  271. package/types/call.d.ts +0 -13
  272. package/types/component.d.ts +0 -12
  273. package/types/connector.d.ts +0 -18
  274. package/types/context.d.ts +0 -24
  275. package/types/entity.d.ts +0 -46
  276. package/types/event.d.ts +0 -11
  277. package/types/exception.d.ts +0 -4
  278. package/types/extensions.d.ts +0 -73
  279. package/types/guard.d.ts +0 -5
  280. package/types/locator.d.ts +0 -13
  281. package/types/operations.d.ts +0 -8
  282. package/types/outbox.d.ts +0 -53
  283. package/types/query.d.ts +0 -17
  284. package/types/receiver.d.ts +0 -12
  285. package/types/reflection.d.ts +0 -14
  286. package/types/remote.d.ts +0 -19
  287. package/types/state.d.ts +0 -38
  288. package/types/storages.d.ts +0 -96
  289. /package/{src/contract/index.js → source/contract/index.ts} +0 -0
  290. /package/{src/outbox/index.js → source/outbox/index.ts} +0 -0
@@ -1,4 +1,4 @@
1
- import { Exception } from './exception.js'
1
+ import type { Exception } from '../exceptions.js'
2
2
 
3
3
  /**
4
4
  * What a call asks for. `Entity` is the record it is about, which narrows the projection;
@@ -6,24 +6,22 @@ import { Exception } from './exception.js'
6
6
  */
7
7
  export interface Query<Entity = any> {
8
8
  id?: string
9
- ids?: Array<string>
9
+ ids?: string[]
10
10
  criteria?: string
11
11
  search?: string
12
12
  sample?: number
13
13
  omit?: number
14
14
  limit?: number
15
- sort?: Array<string>
15
+ sort?: string[]
16
16
  projection?: Array<string & keyof Entity>
17
17
  version?: number
18
18
  deleted?: boolean
19
19
  }
20
20
 
21
- /**
22
- * Origin of a call. Stamped by the caller, sanitized by the request contract.
23
- */
21
+ /** Origin of a call. Stamped by the framework; whoever reads it takes the keys it knows. */
24
22
  export type Source =
25
- | { namespace: string, component: string, operation: string }
26
- | { namespace: string, component: string, event: string }
23
+ | { namespace: string; component: string; operation: string }
24
+ | { namespace: string; component: string; event: string }
27
25
  | { service: string }
28
26
 
29
27
  export interface Request<Input = any, Entity = any> {
@@ -33,7 +31,8 @@ export interface Request<Input = any, Entity = any> {
33
31
  entity?: Entity
34
32
  authentic?: boolean
35
33
  task?: boolean
36
- telemetry?: string // W3C traceparent
34
+ /** W3C traceparent */
35
+ telemetry?: string
37
36
  source?: Source
38
37
  }
39
38
 
@@ -0,0 +1,9 @@
1
+ /** What a commit publishes: the images of one state change. */
2
+ export interface Event<State = object, Trailers = object> {
3
+ /** the pre-image; null when the entity did not exist before */
4
+ origin: State | null
5
+ state: State
6
+ /** out-of-band values an algorithm wrote into `state._trailers`; must be serializable */
7
+ trailers?: Trailers
8
+ input?: object
9
+ }
@@ -0,0 +1,129 @@
1
+ import type { Readable } from 'node:stream'
2
+ import type { Connector } from '../connector.js'
3
+ import type { Locator } from '../locator.js'
4
+ import type { Row, Storage as Outbox } from './outbox.js'
5
+
6
+ /** the RSQL tree `@rsql/parser` produces; a storage translates it into its own dialect */
7
+ export interface Node {
8
+ type: 'LOGIC' | 'COMPARISON' | 'SELECTOR' | 'VALUE'
9
+ left?: Node
10
+ right?: Node
11
+ operator?: string
12
+ selector?: string
13
+ value?: unknown
14
+ }
15
+
16
+ /** What a storage stores. `id` is the key; the upper-cased names are core's. */
17
+ export interface Record {
18
+ id: string
19
+ VERSION: number
20
+ CREATED?: number
21
+ UPDATED?: number
22
+ /** a tombstone's timestamp; `null` on a live record */
23
+ DELETED?: number | null
24
+ [key: string]: any
25
+ }
26
+
27
+ /** Everything a request query carried that was not a selector. */
28
+ export interface Options {
29
+ omit?: number
30
+ limit?: number
31
+ /** normalised by `query/options.ts` from `'name:asc'` into pairs */
32
+ sort?: Array<[property: string, direction: string]>
33
+ /** always includes `VERSION`, `CREATED`, `UPDATED`, `DELETED` */
34
+ projection?: string[]
35
+ sample?: number
36
+ /** include tombstones; without it an observation answers `null` over one */
37
+ deleted?: boolean
38
+ }
39
+
40
+ /** What core hands a storage, written by `Query.parse`. */
41
+ export interface Query {
42
+ id?: string
43
+ /** many identities at once; `State.objects` inits whichever of them are missing */
44
+ ids?: string[]
45
+ version?: number
46
+ criteria?: Node
47
+ search?: string
48
+ options?: Options
49
+ }
50
+
51
+ /**
52
+ * What core requires of a storage. Every member but `outbox` is called unguarded, so a
53
+ * storage that leaves one out throws where an operation needs it — a storage may serve
54
+ * only some scopes, and says so by failing rather than by declaring less.
55
+ */
56
+ export interface Storage extends Connector {
57
+ /** scope `object` */
58
+ get(query: Query): Promise<Record | null>
59
+
60
+ /** scope `objects` */
61
+ find(query: Query): Promise<Record[]>
62
+
63
+ /** scope `stream` */
64
+ stream(query?: Query): Promise<Readable>
65
+
66
+ /**
67
+ * A transition's commit. `false` is a lost compare-and-swap, not an error. Where `row` is
68
+ * given, it is committed in the same transaction as the record or not at all.
69
+ */
70
+ store(record: Record, row?: Row): Promise<boolean>
71
+
72
+ /** a transition over `objects` */
73
+ massStore(records: Record[], rows?: Row[]): Promise<boolean>
74
+
75
+ /** an assignment; `null` where the query matched nothing */
76
+ upsert(query: Query, changeset: object, row?: Row): Promise<Record | null>
77
+
78
+ /** get-or-create, in one indivisible step */
79
+ ensure(
80
+ query: Query | undefined,
81
+ properties: object,
82
+ record: Record,
83
+ row?: Row
84
+ ): Promise<Record>
85
+
86
+ /** the driver's own handle, which an unmanaged operation is given as its state */
87
+ readonly raw: unknown
88
+
89
+ /**
90
+ * Present only where a row can be committed atomically with the entity. Its absence is
91
+ * what makes the runtime publish inline, so a storage that cannot do this must not offer
92
+ * it: a row written outside the transaction would be a second write with a crash window
93
+ * in front of it, which is the defect the outbox exists to close.
94
+ */
95
+ readonly outbox?: Outbox
96
+
97
+ /**
98
+ * Whether this storage applies `entity.migrations`. Absent is what a storage that does not
99
+ * says, and a component declaring migrations against one is refused at boot rather than
100
+ * starting with a structure nothing has made.
101
+ */
102
+ readonly migrates?: boolean
103
+ }
104
+
105
+ /**
106
+ * The subset of a component's entity declaration a storage reads. Structural, so that core
107
+ * names no type of `@toa.io/norm`, which depends on core.
108
+ */
109
+ export interface Entity {
110
+ associated?: boolean
111
+ custom?: boolean
112
+ /** in the order they are applied; what a step is belongs to the storage that reads it */
113
+ migrations?: Migration[]
114
+ }
115
+
116
+ /** One file of a component's `migrations` directory, named after it. */
117
+ export interface Migration {
118
+ id: string
119
+ steps: unknown[]
120
+ }
121
+
122
+ export interface StorageOptions {
123
+ /** whether the component publishes anything, and so needs a place to commit a row */
124
+ outbox?: boolean
125
+ }
126
+
127
+ export interface Factory {
128
+ storage(locator: Locator, entity: Entity, options?: StorageOptions): Storage
129
+ }
@@ -0,0 +1,8 @@
1
+ import { Operation } from './operation.js'
2
+ import type { Store } from './operation.js'
3
+
4
+ export class Unmanaged extends Operation {
5
+ protected override async acquire(store: Store): Promise<void> {
6
+ store.state = this.scope.storage.raw
7
+ }
8
+ }
@@ -4,7 +4,9 @@ import { generate } from 'randomstring'
4
4
 
5
5
  // noinspection JSCheckFunctionSignatures
6
6
  export const transmission = {
7
- request: mock.fn((request) => ({ [request.invalid ? 'exception' : 'output']: generate() })),
7
+ request: mock.fn((request) => ({
8
+ [request.invalid ? 'exception' : 'output']: generate()
9
+ })),
8
10
  link: mock.fn()
9
11
  }
10
12
 
package/test/call.test.js CHANGED
@@ -1,9 +1,9 @@
1
- import { it, beforeEach, mock } from 'node:test'
1
+ import { it, beforeEach } from 'node:test'
2
2
  import assert from 'node:assert/strict'
3
3
  import { isDeepStrictEqual } from 'node:util'
4
4
 
5
5
  import * as fixtures from './call.fixtures.js'
6
- import { Call } from '../src/call.js'
6
+ import { Call } from '../source/call.js'
7
7
 
8
8
  let call
9
9
 
@@ -14,7 +14,13 @@ beforeEach(() => {
14
14
  })
15
15
 
16
16
  it('should depend on transmission', () => {
17
- assert.ok(((invocation) => invocation.arguments.length === 1 && isDeepStrictEqual(invocation.arguments[0], call))(fixtures.transmission.link.mock.calls.at(-1) ?? { arguments: [] }))
17
+ assert.ok(
18
+ ((invocation) =>
19
+ invocation.arguments.length === 1 &&
20
+ isDeepStrictEqual(invocation.arguments[0], call))(
21
+ fixtures.transmission.link.mock.calls.at(-1) ?? { arguments: [] }
22
+ )
23
+ )
18
24
  })
19
25
 
20
26
  it('should call transmission', async () => {
@@ -22,7 +28,13 @@ it('should call transmission', async () => {
22
28
 
23
29
  await call.invoke(request)
24
30
 
25
- assert.ok(fixtures.transmission.request.mock.calls.some((invocation) => invocation.arguments.length === 1 && isDeepStrictEqual(invocation.arguments[0], request)))
31
+ assert.ok(
32
+ fixtures.transmission.request.mock.calls.some(
33
+ (invocation) =>
34
+ invocation.arguments.length === 1 &&
35
+ isDeepStrictEqual(invocation.arguments[0], request)
36
+ )
37
+ )
26
38
  })
27
39
 
28
40
  it('should fit request', async () => {
@@ -30,7 +42,13 @@ it('should fit request', async () => {
30
42
 
31
43
  await call.invoke(request)
32
44
 
33
- assert.ok(((invocation) => invocation.arguments.length === 1 && isDeepStrictEqual(invocation.arguments[0], request))(fixtures.contract.fit.mock.calls.at(-1) ?? { arguments: [] }))
45
+ assert.ok(
46
+ ((invocation) =>
47
+ invocation.arguments.length === 1 &&
48
+ isDeepStrictEqual(invocation.arguments[0], request))(
49
+ fixtures.contract.fit.mock.calls.at(-1) ?? { arguments: [] }
50
+ )
51
+ )
34
52
  })
35
53
 
36
54
  it('should return reply', async () => {
@@ -44,10 +62,13 @@ it('should return reply', async () => {
44
62
  it('should throw received exceptions', async () => {
45
63
  const request = fixtures.request().bad
46
64
 
47
- await assert.rejects(call.invoke(request), (error) => { assert.notStrictEqual(error, undefined); return true })
65
+ await assert.rejects(call.invoke(request), (error) => {
66
+ assert.notStrictEqual(error, undefined)
67
+ return true
68
+ })
48
69
  })
49
70
 
50
- function resetCalls (target = [assert, fixtures], seen = new Set()) {
71
+ function resetCalls(target = [assert, fixtures], seen = new Set()) {
51
72
  if (target === null || typeof target !== 'object' || seen.has(target)) return
52
73
 
53
74
  seen.add(target)
@@ -1,9 +1,8 @@
1
- import { describe, it, beforeEach, mock } from 'node:test'
1
+ import { describe, it, beforeEach } from 'node:test'
2
2
  import assert from 'node:assert/strict'
3
3
  import { isDeepStrictEqual } from 'node:util'
4
4
 
5
- import { Component } from '../src/component.js'
6
- import { codes } from '../src/exceptions.js'
5
+ import { Component } from '../source/component.js'
7
6
  import * as fixtures from './component.fixtures.js'
8
7
  import { AssertionError } from 'node:assert'
9
8
 
@@ -31,7 +30,13 @@ describe('Invocations', () => {
31
30
  const query = { test: Math.random() }
32
31
  await component.invoke(name, { input, query })
33
32
 
34
- assert.ok(invocation.invoke.mock.calls.some((call) => call.arguments.length === 1 && isDeepStrictEqual(call.arguments[0], { input, query })))
33
+ assert.ok(
34
+ invocation.invoke.mock.calls.some(
35
+ (call) =>
36
+ call.arguments.length === 1 &&
37
+ isDeepStrictEqual(call.arguments[0], { input, query })
38
+ )
39
+ )
35
40
  })
36
41
 
37
42
  it('should return io', async () => {
@@ -41,7 +46,7 @@ describe('Invocations', () => {
41
46
  })
42
47
  })
43
48
 
44
- function resetCalls (target = [assert, fixtures], seen = new Set()) {
49
+ function resetCalls(target = [assert, fixtures], seen = new Set()) {
45
50
  if (target === null || typeof target !== 'object' || seen.has(target)) return
46
51
 
47
52
  seen.add(target)
@@ -1,34 +1,34 @@
1
1
  import { random, timeout } from '@toa.io/generic'
2
- import { Connector } from '../src/connector.js'
2
+ import { Connector } from '../source/connector.js'
3
3
 
4
4
  export class TestConnector extends Connector {
5
5
  #label
6
6
  #seq
7
7
 
8
- constructor (label, seq) {
8
+ constructor(label, seq) {
9
9
  super()
10
10
 
11
11
  this.#seq = seq
12
12
  this.#label = label
13
13
  }
14
14
 
15
- async open () {
15
+ async open() {
16
16
  await timeout(random(10))
17
17
  this.#seq.push(`+${this.#label}`)
18
18
  }
19
19
 
20
- async close () {
20
+ async close() {
21
21
  await timeout(random(10))
22
22
  this.#seq.push(`-${this.#label}`)
23
23
  }
24
24
 
25
- async dispose () {
25
+ async dispose() {
26
26
  this.#seq.push(`*${this.#label}`)
27
27
  }
28
28
  }
29
29
 
30
30
  export class FailingConnector extends Connector {
31
- async open () {
31
+ async open() {
32
32
  await timeout(random(10))
33
33
  throw new Error('FailingConnector')
34
34
  }
@@ -36,7 +36,7 @@ export class FailingConnector extends Connector {
36
36
 
37
37
  /** Stands for a connector waiting for something that never arrives. */
38
38
  export class StuckConnector extends Connector {
39
- async open () {
39
+ async open() {
40
40
  return new Promise(() => undefined)
41
41
  }
42
42
  }
@@ -87,7 +87,10 @@ describe('dependencies', () => {
87
87
  })
88
88
 
89
89
  it('should throw on empty array', async () => {
90
- assert.throws(() => a.depends([]), (error) => /must not be empty/.test(error.message))
90
+ assert.throws(
91
+ () => a.depends([]),
92
+ (error) => /must not be empty/.test(error.message)
93
+ )
91
94
  })
92
95
 
93
96
  it('should await 2-way dependencies', async () => {
@@ -143,7 +146,17 @@ describe('dependencies', () => {
143
146
 
144
147
  await b.disconnect()
145
148
 
146
- assert.deepStrictEqual(sequence, ['+c', '+a', '+b', '-a', '*a', '-b', '-c', '*c', '*b'])
149
+ assert.deepStrictEqual(sequence, [
150
+ '+c',
151
+ '+a',
152
+ '+b',
153
+ '-a',
154
+ '*a',
155
+ '-b',
156
+ '-c',
157
+ '*c',
158
+ '*b'
159
+ ])
147
160
  })
148
161
 
149
162
  it('should throw if depends not on Connector', async () => {
@@ -1,6 +1,6 @@
1
1
  import { mock } from 'node:test'
2
2
 
3
- import { Connector } from '../src/connector.js'
3
+ import { Connector } from '../source/connector.js'
4
4
 
5
5
  export const local = {
6
6
  link: mock.fn()
@@ -1,10 +1,10 @@
1
- import { describe, it, beforeEach, mock } from 'node:test'
1
+ import { describe, it, beforeEach } from 'node:test'
2
2
  import assert from 'node:assert/strict'
3
3
 
4
4
  import * as fixtures from './context.fixtures.js'
5
- import { Context } from '../src/context.js'
5
+ import { Context } from '../source/context.js'
6
6
 
7
- /** @type {toa.core.Context} */
7
+ /** @type {import('@toa.io/core').Context} */
8
8
  let context
9
9
 
10
10
  beforeEach(() => {
@@ -29,7 +29,7 @@ describe('call', () => {
29
29
  })
30
30
  })
31
31
 
32
- function resetCalls (target = [assert, fixtures], seen = new Set()) {
32
+ function resetCalls(target = [assert, fixtures], seen = new Set()) {
33
33
  if (target === null || typeof target !== 'object' || seen.has(target)) return
34
34
 
35
35
  seen.add(target)
@@ -4,7 +4,7 @@ import { isDeepStrictEqual } from 'node:util'
4
4
 
5
5
  import { generate } from 'randomstring'
6
6
 
7
- import { Contract } from '../../src/contract/contract.js'
7
+ import { Contract } from '../../source/contract/contract.js'
8
8
  import * as fixtures from './contract.fixtures.js'
9
9
 
10
10
  let contract
@@ -18,7 +18,11 @@ it('should fit value', () => {
18
18
 
19
19
  contract.fit(value)
20
20
 
21
- assert.ok(fixtures.schema.fit.mock.calls.some((call) => call.arguments.length === 1 && isDeepStrictEqual(call.arguments[0], value)))
21
+ assert.ok(
22
+ fixtures.schema.fit.mock.calls.some(
23
+ (call) => call.arguments.length === 1 && isDeepStrictEqual(call.arguments[0], value)
24
+ )
25
+ )
22
26
  })
23
27
 
24
28
  it('should throw on invalid value', () => {
@@ -1,9 +1,8 @@
1
1
  import { mock } from 'node:test'
2
2
 
3
3
  import { generate } from 'randomstring'
4
- import { readFileSync } from 'node:fs'
5
- import { load as parseYAML } from 'js-yaml'
6
- import { resolve } from 'path'
4
+
5
+ import { query as querySchema } from '../../source/contract/schemas.js'
7
6
 
8
7
  // noinspection JSCheckFunctionSignatures
9
8
  export const schema = {
@@ -21,7 +20,7 @@ export const schemas = {
21
20
  type: 'object',
22
21
  properties: {
23
22
  input: { type: 'null' },
24
- query: parseYAML(readFileSync(resolve(import.meta.dirname, '../../src/contract/schemas/query.yaml'), 'utf8')),
23
+ query: querySchema,
25
24
  authentic: { type: 'boolean' }
26
25
  },
27
26
  additionalProperties: true
@@ -6,8 +6,8 @@ import { isDeepStrictEqual } from 'node:util'
6
6
  import clone from 'clone-deep'
7
7
  import { generate } from 'randomstring'
8
8
 
9
- import { Request } from '../../src/contract/request.js'
10
- import { Contract } from '../../src/contract/contract.js'
9
+ import { Request } from '../../source/contract/request.js'
10
+ import { Contract } from '../../source/contract/contract.js'
11
11
  import * as fixtures from './contract.fixtures.js'
12
12
 
13
13
  // the base is real; what it was constructed with and told to fit is observable
@@ -22,7 +22,7 @@ beforeEach(() => {
22
22
  contract = new Request(fixtures.schema, {})
23
23
  })
24
24
 
25
- const dummy = { schema: { properties: {} } }
25
+ const dummy = { properties: {} }
26
26
 
27
27
  it('should extend Conditions', () => {
28
28
  assert.ok(contract instanceof Contract)
@@ -34,8 +34,11 @@ it('should fit request', () => {
34
34
 
35
35
  contract.fit(request)
36
36
 
37
- assert.ok(fit.mock.calls.some((call) =>
38
- call.this === contract && isDeepStrictEqual(call.arguments[0], request)))
37
+ assert.ok(
38
+ fit.mock.calls.some(
39
+ (call) => call.this === contract && isDeepStrictEqual(call.arguments[0], request)
40
+ )
41
+ )
39
42
  })
40
43
 
41
44
  describe('schema', () => {
@@ -70,21 +73,45 @@ describe('schema', () => {
70
73
 
71
74
  it('should require query if declaration.query is true', () => {
72
75
  schema.required = ['query']
73
- assert.ok(['query'].every((item) => Request.schema({ query: true }, dummy).required.some((candidate) => isDeepStrictEqual(candidate, item))))
76
+ assert.ok(
77
+ ['query'].every((item) =>
78
+ Request.schema({ query: true }, dummy).required.some((candidate) =>
79
+ isDeepStrictEqual(candidate, item)
80
+ )
81
+ )
82
+ )
74
83
  })
75
84
 
76
85
  it('should forbid projection for non observations', () => {
77
- assert.strictEqual(Request.schema({ type: 'transition' }, dummy).properties.query.properties.projection, undefined)
78
-
79
- assert.strictEqual(Request.schema({ type: 'assignment' }, dummy).properties.query.properties.projection, undefined)
80
-
81
- assert.notStrictEqual(Request.schema({ type: 'observation' }, dummy).properties.query.properties.projection, undefined)
86
+ assert.strictEqual(
87
+ Request.schema({ type: 'transition' }, dummy).properties.query.properties
88
+ .projection,
89
+ undefined
90
+ )
91
+
92
+ assert.strictEqual(
93
+ Request.schema({ type: 'assignment' }, dummy).properties.query.properties
94
+ .projection,
95
+ undefined
96
+ )
97
+
98
+ assert.notStrictEqual(
99
+ Request.schema({ type: 'observation' }, dummy).properties.query.properties
100
+ .projection,
101
+ undefined
102
+ )
82
103
  })
83
104
 
84
105
  it('should forbid version for observations', () => {
85
- assert.notStrictEqual(Request.schema({ type: 'transition' }, dummy).properties.query.properties.version, undefined)
86
-
87
- assert.strictEqual(Request.schema({ type: 'observation' }, dummy).properties.query.properties.version, undefined)
106
+ assert.notStrictEqual(
107
+ Request.schema({ type: 'transition' }, dummy).properties.query.properties.version,
108
+ undefined
109
+ )
110
+
111
+ assert.strictEqual(
112
+ Request.schema({ type: 'observation' }, dummy).properties.query.properties.version,
113
+ undefined
114
+ )
88
115
  })
89
116
 
90
117
  it('should allow omit, limit only for set observations', () => {
@@ -94,18 +121,24 @@ describe('schema', () => {
94
121
  assert.strictEqual(transition.omit, undefined)
95
122
  assert.strictEqual(transition.limit, undefined)
96
123
 
97
- const object = Request.schema({
98
- type: 'observation',
99
- scope: 'object'
100
- }, dummy).properties.query.properties
124
+ const object = Request.schema(
125
+ {
126
+ type: 'observation',
127
+ scope: 'object'
128
+ },
129
+ dummy
130
+ ).properties.query.properties
101
131
 
102
132
  assert.strictEqual(object.omit, undefined)
103
133
  assert.strictEqual(object.limit, undefined)
104
134
 
105
- const objects = Request.schema({
106
- type: 'observation',
107
- scope: 'objects'
108
- }, dummy).properties.query.properties
135
+ const objects = Request.schema(
136
+ {
137
+ type: 'observation',
138
+ scope: 'objects'
139
+ },
140
+ dummy
141
+ ).properties.query.properties
109
142
 
110
143
  assert.notStrictEqual(objects.omit, undefined)
111
144
  assert.notStrictEqual(objects.limit, undefined)
@@ -113,16 +146,13 @@ describe('schema', () => {
113
146
  })
114
147
 
115
148
  describe('source', () => {
116
-
117
-
118
149
  const compile = (definition, entity) =>
119
- schemas.schema(Request.schema(definition, entity), { removeAdditional: true })
150
+ schemas.schema(Request.schema(definition, entity))
120
151
 
121
- it('should declare source', () => {
152
+ it('should not hold source to a schema', () => {
122
153
  const schema = Request.schema({}, dummy)
123
154
 
124
- assert.notStrictEqual(schema.properties.source, undefined)
125
- assert.deepStrictEqual(schema.properties.source.additionalProperties, false)
155
+ assert.strictEqual(schema.properties.source, undefined)
126
156
  })
127
157
 
128
158
  it('should pass known source variants', () => {
@@ -140,18 +170,22 @@ describe('source', () => {
140
170
  }
141
171
  })
142
172
 
143
- // `source` crosses the wire and keys the introspection map, so whatever
144
- // a peer adds to it must not survive
145
- it('should strip unknown source properties', () => {
173
+ // whatever a peer puts beside the keys this release knows travels through: what reads
174
+ // `source` takes the keys it knows, and the contract does not fail a call over the rest
175
+ it('should pass a source a peer added to', () => {
146
176
  const schema = compile({}, undefined)
147
- const request = { input: null, query: null, source: { service: 'exposition', evil: 'x' } }
177
+ const request = {
178
+ input: null,
179
+ query: null,
180
+ source: { service: 'exposition', mystery: 'x' }
181
+ }
148
182
 
149
183
  assert.deepStrictEqual(schema.fit(request), null)
150
- assert.deepStrictEqual(request.source, { service: 'exposition' })
184
+ assert.deepStrictEqual(request.source, { service: 'exposition', mystery: 'x' })
151
185
  })
152
186
  })
153
187
 
154
- function resetCalls (target = [assert, clone, fixtures, dummy], seen = new Set()) {
188
+ function resetCalls(target = [assert, clone, fixtures, dummy], seen = new Set()) {
155
189
  if (target === null || typeof target !== 'object' || seen.has(target)) return
156
190
 
157
191
  seen.add(target)
@@ -3,8 +3,8 @@ import assert from 'node:assert/strict'
3
3
  import { isDeepStrictEqual } from 'node:util'
4
4
 
5
5
  import { console } from 'openspan'
6
- import { Connector } from '../src/connector.js'
7
- import { Discovery } from '../src/discovery.js'
6
+ import { Connector } from '../source/connector.js'
7
+ import { Discovery } from '../source/discovery.js'
8
8
 
9
9
  class Lookup extends Connector {
10
10
  invoke = mock.fn(async () => ({ operations: {} }))
@@ -60,5 +60,12 @@ it('should keep warning while a lookup is unanswered', async () => {
60
60
 
61
61
  assert.strictEqual(warn.mock.callCount(), 2)
62
62
 
63
- assert.ok(((call) => call.arguments.length === 2 && isDeepStrictEqual(call.arguments[0], 'Waiting for lookup response') && isDeepStrictEqual(call.arguments[1], { component: locator.id, waiting: 10 }))(warn.mock.calls.at(-1) ?? { arguments: [] }))
63
+ assert.ok(
64
+ ((call) =>
65
+ call.arguments.length === 2 &&
66
+ isDeepStrictEqual(call.arguments[0], 'Waiting for lookup response') &&
67
+ isDeepStrictEqual(call.arguments[1], { component: locator.id, waiting: 10 }))(
68
+ warn.mock.calls.at(-1) ?? { arguments: [] }
69
+ )
70
+ )
64
71
  })