cojson-storage-indexeddb 0.10.6 → 0.10.8

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/src/idbClient.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { CojsonInternalTypes, RawCoID } from "cojson";
1
+ import type { CojsonInternalTypes, RawCoID, SessionID } from "cojson";
2
2
  import type {
3
3
  CoValueRow,
4
4
  DBClientInterface,
@@ -8,119 +8,60 @@ import type {
8
8
  StoredSessionRow,
9
9
  TransactionRow,
10
10
  } from "cojson-storage";
11
- import { SyncPromise } from "./syncPromises.js";
11
+ import { CoJsonIDBTransaction } from "./CoJsonIDBTransaction.js";
12
12
 
13
13
  export class IDBClient implements DBClientInterface {
14
14
  private db;
15
15
 
16
- currentTx:
17
- | {
18
- id: number;
19
- tx: IDBTransaction;
20
- stores: {
21
- coValues: IDBObjectStore;
22
- sessions: IDBObjectStore;
23
- transactions: IDBObjectStore;
24
- signatureAfter: IDBObjectStore;
25
- };
26
- startedAt: number;
27
- pendingRequests: ((txEntry: {
28
- stores: {
29
- coValues: IDBObjectStore;
30
- sessions: IDBObjectStore;
31
- transactions: IDBObjectStore;
32
- signatureAfter: IDBObjectStore;
33
- };
34
- }) => void)[];
35
- }
36
- | undefined;
37
-
38
- currentTxID = 0;
16
+ activeTransaction: CoJsonIDBTransaction | undefined;
17
+ autoBatchingTransaction: CoJsonIDBTransaction | undefined;
39
18
 
40
19
  constructor(db: IDBDatabase) {
41
20
  this.db = db;
42
21
  }
43
22
 
44
23
  makeRequest<T>(
45
- handler: (stores: {
46
- coValues: IDBObjectStore;
47
- sessions: IDBObjectStore;
48
- transactions: IDBObjectStore;
49
- signatureAfter: IDBObjectStore;
50
- }) => IDBRequest,
51
- ): SyncPromise<T> {
52
- return new SyncPromise((resolve, reject) => {
53
- let txEntry = this.currentTx;
54
-
55
- const requestEntry = ({
56
- stores,
57
- }: {
58
- stores: {
59
- coValues: IDBObjectStore;
60
- sessions: IDBObjectStore;
61
- transactions: IDBObjectStore;
62
- signatureAfter: IDBObjectStore;
63
- };
64
- }) => {
65
- const request = handler(stores);
66
- request.onerror = () => {
67
- console.error("Error in request", request.error);
68
- this.currentTx = undefined;
69
- reject(request.error);
70
- };
71
- request.onsuccess = () => {
72
- const value = request.result as T;
73
- resolve(value);
74
-
75
- const next = txEntry?.pendingRequests.shift();
76
-
77
- if (next) {
78
- next({ stores });
79
- } else {
80
- if (this.currentTx === txEntry) {
81
- this.currentTx = undefined;
82
- }
83
- }
84
- };
85
- };
86
-
87
- // Transaction batching
88
- if (!txEntry || performance.now() - txEntry.startedAt > 20) {
89
- const tx = this.db.transaction(
90
- ["coValues", "sessions", "transactions", "signatureAfter"],
91
- "readwrite",
92
- );
93
- txEntry = {
94
- id: this.currentTxID++,
95
- tx,
96
- stores: {
97
- coValues: tx.objectStore("coValues"),
98
- sessions: tx.objectStore("sessions"),
99
- transactions: tx.objectStore("transactions"),
100
- signatureAfter: tx.objectStore("signatureAfter"),
101
- },
102
- startedAt: performance.now(),
103
- pendingRequests: [],
104
- };
105
-
106
- this.currentTx = txEntry;
107
-
108
- requestEntry(txEntry);
109
- } else {
110
- txEntry.pendingRequests.push(requestEntry);
111
- }
112
- });
24
+ handler: (txEntry: CoJsonIDBTransaction) => IDBRequest<T>,
25
+ ): Promise<T> {
26
+ if (this.activeTransaction) {
27
+ return this.activeTransaction.handleRequest<T>(handler);
28
+ }
29
+
30
+ if (this.autoBatchingTransaction?.isReusable()) {
31
+ return this.autoBatchingTransaction.handleRequest<T>(handler);
32
+ }
33
+
34
+ const tx = new CoJsonIDBTransaction(this.db);
35
+
36
+ this.autoBatchingTransaction = tx;
37
+
38
+ return tx.handleRequest<T>(handler);
113
39
  }
114
40
 
115
41
  async getCoValue(coValueId: RawCoID): Promise<StoredCoValueRow | undefined> {
116
- return this.makeRequest<StoredCoValueRow | undefined>(({ coValues }) =>
117
- coValues.index("coValuesById").get(coValueId),
42
+ return this.makeRequest<StoredCoValueRow | undefined>((tx) =>
43
+ tx.getObjectStore("coValues").index("coValuesById").get(coValueId),
118
44
  );
119
45
  }
120
46
 
121
47
  async getCoValueSessions(coValueRowId: number): Promise<StoredSessionRow[]> {
122
- return this.makeRequest<StoredSessionRow[]>(({ sessions }) =>
123
- sessions.index("sessionsByCoValue").getAll(coValueRowId),
48
+ return this.makeRequest<StoredSessionRow[]>((tx) =>
49
+ tx
50
+ .getObjectStore("sessions")
51
+ .index("sessionsByCoValue")
52
+ .getAll(coValueRowId),
53
+ );
54
+ }
55
+
56
+ async getSingleCoValueSession(
57
+ coValueRowId: number,
58
+ sessionID: SessionID,
59
+ ): Promise<StoredSessionRow | undefined> {
60
+ return this.makeRequest<StoredSessionRow>((tx) =>
61
+ tx
62
+ .getObjectStore("sessions")
63
+ .index("uniqueSessions")
64
+ .get([coValueRowId, sessionID]),
124
65
  );
125
66
  }
126
67
 
@@ -128,13 +69,15 @@ export class IDBClient implements DBClientInterface {
128
69
  sessionRowId: number,
129
70
  firstNewTxIdx: number,
130
71
  ): Promise<TransactionRow[]> {
131
- return this.makeRequest<TransactionRow[]>(({ transactions }) =>
132
- transactions.getAll(
133
- IDBKeyRange.bound(
134
- [sessionRowId, firstNewTxIdx],
135
- [sessionRowId, Number.POSITIVE_INFINITY],
72
+ return this.makeRequest<TransactionRow[]>((tx) =>
73
+ tx
74
+ .getObjectStore("transactions")
75
+ .getAll(
76
+ IDBKeyRange.bound(
77
+ [sessionRowId, firstNewTxIdx],
78
+ [sessionRowId, Number.POSITIVE_INFINITY],
79
+ ),
136
80
  ),
137
- ),
138
81
  );
139
82
  }
140
83
 
@@ -142,9 +85,10 @@ export class IDBClient implements DBClientInterface {
142
85
  sessionRowId: number,
143
86
  firstNewTxIdx: number,
144
87
  ): Promise<SignatureAfterRow[]> {
145
- return this.makeRequest<SignatureAfterRow[]>(
146
- ({ signatureAfter }: { signatureAfter: IDBObjectStore }) =>
147
- signatureAfter.getAll(
88
+ return this.makeRequest<SignatureAfterRow[]>((tx) =>
89
+ tx
90
+ .getObjectStore("signatureAfter")
91
+ .getAll(
148
92
  IDBKeyRange.bound(
149
93
  [sessionRowId, firstNewTxIdx],
150
94
  [sessionRowId, Number.POSITIVE_INFINITY],
@@ -160,8 +104,8 @@ export class IDBClient implements DBClientInterface {
160
104
  throw new Error(`Header is required, coId: ${msg.id}`);
161
105
  }
162
106
 
163
- return (await this.makeRequest<IDBValidKey>(({ coValues }) =>
164
- coValues.put({
107
+ return (await this.makeRequest<IDBValidKey>((tx) =>
108
+ tx.getObjectStore("coValues").put({
165
109
  id: msg.id,
166
110
  // biome-ignore lint/style/noNonNullAssertion: TODO(JAZZ-561): Review
167
111
  header: msg.header!,
@@ -176,25 +120,26 @@ export class IDBClient implements DBClientInterface {
176
120
  sessionUpdate: SessionRow;
177
121
  sessionRow?: StoredSessionRow;
178
122
  }): Promise<number> {
179
- return this.makeRequest<number>(({ sessions }) =>
180
- sessions.put(
181
- sessionRow?.rowID
182
- ? {
183
- rowID: sessionRow.rowID,
184
- ...sessionUpdate,
185
- }
186
- : sessionUpdate,
187
- ),
123
+ return this.makeRequest<number>(
124
+ (tx) =>
125
+ tx.getObjectStore("sessions").put(
126
+ sessionRow?.rowID
127
+ ? {
128
+ rowID: sessionRow.rowID,
129
+ ...sessionUpdate,
130
+ }
131
+ : sessionUpdate,
132
+ ) as IDBRequest<number>,
188
133
  );
189
134
  }
190
135
 
191
- addTransaction(
136
+ async addTransaction(
192
137
  sessionRowID: number,
193
138
  idx: number,
194
139
  newTransaction: CojsonInternalTypes.Transaction,
195
140
  ) {
196
- return this.makeRequest(({ transactions }) =>
197
- transactions.add({
141
+ await this.makeRequest((tx) =>
142
+ tx.getObjectStore("transactions").add({
198
143
  ses: sessionRowID,
199
144
  idx,
200
145
  tx: newTransaction,
@@ -211,8 +156,8 @@ export class IDBClient implements DBClientInterface {
211
156
  idx: number;
212
157
  signature: CojsonInternalTypes.Signature;
213
158
  }) {
214
- return this.makeRequest(({ signatureAfter }) =>
215
- signatureAfter.put({
159
+ return this.makeRequest((tx) =>
160
+ tx.getObjectStore("signatureAfter").put({
216
161
  ses: sessionRowID,
217
162
  idx,
218
163
  signature,
@@ -220,7 +165,24 @@ export class IDBClient implements DBClientInterface {
220
165
  );
221
166
  }
222
167
 
223
- async unitOfWork(operationsCallback: () => unknown[]) {
224
- return Promise.all(operationsCallback());
168
+ closeTransaction(tx: CoJsonIDBTransaction) {
169
+ tx.commit();
170
+
171
+ if (tx === this.activeTransaction) {
172
+ this.activeTransaction = undefined;
173
+ }
174
+ }
175
+
176
+ async transaction(operationsCallback: () => unknown) {
177
+ const tx = new CoJsonIDBTransaction(this.db);
178
+
179
+ this.activeTransaction = tx;
180
+
181
+ try {
182
+ await operationsCallback();
183
+ tx.commit(); // Tells the browser to not wait for another possible request and commit the transaction immediately
184
+ } finally {
185
+ this.activeTransaction = undefined;
186
+ }
225
187
  }
226
188
  }
package/src/idbNode.ts CHANGED
@@ -33,18 +33,7 @@ export class IDBNode {
33
33
  }
34
34
  await this.syncManager.handleSyncMessage(msg);
35
35
  } catch (e) {
36
- console.error(
37
- new Error(
38
- `Error reading from localNode, handling msg\n\n${JSON.stringify(
39
- msg,
40
- (k, v) =>
41
- k === "changes" || k === "encryptedChanges"
42
- ? `${v.slice(0, 20)}...`
43
- : v,
44
- )}`,
45
- { cause: e },
46
- ),
47
- );
36
+ console.error(e);
48
37
  }
49
38
  }
50
39
  };
@@ -0,0 +1,170 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "vitest";
2
+ import { CoJsonIDBTransaction } from "../CoJsonIDBTransaction";
3
+
4
+ const TEST_DB_NAME = "test-cojson-idb-transaction";
5
+
6
+ describe("CoJsonIDBTransaction", () => {
7
+ let db: IDBDatabase;
8
+
9
+ beforeEach(async () => {
10
+ // Create test database
11
+ await new Promise<void>((resolve, reject) => {
12
+ const request = indexedDB.open(TEST_DB_NAME, 1);
13
+
14
+ request.onerror = () => reject(request.error);
15
+
16
+ request.onupgradeneeded = (event) => {
17
+ const db = request.result;
18
+ // Create test stores
19
+ db.createObjectStore("coValues", { keyPath: "id" });
20
+ const sessions = db.createObjectStore("sessions", { keyPath: "id" });
21
+ sessions.createIndex("uniqueSessions", ["coValue", "sessionID"], {
22
+ unique: true,
23
+ });
24
+ db.createObjectStore("transactions", { keyPath: "id" });
25
+ db.createObjectStore("signatureAfter", { keyPath: "id" });
26
+ };
27
+
28
+ request.onsuccess = () => {
29
+ db = request.result;
30
+ resolve();
31
+ };
32
+ });
33
+ });
34
+
35
+ afterEach(async () => {
36
+ // Close and delete test database
37
+ db.close();
38
+ await new Promise<void>((resolve, reject) => {
39
+ const request = indexedDB.deleteDatabase(TEST_DB_NAME);
40
+ request.onerror = () => reject(request.error);
41
+ request.onsuccess = () => resolve();
42
+ });
43
+ });
44
+
45
+ test("handles successful write and read operations", async () => {
46
+ const tx = new CoJsonIDBTransaction(db);
47
+
48
+ // Write test
49
+ await tx.handleRequest((tx) =>
50
+ tx.getObjectStore("coValues").put({
51
+ id: "test1",
52
+ value: "hello",
53
+ }),
54
+ );
55
+
56
+ // Read test
57
+ const readTx = new CoJsonIDBTransaction(db);
58
+ const result = await readTx.handleRequest((tx) =>
59
+ tx.getObjectStore("coValues").get("test1"),
60
+ );
61
+
62
+ expect(result).toEqual({
63
+ id: "test1",
64
+ value: "hello",
65
+ });
66
+ });
67
+
68
+ test("handles multiple operations in single transaction", async () => {
69
+ const tx = new CoJsonIDBTransaction(db);
70
+
71
+ // Multiple writes
72
+ await Promise.all([
73
+ tx.handleRequest((tx) =>
74
+ tx.getObjectStore("coValues").put({
75
+ id: "test1",
76
+ value: "hello",
77
+ }),
78
+ ),
79
+ tx.handleRequest((tx) =>
80
+ tx.getObjectStore("coValues").put({
81
+ id: "test2",
82
+ value: "world",
83
+ }),
84
+ ),
85
+ ]);
86
+
87
+ // Read results
88
+ const readTx = new CoJsonIDBTransaction(db);
89
+ const [result1, result2] = await Promise.all([
90
+ readTx.handleRequest((tx) => tx.getObjectStore("coValues").get("test1")),
91
+ readTx.handleRequest((tx) => tx.getObjectStore("coValues").get("test2")),
92
+ ]);
93
+
94
+ expect(result1).toEqual({
95
+ id: "test1",
96
+ value: "hello",
97
+ });
98
+ expect(result2).toEqual({
99
+ id: "test2",
100
+ value: "world",
101
+ });
102
+ });
103
+
104
+ test("handles transaction across multiple stores", async () => {
105
+ const tx = new CoJsonIDBTransaction(db);
106
+
107
+ await Promise.all([
108
+ tx.handleRequest((tx) =>
109
+ tx.getObjectStore("coValues").put({
110
+ id: "value1",
111
+ data: "value data",
112
+ }),
113
+ ),
114
+ tx.handleRequest((tx) =>
115
+ tx.getObjectStore("sessions").put({
116
+ id: "session1",
117
+ data: "session data",
118
+ }),
119
+ ),
120
+ ]);
121
+
122
+ const readTx = new CoJsonIDBTransaction(db);
123
+ const [valueResult, sessionResult] = await Promise.all([
124
+ readTx.handleRequest((tx) => tx.getObjectStore("coValues").get("value1")),
125
+ readTx.handleRequest((tx) =>
126
+ tx.getObjectStore("sessions").get("session1"),
127
+ ),
128
+ ]);
129
+
130
+ expect(valueResult).toEqual({
131
+ id: "value1",
132
+ data: "value data",
133
+ });
134
+ expect(sessionResult).toEqual({
135
+ id: "session1",
136
+ data: "session data",
137
+ });
138
+ });
139
+
140
+ test("handles failed transactions", async () => {
141
+ const tx = new CoJsonIDBTransaction(db);
142
+
143
+ await expect(
144
+ tx.handleRequest((tx) =>
145
+ tx.getObjectStore("sessions").put({
146
+ id: 1,
147
+ coValue: "value1",
148
+ sessionID: "session1",
149
+ data: "session data",
150
+ }),
151
+ ),
152
+ ).resolves.toBe(1);
153
+
154
+ expect(tx.failed).toBe(false);
155
+
156
+ const badTx = new CoJsonIDBTransaction(db);
157
+ await expect(
158
+ badTx.handleRequest((tx) =>
159
+ tx.getObjectStore("sessions").put({
160
+ id: 2,
161
+ coValue: "value1",
162
+ sessionID: "session1",
163
+ data: "session data",
164
+ }),
165
+ ),
166
+ ).rejects.toThrow();
167
+
168
+ expect(badTx.failed).toBe(true);
169
+ });
170
+ });
@@ -1,163 +0,0 @@
1
- const isFunction = (func) => typeof func === "function";
2
- const isObject = (supposedObject) => typeof supposedObject === "object" &&
3
- supposedObject !== null &&
4
- !Array.isArray(supposedObject);
5
- const isThenable = (obj) => isObject(obj) && isFunction(obj.then);
6
- const identity = (co) => co;
7
- export { identity, isFunction, isObject, isThenable };
8
- var States;
9
- (function (States) {
10
- States["PENDING"] = "PENDING";
11
- States["RESOLVED"] = "RESOLVED";
12
- States["REJECTED"] = "REJECTED";
13
- })(States || (States = {}));
14
- export class SyncPromise {
15
- constructor(callback) {
16
- this.state = States.PENDING;
17
- this.handlers = [];
18
- this.resolve = (value) => {
19
- return this.setResult(value, States.RESOLVED);
20
- };
21
- this.reject = (reason) => {
22
- return this.setResult(reason, States.REJECTED);
23
- };
24
- this.setResult = (value, state) => {
25
- const set = () => {
26
- if (this.state !== States.PENDING) {
27
- return null;
28
- }
29
- if (isThenable(value)) {
30
- return value.then(this.resolve, this.reject);
31
- }
32
- this.value = value;
33
- this.state = state;
34
- return this.executeHandlers();
35
- };
36
- void set();
37
- };
38
- this.executeHandlers = () => {
39
- if (this.state === States.PENDING) {
40
- return null;
41
- }
42
- for (const handler of this.handlers) {
43
- if (this.state === States.REJECTED) {
44
- handler.onFail(this.value);
45
- }
46
- else {
47
- handler.onSuccess(this.value);
48
- }
49
- }
50
- this.handlers = [];
51
- };
52
- this.attachHandler = (handler) => {
53
- this.handlers = [...this.handlers, handler];
54
- this.executeHandlers();
55
- };
56
- try {
57
- callback(this.resolve, this.reject);
58
- }
59
- catch (e) {
60
- this.reject(e);
61
- }
62
- }
63
- // biome-ignore lint/suspicious/noThenProperty: TODO(JAZZ-561): Review
64
- then(onSuccess, onFail) {
65
- return new SyncPromise((resolve, reject) => {
66
- return this.attachHandler({
67
- onSuccess: (result) => {
68
- try {
69
- return resolve(onSuccess(result));
70
- }
71
- catch (e) {
72
- return reject(e);
73
- }
74
- },
75
- onFail: (reason) => {
76
- if (!onFail) {
77
- return reject(reason);
78
- }
79
- try {
80
- return resolve(onFail(reason));
81
- }
82
- catch (e) {
83
- return reject(e);
84
- }
85
- },
86
- });
87
- });
88
- }
89
- catch(onFail) {
90
- return this.then(identity, onFail);
91
- }
92
- // methods
93
- toString() {
94
- return "[object SyncPromise]";
95
- }
96
- finally(cb) {
97
- return new SyncPromise((resolve, reject) => {
98
- let co;
99
- let isRejected;
100
- return this.then((value) => {
101
- isRejected = false;
102
- co = value;
103
- return cb();
104
- }, (reason) => {
105
- isRejected = true;
106
- co = reason;
107
- return cb();
108
- }).then(() => {
109
- if (isRejected) {
110
- return reject(co);
111
- }
112
- return resolve(co);
113
- });
114
- });
115
- }
116
- spread(handler) {
117
- return this.then((collection) => {
118
- if (Array.isArray(collection)) {
119
- return handler(...collection);
120
- }
121
- return handler(collection);
122
- });
123
- }
124
- // static
125
- static resolve(value) {
126
- return new SyncPromise((resolve) => {
127
- return resolve(value);
128
- });
129
- }
130
- static reject(reason) {
131
- return new SyncPromise((_resolve, reject) => {
132
- return reject(reason);
133
- });
134
- }
135
- static all(collection) {
136
- return new SyncPromise((resolve, reject) => {
137
- if (!Array.isArray(collection)) {
138
- return reject(new TypeError("An array must be provided."));
139
- }
140
- if (collection.length === 0) {
141
- return resolve([]);
142
- }
143
- let counter = collection.length;
144
- const resolvedCollection = [];
145
- const tryResolve = (value, index) => {
146
- counter -= 1;
147
- resolvedCollection[index] = value;
148
- if (counter !== 0) {
149
- return null;
150
- }
151
- return resolve(resolvedCollection);
152
- };
153
- return collection.forEach((item, index) => {
154
- return SyncPromise.resolve(item)
155
- .then((value) => {
156
- return tryResolve(value, index);
157
- })
158
- .catch(reject);
159
- });
160
- });
161
- }
162
- }
163
- //# sourceMappingURL=syncPromises.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"syncPromises.js","sourceRoot":"","sources":["../src/syncPromises.ts"],"names":[],"mappings":"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"}