@helipod/client 0.1.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/dist/chunk-DW55SNHW.js +286 -0
- package/dist/chunk-DW55SNHW.js.map +1 -0
- package/dist/chunk-I7ZJFW4E.js +2232 -0
- package/dist/chunk-I7ZJFW4E.js.map +1 -0
- package/dist/client-mJZjEkhK.d.ts +1144 -0
- package/dist/index.d.ts +268 -0
- package/dist/index.js +642 -0
- package/dist/index.js.map +1 -0
- package/dist/outbox-fs.d.ts +102 -0
- package/dist/outbox-fs.js +361 -0
- package/dist/outbox-fs.js.map +1 -0
- package/dist/outbox-storage-C6VHkXs9.d.ts +191 -0
- package/dist/react.d.ts +114 -0
- package/dist/react.js +139 -0
- package/dist/react.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
// src/outbox-idb.ts
|
|
2
|
+
var OUTBOX_VERSION = 1;
|
|
3
|
+
var OUTBOX_DB_NAME = "helipod-outbox";
|
|
4
|
+
var DB_VERSION = 1;
|
|
5
|
+
var ENTRIES_STORE = "entries";
|
|
6
|
+
var META_STORE = "meta";
|
|
7
|
+
function dropStaleVersion(all) {
|
|
8
|
+
const entries = [];
|
|
9
|
+
const dropped = [];
|
|
10
|
+
for (const e of all) {
|
|
11
|
+
if (e.outboxVersion === OUTBOX_VERSION) entries.push(e);
|
|
12
|
+
else dropped.push(e);
|
|
13
|
+
}
|
|
14
|
+
return { entries, dropped };
|
|
15
|
+
}
|
|
16
|
+
function promisifyRequest(request) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
request.onsuccess = () => resolve(request.result);
|
|
19
|
+
request.onerror = () => reject(request.error ?? new Error("IndexedDB request failed"));
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function promisifyTransaction(tx) {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
tx.oncomplete = () => resolve();
|
|
25
|
+
tx.onerror = () => reject(tx.error ?? new Error("IndexedDB transaction failed"));
|
|
26
|
+
tx.onabort = () => reject(tx.error ?? new Error("IndexedDB transaction aborted"));
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function openDatabase(idbFactory, dbName) {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
let request;
|
|
32
|
+
try {
|
|
33
|
+
request = idbFactory.open(dbName, DB_VERSION);
|
|
34
|
+
} catch (err) {
|
|
35
|
+
reject(err);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
request.onupgradeneeded = () => {
|
|
39
|
+
const db = request.result;
|
|
40
|
+
if (!db.objectStoreNames.contains(ENTRIES_STORE)) {
|
|
41
|
+
const store = db.createObjectStore(ENTRIES_STORE, { keyPath: ["clientId", "seq"] });
|
|
42
|
+
store.createIndex("order", "order", { unique: false });
|
|
43
|
+
store.createIndex("status", "status", { unique: false });
|
|
44
|
+
}
|
|
45
|
+
if (!db.objectStoreNames.contains(META_STORE)) {
|
|
46
|
+
db.createObjectStore(META_STORE, { keyPath: "clientId" });
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
request.onsuccess = () => resolve(request.result);
|
|
50
|
+
request.onerror = () => reject(request.error ?? new Error("IndexedDB open failed"));
|
|
51
|
+
request.onblocked = () => reject(new Error("IndexedDB open blocked by another connection"));
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
var IndexedDBOutboxStorage = class {
|
|
55
|
+
constructor(db) {
|
|
56
|
+
this.db = db;
|
|
57
|
+
}
|
|
58
|
+
db;
|
|
59
|
+
queue = [];
|
|
60
|
+
flushScheduled = false;
|
|
61
|
+
/** Exposed for tests only: how many `readwrite` transactions have actually been opened by the
|
|
62
|
+
* write-behind flush — the write-behind-batching property is "N appends in one microtask turn
|
|
63
|
+
* bump this by at most 1", not N. */
|
|
64
|
+
txnCount = 0;
|
|
65
|
+
schedule(build) {
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
this.queue.push(build(resolve, reject));
|
|
68
|
+
if (!this.flushScheduled) {
|
|
69
|
+
this.flushScheduled = true;
|
|
70
|
+
queueMicrotask(() => void this.flush());
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
append(entry) {
|
|
75
|
+
return this.schedule((resolve, reject) => ({ kind: "append", entry, resolve, reject }));
|
|
76
|
+
}
|
|
77
|
+
updateStatus(clientId, seq, status, error) {
|
|
78
|
+
return this.schedule((resolve, reject) => ({ kind: "updateStatus", clientId, seq, status, error, resolve, reject }));
|
|
79
|
+
}
|
|
80
|
+
dequeue(clientId, seq) {
|
|
81
|
+
return this.schedule((resolve, reject) => ({ kind: "dequeue", clientId, seq, resolve, reject }));
|
|
82
|
+
}
|
|
83
|
+
setMeta(clientId, meta) {
|
|
84
|
+
return this.schedule((resolve, reject) => ({ kind: "setMeta", clientId, meta, resolve, reject }));
|
|
85
|
+
}
|
|
86
|
+
async flush() {
|
|
87
|
+
const ops = this.queue;
|
|
88
|
+
this.queue = [];
|
|
89
|
+
this.flushScheduled = false;
|
|
90
|
+
if (ops.length === 0) return;
|
|
91
|
+
this.txnCount++;
|
|
92
|
+
let tx;
|
|
93
|
+
try {
|
|
94
|
+
tx = this.db.transaction([ENTRIES_STORE, META_STORE], "readwrite");
|
|
95
|
+
} catch (err) {
|
|
96
|
+
for (const op of ops) op.reject(err);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const entriesStore = tx.objectStore(ENTRIES_STORE);
|
|
100
|
+
const metaStore = tx.objectStore(META_STORE);
|
|
101
|
+
for (const op of ops) {
|
|
102
|
+
switch (op.kind) {
|
|
103
|
+
case "append":
|
|
104
|
+
entriesStore.put(op.entry);
|
|
105
|
+
break;
|
|
106
|
+
case "updateStatus": {
|
|
107
|
+
const getReq = entriesStore.get([op.clientId, op.seq]);
|
|
108
|
+
getReq.onsuccess = () => {
|
|
109
|
+
const existing = getReq.result;
|
|
110
|
+
if (existing) entriesStore.put({ ...existing, status: op.status, ...op.error !== void 0 ? { error: op.error } : {} });
|
|
111
|
+
};
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
case "dequeue":
|
|
115
|
+
entriesStore.delete([op.clientId, op.seq]);
|
|
116
|
+
break;
|
|
117
|
+
case "setMeta":
|
|
118
|
+
metaStore.put({ ...op.meta, clientId: op.clientId });
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
await promisifyTransaction(tx);
|
|
124
|
+
for (const op of ops) op.resolve();
|
|
125
|
+
} catch (err) {
|
|
126
|
+
for (const op of ops) op.reject(err);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async getMeta(clientId) {
|
|
130
|
+
const tx = this.db.transaction(META_STORE, "readonly");
|
|
131
|
+
const row = await promisifyRequest(tx.objectStore(META_STORE).get(clientId));
|
|
132
|
+
if (!row) return void 0;
|
|
133
|
+
const { clientId: _clientId, ...meta } = row;
|
|
134
|
+
void _clientId;
|
|
135
|
+
return meta;
|
|
136
|
+
}
|
|
137
|
+
async listMetaClientIds() {
|
|
138
|
+
const tx = this.db.transaction(META_STORE, "readonly");
|
|
139
|
+
const keys = await promisifyRequest(tx.objectStore(META_STORE).getAllKeys());
|
|
140
|
+
return keys.map((k) => String(k));
|
|
141
|
+
}
|
|
142
|
+
async deleteMeta(clientId) {
|
|
143
|
+
const tx = this.db.transaction(META_STORE, "readwrite");
|
|
144
|
+
tx.objectStore(META_STORE).delete(clientId);
|
|
145
|
+
await promisifyTransaction(tx);
|
|
146
|
+
}
|
|
147
|
+
async loadAll() {
|
|
148
|
+
const tx = this.db.transaction(ENTRIES_STORE, "readonly");
|
|
149
|
+
const all = await promisifyRequest(tx.objectStore(ENTRIES_STORE).getAll());
|
|
150
|
+
all.sort((a, b) => a.order - b.order);
|
|
151
|
+
const { entries, dropped } = dropStaleVersion(all);
|
|
152
|
+
if (dropped.length > 0) {
|
|
153
|
+
const delTx = this.db.transaction(ENTRIES_STORE, "readwrite");
|
|
154
|
+
const store = delTx.objectStore(ENTRIES_STORE);
|
|
155
|
+
for (const e of dropped) store.delete([e.clientId, e.seq]);
|
|
156
|
+
await promisifyTransaction(delTx);
|
|
157
|
+
}
|
|
158
|
+
return { entries, dropped };
|
|
159
|
+
}
|
|
160
|
+
persist() {
|
|
161
|
+
if (typeof navigator !== "undefined" && navigator.storage?.persist) {
|
|
162
|
+
void navigator.storage.persist().catch(() => {
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async close() {
|
|
167
|
+
await this.flush();
|
|
168
|
+
this.db.close();
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
async function openIndexedDBOutbox(idbFactory, dbName = OUTBOX_DB_NAME) {
|
|
172
|
+
const db = await openDatabase(idbFactory, dbName);
|
|
173
|
+
return new IndexedDBOutboxStorage(db);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// src/outbox-storage.ts
|
|
177
|
+
var DEFAULT_OUTBOX_MAX_QUEUE_SIZE = 1e3;
|
|
178
|
+
var OutboxOverflowError = class extends Error {
|
|
179
|
+
code = "OUTBOX_OVERFLOW";
|
|
180
|
+
constructor(message = "the durable outbox is full \u2014 this mutation was rejected so an already-queued promise, which may have no live awaiter across a reload, is never silently evicted") {
|
|
181
|
+
super(message);
|
|
182
|
+
this.name = "OutboxOverflowError";
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
var OfflineClientResetError = class extends Error {
|
|
186
|
+
code = "OFFLINE_CLIENT_RESET";
|
|
187
|
+
constructor(message = "the server disowned this client's mutation history (swept/foreign timeline); its identity was reset and this in-flight-at-disconnect mutation, whose outcome is unknowable, was rejected rather than blindly resent") {
|
|
188
|
+
super(message);
|
|
189
|
+
this.name = "OfflineClientResetError";
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
function memoryOutbox() {
|
|
193
|
+
const entries = /* @__PURE__ */ new Map();
|
|
194
|
+
const meta = /* @__PURE__ */ new Map();
|
|
195
|
+
const key = (clientId, seq) => `${clientId}\0${seq}`;
|
|
196
|
+
return {
|
|
197
|
+
async append(entry) {
|
|
198
|
+
entries.set(key(entry.clientId, entry.seq), { ...entry });
|
|
199
|
+
},
|
|
200
|
+
async updateStatus(clientId, seq, status, error) {
|
|
201
|
+
const existing = entries.get(key(clientId, seq));
|
|
202
|
+
if (existing) entries.set(key(clientId, seq), { ...existing, status, ...error !== void 0 ? { error } : {} });
|
|
203
|
+
},
|
|
204
|
+
async dequeue(clientId, seq) {
|
|
205
|
+
entries.delete(key(clientId, seq));
|
|
206
|
+
},
|
|
207
|
+
async loadAll() {
|
|
208
|
+
const all = [...entries.values()].sort((a, b) => a.order - b.order);
|
|
209
|
+
const { entries: current, dropped } = dropStaleVersion(all);
|
|
210
|
+
for (const e of dropped) entries.delete(key(e.clientId, e.seq));
|
|
211
|
+
return { entries: current, dropped };
|
|
212
|
+
},
|
|
213
|
+
async getMeta(clientId) {
|
|
214
|
+
const m = meta.get(clientId);
|
|
215
|
+
return m ? { ...m } : void 0;
|
|
216
|
+
},
|
|
217
|
+
async setMeta(clientId, m) {
|
|
218
|
+
meta.set(clientId, { ...m });
|
|
219
|
+
},
|
|
220
|
+
async listMetaClientIds() {
|
|
221
|
+
return [...meta.keys()];
|
|
222
|
+
},
|
|
223
|
+
async deleteMeta(clientId) {
|
|
224
|
+
meta.delete(clientId);
|
|
225
|
+
},
|
|
226
|
+
persist() {
|
|
227
|
+
},
|
|
228
|
+
async close() {
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function indexedDBOutbox(opts = {}) {
|
|
233
|
+
const idbFactory = opts.indexedDB ?? (typeof indexedDB !== "undefined" ? indexedDB : void 0);
|
|
234
|
+
if (!idbFactory) {
|
|
235
|
+
opts.onFallback?.(new Error("IndexedDB is not available in this runtime"));
|
|
236
|
+
return memoryOutbox();
|
|
237
|
+
}
|
|
238
|
+
let resolved;
|
|
239
|
+
const ready = openIndexedDBOutbox(idbFactory, opts.dbName ?? OUTBOX_DB_NAME).catch((err) => {
|
|
240
|
+
opts.onFallback?.(err);
|
|
241
|
+
return memoryOutbox();
|
|
242
|
+
});
|
|
243
|
+
void ready.then((impl2) => {
|
|
244
|
+
resolved = impl2;
|
|
245
|
+
});
|
|
246
|
+
const impl = async () => resolved ?? ready;
|
|
247
|
+
return {
|
|
248
|
+
append: async (entry) => (await impl()).append(entry),
|
|
249
|
+
updateStatus: async (clientId, seq, status, error) => (await impl()).updateStatus(clientId, seq, status, error),
|
|
250
|
+
dequeue: async (clientId, seq) => (await impl()).dequeue(clientId, seq),
|
|
251
|
+
loadAll: async () => (await impl()).loadAll(),
|
|
252
|
+
getMeta: async (clientId) => (await impl()).getMeta(clientId),
|
|
253
|
+
setMeta: async (clientId, meta) => (await impl()).setMeta(clientId, meta),
|
|
254
|
+
listMetaClientIds: async () => (await impl()).listMetaClientIds?.() ?? [],
|
|
255
|
+
deleteMeta: async (clientId) => (await impl()).deleteMeta?.(clientId),
|
|
256
|
+
persist: () => {
|
|
257
|
+
void impl().then((i) => i.persist());
|
|
258
|
+
},
|
|
259
|
+
close: async () => (await impl()).close?.()
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
var identityEntropyCounter = 0;
|
|
263
|
+
function defaultMintClientId() {
|
|
264
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
265
|
+
return `c-${Date.now().toString(36)}-${(identityEntropyCounter++).toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
266
|
+
}
|
|
267
|
+
async function mintIdentity(storage, opts = {}) {
|
|
268
|
+
const clientId = (opts.mintClientId ?? defaultMintClientId)();
|
|
269
|
+
const existing = await storage.getMeta(clientId);
|
|
270
|
+
const nextSeq = existing?.nextSeq ?? 0;
|
|
271
|
+
await storage.setMeta(clientId, { nextSeq, deployment: opts.deployment });
|
|
272
|
+
return { clientId, nextSeq };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export {
|
|
276
|
+
OUTBOX_VERSION,
|
|
277
|
+
dropStaleVersion,
|
|
278
|
+
DEFAULT_OUTBOX_MAX_QUEUE_SIZE,
|
|
279
|
+
OutboxOverflowError,
|
|
280
|
+
OfflineClientResetError,
|
|
281
|
+
memoryOutbox,
|
|
282
|
+
indexedDBOutbox,
|
|
283
|
+
defaultMintClientId,
|
|
284
|
+
mintIdentity
|
|
285
|
+
};
|
|
286
|
+
//# sourceMappingURL=chunk-DW55SNHW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/outbox-idb.ts","../src/outbox-storage.ts"],"sourcesContent":["/**\n * The IndexedDB-backed `OutboxStorage` (verdict §(d) decision 8, `docs/dev/research/offline-outbox/\n * verdict.md`). ONE database, `helipod-outbox` — the whole seam (the shared mutation queue AND\n * per-clientId identity) lives together so an origin eviction takes BOTH atomically. That is\n * verdict §(g) hazard 1 (\"whole-origin eviction... co-evict — one database\") pinned structurally:\n * there is no separate identity store to fall out of sync with the queue, because there is only\n * one store to begin with.\n *\n * Schema (v1):\n * - `entries` — keyPath `[\"clientId\", \"seq\"]` (the durable identity pair, verdict §(b)'s governing\n * invariant: `(clientId, seq) -> payload` written exactly once); index `order`\n * (drain FIFO across the WHOLE shared queue, every clientId); index `status`\n * (candidate scans — e.g. everything still `unsent`/`inflight` — consumed from a\n * later task onward).\n * - `meta` — keyPath `\"clientId\"`; one row per clientId, `{clientId, nextSeq, deployment}`.\n *\n * Write-behind: every mutating call (`append`/`updateStatus`/`dequeue`/`setMeta`) enqueues a\n * pending op and schedules (once) a microtask flush that opens ONE `readwrite` transaction across\n * both stores and applies every op queued since the last flush, in call order. Callers that fire\n * several appends synchronously in the same microtask turn get ONE transaction, not N.\n */\nimport type { HydrateResult, OutboxEntry, OutboxEntryError, OutboxEntryStatus, OutboxMeta, OutboxStorage } from \"./outbox-storage\";\n\n/** Bump when the persisted entry shape changes incompatibly. A hydrate that finds an entry\n * stamped with a different version DROPS it (verdict §(g) hazard 10) — never runs it, never\n * guesses a migration. */\nexport const OUTBOX_VERSION = 1;\n\nexport const OUTBOX_DB_NAME = \"helipod-outbox\";\nconst DB_VERSION = 1;\nconst ENTRIES_STORE = \"entries\";\nconst META_STORE = \"meta\";\n\n/** Shared by both `OutboxStorage` implementations (`outbox-storage.ts`'s `memoryOutbox()` and this\n * file's IndexedDB backend) so \"stale-version entries are dropped, with the caller able to raise\n * a verdict for them\" is one rule, not two independently-maintained copies of it. */\nexport function dropStaleVersion(all: OutboxEntry[]): HydrateResult {\n const entries: OutboxEntry[] = [];\n const dropped: OutboxEntry[] = [];\n for (const e of all) {\n if (e.outboxVersion === OUTBOX_VERSION) entries.push(e);\n else dropped.push(e);\n }\n return { entries, dropped };\n}\n\nfunction promisifyRequest<T>(request: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new Error(\"IndexedDB request failed\"));\n });\n}\n\nfunction promisifyTransaction(tx: IDBTransaction): Promise<void> {\n return new Promise((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error ?? new Error(\"IndexedDB transaction failed\"));\n tx.onabort = () => reject(tx.error ?? new Error(\"IndexedDB transaction aborted\"));\n });\n}\n\nfunction openDatabase(idbFactory: IDBFactory, dbName: string): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n let request: IDBOpenDBRequest;\n try {\n request = idbFactory.open(dbName, DB_VERSION);\n } catch (err) {\n reject(err);\n return;\n }\n request.onupgradeneeded = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(ENTRIES_STORE)) {\n const store = db.createObjectStore(ENTRIES_STORE, { keyPath: [\"clientId\", \"seq\"] });\n store.createIndex(\"order\", \"order\", { unique: false });\n store.createIndex(\"status\", \"status\", { unique: false });\n }\n if (!db.objectStoreNames.contains(META_STORE)) {\n db.createObjectStore(META_STORE, { keyPath: \"clientId\" });\n }\n };\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new Error(\"IndexedDB open failed\"));\n request.onblocked = () => reject(new Error(\"IndexedDB open blocked by another connection\"));\n });\n}\n\ntype PendingOp =\n | { kind: \"append\"; entry: OutboxEntry; resolve: () => void; reject: (e: unknown) => void }\n | {\n kind: \"updateStatus\";\n clientId: string;\n seq: number;\n status: OutboxEntryStatus;\n error?: OutboxEntryError;\n resolve: () => void;\n reject: (e: unknown) => void;\n }\n | { kind: \"dequeue\"; clientId: string; seq: number; resolve: () => void; reject: (e: unknown) => void }\n | { kind: \"setMeta\"; clientId: string; meta: OutboxMeta; resolve: () => void; reject: (e: unknown) => void };\n\n/** Exported (beyond the `openIndexedDBOutbox` factory) so tests can inspect `txnCount` — the\n * concrete write-behind-batching evidence — without reaching into module internals. */\nexport class IndexedDBOutboxStorage implements OutboxStorage {\n private queue: PendingOp[] = [];\n private flushScheduled = false;\n /** Exposed for tests only: how many `readwrite` transactions have actually been opened by the\n * write-behind flush — the write-behind-batching property is \"N appends in one microtask turn\n * bump this by at most 1\", not N. */\n txnCount = 0;\n\n constructor(private readonly db: IDBDatabase) {}\n\n private schedule(build: (resolve: () => void, reject: (e: unknown) => void) => PendingOp): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n this.queue.push(build(resolve, reject));\n if (!this.flushScheduled) {\n this.flushScheduled = true;\n queueMicrotask(() => void this.flush());\n }\n });\n }\n\n append(entry: OutboxEntry): Promise<void> {\n return this.schedule((resolve, reject) => ({ kind: \"append\", entry, resolve, reject }));\n }\n\n updateStatus(clientId: string, seq: number, status: OutboxEntryStatus, error?: OutboxEntryError): Promise<void> {\n return this.schedule((resolve, reject) => ({ kind: \"updateStatus\", clientId, seq, status, error, resolve, reject }));\n }\n\n dequeue(clientId: string, seq: number): Promise<void> {\n return this.schedule((resolve, reject) => ({ kind: \"dequeue\", clientId, seq, resolve, reject }));\n }\n\n setMeta(clientId: string, meta: OutboxMeta): Promise<void> {\n return this.schedule((resolve, reject) => ({ kind: \"setMeta\", clientId, meta, resolve, reject }));\n }\n\n private async flush(): Promise<void> {\n const ops = this.queue;\n this.queue = [];\n this.flushScheduled = false;\n if (ops.length === 0) return;\n\n this.txnCount++;\n let tx: IDBTransaction;\n try {\n tx = this.db.transaction([ENTRIES_STORE, META_STORE], \"readwrite\");\n } catch (err) {\n for (const op of ops) op.reject(err);\n return;\n }\n const entriesStore = tx.objectStore(ENTRIES_STORE);\n const metaStore = tx.objectStore(META_STORE);\n\n for (const op of ops) {\n switch (op.kind) {\n case \"append\":\n entriesStore.put(op.entry);\n break;\n case \"updateStatus\": {\n const getReq = entriesStore.get([op.clientId, op.seq]);\n getReq.onsuccess = () => {\n const existing = getReq.result as OutboxEntry | undefined;\n if (existing) entriesStore.put({ ...existing, status: op.status, ...(op.error !== undefined ? { error: op.error } : {}) });\n };\n break;\n }\n case \"dequeue\":\n entriesStore.delete([op.clientId, op.seq]);\n break;\n case \"setMeta\":\n metaStore.put({ ...op.meta, clientId: op.clientId });\n break;\n }\n }\n\n try {\n await promisifyTransaction(tx);\n for (const op of ops) op.resolve();\n } catch (err) {\n for (const op of ops) op.reject(err);\n }\n }\n\n async getMeta(clientId: string): Promise<OutboxMeta | undefined> {\n const tx = this.db.transaction(META_STORE, \"readonly\");\n const row = (await promisifyRequest(tx.objectStore(META_STORE).get(clientId))) as\n | (OutboxMeta & { clientId: string })\n | undefined;\n if (!row) return undefined;\n const { clientId: _clientId, ...meta } = row;\n void _clientId;\n return meta;\n }\n\n async listMetaClientIds(): Promise<string[]> {\n const tx = this.db.transaction(META_STORE, \"readonly\");\n const keys = (await promisifyRequest(tx.objectStore(META_STORE).getAllKeys())) as IDBValidKey[];\n return keys.map((k) => String(k));\n }\n\n async deleteMeta(clientId: string): Promise<void> {\n const tx = this.db.transaction(META_STORE, \"readwrite\");\n tx.objectStore(META_STORE).delete(clientId);\n await promisifyTransaction(tx);\n }\n\n async loadAll(): Promise<HydrateResult> {\n const tx = this.db.transaction(ENTRIES_STORE, \"readonly\");\n const all = (await promisifyRequest(tx.objectStore(ENTRIES_STORE).getAll())) as OutboxEntry[];\n all.sort((a, b) => a.order - b.order);\n const { entries, dropped } = dropStaleVersion(all);\n\n if (dropped.length > 0) {\n const delTx = this.db.transaction(ENTRIES_STORE, \"readwrite\");\n const store = delTx.objectStore(ENTRIES_STORE);\n for (const e of dropped) store.delete([e.clientId, e.seq]);\n await promisifyTransaction(delTx);\n }\n return { entries, dropped };\n }\n\n persist(): void {\n // Advisory only — no behavior anywhere branches on the grant (verdict §(g) hazard 3).\n if (typeof navigator !== \"undefined\" && navigator.storage?.persist) {\n void navigator.storage.persist().catch(() => {});\n }\n }\n\n async close(): Promise<void> {\n await this.flush();\n this.db.close();\n }\n}\n\n/** Open (creating if needed) the one shared outbox database and return a ready `OutboxStorage`.\n * Rejects if `open()` fails — the caller (`outbox-storage.ts`'s `indexedDBOutbox()`) is what\n * turns that rejection into the memory fallback; this function itself makes no fallback decision. */\nexport async function openIndexedDBOutbox(\n idbFactory: IDBFactory,\n dbName: string = OUTBOX_DB_NAME,\n): Promise<IndexedDBOutboxStorage> {\n const db = await openDatabase(idbFactory, dbName);\n return new IndexedDBOutboxStorage(db);\n}\n","/**\n * The `OutboxStorage` seam — the client's first storage API (verdict §(d), `docs/dev/research/\n * offline-outbox/verdict.md`). Two implementations: `memoryOutbox()` (the default — preserves\n * today's behavior byte-for-byte; nothing persists across a reload) and `indexedDBOutbox()`\n * (probe-and-fallback: real durability in a browser, transparently degrades to `memoryOutbox()`\n * wherever IndexedDB is unavailable or fails to open — Node, private-mode Safari, a corrupt\n * origin). Durability is opt-in constructor config (`new HelipodClient(transport, { outbox:\n * indexedDBOutbox() })`); a client constructed without `outbox` never touches this file's runtime\n * branches that matter — `memoryOutbox()`'s Maps are just as ephemeral as the pre-outbox client.\n *\n * Persisted record shape is verdict §(d) verbatim: `{clientId, seq, requestId, udfPath, args, seed,\n * order, status, identityFingerprint, outboxVersion, enqueuedAt}`. `order` is an explicit column —\n * Map/array insertion order does not survive IndexedDB, and the drain (a later task) needs a\n * persisted total order across the WHOLE shared queue (every clientId sharing this database), not\n * just within one clientId's entries.\n *\n * Identity (verdict §(d) \"Identity\", hazard 8): **one clientId per tab-session, minted at client\n * construction, never reused across a reload.** A fresh `HelipodClient` instance always mints a\n * BRAND NEW clientId — there is no reseed protocol, so there is nothing to get wrong (hazard 8:\n * \"reload resets counters — dissolved structurally\"). Entries persisted under an OLDER clientId\n * from a previous session are untouched by minting a new one; they hydrate and drain under their\n * own **recorded** `(clientId, seq)` pair later (a later task's drain). The \"-or-loaded\" half of\n * `mintIdentity` concerns `nextSeq`, not `clientId`: a fresh clientId's meta row does not exist, so\n * `nextSeq` starts at 0 — but the loader still calls `getMeta` first rather than assuming absence,\n * so a colliding clientId (astronomically unlikely — see `defaultMintClientId`) resumes from its\n * recorded `nextSeq` instead of silently reusing a seq that already named a payload (verdict §(b)'s\n * governing invariant: the map `(clientId, seq) -> payload` is written exactly once).\n */\nimport type { JSONValue } from \"@helipod/values\";\nimport { OUTBOX_DB_NAME, OUTBOX_VERSION, dropStaleVersion, openIndexedDBOutbox } from \"./outbox-idb\";\n\nexport { OUTBOX_VERSION };\n\nexport type OutboxEntryStatus = \"unsent\" | \"inflight\" | \"parked\" | \"completed\" | \"failed\";\n\n/** T5 (R9): the terminal verdict recorded alongside a `\"failed\"` durable entry — surfaced through\n * `client.pendingMutations()`/`usePendingMutations()` and `onMutationFailed`. */\nexport interface OutboxEntryError {\n message: string;\n code?: string;\n}\n\n/** One durable outbox record — the persisted twin of `PendingMutation` (`./mutation-log`), plus\n * the fields only a durable queue needs (`clientId`, `seq`, `order`, `identityFingerprint`,\n * `outboxVersion`, `enqueuedAt`). `args`/`seed` are the fields whose omission would make a\n * hydrated replay non-deterministic (verdict D2) — everything else (`touched`, the `update`\n * closure) is recomputed or looked up in the optimistic-updater registry, never persisted. */\nexport interface OutboxEntry {\n clientId: string;\n seq: number;\n requestId: string;\n udfPath: string;\n args: JSONValue;\n seed: { entropy: string; now: number };\n /** Global position across the WHOLE shared queue (every clientId) — the drain's FIFO key. */\n order: number;\n status: OutboxEntryStatus;\n /** SHA-256 of the `SetAuth` token at enqueue time; absent for an unauthenticated mutation. Set\n * at flush-time by a later task; carried as a plain field here so the schema is stable from\n * day one (verdict §(g) hazard 9). */\n identityFingerprint?: string;\n outboxVersion: number;\n enqueuedAt: number;\n /** T5 (R9): set (via `updateStatus`'s optional 4th argument) when `status` transitions to\n * `\"failed\"` — a terminal, server-recorded verdict a live `mutation()` promise may have no\n * awaiter for (a hydrated cross-reload entry, or a retried one). Absent for every other status. */\n error?: OutboxEntryError;\n}\n\nexport interface OutboxMeta {\n /** In-memory-serial cursor for this clientId's NEXT mutation, loaded once at mint time. */\n nextSeq: number;\n deployment?: string;\n}\n\n/** The result of a full-queue hydrate. `dropped` holds entries whose `outboxVersion` didn't match\n * the running code's `OUTBOX_VERSION` — removed from storage as a side effect of the hydrate\n * itself, returned so the caller can settle them with a terminal verdict rather than silently\n * discarding a promise's fate (verdict §(g) hazard 10: \"outboxVersion stamp, drop-with-verdict at\n * hydrate\"). */\nexport interface HydrateResult {\n /** Current-version entries, in persisted `order`. */\n entries: OutboxEntry[];\n /** Stale-version entries deleted during this hydrate. */\n dropped: OutboxEntry[];\n}\n\n/** The seam. Every method is safe to call concurrently — the IndexedDB implementation\n * write-behind-batches same-microtask calls into one transaction (see `outbox-idb.ts`). */\nexport interface OutboxStorage {\n /** Durably record a new entry. The caller must NOT await this before sending the mutation on the\n * wire — \"the send never waits for the append\" (verdict §(d)); this promise exists so a caller\n * CAN confirm durability later (e.g. park-eligibility at transport close). */\n append(entry: OutboxEntry): Promise<void>;\n /** Mutate only `status` (and, when given, `error` — T5's R9 terminal-failure record; every other\n * field is preserved verbatim). Called with `status: \"failed\"` INSTEAD OF `dequeue()` on a\n * terminal failure (verdict §(d) R9: \"failed entries persist until dismissed/retried\") — every\n * other status transition (`\"inflight\"`/`\"unsent\"`/`\"parked\"`) omits `error`. */\n updateStatus(clientId: string, seq: number, status: OutboxEntryStatus, error?: OutboxEntryError): Promise<void>;\n /** Remove a fully-settled entry. */\n dequeue(clientId: string, seq: number): Promise<void>;\n /** Hydrate the whole shared queue, across every clientId, in persisted `order`. */\n loadAll(): Promise<HydrateResult>;\n getMeta(clientId: string): Promise<OutboxMeta | undefined>;\n setMeta(clientId: string, meta: OutboxMeta): Promise<void>;\n /** OPTIONAL (verdict §(g) hazard 1 / Task 4's dead-meta prune): every clientId with a meta row.\n * The drain uses it at hydrate to reclaim rows for clientIds that are neither the current session\n * nor have any live queue entries — one such tiny row otherwise accrues per prior tab-session and\n * every `onClientReset`. Optional so a minimal `OutboxStorage` double (which never prunes) stays\n * valid; both shipped backends implement it. */\n listMetaClientIds?(): Promise<string[]>;\n /** OPTIONAL companion to {@link listMetaClientIds} — delete one dead meta row. */\n deleteMeta?(clientId: string): Promise<void>;\n /** Advisory `navigator.storage.persist()` request — fire-and-forget, no return value, and no\n * behavior anywhere ever branches on whether the grant is honored (verdict §(g) hazard 3: \"zero\n * behavior branches on the grant\"). A no-op for the memory backend. */\n persist(): void;\n /** OPTIONAL: flush pending writes and release any backing resources (fs lock, IDB handle).\n * Additive like `listMetaClientIds` — callers guard with `?.`. Nothing in the client core\n * calls it; it exists for host shutdown/relaunch flows (Electron window cycling) and tests.\n * A client that never calls it loses nothing (backends self-heal: exit hooks, stale-lock\n * steal, IDB's own connection lifecycle). */\n close?(): Promise<void>;\n}\n\n/** Default cap on how many outbox-tracked entries (`unsent`/`inflight`/`parked` — not yet fully\n * settled) may sit in `client.ts`'s live log at once, per verdict §(d) \"Enqueue\": \"bounded\n * (default 1000)\". Overridable via `new HelipodClient(transport, { outboxMaxQueueSize })`. */\nexport const DEFAULT_OUTBOX_MAX_QUEUE_SIZE = 1000;\n\n/** Thrown (as a rejected `mutation()` promise, never a synchronous throw) when the durable outbox\n * is at capacity. Verdict §(d): the NEW enqueue is rejected, not the oldest queued one — \"the new\n * write has a live awaiter [this very call]; the oldest durable promise may not [e.g. it survived\n * a reload, where there is no live JS promise for it at all until the registry rebuilds a layer].\"\n * A coded error (`.code`) so callers can distinguish this from every other mutation failure. */\nexport class OutboxOverflowError extends Error {\n readonly code = \"OUTBOX_OVERFLOW\";\n constructor(\n message = \"the durable outbox is full — this mutation was rejected so an already-queued promise, \" +\n \"which may have no live awaiter across a reload, is never silently evicted\",\n ) {\n super(message);\n this.name = \"OutboxOverflowError\";\n }\n}\n\n/** Rejection for a parked mutation the server disowned on `ConnectAck{known: false}` — the client's\n * presented history matched neither a record nor a floor (a swept/foreign/reset timeline), so the\n * client re-mints its identity (`onClientReset`). A parked entry was in-flight when the socket\n * dropped: its outcome is genuinely unknowable and, since the server has no dedup record for it,\n * a blind resend under a fresh clientId could double-apply — so it rejects LOUDLY (verdict §(d)\n * Retention: \"parked entries reject loudly\"). Coded so apps can distinguish it from every other\n * failure. `unsent` entries (never hit the wire) are safe to re-enqueue instead and are NOT\n * rejected. */\nexport class OfflineClientResetError extends Error {\n readonly code = \"OFFLINE_CLIENT_RESET\";\n constructor(\n message = \"the server disowned this client's mutation history (swept/foreign timeline); its \" +\n \"identity was reset and this in-flight-at-disconnect mutation, whose outcome is unknowable, \" +\n \"was rejected rather than blindly resent\",\n ) {\n super(message);\n this.name = \"OfflineClientResetError\";\n }\n}\n\n/** The in-memory default — what a client gets when it passes no `outbox` at all. Nothing here\n * survives past this `HelipodClient` instance's lifetime, which is exactly today's (pre-outbox)\n * behavior: a reload has no durable queue to hydrate, because there never was one. */\nexport function memoryOutbox(): OutboxStorage {\n const entries = new Map<string, OutboxEntry>();\n const meta = new Map<string, OutboxMeta>();\n const key = (clientId: string, seq: number) => `${clientId}\\0${seq}`;\n\n return {\n async append(entry) {\n entries.set(key(entry.clientId, entry.seq), { ...entry });\n },\n async updateStatus(clientId, seq, status, error) {\n const existing = entries.get(key(clientId, seq));\n if (existing) entries.set(key(clientId, seq), { ...existing, status, ...(error !== undefined ? { error } : {}) });\n },\n async dequeue(clientId, seq) {\n entries.delete(key(clientId, seq));\n },\n async loadAll() {\n const all = [...entries.values()].sort((a, b) => a.order - b.order);\n const { entries: current, dropped } = dropStaleVersion(all);\n for (const e of dropped) entries.delete(key(e.clientId, e.seq));\n return { entries: current, dropped };\n },\n async getMeta(clientId) {\n const m = meta.get(clientId);\n return m ? { ...m } : undefined;\n },\n async setMeta(clientId, m) {\n meta.set(clientId, { ...m });\n },\n async listMetaClientIds() {\n return [...meta.keys()];\n },\n async deleteMeta(clientId) {\n meta.delete(clientId);\n },\n persist() {\n // No-op: nothing here survives a reload anyway, so there is nothing worth asking the\n // browser to protect from eviction.\n },\n async close() {\n // No backing resource — a memory queue's lifetime IS the instance's lifetime.\n },\n };\n}\n\nexport interface IndexedDBOutboxOptions {\n /** Injectable for tests / non-default globals — defaults to `globalThis.indexedDB`. */\n indexedDB?: IDBFactory;\n dbName?: string;\n /** Best-effort, fire-and-forget notification whenever the adapter falls back to memory — e.g.\n * no IndexedDB in this runtime, or `open()` failed (private-mode Safari, a corrupt origin). The\n * fallback itself is never gated on this callback existing or succeeding. */\n onFallback?: (reason: unknown) => void;\n}\n\n/** Probe-and-fallback (verdict §(g) hazard 5): if IndexedDB isn't available in this runtime at\n * all, or `open()` fails for any reason, every method transparently delegates to a fresh\n * `memoryOutbox()` instead — same interface, same call sites, only durability is lost. The probe\n * is asynchronous (an IDB `open()` can only fail asynchronously), so calls made before the\n * outcome is known queue behind the open attempt; every call after routes directly with no added\n * latency. */\nexport function indexedDBOutbox(opts: IndexedDBOutboxOptions = {}): OutboxStorage {\n const idbFactory = opts.indexedDB ?? (typeof indexedDB !== \"undefined\" ? indexedDB : undefined);\n if (!idbFactory) {\n opts.onFallback?.(new Error(\"IndexedDB is not available in this runtime\"));\n return memoryOutbox();\n }\n\n let resolved: OutboxStorage | undefined;\n const ready: Promise<OutboxStorage> = openIndexedDBOutbox(idbFactory, opts.dbName ?? OUTBOX_DB_NAME).catch((err: unknown) => {\n opts.onFallback?.(err);\n return memoryOutbox();\n });\n void ready.then((impl) => {\n resolved = impl;\n });\n\n const impl = async (): Promise<OutboxStorage> => resolved ?? ready;\n\n return {\n append: async (entry) => (await impl()).append(entry),\n updateStatus: async (clientId, seq, status, error) => (await impl()).updateStatus(clientId, seq, status, error),\n dequeue: async (clientId, seq) => (await impl()).dequeue(clientId, seq),\n loadAll: async () => (await impl()).loadAll(),\n getMeta: async (clientId) => (await impl()).getMeta(clientId),\n setMeta: async (clientId, meta) => (await impl()).setMeta(clientId, meta),\n listMetaClientIds: async () => (await impl()).listMetaClientIds?.() ?? [],\n deleteMeta: async (clientId) => (await impl()).deleteMeta?.(clientId),\n persist: () => {\n // Advisory-only — fire-and-forget even the routing to the resolved implementation.\n void impl().then((i) => i.persist());\n },\n close: async () => (await impl()).close?.(),\n };\n}\n\nlet identityEntropyCounter = 0;\n/** Not `crypto.randomUUID()` unconditionally — some test/SSR runtimes lack it. Collision odds are\n * irrelevant either way: `mintIdentity` re-reads `getMeta` for whatever id comes out, so even a\n * freak collision resumes from a recorded `nextSeq` rather than silently reusing a seq.\n *\n * Exported (beyond `mintIdentity`'s internal default) so `client.ts` can mint a clientId\n * SYNCHRONOUSLY at construction — `mutation()` must stay fully synchronous (T1's open concern),\n * so the clientId every entry stamps cannot wait on `mintIdentity`'s async `getMeta`/`setMeta`\n * round-trip. `client.ts` mints with this directly, then feeds the SAME id into `mintIdentity` via\n * `opts.mintClientId` so the durable meta row it persists names the id actually in use. */\nexport function defaultMintClientId(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") return crypto.randomUUID();\n return `c-${Date.now().toString(36)}-${(identityEntropyCounter++).toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\n/**\n * Mint this tab-session's clientId and establish its `nextSeq` cursor in the outbox's meta store.\n * Per the file doc's identity model: ALWAYS mints a fresh clientId — never reuses one from a prior\n * session. Returns `{clientId, nextSeq}`; the caller (client construction) keeps `nextSeq` as an\n * in-memory serial counter from here on (verdict §(d): \"seqs minted serially in-memory per tab\") —\n * it is not re-read from storage again this session.\n */\nexport async function mintIdentity(\n storage: OutboxStorage,\n opts: { deployment?: string; mintClientId?: () => string } = {},\n): Promise<{ clientId: string; nextSeq: number }> {\n const clientId = (opts.mintClientId ?? defaultMintClientId)();\n const existing = await storage.getMeta(clientId);\n const nextSeq = existing?.nextSeq ?? 0;\n await storage.setMeta(clientId, { nextSeq, deployment: opts.deployment });\n return { clientId, nextSeq };\n}\n"],"mappings":"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","names":["impl"]}
|