@fragno-dev/tanstack-db-adapter 0.0.0-stage → 0.0.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.
Files changed (37) hide show
  1. package/LICENSE.md +16 -0
  2. package/README.md +207 -2
  3. package/dist/checkpoint.d.ts +9 -0
  4. package/dist/checkpoint.d.ts.map +1 -0
  5. package/dist/checkpoint.js +11 -0
  6. package/dist/checkpoint.js.map +1 -0
  7. package/dist/coordinator/fragno-browser-persistence-startup.d.ts +92 -0
  8. package/dist/coordinator/fragno-browser-persistence-startup.d.ts.map +1 -0
  9. package/dist/coordinator/fragno-browser-persistence-startup.js +366 -0
  10. package/dist/coordinator/fragno-browser-persistence-startup.js.map +1 -0
  11. package/dist/coordinator/fragno-collection-registry.js +85 -0
  12. package/dist/coordinator/fragno-collection-registry.js.map +1 -0
  13. package/dist/coordinator/fragno-internal-collection.d.ts +40 -0
  14. package/dist/coordinator/fragno-internal-collection.d.ts.map +1 -0
  15. package/dist/coordinator/fragno-internal-collection.js +142 -0
  16. package/dist/coordinator/fragno-internal-collection.js.map +1 -0
  17. package/dist/coordinator/fragno-internal-fetcher.d.ts +23 -0
  18. package/dist/coordinator/fragno-internal-fetcher.d.ts.map +1 -0
  19. package/dist/coordinator/fragno-internal-fetcher.js +60 -0
  20. package/dist/coordinator/fragno-internal-fetcher.js.map +1 -0
  21. package/dist/coordinator/fragno-ordered-persistence.js +66 -0
  22. package/dist/coordinator/fragno-ordered-persistence.js.map +1 -0
  23. package/dist/coordinator/fragno-outbox-synchronizer.js +193 -0
  24. package/dist/coordinator/fragno-outbox-synchronizer.js.map +1 -0
  25. package/dist/coordinator/fragno-table-collection.js +91 -0
  26. package/dist/coordinator/fragno-table-collection.js.map +1 -0
  27. package/dist/coordinator.d.ts +113 -0
  28. package/dist/coordinator.d.ts.map +1 -0
  29. package/dist/coordinator.js +231 -0
  30. package/dist/coordinator.js.map +1 -0
  31. package/dist/outbox-stream.js +85 -0
  32. package/dist/outbox-stream.js.map +1 -0
  33. package/dist/outbox-transport-error.js +12 -0
  34. package/dist/outbox-transport-error.js.map +1 -0
  35. package/dist/protocol.js +115 -0
  36. package/dist/protocol.js.map +1 -0
  37. package/package.json +83 -3
@@ -0,0 +1,366 @@
1
+ //#region src/coordinator/fragno-browser-persistence-startup.ts
2
+ const FRAGNO_BROWSER_PERSISTENCE_DIAGNOSTICS_INTERVAL_MS = 3e3;
3
+ const FRAGNO_BROWSER_PERSISTENCE_INSPECTION_TIMEOUT_MS = 1e3;
4
+ /** Opens browser persistence immediately while an observational worker probe reports startup diagnostics. */
5
+ async function openFragnoBrowserPersistenceWithDiagnostics(options) {
6
+ const startedAt = Date.now();
7
+ const workerProbe = startFragnoBrowserWorkerProbe();
8
+ const diagnosticsInspectors = createFragnoBrowserPersistenceDiagnosticInspectors(options.databaseName);
9
+ let settled = false;
10
+ let expectedDiagnosticsElapsedMs = FRAGNO_BROWSER_PERSISTENCE_DIAGNOSTICS_INTERVAL_MS;
11
+ let diagnosticsTimer;
12
+ function scheduleDiagnostics() {
13
+ diagnosticsTimer = setTimeout(() => {
14
+ const elapsedMs = Date.now() - startedAt;
15
+ collectFragnoBrowserPersistenceDiagnostics({
16
+ databaseName: options.databaseName,
17
+ elapsedMs,
18
+ timerDriftMs: Math.max(0, elapsedMs - expectedDiagnosticsElapsedMs),
19
+ workerProbe: workerProbe.snapshot(),
20
+ inspectors: diagnosticsInspectors
21
+ }).then((diagnostics) => {
22
+ if (settled) return;
23
+ console.warn(`Fragno outbox browser persistence is still opening after ${diagnostics.elapsedMs}ms. ${diagnostics.likelyCause} Initialization will keep waiting.`, diagnostics);
24
+ try {
25
+ options.onDiagnostic?.(diagnostics);
26
+ } catch (error) {
27
+ console.error("Fragno browser persistence diagnostic listener failed.", error);
28
+ }
29
+ expectedDiagnosticsElapsedMs += FRAGNO_BROWSER_PERSISTENCE_DIAGNOSTICS_INTERVAL_MS;
30
+ scheduleDiagnostics();
31
+ });
32
+ }, FRAGNO_BROWSER_PERSISTENCE_DIAGNOSTICS_INTERVAL_MS);
33
+ }
34
+ scheduleDiagnostics();
35
+ try {
36
+ return await options.openPersistence();
37
+ } finally {
38
+ settled = true;
39
+ clearTimeout(diagnosticsTimer);
40
+ workerProbe.cleanup();
41
+ }
42
+ }
43
+ function startFragnoBrowserWorkerProbe() {
44
+ const browserGlobal = globalThis;
45
+ if (typeof browserGlobal.Worker !== "function" || typeof browserGlobal.Blob !== "function" || typeof browserGlobal.URL?.createObjectURL !== "function") return {
46
+ snapshot: () => ({
47
+ status: "unsupported",
48
+ reason: "Dedicated worker probing is unavailable in this runtime."
49
+ }),
50
+ cleanup() {}
51
+ };
52
+ const startedAt = Date.now();
53
+ let snapshot = {
54
+ status: "starting",
55
+ elapsedMs: 0
56
+ };
57
+ let worker = null;
58
+ let workerUrl = null;
59
+ function cleanup() {
60
+ worker?.terminate();
61
+ worker = null;
62
+ if (workerUrl) {
63
+ browserGlobal.URL.revokeObjectURL(workerUrl);
64
+ workerUrl = null;
65
+ }
66
+ }
67
+ try {
68
+ workerUrl = browserGlobal.URL.createObjectURL(new browserGlobal.Blob([`globalThis.postMessage({
69
+ type: "fragno-browser-worker-ready",
70
+ timerAvailable: typeof globalThis.setTimeout === "function",
71
+ opfsAvailable: typeof globalThis.navigator?.storage?.getDirectory === "function",
72
+ fileSystemFileHandleGlobalAvailable: typeof globalThis.FileSystemFileHandle === "function",
73
+ syncAccessHandlePrototypeAvailable:
74
+ typeof globalThis.FileSystemFileHandle?.prototype?.createSyncAccessHandle === "function",
75
+ });`], { type: "text/javascript" }));
76
+ worker = new browserGlobal.Worker(workerUrl);
77
+ worker.addEventListener("message", (event) => {
78
+ const runtime = event.data;
79
+ snapshot = {
80
+ status: "responsive",
81
+ responseMs: Date.now() - startedAt,
82
+ runtime: {
83
+ timerAvailable: runtime.timerAvailable,
84
+ opfsAvailable: runtime.opfsAvailable,
85
+ fileSystemFileHandleGlobalAvailable: runtime.fileSystemFileHandleGlobalAvailable,
86
+ syncAccessHandlePrototypeAvailable: runtime.syncAccessHandlePrototypeAvailable
87
+ }
88
+ };
89
+ cleanup();
90
+ });
91
+ worker.addEventListener("error", (event) => {
92
+ snapshot = {
93
+ status: "failed",
94
+ error: event.message || "Dedicated worker probe failed without an error message."
95
+ };
96
+ cleanup();
97
+ });
98
+ worker.addEventListener("messageerror", () => {
99
+ snapshot = {
100
+ status: "failed",
101
+ error: "Dedicated worker probe response could not be deserialized."
102
+ };
103
+ cleanup();
104
+ });
105
+ } catch (error) {
106
+ snapshot = {
107
+ status: "failed",
108
+ error: describeFragnoBrowserDiagnosticError(error)
109
+ };
110
+ cleanup();
111
+ }
112
+ return {
113
+ snapshot() {
114
+ if (snapshot.status !== "starting") return snapshot;
115
+ return {
116
+ status: "starting",
117
+ elapsedMs: Date.now() - startedAt
118
+ };
119
+ },
120
+ cleanup
121
+ };
122
+ }
123
+ function createFragnoBrowserPersistenceDiagnosticInspectors(databaseName) {
124
+ return {
125
+ webLocks: createRecurringFragnoBrowserDiagnosticInspector("Web Locks inspection", () => inspectFragnoBrowserLocks(databaseName)),
126
+ storageEstimate: createRecurringFragnoBrowserDiagnosticInspector("Storage estimate inspection", inspectFragnoBrowserStorageEstimate),
127
+ persistedStorage: createRecurringFragnoBrowserDiagnosticInspector("Persisted storage inspection", inspectFragnoBrowserPersistedStorage),
128
+ opfsEntries: createRecurringFragnoBrowserDiagnosticInspector("OPFS inspection", () => inspectFragnoBrowserOPFSEntries(databaseName))
129
+ };
130
+ }
131
+ function createRecurringFragnoBrowserDiagnosticInspector(inspectionName, inspect) {
132
+ let activeInspection = null;
133
+ return function inspectFragnoBrowserDiagnostic() {
134
+ if (activeInspection === null) activeInspection = inspect().finally(() => {
135
+ activeInspection = null;
136
+ });
137
+ return waitForFragnoBrowserDiagnosticInspection(inspectionName, activeInspection);
138
+ };
139
+ }
140
+ function waitForFragnoBrowserDiagnosticInspection(inspectionName, inspection) {
141
+ return new Promise((resolve) => {
142
+ let settled = false;
143
+ const timeout = setTimeout(() => {
144
+ settled = true;
145
+ resolve({
146
+ status: "failed",
147
+ error: `${inspectionName} timed out after ${FRAGNO_BROWSER_PERSISTENCE_INSPECTION_TIMEOUT_MS}ms.`
148
+ });
149
+ }, FRAGNO_BROWSER_PERSISTENCE_INSPECTION_TIMEOUT_MS);
150
+ inspection.then((diagnostic) => {
151
+ if (settled) return;
152
+ settled = true;
153
+ clearTimeout(timeout);
154
+ resolve(diagnostic);
155
+ }, (error) => {
156
+ if (settled) return;
157
+ settled = true;
158
+ clearTimeout(timeout);
159
+ resolve({
160
+ status: "failed",
161
+ error: describeFragnoBrowserDiagnosticError(error)
162
+ });
163
+ });
164
+ });
165
+ }
166
+ async function collectFragnoBrowserPersistenceDiagnostics(options) {
167
+ const browserGlobal = globalThis;
168
+ const browserNavigator = browserGlobal.navigator;
169
+ const browserDocument = browserGlobal.document;
170
+ const browserLocation = browserGlobal.location;
171
+ const [webLocks, storageEstimate, persistedStorage, opfsEntries] = await Promise.all([
172
+ options.inspectors.webLocks(),
173
+ options.inspectors.storageEstimate(),
174
+ options.inspectors.persistedStorage(),
175
+ options.inspectors.opfsEntries()
176
+ ]);
177
+ const visibilityState = browserDocument?.visibilityState ?? null;
178
+ const likelyCause = diagnoseFragnoBrowserPersistenceStall({
179
+ visibilityState,
180
+ workerProbe: options.workerProbe,
181
+ webLocks
182
+ });
183
+ return {
184
+ databaseName: options.databaseName,
185
+ elapsedMs: options.elapsedMs,
186
+ timerDriftMs: options.timerDriftMs,
187
+ collectedAt: (/* @__PURE__ */ new Date()).toISOString(),
188
+ likelyCause,
189
+ workerProbe: options.workerProbe,
190
+ runtime: {
191
+ href: browserLocation?.href ?? null,
192
+ origin: browserLocation?.origin ?? null,
193
+ userAgent: browserNavigator?.userAgent ?? null,
194
+ visibilityState,
195
+ readyState: browserDocument?.readyState ?? null,
196
+ documentHasFocus: browserDocument?.hasFocus() ?? null,
197
+ isSecureContext: browserGlobal.isSecureContext ?? false,
198
+ crossOriginIsolated: browserGlobal.crossOriginIsolated ?? false,
199
+ online: browserNavigator?.onLine ?? null,
200
+ hardwareConcurrency: browserNavigator?.hardwareConcurrency ?? null,
201
+ workerAvailable: typeof browserGlobal.Worker === "function",
202
+ broadcastChannelAvailable: typeof browserGlobal.BroadcastChannel === "function",
203
+ webLocksAvailable: typeof browserNavigator?.locks?.query === "function",
204
+ opfsAvailable: typeof browserNavigator?.storage?.getDirectory === "function",
205
+ fileSystemFileHandleGlobalAvailable: typeof browserGlobal.FileSystemFileHandle === "function"
206
+ },
207
+ webLocks,
208
+ storageEstimate,
209
+ persistedStorage,
210
+ opfsEntries
211
+ };
212
+ }
213
+ function diagnoseFragnoBrowserPersistenceStall(options) {
214
+ const hasRelevantLocks = options.webLocks.status === "available" && (options.webLocks.value.relevantHeld.length > 0 || options.webLocks.value.relevantPending.length > 0);
215
+ if (options.workerProbe.status === "starting" && !hasRelevantLocks) {
216
+ if (options.visibilityState === "hidden") return "The diagnostic worker has not responded and OPFS has created no locks. The document is hidden, which may be contributing to worker scheduling delays in this Electron runtime; the SQLite persistence worker may also be waiting to execute.";
217
+ return "The diagnostic worker has not responded and OPFS has created no locks. The SQLite persistence worker may also be waiting to execute.";
218
+ }
219
+ if (options.workerProbe.status === "failed") return `Diagnostic worker startup failed: ${options.workerProbe.error}`;
220
+ if (options.webLocks.status === "available") return options.webLocks.value.interpretation;
221
+ return "The upstream persistence API has not completed and exposes no finer-grained initialization progress.";
222
+ }
223
+ async function inspectFragnoBrowserLocks(databaseName) {
224
+ try {
225
+ const lockManager = globalThis.navigator?.locks;
226
+ if (!lockManager?.query) return {
227
+ status: "unavailable",
228
+ reason: "Web Locks query is unavailable."
229
+ };
230
+ const snapshot = await lockManager.query();
231
+ const held = snapshot.held.map(materializeFragnoBrowserLock);
232
+ const pending = snapshot.pending.map(materializeFragnoBrowserLock);
233
+ const databaseLockName = `ahp:/${databaseName}`;
234
+ const isRelevantLock = (lock) => lock.name === databaseLockName || lock.name.startsWith(".ahp-");
235
+ const relevantHeld = held.filter(isRelevantLock);
236
+ const relevantPending = pending.filter(isRelevantLock);
237
+ let interpretation = "OPFS did not expose an active initialization lock.";
238
+ if (relevantPending.some((lock) => lock.name === databaseLockName)) interpretation = "OPFS is waiting to acquire the database access-handle lock.";
239
+ else if (relevantHeld.some((lock) => lock.name === databaseLockName)) interpretation = "OPFS acquired the database access-handle lock but did not finish opening SQLite.";
240
+ else if (relevantHeld.some((lock) => lock.name.startsWith(".ahp-"))) interpretation = "OPFS created its temporary VFS lock but has not requested the database access-handle lock.";
241
+ return {
242
+ status: "available",
243
+ value: {
244
+ held,
245
+ pending,
246
+ relevantHeld,
247
+ relevantPending,
248
+ interpretation
249
+ }
250
+ };
251
+ } catch (error) {
252
+ return {
253
+ status: "failed",
254
+ error: describeFragnoBrowserDiagnosticError(error)
255
+ };
256
+ }
257
+ }
258
+ async function inspectFragnoBrowserStorageEstimate() {
259
+ try {
260
+ const storage = globalThis.navigator?.storage;
261
+ if (!storage?.estimate) return {
262
+ status: "unavailable",
263
+ reason: "Storage estimate is unavailable."
264
+ };
265
+ const estimate = await storage.estimate();
266
+ return {
267
+ status: "available",
268
+ value: {
269
+ quota: estimate.quota ?? null,
270
+ usage: estimate.usage ?? null,
271
+ usageDetails: estimate.usageDetails ? { ...estimate.usageDetails } : null
272
+ }
273
+ };
274
+ } catch (error) {
275
+ return {
276
+ status: "failed",
277
+ error: describeFragnoBrowserDiagnosticError(error)
278
+ };
279
+ }
280
+ }
281
+ async function inspectFragnoBrowserPersistedStorage() {
282
+ try {
283
+ const storage = globalThis.navigator?.storage;
284
+ if (!storage?.persisted) return {
285
+ status: "unavailable",
286
+ reason: "Persisted storage status is unavailable."
287
+ };
288
+ return {
289
+ status: "available",
290
+ value: await storage.persisted()
291
+ };
292
+ } catch (error) {
293
+ return {
294
+ status: "failed",
295
+ error: describeFragnoBrowserDiagnosticError(error)
296
+ };
297
+ }
298
+ }
299
+ async function inspectFragnoBrowserOPFSEntries(databaseName) {
300
+ try {
301
+ const browserNavigator = globalThis.navigator;
302
+ const getDirectory = browserNavigator?.storage?.getDirectory;
303
+ if (!getDirectory) return {
304
+ status: "unavailable",
305
+ reason: "OPFS directory inspection is unavailable."
306
+ };
307
+ const root = await getDirectory.call(browserNavigator.storage);
308
+ const entries = [];
309
+ for await (const entry of iterateFragnoFileSystemDirectory(root)) {
310
+ if (!entry.name.startsWith(".ahp-") && !entry.name.startsWith(databaseName)) continue;
311
+ if (entry.kind === "file") {
312
+ entries.push({
313
+ kind: "file",
314
+ name: entry.name
315
+ });
316
+ continue;
317
+ }
318
+ try {
319
+ const children = [];
320
+ for await (const child of iterateFragnoFileSystemDirectory(entry)) children.push({
321
+ name: child.name,
322
+ kind: child.kind
323
+ });
324
+ entries.push({
325
+ kind: "directory",
326
+ name: entry.name,
327
+ entries: children,
328
+ inspectionError: null
329
+ });
330
+ } catch (error) {
331
+ entries.push({
332
+ kind: "directory",
333
+ name: entry.name,
334
+ entries: [],
335
+ inspectionError: describeFragnoBrowserDiagnosticError(error)
336
+ });
337
+ }
338
+ }
339
+ return {
340
+ status: "available",
341
+ value: entries
342
+ };
343
+ } catch (error) {
344
+ return {
345
+ status: "failed",
346
+ error: describeFragnoBrowserDiagnosticError(error)
347
+ };
348
+ }
349
+ }
350
+ function iterateFragnoFileSystemDirectory(directory) {
351
+ return directory.values();
352
+ }
353
+ function materializeFragnoBrowserLock(lock) {
354
+ return {
355
+ name: lock.name,
356
+ mode: lock.mode,
357
+ clientId: lock.clientId ?? null
358
+ };
359
+ }
360
+ function describeFragnoBrowserDiagnosticError(error) {
361
+ return error instanceof Error ? `${error.name}: ${error.message}` : String(error);
362
+ }
363
+ //#endregion
364
+ export { openFragnoBrowserPersistenceWithDiagnostics };
365
+
366
+ //# sourceMappingURL=fragno-browser-persistence-startup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragno-browser-persistence-startup.js","names":[],"sources":["../../src/coordinator/fragno-browser-persistence-startup.ts"],"sourcesContent":["const FRAGNO_BROWSER_PERSISTENCE_DIAGNOSTICS_INTERVAL_MS = 3_000;\nconst FRAGNO_BROWSER_PERSISTENCE_INSPECTION_TIMEOUT_MS = 1_000;\n\ntype FragnoBrowserDiagnostic<TValue> =\n | { status: \"available\"; value: TValue }\n | { status: \"unavailable\"; reason: string }\n | { status: \"failed\"; error: string };\n\ntype FragnoBrowserLock = {\n name: string;\n mode: string;\n clientId: string | null;\n};\n\ntype FragnoBrowserLockSnapshot = {\n held: FragnoBrowserLock[];\n pending: FragnoBrowserLock[];\n relevantHeld: FragnoBrowserLock[];\n relevantPending: FragnoBrowserLock[];\n interpretation: string;\n};\n\ntype FragnoBrowserStorageEstimate = {\n quota: number | null;\n usage: number | null;\n usageDetails: Record<string, number> | null;\n};\n\ntype FragnoBrowserWorkerProbeSnapshot =\n | { status: \"unsupported\"; reason: string }\n | { status: \"starting\"; elapsedMs: number }\n | {\n status: \"responsive\";\n responseMs: number;\n runtime: {\n timerAvailable: boolean;\n opfsAvailable: boolean;\n fileSystemFileHandleGlobalAvailable: boolean;\n syncAccessHandlePrototypeAvailable: boolean;\n };\n }\n | { status: \"failed\"; error: string };\n\ntype FragnoBrowserWorkerProbe = {\n snapshot(): FragnoBrowserWorkerProbeSnapshot;\n cleanup(): void;\n};\n\ntype FragnoOPFSEntry =\n | { kind: \"file\"; name: string }\n | {\n kind: \"directory\";\n name: string;\n entries: Array<{ name: string; kind: string }>;\n inspectionError: string | null;\n };\n\ntype FragnoBrowserDiagnosticsGlobal = typeof globalThis & {\n FileSystemFileHandle?: {\n prototype?: { createSyncAccessHandle?: unknown };\n };\n navigator?: Navigator & {\n storage?: StorageManager & {\n getDirectory?: () => Promise<FileSystemDirectoryHandle>;\n };\n };\n};\n\n/** Captures worker, runtime, storage, Web Locks, and OPFS state during a stalled browser startup. */\nexport type FragnoBrowserPersistenceDiagnostics = {\n databaseName: string;\n elapsedMs: number;\n timerDriftMs: number;\n collectedAt: string;\n likelyCause: string;\n workerProbe: FragnoBrowserWorkerProbeSnapshot;\n runtime: {\n href: string | null;\n origin: string | null;\n userAgent: string | null;\n visibilityState: DocumentVisibilityState | null;\n readyState: DocumentReadyState | null;\n documentHasFocus: boolean | null;\n isSecureContext: boolean;\n crossOriginIsolated: boolean;\n online: boolean | null;\n hardwareConcurrency: number | null;\n workerAvailable: boolean;\n broadcastChannelAvailable: boolean;\n webLocksAvailable: boolean;\n opfsAvailable: boolean;\n fileSystemFileHandleGlobalAvailable: boolean;\n };\n webLocks: FragnoBrowserDiagnostic<FragnoBrowserLockSnapshot>;\n storageEstimate: FragnoBrowserDiagnostic<FragnoBrowserStorageEstimate>;\n persistedStorage: FragnoBrowserDiagnostic<boolean>;\n opfsEntries: FragnoBrowserDiagnostic<FragnoOPFSEntry[]>;\n};\n\ntype FragnoBrowserPersistenceStartupOptions<TResource> = {\n databaseName: string;\n onDiagnostic: ((diagnostics: FragnoBrowserPersistenceDiagnostics) => void) | null;\n openPersistence(): Promise<TResource>;\n};\n\ntype FragnoBrowserPersistenceDiagnosticInspectors = {\n webLocks(): Promise<FragnoBrowserDiagnostic<FragnoBrowserLockSnapshot>>;\n storageEstimate(): Promise<FragnoBrowserDiagnostic<FragnoBrowserStorageEstimate>>;\n persistedStorage(): Promise<FragnoBrowserDiagnostic<boolean>>;\n opfsEntries(): Promise<FragnoBrowserDiagnostic<FragnoOPFSEntry[]>>;\n};\n\n/** Opens browser persistence immediately while an observational worker probe reports startup diagnostics. */\nexport async function openFragnoBrowserPersistenceWithDiagnostics<TResource>(\n options: FragnoBrowserPersistenceStartupOptions<TResource>,\n): Promise<TResource> {\n const startedAt = Date.now();\n const workerProbe = startFragnoBrowserWorkerProbe();\n const diagnosticsInspectors = createFragnoBrowserPersistenceDiagnosticInspectors(\n options.databaseName,\n );\n let settled = false;\n let expectedDiagnosticsElapsedMs = FRAGNO_BROWSER_PERSISTENCE_DIAGNOSTICS_INTERVAL_MS;\n let diagnosticsTimer: ReturnType<typeof setTimeout> | undefined;\n\n function scheduleDiagnostics() {\n diagnosticsTimer = setTimeout(() => {\n const elapsedMs = Date.now() - startedAt;\n void collectFragnoBrowserPersistenceDiagnostics({\n databaseName: options.databaseName,\n elapsedMs,\n timerDriftMs: Math.max(0, elapsedMs - expectedDiagnosticsElapsedMs),\n workerProbe: workerProbe.snapshot(),\n inspectors: diagnosticsInspectors,\n }).then((diagnostics) => {\n if (settled) {\n return;\n }\n\n console.warn(\n `Fragno outbox browser persistence is still opening after ${diagnostics.elapsedMs}ms. ${diagnostics.likelyCause} Initialization will keep waiting.`,\n diagnostics,\n );\n try {\n options.onDiagnostic?.(diagnostics);\n } catch (error) {\n console.error(\"Fragno browser persistence diagnostic listener failed.\", error);\n }\n expectedDiagnosticsElapsedMs += FRAGNO_BROWSER_PERSISTENCE_DIAGNOSTICS_INTERVAL_MS;\n scheduleDiagnostics();\n });\n }, FRAGNO_BROWSER_PERSISTENCE_DIAGNOSTICS_INTERVAL_MS);\n }\n\n scheduleDiagnostics();\n try {\n return await options.openPersistence();\n } finally {\n settled = true;\n clearTimeout(diagnosticsTimer);\n workerProbe.cleanup();\n }\n}\n\nfunction startFragnoBrowserWorkerProbe(): FragnoBrowserWorkerProbe {\n const browserGlobal = globalThis as FragnoBrowserDiagnosticsGlobal;\n if (\n typeof browserGlobal.Worker !== \"function\" ||\n typeof browserGlobal.Blob !== \"function\" ||\n typeof browserGlobal.URL?.createObjectURL !== \"function\"\n ) {\n return {\n snapshot: () => ({\n status: \"unsupported\",\n reason: \"Dedicated worker probing is unavailable in this runtime.\",\n }),\n cleanup() {},\n };\n }\n\n const startedAt = Date.now();\n let snapshot: FragnoBrowserWorkerProbeSnapshot = { status: \"starting\", elapsedMs: 0 };\n let worker: Worker | null = null;\n let workerUrl: string | null = null;\n\n function cleanup() {\n worker?.terminate();\n worker = null;\n if (workerUrl) {\n browserGlobal.URL.revokeObjectURL(workerUrl);\n workerUrl = null;\n }\n }\n\n try {\n const source = `globalThis.postMessage({\n type: \"fragno-browser-worker-ready\",\n timerAvailable: typeof globalThis.setTimeout === \"function\",\n opfsAvailable: typeof globalThis.navigator?.storage?.getDirectory === \"function\",\n fileSystemFileHandleGlobalAvailable: typeof globalThis.FileSystemFileHandle === \"function\",\n syncAccessHandlePrototypeAvailable:\n typeof globalThis.FileSystemFileHandle?.prototype?.createSyncAccessHandle === \"function\",\n });`;\n workerUrl = browserGlobal.URL.createObjectURL(\n new browserGlobal.Blob([source], { type: \"text/javascript\" }),\n );\n worker = new browserGlobal.Worker(workerUrl);\n worker.addEventListener(\"message\", (event: MessageEvent<unknown>) => {\n const runtime = event.data as {\n type: \"fragno-browser-worker-ready\";\n timerAvailable: boolean;\n opfsAvailable: boolean;\n fileSystemFileHandleGlobalAvailable: boolean;\n syncAccessHandlePrototypeAvailable: boolean;\n };\n snapshot = {\n status: \"responsive\",\n responseMs: Date.now() - startedAt,\n runtime: {\n timerAvailable: runtime.timerAvailable,\n opfsAvailable: runtime.opfsAvailable,\n fileSystemFileHandleGlobalAvailable: runtime.fileSystemFileHandleGlobalAvailable,\n syncAccessHandlePrototypeAvailable: runtime.syncAccessHandlePrototypeAvailable,\n },\n };\n cleanup();\n });\n worker.addEventListener(\"error\", (event: ErrorEvent) => {\n snapshot = {\n status: \"failed\",\n error: event.message || \"Dedicated worker probe failed without an error message.\",\n };\n cleanup();\n });\n worker.addEventListener(\"messageerror\", () => {\n snapshot = {\n status: \"failed\",\n error: \"Dedicated worker probe response could not be deserialized.\",\n };\n cleanup();\n });\n } catch (error) {\n snapshot = { status: \"failed\", error: describeFragnoBrowserDiagnosticError(error) };\n cleanup();\n }\n\n return {\n snapshot() {\n if (snapshot.status !== \"starting\") {\n return snapshot;\n }\n return { status: \"starting\", elapsedMs: Date.now() - startedAt };\n },\n cleanup,\n };\n}\n\nfunction createFragnoBrowserPersistenceDiagnosticInspectors(\n databaseName: string,\n): FragnoBrowserPersistenceDiagnosticInspectors {\n return {\n webLocks: createRecurringFragnoBrowserDiagnosticInspector(\"Web Locks inspection\", () =>\n inspectFragnoBrowserLocks(databaseName),\n ),\n storageEstimate: createRecurringFragnoBrowserDiagnosticInspector(\n \"Storage estimate inspection\",\n inspectFragnoBrowserStorageEstimate,\n ),\n persistedStorage: createRecurringFragnoBrowserDiagnosticInspector(\n \"Persisted storage inspection\",\n inspectFragnoBrowserPersistedStorage,\n ),\n opfsEntries: createRecurringFragnoBrowserDiagnosticInspector(\"OPFS inspection\", () =>\n inspectFragnoBrowserOPFSEntries(databaseName),\n ),\n };\n}\n\nfunction createRecurringFragnoBrowserDiagnosticInspector<TValue>(\n inspectionName: string,\n inspect: () => Promise<FragnoBrowserDiagnostic<TValue>>,\n): () => Promise<FragnoBrowserDiagnostic<TValue>> {\n let activeInspection: Promise<FragnoBrowserDiagnostic<TValue>> | null = null;\n\n return function inspectFragnoBrowserDiagnostic() {\n if (activeInspection === null) {\n activeInspection = inspect().finally(() => {\n activeInspection = null;\n });\n }\n\n return waitForFragnoBrowserDiagnosticInspection(inspectionName, activeInspection);\n };\n}\n\nfunction waitForFragnoBrowserDiagnosticInspection<TValue>(\n inspectionName: string,\n inspection: Promise<FragnoBrowserDiagnostic<TValue>>,\n): Promise<FragnoBrowserDiagnostic<TValue>> {\n return new Promise((resolve) => {\n let settled = false;\n const timeout = setTimeout(() => {\n settled = true;\n resolve({\n status: \"failed\",\n error: `${inspectionName} timed out after ${FRAGNO_BROWSER_PERSISTENCE_INSPECTION_TIMEOUT_MS}ms.`,\n });\n }, FRAGNO_BROWSER_PERSISTENCE_INSPECTION_TIMEOUT_MS);\n\n void inspection.then(\n (diagnostic) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timeout);\n resolve(diagnostic);\n },\n (error: unknown) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timeout);\n resolve({ status: \"failed\", error: describeFragnoBrowserDiagnosticError(error) });\n },\n );\n });\n}\n\nasync function collectFragnoBrowserPersistenceDiagnostics(options: {\n databaseName: string;\n elapsedMs: number;\n timerDriftMs: number;\n workerProbe: FragnoBrowserWorkerProbeSnapshot;\n inspectors: FragnoBrowserPersistenceDiagnosticInspectors;\n}): Promise<FragnoBrowserPersistenceDiagnostics> {\n const browserGlobal = globalThis as FragnoBrowserDiagnosticsGlobal;\n const browserNavigator = browserGlobal.navigator;\n const browserDocument = browserGlobal.document;\n const browserLocation = browserGlobal.location;\n\n const [webLocks, storageEstimate, persistedStorage, opfsEntries] = await Promise.all([\n options.inspectors.webLocks(),\n options.inspectors.storageEstimate(),\n options.inspectors.persistedStorage(),\n options.inspectors.opfsEntries(),\n ]);\n\n const visibilityState = browserDocument?.visibilityState ?? null;\n const likelyCause = diagnoseFragnoBrowserPersistenceStall({\n visibilityState,\n workerProbe: options.workerProbe,\n webLocks,\n });\n\n return {\n databaseName: options.databaseName,\n elapsedMs: options.elapsedMs,\n timerDriftMs: options.timerDriftMs,\n collectedAt: new Date().toISOString(),\n likelyCause,\n workerProbe: options.workerProbe,\n runtime: {\n href: browserLocation?.href ?? null,\n origin: browserLocation?.origin ?? null,\n userAgent: browserNavigator?.userAgent ?? null,\n visibilityState,\n readyState: browserDocument?.readyState ?? null,\n documentHasFocus: browserDocument?.hasFocus() ?? null,\n isSecureContext: browserGlobal.isSecureContext ?? false,\n crossOriginIsolated: browserGlobal.crossOriginIsolated ?? false,\n online: browserNavigator?.onLine ?? null,\n hardwareConcurrency: browserNavigator?.hardwareConcurrency ?? null,\n workerAvailable: typeof browserGlobal.Worker === \"function\",\n broadcastChannelAvailable: typeof browserGlobal.BroadcastChannel === \"function\",\n webLocksAvailable: typeof browserNavigator?.locks?.query === \"function\",\n opfsAvailable: typeof browserNavigator?.storage?.getDirectory === \"function\",\n fileSystemFileHandleGlobalAvailable: typeof browserGlobal.FileSystemFileHandle === \"function\",\n },\n webLocks,\n storageEstimate,\n persistedStorage,\n opfsEntries,\n };\n}\n\nfunction diagnoseFragnoBrowserPersistenceStall(options: {\n visibilityState: DocumentVisibilityState | null;\n workerProbe: FragnoBrowserWorkerProbeSnapshot;\n webLocks: Awaited<ReturnType<typeof inspectFragnoBrowserLocks>>;\n}): string {\n const hasRelevantLocks =\n options.webLocks.status === \"available\" &&\n (options.webLocks.value.relevantHeld.length > 0 ||\n options.webLocks.value.relevantPending.length > 0);\n\n if (options.workerProbe.status === \"starting\" && !hasRelevantLocks) {\n if (options.visibilityState === \"hidden\") {\n return \"The diagnostic worker has not responded and OPFS has created no locks. The document is hidden, which may be contributing to worker scheduling delays in this Electron runtime; the SQLite persistence worker may also be waiting to execute.\";\n }\n return \"The diagnostic worker has not responded and OPFS has created no locks. The SQLite persistence worker may also be waiting to execute.\";\n }\n\n if (options.workerProbe.status === \"failed\") {\n return `Diagnostic worker startup failed: ${options.workerProbe.error}`;\n }\n\n if (options.webLocks.status === \"available\") {\n return options.webLocks.value.interpretation;\n }\n\n return \"The upstream persistence API has not completed and exposes no finer-grained initialization progress.\";\n}\n\nasync function inspectFragnoBrowserLocks(\n databaseName: string,\n): Promise<FragnoBrowserDiagnostic<FragnoBrowserLockSnapshot>> {\n try {\n const lockManager = globalThis.navigator?.locks;\n if (!lockManager?.query) {\n return { status: \"unavailable\", reason: \"Web Locks query is unavailable.\" };\n }\n\n const snapshot = (await lockManager.query()) as {\n held: Array<{ name: string; mode: string; clientId?: string }>;\n pending: Array<{ name: string; mode: string; clientId?: string }>;\n };\n const held = snapshot.held.map(materializeFragnoBrowserLock);\n const pending = snapshot.pending.map(materializeFragnoBrowserLock);\n const databaseLockName = `ahp:/${databaseName}`;\n const isRelevantLock = (lock: FragnoBrowserLock) =>\n lock.name === databaseLockName || lock.name.startsWith(\".ahp-\");\n const relevantHeld = held.filter(isRelevantLock);\n const relevantPending = pending.filter(isRelevantLock);\n\n let interpretation = \"OPFS did not expose an active initialization lock.\";\n if (relevantPending.some((lock) => lock.name === databaseLockName)) {\n interpretation = \"OPFS is waiting to acquire the database access-handle lock.\";\n } else if (relevantHeld.some((lock) => lock.name === databaseLockName)) {\n interpretation =\n \"OPFS acquired the database access-handle lock but did not finish opening SQLite.\";\n } else if (relevantHeld.some((lock) => lock.name.startsWith(\".ahp-\"))) {\n interpretation =\n \"OPFS created its temporary VFS lock but has not requested the database access-handle lock.\";\n }\n\n return {\n status: \"available\",\n value: { held, pending, relevantHeld, relevantPending, interpretation },\n };\n } catch (error) {\n return { status: \"failed\", error: describeFragnoBrowserDiagnosticError(error) };\n }\n}\n\nasync function inspectFragnoBrowserStorageEstimate(): Promise<\n FragnoBrowserDiagnostic<FragnoBrowserStorageEstimate>\n> {\n try {\n const storage = globalThis.navigator?.storage;\n if (!storage?.estimate) {\n return { status: \"unavailable\", reason: \"Storage estimate is unavailable.\" };\n }\n\n const estimate = (await storage.estimate()) as StorageEstimate & {\n usageDetails?: Record<string, number>;\n };\n return {\n status: \"available\",\n value: {\n quota: estimate.quota ?? null,\n usage: estimate.usage ?? null,\n usageDetails: estimate.usageDetails ? { ...estimate.usageDetails } : null,\n },\n };\n } catch (error) {\n return { status: \"failed\", error: describeFragnoBrowserDiagnosticError(error) };\n }\n}\n\nasync function inspectFragnoBrowserPersistedStorage(): Promise<FragnoBrowserDiagnostic<boolean>> {\n try {\n const storage = globalThis.navigator?.storage;\n if (!storage?.persisted) {\n return { status: \"unavailable\", reason: \"Persisted storage status is unavailable.\" };\n }\n\n return { status: \"available\", value: await storage.persisted() };\n } catch (error) {\n return { status: \"failed\", error: describeFragnoBrowserDiagnosticError(error) };\n }\n}\n\nasync function inspectFragnoBrowserOPFSEntries(\n databaseName: string,\n): Promise<FragnoBrowserDiagnostic<FragnoOPFSEntry[]>> {\n try {\n const browserNavigator = (globalThis as FragnoBrowserDiagnosticsGlobal).navigator;\n const getDirectory = browserNavigator?.storage?.getDirectory;\n if (!getDirectory) {\n return { status: \"unavailable\", reason: \"OPFS directory inspection is unavailable.\" };\n }\n\n const root = await getDirectory.call(browserNavigator.storage);\n const entries: FragnoOPFSEntry[] = [];\n for await (const entry of iterateFragnoFileSystemDirectory(root)) {\n if (!entry.name.startsWith(\".ahp-\") && !entry.name.startsWith(databaseName)) {\n continue;\n }\n\n if (entry.kind === \"file\") {\n // Reading file metadata can contend with the sync access handles whose startup we are\n // diagnosing. Directory names reveal the relevant OPFS state without perturbing it.\n entries.push({ kind: \"file\", name: entry.name });\n continue;\n }\n\n try {\n const children = [];\n for await (const child of iterateFragnoFileSystemDirectory(\n entry as FileSystemDirectoryHandle,\n )) {\n children.push({ name: child.name, kind: child.kind });\n }\n entries.push({\n kind: \"directory\",\n name: entry.name,\n entries: children,\n inspectionError: null,\n });\n } catch (error) {\n entries.push({\n kind: \"directory\",\n name: entry.name,\n entries: [],\n inspectionError: describeFragnoBrowserDiagnosticError(error),\n });\n }\n }\n\n return { status: \"available\", value: entries };\n } catch (error) {\n return { status: \"failed\", error: describeFragnoBrowserDiagnosticError(error) };\n }\n}\n\nfunction iterateFragnoFileSystemDirectory(\n directory: FileSystemDirectoryHandle,\n): AsyncIterable<FileSystemHandle> {\n return (\n directory as FileSystemDirectoryHandle & {\n values(): AsyncIterable<FileSystemHandle>;\n }\n ).values();\n}\n\nfunction materializeFragnoBrowserLock(lock: {\n name: string;\n mode: string;\n clientId?: string;\n}): FragnoBrowserLock {\n return { name: lock.name, mode: lock.mode, clientId: lock.clientId ?? null };\n}\n\nfunction describeFragnoBrowserDiagnosticError(error: unknown): string {\n return error instanceof Error ? `${error.name}: ${error.message}` : String(error);\n}\n"],"mappings":";AAAA,MAAM,qDAAqD;AAC3D,MAAM,mDAAmD;;AAgHzD,eAAsB,4CACpB,SACoB;CACpB,MAAM,YAAY,KAAK,IAAI;CAC3B,MAAM,cAAc,8BAA8B;CAClD,MAAM,wBAAwB,mDAC5B,QAAQ,YACV;CACA,IAAI,UAAU;CACd,IAAI,+BAA+B;CACnC,IAAI;CAEJ,SAAS,sBAAsB;EAC7B,mBAAmB,iBAAiB;GAClC,MAAM,YAAY,KAAK,IAAI,IAAI;GAC/B,2CAAgD;IAC9C,cAAc,QAAQ;IACtB;IACA,cAAc,KAAK,IAAI,GAAG,YAAY,4BAA4B;IAClE,aAAa,YAAY,SAAS;IAClC,YAAY;GACd,CAAC,CAAC,CAAC,MAAM,gBAAgB;IACvB,IAAI,SACF;IAGF,QAAQ,KACN,4DAA4D,YAAY,UAAU,MAAM,YAAY,YAAY,qCAChH,WACF;IACA,IAAI;KACF,QAAQ,eAAe,WAAW;IACpC,SAAS,OAAO;KACd,QAAQ,MAAM,0DAA0D,KAAK;IAC/E;IACA,gCAAgC;IAChC,oBAAoB;GACtB,CAAC;EACH,GAAG,kDAAkD;CACvD;CAEA,oBAAoB;CACpB,IAAI;EACF,OAAO,MAAM,QAAQ,gBAAgB;CACvC,UAAU;EACR,UAAU;EACV,aAAa,gBAAgB;EAC7B,YAAY,QAAQ;CACtB;AACF;AAEA,SAAS,gCAA0D;CACjE,MAAM,gBAAgB;CACtB,IACE,OAAO,cAAc,WAAW,cAChC,OAAO,cAAc,SAAS,cAC9B,OAAO,cAAc,KAAK,oBAAoB,YAE9C,OAAO;EACL,iBAAiB;GACf,QAAQ;GACR,QAAQ;EACV;EACA,UAAU,CAAC;CACb;CAGF,MAAM,YAAY,KAAK,IAAI;CAC3B,IAAI,WAA6C;EAAE,QAAQ;EAAY,WAAW;CAAE;CACpF,IAAI,SAAwB;CAC5B,IAAI,YAA2B;CAE/B,SAAS,UAAU;EACjB,QAAQ,UAAU;EAClB,SAAS;EACT,IAAI,WAAW;GACb,cAAc,IAAI,gBAAgB,SAAS;GAC3C,YAAY;EACd;CACF;CAEA,IAAI;EASF,YAAY,cAAc,IAAI,gBAC5B,IAAI,cAAc,KAAK,CAAC;;;;;;;QAAM,GAAG,EAAE,MAAM,kBAAkB,CAAC,CAC9D;EACA,SAAS,IAAI,cAAc,OAAO,SAAS;EAC3C,OAAO,iBAAiB,YAAY,UAAiC;GACnE,MAAM,UAAU,MAAM;GAOtB,WAAW;IACT,QAAQ;IACR,YAAY,KAAK,IAAI,IAAI;IACzB,SAAS;KACP,gBAAgB,QAAQ;KACxB,eAAe,QAAQ;KACvB,qCAAqC,QAAQ;KAC7C,oCAAoC,QAAQ;IAC9C;GACF;GACA,QAAQ;EACV,CAAC;EACD,OAAO,iBAAiB,UAAU,UAAsB;GACtD,WAAW;IACT,QAAQ;IACR,OAAO,MAAM,WAAW;GAC1B;GACA,QAAQ;EACV,CAAC;EACD,OAAO,iBAAiB,sBAAsB;GAC5C,WAAW;IACT,QAAQ;IACR,OAAO;GACT;GACA,QAAQ;EACV,CAAC;CACH,SAAS,OAAO;EACd,WAAW;GAAE,QAAQ;GAAU,OAAO,qCAAqC,KAAK;EAAE;EAClF,QAAQ;CACV;CAEA,OAAO;EACL,WAAW;GACT,IAAI,SAAS,WAAW,YACtB,OAAO;GAET,OAAO;IAAE,QAAQ;IAAY,WAAW,KAAK,IAAI,IAAI;GAAU;EACjE;EACA;CACF;AACF;AAEA,SAAS,mDACP,cAC8C;CAC9C,OAAO;EACL,UAAU,gDAAgD,8BACxD,0BAA0B,YAAY,CACxC;EACA,iBAAiB,gDACf,+BACA,mCACF;EACA,kBAAkB,gDAChB,gCACA,oCACF;EACA,aAAa,gDAAgD,yBAC3D,gCAAgC,YAAY,CAC9C;CACF;AACF;AAEA,SAAS,gDACP,gBACA,SACgD;CAChD,IAAI,mBAAoE;CAExE,OAAO,SAAS,iCAAiC;EAC/C,IAAI,qBAAqB,MACvB,mBAAmB,QAAQ,CAAC,CAAC,cAAc;GACzC,mBAAmB;EACrB,CAAC;EAGH,OAAO,yCAAyC,gBAAgB,gBAAgB;CAClF;AACF;AAEA,SAAS,yCACP,gBACA,YAC0C;CAC1C,OAAO,IAAI,SAAS,YAAY;EAC9B,IAAI,UAAU;EACd,MAAM,UAAU,iBAAiB;GAC/B,UAAU;GACV,QAAQ;IACN,QAAQ;IACR,OAAO,GAAG,eAAe,mBAAmB,iDAAiD;GAC/F,CAAC;EACH,GAAG,gDAAgD;EAEnD,WAAgB,MACb,eAAe;GACd,IAAI,SACF;GAEF,UAAU;GACV,aAAa,OAAO;GACpB,QAAQ,UAAU;EACpB,IACC,UAAmB;GAClB,IAAI,SACF;GAEF,UAAU;GACV,aAAa,OAAO;GACpB,QAAQ;IAAE,QAAQ;IAAU,OAAO,qCAAqC,KAAK;GAAE,CAAC;EAClF,CACF;CACF,CAAC;AACH;AAEA,eAAe,2CAA2C,SAMT;CAC/C,MAAM,gBAAgB;CACtB,MAAM,mBAAmB,cAAc;CACvC,MAAM,kBAAkB,cAAc;CACtC,MAAM,kBAAkB,cAAc;CAEtC,MAAM,CAAC,UAAU,iBAAiB,kBAAkB,eAAe,MAAM,QAAQ,IAAI;EACnF,QAAQ,WAAW,SAAS;EAC5B,QAAQ,WAAW,gBAAgB;EACnC,QAAQ,WAAW,iBAAiB;EACpC,QAAQ,WAAW,YAAY;CACjC,CAAC;CAED,MAAM,kBAAkB,iBAAiB,mBAAmB;CAC5D,MAAM,cAAc,sCAAsC;EACxD;EACA,aAAa,QAAQ;EACrB;CACF,CAAC;CAED,OAAO;EACL,cAAc,QAAQ;EACtB,WAAW,QAAQ;EACnB,cAAc,QAAQ;EACtB,8BAAa,IAAI,KAAK,EAAA,CAAE,YAAY;EACpC;EACA,aAAa,QAAQ;EACrB,SAAS;GACP,MAAM,iBAAiB,QAAQ;GAC/B,QAAQ,iBAAiB,UAAU;GACnC,WAAW,kBAAkB,aAAa;GAC1C;GACA,YAAY,iBAAiB,cAAc;GAC3C,kBAAkB,iBAAiB,SAAS,KAAK;GACjD,iBAAiB,cAAc,mBAAmB;GAClD,qBAAqB,cAAc,uBAAuB;GAC1D,QAAQ,kBAAkB,UAAU;GACpC,qBAAqB,kBAAkB,uBAAuB;GAC9D,iBAAiB,OAAO,cAAc,WAAW;GACjD,2BAA2B,OAAO,cAAc,qBAAqB;GACrE,mBAAmB,OAAO,kBAAkB,OAAO,UAAU;GAC7D,eAAe,OAAO,kBAAkB,SAAS,iBAAiB;GAClE,qCAAqC,OAAO,cAAc,yBAAyB;EACrF;EACA;EACA;EACA;EACA;CACF;AACF;AAEA,SAAS,sCAAsC,SAIpC;CACT,MAAM,mBACJ,QAAQ,SAAS,WAAW,gBAC3B,QAAQ,SAAS,MAAM,aAAa,SAAS,KAC5C,QAAQ,SAAS,MAAM,gBAAgB,SAAS;CAEpD,IAAI,QAAQ,YAAY,WAAW,cAAc,CAAC,kBAAkB;EAClE,IAAI,QAAQ,oBAAoB,UAC9B,OAAO;EAET,OAAO;CACT;CAEA,IAAI,QAAQ,YAAY,WAAW,UACjC,OAAO,qCAAqC,QAAQ,YAAY;CAGlE,IAAI,QAAQ,SAAS,WAAW,aAC9B,OAAO,QAAQ,SAAS,MAAM;CAGhC,OAAO;AACT;AAEA,eAAe,0BACb,cAC6D;CAC7D,IAAI;EACF,MAAM,cAAc,WAAW,WAAW;EAC1C,IAAI,CAAC,aAAa,OAChB,OAAO;GAAE,QAAQ;GAAe,QAAQ;EAAkC;EAG5E,MAAM,WAAY,MAAM,YAAY,MAAM;EAI1C,MAAM,OAAO,SAAS,KAAK,IAAI,4BAA4B;EAC3D,MAAM,UAAU,SAAS,QAAQ,IAAI,4BAA4B;EACjE,MAAM,mBAAmB,QAAQ;EACjC,MAAM,kBAAkB,SACtB,KAAK,SAAS,oBAAoB,KAAK,KAAK,WAAW,OAAO;EAChE,MAAM,eAAe,KAAK,OAAO,cAAc;EAC/C,MAAM,kBAAkB,QAAQ,OAAO,cAAc;EAErD,IAAI,iBAAiB;EACrB,IAAI,gBAAgB,MAAM,SAAS,KAAK,SAAS,gBAAgB,GAC/D,iBAAiB;OACZ,IAAI,aAAa,MAAM,SAAS,KAAK,SAAS,gBAAgB,GACnE,iBACE;OACG,IAAI,aAAa,MAAM,SAAS,KAAK,KAAK,WAAW,OAAO,CAAC,GAClE,iBACE;EAGJ,OAAO;GACL,QAAQ;GACR,OAAO;IAAE;IAAM;IAAS;IAAc;IAAiB;GAAe;EACxE;CACF,SAAS,OAAO;EACd,OAAO;GAAE,QAAQ;GAAU,OAAO,qCAAqC,KAAK;EAAE;CAChF;AACF;AAEA,eAAe,sCAEb;CACA,IAAI;EACF,MAAM,UAAU,WAAW,WAAW;EACtC,IAAI,CAAC,SAAS,UACZ,OAAO;GAAE,QAAQ;GAAe,QAAQ;EAAmC;EAG7E,MAAM,WAAY,MAAM,QAAQ,SAAS;EAGzC,OAAO;GACL,QAAQ;GACR,OAAO;IACL,OAAO,SAAS,SAAS;IACzB,OAAO,SAAS,SAAS;IACzB,cAAc,SAAS,eAAe,EAAE,GAAG,SAAS,aAAa,IAAI;GACvE;EACF;CACF,SAAS,OAAO;EACd,OAAO;GAAE,QAAQ;GAAU,OAAO,qCAAqC,KAAK;EAAE;CAChF;AACF;AAEA,eAAe,uCAAkF;CAC/F,IAAI;EACF,MAAM,UAAU,WAAW,WAAW;EACtC,IAAI,CAAC,SAAS,WACZ,OAAO;GAAE,QAAQ;GAAe,QAAQ;EAA2C;EAGrF,OAAO;GAAE,QAAQ;GAAa,OAAO,MAAM,QAAQ,UAAU;EAAE;CACjE,SAAS,OAAO;EACd,OAAO;GAAE,QAAQ;GAAU,OAAO,qCAAqC,KAAK;EAAE;CAChF;AACF;AAEA,eAAe,gCACb,cACqD;CACrD,IAAI;EACF,MAAM,mBAAoB,WAA8C;EACxE,MAAM,eAAe,kBAAkB,SAAS;EAChD,IAAI,CAAC,cACH,OAAO;GAAE,QAAQ;GAAe,QAAQ;EAA4C;EAGtF,MAAM,OAAO,MAAM,aAAa,KAAK,iBAAiB,OAAO;EAC7D,MAAM,UAA6B,CAAC;EACpC,WAAW,MAAM,SAAS,iCAAiC,IAAI,GAAG;GAChE,IAAI,CAAC,MAAM,KAAK,WAAW,OAAO,KAAK,CAAC,MAAM,KAAK,WAAW,YAAY,GACxE;GAGF,IAAI,MAAM,SAAS,QAAQ;IAGzB,QAAQ,KAAK;KAAE,MAAM;KAAQ,MAAM,MAAM;IAAK,CAAC;IAC/C;GACF;GAEA,IAAI;IACF,MAAM,WAAW,CAAC;IAClB,WAAW,MAAM,SAAS,iCACxB,KACF,GACE,SAAS,KAAK;KAAE,MAAM,MAAM;KAAM,MAAM,MAAM;IAAK,CAAC;IAEtD,QAAQ,KAAK;KACX,MAAM;KACN,MAAM,MAAM;KACZ,SAAS;KACT,iBAAiB;IACnB,CAAC;GACH,SAAS,OAAO;IACd,QAAQ,KAAK;KACX,MAAM;KACN,MAAM,MAAM;KACZ,SAAS,CAAC;KACV,iBAAiB,qCAAqC,KAAK;IAC7D,CAAC;GACH;EACF;EAEA,OAAO;GAAE,QAAQ;GAAa,OAAO;EAAQ;CAC/C,SAAS,OAAO;EACd,OAAO;GAAE,QAAQ;GAAU,OAAO,qCAAqC,KAAK;EAAE;CAChF;AACF;AAEA,SAAS,iCACP,WACiC;CACjC,OACE,UAGA,OAAO;AACX;AAEA,SAAS,6BAA6B,MAIhB;CACpB,OAAO;EAAE,MAAM,KAAK;EAAM,MAAM,KAAK;EAAM,UAAU,KAAK,YAAY;CAAK;AAC7E;AAEA,SAAS,qCAAqC,OAAwB;CACpE,OAAO,iBAAiB,QAAQ,GAAG,MAAM,KAAK,IAAI,MAAM,YAAY,OAAO,KAAK;AAClF"}
@@ -0,0 +1,85 @@
1
+ import { fragnoOutboxTargetKey } from "./fragno-outbox-synchronizer.js";
2
+ import { FragnoTableCollection } from "./fragno-table-collection.js";
3
+ import { resolveDatabaseNamespace } from "@fragno-dev/db/database-namespace";
4
+ //#region src/coordinator/fragno-collection-registry.ts
5
+ /** Owns the table collections registered against one Fragno outbox coordinator. */
6
+ var FragnoCollectionRegistry = class {
7
+ #schemas;
8
+ #persistence;
9
+ #schemaVersion;
10
+ #outbox;
11
+ #registrations = /* @__PURE__ */ new Map();
12
+ constructor(options) {
13
+ this.#schemas = validateSchemas(options.schemas);
14
+ this.#persistence = options.persistence;
15
+ this.#schemaVersion = options.schemaVersion;
16
+ this.#outbox = options.outbox;
17
+ }
18
+ registerCollection(schema, tableName, coordinatorState, collectionOptions = {}) {
19
+ if (!this.#schemas.has(schema)) throw new Error(`Schema ${schema.name} was not supplied to this Fragno coordinator.`);
20
+ const table = schema.tables[tableName];
21
+ if (!table) throw new Error(`Table ${schema.name}.${tableName} does not exist.`);
22
+ const namespace = resolveDatabaseNamespace(schema.name) ?? "";
23
+ const targetKey = fragnoOutboxTargetKey(namespace, tableName);
24
+ const existing = this.#registrations.get(targetKey);
25
+ if (existing) return existing.collection;
26
+ if (coordinatorState !== "idle") throw new Error(`Fragno collection ${schema.name}.${tableName} cannot be registered while the coordinator is ${coordinatorState}.`);
27
+ const tableCollection = new FragnoTableCollection({
28
+ id: fragnoTableCollectionId(namespace, tableName),
29
+ idColumnName: table.getIdColumn().name,
30
+ persistence: this.#persistence,
31
+ schemaVersion: this.#schemaVersion,
32
+ outbox: this.#outbox,
33
+ rowUpdateMode: collectionOptions.rowUpdateMode ?? "partial",
34
+ skipMissingTruncateDeletes: collectionOptions.skipMissingTruncateDeletes ?? false,
35
+ target: {
36
+ key: targetKey,
37
+ namespace,
38
+ schema,
39
+ tableName
40
+ }
41
+ });
42
+ this.#registrations.set(targetKey, tableCollection);
43
+ return tableCollection.collection;
44
+ }
45
+ registeredTargets() {
46
+ return [...this.#registrations.keys()].sort();
47
+ }
48
+ preload() {
49
+ if (this.#registrations.size === 0) return Promise.reject(/* @__PURE__ */ new Error("At least one Fragno collection must be registered before preload()."));
50
+ return Promise.all([...this.#registrations.values()].map(({ collection }) => collection.preload())).then(() => {});
51
+ }
52
+ async cleanup() {
53
+ await Promise.all([...this.#registrations.values()].map(({ collection }) => collection.cleanup()));
54
+ }
55
+ };
56
+ function validateSchemas(schemas) {
57
+ if (schemas.length === 0) throw new Error("createFragnoOutboxCoordinator requires at least one schema.");
58
+ const suppliedSchemas = /* @__PURE__ */ new Set();
59
+ const schemaNames = /* @__PURE__ */ new Set();
60
+ const physicalTargets = /* @__PURE__ */ new Map();
61
+ for (const schema of schemas) {
62
+ if (suppliedSchemas.has(schema)) throw new Error(`Duplicate Fragno schema ${schema.name}.`);
63
+ if (schemaNames.has(schema.name)) throw new Error(`Duplicate Fragno schema name ${schema.name}.`);
64
+ suppliedSchemas.add(schema);
65
+ schemaNames.add(schema.name);
66
+ const namespace = resolveDatabaseNamespace(schema.name) ?? "";
67
+ for (const tableName of Object.keys(schema.tables)) {
68
+ const targetKey = fragnoOutboxTargetKey(namespace, tableName);
69
+ const existingTarget = physicalTargets.get(targetKey);
70
+ if (existingTarget) throw new Error(`Fragno schemas ${existingTarget.schemaName} and ${schema.name} both target ${namespace}.${tableName}.`);
71
+ physicalTargets.set(targetKey, {
72
+ schemaName: schema.name,
73
+ tableName
74
+ });
75
+ }
76
+ }
77
+ return suppliedSchemas;
78
+ }
79
+ function fragnoTableCollectionId(namespace, tableName) {
80
+ return `fragno.outbox.table.v1:${fragnoOutboxTargetKey(namespace, tableName)}`;
81
+ }
82
+ //#endregion
83
+ export { FragnoCollectionRegistry };
84
+
85
+ //# sourceMappingURL=fragno-collection-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragno-collection-registry.js","names":["#schemas","#persistence","#schemaVersion","#outbox","#registrations"],"sources":["../../src/coordinator/fragno-collection-registry.ts"],"sourcesContent":["import { resolveDatabaseNamespace } from \"@fragno-dev/db/database-namespace\";\nimport type { AnySchema } from \"@fragno-dev/db/schema\";\n\nimport type { Collection } from \"@tanstack/db\";\nimport type { PersistedCollectionPersistence } from \"@tanstack/db-sqlite-persistence-core\";\n\nimport type {\n FragnoCollectionOptions,\n FragnoCollectionRow,\n FragnoOutboxCoordinatorState,\n} from \"../coordinator\";\nimport { fragnoOutboxTargetKey, type FragnoOutboxSynchronizer } from \"./fragno-outbox-synchronizer\";\nimport { FragnoTableCollection } from \"./fragno-table-collection\";\n\n/** Owns the table collections registered against one Fragno outbox coordinator. */\nexport class FragnoCollectionRegistry<TSchemas extends readonly AnySchema[]> {\n readonly #schemas: Set<AnySchema>;\n readonly #persistence: PersistedCollectionPersistence;\n readonly #schemaVersion: number;\n readonly #outbox: FragnoOutboxSynchronizer;\n readonly #registrations = new Map<string, FragnoTableCollection>();\n\n constructor(options: {\n schemas: TSchemas;\n persistence: PersistedCollectionPersistence;\n schemaVersion: number;\n outbox: FragnoOutboxSynchronizer;\n }) {\n this.#schemas = validateSchemas(options.schemas);\n this.#persistence = options.persistence;\n this.#schemaVersion = options.schemaVersion;\n this.#outbox = options.outbox;\n }\n\n registerCollection<\n TSchema extends TSchemas[number],\n TTableName extends keyof TSchema[\"tables\"] & string,\n >(\n schema: TSchema,\n tableName: TTableName,\n coordinatorState: FragnoOutboxCoordinatorState,\n collectionOptions: FragnoCollectionOptions = {},\n ): Collection<FragnoCollectionRow<TSchema[\"tables\"][TTableName]>, string> {\n if (!this.#schemas.has(schema)) {\n throw new Error(`Schema ${schema.name} was not supplied to this Fragno coordinator.`);\n }\n\n const table = schema.tables[tableName];\n if (!table) {\n throw new Error(`Table ${schema.name}.${tableName} does not exist.`);\n }\n\n const namespace = resolveDatabaseNamespace(schema.name) ?? \"\";\n const targetKey = fragnoOutboxTargetKey(namespace, tableName);\n const existing = this.#registrations.get(targetKey);\n if (existing) {\n return existing.collection as unknown as Collection<\n FragnoCollectionRow<TSchema[\"tables\"][TTableName]>,\n string\n >;\n }\n\n if (coordinatorState !== \"idle\") {\n throw new Error(\n `Fragno collection ${schema.name}.${tableName} cannot be registered while the coordinator is ${coordinatorState}.`,\n );\n }\n\n const tableCollection = new FragnoTableCollection({\n id: fragnoTableCollectionId(namespace, tableName),\n idColumnName: table.getIdColumn().name,\n persistence: this.#persistence,\n schemaVersion: this.#schemaVersion,\n outbox: this.#outbox,\n rowUpdateMode: collectionOptions.rowUpdateMode ?? \"partial\",\n skipMissingTruncateDeletes: collectionOptions.skipMissingTruncateDeletes ?? false,\n target: {\n key: targetKey,\n namespace,\n schema,\n tableName,\n },\n });\n this.#registrations.set(targetKey, tableCollection);\n\n return tableCollection.collection as unknown as Collection<\n FragnoCollectionRow<TSchema[\"tables\"][TTableName]>,\n string\n >;\n }\n\n registeredTargets(): readonly string[] {\n return [...this.#registrations.keys()].sort();\n }\n\n preload(): Promise<void> {\n if (this.#registrations.size === 0) {\n return Promise.reject(\n new Error(\"At least one Fragno collection must be registered before preload().\"),\n );\n }\n\n return Promise.all(\n [...this.#registrations.values()].map(({ collection }) => collection.preload()),\n ).then(() => {});\n }\n\n async cleanup(): Promise<void> {\n await Promise.all(\n [...this.#registrations.values()].map(({ collection }) => collection.cleanup()),\n );\n }\n}\n\nfunction validateSchemas(schemas: readonly AnySchema[]): Set<AnySchema> {\n if (schemas.length === 0) {\n throw new Error(\"createFragnoOutboxCoordinator requires at least one schema.\");\n }\n\n const suppliedSchemas = new Set<AnySchema>();\n const schemaNames = new Set<string>();\n const physicalTargets = new Map<string, { schemaName: string; tableName: string }>();\n\n for (const schema of schemas) {\n if (suppliedSchemas.has(schema)) {\n throw new Error(`Duplicate Fragno schema ${schema.name}.`);\n }\n if (schemaNames.has(schema.name)) {\n throw new Error(`Duplicate Fragno schema name ${schema.name}.`);\n }\n suppliedSchemas.add(schema);\n schemaNames.add(schema.name);\n\n const namespace = resolveDatabaseNamespace(schema.name) ?? \"\";\n for (const tableName of Object.keys(schema.tables)) {\n const targetKey = fragnoOutboxTargetKey(namespace, tableName);\n const existingTarget = physicalTargets.get(targetKey);\n if (existingTarget) {\n throw new Error(\n `Fragno schemas ${existingTarget.schemaName} and ${schema.name} both target ${namespace}.${tableName}.`,\n );\n }\n physicalTargets.set(targetKey, { schemaName: schema.name, tableName });\n }\n }\n\n return suppliedSchemas;\n}\n\nfunction fragnoTableCollectionId(namespace: string, tableName: string): string {\n return `fragno.outbox.table.v1:${fragnoOutboxTargetKey(namespace, tableName)}`;\n}\n"],"mappings":";;;;;AAeA,IAAa,2BAAb,MAA6E;CAC3E;CACA;CACA;CACA;CACA,iCAA0B,IAAI,IAAmC;CAEjE,YAAY,SAKT;EACD,KAAKA,WAAW,gBAAgB,QAAQ,OAAO;EAC/C,KAAKC,eAAe,QAAQ;EAC5B,KAAKC,iBAAiB,QAAQ;EAC9B,KAAKC,UAAU,QAAQ;CACzB;CAEA,mBAIE,QACA,WACA,kBACA,oBAA6C,CAAC,GAC0B;EACxE,IAAI,CAAC,KAAKH,SAAS,IAAI,MAAM,GAC3B,MAAM,IAAI,MAAM,UAAU,OAAO,KAAK,8CAA8C;EAGtF,MAAM,QAAQ,OAAO,OAAO;EAC5B,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,SAAS,OAAO,KAAK,GAAG,UAAU,iBAAiB;EAGrE,MAAM,YAAY,yBAAyB,OAAO,IAAI,KAAK;EAC3D,MAAM,YAAY,sBAAsB,WAAW,SAAS;EAC5D,MAAM,WAAW,KAAKI,eAAe,IAAI,SAAS;EAClD,IAAI,UACF,OAAO,SAAS;EAMlB,IAAI,qBAAqB,QACvB,MAAM,IAAI,MACR,qBAAqB,OAAO,KAAK,GAAG,UAAU,iDAAiD,iBAAiB,EAClH;EAGF,MAAM,kBAAkB,IAAI,sBAAsB;GAChD,IAAI,wBAAwB,WAAW,SAAS;GAChD,cAAc,MAAM,YAAY,CAAC,CAAC;GAClC,aAAa,KAAKH;GAClB,eAAe,KAAKC;GACpB,QAAQ,KAAKC;GACb,eAAe,kBAAkB,iBAAiB;GAClD,4BAA4B,kBAAkB,8BAA8B;GAC5E,QAAQ;IACN,KAAK;IACL;IACA;IACA;GACF;EACF,CAAC;EACD,KAAKC,eAAe,IAAI,WAAW,eAAe;EAElD,OAAO,gBAAgB;CAIzB;CAEA,oBAAuC;EACrC,OAAO,CAAC,GAAG,KAAKA,eAAe,KAAK,CAAC,CAAC,CAAC,KAAK;CAC9C;CAEA,UAAyB;EACvB,IAAI,KAAKA,eAAe,SAAS,GAC/B,OAAO,QAAQ,uBACb,IAAI,MAAM,qEAAqE,CACjF;EAGF,OAAO,QAAQ,IACb,CAAC,GAAG,KAAKA,eAAe,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,iBAAiB,WAAW,QAAQ,CAAC,CAChF,CAAC,CAAC,WAAW,CAAC,CAAC;CACjB;CAEA,MAAM,UAAyB;EAC7B,MAAM,QAAQ,IACZ,CAAC,GAAG,KAAKA,eAAe,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,iBAAiB,WAAW,QAAQ,CAAC,CAChF;CACF;AACF;AAEA,SAAS,gBAAgB,SAA+C;CACtE,IAAI,QAAQ,WAAW,GACrB,MAAM,IAAI,MAAM,6DAA6D;CAG/E,MAAM,kCAAkB,IAAI,IAAe;CAC3C,MAAM,8BAAc,IAAI,IAAY;CACpC,MAAM,kCAAkB,IAAI,IAAuD;CAEnF,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,gBAAgB,IAAI,MAAM,GAC5B,MAAM,IAAI,MAAM,2BAA2B,OAAO,KAAK,EAAE;EAE3D,IAAI,YAAY,IAAI,OAAO,IAAI,GAC7B,MAAM,IAAI,MAAM,gCAAgC,OAAO,KAAK,EAAE;EAEhE,gBAAgB,IAAI,MAAM;EAC1B,YAAY,IAAI,OAAO,IAAI;EAE3B,MAAM,YAAY,yBAAyB,OAAO,IAAI,KAAK;EAC3D,KAAK,MAAM,aAAa,OAAO,KAAK,OAAO,MAAM,GAAG;GAClD,MAAM,YAAY,sBAAsB,WAAW,SAAS;GAC5D,MAAM,iBAAiB,gBAAgB,IAAI,SAAS;GACpD,IAAI,gBACF,MAAM,IAAI,MACR,kBAAkB,eAAe,WAAW,OAAO,OAAO,KAAK,eAAe,UAAU,GAAG,UAAU,EACvG;GAEF,gBAAgB,IAAI,WAAW;IAAE,YAAY,OAAO;IAAM;GAAU,CAAC;EACvE;CACF;CAEA,OAAO;AACT;AAEA,SAAS,wBAAwB,WAAmB,WAA2B;CAC7E,OAAO,0BAA0B,sBAAsB,WAAW,SAAS;AAC7E"}
@@ -0,0 +1,40 @@
1
+ import { FragnoOutboxCheckpoint } from "../checkpoint.js";
2
+ import { Collection, SyncAppliedReceipt } from "@tanstack/db";
3
+ import { PersistedCollectionPersistence } from "@tanstack/db-sqlite-persistence-core";
4
+ //#region src/coordinator/fragno-internal-collection.d.ts
5
+ declare const FRAGNO_INTERNAL_STATUS_ID: "coordinator";
6
+ type FragnoOutboxCoordinatorState = "opening" | "idle" | "registering" | "catching-up" | "replaying" | "live" | "retrying" | "rotating" | "failed" | "disposed";
7
+ type FragnoOutboxCoordinatorError = {
8
+ name: string;
9
+ message: string;
10
+ };
11
+ type FragnoInternalRow = {
12
+ id: typeof FRAGNO_INTERNAL_STATUS_ID;
13
+ state: FragnoOutboxCoordinatorState;
14
+ checkpoint: FragnoOutboxCheckpoint | null;
15
+ error: FragnoOutboxCoordinatorError | null;
16
+ };
17
+ /** Persisted and user-queryable coordinator state for one Fragno outbox database. */
18
+ declare class FragnoInternalCollection {
19
+ #private;
20
+ readonly collection: Collection<FragnoInternalRow, string>;
21
+ constructor(options: {
22
+ persistence: PersistedCollectionPersistence;
23
+ schemaVersion?: number;
24
+ state?: FragnoOutboxCoordinatorState;
25
+ });
26
+ preload(): Promise<void>;
27
+ getCheckpoint(): FragnoOutboxCheckpoint | undefined;
28
+ setCheckpoint(checkpoint: FragnoOutboxCheckpoint): SyncAppliedReceipt;
29
+ /**
30
+ * Settles once every earlier write in the shared persistence database is durable: a persisted
31
+ * commit's receipt settles with its durable write, and writes persist in order.
32
+ */
33
+ flushPersistence(): SyncAppliedReceipt;
34
+ clearCheckpoint(): SyncAppliedReceipt;
35
+ setState(state: FragnoOutboxCoordinatorState, error?: unknown): SyncAppliedReceipt;
36
+ cleanup(): Promise<void>;
37
+ }
38
+ //#endregion
39
+ export { FRAGNO_INTERNAL_STATUS_ID, FragnoInternalCollection, FragnoInternalRow, FragnoOutboxCoordinatorError, FragnoOutboxCoordinatorState };
40
+ //# sourceMappingURL=fragno-internal-collection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragno-internal-collection.d.ts","names":[],"sources":["../../src/coordinator/fragno-internal-collection.ts"],"mappings":";;;;cAca;KAID;KAYA;EACV;EACA;;KAGU;EACV,WAAW;EACX,OAAO;EACP,YAAY;EACZ,OAAO;;;cAMI;;WACF,YAAY,WAAW;cAOpB;IACV,aAAa;IACb;IACA,QAAQ;;EAiDV,WAAW;EAUX,iBAAiB;EAIjB,cAAc,YAAY,yBAAyB;;;;;EAenD,oBAAoB;EAWpB,mBAAmB;EAWnB,SAAS,OAAO,8BAA8B,kBAAwB;EAStE,WAAW"}