cojson 0.19.21 → 0.20.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +67 -0
- package/dist/CojsonMessageChannel/CojsonMessageChannel.d.ts +42 -0
- package/dist/CojsonMessageChannel/CojsonMessageChannel.d.ts.map +1 -0
- package/dist/CojsonMessageChannel/CojsonMessageChannel.js +261 -0
- package/dist/CojsonMessageChannel/CojsonMessageChannel.js.map +1 -0
- package/dist/CojsonMessageChannel/MessagePortOutgoingChannel.d.ts +18 -0
- package/dist/CojsonMessageChannel/MessagePortOutgoingChannel.d.ts.map +1 -0
- package/dist/CojsonMessageChannel/MessagePortOutgoingChannel.js +37 -0
- package/dist/CojsonMessageChannel/MessagePortOutgoingChannel.js.map +1 -0
- package/dist/CojsonMessageChannel/index.d.ts +3 -0
- package/dist/CojsonMessageChannel/index.d.ts.map +1 -0
- package/dist/CojsonMessageChannel/index.js +2 -0
- package/dist/CojsonMessageChannel/index.js.map +1 -0
- package/dist/CojsonMessageChannel/types.d.ts +149 -0
- package/dist/CojsonMessageChannel/types.d.ts.map +1 -0
- package/dist/CojsonMessageChannel/types.js +36 -0
- package/dist/CojsonMessageChannel/types.js.map +1 -0
- package/dist/GarbageCollector.d.ts +4 -2
- package/dist/GarbageCollector.d.ts.map +1 -1
- package/dist/GarbageCollector.js +5 -3
- package/dist/GarbageCollector.js.map +1 -1
- package/dist/SyncStateManager.d.ts +3 -3
- package/dist/SyncStateManager.d.ts.map +1 -1
- package/dist/SyncStateManager.js +4 -4
- package/dist/SyncStateManager.js.map +1 -1
- package/dist/coValueContentMessage.d.ts +0 -2
- package/dist/coValueContentMessage.d.ts.map +1 -1
- package/dist/coValueContentMessage.js +0 -8
- package/dist/coValueContentMessage.js.map +1 -1
- package/dist/coValueCore/SessionMap.d.ts +4 -2
- package/dist/coValueCore/SessionMap.d.ts.map +1 -1
- package/dist/coValueCore/SessionMap.js +30 -0
- package/dist/coValueCore/SessionMap.js.map +1 -1
- package/dist/coValueCore/coValueCore.d.ts +86 -4
- package/dist/coValueCore/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore/coValueCore.js +318 -17
- package/dist/coValueCore/coValueCore.js.map +1 -1
- package/dist/coValueCore/verifiedState.d.ts +6 -1
- package/dist/coValueCore/verifiedState.d.ts.map +1 -1
- package/dist/coValueCore/verifiedState.js +9 -0
- package/dist/coValueCore/verifiedState.js.map +1 -1
- package/dist/coValues/coList.d.ts +3 -2
- package/dist/coValues/coList.d.ts.map +1 -1
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/group.d.ts.map +1 -1
- package/dist/coValues/group.js +3 -6
- package/dist/coValues/group.js.map +1 -1
- package/dist/config.d.ts +0 -6
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -8
- package/dist/config.js.map +1 -1
- package/dist/crypto/NapiCrypto.d.ts +1 -2
- package/dist/crypto/NapiCrypto.d.ts.map +1 -1
- package/dist/crypto/NapiCrypto.js +19 -4
- package/dist/crypto/NapiCrypto.js.map +1 -1
- package/dist/crypto/RNCrypto.d.ts.map +1 -1
- package/dist/crypto/RNCrypto.js +19 -4
- package/dist/crypto/RNCrypto.js.map +1 -1
- package/dist/crypto/WasmCrypto.d.ts +11 -4
- package/dist/crypto/WasmCrypto.d.ts.map +1 -1
- package/dist/crypto/WasmCrypto.js +52 -10
- package/dist/crypto/WasmCrypto.js.map +1 -1
- package/dist/crypto/WasmCryptoEdge.d.ts +1 -0
- package/dist/crypto/WasmCryptoEdge.d.ts.map +1 -1
- package/dist/crypto/WasmCryptoEdge.js +4 -1
- package/dist/crypto/WasmCryptoEdge.js.map +1 -1
- package/dist/crypto/crypto.d.ts +3 -3
- package/dist/crypto/crypto.d.ts.map +1 -1
- package/dist/crypto/crypto.js +6 -1
- package/dist/crypto/crypto.js.map +1 -1
- package/dist/exports.d.ts +3 -2
- package/dist/exports.d.ts.map +1 -1
- package/dist/exports.js +3 -1
- package/dist/exports.js.map +1 -1
- package/dist/ids.d.ts +4 -1
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +4 -0
- package/dist/ids.js.map +1 -1
- package/dist/knownState.d.ts +2 -0
- package/dist/knownState.d.ts.map +1 -1
- package/dist/localNode.d.ts +13 -3
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +17 -2
- package/dist/localNode.js.map +1 -1
- package/dist/platformUtils.d.ts +3 -0
- package/dist/platformUtils.d.ts.map +1 -0
- package/dist/platformUtils.js +24 -0
- package/dist/platformUtils.js.map +1 -0
- package/dist/storage/DeletedCoValuesEraserScheduler.d.ts +30 -0
- package/dist/storage/DeletedCoValuesEraserScheduler.d.ts.map +1 -0
- package/dist/storage/DeletedCoValuesEraserScheduler.js +84 -0
- package/dist/storage/DeletedCoValuesEraserScheduler.js.map +1 -0
- package/dist/storage/sqlite/client.d.ts +3 -0
- package/dist/storage/sqlite/client.d.ts.map +1 -1
- package/dist/storage/sqlite/client.js +44 -0
- package/dist/storage/sqlite/client.js.map +1 -1
- package/dist/storage/sqlite/sqliteMigrations.d.ts.map +1 -1
- package/dist/storage/sqlite/sqliteMigrations.js +7 -0
- package/dist/storage/sqlite/sqliteMigrations.js.map +1 -1
- package/dist/storage/sqliteAsync/client.d.ts +3 -0
- package/dist/storage/sqliteAsync/client.d.ts.map +1 -1
- package/dist/storage/sqliteAsync/client.js +42 -0
- package/dist/storage/sqliteAsync/client.js.map +1 -1
- package/dist/storage/storageAsync.d.ts +15 -3
- package/dist/storage/storageAsync.d.ts.map +1 -1
- package/dist/storage/storageAsync.js +60 -3
- package/dist/storage/storageAsync.js.map +1 -1
- package/dist/storage/storageSync.d.ts +14 -3
- package/dist/storage/storageSync.d.ts.map +1 -1
- package/dist/storage/storageSync.js +54 -3
- package/dist/storage/storageSync.js.map +1 -1
- package/dist/storage/types.d.ts +64 -0
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/types.js +12 -1
- package/dist/storage/types.js.map +1 -1
- package/dist/sync.d.ts +6 -0
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +69 -15
- package/dist/sync.js.map +1 -1
- package/dist/tests/CojsonMessageChannel.test.d.ts +2 -0
- package/dist/tests/CojsonMessageChannel.test.d.ts.map +1 -0
- package/dist/tests/CojsonMessageChannel.test.js +236 -0
- package/dist/tests/CojsonMessageChannel.test.js.map +1 -0
- package/dist/tests/DeletedCoValuesEraserScheduler.test.d.ts +2 -0
- package/dist/tests/DeletedCoValuesEraserScheduler.test.d.ts.map +1 -0
- package/dist/tests/DeletedCoValuesEraserScheduler.test.js +149 -0
- package/dist/tests/DeletedCoValuesEraserScheduler.test.js.map +1 -0
- package/dist/tests/GarbageCollector.test.js +91 -18
- package/dist/tests/GarbageCollector.test.js.map +1 -1
- package/dist/tests/StorageApiAsync.test.js +510 -146
- package/dist/tests/StorageApiAsync.test.js.map +1 -1
- package/dist/tests/StorageApiSync.test.js +531 -130
- package/dist/tests/StorageApiSync.test.js.map +1 -1
- package/dist/tests/SyncManager.processQueues.test.js +1 -1
- package/dist/tests/SyncManager.processQueues.test.js.map +1 -1
- package/dist/tests/SyncStateManager.test.js +1 -1
- package/dist/tests/SyncStateManager.test.js.map +1 -1
- package/dist/tests/WasmCrypto.test.js +6 -3
- package/dist/tests/WasmCrypto.test.js.map +1 -1
- package/dist/tests/coPlainText.test.js +1 -1
- package/dist/tests/coPlainText.test.js.map +1 -1
- package/dist/tests/coValueCore.loadFromStorage.test.js +4 -0
- package/dist/tests/coValueCore.loadFromStorage.test.js.map +1 -1
- package/dist/tests/coValueCore.test.js +34 -13
- package/dist/tests/coValueCore.test.js.map +1 -1
- package/dist/tests/coreWasm.test.js +127 -4
- package/dist/tests/coreWasm.test.js.map +1 -1
- package/dist/tests/crypto.test.js +89 -93
- package/dist/tests/crypto.test.js.map +1 -1
- package/dist/tests/deleteCoValue.test.d.ts +2 -0
- package/dist/tests/deleteCoValue.test.d.ts.map +1 -0
- package/dist/tests/deleteCoValue.test.js +313 -0
- package/dist/tests/deleteCoValue.test.js.map +1 -0
- package/dist/tests/group.removeMember.test.js +18 -30
- package/dist/tests/group.removeMember.test.js.map +1 -1
- package/dist/tests/knownState.lazyLoading.test.js +4 -0
- package/dist/tests/knownState.lazyLoading.test.js.map +1 -1
- package/dist/tests/sync.deleted.test.d.ts +2 -0
- package/dist/tests/sync.deleted.test.d.ts.map +1 -0
- package/dist/tests/sync.deleted.test.js +214 -0
- package/dist/tests/sync.deleted.test.js.map +1 -0
- package/dist/tests/sync.garbageCollection.test.js +56 -32
- package/dist/tests/sync.garbageCollection.test.js.map +1 -1
- package/dist/tests/sync.load.test.js +3 -5
- package/dist/tests/sync.load.test.js.map +1 -1
- package/dist/tests/sync.mesh.test.js +4 -3
- package/dist/tests/sync.mesh.test.js.map +1 -1
- package/dist/tests/sync.peerReconciliation.test.js +3 -3
- package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
- package/dist/tests/sync.storage.test.js +12 -11
- package/dist/tests/sync.storage.test.js.map +1 -1
- package/dist/tests/sync.storageAsync.test.js +7 -7
- package/dist/tests/sync.storageAsync.test.js.map +1 -1
- package/dist/tests/sync.test.js +3 -2
- package/dist/tests/sync.test.js.map +1 -1
- package/dist/tests/sync.tracking.test.js +35 -4
- package/dist/tests/sync.tracking.test.js.map +1 -1
- package/dist/tests/testStorage.d.ts +3 -0
- package/dist/tests/testStorage.d.ts.map +1 -1
- package/dist/tests/testStorage.js +16 -2
- package/dist/tests/testStorage.js.map +1 -1
- package/dist/tests/testUtils.d.ts +29 -4
- package/dist/tests/testUtils.d.ts.map +1 -1
- package/dist/tests/testUtils.js +84 -9
- package/dist/tests/testUtils.js.map +1 -1
- package/package.json +6 -16
- package/src/CojsonMessageChannel/CojsonMessageChannel.ts +332 -0
- package/src/CojsonMessageChannel/MessagePortOutgoingChannel.ts +52 -0
- package/src/CojsonMessageChannel/index.ts +9 -0
- package/src/CojsonMessageChannel/types.ts +200 -0
- package/src/GarbageCollector.ts +5 -5
- package/src/SyncStateManager.ts +6 -6
- package/src/coValueContentMessage.ts +0 -14
- package/src/coValueCore/SessionMap.ts +43 -1
- package/src/coValueCore/coValueCore.ts +430 -15
- package/src/coValueCore/verifiedState.ts +26 -3
- package/src/coValues/coList.ts +5 -3
- package/src/coValues/group.ts +5 -6
- package/src/config.ts +0 -9
- package/src/crypto/NapiCrypto.ts +29 -13
- package/src/crypto/RNCrypto.ts +29 -11
- package/src/crypto/WasmCrypto.ts +67 -20
- package/src/crypto/WasmCryptoEdge.ts +5 -1
- package/src/crypto/crypto.ts +16 -4
- package/src/exports.ts +3 -0
- package/src/ids.ts +11 -1
- package/src/localNode.ts +18 -5
- package/src/platformUtils.ts +26 -0
- package/src/storage/DeletedCoValuesEraserScheduler.ts +124 -0
- package/src/storage/sqlite/client.ts +77 -0
- package/src/storage/sqlite/sqliteMigrations.ts +7 -0
- package/src/storage/sqliteAsync/client.ts +75 -0
- package/src/storage/storageAsync.ts +77 -4
- package/src/storage/storageSync.ts +73 -4
- package/src/storage/types.ts +75 -0
- package/src/sync.ts +84 -15
- package/src/tests/CojsonMessageChannel.test.ts +306 -0
- package/src/tests/DeletedCoValuesEraserScheduler.test.ts +185 -0
- package/src/tests/GarbageCollector.test.ts +119 -22
- package/src/tests/StorageApiAsync.test.ts +615 -156
- package/src/tests/StorageApiSync.test.ts +623 -137
- package/src/tests/SyncManager.processQueues.test.ts +1 -1
- package/src/tests/SyncStateManager.test.ts +1 -1
- package/src/tests/WasmCrypto.test.ts +8 -3
- package/src/tests/coPlainText.test.ts +1 -1
- package/src/tests/coValueCore.loadFromStorage.test.ts +8 -0
- package/src/tests/coValueCore.test.ts +49 -14
- package/src/tests/coreWasm.test.ts +319 -10
- package/src/tests/crypto.test.ts +141 -150
- package/src/tests/deleteCoValue.test.ts +528 -0
- package/src/tests/group.removeMember.test.ts +35 -35
- package/src/tests/knownState.lazyLoading.test.ts +8 -0
- package/src/tests/sync.deleted.test.ts +294 -0
- package/src/tests/sync.garbageCollection.test.ts +69 -36
- package/src/tests/sync.load.test.ts +3 -5
- package/src/tests/sync.mesh.test.ts +6 -3
- package/src/tests/sync.peerReconciliation.test.ts +3 -3
- package/src/tests/sync.storage.test.ts +14 -11
- package/src/tests/sync.storageAsync.test.ts +7 -7
- package/src/tests/sync.test.ts +5 -2
- package/src/tests/sync.tracking.test.ts +54 -4
- package/src/tests/testStorage.ts +30 -3
- package/src/tests/testUtils.ts +113 -15
- package/dist/crypto/PureJSCrypto.d.ts +0 -77
- package/dist/crypto/PureJSCrypto.d.ts.map +0 -1
- package/dist/crypto/PureJSCrypto.js +0 -236
- package/dist/crypto/PureJSCrypto.js.map +0 -1
- package/dist/tests/PureJSCrypto.test.d.ts +0 -2
- package/dist/tests/PureJSCrypto.test.d.ts.map +0 -1
- package/dist/tests/PureJSCrypto.test.js +0 -145
- package/dist/tests/PureJSCrypto.test.js.map +0 -1
- package/src/crypto/PureJSCrypto.ts +0 -429
- package/src/tests/PureJSCrypto.test.ts +0 -217
|
@@ -16,6 +16,7 @@ import type {
|
|
|
16
16
|
StoredSessionRow,
|
|
17
17
|
TransactionRow,
|
|
18
18
|
} from "../types.js";
|
|
19
|
+
import { DeletedCoValueDeletionStatus } from "../types.js";
|
|
19
20
|
import type { SQLiteDatabaseDriver } from "./types.js";
|
|
20
21
|
|
|
21
22
|
export type RawCoValueRow = {
|
|
@@ -29,6 +30,10 @@ export type RawTransactionRow = {
|
|
|
29
30
|
tx: string;
|
|
30
31
|
};
|
|
31
32
|
|
|
33
|
+
type DeletedCoValueQueueRow = {
|
|
34
|
+
id: RawCoID;
|
|
35
|
+
};
|
|
36
|
+
|
|
32
37
|
export function getErrorMessage(error: unknown) {
|
|
33
38
|
return error instanceof Error ? error.message : "Unknown error";
|
|
34
39
|
}
|
|
@@ -143,6 +148,78 @@ export class SQLiteClient
|
|
|
143
148
|
return result.rowID;
|
|
144
149
|
}
|
|
145
150
|
|
|
151
|
+
markCoValueAsDeleted(id: RawCoID) {
|
|
152
|
+
// Work queue entry. Table only stores the coValueID.
|
|
153
|
+
// Idempotent by design.
|
|
154
|
+
this.db.run(
|
|
155
|
+
`INSERT INTO deletedCoValues (coValueID) VALUES (?) ON CONFLICT(coValueID) DO NOTHING`,
|
|
156
|
+
[id],
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
eraseCoValueButKeepTombstone(coValueId: RawCoID) {
|
|
161
|
+
const coValueRow = this.db.get<{ rowID: number }>(
|
|
162
|
+
"SELECT rowID FROM coValues WHERE id = ?",
|
|
163
|
+
[coValueId],
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
if (!coValueRow) {
|
|
167
|
+
logger.warn(`CoValue ${coValueId} not found, skipping deletion`);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
this.transaction(() => {
|
|
172
|
+
this.db.run(
|
|
173
|
+
`DELETE FROM transactions
|
|
174
|
+
WHERE ses IN (
|
|
175
|
+
SELECT rowID FROM sessions
|
|
176
|
+
WHERE coValue = ?
|
|
177
|
+
AND sessionID NOT LIKE '%$'
|
|
178
|
+
)`,
|
|
179
|
+
[coValueRow.rowID],
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
this.db.run(
|
|
183
|
+
`DELETE FROM signatureAfter
|
|
184
|
+
WHERE ses IN (
|
|
185
|
+
SELECT rowID FROM sessions
|
|
186
|
+
WHERE coValue = ?
|
|
187
|
+
AND sessionID NOT LIKE '%$'
|
|
188
|
+
)`,
|
|
189
|
+
[coValueRow.rowID],
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
this.db.run(
|
|
193
|
+
`DELETE FROM sessions
|
|
194
|
+
WHERE coValue = ?
|
|
195
|
+
AND sessionID NOT LIKE '%$'`,
|
|
196
|
+
[coValueRow.rowID],
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
// Mark the delete as done
|
|
200
|
+
this.db.run(
|
|
201
|
+
`INSERT INTO deletedCoValues (coValueID, status) VALUES (?, ?)
|
|
202
|
+
ON CONFLICT(coValueID) DO UPDATE SET status=?`,
|
|
203
|
+
[
|
|
204
|
+
coValueId,
|
|
205
|
+
DeletedCoValueDeletionStatus.Done,
|
|
206
|
+
DeletedCoValueDeletionStatus.Done,
|
|
207
|
+
],
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
getAllCoValuesWaitingForDelete(): RawCoID[] {
|
|
213
|
+
return this.db
|
|
214
|
+
.query<DeletedCoValueQueueRow>(
|
|
215
|
+
`SELECT coValueID as id
|
|
216
|
+
FROM deletedCoValues
|
|
217
|
+
WHERE status = ?`,
|
|
218
|
+
[DeletedCoValueDeletionStatus.Pending],
|
|
219
|
+
)
|
|
220
|
+
.map((r) => r.id);
|
|
221
|
+
}
|
|
222
|
+
|
|
146
223
|
addSessionUpdate({ sessionUpdate }: { sessionUpdate: SessionRow }): number {
|
|
147
224
|
const result = this.db.get<{ rowID: number }>(
|
|
148
225
|
`INSERT INTO sessions (coValue, sessionID, lastIdx, lastSignature, bytesSinceLastSignature) VALUES (?, ?, ?, ?, ?)
|
|
@@ -40,6 +40,13 @@ export const migrations: Record<number, string[]> = {
|
|
|
40
40
|
);`,
|
|
41
41
|
"CREATE INDEX IF NOT EXISTS idx_unsynced_covalues_co_value_id ON unsynced_covalues(co_value_id);",
|
|
42
42
|
],
|
|
43
|
+
5: [
|
|
44
|
+
`CREATE TABLE IF NOT EXISTS deletedCoValues (
|
|
45
|
+
coValueID TEXT PRIMARY KEY,
|
|
46
|
+
status INTEGER NOT NULL DEFAULT 0
|
|
47
|
+
) WITHOUT ROWID;`,
|
|
48
|
+
"CREATE INDEX IF NOT EXISTS deletedCoValuesByStatus ON deletedCoValues (status);",
|
|
49
|
+
],
|
|
43
50
|
};
|
|
44
51
|
|
|
45
52
|
type Migration = {
|
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
StoredSessionRow,
|
|
16
16
|
TransactionRow,
|
|
17
17
|
} from "../types.js";
|
|
18
|
+
import { DeletedCoValueDeletionStatus } from "../types.js";
|
|
18
19
|
import type { SQLiteDatabaseDriverAsync } from "./types.js";
|
|
19
20
|
import type { PeerID } from "../../sync.js";
|
|
20
21
|
|
|
@@ -29,6 +30,10 @@ export type RawTransactionRow = {
|
|
|
29
30
|
tx: string;
|
|
30
31
|
};
|
|
31
32
|
|
|
33
|
+
type DeletedCoValueQueueRow = {
|
|
34
|
+
id: RawCoID;
|
|
35
|
+
};
|
|
36
|
+
|
|
32
37
|
export function getErrorMessage(error: unknown) {
|
|
33
38
|
return error instanceof Error ? error.message : "Unknown error";
|
|
34
39
|
}
|
|
@@ -146,6 +151,76 @@ export class SQLiteClientAsync
|
|
|
146
151
|
return result.rowID;
|
|
147
152
|
}
|
|
148
153
|
|
|
154
|
+
async markCoValueAsDeleted(id: RawCoID) {
|
|
155
|
+
// Work queue entry. Table only stores the coValueID.
|
|
156
|
+
// Idempotent by design.
|
|
157
|
+
await this.db.run(
|
|
158
|
+
`INSERT INTO deletedCoValues (coValueID) VALUES (?) ON CONFLICT(coValueID) DO NOTHING`,
|
|
159
|
+
[id],
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async eraseCoValueButKeepTombstone(coValueId: RawCoID) {
|
|
164
|
+
const coValueRow = await this.db.get<RawCoValueRow & { rowID: number }>(
|
|
165
|
+
"SELECT * FROM coValues WHERE id = ?",
|
|
166
|
+
[coValueId],
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
if (!coValueRow) {
|
|
170
|
+
logger.warn(`CoValue ${coValueId} not found, skipping deletion`);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
await this.transaction(async () => {
|
|
175
|
+
await this.db.run(
|
|
176
|
+
`DELETE FROM transactions
|
|
177
|
+
WHERE ses IN (
|
|
178
|
+
SELECT rowID FROM sessions
|
|
179
|
+
WHERE coValue = ?
|
|
180
|
+
AND sessionID NOT LIKE '%$'
|
|
181
|
+
)`,
|
|
182
|
+
[coValueRow.rowID],
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
await this.db.run(
|
|
186
|
+
`DELETE FROM signatureAfter
|
|
187
|
+
WHERE ses IN (
|
|
188
|
+
SELECT rowID FROM sessions
|
|
189
|
+
WHERE coValue = ?
|
|
190
|
+
AND sessionID NOT LIKE '%$'
|
|
191
|
+
)`,
|
|
192
|
+
[coValueRow.rowID],
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
await this.db.run(
|
|
196
|
+
`DELETE FROM sessions
|
|
197
|
+
WHERE coValue = ?
|
|
198
|
+
AND sessionID NOT LIKE '%$'`,
|
|
199
|
+
[coValueRow.rowID],
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
await this.db.run(
|
|
203
|
+
`INSERT INTO deletedCoValues (coValueID, status) VALUES (?, ?)
|
|
204
|
+
ON CONFLICT(coValueID) DO UPDATE SET status=?`,
|
|
205
|
+
[
|
|
206
|
+
coValueId,
|
|
207
|
+
DeletedCoValueDeletionStatus.Done,
|
|
208
|
+
DeletedCoValueDeletionStatus.Done,
|
|
209
|
+
],
|
|
210
|
+
);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async getAllCoValuesWaitingForDelete(): Promise<RawCoID[]> {
|
|
215
|
+
const rows = await this.db.query<DeletedCoValueQueueRow>(
|
|
216
|
+
`SELECT coValueID as id
|
|
217
|
+
FROM deletedCoValues
|
|
218
|
+
WHERE status = ?`,
|
|
219
|
+
[DeletedCoValueDeletionStatus.Pending],
|
|
220
|
+
);
|
|
221
|
+
return rows.map((r) => r.id);
|
|
222
|
+
}
|
|
223
|
+
|
|
149
224
|
async addSessionUpdate({
|
|
150
225
|
sessionUpdate,
|
|
151
226
|
}: {
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
setSessionCounter,
|
|
18
18
|
} from "../knownState.js";
|
|
19
19
|
import { StorageKnownState } from "./knownState.js";
|
|
20
|
+
import { DeletedCoValuesEraserScheduler } from "./DeletedCoValuesEraserScheduler.js";
|
|
20
21
|
import {
|
|
21
22
|
collectNewTxs,
|
|
22
23
|
getDependedOnCoValues,
|
|
@@ -30,11 +31,20 @@ import type {
|
|
|
30
31
|
StoredCoValueRow,
|
|
31
32
|
StoredSessionRow,
|
|
32
33
|
} from "./types.js";
|
|
34
|
+
import { isDeleteSessionID } from "../ids.js";
|
|
33
35
|
|
|
34
36
|
export class StorageApiAsync implements StorageAPI {
|
|
35
37
|
private readonly dbClient: DBClientInterfaceAsync;
|
|
36
38
|
|
|
37
|
-
private
|
|
39
|
+
private deletedCoValuesEraserScheduler:
|
|
40
|
+
| DeletedCoValuesEraserScheduler
|
|
41
|
+
| undefined;
|
|
42
|
+
private eraserController: AbortController | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Keeps track of CoValues that are in memory, to avoid reloading them from storage
|
|
45
|
+
* when it isn't necessary
|
|
46
|
+
*/
|
|
47
|
+
private inMemoryCoValues = new Set<RawCoID>();
|
|
38
48
|
|
|
39
49
|
// Track pending loads to deduplicate concurrent requests
|
|
40
50
|
private pendingKnownStateLoads = new Map<
|
|
@@ -110,6 +120,7 @@ export class StorageApiAsync implements StorageAPI {
|
|
|
110
120
|
callback: (data: NewContentMessage) => void,
|
|
111
121
|
done: (found: boolean) => void,
|
|
112
122
|
) {
|
|
123
|
+
this.interruptEraser("load");
|
|
113
124
|
const coValueRow = await this.dbClient.getCoValue(id);
|
|
114
125
|
|
|
115
126
|
if (!coValueRow) {
|
|
@@ -153,7 +164,7 @@ export class StorageApiAsync implements StorageAPI {
|
|
|
153
164
|
);
|
|
154
165
|
}
|
|
155
166
|
|
|
156
|
-
this.
|
|
167
|
+
this.inMemoryCoValues.add(coValueRow.id);
|
|
157
168
|
|
|
158
169
|
let contentMessage = createContentMessage(coValueRow.id, coValueRow.header);
|
|
159
170
|
|
|
@@ -224,7 +235,7 @@ export class StorageApiAsync implements StorageAPI {
|
|
|
224
235
|
done?.(true);
|
|
225
236
|
}
|
|
226
237
|
|
|
227
|
-
async pushContentWithDependencies(
|
|
238
|
+
private async pushContentWithDependencies(
|
|
228
239
|
coValueRow: StoredCoValueRow,
|
|
229
240
|
contentMessage: NewContentMessage,
|
|
230
241
|
pushCallback: (data: NewContentMessage) => void,
|
|
@@ -237,7 +248,7 @@ export class StorageApiAsync implements StorageAPI {
|
|
|
237
248
|
const promises = [];
|
|
238
249
|
|
|
239
250
|
for (const dependedOnCoValue of dependedOnCoValuesList) {
|
|
240
|
-
if (this.
|
|
251
|
+
if (this.inMemoryCoValues.has(dependedOnCoValue)) {
|
|
241
252
|
continue;
|
|
242
253
|
}
|
|
243
254
|
|
|
@@ -263,10 +274,35 @@ export class StorageApiAsync implements StorageAPI {
|
|
|
263
274
|
this.storeQueue.push(msg, correctionCallback);
|
|
264
275
|
|
|
265
276
|
this.storeQueue.processQueue(async (data, correctionCallback) => {
|
|
277
|
+
this.interruptEraser("store");
|
|
266
278
|
return this.storeSingle(data, correctionCallback);
|
|
267
279
|
});
|
|
268
280
|
}
|
|
269
281
|
|
|
282
|
+
private interruptEraser(reason: string) {
|
|
283
|
+
// Cooperative cancellation: a DB transaction already in progress will complete,
|
|
284
|
+
// but the eraser loop will stop starting further work at its next abort check.
|
|
285
|
+
if (this.eraserController) {
|
|
286
|
+
this.eraserController.abort(reason);
|
|
287
|
+
this.eraserController = undefined;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async eraseAllDeletedCoValues() {
|
|
292
|
+
const ids = await this.dbClient.getAllCoValuesWaitingForDelete();
|
|
293
|
+
|
|
294
|
+
this.eraserController = new AbortController();
|
|
295
|
+
const signal = this.eraserController.signal;
|
|
296
|
+
|
|
297
|
+
for (const id of ids) {
|
|
298
|
+
if (signal.aborted) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
await this.dbClient.eraseCoValueButKeepTombstone(id);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
270
306
|
/**
|
|
271
307
|
* This function is called when the storage lacks the information required to store the incoming content.
|
|
272
308
|
*
|
|
@@ -309,6 +345,7 @@ export class StorageApiAsync implements StorageAPI {
|
|
|
309
345
|
msg: NewContentMessage,
|
|
310
346
|
correctionCallback: CorrectionCallback,
|
|
311
347
|
): Promise<boolean> {
|
|
348
|
+
this.interruptEraser("store");
|
|
312
349
|
if (this.storeQueue.closed) {
|
|
313
350
|
return false;
|
|
314
351
|
}
|
|
@@ -338,6 +375,10 @@ export class StorageApiAsync implements StorageAPI {
|
|
|
338
375
|
sessionID,
|
|
339
376
|
);
|
|
340
377
|
|
|
378
|
+
if (this.deletedValues.has(id) && isDeleteSessionID(sessionID)) {
|
|
379
|
+
await tx.markCoValueAsDeleted(id);
|
|
380
|
+
}
|
|
381
|
+
|
|
341
382
|
if (sessionRow) {
|
|
342
383
|
setSessionCounter(
|
|
343
384
|
knownState.sessions,
|
|
@@ -364,6 +405,8 @@ export class StorageApiAsync implements StorageAPI {
|
|
|
364
405
|
});
|
|
365
406
|
}
|
|
366
407
|
|
|
408
|
+
this.inMemoryCoValues.add(id);
|
|
409
|
+
|
|
367
410
|
this.knownStates.handleUpdate(id, knownState);
|
|
368
411
|
|
|
369
412
|
if (invalidAssumptions) {
|
|
@@ -439,6 +482,30 @@ export class StorageApiAsync implements StorageAPI {
|
|
|
439
482
|
return newLastIdx;
|
|
440
483
|
}
|
|
441
484
|
|
|
485
|
+
deletedValues = new Set<RawCoID>();
|
|
486
|
+
|
|
487
|
+
markDeleteAsValid(id: RawCoID) {
|
|
488
|
+
this.deletedValues.add(id);
|
|
489
|
+
|
|
490
|
+
if (this.deletedCoValuesEraserScheduler) {
|
|
491
|
+
this.deletedCoValuesEraserScheduler.onEnqueueDeletedCoValue();
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
enableDeletedCoValuesErasure() {
|
|
496
|
+
if (this.deletedCoValuesEraserScheduler) return;
|
|
497
|
+
|
|
498
|
+
this.deletedCoValuesEraserScheduler = new DeletedCoValuesEraserScheduler({
|
|
499
|
+
run: async () => {
|
|
500
|
+
// Async storage: no max-time budgeting; drain to completion when scheduled.
|
|
501
|
+
await this.eraseAllDeletedCoValues();
|
|
502
|
+
const remaining = await this.dbClient.getAllCoValuesWaitingForDelete();
|
|
503
|
+
return { hasMore: remaining.length > 0 };
|
|
504
|
+
},
|
|
505
|
+
});
|
|
506
|
+
this.deletedCoValuesEraserScheduler.scheduleStartupDrain();
|
|
507
|
+
}
|
|
508
|
+
|
|
442
509
|
waitForSync(id: string, coValue: CoValueCore) {
|
|
443
510
|
return this.knownStates.waitForSync(id, coValue);
|
|
444
511
|
}
|
|
@@ -460,7 +527,13 @@ export class StorageApiAsync implements StorageAPI {
|
|
|
460
527
|
this.dbClient.stopTrackingSyncState(id);
|
|
461
528
|
}
|
|
462
529
|
|
|
530
|
+
onCoValueUnmounted(id: RawCoID): void {
|
|
531
|
+
this.inMemoryCoValues.delete(id);
|
|
532
|
+
}
|
|
533
|
+
|
|
463
534
|
close() {
|
|
535
|
+
this.deletedCoValuesEraserScheduler?.dispose();
|
|
536
|
+
this.inMemoryCoValues.clear();
|
|
464
537
|
return this.storeQueue.close();
|
|
465
538
|
}
|
|
466
539
|
}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
emptyKnownState,
|
|
17
17
|
setSessionCounter,
|
|
18
18
|
} from "../knownState.js";
|
|
19
|
+
import { isDeleteSessionID } from "../ids.js";
|
|
19
20
|
import {
|
|
20
21
|
collectNewTxs,
|
|
21
22
|
getDependedOnCoValues,
|
|
@@ -29,15 +30,26 @@ import type {
|
|
|
29
30
|
StoredCoValueRow,
|
|
30
31
|
StoredSessionRow,
|
|
31
32
|
} from "./types.js";
|
|
33
|
+
import { DeletedCoValuesEraserScheduler } from "./DeletedCoValuesEraserScheduler.js";
|
|
32
34
|
import {
|
|
33
35
|
ContentCallback,
|
|
34
36
|
StorageStreamingQueue,
|
|
35
37
|
} from "../queue/StorageStreamingQueue.js";
|
|
36
38
|
import { getPriorityFromHeader } from "../priority.js";
|
|
37
39
|
|
|
40
|
+
const MAX_DELETE_SCHEDULE_DURATION_MS = 100;
|
|
41
|
+
|
|
38
42
|
export class StorageApiSync implements StorageAPI {
|
|
39
43
|
private readonly dbClient: DBClientInterfaceSync;
|
|
40
|
-
|
|
44
|
+
|
|
45
|
+
private deletedCoValuesEraserScheduler:
|
|
46
|
+
| DeletedCoValuesEraserScheduler
|
|
47
|
+
| undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Keeps track of CoValues that are in memory, to avoid reloading them from storage
|
|
50
|
+
* when it isn't necessary
|
|
51
|
+
*/
|
|
52
|
+
private inMemoryCoValues = new Set<RawCoID>();
|
|
41
53
|
|
|
42
54
|
/**
|
|
43
55
|
* Queue for streaming content that will be pulled by SyncManager.
|
|
@@ -138,7 +150,7 @@ export class StorageApiSync implements StorageAPI {
|
|
|
138
150
|
);
|
|
139
151
|
}
|
|
140
152
|
|
|
141
|
-
this.
|
|
153
|
+
this.inMemoryCoValues.add(coValueRow.id);
|
|
142
154
|
|
|
143
155
|
const priority = getPriorityFromHeader(coValueRow.header);
|
|
144
156
|
const contentMessage = createContentMessage(
|
|
@@ -244,7 +256,7 @@ export class StorageApiSync implements StorageAPI {
|
|
|
244
256
|
});
|
|
245
257
|
}
|
|
246
258
|
|
|
247
|
-
async pushContentWithDependencies(
|
|
259
|
+
private async pushContentWithDependencies(
|
|
248
260
|
coValueRow: StoredCoValueRow,
|
|
249
261
|
contentMessage: NewContentMessage,
|
|
250
262
|
pushCallback: (data: NewContentMessage) => void,
|
|
@@ -255,7 +267,7 @@ export class StorageApiSync implements StorageAPI {
|
|
|
255
267
|
);
|
|
256
268
|
|
|
257
269
|
for (const dependedOnCoValue of dependedOnCoValuesList) {
|
|
258
|
-
if (this.
|
|
270
|
+
if (this.inMemoryCoValues.has(dependedOnCoValue)) {
|
|
259
271
|
continue;
|
|
260
272
|
}
|
|
261
273
|
|
|
@@ -325,6 +337,10 @@ export class StorageApiSync implements StorageAPI {
|
|
|
325
337
|
|
|
326
338
|
for (const sessionID of Object.keys(msg.new) as SessionID[]) {
|
|
327
339
|
this.dbClient.transaction((tx) => {
|
|
340
|
+
if (this.deletedValues.has(id) && isDeleteSessionID(sessionID)) {
|
|
341
|
+
tx.markCoValueAsDeleted(id);
|
|
342
|
+
}
|
|
343
|
+
|
|
328
344
|
const sessionRow = tx.getSingleCoValueSession(
|
|
329
345
|
storedCoValueRowID,
|
|
330
346
|
sessionID,
|
|
@@ -353,6 +369,8 @@ export class StorageApiSync implements StorageAPI {
|
|
|
353
369
|
});
|
|
354
370
|
}
|
|
355
371
|
|
|
372
|
+
this.inMemoryCoValues.add(id);
|
|
373
|
+
|
|
356
374
|
this.knownStates.handleUpdate(id, knownState);
|
|
357
375
|
|
|
358
376
|
if (invalidAssumptions) {
|
|
@@ -427,6 +445,51 @@ export class StorageApiSync implements StorageAPI {
|
|
|
427
445
|
return newLastIdx;
|
|
428
446
|
}
|
|
429
447
|
|
|
448
|
+
deletedValues = new Set<RawCoID>();
|
|
449
|
+
|
|
450
|
+
markDeleteAsValid(id: RawCoID) {
|
|
451
|
+
this.deletedValues.add(id);
|
|
452
|
+
|
|
453
|
+
if (this.deletedCoValuesEraserScheduler) {
|
|
454
|
+
this.deletedCoValuesEraserScheduler.onEnqueueDeletedCoValue();
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
async eraseAllDeletedCoValues(): Promise<void> {
|
|
459
|
+
const ids = this.dbClient.getAllCoValuesWaitingForDelete();
|
|
460
|
+
|
|
461
|
+
for (const id of ids) {
|
|
462
|
+
this.dbClient.eraseCoValueButKeepTombstone(id);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
enableDeletedCoValuesErasure() {
|
|
467
|
+
if (this.deletedCoValuesEraserScheduler) return;
|
|
468
|
+
this.deletedCoValuesEraserScheduler = new DeletedCoValuesEraserScheduler({
|
|
469
|
+
run: async () =>
|
|
470
|
+
this.eraseDeletedCoValuesOnceBudgeted(MAX_DELETE_SCHEDULE_DURATION_MS),
|
|
471
|
+
});
|
|
472
|
+
this.deletedCoValuesEraserScheduler.scheduleStartupDrain();
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
private eraseDeletedCoValuesOnceBudgeted(budgetMs?: number) {
|
|
476
|
+
const startedAt = Date.now();
|
|
477
|
+
const ids = this.dbClient.getAllCoValuesWaitingForDelete();
|
|
478
|
+
|
|
479
|
+
for (const id of ids) {
|
|
480
|
+
// Strict time budget for sync storage to avoid blocking.
|
|
481
|
+
if (budgetMs && Date.now() - startedAt >= budgetMs) {
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
this.dbClient.eraseCoValueButKeepTombstone(id);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
return {
|
|
489
|
+
hasMore: this.dbClient.getAllCoValuesWaitingForDelete().length > 0,
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
430
493
|
waitForSync(id: string, coValue: CoValueCore) {
|
|
431
494
|
return this.knownStates.waitForSync(id, coValue);
|
|
432
495
|
}
|
|
@@ -450,7 +513,13 @@ export class StorageApiSync implements StorageAPI {
|
|
|
450
513
|
this.dbClient.stopTrackingSyncState(id);
|
|
451
514
|
}
|
|
452
515
|
|
|
516
|
+
onCoValueUnmounted(id: RawCoID): void {
|
|
517
|
+
this.inMemoryCoValues.delete(id);
|
|
518
|
+
}
|
|
519
|
+
|
|
453
520
|
close() {
|
|
521
|
+
this.deletedCoValuesEraserScheduler?.dispose();
|
|
522
|
+
this.inMemoryCoValues.clear();
|
|
454
523
|
return undefined;
|
|
455
524
|
}
|
|
456
525
|
}
|
package/src/storage/types.ts
CHANGED
|
@@ -13,12 +13,43 @@ export type CorrectionCallback = (
|
|
|
13
13
|
correction: CoValueKnownState,
|
|
14
14
|
) => NewContentMessage[] | undefined;
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Deletion work queue status for `deletedCoValues` (SQLite).
|
|
18
|
+
*
|
|
19
|
+
* Stored as an INTEGER in SQLite:
|
|
20
|
+
* - 0 = pending
|
|
21
|
+
* - 1 = done
|
|
22
|
+
*/
|
|
23
|
+
export enum DeletedCoValueDeletionStatus {
|
|
24
|
+
Pending = 0,
|
|
25
|
+
Done = 1,
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
/**
|
|
17
29
|
* The StorageAPI is the interface that the StorageSync and StorageAsync classes implement.
|
|
18
30
|
*
|
|
19
31
|
* It uses callbacks instead of promises to have no overhead when using the StorageSync and less overhead when using the StorageAsync.
|
|
20
32
|
*/
|
|
21
33
|
export interface StorageAPI {
|
|
34
|
+
/**
|
|
35
|
+
* Flags that the coValue delete is valid.
|
|
36
|
+
*
|
|
37
|
+
* When the delete tx is stored, the storage will mark the coValue as deleted.
|
|
38
|
+
*/
|
|
39
|
+
markDeleteAsValid(id: RawCoID): void;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Enable the background erasure scheduler that drains the `deletedCoValues` work queue.
|
|
43
|
+
* This is intentionally opt-in and should be activated by `LocalNode`.
|
|
44
|
+
*/
|
|
45
|
+
enableDeletedCoValuesErasure(): void;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Batch physical deletion for coValues queued in `deletedCoValues` with status `Pending`.
|
|
49
|
+
* Must preserve tombstones (header + delete session(s) + their tx/signatures).
|
|
50
|
+
*/
|
|
51
|
+
eraseAllDeletedCoValues(): Promise<void>;
|
|
52
|
+
|
|
22
53
|
load(
|
|
23
54
|
id: string,
|
|
24
55
|
// This callback is fired when data is found, might be called multiple times if the content requires streaming (e.g when loading files)
|
|
@@ -67,6 +98,12 @@ export interface StorageAPI {
|
|
|
67
98
|
callback: (knownState: CoValueKnownState | undefined) => void,
|
|
68
99
|
): void;
|
|
69
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Called when a CoValue is unmounted from memory.
|
|
103
|
+
* Used to clean up the metadata associated with that CoValue.
|
|
104
|
+
*/
|
|
105
|
+
onCoValueUnmounted(id: RawCoID): void;
|
|
106
|
+
|
|
70
107
|
close(): Promise<unknown> | undefined;
|
|
71
108
|
}
|
|
72
109
|
|
|
@@ -105,6 +142,13 @@ export interface DBTransactionInterfaceAsync {
|
|
|
105
142
|
sessionID: SessionID,
|
|
106
143
|
): Promise<StoredSessionRow | undefined>;
|
|
107
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Persist a "deleted coValue" marker in storage (work queue entry).
|
|
147
|
+
* This is an enqueue signal: implementations should set status to `Pending`.
|
|
148
|
+
* This is expected to be idempotent (safe to call repeatedly).
|
|
149
|
+
*/
|
|
150
|
+
markCoValueAsDeleted(id: RawCoID): Promise<unknown>;
|
|
151
|
+
|
|
108
152
|
addSessionUpdate({
|
|
109
153
|
sessionUpdate,
|
|
110
154
|
sessionRow,
|
|
@@ -140,6 +184,11 @@ export interface DBClientInterfaceAsync {
|
|
|
140
184
|
header?: CoValueHeader,
|
|
141
185
|
): Promise<number | undefined>;
|
|
142
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Enumerate all coValue IDs currently pending in the "deleted coValues" work queue.
|
|
189
|
+
*/
|
|
190
|
+
getAllCoValuesWaitingForDelete(): Promise<RawCoID[]>;
|
|
191
|
+
|
|
143
192
|
getCoValueSessions(coValueRowId: number): Promise<StoredSessionRow[]>;
|
|
144
193
|
|
|
145
194
|
getNewTransactionInSession(
|
|
@@ -165,6 +214,13 @@ export interface DBClientInterfaceAsync {
|
|
|
165
214
|
|
|
166
215
|
stopTrackingSyncState(id: RawCoID): Promise<void>;
|
|
167
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Physical deletion primitive: erase all persisted history for a deleted coValue,
|
|
219
|
+
* while preserving the tombstone (header + delete session(s)).
|
|
220
|
+
* Must run inside a single storage transaction.
|
|
221
|
+
*/
|
|
222
|
+
eraseCoValueButKeepTombstone(coValueID: RawCoID): Promise<unknown>;
|
|
223
|
+
|
|
168
224
|
/**
|
|
169
225
|
* Get the knownState for a CoValue without loading transactions.
|
|
170
226
|
* Returns undefined if the CoValue doesn't exist.
|
|
@@ -180,6 +236,13 @@ export interface DBTransactionInterfaceSync {
|
|
|
180
236
|
sessionID: SessionID,
|
|
181
237
|
): StoredSessionRow | undefined;
|
|
182
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Persist a "deleted coValue" marker in storage (work queue entry).
|
|
241
|
+
* This is an enqueue signal: implementations should set status to `"pending"`.
|
|
242
|
+
* This is expected to be idempotent (safe to call repeatedly).
|
|
243
|
+
*/
|
|
244
|
+
markCoValueAsDeleted(id: RawCoID): unknown;
|
|
245
|
+
|
|
183
246
|
addSessionUpdate({
|
|
184
247
|
sessionUpdate,
|
|
185
248
|
sessionRow,
|
|
@@ -210,6 +273,11 @@ export interface DBClientInterfaceSync {
|
|
|
210
273
|
|
|
211
274
|
upsertCoValue(id: string, header?: CoValueHeader): number | undefined;
|
|
212
275
|
|
|
276
|
+
/**
|
|
277
|
+
* Enumerate all coValue IDs currently pending in the "deleted coValues" work queue.
|
|
278
|
+
*/
|
|
279
|
+
getAllCoValuesWaitingForDelete(): RawCoID[];
|
|
280
|
+
|
|
213
281
|
getCoValueSessions(coValueRowId: number): StoredSessionRow[];
|
|
214
282
|
|
|
215
283
|
getNewTransactionInSession(
|
|
@@ -233,6 +301,13 @@ export interface DBClientInterfaceSync {
|
|
|
233
301
|
|
|
234
302
|
stopTrackingSyncState(id: RawCoID): void;
|
|
235
303
|
|
|
304
|
+
/**
|
|
305
|
+
* Physical deletion primitive: erase all persisted history for a deleted coValue,
|
|
306
|
+
* while preserving the tombstone (header + delete session(s)).
|
|
307
|
+
* Must run inside a single storage transaction.
|
|
308
|
+
*/
|
|
309
|
+
eraseCoValueButKeepTombstone(coValueID: RawCoID): unknown;
|
|
310
|
+
|
|
236
311
|
/**
|
|
237
312
|
* Get the knownState for a CoValue without loading transactions.
|
|
238
313
|
* Returns undefined if the CoValue doesn't exist.
|