cojson 0.18.5 → 0.18.7
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 +18 -0
- package/dist/coValueContentMessage.d.ts +2 -0
- package/dist/coValueContentMessage.d.ts.map +1 -1
- package/dist/coValueContentMessage.js +7 -0
- package/dist/coValueContentMessage.js.map +1 -1
- package/dist/coValueCore/SessionMap.d.ts +2 -2
- package/dist/coValueCore/SessionMap.d.ts.map +1 -1
- package/dist/coValueCore/SessionMap.js +2 -4
- package/dist/coValueCore/SessionMap.js.map +1 -1
- package/dist/coValueCore/branching.d.ts +35 -13
- package/dist/coValueCore/branching.d.ts.map +1 -1
- package/dist/coValueCore/branching.js +55 -37
- package/dist/coValueCore/branching.js.map +1 -1
- package/dist/coValueCore/coValueCore.d.ts +15 -7
- package/dist/coValueCore/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore/coValueCore.js +126 -35
- package/dist/coValueCore/coValueCore.js.map +1 -1
- package/dist/coValueCore/verifiedState.d.ts +4 -2
- package/dist/coValueCore/verifiedState.d.ts.map +1 -1
- package/dist/coValueCore/verifiedState.js +6 -4
- package/dist/coValueCore/verifiedState.js.map +1 -1
- package/dist/coValues/coList.d.ts +8 -2
- package/dist/coValues/coList.d.ts.map +1 -1
- package/dist/coValues/coList.js +95 -58
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.d.ts +2 -2
- package/dist/coValues/coMap.d.ts.map +1 -1
- package/dist/coValues/coMap.js +8 -8
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/group.d.ts.map +1 -1
- package/dist/coValues/group.js +14 -1
- package/dist/coValues/group.js.map +1 -1
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/crypto/PureJSCrypto.d.ts.map +1 -1
- package/dist/crypto/PureJSCrypto.js +14 -6
- package/dist/crypto/PureJSCrypto.js.map +1 -1
- package/dist/exports.d.ts +5 -4
- package/dist/exports.d.ts.map +1 -1
- package/dist/exports.js +3 -3
- package/dist/exports.js.map +1 -1
- package/dist/ids.d.ts +1 -0
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js.map +1 -1
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +7 -2
- package/dist/localNode.js.map +1 -1
- package/dist/storage/storageAsync.d.ts.map +1 -1
- package/dist/storage/storageAsync.js +7 -4
- package/dist/storage/storageAsync.js.map +1 -1
- package/dist/storage/storageSync.d.ts.map +1 -1
- package/dist/storage/storageSync.js +7 -4
- package/dist/storage/storageSync.js.map +1 -1
- package/dist/sync.d.ts +1 -3
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +8 -18
- package/dist/sync.js.map +1 -1
- package/dist/tests/branching.test.js +166 -4
- package/dist/tests/branching.test.js.map +1 -1
- package/dist/tests/coList.test.js +367 -1
- package/dist/tests/coList.test.js.map +1 -1
- package/dist/tests/coValueCore.test.js +45 -1
- package/dist/tests/coValueCore.test.js.map +1 -1
- package/dist/tests/messagesTestUtils.d.ts +1 -1
- package/dist/tests/sync.content.test.d.ts +2 -0
- package/dist/tests/sync.content.test.d.ts.map +1 -0
- package/dist/tests/sync.content.test.js +120 -0
- package/dist/tests/sync.content.test.js.map +1 -0
- package/dist/tests/sync.load.test.js +4 -4
- package/dist/tests/sync.load.test.js.map +1 -1
- package/dist/tests/sync.storage.test.js +1 -1
- package/dist/tests/sync.storageAsync.test.js +6 -10
- package/dist/tests/sync.storageAsync.test.js.map +1 -1
- package/dist/tests/sync.upload.test.js +2 -2
- package/dist/tests/testUtils.d.ts +2 -2
- package/package.json +2 -2
- package/src/coValueContentMessage.ts +13 -0
- package/src/coValueCore/SessionMap.ts +2 -2
- package/src/coValueCore/branching.ts +105 -60
- package/src/coValueCore/coValueCore.ts +163 -41
- package/src/coValueCore/verifiedState.ts +8 -0
- package/src/coValues/coList.ts +129 -78
- package/src/coValues/coMap.ts +10 -12
- package/src/coValues/group.ts +14 -1
- package/src/config.ts +9 -0
- package/src/crypto/PureJSCrypto.ts +25 -13
- package/src/exports.ts +13 -2
- package/src/ids.ts +1 -0
- package/src/localNode.ts +8 -2
- package/src/storage/storageAsync.ts +8 -5
- package/src/storage/storageSync.ts +8 -4
- package/src/sync.ts +8 -32
- package/src/tests/branching.test.ts +255 -4
- package/src/tests/coList.test.ts +529 -1
- package/src/tests/coValueCore.test.ts +62 -2
- package/src/tests/sync.content.test.ts +153 -0
- package/src/tests/sync.load.test.ts +4 -4
- package/src/tests/sync.storage.test.ts +1 -1
- package/src/tests/sync.storageAsync.test.ts +9 -12
- package/src/tests/sync.upload.test.ts +2 -2
|
@@ -1,8 +1,41 @@
|
|
|
1
|
-
import { CoValueCore } from "../exports.js";
|
|
2
|
-
import { RawCoID, SessionID } from "../ids.js";
|
|
3
|
-
import { AvailableCoValueCore, idforHeader } from "./coValueCore.js";
|
|
4
|
-
import { CoValueHeader } from "./verifiedState.js";
|
|
5
|
-
import { CoValueKnownState } from "../sync.js";
|
|
1
|
+
import type { CoValueCore } from "../exports.js";
|
|
2
|
+
import type { RawCoID, SessionID } from "../ids.js";
|
|
3
|
+
import { type AvailableCoValueCore, idforHeader } from "./coValueCore.js";
|
|
4
|
+
import type { CoValueHeader } from "./verifiedState.js";
|
|
5
|
+
import type { CoValueKnownState } from "../sync.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Commit to identify the starting point of the branch
|
|
9
|
+
*
|
|
10
|
+
* In case of clonflicts, the first commit of this kind is considered the source of truth
|
|
11
|
+
*/
|
|
12
|
+
export type BranchStartCommit = {
|
|
13
|
+
from: CoValueKnownState["sessions"];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Commit that tracks a branch creation
|
|
18
|
+
*/
|
|
19
|
+
export type BranchPointerCommit = {
|
|
20
|
+
branch: string;
|
|
21
|
+
ownerId?: RawCoID;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Meta information attached to each merged transaction to retrieve the original transaction ID
|
|
26
|
+
*/
|
|
27
|
+
export type MergedTransactionMetadata = {
|
|
28
|
+
mi: number; // Transaction index and marker of a merge commit
|
|
29
|
+
s?: SessionID;
|
|
30
|
+
b?: RawCoID;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Merge commit located in a branch to track how many transactions have already been merged
|
|
35
|
+
*/
|
|
36
|
+
export type MergeCommit = {
|
|
37
|
+
merged: CoValueKnownState["sessions"];
|
|
38
|
+
};
|
|
6
39
|
|
|
7
40
|
export function getBranchHeader({
|
|
8
41
|
type,
|
|
@@ -48,30 +81,37 @@ export function getBranchId(
|
|
|
48
81
|
);
|
|
49
82
|
}
|
|
50
83
|
|
|
51
|
-
|
|
52
|
-
const header = coValue.verified.header;
|
|
84
|
+
const currentOwnerId = ownerId ?? getBranchOwnerId(coValue);
|
|
53
85
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return coValue.id;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
ownerId = header.ruleset.group;
|
|
86
|
+
if (!currentOwnerId) {
|
|
87
|
+
return coValue.id;
|
|
60
88
|
}
|
|
61
89
|
|
|
62
90
|
const header = getBranchHeader({
|
|
63
91
|
type: coValue.verified.header.type,
|
|
64
92
|
branchName: name,
|
|
65
|
-
ownerId,
|
|
93
|
+
ownerId: currentOwnerId,
|
|
66
94
|
sourceId: coValue.id,
|
|
67
95
|
});
|
|
68
96
|
|
|
69
97
|
return idforHeader(header, coValue.node.crypto);
|
|
70
98
|
}
|
|
71
99
|
|
|
72
|
-
export
|
|
73
|
-
|
|
74
|
-
|
|
100
|
+
export function getBranchOwnerId(coValue: CoValueCore) {
|
|
101
|
+
if (!coValue.verified) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
"CoValueCore: getBranchOwnerId called on coValue without verified state",
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const header = coValue.verified.header;
|
|
108
|
+
|
|
109
|
+
if (header.ruleset.type !== "ownedByGroup") {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return header.ruleset.group;
|
|
114
|
+
}
|
|
75
115
|
|
|
76
116
|
/**
|
|
77
117
|
* Given a coValue, a branch name and an owner id, creates a new branch CoValue
|
|
@@ -87,32 +127,34 @@ export function createBranch(
|
|
|
87
127
|
);
|
|
88
128
|
}
|
|
89
129
|
|
|
90
|
-
|
|
91
|
-
const header = coValue.verified.header;
|
|
92
|
-
|
|
93
|
-
// Group and account coValues can't have branches, so we return the source coValue
|
|
94
|
-
if (header.ruleset.type !== "ownedByGroup") {
|
|
95
|
-
return coValue;
|
|
96
|
-
}
|
|
130
|
+
const branchOwnerId = ownerId ?? getBranchOwnerId(coValue);
|
|
97
131
|
|
|
98
|
-
|
|
132
|
+
if (!branchOwnerId) {
|
|
133
|
+
return coValue;
|
|
99
134
|
}
|
|
100
135
|
|
|
101
136
|
const header = getBranchHeader({
|
|
102
137
|
type: coValue.verified.header.type,
|
|
103
138
|
branchName: name,
|
|
104
|
-
ownerId,
|
|
139
|
+
ownerId: branchOwnerId,
|
|
105
140
|
sourceId: coValue.id,
|
|
106
141
|
});
|
|
107
142
|
|
|
108
|
-
const
|
|
143
|
+
const branch = coValue.node.createCoValue(header);
|
|
144
|
+
const sessions = { ...coValue.knownState().sessions };
|
|
109
145
|
|
|
110
146
|
// Create a branch commit to identify the starting point of the branch
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
} satisfies
|
|
147
|
+
branch.makeTransaction([], "private", {
|
|
148
|
+
from: sessions,
|
|
149
|
+
} satisfies BranchStartCommit);
|
|
150
|
+
|
|
151
|
+
// Create a branch pointer, to identify that we created a branch
|
|
152
|
+
coValue.makeTransaction([], "private", {
|
|
153
|
+
branch: name,
|
|
154
|
+
ownerId,
|
|
155
|
+
} satisfies BranchPointerCommit);
|
|
114
156
|
|
|
115
|
-
return
|
|
157
|
+
return branch;
|
|
116
158
|
}
|
|
117
159
|
|
|
118
160
|
/**
|
|
@@ -125,7 +167,7 @@ export function getBranchSource(
|
|
|
125
167
|
return undefined;
|
|
126
168
|
}
|
|
127
169
|
|
|
128
|
-
const sourceId = coValue.
|
|
170
|
+
const sourceId = coValue.getCurrentBranchSourceId();
|
|
129
171
|
|
|
130
172
|
if (!sourceId) {
|
|
131
173
|
return undefined;
|
|
@@ -140,15 +182,6 @@ export function getBranchSource(
|
|
|
140
182
|
return source;
|
|
141
183
|
}
|
|
142
184
|
|
|
143
|
-
export type MergeCommit = {
|
|
144
|
-
// The point where the branch was merged
|
|
145
|
-
merge: CoValueKnownState["sessions"];
|
|
146
|
-
// The id of the branch that was merged
|
|
147
|
-
id: RawCoID;
|
|
148
|
-
// The number of transactions that were merged, will be used in the future to handle the edits history properly
|
|
149
|
-
count: number;
|
|
150
|
-
};
|
|
151
|
-
|
|
152
185
|
/**
|
|
153
186
|
* Given a branch coValue, merges the branch into the source coValue
|
|
154
187
|
*/
|
|
@@ -159,10 +192,8 @@ export function mergeBranch(branch: CoValueCore): CoValueCore {
|
|
|
159
192
|
);
|
|
160
193
|
}
|
|
161
194
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (!sourceId) {
|
|
165
|
-
throw new Error("CoValueCore: mergeBranch called on a non-branch coValue");
|
|
195
|
+
if (branch.verified.header.ruleset.type !== "ownedByGroup") {
|
|
196
|
+
return branch;
|
|
166
197
|
}
|
|
167
198
|
|
|
168
199
|
const target = getBranchSource(branch);
|
|
@@ -173,13 +204,9 @@ export function mergeBranch(branch: CoValueCore): CoValueCore {
|
|
|
173
204
|
|
|
174
205
|
// Look for previous merge commits, to see which transactions needs to be merged
|
|
175
206
|
// Done mostly for performance reasons, as we could merge all the transactions every time and nothing would change
|
|
176
|
-
const mergedTransactions =
|
|
177
|
-
(acc, {
|
|
178
|
-
|
|
179
|
-
return acc;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
for (const [sessionID, count] of Object.entries(commit.merge) as [
|
|
207
|
+
const mergedTransactions = branch.getMergeCommits().reduce(
|
|
208
|
+
(acc, { merged }) => {
|
|
209
|
+
for (const [sessionID, count] of Object.entries(merged) as [
|
|
183
210
|
SessionID,
|
|
184
211
|
number,
|
|
185
212
|
][]) {
|
|
@@ -205,16 +232,34 @@ export function mergeBranch(branch: CoValueCore): CoValueCore {
|
|
|
205
232
|
return target;
|
|
206
233
|
}
|
|
207
234
|
|
|
208
|
-
//
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
235
|
+
// We do track in the meta information the original txID to make sure that
|
|
236
|
+
// the CoList opid still point to the correct transaction
|
|
237
|
+
// To reduce the cost of the meta we skip the repeated information
|
|
238
|
+
let lastSessionId: string | undefined = undefined;
|
|
239
|
+
let lastBranchId: string | undefined = undefined;
|
|
240
|
+
|
|
241
|
+
for (const tx of branchValidTransactions) {
|
|
242
|
+
const mergeMeta: MergedTransactionMetadata = {
|
|
243
|
+
mi: tx.txID.txIndex,
|
|
244
|
+
};
|
|
214
245
|
|
|
215
|
-
|
|
216
|
-
|
|
246
|
+
if (lastSessionId !== tx.txID.sessionID) {
|
|
247
|
+
mergeMeta.s = tx.txID.sessionID;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (lastBranchId !== tx.txID.branch) {
|
|
251
|
+
mergeMeta.b = tx.txID.branch;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
target.makeTransaction(tx.changes, tx.tx.privacy, mergeMeta, tx.madeAt);
|
|
255
|
+
lastSessionId = tx.txID.sessionID;
|
|
256
|
+
lastBranchId = tx.txID.branch;
|
|
217
257
|
}
|
|
218
258
|
|
|
259
|
+
// Track the merged transactions for the branch, so future merges will know which transactions have already been merged
|
|
260
|
+
branch.makeTransaction([], "private", {
|
|
261
|
+
merged: branch.knownState().sessions,
|
|
262
|
+
} satisfies MergeCommit);
|
|
263
|
+
|
|
219
264
|
return target;
|
|
220
265
|
}
|
|
@@ -5,10 +5,10 @@ import type { RawCoValue } from "../coValue.js";
|
|
|
5
5
|
import type { ControlledAccountOrAgent } from "../coValues/account.js";
|
|
6
6
|
import type { RawGroup } from "../coValues/group.js";
|
|
7
7
|
import { CO_VALUE_LOADING_CONFIG } from "../config.js";
|
|
8
|
+
import { validateTxSizeLimitInBytes } from "../coValueContentMessage.js";
|
|
8
9
|
import { coreToCoValue } from "../coreToCoValue.js";
|
|
9
10
|
import {
|
|
10
11
|
CryptoProvider,
|
|
11
|
-
Encrypted,
|
|
12
12
|
Hash,
|
|
13
13
|
KeyID,
|
|
14
14
|
KeySecret,
|
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
} from "../crypto/crypto.js";
|
|
18
18
|
import { AgentID, RawCoID, SessionID, TransactionID } from "../ids.js";
|
|
19
19
|
import { JsonObject, JsonValue } from "../jsonValue.js";
|
|
20
|
-
import { parseJSON, safeParseJSON } from "../jsonStringify.js";
|
|
21
20
|
import { LocalNode, ResolveAccountAgentError } from "../localNode.js";
|
|
22
21
|
import { logger } from "../logger.js";
|
|
23
22
|
import { determineValidTransactions } from "../permissions.js";
|
|
@@ -29,10 +28,14 @@ import { CoValueHeader, Transaction, VerifiedState } from "./verifiedState.js";
|
|
|
29
28
|
import { SessionMap } from "./SessionMap.js";
|
|
30
29
|
import {
|
|
31
30
|
MergeCommit,
|
|
31
|
+
BranchPointerCommit,
|
|
32
|
+
MergedTransactionMetadata,
|
|
32
33
|
createBranch,
|
|
33
34
|
getBranchId,
|
|
35
|
+
getBranchOwnerId,
|
|
34
36
|
getBranchSource,
|
|
35
37
|
mergeBranch,
|
|
38
|
+
BranchStartCommit,
|
|
36
39
|
} from "./branching.js";
|
|
37
40
|
import { type RawAccountID } from "../coValues/account.js";
|
|
38
41
|
import { decodeTransactionChangesAndMeta } from "./decodeTransactionChangesAndMeta.js";
|
|
@@ -70,6 +73,9 @@ export type VerifiedTransaction = {
|
|
|
70
73
|
|
|
71
74
|
// True if the meta information has been parsed and loaded in the CoValueCore
|
|
72
75
|
hasMetaBeenParsed: boolean;
|
|
76
|
+
|
|
77
|
+
// The previous verified transaction for the same session
|
|
78
|
+
previous: VerifiedTransaction | undefined;
|
|
73
79
|
};
|
|
74
80
|
|
|
75
81
|
export type DecryptedTransaction = {
|
|
@@ -215,6 +221,20 @@ export class CoValueCore {
|
|
|
215
221
|
});
|
|
216
222
|
}
|
|
217
223
|
|
|
224
|
+
waitForFullStreaming(): Promise<CoValueCore> {
|
|
225
|
+
return new Promise<CoValueCore>((resolve) => {
|
|
226
|
+
const listener = (core: CoValueCore) => {
|
|
227
|
+
if (core.isAvailable() && !core.verified.isStreaming()) {
|
|
228
|
+
resolve(core);
|
|
229
|
+
this.listeners.delete(listener);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
this.listeners.add(listener);
|
|
234
|
+
listener(this);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
218
238
|
getStateForPeer(peerId: PeerID) {
|
|
219
239
|
return this.peers.get(peerId);
|
|
220
240
|
}
|
|
@@ -586,6 +606,7 @@ export class CoValueCore {
|
|
|
586
606
|
changes: JsonValue[],
|
|
587
607
|
privacy: "private" | "trusting",
|
|
588
608
|
meta?: JsonObject,
|
|
609
|
+
madeAt?: number,
|
|
589
610
|
): boolean {
|
|
590
611
|
if (!this.verified) {
|
|
591
612
|
throw new Error(
|
|
@@ -593,6 +614,8 @@ export class CoValueCore {
|
|
|
593
614
|
);
|
|
594
615
|
}
|
|
595
616
|
|
|
617
|
+
validateTxSizeLimitInBytes(changes);
|
|
618
|
+
|
|
596
619
|
// This is an ugly hack to get a unique but stable session ID for editing the current account
|
|
597
620
|
const sessionID =
|
|
598
621
|
this.verified.header.meta?.type === "account"
|
|
@@ -620,6 +643,7 @@ export class CoValueCore {
|
|
|
620
643
|
keyID,
|
|
621
644
|
keySecret,
|
|
622
645
|
meta,
|
|
646
|
+
madeAt ?? Date.now(),
|
|
623
647
|
);
|
|
624
648
|
} else {
|
|
625
649
|
result = this.verified.makeNewTrustingTransaction(
|
|
@@ -627,6 +651,7 @@ export class CoValueCore {
|
|
|
627
651
|
signerAgent,
|
|
628
652
|
changes,
|
|
629
653
|
meta,
|
|
654
|
+
madeAt ?? Date.now(),
|
|
630
655
|
);
|
|
631
656
|
}
|
|
632
657
|
|
|
@@ -675,12 +700,13 @@ export class CoValueCore {
|
|
|
675
700
|
}
|
|
676
701
|
|
|
677
702
|
// The starting point of the branch, in case this CoValue is a branch
|
|
678
|
-
branchStart:
|
|
679
|
-
| { branch: CoValueKnownState["sessions"]; madeAt: number }
|
|
680
|
-
| undefined;
|
|
703
|
+
branchStart: { from: BranchStartCommit["from"]; madeAt: number } | undefined;
|
|
681
704
|
|
|
682
705
|
// The list of merge commits that have been made
|
|
683
|
-
mergeCommits:
|
|
706
|
+
mergeCommits: MergeCommit[] = [];
|
|
707
|
+
branches: BranchPointerCommit[] = [];
|
|
708
|
+
earliestTxMadeAt: number = Number.MAX_SAFE_INTEGER;
|
|
709
|
+
latestTxMadeAt: number = 0;
|
|
684
710
|
|
|
685
711
|
// Reset the parsed transactions and branches, to validate them again from scratch when the group is updated
|
|
686
712
|
resetParsedTransactions() {
|
|
@@ -696,6 +722,11 @@ export class CoValueCore {
|
|
|
696
722
|
verifiedTransactions: VerifiedTransaction[] = [];
|
|
697
723
|
private verifiedTransactionsKnownSessions: CoValueKnownState["sessions"] = {};
|
|
698
724
|
|
|
725
|
+
private lastVerifiedTransactionBySessionID: Record<
|
|
726
|
+
SessionID,
|
|
727
|
+
VerifiedTransaction
|
|
728
|
+
> = {};
|
|
729
|
+
|
|
699
730
|
/**
|
|
700
731
|
* Loads the new transaction from the SessionMap into verifiedTransactions as a VerifiedTransaction.
|
|
701
732
|
*
|
|
@@ -717,6 +748,8 @@ export class CoValueCore {
|
|
|
717
748
|
return;
|
|
718
749
|
}
|
|
719
750
|
|
|
751
|
+
const isBranch = this.isBranch();
|
|
752
|
+
|
|
720
753
|
for (const [sessionID, sessionLog] of this.verified.sessions.entries()) {
|
|
721
754
|
const count = this.verifiedTransactionsKnownSessions[sessionID] ?? 0;
|
|
722
755
|
|
|
@@ -725,12 +758,20 @@ export class CoValueCore {
|
|
|
725
758
|
return;
|
|
726
759
|
}
|
|
727
760
|
|
|
728
|
-
|
|
761
|
+
const txID = isBranch
|
|
762
|
+
? {
|
|
763
|
+
sessionID,
|
|
764
|
+
txIndex,
|
|
765
|
+
branch: this.id,
|
|
766
|
+
}
|
|
767
|
+
: {
|
|
768
|
+
sessionID,
|
|
769
|
+
txIndex,
|
|
770
|
+
};
|
|
771
|
+
|
|
772
|
+
const verifiedTransaction = {
|
|
729
773
|
author: accountOrAgentIDfromSessionID(sessionID),
|
|
730
|
-
txID
|
|
731
|
-
sessionID,
|
|
732
|
-
txIndex,
|
|
733
|
-
},
|
|
774
|
+
txID,
|
|
734
775
|
madeAt: tx.madeAt,
|
|
735
776
|
isValidated: false,
|
|
736
777
|
isValid: false,
|
|
@@ -740,7 +781,20 @@ export class CoValueCore {
|
|
|
740
781
|
hasInvalidMeta: false,
|
|
741
782
|
hasMetaBeenParsed: false,
|
|
742
783
|
tx,
|
|
743
|
-
|
|
784
|
+
previous: this.lastVerifiedTransactionBySessionID[sessionID],
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
if (verifiedTransaction.madeAt > this.latestTxMadeAt) {
|
|
788
|
+
this.latestTxMadeAt = verifiedTransaction.madeAt;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
if (verifiedTransaction.madeAt < this.earliestTxMadeAt) {
|
|
792
|
+
this.earliestTxMadeAt = verifiedTransaction.madeAt;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
this.verifiedTransactions.push(verifiedTransaction);
|
|
796
|
+
this.lastVerifiedTransactionBySessionID[sessionID] =
|
|
797
|
+
verifiedTransaction;
|
|
744
798
|
});
|
|
745
799
|
|
|
746
800
|
this.verifiedTransactionsKnownSessions[sessionID] =
|
|
@@ -769,23 +823,54 @@ export class CoValueCore {
|
|
|
769
823
|
|
|
770
824
|
transaction.hasMetaBeenParsed = true;
|
|
771
825
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
826
|
+
// Branch related meta information
|
|
827
|
+
if (this.isBranch()) {
|
|
828
|
+
// Check if the transaction is a branch start
|
|
829
|
+
if ("from" in transaction.meta) {
|
|
830
|
+
if (!this.branchStart || transaction.madeAt < this.branchStart.madeAt) {
|
|
831
|
+
const commit = transaction.meta as BranchStartCommit;
|
|
832
|
+
|
|
833
|
+
this.branchStart = {
|
|
834
|
+
from: commit.from,
|
|
835
|
+
madeAt: transaction.madeAt,
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Check if the transaction is a merged checkpoint for a branch
|
|
841
|
+
if ("merged" in transaction.meta) {
|
|
842
|
+
const mergeCommit = transaction.meta as MergeCommit;
|
|
843
|
+
this.mergeCommits.push(mergeCommit);
|
|
844
|
+
}
|
|
780
845
|
}
|
|
781
846
|
|
|
782
|
-
if
|
|
783
|
-
|
|
847
|
+
// Check if the transaction is a branch pointer
|
|
848
|
+
if ("branch" in transaction.meta) {
|
|
849
|
+
const branch = transaction.meta as BranchPointerCommit;
|
|
784
850
|
|
|
785
|
-
this.
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
851
|
+
this.branches.push(branch);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// Check if the transaction has been merged from a branch
|
|
855
|
+
if ("mi" in transaction.meta) {
|
|
856
|
+
const meta = transaction.meta as MergedTransactionMetadata;
|
|
857
|
+
|
|
858
|
+
// Check if the transaction is a merge commit
|
|
859
|
+
const previousTransaction = transaction.previous?.txID;
|
|
860
|
+
const sessionID = meta.s ?? previousTransaction?.sessionID;
|
|
861
|
+
|
|
862
|
+
if (sessionID) {
|
|
863
|
+
transaction.txID = {
|
|
864
|
+
sessionID,
|
|
865
|
+
txIndex: meta.mi,
|
|
866
|
+
branch: meta.b ?? previousTransaction?.branch,
|
|
867
|
+
};
|
|
868
|
+
} else {
|
|
869
|
+
logger.error("Merge commit without session ID", {
|
|
870
|
+
txID: transaction.txID,
|
|
871
|
+
prev: previousTransaction ?? null,
|
|
872
|
+
});
|
|
873
|
+
}
|
|
789
874
|
}
|
|
790
875
|
}
|
|
791
876
|
|
|
@@ -836,6 +921,8 @@ export class CoValueCore {
|
|
|
836
921
|
|
|
837
922
|
const matchingTransactions: DecryptedTransaction[] = [];
|
|
838
923
|
|
|
924
|
+
const source = getBranchSource(this);
|
|
925
|
+
|
|
839
926
|
for (const transaction of this.verifiedTransactions) {
|
|
840
927
|
if (!isValidTransactionWithChanges(transaction)) {
|
|
841
928
|
continue;
|
|
@@ -847,10 +934,12 @@ export class CoValueCore {
|
|
|
847
934
|
|
|
848
935
|
options?.knownTransactions?.add(transaction.tx);
|
|
849
936
|
|
|
850
|
-
const { txID
|
|
937
|
+
const { txID } = transaction;
|
|
851
938
|
|
|
852
939
|
const from = options?.from?.[txID.sessionID] ?? -1;
|
|
853
|
-
|
|
940
|
+
|
|
941
|
+
// Load the to filter index. Sessions that are not in the to filter will be skipped
|
|
942
|
+
const to = options?.to ? (options.to[txID.sessionID] ?? -1) : Infinity;
|
|
854
943
|
|
|
855
944
|
// The txIndex starts at 0 and from/to are referring to the count of transactions
|
|
856
945
|
if (from > txID.txIndex || to < txID.txIndex) {
|
|
@@ -860,26 +949,16 @@ export class CoValueCore {
|
|
|
860
949
|
matchingTransactions.push(transaction);
|
|
861
950
|
}
|
|
862
951
|
|
|
863
|
-
const source = getBranchSource(this);
|
|
864
|
-
|
|
865
952
|
// If this is a branch, we load the valid transactions from the source
|
|
866
953
|
if (source && this.branchStart && !options?.skipBranchSource) {
|
|
867
954
|
const sourceTransactions = source.getValidTransactions({
|
|
868
|
-
to: this.branchStart.
|
|
955
|
+
to: this.branchStart.from,
|
|
869
956
|
ignorePrivateTransactions: options?.ignorePrivateTransactions ?? false,
|
|
870
957
|
knownTransactions: options?.knownTransactions,
|
|
871
958
|
});
|
|
872
959
|
|
|
873
|
-
for (const
|
|
874
|
-
matchingTransactions.push(
|
|
875
|
-
txID: {
|
|
876
|
-
sessionID: `${txID.sessionID}_branch_${source.id}`,
|
|
877
|
-
txIndex: txID.txIndex,
|
|
878
|
-
},
|
|
879
|
-
madeAt,
|
|
880
|
-
changes,
|
|
881
|
-
tx,
|
|
882
|
-
});
|
|
960
|
+
for (const transaction of sourceTransactions) {
|
|
961
|
+
matchingTransactions.push(transaction);
|
|
883
962
|
}
|
|
884
963
|
}
|
|
885
964
|
|
|
@@ -898,6 +977,49 @@ export class CoValueCore {
|
|
|
898
977
|
return this.node.getCoValue(getBranchId(this, name, ownerId));
|
|
899
978
|
}
|
|
900
979
|
|
|
980
|
+
getCurrentBranchName() {
|
|
981
|
+
return this.verified?.branchName;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
getCurrentBranchSourceId() {
|
|
985
|
+
return this.verified?.branchSourceId;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
isBranch() {
|
|
989
|
+
return Boolean(this.verified?.branchSourceId);
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
hasBranch(name: string, ownerId?: RawCoID) {
|
|
993
|
+
// This function requires the meta information to be parsed, which might not be the case
|
|
994
|
+
// if the value content hasn't been loaded yet
|
|
995
|
+
this.parseNewTransactions(false);
|
|
996
|
+
|
|
997
|
+
const currentOwnerId = getBranchOwnerId(this);
|
|
998
|
+
return this.branches.some((item) => {
|
|
999
|
+
if (item.branch !== name) {
|
|
1000
|
+
return false;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
if (item.ownerId === ownerId) {
|
|
1004
|
+
return true;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
if (!ownerId) {
|
|
1008
|
+
return item.ownerId === currentOwnerId;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
if (!item.ownerId) {
|
|
1012
|
+
return ownerId === currentOwnerId;
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
getMergeCommits() {
|
|
1018
|
+
this.parseNewTransactions(false);
|
|
1019
|
+
|
|
1020
|
+
return this.mergeCommits;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
901
1023
|
getValidSortedTransactions(options?: {
|
|
902
1024
|
ignorePrivateTransactions: boolean;
|
|
903
1025
|
|
|
@@ -59,6 +59,8 @@ export class VerifiedState {
|
|
|
59
59
|
private _cachedNewContentSinceEmpty: NewContentMessage[] | undefined;
|
|
60
60
|
private streamingKnownState?: CoValueKnownState["sessions"];
|
|
61
61
|
public lastAccessed: number | undefined;
|
|
62
|
+
public branchSourceId?: RawCoID;
|
|
63
|
+
public branchName?: string;
|
|
62
64
|
|
|
63
65
|
constructor(
|
|
64
66
|
id: RawCoID,
|
|
@@ -74,6 +76,8 @@ export class VerifiedState {
|
|
|
74
76
|
this.streamingKnownState = streamingKnownState
|
|
75
77
|
? { ...streamingKnownState }
|
|
76
78
|
: undefined;
|
|
79
|
+
this.branchSourceId = header.meta?.source as RawCoID | undefined;
|
|
80
|
+
this.branchName = header.meta?.branch as string | undefined;
|
|
77
81
|
}
|
|
78
82
|
|
|
79
83
|
clone(): VerifiedState {
|
|
@@ -114,12 +118,14 @@ export class VerifiedState {
|
|
|
114
118
|
signerAgent: ControlledAccountOrAgent,
|
|
115
119
|
changes: JsonValue[],
|
|
116
120
|
meta: JsonObject | undefined,
|
|
121
|
+
madeAt: number,
|
|
117
122
|
) {
|
|
118
123
|
const result = this.sessions.makeNewTrustingTransaction(
|
|
119
124
|
sessionID,
|
|
120
125
|
signerAgent,
|
|
121
126
|
changes,
|
|
122
127
|
meta,
|
|
128
|
+
madeAt,
|
|
123
129
|
);
|
|
124
130
|
|
|
125
131
|
this._cachedNewContentSinceEmpty = undefined;
|
|
@@ -135,6 +141,7 @@ export class VerifiedState {
|
|
|
135
141
|
keyID: KeyID,
|
|
136
142
|
keySecret: KeySecret,
|
|
137
143
|
meta: JsonObject | undefined,
|
|
144
|
+
madeAt: number,
|
|
138
145
|
) {
|
|
139
146
|
const result = this.sessions.makeNewPrivateTransaction(
|
|
140
147
|
sessionID,
|
|
@@ -143,6 +150,7 @@ export class VerifiedState {
|
|
|
143
150
|
keyID,
|
|
144
151
|
keySecret,
|
|
145
152
|
meta,
|
|
153
|
+
madeAt,
|
|
146
154
|
);
|
|
147
155
|
|
|
148
156
|
this._cachedNewContentSinceEmpty = undefined;
|