@affiant/core 0.1.0-alpha.0
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/LICENSE +202 -0
- package/README.md +354 -0
- package/dist/context.d.ts +136 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +30 -0
- package/dist/context.js.map +1 -0
- package/dist/docket/entry.d.ts +421 -0
- package/dist/docket/entry.d.ts.map +1 -0
- package/dist/docket/entry.js +155 -0
- package/dist/docket/entry.js.map +1 -0
- package/dist/docket/expiry.d.ts +82 -0
- package/dist/docket/expiry.d.ts.map +1 -0
- package/dist/docket/expiry.js +106 -0
- package/dist/docket/expiry.js.map +1 -0
- package/dist/docket/memory.d.ts +163 -0
- package/dist/docket/memory.d.ts.map +1 -0
- package/dist/docket/memory.js +528 -0
- package/dist/docket/memory.js.map +1 -0
- package/dist/docket/store.d.ts +387 -0
- package/dist/docket/store.d.ts.map +1 -0
- package/dist/docket/store.js +51 -0
- package/dist/docket/store.js.map +1 -0
- package/dist/errors.d.ts +153 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +164 -0
- package/dist/errors.js.map +1 -0
- package/dist/gate/coverage.d.ts +152 -0
- package/dist/gate/coverage.d.ts.map +1 -0
- package/dist/gate/coverage.js +114 -0
- package/dist/gate/coverage.js.map +1 -0
- package/dist/gate/decide.d.ts +207 -0
- package/dist/gate/decide.d.ts.map +1 -0
- package/dist/gate/decide.js +559 -0
- package/dist/gate/decide.js.map +1 -0
- package/dist/gate/gate.d.ts +212 -0
- package/dist/gate/gate.d.ts.map +1 -0
- package/dist/gate/gate.js +175 -0
- package/dist/gate/gate.js.map +1 -0
- package/dist/gate/pipeline.d.ts +285 -0
- package/dist/gate/pipeline.d.ts.map +1 -0
- package/dist/gate/pipeline.js +515 -0
- package/dist/gate/pipeline.js.map +1 -0
- package/dist/gate/policy.d.ts +272 -0
- package/dist/gate/policy.d.ts.map +1 -0
- package/dist/gate/policy.js +396 -0
- package/dist/gate/policy.js.map +1 -0
- package/dist/gate/wrap.d.ts +107 -0
- package/dist/gate/wrap.d.ts.map +1 -0
- package/dist/gate/wrap.js +164 -0
- package/dist/gate/wrap.js.map +1 -0
- package/dist/index.d.ts +95 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +111 -0
- package/dist/index.js.map +1 -0
- package/dist/model/affidavit.d.ts +354 -0
- package/dist/model/affidavit.d.ts.map +1 -0
- package/dist/model/affidavit.js +417 -0
- package/dist/model/affidavit.js.map +1 -0
- package/dist/model/amendments.d.ts +160 -0
- package/dist/model/amendments.d.ts.map +1 -0
- package/dist/model/amendments.js +183 -0
- package/dist/model/amendments.js.map +1 -0
- package/dist/model/canonical.d.ts +311 -0
- package/dist/model/canonical.d.ts.map +1 -0
- package/dist/model/canonical.js +665 -0
- package/dist/model/canonical.js.map +1 -0
- package/dist/model/money.d.ts +127 -0
- package/dist/model/money.d.ts.map +1 -0
- package/dist/model/money.js +177 -0
- package/dist/model/money.js.map +1 -0
- package/dist/model/provenance.d.ts +315 -0
- package/dist/model/provenance.d.ts.map +1 -0
- package/dist/model/provenance.js +223 -0
- package/dist/model/provenance.js.map +1 -0
- package/dist/ports.d.ts +269 -0
- package/dist/ports.d.ts.map +1 -0
- package/dist/ports.js +34 -0
- package/dist/ports.js.map +1 -0
- package/dist/store-memory.d.ts +21 -0
- package/dist/store-memory.d.ts.map +1 -0
- package/dist/store-memory.js +20 -0
- package/dist/store-memory.js.map +1 -0
- package/dist/telemetry-keys.d.ts +65 -0
- package/dist/telemetry-keys.d.ts.map +1 -0
- package/dist/telemetry-keys.js +72 -0
- package/dist/telemetry-keys.js.map +1 -0
- package/dist/telemetry.d.ts +77 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +43 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/testing.d.ts +574 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +1291 -0
- package/dist/testing.js.map +1 -0
- package/package.json +75 -0
- package/telemetry-keys.json +92 -0
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The reference Docket and Session stores, in memory.
|
|
3
|
+
*
|
|
4
|
+
* **Rules served: DK-1** (idempotent filing, the guarded compare-and-set, expiry as
|
|
5
|
+
* queryable state, preserved amendments on a late decision, execution outcome,
|
|
6
|
+
* lineage), **DK-3** (a bounded, paged sweep and no timer), **DK-4** (retention,
|
|
7
|
+
* purge, export, read-forward), **DK-5** (rehydration order), **AZ-2** (tenant
|
|
8
|
+
* isolation), **RT-2** (nothing unbounded but a purge).
|
|
9
|
+
*
|
|
10
|
+
* "In memory" is not "toy". This is the *reference* store: it is what the
|
|
11
|
+
* store-semantics fixtures are written against, and a Postgres store earns the name
|
|
12
|
+
* by passing the same ones. Everything a production store has to get right is here
|
|
13
|
+
* — the compare-and-set, the deadline applied on read, the opaque paging, the
|
|
14
|
+
* tenant partition — because a reference that skipped them would let a fixture pass
|
|
15
|
+
* for the wrong reason.
|
|
16
|
+
*
|
|
17
|
+
* **How the compare-and-set is atomic without a lock.** JavaScript is
|
|
18
|
+
* single-threaded, but `await` is an interleaving point: any method that reads state,
|
|
19
|
+
* awaits, and then writes it has a window in which another caller can run. So every
|
|
20
|
+
* mutating method here does its read and its write in **one synchronous block** —
|
|
21
|
+
* the `async` wrapper contains no `await` at all, which means the body runs to
|
|
22
|
+
* completion before the caller's next turn of the event loop. Two `transition` calls
|
|
23
|
+
* launched together with `Promise.all` therefore serialize, and exactly one wins the
|
|
24
|
+
* guard. A production store gets the same property from a conditional `UPDATE`; the
|
|
25
|
+
* shape of the fixture is identical either way.
|
|
26
|
+
*
|
|
27
|
+
* **Storage layout.** One `Map` per tenant, keyed by entry id, so a cross-tenant read
|
|
28
|
+
* is not a filtered read but an impossible one — there is no code path that looks in
|
|
29
|
+
* another tenant's map. `Map` preserves insertion order, which *is* filing order, so
|
|
30
|
+
* every list, sweep and export walks it directly rather than sorting. A sequence
|
|
31
|
+
* number per row makes that position addressable by an opaque cursor.
|
|
32
|
+
*
|
|
33
|
+
* @packageDocumentation
|
|
34
|
+
*/
|
|
35
|
+
import { defaultClock } from "../ports.js";
|
|
36
|
+
import { readStatus } from "./entry.js";
|
|
37
|
+
import { isDue, instantMs } from "./expiry.js";
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Cursors
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
/**
|
|
42
|
+
* The tag every cursor this store mints carries.
|
|
43
|
+
*
|
|
44
|
+
* A cursor is opaque to callers, and opaque has to mean *checked*, not merely
|
|
45
|
+
* *ugly*: a cursor from one list handed to another, or a string a caller invented,
|
|
46
|
+
* has to be a loud failure rather than a quietly different answer. The tag names the
|
|
47
|
+
* store and the cursor format version, and the list kind is encoded beside it.
|
|
48
|
+
*/
|
|
49
|
+
const CURSOR_TAG = "affiant.docket.v1";
|
|
50
|
+
/** An opaque cursor naming a position within one list. */
|
|
51
|
+
function encodeCursor(kind, position) {
|
|
52
|
+
return btoa(`${CURSOR_TAG}|${kind}|${position}`);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The position `cursor` names within `kind`'s list.
|
|
56
|
+
*
|
|
57
|
+
* @throws RangeError when the cursor is unreadable or belongs to a different list.
|
|
58
|
+
* A `RangeError` and not an `AffiantError`: the error-code registry names
|
|
59
|
+
* the reasons *the gate refuses a request*, and a bad cursor is a caller's
|
|
60
|
+
* programming error, not a request the framework declined.
|
|
61
|
+
*/
|
|
62
|
+
function decodeCursor(cursor, kind) {
|
|
63
|
+
let decoded;
|
|
64
|
+
try {
|
|
65
|
+
decoded = atob(cursor);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
throw new RangeError("cursor is not a cursor this store minted");
|
|
69
|
+
}
|
|
70
|
+
const tag = `${CURSOR_TAG}|${kind}|`;
|
|
71
|
+
if (!decoded.startsWith(tag)) {
|
|
72
|
+
throw new RangeError(`cursor does not belong to the ${kind} list`);
|
|
73
|
+
}
|
|
74
|
+
return decoded.slice(tag.length);
|
|
75
|
+
}
|
|
76
|
+
/** The sequence position a plain list cursor names, or `0` to start from the beginning. */
|
|
77
|
+
function decodeSequence(page, kind) {
|
|
78
|
+
const cursor = page.cursor;
|
|
79
|
+
if (cursor === undefined || cursor === null)
|
|
80
|
+
return 0;
|
|
81
|
+
const position = Number(decodeCursor(cursor, kind));
|
|
82
|
+
if (!Number.isInteger(position) || position < 0) {
|
|
83
|
+
throw new RangeError("cursor does not name a position in this list");
|
|
84
|
+
}
|
|
85
|
+
return position;
|
|
86
|
+
}
|
|
87
|
+
/** Rejects a page size that would make a list unbounded or empty (RT-2). */
|
|
88
|
+
function requireLimit(limit) {
|
|
89
|
+
if (!Number.isInteger(limit) || limit < 1) {
|
|
90
|
+
throw new RangeError(`limit must be a positive integer, got: ${String(limit)}`);
|
|
91
|
+
}
|
|
92
|
+
return limit;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The reference Docket store: one `Map` per tenant, every rule in the DK area
|
|
96
|
+
* enforced, no timer anywhere.
|
|
97
|
+
*/
|
|
98
|
+
export class InMemoryDocketStore {
|
|
99
|
+
#tenants = new Map();
|
|
100
|
+
#clock;
|
|
101
|
+
constructor(options = {}) {
|
|
102
|
+
this.#clock = options.clock ?? defaultClock;
|
|
103
|
+
}
|
|
104
|
+
// ------------------------------------------------------------------ filing
|
|
105
|
+
/**
|
|
106
|
+
* File `entry`, or return the one already filed under its id — never an error,
|
|
107
|
+
* never an overwrite, never a refreshed deadline (DK-1, GT-4).
|
|
108
|
+
*/
|
|
109
|
+
async file(entry) {
|
|
110
|
+
const shard = this.#ensureShard(entry.tenantId);
|
|
111
|
+
const existing = shard.entries.get(entry.entryId);
|
|
112
|
+
if (existing !== undefined) {
|
|
113
|
+
return { entry: this.#read(existing.entry), created: false };
|
|
114
|
+
}
|
|
115
|
+
shard.entries.set(entry.entryId, { entry, seq: shard.next });
|
|
116
|
+
shard.next += 1;
|
|
117
|
+
return { entry: this.#read(entry), created: true };
|
|
118
|
+
}
|
|
119
|
+
/** The entry as it reads now, or `null` when nothing in `scope` has that id. */
|
|
120
|
+
async get(entryId, scope) {
|
|
121
|
+
const stored = this.#find(entryId, scope);
|
|
122
|
+
return stored === null ? null : this.#read(stored.entry);
|
|
123
|
+
}
|
|
124
|
+
// ------------------------------------------------------------- transitions
|
|
125
|
+
/**
|
|
126
|
+
* The guarded compare-and-set (DK-1).
|
|
127
|
+
*
|
|
128
|
+
* No `await` between the read of the current state and the write of the new one:
|
|
129
|
+
* that is what makes it atomic against every other caller in this isolate.
|
|
130
|
+
*/
|
|
131
|
+
async transition(entryId, scope, expected, patch) {
|
|
132
|
+
const stored = this.#find(entryId, scope);
|
|
133
|
+
if (stored === null)
|
|
134
|
+
return "not-found";
|
|
135
|
+
const current = readStatus(stored.entry, this.#clock.now());
|
|
136
|
+
if (current !== expected) {
|
|
137
|
+
// An expired row was nobody's decision; an approved or rejected one was
|
|
138
|
+
// somebody's, and DK-1 requires the second decision be refused as such.
|
|
139
|
+
return current === "expired" ? "expired" : "already-decided";
|
|
140
|
+
}
|
|
141
|
+
const next = applyPatch(stored.entry, patch, this.#clock.now());
|
|
142
|
+
stored.entry = next;
|
|
143
|
+
return next;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Preserve the amendments a late decision carried, for a resubmission (DK-1).
|
|
147
|
+
*
|
|
148
|
+
* Writes `preservedAmendments` and nothing else: not `status`, not `decision`, not
|
|
149
|
+
* `attestation`. The row's stored status stays as it was — it already *reads*
|
|
150
|
+
* `expired`, and recording a sweep the host did not run would be this method
|
|
151
|
+
* quietly doing a second job.
|
|
152
|
+
*/
|
|
153
|
+
async preserveAmendments(entryId, scope, amendments, act) {
|
|
154
|
+
const stored = this.#find(entryId, scope);
|
|
155
|
+
if (stored === null)
|
|
156
|
+
return "not-found";
|
|
157
|
+
if (readStatus(stored.entry, this.#clock.now()) !== "expired")
|
|
158
|
+
return "not-expired";
|
|
159
|
+
// The refused decision's own instant and principal, so a resubmission's
|
|
160
|
+
// prefilled values bind to the act that actually happened (PV-2).
|
|
161
|
+
stored.entry = {
|
|
162
|
+
...stored.entry,
|
|
163
|
+
preservedAmendments: { amendments, at: act.at, by: act.by },
|
|
164
|
+
};
|
|
165
|
+
return this.#read(stored.entry);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Record what the host's executor reported on an approved row, once (DK-1, DK-4).
|
|
169
|
+
*
|
|
170
|
+
* A guarded compare-and-set on the row's own `execution`, read and written in the
|
|
171
|
+
* same synchronous block as every other mutation here, so two reports racing
|
|
172
|
+
* serialize and exactly one wins. The second is refused rather than applied on
|
|
173
|
+
* top: an outcome is a recorded fact, and a row reads forward.
|
|
174
|
+
*/
|
|
175
|
+
async recordExecution(entryId, scope, outcome, detail, expected) {
|
|
176
|
+
const stored = this.#find(entryId, scope);
|
|
177
|
+
if (stored === null)
|
|
178
|
+
return "not-found";
|
|
179
|
+
if (readStatus(stored.entry, this.#clock.now()) !== "approved")
|
|
180
|
+
return "not-approved";
|
|
181
|
+
if (stored.entry.execution !== expected)
|
|
182
|
+
return "execution-already-recorded";
|
|
183
|
+
stored.entry = { ...stored.entry, execution: outcome, executionDetail: detail };
|
|
184
|
+
return this.#read(stored.entry);
|
|
185
|
+
}
|
|
186
|
+
/** Record the successor of a terminal row (DK-1); the row keeps its terminal state. */
|
|
187
|
+
async recordSupersession(entryId, scope, supersededBy) {
|
|
188
|
+
const stored = this.#find(entryId, scope);
|
|
189
|
+
if (stored === null)
|
|
190
|
+
return "not-found";
|
|
191
|
+
if (readStatus(stored.entry, this.#clock.now()) === "pending")
|
|
192
|
+
return "not-terminal";
|
|
193
|
+
stored.entry = {
|
|
194
|
+
...stored.entry,
|
|
195
|
+
lineage: { supersedes: stored.entry.lineage.supersedes, supersededBy },
|
|
196
|
+
};
|
|
197
|
+
return this.#read(stored.entry);
|
|
198
|
+
}
|
|
199
|
+
// ------------------------------------------------------------------- lists
|
|
200
|
+
/** Everything that reads `pending` right now, in filing order, paged. */
|
|
201
|
+
async listPending(scope, page) {
|
|
202
|
+
const now = this.#clock.now();
|
|
203
|
+
return this.#page(scope, page, "pending", (entry) => readStatus(entry, now) === "pending");
|
|
204
|
+
}
|
|
205
|
+
/** Everything approved and still unexecuted, in filing order, paged. */
|
|
206
|
+
async listApprovedUnexecuted(scope, page) {
|
|
207
|
+
const now = this.#clock.now();
|
|
208
|
+
return this.#page(scope, page, "approved-unexecuted", (entry) => readStatus(entry, now) === "approved" && entry.execution === "unexecuted");
|
|
209
|
+
}
|
|
210
|
+
// ------------------------------------------------------------------- sweep
|
|
211
|
+
/**
|
|
212
|
+
* Mark at most `limit` due entries `expired`, in filing order, and say whether more
|
|
213
|
+
* remain (DK-3).
|
|
214
|
+
*
|
|
215
|
+
* `decidedAt` is set to the entry's own `expiresAt`, not to `now`: the row left
|
|
216
|
+
* `pending` at its deadline, which is when a reader who never ran a sweep would
|
|
217
|
+
* have seen it go. A swept row and an unswept one past the same deadline are then
|
|
218
|
+
* the same value, so a host cannot tell — and cannot come to depend on — whether
|
|
219
|
+
* the sweep has caught up.
|
|
220
|
+
*/
|
|
221
|
+
async expireDue(now, scope, limit) {
|
|
222
|
+
requireLimit(limit);
|
|
223
|
+
const shard = this.#shard(scope.tenantId);
|
|
224
|
+
const expired = [];
|
|
225
|
+
let more = false;
|
|
226
|
+
if (shard !== null) {
|
|
227
|
+
for (const stored of shard.entries.values()) {
|
|
228
|
+
if (!inScope(stored.entry, scope) || !isDue(stored.entry, now))
|
|
229
|
+
continue;
|
|
230
|
+
if (expired.length === limit) {
|
|
231
|
+
more = true;
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
stored.entry = expire(stored.entry);
|
|
235
|
+
expired.push(stored.entry.entryId);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return { expired, more };
|
|
239
|
+
}
|
|
240
|
+
// ------------------------------------------------- retention, purge, export
|
|
241
|
+
/**
|
|
242
|
+
* Remove at most `limit` terminal entries whose terminal instant is before
|
|
243
|
+
* `policy.olderThan` (DK-4).
|
|
244
|
+
*
|
|
245
|
+
* An `approved` row that is still `unexecuted` is **never** eligible, however old.
|
|
246
|
+
* It is the only record that a write was authorised and has not yet happened, and
|
|
247
|
+
* the Docket is the sole record of approval authority (AZ-5) — ageing it out would
|
|
248
|
+
* delete the evidence and the outstanding work in one move.
|
|
249
|
+
*/
|
|
250
|
+
async retention(policy, scope, limit) {
|
|
251
|
+
requireLimit(limit);
|
|
252
|
+
const olderThan = instantMs(policy.olderThan, "olderThan");
|
|
253
|
+
const now = this.#clock.now();
|
|
254
|
+
const shard = this.#shard(scope.tenantId);
|
|
255
|
+
const remove = [];
|
|
256
|
+
let more = false;
|
|
257
|
+
if (shard !== null) {
|
|
258
|
+
for (const stored of shard.entries.values()) {
|
|
259
|
+
if (!inScope(stored.entry, scope))
|
|
260
|
+
continue;
|
|
261
|
+
if (!isRetainable(stored.entry, now, olderThan))
|
|
262
|
+
continue;
|
|
263
|
+
if (remove.length === limit) {
|
|
264
|
+
more = true;
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
remove.push(stored.entry.entryId);
|
|
268
|
+
}
|
|
269
|
+
for (const entryId of remove)
|
|
270
|
+
shard.entries.delete(entryId);
|
|
271
|
+
}
|
|
272
|
+
return { removed: remove.length, more };
|
|
273
|
+
}
|
|
274
|
+
/** Remove everything belonging to `tenantId`, and nothing else (DK-4). */
|
|
275
|
+
async purge(tenantId) {
|
|
276
|
+
const shard = this.#tenants.get(tenantId);
|
|
277
|
+
if (shard === undefined)
|
|
278
|
+
return { removed: 0 };
|
|
279
|
+
const removed = shard.entries.size;
|
|
280
|
+
this.#tenants.delete(tenantId);
|
|
281
|
+
return { removed };
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Every entry in `scope`, in filing order, streamed (DK-4).
|
|
285
|
+
*
|
|
286
|
+
* The ids are snapshotted when iteration begins and each row is read as the
|
|
287
|
+
* consumer reaches it, so a concurrent transition is reflected and a concurrent
|
|
288
|
+
* retention pass cannot make the iterator yield a row that has been removed.
|
|
289
|
+
*/
|
|
290
|
+
async *export(scope) {
|
|
291
|
+
const shard = this.#shard(scope.tenantId);
|
|
292
|
+
if (shard === null)
|
|
293
|
+
return;
|
|
294
|
+
const ids = [];
|
|
295
|
+
for (const stored of shard.entries.values()) {
|
|
296
|
+
if (inScope(stored.entry, scope))
|
|
297
|
+
ids.push(stored.entry.entryId);
|
|
298
|
+
}
|
|
299
|
+
for (const entryId of ids) {
|
|
300
|
+
const stored = shard.entries.get(entryId);
|
|
301
|
+
if (stored !== undefined)
|
|
302
|
+
yield this.#read(stored.entry);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
// ---------------------------------------------------------------- internals
|
|
306
|
+
/** The tenant's rows, or `null` when nothing has been filed for it. */
|
|
307
|
+
#shard(tenantId) {
|
|
308
|
+
return this.#tenants.get(tenantId) ?? null;
|
|
309
|
+
}
|
|
310
|
+
/** The tenant's rows, opening a partition for it on first filing. */
|
|
311
|
+
#ensureShard(tenantId) {
|
|
312
|
+
const existing = this.#tenants.get(tenantId);
|
|
313
|
+
if (existing !== undefined)
|
|
314
|
+
return existing;
|
|
315
|
+
const shard = { entries: new Map(), next: 0 };
|
|
316
|
+
this.#tenants.set(tenantId, shard);
|
|
317
|
+
return shard;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* The stored row with that id inside `scope`, or `null`.
|
|
321
|
+
*
|
|
322
|
+
* The tenant is a map lookup, not a filter: there is no code path here that reads
|
|
323
|
+
* another tenant's rows and then discards them, so a mismatched tenant is a miss
|
|
324
|
+
* and tells the caller nothing about whether the id exists (AZ-2).
|
|
325
|
+
*/
|
|
326
|
+
#find(entryId, scope) {
|
|
327
|
+
const shard = this.#shard(scope.tenantId);
|
|
328
|
+
if (shard === null)
|
|
329
|
+
return null;
|
|
330
|
+
const stored = shard.entries.get(entryId);
|
|
331
|
+
if (stored === undefined)
|
|
332
|
+
return null;
|
|
333
|
+
return inScope(stored.entry, scope) ? stored : null;
|
|
334
|
+
}
|
|
335
|
+
/** The row as it reads now: the deadline applied, whether or not a sweep has run (DK-1). */
|
|
336
|
+
#read(entry) {
|
|
337
|
+
return isDue(entry, this.#clock.now()) ? expire(entry) : entry;
|
|
338
|
+
}
|
|
339
|
+
/** One page of the rows in `scope` that `matches` accepts, in filing order. */
|
|
340
|
+
#page(scope, page, kind, matches) {
|
|
341
|
+
const limit = requireLimit(page.limit);
|
|
342
|
+
const after = decodeSequence(page, kind);
|
|
343
|
+
const shard = this.#shard(scope.tenantId);
|
|
344
|
+
const items = [];
|
|
345
|
+
let last = after;
|
|
346
|
+
let more = false;
|
|
347
|
+
if (shard !== null) {
|
|
348
|
+
for (const stored of shard.entries.values()) {
|
|
349
|
+
if (stored.seq < after)
|
|
350
|
+
continue;
|
|
351
|
+
if (!inScope(stored.entry, scope) || !matches(stored.entry))
|
|
352
|
+
continue;
|
|
353
|
+
if (items.length === limit) {
|
|
354
|
+
more = true;
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
items.push(this.#read(stored.entry));
|
|
358
|
+
last = stored.seq + 1;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return { items, cursor: more ? encodeCursor(kind, String(last)) : null, more };
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
// ---------------------------------------------------------------------------
|
|
365
|
+
// Row helpers
|
|
366
|
+
// ---------------------------------------------------------------------------
|
|
367
|
+
/** Whether `entry` is inside `scope`'s conversation, when the scope names one. */
|
|
368
|
+
function inScope(entry, scope) {
|
|
369
|
+
if (entry.tenantId !== scope.tenantId)
|
|
370
|
+
return false;
|
|
371
|
+
return scope.conversationId === undefined || entry.conversationId === scope.conversationId;
|
|
372
|
+
}
|
|
373
|
+
/** The row as an expired one: it left `pending` at its own deadline. */
|
|
374
|
+
function expire(entry) {
|
|
375
|
+
return { ...entry, status: "expired", execution: null, decidedAt: entry.expiresAt };
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Whether `entry` may be aged out under a retention cut at `olderThan`.
|
|
379
|
+
*
|
|
380
|
+
* Terminal, past the cut, and — for an approved row — already reported on by the
|
|
381
|
+
* executor. The terminal instant is `decidedAt` where the row has one and the
|
|
382
|
+
* deadline otherwise, which covers a row that expired without ever being swept.
|
|
383
|
+
*/
|
|
384
|
+
function isRetainable(entry, now, olderThan) {
|
|
385
|
+
const status = readStatus(entry, now);
|
|
386
|
+
if (status === "pending")
|
|
387
|
+
return false;
|
|
388
|
+
if (status === "approved" && entry.execution === "unexecuted")
|
|
389
|
+
return false;
|
|
390
|
+
const terminalAt = status === "expired" ? (entry.decidedAt ?? entry.expiresAt) : entry.decidedAt;
|
|
391
|
+
if (terminalAt === null)
|
|
392
|
+
return false;
|
|
393
|
+
return instantMs(terminalAt, "decidedAt") < olderThan;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* `entry` with `patch` applied, with the correlations DK-1 requires checked.
|
|
397
|
+
*
|
|
398
|
+
* A patch that contradicts itself — an approved row with no execution outcome, a
|
|
399
|
+
* rejected row that carries one, a transition back to `pending` — is a `RangeError`
|
|
400
|
+
* rather than a silently repaired row. A store that quietly fixed the caller's patch
|
|
401
|
+
* would be the store deciding what the state machine means.
|
|
402
|
+
*/
|
|
403
|
+
function applyPatch(entry, patch, now) {
|
|
404
|
+
const status = patch.status;
|
|
405
|
+
if (status === "pending") {
|
|
406
|
+
throw new RangeError("a transition never returns an entry to pending (DK-1)");
|
|
407
|
+
}
|
|
408
|
+
const execution = patch.execution === undefined ? (status === "approved" ? "unexecuted" : null) : patch.execution;
|
|
409
|
+
if (status === "approved" && execution === null) {
|
|
410
|
+
throw new RangeError("an approved entry carries an execution outcome (DK-1)");
|
|
411
|
+
}
|
|
412
|
+
if (status !== "approved" && execution !== null) {
|
|
413
|
+
throw new RangeError(`a ${status} entry carries no execution outcome (DK-1)`);
|
|
414
|
+
}
|
|
415
|
+
const decidedAt = patch.decidedAt === undefined ? now : patch.decidedAt;
|
|
416
|
+
if (decidedAt === null) {
|
|
417
|
+
throw new RangeError(`a ${status} entry records when it left pending (DK-1)`);
|
|
418
|
+
}
|
|
419
|
+
return {
|
|
420
|
+
...entry,
|
|
421
|
+
status,
|
|
422
|
+
execution,
|
|
423
|
+
// DK-4: the proposal is never overwritten. An approval that accepted amendments
|
|
424
|
+
// writes the accepted state **beside** it; absent leaves the row carrying its
|
|
425
|
+
// proposal alone (AF-4).
|
|
426
|
+
amendedAffidavit: patch.amendedAffidavit ?? entry.amendedAffidavit,
|
|
427
|
+
decision: patch.decision ?? null,
|
|
428
|
+
amendments: patch.amendments === undefined ? entry.amendments : patch.amendments,
|
|
429
|
+
attestation: patch.attestation === undefined ? entry.attestation : patch.attestation,
|
|
430
|
+
executionDetail: patch.executionDetail === undefined ? entry.executionDetail : patch.executionDetail,
|
|
431
|
+
lineage: {
|
|
432
|
+
supersedes: entry.lineage.supersedes,
|
|
433
|
+
supersededBy: patch.lineage?.supersededBy === undefined
|
|
434
|
+
? entry.lineage.supersededBy
|
|
435
|
+
: patch.lineage.supersededBy,
|
|
436
|
+
},
|
|
437
|
+
decidedAt,
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
// ---------------------------------------------------------------------------
|
|
441
|
+
// The session store (DK-5)
|
|
442
|
+
// ---------------------------------------------------------------------------
|
|
443
|
+
/** Which half of the rehydration sequence a cursor is in. */
|
|
444
|
+
const REHYDRATE_PENDING = "0";
|
|
445
|
+
/** The second half: approved rows the executor has not reported on. */
|
|
446
|
+
const REHYDRATE_APPROVED = "1";
|
|
447
|
+
/**
|
|
448
|
+
* The reference Session store: rehydration in the order DK-5 fixes.
|
|
449
|
+
*
|
|
450
|
+
* It is built **on** a {@link DocketStore} rather than beside one, and it uses only
|
|
451
|
+
* the public interface. That is deliberate: DK-5's order has to hold for any store a
|
|
452
|
+
* host plugs in, so the reference implementation proves the `DocketStore` contract
|
|
453
|
+
* is sufficient to produce it. A session store that reached into the in-memory
|
|
454
|
+
* store's internals would prove nothing.
|
|
455
|
+
*/
|
|
456
|
+
export class InMemorySessionStore {
|
|
457
|
+
#docket;
|
|
458
|
+
constructor(docket) {
|
|
459
|
+
this.#docket = docket;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* One page of: everything that reads `pending`, then everything `approved` and
|
|
463
|
+
* `unexecuted`, each in filing order (DK-5).
|
|
464
|
+
*
|
|
465
|
+
* A page boundary that falls inside the first group resumes there; one that drains
|
|
466
|
+
* it resumes at the start of the second. A reconnecting client therefore sees what
|
|
467
|
+
* still needs a decision before what still needs execution, however small its page
|
|
468
|
+
* size.
|
|
469
|
+
*/
|
|
470
|
+
async rehydrate(scope, page) {
|
|
471
|
+
const limit = requireLimit(page.limit);
|
|
472
|
+
const [group, inner] = splitRehydrateCursor(page);
|
|
473
|
+
if (group === REHYDRATE_APPROVED) {
|
|
474
|
+
const approved = await this.#docket.listApprovedUnexecuted(scope, pageAt(inner, limit));
|
|
475
|
+
return this.#result(approved.items, approved, REHYDRATE_APPROVED);
|
|
476
|
+
}
|
|
477
|
+
const pending = await this.#docket.listPending(scope, pageAt(inner, limit));
|
|
478
|
+
if (pending.more) {
|
|
479
|
+
return this.#result(pending.items, pending, REHYDRATE_PENDING);
|
|
480
|
+
}
|
|
481
|
+
// The pending group is drained; the rest of this page comes from the second.
|
|
482
|
+
const remaining = limit - pending.items.length;
|
|
483
|
+
if (remaining === 0) {
|
|
484
|
+
const peek = await this.#docket.listApprovedUnexecuted(scope, { limit: 1 });
|
|
485
|
+
return {
|
|
486
|
+
items: pending.items,
|
|
487
|
+
cursor: peek.items.length > 0 ? encodeRehydrateCursor(REHYDRATE_APPROVED, null) : null,
|
|
488
|
+
more: peek.items.length > 0,
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
const approved = await this.#docket.listApprovedUnexecuted(scope, { limit: remaining });
|
|
492
|
+
return this.#result([...pending.items, ...approved.items], approved, REHYDRATE_APPROVED);
|
|
493
|
+
}
|
|
494
|
+
/** One page of the sequence, with the underlying cursor wrapped in this one's group. */
|
|
495
|
+
#result(items, tail, group) {
|
|
496
|
+
return {
|
|
497
|
+
items,
|
|
498
|
+
cursor: tail.more ? encodeRehydrateCursor(group, tail.cursor) : null,
|
|
499
|
+
more: tail.more,
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
/** A page request continuing at `cursor`, or starting from the beginning. */
|
|
504
|
+
function pageAt(cursor, limit) {
|
|
505
|
+
return cursor === null ? { limit } : { cursor, limit };
|
|
506
|
+
}
|
|
507
|
+
/** The rehydration cursor: which group, and the underlying list cursor within it. */
|
|
508
|
+
function encodeRehydrateCursor(group, inner) {
|
|
509
|
+
return encodeCursor("rehydrate", `${group}:${inner ?? ""}`);
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* `page`'s rehydration cursor as `[group, innerCursor]`, or the start of the
|
|
513
|
+
* sequence when it has none.
|
|
514
|
+
*/
|
|
515
|
+
function splitRehydrateCursor(page) {
|
|
516
|
+
const cursor = page.cursor;
|
|
517
|
+
if (cursor === undefined || cursor === null)
|
|
518
|
+
return [REHYDRATE_PENDING, null];
|
|
519
|
+
const position = decodeCursor(cursor, "rehydrate");
|
|
520
|
+
const separator = position.indexOf(":");
|
|
521
|
+
const group = separator === -1 ? "" : position.slice(0, separator);
|
|
522
|
+
if (group !== REHYDRATE_PENDING && group !== REHYDRATE_APPROVED) {
|
|
523
|
+
throw new RangeError("cursor does not name a position in the rehydration sequence");
|
|
524
|
+
}
|
|
525
|
+
const inner = position.slice(separator + 1);
|
|
526
|
+
return [group, inner === "" ? null : inner];
|
|
527
|
+
}
|
|
528
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/docket/memory.ts"],"names":[],"mappings":"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"}
|