@makaio/framework 1.0.0-dev-1787949057928 → 1.0.0-dev-1788511348215
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/dist/.makaio-build.json +2 -2
- package/dist/attempt-record-codec-dC9LSFUE.mjs +1 -0
- package/dist/authority-state-bootstrap-eitx6Ukn.mjs +1 -0
- package/dist/bus/index.mjs +1 -1
- package/dist/capabilities-DEvmD9VJ.mjs +1 -0
- package/dist/contracts/artifact/index.d.mts +1 -1
- package/dist/contracts/capabilities/index.d.mts +2 -2
- package/dist/contracts/capabilities/index.mjs +1 -1
- package/dist/contracts/client/index.d.mts +1 -1
- package/dist/contracts/extension/index.d.mts +2 -2
- package/dist/contracts/index.d.mts +12 -12
- package/dist/contracts/index.mjs +1 -1
- package/dist/contracts/worker/index.d.mts +2 -0
- package/dist/contracts/worker/index.mjs +1 -0
- package/dist/contracts/workflow/index.d.mts +1 -1
- package/dist/contracts/workflow/index.mjs +1 -1
- package/dist/{execution-attempt-repository-BJ2W6wwx.d.mts → execution-attempt-repository-DY4KTptE.d.mts} +359 -57
- package/dist/{index-8NdPPzUh2.d.mts → index-6trZYnUP2.d.mts} +12 -12
- package/dist/{index-CPi0UpRh.d.mts → index-B_8bA2JW.d.mts} +1 -1
- package/dist/{index-DfUpqwBa2.d.mts → index-BmilK-co2.d.mts} +53 -53
- package/dist/{index-CMekqu_D.d.mts → index-C6aSnO8m.d.mts} +42 -42
- package/dist/{index-BUwDmh1I.d.mts → index-CDud4NAm.d.mts} +10 -10
- package/dist/{index-CYjgoYeX.d.mts → index-DHH-93YD.d.mts} +1 -1
- package/dist/{index-B9io6JAL.d.mts → index-DI2lS7VT.d.mts} +1 -1
- package/dist/{index-cLB38AWX.d.mts → index-DNI6UF9h.d.mts} +60 -60
- package/dist/{index-NUByQvlg.d.mts → index-DXtQRehh.d.mts} +14 -14
- package/dist/{index-CVNbBtA-.d.mts → index-Wbd_syN2.d.mts} +112 -112
- package/dist/kernel/extension/index.d.mts +1 -1
- package/dist/kernel/index.d.mts +2 -2
- package/dist/kernel/observability/index.d.mts +1 -1
- package/dist/{namespace-C06lJSuj.d.mts → namespace-K23m4b0n.d.mts} +6 -6
- package/dist/package-Cgjpm9om.mjs +7 -0
- package/dist/{package-BNFTUFLB.d.mts → package-efR90JGe.d.mts} +59 -30
- package/dist/package.json +1 -1
- package/dist/runtime-node/index.d.mts +1 -1
- package/dist/runtime-node/index.mjs +1 -1
- package/dist/runtime-node/makaio-config.d.mts +2 -2
- package/dist/runtime-node/makaio-config.mjs +1 -1
- package/dist/runtime-node/workflow-execution-bus-access.mjs +1 -1
- package/dist/runtime-node/workflow-worker/index.d.mts +2 -2
- package/dist/runtime-node/workflow-worker/index.mjs +1 -1
- package/dist/{schemas-OWvBn7AH.d.mts → schemas-BCXvEWyF.d.mts} +4 -4
- package/dist/{schemas-BAv9RB9M.d.mts → schemas-D7EeKBDJ.d.mts} +3 -3
- package/dist/services/adapter-subsystem/index.d.mts +2 -2
- package/dist/services/adapter-subsystem/namespace.d.mts +1 -1
- package/dist/services/filesystem/namespace.d.mts +6 -6
- package/dist/services/filesystem/schemas.d.mts +3 -3
- package/dist/services/index.d.mts +9 -9
- package/dist/services/provider-context/index.d.mts +1 -1
- package/dist/services/session/index.d.mts +1 -1
- package/dist/services/settings/namespace.d.mts +2 -2
- package/dist/services/subagent-template/index.d.mts +1 -1
- package/dist/services/subagent-template/schemas.d.mts +1 -1
- package/dist/worker-CARV52hT.mjs +1 -0
- package/dist/worker-CLsRbzpk.mjs +1 -0
- package/dist/workflow-BomH9DBB.mjs +1 -0
- package/dist/workflow-engine/execution-attempt-repository.d.mts +2 -2
- package/dist/workflow-engine/execution-attempt-repository.mjs +1 -1
- package/dist/workflow-engine/index.d.mts +178 -23
- package/dist/workflow-engine/index.mjs +1 -1
- package/dist/workflow-engine/package.d.mts +1 -1
- package/dist/workflow-engine/package.mjs +1 -1
- package/dist/workflow-engine/testing/index.d.mts +30 -9
- package/dist/workflow-engine/testing/index.mjs +1 -1
- package/dist/workflow-engine/testing/sqlite.d.mts +3 -2
- package/dist/workflow-engine/testing/sqlite.mjs +20 -20
- package/dist/workflow-engine/workflow-orchestrator.mjs +1 -1
- package/dist/workflow-worker-Dul-wcOR.mjs +1 -0
- package/package.json +4 -4
- package/dist/attempt-record-codec-BYfYkj4M.mjs +0 -1
- package/dist/authority-state-bootstrap-CLYus369.mjs +0 -1
- package/dist/capabilities-BUwuA7XX.mjs +0 -1
- package/dist/contracts/worker-node/index.d.mts +0 -2
- package/dist/contracts/worker-node/index.mjs +0 -1
- package/dist/package-B01gmu75.mjs +0 -7
- package/dist/worker-node-CjuQb9hl.mjs +0 -1
- package/dist/worker-node-Dtf9ilsR.mjs +0 -1
- package/dist/workflow-C4mck_NW.mjs +0 -1
- package/dist/workflow-worker-aNbdgqml.mjs +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as ProviderOperationClaim, i as ProcessBoundProvisionerLossProof, l as ProviderOperationOwnershipRecord, o as ProviderOperationClaimDecision, s as ProviderOperationMutationDecision } from "./provider-operation-DbdyFwNm.mjs";
|
|
2
|
-
import { BoundedRecoveryEvidence, ProviderAllocationRef,
|
|
2
|
+
import { BoundedRecoveryEvidence, ProviderAllocationRef, WorkerAllocationLifetime } from "@makaio/framework/contracts";
|
|
3
3
|
|
|
4
4
|
//#region subsystems/workflow-engine/src/execution-attempt-repository.d.ts
|
|
5
5
|
/**
|
|
@@ -28,9 +28,9 @@ declare const EXECUTION_ATTEMPT_SETTLEMENT_KINDS: readonly ["outcome", "abandone
|
|
|
28
28
|
/**
|
|
29
29
|
* How a settled attempt reached its terminal state.
|
|
30
30
|
*
|
|
31
|
-
* - `outcome`: a worker submitted
|
|
31
|
+
* - `outcome`: a worker submitted an outcome that was accepted.
|
|
32
32
|
* - `infrastructure-failure`: the provider allocation terminated without
|
|
33
|
-
* an acknowledged worker outcome. The
|
|
33
|
+
* an acknowledged worker outcome. The owner may retry the work.
|
|
34
34
|
* - `abandoned`: dispatch ended before allocation, including a positively
|
|
35
35
|
* proven absence recorded through
|
|
36
36
|
* {@link ExecutionAttemptRepository.recordProvisioningAbsent} or a
|
|
@@ -54,16 +54,196 @@ type ExecutionAttemptSettlementKind = (typeof EXECUTION_ATTEMPT_SETTLEMENT_KINDS
|
|
|
54
54
|
*/
|
|
55
55
|
declare function sameAllocationRef(stored: ProviderAllocationRef, candidate: ProviderAllocationRef): boolean;
|
|
56
56
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
|
|
57
|
+
* Identifier of the durable aggregate that owns a series of attempts.
|
|
58
|
+
*
|
|
59
|
+
* For the workflow adapter this is the `WorkflowExecution` id. The generic
|
|
60
|
+
* port never interprets it; it is the fence key that decides which attempt is
|
|
61
|
+
* active for an owner. The alias is scoped to the repository port, the
|
|
62
|
+
* authority, the dispatch runner, and the convergence ports — wire claims and
|
|
63
|
+
* workflow-storage lookup keys keep plain `string`.
|
|
64
|
+
*/
|
|
65
|
+
type ExecutionOwnerId = string;
|
|
66
|
+
/**
|
|
67
|
+
* Compare two durable outcome texts as values.
|
|
68
|
+
*
|
|
69
|
+
* The rule {@link sameAllocationRef} states, for the same reason: an outcome
|
|
70
|
+
* may carry caller-authored data whose key order is incidental, and a replay
|
|
71
|
+
* of the identical outcome must be reported as `duplicate` by every
|
|
72
|
+
* realization. The texts are parsed back before canonicalization because a
|
|
73
|
+
* codec is free to emit whatever key order it likes, and key order is not
|
|
74
|
+
* part of the value.
|
|
75
|
+
*
|
|
76
|
+
* Both operands are the codec's durable text — the only representation every
|
|
77
|
+
* realization shares. The stored one is what the attempt actually holds, read
|
|
78
|
+
* out of the realization's own record rather than reproduced from the decoded
|
|
79
|
+
* value: a codec may normalize while serializing, and the contract requires
|
|
80
|
+
* `parse(JSON.parse(serialize(outcome)))` only to succeed, never to serialize
|
|
81
|
+
* back to the same text. Re-serializing the decoded outcome would therefore
|
|
82
|
+
* compare a retry against a text the first commit never wrote, and would
|
|
83
|
+
* answer `conflict` for a worker's honest replay. Comparing texts also keeps
|
|
84
|
+
* a value such as a `bigint` — which a codec may legitimately encode, and
|
|
85
|
+
* which has no JSON form of its own — away from the canonicalizer entirely.
|
|
86
|
+
* @param stored - Durable text the attempt committed.
|
|
87
|
+
* @param candidate - Durable text the caller's submission would commit.
|
|
88
|
+
* @returns `true` when the two denote the same outcome.
|
|
89
|
+
*/
|
|
90
|
+
declare function sameDurableOutcome(stored: string, candidate: string): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* The durable text a submission commits as, with the value that text yields.
|
|
93
|
+
*
|
|
94
|
+
* The two are produced together, by one call to {@link durableOutcome}, because
|
|
95
|
+
* they are one durable fact: the text is what a realization writes, and the
|
|
96
|
+
* outcome is what a reload of that text returns. Deriving either from the
|
|
97
|
+
* other later would re-serialize an already-normalized value, and a codec is
|
|
98
|
+
* not required to serialize such a value back to the text it came from.
|
|
99
|
+
*
|
|
100
|
+
* It is the currency of the whole outcome path. A caller renders a submission
|
|
101
|
+
* once through {@link ExecutionAttemptRepository.canonicalizeOutcome},
|
|
102
|
+
* validates `outcome`, and hands the same rendering to
|
|
103
|
+
* {@link ExecutionAttemptRepository.commitOutcome}, which stores `text`
|
|
104
|
+
* verbatim. Nothing between those steps re-reads the submitter's object, so a
|
|
105
|
+
* mutable outcome the caller changes afterwards cannot make the committed
|
|
106
|
+
* value differ from the validated one.
|
|
107
|
+
* @typeParam TOutcome - Owner-specific outcome type committed per attempt.
|
|
108
|
+
*/
|
|
109
|
+
interface DurableOutcome<TOutcome> {
|
|
110
|
+
/** The codec text a realization persists for this submission. */
|
|
111
|
+
readonly text: string;
|
|
112
|
+
/** The outcome that text reads back as, produced by the same rendering. */
|
|
113
|
+
readonly outcome: TOutcome;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Render a caller-supplied outcome as the durable fact a commit would write.
|
|
117
|
+
*
|
|
118
|
+
* The rendering rule every realization owes, stated once here for the same
|
|
119
|
+
* reason {@link sameDurableOutcome} is: it decides what an attempt holds, so
|
|
120
|
+
* two realizations deriving it independently could disagree about it.
|
|
121
|
+
*
|
|
122
|
+
* Three steps, in this order and each exactly once. `parse` validates the
|
|
123
|
+
* submission before any durable decision, so a value the codec rejects is
|
|
124
|
+
* refused at the same point by every realization. `serialize` produces the
|
|
125
|
+
* text to persist — the single serialization the pair is built from.
|
|
126
|
+
* `parse(JSON.parse(text))` yields what a reload returns, which is the
|
|
127
|
+
* outcome the port reports and an owner converges on; a codec may normalize
|
|
128
|
+
* while serializing, so it is not in general the submitted value.
|
|
129
|
+
*
|
|
130
|
+
* Nothing is cloned and nothing is frozen. The round trip already produces a
|
|
131
|
+
* value derived from freshly parsed JSON rather than from the caller's
|
|
132
|
+
* object, so no reference reaches back into anything the caller still holds.
|
|
133
|
+
* A `structuredClone` in front of it would reject an outcome type that is
|
|
134
|
+
* codec-serializable but not structured-cloneable, such as a `URL`, and an
|
|
135
|
+
* `Object.freeze` behind it would throw outright for one that is not
|
|
136
|
+
* freezable, such as a non-empty `Uint8Array` — both of which the codec
|
|
137
|
+
* contract allows. What keeps a committed outcome stable instead is that a
|
|
138
|
+
* realization stores the text and decodes it afresh on every read, so a
|
|
139
|
+
* caller that mutates the value it was handed changes nothing a later read
|
|
140
|
+
* reports.
|
|
141
|
+
* @param codec - Owner-injected codec that owns the durable representation.
|
|
142
|
+
* @param outcome - Outcome the caller presented.
|
|
143
|
+
* @returns The text to persist and the outcome it reads back as.
|
|
144
|
+
* @throws When the codec rejects the outcome or its own durable text.
|
|
145
|
+
* @typeParam TOutcome - Owner-specific outcome type committed per attempt.
|
|
146
|
+
*/
|
|
147
|
+
declare function durableOutcome<TOutcome>(codec: OutcomeCodec<TOutcome>, outcome: TOutcome): DurableOutcome<TOutcome>;
|
|
148
|
+
/**
|
|
149
|
+
* Read a committed durable text back as the outcome it holds.
|
|
150
|
+
*
|
|
151
|
+
* The read rule every realization owes, stated once here for the same reason
|
|
152
|
+
* {@link durableOutcome} states the write rule: what a stored text yields
|
|
153
|
+
* must not depend on which realization reads it. `JSON.parse` undoes the
|
|
154
|
+
* transport form and `parse` validates the envelope, so a text the codec
|
|
155
|
+
* refuses — durable corruption, or a codec an owner changed under an existing
|
|
156
|
+
* row — fails loudly rather than reaching a caller as an ordinary outcome.
|
|
157
|
+
*
|
|
158
|
+
* Every read decodes afresh, and nothing is shared, cloned, or frozen. A
|
|
159
|
+
* codec may reconstruct a mutable object whose state a freeze does not even
|
|
160
|
+
* reach, such as a `URL`, so one reader mutating the value it was handed must
|
|
161
|
+
* not change what the next read reports; the stored text stays the only
|
|
162
|
+
* source of truth.
|
|
163
|
+
* @param codec - Owner-injected codec that owns the durable representation.
|
|
164
|
+
* @param text - Durable text an attempt committed.
|
|
165
|
+
* @returns The outcome that text holds, held by nobody else.
|
|
166
|
+
* @throws When the text is not JSON, or not JSON the codec accepts.
|
|
167
|
+
* @typeParam TOutcome - Owner-specific outcome type committed per attempt.
|
|
168
|
+
*/
|
|
169
|
+
declare function decodeDurableOutcome<TOutcome>(codec: OutcomeCodec<TOutcome>, text: string): TOutcome;
|
|
170
|
+
/**
|
|
171
|
+
* Validates and serializes the owner-specific outcome type of an attempt.
|
|
172
|
+
*
|
|
173
|
+
* Injected into every {@link ExecutionAttemptRepository} realization so the
|
|
174
|
+
* generic port can enforce "input validation precedes every durable decision"
|
|
175
|
+
* without knowing the outcome shape. `parse` runs on every submitted outcome
|
|
176
|
+
* before the durable decision and on every committed outcome read back from
|
|
177
|
+
* storage; `serialize` produces the durable representation.
|
|
178
|
+
*
|
|
179
|
+
* **The durable representation is JSON text, and the two members round-trip
|
|
180
|
+
* through it.** A realization persists exactly what `serialize` returned,
|
|
181
|
+
* reads it back with `JSON.parse`, and hands the parsed value to `parse`; a
|
|
182
|
+
* codec must therefore accept `JSON.parse(serialize(outcome))` for every
|
|
183
|
+
* outcome it produced. Which envelope lives inside that text is the codec's
|
|
184
|
+
* own choice — no realization may assume it is `JSON.stringify(outcome)`.
|
|
185
|
+
*
|
|
186
|
+
* **The text is strict JSON, in the sense `JSON.stringify` defines.** Only
|
|
187
|
+
* values that function can represent are inside the contract: finite numbers,
|
|
188
|
+
* strings, booleans, `null`, arrays, and plain objects. A non-finite number —
|
|
189
|
+
* `Infinity`, `-Infinity`, `NaN`, written as `1e9999` or any other spelling —
|
|
190
|
+
* is outside it, as is anything else JSON has no form for, because the port
|
|
191
|
+
* decides outcome equality by canonicalizing the parsed text with
|
|
192
|
+
* `canonicalStringify` (see {@link sameDurableOutcome}), which renders every
|
|
193
|
+
* such value as `null` and would report two different outcomes as the same
|
|
194
|
+
* one. A codec whose outcomes need those values encodes them as values JSON
|
|
195
|
+
* does have — a string, or a tagged envelope — which is the codec's own job
|
|
196
|
+
* and not something a realization can do for it. Equality follows JSON
|
|
197
|
+
* semantics for the same reason: signed zero is outside the contract, `-0`
|
|
198
|
+
* and `0` are one and the same outcome, because `JSON.stringify` renders both
|
|
199
|
+
* as `0` and the canonical form cannot tell them apart. A codec that needs the
|
|
200
|
+
* sign encodes it explicitly, as it would any other value JSON does not carry.
|
|
201
|
+
*
|
|
202
|
+
* **Both members are deterministic, pure functions of their argument.** The
|
|
203
|
+
* same outcome serializes to the same text every time and the same value
|
|
204
|
+
* parses to the same outcome every time; neither may consult history, a
|
|
205
|
+
* counter, a clock, or any state outside the argument it was handed.
|
|
206
|
+
*
|
|
207
|
+
* The port itself renders a submission exactly once, through
|
|
208
|
+
* {@link ExecutionAttemptRepository.canonicalizeOutcome}, and carries that one
|
|
209
|
+
* {@link DurableOutcome} into {@link ExecutionAttemptRepository.commitOutcome}
|
|
210
|
+
* — so validation, the durable write, and convergence are one rendering and
|
|
211
|
+
* cannot drift apart. Determinism is still owed, because two renderings of
|
|
212
|
+
* the same outcome do meet: `commitOutcome` compares the text a retry renders
|
|
213
|
+
* against the text the first commit stored, and a codec that closed over a
|
|
214
|
+
* counter would misclassify a worker's honest retry.
|
|
215
|
+
*
|
|
216
|
+
* Determinism is not idempotence. `serialize` need not map its own decoded
|
|
217
|
+
* output back to the text it came from: a codec may normalize, and every
|
|
218
|
+
* decision the port makes about a committed outcome is made on the text that
|
|
219
|
+
* commit actually stored rather than on a re-serialization of it.
|
|
220
|
+
*
|
|
221
|
+
* `null` and `undefined` are not outcomes. A realization records "no outcome
|
|
222
|
+
* committed" as the absence of a stored value, so a codec that accepted a
|
|
223
|
+
* nullish outcome would make the two indistinguishable.
|
|
224
|
+
* @typeParam TOutcome - Owner-specific outcome type committed per attempt.
|
|
65
225
|
*/
|
|
66
|
-
|
|
226
|
+
interface OutcomeCodec<TOutcome> {
|
|
227
|
+
/**
|
|
228
|
+
* Validate an untrusted value as an outcome.
|
|
229
|
+
*
|
|
230
|
+
* Deterministic and pure: the same input always yields the same outcome.
|
|
231
|
+
* @param input - Value to validate.
|
|
232
|
+
* @returns The validated outcome.
|
|
233
|
+
* @throws When the value violates the outcome contract, including when it is nullish.
|
|
234
|
+
*/
|
|
235
|
+
parse(input: unknown): TOutcome;
|
|
236
|
+
/**
|
|
237
|
+
* Serialize an outcome for durable storage.
|
|
238
|
+
*
|
|
239
|
+
* Deterministic and pure: the same outcome always yields the same text,
|
|
240
|
+
* because a retry's rendering is compared against the text an earlier
|
|
241
|
+
* commit stored.
|
|
242
|
+
* @param outcome - Outcome to serialize.
|
|
243
|
+
* @returns The JSON text to persist, which `parse` accepts after `JSON.parse`.
|
|
244
|
+
*/
|
|
245
|
+
serialize(outcome: TOutcome): string;
|
|
246
|
+
}
|
|
67
247
|
/**
|
|
68
248
|
* Rejection of an attempt identifier that already names a durable attempt.
|
|
69
249
|
*
|
|
@@ -92,8 +272,8 @@ declare class DuplicateExecutionAttemptError extends Error {
|
|
|
92
272
|
interface ExecutionAttemptRecord {
|
|
93
273
|
/** Authority-created attempt identifier. */
|
|
94
274
|
readonly executionAttemptId: string;
|
|
95
|
-
/**
|
|
96
|
-
readonly executionId:
|
|
275
|
+
/** Owner identifier of the aggregate this attempt belongs to. */
|
|
276
|
+
readonly executionId: ExecutionOwnerId;
|
|
97
277
|
/** Current lifecycle status of the attempt. */
|
|
98
278
|
readonly status: ExecutionAttemptStatus;
|
|
99
279
|
/** Provider allocation reference, set after allocation recording. */
|
|
@@ -116,7 +296,7 @@ interface ExecutionAttemptRecord {
|
|
|
116
296
|
* Immutable alongside {@link providerId}. Remediation reads it to decide
|
|
117
297
|
* whether losing the provisioning process also loses the allocation.
|
|
118
298
|
*/
|
|
119
|
-
readonly allocationLifetime:
|
|
299
|
+
readonly allocationLifetime: WorkerAllocationLifetime | null;
|
|
120
300
|
/**
|
|
121
301
|
* Provisioner process incarnation that performed the provider call, or
|
|
122
302
|
* `null` before provisioning began.
|
|
@@ -182,7 +362,7 @@ interface RecoverableAttemptRecord extends ExecutionAttemptRecord {
|
|
|
182
362
|
/** Always bound for recoverable attempts. */
|
|
183
363
|
readonly providerId: string;
|
|
184
364
|
/** Always bound for recoverable attempts. */
|
|
185
|
-
readonly allocationLifetime:
|
|
365
|
+
readonly allocationLifetime: WorkerAllocationLifetime;
|
|
186
366
|
/** Always bound for recoverable attempts. */
|
|
187
367
|
readonly provisionerIncarnationId: string;
|
|
188
368
|
}
|
|
@@ -194,8 +374,8 @@ interface RecoverableAttemptRecord extends ExecutionAttemptRecord {
|
|
|
194
374
|
interface ExecutionAttemptCreate {
|
|
195
375
|
/** Authority-created attempt identifier. */
|
|
196
376
|
readonly executionAttemptId: string;
|
|
197
|
-
/**
|
|
198
|
-
readonly executionId:
|
|
377
|
+
/** Owner identifier the attempt belongs to. */
|
|
378
|
+
readonly executionId: ExecutionOwnerId;
|
|
199
379
|
}
|
|
200
380
|
/**
|
|
201
381
|
* Input for claiming the provisioning phase of an attempt.
|
|
@@ -208,12 +388,12 @@ interface ExecutionAttemptCreate {
|
|
|
208
388
|
interface BeginProvisioningInput {
|
|
209
389
|
/** Attempt whose provider call is about to begin. */
|
|
210
390
|
readonly executionAttemptId: string;
|
|
211
|
-
/**
|
|
212
|
-
readonly executionId:
|
|
391
|
+
/** Owner identifier the attempt must belong to. */
|
|
392
|
+
readonly executionId: ExecutionOwnerId;
|
|
213
393
|
/** Provider to bind to the attempt, immutably. */
|
|
214
394
|
readonly providerId: string;
|
|
215
395
|
/** Allocation lifetime declared by that provider, immutably. */
|
|
216
|
-
readonly allocationLifetime:
|
|
396
|
+
readonly allocationLifetime: WorkerAllocationLifetime;
|
|
217
397
|
/** Provisioner process incarnation performing the call, immutably. */
|
|
218
398
|
readonly provisionerIncarnationId: string;
|
|
219
399
|
/** Controller process incarnation that will hold the initial claim. */
|
|
@@ -228,14 +408,22 @@ interface BeginProvisioningInput {
|
|
|
228
408
|
* and returns the canonical outcome for convergence. Outcome commitment
|
|
229
409
|
* carries no claim: a worker's answer never depends on who currently owns
|
|
230
410
|
* the attempt's provider operation.
|
|
411
|
+
* @typeParam TOutcome - Owner-specific outcome type committed per attempt.
|
|
231
412
|
*/
|
|
232
|
-
interface ExecutionAttemptOutcomeCommit {
|
|
413
|
+
interface ExecutionAttemptOutcomeCommit<TOutcome> {
|
|
233
414
|
/** Authority-created attempt identifier. */
|
|
234
415
|
readonly executionAttemptId: string;
|
|
235
|
-
/**
|
|
236
|
-
readonly executionId:
|
|
237
|
-
/**
|
|
238
|
-
|
|
416
|
+
/** Owner identifier the attempt belongs to. */
|
|
417
|
+
readonly executionId: ExecutionOwnerId;
|
|
418
|
+
/**
|
|
419
|
+
* The rendering to commit, from
|
|
420
|
+
* {@link ExecutionAttemptRepository.canonicalizeOutcome}.
|
|
421
|
+
*
|
|
422
|
+
* A rendering rather than a raw outcome so the value a caller validated and
|
|
423
|
+
* the value that becomes durable are the same one: the caller renders the
|
|
424
|
+
* submission once and never reads its own object again.
|
|
425
|
+
*/
|
|
426
|
+
readonly result: DurableOutcome<TOutcome>;
|
|
239
427
|
}
|
|
240
428
|
/** Input for extending the lease of a currently held provider operation. */
|
|
241
429
|
interface RenewProviderOperationClaimInput {
|
|
@@ -311,8 +499,8 @@ interface RecordAllocationInput {
|
|
|
311
499
|
interface RecordProvisioningAbsentInput {
|
|
312
500
|
/** Claim authorizing the record. */
|
|
313
501
|
readonly claim: ProviderOperationClaim;
|
|
314
|
-
/**
|
|
315
|
-
readonly executionId:
|
|
502
|
+
/** Owner identifier the attempt must belong to. */
|
|
503
|
+
readonly executionId: ExecutionOwnerId;
|
|
316
504
|
/** Bounded evidence supporting the absence claim. */
|
|
317
505
|
readonly evidence: BoundedRecoveryEvidence;
|
|
318
506
|
}
|
|
@@ -327,8 +515,8 @@ interface RecordProvisioningAbsentInput {
|
|
|
327
515
|
interface RecordProvisionerIncarnationLostInput {
|
|
328
516
|
/** Claim authorizing the record. */
|
|
329
517
|
readonly claim: ProviderOperationClaim;
|
|
330
|
-
/**
|
|
331
|
-
readonly executionId:
|
|
518
|
+
/** Owner identifier the attempt must belong to. */
|
|
519
|
+
readonly executionId: ExecutionOwnerId;
|
|
332
520
|
/** Proof that a specific provisioner process incarnation is gone. */
|
|
333
521
|
readonly proof: ProcessBoundProvisionerLossProof;
|
|
334
522
|
}
|
|
@@ -348,8 +536,8 @@ interface RecordAllocationTerminatedInput {
|
|
|
348
536
|
interface RecordInfrastructureFailureInput {
|
|
349
537
|
/** Claim authorizing the settlement. */
|
|
350
538
|
readonly claim: ProviderOperationClaim;
|
|
351
|
-
/**
|
|
352
|
-
readonly executionId:
|
|
539
|
+
/** Owner identifier the attempt must belong to. */
|
|
540
|
+
readonly executionId: ExecutionOwnerId;
|
|
353
541
|
}
|
|
354
542
|
/**
|
|
355
543
|
* Input for a compare-and-set evolution of an allocation reference.
|
|
@@ -365,8 +553,8 @@ interface RecordInfrastructureFailureInput {
|
|
|
365
553
|
interface AllocationRefEvolution {
|
|
366
554
|
/** Claim authorizing the evolution. */
|
|
367
555
|
readonly claim: ProviderOperationClaim;
|
|
368
|
-
/**
|
|
369
|
-
readonly executionId:
|
|
556
|
+
/** Owner identifier the attempt must belong to. */
|
|
557
|
+
readonly executionId: ExecutionOwnerId;
|
|
370
558
|
/**
|
|
371
559
|
* The allocation reference the caller believes is currently stored.
|
|
372
560
|
*
|
|
@@ -510,7 +698,7 @@ type ProvisionerIncarnationLossDecision = {
|
|
|
510
698
|
readonly kind: 'recorded';
|
|
511
699
|
} | {
|
|
512
700
|
readonly kind: 'not-process-bound';
|
|
513
|
-
readonly allocationLifetime:
|
|
701
|
+
readonly allocationLifetime: WorkerAllocationLifetime | null;
|
|
514
702
|
} | {
|
|
515
703
|
readonly kind: 'incarnation-mismatch';
|
|
516
704
|
readonly provisionerIncarnationId: string | null;
|
|
@@ -620,20 +808,35 @@ type PendingAttemptAbandonmentDecision = {
|
|
|
620
808
|
* Durable outcome decision returned by {@link ExecutionAttemptRepository.commitOutcome}.
|
|
621
809
|
*
|
|
622
810
|
* - `accepted`: the outcome was committed as canonical for the first time.
|
|
623
|
-
*
|
|
624
|
-
*
|
|
625
|
-
*
|
|
811
|
+
* The stored text decoded is reported, never the caller's copy of it.
|
|
812
|
+
* - `duplicate`: an outcome whose durable text {@link sameDurableOutcome}
|
|
813
|
+
* judges identical to the committed one was submitted again; this is a
|
|
814
|
+
* replay. The committed outcome is reported, never the caller's copy of it.
|
|
626
815
|
* - `conflict`: the attempt already reached a different terminal state — either
|
|
627
816
|
* a different committed outcome, or a competing terminal transition that
|
|
628
817
|
* settled it without one.
|
|
629
818
|
* - `fenced`: the attempt is no longer the active attempt for this execution.
|
|
819
|
+
*
|
|
820
|
+
* Both settling kinds carry `text`: the durable text the attempt holds for
|
|
821
|
+
* this outcome. For `accepted` that is the text the commit just wrote; for
|
|
822
|
+
* `duplicate` it is the text the earlier commit wrote, which is not
|
|
823
|
+
* necessarily the retry's own rendering — {@link sameDurableOutcome} judges
|
|
824
|
+
* two texts the same outcome while member order, and anything else a codec
|
|
825
|
+
* may render differently, still differs between them. A caller that needs a
|
|
826
|
+
* copy of the committed outcome nobody else has held decodes this text
|
|
827
|
+
* through {@link ExecutionAttemptRepository.decodeOutcome}; decoding its own
|
|
828
|
+
* submission's text instead would hand out the retry's representation rather
|
|
829
|
+
* than the committed one.
|
|
830
|
+
* @typeParam TOutcome - Owner-specific outcome type committed per attempt.
|
|
630
831
|
*/
|
|
631
|
-
type ExecutionAttemptOutcomeDecision = {
|
|
832
|
+
type ExecutionAttemptOutcomeDecision<TOutcome> = {
|
|
632
833
|
readonly kind: 'accepted';
|
|
633
|
-
readonly outcome:
|
|
834
|
+
readonly outcome: TOutcome;
|
|
835
|
+
readonly text: string;
|
|
634
836
|
} | {
|
|
635
837
|
readonly kind: 'duplicate';
|
|
636
|
-
readonly outcome:
|
|
838
|
+
readonly outcome: TOutcome;
|
|
839
|
+
readonly text: string;
|
|
637
840
|
} | {
|
|
638
841
|
readonly kind: 'conflict';
|
|
639
842
|
} | {
|
|
@@ -682,7 +885,7 @@ type ExecutionAttemptOutcomeDecision = {
|
|
|
682
885
|
* let a caller assert that infrastructure ended without ever recording the
|
|
683
886
|
* evidence that it did, and the settlement is irreversible.
|
|
684
887
|
*
|
|
685
|
-
* 2. **Provider-side evidence is claim-fenced;
|
|
888
|
+
* 2. **Provider-side evidence is claim-fenced; the worker outcome is not.**
|
|
686
889
|
* Every provider-side mutation requires the current generation and token.
|
|
687
890
|
* {@link commitOutcome} deliberately requires neither, so a worker can
|
|
688
891
|
* always deliver its canonical answer.
|
|
@@ -725,9 +928,46 @@ type ExecutionAttemptOutcomeDecision = {
|
|
|
725
928
|
* — including when the caller's claim is also stale. Validating after the
|
|
726
929
|
* guards would make the rejection depend on ownership, so the same malformed
|
|
727
930
|
* payload would throw against one implementation and return `stale` from
|
|
728
|
-
* another.
|
|
931
|
+
* another. Submitted outcomes follow the same rule through the injected
|
|
932
|
+
* {@link OutcomeCodec}: `parse` runs before the durable outcome decision. A
|
|
933
|
+
* committed outcome is always a defined value — a nullish outcome is outside
|
|
934
|
+
* this port, because "no outcome committed" is itself recorded as the absence
|
|
935
|
+
* of a stored value and the two would be indistinguishable.
|
|
936
|
+
*
|
|
937
|
+
* **The codec's durable text is what an attempt holds, and it is rendered
|
|
938
|
+
* exactly once per submission.** {@link canonicalizeOutcome} produces the
|
|
939
|
+
* {@link DurableOutcome} — the text to persist and the outcome that text
|
|
940
|
+
* reads back as — and {@link commitOutcome} receives that rendering rather
|
|
941
|
+
* than a raw value, persisting `text` verbatim. What an attempt reports is
|
|
942
|
+
* therefore never the submitter's copy, which a normalizing codec makes a
|
|
943
|
+
* different value, and never a second serialization, which a codec whose
|
|
944
|
+
* serialization is not a fixed point makes a different text. Every later
|
|
945
|
+
* decision about that outcome is made on the stored text or on the value it
|
|
946
|
+
* yields: {@link commitOutcome} compares a retry's rendered text against the
|
|
947
|
+
* stored one through {@link sameDurableOutcome}.
|
|
948
|
+
*
|
|
949
|
+
* **Every stored outcome is parsed before it decides anything.** An
|
|
950
|
+
* implementation that finds a committed text takes it through `parse` ahead
|
|
951
|
+
* of the duplicate-or-conflict decision, not only on the branch that reports
|
|
952
|
+
* a value. A text the codec rejects is broken durable state — corruption, or
|
|
953
|
+
* a codec changed under an existing row — and it fails loudly rather than
|
|
954
|
+
* reaching the caller as an ordinary competing outcome. For the same reason
|
|
955
|
+
* an outcome is decoded on every read instead of being handed out as one
|
|
956
|
+
* shared instance: a codec may reconstruct a mutable object — one that no
|
|
957
|
+
* freeze even reaches, or that cannot be frozen at all — and one reader
|
|
958
|
+
* mutating it must not change what the next read reports.
|
|
959
|
+
* {@link decodeOutcome} is that read, stated on the port so a caller holding
|
|
960
|
+
* only a durable text can perform it too.
|
|
961
|
+
*
|
|
962
|
+
* **An `accepted` decision reports that same fresh decode of the text it just
|
|
963
|
+
* stored**, not the outcome half of the rendering it was handed. The two are
|
|
964
|
+
* one value only until someone touches it: a caller validates
|
|
965
|
+
* {@link DurableOutcome.outcome} before the commit, and a mutable outcome it
|
|
966
|
+
* changed there would otherwise be reported back as the committed one — a
|
|
967
|
+
* value no reload of the attempt ever yields.
|
|
968
|
+
* @typeParam TOutcome - Owner-specific outcome type committed per attempt.
|
|
729
969
|
*/
|
|
730
|
-
interface ExecutionAttemptRepository {
|
|
970
|
+
interface ExecutionAttemptRepository<TOutcome> {
|
|
731
971
|
/**
|
|
732
972
|
* Persist a new execution attempt record.
|
|
733
973
|
*
|
|
@@ -910,11 +1150,58 @@ interface ExecutionAttemptRepository {
|
|
|
910
1150
|
*
|
|
911
1151
|
* Returns `null` when the attempt does not exist or has been superseded
|
|
912
1152
|
* (fenced) by a newer attempt for the same execution.
|
|
913
|
-
* @param executionId -
|
|
1153
|
+
* @param executionId - Owner identifier the attempt belongs to.
|
|
914
1154
|
* @param executionAttemptId - Attempt identifier to look up.
|
|
915
1155
|
* @returns The attempt record if active, or `null`.
|
|
916
1156
|
*/
|
|
917
|
-
getActiveAttempt(executionId:
|
|
1157
|
+
getActiveAttempt(executionId: ExecutionOwnerId, executionAttemptId: string): Promise<ExecutionAttemptRecord | null>;
|
|
1158
|
+
/**
|
|
1159
|
+
* Render a submission as the durable fact a commit of it would write.
|
|
1160
|
+
*
|
|
1161
|
+
* **The port renders an outcome exactly once, and this is where.** A
|
|
1162
|
+
* realization stores the codec's durable text and reports what a reload of
|
|
1163
|
+
* that text yields, so the value an owner converges on is not in general
|
|
1164
|
+
* the value the submitter handed in — a codec may normalize while
|
|
1165
|
+
* serializing. This member produces both halves of that fact together,
|
|
1166
|
+
* without committing anything, so a caller can validate the outcome it will
|
|
1167
|
+
* actually receive and then hand the very same rendering to
|
|
1168
|
+
* {@link commitOutcome}.
|
|
1169
|
+
*
|
|
1170
|
+
* Carrying the rendering rather than re-deriving it is what closes the gap
|
|
1171
|
+
* between the two calls: the submitter's object is read once, so neither a
|
|
1172
|
+
* mutation of it nor a second serialization can make the committed outcome
|
|
1173
|
+
* differ from the validated one.
|
|
1174
|
+
*
|
|
1175
|
+
* Synchronous and free of durable effects: it consults the injected
|
|
1176
|
+
* {@link OutcomeCodec} and nothing else — {@link durableOutcome} is the
|
|
1177
|
+
* rendering rule, and a realization has no freedom to deviate from it.
|
|
1178
|
+
* @param outcome - Outcome a caller is about to submit.
|
|
1179
|
+
* @returns The text a commit would persist and the outcome that text yields.
|
|
1180
|
+
* @throws When the codec rejects the outcome or its own durable text.
|
|
1181
|
+
*/
|
|
1182
|
+
canonicalizeOutcome(outcome: TOutcome): DurableOutcome<TOutcome>;
|
|
1183
|
+
/**
|
|
1184
|
+
* Read a committed durable text back as the outcome it holds.
|
|
1185
|
+
*
|
|
1186
|
+
* The counterpart of {@link canonicalizeOutcome}: that member says what a
|
|
1187
|
+
* commit writes, this one says what a read of that text yields. Both are
|
|
1188
|
+
* the codec's rules rather than a realization's, so
|
|
1189
|
+
* {@link decodeDurableOutcome} is the rendering and a realization has no
|
|
1190
|
+
* freedom to deviate from it.
|
|
1191
|
+
*
|
|
1192
|
+
* It exists because the durable text is the only copy of an outcome nobody
|
|
1193
|
+
* can have touched. Every value the port hands out is decoded from it, and
|
|
1194
|
+
* a caller that has passed one on — to an owner validation, to convergence
|
|
1195
|
+
* — and needs the committed outcome again asks for a fresh decode instead
|
|
1196
|
+
* of reusing a value some other step may have mutated. Each call returns
|
|
1197
|
+
* its own value; a mutation of one changes nothing the next one reports.
|
|
1198
|
+
*
|
|
1199
|
+
* Synchronous and free of durable effects: the caller supplies the text.
|
|
1200
|
+
* @param text - Durable text an attempt committed, as {@link DurableOutcome.text} carries it.
|
|
1201
|
+
* @returns The outcome that text holds, held by nobody else.
|
|
1202
|
+
* @throws When the text is not JSON, or not JSON the codec accepts.
|
|
1203
|
+
*/
|
|
1204
|
+
decodeOutcome(text: string): TOutcome;
|
|
918
1205
|
/**
|
|
919
1206
|
* Commit a terminal outcome for an attempt.
|
|
920
1207
|
*
|
|
@@ -928,12 +1215,13 @@ interface ExecutionAttemptRepository {
|
|
|
928
1215
|
*
|
|
929
1216
|
* 1. `fenced` — the attempt is no longer the active attempt for its
|
|
930
1217
|
* execution. Evaluated first because `accepted` and `duplicate` oblige
|
|
931
|
-
* the caller to converge
|
|
1218
|
+
* the caller to converge owner state, and a superseded attempt must
|
|
932
1219
|
* never drive that convergence.
|
|
933
1220
|
* 2. `duplicate` / `conflict` — an outcome is already committed for this
|
|
934
|
-
* attempt: `duplicate` when {@link
|
|
935
|
-
* canonically equal
|
|
936
|
-
* `conflict` when it differs
|
|
1221
|
+
* attempt: `duplicate` when {@link sameDurableOutcome} judges the text
|
|
1222
|
+
* the submission would commit canonically equal to the stored one,
|
|
1223
|
+
* including member-order-insensitive objects; `conflict` when it differs
|
|
1224
|
+
* under that rule.
|
|
937
1225
|
* 3. `conflict` — a competing terminal transition already settled the
|
|
938
1226
|
* attempt without committing an outcome, that is
|
|
939
1227
|
* {@link recordInfrastructureFailure} or
|
|
@@ -943,10 +1231,24 @@ interface ExecutionAttemptRepository {
|
|
|
943
1231
|
* `settlementKind` nor reopen the attempt.
|
|
944
1232
|
* 4. `accepted` — the outcome becomes canonical, the attempt settles as
|
|
945
1233
|
* `outcome`, and its operation closes.
|
|
946
|
-
*
|
|
1234
|
+
*
|
|
1235
|
+
* The submission arrives already rendered, as the {@link DurableOutcome} the
|
|
1236
|
+
* caller obtained from {@link canonicalizeOutcome}. An implementation
|
|
1237
|
+
* persists `result.text` verbatim and never re-serializes: a second
|
|
1238
|
+
* rendering is what would let the durable answer differ from the one the
|
|
1239
|
+
* owner validated. What it reports for `accepted` is that text decoded
|
|
1240
|
+
* again — {@link decodeOutcome} of `result.text` — rather than
|
|
1241
|
+
* `result.outcome`, which the caller has held since before its own
|
|
1242
|
+
* validation and may have mutated there.
|
|
1243
|
+
*
|
|
1244
|
+
* A settling decision also reports the durable text the attempt holds:
|
|
1245
|
+
* `result.text` for `accepted`, and the stored text for `duplicate` — the
|
|
1246
|
+
* record's own, not the submission's, because the two are the same outcome
|
|
1247
|
+
* without being the same text.
|
|
1248
|
+
* @param input - Attempt identity and the rendering to commit.
|
|
947
1249
|
* @returns The durable decision with the canonical outcome when applicable.
|
|
948
1250
|
*/
|
|
949
|
-
commitOutcome(input: ExecutionAttemptOutcomeCommit): Promise<ExecutionAttemptOutcomeDecision
|
|
1251
|
+
commitOutcome(input: ExecutionAttemptOutcomeCommit<TOutcome>): Promise<ExecutionAttemptOutcomeDecision<TOutcome>>;
|
|
950
1252
|
/**
|
|
951
1253
|
* Settle a pending attempt when dispatch cannot continue before provisioning.
|
|
952
1254
|
*
|
|
@@ -954,10 +1256,10 @@ interface ExecutionAttemptRepository {
|
|
|
954
1256
|
* mean provisioning already began, so the caller must converge the provider
|
|
955
1257
|
* operation instead of abandoning the attempt.
|
|
956
1258
|
* @param executionAttemptId - Pending attempt to abandon.
|
|
957
|
-
* @param executionId -
|
|
1259
|
+
* @param executionId - Owner identifier the attempt belongs to.
|
|
958
1260
|
* @returns The durable abandonment decision.
|
|
959
1261
|
*/
|
|
960
|
-
abandonPendingAttempt(executionAttemptId: string, executionId:
|
|
1262
|
+
abandonPendingAttempt(executionAttemptId: string, executionId: ExecutionOwnerId): Promise<PendingAttemptAbandonmentDecision>;
|
|
961
1263
|
/**
|
|
962
1264
|
* Recovery operations, present only on a recovery-capable repository.
|
|
963
1265
|
*
|
|
@@ -1047,10 +1349,10 @@ interface ExecutionAttemptRecoveryOperations {
|
|
|
1047
1349
|
* same millisecond from ordering differently on two stores. An
|
|
1048
1350
|
* implementation that returned an arbitrary order would make a caller that
|
|
1049
1351
|
* bounds its pass reclaim a different subset on each realization.
|
|
1050
|
-
* @param executionId -
|
|
1352
|
+
* @param executionId - Owner identifier the attempts belong to.
|
|
1051
1353
|
* @returns Allocated, non-settled attempts eligible for recovery, oldest first.
|
|
1052
1354
|
*/
|
|
1053
|
-
getRecoverableAttempts(executionId:
|
|
1355
|
+
getRecoverableAttempts(executionId: ExecutionOwnerId): Promise<readonly RecoverableAttemptRecord[]>;
|
|
1054
1356
|
}
|
|
1055
1357
|
//#endregion
|
|
1056
|
-
export {
|
|
1358
|
+
export { RecordProviderOperationUncertaintyInput as A, PendingAttemptAbandonmentDecision as C, RecordAllocationInput as D, ProvisioningClaimDecision as E, TakeOverProviderOperationInput as F, decodeDurableOutcome as I, durableOutcome as L, RecordProvisioningAbsentInput as M, RecoverableAttemptRecord as N, RecordAllocationTerminatedInput as O, RenewProviderOperationClaimInput as P, sameAllocationRef as R, OutcomeCodec as S, ProvisioningAbsenceDecision as T, ExecutionAttemptSettlementKind as _, BeginProvisioningInput as a, HandoffProviderOperationInput as b, DurableOutcome as c, ExecutionAttemptCreate as d, ExecutionAttemptOutcomeCommit as f, ExecutionAttemptRepository as g, ExecutionAttemptRecoveryOperations as h, AllocationTerminationDecision as i, RecordProvisionerIncarnationLostInput as j, RecordInfrastructureFailureInput as k, EXECUTION_ATTEMPT_SETTLEMENT_KINDS as l, ExecutionAttemptRecord as m, AllocationRefEvolution as n, DiscoveredAllocationDecision as o, ExecutionAttemptOutcomeDecision as p, AllocationRefEvolutionDecision as r, DuplicateExecutionAttemptError as s, AllocationRecordingDecision as t, EXECUTION_ATTEMPT_STATUSES as u, ExecutionAttemptStatus as v, ProvisionerIncarnationLossDecision as w, InfrastructureFailureDecision as x, ExecutionOwnerId as y, sameDurableOutcome as z };
|