@toa.io/core 1.0.0-alpha.32 → 1.0.0-alpha.320
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +28 -12
- package/transpiled/abandon.d.ts +9 -0
- package/transpiled/abandon.js +37 -0
- package/transpiled/abandon.js.map +1 -0
- package/transpiled/assignment.d.ts +6 -0
- package/transpiled/assignment.js +25 -0
- package/transpiled/assignment.js.map +1 -0
- package/transpiled/call.d.ts +10 -0
- package/transpiled/call.js +187 -0
- package/transpiled/call.js.map +1 -0
- package/transpiled/cascade.d.ts +7 -0
- package/transpiled/cascade.js +28 -0
- package/transpiled/cascade.js.map +1 -0
- package/transpiled/component.d.ts +15 -0
- package/transpiled/component.js +148 -0
- package/transpiled/component.js.map +1 -0
- package/transpiled/composition.d.ts +6 -0
- package/transpiled/composition.js +20 -0
- package/transpiled/composition.js.map +1 -0
- package/transpiled/connector.d.ts +71 -0
- package/transpiled/connector.js +248 -0
- package/transpiled/connector.js.map +1 -0
- package/transpiled/context.d.ts +25 -0
- package/transpiled/context.js +55 -0
- package/transpiled/context.js.map +1 -0
- package/transpiled/contract/component.d.ts +37 -0
- package/transpiled/contract/component.js +94 -0
- package/transpiled/contract/component.js.map +1 -0
- package/transpiled/contract/contract.d.ts +10 -0
- package/transpiled/contract/contract.js +14 -0
- package/transpiled/contract/contract.js.map +1 -0
- package/transpiled/contract/index.d.ts +4 -0
- package/transpiled/contract/index.js +5 -0
- package/transpiled/contract/index.js.map +1 -0
- package/transpiled/contract/reply.d.ts +7 -0
- package/transpiled/contract/reply.js +48 -0
- package/transpiled/contract/reply.js.map +1 -0
- package/transpiled/contract/request.d.ts +49 -0
- package/transpiled/contract/request.js +152 -0
- package/transpiled/contract/request.js.map +1 -0
- package/transpiled/contract/schemas.d.ts +4 -0
- package/transpiled/contract/schemas.js +38 -0
- package/transpiled/contract/schemas.js.map +1 -0
- package/transpiled/contract/system.d.ts +33 -0
- package/transpiled/contract/system.js +76 -0
- package/transpiled/contract/system.js.map +1 -0
- package/transpiled/deliveries.d.ts +22 -0
- package/transpiled/deliveries.js +31 -0
- package/transpiled/deliveries.js.map +1 -0
- package/transpiled/effect.d.ts +7 -0
- package/transpiled/effect.js +13 -0
- package/transpiled/effect.js.map +1 -0
- package/transpiled/emission.d.ts +10 -0
- package/transpiled/emission.js +16 -0
- package/transpiled/emission.js.map +1 -0
- package/transpiled/encoded.d.ts +16 -0
- package/transpiled/encoded.js +25 -0
- package/transpiled/encoded.js.map +1 -0
- package/transpiled/entities/changeset.d.ts +10 -0
- package/transpiled/entities/changeset.js +27 -0
- package/transpiled/entities/changeset.js.map +1 -0
- package/transpiled/entities/entity.d.ts +19 -0
- package/transpiled/entities/entity.js +137 -0
- package/transpiled/entities/entity.js.map +1 -0
- package/transpiled/entities/factory.d.ts +27 -0
- package/transpiled/entities/factory.js +38 -0
- package/transpiled/entities/factory.js.map +1 -0
- package/transpiled/entities/index.d.ts +5 -0
- package/transpiled/entities/index.js +6 -0
- package/transpiled/entities/index.js.map +1 -0
- package/transpiled/entities/newid.d.ts +11 -0
- package/transpiled/entities/newid.js +41 -0
- package/transpiled/entities/newid.js.map +1 -0
- package/transpiled/entities/set.d.ts +10 -0
- package/transpiled/entities/set.js +19 -0
- package/transpiled/entities/set.js.map +1 -0
- package/transpiled/event.d.ts +20 -0
- package/transpiled/event.js +65 -0
- package/transpiled/event.js.map +1 -0
- package/transpiled/exceptions.d.ts +110 -0
- package/transpiled/exceptions.js +215 -0
- package/transpiled/exceptions.js.map +1 -0
- package/transpiled/gate.d.ts +40 -0
- package/transpiled/gate.js +84 -0
- package/transpiled/gate.js.map +1 -0
- package/transpiled/guard.d.ts +7 -0
- package/transpiled/guard.js +12 -0
- package/transpiled/guard.js.map +1 -0
- package/transpiled/halting.d.ts +16 -0
- package/transpiled/halting.js +25 -0
- package/transpiled/halting.js.map +1 -0
- package/transpiled/index.d.ts +44 -0
- package/transpiled/index.js +36 -0
- package/transpiled/index.js.map +1 -0
- package/transpiled/instance.d.ts +10 -0
- package/transpiled/instance.js +30 -0
- package/transpiled/instance.js.map +1 -0
- package/transpiled/locator.d.ts +12 -0
- package/transpiled/locator.js +32 -0
- package/transpiled/locator.js.map +1 -0
- package/transpiled/measurements.d.ts +20 -0
- package/transpiled/measurements.js +40 -0
- package/transpiled/measurements.js.map +1 -0
- package/transpiled/observation.d.ts +7 -0
- package/transpiled/observation.js +14 -0
- package/transpiled/observation.js.map +1 -0
- package/transpiled/operation.d.ts +73 -0
- package/transpiled/operation.js +213 -0
- package/transpiled/operation.js.map +1 -0
- package/transpiled/outbox.d.ts +45 -0
- package/transpiled/outbox.js +448 -0
- package/transpiled/outbox.js.map +1 -0
- package/transpiled/outbox.measurements.d.ts +4 -0
- package/transpiled/outbox.measurements.js +38 -0
- package/transpiled/outbox.measurements.js.map +1 -0
- package/transpiled/parts.d.ts +20 -0
- package/transpiled/parts.js +23 -0
- package/transpiled/parts.js.map +1 -0
- package/transpiled/query/criteria.d.ts +6 -0
- package/transpiled/query/criteria.js +177 -0
- package/transpiled/query/criteria.js.map +1 -0
- package/transpiled/query/options.d.ts +3 -0
- package/transpiled/query/options.js +34 -0
- package/transpiled/query/options.js.map +1 -0
- package/transpiled/query.d.ts +9 -0
- package/transpiled/query.js +57 -0
- package/transpiled/query.js.map +1 -0
- package/transpiled/receiver.d.ts +20 -0
- package/transpiled/receiver.js +113 -0
- package/transpiled/receiver.js.map +1 -0
- package/transpiled/reflection.d.ts +9 -0
- package/transpiled/reflection.js +13 -0
- package/transpiled/reflection.js.map +1 -0
- package/transpiled/remote.d.ts +10 -0
- package/transpiled/remote.js +11 -0
- package/transpiled/remote.js.map +1 -0
- package/transpiled/rendering.d.ts +13 -0
- package/transpiled/rendering.js +21 -0
- package/transpiled/rendering.js.map +1 -0
- package/transpiled/safety.d.ts +5 -0
- package/transpiled/safety.js +26 -0
- package/transpiled/safety.js.map +1 -0
- package/transpiled/state.d.ts +36 -0
- package/transpiled/state.js +131 -0
- package/transpiled/state.js.map +1 -0
- package/transpiled/trail.d.ts +78 -0
- package/transpiled/trail.js +112 -0
- package/transpiled/trail.js.map +1 -0
- package/transpiled/transition.d.ts +14 -0
- package/transpiled/transition.js +51 -0
- package/transpiled/transition.js.map +1 -0
- package/transpiled/transmission.d.ts +8 -0
- package/transpiled/transmission.js +30 -0
- package/transpiled/transmission.js.map +1 -0
- package/transpiled/types/atomicity.d.ts +58 -0
- package/transpiled/types/atomicity.js +2 -0
- package/transpiled/types/atomicity.js.map +1 -0
- package/transpiled/types/bindings.d.ts +82 -0
- package/transpiled/types/bindings.js +2 -0
- package/transpiled/types/bindings.js.map +1 -0
- package/transpiled/types/bridges.d.ts +41 -0
- package/transpiled/types/bridges.js +2 -0
- package/transpiled/types/bridges.js.map +1 -0
- package/transpiled/types/extensions.d.ts +121 -0
- package/transpiled/types/extensions.js +2 -0
- package/transpiled/types/extensions.js.map +1 -0
- package/transpiled/types/inbox.d.ts +27 -0
- package/transpiled/types/inbox.js +2 -0
- package/transpiled/types/inbox.js.map +1 -0
- package/transpiled/types/index.d.ts +14 -0
- package/transpiled/types/index.js +9 -0
- package/transpiled/types/index.js.map +1 -0
- package/transpiled/types/message.d.ts +16 -0
- package/transpiled/types/message.js +2 -0
- package/transpiled/types/message.js.map +1 -0
- package/transpiled/types/operations.d.ts +15 -0
- package/transpiled/types/operations.js +2 -0
- package/transpiled/types/operations.js.map +1 -0
- package/transpiled/types/outbox.d.ts +68 -0
- package/transpiled/types/outbox.js +2 -0
- package/transpiled/types/outbox.js.map +1 -0
- package/transpiled/types/receiver.d.ts +9 -0
- package/transpiled/types/receiver.js +2 -0
- package/transpiled/types/receiver.js.map +1 -0
- package/transpiled/types/request.d.ts +119 -0
- package/transpiled/types/request.js +2 -0
- package/transpiled/types/request.js.map +1 -0
- package/transpiled/types/state.d.ts +9 -0
- package/transpiled/types/state.js +2 -0
- package/transpiled/types/state.js.map +1 -0
- package/transpiled/types/storages.d.ts +154 -0
- package/transpiled/types/storages.js +2 -0
- package/transpiled/types/storages.js.map +1 -0
- package/transpiled/unmanaged.d.ts +5 -0
- package/transpiled/unmanaged.js +7 -0
- package/transpiled/unmanaged.js.map +1 -0
- package/src/assignment.js +0 -31
- package/src/call.js +0 -42
- package/src/cascade.js +0 -35
- package/src/component.js +0 -41
- package/src/composition.js +0 -25
- package/src/connector.js +0 -183
- package/src/context.js +0 -55
- package/src/contract/conditions.js +0 -21
- package/src/contract/index.js +0 -7
- package/src/contract/reply.js +0 -25
- package/src/contract/request.js +0 -67
- package/src/contract/schemas/error.yaml +0 -9
- package/src/contract/schemas/index.js +0 -7
- package/src/contract/schemas/query.yaml +0 -30
- package/src/discovery.js +0 -44
- package/src/emission.js +0 -23
- package/src/entities/changeset.js +0 -38
- package/src/entities/entity.js +0 -74
- package/src/entities/factory.js +0 -33
- package/src/entities/index.js +0 -5
- package/src/entities/set.js +0 -15
- package/src/event.js +0 -40
- package/src/exceptions.js +0 -100
- package/src/exposition.js +0 -27
- package/src/index.js +0 -47
- package/src/locator.js +0 -54
- package/src/observation.js +0 -20
- package/src/operation.js +0 -69
- package/src/query/criteria.js +0 -41
- package/src/query/options.js +0 -40
- package/src/query.js +0 -41
- package/src/receiver.js +0 -61
- package/src/reflection.js +0 -28
- package/src/remote.js +0 -17
- package/src/state.js +0 -99
- package/src/transition.js +0 -67
- package/src/transmission.js +0 -33
- package/test/call.fixtures.js +0 -27
- package/test/call.test.js +0 -46
- package/test/component.fixtures.js +0 -21
- package/test/component.test.js +0 -40
- package/test/connector.fixtures.js +0 -40
- package/test/connector.test.js +0 -176
- package/test/context.fixtures.js +0 -18
- package/test/context.test.js +0 -29
- package/test/contract/conditions.test.js +0 -26
- package/test/contract/contract.fixtures.js +0 -33
- package/test/contract/request.test.js +0 -112
- package/test/emission.fixtures.js +0 -17
- package/test/emission.test.js +0 -35
- package/test/entities/entity.fixtures.js +0 -29
- package/test/entities/entity.test.js +0 -65
- package/test/entities/factory.fixtures.js +0 -18
- package/test/entities/factory.test.js +0 -48
- package/test/entities/set.fixtures.js +0 -11
- package/test/entities/set.test.js +0 -12
- package/test/event.fixtures.js +0 -29
- package/test/event.test.js +0 -118
- package/test/locator.test.js +0 -113
- package/test/query.fixtures.js +0 -100
- package/test/query.test.js +0 -86
- package/test/receiver.fixtures.js +0 -23
- package/test/receiver.test.js +0 -95
- package/test/reflection.test.js +0 -30
- package/test/state.fixtures.js +0 -47
- package/test/state.test.js +0 -46
- package/test/transmission.fixtures.js +0 -15
- package/test/transmission.test.js +0 -46
- package/types/bindings.d.ts +0 -40
- package/types/bridges.ts +0 -30
- package/types/component.d.ts +0 -9
- package/types/connector.d.ts +0 -18
- package/types/context.d.ts +0 -21
- package/types/entity.d.ts +0 -46
- package/types/event.d.ts +0 -11
- package/types/exception.d.ts +0 -4
- package/types/extensions.d.ts +0 -29
- package/types/index.ts +0 -14
- package/types/locator.d.ts +0 -13
- package/types/message.d.ts +0 -3
- package/types/operations.d.ts +0 -2
- package/types/query.d.ts +0 -15
- package/types/receiver.d.ts +0 -12
- package/types/reflection.d.ts +0 -14
- package/types/request.d.ts +0 -23
- package/types/state.d.ts +0 -35
- package/types/storages.d.ts +0 -71
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { EntitySet } from './entities/set.js';
|
|
2
|
+
import { StatePreconditionException, StateNotFoundException } from './exceptions.js';
|
|
3
|
+
/**
|
|
4
|
+
* What an operation acquires and commits. One method per scope a manifest may declare, which
|
|
5
|
+
* is how an operation reaches the one it was declared with.
|
|
6
|
+
*/
|
|
7
|
+
export class State {
|
|
8
|
+
storage;
|
|
9
|
+
#entities;
|
|
10
|
+
#outbox;
|
|
11
|
+
/** whether an entity exists by virtue of the identity it is looked up by */
|
|
12
|
+
#associated;
|
|
13
|
+
// eslint-disable-next-line max-params
|
|
14
|
+
constructor(storage, entities, outbox, associated) {
|
|
15
|
+
this.storage = storage;
|
|
16
|
+
this.#entities = entities;
|
|
17
|
+
this.#outbox = outbox;
|
|
18
|
+
this.#associated = associated === true;
|
|
19
|
+
}
|
|
20
|
+
init(id) {
|
|
21
|
+
return this.#entities.init(id);
|
|
22
|
+
}
|
|
23
|
+
fit(values) {
|
|
24
|
+
this.#entities.fit(values);
|
|
25
|
+
}
|
|
26
|
+
/** scope `entry` */
|
|
27
|
+
async entry(query, mutable = true) {
|
|
28
|
+
const record = await this.storage.get(query);
|
|
29
|
+
if (record !== null)
|
|
30
|
+
return this.#entities.object(record, mutable);
|
|
31
|
+
// an associated entity is whatever the identity it is looked up by names, so a miss on
|
|
32
|
+
// that identity alone is a blank rather than an absence
|
|
33
|
+
if (this.#associated &&
|
|
34
|
+
query.id !== undefined &&
|
|
35
|
+
query.criteria === undefined &&
|
|
36
|
+
query.version === undefined)
|
|
37
|
+
return this.init(query.id);
|
|
38
|
+
if (query.version !== undefined)
|
|
39
|
+
throw new StatePreconditionException();
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
/** scope `entries` */
|
|
43
|
+
async entries(query, mutable = true) {
|
|
44
|
+
const recordset = await this.storage.find(query);
|
|
45
|
+
const ids = query.ids;
|
|
46
|
+
const missing = this.#associated && ids !== undefined && recordset.length < ids.length;
|
|
47
|
+
const init = missing
|
|
48
|
+
? ids.filter((id) => !recordset.some((record) => record.id === id))
|
|
49
|
+
: undefined;
|
|
50
|
+
return this.#entities.objects(recordset, init, mutable);
|
|
51
|
+
}
|
|
52
|
+
/** scope `stream` */
|
|
53
|
+
async stream(query) {
|
|
54
|
+
return this.storage.stream(query);
|
|
55
|
+
}
|
|
56
|
+
/** scope `changeset` */
|
|
57
|
+
changeset(query) {
|
|
58
|
+
return this.#entities.changeset(query);
|
|
59
|
+
}
|
|
60
|
+
/** scope `none`: an operation that acquires nothing still asks for its scope */
|
|
61
|
+
none() {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
/** get-or-create, in one indivisible step */
|
|
65
|
+
/** what the call under this identity answered, or `null` where it was never made */
|
|
66
|
+
async recall(id) {
|
|
67
|
+
return (await this.storage.inbox?.recall(id)) ?? null;
|
|
68
|
+
}
|
|
69
|
+
// eslint-disable-next-line max-params
|
|
70
|
+
async ensure(query, properties, input, call) {
|
|
71
|
+
const object = this.#entities.init();
|
|
72
|
+
const blank = object.get();
|
|
73
|
+
Object.assign(blank, properties);
|
|
74
|
+
object.set(blank);
|
|
75
|
+
const row = this.#outbox?.row(object.event(input));
|
|
76
|
+
const record = await this.storage.ensure(query, properties, object.get(), row, call);
|
|
77
|
+
// whatever came back under another id was already there, and an effect never commits it
|
|
78
|
+
if (record.id !== blank.id)
|
|
79
|
+
return this.#entities.object(record, NOT_MUTABLE);
|
|
80
|
+
await this.#publish(row);
|
|
81
|
+
return object;
|
|
82
|
+
}
|
|
83
|
+
async commit(state, input, call) {
|
|
84
|
+
if (state instanceof EntitySet)
|
|
85
|
+
return this.massCommit(state, input);
|
|
86
|
+
// the row is built before the write so that the storage can commit it in the same
|
|
87
|
+
// transaction, closing the window this used to have
|
|
88
|
+
const row = this.#outbox?.row(state.event(input));
|
|
89
|
+
const ok = await this.storage.store(state.get(), row, call);
|
|
90
|
+
if (ok)
|
|
91
|
+
await this.#publish(row);
|
|
92
|
+
return ok;
|
|
93
|
+
}
|
|
94
|
+
async massCommit(state, input) {
|
|
95
|
+
const outbox = this.#outbox;
|
|
96
|
+
const rows = outbox === undefined
|
|
97
|
+
? undefined
|
|
98
|
+
: state.events(input).map((event) => outbox.row(event));
|
|
99
|
+
const ok = await this.storage.massStore(state.get(), rows);
|
|
100
|
+
if (ok && rows !== undefined)
|
|
101
|
+
await Promise.all(rows.map(async (row) => this.#publish(row)));
|
|
102
|
+
return ok;
|
|
103
|
+
}
|
|
104
|
+
async apply(state, input, call) {
|
|
105
|
+
// an assignment's images are the write's own, so the storage fills them in
|
|
106
|
+
const row = this.#outbox?.row({ input });
|
|
107
|
+
const result = await this.storage.upsert(state.query, state.export(), row, call);
|
|
108
|
+
if (result === null)
|
|
109
|
+
throw state.query.version === undefined
|
|
110
|
+
? new StateNotFoundException()
|
|
111
|
+
: new StatePreconditionException();
|
|
112
|
+
if (row !== undefined) {
|
|
113
|
+
// a storage that does not know how leaves the event with what it was given
|
|
114
|
+
row.event.state ??= result;
|
|
115
|
+
row.event.origin ??= null;
|
|
116
|
+
await this.#publish(row);
|
|
117
|
+
}
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Without a durable outbox this is the emission itself, and the operation waits for it;
|
|
122
|
+
* with one the row is already committed and this returns at once.
|
|
123
|
+
*/
|
|
124
|
+
async #publish(row) {
|
|
125
|
+
if (row !== undefined)
|
|
126
|
+
await this.#outbox?.publish(row);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/** an effect never commits what `ensure` hands it, see `Effect` */
|
|
130
|
+
const NOT_MUTABLE = false;
|
|
131
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../source/state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** What one invocation carries for the calls it makes. */
|
|
2
|
+
export interface Invocation {
|
|
3
|
+
/** the hops it came by, oldest first, ending with its own */
|
|
4
|
+
hops: string[];
|
|
5
|
+
/**
|
|
6
|
+
* The identity of the request being served. Absent where the caller stamped none, which is a
|
|
7
|
+
* caller from before this existed; a call made under one mints its own identity instead.
|
|
8
|
+
*/
|
|
9
|
+
id?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Whether what it may reach is a safe operation and nothing else. Carried onto every call it
|
|
12
|
+
* makes, and never dropped: the request that arrived said so, and so does every one below it.
|
|
13
|
+
*/
|
|
14
|
+
readonly?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* How many calls this invocation has already made to each endpoint. Two calls to one endpoint
|
|
17
|
+
* are two calls, and their identities have to say so — without this an operation looping over
|
|
18
|
+
* five items would make one identity five times and have four of them refused.
|
|
19
|
+
*
|
|
20
|
+
* Made where the first call is made: most operations make none.
|
|
21
|
+
*/
|
|
22
|
+
calls?: Map<string, number>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* How many times one hop may appear before the call is refused, and how long a chain may grow
|
|
26
|
+
* at all. Repetition is the signal and names the cycle; depth is the backstop for a chain that
|
|
27
|
+
* grows without repeating a hop, and it is what bounds the array on the wire.
|
|
28
|
+
*/
|
|
29
|
+
export interface Limits {
|
|
30
|
+
repeats: number;
|
|
31
|
+
depth: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* What the environment says, read where a component is built rather than once for the process,
|
|
35
|
+
* as the outbox reads its own — so a composition booted after a variable was set sees it.
|
|
36
|
+
*
|
|
37
|
+
* `TOA_TRAIL_REPEATS=0` refuses nothing: the chain is still stamped, carried and bounded. It is
|
|
38
|
+
* the off switch, and it exists because this refuses calls an application may be making today —
|
|
39
|
+
* a handshake written as `a > b > a > b > a` is three occurrences of one hop. A breaker with no
|
|
40
|
+
* way to open it is itself the outage.
|
|
41
|
+
*/
|
|
42
|
+
export declare function limits(): Limits;
|
|
43
|
+
/** The invocation running now, where there is one. */
|
|
44
|
+
export declare function current(): Invocation | undefined;
|
|
45
|
+
/** Runs `task` as the invocation the chain now ends with. */
|
|
46
|
+
export declare function follow<T>(invocation: Invocation, task: () => Promise<T>): Promise<T>;
|
|
47
|
+
/**
|
|
48
|
+
* The ordinal of the call `invocation` is about to make to `endpoint`, counting from zero, and
|
|
49
|
+
* counts it. Read once per call, on the caller's own path, which is where the order is the order
|
|
50
|
+
* the algorithm made them in.
|
|
51
|
+
*/
|
|
52
|
+
export declare function ordinal(invocation: Invocation, endpoint: string): number;
|
|
53
|
+
/**
|
|
54
|
+
* The chain this hop makes, or a raise where it is one hop too many: a hop already taken
|
|
55
|
+
* `repeats` times is a cycle, and a chain past `depth` is one nothing meant to make. Both are
|
|
56
|
+
* permanent, so what hits one is set aside rather than tried again into the same loop.
|
|
57
|
+
*
|
|
58
|
+
* The chain is copied rather than appended to, which is what makes it a path down the call
|
|
59
|
+
* tree rather than a log of everything that happened — an operation calling one endpoint fifty
|
|
60
|
+
* times makes fifty chains of one hop, not one chain of fifty.
|
|
61
|
+
*/
|
|
62
|
+
export declare function extend(inbound: unknown, hop: string, limits: Limits): string[];
|
|
63
|
+
/**
|
|
64
|
+
* How an event is named in a chain. The sigil is what tells it from an operation: the two are
|
|
65
|
+
* the same shape, a component may declare one of each under a single name, and `sync` is both
|
|
66
|
+
* the event every component inherits and an ordinary name for an operation.
|
|
67
|
+
*
|
|
68
|
+
* What an operator rewires to break a cycle is the subscription rather than the operation, so a
|
|
69
|
+
* chain that named only operations would not say how a component was re-entered.
|
|
70
|
+
*/
|
|
71
|
+
export declare function event(destination: string): string;
|
|
72
|
+
/**
|
|
73
|
+
* What came off the wire, as a chain and nothing else. Neither the request contract nor a
|
|
74
|
+
* message validates this — a request is not validated at all once it is `authentic` — so a
|
|
75
|
+
* malformed one would otherwise become a `TypeError` where a named exception was contracted
|
|
76
|
+
* for. Bounding it is `extend`'s, one hop later, which is the only place the limits are known.
|
|
77
|
+
*/
|
|
78
|
+
export declare function received(value: unknown): string[];
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { environment } from '@toa.io/generic';
|
|
3
|
+
import { LoopException } from './exceptions.js';
|
|
4
|
+
/**
|
|
5
|
+
* What the invocation running now came by: the chain of hops it passed through and the rule that
|
|
6
|
+
* refuses one that has come back to where it had been, and the identity of the call it is
|
|
7
|
+
* serving, from which the identity of every call it makes is derived.
|
|
8
|
+
*
|
|
9
|
+
* Ambient, like the trace context and for the same reason: an algorithm's `context` is built
|
|
10
|
+
* once per operation at boot and shared by every invocation of it, so nothing per-invocation
|
|
11
|
+
* can be handed to it. It is not the trace context's own store, which `console.span` replaces
|
|
12
|
+
* on every span and does not enter at all when the trace is unsampled — a chain that
|
|
13
|
+
* disappears under sampling is a breaker that stops breaking in production.
|
|
14
|
+
*
|
|
15
|
+
* One writer, `Component.invoke`, where the hop is appended and the identity and the safety of
|
|
16
|
+
* what may be reached are put in scope; two readers, `Call.invoke`, which puts them on the request
|
|
17
|
+
* it is about to send, and `Outbox.row`, which writes the chain onto the row so it outlives the
|
|
18
|
+
* operation that caused it.
|
|
19
|
+
*/
|
|
20
|
+
// as openspan holds its own: a process may carry two copies of this module, and a chain that
|
|
21
|
+
// is empty because the writer sat in the other one is a breaker that never fires. The name says
|
|
22
|
+
// what is stored, so a copy carrying the chain alone does not answer where the whole is expected
|
|
23
|
+
const KEY = Symbol.for('toa.core.invocation');
|
|
24
|
+
const storage = (globalThis[KEY] ??= new AsyncLocalStorage());
|
|
25
|
+
/**
|
|
26
|
+
* What the environment says, read where a component is built rather than once for the process,
|
|
27
|
+
* as the outbox reads its own — so a composition booted after a variable was set sees it.
|
|
28
|
+
*
|
|
29
|
+
* `TOA_TRAIL_REPEATS=0` refuses nothing: the chain is still stamped, carried and bounded. It is
|
|
30
|
+
* the off switch, and it exists because this refuses calls an application may be making today —
|
|
31
|
+
* a handshake written as `a > b > a > b > a` is three occurrences of one hop. A breaker with no
|
|
32
|
+
* way to open it is itself the outage.
|
|
33
|
+
*/
|
|
34
|
+
export function limits() {
|
|
35
|
+
return {
|
|
36
|
+
repeats: number('TOA_TRAIL_REPEATS', 3),
|
|
37
|
+
depth: number('TOA_TRAIL_DEPTH', 32)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** The invocation running now, where there is one. */
|
|
41
|
+
export function current() {
|
|
42
|
+
return storage.getStore();
|
|
43
|
+
}
|
|
44
|
+
/** Runs `task` as the invocation the chain now ends with. */
|
|
45
|
+
export async function follow(invocation, task) {
|
|
46
|
+
return storage.run(invocation, task);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The ordinal of the call `invocation` is about to make to `endpoint`, counting from zero, and
|
|
50
|
+
* counts it. Read once per call, on the caller's own path, which is where the order is the order
|
|
51
|
+
* the algorithm made them in.
|
|
52
|
+
*/
|
|
53
|
+
export function ordinal(invocation, endpoint) {
|
|
54
|
+
const calls = (invocation.calls ??= new Map());
|
|
55
|
+
const made = calls.get(endpoint) ?? 0;
|
|
56
|
+
calls.set(endpoint, made + 1);
|
|
57
|
+
return made;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The chain this hop makes, or a raise where it is one hop too many: a hop already taken
|
|
61
|
+
* `repeats` times is a cycle, and a chain past `depth` is one nothing meant to make. Both are
|
|
62
|
+
* permanent, so what hits one is set aside rather than tried again into the same loop.
|
|
63
|
+
*
|
|
64
|
+
* The chain is copied rather than appended to, which is what makes it a path down the call
|
|
65
|
+
* tree rather than a log of everything that happened — an operation calling one endpoint fifty
|
|
66
|
+
* times makes fifty chains of one hop, not one chain of fifty.
|
|
67
|
+
*/
|
|
68
|
+
export function extend(inbound, hop, limits) {
|
|
69
|
+
const hops = clip(received(inbound), limits.depth);
|
|
70
|
+
const trail = [...hops, hop];
|
|
71
|
+
if (limits.repeats === 0)
|
|
72
|
+
return trail;
|
|
73
|
+
if (trail.length > limits.depth)
|
|
74
|
+
throw new LoopException(`Call chain is ${trail.length} hops deep`, trail);
|
|
75
|
+
let seen = 0;
|
|
76
|
+
for (const passed of hops)
|
|
77
|
+
if (passed === hop)
|
|
78
|
+
seen++;
|
|
79
|
+
if (seen + 1 >= limits.repeats)
|
|
80
|
+
throw new LoopException(`'${hop}' is hop ${seen + 1} of this chain`, trail);
|
|
81
|
+
return trail;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* How an event is named in a chain. The sigil is what tells it from an operation: the two are
|
|
85
|
+
* the same shape, a component may declare one of each under a single name, and `sync` is both
|
|
86
|
+
* the event every component inherits and an ordinary name for an operation.
|
|
87
|
+
*
|
|
88
|
+
* What an operator rewires to break a cycle is the subscription rather than the operation, so a
|
|
89
|
+
* chain that named only operations would not say how a component was re-entered.
|
|
90
|
+
*/
|
|
91
|
+
export function event(destination) {
|
|
92
|
+
return '~' + destination;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* What came off the wire, as a chain and nothing else. Neither the request contract nor a
|
|
96
|
+
* message validates this — a request is not validated at all once it is `authentic` — so a
|
|
97
|
+
* malformed one would otherwise become a `TypeError` where a named exception was contracted
|
|
98
|
+
* for. Bounding it is `extend`'s, one hop later, which is the only place the limits are known.
|
|
99
|
+
*/
|
|
100
|
+
export function received(value) {
|
|
101
|
+
return Array.isArray(value) ? value.filter((hop) => typeof hop === 'string') : [];
|
|
102
|
+
}
|
|
103
|
+
// with the hop being appended that is one past the cap, which is enough to be refused; the
|
|
104
|
+
// rest is a message buying memory, and where the rule is off it is what bounds the chain
|
|
105
|
+
function clip(hops, depth) {
|
|
106
|
+
return hops.length > depth ? hops.slice(0, depth) : hops;
|
|
107
|
+
}
|
|
108
|
+
function number(variable, fallback) {
|
|
109
|
+
const declared = Number(environment.get(variable));
|
|
110
|
+
return Number.isNaN(declared) || declared < 0 ? fallback : declared;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=trail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trail.js","sourceRoot":"","sources":["../source/trail.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Operation } from './operation.ts';
|
|
2
|
+
import type { Contracts, Definition, Store } from './operation.ts';
|
|
3
|
+
import type { Cascade } from './cascade.ts';
|
|
4
|
+
import type { State } from './state.ts';
|
|
5
|
+
import type { Query as Translator } from './query.ts';
|
|
6
|
+
export declare class Transition extends Operation {
|
|
7
|
+
#private;
|
|
8
|
+
/** a transition is the only operation that commits */
|
|
9
|
+
protected mutable: boolean;
|
|
10
|
+
constructor(cascade: Cascade, scope: State, contract: Contracts, query: Translator, definition: Definition);
|
|
11
|
+
protected process(store: Store): Promise<any>;
|
|
12
|
+
protected acquire(store: Store): Promise<void>;
|
|
13
|
+
protected commit(store: Store): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { retry } from '@toa.io/generic';
|
|
2
|
+
import { Operation } from './operation.js';
|
|
3
|
+
import { StateConcurrencyException, StateNotFoundException } from './exceptions.js';
|
|
4
|
+
export class Transition extends Operation {
|
|
5
|
+
/** a transition is the only operation that commits */
|
|
6
|
+
mutable = true;
|
|
7
|
+
#concurrency;
|
|
8
|
+
// eslint-disable-next-line max-params
|
|
9
|
+
constructor(cascade, scope, contract, query, definition) {
|
|
10
|
+
super(cascade, scope, contract, query, definition);
|
|
11
|
+
this.#concurrency = definition.concurrency;
|
|
12
|
+
}
|
|
13
|
+
async process(store) {
|
|
14
|
+
return retry(async (retry) => this.#retry(store, retry), RETRY);
|
|
15
|
+
}
|
|
16
|
+
async acquire(store) {
|
|
17
|
+
const { request } = store;
|
|
18
|
+
store.scope =
|
|
19
|
+
request.query === undefined ? this.scope.init() : await this.query(request.query);
|
|
20
|
+
const entity = store.scope;
|
|
21
|
+
if (entity === null ||
|
|
22
|
+
(entity.deleted && request.query?.options?.deleted !== true))
|
|
23
|
+
throw new StateNotFoundException();
|
|
24
|
+
store.state = entity.get();
|
|
25
|
+
}
|
|
26
|
+
async commit(store) {
|
|
27
|
+
const { scope, state, reply, retry } = store;
|
|
28
|
+
if (reply.error !== undefined)
|
|
29
|
+
return;
|
|
30
|
+
const entity = scope;
|
|
31
|
+
entity.set(state);
|
|
32
|
+
const result = await this.scope.commit(entity, store.request.input, this.call(store));
|
|
33
|
+
if (result !== false)
|
|
34
|
+
return;
|
|
35
|
+
if (this.#concurrency === 'retry')
|
|
36
|
+
await retry?.();
|
|
37
|
+
else
|
|
38
|
+
throw new StateConcurrencyException();
|
|
39
|
+
}
|
|
40
|
+
async #retry(store, retry) {
|
|
41
|
+
store.retry = retry;
|
|
42
|
+
return super.process(store);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const RETRY = {
|
|
46
|
+
base: 10,
|
|
47
|
+
max: 5000,
|
|
48
|
+
dispersion: 1,
|
|
49
|
+
retries: 32
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=transition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transition.js","sourceRoot":"","sources":["../source/transition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAOnF,MAAM,OAAO,UAAW,SAAQ,SAAS;IACvC,sDAAsD;IACnC,OAAO,GAAG,IAAI,CAAA;IAExB,YAAY,CAAoB;IAEzC,sCAAsC;IACtC,YACE,OAAgB,EAChB,KAAY,EACZ,QAAmB,EACnB,KAAiB,EACjB,UAAsB;QAEtB,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,CAAA;QAElD,IAAI,CAAC,YAAY,GAAG,UAAU,CAAC,WAAW,CAAA;IAC5C,CAAC;IAEkB,KAAK,CAAC,OAAO,CAAC,KAAY;QAC3C,OAAO,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAA;IACjE,CAAC;IAEkB,KAAK,CAAC,OAAO,CAAC,KAAY;QAC3C,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAA;QAEzB,KAAK,CAAC,KAAK;YACT,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QAEnF,MAAM,MAAM,GAAG,KAAK,CAAC,KAAsB,CAAA;QAE3C,IACE,MAAM,KAAK,IAAI;YACf,CAAC,MAAM,CAAC,OAAO,IAAK,OAAO,CAAC,KAAa,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;YAErE,MAAM,IAAI,sBAAsB,EAAE,CAAA;QAEpC,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,GAAG,EAAE,CAAA;IAC5B,CAAC;IAEkB,KAAK,CAAC,MAAM,CAAC,KAAY;QAC1C,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,KAAK,CAAA;QAE5C,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;YAAE,OAAM;QAErC,MAAM,MAAM,GAAG,KAAe,CAAA;QAE9B,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QAEjB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;QAErF,IAAI,MAAM,KAAK,KAAK;YAAE,OAAM;QAE5B,IAAI,IAAI,CAAC,YAAY,KAAK,OAAO;YAAE,MAAM,KAAK,EAAE,EAAE,CAAA;;YAC7C,MAAM,IAAI,yBAAyB,EAAE,CAAA;IAC5C,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAY,EAAE,KAAyB;QAClD,KAAK,CAAC,KAAK,GAAG,KAAK,CAAA;QAEnB,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAC7B,CAAC;CACF;AAED,MAAM,KAAK,GAAG;IACZ,IAAI,EAAE,EAAE;IACR,GAAG,EAAE,IAAI;IACT,UAAU,EAAE,CAAC;IACb,OAAO,EAAE,EAAE;CACZ,CAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Connector } from './connector.ts';
|
|
2
|
+
import type { Consumer, Terms } from './types/bindings.ts';
|
|
3
|
+
import type { Request } from './types/request.ts';
|
|
4
|
+
export declare class Transmission extends Connector {
|
|
5
|
+
#private;
|
|
6
|
+
constructor(bindings: Consumer[]);
|
|
7
|
+
request(request: Request, terms?: Terms): Promise<any>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Connector } from './connector.js';
|
|
2
|
+
import { TransmissionException } from './exceptions.js';
|
|
3
|
+
export class Transmission extends Connector {
|
|
4
|
+
#bindings;
|
|
5
|
+
constructor(bindings) {
|
|
6
|
+
super();
|
|
7
|
+
this.#bindings = bindings;
|
|
8
|
+
this.depends(bindings);
|
|
9
|
+
}
|
|
10
|
+
async request(request, terms) {
|
|
11
|
+
let reply = false;
|
|
12
|
+
let i = 0;
|
|
13
|
+
while (reply === false && i < this.#bindings.length) {
|
|
14
|
+
const binding = this.#bindings[i];
|
|
15
|
+
i++;
|
|
16
|
+
if (request?.task === true) {
|
|
17
|
+
if (binding.task === undefined)
|
|
18
|
+
continue;
|
|
19
|
+
await binding.task(request);
|
|
20
|
+
reply = null;
|
|
21
|
+
}
|
|
22
|
+
else
|
|
23
|
+
reply = await binding.request(request, terms);
|
|
24
|
+
}
|
|
25
|
+
if (reply === false)
|
|
26
|
+
throw new TransmissionException(`All (${this.#bindings.length}) bindings rejected.`);
|
|
27
|
+
return reply;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=transmission.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transmission.js","sourceRoot":"","sources":["../source/transmission.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AAIvD,MAAM,OAAO,YAAa,SAAQ,SAAS;IAChC,SAAS,CAAY;IAE9B,YAAmB,QAAoB;QACrC,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAA;QACzB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IACxB,CAAC;IAEM,KAAK,CAAC,OAAO,CAAC,OAAgB,EAAE,KAAa;QAClD,IAAI,KAAK,GAAQ,KAAK,CAAA;QACtB,IAAI,CAAC,GAAG,CAAC,CAAA;QAET,OAAO,KAAK,KAAK,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;YACpD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;YAEjC,CAAC,EAAE,CAAA;YAEH,IAAI,OAAO,EAAE,IAAI,KAAK,IAAI,EAAE,CAAC;gBAC3B,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;oBAAE,SAAQ;gBAExC,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;gBAC3B,KAAK,GAAG,IAAI,CAAA;YACd,CAAC;;gBAAM,KAAK,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QACtD,CAAC;QAED,IAAI,KAAK,KAAK,KAAK;YACjB,MAAM,IAAI,qBAAqB,CAAC,QAAQ,IAAI,CAAC,SAAS,CAAC,MAAM,sBAAsB,CAAC,CAAA;QAEtF,OAAO,KAAK,CAAA;IACd,CAAC;CACF"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { Connector } from '../connector.ts';
|
|
2
|
+
/** Which of the group this replica is, and how many of them there are. */
|
|
3
|
+
export interface Assignment {
|
|
4
|
+
i: number;
|
|
5
|
+
n: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* What one group of replicas decides together, in one place. The decisions here are the ones
|
|
9
|
+
* processes cannot arrange by talking to each other: they need a single arbiter and a step
|
|
10
|
+
* indivisible from its point of view.
|
|
11
|
+
*/
|
|
12
|
+
export interface Atom extends Connector {
|
|
13
|
+
/**
|
|
14
|
+
* An exclusive claim on slots of `0..total`: while this replica holds one, no other
|
|
15
|
+
* replica of the group does. Answered from memory, so it costs nothing to ask.
|
|
16
|
+
*
|
|
17
|
+
* `null` while this replica owns nothing — after a restart, during a rollout, or while
|
|
18
|
+
* coordination is unreachable. Whoever asks must be able to stand down: acting on a claim
|
|
19
|
+
* that cannot be supported is a different guarantee, not a degraded one.
|
|
20
|
+
*/
|
|
21
|
+
slots(total: number): number[] | null;
|
|
22
|
+
/**
|
|
23
|
+
* Calls `listener` with the assignment this replica holds, and again whenever it changes —
|
|
24
|
+
* one arrived, was lost, or the group resized. Answers with what removes the listener
|
|
25
|
+
* again.
|
|
26
|
+
*
|
|
27
|
+
* A change and not a heartbeat: a group that stays as it is never calls back. It is called
|
|
28
|
+
* once as it is added, with the claim as it stands.
|
|
29
|
+
*/
|
|
30
|
+
onassigned(listener: (assignment: Assignment | null) => void): () => void;
|
|
31
|
+
/**
|
|
32
|
+
* Debt the group has run up under each key, in milliseconds. Every call adds its own deltas
|
|
33
|
+
* and reads back where the group stands, so a replica reports what it alone has spent and
|
|
34
|
+
* still decides on what all of them have.
|
|
35
|
+
*
|
|
36
|
+
* Rejects where there is nothing to arbitrate through.
|
|
37
|
+
*/
|
|
38
|
+
meter(keys: string[], deltas: number[]): Promise<number[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Runs `routine` holding `keys`, and while it holds them no other replica of the group
|
|
41
|
+
* does. Waits for as long as it takes to acquire them.
|
|
42
|
+
*
|
|
43
|
+
* The lease is extended for as long as the routine runs. An extension that fails aborts the
|
|
44
|
+
* signal the routine is given, which is the only way it learns it no longer holds what it
|
|
45
|
+
* is working under.
|
|
46
|
+
*
|
|
47
|
+
* Rejects where there is nothing to arbitrate through.
|
|
48
|
+
*/
|
|
49
|
+
lock<T>(keys: string | string[], routine: (signal: AbortSignal & {
|
|
50
|
+
error: Error;
|
|
51
|
+
}, context: unknown) => Promise<T>): Promise<T>;
|
|
52
|
+
}
|
|
53
|
+
export interface Factory {
|
|
54
|
+
/** @param group what the replicas deciding together have in common */
|
|
55
|
+
atom(group: string, options?: {
|
|
56
|
+
interval?: number;
|
|
57
|
+
}): Atom;
|
|
58
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomicity.js","sourceRoot":"","sources":["../../source/types/atomicity.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { Readable } from 'node:stream';
|
|
2
|
+
import type { Connector } from '../connector.ts';
|
|
3
|
+
import type { Locator } from '../locator.ts';
|
|
4
|
+
import type { Component } from '../component.ts';
|
|
5
|
+
import type { Receiver } from './receiver.ts';
|
|
6
|
+
import type { Message } from './message.ts';
|
|
7
|
+
import type { Reply, Request } from './request.ts';
|
|
8
|
+
/** What a binding module exports beside its factory. */
|
|
9
|
+
export interface Properties {
|
|
10
|
+
/** delivers asynchronously; an event's binding must */
|
|
11
|
+
async?: boolean;
|
|
12
|
+
/** in-process: its producers are connected first and torn down last */
|
|
13
|
+
local?: boolean;
|
|
14
|
+
/** carries a call whose input holds a stream; an endpoint that takes one is offered to no other */
|
|
15
|
+
streams?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* How a call is carried, as its caller settled it. None of it is part of the request, and none of
|
|
19
|
+
* it is sent as part of one.
|
|
20
|
+
*/
|
|
21
|
+
export interface Terms {
|
|
22
|
+
/** the process an addressed call goes to */
|
|
23
|
+
instance?: string;
|
|
24
|
+
/** milliseconds the caller of an addressed call waits */
|
|
25
|
+
timeout?: number;
|
|
26
|
+
/** aborts when the caller stops waiting */
|
|
27
|
+
signal?: AbortSignal;
|
|
28
|
+
}
|
|
29
|
+
export interface Consumer extends Connector {
|
|
30
|
+
/**
|
|
31
|
+
* `false` says this binding does not carry the endpoint, and the transmission tries the
|
|
32
|
+
* next one. A `Readable` is a streamed reply.
|
|
33
|
+
*/
|
|
34
|
+
request(request: Request, terms?: Terms): Promise<Reply | Readable | false>;
|
|
35
|
+
/**
|
|
36
|
+
* Absent where the binding cannot enqueue — a transmission skips a binding that offers
|
|
37
|
+
* no `task` rather than failing over it.
|
|
38
|
+
*/
|
|
39
|
+
task?(request: Request): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
export interface Emitter extends Connector {
|
|
42
|
+
emit(message: Message): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
export interface Broadcast<L extends string = string> extends Connector {
|
|
45
|
+
transmit<T>(label: L, payload: T): Promise<void>;
|
|
46
|
+
receive<T>(label: L, callback: (payload: T) => void | Promise<void>): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
export interface Factory {
|
|
49
|
+
/**
|
|
50
|
+
* `stateful` are the endpoints among `endpoints` that take addressed calls only, and
|
|
51
|
+
* `streamed` those that take one of their input properties as a stream.
|
|
52
|
+
*/
|
|
53
|
+
producer(locator: Locator, endpoints: string[], component: Component, stateful?: string[], streamed?: string[]): Connector;
|
|
54
|
+
consumer(locator: Locator, endpoint: string): Consumer;
|
|
55
|
+
/** only the binding an event declares is asked for one */
|
|
56
|
+
emitter?(locator: Locator, label: string): Emitter;
|
|
57
|
+
/** `group` is absent for an exclusive subscription */
|
|
58
|
+
receiver?(locator: Locator, label: string, group: string | undefined, receiver: Receiver): Connector;
|
|
59
|
+
broadcast?(name: string, group?: string): Broadcast;
|
|
60
|
+
/**
|
|
61
|
+
* A channel is a name the caller picks, from which the binding derives whatever its
|
|
62
|
+
* transport needs, and `uris` are the brokers to carry it over — so a second broker set
|
|
63
|
+
* costs the binding no configuration and no variable of its own.
|
|
64
|
+
*/
|
|
65
|
+
outbound?(channel: string, uris: string[]): Outbound;
|
|
66
|
+
/** what arrives on `channel` under `label` */
|
|
67
|
+
inbound?(channel: string, uris: string[], label: string, sink: Inbound): Connector;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Publishes to a channel, addressed by label. It forwards messages and nothing else: what
|
|
71
|
+
* `send` is handed is what is published, with no envelope, no field and no header of the
|
|
72
|
+
* binding's own, and none stripped — because a message shape is often somebody else's
|
|
73
|
+
* contract, and an extension shipping changes into another system must be able to send
|
|
74
|
+
* exactly what that system accepts.
|
|
75
|
+
*/
|
|
76
|
+
export interface Outbound extends Connector {
|
|
77
|
+
send(label: string, message: object): Promise<void>;
|
|
78
|
+
}
|
|
79
|
+
/** What a binding hands a message to. */
|
|
80
|
+
export interface Inbound {
|
|
81
|
+
accept(message: object): Promise<void>;
|
|
82
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bindings.js","sourceRoot":"","sources":["../../source/types/bindings.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Readable } from 'node:stream';
|
|
2
|
+
import type { Connector } from '../connector.ts';
|
|
3
|
+
import type { Context } from '../context.ts';
|
|
4
|
+
import type { Event as StateEvent } from './state.ts';
|
|
5
|
+
import type { Reply, Request } from './request.ts';
|
|
6
|
+
/**
|
|
7
|
+
* What runs one endpoint. `mount` and `unmount` are the component author's, called by the
|
|
8
|
+
* bridge as it opens and closes; core only ever asks for `execute`.
|
|
9
|
+
*/
|
|
10
|
+
export interface Algorithm extends Connector {
|
|
11
|
+
execute(input: any, scope?: object | object[]): Promise<Reply | Readable>;
|
|
12
|
+
}
|
|
13
|
+
export interface Event extends Connector {
|
|
14
|
+
condition(event: StateEvent): Promise<boolean>;
|
|
15
|
+
payload(event: StateEvent): Promise<object>;
|
|
16
|
+
}
|
|
17
|
+
export interface Receiver extends Connector {
|
|
18
|
+
condition(payload: object): Promise<boolean>;
|
|
19
|
+
/** the trailing arguments are what the receiver declaration named */
|
|
20
|
+
request(payload: object, ...args: unknown[]): Promise<Request>;
|
|
21
|
+
}
|
|
22
|
+
/** Runs on every change to an entity's state, before the contract is applied. */
|
|
23
|
+
export interface Guard {
|
|
24
|
+
fit(state: object, origin: object | null): boolean;
|
|
25
|
+
}
|
|
26
|
+
/** Connectors whose lifecycle moments are the component's. */
|
|
27
|
+
export interface RunCommands {
|
|
28
|
+
preflight?: Connector;
|
|
29
|
+
settle?: Connector;
|
|
30
|
+
ready?: Connector;
|
|
31
|
+
dispose?: Connector;
|
|
32
|
+
/** what the component pauses while the process is halted, and takes again after */
|
|
33
|
+
quiescence?: Connector;
|
|
34
|
+
}
|
|
35
|
+
export interface Factory {
|
|
36
|
+
algorithm(path: string, endpoint: string, context: Context): Algorithm | Promise<Algorithm>;
|
|
37
|
+
event?(path: string, label: string, context: Context): Event | Promise<Event>;
|
|
38
|
+
receiver?(path: string, label: string): Receiver | Promise<Receiver>;
|
|
39
|
+
guard?(path: string, name: string, context: Context): Guard | Promise<Guard>;
|
|
40
|
+
rc?(path: string, context: Context): Promise<RunCommands | undefined>;
|
|
41
|
+
}
|