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
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { UpDownCounter, ValueType, metrics } from "@opentelemetry/api";
|
|
2
2
|
import type { PeerState } from "../PeerState.js";
|
|
3
3
|
import type { RawCoValue } from "../coValue.js";
|
|
4
|
-
import
|
|
4
|
+
import {
|
|
5
|
+
RawAccount,
|
|
6
|
+
type ControlledAccountOrAgent,
|
|
7
|
+
} from "../coValues/account.js";
|
|
5
8
|
import type { RawGroup } from "../coValues/group.js";
|
|
6
9
|
import { CO_VALUE_LOADING_CONFIG } from "../config.js";
|
|
7
|
-
import { validateTxSizeLimitInBytes } from "../coValueContentMessage.js";
|
|
8
10
|
import { coreToCoValue } from "../coreToCoValue.js";
|
|
9
11
|
import {
|
|
10
12
|
CryptoProvider,
|
|
@@ -14,12 +16,18 @@ import {
|
|
|
14
16
|
Signature,
|
|
15
17
|
SignerID,
|
|
16
18
|
} from "../crypto/crypto.js";
|
|
17
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
AgentID,
|
|
21
|
+
isDeleteSessionID,
|
|
22
|
+
RawCoID,
|
|
23
|
+
SessionID,
|
|
24
|
+
TransactionID,
|
|
25
|
+
} from "../ids.js";
|
|
18
26
|
import { JsonObject, JsonValue } from "../jsonValue.js";
|
|
19
27
|
import { LocalNode, ResolveAccountAgentError } from "../localNode.js";
|
|
20
28
|
import { logger } from "../logger.js";
|
|
21
29
|
import { determineValidTransactions } from "../permissions.js";
|
|
22
|
-
import { NewContentMessage, PeerID } from "../sync.js";
|
|
30
|
+
import { KnownStateMessage, NewContentMessage, PeerID } from "../sync.js";
|
|
23
31
|
import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfromSessionID.js";
|
|
24
32
|
import { expectGroup } from "../typeUtils/expectGroup.js";
|
|
25
33
|
import {
|
|
@@ -27,7 +35,12 @@ import {
|
|
|
27
35
|
getDependenciesFromGroupRawTransactions,
|
|
28
36
|
getDependenciesFromHeader,
|
|
29
37
|
} from "./utils.js";
|
|
30
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
CoValueHeader,
|
|
40
|
+
Transaction,
|
|
41
|
+
Uniqueness,
|
|
42
|
+
VerifiedState,
|
|
43
|
+
} from "./verifiedState.js";
|
|
31
44
|
import { SessionMap } from "./SessionMap.js";
|
|
32
45
|
import {
|
|
33
46
|
MergeCommit,
|
|
@@ -51,6 +64,45 @@ import {
|
|
|
51
64
|
} from "../knownState.js";
|
|
52
65
|
import { safeParseJSON } from "../jsonStringify.js";
|
|
53
66
|
|
|
67
|
+
export type ValidationValue =
|
|
68
|
+
| { isOk: true }
|
|
69
|
+
| {
|
|
70
|
+
isOk: false;
|
|
71
|
+
message: string;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
function validateUniqueness(uniqueness: Uniqueness): ValidationValue {
|
|
75
|
+
if (typeof uniqueness === "number" && !Number.isInteger(uniqueness)) {
|
|
76
|
+
return {
|
|
77
|
+
isOk: false,
|
|
78
|
+
message: "Uniqueness cannot be a non-integer number, got " + uniqueness,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (Array.isArray(uniqueness)) {
|
|
83
|
+
return {
|
|
84
|
+
isOk: false,
|
|
85
|
+
message: "Uniqueness cannot be an array, got " + uniqueness,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (typeof uniqueness === "object" && uniqueness !== null) {
|
|
90
|
+
for (let [key, value] of Object.entries(uniqueness)) {
|
|
91
|
+
if (typeof value !== "string") {
|
|
92
|
+
return {
|
|
93
|
+
isOk: false,
|
|
94
|
+
message:
|
|
95
|
+
"Uniqueness object values must be a string, got " +
|
|
96
|
+
value +
|
|
97
|
+
" for key " +
|
|
98
|
+
key,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { isOk: true };
|
|
104
|
+
}
|
|
105
|
+
|
|
54
106
|
export function idforHeader(
|
|
55
107
|
header: CoValueHeader,
|
|
56
108
|
crypto: CryptoProvider,
|
|
@@ -197,6 +249,8 @@ export class CoValueCore {
|
|
|
197
249
|
// context
|
|
198
250
|
readonly node: LocalNode;
|
|
199
251
|
private readonly crypto: CryptoProvider;
|
|
252
|
+
// Whether the coValue is deleted
|
|
253
|
+
public isDeleted: boolean = false;
|
|
200
254
|
|
|
201
255
|
// state
|
|
202
256
|
id: RawCoID;
|
|
@@ -373,16 +427,26 @@ export class CoValueCore {
|
|
|
373
427
|
}
|
|
374
428
|
}
|
|
375
429
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
430
|
+
/**
|
|
431
|
+
* Removes the CoValue from memory.
|
|
432
|
+
*
|
|
433
|
+
* @returns true if the coValue was successfully unmounted, false otherwise
|
|
434
|
+
*/
|
|
435
|
+
unmount(): boolean {
|
|
436
|
+
if (this.listeners.size > 0) {
|
|
437
|
+
// The coValue is still in use
|
|
381
438
|
return false;
|
|
382
439
|
}
|
|
383
440
|
|
|
384
|
-
|
|
385
|
-
|
|
441
|
+
for (const dependant of this.dependant) {
|
|
442
|
+
if (this.node.hasCoValue(dependant)) {
|
|
443
|
+
// Another in-memory coValue depends on this coValue
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (!this.node.syncManager.isSyncedToServerPeers(this.id)) {
|
|
449
|
+
return false;
|
|
386
450
|
}
|
|
387
451
|
|
|
388
452
|
this.counter.add(-1, { state: this.loadingState });
|
|
@@ -482,6 +546,15 @@ export class CoValueCore {
|
|
|
482
546
|
skipVerify?: boolean,
|
|
483
547
|
) {
|
|
484
548
|
if (!skipVerify) {
|
|
549
|
+
const validation = validateUniqueness(header.uniqueness);
|
|
550
|
+
if (!validation.isOk) {
|
|
551
|
+
logger.error("Invalid uniqueness", {
|
|
552
|
+
header,
|
|
553
|
+
errorMessage: validation.message,
|
|
554
|
+
});
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
557
|
+
|
|
485
558
|
const expectedId = idforHeader(header, this.node.crypto);
|
|
486
559
|
|
|
487
560
|
if (this.id !== expectedId) {
|
|
@@ -549,6 +622,33 @@ export class CoValueCore {
|
|
|
549
622
|
return this.verified?.immutableKnownState() ?? emptyKnownState(this.id);
|
|
550
623
|
}
|
|
551
624
|
|
|
625
|
+
/**
|
|
626
|
+
* Returns a known state message to signal to the peer that the coValue doesn't need to be synced anymore
|
|
627
|
+
*
|
|
628
|
+
* Implemented to be backward compatible with clients that don't support deleted coValues
|
|
629
|
+
*/
|
|
630
|
+
stopSyncingKnownStateMessage(
|
|
631
|
+
peerKnownState: CoValueKnownState | undefined,
|
|
632
|
+
): KnownStateMessage {
|
|
633
|
+
if (!peerKnownState) {
|
|
634
|
+
return {
|
|
635
|
+
action: "known",
|
|
636
|
+
...this.knownState(),
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const knownState = cloneKnownState(this.knownState());
|
|
641
|
+
|
|
642
|
+
// We combine everything for backward compatibility with clients that don't support deleted coValues
|
|
643
|
+
// This way they won't try to sync their own content that we have discarded because the coValue is deleted
|
|
644
|
+
combineKnownStateSessions(knownState.sessions, peerKnownState.sessions);
|
|
645
|
+
|
|
646
|
+
return {
|
|
647
|
+
action: "known",
|
|
648
|
+
...knownState,
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
|
|
552
652
|
get meta(): JsonValue {
|
|
553
653
|
return this.verified?.header.meta ?? null;
|
|
554
654
|
}
|
|
@@ -583,6 +683,107 @@ export class CoValueCore {
|
|
|
583
683
|
}
|
|
584
684
|
}
|
|
585
685
|
|
|
686
|
+
#isDeleteTransaction(
|
|
687
|
+
sessionID: SessionID,
|
|
688
|
+
newTransactions: Transaction[],
|
|
689
|
+
skipVerify: boolean,
|
|
690
|
+
) {
|
|
691
|
+
if (!this.verified) {
|
|
692
|
+
return {
|
|
693
|
+
value: false,
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// Detect + validate delete transactions during ingestion
|
|
698
|
+
// Delete transactions are:
|
|
699
|
+
// - in a delete session (sessionID ends with `$`)
|
|
700
|
+
// - trusting (unencrypted)
|
|
701
|
+
// - have meta `{ deleted: true }`
|
|
702
|
+
let deleteTransaction: Transaction | undefined = undefined;
|
|
703
|
+
|
|
704
|
+
if (isDeleteSessionID(sessionID)) {
|
|
705
|
+
const txCount =
|
|
706
|
+
this.verified.sessions.get(sessionID)?.transactions.length ?? 0;
|
|
707
|
+
if (txCount > 0 || newTransactions.length > 1) {
|
|
708
|
+
return {
|
|
709
|
+
value: true,
|
|
710
|
+
err: {
|
|
711
|
+
type: "DeleteTransactionRejected",
|
|
712
|
+
id: this.id,
|
|
713
|
+
sessionID,
|
|
714
|
+
reason: "InvalidDeleteTransaction",
|
|
715
|
+
error: new Error(
|
|
716
|
+
"Delete transaction must be the only transaction in the session",
|
|
717
|
+
),
|
|
718
|
+
} as const,
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
const firstTransaction = newTransactions[0];
|
|
723
|
+
const deleteMarker =
|
|
724
|
+
firstTransaction && this.#getDeleteMarker(firstTransaction);
|
|
725
|
+
|
|
726
|
+
if (deleteMarker) {
|
|
727
|
+
deleteTransaction = firstTransaction;
|
|
728
|
+
|
|
729
|
+
if (deleteMarker.deleted !== this.id) {
|
|
730
|
+
return {
|
|
731
|
+
value: true,
|
|
732
|
+
err: {
|
|
733
|
+
type: "DeleteTransactionRejected",
|
|
734
|
+
id: this.id,
|
|
735
|
+
sessionID,
|
|
736
|
+
reason: "InvalidDeleteTransaction",
|
|
737
|
+
error: new Error(
|
|
738
|
+
`Delete transaction ID mismatch: expected ${this.id}, got ${deleteMarker.deleted}`,
|
|
739
|
+
),
|
|
740
|
+
} as const,
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
if (this.isGroupOrAccount()) {
|
|
746
|
+
return {
|
|
747
|
+
value: true,
|
|
748
|
+
err: {
|
|
749
|
+
type: "DeleteTransactionRejected",
|
|
750
|
+
id: this.id,
|
|
751
|
+
sessionID,
|
|
752
|
+
reason: "CoValueNotDeletable",
|
|
753
|
+
error: new Error("Cannot delete Group or Account coValues"),
|
|
754
|
+
},
|
|
755
|
+
} as const;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
if (!skipVerify && deleteTransaction) {
|
|
760
|
+
const author = accountOrAgentIDfromSessionID(sessionID);
|
|
761
|
+
|
|
762
|
+
const permission = this.#canAuthorDeleteCoValueAtTime(
|
|
763
|
+
author,
|
|
764
|
+
deleteTransaction.madeAt,
|
|
765
|
+
);
|
|
766
|
+
|
|
767
|
+
if (!permission.ok) {
|
|
768
|
+
return {
|
|
769
|
+
value: true,
|
|
770
|
+
err: {
|
|
771
|
+
type: "DeleteTransactionRejected",
|
|
772
|
+
id: this.id,
|
|
773
|
+
sessionID,
|
|
774
|
+
author,
|
|
775
|
+
reason: permission.reason,
|
|
776
|
+
error: new Error(permission.message),
|
|
777
|
+
},
|
|
778
|
+
} as const;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
return {
|
|
783
|
+
value: Boolean(deleteTransaction),
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
|
|
586
787
|
/**
|
|
587
788
|
* Apply new transactions that were not generated by the current node to the CoValue
|
|
588
789
|
*/
|
|
@@ -592,8 +793,22 @@ export class CoValueCore {
|
|
|
592
793
|
newSignature: Signature,
|
|
593
794
|
skipVerify: boolean = false,
|
|
594
795
|
) {
|
|
796
|
+
if (newTransactions.length === 0) {
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
|
|
595
800
|
let signerID: SignerID | undefined;
|
|
596
801
|
|
|
802
|
+
// sync should never try to add transactions to a deleted coValue
|
|
803
|
+
// this can only happen if `tryAddTransactions` is called directly, without going through `handleNewContent`
|
|
804
|
+
if (this.isDeleted && !isDeleteSessionID(sessionID)) {
|
|
805
|
+
return {
|
|
806
|
+
type: "CoValueDeleted",
|
|
807
|
+
id: this.id,
|
|
808
|
+
error: new Error("Cannot add transactions to a deleted coValue"),
|
|
809
|
+
} as const;
|
|
810
|
+
}
|
|
811
|
+
|
|
597
812
|
if (!skipVerify) {
|
|
598
813
|
const result = this.node.resolveAccountAgent(
|
|
599
814
|
accountOrAgentIDfromSessionID(sessionID),
|
|
@@ -619,6 +834,16 @@ export class CoValueCore {
|
|
|
619
834
|
};
|
|
620
835
|
}
|
|
621
836
|
|
|
837
|
+
const isDeleteTransaction = this.#isDeleteTransaction(
|
|
838
|
+
sessionID,
|
|
839
|
+
newTransactions,
|
|
840
|
+
skipVerify,
|
|
841
|
+
);
|
|
842
|
+
|
|
843
|
+
if (isDeleteTransaction.err) {
|
|
844
|
+
return isDeleteTransaction.err;
|
|
845
|
+
}
|
|
846
|
+
|
|
622
847
|
try {
|
|
623
848
|
this.verified.tryAddTransactions(
|
|
624
849
|
sessionID,
|
|
@@ -628,6 +853,13 @@ export class CoValueCore {
|
|
|
628
853
|
skipVerify,
|
|
629
854
|
);
|
|
630
855
|
|
|
856
|
+
// Mark deleted state when a delete marker transaction is accepted.
|
|
857
|
+
// - In skipVerify mode (storage shards), we accept + mark without permission checks.
|
|
858
|
+
// - In verify mode, we only reach here if the delete permission check passed.
|
|
859
|
+
if (isDeleteTransaction.value) {
|
|
860
|
+
this.#markAsDeleted();
|
|
861
|
+
}
|
|
862
|
+
|
|
631
863
|
this.processNewTransactions();
|
|
632
864
|
this.scheduleNotifyUpdate();
|
|
633
865
|
this.invalidateDependants();
|
|
@@ -636,6 +868,78 @@ export class CoValueCore {
|
|
|
636
868
|
}
|
|
637
869
|
}
|
|
638
870
|
|
|
871
|
+
#markAsDeleted() {
|
|
872
|
+
this.isDeleted = true;
|
|
873
|
+
this.verified?.markAsDeleted();
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
#getDeleteMarker(tx: Transaction): { deleted: RawCoID } | undefined {
|
|
877
|
+
if (tx.privacy !== "trusting") {
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
if (!tx.meta) {
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
const meta = safeParseJSON(tx.meta);
|
|
884
|
+
|
|
885
|
+
return meta && typeof meta.deleted === "string"
|
|
886
|
+
? (meta as { deleted: RawCoID })
|
|
887
|
+
: undefined;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
#canAuthorDeleteCoValueAtTime(
|
|
891
|
+
author: RawAccountID | AgentID,
|
|
892
|
+
madeAt: number,
|
|
893
|
+
):
|
|
894
|
+
| { ok: true }
|
|
895
|
+
| {
|
|
896
|
+
ok: false;
|
|
897
|
+
reason: DeleteTransactionRejectedError["reason"];
|
|
898
|
+
message: string;
|
|
899
|
+
} {
|
|
900
|
+
if (!this.verified) {
|
|
901
|
+
return {
|
|
902
|
+
ok: false,
|
|
903
|
+
reason: "CannotVerifyPermissions",
|
|
904
|
+
message: "Cannot verify delete permissions without verified state",
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
if (this.isGroupOrAccount()) {
|
|
909
|
+
return {
|
|
910
|
+
ok: false,
|
|
911
|
+
reason: "CoValueNotDeletable",
|
|
912
|
+
message: "Cannot delete Group or Account coValues",
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
const group = this.safeGetGroup();
|
|
917
|
+
|
|
918
|
+
// Today, delete permission is defined in terms of group-admin on the owning group.
|
|
919
|
+
// If we cannot derive that (non-owned coValues), we reject the delete when verification is required.
|
|
920
|
+
if (!group) {
|
|
921
|
+
return {
|
|
922
|
+
ok: false,
|
|
923
|
+
reason: "CannotVerifyPermissions",
|
|
924
|
+
message:
|
|
925
|
+
"Cannot verify delete permissions for coValues not owned by a group",
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
const groupAtTime = group.atTime(madeAt);
|
|
930
|
+
const role = groupAtTime.roleOfInternal(author);
|
|
931
|
+
|
|
932
|
+
if (role !== "admin") {
|
|
933
|
+
return {
|
|
934
|
+
ok: false,
|
|
935
|
+
reason: "NotAdmin",
|
|
936
|
+
message: "Delete transaction rejected: author is not an admin",
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
return { ok: true };
|
|
941
|
+
}
|
|
942
|
+
|
|
639
943
|
notifyDependants() {
|
|
640
944
|
if (!this.isGroup()) {
|
|
641
945
|
return;
|
|
@@ -733,6 +1037,70 @@ export class CoValueCore {
|
|
|
733
1037
|
};
|
|
734
1038
|
}
|
|
735
1039
|
|
|
1040
|
+
validateDeletePermissions() {
|
|
1041
|
+
if (!this.verified) {
|
|
1042
|
+
return {
|
|
1043
|
+
ok: false,
|
|
1044
|
+
reason: "CannotVerifyPermissions",
|
|
1045
|
+
message: "Cannot verify delete permissions without verified state",
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
if (this.isGroupOrAccount()) {
|
|
1050
|
+
return {
|
|
1051
|
+
ok: false,
|
|
1052
|
+
reason: "CoValueNotDeletable",
|
|
1053
|
+
message: "Cannot delete Group or Account coValues",
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
const group = this.safeGetGroup();
|
|
1058
|
+
if (!group) {
|
|
1059
|
+
return {
|
|
1060
|
+
ok: false,
|
|
1061
|
+
reason: "CannotVerifyPermissions",
|
|
1062
|
+
message:
|
|
1063
|
+
"Cannot verify delete permissions for coValues not owned by a group",
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
const role = group.myRole();
|
|
1068
|
+
if (role !== "admin") {
|
|
1069
|
+
return {
|
|
1070
|
+
ok: false,
|
|
1071
|
+
reason: "NotAdmin",
|
|
1072
|
+
message:
|
|
1073
|
+
"The current account lacks admin permissions to delete this coValue",
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
return { ok: true };
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
/**
|
|
1081
|
+
* Creates a delete marker transaction for this CoValue and sets the coValue as deleted
|
|
1082
|
+
*
|
|
1083
|
+
* Constraints:
|
|
1084
|
+
* - Account and Group CoValues cannot be deleted.
|
|
1085
|
+
* - Only admins can delete a coValue.
|
|
1086
|
+
*/
|
|
1087
|
+
deleteCoValue() {
|
|
1088
|
+
if (this.isDeleted) {
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
const result = this.validateDeletePermissions();
|
|
1093
|
+
if (!result.ok) {
|
|
1094
|
+
throw new Error(result.message);
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
this.makeTransaction(
|
|
1098
|
+
[], // Empty changes array
|
|
1099
|
+
"trusting", // Unencrypted
|
|
1100
|
+
{ deleted: this.id }, // Delete metadata
|
|
1101
|
+
);
|
|
1102
|
+
}
|
|
1103
|
+
|
|
736
1104
|
/**
|
|
737
1105
|
* Creates a new transaction with local changes and syncs it to all peers
|
|
738
1106
|
*/
|
|
@@ -747,11 +1115,17 @@ export class CoValueCore {
|
|
|
747
1115
|
"CoValueCore: makeTransaction called on coValue without verified state",
|
|
748
1116
|
);
|
|
749
1117
|
}
|
|
1118
|
+
const isDeleteTransaction = Boolean(meta?.deleted);
|
|
750
1119
|
|
|
751
|
-
|
|
1120
|
+
if (this.isDeleted && !isDeleteTransaction) {
|
|
1121
|
+
logger.error("Cannot make transaction on a deleted coValue", {
|
|
1122
|
+
id: this.id,
|
|
1123
|
+
});
|
|
1124
|
+
return false;
|
|
1125
|
+
}
|
|
752
1126
|
|
|
753
1127
|
// This is an ugly hack to get a unique but stable session ID for editing the current account
|
|
754
|
-
|
|
1128
|
+
let sessionID =
|
|
755
1129
|
this.verified.header.meta?.type === "account"
|
|
756
1130
|
? (this.node.currentSessionID.replace(
|
|
757
1131
|
this.node.getCurrentAgent().id,
|
|
@@ -759,6 +1133,12 @@ export class CoValueCore {
|
|
|
759
1133
|
) as SessionID)
|
|
760
1134
|
: this.node.currentSessionID;
|
|
761
1135
|
|
|
1136
|
+
if (isDeleteTransaction) {
|
|
1137
|
+
sessionID = this.crypto.newDeleteSessionID(
|
|
1138
|
+
this.node.getCurrentAccountOrAgentID(),
|
|
1139
|
+
);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
762
1142
|
const signerAgent = this.node.getCurrentAgent();
|
|
763
1143
|
|
|
764
1144
|
let result: { signature: Signature; transaction: Transaction };
|
|
@@ -791,6 +1171,10 @@ export class CoValueCore {
|
|
|
791
1171
|
);
|
|
792
1172
|
}
|
|
793
1173
|
|
|
1174
|
+
if (isDeleteTransaction) {
|
|
1175
|
+
this.#markAsDeleted();
|
|
1176
|
+
}
|
|
1177
|
+
|
|
794
1178
|
const { transaction } = result;
|
|
795
1179
|
|
|
796
1180
|
// Assign pre-parsed meta and changes to skip the parse/decrypt operation when loading
|
|
@@ -1193,6 +1577,15 @@ export class CoValueCore {
|
|
|
1193
1577
|
return matchingTransactions;
|
|
1194
1578
|
}
|
|
1195
1579
|
|
|
1580
|
+
/**
|
|
1581
|
+
* The CoValues that this CoValue depends on.
|
|
1582
|
+
* We currently track dependencies for:
|
|
1583
|
+
* - Ownership (a CoValue depends on its account/group owner)
|
|
1584
|
+
* - Group membership (a group depends on its direct account/group members)
|
|
1585
|
+
* - Sessions (a CoValue depends on Accounts that made changes to it)
|
|
1586
|
+
* - Branches (a branched CoValue depends on its branch source)
|
|
1587
|
+
* See {@link dependant} for the CoValues that depend on this CoValue.
|
|
1588
|
+
*/
|
|
1196
1589
|
dependencies: Set<RawCoID> = new Set();
|
|
1197
1590
|
incompleteDependencies: Set<RawCoID> = new Set();
|
|
1198
1591
|
private addDependency(dependency: RawCoID) {
|
|
@@ -1238,11 +1631,23 @@ export class CoValueCore {
|
|
|
1238
1631
|
}
|
|
1239
1632
|
}
|
|
1240
1633
|
|
|
1634
|
+
/**
|
|
1635
|
+
* The CoValues that depend on this CoValue.
|
|
1636
|
+
* This is the inverse relationship of {@link dependencies}.
|
|
1637
|
+
*/
|
|
1241
1638
|
dependant: Set<RawCoID> = new Set();
|
|
1242
1639
|
private addDependant(dependant: RawCoID) {
|
|
1243
1640
|
this.dependant.add(dependant);
|
|
1244
1641
|
}
|
|
1245
1642
|
|
|
1643
|
+
isGroupOrAccount() {
|
|
1644
|
+
if (!this.verified) {
|
|
1645
|
+
return false;
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
return this.verified.header.ruleset.type === "group";
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1246
1651
|
isGroup() {
|
|
1247
1652
|
if (!this.verified) {
|
|
1248
1653
|
return false;
|
|
@@ -1622,9 +2027,19 @@ export type TriedToAddTransactionsWithoutSignerIDError = {
|
|
|
1622
2027
|
sessionID: SessionID;
|
|
1623
2028
|
};
|
|
1624
2029
|
|
|
2030
|
+
export type DeleteTransactionRejectedError = {
|
|
2031
|
+
type: "DeleteTransactionRejected";
|
|
2032
|
+
id: RawCoID;
|
|
2033
|
+
sessionID: SessionID;
|
|
2034
|
+
author: RawAccountID | AgentID;
|
|
2035
|
+
reason: "NotAdmin" | "CoValueNotDeletable" | "CannotVerifyPermissions";
|
|
2036
|
+
error: Error;
|
|
2037
|
+
};
|
|
2038
|
+
|
|
1625
2039
|
export type TryAddTransactionsError =
|
|
1626
2040
|
| TriedToAddTransactionsWithoutVerifiedStateErrpr
|
|
1627
2041
|
| TriedToAddTransactionsWithoutSignerIDError
|
|
1628
2042
|
| ResolveAccountAgentError
|
|
1629
2043
|
| InvalidHashError
|
|
1630
|
-
| InvalidSignatureError
|
|
2044
|
+
| InvalidSignatureError
|
|
2045
|
+
| DeleteTransactionRejectedError;
|
|
@@ -14,12 +14,16 @@ import {
|
|
|
14
14
|
Signature,
|
|
15
15
|
SignerID,
|
|
16
16
|
} from "../crypto/crypto.js";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
isDeleteSessionID,
|
|
19
|
+
RawCoID,
|
|
20
|
+
SessionID,
|
|
21
|
+
TransactionID,
|
|
22
|
+
} from "../ids.js";
|
|
18
23
|
import { Stringified } from "../jsonStringify.js";
|
|
19
24
|
import { JsonObject, JsonValue } from "../jsonValue.js";
|
|
20
25
|
import { PermissionsDef as RulesetDef } from "../permissions.js";
|
|
21
26
|
import { NewContentMessage } from "../sync.js";
|
|
22
|
-
import { TryAddTransactionsError } from "./coValueCore.js";
|
|
23
27
|
import { SessionMap } from "./SessionMap.js";
|
|
24
28
|
import { ControlledAccountOrAgent } from "../coValues/account.js";
|
|
25
29
|
import {
|
|
@@ -35,10 +39,19 @@ export type CoValueHeader = {
|
|
|
35
39
|
} & CoValueUniqueness;
|
|
36
40
|
|
|
37
41
|
export type CoValueUniqueness = {
|
|
38
|
-
uniqueness:
|
|
42
|
+
uniqueness: Uniqueness;
|
|
39
43
|
createdAt?: `2${string}` | null;
|
|
40
44
|
};
|
|
41
45
|
|
|
46
|
+
export type Uniqueness =
|
|
47
|
+
| string
|
|
48
|
+
| boolean
|
|
49
|
+
| null
|
|
50
|
+
| undefined
|
|
51
|
+
| {
|
|
52
|
+
[key: string]: string;
|
|
53
|
+
};
|
|
54
|
+
|
|
42
55
|
export type PrivateTransaction = {
|
|
43
56
|
privacy: "private";
|
|
44
57
|
madeAt: number;
|
|
@@ -63,6 +76,7 @@ export class VerifiedState {
|
|
|
63
76
|
public lastAccessed: number | undefined;
|
|
64
77
|
public branchSourceId?: RawCoID;
|
|
65
78
|
public branchName?: string;
|
|
79
|
+
private isDeleted: boolean = false;
|
|
66
80
|
|
|
67
81
|
constructor(
|
|
68
82
|
id: RawCoID,
|
|
@@ -87,6 +101,11 @@ export class VerifiedState {
|
|
|
87
101
|
);
|
|
88
102
|
}
|
|
89
103
|
|
|
104
|
+
markAsDeleted() {
|
|
105
|
+
this.isDeleted = true;
|
|
106
|
+
this.sessions.markAsDeleted();
|
|
107
|
+
}
|
|
108
|
+
|
|
90
109
|
tryAddTransactions(
|
|
91
110
|
sessionID: SessionID,
|
|
92
111
|
signerID: SignerID | undefined,
|
|
@@ -199,6 +218,10 @@ export class VerifiedState {
|
|
|
199
218
|
const sessionSent = knownState?.sessions;
|
|
200
219
|
|
|
201
220
|
for (const [sessionID, log] of this.sessions.sessions) {
|
|
221
|
+
if (this.isDeleted && !isDeleteSessionID(sessionID)) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
|
|
202
225
|
const startFrom = sessionSent?.[sessionID] ?? 0;
|
|
203
226
|
|
|
204
227
|
let currentSessionSize = 0;
|
package/src/coValues/coList.ts
CHANGED
|
@@ -59,14 +59,14 @@ export class RawCoList<
|
|
|
59
59
|
afterStart: OpID[] = [];
|
|
60
60
|
beforeEnd: OpID[] = [];
|
|
61
61
|
insertions: {
|
|
62
|
-
[sessionID:
|
|
62
|
+
[sessionID: SessionIndex]: {
|
|
63
63
|
[txIdx: number]: {
|
|
64
64
|
[changeIdx: number]: InsertionEntry<Item>;
|
|
65
65
|
};
|
|
66
66
|
};
|
|
67
67
|
} = {};
|
|
68
68
|
deletionsByInsertion: {
|
|
69
|
-
[deletedSessionID:
|
|
69
|
+
[deletedSessionID: SessionIndex]: {
|
|
70
70
|
[deletedTxIdx: number]: {
|
|
71
71
|
[deletedChangeIdx: number]: DeletionEntry[];
|
|
72
72
|
};
|
|
@@ -700,7 +700,9 @@ export class RawCoList<
|
|
|
700
700
|
}
|
|
701
701
|
}
|
|
702
702
|
|
|
703
|
-
|
|
703
|
+
type SessionIndex = SessionID | `${SessionID}_branch_${RawCoID}`;
|
|
704
|
+
|
|
705
|
+
function getSessionIndex(txID: TransactionID): SessionIndex {
|
|
704
706
|
if (txID.branch) {
|
|
705
707
|
return `${txID.sessionID}_branch_${txID.branch}`;
|
|
706
708
|
}
|
package/src/coValues/group.ts
CHANGED
|
@@ -1203,12 +1203,11 @@ export class RawGroup<
|
|
|
1203
1203
|
|
|
1204
1204
|
this.set(memberKey, "revoked", "trusting");
|
|
1205
1205
|
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
// }
|
|
1206
|
+
if (this.get(memberKey) !== "revoked") {
|
|
1207
|
+
throw new Error(
|
|
1208
|
+
`Failed to revoke role to ${memberKey} (role of current account is ${this.myRole()})`,
|
|
1209
|
+
);
|
|
1210
|
+
}
|
|
1212
1211
|
}
|
|
1213
1212
|
|
|
1214
1213
|
/**
|