@earendil-works/pi-durable 0.87.1 → 0.99.1
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/README.md +46 -4
- package/dist/documents.d.ts +66 -0
- package/dist/documents.d.ts.map +1 -0
- package/dist/documents.js +98 -0
- package/dist/documents.js.map +1 -0
- package/dist/entries.d.ts +13 -0
- package/dist/entries.d.ts.map +1 -0
- package/dist/entries.js +18 -0
- package/dist/entries.js.map +1 -0
- package/dist/env/index.d.ts +131 -0
- package/dist/env/index.d.ts.map +1 -0
- package/dist/env/index.js +45 -0
- package/dist/env/index.js.map +1 -0
- package/dist/env/node.d.ts +45 -0
- package/dist/env/node.d.ts.map +1 -0
- package/dist/env/node.js +908 -0
- package/dist/env/node.js.map +1 -0
- package/dist/env/utils/adaptive-publisher.d.ts +24 -0
- package/dist/env/utils/adaptive-publisher.d.ts.map +1 -0
- package/dist/env/utils/adaptive-publisher.js +79 -0
- package/dist/env/utils/adaptive-publisher.js.map +1 -0
- package/dist/env/utils/output-capture.d.ts +31 -0
- package/dist/env/utils/output-capture.d.ts.map +1 -0
- package/dist/env/utils/output-capture.js +238 -0
- package/dist/env/utils/output-capture.js.map +1 -0
- package/dist/env/utils/truncate.d.ts +71 -0
- package/dist/env/utils/truncate.d.ts.map +1 -0
- package/dist/env/utils/truncate.js +294 -0
- package/dist/env/utils/truncate.js.map +1 -0
- package/dist/errors.d.ts +15 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +24 -0
- package/dist/errors.js.map +1 -0
- package/dist/harness/config.d.ts +20 -0
- package/dist/harness/config.d.ts.map +1 -0
- package/dist/harness/config.js +18 -0
- package/dist/harness/config.js.map +1 -0
- package/dist/harness/context.d.ts +27 -0
- package/dist/harness/context.d.ts.map +1 -0
- package/dist/harness/context.js +109 -0
- package/dist/harness/context.js.map +1 -0
- package/dist/harness/generation.d.ts +36 -0
- package/dist/harness/generation.d.ts.map +1 -0
- package/dist/harness/generation.js +266 -0
- package/dist/harness/generation.js.map +1 -0
- package/dist/harness/harness.d.ts +10 -0
- package/dist/harness/harness.d.ts.map +1 -0
- package/dist/harness/harness.js +266 -0
- package/dist/harness/harness.js.map +1 -0
- package/dist/harness/live.d.ts +40 -0
- package/dist/harness/live.d.ts.map +1 -0
- package/dist/harness/live.js +41 -0
- package/dist/harness/live.js.map +1 -0
- package/dist/harness/prompt.d.ts +24 -0
- package/dist/harness/prompt.d.ts.map +1 -0
- package/dist/harness/prompt.js +88 -0
- package/dist/harness/prompt.js.map +1 -0
- package/dist/harness/registry.d.ts +7 -0
- package/dist/harness/registry.d.ts.map +1 -0
- package/dist/harness/registry.js +309 -0
- package/dist/harness/registry.js.map +1 -0
- package/dist/harness/scheduler.d.ts +63 -0
- package/dist/harness/scheduler.d.ts.map +1 -0
- package/dist/harness/scheduler.js +605 -0
- package/dist/harness/scheduler.js.map +1 -0
- package/dist/harness/submissions.d.ts +24 -0
- package/dist/harness/submissions.d.ts.map +1 -0
- package/dist/harness/submissions.js +133 -0
- package/dist/harness/submissions.js.map +1 -0
- package/dist/harness/types.d.ts +358 -0
- package/dist/harness/types.d.ts.map +1 -0
- package/dist/harness/types.js +2 -0
- package/dist/harness/types.js.map +1 -0
- package/dist/harness/util.d.ts +14 -0
- package/dist/harness/util.d.ts.map +1 -0
- package/dist/harness/util.js +55 -0
- package/dist/harness/util.js.map +1 -0
- package/dist/ids.d.ts +6 -0
- package/dist/ids.d.ts.map +1 -0
- package/dist/ids.js +9 -0
- package/dist/ids.js.map +1 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/session/forks.d.ts +10 -0
- package/dist/session/forks.d.ts.map +1 -0
- package/dist/session/forks.js +42 -0
- package/dist/session/forks.js.map +1 -0
- package/dist/session/observation.d.ts +28 -0
- package/dist/session/observation.d.ts.map +1 -0
- package/dist/session/observation.js +280 -0
- package/dist/session/observation.js.map +1 -0
- package/dist/session/session.d.ts +59 -0
- package/dist/session/session.d.ts.map +1 -0
- package/dist/session/session.js +380 -0
- package/dist/session/session.js.map +1 -0
- package/dist/session/transaction.d.ts +105 -0
- package/dist/session/transaction.d.ts.map +1 -0
- package/dist/session/transaction.js +718 -0
- package/dist/session/transaction.js.map +1 -0
- package/dist/storage/jsonl/index.d.ts +2 -0
- package/dist/storage/jsonl/index.d.ts.map +1 -0
- package/dist/storage/jsonl/index.js +2 -0
- package/dist/storage/jsonl/index.js.map +1 -0
- package/dist/storage/jsonl/node.d.ts +6 -0
- package/dist/storage/jsonl/node.d.ts.map +1 -0
- package/dist/storage/jsonl/node.js +8 -0
- package/dist/storage/jsonl/node.js.map +1 -0
- package/dist/storage/jsonl/storage.d.ts +115 -0
- package/dist/storage/jsonl/storage.d.ts.map +1 -0
- package/dist/storage/jsonl/storage.js +681 -0
- package/dist/storage/jsonl/storage.js.map +1 -0
- package/dist/storage/memory.d.ts +31 -14
- package/dist/storage/memory.d.ts.map +1 -1
- package/dist/storage/memory.js +208 -61
- package/dist/storage/memory.js.map +1 -1
- package/dist/storage/sqlite/database.d.ts +5 -1
- package/dist/storage/sqlite/database.d.ts.map +1 -1
- package/dist/storage/sqlite/database.js.map +1 -1
- package/dist/storage/sqlite/index.d.ts.map +1 -1
- package/dist/storage/sqlite/migrations.d.ts +1 -1
- package/dist/storage/sqlite/migrations.d.ts.map +1 -1
- package/dist/storage/sqlite/migrations.js +8 -1
- package/dist/storage/sqlite/migrations.js.map +1 -1
- package/dist/storage/sqlite/node.d.ts.map +1 -1
- package/dist/storage/sqlite/node.js +2 -2
- package/dist/storage/sqlite/node.js.map +1 -1
- package/dist/storage/sqlite/storage.d.ts +20 -14
- package/dist/storage/sqlite/storage.d.ts.map +1 -1
- package/dist/storage/sqlite/storage.js +159 -55
- package/dist/storage/sqlite/storage.js.map +1 -1
- package/dist/tasks.d.ts +6 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +5 -0
- package/dist/tasks.js.map +1 -0
- package/dist/testing/assertions.d.ts +5 -0
- package/dist/testing/assertions.d.ts.map +1 -0
- package/dist/testing/assertions.js +25 -0
- package/dist/testing/assertions.js.map +1 -0
- package/dist/testing/index.d.ts +6 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +5 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/runner.d.ts +10 -0
- package/dist/testing/runner.d.ts.map +1 -0
- package/dist/testing/runner.js +14 -0
- package/dist/testing/runner.js.map +1 -0
- package/dist/testing/storage-benchmark.d.ts +41 -0
- package/dist/testing/storage-benchmark.d.ts.map +1 -0
- package/dist/testing/storage-benchmark.js +351 -0
- package/dist/testing/storage-benchmark.js.map +1 -0
- package/dist/testing/storage-conformance.d.ts +4 -0
- package/dist/testing/storage-conformance.d.ts.map +1 -0
- package/dist/testing/storage-conformance.js +1122 -0
- package/dist/testing/storage-conformance.js.map +1 -0
- package/dist/testing/types.d.ts +19 -0
- package/dist/testing/types.d.ts.map +1 -0
- package/dist/testing/types.js +2 -0
- package/dist/testing/types.js.map +1 -0
- package/dist/types.d.ts +422 -54
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/docs/chord-delta-findings.md +1 -1
- package/docs/pico-v5-chord-usage.md +47 -60
- package/docs/pico-v5-handoff.md +469 -264
- package/docs/pico-v5.md +1523 -639
- package/package.json +30 -5
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import { replicatedState } from "@earendil-works/chord";
|
|
2
|
+
import { awaitWithContext, withoutAbortSignal } from "@earendil-works/chord/context";
|
|
3
|
+
import { track } from "@earendil-works/chord/delta";
|
|
4
|
+
import { checkRecordScope, checkRecordVersion, materializeDocument, resolveAddress, } from "../documents.js";
|
|
5
|
+
import { StorageRejected } from "../errors.js";
|
|
6
|
+
import { idFromNumber } from "../ids.js";
|
|
7
|
+
import { RETIREMENT_OPERATIONS, SessionDocumentSource, SessionDocumentWatch } from "./observation.js";
|
|
8
|
+
import { Transaction } from "./transaction.js";
|
|
9
|
+
/** Open a Session kernel over one storage backend. */
|
|
10
|
+
export function createSession(storage) {
|
|
11
|
+
return new SessionImpl(storage);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Session kernel: one mutation line, the loaded document tracker cache, and committed publication.
|
|
15
|
+
*
|
|
16
|
+
* Only committed state is observable. Every commit callback, preparation, Storage settlement, adoption, and
|
|
17
|
+
* publication enqueue runs while the line is held; listeners run later.
|
|
18
|
+
*/
|
|
19
|
+
export class SessionImpl {
|
|
20
|
+
#storage;
|
|
21
|
+
#documents = new Map();
|
|
22
|
+
#commitListeners = new Set();
|
|
23
|
+
#closeListeners = new Set();
|
|
24
|
+
#host;
|
|
25
|
+
#tail = Promise.resolve();
|
|
26
|
+
#closing;
|
|
27
|
+
#poison;
|
|
28
|
+
constructor(storage) {
|
|
29
|
+
this.#storage = storage;
|
|
30
|
+
this.#host = {
|
|
31
|
+
storage,
|
|
32
|
+
cached: (id) => this.#documents.get(id),
|
|
33
|
+
load: (definition, addressId, address, context) => this.#loadDocument(definition, addressId, address, context),
|
|
34
|
+
install: (document) => {
|
|
35
|
+
this.#documents.set(document.addressId, document);
|
|
36
|
+
},
|
|
37
|
+
evict: (id, recordId) => {
|
|
38
|
+
if (this.#documents.get(id)?.record.id === recordId)
|
|
39
|
+
this.#documents.delete(id);
|
|
40
|
+
},
|
|
41
|
+
conversationCreated: (tx, record) => this.conversationCreated(tx, record),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
commit(change, context) {
|
|
45
|
+
return this.commitWith(change, context);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Internal commit exposing the concrete transaction and its internal operations, such as the reserved-ID root
|
|
49
|
+
* bootstrap and task replacement. `scope` sets the default `tx.createTask()` conversation and the task attributed to
|
|
50
|
+
* appended entries.
|
|
51
|
+
*/
|
|
52
|
+
commitWith(change, context, scope) {
|
|
53
|
+
try {
|
|
54
|
+
this.#assertUsable();
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
return Promise.reject(error);
|
|
58
|
+
}
|
|
59
|
+
return this.#enqueue(() => this.#runCommit(change, context, scope));
|
|
60
|
+
}
|
|
61
|
+
/** Internal: run a read-only job on the mutation line so multi-read derivations observe one committed state. */
|
|
62
|
+
readOnLine(job) {
|
|
63
|
+
try {
|
|
64
|
+
this.#assertUsable();
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
return Promise.reject(error);
|
|
68
|
+
}
|
|
69
|
+
return this.#enqueue(async () => {
|
|
70
|
+
this.#assertHealthy();
|
|
71
|
+
return job();
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
async snapshot(token, ...args) {
|
|
75
|
+
this.#assertUsable();
|
|
76
|
+
const definition = token.definition;
|
|
77
|
+
const resolved = resolveAddress(definition, args);
|
|
78
|
+
const context = args[resolved.nextArgument];
|
|
79
|
+
const cached = this.#documents.get(resolved.id);
|
|
80
|
+
const loaded = cached?.valueVersion === definition.version
|
|
81
|
+
? cached
|
|
82
|
+
: await this.#enqueue(async () => {
|
|
83
|
+
this.#assertHealthy();
|
|
84
|
+
return this.#loadDocument(definition, resolved.id, resolved.address, context);
|
|
85
|
+
});
|
|
86
|
+
if (loaded === undefined)
|
|
87
|
+
return undefined;
|
|
88
|
+
checkRecordScope(definition, loaded.record);
|
|
89
|
+
checkRecordVersion(definition, loaded.record, loaded.storedVersion);
|
|
90
|
+
return loaded.tracker.value;
|
|
91
|
+
}
|
|
92
|
+
documentState(token, ...args) {
|
|
93
|
+
try {
|
|
94
|
+
this.#assertUsable();
|
|
95
|
+
const definition = token.definition;
|
|
96
|
+
const resolved = resolveAddress(definition, args);
|
|
97
|
+
const context = args[resolved.nextArgument];
|
|
98
|
+
return this.#enqueue(async () => {
|
|
99
|
+
this.#assertHealthy();
|
|
100
|
+
const loaded = await this.#loadDocument(definition, resolved.id, resolved.address, context);
|
|
101
|
+
if (loaded === undefined)
|
|
102
|
+
return undefined;
|
|
103
|
+
const { observer: source, detach } = this.#attachDocument(definition, loaded, (value, release) => new SessionDocumentSource(value, release));
|
|
104
|
+
try {
|
|
105
|
+
return replicatedState(source);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
detach();
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
return Promise.reject(error);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async watchDoc(token, ...args) {
|
|
118
|
+
this.#assertUsable();
|
|
119
|
+
const definition = token.definition;
|
|
120
|
+
const resolved = resolveAddress(definition, args);
|
|
121
|
+
const context = args[resolved.nextArgument];
|
|
122
|
+
const signal = context.abortSignal;
|
|
123
|
+
let cancelled = signal?.aborted ?? false;
|
|
124
|
+
const markCancelled = () => {
|
|
125
|
+
cancelled = true;
|
|
126
|
+
};
|
|
127
|
+
signal?.addEventListener("abort", markCancelled, { once: true });
|
|
128
|
+
try {
|
|
129
|
+
const watch = await this.#enqueue(async () => {
|
|
130
|
+
this.#assertHealthy();
|
|
131
|
+
if (cancelled)
|
|
132
|
+
throw cancellationError(signal);
|
|
133
|
+
const loaded = await this.#loadDocument(definition, resolved.id, resolved.address, context);
|
|
134
|
+
if (cancelled)
|
|
135
|
+
throw cancellationError(signal);
|
|
136
|
+
if (loaded === undefined)
|
|
137
|
+
return undefined;
|
|
138
|
+
return this.#attachDocument(definition, loaded, (value, release) => new SessionDocumentWatch(value, release)).observer;
|
|
139
|
+
});
|
|
140
|
+
if (watch === undefined)
|
|
141
|
+
return undefined;
|
|
142
|
+
if (cancelled) {
|
|
143
|
+
watch.cancel();
|
|
144
|
+
throw cancellationError(signal);
|
|
145
|
+
}
|
|
146
|
+
if (signal !== undefined)
|
|
147
|
+
watch.observeCancellation(signal);
|
|
148
|
+
return watch;
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
signal?.removeEventListener("abort", markCancelled);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async snapshotAsOf(token, ...args) {
|
|
155
|
+
this.#assertUsable();
|
|
156
|
+
const definition = token.definition;
|
|
157
|
+
const resolved = resolveAddress(definition, args);
|
|
158
|
+
if (resolved.address.scope.kind !== "conversation") {
|
|
159
|
+
throw new TypeError("Session.snapshotAsOf() requires a conversation document");
|
|
160
|
+
}
|
|
161
|
+
const conversationId = resolved.address.scope.conversationId;
|
|
162
|
+
const atValue = args[resolved.nextArgument];
|
|
163
|
+
if (typeof atValue !== "number" || !Number.isSafeInteger(atValue)) {
|
|
164
|
+
throw new TypeError("Session.snapshotAsOf() requires an entry ID");
|
|
165
|
+
}
|
|
166
|
+
const at = idFromNumber(atValue);
|
|
167
|
+
const context = args[resolved.nextArgument + 1];
|
|
168
|
+
return this.#enqueue(async () => {
|
|
169
|
+
this.#assertHealthy();
|
|
170
|
+
const storedEntry = await this.#storage.entry(conversationId, at, context);
|
|
171
|
+
if (storedEntry === undefined) {
|
|
172
|
+
throw new Error(`Entry ${at} is not visible from conversation ${conversationId}`);
|
|
173
|
+
}
|
|
174
|
+
const address = {
|
|
175
|
+
...resolved.address,
|
|
176
|
+
scope: { kind: "conversation", conversationId: storedEntry.entry.conversationId },
|
|
177
|
+
};
|
|
178
|
+
const record = await this.#storage.findDocument(address, storedEntry.commitSeq, context);
|
|
179
|
+
if (record === undefined)
|
|
180
|
+
return undefined;
|
|
181
|
+
const stored = await this.#storage.document(record.id, storedEntry.commitSeq, context);
|
|
182
|
+
if (stored === undefined) {
|
|
183
|
+
throw new Error(`Historical document ${record.id} (${record.kind}) cannot be read`);
|
|
184
|
+
}
|
|
185
|
+
return materializeDocument(definition, stored);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
close(context) {
|
|
189
|
+
if (this.#closing === undefined) {
|
|
190
|
+
const cleanup = withoutAbortSignal(context);
|
|
191
|
+
// Seal admission before anything else runs, then stop observers; admitted work settles before Storage closes.
|
|
192
|
+
this.#closing = Promise.resolve()
|
|
193
|
+
.then(() => this.beforeClose())
|
|
194
|
+
.then(() => this.#enqueue(async () => {
|
|
195
|
+
this.#commitListeners.clear();
|
|
196
|
+
this.#documents.clear();
|
|
197
|
+
await this.#storage.close(cleanup);
|
|
198
|
+
}));
|
|
199
|
+
const listeners = [...this.#closeListeners];
|
|
200
|
+
this.#closeListeners.clear();
|
|
201
|
+
for (const listener of listeners)
|
|
202
|
+
listener();
|
|
203
|
+
}
|
|
204
|
+
return awaitWithContext(this.#closing, context);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Runs inside every transaction that creates or forks a conversation, after the conversation record is staged. A
|
|
208
|
+
* plain Session stages nothing; a Harness stages its built-in documents.
|
|
209
|
+
*/
|
|
210
|
+
conversationCreated(_tx, _record) {
|
|
211
|
+
return Promise.resolve();
|
|
212
|
+
}
|
|
213
|
+
/** Runs after close seals admission and before the line closes Storage; must not reject. */
|
|
214
|
+
beforeClose() {
|
|
215
|
+
return Promise.resolve();
|
|
216
|
+
}
|
|
217
|
+
/** Register a synchronous post-adoption listener. It must not throw, block, or call Session operations. */
|
|
218
|
+
subscribeCommits(listener) {
|
|
219
|
+
this.#assertUsable();
|
|
220
|
+
this.#commitListeners.add(listener);
|
|
221
|
+
return () => this.#commitListeners.delete(listener);
|
|
222
|
+
}
|
|
223
|
+
/** Register a listener called synchronously when close begins. It must not throw, block, or call Session operations. */
|
|
224
|
+
subscribeClose(listener) {
|
|
225
|
+
this.#assertUsable();
|
|
226
|
+
this.#closeListeners.add(listener);
|
|
227
|
+
return () => this.#closeListeners.delete(listener);
|
|
228
|
+
}
|
|
229
|
+
/** Drop every loaded tracker on the mutation line; later access cold-loads from Storage. */
|
|
230
|
+
unloadDocuments() {
|
|
231
|
+
return this.#enqueue(async () => {
|
|
232
|
+
this.#documents.clear();
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
async #runCommit(change, context, scope) {
|
|
236
|
+
this.#assertHealthy();
|
|
237
|
+
context.abortSignal?.throwIfAborted();
|
|
238
|
+
const tx = new Transaction(this.#host, context, scope);
|
|
239
|
+
let result;
|
|
240
|
+
try {
|
|
241
|
+
result = await change(tx);
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
await tx.settleFailure();
|
|
245
|
+
throw error;
|
|
246
|
+
}
|
|
247
|
+
const writes = await tx.settleSuccess();
|
|
248
|
+
if (writes.length === 0) {
|
|
249
|
+
tx.discard();
|
|
250
|
+
return result;
|
|
251
|
+
}
|
|
252
|
+
let seq;
|
|
253
|
+
try {
|
|
254
|
+
// Once admitted, caller cancellation does not interrupt Storage settlement.
|
|
255
|
+
seq = await this.#storage.commit(writes, withoutAbortSignal(context));
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
tx.discard();
|
|
259
|
+
// Callback errors never reach this branch; StorageRejected alone guarantees that no batch effect committed.
|
|
260
|
+
if (!(error instanceof StorageRejected))
|
|
261
|
+
this.#poison = { error };
|
|
262
|
+
throw error;
|
|
263
|
+
}
|
|
264
|
+
let documents;
|
|
265
|
+
try {
|
|
266
|
+
documents = tx.adopt(seq);
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
// Storage already committed; a failed adoption leaves memory behind durable state.
|
|
270
|
+
this.#poison = { error };
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
this.#publish(seq, writes, documents, context);
|
|
274
|
+
return result;
|
|
275
|
+
}
|
|
276
|
+
#publish(seq, writes, documents, context) {
|
|
277
|
+
if (this.#commitListeners.size === 0)
|
|
278
|
+
return;
|
|
279
|
+
const changes = [];
|
|
280
|
+
for (const write of writes) {
|
|
281
|
+
switch (write.type) {
|
|
282
|
+
case "conversation":
|
|
283
|
+
case "entry":
|
|
284
|
+
case "task":
|
|
285
|
+
case "submission":
|
|
286
|
+
changes.push(write);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
for (const document of documents)
|
|
290
|
+
changes.push(document);
|
|
291
|
+
const publication = { seq, changes };
|
|
292
|
+
for (const listener of [...this.#commitListeners])
|
|
293
|
+
listener(publication, context);
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Attach an observer to one committed incarnation: check the definition, then forward this incarnation's committed
|
|
297
|
+
* changes and close. `detach` removes both subscriptions.
|
|
298
|
+
*/
|
|
299
|
+
#attachDocument(definition, loaded, create) {
|
|
300
|
+
checkRecordScope(definition, loaded.record);
|
|
301
|
+
checkRecordVersion(definition, loaded.record, loaded.storedVersion);
|
|
302
|
+
let unsubscribeCommit = () => { };
|
|
303
|
+
let unsubscribeClose = () => { };
|
|
304
|
+
const detach = () => {
|
|
305
|
+
unsubscribeCommit();
|
|
306
|
+
unsubscribeClose();
|
|
307
|
+
};
|
|
308
|
+
const observer = create(loaded.tracker.value, detach);
|
|
309
|
+
const observed = { version: loaded.valueVersion };
|
|
310
|
+
unsubscribeCommit = this.subscribeCommits((publication, context) => {
|
|
311
|
+
for (const change of publication.changes) {
|
|
312
|
+
if (change.type !== "document" || change.record.id !== loaded.record.id)
|
|
313
|
+
continue;
|
|
314
|
+
// A document state's frames carry no caller cancellation; a watch observes its own cancellation.
|
|
315
|
+
const frameContext = observer instanceof SessionDocumentSource ? withoutAbortSignal(context) : context;
|
|
316
|
+
observer.advance(change.value, observedOperations(observed, change), frameContext);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
unsubscribeClose = this.subscribeClose(() => observer.closeSession());
|
|
320
|
+
return { observer, detach };
|
|
321
|
+
}
|
|
322
|
+
async #loadDocument(definition, addressId, address, context) {
|
|
323
|
+
const cached = this.#documents.get(addressId);
|
|
324
|
+
// A tracker serves only tokens of the version its value was materialized for; others reload from Storage.
|
|
325
|
+
if (cached?.valueVersion === definition.version)
|
|
326
|
+
return cached;
|
|
327
|
+
if (cached !== undefined)
|
|
328
|
+
this.#documents.delete(addressId);
|
|
329
|
+
const record = await this.#storage.findDocument(address, "current", context);
|
|
330
|
+
if (record === undefined)
|
|
331
|
+
return undefined;
|
|
332
|
+
const stored = await this.#storage.document(record.id, "current", context);
|
|
333
|
+
if (stored === undefined)
|
|
334
|
+
throw new Error(`Current document ${record.id} (${record.kind}) cannot be read`);
|
|
335
|
+
const value = materializeDocument(definition, stored);
|
|
336
|
+
const loaded = {
|
|
337
|
+
addressId,
|
|
338
|
+
record: stored.record,
|
|
339
|
+
storedVersion: stored.version,
|
|
340
|
+
valueVersion: definition.version,
|
|
341
|
+
deltasSinceBase: stored.deltasSinceBase,
|
|
342
|
+
tracker: track(value),
|
|
343
|
+
};
|
|
344
|
+
this.#documents.set(addressId, loaded);
|
|
345
|
+
return loaded;
|
|
346
|
+
}
|
|
347
|
+
#enqueue(job) {
|
|
348
|
+
const run = this.#tail.then(job);
|
|
349
|
+
this.#tail = run.then(() => undefined, () => undefined);
|
|
350
|
+
return run;
|
|
351
|
+
}
|
|
352
|
+
#assertUsable() {
|
|
353
|
+
if (this.#closing !== undefined)
|
|
354
|
+
throw new Error("Session is closed");
|
|
355
|
+
this.#assertHealthy();
|
|
356
|
+
}
|
|
357
|
+
#assertHealthy() {
|
|
358
|
+
if (this.#poison !== undefined) {
|
|
359
|
+
throw new Error("Session is poisoned by a failed commit after storage admission; reopen it", {
|
|
360
|
+
cause: this.#poison.error,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Operations an observer applies for one committed change. An observer hydrated under another definition version holds
|
|
367
|
+
* a differently shaped value, so it receives the new value as a root replacement instead of operations for that shape.
|
|
368
|
+
*/
|
|
369
|
+
function observedOperations(observed, change) {
|
|
370
|
+
if (change.value === null)
|
|
371
|
+
return RETIREMENT_OPERATIONS;
|
|
372
|
+
if (change.version === observed.version)
|
|
373
|
+
return change.ops;
|
|
374
|
+
observed.version = change.version;
|
|
375
|
+
return [["r", change.value]];
|
|
376
|
+
}
|
|
377
|
+
function cancellationError(signal) {
|
|
378
|
+
return signal.reason instanceof Error ? signal.reason : new DOMException("The operation was aborted", "AbortError");
|
|
379
|
+
}
|
|
380
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/session/session.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Context, type JsonValue, replicatedState } from \"@earendil-works/chord\";\nimport { awaitWithContext, withoutAbortSignal } from \"@earendil-works/chord/context\";\nimport { type Op, track } from \"@earendil-works/chord/delta\";\nimport {\n\ttype AnyDocToken,\n\tcheckRecordScope,\n\tcheckRecordVersion,\n\tmaterializeDocument,\n\tresolveAddress,\n} from \"../documents.ts\";\nimport { StorageRejected } from \"../errors.ts\";\nimport { idFromNumber } from \"../ids.ts\";\nimport type {\n\tCommitChange,\n\tCommitPublication,\n\tConversationDocFamilyToken,\n\tConversationDocToken,\n\tConversationId,\n\tConversationRecord,\n\tDocumentAddress,\n\tDocumentCommitChange,\n\tDocumentState,\n\tDocumentWatch,\n\tEntryId,\n\tJsonObject,\n\tRewindableConversationDocFamilyToken,\n\tRewindableConversationDocToken,\n\tSeq,\n\tSession,\n\tSessionDocFamilyToken,\n\tSessionDocToken,\n\tStorage,\n\tStorageWrite,\n\tTaskDocFamilyToken,\n\tTaskDocToken,\n\tTaskId,\n\tTx,\n} from \"../types.ts\";\nimport { RETIREMENT_OPERATIONS, SessionDocumentSource, SessionDocumentWatch } from \"./observation.ts\";\nimport { type LoadedDocument, Transaction, type TransactionHost, type TransactionScope } from \"./transaction.ts\";\n\n/** Open a Session kernel over one storage backend. */\nexport function createSession(storage: Storage): Session {\n\treturn new SessionImpl(storage);\n}\n\n/**\n * Session kernel: one mutation line, the loaded document tracker cache, and committed publication.\n *\n * Only committed state is observable. Every commit callback, preparation, Storage settlement, adoption, and\n * publication enqueue runs while the line is held; listeners run later.\n */\nexport class SessionImpl implements Session {\n\treadonly #storage: Storage;\n\treadonly #documents = new Map<string, LoadedDocument>();\n\treadonly #commitListeners = new Set<(publication: CommitPublication, context: Context) => void>();\n\treadonly #closeListeners = new Set<() => void>();\n\treadonly #host: TransactionHost;\n\t#tail: Promise<void> = Promise.resolve();\n\t#closing: Promise<void> | undefined;\n\t#poison: { readonly error: unknown } | undefined;\n\n\tconstructor(storage: Storage) {\n\t\tthis.#storage = storage;\n\t\tthis.#host = {\n\t\t\tstorage,\n\t\t\tcached: (id) => this.#documents.get(id),\n\t\t\tload: (definition, addressId, address, context) => this.#loadDocument(definition, addressId, address, context),\n\t\t\tinstall: (document) => {\n\t\t\t\tthis.#documents.set(document.addressId, document);\n\t\t\t},\n\t\t\tevict: (id, recordId) => {\n\t\t\t\tif (this.#documents.get(id)?.record.id === recordId) this.#documents.delete(id);\n\t\t\t},\n\t\t\tconversationCreated: (tx, record) => this.conversationCreated(tx, record),\n\t\t};\n\t}\n\n\tcommit<T>(change: (tx: Tx) => T | Promise<T>, context: Context): Promise<T> {\n\t\treturn this.commitWith(change, context);\n\t}\n\n\t/**\n\t * Internal commit exposing the concrete transaction and its internal operations, such as the reserved-ID root\n\t * bootstrap and task replacement. `scope` sets the default `tx.createTask()` conversation and the task attributed to\n\t * appended entries.\n\t */\n\tcommitWith<T>(change: (tx: Transaction) => T | Promise<T>, context: Context, scope?: TransactionScope): Promise<T> {\n\t\ttry {\n\t\t\tthis.#assertUsable();\n\t\t} catch (error) {\n\t\t\treturn Promise.reject(error);\n\t\t}\n\t\treturn this.#enqueue(() => this.#runCommit(change, context, scope));\n\t}\n\n\t/** Internal: run a read-only job on the mutation line so multi-read derivations observe one committed state. */\n\treadOnLine<T>(job: () => Promise<T>): Promise<T> {\n\t\ttry {\n\t\t\tthis.#assertUsable();\n\t\t} catch (error) {\n\t\t\treturn Promise.reject(error);\n\t\t}\n\t\treturn this.#enqueue(async () => {\n\t\t\tthis.#assertHealthy();\n\t\t\treturn job();\n\t\t});\n\t}\n\n\tsnapshot<T extends JsonObject>(token: SessionDocToken<T>, context: Context): Promise<Readonly<T> | undefined>;\n\tsnapshot<T extends JsonObject>(\n\t\ttoken: ConversationDocToken<T>,\n\t\tconversationId: ConversationId,\n\t\tcontext: Context,\n\t): Promise<Readonly<T> | undefined>;\n\tsnapshot<T extends JsonObject>(\n\t\ttoken: TaskDocToken<T>,\n\t\ttaskId: TaskId,\n\t\tcontext: Context,\n\t): Promise<Readonly<T> | undefined>;\n\tsnapshot<T extends JsonObject, I extends JsonValue>(\n\t\ttoken: SessionDocFamilyToken<T, I>,\n\t\tkey: string,\n\t\tcontext: Context,\n\t): Promise<Readonly<T> | undefined>;\n\tsnapshot<T extends JsonObject, I extends JsonValue>(\n\t\ttoken: ConversationDocFamilyToken<T, I>,\n\t\tconversationId: ConversationId,\n\t\tkey: string,\n\t\tcontext: Context,\n\t): Promise<Readonly<T> | undefined>;\n\tsnapshot<T extends JsonObject, I extends JsonValue>(\n\t\ttoken: TaskDocFamilyToken<T, I>,\n\t\ttaskId: TaskId,\n\t\tkey: string,\n\t\tcontext: Context,\n\t): Promise<Readonly<T> | undefined>;\n\tasync snapshot(token: AnyDocToken, ...args: readonly unknown[]): Promise<JsonObject | undefined> {\n\t\tthis.#assertUsable();\n\t\tconst definition = token.definition;\n\t\tconst resolved = resolveAddress(definition, args);\n\t\tconst context = args[resolved.nextArgument] as Context;\n\t\tconst cached = this.#documents.get(resolved.id);\n\t\tconst loaded =\n\t\t\tcached?.valueVersion === definition.version\n\t\t\t\t? cached\n\t\t\t\t: await this.#enqueue(async () => {\n\t\t\t\t\t\tthis.#assertHealthy();\n\t\t\t\t\t\treturn this.#loadDocument(definition, resolved.id, resolved.address, context);\n\t\t\t\t\t});\n\t\tif (loaded === undefined) return undefined;\n\t\tcheckRecordScope(definition, loaded.record);\n\t\tcheckRecordVersion(definition, loaded.record, loaded.storedVersion);\n\t\treturn loaded.tracker.value;\n\t}\n\n\tdocumentState<T extends JsonObject>(\n\t\ttoken: SessionDocToken<T>,\n\t\tcontext: Context,\n\t): Promise<DocumentState<T> | undefined>;\n\tdocumentState<T extends JsonObject>(\n\t\ttoken: ConversationDocToken<T>,\n\t\tconversationId: ConversationId,\n\t\tcontext: Context,\n\t): Promise<DocumentState<T> | undefined>;\n\tdocumentState<T extends JsonObject>(\n\t\ttoken: TaskDocToken<T>,\n\t\ttaskId: TaskId,\n\t\tcontext: Context,\n\t): Promise<DocumentState<T> | undefined>;\n\tdocumentState<T extends JsonObject, I extends JsonValue>(\n\t\ttoken: SessionDocFamilyToken<T, I>,\n\t\tkey: string,\n\t\tcontext: Context,\n\t): Promise<DocumentState<T> | undefined>;\n\tdocumentState<T extends JsonObject, I extends JsonValue>(\n\t\ttoken: ConversationDocFamilyToken<T, I>,\n\t\tconversationId: ConversationId,\n\t\tkey: string,\n\t\tcontext: Context,\n\t): Promise<DocumentState<T> | undefined>;\n\tdocumentState<T extends JsonObject, I extends JsonValue>(\n\t\ttoken: TaskDocFamilyToken<T, I>,\n\t\ttaskId: TaskId,\n\t\tkey: string,\n\t\tcontext: Context,\n\t): Promise<DocumentState<T> | undefined>;\n\tdocumentState(token: AnyDocToken, ...args: readonly unknown[]): Promise<DocumentState<JsonObject> | undefined> {\n\t\ttry {\n\t\t\tthis.#assertUsable();\n\t\t\tconst definition = token.definition;\n\t\t\tconst resolved = resolveAddress(definition, args);\n\t\t\tconst context = args[resolved.nextArgument] as Context;\n\t\t\treturn this.#enqueue(async () => {\n\t\t\t\tthis.#assertHealthy();\n\t\t\t\tconst loaded = await this.#loadDocument(definition, resolved.id, resolved.address, context);\n\t\t\t\tif (loaded === undefined) return undefined;\n\t\t\t\tconst { observer: source, detach } = this.#attachDocument(\n\t\t\t\t\tdefinition,\n\t\t\t\t\tloaded,\n\t\t\t\t\t(value, release) => new SessionDocumentSource(value, release),\n\t\t\t\t);\n\t\t\t\ttry {\n\t\t\t\t\treturn replicatedState(source) as DocumentState<JsonObject>;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tdetach();\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (error) {\n\t\t\treturn Promise.reject(error);\n\t\t}\n\t}\n\n\twatchDoc<T extends JsonObject>(token: SessionDocToken<T>, context: Context): Promise<DocumentWatch<T> | undefined>;\n\twatchDoc<T extends JsonObject>(\n\t\ttoken: ConversationDocToken<T>,\n\t\tconversationId: ConversationId,\n\t\tcontext: Context,\n\t): Promise<DocumentWatch<T> | undefined>;\n\twatchDoc<T extends JsonObject>(\n\t\ttoken: TaskDocToken<T>,\n\t\ttaskId: TaskId,\n\t\tcontext: Context,\n\t): Promise<DocumentWatch<T> | undefined>;\n\twatchDoc<T extends JsonObject, I extends JsonValue>(\n\t\ttoken: SessionDocFamilyToken<T, I>,\n\t\tkey: string,\n\t\tcontext: Context,\n\t): Promise<DocumentWatch<T> | undefined>;\n\twatchDoc<T extends JsonObject, I extends JsonValue>(\n\t\ttoken: ConversationDocFamilyToken<T, I>,\n\t\tconversationId: ConversationId,\n\t\tkey: string,\n\t\tcontext: Context,\n\t): Promise<DocumentWatch<T> | undefined>;\n\twatchDoc<T extends JsonObject, I extends JsonValue>(\n\t\ttoken: TaskDocFamilyToken<T, I>,\n\t\ttaskId: TaskId,\n\t\tkey: string,\n\t\tcontext: Context,\n\t): Promise<DocumentWatch<T> | undefined>;\n\tasync watchDoc(token: AnyDocToken, ...args: readonly unknown[]): Promise<DocumentWatch<JsonObject> | undefined> {\n\t\tthis.#assertUsable();\n\t\tconst definition = token.definition;\n\t\tconst resolved = resolveAddress(definition, args);\n\t\tconst context = args[resolved.nextArgument] as Context;\n\t\tconst signal = context.abortSignal;\n\t\tlet cancelled = signal?.aborted ?? false;\n\t\tconst markCancelled = (): void => {\n\t\t\tcancelled = true;\n\t\t};\n\t\tsignal?.addEventListener(\"abort\", markCancelled, { once: true });\n\t\ttry {\n\t\t\tconst watch = await this.#enqueue(async () => {\n\t\t\t\tthis.#assertHealthy();\n\t\t\t\tif (cancelled) throw cancellationError(signal!);\n\t\t\t\tconst loaded = await this.#loadDocument(definition, resolved.id, resolved.address, context);\n\t\t\t\tif (cancelled) throw cancellationError(signal!);\n\t\t\t\tif (loaded === undefined) return undefined;\n\t\t\t\treturn this.#attachDocument(\n\t\t\t\t\tdefinition,\n\t\t\t\t\tloaded,\n\t\t\t\t\t(value, release) => new SessionDocumentWatch(value, release),\n\t\t\t\t).observer;\n\t\t\t});\n\t\t\tif (watch === undefined) return undefined;\n\t\t\tif (cancelled) {\n\t\t\t\twatch.cancel();\n\t\t\t\tthrow cancellationError(signal!);\n\t\t\t}\n\t\t\tif (signal !== undefined) watch.observeCancellation(signal);\n\t\t\treturn watch as DocumentWatch<JsonObject>;\n\t\t} finally {\n\t\t\tsignal?.removeEventListener(\"abort\", markCancelled);\n\t\t}\n\t}\n\n\tsnapshotAsOf<T extends JsonObject>(\n\t\ttoken: RewindableConversationDocToken<T>,\n\t\tconversationId: ConversationId,\n\t\tat: EntryId,\n\t\tcontext: Context,\n\t): Promise<Readonly<T> | undefined>;\n\tsnapshotAsOf<T extends JsonObject, I extends JsonValue>(\n\t\ttoken: RewindableConversationDocFamilyToken<T, I>,\n\t\tconversationId: ConversationId,\n\t\tkey: string,\n\t\tat: EntryId,\n\t\tcontext: Context,\n\t): Promise<Readonly<T> | undefined>;\n\tasync snapshotAsOf(token: AnyDocToken, ...args: readonly unknown[]): Promise<JsonObject | undefined> {\n\t\tthis.#assertUsable();\n\t\tconst definition = token.definition;\n\t\tconst resolved = resolveAddress(definition, args);\n\t\tif (resolved.address.scope.kind !== \"conversation\") {\n\t\t\tthrow new TypeError(\"Session.snapshotAsOf() requires a conversation document\");\n\t\t}\n\t\tconst conversationId = resolved.address.scope.conversationId;\n\t\tconst atValue = args[resolved.nextArgument];\n\t\tif (typeof atValue !== \"number\" || !Number.isSafeInteger(atValue)) {\n\t\t\tthrow new TypeError(\"Session.snapshotAsOf() requires an entry ID\");\n\t\t}\n\t\tconst at = idFromNumber<EntryId>(atValue);\n\t\tconst context = args[resolved.nextArgument + 1] as Context;\n\t\treturn this.#enqueue(async () => {\n\t\t\tthis.#assertHealthy();\n\t\t\tconst storedEntry = await this.#storage.entry(conversationId, at, context);\n\t\t\tif (storedEntry === undefined) {\n\t\t\t\tthrow new Error(`Entry ${at} is not visible from conversation ${conversationId}`);\n\t\t\t}\n\t\t\tconst address: DocumentAddress = {\n\t\t\t\t...resolved.address,\n\t\t\t\tscope: { kind: \"conversation\", conversationId: storedEntry.entry.conversationId },\n\t\t\t};\n\t\t\tconst record = await this.#storage.findDocument(address, storedEntry.commitSeq, context);\n\t\t\tif (record === undefined) return undefined;\n\t\t\tconst stored = await this.#storage.document(record.id, storedEntry.commitSeq, context);\n\t\t\tif (stored === undefined) {\n\t\t\t\tthrow new Error(`Historical document ${record.id} (${record.kind}) cannot be read`);\n\t\t\t}\n\t\t\treturn materializeDocument(definition, stored);\n\t\t});\n\t}\n\n\tclose(context: Context): Promise<void> {\n\t\tif (this.#closing === undefined) {\n\t\t\tconst cleanup = withoutAbortSignal(context);\n\t\t\t// Seal admission before anything else runs, then stop observers; admitted work settles before Storage closes.\n\t\t\tthis.#closing = Promise.resolve()\n\t\t\t\t.then(() => this.beforeClose())\n\t\t\t\t.then(() =>\n\t\t\t\t\tthis.#enqueue(async () => {\n\t\t\t\t\t\tthis.#commitListeners.clear();\n\t\t\t\t\t\tthis.#documents.clear();\n\t\t\t\t\t\tawait this.#storage.close(cleanup);\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\tconst listeners = [...this.#closeListeners];\n\t\t\tthis.#closeListeners.clear();\n\t\t\tfor (const listener of listeners) listener();\n\t\t}\n\t\treturn awaitWithContext(this.#closing, context);\n\t}\n\n\t/**\n\t * Runs inside every transaction that creates or forks a conversation, after the conversation record is staged. A\n\t * plain Session stages nothing; a Harness stages its built-in documents.\n\t */\n\tprotected conversationCreated(_tx: Transaction, _record: ConversationRecord): Promise<void> {\n\t\treturn Promise.resolve();\n\t}\n\n\t/** Runs after close seals admission and before the line closes Storage; must not reject. */\n\tprotected beforeClose(): Promise<void> {\n\t\treturn Promise.resolve();\n\t}\n\n\t/** Register a synchronous post-adoption listener. It must not throw, block, or call Session operations. */\n\tsubscribeCommits(listener: (publication: CommitPublication, context: Context) => void): () => void {\n\t\tthis.#assertUsable();\n\t\tthis.#commitListeners.add(listener);\n\t\treturn () => this.#commitListeners.delete(listener);\n\t}\n\n\t/** Register a listener called synchronously when close begins. It must not throw, block, or call Session operations. */\n\tsubscribeClose(listener: () => void): () => void {\n\t\tthis.#assertUsable();\n\t\tthis.#closeListeners.add(listener);\n\t\treturn () => this.#closeListeners.delete(listener);\n\t}\n\n\t/** Drop every loaded tracker on the mutation line; later access cold-loads from Storage. */\n\tunloadDocuments(): Promise<void> {\n\t\treturn this.#enqueue(async () => {\n\t\t\tthis.#documents.clear();\n\t\t});\n\t}\n\n\tasync #runCommit<T>(\n\t\tchange: (tx: Transaction) => T | Promise<T>,\n\t\tcontext: Context,\n\t\tscope?: TransactionScope,\n\t): Promise<T> {\n\t\tthis.#assertHealthy();\n\t\tcontext.abortSignal?.throwIfAborted();\n\t\tconst tx = new Transaction(this.#host, context, scope);\n\t\tlet result: T;\n\t\ttry {\n\t\t\tresult = await change(tx);\n\t\t} catch (error) {\n\t\t\tawait tx.settleFailure();\n\t\t\tthrow error;\n\t\t}\n\t\tconst writes = await tx.settleSuccess();\n\t\tif (writes.length === 0) {\n\t\t\ttx.discard();\n\t\t\treturn result;\n\t\t}\n\t\tlet seq: Seq;\n\t\ttry {\n\t\t\t// Once admitted, caller cancellation does not interrupt Storage settlement.\n\t\t\tseq = await this.#storage.commit(writes, withoutAbortSignal(context));\n\t\t} catch (error) {\n\t\t\ttx.discard();\n\t\t\t// Callback errors never reach this branch; StorageRejected alone guarantees that no batch effect committed.\n\t\t\tif (!(error instanceof StorageRejected)) this.#poison = { error };\n\t\t\tthrow error;\n\t\t}\n\t\tlet documents: DocumentCommitChange[];\n\t\ttry {\n\t\t\tdocuments = tx.adopt(seq);\n\t\t} catch (error) {\n\t\t\t// Storage already committed; a failed adoption leaves memory behind durable state.\n\t\t\tthis.#poison = { error };\n\t\t\tthrow error;\n\t\t}\n\t\tthis.#publish(seq, writes, documents, context);\n\t\treturn result;\n\t}\n\n\t#publish(\n\t\tseq: Seq,\n\t\twrites: readonly StorageWrite[],\n\t\tdocuments: readonly DocumentCommitChange[],\n\t\tcontext: Context,\n\t): void {\n\t\tif (this.#commitListeners.size === 0) return;\n\t\tconst changes: CommitChange[] = [];\n\t\tfor (const write of writes) {\n\t\t\tswitch (write.type) {\n\t\t\t\tcase \"conversation\":\n\t\t\t\tcase \"entry\":\n\t\t\t\tcase \"task\":\n\t\t\t\tcase \"submission\":\n\t\t\t\t\tchanges.push(write);\n\t\t\t}\n\t\t}\n\t\tfor (const document of documents) changes.push(document);\n\t\tconst publication: CommitPublication = { seq, changes };\n\t\tfor (const listener of [...this.#commitListeners]) listener(publication, context);\n\t}\n\n\t/**\n\t * Attach an observer to one committed incarnation: check the definition, then forward this incarnation's committed\n\t * changes and close. `detach` removes both subscriptions.\n\t */\n\t#attachDocument<O extends SessionDocumentSource | SessionDocumentWatch>(\n\t\tdefinition: AnyDocToken[\"definition\"],\n\t\tloaded: LoadedDocument,\n\t\tcreate: (value: JsonObject, detach: () => void) => O,\n\t): { observer: O; detach: () => void } {\n\t\tcheckRecordScope(definition, loaded.record);\n\t\tcheckRecordVersion(definition, loaded.record, loaded.storedVersion);\n\t\tlet unsubscribeCommit = (): void => {};\n\t\tlet unsubscribeClose = (): void => {};\n\t\tconst detach = (): void => {\n\t\t\tunsubscribeCommit();\n\t\t\tunsubscribeClose();\n\t\t};\n\t\tconst observer = create(loaded.tracker.value, detach);\n\t\tconst observed = { version: loaded.valueVersion };\n\t\tunsubscribeCommit = this.subscribeCommits((publication, context) => {\n\t\t\tfor (const change of publication.changes) {\n\t\t\t\tif (change.type !== \"document\" || change.record.id !== loaded.record.id) continue;\n\t\t\t\t// A document state's frames carry no caller cancellation; a watch observes its own cancellation.\n\t\t\t\tconst frameContext = observer instanceof SessionDocumentSource ? withoutAbortSignal(context) : context;\n\t\t\t\tobserver.advance(change.value, observedOperations(observed, change), frameContext);\n\t\t\t}\n\t\t});\n\t\tunsubscribeClose = this.subscribeClose(() => observer.closeSession());\n\t\treturn { observer, detach };\n\t}\n\n\tasync #loadDocument(\n\t\tdefinition: AnyDocToken[\"definition\"],\n\t\taddressId: string,\n\t\taddress: DocumentAddress,\n\t\tcontext: Context,\n\t): Promise<LoadedDocument | undefined> {\n\t\tconst cached = this.#documents.get(addressId);\n\t\t// A tracker serves only tokens of the version its value was materialized for; others reload from Storage.\n\t\tif (cached?.valueVersion === definition.version) return cached;\n\t\tif (cached !== undefined) this.#documents.delete(addressId);\n\t\tconst record = await this.#storage.findDocument(address, \"current\", context);\n\t\tif (record === undefined) return undefined;\n\t\tconst stored = await this.#storage.document(record.id, \"current\", context);\n\t\tif (stored === undefined) throw new Error(`Current document ${record.id} (${record.kind}) cannot be read`);\n\t\tconst value = materializeDocument(definition, stored);\n\t\tconst loaded: LoadedDocument = {\n\t\t\taddressId,\n\t\t\trecord: stored.record,\n\t\t\tstoredVersion: stored.version,\n\t\t\tvalueVersion: definition.version,\n\t\t\tdeltasSinceBase: stored.deltasSinceBase,\n\t\t\ttracker: track(value),\n\t\t};\n\t\tthis.#documents.set(addressId, loaded);\n\t\treturn loaded;\n\t}\n\n\t#enqueue<T>(job: () => Promise<T>): Promise<T> {\n\t\tconst run = this.#tail.then(job);\n\t\tthis.#tail = run.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn run;\n\t}\n\n\t#assertUsable(): void {\n\t\tif (this.#closing !== undefined) throw new Error(\"Session is closed\");\n\t\tthis.#assertHealthy();\n\t}\n\n\t#assertHealthy(): void {\n\t\tif (this.#poison !== undefined) {\n\t\t\tthrow new Error(\"Session is poisoned by a failed commit after storage admission; reopen it\", {\n\t\t\t\tcause: this.#poison.error,\n\t\t\t});\n\t\t}\n\t}\n}\n\n/**\n * Operations an observer applies for one committed change. An observer hydrated under another definition version holds\n * a differently shaped value, so it receives the new value as a root replacement instead of operations for that shape.\n */\nfunction observedOperations(\n\tobserved: { version: number },\n\tchange: Extract<DocumentCommitChange, { readonly type: \"document\" }>,\n): readonly Op[] {\n\tif (change.value === null) return RETIREMENT_OPERATIONS;\n\tif (change.version === observed.version) return change.ops;\n\tobserved.version = change.version!;\n\treturn [[\"r\", change.value]];\n}\n\nfunction cancellationError(signal: AbortSignal): Error {\n\treturn signal.reason instanceof Error ? signal.reason : new DOMException(\"The operation was aborted\", \"AbortError\");\n}\n"]}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { type Context, type Draft, type JsonValue } from "@earendil-works/chord";
|
|
2
|
+
import { type Tracker } from "@earendil-works/chord/delta";
|
|
3
|
+
import { type AnyDocDefinition } from "../documents.ts";
|
|
4
|
+
import type { ConversationDocFamilyToken, ConversationDocToken, ConversationId, ConversationOwnership, ConversationQuery, ConversationRecord, Cursor, DocumentAddress, DocumentCommitChange, DocumentId, DocumentRecord, Entry, EntryDraft, EntryId, EntryQuery, EntryRecord, JsonObject, Seq, SessionDocFamilyToken, SessionDocToken, Storage, StorageWrite, SubmissionCreate, SubmissionId, SubmissionRecord, SubmissionSettlement, Task, TaskDocFamilyToken, TaskDocToken, TaskId, TaskOptions, TaskQuery, TaskRecord, Tx, TypedEntry, TypedEntryDraft } from "../types.ts";
|
|
5
|
+
type AnyTaskRecord = TaskRecord<JsonValue, JsonValue, JsonValue>;
|
|
6
|
+
/** One committed document incarnation owned by the Session tracker cache. */
|
|
7
|
+
export type LoadedDocument = {
|
|
8
|
+
readonly addressId: string;
|
|
9
|
+
readonly record: DocumentRecord;
|
|
10
|
+
/** Persisted definition version; older while the tracked value is migrated only in memory. */
|
|
11
|
+
storedVersion: number;
|
|
12
|
+
/** Definition version whose shape the tracked value has; access with another version reloads from Storage. */
|
|
13
|
+
readonly valueVersion: number;
|
|
14
|
+
/** Stored deltas after the newest base; advanced by adoption so the next predicate call needs no read. */
|
|
15
|
+
deltasSinceBase: number;
|
|
16
|
+
readonly tracker: Tracker<JsonObject>;
|
|
17
|
+
};
|
|
18
|
+
/** Session services used by a transaction while it holds the mutation line. */
|
|
19
|
+
export interface TransactionHost {
|
|
20
|
+
readonly storage: Storage;
|
|
21
|
+
/** Return the cached current incarnation without loading. */
|
|
22
|
+
cached(addressId: string): LoadedDocument | undefined;
|
|
23
|
+
/** Return the cached current incarnation, cold-loading and migrating it when necessary. */
|
|
24
|
+
load(definition: AnyDocDefinition, addressId: string, address: DocumentAddress, context: Context): Promise<LoadedDocument | undefined>;
|
|
25
|
+
/** Install a newly committed incarnation. */
|
|
26
|
+
install(document: LoadedDocument): void;
|
|
27
|
+
/** Remove a retired incarnation if it is still the cached occupant of its address. */
|
|
28
|
+
evict(addressId: string, recordId: DocumentId): void;
|
|
29
|
+
/** Stage writes that belong to every newly created or forked conversation, in its creating transaction. */
|
|
30
|
+
conversationCreated(tx: Transaction, record: ConversationRecord): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
/** Defaults a commit binds to: `tx.createTask()` conversation and the task attributed to appended entries. */
|
|
33
|
+
export type TransactionScope = {
|
|
34
|
+
readonly conversationId?: ConversationId;
|
|
35
|
+
/** Task whose runtime commit this is; stamped as `byTaskId` on appended entries. */
|
|
36
|
+
readonly taskId?: TaskId;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Transaction for one Session commit callback.
|
|
40
|
+
*
|
|
41
|
+
* Every asynchronous operation is tracked so callback settlement can reject and drain unfinished work. Session calls
|
|
42
|
+
* one settlement method, then either discards prepared changes or adopts them once after Storage succeeds.
|
|
43
|
+
*/
|
|
44
|
+
export declare class Transaction implements Tx {
|
|
45
|
+
#private;
|
|
46
|
+
constructor(host: TransactionHost, context: Context, scope?: TransactionScope);
|
|
47
|
+
conversation(id: ConversationId): Promise<ConversationRecord | undefined>;
|
|
48
|
+
entry(id: EntryId): Promise<EntryRecord | undefined>;
|
|
49
|
+
entry<D extends JsonValue>(token: Entry<D>, id: EntryId): Promise<TypedEntry<D> | undefined>;
|
|
50
|
+
task(id: TaskId): Promise<AnyTaskRecord | undefined>;
|
|
51
|
+
scanConversations(query: ConversationQuery, limit: number, cursor?: Cursor): Promise<import("../types.ts").Page<ConversationRecord, Readonly<Record<string, JsonValue>>>>;
|
|
52
|
+
scanEntries(query: EntryQuery, limit: number, cursor?: Cursor): Promise<import("../types.ts").Page<EntryRecord, Readonly<Record<string, JsonValue>>>>;
|
|
53
|
+
scanTasks(query: TaskQuery, limit: number, cursor?: Cursor): Promise<import("../types.ts").Page<TaskRecord<JsonValue, JsonValue, JsonValue>, Readonly<Record<string, JsonValue>>>>;
|
|
54
|
+
/** Internal: committed submission record. */
|
|
55
|
+
submission(id: SubmissionId): Promise<SubmissionRecord | undefined>;
|
|
56
|
+
/** Internal: committed submission with a conversation-scoped request ID. */
|
|
57
|
+
submissionByRequest(conversationId: ConversationId, requestId: string): Promise<SubmissionRecord | undefined>;
|
|
58
|
+
createConversation(options: {
|
|
59
|
+
readonly ownership: ConversationOwnership;
|
|
60
|
+
}): Promise<ConversationRecord>;
|
|
61
|
+
/** Internal final-form bootstrap path for the reserved root identity. */
|
|
62
|
+
createRootConversation(): Promise<ConversationRecord>;
|
|
63
|
+
forkConversation(parentConversationId: ConversationId, at: EntryId, options: {
|
|
64
|
+
readonly ownership: ConversationOwnership;
|
|
65
|
+
}): Promise<ConversationRecord>;
|
|
66
|
+
appendEntry(conversationId: ConversationId, value: EntryDraft): Promise<EntryRecord>;
|
|
67
|
+
appendEntry<D extends JsonValue>(token: Entry<D>, conversationId: ConversationId, value: TypedEntryDraft<NoInfer<D>>): Promise<TypedEntry<D>>;
|
|
68
|
+
createTask<I, S extends {
|
|
69
|
+
phase: string;
|
|
70
|
+
}, R, H extends object>(task: Task<I, S, R, H>, input: I, options?: TaskOptions): Promise<TaskId<R>>;
|
|
71
|
+
/** Internal: create a submission record with a fresh ID. */
|
|
72
|
+
createSubmission(create: SubmissionCreate): Promise<SubmissionRecord>;
|
|
73
|
+
/**
|
|
74
|
+
* Settle a submission. Resolved during assembly against the transaction's latest candidate record, falling back to
|
|
75
|
+
* committed state, so it is not a caller table read and works after the first table write.
|
|
76
|
+
*/
|
|
77
|
+
settleSubmission(id: SubmissionId, settlement: SubmissionSettlement): void;
|
|
78
|
+
/** Internal: replace one task record completely. Tasks change their own state through their runtime. */
|
|
79
|
+
setTask(value: AnyTaskRecord): void;
|
|
80
|
+
doc<T extends JsonObject>(token: SessionDocToken<T>): Promise<Draft<T>>;
|
|
81
|
+
doc<T extends JsonObject>(token: ConversationDocToken<T>, conversationId: ConversationId): Promise<Draft<T>>;
|
|
82
|
+
doc<T extends JsonObject>(token: TaskDocToken<T>, taskId: TaskId): Promise<Draft<T>>;
|
|
83
|
+
doc<T extends JsonObject, I extends JsonValue>(token: SessionDocFamilyToken<T, I>, key: string, seed: I): Promise<Draft<T>>;
|
|
84
|
+
doc<T extends JsonObject, I extends JsonValue>(token: ConversationDocFamilyToken<T, I>, conversationId: ConversationId, key: string, seed: I): Promise<Draft<T>>;
|
|
85
|
+
doc<T extends JsonObject, I extends JsonValue>(token: TaskDocFamilyToken<T, I>, taskId: TaskId, key: string, seed: I): Promise<Draft<T>>;
|
|
86
|
+
retireDoc<T extends JsonObject>(token: SessionDocToken<T>): Promise<void>;
|
|
87
|
+
retireDoc<T extends JsonObject>(token: ConversationDocToken<T>, conversationId: ConversationId): Promise<void>;
|
|
88
|
+
retireDoc<T extends JsonObject>(token: TaskDocToken<T>, taskId: TaskId): Promise<void>;
|
|
89
|
+
retireDoc<T extends JsonObject, I extends JsonValue>(token: SessionDocFamilyToken<T, I>, key: string): Promise<void>;
|
|
90
|
+
retireDoc<T extends JsonObject, I extends JsonValue>(token: ConversationDocFamilyToken<T, I>, conversationId: ConversationId, key: string): Promise<void>;
|
|
91
|
+
retireDoc<T extends JsonObject, I extends JsonValue>(token: TaskDocFamilyToken<T, I>, taskId: TaskId, key: string): Promise<void>;
|
|
92
|
+
/** Seal after callback failure: abort every change and observe every pending operation. */
|
|
93
|
+
settleFailure(): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Seal after callback success, prepare every open change, and assemble the atomic batch.
|
|
96
|
+
* Any failure aborts every change before Storage admission.
|
|
97
|
+
*/
|
|
98
|
+
settleSuccess(): Promise<readonly StorageWrite[]>;
|
|
99
|
+
/** Abort every prepared change after Storage failure or when no write is required. */
|
|
100
|
+
discard(): void;
|
|
101
|
+
/** Adopt every prepared change by pointer swap after Storage success and describe the publication. */
|
|
102
|
+
adopt(seq: Seq): DocumentCommitChange[];
|
|
103
|
+
}
|
|
104
|
+
export {};
|
|
105
|
+
//# sourceMappingURL=transaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../../src/session/transaction.ts"],"names":[],"mappings":"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"}
|