cojson-storage-indexeddb 0.8.12 → 0.8.16
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 +157 -150
- package/dist/index.js +15 -38
- package/dist/index.js.map +1 -1
- package/dist/syncPromises.js.map +1 -1
- package/dist/tests/index.test.js +1 -1
- package/dist/tests/index.test.js.map +1 -1
- package/package.json +5 -9
- package/src/index.ts +663 -721
- package/src/syncPromises.ts +181 -186
- package/src/tests/index.test.ts +39 -39
- package/tsconfig.json +2 -2
- package/.eslintrc.cjs +0 -24
- package/.prettierrc.js +0 -9
package/src/syncPromises.ts
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
const isFunction = (func: any) => typeof func === "function";
|
|
3
3
|
|
|
4
4
|
const isObject = (supposedObject: any) =>
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
typeof supposedObject === "object" &&
|
|
6
|
+
supposedObject !== null &&
|
|
7
|
+
!Array.isArray(supposedObject);
|
|
8
8
|
|
|
9
9
|
const isThenable = (obj: any) => isObject(obj) && isFunction(obj.then);
|
|
10
10
|
|
|
@@ -13,14 +13,14 @@ const identity = (co: any) => co;
|
|
|
13
13
|
export { isObject, isThenable, identity, isFunction };
|
|
14
14
|
|
|
15
15
|
enum States {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
PENDING = "PENDING",
|
|
17
|
+
RESOLVED = "RESOLVED",
|
|
18
|
+
REJECTED = "REJECTED",
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
interface Handler<T, U> {
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
onSuccess: HandlerOnSuccess<T, U>;
|
|
23
|
+
onFail: HandlerOnFail<U>;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
type HandlerOnSuccess<T, U = any> = (value: T) => U | Thenable<U>;
|
|
@@ -28,202 +28,197 @@ type HandlerOnFail<U = any> = (reason: any) => U | Thenable<U>;
|
|
|
28
28
|
type Finally<U> = () => U | Thenable<U>;
|
|
29
29
|
|
|
30
30
|
interface Thenable<T> {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
then<U>(
|
|
32
|
+
onSuccess?: HandlerOnSuccess<T, U>,
|
|
33
|
+
onFail?: HandlerOnFail<U>,
|
|
34
|
+
): Thenable<U>;
|
|
35
|
+
then<U>(
|
|
36
|
+
onSuccess?: HandlerOnSuccess<T, U>,
|
|
37
|
+
onFail?: (reason: any) => void,
|
|
38
|
+
): Thenable<U>;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
type Resolve<R> = (value?: R | Thenable<R>) => void;
|
|
42
42
|
type Reject = (value?: any) => void;
|
|
43
43
|
|
|
44
44
|
export class SyncPromise<T> {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
} catch (e) {
|
|
55
|
-
this.reject(e);
|
|
56
|
-
}
|
|
45
|
+
private state: States = States.PENDING;
|
|
46
|
+
private handlers: Handler<T, any>[] = [];
|
|
47
|
+
private value: T | any;
|
|
48
|
+
|
|
49
|
+
public constructor(callback: (resolve: Resolve<T>, reject: Reject) => void) {
|
|
50
|
+
try {
|
|
51
|
+
callback(this.resolve as Resolve<T>, this.reject);
|
|
52
|
+
} catch (e) {
|
|
53
|
+
this.reject(e);
|
|
57
54
|
}
|
|
55
|
+
}
|
|
58
56
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
private reject = (reason: any) => {
|
|
64
|
-
return this.setResult(reason, States.REJECTED);
|
|
65
|
-
};
|
|
57
|
+
private resolve = (value: T) => {
|
|
58
|
+
return this.setResult(value, States.RESOLVED);
|
|
59
|
+
};
|
|
66
60
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
61
|
+
private reject = (reason: any) => {
|
|
62
|
+
return this.setResult(reason, States.REJECTED);
|
|
63
|
+
};
|
|
72
64
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
65
|
+
private setResult = (value: T | any, state: States) => {
|
|
66
|
+
const set = () => {
|
|
67
|
+
if (this.state !== States.PENDING) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
76
70
|
|
|
77
|
-
|
|
78
|
-
|
|
71
|
+
if (isThenable(value)) {
|
|
72
|
+
return (value as Thenable<T>).then(this.resolve, this.reject);
|
|
73
|
+
}
|
|
79
74
|
|
|
80
|
-
|
|
81
|
-
|
|
75
|
+
this.value = value;
|
|
76
|
+
this.state = state;
|
|
82
77
|
|
|
83
|
-
|
|
78
|
+
return this.executeHandlers();
|
|
84
79
|
};
|
|
85
80
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
this.handlers.forEach((handler) => {
|
|
92
|
-
if (this.state === States.REJECTED) {
|
|
93
|
-
return handler.onFail(this.value);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return handler.onSuccess(this.value);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
this.handlers = [];
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
private attachHandler = (handler: Handler<T, any>) => {
|
|
103
|
-
this.handlers = [...this.handlers, handler];
|
|
104
|
-
|
|
105
|
-
this.executeHandlers();
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
public then<U>(
|
|
109
|
-
onSuccess: HandlerOnSuccess<T, U>,
|
|
110
|
-
onFail?: HandlerOnFail<U>,
|
|
111
|
-
) {
|
|
112
|
-
return new SyncPromise<U>((resolve, reject) => {
|
|
113
|
-
return this.attachHandler({
|
|
114
|
-
onSuccess: (result) => {
|
|
115
|
-
try {
|
|
116
|
-
return resolve(onSuccess(result));
|
|
117
|
-
} catch (e) {
|
|
118
|
-
return reject(e);
|
|
119
|
-
}
|
|
120
|
-
},
|
|
121
|
-
onFail: (reason) => {
|
|
122
|
-
if (!onFail) {
|
|
123
|
-
return reject(reason);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
try {
|
|
127
|
-
return resolve(onFail(reason));
|
|
128
|
-
} catch (e) {
|
|
129
|
-
return reject(e);
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
public catch<U>(onFail: HandlerOnFail<U>) {
|
|
137
|
-
return this.then<U>(identity, onFail);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// methods
|
|
141
|
-
|
|
142
|
-
public toString() {
|
|
143
|
-
return `[object SyncPromise]`;
|
|
144
|
-
}
|
|
81
|
+
void set();
|
|
82
|
+
};
|
|
145
83
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
let isRejected: boolean;
|
|
150
|
-
|
|
151
|
-
return this.then(
|
|
152
|
-
(value) => {
|
|
153
|
-
isRejected = false;
|
|
154
|
-
co = value;
|
|
155
|
-
return cb();
|
|
156
|
-
},
|
|
157
|
-
(reason) => {
|
|
158
|
-
isRejected = true;
|
|
159
|
-
co = reason;
|
|
160
|
-
return cb();
|
|
161
|
-
},
|
|
162
|
-
).then(() => {
|
|
163
|
-
if (isRejected) {
|
|
164
|
-
return reject(co);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return resolve(co);
|
|
168
|
-
});
|
|
169
|
-
});
|
|
84
|
+
private executeHandlers = () => {
|
|
85
|
+
if (this.state === States.PENDING) {
|
|
86
|
+
return null;
|
|
170
87
|
}
|
|
171
88
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
89
|
+
this.handlers.forEach((handler) => {
|
|
90
|
+
if (this.state === States.REJECTED) {
|
|
91
|
+
return handler.onFail(this.value);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return handler.onSuccess(this.value);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
this.handlers = [];
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
private attachHandler = (handler: Handler<T, any>) => {
|
|
101
|
+
this.handlers = [...this.handlers, handler];
|
|
102
|
+
|
|
103
|
+
this.executeHandlers();
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
public then<U>(onSuccess: HandlerOnSuccess<T, U>, onFail?: HandlerOnFail<U>) {
|
|
107
|
+
return new SyncPromise<U>((resolve, reject) => {
|
|
108
|
+
return this.attachHandler({
|
|
109
|
+
onSuccess: (result) => {
|
|
110
|
+
try {
|
|
111
|
+
return resolve(onSuccess(result));
|
|
112
|
+
} catch (e) {
|
|
113
|
+
return reject(e);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
onFail: (reason) => {
|
|
117
|
+
if (!onFail) {
|
|
192
118
|
return reject(reason);
|
|
193
|
-
|
|
194
|
-
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
return resolve(onFail(reason));
|
|
123
|
+
} catch (e) {
|
|
124
|
+
return reject(e);
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
public catch<U>(onFail: HandlerOnFail<U>) {
|
|
132
|
+
return this.then<U>(identity, onFail);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// methods
|
|
136
|
+
|
|
137
|
+
public toString() {
|
|
138
|
+
return `[object SyncPromise]`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public finally<U>(cb: Finally<U>) {
|
|
142
|
+
return new SyncPromise<U>((resolve, reject) => {
|
|
143
|
+
let co: U | any;
|
|
144
|
+
let isRejected: boolean;
|
|
145
|
+
|
|
146
|
+
return this.then(
|
|
147
|
+
(value) => {
|
|
148
|
+
isRejected = false;
|
|
149
|
+
co = value;
|
|
150
|
+
return cb();
|
|
151
|
+
},
|
|
152
|
+
(reason) => {
|
|
153
|
+
isRejected = true;
|
|
154
|
+
co = reason;
|
|
155
|
+
return cb();
|
|
156
|
+
},
|
|
157
|
+
).then(() => {
|
|
158
|
+
if (isRejected) {
|
|
159
|
+
return reject(co);
|
|
160
|
+
}
|
|
195
161
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
162
|
+
return resolve(co);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
public spread<U>(handler: (...args: any[]) => U) {
|
|
168
|
+
return this.then<U>((collection) => {
|
|
169
|
+
if (Array.isArray(collection)) {
|
|
170
|
+
return handler(...collection);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return handler(collection);
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// static
|
|
178
|
+
|
|
179
|
+
public static resolve<U = any>(value?: U | Thenable<U>) {
|
|
180
|
+
return new SyncPromise<U>((resolve) => {
|
|
181
|
+
return resolve(value);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
public static reject<U>(reason?: any) {
|
|
186
|
+
return new SyncPromise<U>((_resolve, reject) => {
|
|
187
|
+
return reject(reason);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
public static all<U = any>(collection: (U | Thenable<U>)[]) {
|
|
192
|
+
return new SyncPromise<U[]>((resolve, reject) => {
|
|
193
|
+
if (!Array.isArray(collection)) {
|
|
194
|
+
return reject(new TypeError("An array must be provided."));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (collection.length === 0) {
|
|
198
|
+
return resolve([]);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
let counter = collection.length;
|
|
202
|
+
const resolvedCollection: U[] = [];
|
|
203
|
+
|
|
204
|
+
const tryResolve = (value: U, index: number) => {
|
|
205
|
+
counter -= 1;
|
|
206
|
+
resolvedCollection[index] = value;
|
|
207
|
+
|
|
208
|
+
if (counter !== 0) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return resolve(resolvedCollection);
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
return collection.forEach((item, index) => {
|
|
216
|
+
return SyncPromise.resolve(item)
|
|
217
|
+
.then((value) => {
|
|
218
|
+
return tryResolve(value, index);
|
|
219
|
+
})
|
|
220
|
+
.catch(reject);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
}
|
|
229
224
|
}
|
package/src/tests/index.test.ts
CHANGED
|
@@ -1,68 +1,68 @@
|
|
|
1
1
|
import "fake-indexeddb/auto"; // Polyfill for IndexedDB
|
|
2
2
|
|
|
3
|
-
import { expect, test } from "vitest";
|
|
4
3
|
import { ControlledAgent, LocalNode, WasmCrypto } from "cojson";
|
|
4
|
+
import { expect, test } from "vitest";
|
|
5
5
|
import { IDBStorage } from "../index.js";
|
|
6
6
|
|
|
7
7
|
const Crypto = await WasmCrypto.create();
|
|
8
8
|
|
|
9
9
|
test.skip("Should be able to initialize and load from empty DB", async () => {
|
|
10
|
-
|
|
10
|
+
const agentSecret = Crypto.newRandomAgentSecret();
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
const node = new LocalNode(
|
|
13
|
+
new ControlledAgent(agentSecret, Crypto),
|
|
14
|
+
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
|
15
|
+
Crypto,
|
|
16
|
+
);
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
node.syncManager.addPeer(await IDBStorage.asPeer({ trace: true }));
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
console.log("yay!");
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
const _group = node.createGroup();
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
expect(node.syncManager.peers["storage"]).toBeDefined();
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
test("Should be able to sync data to database and then load that from a new node", async () => {
|
|
30
|
-
|
|
30
|
+
const agentSecret = Crypto.newRandomAgentSecret();
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
const node1 = new LocalNode(
|
|
33
|
+
new ControlledAgent(agentSecret, Crypto),
|
|
34
|
+
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
|
35
|
+
Crypto,
|
|
36
|
+
);
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
node1.syncManager.addPeer(
|
|
39
|
+
await IDBStorage.asPeer({ trace: true, localNodeName: "node1" }),
|
|
40
|
+
);
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
console.log("yay!");
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
const group = node1.createGroup();
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
const map = group.createMap();
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
map.set("hello", "world");
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
const node2 = new LocalNode(
|
|
53
|
+
new ControlledAgent(agentSecret, Crypto),
|
|
54
|
+
Crypto.newRandomSessionID(Crypto.getAgentID(agentSecret)),
|
|
55
|
+
Crypto,
|
|
56
|
+
);
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
node2.syncManager.addPeer(
|
|
59
|
+
await IDBStorage.asPeer({ trace: true, localNodeName: "node2" }),
|
|
60
|
+
);
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
const map2 = await node2.load(map.id);
|
|
63
|
+
if (map2 === "unavailable") {
|
|
64
|
+
throw new Error("Map is unavailable");
|
|
65
|
+
}
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
expect(map2.get("hello")).toBe("world");
|
|
68
68
|
});
|
package/tsconfig.json
CHANGED
package/.eslintrc.cjs
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
extends: [
|
|
3
|
-
"eslint:recommended",
|
|
4
|
-
"plugin:@typescript-eslint/recommended",
|
|
5
|
-
"plugin:require-extensions/recommended",
|
|
6
|
-
"prettier"
|
|
7
|
-
],
|
|
8
|
-
parser: "@typescript-eslint/parser",
|
|
9
|
-
plugins: ["@typescript-eslint", "require-extensions"],
|
|
10
|
-
parserOptions: {
|
|
11
|
-
project: "./tsconfig.json",
|
|
12
|
-
tsconfigRootDir: __dirname,
|
|
13
|
-
},
|
|
14
|
-
ignorePatterns: [".eslint.cjs"],
|
|
15
|
-
root: true,
|
|
16
|
-
rules: {
|
|
17
|
-
"no-unused-vars": "off",
|
|
18
|
-
"@typescript-eslint/no-unused-vars": [
|
|
19
|
-
"error",
|
|
20
|
-
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
|
|
21
|
-
],
|
|
22
|
-
"@typescript-eslint/no-floating-promises": "error",
|
|
23
|
-
},
|
|
24
|
-
}
|