cojson-storage-indexeddb 0.8.34 → 0.8.35
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/CHANGELOG.md +12 -0
- package/dist/idbClient.js +98 -0
- package/dist/idbClient.js.map +1 -0
- package/dist/idbNode.js +78 -0
- package/dist/idbNode.js.map +1 -0
- package/dist/index.js +1 -410
- package/dist/index.js.map +1 -1
- package/dist/syncManager.js +198 -0
- package/dist/syncManager.js.map +1 -0
- package/dist/syncUtils.js +95 -0
- package/dist/syncUtils.js.map +1 -0
- package/dist/tests/fixtureMessages.js +83 -0
- package/dist/tests/fixtureMessages.js.map +1 -0
- package/dist/tests/getDependedOnCoValues.test.js +149 -0
- package/dist/tests/getDependedOnCoValues.test.js.map +1 -0
- package/dist/tests/{index.test.js → idbNode.test.js} +3 -3
- package/dist/tests/idbNode.test.js.map +1 -0
- package/dist/tests/syncManager.test.js +251 -0
- package/dist/tests/syncManager.test.js.map +1 -0
- package/package.json +2 -2
- package/src/idbClient.ts +237 -0
- package/src/idbNode.ts +123 -0
- package/src/index.ts +1 -733
- package/src/syncManager.ts +336 -0
- package/src/syncUtils.ts +146 -0
- package/src/tests/fixtureMessages.ts +95 -0
- package/src/tests/getDependedOnCoValues.test.ts +179 -0
- package/src/tests/{index.test.ts → idbNode.test.ts} +2 -2
- package/src/tests/syncManager.test.ts +337 -0
- package/dist/tests/index.test.js.map +0 -1
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CojsonInternalTypes,
|
|
3
|
+
MAX_RECOMMENDED_TX_SIZE,
|
|
4
|
+
OutgoingSyncQueue,
|
|
5
|
+
SessionID,
|
|
6
|
+
SyncMessage,
|
|
7
|
+
cojsonInternals,
|
|
8
|
+
emptyKnownState,
|
|
9
|
+
} from "cojson";
|
|
10
|
+
import { IDBClient, StoredSessionRow } from "./idbClient";
|
|
11
|
+
import { SyncPromise } from "./syncPromises.js";
|
|
12
|
+
import { collectNewTxs, getDependedOnCoValues } from "./syncUtils";
|
|
13
|
+
import NewContentMessage = CojsonInternalTypes.NewContentMessage;
|
|
14
|
+
import KnownStateMessage = CojsonInternalTypes.KnownStateMessage;
|
|
15
|
+
import RawCoID = CojsonInternalTypes.RawCoID;
|
|
16
|
+
|
|
17
|
+
type OutputMessageMap = Record<
|
|
18
|
+
RawCoID,
|
|
19
|
+
{ knownMessage: KnownStateMessage; contentMessages?: NewContentMessage[] }
|
|
20
|
+
>;
|
|
21
|
+
|
|
22
|
+
export class SyncManager {
|
|
23
|
+
private readonly toLocalNode: OutgoingSyncQueue;
|
|
24
|
+
private readonly idbClient: IDBClient;
|
|
25
|
+
|
|
26
|
+
constructor(idbClient: IDBClient, toLocalNode: OutgoingSyncQueue) {
|
|
27
|
+
this.toLocalNode = toLocalNode;
|
|
28
|
+
this.idbClient = idbClient;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async handleSyncMessage(msg: SyncMessage) {
|
|
32
|
+
switch (msg.action) {
|
|
33
|
+
case "load":
|
|
34
|
+
await this.handleLoad(msg);
|
|
35
|
+
break;
|
|
36
|
+
case "content":
|
|
37
|
+
await this.handleContent(msg);
|
|
38
|
+
break;
|
|
39
|
+
case "known":
|
|
40
|
+
await this.handleKnown(msg);
|
|
41
|
+
break;
|
|
42
|
+
case "done":
|
|
43
|
+
await this.handleDone(msg);
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async handleSessionUpdate({
|
|
49
|
+
sessionRow,
|
|
50
|
+
peerKnownState,
|
|
51
|
+
newContentMessages,
|
|
52
|
+
}: {
|
|
53
|
+
sessionRow: StoredSessionRow;
|
|
54
|
+
peerKnownState: CojsonInternalTypes.CoValueKnownState;
|
|
55
|
+
newContentMessages: CojsonInternalTypes.NewContentMessage[];
|
|
56
|
+
}) {
|
|
57
|
+
if (
|
|
58
|
+
sessionRow.lastIdx <= (peerKnownState.sessions[sessionRow.sessionID] || 0)
|
|
59
|
+
)
|
|
60
|
+
return;
|
|
61
|
+
|
|
62
|
+
const firstNewTxIdx = peerKnownState.sessions[sessionRow.sessionID] || 0;
|
|
63
|
+
const signaturesAndIdxs = await this.idbClient.getSignatures(
|
|
64
|
+
sessionRow,
|
|
65
|
+
firstNewTxIdx,
|
|
66
|
+
);
|
|
67
|
+
const newTxsInSession = await this.idbClient.getNewTransactionInSession(
|
|
68
|
+
sessionRow,
|
|
69
|
+
firstNewTxIdx,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
collectNewTxs(
|
|
73
|
+
newTxsInSession,
|
|
74
|
+
newContentMessages,
|
|
75
|
+
sessionRow,
|
|
76
|
+
signaturesAndIdxs,
|
|
77
|
+
peerKnownState,
|
|
78
|
+
firstNewTxIdx,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async sendNewContent(
|
|
83
|
+
coValueKnownState: CojsonInternalTypes.CoValueKnownState,
|
|
84
|
+
): Promise<void> {
|
|
85
|
+
const outputMessages: OutputMessageMap =
|
|
86
|
+
await this.collectCoValueData(coValueKnownState);
|
|
87
|
+
|
|
88
|
+
// reverse it to send the top level id the last in the order
|
|
89
|
+
const collectedMessages = Object.values(outputMessages).reverse();
|
|
90
|
+
collectedMessages.forEach(({ knownMessage, contentMessages }) => {
|
|
91
|
+
this.sendStateMessage(knownMessage);
|
|
92
|
+
|
|
93
|
+
contentMessages?.length &&
|
|
94
|
+
contentMessages.forEach((msg) => this.sendStateMessage(msg));
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private async collectCoValueData(
|
|
99
|
+
peerKnownState: CojsonInternalTypes.CoValueKnownState,
|
|
100
|
+
messageMap: OutputMessageMap = {},
|
|
101
|
+
asDependencyOf?: CojsonInternalTypes.RawCoID,
|
|
102
|
+
) {
|
|
103
|
+
if (messageMap[peerKnownState.id]) {
|
|
104
|
+
return messageMap;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const coValueRow = await this.idbClient.getCoValue(peerKnownState.id);
|
|
108
|
+
|
|
109
|
+
if (!coValueRow) {
|
|
110
|
+
const emptyKnownMessage: KnownStateMessage = {
|
|
111
|
+
action: "known",
|
|
112
|
+
...emptyKnownState(peerKnownState.id),
|
|
113
|
+
};
|
|
114
|
+
asDependencyOf && (emptyKnownMessage.asDependencyOf = asDependencyOf);
|
|
115
|
+
messageMap[peerKnownState.id] = { knownMessage: emptyKnownMessage };
|
|
116
|
+
return messageMap;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const allCoValueSessions = await this.idbClient.getCoValueSessions(
|
|
120
|
+
coValueRow.rowID,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const newCoValueKnownState: CojsonInternalTypes.CoValueKnownState = {
|
|
124
|
+
id: coValueRow.id,
|
|
125
|
+
header: true,
|
|
126
|
+
sessions: {},
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const newContentMessages: CojsonInternalTypes.NewContentMessage[] = [
|
|
130
|
+
{
|
|
131
|
+
action: "content",
|
|
132
|
+
id: coValueRow.id,
|
|
133
|
+
header: coValueRow.header,
|
|
134
|
+
new: {},
|
|
135
|
+
priority: cojsonInternals.getPriorityFromHeader(coValueRow.header),
|
|
136
|
+
},
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
await Promise.all(
|
|
140
|
+
allCoValueSessions.map((sessionRow) => {
|
|
141
|
+
newCoValueKnownState.sessions[sessionRow.sessionID] =
|
|
142
|
+
sessionRow.lastIdx;
|
|
143
|
+
// Collect new sessions data into newContentMessages
|
|
144
|
+
return this.handleSessionUpdate({
|
|
145
|
+
sessionRow,
|
|
146
|
+
peerKnownState,
|
|
147
|
+
newContentMessages,
|
|
148
|
+
});
|
|
149
|
+
}),
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
const dependedOnCoValuesList = getDependedOnCoValues({
|
|
153
|
+
coValueRow,
|
|
154
|
+
newContentMessages,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const knownMessage: KnownStateMessage = {
|
|
158
|
+
action: "known",
|
|
159
|
+
...newCoValueKnownState,
|
|
160
|
+
};
|
|
161
|
+
asDependencyOf && (knownMessage.asDependencyOf = asDependencyOf);
|
|
162
|
+
messageMap[newCoValueKnownState.id] = {
|
|
163
|
+
knownMessage: knownMessage,
|
|
164
|
+
contentMessages: newContentMessages,
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
await Promise.all(
|
|
168
|
+
dependedOnCoValuesList.map((dependedOnCoValue) =>
|
|
169
|
+
this.collectCoValueData(
|
|
170
|
+
{
|
|
171
|
+
id: dependedOnCoValue,
|
|
172
|
+
header: false,
|
|
173
|
+
sessions: {},
|
|
174
|
+
},
|
|
175
|
+
messageMap,
|
|
176
|
+
asDependencyOf || coValueRow.id,
|
|
177
|
+
),
|
|
178
|
+
),
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
return messageMap;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
handleLoad(msg: CojsonInternalTypes.LoadMessage) {
|
|
185
|
+
return this.sendNewContent(msg);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async handleContent(
|
|
189
|
+
msg: CojsonInternalTypes.NewContentMessage,
|
|
190
|
+
): Promise<void | unknown> {
|
|
191
|
+
const coValueRow = await this.idbClient.getCoValue(msg.id);
|
|
192
|
+
|
|
193
|
+
// We have no info about coValue header
|
|
194
|
+
const invalidAssumptionOnHeaderPresence = !msg.header && !coValueRow;
|
|
195
|
+
|
|
196
|
+
if (invalidAssumptionOnHeaderPresence) {
|
|
197
|
+
return this.sendStateMessage({
|
|
198
|
+
action: "known",
|
|
199
|
+
id: msg.id,
|
|
200
|
+
header: false,
|
|
201
|
+
sessions: {},
|
|
202
|
+
isCorrection: true,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const storedCoValueRowID: number = coValueRow
|
|
207
|
+
? coValueRow.rowID
|
|
208
|
+
: await this.idbClient.addCoValue(msg);
|
|
209
|
+
|
|
210
|
+
const allOurSessionsEntries =
|
|
211
|
+
await this.idbClient.getCoValueSessions(storedCoValueRowID);
|
|
212
|
+
|
|
213
|
+
const allOurSessions: {
|
|
214
|
+
[sessionID: SessionID]: StoredSessionRow;
|
|
215
|
+
} = Object.fromEntries(
|
|
216
|
+
allOurSessionsEntries.map((row) => [row.sessionID, row]),
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
const ourKnown: CojsonInternalTypes.CoValueKnownState = {
|
|
220
|
+
id: msg.id,
|
|
221
|
+
header: true,
|
|
222
|
+
sessions: {},
|
|
223
|
+
};
|
|
224
|
+
let invalidAssumptions = false;
|
|
225
|
+
|
|
226
|
+
await Promise.all(
|
|
227
|
+
(Object.keys(msg.new) as SessionID[]).map((sessionID) => {
|
|
228
|
+
const sessionRow = allOurSessions[sessionID];
|
|
229
|
+
if (sessionRow) {
|
|
230
|
+
ourKnown.sessions[sessionRow.sessionID] = sessionRow.lastIdx;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if ((sessionRow?.lastIdx || 0) < (msg.new[sessionID]?.after || 0)) {
|
|
234
|
+
invalidAssumptions = true;
|
|
235
|
+
} else {
|
|
236
|
+
return this.putNewTxs(msg, sessionID, sessionRow, storedCoValueRowID);
|
|
237
|
+
}
|
|
238
|
+
}),
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
if (invalidAssumptions) {
|
|
242
|
+
this.sendStateMessage({
|
|
243
|
+
action: "known",
|
|
244
|
+
...ourKnown,
|
|
245
|
+
isCorrection: invalidAssumptions,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private async putNewTxs(
|
|
251
|
+
msg: CojsonInternalTypes.NewContentMessage,
|
|
252
|
+
sessionID: SessionID,
|
|
253
|
+
sessionRow: StoredSessionRow | undefined,
|
|
254
|
+
storedCoValueRowID: number,
|
|
255
|
+
) {
|
|
256
|
+
const newTransactions = msg.new[sessionID]?.newTransactions || [];
|
|
257
|
+
|
|
258
|
+
const actuallyNewOffset =
|
|
259
|
+
(sessionRow?.lastIdx || 0) - (msg.new[sessionID]?.after || 0);
|
|
260
|
+
|
|
261
|
+
const actuallyNewTransactions = newTransactions.slice(actuallyNewOffset);
|
|
262
|
+
|
|
263
|
+
let newBytesSinceLastSignature =
|
|
264
|
+
(sessionRow?.bytesSinceLastSignature || 0) +
|
|
265
|
+
actuallyNewTransactions.reduce(
|
|
266
|
+
(sum, tx) =>
|
|
267
|
+
sum +
|
|
268
|
+
(tx.privacy === "private"
|
|
269
|
+
? tx.encryptedChanges.length
|
|
270
|
+
: tx.changes.length),
|
|
271
|
+
0,
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
const newLastIdx =
|
|
275
|
+
(sessionRow?.lastIdx || 0) + actuallyNewTransactions.length;
|
|
276
|
+
|
|
277
|
+
let shouldWriteSignature = false;
|
|
278
|
+
|
|
279
|
+
if (newBytesSinceLastSignature > MAX_RECOMMENDED_TX_SIZE) {
|
|
280
|
+
shouldWriteSignature = true;
|
|
281
|
+
newBytesSinceLastSignature = 0;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const nextIdx = sessionRow?.lastIdx || 0;
|
|
285
|
+
|
|
286
|
+
const sessionUpdate = {
|
|
287
|
+
coValue: storedCoValueRowID,
|
|
288
|
+
sessionID: sessionID,
|
|
289
|
+
lastIdx: newLastIdx,
|
|
290
|
+
lastSignature: msg.new[sessionID]!.lastSignature,
|
|
291
|
+
bytesSinceLastSignature: newBytesSinceLastSignature,
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const sessionRowID: number = await this.idbClient.addSessionUpdate(
|
|
295
|
+
sessionRow,
|
|
296
|
+
sessionUpdate,
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
let maybePutRequest;
|
|
300
|
+
if (shouldWriteSignature) {
|
|
301
|
+
maybePutRequest = this.idbClient.addSignatureAfter({
|
|
302
|
+
sessionRowID,
|
|
303
|
+
idx: newLastIdx - 1,
|
|
304
|
+
signature: msg.new[sessionID]!.lastSignature,
|
|
305
|
+
});
|
|
306
|
+
} else {
|
|
307
|
+
maybePutRequest = SyncPromise.resolve();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return maybePutRequest.then(() =>
|
|
311
|
+
Promise.all(
|
|
312
|
+
actuallyNewTransactions.map((newTransaction, i) => {
|
|
313
|
+
return this.idbClient.addTransaction(
|
|
314
|
+
sessionRowID,
|
|
315
|
+
nextIdx + i,
|
|
316
|
+
newTransaction,
|
|
317
|
+
);
|
|
318
|
+
}),
|
|
319
|
+
),
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
handleKnown(_msg: KnownStateMessage) {
|
|
324
|
+
// We don't intend to use IndexedDB itself as a synchronisation mechanism, so we can ignore the known messages
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async sendStateMessage(msg: any): Promise<unknown> {
|
|
328
|
+
return this.toLocalNode
|
|
329
|
+
.push(msg)
|
|
330
|
+
.catch((e) =>
|
|
331
|
+
console.error(`Error sending ${msg.action} state, id ${msg.id}`, e),
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
handleDone(_msg: CojsonInternalTypes.DoneMessage) {}
|
|
336
|
+
}
|
package/src/syncUtils.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CojsonInternalTypes,
|
|
3
|
+
JsonValue,
|
|
4
|
+
SessionID,
|
|
5
|
+
Stringified,
|
|
6
|
+
cojsonInternals,
|
|
7
|
+
} from "cojson";
|
|
8
|
+
import {
|
|
9
|
+
SignatureAfterRow,
|
|
10
|
+
StoredCoValueRow,
|
|
11
|
+
StoredSessionRow,
|
|
12
|
+
TransactionRow,
|
|
13
|
+
} from "./idbClient";
|
|
14
|
+
|
|
15
|
+
export function collectNewTxs(
|
|
16
|
+
newTxsInSession: TransactionRow[],
|
|
17
|
+
newContentMessages: CojsonInternalTypes.NewContentMessage[],
|
|
18
|
+
sessionRow: StoredSessionRow,
|
|
19
|
+
signaturesAndIdxs: SignatureAfterRow[],
|
|
20
|
+
theirKnown: CojsonInternalTypes.CoValueKnownState,
|
|
21
|
+
firstNewTxIdx: number,
|
|
22
|
+
) {
|
|
23
|
+
let idx = firstNewTxIdx;
|
|
24
|
+
|
|
25
|
+
for (const tx of newTxsInSession) {
|
|
26
|
+
let sessionEntry =
|
|
27
|
+
newContentMessages[newContentMessages.length - 1]!.new[
|
|
28
|
+
sessionRow.sessionID
|
|
29
|
+
];
|
|
30
|
+
if (!sessionEntry) {
|
|
31
|
+
sessionEntry = {
|
|
32
|
+
after: idx,
|
|
33
|
+
lastSignature: "WILL_BE_REPLACED" as CojsonInternalTypes.Signature,
|
|
34
|
+
newTransactions: [],
|
|
35
|
+
};
|
|
36
|
+
newContentMessages[newContentMessages.length - 1]!.new[
|
|
37
|
+
sessionRow.sessionID
|
|
38
|
+
] = sessionEntry;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
sessionEntry.newTransactions.push(tx.tx);
|
|
42
|
+
|
|
43
|
+
if (signaturesAndIdxs[0] && idx === signaturesAndIdxs[0].idx) {
|
|
44
|
+
sessionEntry.lastSignature = signaturesAndIdxs[0].signature;
|
|
45
|
+
signaturesAndIdxs.shift();
|
|
46
|
+
newContentMessages.push({
|
|
47
|
+
action: "content",
|
|
48
|
+
id: theirKnown.id,
|
|
49
|
+
new: {},
|
|
50
|
+
priority: cojsonInternals.getPriorityFromHeader(undefined),
|
|
51
|
+
});
|
|
52
|
+
} else if (idx === firstNewTxIdx + newTxsInSession.length - 1) {
|
|
53
|
+
sessionEntry.lastSignature = sessionRow.lastSignature;
|
|
54
|
+
}
|
|
55
|
+
idx += 1;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function getDependedOnCoValues({
|
|
60
|
+
coValueRow,
|
|
61
|
+
newContentMessages,
|
|
62
|
+
}: {
|
|
63
|
+
coValueRow: StoredCoValueRow;
|
|
64
|
+
newContentMessages: CojsonInternalTypes.NewContentMessage[];
|
|
65
|
+
}) {
|
|
66
|
+
return coValueRow.header.ruleset.type === "group"
|
|
67
|
+
? getGroupDependedOnCoValues(newContentMessages)
|
|
68
|
+
: coValueRow.header.ruleset.type === "ownedByGroup"
|
|
69
|
+
? getOwnedByGroupDependedOnCoValues(coValueRow, newContentMessages)
|
|
70
|
+
: [];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getGroupDependedOnCoValues(
|
|
74
|
+
newContentMessages: CojsonInternalTypes.NewContentMessage[],
|
|
75
|
+
) {
|
|
76
|
+
const keys: CojsonInternalTypes.RawCoID[] = [];
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Collect all the signing keys inside the transactions to list all the
|
|
80
|
+
* dependencies required to correctly access the CoValue.
|
|
81
|
+
*/
|
|
82
|
+
for (const piece of newContentMessages) {
|
|
83
|
+
for (const sessionEntry of Object.values(piece.new)) {
|
|
84
|
+
for (const tx of sessionEntry.newTransactions) {
|
|
85
|
+
if (tx.privacy !== "trusting") continue;
|
|
86
|
+
|
|
87
|
+
const changes = safeParseChanges(tx.changes);
|
|
88
|
+
for (const change of changes) {
|
|
89
|
+
if (
|
|
90
|
+
change &&
|
|
91
|
+
typeof change === "object" &&
|
|
92
|
+
"op" in change &&
|
|
93
|
+
change.op === "set" &&
|
|
94
|
+
"key" in change &&
|
|
95
|
+
change.key
|
|
96
|
+
) {
|
|
97
|
+
const key = cojsonInternals.getGroupDependentKey(change.key);
|
|
98
|
+
|
|
99
|
+
if (key) {
|
|
100
|
+
keys.push(key);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return keys;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function getOwnedByGroupDependedOnCoValues(
|
|
112
|
+
coValueRow: StoredCoValueRow,
|
|
113
|
+
newContentMessages: CojsonInternalTypes.NewContentMessage[],
|
|
114
|
+
) {
|
|
115
|
+
if (coValueRow.header.ruleset.type !== "ownedByGroup") return [];
|
|
116
|
+
|
|
117
|
+
const keys: CojsonInternalTypes.RawCoID[] = [coValueRow.header.ruleset.group];
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Collect all the signing keys inside the transactions to list all the
|
|
121
|
+
* dependencies required to correctly access the CoValue.
|
|
122
|
+
*/
|
|
123
|
+
for (const piece of newContentMessages) {
|
|
124
|
+
for (const sessionID of Object.keys(piece.new) as SessionID[]) {
|
|
125
|
+
const accountId =
|
|
126
|
+
cojsonInternals.accountOrAgentIDfromSessionID(sessionID);
|
|
127
|
+
|
|
128
|
+
if (
|
|
129
|
+
cojsonInternals.isAccountID(accountId) &&
|
|
130
|
+
accountId !== coValueRow.id
|
|
131
|
+
) {
|
|
132
|
+
keys.push(accountId);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return keys;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function safeParseChanges(changes: Stringified<JsonValue[]>) {
|
|
141
|
+
try {
|
|
142
|
+
return cojsonInternals.parseJSON(changes);
|
|
143
|
+
} catch (e) {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
export const fixtures = {
|
|
2
|
+
co_zKwG8NyfZ8GXqcjDHY4NS3SbU2m: {
|
|
3
|
+
getContent: ({ after = 0 }: { after?: number }) => ({
|
|
4
|
+
action: "content",
|
|
5
|
+
id: "co_zKwG8NyfZ8GXqcjDHY4NS3SbU2m",
|
|
6
|
+
header: {
|
|
7
|
+
type: "comap",
|
|
8
|
+
ruleset: {
|
|
9
|
+
type: "group",
|
|
10
|
+
initialAdmin:
|
|
11
|
+
"sealer_zRKetKBH6tdGP8poA2rV9JDejXqTyAmpusCT4jRcXa4m/signer_z6bcctDRiWxtgmuqLRR6rVhM54DA3xJ2pWCEs6DVf4PSy",
|
|
12
|
+
},
|
|
13
|
+
meta: {
|
|
14
|
+
type: "account",
|
|
15
|
+
},
|
|
16
|
+
createdAt: null,
|
|
17
|
+
uniqueness: null,
|
|
18
|
+
},
|
|
19
|
+
new: {
|
|
20
|
+
"sealer_zRKetKBH6tdGP8poA2rV9JDejXqTyAmpusCT4jRcXa4m/signer_z6bcctDRiWxtgmuqLRR6rVhM54DA3xJ2pWCEs6DVf4PSy_session_zbcBS6rHy8kA":
|
|
21
|
+
{
|
|
22
|
+
after,
|
|
23
|
+
lastSignature:
|
|
24
|
+
"signature_z2kcFHUPe1qGFYDY4ayvvFR2unFc4jeYph93nSCSjZYS14vnGN4uAw7pKZx1PEhwnspJcDizMRbLaFC8v13i6S79A",
|
|
25
|
+
newTransactions: [
|
|
26
|
+
{
|
|
27
|
+
privacy: "trusting",
|
|
28
|
+
madeAt: 1732368535089,
|
|
29
|
+
changes:
|
|
30
|
+
'[{"key":"sealer_zRKetKBH6tdGP8poA2rV9JDejXqTyAmpusCT4jRcXa4m/signer_z6bcctDRiWxtgmuqLRR6rVhM54DA3xJ2pWCEs6DVf4PSy","op":"set","value":"admin"}]',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
privacy: "trusting",
|
|
34
|
+
madeAt: 1732368535096,
|
|
35
|
+
changes:
|
|
36
|
+
'[{"key":"key_z2YMuLXEfXG44Z2jGk_for_sealer_zRKetKBH6tdGP8poA2rV9JDejXqTyAmpusCT4jRcXa4m/signer_z6bcctDRiWxtgmuqLRR6rVhM54DA3xJ2pWCEs6DVf4PSy","op":"set","value":"sealed_UAIpJTby8EovZW6WPtAqdaczA2_r6PEWRBuEtLN93-Dh9xDJFaGUNTXK1Cck61tjvA3GoGn9EyQdNN2fU6tnmWP2M09a83dG41Q=="}]',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
privacy: "trusting",
|
|
40
|
+
madeAt: 1732368535099,
|
|
41
|
+
changes:
|
|
42
|
+
'[{"key":"readKey","op":"set","value":"key_z2YMuLXEfXG44Z2jGk"}]',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
"sealer_zRKetKBH6tdGP8poA2rV9JDejXqTyAmpusCT4jRcXa4m/signer_z6bcctDRiWxtgmuqLRR6rVhM54DA3xJ2pWCEs6DVf4PSy_session_zXgW54i2cCNA":
|
|
47
|
+
{
|
|
48
|
+
after,
|
|
49
|
+
lastSignature:
|
|
50
|
+
"signature_z5FsinkJCpqZfozVBkEMSchCQarsAjvMYpWN4d227PZtqCiM7KRBNukND3B25Q73idBLdY2MsghbmYFz5JHXk3d4D",
|
|
51
|
+
newTransactions: [
|
|
52
|
+
{
|
|
53
|
+
privacy: "trusting",
|
|
54
|
+
madeAt: 1732368535113,
|
|
55
|
+
changes:
|
|
56
|
+
'[{"key":"profile","op":"set","value":"co_zMKhQJs5rAeGjta3JX2qEdBS6hS"}]',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
priority: 0,
|
|
62
|
+
}),
|
|
63
|
+
known: {
|
|
64
|
+
action: "known",
|
|
65
|
+
id: "co_zKwG8NyfZ8GXqcjDHY4NS3SbU2m",
|
|
66
|
+
header: true,
|
|
67
|
+
sessions: {
|
|
68
|
+
"sealer_zRKetKBH6tdGP8poA2rV9JDejXqTyAmpusCT4jRcXa4m/signer_z6bcctDRiWxtgmuqLRR6rVhM54DA3xJ2pWCEs6DVf4PSy_session_zbcBS6rHy8kA": 3,
|
|
69
|
+
"sealer_zRKetKBH6tdGP8poA2rV9JDejXqTyAmpusCT4jRcXa4m/signer_z6bcctDRiWxtgmuqLRR6rVhM54DA3xJ2pWCEs6DVf4PSy_session_zXgW54i2cCNA": 1,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
sessionRecords: [
|
|
73
|
+
{
|
|
74
|
+
bytesSinceLastSignature: 479,
|
|
75
|
+
coValue: 2,
|
|
76
|
+
lastIdx: 3,
|
|
77
|
+
lastSignature:
|
|
78
|
+
"signature_z2kcFHUPe1qGFYDY4ayvvFR2unFc4jeYph93nSCSjZYS14vnGN4uAw7pKZx1PEhwnspJcDizMRbLaFC8v13i6S79A",
|
|
79
|
+
rowID: 2,
|
|
80
|
+
sessionID:
|
|
81
|
+
"sealer_zRKetKBH6tdGP8poA2rV9JDejXqTyAmpusCT4jRcXa4m/signer_z6bcctDRiWxtgmuqLRR6rVhM54DA3xJ2pWCEs6DVf4PSy_session_zbcBS6rHy8kA",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
bytesSinceLastSignature: 71,
|
|
85
|
+
coValue: 2,
|
|
86
|
+
lastIdx: 1,
|
|
87
|
+
lastSignature:
|
|
88
|
+
"signature_z5FsinkJCpqZfozVBkEMSchCQarsAjvMYpWN4d227PZtqCiM7KRBNukND3B25Q73idBLdY2MsghbmYFz5JHXk3d4D",
|
|
89
|
+
rowID: 3,
|
|
90
|
+
sessionID:
|
|
91
|
+
"sealer_zRKetKBH6tdGP8poA2rV9JDejXqTyAmpusCT4jRcXa4m/signer_z6bcctDRiWxtgmuqLRR6rVhM54DA3xJ2pWCEs6DVf4PSy_session_zXgW54i2cCNA",
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
};
|