cojson 0.13.17 → 0.13.18
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 +9 -0
- package/dist/PeerState.d.ts +3 -0
- package/dist/PeerState.d.ts.map +1 -1
- package/dist/PeerState.js +9 -0
- package/dist/PeerState.js.map +1 -1
- package/dist/SyncStateManager.d.ts.map +1 -1
- package/dist/SyncStateManager.js +2 -3
- package/dist/SyncStateManager.js.map +1 -1
- package/dist/coValue.d.ts +4 -4
- package/dist/coValue.d.ts.map +1 -1
- package/dist/coValue.js +4 -4
- package/dist/coValue.js.map +1 -1
- package/dist/coValueCore/coValueCore.d.ts +143 -0
- package/dist/coValueCore/coValueCore.d.ts.map +1 -0
- package/dist/{coValueCore.js → coValueCore/coValueCore.js} +314 -246
- package/dist/coValueCore/coValueCore.js.map +1 -0
- package/dist/coValueCore/verifiedState.d.ts +65 -0
- package/dist/coValueCore/verifiedState.d.ts.map +1 -0
- package/dist/coValueCore/verifiedState.js +210 -0
- package/dist/coValueCore/verifiedState.js.map +1 -0
- package/dist/coValues/account.d.ts +8 -10
- package/dist/coValues/account.d.ts.map +1 -1
- package/dist/coValues/account.js +12 -13
- package/dist/coValues/account.js.map +1 -1
- package/dist/coValues/coList.d.ts +3 -3
- package/dist/coValues/coList.d.ts.map +1 -1
- package/dist/coValues/coList.js +6 -3
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.d.ts +3 -3
- package/dist/coValues/coMap.d.ts.map +1 -1
- package/dist/coValues/coMap.js +3 -3
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/coPlainText.d.ts +2 -2
- package/dist/coValues/coPlainText.d.ts.map +1 -1
- package/dist/coValues/coPlainText.js +4 -4
- package/dist/coValues/coPlainText.js.map +1 -1
- package/dist/coValues/coStream.d.ts +3 -3
- package/dist/coValues/coStream.d.ts.map +1 -1
- package/dist/coValues/coStream.js +3 -3
- package/dist/coValues/coStream.js.map +1 -1
- package/dist/coValues/group.d.ts +7 -2
- package/dist/coValues/group.d.ts.map +1 -1
- package/dist/coValues/group.js +29 -26
- package/dist/coValues/group.js.map +1 -1
- package/dist/coreToCoValue.d.ts +2 -2
- package/dist/coreToCoValue.d.ts.map +1 -1
- package/dist/coreToCoValue.js +10 -14
- package/dist/coreToCoValue.js.map +1 -1
- package/dist/exports.d.ts +6 -5
- package/dist/exports.d.ts.map +1 -1
- package/dist/exports.js +3 -4
- package/dist/exports.js.map +1 -1
- package/dist/localNode.d.ts +30 -24
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +139 -170
- package/dist/localNode.js.map +1 -1
- package/dist/permissions.d.ts +2 -1
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +15 -11
- package/dist/permissions.js.map +1 -1
- package/dist/priority.d.ts +1 -1
- package/dist/priority.d.ts.map +1 -1
- package/dist/sync.d.ts +2 -2
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +86 -55
- package/dist/sync.js.map +1 -1
- package/dist/tests/coList.test.js +19 -16
- package/dist/tests/coList.test.js.map +1 -1
- package/dist/tests/coMap.test.js +12 -13
- package/dist/tests/coMap.test.js.map +1 -1
- package/dist/tests/coPlainText.test.js +9 -10
- package/dist/tests/coPlainText.test.js.map +1 -1
- package/dist/tests/coStream.test.js +22 -17
- package/dist/tests/coStream.test.js.map +1 -1
- package/dist/tests/coValueCore.test.js +22 -28
- package/dist/tests/coValueCore.test.js.map +1 -1
- package/dist/tests/coValueCoreLoadingState.test.d.ts +2 -0
- package/dist/tests/coValueCoreLoadingState.test.d.ts.map +1 -0
- package/dist/tests/{coValueState.test.js → coValueCoreLoadingState.test.js} +62 -46
- package/dist/tests/coValueCoreLoadingState.test.js.map +1 -0
- package/dist/tests/group.test.js +42 -43
- package/dist/tests/group.test.js.map +1 -1
- package/dist/tests/messagesTestUtils.d.ts +2 -2
- package/dist/tests/messagesTestUtils.d.ts.map +1 -1
- package/dist/tests/messagesTestUtils.js +1 -1
- package/dist/tests/messagesTestUtils.js.map +1 -1
- package/dist/tests/permissions.test.js +224 -292
- package/dist/tests/permissions.test.js.map +1 -1
- package/dist/tests/priority.test.js +13 -14
- package/dist/tests/priority.test.js.map +1 -1
- package/dist/tests/sync.auth.test.d.ts +2 -0
- package/dist/tests/sync.auth.test.d.ts.map +1 -0
- package/dist/tests/sync.auth.test.js +141 -0
- package/dist/tests/sync.auth.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.mesh.test.js +25 -12
- package/dist/tests/sync.mesh.test.js.map +1 -1
- package/dist/tests/sync.peerReconciliation.test.js +19 -19
- package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
- package/dist/tests/sync.storage.test.js +20 -13
- package/dist/tests/sync.storage.test.js.map +1 -1
- package/dist/tests/sync.test.js +32 -39
- package/dist/tests/sync.test.js.map +1 -1
- package/dist/tests/sync.upload.test.js +126 -37
- package/dist/tests/sync.upload.test.js.map +1 -1
- package/dist/tests/testUtils.d.ts +24 -15
- package/dist/tests/testUtils.d.ts.map +1 -1
- package/dist/tests/testUtils.js +88 -61
- package/dist/tests/testUtils.js.map +1 -1
- package/dist/typeUtils/expectGroup.js +1 -1
- package/dist/typeUtils/expectGroup.js.map +1 -1
- package/package.json +1 -1
- package/src/PeerState.ts +11 -0
- package/src/SyncStateManager.ts +2 -3
- package/src/coValue.ts +11 -8
- package/src/{coValueCore.ts → coValueCore/coValueCore.ts} +469 -413
- package/src/coValueCore/verifiedState.ts +376 -0
- package/src/coValues/account.ts +20 -25
- package/src/coValues/coList.ts +12 -6
- package/src/coValues/coMap.ts +9 -6
- package/src/coValues/coPlainText.ts +9 -6
- package/src/coValues/coStream.ts +9 -6
- package/src/coValues/group.ts +50 -28
- package/src/coreToCoValue.ts +14 -15
- package/src/exports.ts +9 -7
- package/src/localNode.ts +227 -273
- package/src/permissions.ts +18 -12
- package/src/priority.ts +1 -1
- package/src/sync.ts +96 -63
- package/src/tests/coList.test.ts +21 -15
- package/src/tests/coMap.test.ts +12 -13
- package/src/tests/coPlainText.test.ts +12 -9
- package/src/tests/coStream.test.ts +25 -16
- package/src/tests/coValueCore.test.ts +30 -27
- package/src/tests/{coValueState.test.ts → coValueCoreLoadingState.test.ts} +67 -57
- package/src/tests/group.test.ts +44 -68
- package/src/tests/messagesTestUtils.ts +3 -8
- package/src/tests/permissions.test.ts +283 -449
- package/src/tests/priority.test.ts +17 -13
- package/src/tests/sync.auth.test.ts +188 -0
- package/src/tests/sync.load.test.ts +4 -4
- package/src/tests/sync.mesh.test.ts +25 -12
- package/src/tests/sync.peerReconciliation.test.ts +25 -25
- package/src/tests/sync.storage.test.ts +20 -13
- package/src/tests/sync.test.ts +43 -43
- package/src/tests/sync.upload.test.ts +157 -37
- package/src/tests/testUtils.ts +120 -74
- package/src/typeUtils/expectGroup.ts +1 -1
- package/dist/CoValuesStore.d.ts +0 -14
- package/dist/CoValuesStore.d.ts.map +0 -1
- package/dist/CoValuesStore.js +0 -32
- package/dist/CoValuesStore.js.map +0 -1
- package/dist/coValueCore.d.ts +0 -142
- package/dist/coValueCore.d.ts.map +0 -1
- package/dist/coValueCore.js.map +0 -1
- package/dist/coValueState.d.ts +0 -34
- package/dist/coValueState.d.ts.map +0 -1
- package/dist/coValueState.js +0 -190
- package/dist/coValueState.js.map +0 -1
- package/dist/tests/coValueState.test.d.ts +0 -2
- package/dist/tests/coValueState.test.d.ts.map +0 -1
- package/dist/tests/coValueState.test.js.map +0 -1
- package/src/CoValuesStore.ts +0 -41
- package/src/coValueState.ts +0 -245
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
import { Result, err, ok } from "neverthrow";
|
|
2
|
+
import { AnyRawCoValue } from "../coValue.js";
|
|
3
|
+
import {
|
|
4
|
+
CryptoProvider,
|
|
5
|
+
Encrypted,
|
|
6
|
+
Hash,
|
|
7
|
+
KeyID,
|
|
8
|
+
Signature,
|
|
9
|
+
SignerID,
|
|
10
|
+
StreamingHash,
|
|
11
|
+
} from "../crypto/crypto.js";
|
|
12
|
+
import { RawCoID, SessionID, TransactionID } from "../ids.js";
|
|
13
|
+
import { Stringified } from "../jsonStringify.js";
|
|
14
|
+
import { JsonObject, JsonValue } from "../jsonValue.js";
|
|
15
|
+
import { PermissionsDef as RulesetDef } from "../permissions.js";
|
|
16
|
+
import { getPriorityFromHeader } from "../priority.js";
|
|
17
|
+
import { CoValueKnownState, NewContentMessage } from "../sync.js";
|
|
18
|
+
import {
|
|
19
|
+
InvalidHashError,
|
|
20
|
+
InvalidSignatureError,
|
|
21
|
+
MAX_RECOMMENDED_TX_SIZE,
|
|
22
|
+
} from "./coValueCore.js";
|
|
23
|
+
import { TryAddTransactionsError } from "./coValueCore.js";
|
|
24
|
+
|
|
25
|
+
export type CoValueHeader = {
|
|
26
|
+
type: AnyRawCoValue["type"];
|
|
27
|
+
ruleset: RulesetDef;
|
|
28
|
+
meta: JsonObject | null;
|
|
29
|
+
} & CoValueUniqueness;
|
|
30
|
+
|
|
31
|
+
export type CoValueUniqueness = {
|
|
32
|
+
uniqueness: JsonValue;
|
|
33
|
+
createdAt?: `2${string}` | null;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type PrivateTransaction = {
|
|
37
|
+
privacy: "private";
|
|
38
|
+
madeAt: number;
|
|
39
|
+
keyUsed: KeyID;
|
|
40
|
+
encryptedChanges: Encrypted<JsonValue[], { in: RawCoID; tx: TransactionID }>;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type TrustingTransaction = {
|
|
44
|
+
privacy: "trusting";
|
|
45
|
+
madeAt: number;
|
|
46
|
+
changes: Stringified<JsonValue[]>;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type Transaction = PrivateTransaction | TrustingTransaction;
|
|
50
|
+
|
|
51
|
+
type SessionLog = {
|
|
52
|
+
readonly transactions: Transaction[];
|
|
53
|
+
lastHash?: Hash;
|
|
54
|
+
streamingHash: StreamingHash;
|
|
55
|
+
readonly signatureAfter: { [txIdx: number]: Signature | undefined };
|
|
56
|
+
lastSignature: Signature;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export type ValidatedSessions = Map<SessionID, SessionLog>;
|
|
60
|
+
|
|
61
|
+
export class VerifiedState {
|
|
62
|
+
readonly id: RawCoID;
|
|
63
|
+
readonly crypto: CryptoProvider;
|
|
64
|
+
readonly header: CoValueHeader;
|
|
65
|
+
readonly sessions: ValidatedSessions;
|
|
66
|
+
private _cachedKnownState?: CoValueKnownState;
|
|
67
|
+
private _cachedNewContentSinceEmpty: NewContentMessage[] | undefined;
|
|
68
|
+
|
|
69
|
+
constructor(
|
|
70
|
+
id: RawCoID,
|
|
71
|
+
crypto: CryptoProvider,
|
|
72
|
+
header: CoValueHeader,
|
|
73
|
+
sessions: ValidatedSessions,
|
|
74
|
+
) {
|
|
75
|
+
this.id = id;
|
|
76
|
+
this.crypto = crypto;
|
|
77
|
+
this.header = header;
|
|
78
|
+
this.sessions = sessions;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
clone(): VerifiedState {
|
|
82
|
+
// do a deep clone, including the sessions
|
|
83
|
+
const clonedSessions = new Map();
|
|
84
|
+
for (let [sessionID, sessionLog] of this.sessions) {
|
|
85
|
+
clonedSessions.set(sessionID, {
|
|
86
|
+
lastSignature: sessionLog.lastSignature,
|
|
87
|
+
lastHash: sessionLog.lastHash,
|
|
88
|
+
streamingHash: sessionLog.streamingHash.clone(),
|
|
89
|
+
signatureAfter: { ...sessionLog.signatureAfter },
|
|
90
|
+
transactions: sessionLog.transactions.slice(),
|
|
91
|
+
} satisfies SessionLog);
|
|
92
|
+
}
|
|
93
|
+
return new VerifiedState(this.id, this.crypto, this.header, clonedSessions);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
tryAddTransactions(
|
|
97
|
+
sessionID: SessionID,
|
|
98
|
+
signerID: SignerID,
|
|
99
|
+
newTransactions: Transaction[],
|
|
100
|
+
givenExpectedNewHash: Hash | undefined,
|
|
101
|
+
newSignature: Signature,
|
|
102
|
+
skipVerify: boolean = false,
|
|
103
|
+
givenNewStreamingHash?: StreamingHash,
|
|
104
|
+
): Result<true, TryAddTransactionsError> {
|
|
105
|
+
if (skipVerify === true && givenNewStreamingHash && givenExpectedNewHash) {
|
|
106
|
+
this.doAddTransactions(
|
|
107
|
+
sessionID,
|
|
108
|
+
newTransactions,
|
|
109
|
+
newSignature,
|
|
110
|
+
givenExpectedNewHash,
|
|
111
|
+
givenNewStreamingHash,
|
|
112
|
+
);
|
|
113
|
+
} else {
|
|
114
|
+
const { expectedNewHash, newStreamingHash } = this.expectedNewHashAfter(
|
|
115
|
+
sessionID,
|
|
116
|
+
newTransactions,
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
if (givenExpectedNewHash && givenExpectedNewHash !== expectedNewHash) {
|
|
120
|
+
return err({
|
|
121
|
+
type: "InvalidHash",
|
|
122
|
+
id: this.id,
|
|
123
|
+
expectedNewHash,
|
|
124
|
+
givenExpectedNewHash,
|
|
125
|
+
} satisfies InvalidHashError);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!this.crypto.verify(newSignature, expectedNewHash, signerID)) {
|
|
129
|
+
return err({
|
|
130
|
+
type: "InvalidSignature",
|
|
131
|
+
id: this.id,
|
|
132
|
+
newSignature,
|
|
133
|
+
sessionID,
|
|
134
|
+
signerID,
|
|
135
|
+
} satisfies InvalidSignatureError);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
this.doAddTransactions(
|
|
139
|
+
sessionID,
|
|
140
|
+
newTransactions,
|
|
141
|
+
newSignature,
|
|
142
|
+
expectedNewHash,
|
|
143
|
+
newStreamingHash,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return ok(true as const);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private doAddTransactions(
|
|
151
|
+
sessionID: SessionID,
|
|
152
|
+
newTransactions: Transaction[],
|
|
153
|
+
newSignature: Signature,
|
|
154
|
+
expectedNewHash: Hash,
|
|
155
|
+
newStreamingHash: StreamingHash,
|
|
156
|
+
) {
|
|
157
|
+
const transactions = this.sessions.get(sessionID)?.transactions ?? [];
|
|
158
|
+
|
|
159
|
+
for (const tx of newTransactions) {
|
|
160
|
+
transactions.push(tx);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const signatureAfter = this.sessions.get(sessionID)?.signatureAfter ?? {};
|
|
164
|
+
|
|
165
|
+
const lastInbetweenSignatureIdx = Object.keys(signatureAfter).reduce(
|
|
166
|
+
(max, idx) => (parseInt(idx) > max ? parseInt(idx) : max),
|
|
167
|
+
-1,
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const sizeOfTxsSinceLastInbetweenSignature = transactions
|
|
171
|
+
.slice(lastInbetweenSignatureIdx + 1)
|
|
172
|
+
.reduce(
|
|
173
|
+
(sum, tx) =>
|
|
174
|
+
sum +
|
|
175
|
+
(tx.privacy === "private"
|
|
176
|
+
? tx.encryptedChanges.length
|
|
177
|
+
: tx.changes.length),
|
|
178
|
+
0,
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
if (sizeOfTxsSinceLastInbetweenSignature > MAX_RECOMMENDED_TX_SIZE) {
|
|
182
|
+
signatureAfter[transactions.length - 1] = newSignature;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
this.sessions.set(sessionID, {
|
|
186
|
+
transactions,
|
|
187
|
+
lastHash: expectedNewHash,
|
|
188
|
+
streamingHash: newStreamingHash,
|
|
189
|
+
lastSignature: newSignature,
|
|
190
|
+
signatureAfter: signatureAfter,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
this._cachedNewContentSinceEmpty = undefined;
|
|
194
|
+
this._cachedKnownState = undefined;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
expectedNewHashAfter(
|
|
198
|
+
sessionID: SessionID,
|
|
199
|
+
newTransactions: Transaction[],
|
|
200
|
+
): { expectedNewHash: Hash; newStreamingHash: StreamingHash } {
|
|
201
|
+
const streamingHash =
|
|
202
|
+
this.sessions.get(sessionID)?.streamingHash.clone() ??
|
|
203
|
+
new StreamingHash(this.crypto);
|
|
204
|
+
|
|
205
|
+
for (const transaction of newTransactions) {
|
|
206
|
+
streamingHash.update(transaction);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
expectedNewHash: streamingHash.digest(),
|
|
211
|
+
newStreamingHash: streamingHash,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
newContentSince(
|
|
216
|
+
knownState: CoValueKnownState | undefined,
|
|
217
|
+
): NewContentMessage[] | undefined {
|
|
218
|
+
const isKnownStateEmpty = !knownState?.header && !knownState?.sessions;
|
|
219
|
+
|
|
220
|
+
if (isKnownStateEmpty && this._cachedNewContentSinceEmpty) {
|
|
221
|
+
return this._cachedNewContentSinceEmpty;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
let currentPiece: NewContentMessage = {
|
|
225
|
+
action: "content",
|
|
226
|
+
id: this.id,
|
|
227
|
+
header: knownState?.header ? undefined : this.header,
|
|
228
|
+
priority: getPriorityFromHeader(this.header),
|
|
229
|
+
new: {},
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const pieces = [currentPiece];
|
|
233
|
+
|
|
234
|
+
const sentState: CoValueKnownState["sessions"] = {};
|
|
235
|
+
|
|
236
|
+
let pieceSize = 0;
|
|
237
|
+
|
|
238
|
+
let sessionsTodoAgain: Set<SessionID> | undefined | "first" = "first";
|
|
239
|
+
|
|
240
|
+
while (sessionsTodoAgain === "first" || sessionsTodoAgain?.size || 0 > 0) {
|
|
241
|
+
if (sessionsTodoAgain === "first") {
|
|
242
|
+
sessionsTodoAgain = undefined;
|
|
243
|
+
}
|
|
244
|
+
const sessionsTodo = sessionsTodoAgain ?? this.sessions.keys();
|
|
245
|
+
|
|
246
|
+
for (const sessionIDKey of sessionsTodo) {
|
|
247
|
+
const sessionID = sessionIDKey as SessionID;
|
|
248
|
+
const log = this.sessions.get(sessionID)!;
|
|
249
|
+
const knownStateForSessionID = knownState?.sessions[sessionID];
|
|
250
|
+
const sentStateForSessionID = sentState[sessionID];
|
|
251
|
+
const nextKnownSignatureIdx = getNextKnownSignatureIdx(
|
|
252
|
+
log,
|
|
253
|
+
knownStateForSessionID,
|
|
254
|
+
sentStateForSessionID,
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
const firstNewTxIdx =
|
|
258
|
+
sentStateForSessionID ?? knownStateForSessionID ?? 0;
|
|
259
|
+
const afterLastNewTxIdx =
|
|
260
|
+
nextKnownSignatureIdx === undefined
|
|
261
|
+
? log.transactions.length
|
|
262
|
+
: nextKnownSignatureIdx + 1;
|
|
263
|
+
|
|
264
|
+
const nNewTx = Math.max(0, afterLastNewTxIdx - firstNewTxIdx);
|
|
265
|
+
|
|
266
|
+
if (nNewTx === 0) {
|
|
267
|
+
sessionsTodoAgain?.delete(sessionID);
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (afterLastNewTxIdx < log.transactions.length) {
|
|
272
|
+
if (!sessionsTodoAgain) {
|
|
273
|
+
sessionsTodoAgain = new Set();
|
|
274
|
+
}
|
|
275
|
+
sessionsTodoAgain.add(sessionID);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const oldPieceSize = pieceSize;
|
|
279
|
+
for (let txIdx = firstNewTxIdx; txIdx < afterLastNewTxIdx; txIdx++) {
|
|
280
|
+
const tx = log.transactions[txIdx]!;
|
|
281
|
+
pieceSize +=
|
|
282
|
+
tx.privacy === "private"
|
|
283
|
+
? tx.encryptedChanges.length
|
|
284
|
+
: tx.changes.length;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (pieceSize >= MAX_RECOMMENDED_TX_SIZE) {
|
|
288
|
+
currentPiece = {
|
|
289
|
+
action: "content",
|
|
290
|
+
id: this.id,
|
|
291
|
+
header: undefined,
|
|
292
|
+
new: {},
|
|
293
|
+
priority: getPriorityFromHeader(this.header),
|
|
294
|
+
};
|
|
295
|
+
pieces.push(currentPiece);
|
|
296
|
+
pieceSize = pieceSize - oldPieceSize;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
let sessionEntry = currentPiece.new[sessionID];
|
|
300
|
+
if (!sessionEntry) {
|
|
301
|
+
sessionEntry = {
|
|
302
|
+
after: sentStateForSessionID ?? knownStateForSessionID ?? 0,
|
|
303
|
+
newTransactions: [],
|
|
304
|
+
lastSignature: "WILL_BE_REPLACED" as Signature,
|
|
305
|
+
};
|
|
306
|
+
currentPiece.new[sessionID] = sessionEntry;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
for (let txIdx = firstNewTxIdx; txIdx < afterLastNewTxIdx; txIdx++) {
|
|
310
|
+
const tx = log.transactions[txIdx]!;
|
|
311
|
+
sessionEntry.newTransactions.push(tx);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
sessionEntry.lastSignature =
|
|
315
|
+
nextKnownSignatureIdx === undefined
|
|
316
|
+
? log.lastSignature!
|
|
317
|
+
: log.signatureAfter[nextKnownSignatureIdx]!;
|
|
318
|
+
|
|
319
|
+
sentState[sessionID] =
|
|
320
|
+
(sentStateForSessionID ?? knownStateForSessionID ?? 0) + nNewTx;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const piecesWithContent = pieces.filter(
|
|
325
|
+
(piece) => Object.keys(piece.new).length > 0 || piece.header,
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
if (piecesWithContent.length === 0) {
|
|
329
|
+
return undefined;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (isKnownStateEmpty) {
|
|
333
|
+
this._cachedNewContentSinceEmpty = piecesWithContent;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return piecesWithContent;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
knownState(): CoValueKnownState {
|
|
340
|
+
if (this._cachedKnownState) {
|
|
341
|
+
return this._cachedKnownState;
|
|
342
|
+
} else {
|
|
343
|
+
const knownState = this.knownStateUncached();
|
|
344
|
+
this._cachedKnownState = knownState;
|
|
345
|
+
return knownState;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** @internal */
|
|
350
|
+
knownStateUncached(): CoValueKnownState {
|
|
351
|
+
const sessions: CoValueKnownState["sessions"] = {};
|
|
352
|
+
|
|
353
|
+
for (const [sessionID, sessionLog] of this.sessions.entries()) {
|
|
354
|
+
sessions[sessionID] = sessionLog.transactions.length;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
id: this.id,
|
|
359
|
+
header: true,
|
|
360
|
+
sessions,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function getNextKnownSignatureIdx(
|
|
366
|
+
log: SessionLog,
|
|
367
|
+
knownStateForSessionID?: number,
|
|
368
|
+
sentStateForSessionID?: number,
|
|
369
|
+
) {
|
|
370
|
+
return Object.keys(log.signatureAfter)
|
|
371
|
+
.map(Number)
|
|
372
|
+
.sort((a, b) => a - b)
|
|
373
|
+
.find(
|
|
374
|
+
(idx) => idx >= (sentStateForSessionID ?? knownStateForSessionID ?? -1),
|
|
375
|
+
);
|
|
376
|
+
}
|
package/src/coValues/account.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { CoID, RawCoValue } from "../coValue.js";
|
|
2
2
|
import {
|
|
3
|
+
AvailableCoValueCore,
|
|
3
4
|
CoValueCore,
|
|
5
|
+
} from "../coValueCore/coValueCore.js";
|
|
6
|
+
import {
|
|
4
7
|
CoValueHeader,
|
|
5
8
|
CoValueUniqueness,
|
|
6
|
-
} from "../coValueCore.js";
|
|
9
|
+
} from "../coValueCore/verifiedState.js";
|
|
7
10
|
import {
|
|
8
11
|
AgentSecret,
|
|
9
12
|
CryptoProvider,
|
|
@@ -85,35 +88,20 @@ export interface ControlledAccountOrAgent {
|
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
/** @hidden */
|
|
88
|
-
export class
|
|
89
|
-
|
|
90
|
-
implements ControlledAccountOrAgent
|
|
91
|
-
{
|
|
91
|
+
export class ControlledAccount implements ControlledAccountOrAgent {
|
|
92
|
+
account: RawAccount<AccountMeta>;
|
|
92
93
|
agentSecret: AgentSecret;
|
|
94
|
+
_cachedCurrentAgentID: AgentID | undefined;
|
|
93
95
|
crypto: CryptoProvider;
|
|
94
96
|
|
|
95
|
-
constructor(
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
constructor(account: RawAccount<AccountMeta>, agentSecret: AgentSecret) {
|
|
98
|
+
this.account = account;
|
|
98
99
|
this.agentSecret = agentSecret;
|
|
99
|
-
this.crypto = core.node.crypto;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Creates a new group (with the current account as the group's first admin).
|
|
104
|
-
* @category 1. High-level
|
|
105
|
-
*/
|
|
106
|
-
createGroup(
|
|
107
|
-
uniqueness: CoValueUniqueness = this.core.crypto.createdNowUnique(),
|
|
108
|
-
) {
|
|
109
|
-
return this.core.node.createGroup(uniqueness);
|
|
100
|
+
this.crypto = account.core.node.crypto;
|
|
110
101
|
}
|
|
111
102
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
inviteSecret: InviteSecret,
|
|
115
|
-
): Promise<void> {
|
|
116
|
-
return this.core.node.acceptInvite(groupOrOwnedValueID, inviteSecret);
|
|
103
|
+
get id(): RawAccountID {
|
|
104
|
+
return this.account.id;
|
|
117
105
|
}
|
|
118
106
|
|
|
119
107
|
currentAgentID(): AgentID {
|
|
@@ -186,7 +174,14 @@ export class RawProfile<
|
|
|
186
174
|
> extends RawCoMap<Shape, Meta> {}
|
|
187
175
|
|
|
188
176
|
export type RawAccountMigration<Meta extends AccountMeta = AccountMeta> = (
|
|
189
|
-
account:
|
|
177
|
+
account: RawAccount<Meta>,
|
|
190
178
|
localNode: LocalNode,
|
|
191
179
|
creationProps?: { name: string },
|
|
192
180
|
) => void | Promise<void>;
|
|
181
|
+
|
|
182
|
+
export function expectAccount(content: RawCoValue): RawAccount {
|
|
183
|
+
if (!(content instanceof RawAccount)) {
|
|
184
|
+
throw new Error("Expected an account");
|
|
185
|
+
}
|
|
186
|
+
return content;
|
|
187
|
+
}
|
package/src/coValues/coList.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { CoID, RawCoValue } from "../coValue.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
AvailableCoValueCore,
|
|
4
|
+
CoValueCore,
|
|
5
|
+
} from "../coValueCore/coValueCore.js";
|
|
3
6
|
import { AgentID, SessionID, TransactionID } from "../ids.js";
|
|
4
7
|
import { JsonObject, JsonValue } from "../jsonValue.js";
|
|
5
8
|
import { CoValueKnownState } from "../sync.js";
|
|
@@ -52,7 +55,7 @@ export class RawCoList<
|
|
|
52
55
|
/** @category 6. Meta */
|
|
53
56
|
type: "colist" | "coplaintext" = "colist" as const;
|
|
54
57
|
/** @category 6. Meta */
|
|
55
|
-
core:
|
|
58
|
+
core: AvailableCoValueCore;
|
|
56
59
|
/** @internal */
|
|
57
60
|
afterStart: OpID[];
|
|
58
61
|
/** @internal */
|
|
@@ -88,7 +91,7 @@ export class RawCoList<
|
|
|
88
91
|
lastValidTransaction: number | undefined;
|
|
89
92
|
|
|
90
93
|
/** @internal */
|
|
91
|
-
constructor(core:
|
|
94
|
+
constructor(core: AvailableCoValueCore) {
|
|
92
95
|
this.id = core.id as CoID<this>;
|
|
93
96
|
this.core = core;
|
|
94
97
|
|
|
@@ -234,7 +237,7 @@ export class RawCoList<
|
|
|
234
237
|
|
|
235
238
|
/** @category 6. Meta */
|
|
236
239
|
get headerMeta(): Meta {
|
|
237
|
-
return this.core.header.meta as Meta;
|
|
240
|
+
return this.core.verified.header.meta as Meta;
|
|
238
241
|
}
|
|
239
242
|
|
|
240
243
|
/** @category 6. Meta */
|
|
@@ -440,8 +443,8 @@ export class RawCoList<
|
|
|
440
443
|
|
|
441
444
|
/** @category 3. Subscription */
|
|
442
445
|
subscribe(listener: (coList: this) => void): () => void {
|
|
443
|
-
return this.core.subscribe((
|
|
444
|
-
listener(
|
|
446
|
+
return this.core.subscribe((core) => {
|
|
447
|
+
listener(core.getCurrentContent() as this);
|
|
445
448
|
});
|
|
446
449
|
}
|
|
447
450
|
|
|
@@ -621,6 +624,9 @@ export class RawCoList<
|
|
|
621
624
|
this.afterStart = listAfter.afterStart;
|
|
622
625
|
this.beforeEnd = listAfter.beforeEnd;
|
|
623
626
|
this.insertions = listAfter.insertions;
|
|
627
|
+
this.totalValidTransactions = listAfter.totalValidTransactions;
|
|
628
|
+
this.lastValidTransaction = listAfter.lastValidTransaction;
|
|
629
|
+
this.knownTransactions = listAfter.knownTransactions;
|
|
624
630
|
this.deletionsByInsertion = listAfter.deletionsByInsertion;
|
|
625
631
|
this._cachedEntries = undefined;
|
|
626
632
|
}
|
package/src/coValues/coMap.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { CoID, RawCoValue } from "../coValue.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
AvailableCoValueCore,
|
|
4
|
+
CoValueCore,
|
|
5
|
+
} from "../coValueCore/coValueCore.js";
|
|
3
6
|
import { AgentID, TransactionID } from "../ids.js";
|
|
4
7
|
import { JsonObject, JsonValue } from "../jsonValue.js";
|
|
5
8
|
import { CoValueKnownState } from "../sync.js";
|
|
@@ -39,7 +42,7 @@ export class RawCoMapView<
|
|
|
39
42
|
/** @category 6. Meta */
|
|
40
43
|
type = "comap" as const;
|
|
41
44
|
/** @category 6. Meta */
|
|
42
|
-
core:
|
|
45
|
+
core: AvailableCoValueCore;
|
|
43
46
|
/** @internal */
|
|
44
47
|
latest: {
|
|
45
48
|
[Key in keyof Shape & string]?: MapOp<Key, Shape[Key]>;
|
|
@@ -64,7 +67,7 @@ export class RawCoMapView<
|
|
|
64
67
|
|
|
65
68
|
/** @internal */
|
|
66
69
|
constructor(
|
|
67
|
-
core:
|
|
70
|
+
core: AvailableCoValueCore,
|
|
68
71
|
options?: {
|
|
69
72
|
ignorePrivateTransactions: boolean;
|
|
70
73
|
},
|
|
@@ -152,7 +155,7 @@ export class RawCoMapView<
|
|
|
152
155
|
|
|
153
156
|
/** @category 6. Meta */
|
|
154
157
|
get headerMeta(): Meta {
|
|
155
|
-
return this.core.header.meta as Meta;
|
|
158
|
+
return this.core.verified.header.meta as Meta;
|
|
156
159
|
}
|
|
157
160
|
|
|
158
161
|
/** @category 6. Meta */
|
|
@@ -344,8 +347,8 @@ export class RawCoMapView<
|
|
|
344
347
|
|
|
345
348
|
/** @category 3. Subscription */
|
|
346
349
|
subscribe(listener: (coMap: this) => void): () => void {
|
|
347
|
-
return this.core.subscribe((
|
|
348
|
-
listener(
|
|
350
|
+
return this.core.subscribe((core) => {
|
|
351
|
+
listener(core.getCurrentContent() as this);
|
|
349
352
|
});
|
|
350
353
|
}
|
|
351
354
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
AvailableCoValueCore,
|
|
3
|
+
CoValueCore,
|
|
4
|
+
} from "../coValueCore/coValueCore.js";
|
|
2
5
|
import { JsonObject } from "../jsonValue.js";
|
|
3
6
|
import { DeletionOpPayload, OpID, RawCoList } from "./coList.js";
|
|
4
7
|
|
|
@@ -61,7 +64,7 @@ export class RawCoPlainText<
|
|
|
61
64
|
PlaintextIdxMapping
|
|
62
65
|
>;
|
|
63
66
|
|
|
64
|
-
constructor(core:
|
|
67
|
+
constructor(core: AvailableCoValueCore) {
|
|
65
68
|
super(core);
|
|
66
69
|
this._cachedMapping = new WeakMap();
|
|
67
70
|
if (!Intl.Segmenter) {
|
|
@@ -72,10 +75,10 @@ export class RawCoPlainText<
|
|
|
72
75
|
|
|
73
76
|
// Use locale from meta if provided, fallback to browser locale, or 'en' as last resort
|
|
74
77
|
const effectiveLocale =
|
|
75
|
-
(core.header.meta &&
|
|
76
|
-
typeof core.header.meta === "object" &&
|
|
77
|
-
"locale" in core.header.meta
|
|
78
|
-
? (core.header.meta.locale as string)
|
|
78
|
+
(core.verified.header.meta &&
|
|
79
|
+
typeof core.verified.header.meta === "object" &&
|
|
80
|
+
"locale" in core.verified.header.meta
|
|
81
|
+
? (core.verified.header.meta.locale as string)
|
|
79
82
|
: undefined) ||
|
|
80
83
|
(typeof navigator !== "undefined" ? navigator.language : "en");
|
|
81
84
|
|
package/src/coValues/coStream.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { base64URLtoBytes, bytesToBase64url } from "../base64url.js";
|
|
2
2
|
import { CoID, RawCoValue } from "../coValue.js";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
AvailableCoValueCore,
|
|
5
|
+
CoValueCore,
|
|
6
|
+
} from "../coValueCore/coValueCore.js";
|
|
4
7
|
import { AgentID, SessionID, TransactionID } from "../ids.js";
|
|
5
8
|
import { JsonObject, JsonValue } from "../jsonValue.js";
|
|
6
9
|
import { logger } from "../logger.js";
|
|
@@ -50,7 +53,7 @@ export class RawCoStreamView<
|
|
|
50
53
|
{
|
|
51
54
|
id: CoID<this>;
|
|
52
55
|
type = "costream" as const;
|
|
53
|
-
core:
|
|
56
|
+
core: AvailableCoValueCore;
|
|
54
57
|
items: {
|
|
55
58
|
[key: SessionID]: CoStreamItem<Item>[];
|
|
56
59
|
};
|
|
@@ -59,7 +62,7 @@ export class RawCoStreamView<
|
|
|
59
62
|
totalValidTransactions = 0;
|
|
60
63
|
readonly _item!: Item;
|
|
61
64
|
|
|
62
|
-
constructor(core:
|
|
65
|
+
constructor(core: AvailableCoValueCore) {
|
|
63
66
|
this.id = core.id as CoID<this>;
|
|
64
67
|
this.core = core;
|
|
65
68
|
this.items = {};
|
|
@@ -68,7 +71,7 @@ export class RawCoStreamView<
|
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
get headerMeta(): Meta {
|
|
71
|
-
return this.core.header.meta as Meta;
|
|
74
|
+
return this.core.verified.header.meta as Meta;
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
get group(): RawGroup {
|
|
@@ -277,8 +280,8 @@ export class RawCoStreamView<
|
|
|
277
280
|
}
|
|
278
281
|
|
|
279
282
|
subscribe(listener: (coStream: this) => void): () => void {
|
|
280
|
-
return this.core.subscribe((
|
|
281
|
-
listener(
|
|
283
|
+
return this.core.subscribe((core) => {
|
|
284
|
+
listener(core.getCurrentContent() as this);
|
|
282
285
|
});
|
|
283
286
|
}
|
|
284
287
|
}
|