@peerbit/react 0.0.9 → 0.0.11
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/lib/esm/__tests__/lockstorage.test.js +39 -35
- package/lib/esm/__tests__/lockstorage.test.js.map +1 -1
- package/lib/esm/__tests__/utils.test.js +21 -17
- package/lib/esm/__tests__/utils.test.js.map +1 -1
- package/lib/esm/index.d.ts +1 -0
- package/lib/esm/index.js +1 -0
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/lockstorage.d.ts +2 -2
- package/lib/esm/lockstorage.js.map +1 -1
- package/lib/esm/useLocal.d.ts +2 -0
- package/lib/esm/useLocal.js +30 -0
- package/lib/esm/useLocal.js.map +1 -0
- package/lib/esm/usePeer.js +24 -22
- package/lib/esm/usePeer.js.map +1 -1
- package/lib/esm/useProgram.d.ts +1 -1
- package/lib/esm/useProgram.js +11 -9
- package/lib/esm/useProgram.js.map +1 -1
- package/lib/esm/utils.d.ts +6 -2
- package/lib/esm/utils.js +16 -4
- package/lib/esm/utils.js.map +1 -1
- package/package.json +10 -9
- package/src/__tests__/lockstorage.test.ts +47 -38
- package/src/__tests__/utils.test.ts +23 -17
- package/src/index.ts +1 -0
- package/src/lockstorage.ts +1 -1
- package/src/useLocal.tsx +35 -0
- package/src/usePeer.tsx +31 -21
- package/src/useProgram.tsx +20 -11
- package/src/utils.ts +31 -4
package/lib/esm/utils.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { Ed25519Keypair } from "@peerbit/crypto";
|
|
2
2
|
import { FastMutex } from "./lockstorage";
|
|
3
|
-
export declare const
|
|
3
|
+
export declare const cookiesWhereClearedJustNow: () => boolean;
|
|
4
|
+
export declare const getTabId: () => string;
|
|
4
5
|
export declare const releaseKey: (path: string, lock?: FastMutex) => void;
|
|
5
|
-
export declare const getFreeKeypair: (id?: string, lock?: FastMutex, lockCondition?: () => boolean,
|
|
6
|
+
export declare const getFreeKeypair: (id?: string, lock?: FastMutex, lockCondition?: () => boolean, options?: {
|
|
7
|
+
releaseLockIfSameId?: boolean;
|
|
8
|
+
releaseFirstLock?: boolean;
|
|
9
|
+
}) => Promise<{
|
|
6
10
|
path: string;
|
|
7
11
|
key: Ed25519Keypair;
|
|
8
12
|
}>;
|
package/lib/esm/utils.js
CHANGED
|
@@ -3,14 +3,23 @@ import { Ed25519Keypair, toBase64, fromBase64 } from "@peerbit/crypto";
|
|
|
3
3
|
import { FastMutex } from "./lockstorage";
|
|
4
4
|
import { v4 as uuid } from "uuid";
|
|
5
5
|
import sodium from "libsodium-wrappers";
|
|
6
|
+
const TAB_ID_KEY = "TAB_ID";
|
|
7
|
+
export const cookiesWhereClearedJustNow = () => {
|
|
8
|
+
const lastPersistedAt = localStorage.getItem("lastPersistedAt");
|
|
9
|
+
if (lastPersistedAt) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
localStorage.setItem("lastPersistedAt", Date.now().toString());
|
|
13
|
+
return true;
|
|
14
|
+
};
|
|
6
15
|
export const getTabId = () => {
|
|
7
|
-
const idFromStorage = sessionStorage.getItem(
|
|
16
|
+
const idFromStorage = sessionStorage.getItem(TAB_ID_KEY);
|
|
8
17
|
if (idFromStorage) {
|
|
9
18
|
return idFromStorage;
|
|
10
19
|
}
|
|
11
20
|
else {
|
|
12
21
|
const id = uuid(); // generate unique UUID
|
|
13
|
-
sessionStorage.setItem(
|
|
22
|
+
sessionStorage.setItem(TAB_ID_KEY, id);
|
|
14
23
|
return id;
|
|
15
24
|
}
|
|
16
25
|
};
|
|
@@ -19,7 +28,7 @@ const getKeyId = (prefix, id) => prefix + "/" + id;
|
|
|
19
28
|
export const releaseKey = (path, lock = new FastMutex({ clientId: getTabId() })) => {
|
|
20
29
|
lock.release(path);
|
|
21
30
|
};
|
|
22
|
-
export const getFreeKeypair = async (id = "", lock = new FastMutex({ clientId: getTabId() }), lockCondition = () => true,
|
|
31
|
+
export const getFreeKeypair = async (id = "", lock = new FastMutex({ clientId: getTabId() }), lockCondition = () => true, options) => {
|
|
23
32
|
await sodium.ready;
|
|
24
33
|
const idCounterKey = ID_COUNTER_KEY + id;
|
|
25
34
|
await lock.lock(idCounterKey, () => true);
|
|
@@ -27,8 +36,11 @@ export const getFreeKeypair = async (id = "", lock = new FastMutex({ clientId: g
|
|
|
27
36
|
for (let i = 0; i < 10000; i++) {
|
|
28
37
|
const key = getKeyId(id, i);
|
|
29
38
|
let lockedInfo = lock.getLockedInfo(key);
|
|
39
|
+
console.log("KEY KEY AT", key, id, i, lockedInfo, lockedInfo === lock.clientId, options);
|
|
30
40
|
if (lockedInfo) {
|
|
31
|
-
if (lockedInfo === lock.clientId &&
|
|
41
|
+
if ((lockedInfo === lock.clientId &&
|
|
42
|
+
options?.releaseLockIfSameId) ||
|
|
43
|
+
options?.releaseFirstLock) {
|
|
32
44
|
await lock.release(key); // Release lock
|
|
33
45
|
}
|
|
34
46
|
else {
|
package/lib/esm/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,EAAE,IAAI,IAAI,EAAE,MAAM,MAAM,CAAC;AAClC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AAExC,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAG,EAAE;IACzB,MAAM,aAAa,GAAG,cAAc,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peerbit/react",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"homepage": "https://dao-xyz.github.io/peerbit-examples",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"module": "lib/esm/index.js",
|
|
6
7
|
"types": "lib/esm/index.d.ts",
|
|
7
8
|
"exports": {
|
|
@@ -22,9 +23,9 @@
|
|
|
22
23
|
"react": "*"
|
|
23
24
|
},
|
|
24
25
|
"dependencies": {
|
|
25
|
-
"@chainsafe/libp2p-yamux": "^
|
|
26
|
-
"@libp2p/webrtc": "^
|
|
27
|
-
"@peerbit/document": "^
|
|
26
|
+
"@chainsafe/libp2p-yamux": "^7",
|
|
27
|
+
"@libp2p/webrtc": "^5",
|
|
28
|
+
"@peerbit/document": "^9",
|
|
28
29
|
"@peerbit/proxy-window": "^3",
|
|
29
30
|
"@types/react": "^18.2.46",
|
|
30
31
|
"@types/react-dom": "^18.2.18",
|
|
@@ -33,13 +34,14 @@
|
|
|
33
34
|
"peerbit": "^4",
|
|
34
35
|
"react": "^18.2.0",
|
|
35
36
|
"react-dom": "^18.2.0",
|
|
36
|
-
"react-router-dom": "^6.
|
|
37
|
+
"react-router-dom": "^6.27.0",
|
|
37
38
|
"react-use": "^17.4.0"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
41
|
"@babel/plugin-proposal-private-property-in-object": "^7.18.6",
|
|
41
42
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
|
42
43
|
"@babel/plugin-transform-typescript": "^7.20.2",
|
|
44
|
+
"@types/node-localstorage": "^1",
|
|
43
45
|
"@types/sinon": "^10.0.13",
|
|
44
46
|
"node-localstorage": "^2.2.1",
|
|
45
47
|
"sinon": "^15.0.1"
|
|
@@ -47,12 +49,11 @@
|
|
|
47
49
|
"scripts": {
|
|
48
50
|
"clean": "shx rm -rf lib/*",
|
|
49
51
|
"build": "yarn clean && tsc -p tsconfig.json",
|
|
50
|
-
"test": "
|
|
52
|
+
"test": "mocha"
|
|
51
53
|
},
|
|
52
54
|
"eslintConfig": {
|
|
53
55
|
"extends": [
|
|
54
|
-
"react-app"
|
|
55
|
-
"react-app/jest"
|
|
56
|
+
"react-app"
|
|
56
57
|
]
|
|
57
58
|
},
|
|
58
59
|
"browserslist": {
|
|
@@ -69,5 +70,5 @@
|
|
|
69
70
|
"last 1 safari version"
|
|
70
71
|
]
|
|
71
72
|
},
|
|
72
|
-
"gitHead": "
|
|
73
|
+
"gitHead": "55ae175bfbdd39bbdef5f8d1ea6ad73580a208f3"
|
|
73
74
|
}
|
|
@@ -17,15 +17,24 @@ THIS SOFTWARE.
|
|
|
17
17
|
|
|
18
18
|
import { FastMutex } from "../lockstorage.js";
|
|
19
19
|
import sinon from "sinon";
|
|
20
|
-
|
|
21
20
|
import nodelocalstorage from "node-localstorage";
|
|
22
|
-
import {
|
|
21
|
+
import { expect } from "chai";
|
|
23
22
|
import { delay } from "@peerbit/time";
|
|
24
|
-
|
|
25
|
-
var localStorage = new LocalStorage("./tmp/FastMutex");
|
|
26
|
-
globalThis.localStorage = localStorage;
|
|
23
|
+
|
|
27
24
|
describe("FastMutex", () => {
|
|
28
25
|
let sandbox;
|
|
26
|
+
|
|
27
|
+
before(() => {
|
|
28
|
+
var LocalStorage = nodelocalstorage!.LocalStorage;
|
|
29
|
+
var localStorage = new LocalStorage("./tmp/FastMutex");
|
|
30
|
+
localStorage.clear();
|
|
31
|
+
globalThis.localStorage = localStorage;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
after(() => {
|
|
35
|
+
globalThis.localStorage.clear();
|
|
36
|
+
});
|
|
37
|
+
|
|
29
38
|
beforeEach(() => {
|
|
30
39
|
sandbox = sinon.createSandbox();
|
|
31
40
|
localStorage.clear();
|
|
@@ -33,15 +42,15 @@ describe("FastMutex", () => {
|
|
|
33
42
|
afterEach(() => {
|
|
34
43
|
sandbox.restore();
|
|
35
44
|
localStorage.clear();
|
|
36
|
-
expect(localStorage.length).
|
|
45
|
+
expect(localStorage.length).to.eq(0);
|
|
37
46
|
});
|
|
38
47
|
|
|
39
48
|
it("should immediately establish a lock when there is no contention", async () => {
|
|
40
49
|
const fm1 = new FastMutex({ localStorage: localStorage });
|
|
41
50
|
|
|
42
|
-
expect(fm1.isLocked("clientId")).
|
|
51
|
+
expect(fm1.isLocked("clientId")).to.be.false;
|
|
43
52
|
const stats = await fm1.lock("clientId");
|
|
44
|
-
expect(fm1.isLocked("clientId")).
|
|
53
|
+
expect(fm1.isLocked("clientId")).to.be.true;
|
|
45
54
|
});
|
|
46
55
|
|
|
47
56
|
it("When another client has a lock (Y is not 0), it should restart to acquire a lock at a later time", function () {
|
|
@@ -59,7 +68,7 @@ describe("FastMutex", () => {
|
|
|
59
68
|
}, 20);
|
|
60
69
|
|
|
61
70
|
return fm1.lock(key).then(() => {
|
|
62
|
-
expect(fm1.getLockedInfo(key)).
|
|
71
|
+
expect(fm1.getLockedInfo(key)).to.exist;
|
|
63
72
|
});
|
|
64
73
|
});
|
|
65
74
|
|
|
@@ -81,9 +90,9 @@ describe("FastMutex", () => {
|
|
|
81
90
|
stub.onCall(4).returns("uniqueId");
|
|
82
91
|
|
|
83
92
|
return fm.lock(key).then((stats) => {
|
|
84
|
-
expect(stats.restartCount).
|
|
85
|
-
expect(stats.locksLost).
|
|
86
|
-
expect(stats.contentionCount).
|
|
93
|
+
expect(stats.restartCount).to.eq(1);
|
|
94
|
+
expect(stats.locksLost).to.eq(1);
|
|
95
|
+
expect(stats.contentionCount).to.eq(1);
|
|
87
96
|
});
|
|
88
97
|
});
|
|
89
98
|
|
|
@@ -103,10 +112,10 @@ describe("FastMutex", () => {
|
|
|
103
112
|
const spy = sandbox.spy(fm, "lock");
|
|
104
113
|
|
|
105
114
|
return fm.lock(key).then((stats) => {
|
|
106
|
-
expect(stats.restartCount).
|
|
107
|
-
expect(stats.locksLost).
|
|
108
|
-
expect(stats.contentionCount).
|
|
109
|
-
expect(spy.callCount).
|
|
115
|
+
expect(stats.restartCount).to.eq(0);
|
|
116
|
+
expect(stats.locksLost).to.eq(0);
|
|
117
|
+
expect(stats.contentionCount).to.eq(1);
|
|
118
|
+
expect(spy.callCount).to.eq(1);
|
|
110
119
|
});
|
|
111
120
|
});
|
|
112
121
|
|
|
@@ -127,20 +136,20 @@ describe("FastMutex", () => {
|
|
|
127
136
|
/* eslint-disable jest/valid-expect-in-promise */
|
|
128
137
|
const lock1Promise = fm1.lock("lock1").then((stats) => {
|
|
129
138
|
lock1Acquired = true;
|
|
130
|
-
expect(localStorage.getItem(yPrefix + "lock1")).
|
|
139
|
+
expect(localStorage.getItem(yPrefix + "lock1")).to.exist;
|
|
131
140
|
return stats;
|
|
132
141
|
});
|
|
133
142
|
|
|
134
143
|
/* eslint-disable jest/valid-expect-in-promise */
|
|
135
144
|
const lock2Promise = fm2.lock("lock2").then((stats) => {
|
|
136
145
|
lock2Acquired = true;
|
|
137
|
-
expect(localStorage.getItem(yPrefix + "lock2")).
|
|
146
|
+
expect(localStorage.getItem(yPrefix + "lock2")).to.exist;
|
|
138
147
|
return stats;
|
|
139
148
|
});
|
|
140
149
|
|
|
141
150
|
await Promise.all([lock1Promise, lock2Promise]).then(() => {
|
|
142
|
-
expect(lock1Acquired).
|
|
143
|
-
expect(lock2Acquired).
|
|
151
|
+
expect(lock1Acquired).to.be.true;
|
|
152
|
+
expect(lock2Acquired).to.be.true;
|
|
144
153
|
});
|
|
145
154
|
});
|
|
146
155
|
|
|
@@ -154,11 +163,11 @@ describe("FastMutex", () => {
|
|
|
154
163
|
return fm1
|
|
155
164
|
.lock(key)
|
|
156
165
|
.then(() => {
|
|
157
|
-
expect(fm1.getItem("yLock" + key)).
|
|
166
|
+
expect(fm1.getItem("yLock" + key)).to.eq("releaseTestId");
|
|
158
167
|
return fm1.release(key);
|
|
159
168
|
})
|
|
160
169
|
.then(() => {
|
|
161
|
-
expect(fm1.getItem("yLock" + key)).
|
|
170
|
+
expect(fm1.getItem("yLock" + key)).to.be.undefined;
|
|
162
171
|
});
|
|
163
172
|
});
|
|
164
173
|
|
|
@@ -176,7 +185,7 @@ describe("FastMutex", () => {
|
|
|
176
185
|
.then(() => {
|
|
177
186
|
// before the lock is released, try to establish another lock:
|
|
178
187
|
var lock2Promise = fm2.lock("clientId");
|
|
179
|
-
expect(fm1LockReleased).
|
|
188
|
+
expect(fm1LockReleased).to.be.false;
|
|
180
189
|
|
|
181
190
|
// in a few milliseconds, release the lock
|
|
182
191
|
setTimeout(() => {
|
|
@@ -188,14 +197,11 @@ describe("FastMutex", () => {
|
|
|
188
197
|
})
|
|
189
198
|
.then((lock2) => {
|
|
190
199
|
// this will only execute once the other lock was released
|
|
191
|
-
expect(fm1LockReleased).
|
|
200
|
+
expect(fm1LockReleased).to.be.true;
|
|
192
201
|
});
|
|
193
202
|
});
|
|
194
203
|
|
|
195
|
-
it("should throw if lock is never acquired after set time period",
|
|
196
|
-
jest.setTimeout(1000);
|
|
197
|
-
// this.slow(500);
|
|
198
|
-
|
|
204
|
+
it("should throw if lock is never acquired after set time period", () => {
|
|
199
205
|
const fm1 = new FastMutex({ localStorage: localStorage, timeout: 50 });
|
|
200
206
|
const fm2 = new FastMutex({ localStorage: localStorage, timeout: 50 });
|
|
201
207
|
|
|
@@ -204,10 +210,10 @@ describe("FastMutex", () => {
|
|
|
204
210
|
return fm2.lock("timeoutTest");
|
|
205
211
|
});
|
|
206
212
|
|
|
207
|
-
|
|
213
|
+
expect(p).eventually.to.throw();
|
|
208
214
|
});
|
|
209
215
|
|
|
210
|
-
it("should ignore expired locks", () => {
|
|
216
|
+
it("should ignore expired locks", async () => {
|
|
211
217
|
const fm1 = new FastMutex({
|
|
212
218
|
localStorage: localStorage,
|
|
213
219
|
timeout: 5000,
|
|
@@ -221,9 +227,10 @@ describe("FastMutex", () => {
|
|
|
221
227
|
|
|
222
228
|
localStorage.setItem("yLocktimeoutTest", JSON.stringify(expiredRecord));
|
|
223
229
|
expect(
|
|
224
|
-
JSON.parse(localStorage.getItem("yLocktimeoutTest")).value
|
|
225
|
-
).
|
|
226
|
-
|
|
230
|
+
JSON.parse(localStorage.getItem("yLocktimeoutTest")!).value
|
|
231
|
+
).to.eq("oldclient");
|
|
232
|
+
|
|
233
|
+
await fm1.lock("timeoutTest"); // should not throw
|
|
227
234
|
});
|
|
228
235
|
|
|
229
236
|
it("should reset the client stats after lock is released", async () => {
|
|
@@ -233,19 +240,20 @@ describe("FastMutex", () => {
|
|
|
233
240
|
let keepLock = true;
|
|
234
241
|
let keepLockFn = () => keepLock;
|
|
235
242
|
await fm1.lock("resetStats", keepLockFn);
|
|
236
|
-
expect(fm1.isLocked("resetStats")).
|
|
243
|
+
expect(fm1.isLocked("resetStats")).to.be.true;
|
|
237
244
|
keepLock = false;
|
|
238
245
|
await delay(100); // await timeout
|
|
239
|
-
expect(fm1.isLocked("resetStats")).
|
|
246
|
+
expect(fm1.isLocked("resetStats")).to.be.false;
|
|
240
247
|
const p = fm1.lock("resetStats").then(() => fm1.release("resetStats"));
|
|
241
|
-
|
|
248
|
+
|
|
249
|
+
await p; // should not throw
|
|
242
250
|
});
|
|
243
251
|
|
|
244
252
|
it("can keep lock with callback function", async () => {
|
|
245
253
|
const fm1 = new FastMutex({ localStorage: localStorage, timeout: 50 });
|
|
246
254
|
await fm1.lock("x");
|
|
247
255
|
await fm1.release("x");
|
|
248
|
-
expect(fm1.isLocked("x")).
|
|
256
|
+
expect(fm1.isLocked("x")).to.be.false;
|
|
249
257
|
await fm1.lock("x").then(() => fm1.release("x"));
|
|
250
258
|
});
|
|
251
259
|
|
|
@@ -259,6 +267,7 @@ describe("FastMutex", () => {
|
|
|
259
267
|
|
|
260
268
|
// try to acquire a lock after `timeout`:
|
|
261
269
|
await delay(50);
|
|
262
|
-
|
|
270
|
+
|
|
271
|
+
await fm1.lock("resetStats"); // should not throw
|
|
263
272
|
});
|
|
264
273
|
});
|
|
@@ -4,13 +4,19 @@ import { FastMutex } from "../lockstorage";
|
|
|
4
4
|
import { delay } from "@peerbit/time";
|
|
5
5
|
import { default as sodium } from "libsodium-wrappers";
|
|
6
6
|
import { v4 as uuid } from "uuid";
|
|
7
|
-
|
|
8
|
-
var localStorage = new LocalStorage("./tmp/getKeypair");
|
|
9
|
-
globalThis.localStorage = localStorage;
|
|
7
|
+
import { expect } from "chai";
|
|
10
8
|
|
|
11
9
|
describe("getKeypair", () => {
|
|
12
|
-
|
|
10
|
+
before(async () => {
|
|
13
11
|
await sodium.ready;
|
|
12
|
+
|
|
13
|
+
var LocalStorage = nodelocalstorage.LocalStorage;
|
|
14
|
+
var localStorage = new LocalStorage("./tmp/getKeypair");
|
|
15
|
+
globalThis.localStorage = localStorage;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
after(() => {
|
|
19
|
+
globalThis.localStorage.clear();
|
|
14
20
|
});
|
|
15
21
|
|
|
16
22
|
it("can aquire multiple keypairs", async () => {
|
|
@@ -25,16 +31,16 @@ describe("getKeypair", () => {
|
|
|
25
31
|
lockCondition
|
|
26
32
|
);
|
|
27
33
|
const { key: keypair2, path: path2 } = await getFreeKeypair(id, mutex);
|
|
28
|
-
expect(keypair!.equals(keypair2!)).
|
|
29
|
-
expect(path1).not.
|
|
34
|
+
expect(keypair!.equals(keypair2!)).to.be.false;
|
|
35
|
+
expect(path1).not.to.eq(path2);
|
|
30
36
|
lock = false;
|
|
31
37
|
await delay(timeout);
|
|
32
38
|
const { path: path3, key: keypair3 } = await getFreeKeypair(id, mutex);
|
|
33
|
-
expect(path3).
|
|
34
|
-
expect(keypair3.equals(keypair)).
|
|
39
|
+
expect(path3).to.eq(path1);
|
|
40
|
+
expect(keypair3.equals(keypair)).to.be.true;
|
|
35
41
|
|
|
36
42
|
const allKeypair = await getAllKeyPairs(id);
|
|
37
|
-
expect(allKeypair.map((x) => x.publicKey.hashcode())).
|
|
43
|
+
expect(allKeypair.map((x) => x.publicKey.hashcode())).to.deep.eq([
|
|
38
44
|
keypair3.publicKey.hashcode(),
|
|
39
45
|
keypair2.publicKey.hashcode(),
|
|
40
46
|
]);
|
|
@@ -50,18 +56,18 @@ describe("getKeypair", () => {
|
|
|
50
56
|
id,
|
|
51
57
|
mutex,
|
|
52
58
|
lockCondition,
|
|
53
|
-
true
|
|
59
|
+
{ releaseLockIfSameId: true }
|
|
54
60
|
);
|
|
55
61
|
const { key: keypair2, path: path2 } = await getFreeKeypair(
|
|
56
62
|
id,
|
|
57
63
|
mutex,
|
|
58
64
|
undefined,
|
|
59
|
-
true
|
|
65
|
+
{ releaseLockIfSameId: true }
|
|
60
66
|
);
|
|
61
|
-
expect(keypair!.equals(keypair2!)).
|
|
62
|
-
expect(path1).
|
|
67
|
+
expect(keypair!.equals(keypair2!)).to.be.true;
|
|
68
|
+
expect(path1).to.eq(path2);
|
|
63
69
|
const allKeypair = await getAllKeyPairs(id);
|
|
64
|
-
expect(allKeypair).
|
|
70
|
+
expect(allKeypair).to.have.length(1);
|
|
65
71
|
});
|
|
66
72
|
|
|
67
73
|
it("releases manually", async () => {
|
|
@@ -73,11 +79,11 @@ describe("getKeypair", () => {
|
|
|
73
79
|
|
|
74
80
|
const { key: keypair2, path: path2 } = await getFreeKeypair(id, mutex);
|
|
75
81
|
|
|
76
|
-
expect(path1).not.
|
|
82
|
+
expect(path1).not.to.eq(path2);
|
|
77
83
|
releaseKey(path1, mutex);
|
|
78
|
-
expect(mutex.getLockedInfo(path1)).
|
|
84
|
+
expect(mutex.getLockedInfo(path1)).to.be.undefined;
|
|
79
85
|
const { key: keypair3, path: path3 } = await getFreeKeypair(id, mutex);
|
|
80
86
|
|
|
81
|
-
expect(path1).
|
|
87
|
+
expect(path1).to.eq(path3); // we can now acquire key at path1 again, since we released it
|
|
82
88
|
});
|
|
83
89
|
});
|
package/src/index.ts
CHANGED
package/src/lockstorage.ts
CHANGED
package/src/useLocal.tsx
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ClosedError, Documents, SearchRequest } from "@peerbit/document";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
export const useLocal = <T extends Record<string, any>>(
|
|
4
|
+
db?: Documents<T, any>
|
|
5
|
+
) => {
|
|
6
|
+
const [all, setAll] = useState<T[]>([]);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (!db || db.closed) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const changeListener = async () => {
|
|
13
|
+
try {
|
|
14
|
+
setAll(
|
|
15
|
+
await db.index.search(new SearchRequest(), {
|
|
16
|
+
local: true,
|
|
17
|
+
remote: false,
|
|
18
|
+
})
|
|
19
|
+
);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
if (error instanceof ClosedError) {
|
|
22
|
+
// ignore
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
changeListener();
|
|
30
|
+
db.events.addEventListener("change", changeListener);
|
|
31
|
+
|
|
32
|
+
return () => db.events.addEventListener("change", changeListener);
|
|
33
|
+
}, [db?.address, db?.closed]);
|
|
34
|
+
return all;
|
|
35
|
+
};
|
package/src/usePeer.tsx
CHANGED
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
import React, { useContext } from "react";
|
|
2
2
|
import { Multiaddr } from "@multiformats/multiaddr";
|
|
3
3
|
import { Peerbit } from "peerbit";
|
|
4
|
-
import { webSockets } from "@libp2p/websockets";
|
|
5
4
|
import { DirectSub } from "@peerbit/pubsub";
|
|
6
5
|
import { yamux } from "@chainsafe/libp2p-yamux";
|
|
7
|
-
import {
|
|
8
|
-
|
|
6
|
+
import {
|
|
7
|
+
getFreeKeypair,
|
|
8
|
+
getTabId,
|
|
9
|
+
inIframe,
|
|
10
|
+
cookiesWhereClearedJustNow,
|
|
11
|
+
} from "./utils.js";
|
|
12
|
+
import { noise } from "@chainsafe/libp2p-noise";
|
|
9
13
|
import { v4 as uuid } from "uuid";
|
|
10
14
|
import { Ed25519Keypair } from "@peerbit/crypto";
|
|
11
15
|
import { FastMutex } from "./lockstorage.js";
|
|
12
16
|
import sodium from "libsodium-wrappers";
|
|
13
|
-
|
|
17
|
+
|
|
14
18
|
import { useMount } from "./useMount.js";
|
|
15
19
|
import { createClient, createHost } from "@peerbit/proxy-window";
|
|
16
20
|
import { ProgramClient } from "@peerbit/program";
|
|
17
|
-
import { circuitRelayTransport } from "@libp2p/circuit-relay-v2";
|
|
18
|
-
import { webRTC } from "@libp2p/webrtc";
|
|
19
21
|
import { identify } from "@libp2p/identify";
|
|
22
|
+
import { webSockets } from "@libp2p/websockets";
|
|
23
|
+
import { circuitRelayTransport } from "@libp2p/circuit-relay-v2";
|
|
24
|
+
|
|
25
|
+
import * as filters from "@libp2p/websockets/filters";
|
|
20
26
|
import { detectIncognito } from "detectincognitojs";
|
|
21
27
|
|
|
22
28
|
export type ConnectionStatus =
|
|
@@ -114,6 +120,7 @@ export const PeerProvider = (options: PeerOptions) => {
|
|
|
114
120
|
: (options as TopOptions);
|
|
115
121
|
|
|
116
122
|
if (nodeOptions.type !== "proxy") {
|
|
123
|
+
const releaseFirstLock = cookiesWhereClearedJustNow();
|
|
117
124
|
const nodeId =
|
|
118
125
|
nodeOptions.keypair ||
|
|
119
126
|
(
|
|
@@ -124,10 +131,13 @@ export const PeerProvider = (options: PeerOptions) => {
|
|
|
124
131
|
timeout: 1000,
|
|
125
132
|
}),
|
|
126
133
|
undefined,
|
|
127
|
-
|
|
134
|
+
{
|
|
135
|
+
releaseFirstLock, // when clearing cookies sometimes the localStorage is not cleared immediately so we need to release the lock forcefully. TODO investigate why this is happening
|
|
136
|
+
releaseLockIfSameId: true, // reuse keypairs from same tab, (force release)
|
|
137
|
+
}
|
|
128
138
|
)
|
|
129
139
|
).key;
|
|
130
|
-
const peerId =
|
|
140
|
+
const peerId = nodeId.toPeerId();
|
|
131
141
|
|
|
132
142
|
let directory: string | undefined = undefined;
|
|
133
143
|
if (
|
|
@@ -157,13 +167,15 @@ export const PeerProvider = (options: PeerOptions) => {
|
|
|
157
167
|
newPeer = await Peerbit.create({
|
|
158
168
|
libp2p: {
|
|
159
169
|
addresses: {
|
|
160
|
-
listen: [
|
|
170
|
+
listen: [
|
|
171
|
+
"/p2p-circuit",
|
|
172
|
+
/* "/webrtc" */
|
|
173
|
+
], // TMP disable because flaky behaviour with libp2p 1.8.1
|
|
161
174
|
},
|
|
162
|
-
|
|
175
|
+
connectionEncrypters: [noise()],
|
|
163
176
|
peerId, //, having the same peer accross broswers does not work, only one tab will be recognized by other peers
|
|
164
177
|
connectionManager: {
|
|
165
178
|
maxConnections: 100,
|
|
166
|
-
minConnections: 1,
|
|
167
179
|
},
|
|
168
180
|
|
|
169
181
|
streamMuxers: [yamux()],
|
|
@@ -183,19 +195,15 @@ export const PeerProvider = (options: PeerOptions) => {
|
|
|
183
195
|
webSockets({
|
|
184
196
|
filter: filters.all,
|
|
185
197
|
}),
|
|
186
|
-
circuitRelayTransport(
|
|
187
|
-
|
|
188
|
-
}),
|
|
189
|
-
webRTC(),
|
|
198
|
+
circuitRelayTransport(),
|
|
199
|
+
/* webRTC(), */ // TMP disable because flaky behaviour with libp2p 1.8.1
|
|
190
200
|
],
|
|
191
201
|
}
|
|
192
202
|
: {
|
|
193
203
|
transports: [
|
|
194
204
|
webSockets({ filter: filters.wss }),
|
|
195
|
-
circuitRelayTransport(
|
|
196
|
-
|
|
197
|
-
}),
|
|
198
|
-
webRTC(),
|
|
205
|
+
circuitRelayTransport(),
|
|
206
|
+
/* webRTC(), */ // TMP disable because flaky behaviour with libp2p 1.8.1
|
|
199
207
|
],
|
|
200
208
|
}),
|
|
201
209
|
|
|
@@ -212,8 +220,10 @@ export const PeerProvider = (options: PeerOptions) => {
|
|
|
212
220
|
},
|
|
213
221
|
directory,
|
|
214
222
|
});
|
|
215
|
-
console.log("
|
|
216
|
-
|
|
223
|
+
console.log("Client created", {
|
|
224
|
+
directory,
|
|
225
|
+
peerHash: newPeer?.identity.publicKey.hashcode(),
|
|
226
|
+
});
|
|
217
227
|
|
|
218
228
|
setConnectionState("connecting");
|
|
219
229
|
|
package/src/useProgram.tsx
CHANGED
|
@@ -38,6 +38,7 @@ export const useProgram = <
|
|
|
38
38
|
}
|
|
39
39
|
setLoading(true);
|
|
40
40
|
let changeListener: () => void;
|
|
41
|
+
|
|
41
42
|
closingRef.current.then(() => {
|
|
42
43
|
programLoadingRef.current = peer
|
|
43
44
|
?.open(addressOrOpen, { ...options, existing: "reuse" })
|
|
@@ -67,18 +68,26 @@ export const useProgram = <
|
|
|
67
68
|
let startRef = programLoadingRef.current;
|
|
68
69
|
|
|
69
70
|
// TODO don't close on reopen the same db?
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
p.
|
|
74
|
-
|
|
71
|
+
if (programLoadingRef.current) {
|
|
72
|
+
closingRef.current =
|
|
73
|
+
programLoadingRef.current.then((p) =>
|
|
74
|
+
p.close().then(() => {
|
|
75
|
+
p.events.removeEventListener(
|
|
76
|
+
"join",
|
|
77
|
+
changeListener
|
|
78
|
+
);
|
|
79
|
+
p.events.removeEventListener(
|
|
80
|
+
"leave",
|
|
81
|
+
changeListener
|
|
82
|
+
);
|
|
75
83
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
if (programLoadingRef.current === startRef) {
|
|
85
|
+
setProgram(undefined);
|
|
86
|
+
programLoadingRef.current = undefined;
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
) || Promise.resolve();
|
|
90
|
+
}
|
|
82
91
|
};
|
|
83
92
|
}, [
|
|
84
93
|
peer?.identity.publicKey.hashcode(),
|