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
package/src/coValues/coList.ts
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { CoID, RawCoValue } from "../coValue.js";
|
|
2
|
-
import {
|
|
3
|
-
AvailableCoValueCore,
|
|
4
|
-
CoValueCore,
|
|
5
|
-
} from "../coValueCore/coValueCore.js";
|
|
2
|
+
import { AvailableCoValueCore } from "../coValueCore/coValueCore.js";
|
|
6
3
|
import { AgentID, SessionID, TransactionID } from "../ids.js";
|
|
7
4
|
import { JsonObject, JsonValue } from "../jsonValue.js";
|
|
8
|
-
import { CoValueKnownState } from "../sync.js";
|
|
9
5
|
import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfromSessionID.js";
|
|
10
6
|
import { isCoValue } from "../typeUtils/isCoValue.js";
|
|
11
7
|
import { RawAccountID } from "./account.js";
|
|
@@ -39,12 +35,14 @@ type InsertionEntry<T extends JsonValue> = {
|
|
|
39
35
|
madeAt: number;
|
|
40
36
|
predecessors: OpID[];
|
|
41
37
|
successors: OpID[];
|
|
42
|
-
|
|
38
|
+
change: InsertionOpPayload<T>;
|
|
39
|
+
};
|
|
43
40
|
|
|
44
41
|
type DeletionEntry = {
|
|
45
42
|
madeAt: number;
|
|
46
43
|
deletionID: OpID;
|
|
47
|
-
|
|
44
|
+
change: DeletionOpPayload;
|
|
45
|
+
};
|
|
48
46
|
|
|
49
47
|
export class RawCoList<
|
|
50
48
|
Item extends JsonValue = JsonValue,
|
|
@@ -109,6 +107,89 @@ export class RawCoList<
|
|
|
109
107
|
this.processNewTransactions();
|
|
110
108
|
}
|
|
111
109
|
|
|
110
|
+
private getInsertionsEntry(opID: OpID) {
|
|
111
|
+
const index = getSessionIndex(opID);
|
|
112
|
+
|
|
113
|
+
const sessionEntry = this.insertions[index];
|
|
114
|
+
if (!sessionEntry) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const txEntry = sessionEntry[opID.txIndex];
|
|
119
|
+
if (!txEntry) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return txEntry[opID.changeIdx];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private createInsertionsEntry(opID: OpID, value: InsertionEntry<Item>) {
|
|
127
|
+
const index = getSessionIndex(opID);
|
|
128
|
+
|
|
129
|
+
let sessionEntry = this.insertions[index];
|
|
130
|
+
if (!sessionEntry) {
|
|
131
|
+
sessionEntry = {};
|
|
132
|
+
this.insertions[index] = sessionEntry;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let txEntry = sessionEntry[opID.txIndex];
|
|
136
|
+
if (!txEntry) {
|
|
137
|
+
txEntry = {};
|
|
138
|
+
sessionEntry[opID.txIndex] = txEntry;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Check if the change index already exists, may be the case of double merges
|
|
142
|
+
if (txEntry[opID.changeIdx]) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
txEntry[opID.changeIdx] = value;
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private isDeleted(opID: OpID) {
|
|
151
|
+
const index = getSessionIndex(opID);
|
|
152
|
+
|
|
153
|
+
const sessionEntry = this.deletionsByInsertion[index];
|
|
154
|
+
|
|
155
|
+
if (!sessionEntry) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const txEntry = sessionEntry[opID.txIndex];
|
|
160
|
+
|
|
161
|
+
if (!txEntry) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return Boolean(txEntry[opID.changeIdx]?.length);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private pushDeletionsByInsertionEntry(opID: OpID, value: DeletionEntry) {
|
|
169
|
+
const index = getSessionIndex(opID);
|
|
170
|
+
|
|
171
|
+
let sessionEntry = this.deletionsByInsertion[index];
|
|
172
|
+
if (!sessionEntry) {
|
|
173
|
+
sessionEntry = {};
|
|
174
|
+
this.deletionsByInsertion[index] = sessionEntry;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
let txEntry = sessionEntry[opID.txIndex];
|
|
178
|
+
if (!txEntry) {
|
|
179
|
+
txEntry = {};
|
|
180
|
+
sessionEntry[opID.txIndex] = txEntry;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
let list = txEntry[opID.changeIdx];
|
|
184
|
+
|
|
185
|
+
if (!list) {
|
|
186
|
+
list = [];
|
|
187
|
+
txEntry[opID.changeIdx] = list;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
list.push(value);
|
|
191
|
+
}
|
|
192
|
+
|
|
112
193
|
processNewTransactions() {
|
|
113
194
|
const transactions = this.core.getValidSortedTransactions({
|
|
114
195
|
ignorePrivateTransactions: false,
|
|
@@ -133,87 +214,56 @@ export class RawCoList<
|
|
|
133
214
|
for (const [changeIdx, changeUntyped] of changes.entries()) {
|
|
134
215
|
const change = changeUntyped as ListOpPayload<Item>;
|
|
135
216
|
|
|
217
|
+
const opID = {
|
|
218
|
+
sessionID: txID.sessionID,
|
|
219
|
+
txIndex: txID.txIndex,
|
|
220
|
+
branch: txID.branch,
|
|
221
|
+
changeIdx,
|
|
222
|
+
};
|
|
223
|
+
|
|
136
224
|
if (change.op === "pre" || change.op === "app") {
|
|
137
|
-
|
|
138
|
-
if (!sessionEntry) {
|
|
139
|
-
sessionEntry = {};
|
|
140
|
-
this.insertions[txID.sessionID] = sessionEntry;
|
|
141
|
-
}
|
|
142
|
-
let txEntry = sessionEntry[txID.txIndex];
|
|
143
|
-
if (!txEntry) {
|
|
144
|
-
txEntry = {};
|
|
145
|
-
sessionEntry[txID.txIndex] = txEntry;
|
|
146
|
-
}
|
|
147
|
-
txEntry[changeIdx] = {
|
|
225
|
+
const created = this.createInsertionsEntry(opID, {
|
|
148
226
|
madeAt,
|
|
149
227
|
predecessors: [],
|
|
150
228
|
successors: [],
|
|
151
|
-
|
|
152
|
-
};
|
|
229
|
+
change,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// If the change index already exists, we don't need to process it again
|
|
233
|
+
if (!created) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
|
|
153
237
|
if (change.op === "pre") {
|
|
154
238
|
if (change.before === "end") {
|
|
155
|
-
this.beforeEnd.push(
|
|
156
|
-
...txID,
|
|
157
|
-
changeIdx,
|
|
158
|
-
});
|
|
239
|
+
this.beforeEnd.push(opID);
|
|
159
240
|
} else {
|
|
160
|
-
const beforeEntry =
|
|
161
|
-
|
|
162
|
-
change.before.txIndex
|
|
163
|
-
]?.[change.before.changeIdx];
|
|
241
|
+
const beforeEntry = this.getInsertionsEntry(change.before);
|
|
242
|
+
|
|
164
243
|
if (!beforeEntry) {
|
|
165
244
|
continue;
|
|
166
245
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
changeIdx,
|
|
170
|
-
});
|
|
246
|
+
|
|
247
|
+
beforeEntry.predecessors.push(opID);
|
|
171
248
|
}
|
|
172
249
|
} else {
|
|
173
250
|
if (change.after === "start") {
|
|
174
|
-
this.afterStart.push(
|
|
175
|
-
...txID,
|
|
176
|
-
changeIdx,
|
|
177
|
-
});
|
|
251
|
+
this.afterStart.push(opID);
|
|
178
252
|
} else {
|
|
179
|
-
const afterEntry =
|
|
180
|
-
|
|
181
|
-
change.after.txIndex
|
|
182
|
-
]?.[change.after.changeIdx];
|
|
253
|
+
const afterEntry = this.getInsertionsEntry(change.after);
|
|
254
|
+
|
|
183
255
|
if (!afterEntry) {
|
|
184
256
|
continue;
|
|
185
257
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
changeIdx,
|
|
189
|
-
});
|
|
258
|
+
|
|
259
|
+
afterEntry.successors.push(opID);
|
|
190
260
|
}
|
|
191
261
|
}
|
|
192
262
|
} else if (change.op === "del") {
|
|
193
|
-
|
|
194
|
-
this.deletionsByInsertion[change.insertion.sessionID];
|
|
195
|
-
if (!sessionEntry) {
|
|
196
|
-
sessionEntry = {};
|
|
197
|
-
this.deletionsByInsertion[change.insertion.sessionID] =
|
|
198
|
-
sessionEntry;
|
|
199
|
-
}
|
|
200
|
-
let txEntry = sessionEntry[change.insertion.txIndex];
|
|
201
|
-
if (!txEntry) {
|
|
202
|
-
txEntry = {};
|
|
203
|
-
sessionEntry[change.insertion.txIndex] = txEntry;
|
|
204
|
-
}
|
|
205
|
-
let changeEntry = txEntry[change.insertion.changeIdx];
|
|
206
|
-
if (!changeEntry) {
|
|
207
|
-
changeEntry = [];
|
|
208
|
-
txEntry[change.insertion.changeIdx] = changeEntry;
|
|
209
|
-
}
|
|
210
|
-
changeEntry.push({
|
|
263
|
+
this.pushDeletionsByInsertionEntry(change.insertion, {
|
|
211
264
|
madeAt,
|
|
212
|
-
deletionID:
|
|
213
|
-
|
|
214
|
-
changeIdx,
|
|
215
|
-
},
|
|
216
|
-
...change,
|
|
265
|
+
deletionID: opID,
|
|
266
|
+
change,
|
|
217
267
|
});
|
|
218
268
|
} else {
|
|
219
269
|
throw new Error(
|
|
@@ -324,10 +374,7 @@ export class RawCoList<
|
|
|
324
374
|
while (todo.length > 0) {
|
|
325
375
|
const currentOpID = todo[todo.length - 1]!;
|
|
326
376
|
|
|
327
|
-
const entry =
|
|
328
|
-
this.insertions[currentOpID.sessionID]?.[currentOpID.txIndex]?.[
|
|
329
|
-
currentOpID.changeIdx
|
|
330
|
-
];
|
|
377
|
+
const entry = this.getInsertionsEntry(currentOpID);
|
|
331
378
|
|
|
332
379
|
if (!entry) {
|
|
333
380
|
throw new Error("Missing op " + currentOpID);
|
|
@@ -338,22 +385,19 @@ export class RawCoList<
|
|
|
338
385
|
|
|
339
386
|
// We navigate the predecessors before processing the current opID in the list
|
|
340
387
|
if (shouldTraversePredecessors) {
|
|
341
|
-
for (
|
|
342
|
-
todo.push(
|
|
388
|
+
for (const predecessor of entry.predecessors) {
|
|
389
|
+
todo.push(predecessor);
|
|
343
390
|
}
|
|
344
391
|
predecessorsVisited.add(currentOpID);
|
|
345
392
|
} else {
|
|
346
393
|
// Remove the current opID from the todo stack to consider it processed.
|
|
347
394
|
todo.pop();
|
|
348
395
|
|
|
349
|
-
const deleted =
|
|
350
|
-
(this.deletionsByInsertion[currentOpID.sessionID]?.[
|
|
351
|
-
currentOpID.txIndex
|
|
352
|
-
]?.[currentOpID.changeIdx]?.length || 0) > 0;
|
|
396
|
+
const deleted = this.isDeleted(currentOpID);
|
|
353
397
|
|
|
354
398
|
if (!deleted) {
|
|
355
399
|
arr.push({
|
|
356
|
-
value: entry.value,
|
|
400
|
+
value: entry.change.value,
|
|
357
401
|
madeAt: entry.madeAt,
|
|
358
402
|
opID: currentOpID,
|
|
359
403
|
});
|
|
@@ -629,3 +673,10 @@ export class RawCoList<
|
|
|
629
673
|
this._cachedEntries = undefined;
|
|
630
674
|
}
|
|
631
675
|
}
|
|
676
|
+
|
|
677
|
+
function getSessionIndex(txID: TransactionID): SessionID {
|
|
678
|
+
if (txID.branch) {
|
|
679
|
+
return `${txID.sessionID}_branch_${txID.branch}`;
|
|
680
|
+
}
|
|
681
|
+
return txID.sessionID;
|
|
682
|
+
}
|
package/src/coValues/coMap.ts
CHANGED
|
@@ -49,10 +49,17 @@ export class RawCoMapView<
|
|
|
49
49
|
latest: {
|
|
50
50
|
[Key in keyof Shape & string]?: MapOp<Key, Shape[Key]>;
|
|
51
51
|
};
|
|
52
|
+
|
|
52
53
|
/** @internal */
|
|
53
|
-
latestTxMadeAt: number
|
|
54
|
+
get latestTxMadeAt(): number {
|
|
55
|
+
return this.core.latestTxMadeAt;
|
|
56
|
+
}
|
|
57
|
+
|
|
54
58
|
/** @internal */
|
|
55
|
-
earliestTxMadeAt: number
|
|
59
|
+
get earliestTxMadeAt(): number {
|
|
60
|
+
return this.core.earliestTxMadeAt;
|
|
61
|
+
}
|
|
62
|
+
|
|
56
63
|
/** @internal */
|
|
57
64
|
ops: {
|
|
58
65
|
[Key in keyof Shape & string]?: MapOp<Key, Shape[Key]>[];
|
|
@@ -80,8 +87,7 @@ export class RawCoMapView<
|
|
|
80
87
|
) {
|
|
81
88
|
this.id = core.id as CoID<this>;
|
|
82
89
|
this.core = core;
|
|
83
|
-
|
|
84
|
-
this.earliestTxMadeAt = null;
|
|
90
|
+
|
|
85
91
|
this.ignorePrivateTransactions =
|
|
86
92
|
options?.ignorePrivateTransactions ?? false;
|
|
87
93
|
this.ops = {};
|
|
@@ -105,10 +111,6 @@ export class RawCoMapView<
|
|
|
105
111
|
return;
|
|
106
112
|
}
|
|
107
113
|
|
|
108
|
-
if (this.earliestTxMadeAt === null && newValidTransactions[0]) {
|
|
109
|
-
this.earliestTxMadeAt = newValidTransactions[0].madeAt;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
114
|
const { ops } = this;
|
|
113
115
|
|
|
114
116
|
const changedEntries = new Map<
|
|
@@ -117,10 +119,6 @@ export class RawCoMapView<
|
|
|
117
119
|
>();
|
|
118
120
|
|
|
119
121
|
for (const { txID, changes, madeAt, tx } of newValidTransactions) {
|
|
120
|
-
if (madeAt > this.latestTxMadeAt) {
|
|
121
|
-
this.latestTxMadeAt = madeAt;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
122
|
for (let changeIdx = 0; changeIdx < changes.length; changeIdx++) {
|
|
125
123
|
const change = changes[changeIdx] as MapOpPayload<
|
|
126
124
|
keyof Shape & string,
|
package/src/coValues/group.ts
CHANGED
|
@@ -1070,6 +1070,9 @@ export class RawGroup<
|
|
|
1070
1070
|
|
|
1071
1071
|
if (init) {
|
|
1072
1072
|
map.assign(init, initPrivacy);
|
|
1073
|
+
} else if (!uniqueness.createdAt) {
|
|
1074
|
+
// If the createdAt is not set, we need to make a trusting transaction to set the createdAt
|
|
1075
|
+
map.core.makeTransaction([], "trusting");
|
|
1073
1076
|
}
|
|
1074
1077
|
|
|
1075
1078
|
return map;
|
|
@@ -1101,6 +1104,9 @@ export class RawGroup<
|
|
|
1101
1104
|
|
|
1102
1105
|
if (init?.length) {
|
|
1103
1106
|
list.appendItems(init, undefined, initPrivacy);
|
|
1107
|
+
} else if (!uniqueness.createdAt) {
|
|
1108
|
+
// If the createdAt is not set, we need to make a trusting transaction to set the createdAt
|
|
1109
|
+
list.core.makeTransaction([], "trusting");
|
|
1104
1110
|
}
|
|
1105
1111
|
|
|
1106
1112
|
return list;
|
|
@@ -1141,7 +1147,7 @@ export class RawGroup<
|
|
|
1141
1147
|
meta?: C["headerMeta"],
|
|
1142
1148
|
uniqueness: CoValueUniqueness = this.crypto.createdNowUnique(),
|
|
1143
1149
|
): C {
|
|
1144
|
-
|
|
1150
|
+
const stream = this.core.node
|
|
1145
1151
|
.createCoValue({
|
|
1146
1152
|
type: "costream",
|
|
1147
1153
|
ruleset: {
|
|
@@ -1152,6 +1158,13 @@ export class RawGroup<
|
|
|
1152
1158
|
...uniqueness,
|
|
1153
1159
|
})
|
|
1154
1160
|
.getCurrentContent() as C;
|
|
1161
|
+
|
|
1162
|
+
if (!uniqueness.createdAt) {
|
|
1163
|
+
// If the createdAt is not set, we need to make a trusting transaction to set the createdAt
|
|
1164
|
+
stream.core.makeTransaction([], "trusting");
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
return stream;
|
|
1155
1168
|
}
|
|
1156
1169
|
|
|
1157
1170
|
/** @category 3. Value creation */
|
package/src/config.ts
CHANGED
|
@@ -7,12 +7,21 @@
|
|
|
7
7
|
**/
|
|
8
8
|
export const TRANSACTION_CONFIG = {
|
|
9
9
|
MAX_RECOMMENDED_TX_SIZE: 100 * 1024,
|
|
10
|
+
/**
|
|
11
|
+
* Messages larger than this will be rejected when creating a transaction.
|
|
12
|
+
* The current limit is set at 1MB because that's the limit imposed by Cloudflare to Websocket messages.
|
|
13
|
+
*/
|
|
14
|
+
MAX_TX_SIZE_BYTES: 1 * 1024 * 1024,
|
|
10
15
|
};
|
|
11
16
|
|
|
12
17
|
export function setMaxRecommendedTxSize(size: number) {
|
|
13
18
|
TRANSACTION_CONFIG.MAX_RECOMMENDED_TX_SIZE = size;
|
|
14
19
|
}
|
|
15
20
|
|
|
21
|
+
export function setMaxTxSizeBytes(size: number) {
|
|
22
|
+
TRANSACTION_CONFIG.MAX_TX_SIZE_BYTES = size;
|
|
23
|
+
}
|
|
24
|
+
|
|
16
25
|
export const CO_VALUE_LOADING_CONFIG = {
|
|
17
26
|
MAX_RETRIES: 1,
|
|
18
27
|
TIMEOUT: 30_000,
|
|
@@ -32,6 +32,29 @@ import { ControlledAccountOrAgent } from "../coValues/account.js";
|
|
|
32
32
|
|
|
33
33
|
type Blake3State = ReturnType<typeof blake3.create>;
|
|
34
34
|
|
|
35
|
+
const x25519SharedSecretCache = new Map<string, Uint8Array>();
|
|
36
|
+
|
|
37
|
+
function getx25519SharedSecret(
|
|
38
|
+
privateKeyA: SealerSecret,
|
|
39
|
+
publicKeyB: SealerID,
|
|
40
|
+
): Uint8Array {
|
|
41
|
+
const cacheKey = `${privateKeyA}-${publicKeyB}`;
|
|
42
|
+
let sharedSecret = x25519SharedSecretCache.get(cacheKey);
|
|
43
|
+
|
|
44
|
+
if (!sharedSecret) {
|
|
45
|
+
const privateKeyABytes = base58.decode(
|
|
46
|
+
privateKeyA.substring("sealerSecret_z".length),
|
|
47
|
+
);
|
|
48
|
+
const publicKeyBBytes = base58.decode(
|
|
49
|
+
publicKeyB.substring("sealer_z".length),
|
|
50
|
+
);
|
|
51
|
+
sharedSecret = x25519.getSharedSecret(privateKeyABytes, publicKeyBBytes);
|
|
52
|
+
x25519SharedSecretCache.set(cacheKey, sharedSecret);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return sharedSecret;
|
|
56
|
+
}
|
|
57
|
+
|
|
35
58
|
/**
|
|
36
59
|
* Pure JavaScript implementation of the CryptoProvider interface using noble-curves and noble-ciphers libraries.
|
|
37
60
|
* This provides a fallback implementation that doesn't require WebAssembly, offering:
|
|
@@ -164,16 +187,10 @@ export class PureJSCrypto extends CryptoProvider<Blake3State> {
|
|
|
164
187
|
to: SealerID;
|
|
165
188
|
nOnceMaterial: { in: RawCoID; tx: TransactionID };
|
|
166
189
|
}): Sealed<T> {
|
|
190
|
+
const sharedSecret = getx25519SharedSecret(from, to);
|
|
167
191
|
const nOnce = this.generateJsonNonce(nOnceMaterial);
|
|
168
|
-
|
|
169
|
-
const sealerPub = base58.decode(to.substring("sealer_z".length));
|
|
170
|
-
|
|
171
|
-
const senderPriv = base58.decode(from.substring("sealerSecret_z".length));
|
|
172
|
-
|
|
173
192
|
const plaintext = textEncoder.encode(stableStringify(message));
|
|
174
193
|
|
|
175
|
-
const sharedSecret = x25519.getSharedSecret(senderPriv, sealerPub);
|
|
176
|
-
|
|
177
194
|
const sealedBytes = xsalsa20poly1305(sharedSecret, nOnce).encrypt(
|
|
178
195
|
plaintext,
|
|
179
196
|
);
|
|
@@ -189,14 +206,9 @@ export class PureJSCrypto extends CryptoProvider<Blake3State> {
|
|
|
189
206
|
): T | undefined {
|
|
190
207
|
const nOnce = this.generateJsonNonce(nOnceMaterial);
|
|
191
208
|
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
const senderPub = base58.decode(from.substring("sealer_z".length));
|
|
195
|
-
|
|
209
|
+
const sharedSecret = getx25519SharedSecret(sealer, from);
|
|
196
210
|
const sealedBytes = base64URLtoBytes(sealed.substring("sealed_U".length));
|
|
197
211
|
|
|
198
|
-
const sharedSecret = x25519.getSharedSecret(sealerPriv, senderPub);
|
|
199
|
-
|
|
200
212
|
const plaintext = xsalsa20poly1305(sharedSecret, nOnce).decrypt(
|
|
201
213
|
sealedBytes,
|
|
202
214
|
);
|
package/src/exports.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { base64URLtoBytes, bytesToBase64url } from "./base64url.js";
|
|
2
2
|
import { type RawCoValue } from "./coValue.js";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
CoValueCore,
|
|
5
|
+
idforHeader,
|
|
6
|
+
type AvailableCoValueCore,
|
|
7
|
+
} from "./coValueCore/coValueCore.js";
|
|
4
8
|
import { CoValueUniqueness } from "./coValueCore/verifiedState.js";
|
|
5
9
|
import {
|
|
6
10
|
ControlledAccount,
|
|
@@ -59,7 +63,12 @@ import type { JsonObject, JsonValue } from "./jsonValue.js";
|
|
|
59
63
|
import type * as Media from "./media.js";
|
|
60
64
|
import { disablePermissionErrors } from "./permissions.js";
|
|
61
65
|
import type { Peer, SyncMessage } from "./sync.js";
|
|
62
|
-
import {
|
|
66
|
+
import {
|
|
67
|
+
DisconnectedError,
|
|
68
|
+
SyncManager,
|
|
69
|
+
emptyKnownState,
|
|
70
|
+
hwrServerPeerSelector,
|
|
71
|
+
} from "./sync.js";
|
|
63
72
|
|
|
64
73
|
import {
|
|
65
74
|
getContentMessageSize,
|
|
@@ -159,6 +168,7 @@ export {
|
|
|
159
168
|
LogLevel,
|
|
160
169
|
base64URLtoBytes,
|
|
161
170
|
bytesToBase64url,
|
|
171
|
+
hwrServerPeerSelector,
|
|
162
172
|
};
|
|
163
173
|
|
|
164
174
|
export type {
|
|
@@ -171,6 +181,7 @@ export type {
|
|
|
171
181
|
BinaryStreamStart,
|
|
172
182
|
OpID,
|
|
173
183
|
AccountRole,
|
|
184
|
+
AvailableCoValueCore,
|
|
174
185
|
};
|
|
175
186
|
|
|
176
187
|
export * from "./storage/index.js";
|
package/src/ids.ts
CHANGED
package/src/localNode.ts
CHANGED
|
@@ -479,8 +479,14 @@ export class LocalNode {
|
|
|
479
479
|
return branch.getCurrentContent() as T;
|
|
480
480
|
}
|
|
481
481
|
|
|
482
|
-
//
|
|
483
|
-
|
|
482
|
+
// Do a synchronous check to see if the branch exists, if not we don't need to try to load the branch
|
|
483
|
+
if (!source.hasBranch(branchName, branchOwnerID)) {
|
|
484
|
+
return source
|
|
485
|
+
.createBranch(branchName, branchOwnerID)
|
|
486
|
+
.getCurrentContent() as T;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
await this.loadCoValueCore(branch.id);
|
|
484
490
|
|
|
485
491
|
if (!branch.isAvailable()) {
|
|
486
492
|
return source
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createContentMessage,
|
|
3
3
|
exceedsRecommendedSize,
|
|
4
|
-
getTransactionSize,
|
|
5
4
|
} from "../coValueContentMessage.js";
|
|
6
5
|
import {
|
|
7
6
|
type CoValueCore,
|
|
@@ -110,10 +109,14 @@ export class StorageApiAsync implements StorageAPI {
|
|
|
110
109
|
|
|
111
110
|
let idx = 0;
|
|
112
111
|
|
|
113
|
-
signatures.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
112
|
+
const lastSignature = signatures[signatures.length - 1];
|
|
113
|
+
|
|
114
|
+
if (lastSignature?.signature !== sessionRow.lastSignature) {
|
|
115
|
+
signatures.push({
|
|
116
|
+
idx: sessionRow.lastIdx,
|
|
117
|
+
signature: sessionRow.lastSignature,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
117
120
|
|
|
118
121
|
for (const signature of signatures) {
|
|
119
122
|
const newTxsInSession = await this.dbClient.getNewTransactionInSession(
|
|
@@ -113,10 +113,14 @@ export class StorageApiSync implements StorageAPI {
|
|
|
113
113
|
|
|
114
114
|
let idx = 0;
|
|
115
115
|
|
|
116
|
-
signatures.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
const lastSignature = signatures[signatures.length - 1];
|
|
117
|
+
|
|
118
|
+
if (lastSignature?.signature !== sessionRow.lastSignature) {
|
|
119
|
+
signatures.push({
|
|
120
|
+
idx: sessionRow.lastIdx,
|
|
121
|
+
signature: sessionRow.lastSignature,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
120
124
|
|
|
121
125
|
for (const signature of signatures) {
|
|
122
126
|
const newTxsInSession = this.dbClient.getNewTransactionInSession(
|
package/src/sync.ts
CHANGED
|
@@ -219,28 +219,7 @@ export class SyncManager {
|
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
-
|
|
223
|
-
id: RawCoID,
|
|
224
|
-
peer: PeerState,
|
|
225
|
-
seen: Set<RawCoID> = new Set(),
|
|
226
|
-
) {
|
|
227
|
-
this.sendNewContent(id, peer, seen, true);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
sendNewContentWithoutDependencies(
|
|
231
|
-
id: RawCoID,
|
|
232
|
-
peer: PeerState,
|
|
233
|
-
seen: Set<RawCoID> = new Set(),
|
|
234
|
-
) {
|
|
235
|
-
this.sendNewContent(id, peer, seen, false);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
private sendNewContent(
|
|
239
|
-
id: RawCoID,
|
|
240
|
-
peer: PeerState,
|
|
241
|
-
seen: Set<RawCoID> = new Set(),
|
|
242
|
-
includeDependencies: boolean,
|
|
243
|
-
) {
|
|
222
|
+
sendNewContent(id: RawCoID, peer: PeerState, seen: Set<RawCoID> = new Set()) {
|
|
244
223
|
if (seen.has(id)) {
|
|
245
224
|
return;
|
|
246
225
|
}
|
|
@@ -253,9 +232,10 @@ export class SyncManager {
|
|
|
253
232
|
return;
|
|
254
233
|
}
|
|
255
234
|
|
|
235
|
+
const includeDependencies = peer.role !== "server";
|
|
256
236
|
if (includeDependencies) {
|
|
257
237
|
for (const dependency of coValue.getDependedOnCoValues()) {
|
|
258
|
-
this.
|
|
238
|
+
this.sendNewContent(dependency, peer, seen);
|
|
259
239
|
}
|
|
260
240
|
}
|
|
261
241
|
|
|
@@ -434,7 +414,7 @@ export class SyncManager {
|
|
|
434
414
|
const coValue = this.local.getCoValue(msg.id);
|
|
435
415
|
|
|
436
416
|
if (coValue.isAvailable()) {
|
|
437
|
-
this.
|
|
417
|
+
this.sendNewContent(msg.id, peer);
|
|
438
418
|
return;
|
|
439
419
|
}
|
|
440
420
|
|
|
@@ -444,7 +424,7 @@ export class SyncManager {
|
|
|
444
424
|
|
|
445
425
|
const handleLoadResult = () => {
|
|
446
426
|
if (coValue.isAvailable()) {
|
|
447
|
-
this.
|
|
427
|
+
this.sendNewContent(msg.id, peer);
|
|
448
428
|
return;
|
|
449
429
|
}
|
|
450
430
|
|
|
@@ -478,11 +458,7 @@ export class SyncManager {
|
|
|
478
458
|
}
|
|
479
459
|
|
|
480
460
|
if (coValue.isAvailable()) {
|
|
481
|
-
|
|
482
|
-
this.sendNewContentWithoutDependencies(msg.id, peer);
|
|
483
|
-
} else {
|
|
484
|
-
this.sendNewContentIncludingDependencies(msg.id, peer);
|
|
485
|
-
}
|
|
461
|
+
this.sendNewContent(msg.id, peer);
|
|
486
462
|
}
|
|
487
463
|
}
|
|
488
464
|
|
|
@@ -777,7 +753,7 @@ export class SyncManager {
|
|
|
777
753
|
|
|
778
754
|
// We directly forward the new content to peers that have an active subscription
|
|
779
755
|
if (peer.optimisticKnownStates.has(coValue.id)) {
|
|
780
|
-
this.
|
|
756
|
+
this.sendNewContent(coValue.id, peer);
|
|
781
757
|
syncedPeers.push(peer);
|
|
782
758
|
} else if (
|
|
783
759
|
peer.role === "server" &&
|
|
@@ -808,7 +784,7 @@ export class SyncManager {
|
|
|
808
784
|
handleCorrection(msg: KnownStateMessage, peer: PeerState) {
|
|
809
785
|
peer.setKnownState(msg.id, knownStateIn(msg));
|
|
810
786
|
|
|
811
|
-
return this.
|
|
787
|
+
return this.sendNewContent(msg.id, peer);
|
|
812
788
|
}
|
|
813
789
|
|
|
814
790
|
private syncQueue = new LocalTransactionsSyncQueue((content) =>
|