@peerbit/react 1.1.30 → 1.1.32

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/dist/src/utils.js CHANGED
@@ -2,9 +2,11 @@ import { deserialize, serialize } from "@dao-xyz/borsh";
2
2
  import { Ed25519Keypair, fromBase64, toBase64 } from "@peerbit/crypto";
3
3
  import sodium from "libsodium-wrappers";
4
4
  import { v4 as uuid } from "uuid";
5
- import { FastMutex } from "./lockstorage.js";
5
+ import { acquireWebLock, FastMutex } from "./lockstorage.js";
6
6
  const CLIENT_ID_STORAGE_KEY = "CLIENT_ID";
7
7
  const ID_COUNTER_KEY = "idc/";
8
+ export const WEB_LOCK_CLIENT_ID_PREFIX = "@peerbit/react:web-lock/v1:";
9
+ const WEB_LOCK_NAME_PREFIX = "@peerbit/react:";
8
10
  const getKeyId = (prefix, id) => `${prefix}/${id}`;
9
11
  export const cookiesWhereClearedJustNow = () => {
10
12
  const lastPersistedAt = localStorage.getItem("lastPersistedAt");
@@ -27,33 +29,176 @@ export const getClientId = (type) => {
27
29
  export const releaseKey = (path, lock) => {
28
30
  lock.release(path);
29
31
  };
32
+ export const createWebLockClientId = () => `${WEB_LOCK_CLIENT_ID_PREFIX}${uuid()}`;
33
+ const isWebLockClientId = (clientId) => clientId.startsWith(WEB_LOCK_CLIENT_ID_PREFIX);
34
+ const getSelectionLockName = (id) => `${WEB_LOCK_NAME_PREFIX}keypair-selection:${JSON.stringify(id)}`;
35
+ const getKeypairLockName = (id, index) => `${WEB_LOCK_NAME_PREFIX}keypair:${JSON.stringify([id, index])}`;
36
+ const getAbortReason = (signal) => signal.reason ?? new DOMException("Aborted", "AbortError");
37
+ const throwIfAborted = (signal) => {
38
+ if (signal?.aborted) {
39
+ throw getAbortReason(signal);
40
+ }
41
+ };
42
+ const waitForLegacyCounter = async (key, lock, signal) => {
43
+ const startedAt = Date.now();
44
+ while (true) {
45
+ throwIfAborted(signal);
46
+ const owners = lock.getLockedOwners(key);
47
+ if (owners.length === 0) {
48
+ return;
49
+ }
50
+ if (owners.every(isWebLockClientId)) {
51
+ lock.releaseIfOwnedBy(key, isWebLockClientId);
52
+ return;
53
+ }
54
+ if (Date.now() - startedAt >= lock.timeout * 2 + 50) {
55
+ throw new Error("Timed out waiting for the legacy key counter lock");
56
+ }
57
+ await new Promise((resolve) => setTimeout(resolve, 10));
58
+ }
59
+ };
60
+ const acquirePinnedKeypairLock = async ({ id, index, lock, lockManager, lockCondition, signal, }) => {
61
+ const webLock = await acquireWebLock(lockManager, getKeypairLockName(id, index), { ifAvailable: true, signal });
62
+ if (!webLock) {
63
+ return;
64
+ }
65
+ const key = getKeyId(id, index);
66
+ const owners = lock.getLockedOwners(key);
67
+ if (owners.some((owner) => !isWebLockClientId(owner))) {
68
+ webLock.release();
69
+ return;
70
+ }
71
+ lock.releaseIfOwnedBy(key, isWebLockClientId);
72
+ try {
73
+ await lock.lock(key, lockCondition);
74
+ throwIfAborted(signal);
75
+ lock.pin(key);
76
+ }
77
+ catch (error) {
78
+ try {
79
+ lock.release(key);
80
+ }
81
+ finally {
82
+ webLock.release();
83
+ }
84
+ throw error;
85
+ }
86
+ let released = false;
87
+ const release = () => {
88
+ if (released)
89
+ return;
90
+ released = true;
91
+ signal?.removeEventListener("abort", release);
92
+ try {
93
+ lock.release(key);
94
+ }
95
+ finally {
96
+ webLock.release();
97
+ }
98
+ };
99
+ signal?.addEventListener("abort", release, { once: true });
100
+ if (signal?.aborted) {
101
+ release();
102
+ throw getAbortReason(signal);
103
+ }
104
+ return {
105
+ release,
106
+ detachAbort: () => {
107
+ signal?.removeEventListener("abort", release);
108
+ webLock.detachAbort();
109
+ },
110
+ };
111
+ };
30
112
  export const getFreeKeypair = async (id = "", lock, lockCondition = () => true, options) => {
31
113
  await sodium.ready;
32
114
  const idCounterKey = ID_COUNTER_KEY + id;
33
115
  await lock.lock(idCounterKey, () => true);
34
- let idCounter = JSON.parse(localStorage.getItem(idCounterKey) || "0");
35
- for (let i = 0; i < 10000; i++) {
36
- const key = getKeyId(id, i);
37
- const lockedInfo = lock.getLockedInfo(key);
38
- if (lockedInfo) {
39
- if ((lockedInfo === lock.clientId && options?.releaseLockIfSameId) ||
40
- options?.releaseFirstLock) {
41
- await lock.release(key);
116
+ try {
117
+ const idCounter = JSON.parse(localStorage.getItem(idCounterKey) || "0");
118
+ for (let i = 0; i < 10000; i++) {
119
+ const key = getKeyId(id, i);
120
+ const lockedInfo = lock.getLockedInfo(key);
121
+ if (lockedInfo) {
122
+ if (lockedInfo === lock.clientId &&
123
+ options?.releaseLockIfSameId) {
124
+ lock.release(key);
125
+ }
126
+ else if (options?.releaseFirstLock) {
127
+ lock.releaseIfOwnedBy(key, () => true);
128
+ }
129
+ else {
130
+ continue;
131
+ }
42
132
  }
43
- else {
133
+ await lock.lock(key, lockCondition);
134
+ localStorage.setItem(idCounterKey, JSON.stringify(Math.max(idCounter, i + 1)));
135
+ return {
136
+ index: i,
137
+ path: key,
138
+ key: await getKeypair(key),
139
+ };
140
+ }
141
+ throw new Error("Failed to resolve key");
142
+ }
143
+ finally {
144
+ lock.release(idCounterKey);
145
+ }
146
+ };
147
+ export const getFreeKeypairWithWebLock = async (id, lock, lockManager, lockCondition = () => true, signal) => {
148
+ if (!isWebLockClientId(lock.clientId)) {
149
+ throw new Error("Web Lock key allocation requires a tagged mutex client ID");
150
+ }
151
+ await sodium.ready;
152
+ throwIfAborted(signal);
153
+ const selectionLock = await acquireWebLock(lockManager, getSelectionLockName(id), { signal });
154
+ if (!selectionLock) {
155
+ throw new Error("Failed to acquire the keypair selection lock");
156
+ }
157
+ const idCounterKey = ID_COUNTER_KEY + id;
158
+ let counterLocked = false;
159
+ try {
160
+ await waitForLegacyCounter(idCounterKey, lock, signal);
161
+ await lock.lock(idCounterKey, () => true);
162
+ counterLocked = true;
163
+ const idCounter = JSON.parse(localStorage.getItem(idCounterKey) || "0");
164
+ for (let i = 0; i < 10000; i++) {
165
+ throwIfAborted(signal);
166
+ const selected = await acquirePinnedKeypairLock({
167
+ id,
168
+ index: i,
169
+ lock,
170
+ lockManager,
171
+ lockCondition,
172
+ signal,
173
+ });
174
+ if (!selected) {
44
175
  continue;
45
176
  }
177
+ const path = getKeyId(id, i);
178
+ try {
179
+ const key = await getKeypair(path);
180
+ throwIfAborted(signal);
181
+ localStorage.setItem(idCounterKey, JSON.stringify(Math.max(idCounter, i + 1)));
182
+ selected.detachAbort();
183
+ return { index: i, path, key, release: selected.release };
184
+ }
185
+ catch (error) {
186
+ selected.release();
187
+ throw error;
188
+ }
46
189
  }
47
- await lock.lock(key, lockCondition);
48
- localStorage.setItem(idCounterKey, JSON.stringify(Math.max(idCounter, i + 1)));
49
- await lock.release(idCounterKey);
50
- return {
51
- index: i,
52
- path: key,
53
- key: await getKeypair(key),
54
- };
55
- }
56
- throw new Error("Failed to resolve key");
190
+ throw new Error("Failed to resolve key");
191
+ }
192
+ finally {
193
+ try {
194
+ if (counterLocked) {
195
+ lock.release(idCounterKey);
196
+ }
197
+ }
198
+ finally {
199
+ selectionLock.release();
200
+ }
201
+ }
57
202
  };
58
203
  export const getAllKeyPairs = async (id = "") => {
59
204
  const idCounterKey = ID_COUNTER_KEY + id;
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peerbit/react",
3
- "version": "1.1.30",
3
+ "version": "1.1.32",
4
4
  "description": "React bindings for Peerbit clients",
5
5
  "sideEffects": false,
6
6
  "type": "module",
@@ -53,14 +53,14 @@
53
53
  "debug": "^4.3.7",
54
54
  "detectincognitojs": "^1.6.1",
55
55
  "libsodium-wrappers": "0.7.15",
56
- "uuid": "^10.0.0",
57
- "@peerbit/canonical-client": "1.1.38",
58
- "@peerbit/crypto": "3.1.2",
56
+ "uuid": "^11.1.1",
57
+ "@peerbit/canonical-client": "1.1.40",
58
+ "@peerbit/crypto": "3.1.4",
59
+ "@peerbit/indexer-interface": "3.0.8",
60
+ "@peerbit/program": "6.0.37",
59
61
  "@peerbit/logger": "2.0.1",
60
- "@peerbit/program": "6.0.35",
61
- "@peerbit/program-react": "0.4.38",
62
- "@peerbit/indexer-interface": "3.0.6",
63
- "peerbit": "5.3.6"
62
+ "@peerbit/program-react": "0.4.40",
63
+ "peerbit": "5.3.8"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@testing-library/dom": "^10.4.0",
@@ -69,15 +69,15 @@
69
69
  "@types/node-localstorage": "^1.3.3",
70
70
  "@types/react": "^18.3.11",
71
71
  "@types/react-dom": "^18.3.5",
72
- "@vitest/coverage-v8": "^4.0.12",
72
+ "@vitest/coverage-v8": "^4.1.10",
73
73
  "chai": "^5.2.0",
74
74
  "node-localstorage": "^3.0.5",
75
75
  "react-dom": "^19.1.0",
76
- "sinon": "^21.0.0",
77
- "vitest": "^4.0.12",
78
- "@peerbit/time": "3.0.0"
76
+ "sinon": "^22.0.0",
77
+ "vitest": "^4.1.10",
78
+ "@peerbit/time": "3.0.1"
79
79
  },
80
- "author": "dao.xyz",
80
+ "author": "Peerbit contributors",
81
81
  "license": "MIT",
82
82
  "repository": {
83
83
  "type": "git",
@@ -43,6 +43,32 @@ export class FastMutex {
43
43
  this.localStorage = localStorage || window.localStorage;
44
44
  }
45
45
 
46
+ private clearKeepAlive(key: string) {
47
+ clearInterval(this.intervals.get(key));
48
+ this.intervals.delete(key);
49
+ }
50
+
51
+ private getStoredItem(
52
+ key: string,
53
+ ): { expiresAt: number; value: string } | undefined {
54
+ const item = this.localStorage.getItem(key);
55
+ if (!item) return;
56
+ const parsed = JSON.parse(item) as { expiresAt: number; value: string };
57
+ // Preserve the legacy grace period so a current bundle does not steal an
58
+ // identity from an older tab whose keep-alive timer was briefly throttled.
59
+ if (Date.now() - parsed.expiresAt >= this.timeout) {
60
+ debug(
61
+ 'FastMutex client "%s" removed an expired record on "%s"',
62
+ this.clientId,
63
+ key,
64
+ );
65
+ this.localStorage.removeItem(key);
66
+ this.clearKeepAlive(key);
67
+ return;
68
+ }
69
+ return parsed;
70
+ }
71
+
46
72
  lock(
47
73
  key: string,
48
74
  keepLocked?: () => boolean,
@@ -89,6 +115,7 @@ export class FastMutex {
89
115
  if (settled) return;
90
116
  settled = true;
91
117
  clearPendingRetries();
118
+ this.release(key);
92
119
  reject(error);
93
120
  };
94
121
 
@@ -219,17 +246,44 @@ export class FastMutex {
219
246
  return this.getItem(x) || this.getItem(y);
220
247
  }
221
248
 
249
+ getLockedOwners(key: string): string[] {
250
+ const owners = [
251
+ this.getItem(this.xPrefix + key),
252
+ this.getItem(this.yPrefix + key),
253
+ ].filter((owner): owner is string => !!owner);
254
+ return [...new Set(owners)];
255
+ }
256
+
222
257
  release(key: string) {
258
+ this.releaseIfOwnedBy(key, (owner) => owner === this.clientId);
259
+ }
260
+
261
+ releaseIfOwnedBy(key: string, predicate: (owner: string) => boolean) {
223
262
  debug(
224
263
  'FastMutex client "%s" is releasing lock on "%s"',
225
264
  this.clientId,
226
265
  key,
227
266
  );
228
- let ps = [this.yPrefix + key, this.xPrefix + key];
267
+ const ps = [this.yPrefix + key, this.xPrefix + key];
229
268
  for (const p of ps) {
230
- clearInterval(this.intervals.get(p));
231
- this.intervals.delete(p);
232
- this.localStorage.removeItem(p);
269
+ this.clearKeepAlive(p);
270
+ const stored = this.getStoredItem(p);
271
+ if (stored && predicate(stored.value)) {
272
+ this.localStorage.removeItem(p);
273
+ }
274
+ }
275
+ }
276
+
277
+ pin(key: string) {
278
+ for (const p of [this.xPrefix + key, this.yPrefix + key]) {
279
+ this.clearKeepAlive(p);
280
+ this.localStorage.setItem(
281
+ p,
282
+ JSON.stringify({
283
+ expiresAt: Number.MAX_SAFE_INTEGER,
284
+ value: this.clientId,
285
+ }),
286
+ );
233
287
  }
234
288
  }
235
289
 
@@ -239,11 +293,7 @@ export class FastMutex {
239
293
  */
240
294
  setItem(key: string, value: any, keepLocked?: () => boolean) {
241
295
  // Avoid leaking keep-alive intervals when re-setting the same key (e.g. re-lock with replaceIfSameClient).
242
- const existing = this.intervals.get(key);
243
- if (existing) {
244
- clearInterval(existing);
245
- this.intervals.delete(key);
246
- }
296
+ this.clearKeepAlive(key);
247
297
  if (!keepLocked) {
248
298
  return this.localStorage.setItem(
249
299
  key,
@@ -262,15 +312,13 @@ export class FastMutex {
262
312
  }),
263
313
  );
264
314
  const interval = setInterval(() => {
315
+ if (this.getStoredItem(key)?.value !== value) {
316
+ this.clearKeepAlive(key);
317
+ return;
318
+ }
265
319
  if (!keepLocked()) {
266
- this.localStorage.setItem(
267
- // TODO, release directly?
268
- key,
269
- JSON.stringify({
270
- expiresAt: 0,
271
- value,
272
- }),
273
- );
320
+ this.localStorage.removeItem(key);
321
+ this.clearKeepAlive(key);
274
322
  } else {
275
323
  this.localStorage.setItem(
276
324
  key,
@@ -281,6 +329,10 @@ export class FastMutex {
281
329
  );
282
330
  }
283
331
  }, this.timeout);
332
+ // A browser timer has no `unref`, while Node returns a Timeout object.
333
+ // Keeping the lease alive must not keep an otherwise finished Node
334
+ // process (or a Vitest coverage worker) alive indefinitely.
335
+ (interval as unknown as { unref?: () => void }).unref?.();
284
336
  this.intervals.set(key, interval);
285
337
  return ret;
286
338
  }
@@ -290,22 +342,92 @@ export class FastMutex {
290
342
  * Helper function to parse JSON encoded values set in localStorage
291
343
  */
292
344
  getItem(key: string): string | undefined {
293
- const item = this.localStorage.getItem(key);
294
- if (!item) return;
345
+ return this.getStoredItem(key)?.value;
346
+ }
347
+ }
295
348
 
296
- const parsed = JSON.parse(item);
297
- if (new Date().getTime() - parsed.expiresAt >= this.timeout) {
298
- debug(
299
- 'FastMutex client "%s" removed an expired record on "%s"',
300
- this.clientId,
301
- key,
302
- );
303
- this.localStorage.removeItem(key);
304
- clearInterval(this.intervals.get(key));
305
- this.intervals.delete(key);
349
+ export type WebLockLease = {
350
+ release: () => void;
351
+ detachAbort: () => void;
352
+ };
353
+
354
+ const getAbortReason = (signal: AbortSignal) =>
355
+ signal.reason ?? new DOMException("Aborted", "AbortError");
356
+
357
+ export const acquireWebLock = (
358
+ manager: LockManager,
359
+ name: string,
360
+ options: { ifAvailable?: boolean; signal?: AbortSignal } = {},
361
+ ): Promise<WebLockLease | undefined> => {
362
+ return new Promise((resolve, reject) => {
363
+ let settled = false;
364
+ let held = false;
365
+ let released = false;
366
+ let releaseHold: (() => void) | undefined;
367
+ const hold = new Promise<void>((resolveHold) => {
368
+ releaseHold = resolveHold;
369
+ });
370
+ const signal = options.signal;
371
+
372
+ const detachAbort = () => {
373
+ signal?.removeEventListener("abort", onAbort);
374
+ };
375
+ const release = () => {
376
+ if (released) return;
377
+ released = true;
378
+ detachAbort();
379
+ releaseHold?.();
380
+ };
381
+ const rejectOnce = (error: unknown) => {
382
+ if (settled) return;
383
+ settled = true;
384
+ detachAbort();
385
+ reject(error);
386
+ };
387
+ const onAbort = () => {
388
+ if (held) {
389
+ release();
390
+ }
391
+ rejectOnce(getAbortReason(signal!));
392
+ };
393
+
394
+ if (signal?.aborted) {
395
+ rejectOnce(getAbortReason(signal));
306
396
  return;
307
397
  }
398
+ signal?.addEventListener("abort", onAbort, { once: true });
308
399
 
309
- return JSON.parse(item).value;
310
- }
311
- }
400
+ void manager
401
+ .request(
402
+ name,
403
+ options.ifAvailable
404
+ ? { mode: "exclusive", ifAvailable: true }
405
+ : signal
406
+ ? { mode: "exclusive", signal }
407
+ : { mode: "exclusive" },
408
+ async (lock) => {
409
+ if (!lock) {
410
+ if (!settled) {
411
+ settled = true;
412
+ detachAbort();
413
+ resolve(undefined);
414
+ }
415
+ return;
416
+ }
417
+
418
+ held = true;
419
+ if (signal?.aborted) {
420
+ release();
421
+ rejectOnce(getAbortReason(signal));
422
+ return;
423
+ }
424
+ if (!settled) {
425
+ settled = true;
426
+ resolve({ release, detachAbort });
427
+ }
428
+ await hold;
429
+ },
430
+ )
431
+ .catch((error) => rejectOnce(error));
432
+ });
433
+ };