@optimystic/db-p2p-storage-rn 1.15.0 → 1.17.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/README.md CHANGED
@@ -56,6 +56,107 @@ const libp2p = await createLibp2pNode({
56
56
  Use the Node-free `/rn` entry point of `@optimystic/db-p2p` so the Node-only
57
57
  TCP transport doesn't get bundled into the React Native app.
58
58
 
59
+ The same handle is shared by all three consumers. Close the storage and the
60
+ key-value store before the handle, and the handle last:
61
+
62
+ ```ts
63
+ await rawStorage.close(); // applies what it still holds, disarms its timers
64
+ await kvStore.close(); // commits what it still holds, disarms its timer
65
+ await db.close();
66
+ ```
67
+
68
+ Neither `close()` closes the shared handle. A write a store still holds when the
69
+ handle is closed under it is lost, and a timer it still has armed would fire on
70
+ a closed handle.
71
+
72
+ ## Persistence semantics
73
+
74
+ Everything lives in one LevelDB database (`optimystic` by default), one
75
+ ordered byte keyspace partitioned by a leading tag byte (`src/keys.ts`).
76
+ `LevelDBRawStorage` is the shared `KvRawStorage` kernel over a
77
+ `WriteGroupDriver` over a `JournalBatchBackend` over a `LevelDBJournalStore`:
78
+ the kernel owns all JSON/UTF-8 serialization, so the six block-storage stores
79
+ hold opaque kernel-encoded bytes. The keys and the logical types the kernel
80
+ decodes them back into are unchanged from earlier builds, so data written by
81
+ one reads unchanged, and a database an earlier build wrote has no journal, so
82
+ the first open over it applies nothing.
83
+
84
+ | Tag | Store | Key suffix | Decoded (logical) type |
85
+ |-----|-------|------------|------------------------|
86
+ | `0x01` | `metadata` | `blockId` | `BlockMetadata` |
87
+ | `0x02` | `revisions` | `blockId`, rev (8 bytes big-endian) | `ActionId` |
88
+ | `0x03` | `pending` | `blockId`, `actionId` | `Transform` |
89
+ | `0x04` | `transactions` | `blockId`, `actionId` | `Transform` |
90
+ | `0x05` | `materialized` | `blockId`, `actionId` | `IBlock` |
91
+ | `0x06` | `proofs` | `blockId`, rev (8 bytes big-endian) | `BlockCommitProof` |
92
+ | `0x07` | `journal` | the record's sequence (8 bytes big-endian), or nothing for the applied-through mark | a journal record as bytes, or the number of the last record applied — owned by `LevelDBJournalStore` |
93
+ | `0x10` | `kv` | the key, UTF-8 | a string (not kernel-backed) |
94
+ | `0x20` | `identity` | the key name, UTF-8 | raw protobuf bytes of the private key |
95
+
96
+ `listRevisions` and `listPendingTransactions` are range-bounded native scans,
97
+ drained before any result is yielded so a native iterator never stays open
98
+ across a consumer's `await`. `promotePendingTransaction` records two entries of
99
+ one write group — the committed row's put and the pending row's delete — applied
100
+ in that group's batch, so the move is atomic across crashes.
101
+
102
+ ## Durability
103
+
104
+ A write resolves into memory and is served from there; `flush()` hands every
105
+ write made since the last flush to the journal backend as **one group**, and
106
+ every write this package makes to LevelDB is one `WriteBatch` — the handle
107
+ surface has no single put or delete — so a group lands whole or not at all.
108
+
109
+ What a crash loses today, with `rn-leveldb` 3.11:
110
+
111
+ | Crash | Lost |
112
+ |---|---|
113
+ | the app's process | a group not yet flushed, still in memory, whole; nothing a flush has resolved for |
114
+ | a power cut or operating-system crash | any write the operating system has not yet written out, whatever the window; a later group may survive while an earlier one is lost |
115
+
116
+ The second row is the binding's: `rn-leveldb` writes every batch with LevelDB's
117
+ default options, which hand the bytes to the operating system without syncing,
118
+ and offers no way to ask for a sync. So `LevelDBRawStorage` reports
119
+ `durability: 'process-crash'`, the backend applies each group at once as one
120
+ unsynced batch and journals nothing, every `flush({ withinMs })` is treated as
121
+ strict, and the node's start-up line says such a store cannot keep the window.
122
+
123
+ The synced path is built, and it runs in this package's suite over
124
+ `classic-level`, which can sync. On a handle whose `canSync` is true the
125
+ backend journals: a relaxed `flush({ withinMs })` — what a node uses for a write
126
+ its cohort also holds, see [storage.md §How long a write may wait for its sync](../db-p2p/docs/storage.md#how-long-a-write-may-wait-for-its-sync) —
127
+ appends the group as one journal record in one unsynced batch and resolves,
128
+ and one synced batch by the earliest deadline, or a strict flush before then,
129
+ applies every record appended since the last apply, in order, sets the
130
+ applied-through mark and deletes them. Every apply carries every byte it
131
+ applies, so what a power cut leaves is a prefix of the groups: a journal record
132
+ LevelDB lost is a missing number, and recovery at the next open applies the
133
+ consecutive run before it and discards everything after it (details in
134
+ [storage.md §6, *The journal backend*](../db-p2p/docs/storage.md#6-a-write-survives-a-crash-once-a-flush-called-after-it-has-resolved)).
135
+ The step that turns it on for phones is upstream: a release of `rn-leveldb`
136
+ whose batch write accepts a sync flag (the blocked ticket
137
+ `react-native-storage-syncs-once-rn-leveldb-can`). The adapter in this package
138
+ then reports `canSync: true`, the storage reports `'power-loss'`, and nothing
139
+ above it changes.
140
+
141
+ A failed batch latches the storage: every later write, flush and
142
+ `whenDurable()` rejects with `StoreFailedError` until the app reopens the store
143
+ (rule 5 of the flush contract,
144
+ [storage.md §6](../db-p2p/docs/storage.md#6-a-write-survives-a-crash-once-a-flush-called-after-it-has-resolved)).
145
+
146
+ `LevelDBKVStore` writes through a `WriteGroup` over its keys the same way:
147
+ `set` and `delete` resolve into memory, and `flush()` commits everything since
148
+ the last flush as one batch, synced where the handle can — every flush of this
149
+ store is strict, so nothing of it is ever journaled. `whenDurable()` and
150
+ `close()` are the group's.
151
+
152
+ ## Identity
153
+
154
+ `loadOrCreateRNPeerKey(db, keyName?)` writes the libp2p private key as raw
155
+ protobuf bytes under `keyName` (default `peer-private-key`) in one batch,
156
+ synced where the handle can, so a power cut right after first start cannot give
157
+ the node a different peer id next time. LevelDB stores bytes natively, so no
158
+ base64 round-trip is needed.
159
+
59
160
  ## Debug logging
60
161
 
61
162
  `DEBUG=` does not work in React Native, and without it every `optimystic:*`
@@ -72,6 +173,15 @@ It prints one `optimystic logging on: …` confirmation line; look for that
72
173
  first. See `docs/debugging.md` § "Turning logging on" in the repository for
73
174
  the `log` option, libp2p's separate switch, and the cost on device.
74
175
 
176
+ ## Tests
177
+
178
+ `yarn test` runs the spec suite under `classic-level` in Node, so CI needs no
179
+ device. `classic-level` honours LevelDB's `sync` option, so the suite
180
+ exercises the journaled, synced path a phone will take once its binding can
181
+ sync, and checks which batches were written and whether each asked to sync;
182
+ nothing here cuts the power. The phone case is covered by presenting the same
183
+ binding as unable to sync.
184
+
75
185
  ## Related packages
76
186
 
77
187
  - **[@optimystic/db-p2p](../db-p2p)** — the distributed layer this backend
@@ -3,7 +3,9 @@ import type { LevelDBLike } from './leveldb-like.js';
3
3
  export declare const DEFAULT_PEER_KEY_NAME = "peer-private-key";
4
4
  /**
5
5
  * Loads the persisted React Native peer's libp2p private key, or generates a
6
- * fresh Ed25519 key on first call and persists it.
6
+ * fresh Ed25519 key on first call and persists it in one batch, synced where
7
+ * the handle can sync, so a power cut right after first start cannot give the
8
+ * node a different peer id next time. An existing key is read as it is.
7
9
  *
8
10
  * The key is stored as raw protobuf bytes under a dedicated identity tag —
9
11
  * LevelDB stores arbitrary byte sequences natively, so there is no base64
@@ -1 +1 @@
1
- {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/identity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAExD;;;;;;;;;;;;GAYG;AACH,wBAAsB,qBAAqB,CAC1C,EAAE,EAAE,WAAW,EACf,OAAO,GAAE,MAA8B,GACrC,OAAO,CAAC,UAAU,CAAC,CASrB"}
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/identity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAExD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,qBAAqB,CAC1C,EAAE,EAAE,WAAW,EACf,OAAO,GAAE,MAA8B,GACrC,OAAO,CAAC,UAAU,CAAC,CASrB"}
@@ -3,7 +3,9 @@ import { identityKey } from './keys.js';
3
3
  export const DEFAULT_PEER_KEY_NAME = 'peer-private-key';
4
4
  /**
5
5
  * Loads the persisted React Native peer's libp2p private key, or generates a
6
- * fresh Ed25519 key on first call and persists it.
6
+ * fresh Ed25519 key on first call and persists it in one batch, synced where
7
+ * the handle can sync, so a power cut right after first start cannot give the
8
+ * node a different peer id next time. An existing key is read as it is.
7
9
  *
8
10
  * The key is stored as raw protobuf bytes under a dedicated identity tag —
9
11
  * LevelDB stores arbitrary byte sequences natively, so there is no base64
@@ -21,7 +23,7 @@ export async function loadOrCreateRNPeerKey(db, keyName = DEFAULT_PEER_KEY_NAME)
21
23
  }
22
24
  const key = await generateKeyPair('Ed25519');
23
25
  const bytes = privateKeyToProtobuf(key);
24
- await db.put(identityKey(keyName), bytes);
26
+ await db.batch().put(identityKey(keyName), bytes).write({ sync: db.canSync });
25
27
  return key;
26
28
  }
27
29
  //# sourceMappingURL=identity.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAGpG,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,MAAM,CAAC,MAAM,qBAAqB,GAAG,kBAAkB,CAAC;AAExD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAC1C,EAAe,EACf,UAAkB,qBAAqB;IAEvC,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;IAClD,IAAI,MAAM,IAAI,MAAM,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;QACrC,OAAO,sBAAsB,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,eAAe,CAAC,SAAS,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC;IAC1C,OAAO,GAAG,CAAC;AACZ,CAAC"}
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAGpG,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,MAAM,CAAC,MAAM,qBAAqB,GAAG,kBAAkB,CAAC;AAExD;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAC1C,EAAe,EACf,UAAkB,qBAAqB;IAEvC,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;IAClD,IAAI,MAAM,IAAI,MAAM,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;QACrC,OAAO,sBAAsB,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,eAAe,CAAC,SAAS,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9E,OAAO,GAAG,CAAC;AACZ,CAAC"}
@@ -1,5 +1,5 @@
1
- export { LevelDBRawStorage, LevelDBStoreDriver } from './leveldb-storage.js';
2
- export { LevelDBKVStore } from './leveldb-kv-store.js';
1
+ export { LevelDBRawStorage, LevelDBJournalStore, type LevelDBRawStorageOptions } from './leveldb-storage.js';
2
+ export { LevelDBKVStore, type LevelDBKVStoreOptions } from './leveldb-kv-store.js';
3
3
  export { loadOrCreateRNPeerKey, DEFAULT_PEER_KEY_NAME } from './identity.js';
4
4
  export { openOptimysticRNDb, wrapRNLevelDB, DEFAULT_DB_NAME, type OpenOptimysticRNDbOptions, type RNLevelDBNative, type RNLevelDBWriteBatchNative, type RNLevelDBIteratorNative, type RNLevelDBWriteBatchCtor, type RNLevelDBOpenFn, } from './rn-opener.js';
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EACN,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,KAAK,yBAAyB,EAC9B,KAAK,eAAe,EACpB,KAAK,yBAAyB,EAC9B,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,eAAe,GACpB,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAC7G,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnF,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EACN,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,KAAK,yBAAyB,EAC9B,KAAK,eAAe,EACpB,KAAK,yBAAyB,EAC9B,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,eAAe,GACpB,MAAM,gBAAgB,CAAC"}
package/dist/src/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { LevelDBRawStorage, LevelDBStoreDriver } from './leveldb-storage.js';
1
+ export { LevelDBRawStorage, LevelDBJournalStore } from './leveldb-storage.js';
2
2
  export { LevelDBKVStore } from './leveldb-kv-store.js';
3
3
  export { loadOrCreateRNPeerKey, DEFAULT_PEER_KEY_NAME } from './identity.js';
4
4
  export { openOptimysticRNDb, wrapRNLevelDB, DEFAULT_DB_NAME, } from './rn-opener.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EACN,kBAAkB,EAClB,aAAa,EACb,eAAe,GAOf,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAiC,MAAM,sBAAsB,CAAC;AAC7G,OAAO,EAAE,cAAc,EAA8B,MAAM,uBAAuB,CAAC;AACnF,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EACN,kBAAkB,EAClB,aAAa,EACb,eAAe,GAOf,MAAM,gBAAgB,CAAC"}
@@ -16,6 +16,10 @@
16
16
  * - proofs: rev (8-byte big-endian unsigned, same as revisions)
17
17
  * - materialized: actionId UTF-8 (terminal)
18
18
  *
19
+ * The journal (`TAG_JOURNAL`) has no `blockId` envelope: a record key is the
20
+ * tag then its `seq` as 8 bytes big-endian, and the applied-through mark is the
21
+ * bare tag, which sorts before every numbered key.
22
+ *
19
23
  * `kv` and `identity` keys are flat — no `blockId` envelope — under their
20
24
  * own tag bytes (`TAG_KV`, `TAG_IDENTITY`) and use UTF-8 of the full key.
21
25
  *
@@ -29,6 +33,7 @@ export declare const TAG_PENDING = 3;
29
33
  export declare const TAG_TRANSACTIONS = 4;
30
34
  export declare const TAG_MATERIALIZED = 5;
31
35
  export declare const TAG_PROOFS = 6;
36
+ export declare const TAG_JOURNAL = 7;
32
37
  export declare const TAG_KV = 16;
33
38
  export declare const TAG_IDENTITY = 32;
34
39
  export declare function metadataKey(blockId: string): Uint8Array;
@@ -65,6 +70,23 @@ export declare function blockEnvelopeRange(tag: number, blockId: string): {
65
70
  };
66
71
  /** Decode the `actionId` suffix from a `pendingKey` / `transactionKey` / `materializedKey`. */
67
72
  export declare function actionIdFromKey(key: Uint8Array, blockId: string): string;
73
+ /** `TAG_JOURNAL || seq (8 BE)`: the key of the journal record numbered `seq`. */
74
+ export declare function journalRecordKey(seq: number): Uint8Array;
75
+ /** The bare tag: the key of the applied-through mark, which sorts before every record key. */
76
+ export declare function journalMarkKey(): Uint8Array;
77
+ /** The mark's value: the number of the last record applied, as 8 bytes big-endian. */
78
+ export declare function encodeJournalMark(appliedThrough: number): Uint8Array;
79
+ /** The number a mark value holds, or `undefined` when it is not 8 bytes. */
80
+ export declare function decodeJournalMark(value: Uint8Array): number | undefined;
81
+ /** Returns the inclusive lower / exclusive upper range covering the mark and every record key. */
82
+ export declare function journalRange(): {
83
+ gte: Uint8Array;
84
+ lt: Uint8Array;
85
+ };
86
+ /** Whether a key inside `journalRange` is the mark's (the bare tag) rather than a record's. */
87
+ export declare function isJournalMarkKey(key: Uint8Array): boolean;
88
+ /** The `seq` a record key names, or `undefined` for a key of another shape. */
89
+ export declare function seqFromJournalKey(key: Uint8Array): number | undefined;
68
90
  export declare function kvKey(key: string): Uint8Array;
69
91
  /** Returns the inclusive lower / exclusive upper range covering every kv key starting with `prefix`. */
70
92
  export declare function kvPrefixRange(prefix: string): {
@@ -1 +1 @@
1
- {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,eAAO,MAAM,YAAY,IAAO,CAAC;AACjC,eAAO,MAAM,aAAa,IAAO,CAAC;AAClC,eAAO,MAAM,WAAW,IAAO,CAAC;AAChC,eAAO,MAAM,gBAAgB,IAAO,CAAC;AACrC,eAAO,MAAM,gBAAgB,IAAO,CAAC;AACrC,eAAO,MAAM,UAAU,IAAO,CAAC;AAC/B,eAAO,MAAM,MAAM,KAAO,CAAC;AAC3B,eAAO,MAAM,YAAY,KAAO,CAAC;AA6BjC,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAEvD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,IAAI;IAAE,GAAG,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,UAAU,CAAA;CAAE,CAEnE;AAED,4EAA4E;AAC5E,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAI9D;AAeD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAEpE;AAED,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAGvD;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CAExE;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CAE5E;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAEjE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CAE7E;AAED,sGAAsG;AACtG,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,UAAU,CAAA;CAAE,CAapG;AAED,+FAA+F;AAC/F,wBAAgB,eAAe,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAIxE;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAE7C;AAED,wGAAwG;AACxG,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,UAAU,CAAA;CAAE,CAcjF;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAErD;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAEvD"}
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/keys.ts"],"names":[],"mappings":"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"}
package/dist/src/keys.js CHANGED
@@ -16,6 +16,10 @@
16
16
  * - proofs: rev (8-byte big-endian unsigned, same as revisions)
17
17
  * - materialized: actionId UTF-8 (terminal)
18
18
  *
19
+ * The journal (`TAG_JOURNAL`) has no `blockId` envelope: a record key is the
20
+ * tag then its `seq` as 8 bytes big-endian, and the applied-through mark is the
21
+ * bare tag, which sorts before every numbered key.
22
+ *
19
23
  * `kv` and `identity` keys are flat — no `blockId` envelope — under their
20
24
  * own tag bytes (`TAG_KV`, `TAG_IDENTITY`) and use UTF-8 of the full key.
21
25
  *
@@ -29,6 +33,7 @@ export const TAG_PENDING = 0x03;
29
33
  export const TAG_TRANSACTIONS = 0x04;
30
34
  export const TAG_MATERIALIZED = 0x05;
31
35
  export const TAG_PROOFS = 0x06;
36
+ export const TAG_JOURNAL = 0x07;
32
37
  export const TAG_KV = 0x10;
33
38
  export const TAG_IDENTITY = 0x20;
34
39
  const textEncoder = new TextEncoder();
@@ -135,6 +140,48 @@ export function actionIdFromKey(key, blockId) {
135
140
  const suffixOffset = 1 + 4 + blockIdLen;
136
141
  return textDecoder().decode(key.subarray(suffixOffset));
137
142
  }
143
+ // --- the journal ---
144
+ const SEQ_BYTES = 8;
145
+ /** `seq` as 8 bytes big-endian, the suffix of a record key and the value of the mark. */
146
+ function encodeSeq(seq) {
147
+ const out = new Uint8Array(SEQ_BYTES);
148
+ new DataView(out.buffer).setBigUint64(0, BigInt(seq), false);
149
+ return out;
150
+ }
151
+ /** The number 8 bytes big-endian hold, or `undefined` for any other length. */
152
+ function decodeSeq(bytes) {
153
+ if (bytes.byteLength !== SEQ_BYTES)
154
+ return undefined;
155
+ return Number(new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getBigUint64(0, false));
156
+ }
157
+ /** `TAG_JOURNAL || seq (8 BE)`: the key of the journal record numbered `seq`. */
158
+ export function journalRecordKey(seq) {
159
+ return concat(Uint8Array.of(TAG_JOURNAL), encodeSeq(seq));
160
+ }
161
+ /** The bare tag: the key of the applied-through mark, which sorts before every record key. */
162
+ export function journalMarkKey() {
163
+ return Uint8Array.of(TAG_JOURNAL);
164
+ }
165
+ /** The mark's value: the number of the last record applied, as 8 bytes big-endian. */
166
+ export function encodeJournalMark(appliedThrough) {
167
+ return encodeSeq(appliedThrough);
168
+ }
169
+ /** The number a mark value holds, or `undefined` when it is not 8 bytes. */
170
+ export function decodeJournalMark(value) {
171
+ return decodeSeq(value);
172
+ }
173
+ /** Returns the inclusive lower / exclusive upper range covering the mark and every record key. */
174
+ export function journalRange() {
175
+ return { gte: Uint8Array.of(TAG_JOURNAL), lt: Uint8Array.of(TAG_JOURNAL + 1) };
176
+ }
177
+ /** Whether a key inside `journalRange` is the mark's (the bare tag) rather than a record's. */
178
+ export function isJournalMarkKey(key) {
179
+ return key.byteLength === 1;
180
+ }
181
+ /** The `seq` a record key names, or `undefined` for a key of another shape. */
182
+ export function seqFromJournalKey(key) {
183
+ return decodeSeq(key.subarray(1));
184
+ }
138
185
  export function kvKey(key) {
139
186
  return concat(Uint8Array.of(TAG_KV), textEncoder.encode(key));
140
187
  }
@@ -1 +1 @@
1
- {"version":3,"file":"keys.js","sourceRoot":"","sources":["../../src/keys.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"keys.js","sourceRoot":"","sources":["../../src/keys.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,7 @@
1
- import type { IKVStore } from '@optimystic/db-p2p';
1
+ import { type FlushOptions, type IKVStore, type WriteGroupOptions } from '@optimystic/db-p2p';
2
2
  import { type LevelDBLike } from './leveldb-like.js';
3
+ /** The write group's options (`idleFlushMs`, `maxUnsyncedBytes`, the test-only `setTimer`). */
4
+ export type LevelDBKVStoreOptions = WriteGroupOptions;
3
5
  /**
4
6
  * LevelDB-backed `IKVStore` adapter for React Native peers.
5
7
  *
@@ -8,14 +10,34 @@ import { type LevelDBLike } from './leveldb-like.js';
8
10
  * so the three subsystems can't collide regardless of the user-chosen
9
11
  * `prefix`. `list(prefix)` is a range-bounded scan — never a full-database
10
12
  * iteration plus JS-side filter — so listing latency stays bounded.
13
+ *
14
+ * Writes go through a {@link WriteGroup} over the plain keys, as `FileKVStore` does: a `set` or
15
+ * `delete` resolves into memory, served from there to `get` and `list`, and `flush()` commits
16
+ * everything written since the last flush as ONE LevelDB batch, synced wherever the handle can
17
+ * sync (`canSync`), so the writes are on the disk before the flush resolves. Every flush the node
18
+ * makes of this store is strict, and so is the group's own idle flush, so there is no relaxed path
19
+ * to journal: a flush given a window is treated as strict, as a store that cannot defer a sync
20
+ * treats every window, and each batch carries its own data. A failed batch latches the store, after
21
+ * which every write and flush throws `StoreFailedError` (rule 5 of the flush contract on
22
+ * `RawStoreDriver.flush`); reads keep serving the overlay.
11
23
  */
12
24
  export declare class LevelDBKVStore implements IKVStore {
13
25
  private readonly db;
14
26
  private readonly prefix;
15
- constructor(db: LevelDBLike, prefix?: string);
27
+ private readonly group;
28
+ constructor(db: LevelDBLike, prefix?: string, options?: LevelDBKVStoreOptions);
16
29
  get(key: string): Promise<string | undefined>;
17
30
  set(key: string, value: string): Promise<void>;
18
31
  delete(key: string): Promise<void>;
32
+ /** Every key under `prefix`: the store's, plus those written and not yet flushed, less those deleted and not yet flushed. */
19
33
  list(prefix: string): Promise<string[]>;
34
+ /** Make every write resolved before this call durable: one batch, synced where the handle can, whatever `options.withinMs` says. The contract is `RawStoreDriver.flush`'s. */
35
+ flush(options?: FlushOptions): Promise<void>;
36
+ /** Resolves once every write covered by a flush that resolved before this call is durable; `RawStoreDriver.whenDurable`'s contract. */
37
+ whenDurable(): Promise<void>;
38
+ /** Flush what is still held and disarm the idle timer. The host closes the shared handle after this resolves. */
39
+ close(): Promise<void>;
40
+ private commit;
41
+ private listStored;
20
42
  }
21
43
  //# sourceMappingURL=leveldb-kv-store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"leveldb-kv-store.d.ts","sourceRoot":"","sources":["../../src/leveldb-kv-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAS,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAS5D;;;;;;;;GAQG;AACH,qBAAa,cAAe,YAAW,QAAQ;IAGlC,OAAO,CAAC,QAAQ,CAAC,EAAE;IAF/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEH,EAAE,EAAE,WAAW,EAAE,MAAM,GAAE,MAA0B;IAI1E,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAM7C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlC,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAK7C"}
1
+ {"version":3,"file":"leveldb-kv-store.d.ts","sourceRoot":"","sources":["../../src/leveldb-kv-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,KAAK,YAAY,EAAE,KAAK,QAAQ,EAAwB,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACrJ,OAAO,EAAS,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAG5D,+FAA+F;AAC/F,MAAM,MAAM,qBAAqB,GAAG,iBAAiB,CAAC;AAQtD;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,cAAe,YAAW,QAAQ;IAI7C,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;gBAGzB,EAAE,EAAE,WAAW,EACf,MAAM,GAAE,MAA0B,EACnD,OAAO,GAAE,qBAA0B;IAS9B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAQ7C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxC,6HAA6H;IACvH,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAY7C,8KAA8K;IAC9K,KAAK,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5C,uIAAuI;IACvI,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,iHAAiH;IACjH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAIR,MAAM;YAUN,UAAU;CAKxB"}
@@ -1,3 +1,4 @@
1
+ import { WriteGroup } from '@optimystic/db-p2p';
1
2
  import { drain } from './leveldb-like.js';
2
3
  import { kvKey, kvKeyToString, kvPrefixRange } from './keys.js';
3
4
  const textEncoder = new TextEncoder();
@@ -13,27 +14,84 @@ const textDecoder = () => textDecoderInstance ??= new TextDecoder();
13
14
  * so the three subsystems can't collide regardless of the user-chosen
14
15
  * `prefix`. `list(prefix)` is a range-bounded scan — never a full-database
15
16
  * iteration plus JS-side filter — so listing latency stays bounded.
17
+ *
18
+ * Writes go through a {@link WriteGroup} over the plain keys, as `FileKVStore` does: a `set` or
19
+ * `delete` resolves into memory, served from there to `get` and `list`, and `flush()` commits
20
+ * everything written since the last flush as ONE LevelDB batch, synced wherever the handle can
21
+ * sync (`canSync`), so the writes are on the disk before the flush resolves. Every flush the node
22
+ * makes of this store is strict, and so is the group's own idle flush, so there is no relaxed path
23
+ * to journal: a flush given a window is treated as strict, as a store that cannot defer a sync
24
+ * treats every window, and each batch carries its own data. A failed batch latches the store, after
25
+ * which every write and flush throws `StoreFailedError` (rule 5 of the flush contract on
26
+ * `RawStoreDriver.flush`); reads keep serving the overlay.
16
27
  */
17
28
  export class LevelDBKVStore {
18
29
  db;
19
30
  prefix;
20
- constructor(db, prefix = 'optimystic:txn:') {
31
+ group;
32
+ constructor(db, prefix = 'optimystic:txn:', options = {}) {
21
33
  this.db = db;
22
34
  this.prefix = prefix;
35
+ this.group = new WriteGroup(key => key, {
36
+ commit: entries => this.commit(entries),
37
+ // Every commit is as durable as this store can make it when it resolves, so nothing is ever owed a sync.
38
+ sync: () => Promise.resolve(),
39
+ }, options);
23
40
  }
24
41
  async get(key) {
42
+ const held = this.group.lookup(key);
43
+ if (held !== undefined)
44
+ return held === null ? undefined : textDecoder().decode(held);
25
45
  const bytes = await this.db.get(kvKey(this.prefix + key));
26
46
  if (!bytes)
27
47
  return undefined;
28
48
  return textDecoder().decode(bytes);
29
49
  }
30
50
  async set(key, value) {
31
- await this.db.put(kvKey(this.prefix + key), textEncoder.encode(value));
51
+ this.group.put(key, textEncoder.encode(value));
32
52
  }
33
53
  async delete(key) {
34
- await this.db.delete(kvKey(this.prefix + key));
54
+ this.group.delete(key);
35
55
  }
56
+ /** Every key under `prefix`: the store's, plus those written and not yet flushed, less those deleted and not yet flushed. */
36
57
  async list(prefix) {
58
+ // Taken before awaiting the store: a commit resolving meanwhile drops entries from the overlay
59
+ // that the listing below may have been taken without.
60
+ const held = [...this.group.entries()].filter(([key]) => key.startsWith(prefix));
61
+ const keys = new Set(await this.listStored(prefix));
62
+ for (const [key, value] of held) {
63
+ if (value === null)
64
+ keys.delete(key);
65
+ else
66
+ keys.add(key);
67
+ }
68
+ return [...keys];
69
+ }
70
+ /** Make every write resolved before this call durable: one batch, synced where the handle can, whatever `options.withinMs` says. The contract is `RawStoreDriver.flush`'s. */
71
+ flush(options) {
72
+ return this.group.flush(options);
73
+ }
74
+ /** Resolves once every write covered by a flush that resolved before this call is durable; `RawStoreDriver.whenDurable`'s contract. */
75
+ whenDurable() {
76
+ return this.group.whenDurable();
77
+ }
78
+ /** Flush what is still held and disarm the idle timer. The host closes the shared handle after this resolves. */
79
+ close() {
80
+ return this.group.close();
81
+ }
82
+ async commit(entries) {
83
+ const batch = this.db.batch();
84
+ for (const { key, value } of entries) {
85
+ const encoded = kvKey(this.prefix + key);
86
+ if (value === undefined)
87
+ batch.delete(encoded);
88
+ else
89
+ batch.put(encoded, value);
90
+ }
91
+ await batch.write({ sync: this.db.canSync });
92
+ return { durable: Promise.resolve() };
93
+ }
94
+ async listStored(prefix) {
37
95
  const range = kvPrefixRange(this.prefix + prefix);
38
96
  const entries = await drain(this.db.iterator({ gte: range.gte, lt: range.lt, keys: true }));
39
97
  return entries.map(([rawKey]) => kvKeyToString(rawKey).slice(this.prefix.length));
@@ -1 +1 @@
1
- {"version":3,"file":"leveldb-kv-store.js","sourceRoot":"","sources":["../../src/leveldb-kv-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAoB,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAEhE,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;AACtC,kGAAkG;AAClG,mGAAmG;AACnG,IAAI,mBAA4C,CAAC;AACjD,MAAM,WAAW,GAAG,GAAgB,EAAE,CAAC,mBAAmB,KAAK,IAAI,WAAW,EAAE,CAAC;AAEjF;;;;;;;;GAQG;AACH,MAAM,OAAO,cAAc;IAGG;IAFZ,MAAM,CAAS;IAEhC,YAA6B,EAAe,EAAE,SAAiB,iBAAiB;QAAnD,OAAE,GAAF,EAAE,CAAa;QAC3C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACpB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC;QAC1D,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAC7B,OAAO,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAa;QACnC,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACvB,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAc;QACxB,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;QAClD,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC5F,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACnF,CAAC;CACD"}
1
+ {"version":3,"file":"leveldb-kv-store.js","sourceRoot":"","sources":["../../src/leveldb-kv-store.ts"],"names":[],"mappings":"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"}
@@ -10,6 +10,12 @@
10
10
  * Only `openOptimysticRNDb`, the user-facing constructors, and the identity
11
11
  * helper are exported from `index.ts`; the interfaces in this file are
12
12
  * internal — consumers never see them.
13
+ *
14
+ * Every write this package makes is a `batch().write(...)`: the surface has no
15
+ * single `put` or `delete`, on purpose. A direct write lands unsynced in
16
+ * LevelDB's current log file, which LevelDB closes unsynced when it switches to
17
+ * a new one, so a later synced write does not cover it — exactly the write the
18
+ * journal under `LevelDBRawStorage` exists to keep out of the store.
13
19
  */
14
20
  /** Range options for a `LevelDBLike.iterator` scan. */
15
21
  export interface LevelDBIteratorOptions {
@@ -35,12 +41,21 @@ export interface LevelDBIteratorLike {
35
41
  /** Release any native resources held by the iterator. */
36
42
  close(): Promise<void>;
37
43
  }
44
+ /** How a batch is written. */
45
+ export interface LevelDBWriteOptions {
46
+ /**
47
+ * Write with LevelDB's `WriteOptions.sync`: the batch is on the disk before the write resolves.
48
+ * Only valid on a handle whose `canSync` is true; asking a handle that cannot is a caller bug,
49
+ * and the adapter throws rather than write unsynced and report otherwise.
50
+ */
51
+ readonly sync?: boolean;
52
+ }
38
53
  /** Atomic batch of `put` / `delete` operations against a single database. */
39
54
  export interface LevelDBWriteBatchLike {
40
55
  put(key: Uint8Array, value: Uint8Array): this;
41
56
  delete(key: Uint8Array): this;
42
- /** Commit the batch atomically. */
43
- write(): Promise<void>;
57
+ /** Commit the batch atomically; unsynced unless `options.sync`. The batch is consumed either way. */
58
+ write(options?: LevelDBWriteOptions): Promise<void>;
44
59
  }
45
60
  /**
46
61
  * Minimal LevelDB driver surface used by this package.
@@ -51,9 +66,13 @@ export interface LevelDBWriteBatchLike {
51
66
  * uniform with `classic-level`'s native async API.
52
67
  */
53
68
  export interface LevelDBLike {
69
+ /**
70
+ * Whether `batch().write({ sync: true })` is honoured. Fixed for the life of the handle; it is
71
+ * what `LevelDBRawStorage` reports its `durability` from. `rn-leveldb` cannot sync today
72
+ * (`react-native-storage-syncs-once-rn-leveldb-can`); `classic-level` can.
73
+ */
74
+ readonly canSync: boolean;
54
75
  get(key: Uint8Array): Promise<Uint8Array | undefined>;
55
- put(key: Uint8Array, value: Uint8Array): Promise<void>;
56
- delete(key: Uint8Array): Promise<void>;
57
76
  batch(): LevelDBWriteBatchLike;
58
77
  iterator(options?: LevelDBIteratorOptions): LevelDBIteratorLike;
59
78
  /** Release the underlying database handle. */
@@ -1 +1 @@
1
- {"version":3,"file":"leveldb-like.d.ts","sourceRoot":"","sources":["../../src/leveldb-like.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,uDAAuD;AACvD,MAAM,WAAW,sBAAsB;IACtC,6BAA6B;IAC7B,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,6BAA6B;IAC7B,EAAE,CAAC,EAAE,UAAU,CAAC;IAChB,6BAA6B;IAC7B,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,6BAA6B;IAC7B,EAAE,CAAC,EAAE,UAAU,CAAC;IAChB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gGAAgG;IAChG,IAAI,CAAC,EAAE,OAAO,CAAC;CACf;AAED,kFAAkF;AAClF,MAAM,WAAW,mBAAmB;IACnC,yEAAyE;IACzE,IAAI,IAAI,OAAO,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,GAAG,SAAS,CAAC,CAAC;IACtD,yDAAyD;IACzD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,6EAA6E;AAC7E,MAAM,WAAW,qBAAqB;IACrC,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9C,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,mCAAmC;IACnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC3B,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;IACtD,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,KAAK,IAAI,qBAAqB,CAAC;IAC/B,QAAQ,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,mBAAmB,CAAC;IAChE,8CAA8C;IAC9C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAsB,KAAK,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAY/F"}
1
+ {"version":3,"file":"leveldb-like.d.ts","sourceRoot":"","sources":["../../src/leveldb-like.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,uDAAuD;AACvD,MAAM,WAAW,sBAAsB;IACtC,6BAA6B;IAC7B,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,6BAA6B;IAC7B,EAAE,CAAC,EAAE,UAAU,CAAC;IAChB,6BAA6B;IAC7B,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,6BAA6B;IAC7B,EAAE,CAAC,EAAE,UAAU,CAAC;IAChB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gGAAgG;IAChG,IAAI,CAAC,EAAE,OAAO,CAAC;CACf;AAED,kFAAkF;AAClF,MAAM,WAAW,mBAAmB;IACnC,yEAAyE;IACzE,IAAI,IAAI,OAAO,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,GAAG,SAAS,CAAC,CAAC;IACtD,yDAAyD;IACzD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,8BAA8B;AAC9B,MAAM,WAAW,mBAAmB;IACnC;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,6EAA6E;AAC7E,MAAM,WAAW,qBAAqB;IACrC,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9C,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,qGAAqG;IACrG,KAAK,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpD;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;IACtD,KAAK,IAAI,qBAAqB,CAAC;IAC/B,QAAQ,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,mBAAmB,CAAC;IAChE,8CAA8C;IAC9C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAsB,KAAK,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAY/F"}
@@ -10,6 +10,12 @@
10
10
  * Only `openOptimysticRNDb`, the user-facing constructors, and the identity
11
11
  * helper are exported from `index.ts`; the interfaces in this file are
12
12
  * internal — consumers never see them.
13
+ *
14
+ * Every write this package makes is a `batch().write(...)`: the surface has no
15
+ * single `put` or `delete`, on purpose. A direct write lands unsynced in
16
+ * LevelDB's current log file, which LevelDB closes unsynced when it switches to
17
+ * a new one, so a later synced write does not cover it — exactly the write the
18
+ * journal under `LevelDBRawStorage` exists to keep out of the store.
13
19
  */
14
20
  /**
15
21
  * Drain an iterator into an array. Used by storage classes so a native
@@ -1 +1 @@
1
- {"version":3,"file":"leveldb-like.js","sourceRoot":"","sources":["../../src/leveldb-like.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAsDH;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,IAAyB;IACpD,MAAM,GAAG,GAAoC,EAAE,CAAC;IAChD,IAAI,CAAC;QACJ,OAAO,IAAI,EAAE,CAAC;YACb,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;YAChC,IAAI,CAAC,KAAK;gBAAE,MAAM;YAClB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC;IACF,CAAC;YAAS,CAAC;QACV,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC"}
1
+ {"version":3,"file":"leveldb-like.js","sourceRoot":"","sources":["../../src/leveldb-like.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAoEH;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,IAAyB;IACpD,MAAM,GAAG,GAAoC,EAAE,CAAC;IAChD,IAAI,CAAC;QACJ,OAAO,IAAI,EAAE,CAAC;YACb,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;YAChC,IAAI,CAAC,KAAK;gBAAE,MAAM;YAClB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC;IACF,CAAC;YAAS,CAAC;QACV,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC"}