@toa.io/core 1.0.0-alpha.292 → 1.0.0-alpha.299

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 (138) hide show
  1. package/package.json +10 -4
  2. package/transpiled/call.js +12 -0
  3. package/transpiled/call.js.map +1 -1
  4. package/transpiled/component.js +31 -4
  5. package/transpiled/component.js.map +1 -1
  6. package/transpiled/context.d.ts +5 -0
  7. package/transpiled/context.js +6 -0
  8. package/transpiled/context.js.map +1 -1
  9. package/transpiled/emission.d.ts +4 -2
  10. package/transpiled/emission.js +4 -2
  11. package/transpiled/emission.js.map +1 -1
  12. package/transpiled/entities/entity.js +34 -6
  13. package/transpiled/entities/entity.js.map +1 -1
  14. package/transpiled/entities/factory.d.ts +5 -0
  15. package/transpiled/entities/factory.js +6 -2
  16. package/transpiled/entities/factory.js.map +1 -1
  17. package/transpiled/event.d.ts +2 -2
  18. package/transpiled/event.js +6 -1
  19. package/transpiled/event.js.map +1 -1
  20. package/transpiled/exceptions.d.ts +19 -0
  21. package/transpiled/exceptions.js +74 -2
  22. package/transpiled/exceptions.js.map +1 -1
  23. package/transpiled/index.d.ts +2 -1
  24. package/transpiled/index.js +2 -1
  25. package/transpiled/index.js.map +1 -1
  26. package/transpiled/{outbox/outbox.d.ts → outbox.d.ts} +6 -7
  27. package/transpiled/outbox.js +424 -0
  28. package/transpiled/outbox.js.map +1 -0
  29. package/transpiled/query/options.js +1 -1
  30. package/transpiled/query/options.js.map +1 -1
  31. package/transpiled/receiver.js +14 -1
  32. package/transpiled/receiver.js.map +1 -1
  33. package/transpiled/remote.js +2 -3
  34. package/transpiled/remote.js.map +1 -1
  35. package/transpiled/state.d.ts +1 -1
  36. package/transpiled/trail.d.ts +49 -0
  37. package/transpiled/trail.js +98 -0
  38. package/transpiled/trail.js.map +1 -0
  39. package/transpiled/types/bindings.d.ts +22 -0
  40. package/transpiled/types/extensions.d.ts +13 -2
  41. package/transpiled/types/message.d.ts +5 -0
  42. package/transpiled/types/outbox.d.ts +29 -3
  43. package/transpiled/types/request.d.ts +5 -0
  44. package/transpiled/types/storages.d.ts +20 -0
  45. package/CHANGELOG.md +0 -43
  46. package/source/assignment.ts +0 -27
  47. package/source/call.ts +0 -63
  48. package/source/cascade.ts +0 -32
  49. package/source/component.ts +0 -83
  50. package/source/composition.ts +0 -27
  51. package/source/connector.ts +0 -217
  52. package/source/context.ts +0 -68
  53. package/source/contract/contract.ts +0 -23
  54. package/source/contract/index.ts +0 -2
  55. package/source/contract/reply.ts +0 -56
  56. package/source/contract/request.ts +0 -140
  57. package/source/contract/schemas.ts +0 -39
  58. package/source/discovery.ts +0 -53
  59. package/source/effect.ts +0 -14
  60. package/source/emission.ts +0 -21
  61. package/source/entities/changeset.ts +0 -37
  62. package/source/entities/entity.ts +0 -137
  63. package/source/entities/factory.ts +0 -52
  64. package/source/entities/index.ts +0 -4
  65. package/source/entities/newid.ts +0 -9
  66. package/source/entities/set.ts +0 -27
  67. package/source/event.ts +0 -57
  68. package/source/exceptions.ts +0 -133
  69. package/source/exposition.ts +0 -33
  70. package/source/guard.ts +0 -16
  71. package/source/index.ts +0 -36
  72. package/source/locator.ts +0 -37
  73. package/source/observation.ts +0 -16
  74. package/source/operation.ts +0 -139
  75. package/source/outbox/index.ts +0 -1
  76. package/source/outbox/outbox.ts +0 -341
  77. package/source/query/criteria.ts +0 -90
  78. package/source/query/options.ts +0 -35
  79. package/source/query.ts +0 -67
  80. package/source/receiver.ts +0 -120
  81. package/source/reflection.ts +0 -20
  82. package/source/remote.ts +0 -20
  83. package/source/state.ts +0 -179
  84. package/source/transition.ts +0 -79
  85. package/source/transmission.ts +0 -38
  86. package/source/types/atomicity.ts +0 -63
  87. package/source/types/bindings.ts +0 -59
  88. package/source/types/bridges.ts +0 -54
  89. package/source/types/extensions.ts +0 -89
  90. package/source/types/index.ts +0 -14
  91. package/source/types/message.ts +0 -5
  92. package/source/types/operations.ts +0 -31
  93. package/source/types/outbox.ts +0 -35
  94. package/source/types/receiver.ts +0 -10
  95. package/source/types/request.ts +0 -54
  96. package/source/types/state.ts +0 -9
  97. package/source/types/storages.ts +0 -134
  98. package/source/unmanaged.ts +0 -8
  99. package/test/call.fixtures.js +0 -25
  100. package/test/call.test.js +0 -79
  101. package/test/component.fixtures.js +0 -18
  102. package/test/component.test.js +0 -57
  103. package/test/connector.fixtures.js +0 -56
  104. package/test/connector.test.js +0 -266
  105. package/test/context.fixtures.js +0 -14
  106. package/test/context.test.js +0 -40
  107. package/test/contract/conditions.test.js +0 -32
  108. package/test/contract/contract.fixtures.js +0 -28
  109. package/test/contract/request.test.js +0 -196
  110. package/test/discovery.test.js +0 -71
  111. package/test/emission.fixtures.js +0 -13
  112. package/test/emission.test.js +0 -60
  113. package/test/entities/entity.fixtures.js +0 -24
  114. package/test/entities/entity.test.js +0 -137
  115. package/test/entities/factory.fixtures.js +0 -31
  116. package/test/entities/factory.test.js +0 -99
  117. package/test/entities/set.fixtures.js +0 -9
  118. package/test/entities/set.test.js +0 -13
  119. package/test/event.fixtures.js +0 -24
  120. package/test/event.test.js +0 -162
  121. package/test/locator.test.js +0 -120
  122. package/test/outbox.test.js +0 -202
  123. package/test/query.fixtures.js +0 -96
  124. package/test/query.test.js +0 -183
  125. package/test/receiver.fixtures.js +0 -20
  126. package/test/receiver.test.js +0 -144
  127. package/test/reflection.test.js +0 -31
  128. package/test/state.fixtures.js +0 -49
  129. package/test/state.test.js +0 -162
  130. package/test/transmission.fixtures.js +0 -13
  131. package/test/transmission.test.js +0 -77
  132. package/transpiled/outbox/index.d.ts +0 -1
  133. package/transpiled/outbox/index.js +0 -2
  134. package/transpiled/outbox/index.js.map +0 -1
  135. package/transpiled/outbox/outbox.js +0 -268
  136. package/transpiled/outbox/outbox.js.map +0 -1
  137. package/tsconfig.json +0 -10
  138. package/tsconfig.tsbuildinfo +0 -1
@@ -1,89 +0,0 @@
1
- import type { Connector } from '../connector.js'
2
- import type { Locator } from '../locator.js'
3
- import type { Component } from '../component.js'
4
- import type { Remote } from '../remote.js'
5
- import type { Receiver } from './receiver.js'
6
- import type { Context } from '../context.js'
7
- import type { Storage } from './storages.js'
8
- import type { Broadcast, Emitter } from './bindings.js'
9
- import type { Atom } from './atomicity.js'
10
- import type { Source } from './request.js'
11
-
12
- /**
13
- * What the process hosting an extension provides to it: the counterpart of a component's
14
- * context. What is returned is a connector the extension depends on.
15
- */
16
- export interface Host {
17
- /** a component of the context, by locator */
18
- remote(locator: Locator, source?: Source): Promise<Remote>
19
-
20
- /** a channel of the messaging binding */
21
- broadcast<L extends string = string>(
22
- channel: string,
23
- group?: string
24
- ): Promise<Broadcast<L>>
25
-
26
- /** components to run inside the extension's own process */
27
- composition(paths: string[]): Promise<Connector>
28
-
29
- /** a consumer of an event of the context, `namespace.component.event` */
30
- receive(label: string, receiver: Receiver): Promise<Connector>
31
-
32
- /** what the replicas of one group decide together */
33
- atom(group: string): Atom
34
- }
35
-
36
- /**
37
- * `Manifest` is a type parameter rather than an import: `@toa.io/norm` depends on core, so
38
- * core cannot name its types.
39
- */
40
- export interface Factory<Manifest = unknown> {
41
- tenant?(
42
- locator: Locator,
43
- declaration: any,
44
- manifest: Manifest
45
- ): Connector | Promise<Connector>
46
-
47
- aspect?(locator: Locator, declaration: any): Aspect | Aspect[]
48
-
49
- /** what the extension runs as a process of its own; `null` where it is off here */
50
- service?(): Connector | null | Promise<Connector | null>
51
-
52
- component?(component: Component): Component
53
-
54
- context?(context: Context): Context
55
-
56
- manage?(composition: Connector): Connector
57
-
58
- storage?(storage: Storage): Storage
59
-
60
- emitter?(emitter: Emitter, label: string, locator: Locator): Emitter
61
-
62
- receiver?(receiver: Receiver, locator: Locator): Receiver
63
- }
64
-
65
- export interface Aspect extends Connector {
66
- /** the key it takes on the context; a duplicate is a boot error */
67
- readonly name: string
68
-
69
- invoke(...args: any[]): any
70
- }
71
-
72
- /**
73
- * What an extension puts on a component's context, as the extension states it. How it is
74
- * presented there is the bridge's — a bash bridge has no context to put anything on — and
75
- * what is declared here is the key and what it holds.
76
- */
77
- export interface Contribution {
78
- /** the key on the context */
79
- name: string
80
-
81
- /** what the key holds, as TypeScript */
82
- type?: string
83
-
84
- /** what `type` names, by the module it comes from */
85
- imports?: Record<string, string[]>
86
-
87
- /** a JSON Schema to read the type from instead, where a component states one */
88
- schema?: object
89
- }
@@ -1,14 +0,0 @@
1
- export * as atomicity from './atomicity.js'
2
- export * as bindings from './bindings.js'
3
- export * as bridges from './bridges.js'
4
- export * as extensions from './extensions.js'
5
- export * as operations from './operations.js'
6
- export * as outbox from './outbox.js'
7
- export * as storages from './storages.js'
8
-
9
- export type { Contribution } from './extensions.js'
10
- export type { Event } from './state.js'
11
- export type { Message } from './message.js'
12
- export type { Receiver } from './receiver.js'
13
- export type { Call, Guard, Observation, Transition } from './operations.js'
14
- export type { Maybe, Query, Reply, RemoteError, Request, Source } from './request.js'
@@ -1,5 +0,0 @@
1
- export interface Message<T = any> {
2
- payload: T
3
- /** W3C traceparent */
4
- telemetry?: string
5
- }
@@ -1,31 +0,0 @@
1
- import type { Request } from './request.js'
2
-
3
- export type type =
4
- | 'transition'
5
- | 'observation'
6
- | 'assignment'
7
- | 'computation'
8
- | 'effect'
9
- | 'unmanaged'
10
-
11
- export type scope = 'object' | 'objects' | 'changeset' | 'stream' | 'none'
12
-
13
- /**
14
- * How an endpoint is called. What it resolves to is what the operation declares — `toa types`
15
- * writes that per operation, and these are what a hand-written context says instead.
16
- */
17
- export type Call<Output = any, Input = any> = (request: Request<Input>) => Promise<Output>
18
-
19
- export type Observation<Output = any, Input = never, Entity = unknown> = (
20
- request: Request<Input, Entity>
21
- ) => Promise<Output extends unknown[] ? Output : Output | null>
22
-
23
- export type Transition<Output = any, Input = never, Entity = unknown> = (
24
- request: Request<Input, Entity>
25
- ) => Promise<Output | null>
26
-
27
- /**
28
- * Runs on every change to an entity's state, before the contract is applied. `false` refuses
29
- * the change as `EntityGuard`.
30
- */
31
- export type Guard<T, C = unknown> = (state: T, origin: T | null, context: C) => boolean
@@ -1,35 +0,0 @@
1
- import type { Event } from './state.js'
2
-
3
- /**
4
- * The intent to publish, committed with the state change it belongs to. Everything about it
5
- * is core's: the storage writes it and, where the images are the write's own, fills
6
- * `event.origin` and `event.state` in.
7
- */
8
- export interface Row {
9
- id: string
10
-
11
- /** which replica pumps this row; carries no other meaning, and no ordering */
12
- lane: number
13
-
14
- published: boolean
15
-
16
- /** not before this */
17
- pending: number
18
-
19
- /** an assignment's images are absent until the storage fills them in */
20
- event: Event
21
- }
22
-
23
- /**
24
- * The read side of an outbox. What writes a row is the storage's own: it happens inside the
25
- * transaction the storage opened, which core never reaches into.
26
- */
27
- export interface Storage {
28
- /**
29
- * One page of what is due, still unpublished, and in one of the given lanes, in the order
30
- * the rows were written. `after` continues from the last id of the page before.
31
- */
32
- pending(lanes: number[], now: number, limit: number, after?: string): Promise<Row[]>
33
-
34
- settle(ids: string[]): Promise<void>
35
- }
@@ -1,10 +0,0 @@
1
- import type { Connector } from '../connector.js'
2
- import type { Message } from './message.js'
3
-
4
- /**
5
- * What consumes an event of the context. Core ships one, and an extension may put its own
6
- * in its place, so what a binding is handed is this rather than core's class.
7
- */
8
- export interface Receiver extends Connector {
9
- receive(message: Message): Promise<void>
10
- }
@@ -1,54 +0,0 @@
1
- import type { Exception } from '../exceptions.js'
2
-
3
- /**
4
- * What a call asks for. `Entity` is the record it is about, which narrows the projection;
5
- * left out, any name is accepted.
6
- */
7
- export interface Query<Entity = any> {
8
- id?: string
9
- ids?: string[]
10
- criteria?: string
11
- search?: string
12
- sample?: number
13
- omit?: number
14
- limit?: number
15
- sort?: string[]
16
- projection?: Array<string & keyof Entity>
17
- version?: number
18
- deleted?: boolean
19
- }
20
-
21
- /** Origin of a call. Stamped by the framework; whoever reads it takes the keys it knows. */
22
- export type Source =
23
- | { namespace: string; component: string; operation: string }
24
- | { namespace: string; component: string; event: string }
25
- | { service: string }
26
-
27
- export interface Request<Input = any, Entity = any> {
28
- input?: Input
29
- query?: Query<Entity>
30
- /** What the operation acquires, where the caller supplies it rather than the storage. */
31
- entity?: Entity
32
- authentic?: boolean
33
- task?: boolean
34
- /** W3C traceparent */
35
- telemetry?: string
36
- source?: Source
37
- }
38
-
39
- /**
40
- * An error an operation declares and returns. A call resolves to it rather than throwing:
41
- * only an exception is thrown.
42
- */
43
- export interface RemoteError<Code extends string = string> extends Error {
44
- code: Code
45
- }
46
-
47
- /** What an operation returns where it may refuse: the value, or the error it refused with. */
48
- export type Maybe<T> = T | Error
49
-
50
- export interface Reply {
51
- output?: any
52
- error?: object
53
- exception?: Exception
54
- }
@@ -1,9 +0,0 @@
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
- }
@@ -1,134 +0,0 @@
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
- /**
117
- * One file of a `migrations` directory, named after it — the component's own, or a prototype's,
118
- * which every descendant applies to its collection ahead of its own.
119
- */
120
- export interface Migration {
121
- id: string
122
- steps: unknown[]
123
- /** the prototype it is inherited from; absent on the component's own */
124
- prototype?: string
125
- }
126
-
127
- export interface StorageOptions {
128
- /** whether the component publishes anything, and so needs a place to commit a row */
129
- outbox?: boolean
130
- }
131
-
132
- export interface Factory {
133
- storage(locator: Locator, entity: Entity, options?: StorageOptions): Storage
134
- }
@@ -1,8 +0,0 @@
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
- }
@@ -1,25 +0,0 @@
1
- import { mock } from 'node:test'
2
-
3
- import { generate } from 'randomstring'
4
-
5
- // noinspection JSCheckFunctionSignatures
6
- export const transmission = {
7
- request: mock.fn((request) => ({
8
- [request.invalid ? 'exception' : 'output']: generate()
9
- })),
10
- link: mock.fn()
11
- }
12
-
13
- export const contract = {
14
- fit: mock.fn(() => null)
15
- }
16
-
17
- export const request = () => ({
18
- ok: {
19
- input: { [generate()]: generate() },
20
- query: { [generate()]: generate() }
21
- },
22
- bad: {
23
- invalid: true
24
- }
25
- })
package/test/call.test.js DELETED
@@ -1,79 +0,0 @@
1
- import { it, beforeEach } from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import { isDeepStrictEqual } from 'node:util'
4
-
5
- import * as fixtures from './call.fixtures.js'
6
- import { Call } from '../source/call.js'
7
-
8
- let call
9
-
10
- beforeEach(() => {
11
- resetCalls()
12
-
13
- call = new Call(fixtures.transmission, fixtures.contract)
14
- })
15
-
16
- it('should depend on transmission', () => {
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
- )
24
- })
25
-
26
- it('should call transmission', async () => {
27
- const request = fixtures.request().ok
28
-
29
- await call.invoke(request)
30
-
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
- )
38
- })
39
-
40
- it('should fit request', async () => {
41
- const request = fixtures.request().ok
42
-
43
- await call.invoke(request)
44
-
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
- )
52
- })
53
-
54
- it('should return reply', async () => {
55
- const request = fixtures.request().ok
56
-
57
- const reply = await call.invoke(request)
58
-
59
- assert.deepStrictEqual(reply, fixtures.transmission.request.mock.calls[0].result.output)
60
- })
61
-
62
- it('should throw received exceptions', async () => {
63
- const request = fixtures.request().bad
64
-
65
- await assert.rejects(call.invoke(request), (error) => {
66
- assert.notStrictEqual(error, undefined)
67
- return true
68
- })
69
- })
70
-
71
- function resetCalls(target = [assert, fixtures], seen = new Set()) {
72
- if (target === null || typeof target !== 'object' || seen.has(target)) return
73
-
74
- seen.add(target)
75
-
76
- for (const value of Object.values(target))
77
- if (typeof value === 'function' && value.mock !== undefined) value.mock.resetCalls()
78
- else resetCalls(value, seen)
79
- }
@@ -1,18 +0,0 @@
1
- import { mock } from 'node:test'
2
-
3
- import randomstring from 'randomstring'
4
-
5
- const invocation = () => mock.fn(() => randomstring.generate())
6
-
7
- export const invocations = {
8
- foo: {
9
- invoke: invocation('foo'),
10
- link: () => null
11
- },
12
- bar: {
13
- invoke: invocation('bar'),
14
- link: () => null
15
- }
16
- }
17
-
18
- export const locator = {}
@@ -1,57 +0,0 @@
1
- import { describe, it, beforeEach } from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import { isDeepStrictEqual } from 'node:util'
4
-
5
- import { Component } from '../source/component.js'
6
- import * as fixtures from './component.fixtures.js'
7
- import { AssertionError } from 'node:assert'
8
-
9
- describe('Invocations', () => {
10
- const name = ['foo', 'bar'][Math.floor(2 * Math.random())]
11
- const invocation = fixtures.invocations[name]
12
- const component = new Component(fixtures.locator, fixtures.invocations)
13
-
14
- beforeEach(() => {
15
- resetCalls()
16
- })
17
-
18
- it('should invoke', async () => {
19
- await component.invoke(name)
20
-
21
- assert.ok(invocation.invoke.mock.callCount() > 0)
22
- })
23
-
24
- it('should throw on unknown invocation name', async () => {
25
- await assert.rejects(() => component.invoke('baz'), AssertionError)
26
- })
27
-
28
- it('should invoke input and query', async () => {
29
- const input = { test: Math.random() }
30
- const query = { test: Math.random() }
31
- await component.invoke(name, { input, query })
32
-
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
- )
40
- })
41
-
42
- it('should return io', async () => {
43
- const io = await component.invoke(name)
44
-
45
- assert.strictEqual(io, fixtures.invocations[name].invoke.mock.calls[0].result)
46
- })
47
- })
48
-
49
- function resetCalls(target = [assert, fixtures], seen = new Set()) {
50
- if (target === null || typeof target !== 'object' || seen.has(target)) return
51
-
52
- seen.add(target)
53
-
54
- for (const value of Object.values(target))
55
- if (typeof value === 'function' && value.mock !== undefined) value.mock.resetCalls()
56
- else resetCalls(value, seen)
57
- }
@@ -1,56 +0,0 @@
1
- import { random, timeout } from '@toa.io/generic'
2
- import { Connector } from '../source/connector.js'
3
-
4
- export class TestConnector extends Connector {
5
- #label
6
- #seq
7
-
8
- constructor(label, seq) {
9
- super()
10
-
11
- this.#seq = seq
12
- this.#label = label
13
- }
14
-
15
- async open() {
16
- await timeout(random(10))
17
- this.#seq.push(`+${this.#label}`)
18
- }
19
-
20
- async close() {
21
- await timeout(random(10))
22
- this.#seq.push(`-${this.#label}`)
23
- }
24
-
25
- async dispose() {
26
- this.#seq.push(`*${this.#label}`)
27
- }
28
- }
29
-
30
- export class FailingConnector extends Connector {
31
- async open() {
32
- await timeout(random(10))
33
- throw new Error('FailingConnector')
34
- }
35
- }
36
-
37
- /** Stands for a connector whose connection lands once it has been disconnected. */
38
- export class LateConnector extends Connector {
39
- #arrive
40
-
41
- async open() {
42
- await new Promise((resolve) => (this.#arrive = resolve))
43
- }
44
-
45
- /** Lets the connection land. */
46
- arrive() {
47
- this.#arrive()
48
- }
49
- }
50
-
51
- /** Stands for a connector waiting for something that never arrives. */
52
- export class StuckConnector extends Connector {
53
- async open() {
54
- return new Promise(() => undefined)
55
- }
56
- }