@toa.io/core 1.0.0-alpha.283 → 1.0.0-alpha.286

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/package.json +19 -7
  2. package/source/assignment.ts +27 -0
  3. package/{src/call.js → source/call.ts} +16 -21
  4. package/{src/cascade.js → source/cascade.ts} +6 -8
  5. package/{src/component.js → source/component.ts} +27 -25
  6. package/{src/composition.js → source/composition.ts} +9 -3
  7. package/{src/connector.js → source/connector.ts} +36 -55
  8. package/source/context.ts +67 -0
  9. package/source/contract/contract.ts +23 -0
  10. package/{src/contract/reply.js → source/contract/reply.ts} +26 -19
  11. package/source/contract/request.ts +85 -0
  12. package/source/contract/schemas.ts +39 -0
  13. package/{src/discovery.js → source/discovery.ts} +15 -8
  14. package/source/effect.ts +14 -0
  15. package/{src/emission.js → source/emission.ts} +5 -3
  16. package/source/entities/changeset.ts +37 -0
  17. package/source/entities/entity.ts +137 -0
  18. package/source/entities/factory.ts +52 -0
  19. package/source/entities/index.ts +4 -0
  20. package/{src/entities/newid.js → source/entities/newid.ts} +1 -1
  21. package/{src/entities/set.js → source/entities/set.ts} +8 -5
  22. package/source/event.ts +57 -0
  23. package/{src/exceptions.js → source/exceptions.ts} +52 -33
  24. package/source/exposition.ts +33 -0
  25. package/source/guard.ts +16 -0
  26. package/{src/index.js → source/index.ts} +8 -0
  27. package/{src/locator.js → source/locator.ts} +17 -30
  28. package/source/observation.ts +16 -0
  29. package/source/operation.ts +138 -0
  30. package/{src/outbox/outbox.js → source/outbox/outbox.ts} +95 -51
  31. package/source/query/criteria.ts +90 -0
  32. package/source/query/options.ts +35 -0
  33. package/{src/query.js → source/query.ts} +14 -17
  34. package/source/receiver.ts +120 -0
  35. package/source/reflection.ts +20 -0
  36. package/source/remote.ts +20 -0
  37. package/source/state.ts +179 -0
  38. package/source/transition.ts +79 -0
  39. package/{src/transmission.js → source/transmission.ts} +8 -8
  40. package/source/types/atomicity.ts +63 -0
  41. package/source/types/bindings.ts +59 -0
  42. package/source/types/bridges.ts +54 -0
  43. package/source/types/extensions.ts +89 -0
  44. package/{types → source/types}/index.ts +9 -16
  45. package/{types/message.d.ts → source/types/message.ts} +2 -1
  46. package/source/types/operations.ts +31 -0
  47. package/source/types/outbox.ts +35 -0
  48. package/source/types/receiver.ts +10 -0
  49. package/{types/request.d.ts → source/types/request.ts} +8 -9
  50. package/source/types/state.ts +9 -0
  51. package/source/types/storages.ts +129 -0
  52. package/source/unmanaged.ts +8 -0
  53. package/test/call.fixtures.js +3 -1
  54. package/test/call.test.js +28 -7
  55. package/test/component.test.js +10 -5
  56. package/test/connector.fixtures.js +7 -7
  57. package/test/connector.test.js +15 -2
  58. package/test/context.fixtures.js +1 -1
  59. package/test/context.test.js +4 -4
  60. package/test/contract/conditions.test.js +6 -2
  61. package/test/contract/contract.fixtures.js +3 -4
  62. package/test/contract/request.test.js +68 -34
  63. package/test/discovery.test.js +10 -3
  64. package/test/emission.test.js +16 -8
  65. package/test/entities/entity.fixtures.js +5 -6
  66. package/test/entities/entity.test.js +54 -20
  67. package/test/entities/factory.fixtures.js +11 -10
  68. package/test/entities/factory.test.js +47 -10
  69. package/test/entities/set.test.js +1 -1
  70. package/test/event.test.js +29 -11
  71. package/test/locator.test.js +10 -4
  72. package/test/outbox.test.js +95 -14
  73. package/test/query.test.js +76 -7
  74. package/test/receiver.fixtures.js +2 -2
  75. package/test/receiver.test.js +53 -27
  76. package/test/reflection.test.js +3 -3
  77. package/test/state.fixtures.js +10 -3
  78. package/test/state.test.js +77 -14
  79. package/test/transmission.test.js +25 -12
  80. package/transpiled/assignment.d.ts +6 -0
  81. package/transpiled/assignment.js +20 -0
  82. package/transpiled/assignment.js.map +1 -0
  83. package/transpiled/call.d.ts +10 -0
  84. package/transpiled/call.js +52 -0
  85. package/transpiled/call.js.map +1 -0
  86. package/transpiled/cascade.d.ts +7 -0
  87. package/transpiled/cascade.js +28 -0
  88. package/transpiled/cascade.js.map +1 -0
  89. package/transpiled/component.d.ts +15 -0
  90. package/transpiled/component.js +61 -0
  91. package/transpiled/component.js.map +1 -0
  92. package/transpiled/composition.d.ts +6 -0
  93. package/transpiled/composition.js +23 -0
  94. package/transpiled/composition.js.map +1 -0
  95. package/transpiled/connector.d.ts +47 -0
  96. package/transpiled/connector.js +143 -0
  97. package/transpiled/connector.js.map +1 -0
  98. package/transpiled/context.d.ts +18 -0
  99. package/transpiled/context.js +41 -0
  100. package/transpiled/context.js.map +1 -0
  101. package/transpiled/contract/contract.d.ts +10 -0
  102. package/transpiled/contract/contract.js +14 -0
  103. package/transpiled/contract/contract.js.map +1 -0
  104. package/transpiled/contract/index.d.ts +2 -0
  105. package/transpiled/contract/index.js +3 -0
  106. package/transpiled/contract/index.js.map +1 -0
  107. package/transpiled/contract/reply.d.ts +7 -0
  108. package/transpiled/contract/reply.js +48 -0
  109. package/transpiled/contract/reply.js.map +1 -0
  110. package/transpiled/contract/request.d.ts +24 -0
  111. package/transpiled/contract/request.js +59 -0
  112. package/transpiled/contract/request.js.map +1 -0
  113. package/transpiled/contract/schemas.d.ts +4 -0
  114. package/transpiled/contract/schemas.js +36 -0
  115. package/transpiled/contract/schemas.js.map +1 -0
  116. package/transpiled/discovery.d.ts +12 -0
  117. package/transpiled/discovery.js +34 -0
  118. package/transpiled/discovery.js.map +1 -0
  119. package/transpiled/effect.d.ts +5 -0
  120. package/transpiled/effect.js +11 -0
  121. package/transpiled/effect.js.map +1 -0
  122. package/transpiled/emission.d.ts +8 -0
  123. package/transpiled/emission.js +14 -0
  124. package/transpiled/emission.js.map +1 -0
  125. package/transpiled/entities/changeset.d.ts +10 -0
  126. package/transpiled/entities/changeset.js +27 -0
  127. package/transpiled/entities/changeset.js.map +1 -0
  128. package/transpiled/entities/entity.d.ts +19 -0
  129. package/transpiled/entities/entity.js +109 -0
  130. package/transpiled/entities/entity.js.map +1 -0
  131. package/transpiled/entities/factory.d.ts +22 -0
  132. package/transpiled/entities/factory.js +34 -0
  133. package/transpiled/entities/factory.js.map +1 -0
  134. package/transpiled/entities/index.d.ts +4 -0
  135. package/transpiled/entities/index.js +5 -0
  136. package/transpiled/entities/index.js.map +1 -0
  137. package/transpiled/entities/newid.d.ts +1 -0
  138. package/transpiled/entities/newid.js +7 -0
  139. package/transpiled/entities/newid.js.map +1 -0
  140. package/transpiled/entities/set.d.ts +10 -0
  141. package/transpiled/entities/set.js +19 -0
  142. package/transpiled/entities/set.js.map +1 -0
  143. package/transpiled/event.d.ts +14 -0
  144. package/transpiled/event.js +38 -0
  145. package/transpiled/event.js.map +1 -0
  146. package/transpiled/exceptions.d.ts +61 -0
  147. package/transpiled/exceptions.js +93 -0
  148. package/transpiled/exceptions.js.map +1 -0
  149. package/transpiled/exposition.d.ts +18 -0
  150. package/transpiled/exposition.js +18 -0
  151. package/transpiled/exposition.js.map +1 -0
  152. package/transpiled/guard.d.ts +7 -0
  153. package/transpiled/guard.js +12 -0
  154. package/transpiled/guard.js.map +1 -0
  155. package/transpiled/index.d.ts +35 -0
  156. package/transpiled/index.js +29 -0
  157. package/transpiled/index.js.map +1 -0
  158. package/transpiled/locator.d.ts +12 -0
  159. package/transpiled/locator.js +32 -0
  160. package/transpiled/locator.js.map +1 -0
  161. package/transpiled/observation.d.ts +5 -0
  162. package/transpiled/observation.js +12 -0
  163. package/transpiled/observation.js.map +1 -0
  164. package/transpiled/operation.d.ts +49 -0
  165. package/transpiled/operation.js +77 -0
  166. package/transpiled/operation.js.map +1 -0
  167. package/transpiled/outbox/index.d.ts +1 -0
  168. package/transpiled/outbox/index.js +2 -0
  169. package/transpiled/outbox/index.js.map +1 -0
  170. package/transpiled/outbox/outbox.d.ts +46 -0
  171. package/transpiled/outbox/outbox.js +267 -0
  172. package/transpiled/outbox/outbox.js.map +1 -0
  173. package/transpiled/query/criteria.d.ts +6 -0
  174. package/transpiled/query/criteria.js +74 -0
  175. package/transpiled/query/criteria.js.map +1 -0
  176. package/transpiled/query/options.d.ts +3 -0
  177. package/transpiled/query/options.js +27 -0
  178. package/transpiled/query/options.js.map +1 -0
  179. package/transpiled/query.d.ts +9 -0
  180. package/transpiled/query.js +57 -0
  181. package/transpiled/query.js.map +1 -0
  182. package/transpiled/receiver.d.ts +20 -0
  183. package/transpiled/receiver.js +92 -0
  184. package/transpiled/receiver.js.map +1 -0
  185. package/transpiled/reflection.d.ts +9 -0
  186. package/transpiled/reflection.js +13 -0
  187. package/transpiled/reflection.js.map +1 -0
  188. package/transpiled/remote.d.ts +10 -0
  189. package/transpiled/remote.js +12 -0
  190. package/transpiled/remote.js.map +1 -0
  191. package/transpiled/state.d.ts +33 -0
  192. package/transpiled/state.js +126 -0
  193. package/transpiled/state.js.map +1 -0
  194. package/transpiled/transition.d.ts +14 -0
  195. package/transpiled/transition.js +51 -0
  196. package/transpiled/transition.js.map +1 -0
  197. package/transpiled/transmission.d.ts +8 -0
  198. package/transpiled/transmission.js +30 -0
  199. package/transpiled/transmission.js.map +1 -0
  200. package/transpiled/types/atomicity.d.ts +56 -0
  201. package/transpiled/types/atomicity.js +2 -0
  202. package/transpiled/types/atomicity.js.map +1 -0
  203. package/transpiled/types/bindings.d.ts +42 -0
  204. package/transpiled/types/bindings.js +2 -0
  205. package/transpiled/types/bindings.js.map +1 -0
  206. package/transpiled/types/bridges.d.ts +38 -0
  207. package/transpiled/types/bridges.js +2 -0
  208. package/transpiled/types/bridges.js.map +1 -0
  209. package/transpiled/types/extensions.d.ts +62 -0
  210. package/transpiled/types/extensions.js +2 -0
  211. package/transpiled/types/extensions.js.map +1 -0
  212. package/transpiled/types/index.d.ts +13 -0
  213. package/transpiled/types/index.js +8 -0
  214. package/transpiled/types/index.js.map +1 -0
  215. package/transpiled/types/message.d.ts +5 -0
  216. package/transpiled/types/message.js +2 -0
  217. package/transpiled/types/message.js.map +1 -0
  218. package/transpiled/types/operations.d.ts +15 -0
  219. package/transpiled/types/operations.js +2 -0
  220. package/transpiled/types/operations.js.map +1 -0
  221. package/transpiled/types/outbox.d.ts +28 -0
  222. package/transpiled/types/outbox.js +2 -0
  223. package/transpiled/types/outbox.js.map +1 -0
  224. package/transpiled/types/receiver.d.ts +9 -0
  225. package/transpiled/types/receiver.js +2 -0
  226. package/transpiled/types/receiver.js.map +1 -0
  227. package/transpiled/types/request.d.ts +55 -0
  228. package/transpiled/types/request.js +2 -0
  229. package/transpiled/types/request.js.map +1 -0
  230. package/transpiled/types/state.d.ts +9 -0
  231. package/transpiled/types/state.js +2 -0
  232. package/transpiled/types/state.js.map +1 -0
  233. package/transpiled/types/storages.d.ts +106 -0
  234. package/transpiled/types/storages.js +2 -0
  235. package/transpiled/types/storages.js.map +1 -0
  236. package/transpiled/unmanaged.d.ts +5 -0
  237. package/transpiled/unmanaged.js +7 -0
  238. package/transpiled/unmanaged.js.map +1 -0
  239. package/tsconfig.json +10 -0
  240. package/tsconfig.tsbuildinfo +1 -0
  241. package/CHANGELOG.md +0 -180
  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 -37
  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 -58
  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 -18
  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
@@ -4,8 +4,8 @@ import { isDeepStrictEqual } from 'node:util'
4
4
 
5
5
  import clone from 'clone-deep'
6
6
 
7
- import { Connector } from '../src/connector.js'
8
- import { Event } from '../src/event.js'
7
+ import { Connector } from '../source/connector.js'
8
+ import { Event } from '../source/event.js'
9
9
  import * as fixtures from './event.fixtures.js'
10
10
 
11
11
  let event, emit
@@ -13,9 +13,10 @@ let event, emit
13
13
  // the fixtures are not connectors, so the dependency is recorded rather than linked
14
14
  const depends = mock.method(Connector.prototype, 'depends', () => undefined)
15
15
 
16
- const dependencies = (instance) => depends.mock.calls
17
- .filter((call) => call.this === instance)
18
- .map((call) => call.arguments[0])
16
+ const dependencies = (instance) =>
17
+ depends.mock.calls
18
+ .filter((call) => call.this === instance)
19
+ .map((call) => call.arguments[0])
19
20
 
20
21
  beforeEach(() => {
21
22
  resetCalls()
@@ -35,7 +36,9 @@ it('should depend on bridge if provided', () => {
35
36
 
36
37
  const bridgeless = new Event(fixtures.definition, fixtures.binding)
37
38
 
38
- assert.ok(!dependencies(bridgeless).some((one) => isDeepStrictEqual(one, fixtures.bridge)))
39
+ assert.ok(
40
+ !dependencies(bridgeless).some((one) => isDeepStrictEqual(one, fixtures.bridge))
41
+ )
39
42
  })
40
43
 
41
44
  describe('condition', () => {
@@ -43,7 +46,13 @@ describe('condition', () => {
43
46
  it('should call condition', async () => {
44
47
  await emit()
45
48
 
46
- assert.ok(fixtures.bridge.condition.mock.calls.some((call) => call.arguments.length === 1 && isDeepStrictEqual(call.arguments[0], fixtures.event)))
49
+ assert.ok(
50
+ fixtures.bridge.condition.mock.calls.some(
51
+ (call) =>
52
+ call.arguments.length === 1 &&
53
+ isDeepStrictEqual(call.arguments[0], fixtures.event)
54
+ )
55
+ )
47
56
  })
48
57
 
49
58
  it('should emit if condition returns true', async () => {
@@ -74,9 +83,15 @@ describe('condition', () => {
74
83
  })
75
84
 
76
85
  it('should not call condition', async () => {
77
- await event.emit(fixtures.event.origin, fixtures.event.changeset, fixtures.event.state)
86
+ await event.emit(
87
+ fixtures.event.origin,
88
+ fixtures.event.changeset,
89
+ fixtures.event.state
90
+ )
78
91
 
79
- assert.ok(!(fixtures.bridge.condition.mock.calls.some((call) => call.arguments.length === 0)))
92
+ assert.ok(
93
+ !fixtures.bridge.condition.mock.calls.some((call) => call.arguments.length === 0)
94
+ )
80
95
 
81
96
  const payload = await fixtures.bridge.payload.mock.calls[0].result
82
97
  assertEmitted(payload)
@@ -119,7 +134,10 @@ describe('payload', () => {
119
134
  })
120
135
  })
121
136
 
122
- function resetCalls (target = [assert, clone, fixtures, depends, dependencies], seen = new Set()) {
137
+ function resetCalls(
138
+ target = [assert, clone, fixtures, depends, dependencies],
139
+ seen = new Set()
140
+ ) {
123
141
  if (target === null || typeof target !== 'object' || seen.has(target)) return
124
142
 
125
143
  seen.add(target)
@@ -130,7 +148,7 @@ function resetCalls (target = [assert, clone, fixtures, depends, dependencies],
130
148
  }
131
149
 
132
150
  /** The message carries the payload and a telemetry token generated per emission. */
133
- function assertEmitted (payload) {
151
+ function assertEmitted(payload) {
134
152
  const emitted = fixtures.binding.emit.mock.calls
135
153
  .map((call) => call.arguments[0])
136
154
  .filter((message) => isDeepStrictEqual(message.payload, payload))
@@ -2,7 +2,7 @@ import { describe, it, beforeEach } from 'node:test'
2
2
  import assert from 'node:assert/strict'
3
3
 
4
4
  import { generate } from 'randomstring'
5
- import { Locator } from '../src/locator.js'
5
+ import { Locator } from '../source/locator.js'
6
6
 
7
7
  /** @type {string} */
8
8
  let name
@@ -10,7 +10,7 @@ let name
10
10
  /** @type {string} */
11
11
  let namespace
12
12
 
13
- /** @type {toa.core.Locator} */
13
+ /** @type {import('@toa.io/core').Locator} */
14
14
  let locator
15
15
 
16
16
  beforeEach(() => {
@@ -34,7 +34,10 @@ it('should expose id, label', () => {
34
34
  })
35
35
 
36
36
  it('should expose uppercase', () => {
37
- assert.deepStrictEqual(locator.uppercase, (locator.namespace + '_' + locator.name).toUpperCase())
37
+ assert.deepStrictEqual(
38
+ locator.uppercase,
39
+ (locator.namespace + '_' + locator.name).toUpperCase()
40
+ )
38
41
  })
39
42
 
40
43
  it('should throw if name is undefined', () => {
@@ -51,7 +54,10 @@ it('should expose host', () => {
51
54
  it('should expose host with given prefix', () => {
52
55
  const prefix = generate()
53
56
 
54
- assert.deepStrictEqual(locator.hostname(prefix), (prefix + '-' + namespace + '-' + name).toLowerCase())
57
+ assert.deepStrictEqual(
58
+ locator.hostname(prefix),
59
+ (prefix + '-' + namespace + '-' + name).toLowerCase()
60
+ )
55
61
  })
56
62
 
57
63
  describe('global', () => {
@@ -1,9 +1,9 @@
1
1
  import { it, beforeEach, afterEach, mock } from 'node:test'
2
2
  import assert from 'node:assert/strict'
3
3
 
4
- import { Outbox } from '../src/outbox/index.js'
4
+ import { Outbox } from '../source/outbox/index.js'
5
5
 
6
- let emission, storage, atom, outbox
6
+ let emission, storage, atom, outbox, listeners
7
7
 
8
8
  const BATCH = 4
9
9
 
@@ -28,7 +28,20 @@ beforeEach(() => {
28
28
  }
29
29
  }
30
30
 
31
- atom = { slots: mock.fn(() => [0]), link: mock.fn() }
31
+ listeners = []
32
+
33
+ atom = {
34
+ slots: mock.fn(() => [0]),
35
+ onassigned: (listener) => {
36
+ listeners.push(listener)
37
+ listener({ i: 0, n: 1 })
38
+
39
+ return () => {
40
+ listeners = listeners.filter((one) => one !== listener)
41
+ }
42
+ },
43
+ link: mock.fn()
44
+ }
32
45
  outbox = new Outbox(emission, storage, atom, { interval: 1000, batch: BATCH })
33
46
  })
34
47
 
@@ -36,6 +49,11 @@ afterEach(() => {
36
49
  mock.timers.reset()
37
50
  })
38
51
 
52
+ /** everything the current turn awaited, without moving the clock */
53
+ const settled = async () => {
54
+ for (let i = 0; i < 20; i++) await Promise.resolve()
55
+ }
56
+
39
57
  /** one cycle, and everything it awaited */
40
58
  const cycle = async () => {
41
59
  mock.timers.tick(1000)
@@ -54,9 +72,18 @@ it('should read nothing more when the first page is short', async () => {
54
72
  })
55
73
 
56
74
  it('should keep reading while a page comes back full', async () => {
57
- storage.outbox.pending.mock.mockImplementationOnce(async () => page(0, BATCH), storage.outbox.pending.mock.callCount())
58
- storage.outbox.pending.mock.mockImplementationOnce(async () => page(BATCH, BATCH), storage.outbox.pending.mock.callCount() + 1)
59
- storage.outbox.pending.mock.mockImplementationOnce(async () => page(2 * BATCH, 5), storage.outbox.pending.mock.callCount() + 2)
75
+ storage.outbox.pending.mock.mockImplementationOnce(
76
+ async () => page(0, BATCH),
77
+ storage.outbox.pending.mock.callCount()
78
+ )
79
+ storage.outbox.pending.mock.mockImplementationOnce(
80
+ async () => page(BATCH, BATCH),
81
+ storage.outbox.pending.mock.callCount() + 1
82
+ )
83
+ storage.outbox.pending.mock.mockImplementationOnce(
84
+ async () => page(2 * BATCH, 5),
85
+ storage.outbox.pending.mock.callCount() + 2
86
+ )
60
87
 
61
88
  await outbox.open()
62
89
  await cycle()
@@ -66,8 +93,14 @@ it('should keep reading while a page comes back full', async () => {
66
93
  })
67
94
 
68
95
  it('should continue each page from the id the one before ended on', async () => {
69
- storage.outbox.pending.mock.mockImplementationOnce(async () => page(0, BATCH), storage.outbox.pending.mock.callCount())
70
- storage.outbox.pending.mock.mockImplementationOnce(async () => page(BATCH, 1), storage.outbox.pending.mock.callCount() + 1)
96
+ storage.outbox.pending.mock.mockImplementationOnce(
97
+ async () => page(0, BATCH),
98
+ storage.outbox.pending.mock.callCount()
99
+ )
100
+ storage.outbox.pending.mock.mockImplementationOnce(
101
+ async () => page(BATCH, 1),
102
+ storage.outbox.pending.mock.callCount() + 1
103
+ )
71
104
 
72
105
  await outbox.open()
73
106
  await cycle()
@@ -79,8 +112,14 @@ it('should continue each page from the id the one before ended on', async () =>
79
112
  })
80
113
 
81
114
  it('should mark what it published, once, after the last page', async () => {
82
- storage.outbox.pending.mock.mockImplementationOnce(async () => page(0, BATCH), storage.outbox.pending.mock.callCount())
83
- storage.outbox.pending.mock.mockImplementationOnce(async () => page(BATCH, 2), storage.outbox.pending.mock.callCount() + 1)
115
+ storage.outbox.pending.mock.mockImplementationOnce(
116
+ async () => page(0, BATCH),
117
+ storage.outbox.pending.mock.callCount()
118
+ )
119
+ storage.outbox.pending.mock.mockImplementationOnce(
120
+ async () => page(BATCH, 2),
121
+ storage.outbox.pending.mock.callCount() + 1
122
+ )
84
123
 
85
124
  await outbox.open()
86
125
  await cycle()
@@ -90,9 +129,17 @@ it('should mark what it published, once, after the last page', async () => {
90
129
  })
91
130
 
92
131
  it('should not publish a row it has published and not yet marked', async () => {
93
- storage.outbox.settle.mock.mockImplementationOnce(async () => { throw new Error('mongo is out') })
94
- storage.outbox.pending.mock.mockImplementationOnce(async () => page(0, 2), storage.outbox.pending.mock.callCount())
95
- storage.outbox.pending.mock.mockImplementationOnce(async () => page(0, 2), storage.outbox.pending.mock.callCount() + 1)
132
+ storage.outbox.settle.mock.mockImplementationOnce(async () => {
133
+ throw new Error('mongo is out')
134
+ })
135
+ storage.outbox.pending.mock.mockImplementationOnce(
136
+ async () => page(0, 2),
137
+ storage.outbox.pending.mock.callCount()
138
+ )
139
+ storage.outbox.pending.mock.mockImplementationOnce(
140
+ async () => page(0, 2),
141
+ storage.outbox.pending.mock.callCount() + 1
142
+ )
96
143
 
97
144
  await outbox.open()
98
145
  await cycle()
@@ -110,7 +157,7 @@ it('should read nothing while it owns no slots', async () => {
110
157
  assert.strictEqual(storage.outbox.pending.mock.callCount(), 0)
111
158
  })
112
159
 
113
- function resetCalls (target = [assert, BATCH, page, cycle], seen = new Set()) {
160
+ function resetCalls(target = [assert, BATCH, page, cycle], seen = new Set()) {
114
161
  if (target === null || typeof target !== 'object' || seen.has(target)) return
115
162
 
116
163
  seen.add(target)
@@ -119,3 +166,37 @@ function resetCalls (target = [assert, BATCH, page, cycle], seen = new Set()) {
119
166
  if (typeof value === 'function' && value.mock !== undefined) value.mock.resetCalls()
120
167
  else resetCalls(value, seen)
121
168
  }
169
+
170
+ it('should read as soon as a claim arrives, rather than on its next cycle', async () => {
171
+ atom.slots.mock.mockImplementation(() => null)
172
+
173
+ await outbox.open()
174
+ await settled()
175
+
176
+ assert.strictEqual(storage.outbox.pending.mock.callCount(), 0, 'nothing is owned yet')
177
+
178
+ atom.slots.mock.mockImplementation(() => [0])
179
+
180
+ for (const listener of listeners) listener({ i: 0, n: 1 })
181
+
182
+ await settled()
183
+
184
+ assert.strictEqual(
185
+ storage.outbox.pending.mock.callCount(),
186
+ 1,
187
+ 'the lane is its own now, and the cycle is up to five seconds away'
188
+ )
189
+ })
190
+
191
+ it('should read the lanes it inherits when the group resizes', async () => {
192
+ await outbox.open()
193
+ await settled()
194
+
195
+ const before = storage.outbox.pending.mock.callCount()
196
+
197
+ for (const listener of listeners) listener({ i: 0, n: 2 })
198
+
199
+ await settled()
200
+
201
+ assert.strictEqual(storage.outbox.pending.mock.callCount(), before + 1)
202
+ })
@@ -1,7 +1,7 @@
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
- import { Query } from '../src/query.js'
4
+ import { Query } from '../source/query.js'
5
5
  import * as fixtures from './query.fixtures.js'
6
6
 
7
7
  beforeEach(() => {
@@ -23,6 +23,13 @@ describe('criteria', () => {
23
23
  assert.deepStrictEqual(query.criteria, fixtures.samples.simple.parsed.criteria)
24
24
  })
25
25
 
26
+ it('should coerce every value of a list', () => {
27
+ const instance = new Query({ n: { type: 'integer' } })
28
+ const query = instance.parse({ criteria: 'n=in=(1,2,3)' })
29
+
30
+ assert.deepStrictEqual(query.criteria.right.value, [1, 2, 3])
31
+ })
32
+
26
33
  it('should keep a parsed criteria', () => {
27
34
  const instance = new Query(fixtures.samples.simple.properties)
28
35
 
@@ -47,10 +54,62 @@ describe('criteria', () => {
47
54
  assert.deepStrictEqual(query.criteria, fixtures.samples.extended.parsed.criteria)
48
55
  })
49
56
 
57
+ it('should refuse a value the property cannot hold', () => {
58
+ const strict = new Query({
59
+ volume: { type: 'number' },
60
+ count: { type: 'integer' },
61
+ booked: { type: 'boolean' }
62
+ })
63
+
64
+ const refused = [
65
+ ['volume>abc', /takes a number/],
66
+ ['volume==1.5kg', /takes a number/],
67
+ ['count==1.5', /takes an integer/],
68
+ ['count==12kg', /takes an integer/],
69
+ ['booked==yes', /takes a boolean/],
70
+ ['booked==TRUE', /takes a boolean/],
71
+ ['volume=in=(1,two)', /'two' is not one/]
72
+ ]
73
+
74
+ for (const [criteria, message] of refused)
75
+ assert.throws(
76
+ () => strict.parse({ criteria }),
77
+ (error) => message.test(error.message),
78
+ criteria
79
+ )
80
+ })
81
+
82
+ it('should read a value the property can hold', () => {
83
+ const strict = new Query({
84
+ volume: { type: 'number' },
85
+ count: { type: 'integer' },
86
+ booked: { type: 'boolean' },
87
+ title: { type: 'string' }
88
+ })
89
+
90
+ const read = [
91
+ ['volume>1.5', 1.5],
92
+ ['volume>-1.5', -1.5],
93
+ ['count==12', 12],
94
+ ['booked==false', false],
95
+ ['title==12kg', '12kg']
96
+ ]
97
+
98
+ for (const [criteria, value] of read)
99
+ assert.deepStrictEqual(
100
+ strict.parse({ criteria }).criteria.right.value,
101
+ value,
102
+ criteria
103
+ )
104
+ })
105
+
50
106
  it('should throw on unknown properties', () => {
51
107
  const instance = new Query(fixtures.samples.simple.properties)
52
108
 
53
- assert.throws(() => instance.parse({ criteria: 'lastname==Johnson' }), (error) => /not defined/.test(error.message))
109
+ assert.throws(
110
+ () => instance.parse({ criteria: 'lastname==Johnson' }),
111
+ (error) => /not defined/.test(error.message)
112
+ )
54
113
  })
55
114
 
56
115
  it('should parse id', () => {
@@ -84,13 +143,20 @@ describe('options', () => {
84
143
  const sort = ['a', 'b:desc', 'c']
85
144
  const query = instance.parse({ sort })
86
145
 
87
- assert.deepStrictEqual(query.options.sort, [['a', 'asc'], ['b', 'desc'], ['c', 'asc']])
146
+ assert.deepStrictEqual(query.options.sort, [
147
+ ['a', 'asc'],
148
+ ['b', 'desc'],
149
+ ['c', 'asc']
150
+ ])
88
151
  })
89
152
 
90
153
  it('should throw on unknown properties', () => {
91
154
  const sort = ['d:asc']
92
155
 
93
- assert.throws(() => instance.parse({ sort }), (error) => /not defined/.test(error.message))
156
+ assert.throws(
157
+ () => instance.parse({ sort }),
158
+ (error) => /not defined/.test(error.message)
159
+ )
94
160
  })
95
161
  })
96
162
 
@@ -98,12 +164,15 @@ describe('options', () => {
98
164
  it('should throw on unknown properties', () => {
99
165
  const projection = ['a', 'b', 'c', 'd']
100
166
 
101
- assert.throws(() => instance.parse({ projection }), (error) => /not defined/.test(error.message))
167
+ assert.throws(
168
+ () => instance.parse({ projection }),
169
+ (error) => /not defined/.test(error.message)
170
+ )
102
171
  })
103
172
  })
104
173
  })
105
174
 
106
- function resetCalls (target = [assert, fixtures], seen = new Set()) {
175
+ function resetCalls(target = [assert, fixtures], seen = new Set()) {
107
176
  if (target === null || typeof target !== 'object' || seen.has(target)) return
108
177
 
109
178
  seen.add(target)
@@ -8,13 +8,13 @@ export const definition = /** @type {toa.norm.component.Receiver} */ {
8
8
  adaptive: false
9
9
  }
10
10
 
11
- export const local = /** @type {toa.core.Component} */ {
11
+ export const local = /** @type {import('@toa.io/core').Component} */ {
12
12
  locator: { id: 'default.dummy' },
13
13
  invoke: mock.fn()
14
14
  }
15
15
 
16
16
  // noinspection JSCheckFunctionSignatures
17
- export const bridge = /** @type {toa.core.bridges.Event} */ {
17
+ export const bridge = /** @type {import('@toa.io/core/types').bridges.Event} */ {
18
18
  condition: mock.fn(async (payload) => !(payload.reject === true)),
19
19
  request: mock.fn(async () => ({ input: generate() }))
20
20
  }
@@ -6,19 +6,20 @@ import clone from 'clone-deep'
6
6
  import { generate } from 'randomstring'
7
7
  import { merge } from '@toa.io/generic'
8
8
 
9
- import { Connector } from '../src/connector.js'
9
+ import { Connector } from '../source/connector.js'
10
10
 
11
11
  // the fixtures are not connectors, so a dependency is recorded rather than linked
12
12
  const depends = mock.method(Connector.prototype, 'depends', () => undefined)
13
13
 
14
- const dependencies = (instance) => depends.mock.calls
15
- .filter((call) => call.this === instance)
16
- .map((call) => call.arguments[0])
14
+ const dependencies = (instance) =>
15
+ depends.mock.calls
16
+ .filter((call) => call.this === instance)
17
+ .map((call) => call.arguments[0])
17
18
 
18
- import { Receiver } from '../src/receiver.js'
19
+ import { Receiver } from '../source/receiver.js'
19
20
  import * as fixtures from './receiver.fixtures.js'
20
21
 
21
- /** @type {toa.core.Receiver} */
22
+ /** @type {import('@toa.io/core/types').Receiver} */
22
23
  let receiver
23
24
 
24
25
  /** @type {toa.norm.component.Receiver} */
@@ -42,29 +43,38 @@ it('should apply', async () => {
42
43
 
43
44
  await receiver.receive({ payload })
44
45
 
45
- assert.ok(fixtures.local.invoke.mock.calls.some((call) => call.arguments.length === 2 && isDeepStrictEqual(call.arguments[0], definition.operation) && isDeepStrictEqual(call.arguments[1], { input: payload })))
46
+ assert.ok(
47
+ fixtures.local.invoke.mock.calls.some(
48
+ (call) =>
49
+ call.arguments.length === 2 &&
50
+ isDeepStrictEqual(call.arguments[0], definition.operation) &&
51
+ isDeepStrictEqual(call.arguments[1], { input: payload })
52
+ )
53
+ )
46
54
  })
47
55
 
48
56
  for (const [adaptive] of [[false], [true]])
49
- it(`should pass UI extensions (adaptive: ${adaptive})`, async () => {
50
- resetCalls()
57
+ it(`should pass UI extensions (adaptive: ${adaptive})`, async () => {
58
+ resetCalls()
51
59
 
52
- definition.adaptive = adaptive
53
- receiver = new Receiver(definition, fixtures.local, fixtures.bridge)
60
+ definition.adaptive = adaptive
61
+ receiver = new Receiver(definition, fixtures.local, fixtures.bridge)
54
62
 
55
- const payload = { foo: generate() }
56
- const extension = { [generate()]: generate() }
57
- const message = { payload, ...extension }
63
+ const payload = { foo: generate() }
64
+ const extension = { [generate()]: generate() }
65
+ const message = { payload, ...extension }
58
66
 
59
- await receiver.receive(message)
67
+ await receiver.receive(message)
60
68
 
61
- const request = adaptive ? await fixtures.bridge.request.mock.calls[0].result : { input: payload }
62
- const expected = merge(clone(request), extension)
69
+ const request = adaptive
70
+ ? await fixtures.bridge.request.mock.calls[0].result
71
+ : { input: payload }
72
+ const expected = merge(clone(request), extension)
63
73
 
64
- const argument = fixtures.local.invoke.mock.calls[0].arguments[1]
74
+ const argument = fixtures.local.invoke.mock.calls[0].arguments[1]
65
75
 
66
- assert.deepStrictEqual(argument, expected)
67
- })
76
+ assert.deepStrictEqual(argument, expected)
77
+ })
68
78
 
69
79
  describe('conditioned', () => {
70
80
  beforeEach(() => {
@@ -76,8 +86,20 @@ describe('conditioned', () => {
76
86
  const payload = { foo: 'bar' }
77
87
  await receiver.receive({ payload })
78
88
 
79
- assert.ok(fixtures.bridge.condition.mock.calls.some((call) => call.arguments.length === 1 && isDeepStrictEqual(call.arguments[0], payload)))
80
- assert.ok(fixtures.local.invoke.mock.calls.some((call) => call.arguments.length === 2 && isDeepStrictEqual(call.arguments[0], definition.operation) && isDeepStrictEqual(call.arguments[1], { input: payload })))
89
+ assert.ok(
90
+ fixtures.bridge.condition.mock.calls.some(
91
+ (call) =>
92
+ call.arguments.length === 1 && isDeepStrictEqual(call.arguments[0], payload)
93
+ )
94
+ )
95
+ assert.ok(
96
+ fixtures.local.invoke.mock.calls.some(
97
+ (call) =>
98
+ call.arguments.length === 2 &&
99
+ isDeepStrictEqual(call.arguments[0], definition.operation) &&
100
+ isDeepStrictEqual(call.arguments[1], { input: payload })
101
+ )
102
+ )
81
103
  })
82
104
 
83
105
  it('should not apply if condition is false', async () => {
@@ -100,14 +122,18 @@ describe('adaptive', () => {
100
122
 
101
123
  const request = await fixtures.bridge.request.mock.calls[0].result
102
124
 
103
- assert.ok(fixtures.local.invoke.mock.calls.some((call) =>
104
- call.arguments.length === 2 &&
105
- isDeepStrictEqual(call.arguments[0], definition.operation) &&
106
- isDeepStrictEqual(call.arguments[1], request)))
125
+ assert.ok(
126
+ fixtures.local.invoke.mock.calls.some(
127
+ (call) =>
128
+ call.arguments.length === 2 &&
129
+ isDeepStrictEqual(call.arguments[0], definition.operation) &&
130
+ isDeepStrictEqual(call.arguments[1], request)
131
+ )
132
+ )
107
133
  })
108
134
  })
109
135
 
110
- function resetCalls (target = [assert, clone, fixtures], seen = new Set()) {
136
+ function resetCalls(target = [assert, clone, fixtures], seen = new Set()) {
111
137
  if (target === null || typeof target !== 'object' || seen.has(target)) return
112
138
 
113
139
  seen.add(target)
@@ -2,18 +2,18 @@ import { it, beforeEach } from 'node:test'
2
2
  import assert from 'node:assert/strict'
3
3
 
4
4
  import { generate } from 'randomstring'
5
- import { Reflection, Connector } from '../src/index.js'
5
+ import { Reflection, Connector } from '../source/index.js'
6
6
 
7
7
  it('should export', () => {
8
8
  assert.notStrictEqual(Reflection, undefined)
9
9
  })
10
10
 
11
- /** @type {toa.core.Reflection<string>} */
11
+ /** @type {import('@toa.io/core').Reflection<string>} */
12
12
  let reflection
13
13
 
14
14
  const value = generate()
15
15
 
16
- /** @type {toa.core.reflection.Source<string>} */
16
+ /** @type {import('@toa.io/core').ReflectionSource<string>} */
17
17
  const source = async () => value
18
18
 
19
19
  beforeEach(() => {
@@ -5,7 +5,7 @@ import { generate } from 'randomstring'
5
5
  export const storage = {
6
6
  name: 'dummy',
7
7
  get: mock.fn(() => ({ id: generate() })),
8
- find: mock.fn(() => ([{ id: generate() }])),
8
+ find: mock.fn(() => [{ id: generate() }]),
9
9
  add: mock.fn(() => true),
10
10
  set: mock.fn(() => true),
11
11
  store: mock.fn(() => true),
@@ -27,7 +27,8 @@ export const entity = {
27
27
  }
28
28
 
29
29
  export const initial = {
30
- initial: true, ...entity
30
+ initial: true,
31
+ ...entity
31
32
  }
32
33
 
33
34
  export const unchanged = {
@@ -37,6 +38,12 @@ export const unchanged = {
37
38
 
38
39
  // a legacy outbox: no storage capability, so `publish` emits inline
39
40
  export const outbox = {
40
- row: mock.fn((event) => ({ id: generate(), lane: 0, published: false, pending: 0, event })),
41
+ row: mock.fn((event) => ({
42
+ id: generate(),
43
+ lane: 0,
44
+ published: false,
45
+ pending: 0,
46
+ event
47
+ })),
41
48
  publish: mock.fn()
42
49
  }