@bitcobblers/wod-wiki-storage 0.39.2142 → 0.40.2144

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.
@@ -0,0 +1,156 @@
1
+ import { IDBPDatabase, IDBPTransaction } from 'idb';
2
+ import { I as IStorage, S as StoreName, a as IReadOnlyStore, b as StoreType, c as IReadWriteStore, d as IStorageTransaction } from '../wire-DGYxXt3I.cjs';
3
+ import { U as UserScopeContext } from '../policy-DRYF6Y8i.cjs';
4
+ import { S as SyncMutation, a as SyncEntry } from '../sync-DG4Z-szj.cjs';
5
+ import { D as Database } from '../connection-DxuCLpRc.cjs';
6
+ import '@bitcobblers/wod-wiki-core';
7
+ import '@bitcobblers/wod-wiki-lang';
8
+ import '@libsql/client';
9
+
10
+ /**
11
+ * IndexedDB adapter — the browser data plane (moved verbatim from the
12
+ * playground app; the shared user policy and the schema32 sync cutover live
13
+ * here now). Local-first invariants:
14
+ *
15
+ * - Every committed local transaction group is durable in `sync_outbox`,
16
+ * written in the SAME native transaction as the data writes.
17
+ * - Snapshots (`snapshotRows`, bootstrap, conversion) are policy-compliant:
18
+ * they carry user data only, per the shared policy in `../policy`.
19
+ * - `sync_state` holds pull cursors, upload watermarks and frozen
20
+ * bootstrap runs (durable retry identities), all per backup target.
21
+ */
22
+
23
+ declare const IDB_DB_NAME = "wodwiki-db";
24
+ type Mode = 'readonly' | 'readwrite';
25
+ /** Native tx handle; write methods are guarded at runtime by `mutable`, so
26
+ * the handle is typed readwrite to keep put/delete/clear defined. */
27
+ type Tx = IDBPTransaction<unknown, string, 'readwrite'>;
28
+ declare function subscribeOutboxWrites(listener: () => void): () => void;
29
+ declare class IndexedDBStorage implements IStorage {
30
+ private readonly options;
31
+ private opening?;
32
+ constructor(options?: {
33
+ getUserId?: () => string;
34
+ /** Called just before a schema migration runs (oldVersion < DB_VERSION). */
35
+ onUpgradeStart?: (fromVersion: number) => void;
36
+ /** Database file name; the sync engine migrates scratch copies under
37
+ * their own name so a remote-schema conversion never touches the
38
+ * live database. Defaults to the app database. */
39
+ dbName?: string;
40
+ });
41
+ open(): Promise<IDBPDatabase>;
42
+ stamp(store: StoreName, value: unknown): unknown;
43
+ /** User-scope context (proven seed note ids) from the notes of `tx` —
44
+ * journal-time ownership classification reads it INSIDE the same
45
+ * transaction, so a seed import's note writes are visible to the link
46
+ * writes that follow in the same commit. Internal seam for IDBStore. */
47
+ policyContext(tx?: Tx): Promise<UserScopeContext>;
48
+ readonly<K extends StoreName>(store: K): IReadOnlyStore<StoreType<K>>;
49
+ readwrite<K extends StoreName>(store: K): IReadWriteStore<StoreType<K>>;
50
+ transaction<K extends StoreName, R>(stores: K[], mode: Mode, fn: (tx: IStorageTransaction) => Promise<R>): Promise<R>;
51
+ wipeUserData(): Promise<void>;
52
+ wipe(): Promise<void>;
53
+ /** Pending committed transaction groups, in commit order. */
54
+ readOutbox(afterSeq: number, limit: number): Promise<{
55
+ seq: number;
56
+ id: string;
57
+ at: number;
58
+ ops: SyncMutation[];
59
+ }[]>;
60
+ /** Drop acknowledged groups. Callers pass only the contiguous acked
61
+ * watermark — a failed upload never truncates pending writes. */
62
+ ackOutbox(upToSeq: number): Promise<void>;
63
+ getSyncState<T>(key: string): Promise<T | undefined>;
64
+ setSyncState(key: string, value: unknown): Promise<void>;
65
+ /**
66
+ * Freeze ONE durable bootstrap run for `key` (per backup target): a
67
+ * policy-compliant snapshot of the current rows, chunked into ≤500-op /
68
+ * ≤~1MB entries with a unique run UUID and stable chunk ids, persisted in
69
+ * `sync_state[key]` in the SAME readwrite transaction that reads the data
70
+ * — so a retry returns the byte-identical frozen entries (idempotent
71
+ * remote replays), and a crash before the commit freezes nothing. No
72
+ * network happens here.
73
+ *
74
+ * Pending outbox groups are read INSIDE the same transaction: their
75
+ * touched keys/stores are excluded from the snapshot so the journal (which
76
+ * ships after the bootstrap, in commit order) stays the last writer and
77
+ * nothing ships twice. A pending wipe freezes an empty run — the journal
78
+ * wipe itself still ships and decides the remote's end state.
79
+ *
80
+ * The engine deletes the frozen run (`db.delete('sync_state', key)`) only
81
+ * after the remote commit; until then every retry replays these entries.
82
+ */
83
+ captureSyncBootstrap(key: string): Promise<SyncEntry[]>;
84
+ /**
85
+ * Validate every stored pin (note blocks + session pins against
86
+ * block_revisions) in ONE consistent readonly read — the post-restore
87
+ * gate: a replayed backup with missing history fails loudly here instead
88
+ * of surfacing later as broken sessions.
89
+ */
90
+ validateSyncState(): Promise<void>;
91
+ /**
92
+ * Apply remotely committed mutations locally (pull/replay) WITHOUT
93
+ * journaling — a pulled mutation must never re-enter the outbox. Runs in
94
+ * one native transaction together with the cursor update.
95
+ *
96
+ * Unsent-edit overlay: pending outbox groups (committed local writes not
97
+ * yet acknowledged remotely) are read INSIDE the same transaction and
98
+ * replayed after the remote rows — a local edit/delete/clear/wipe that
99
+ * committed while the pull was on the network stays the visible state;
100
+ * the remote row is backed up locally, not clobbered over the user.
101
+ *
102
+ * Conflict policy (deterministic, history-preserving):
103
+ * - notes: an incoming row with a LOWER sourceRevision than the existing
104
+ * row is stale — skipped, never clobbers newer history.
105
+ * - block_revisions: same key with different contents keeps BOTH rows —
106
+ * the incoming version is re-keyed to the next free version for that
107
+ * block. Immutable history is never silently replaced.
108
+ * - memberships (system): remote mutations are skipped — identity truth
109
+ * stays local and snapshot/upload never include them.
110
+ *
111
+ * Returns committed-boundary counts of canonical consumer records (notes +
112
+ * efforts only; internal/journal rows never reach the sync toast).
113
+ * `cursorKey` keeps cursors per backup target.
114
+ */
115
+ applySyncMutations(mutations: SyncMutation[], cursorRev: number | null, cursorKey?: string): Promise<{
116
+ added: number;
117
+ updated: number;
118
+ }>;
119
+ /**
120
+ * Consistent atomic snapshot, policy-compliant (shared user policy): only
121
+ * user-scope rows of the non-system stores, read in ONE readonly
122
+ * transaction — the backup never mixes the pre/post state of a concurrent
123
+ * local commit. System stores (memberships) are excluded: they never sync.
124
+ * Use `snapshotAllRows` for the lossless (unfiltered) form.
125
+ */
126
+ snapshotRows(): Promise<{
127
+ store: StoreName;
128
+ rows: unknown[];
129
+ }[]>;
130
+ /** Lossless snapshot of every NON-SYSTEM store (meta included) in one
131
+ * readonly transaction — local export / restore emptiness checks. A
132
+ * freshly initialized database may hold a default membership, so the
133
+ * system store is excluded from both uses. */
134
+ snapshotAllRows(): Promise<{
135
+ store: StoreName;
136
+ rows: unknown[];
137
+ }[]>;
138
+ close(): Promise<void>;
139
+ }
140
+ /**
141
+ * Convert an older backup into policy-compliant current-schema mutations
142
+ * WITHOUT touching the source:
143
+ *
144
+ * 1. Lift the source data as ordered scratch ops (op-log replay in log
145
+ * order, or legacy direct-Turso per-store rows incl. segments/results).
146
+ * 2. Replay them into a unique scratch IndexedDB created AT `fromVersion`,
147
+ * then open it through IndexedDBStorage — the same versioned migration
148
+ * chain the browser runs locally rebuilds segments-era rows into
149
+ * note-owned blocks, revisions, sessions and events.
150
+ * 3. Validate strictly (all note/session pins, binary payloads, derivable
151
+ * keys — any miss fails the copy) and return policy-compliant PUTS ONLY
152
+ * (the caller applies them to a fresh, reserved target).
153
+ */
154
+ declare function convertSyncSource(db: Database, fromVersion: number, legacy: boolean): Promise<SyncMutation[]>;
155
+
156
+ export { IDB_DB_NAME, IndexedDBStorage, convertSyncSource, subscribeOutboxWrites };
@@ -0,0 +1,156 @@
1
+ import { IDBPDatabase, IDBPTransaction } from 'idb';
2
+ import { I as IStorage, S as StoreName, a as IReadOnlyStore, b as StoreType, c as IReadWriteStore, d as IStorageTransaction } from '../wire-DGYxXt3I.js';
3
+ import { U as UserScopeContext } from '../policy-CPscN81J.js';
4
+ import { S as SyncMutation, a as SyncEntry } from '../sync-Bjj_c1ea.js';
5
+ import { D as Database } from '../connection-DxuCLpRc.js';
6
+ import '@bitcobblers/wod-wiki-core';
7
+ import '@bitcobblers/wod-wiki-lang';
8
+ import '@libsql/client';
9
+
10
+ /**
11
+ * IndexedDB adapter — the browser data plane (moved verbatim from the
12
+ * playground app; the shared user policy and the schema32 sync cutover live
13
+ * here now). Local-first invariants:
14
+ *
15
+ * - Every committed local transaction group is durable in `sync_outbox`,
16
+ * written in the SAME native transaction as the data writes.
17
+ * - Snapshots (`snapshotRows`, bootstrap, conversion) are policy-compliant:
18
+ * they carry user data only, per the shared policy in `../policy`.
19
+ * - `sync_state` holds pull cursors, upload watermarks and frozen
20
+ * bootstrap runs (durable retry identities), all per backup target.
21
+ */
22
+
23
+ declare const IDB_DB_NAME = "wodwiki-db";
24
+ type Mode = 'readonly' | 'readwrite';
25
+ /** Native tx handle; write methods are guarded at runtime by `mutable`, so
26
+ * the handle is typed readwrite to keep put/delete/clear defined. */
27
+ type Tx = IDBPTransaction<unknown, string, 'readwrite'>;
28
+ declare function subscribeOutboxWrites(listener: () => void): () => void;
29
+ declare class IndexedDBStorage implements IStorage {
30
+ private readonly options;
31
+ private opening?;
32
+ constructor(options?: {
33
+ getUserId?: () => string;
34
+ /** Called just before a schema migration runs (oldVersion < DB_VERSION). */
35
+ onUpgradeStart?: (fromVersion: number) => void;
36
+ /** Database file name; the sync engine migrates scratch copies under
37
+ * their own name so a remote-schema conversion never touches the
38
+ * live database. Defaults to the app database. */
39
+ dbName?: string;
40
+ });
41
+ open(): Promise<IDBPDatabase>;
42
+ stamp(store: StoreName, value: unknown): unknown;
43
+ /** User-scope context (proven seed note ids) from the notes of `tx` —
44
+ * journal-time ownership classification reads it INSIDE the same
45
+ * transaction, so a seed import's note writes are visible to the link
46
+ * writes that follow in the same commit. Internal seam for IDBStore. */
47
+ policyContext(tx?: Tx): Promise<UserScopeContext>;
48
+ readonly<K extends StoreName>(store: K): IReadOnlyStore<StoreType<K>>;
49
+ readwrite<K extends StoreName>(store: K): IReadWriteStore<StoreType<K>>;
50
+ transaction<K extends StoreName, R>(stores: K[], mode: Mode, fn: (tx: IStorageTransaction) => Promise<R>): Promise<R>;
51
+ wipeUserData(): Promise<void>;
52
+ wipe(): Promise<void>;
53
+ /** Pending committed transaction groups, in commit order. */
54
+ readOutbox(afterSeq: number, limit: number): Promise<{
55
+ seq: number;
56
+ id: string;
57
+ at: number;
58
+ ops: SyncMutation[];
59
+ }[]>;
60
+ /** Drop acknowledged groups. Callers pass only the contiguous acked
61
+ * watermark — a failed upload never truncates pending writes. */
62
+ ackOutbox(upToSeq: number): Promise<void>;
63
+ getSyncState<T>(key: string): Promise<T | undefined>;
64
+ setSyncState(key: string, value: unknown): Promise<void>;
65
+ /**
66
+ * Freeze ONE durable bootstrap run for `key` (per backup target): a
67
+ * policy-compliant snapshot of the current rows, chunked into ≤500-op /
68
+ * ≤~1MB entries with a unique run UUID and stable chunk ids, persisted in
69
+ * `sync_state[key]` in the SAME readwrite transaction that reads the data
70
+ * — so a retry returns the byte-identical frozen entries (idempotent
71
+ * remote replays), and a crash before the commit freezes nothing. No
72
+ * network happens here.
73
+ *
74
+ * Pending outbox groups are read INSIDE the same transaction: their
75
+ * touched keys/stores are excluded from the snapshot so the journal (which
76
+ * ships after the bootstrap, in commit order) stays the last writer and
77
+ * nothing ships twice. A pending wipe freezes an empty run — the journal
78
+ * wipe itself still ships and decides the remote's end state.
79
+ *
80
+ * The engine deletes the frozen run (`db.delete('sync_state', key)`) only
81
+ * after the remote commit; until then every retry replays these entries.
82
+ */
83
+ captureSyncBootstrap(key: string): Promise<SyncEntry[]>;
84
+ /**
85
+ * Validate every stored pin (note blocks + session pins against
86
+ * block_revisions) in ONE consistent readonly read — the post-restore
87
+ * gate: a replayed backup with missing history fails loudly here instead
88
+ * of surfacing later as broken sessions.
89
+ */
90
+ validateSyncState(): Promise<void>;
91
+ /**
92
+ * Apply remotely committed mutations locally (pull/replay) WITHOUT
93
+ * journaling — a pulled mutation must never re-enter the outbox. Runs in
94
+ * one native transaction together with the cursor update.
95
+ *
96
+ * Unsent-edit overlay: pending outbox groups (committed local writes not
97
+ * yet acknowledged remotely) are read INSIDE the same transaction and
98
+ * replayed after the remote rows — a local edit/delete/clear/wipe that
99
+ * committed while the pull was on the network stays the visible state;
100
+ * the remote row is backed up locally, not clobbered over the user.
101
+ *
102
+ * Conflict policy (deterministic, history-preserving):
103
+ * - notes: an incoming row with a LOWER sourceRevision than the existing
104
+ * row is stale — skipped, never clobbers newer history.
105
+ * - block_revisions: same key with different contents keeps BOTH rows —
106
+ * the incoming version is re-keyed to the next free version for that
107
+ * block. Immutable history is never silently replaced.
108
+ * - memberships (system): remote mutations are skipped — identity truth
109
+ * stays local and snapshot/upload never include them.
110
+ *
111
+ * Returns committed-boundary counts of canonical consumer records (notes +
112
+ * efforts only; internal/journal rows never reach the sync toast).
113
+ * `cursorKey` keeps cursors per backup target.
114
+ */
115
+ applySyncMutations(mutations: SyncMutation[], cursorRev: number | null, cursorKey?: string): Promise<{
116
+ added: number;
117
+ updated: number;
118
+ }>;
119
+ /**
120
+ * Consistent atomic snapshot, policy-compliant (shared user policy): only
121
+ * user-scope rows of the non-system stores, read in ONE readonly
122
+ * transaction — the backup never mixes the pre/post state of a concurrent
123
+ * local commit. System stores (memberships) are excluded: they never sync.
124
+ * Use `snapshotAllRows` for the lossless (unfiltered) form.
125
+ */
126
+ snapshotRows(): Promise<{
127
+ store: StoreName;
128
+ rows: unknown[];
129
+ }[]>;
130
+ /** Lossless snapshot of every NON-SYSTEM store (meta included) in one
131
+ * readonly transaction — local export / restore emptiness checks. A
132
+ * freshly initialized database may hold a default membership, so the
133
+ * system store is excluded from both uses. */
134
+ snapshotAllRows(): Promise<{
135
+ store: StoreName;
136
+ rows: unknown[];
137
+ }[]>;
138
+ close(): Promise<void>;
139
+ }
140
+ /**
141
+ * Convert an older backup into policy-compliant current-schema mutations
142
+ * WITHOUT touching the source:
143
+ *
144
+ * 1. Lift the source data as ordered scratch ops (op-log replay in log
145
+ * order, or legacy direct-Turso per-store rows incl. segments/results).
146
+ * 2. Replay them into a unique scratch IndexedDB created AT `fromVersion`,
147
+ * then open it through IndexedDBStorage — the same versioned migration
148
+ * chain the browser runs locally rebuilds segments-era rows into
149
+ * note-owned blocks, revisions, sessions and events.
150
+ * 3. Validate strictly (all note/session pins, binary payloads, derivable
151
+ * keys — any miss fails the copy) and return policy-compliant PUTS ONLY
152
+ * (the caller applies them to a fresh, reserved target).
153
+ */
154
+ declare function convertSyncSource(db: Database, fromVersion: number, legacy: boolean): Promise<SyncMutation[]>;
155
+
156
+ export { IDB_DB_NAME, IndexedDBStorage, convertSyncSource, subscribeOutboxWrites };