cojson-storage-indexeddb 0.6.0 → 0.6.1

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # cojson-storage-indexeddb
2
2
 
3
+ ## 0.6.1
4
+
5
+ ### Patch Changes
6
+
7
+ - IndexedDB & timer perf improvements
8
+ - Updated dependencies
9
+ - cojson@0.6.4
10
+
3
11
  ## 0.6.0
4
12
 
5
13
  ### Minor Changes
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { cojsonInternals, MAX_RECOMMENDED_TX_SIZE, } from "cojson";
2
+ import { SyncPromise } from "./syncPromises";
2
3
  export class IDBStorage {
3
4
  constructor(db, fromLocalNode, toLocalNode) {
5
+ this.currentTxID = 0;
4
6
  this.db = db;
5
7
  this.fromLocalNode = fromLocalNode.getReader();
6
8
  this.toLocalNode = toLocalNode.getWriter();
@@ -10,12 +12,17 @@ export class IDBStorage {
10
12
  const result = await this.fromLocalNode.read();
11
13
  done = result.done;
12
14
  if (result.value) {
13
- await this.handleSyncMessage(result.value);
14
15
  // console.log(
15
16
  // "IDB: handling msg",
16
17
  // result.value.id,
17
18
  // result.value.action
18
19
  // );
20
+ await this.handleSyncMessage(result.value);
21
+ // console.log(
22
+ // "IDB: handled msg",
23
+ // result.value.id,
24
+ // result.value.action
25
+ // );
19
26
  }
20
27
  }
21
28
  })();
@@ -63,19 +70,21 @@ export class IDBStorage {
63
70
  keyPath: ["ses", "idx"],
64
71
  });
65
72
  }
66
- if (ev.oldVersion !== 0 && ev.oldVersion <= 3) {
67
- // fix embarrassing off-by-one error for transaction indices
68
- console.log("Migration: fixing off-by-one error");
69
- const transaction = ev.target.transaction;
70
- const txsStore = transaction.objectStore("transactions");
71
- const txs = await promised(txsStore.getAll());
72
- for (const tx of txs) {
73
- await promised(txsStore.delete([tx.ses, tx.idx]));
74
- tx.idx -= 1;
75
- await promised(txsStore.add(tx));
76
- }
77
- console.log("Migration: fixing off-by-one error - done");
78
- }
73
+ // if (ev.oldVersion !== 0 && ev.oldVersion <= 3) {
74
+ // // fix embarrassing off-by-one error for transaction indices
75
+ // console.log("Migration: fixing off-by-one error");
76
+ // const transaction = (
77
+ // ev.target as unknown as { transaction: IDBTransaction }
78
+ // ).transaction;
79
+ // const txsStore = transaction.objectStore("transactions");
80
+ // const txs = await promised(txsStore.getAll());
81
+ // for (const tx of txs) {
82
+ // await promised(txsStore.delete([tx.ses, tx.idx]));
83
+ // tx.idx -= 1;
84
+ // await promised(txsStore.add(tx));
85
+ // }
86
+ // console.log("Migration: fixing off-by-one error - done");
87
+ // }
79
88
  };
80
89
  });
81
90
  return new IDBStorage(await dbPromise, fromLocalNode, toLocalNode);
@@ -83,253 +92,343 @@ export class IDBStorage {
83
92
  async handleSyncMessage(msg) {
84
93
  switch (msg.action) {
85
94
  case "load":
86
- await this.handleLoad(msg);
95
+ /*await*/ this.handleLoad(msg);
87
96
  break;
88
97
  case "content":
89
98
  await this.handleContent(msg);
90
99
  break;
91
100
  case "known":
92
- await this.handleKnown(msg);
101
+ /*await*/ this.handleKnown(msg);
93
102
  break;
94
103
  case "done":
95
- await this.handleDone(msg);
104
+ /*await*/ this.handleDone(msg);
96
105
  break;
97
106
  }
98
107
  }
99
- async sendNewContentAfter(theirKnown, { coValues, sessions, transactions, signatureAfter, }, asDependencyOf) {
100
- const coValueRow = await promised(coValues.index("coValuesById").get(theirKnown.id));
101
- const allOurSessions = coValueRow
102
- ? await promised(sessions.index("sessionsByCoValue").getAll(coValueRow.rowID))
103
- : [];
104
- const ourKnown = {
105
- id: theirKnown.id,
106
- header: !!coValueRow,
107
- sessions: {},
108
- };
109
- const newContentPieces = [
110
- {
111
- action: "content",
112
- id: theirKnown.id,
113
- header: theirKnown.header ? undefined : coValueRow?.header,
114
- new: {},
115
- },
116
- ];
117
- for (const sessionRow of allOurSessions) {
118
- ourKnown.sessions[sessionRow.sessionID] = sessionRow.lastIdx;
119
- if (sessionRow.lastIdx >
120
- (theirKnown.sessions[sessionRow.sessionID] || 0)) {
121
- const firstNewTxIdx = theirKnown.sessions[sessionRow.sessionID] || 0;
122
- const signaturesAndIdxs = await promised(signatureAfter.getAll(IDBKeyRange.bound([sessionRow.rowID, firstNewTxIdx], [sessionRow.rowID, Infinity])));
123
- // console.log(
124
- // theirKnown.id,
125
- // "signaturesAndIdxs",
126
- // JSON.stringify(signaturesAndIdxs)
127
- // );
128
- const newTxInSession = await promised(transactions.getAll(IDBKeyRange.bound([sessionRow.rowID, firstNewTxIdx], [sessionRow.rowID, Infinity])));
129
- let idx = firstNewTxIdx;
108
+ makeRequest(handler) {
109
+ return new SyncPromise((resolve, reject) => {
110
+ let txEntry = this.currentTx;
111
+ const requestEntry = ({ stores, }) => {
112
+ const request = handler(stores);
113
+ request.onerror = () => {
114
+ console.error("Error in request", request.error);
115
+ this.currentTx = undefined;
116
+ reject(request.error);
117
+ // TODO: recover pending requests in new tx
118
+ };
119
+ request.onsuccess = () => {
120
+ const value = request.result;
121
+ resolve(value);
122
+ const next = txEntry.pendingRequests.shift();
123
+ if (next) {
124
+ next({ stores });
125
+ }
126
+ else {
127
+ if (this.currentTx === txEntry) {
128
+ this.currentTx = undefined;
129
+ }
130
+ }
131
+ };
132
+ };
133
+ if (!txEntry || performance.now() - txEntry.startedAt > 20) {
134
+ const tx = this.db.transaction(["coValues", "sessions", "transactions", "signatureAfter"], "readwrite");
135
+ txEntry = {
136
+ id: this.currentTxID++,
137
+ tx,
138
+ stores: {
139
+ coValues: tx.objectStore("coValues"),
140
+ sessions: tx.objectStore("sessions"),
141
+ transactions: tx.objectStore("transactions"),
142
+ signatureAfter: tx.objectStore("signatureAfter"),
143
+ },
144
+ startedAt: performance.now(),
145
+ pendingRequests: [],
146
+ };
147
+ console.time("IndexedDB TX" + txEntry.id);
148
+ txEntry.tx.oncomplete = () => {
149
+ console.timeEnd("IndexedDB TX" + txEntry.id);
150
+ };
151
+ this.currentTx = txEntry;
152
+ requestEntry(txEntry);
153
+ }
154
+ else {
155
+ txEntry.pendingRequests.push(requestEntry);
130
156
  // console.log(
131
- // theirKnown.id,
132
- // "newTxInSession",
133
- // newTxInSession.length
157
+ // "Queued request in TX " + txEntry.id,
158
+ // txEntry.pendingRequests.length
134
159
  // );
135
- for (const tx of newTxInSession) {
136
- let sessionEntry = newContentPieces[newContentPieces.length - 1].new[sessionRow.sessionID];
137
- if (!sessionEntry) {
138
- sessionEntry = {
139
- after: idx,
140
- lastSignature: "WILL_BE_REPLACED",
141
- newTransactions: [],
142
- };
143
- newContentPieces[newContentPieces.length - 1].new[sessionRow.sessionID] = sessionEntry;
144
- }
145
- sessionEntry.newTransactions.push(tx.tx);
146
- if (signaturesAndIdxs[0] &&
147
- idx === signaturesAndIdxs[0].idx) {
148
- sessionEntry.lastSignature =
149
- signaturesAndIdxs[0].signature;
150
- signaturesAndIdxs.shift();
151
- newContentPieces.push({
152
- action: "content",
153
- id: theirKnown.id,
154
- new: {},
160
+ }
161
+ });
162
+ }
163
+ sendNewContentAfter(theirKnown, asDependencyOf) {
164
+ return this.makeRequest(({ coValues }) => coValues.index("coValuesById").get(theirKnown.id))
165
+ .then((coValueRow) => {
166
+ return (coValueRow
167
+ ? this.makeRequest(({ sessions }) => sessions
168
+ .index("sessionsByCoValue")
169
+ .getAll(coValueRow.rowID))
170
+ : SyncPromise.resolve([])).then((allOurSessions) => {
171
+ const ourKnown = {
172
+ id: theirKnown.id,
173
+ header: !!coValueRow,
174
+ sessions: {},
175
+ };
176
+ const newContentPieces = [
177
+ {
178
+ action: "content",
179
+ id: theirKnown.id,
180
+ header: theirKnown.header
181
+ ? undefined
182
+ : coValueRow?.header,
183
+ new: {},
184
+ },
185
+ ];
186
+ return SyncPromise.all(allOurSessions.map((sessionRow) => {
187
+ ourKnown.sessions[sessionRow.sessionID] =
188
+ sessionRow.lastIdx;
189
+ if (sessionRow.lastIdx >
190
+ (theirKnown.sessions[sessionRow.sessionID] || 0)) {
191
+ const firstNewTxIdx = theirKnown.sessions[sessionRow.sessionID] ||
192
+ 0;
193
+ return this.makeRequest(({ signatureAfter }) => signatureAfter.getAll(IDBKeyRange.bound([
194
+ sessionRow.rowID,
195
+ firstNewTxIdx,
196
+ ], [sessionRow.rowID, Infinity]))).then((signaturesAndIdxs) => {
197
+ // console.log(
198
+ // theirKnown.id,
199
+ // "signaturesAndIdxs",
200
+ // JSON.stringify(signaturesAndIdxs)
201
+ // );
202
+ return this.makeRequest(({ transactions }) => transactions.getAll(IDBKeyRange.bound([
203
+ sessionRow.rowID,
204
+ firstNewTxIdx,
205
+ ], [sessionRow.rowID, Infinity]))).then((newTxsInSession) => {
206
+ collectNewTxs(newTxsInSession, newContentPieces, sessionRow, signaturesAndIdxs, theirKnown, firstNewTxIdx);
207
+ });
155
208
  });
156
209
  }
157
- else if (idx ===
158
- firstNewTxIdx + newTxInSession.length - 1) {
159
- sessionEntry.lastSignature = sessionRow.lastSignature;
210
+ else {
211
+ return SyncPromise.resolve();
160
212
  }
161
- idx += 1;
162
- }
163
- }
164
- }
165
- const dependedOnCoValues = coValueRow?.header.ruleset.type === "group"
166
- ? newContentPieces
167
- .flatMap((piece) => Object.values(piece.new))
168
- .flatMap((sessionEntry) => sessionEntry.newTransactions.flatMap((tx) => {
169
- if (tx.privacy !== "trusting")
170
- return [];
171
- // TODO: avoid parse here?
172
- return cojsonInternals
173
- .parseJSON(tx.changes)
174
- .map((change) => change &&
175
- typeof change === "object" &&
176
- "op" in change &&
177
- change.op === "set" &&
178
- "key" in change &&
179
- change.key)
180
- .filter((key) => typeof key === "string" &&
181
- key.startsWith("co_"));
182
- }))
183
- : coValueRow?.header.ruleset.type === "ownedByGroup"
184
- ? [
185
- coValueRow?.header.ruleset.group,
186
- ...new Set(newContentPieces.flatMap((piece) => Object.keys(piece)
187
- .map((sessionID) => cojsonInternals.accountOrAgentIDfromSessionID(sessionID))
188
- .filter((accountID) => cojsonInternals.isAccountID(accountID) && accountID !== theirKnown.id))),
189
- ]
190
- : [];
191
- for (const dependedOnCoValue of dependedOnCoValues) {
192
- await this.sendNewContentAfter({ id: dependedOnCoValue, header: false, sessions: {} }, { coValues, sessions, transactions, signatureAfter }, asDependencyOf || theirKnown.id);
193
- }
194
- await this.toLocalNode.write({
195
- action: "known",
196
- ...ourKnown,
197
- asDependencyOf,
198
- });
199
- const nonEmptyNewContentPieces = newContentPieces.filter((piece) => piece.header || Object.keys(piece.new).length > 0);
200
- // console.log(theirKnown.id, nonEmptyNewContentPieces);
201
- for (const piece of nonEmptyNewContentPieces) {
202
- await this.toLocalNode.write(piece);
203
- await new Promise((resolve) => setTimeout(resolve, 0));
204
- }
213
+ })).then(() => {
214
+ const dependedOnCoValues = getDependedOnCoValues(coValueRow, newContentPieces, theirKnown);
215
+ return SyncPromise.all(dependedOnCoValues.map((dependedOnCoValue) => this.sendNewContentAfter({
216
+ id: dependedOnCoValue,
217
+ header: false,
218
+ sessions: {},
219
+ }, asDependencyOf || theirKnown.id))).then(() => {
220
+ // we're done with IndexedDB stuff here so can use native Promises again
221
+ setTimeout(async () => {
222
+ await this.toLocalNode.write({
223
+ action: "known",
224
+ ...ourKnown,
225
+ asDependencyOf,
226
+ });
227
+ const nonEmptyNewContentPieces = newContentPieces.filter((piece) => piece.header ||
228
+ Object.keys(piece.new).length > 0);
229
+ // console.log(theirKnown.id, nonEmptyNewContentPieces);
230
+ for (const piece of nonEmptyNewContentPieces) {
231
+ await this.toLocalNode.write(piece);
232
+ await new Promise((resolve) => setTimeout(resolve, 0));
233
+ }
234
+ }, 0);
235
+ return Promise.resolve();
236
+ });
237
+ });
238
+ });
239
+ })
240
+ .then(() => { });
205
241
  }
206
242
  handleLoad(msg) {
207
- return this.sendNewContentAfter(msg, this.inTransaction("readonly"));
243
+ return this.sendNewContentAfter(msg);
208
244
  }
209
- async handleContent(msg) {
210
- const { coValues, sessions, transactions, signatureAfter } = this.inTransaction("readwrite");
211
- let storedCoValueRowID = (await promised(coValues.index("coValuesById").get(msg.id)))?.rowID;
212
- if (storedCoValueRowID === undefined) {
213
- const header = msg.header;
214
- if (!header) {
215
- console.error("Expected to be sent header first");
216
- await this.toLocalNode.write({
217
- action: "known",
245
+ handleContent(msg) {
246
+ return this.makeRequest(({ coValues }) => coValues.index("coValuesById").get(msg.id))
247
+ .then((coValueRow) => {
248
+ if (coValueRow?.rowID === undefined) {
249
+ const header = msg.header;
250
+ if (!header) {
251
+ console.error("Expected to be sent header first");
252
+ this.toLocalNode.write({
253
+ action: "known",
254
+ id: msg.id,
255
+ header: false,
256
+ sessions: {},
257
+ isCorrection: true,
258
+ });
259
+ throw new Error("Expected to be sent header first");
260
+ }
261
+ return this.makeRequest(({ coValues }) => coValues.put({
218
262
  id: msg.id,
219
- header: false,
220
- sessions: {},
221
- isCorrection: true,
222
- });
223
- return;
263
+ header: header,
264
+ }));
224
265
  }
225
- storedCoValueRowID = (await promised(coValues.put({
226
- id: msg.id,
227
- header: header,
228
- })));
229
- }
230
- const allOurSessions = await new Promise((resolve) => {
231
- const allOurSessionsRequest = sessions
266
+ else {
267
+ return SyncPromise.resolve(coValueRow.rowID);
268
+ }
269
+ })
270
+ .then((storedCoValueRowID) => {
271
+ this.makeRequest(({ sessions }) => sessions
232
272
  .index("sessionsByCoValue")
233
- .getAll(storedCoValueRowID);
234
- allOurSessionsRequest.onsuccess = () => {
235
- resolve(Object.fromEntries(allOurSessionsRequest.result.map((row) => [row.sessionID, row])));
236
- };
273
+ .getAll(storedCoValueRowID)).then((allOurSessionsEntries) => {
274
+ const allOurSessions = Object.fromEntries(allOurSessionsEntries.map((row) => [row.sessionID, row]));
275
+ const ourKnown = {
276
+ id: msg.id,
277
+ header: true,
278
+ sessions: {},
279
+ };
280
+ let invalidAssumptions = false;
281
+ return Promise.all(Object.keys(msg.new).map((sessionID) => {
282
+ const sessionRow = allOurSessions[sessionID];
283
+ if (sessionRow) {
284
+ ourKnown.sessions[sessionRow.sessionID] =
285
+ sessionRow.lastIdx;
286
+ }
287
+ if ((sessionRow?.lastIdx || 0) <
288
+ (msg.new[sessionID]?.after || 0)) {
289
+ invalidAssumptions = true;
290
+ }
291
+ else {
292
+ return this.putNewTxs(msg, sessionID, sessionRow, storedCoValueRowID);
293
+ }
294
+ })).then(() => {
295
+ if (invalidAssumptions) {
296
+ this.toLocalNode.write({
297
+ action: "known",
298
+ ...ourKnown,
299
+ isCorrection: invalidAssumptions,
300
+ });
301
+ }
302
+ });
303
+ });
237
304
  });
238
- const ourKnown = {
239
- id: msg.id,
240
- header: true,
241
- sessions: {},
305
+ }
306
+ putNewTxs(msg, sessionID, sessionRow, storedCoValueRowID) {
307
+ const newTransactions = msg.new[sessionID]?.newTransactions || [];
308
+ const actuallyNewOffset = (sessionRow?.lastIdx || 0) - (msg.new[sessionID]?.after || 0);
309
+ const actuallyNewTransactions = newTransactions.slice(actuallyNewOffset);
310
+ let newBytesSinceLastSignature = (sessionRow?.bytesSinceLastSignature || 0) +
311
+ actuallyNewTransactions.reduce((sum, tx) => sum +
312
+ (tx.privacy === "private"
313
+ ? tx.encryptedChanges.length
314
+ : tx.changes.length), 0);
315
+ const newLastIdx = (sessionRow?.lastIdx || 0) + actuallyNewTransactions.length;
316
+ let shouldWriteSignature = false;
317
+ if (newBytesSinceLastSignature > MAX_RECOMMENDED_TX_SIZE) {
318
+ shouldWriteSignature = true;
319
+ newBytesSinceLastSignature = 0;
320
+ }
321
+ const nextIdx = sessionRow?.lastIdx || 0;
322
+ const sessionUpdate = {
323
+ coValue: storedCoValueRowID,
324
+ sessionID: sessionID,
325
+ lastIdx: newLastIdx,
326
+ lastSignature: msg.new[sessionID].lastSignature,
327
+ bytesSinceLastSignature: newBytesSinceLastSignature,
242
328
  };
243
- let invalidAssumptions = false;
244
- for (const sessionID of Object.keys(msg.new)) {
245
- const sessionRow = allOurSessions[sessionID];
246
- if (sessionRow) {
247
- ourKnown.sessions[sessionRow.sessionID] = sessionRow.lastIdx;
329
+ return this.makeRequest(({ sessions }) => sessions.put(sessionRow?.rowID
330
+ ? {
331
+ rowID: sessionRow.rowID,
332
+ ...sessionUpdate,
248
333
  }
249
- if ((sessionRow?.lastIdx || 0) < (msg.new[sessionID]?.after || 0)) {
250
- invalidAssumptions = true;
334
+ : sessionUpdate)).then((sessionRowID) => {
335
+ let maybePutRequest;
336
+ if (shouldWriteSignature) {
337
+ maybePutRequest = this.makeRequest(({ signatureAfter }) => signatureAfter.put({
338
+ ses: sessionRowID,
339
+ // TODO: newLastIdx is a misnomer, it's actually more like nextIdx or length
340
+ idx: newLastIdx - 1,
341
+ signature: msg.new[sessionID].lastSignature,
342
+ }));
251
343
  }
252
344
  else {
253
- const newTransactions = msg.new[sessionID]?.newTransactions || [];
254
- const actuallyNewOffset = (sessionRow?.lastIdx || 0) -
255
- (msg.new[sessionID]?.after || 0);
256
- const actuallyNewTransactions = newTransactions.slice(actuallyNewOffset);
257
- let newBytesSinceLastSignature = (sessionRow?.bytesSinceLastSignature || 0) +
258
- actuallyNewTransactions.reduce((sum, tx) => sum +
259
- (tx.privacy === "private"
260
- ? tx.encryptedChanges.length
261
- : tx.changes.length), 0);
262
- const newLastIdx = (sessionRow?.lastIdx || 0) + actuallyNewTransactions.length;
263
- let shouldWriteSignature = false;
264
- if (newBytesSinceLastSignature > MAX_RECOMMENDED_TX_SIZE) {
265
- shouldWriteSignature = true;
266
- newBytesSinceLastSignature = 0;
267
- }
268
- let nextIdx = sessionRow?.lastIdx || 0;
269
- const sessionUpdate = {
270
- coValue: storedCoValueRowID,
271
- sessionID: sessionID,
272
- lastIdx: newLastIdx,
273
- lastSignature: msg.new[sessionID].lastSignature,
274
- bytesSinceLastSignature: newBytesSinceLastSignature,
275
- };
276
- const sessionRowID = (await promised(sessions.put(sessionRow?.rowID
277
- ? {
278
- rowID: sessionRow.rowID,
279
- ...sessionUpdate,
280
- }
281
- : sessionUpdate)));
282
- if (shouldWriteSignature) {
283
- await promised(signatureAfter.put({
284
- ses: sessionRowID,
285
- // TODO: newLastIdx is a misnomer, it's actually more like nextIdx or length
286
- idx: newLastIdx - 1,
287
- signature: msg.new[sessionID].lastSignature,
288
- }));
289
- }
290
- for (const newTransaction of actuallyNewTransactions) {
291
- await promised(transactions.add({
292
- ses: sessionRowID,
293
- idx: nextIdx,
294
- tx: newTransaction,
295
- }));
296
- nextIdx++;
297
- }
345
+ maybePutRequest = SyncPromise.resolve();
298
346
  }
299
- }
300
- if (invalidAssumptions) {
301
- await this.toLocalNode.write({
302
- action: "known",
303
- ...ourKnown,
304
- isCorrection: invalidAssumptions,
305
- });
306
- }
347
+ return maybePutRequest.then(() => Promise.all(actuallyNewTransactions.map((newTransaction, i) => {
348
+ return this.makeRequest(({ transactions }) => transactions.add({
349
+ ses: sessionRowID,
350
+ idx: nextIdx + i,
351
+ tx: newTransaction,
352
+ }));
353
+ })));
354
+ });
307
355
  }
308
356
  handleKnown(msg) {
309
- return this.sendNewContentAfter(msg, this.inTransaction("readonly"));
357
+ return this.sendNewContentAfter(msg);
310
358
  }
311
359
  handleDone(_msg) { }
312
- inTransaction(mode) {
313
- const tx = this.db.transaction(["coValues", "sessions", "transactions", "signatureAfter"], mode);
314
- tx.onerror = (event) => {
315
- const target = event.target;
316
- throw new Error(`Error in transaction (${target?.source?.name}): ${target?.error}`, { cause: target?.error });
317
- };
318
- const coValues = tx.objectStore("coValues");
319
- const sessions = tx.objectStore("sessions");
320
- const transactions = tx.objectStore("transactions");
321
- const signatureAfter = tx.objectStore("signatureAfter");
322
- return { coValues, sessions, transactions, signatureAfter };
360
+ }
361
+ function collectNewTxs(newTxsInSession, newContentPieces, sessionRow, signaturesAndIdxs, theirKnown, firstNewTxIdx) {
362
+ let idx = firstNewTxIdx;
363
+ // console.log(
364
+ // theirKnown.id,
365
+ // "newTxInSession",
366
+ // newTxInSession.length
367
+ // );
368
+ for (const tx of newTxsInSession) {
369
+ let sessionEntry = newContentPieces[newContentPieces.length - 1].new[sessionRow.sessionID];
370
+ if (!sessionEntry) {
371
+ sessionEntry = {
372
+ after: idx,
373
+ lastSignature: "WILL_BE_REPLACED",
374
+ newTransactions: [],
375
+ };
376
+ newContentPieces[newContentPieces.length - 1].new[sessionRow.sessionID] = sessionEntry;
377
+ }
378
+ sessionEntry.newTransactions.push(tx.tx);
379
+ if (signaturesAndIdxs[0] && idx === signaturesAndIdxs[0].idx) {
380
+ sessionEntry.lastSignature = signaturesAndIdxs[0].signature;
381
+ signaturesAndIdxs.shift();
382
+ newContentPieces.push({
383
+ action: "content",
384
+ id: theirKnown.id,
385
+ new: {},
386
+ });
387
+ }
388
+ else if (idx === firstNewTxIdx + newTxsInSession.length - 1) {
389
+ sessionEntry.lastSignature = sessionRow.lastSignature;
390
+ }
391
+ idx += 1;
323
392
  }
324
393
  }
325
- function promised(request) {
326
- return new Promise((resolve, reject) => {
327
- request.onsuccess = () => {
328
- resolve(request.result);
329
- };
330
- request.onerror = () => {
331
- reject(request.error);
332
- };
333
- });
394
+ function getDependedOnCoValues(coValueRow, newContentPieces, theirKnown) {
395
+ return coValueRow?.header.ruleset.type === "group"
396
+ ? newContentPieces
397
+ .flatMap((piece) => Object.values(piece.new))
398
+ .flatMap((sessionEntry) => sessionEntry.newTransactions.flatMap((tx) => {
399
+ if (tx.privacy !== "trusting")
400
+ return [];
401
+ // TODO: avoid parse here?
402
+ return cojsonInternals
403
+ .parseJSON(tx.changes)
404
+ .map((change) => change &&
405
+ typeof change === "object" &&
406
+ "op" in change &&
407
+ change.op === "set" &&
408
+ "key" in change &&
409
+ change.key)
410
+ .filter((key) => typeof key === "string" &&
411
+ key.startsWith("co_"));
412
+ }))
413
+ : coValueRow?.header.ruleset.type === "ownedByGroup"
414
+ ? [
415
+ coValueRow?.header.ruleset.group,
416
+ ...new Set(newContentPieces.flatMap((piece) => Object.keys(piece)
417
+ .map((sessionID) => cojsonInternals.accountOrAgentIDfromSessionID(sessionID))
418
+ .filter((accountID) => cojsonInternals.isAccountID(accountID) &&
419
+ accountID !== theirKnown.id))),
420
+ ]
421
+ : [];
334
422
  }
423
+ // let lastTx = 0;
424
+ // function promised<T>(request: IDBRequest<T>): Promise<T> {
425
+ // return new Promise<T>((resolve, reject) => {
426
+ // request.onsuccess = () => {
427
+ // resolve(request.result);
428
+ // };
429
+ // request.onerror = () => {
430
+ // reject(request.error);
431
+ // };
432
+ // });
433
+ // }
335
434
  //# sourceMappingURL=index.js.map