@weasel-js/storage 1.9.3 → 1.9.4
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/README.md +12 -0
- package/dist/index.d.ts +35 -11
- package/dist/index.js +242 -70
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,18 @@ Adapters that can read synchronously (`localStorage`, `sessionStorage`,
|
|
|
10
10
|
memory) also implement `listSync`, and `openRecordsSync` opens a cache from
|
|
11
11
|
one with no `await`.
|
|
12
12
|
|
|
13
|
+
A cache whose first read fails opens empty with `writable` false, and reads
|
|
14
|
+
again on a backoff (`retryMs`, doubling to `retryMaxMs`; `retryMs: false`
|
|
15
|
+
turns that off). `cache.read()` tries at once, and `cache.whenRead()` resolves
|
|
16
|
+
once a read has landed. When a read lands, the cache
|
|
17
|
+
reports the records as `remote` changes and starts writing, beginning with
|
|
18
|
+
whatever was set while it waited; those writes win over what it read.
|
|
19
|
+
|
|
20
|
+
A write storage rejects stays queued and is sent again on the same backoff,
|
|
21
|
+
and `flush()` resolves false until it lands. While a write is queued or on its
|
|
22
|
+
way, another writer's change to that record is ignored. With `retryMs: false`
|
|
23
|
+
a rejected write waits for the next flush.
|
|
24
|
+
|
|
13
25
|
`defaultStorage()` is IndexedDB under the database `'weasel'`, or
|
|
14
26
|
`localStorage`, with a warning, where IndexedDB will not open; it chooses once
|
|
15
27
|
per page. `fallbackStorage(preferred, fallback, label)` makes the same choice
|
package/dist/index.d.ts
CHANGED
|
@@ -79,31 +79,45 @@ interface RecordChange {
|
|
|
79
79
|
*
|
|
80
80
|
* Writes land in memory at once and reach storage on a debounce. The newest
|
|
81
81
|
* write to a record wins: another writer's change to a record with a write
|
|
82
|
-
* still queued here is ignored, since
|
|
82
|
+
* still queued or on its way here is ignored, since that write lands after
|
|
83
|
+
* it. A write storage rejects stays queued and is sent again on a backoff.
|
|
83
84
|
*/
|
|
84
85
|
interface RecordCache {
|
|
85
86
|
readonly prefix: string;
|
|
86
|
-
/** False
|
|
87
|
-
*
|
|
87
|
+
/** False while writing is off, so nothing here can overwrite the records:
|
|
88
|
+
* until a first read that failed lands on a later try, and for good once
|
|
89
|
+
* the opener marks them read-only. */
|
|
88
90
|
readonly writable: boolean;
|
|
89
91
|
has(name: string): boolean;
|
|
90
92
|
get(name: string): unknown;
|
|
91
93
|
/** Every record whose name starts with `namePrefix`. */
|
|
92
94
|
entries(namePrefix?: string): [string, unknown][];
|
|
95
|
+
/** `undefined` is no value a record can hold: setting it deletes. */
|
|
93
96
|
set(name: string, value: unknown): void;
|
|
94
97
|
delete(name: string): void;
|
|
95
98
|
subscribe(listener: (changes: RecordChange[]) => void): () => void;
|
|
96
99
|
/** Send queued writes now. Resolves true when every queued write landed or
|
|
97
|
-
* none was queued; false when a write threw (
|
|
98
|
-
*
|
|
100
|
+
* none was queued; false when a write threw (it stays queued) or writing
|
|
101
|
+
* is off and writes were queued. */
|
|
99
102
|
flush(): Promise<boolean>;
|
|
100
|
-
/**
|
|
103
|
+
/** Try a failed first read again now, ahead of the backoff. Resolves
|
|
104
|
+
* whether the records have been read. */
|
|
105
|
+
read(): Promise<boolean>;
|
|
106
|
+
/** Resolves once the records have been read: at once for a cache that
|
|
107
|
+
* opened with them, and when a later read lands for one whose first read
|
|
108
|
+
* failed. Never resolves for a cache closed or stopped before that. */
|
|
109
|
+
whenRead(): Promise<void>;
|
|
110
|
+
/** Send queued writes, stop hearing other writers, and stop retrying. */
|
|
101
111
|
close(): Promise<void>;
|
|
102
112
|
}
|
|
103
113
|
/** The parts of a cache only its opener uses. */
|
|
104
114
|
interface OwnedRecordCache extends RecordCache {
|
|
105
115
|
/** Hold these records without writing them back. */
|
|
106
116
|
seed(entries: Iterable<[string, unknown]>): void;
|
|
117
|
+
/** Hear another writer's changes this cache did not take, because its own
|
|
118
|
+
* write to the record was queued or on its way: each a name and the value
|
|
119
|
+
* the other writer set, `undefined` for a delete. */
|
|
120
|
+
onOverruled(listener: (changes: [string, unknown][]) => void): () => void;
|
|
107
121
|
stopWriting(): void;
|
|
108
122
|
}
|
|
109
123
|
/** Options for `createRecordCache` and `openRecords`. */
|
|
@@ -114,18 +128,28 @@ interface RecordCacheOptions {
|
|
|
114
128
|
debounceMs?: number;
|
|
115
129
|
/** The longest a write waits under continuous change. Default 1000 ms. */
|
|
116
130
|
maxWaitMs?: number;
|
|
131
|
+
/** The wait before a failed first read, or a write storage rejected, is
|
|
132
|
+
* tried again, doubling after each failure. Default 1000 ms; `false` never
|
|
133
|
+
* tries again on its own, leaving a rejected write for the next flush. */
|
|
134
|
+
retryMs?: number | false;
|
|
135
|
+
/** The longest wait between those tries. Default 30000 ms. */
|
|
136
|
+
retryMaxMs?: number;
|
|
117
137
|
}
|
|
118
|
-
|
|
119
|
-
declare function createRecordCache(options: RecordCacheOptions & {
|
|
138
|
+
type CreateOptions = RecordCacheOptions & {
|
|
120
139
|
initial?: Iterable<[string, unknown]>;
|
|
121
140
|
writable?: boolean;
|
|
122
|
-
}
|
|
141
|
+
};
|
|
142
|
+
/** A cache holding `initial`, with nothing read from storage. */
|
|
143
|
+
declare function createRecordCache(options: CreateOptions): OwnedRecordCache;
|
|
123
144
|
/** Read every record under `prefix` and hold them. A failed read opens the
|
|
124
145
|
* cache empty with writing off, so it never overwrites what it could not
|
|
125
|
-
* read.
|
|
146
|
+
* read, and tries again on a backoff. When a read lands, the cache holds it,
|
|
147
|
+
* reports it as remote changes, and starts writing, sending first whatever
|
|
148
|
+
* was written meanwhile. */
|
|
126
149
|
declare function openRecords(options: RecordCacheOptions): Promise<OwnedRecordCache>;
|
|
127
150
|
/** `openRecords` for an adapter that reads synchronously: the cache is ready
|
|
128
|
-
* on return. A failed read opens it empty with writing off
|
|
151
|
+
* on return. A failed read opens it empty with writing off, and it recovers
|
|
152
|
+
* the way `openRecords` does. */
|
|
129
153
|
declare function openRecordsSync(options: RecordCacheOptions & {
|
|
130
154
|
storage: SyncStorageAdapter;
|
|
131
155
|
}): OwnedRecordCache;
|
package/dist/index.js
CHANGED
|
@@ -4,8 +4,15 @@ function createIndexedDbAdapter({
|
|
|
4
4
|
store = "records"
|
|
5
5
|
} = {}) {
|
|
6
6
|
let opened = null;
|
|
7
|
+
let connection = null;
|
|
8
|
+
const forget = (db) => {
|
|
9
|
+
if (connection !== db) return;
|
|
10
|
+
connection = null;
|
|
11
|
+
opened = null;
|
|
12
|
+
};
|
|
7
13
|
const open = () => {
|
|
8
|
-
|
|
14
|
+
if (opened) return opened;
|
|
15
|
+
const mine = new Promise((resolve, reject) => {
|
|
9
16
|
if (typeof indexedDB === "undefined") {
|
|
10
17
|
reject(new Error("[storage] IndexedDB is not available here"));
|
|
11
18
|
return;
|
|
@@ -19,25 +26,43 @@ function createIndexedDbAdapter({
|
|
|
19
26
|
request.onsuccess = () => {
|
|
20
27
|
const db = request.result;
|
|
21
28
|
if (!db.objectStoreNames.contains(store)) {
|
|
29
|
+
db.close();
|
|
22
30
|
reject(new Error(`[storage] IndexedDB database "${database}" has no store "${store}"`));
|
|
23
31
|
return;
|
|
24
32
|
}
|
|
33
|
+
connection = db;
|
|
34
|
+
db.onclose = () => forget(db);
|
|
35
|
+
db.onversionchange = () => {
|
|
36
|
+
db.close();
|
|
37
|
+
forget(db);
|
|
38
|
+
};
|
|
25
39
|
resolve(db);
|
|
26
40
|
};
|
|
27
41
|
request.onerror = () => reject(request.error);
|
|
28
|
-
request.onblocked = () => reject(new Error(`[storage] IndexedDB "${database}" is blocked`));
|
|
29
42
|
});
|
|
30
|
-
|
|
43
|
+
opened = mine;
|
|
44
|
+
mine.catch(() => {
|
|
45
|
+
if (opened === mine) opened = null;
|
|
46
|
+
});
|
|
47
|
+
return mine;
|
|
31
48
|
};
|
|
32
|
-
const run = (mode, body) =>
|
|
33
|
-
|
|
34
|
-
|
|
49
|
+
const run = async (mode, body) => {
|
|
50
|
+
let db = await open();
|
|
51
|
+
let tx;
|
|
52
|
+
try {
|
|
53
|
+
tx = db.transaction(store, mode);
|
|
54
|
+
} catch {
|
|
55
|
+
forget(db);
|
|
56
|
+
db = await open();
|
|
57
|
+
tx = db.transaction(store, mode);
|
|
58
|
+
}
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
35
60
|
const result = body(tx.objectStore(store));
|
|
36
61
|
tx.oncomplete = () => resolve(result());
|
|
37
62
|
tx.onerror = () => reject(tx.error);
|
|
38
63
|
tx.onabort = () => reject(tx.error);
|
|
39
|
-
})
|
|
40
|
-
|
|
64
|
+
});
|
|
65
|
+
};
|
|
41
66
|
let channel = null;
|
|
42
67
|
const getChannel = () => {
|
|
43
68
|
if (typeof BroadcastChannel === "undefined") return null;
|
|
@@ -125,17 +150,32 @@ function isJsonSafe(value, seen = /* @__PURE__ */ new Set()) {
|
|
|
125
150
|
// src/webStorage.ts
|
|
126
151
|
function webStorageAdapter(area, name, crossTab) {
|
|
127
152
|
const warned = /* @__PURE__ */ new Set();
|
|
128
|
-
const
|
|
153
|
+
const keysNow = (prefix) => {
|
|
129
154
|
const store = area();
|
|
130
155
|
const out = [];
|
|
131
156
|
for (let i = 0; i < store.length; i++) {
|
|
132
157
|
const key = store.key(i);
|
|
133
|
-
if (key
|
|
158
|
+
if (key?.startsWith(prefix)) out.push(key);
|
|
159
|
+
}
|
|
160
|
+
return out;
|
|
161
|
+
};
|
|
162
|
+
const listNow = (prefix) => {
|
|
163
|
+
const store = area();
|
|
164
|
+
const out = [];
|
|
165
|
+
for (const key of keysNow(prefix)) {
|
|
134
166
|
const raw = store.getItem(key);
|
|
135
167
|
if (raw !== null) out.push([key, parse(raw)]);
|
|
136
168
|
}
|
|
137
169
|
return out;
|
|
138
170
|
};
|
|
171
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
172
|
+
const note = (key, held) => {
|
|
173
|
+
for (const sub of subscribers) {
|
|
174
|
+
if (!key.startsWith(sub.prefix)) continue;
|
|
175
|
+
if (held) sub.keys.add(key);
|
|
176
|
+
else sub.keys.delete(key);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
139
179
|
const adapter = {
|
|
140
180
|
get: async (key) => {
|
|
141
181
|
const raw = area().getItem(key);
|
|
@@ -151,22 +191,38 @@ function webStorageAdapter(area, name, crossTab) {
|
|
|
151
191
|
);
|
|
152
192
|
}
|
|
153
193
|
area().setItem(key, JSON.stringify(value));
|
|
194
|
+
note(key, true);
|
|
154
195
|
},
|
|
155
196
|
delete: async (key) => {
|
|
156
197
|
area().removeItem(key);
|
|
198
|
+
note(key, false);
|
|
157
199
|
}
|
|
158
200
|
};
|
|
159
201
|
if (crossTab) {
|
|
160
202
|
adapter.subscribe = (prefix, on) => {
|
|
161
203
|
if (typeof window === "undefined") return () => {
|
|
162
204
|
};
|
|
205
|
+
const sub = { prefix, keys: new Set(keysNow(prefix)) };
|
|
206
|
+
subscribers.add(sub);
|
|
163
207
|
const handler = (e) => {
|
|
164
|
-
|
|
165
|
-
if (e.storageArea !==
|
|
208
|
+
const store = area();
|
|
209
|
+
if (e.storageArea !== store) return;
|
|
210
|
+
if (e.key === null) {
|
|
211
|
+
const gone = [...sub.keys].filter((key) => store.getItem(key) === null);
|
|
212
|
+
for (const key of gone) sub.keys.delete(key);
|
|
213
|
+
if (gone.length > 0) on(gone.map((key) => [key, void 0]));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (!e.key.startsWith(prefix)) return;
|
|
217
|
+
if (e.newValue === null) sub.keys.delete(e.key);
|
|
218
|
+
else sub.keys.add(e.key);
|
|
166
219
|
on([[e.key, e.newValue === null ? void 0 : parse(e.newValue)]]);
|
|
167
220
|
};
|
|
168
221
|
window.addEventListener("storage", handler);
|
|
169
|
-
return () =>
|
|
222
|
+
return () => {
|
|
223
|
+
subscribers.delete(sub);
|
|
224
|
+
window.removeEventListener("storage", handler);
|
|
225
|
+
};
|
|
170
226
|
};
|
|
171
227
|
}
|
|
172
228
|
return adapter;
|
|
@@ -269,21 +325,28 @@ function decodeUrlHash(hash) {
|
|
|
269
325
|
// src/urlHashAdapter.ts
|
|
270
326
|
var URL_HASH_GUARD = typeof window !== "undefined";
|
|
271
327
|
function readHashMap() {
|
|
272
|
-
|
|
328
|
+
const map = /* @__PURE__ */ Object.create(null);
|
|
329
|
+
if (!URL_HASH_GUARD) return map;
|
|
273
330
|
const raw = decodeUrlHash(window.location.hash.replace(/^#/, ""));
|
|
274
|
-
if (!raw) return
|
|
331
|
+
if (!raw) return map;
|
|
332
|
+
let parsed;
|
|
275
333
|
try {
|
|
276
|
-
|
|
334
|
+
parsed = JSON.parse(raw);
|
|
277
335
|
} catch {
|
|
278
|
-
return
|
|
336
|
+
return map;
|
|
279
337
|
}
|
|
338
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return map;
|
|
339
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
340
|
+
if (typeof value === "string") map[key] = value;
|
|
341
|
+
}
|
|
342
|
+
return map;
|
|
280
343
|
}
|
|
281
344
|
var hashSubscribers = /* @__PURE__ */ new Set();
|
|
282
345
|
function writeHashMap(map) {
|
|
283
346
|
if (!URL_HASH_GUARD) return;
|
|
284
347
|
const encoded = encodeUrlHash(JSON.stringify(map));
|
|
285
|
-
window.history.replaceState(
|
|
286
|
-
for (const sub of hashSubscribers) sub.last =
|
|
348
|
+
window.history.replaceState(window.history.state, "", `#${encoded}`);
|
|
349
|
+
for (const sub of hashSubscribers) sub.last = Object.assign(/* @__PURE__ */ Object.create(null), map);
|
|
287
350
|
}
|
|
288
351
|
var urlHashAdapter = {
|
|
289
352
|
get: async (key) => {
|
|
@@ -328,21 +391,45 @@ var urlHashAdapter = {
|
|
|
328
391
|
// src/records.ts
|
|
329
392
|
var DELETED = /* @__PURE__ */ Symbol("deleted");
|
|
330
393
|
function createRecordCache(options) {
|
|
394
|
+
return buildCache(options, false).cache;
|
|
395
|
+
}
|
|
396
|
+
function buildCache(options, startUnread) {
|
|
331
397
|
const { storage, prefix } = options;
|
|
332
398
|
const debounceMs = options.debounceMs ?? 300;
|
|
333
399
|
const maxWaitMs = options.maxWaitMs ?? 1e3;
|
|
400
|
+
const retryMs = options.retryMs ?? 1e3;
|
|
401
|
+
const retryMaxMs = options.retryMaxMs ?? 3e4;
|
|
334
402
|
const values = new Map(options.initial ?? []);
|
|
335
403
|
const queued = /* @__PURE__ */ new Map();
|
|
404
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
336
405
|
const listeners = /* @__PURE__ */ new Set();
|
|
337
|
-
|
|
406
|
+
const overruledListeners = /* @__PURE__ */ new Set();
|
|
407
|
+
let unread = startUnread;
|
|
408
|
+
let stopped = !(options.writable ?? true);
|
|
409
|
+
const writable = () => !unread && !stopped;
|
|
338
410
|
let closed = false;
|
|
339
411
|
let timer = null;
|
|
340
412
|
let firstQueuedAt = null;
|
|
413
|
+
let retryTimer = null;
|
|
414
|
+
const firstRetryWait = retryMs === false ? 0 : retryMs;
|
|
415
|
+
let retryWait = firstRetryWait;
|
|
416
|
+
let reading = null;
|
|
417
|
+
let warned = false;
|
|
418
|
+
let writeRetryTimer = null;
|
|
419
|
+
let writeRetryWait = retryWait;
|
|
420
|
+
let writeWarned = false;
|
|
421
|
+
let sending = null;
|
|
422
|
+
let landed = () => {
|
|
423
|
+
};
|
|
424
|
+
const hasRead = new Promise((resolve) => {
|
|
425
|
+
if (startUnread) landed = resolve;
|
|
426
|
+
else resolve();
|
|
427
|
+
});
|
|
341
428
|
const emit = (changes) => {
|
|
342
429
|
for (const listener of [...listeners]) listener(changes);
|
|
343
430
|
};
|
|
344
431
|
const schedule = () => {
|
|
345
|
-
if (!writable || closed) return;
|
|
432
|
+
if (!writable() || closed) return;
|
|
346
433
|
const now = Date.now();
|
|
347
434
|
firstQueuedAt ??= now;
|
|
348
435
|
if (timer) clearTimeout(timer);
|
|
@@ -352,15 +439,25 @@ function createRecordCache(options) {
|
|
|
352
439
|
void flush();
|
|
353
440
|
}, wait);
|
|
354
441
|
};
|
|
355
|
-
|
|
442
|
+
function flush() {
|
|
356
443
|
if (timer) clearTimeout(timer);
|
|
357
444
|
timer = null;
|
|
358
445
|
firstQueuedAt = null;
|
|
446
|
+
const mine = sending ? sending.then(send) : send();
|
|
447
|
+
sending = mine;
|
|
448
|
+
void mine.finally(() => {
|
|
449
|
+
if (sending === mine) sending = null;
|
|
450
|
+
});
|
|
451
|
+
return mine;
|
|
452
|
+
}
|
|
453
|
+
async function send() {
|
|
359
454
|
if (queued.size === 0) return true;
|
|
360
|
-
if (!writable) return false;
|
|
455
|
+
if (!writable()) return false;
|
|
456
|
+
stopRetryingWrites();
|
|
361
457
|
const batch = [...queued];
|
|
362
458
|
queued.clear();
|
|
363
|
-
const
|
|
459
|
+
for (const [name] of batch) inFlight.set(name, (inFlight.get(name) ?? 0) + 1);
|
|
460
|
+
const landed2 = await Promise.all(
|
|
364
461
|
batch.map(async ([name, value]) => {
|
|
365
462
|
const key = prefix + name;
|
|
366
463
|
try {
|
|
@@ -368,19 +465,50 @@ function createRecordCache(options) {
|
|
|
368
465
|
else await storage.set(key, value);
|
|
369
466
|
return true;
|
|
370
467
|
} catch (error) {
|
|
371
|
-
|
|
468
|
+
if (!writeWarned) {
|
|
469
|
+
writeWarned = true;
|
|
470
|
+
console.warn(`[storage] could not write "${key}"; trying again`, error);
|
|
471
|
+
}
|
|
472
|
+
if (!queued.has(name) && !stopped) queued.set(name, value);
|
|
372
473
|
return false;
|
|
474
|
+
} finally {
|
|
475
|
+
const count = inFlight.get(name) ?? 1;
|
|
476
|
+
if (count > 1) inFlight.set(name, count - 1);
|
|
477
|
+
else inFlight.delete(name);
|
|
373
478
|
}
|
|
374
479
|
})
|
|
375
480
|
);
|
|
376
|
-
|
|
481
|
+
if (landed2.every(Boolean)) {
|
|
482
|
+
writeWarned = false;
|
|
483
|
+
writeRetryWait = firstRetryWait;
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
retryWrites();
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
function stopRetryingWrites() {
|
|
490
|
+
if (writeRetryTimer) clearTimeout(writeRetryTimer);
|
|
491
|
+
writeRetryTimer = null;
|
|
492
|
+
}
|
|
493
|
+
function retryWrites() {
|
|
494
|
+
stopRetryingWrites();
|
|
495
|
+
if (retryMs === false || closed || stopped || queued.size === 0) return;
|
|
496
|
+
writeRetryTimer = setTimeout(() => {
|
|
497
|
+
writeRetryTimer = null;
|
|
498
|
+
void flush();
|
|
499
|
+
}, writeRetryWait);
|
|
500
|
+
writeRetryWait = Math.min(writeRetryWait * 2, retryMaxMs);
|
|
377
501
|
}
|
|
378
502
|
const applyRemote = (changes) => {
|
|
379
503
|
const applied = [];
|
|
504
|
+
const overruled = [];
|
|
380
505
|
for (const [key, value] of changes) {
|
|
381
506
|
if (!key.startsWith(prefix)) continue;
|
|
382
507
|
const name = key.slice(prefix.length);
|
|
383
|
-
if (queued.has(name))
|
|
508
|
+
if (queued.has(name) || inFlight.has(name)) {
|
|
509
|
+
overruled.push([name, value]);
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
384
512
|
if (value === void 0) {
|
|
385
513
|
if (!values.has(name)) continue;
|
|
386
514
|
values.delete(name);
|
|
@@ -389,9 +517,60 @@ function createRecordCache(options) {
|
|
|
389
517
|
}
|
|
390
518
|
applied.push({ name, value, origin: "remote" });
|
|
391
519
|
}
|
|
520
|
+
if (overruled.length > 0) for (const listener of [...overruledListeners]) listener(overruled);
|
|
392
521
|
if (applied.length > 0) emit(applied);
|
|
393
522
|
};
|
|
394
|
-
|
|
523
|
+
let unsubscribe = writable() ? storage.subscribe?.(prefix, applyRemote) : void 0;
|
|
524
|
+
const stopRetrying = () => {
|
|
525
|
+
if (retryTimer) clearTimeout(retryTimer);
|
|
526
|
+
retryTimer = null;
|
|
527
|
+
};
|
|
528
|
+
const failed = (error) => {
|
|
529
|
+
if (!warned) {
|
|
530
|
+
warned = true;
|
|
531
|
+
console.warn(
|
|
532
|
+
`[storage] could not read "${prefix}"; opening empty and holding writes until a read lands`,
|
|
533
|
+
error
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
if (retryMs === false || closed || stopped) return;
|
|
537
|
+
retryTimer = setTimeout(() => {
|
|
538
|
+
retryTimer = null;
|
|
539
|
+
void read();
|
|
540
|
+
}, retryWait);
|
|
541
|
+
retryWait = Math.min(retryWait * 2, retryMaxMs);
|
|
542
|
+
};
|
|
543
|
+
async function attempt() {
|
|
544
|
+
stopRetrying();
|
|
545
|
+
const early = [];
|
|
546
|
+
const stopEarly = storage.subscribe?.(prefix, (c) => early.push(...c));
|
|
547
|
+
let listed;
|
|
548
|
+
try {
|
|
549
|
+
listed = await storage.list(prefix);
|
|
550
|
+
} catch (error) {
|
|
551
|
+
stopEarly?.();
|
|
552
|
+
failed(error);
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
555
|
+
if (closed || stopped) {
|
|
556
|
+
stopEarly?.();
|
|
557
|
+
return false;
|
|
558
|
+
}
|
|
559
|
+
unread = false;
|
|
560
|
+
unsubscribe = storage.subscribe?.(prefix, applyRemote);
|
|
561
|
+
stopEarly?.();
|
|
562
|
+
applyRemote([...listed, ...early]);
|
|
563
|
+
if (queued.size > 0) schedule();
|
|
564
|
+
landed();
|
|
565
|
+
return true;
|
|
566
|
+
}
|
|
567
|
+
function read() {
|
|
568
|
+
if (!unread || stopped || closed) return Promise.resolve(!unread);
|
|
569
|
+
reading ??= attempt().finally(() => {
|
|
570
|
+
reading = null;
|
|
571
|
+
});
|
|
572
|
+
return reading;
|
|
573
|
+
}
|
|
395
574
|
const onPageHide = () => {
|
|
396
575
|
void flush();
|
|
397
576
|
};
|
|
@@ -402,27 +581,29 @@ function createRecordCache(options) {
|
|
|
402
581
|
window.addEventListener("pagehide", onPageHide);
|
|
403
582
|
document.addEventListener("visibilitychange", onVisibility);
|
|
404
583
|
}
|
|
584
|
+
const remove = (name) => {
|
|
585
|
+
if (!unread && !values.has(name) && !queued.has(name)) return;
|
|
586
|
+
values.delete(name);
|
|
587
|
+
queued.set(name, DELETED);
|
|
588
|
+
emit([{ name, value: void 0, origin: "local" }]);
|
|
589
|
+
schedule();
|
|
590
|
+
};
|
|
405
591
|
const cache = {
|
|
406
592
|
prefix,
|
|
407
593
|
get writable() {
|
|
408
|
-
return writable;
|
|
594
|
+
return writable();
|
|
409
595
|
},
|
|
410
596
|
has: (name) => values.has(name),
|
|
411
597
|
get: (name) => values.get(name),
|
|
412
598
|
entries: (namePrefix = "") => [...values].filter(([name]) => name.startsWith(namePrefix)),
|
|
413
599
|
set: (name, value) => {
|
|
600
|
+
if (value === void 0) return remove(name);
|
|
414
601
|
values.set(name, value);
|
|
415
602
|
queued.set(name, value);
|
|
416
603
|
emit([{ name, value, origin: "local" }]);
|
|
417
604
|
schedule();
|
|
418
605
|
},
|
|
419
|
-
delete:
|
|
420
|
-
if (!values.has(name) && !queued.has(name)) return;
|
|
421
|
-
values.delete(name);
|
|
422
|
-
queued.set(name, DELETED);
|
|
423
|
-
emit([{ name, value: void 0, origin: "local" }]);
|
|
424
|
-
schedule();
|
|
425
|
-
},
|
|
606
|
+
delete: remove,
|
|
426
607
|
subscribe: (listener) => {
|
|
427
608
|
listeners.add(listener);
|
|
428
609
|
return () => {
|
|
@@ -430,56 +611,44 @@ function createRecordCache(options) {
|
|
|
430
611
|
};
|
|
431
612
|
},
|
|
432
613
|
flush,
|
|
614
|
+
read,
|
|
615
|
+
whenRead: () => hasRead,
|
|
433
616
|
close: async () => {
|
|
434
617
|
if (closed) return;
|
|
618
|
+
stopRetrying();
|
|
435
619
|
await flush();
|
|
436
620
|
closed = true;
|
|
621
|
+
stopRetryingWrites();
|
|
437
622
|
unsubscribe?.();
|
|
438
623
|
if (typeof window !== "undefined") {
|
|
439
624
|
window.removeEventListener("pagehide", onPageHide);
|
|
440
625
|
document.removeEventListener("visibilitychange", onVisibility);
|
|
441
626
|
}
|
|
442
627
|
},
|
|
628
|
+
onOverruled: (listener) => {
|
|
629
|
+
overruledListeners.add(listener);
|
|
630
|
+
return () => {
|
|
631
|
+
overruledListeners.delete(listener);
|
|
632
|
+
};
|
|
633
|
+
},
|
|
443
634
|
seed: (entries) => {
|
|
444
635
|
for (const [name, value] of entries) values.set(name, value);
|
|
445
636
|
},
|
|
446
637
|
stopWriting: () => {
|
|
447
|
-
|
|
638
|
+
stopped = true;
|
|
448
639
|
queued.clear();
|
|
449
640
|
if (timer) clearTimeout(timer);
|
|
450
641
|
timer = null;
|
|
642
|
+
stopRetrying();
|
|
643
|
+
stopRetryingWrites();
|
|
451
644
|
unsubscribe?.();
|
|
452
|
-
}
|
|
453
|
-
applyRemote
|
|
645
|
+
}
|
|
454
646
|
};
|
|
455
|
-
return cache;
|
|
456
|
-
}
|
|
457
|
-
function stripPrefix(prefix, rows) {
|
|
458
|
-
return rows.map(([key, value]) => [key.slice(prefix.length), value]);
|
|
459
|
-
}
|
|
460
|
-
function unreadable(options, error) {
|
|
461
|
-
console.warn(
|
|
462
|
-
`[storage] could not read "${options.prefix}"; opening empty and not persisting`,
|
|
463
|
-
error
|
|
464
|
-
);
|
|
465
|
-
return createRecordCache({ ...options, writable: false });
|
|
647
|
+
return { cache, failed };
|
|
466
648
|
}
|
|
467
649
|
async function openRecords(options) {
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
let listed;
|
|
471
|
-
try {
|
|
472
|
-
listed = await options.storage.list(options.prefix);
|
|
473
|
-
} catch (error) {
|
|
474
|
-
stopEarly?.();
|
|
475
|
-
return unreadable(options, error);
|
|
476
|
-
}
|
|
477
|
-
const cache = createRecordCache({
|
|
478
|
-
...options,
|
|
479
|
-
initial: stripPrefix(options.prefix, listed)
|
|
480
|
-
});
|
|
481
|
-
stopEarly?.();
|
|
482
|
-
cache.applyRemote(early);
|
|
650
|
+
const { cache } = buildCache(options, true);
|
|
651
|
+
await cache.read();
|
|
483
652
|
return cache;
|
|
484
653
|
}
|
|
485
654
|
function openRecordsSync(options) {
|
|
@@ -487,12 +656,15 @@ function openRecordsSync(options) {
|
|
|
487
656
|
try {
|
|
488
657
|
listed = options.storage.listSync(options.prefix);
|
|
489
658
|
} catch (error) {
|
|
490
|
-
|
|
659
|
+
const { cache, failed } = buildCache(options, true);
|
|
660
|
+
failed(error);
|
|
661
|
+
return cache;
|
|
491
662
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
663
|
+
const initial = listed.map(([key, value]) => [
|
|
664
|
+
key.slice(options.prefix.length),
|
|
665
|
+
value
|
|
666
|
+
]);
|
|
667
|
+
return createRecordCache({ ...options, initial });
|
|
496
668
|
}
|
|
497
669
|
|
|
498
670
|
export { createDefaultStorage, createIndexedDbAdapter, createMemoryAdapter, createRecordCache, decodeUrlHash, defaultStorage, encodeUrlHash, fallbackStorage, indexedDbAdapter, localStorageAdapter, noneAdapter, openRecords, openRecordsSync, sessionStorageAdapter, urlHashAdapter };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/indexedDb.ts","../src/json.ts","../src/webStorage.ts","../src/defaultStorage.ts","../src/memory.ts","../src/urlHash.ts","../src/urlHashAdapter.ts","../src/records.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { StorageAdapter, StorageChange } from './types';\n\n/** Options for `createIndexedDbAdapter`. */\nexport interface IndexedDbAdapterOptions {\n /** Default `'weasel'`. */\n database?: string;\n /** Default `'records'`. One object store per database: a store added to a\n * database that already exists is not created. */\n store?: string;\n}\n\ninterface NodeChannel extends BroadcastChannel {\n unref?: () => void;\n}\n\n/** Persist to IndexedDB, which stores structured-clone values natively — typed\n * arrays, `Map`s, `Blob`s — and holds far more than localStorage. Other tabs\n * hear writes through a `BroadcastChannel`. The database opens on first use. */\nexport function createIndexedDbAdapter({\n database = 'weasel',\n store = 'records',\n}: IndexedDbAdapterOptions = {}): StorageAdapter {\n let opened: Promise<IDBDatabase> | null = null;\n const open = (): Promise<IDBDatabase> => {\n opened ??= new Promise((resolve, reject) => {\n if (typeof indexedDB === 'undefined') {\n reject(new Error('[storage] IndexedDB is not available here'));\n return;\n }\n const request = indexedDB.open(database, 1);\n request.onupgradeneeded = () => {\n if (!request.result.objectStoreNames.contains(store)) {\n request.result.createObjectStore(store);\n }\n };\n request.onsuccess = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(store)) {\n reject(new Error(`[storage] IndexedDB database \"${database}\" has no store \"${store}\"`));\n return;\n }\n resolve(db);\n };\n request.onerror = () => reject(request.error);\n request.onblocked = () => reject(new Error(`[storage] IndexedDB \"${database}\" is blocked`));\n });\n return opened;\n };\n\n const run = <T>(mode: IDBTransactionMode, body: (s: IDBObjectStore) => () => T): Promise<T> =>\n open().then(\n (db) =>\n new Promise<T>((resolve, reject) => {\n const tx = db.transaction(store, mode);\n const result = body(tx.objectStore(store));\n tx.oncomplete = () => resolve(result());\n tx.onerror = () => reject(tx.error);\n tx.onabort = () => reject(tx.error);\n }),\n );\n\n let channel: NodeChannel | null = null;\n const getChannel = (): NodeChannel | null => {\n if (typeof BroadcastChannel === 'undefined') return null;\n if (!channel) {\n channel = new BroadcastChannel(`weasel-storage:${database}:${store}`) as NodeChannel;\n // Node keeps its event loop alive for an open channel; a browser has no unref.\n channel.unref?.();\n }\n return channel;\n };\n const announce = (changes: StorageChange[]): void => {\n getChannel()?.postMessage(changes);\n };\n\n const adapter: StorageAdapter = {\n get: (key) =>\n run('readonly', (s) => {\n const request = s.get(key);\n return () => request.result as unknown;\n }),\n list: (prefix) =>\n run('readonly', (s) => {\n const range = IDBKeyRange.bound(prefix, `${prefix}`);\n const keys = s.getAllKeys(range);\n const values = s.getAll(range);\n return () =>\n keys.result.map((key, i): [string, unknown] => [String(key), values.result[i]]);\n }),\n set: async (key, value) => {\n await run('readwrite', (s) => {\n s.put(value, key);\n return () => undefined;\n });\n announce([[key, value]]);\n },\n delete: async (key) => {\n await run('readwrite', (s) => {\n s.delete(key);\n return () => undefined;\n });\n announce([[key, undefined]]);\n },\n };\n if (typeof BroadcastChannel !== 'undefined') {\n adapter.subscribe = (prefix, on) => {\n const ch = getChannel();\n if (!ch) return () => {};\n const handler = (e: MessageEvent<StorageChange[]>): void => {\n const mine = e.data.filter(([key]) => key.startsWith(prefix));\n if (mine.length > 0) on(mine);\n };\n ch.addEventListener('message', handler);\n return () => ch.removeEventListener('message', handler);\n };\n }\n return adapter;\n}\n\n/** IndexedDB under the default database and store. */\nexport const indexedDbAdapter: StorageAdapter = createIndexedDbAdapter();\n","/** Raw strings written before records existed come back as the string they\n * are. */\nexport function parse(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n}\n\n/** Whether `JSON.stringify` would keep `value` exactly. An object key holding\n * `undefined` counts as kept: JSON drops the key, and reading back an absent\n * key yields the same `undefined`. */\nexport function isJsonSafe(value: unknown, seen: Set<object> = new Set()): boolean {\n if (value === null) return true;\n switch (typeof value) {\n case 'string':\n case 'boolean':\n return true;\n case 'number':\n return Number.isFinite(value);\n case 'object': {\n if (seen.has(value)) return false;\n seen.add(value);\n const proto = Object.getPrototypeOf(value);\n const ok = Array.isArray(value)\n ? value.every((v) => isJsonSafe(v, seen))\n : (proto === Object.prototype || proto === null) &&\n Object.values(value).every((v) => v === undefined || isJsonSafe(v, seen));\n seen.delete(value);\n return ok;\n }\n default:\n return false;\n }\n}\n","import { isJsonSafe, parse } from './json';\nimport type { SyncStorageAdapter } from './types';\n\nfunction webStorageAdapter(area: () => Storage, name: string, crossTab: boolean): SyncStorageAdapter {\n const warned = new Set<string>();\n const listNow = (prefix: string): [string, unknown][] => {\n const store = area();\n const out: [string, unknown][] = [];\n for (let i = 0; i < store.length; i++) {\n const key = store.key(i);\n if (key === null || !key.startsWith(prefix)) continue;\n const raw = store.getItem(key);\n if (raw !== null) out.push([key, parse(raw)]);\n }\n return out;\n };\n const adapter: SyncStorageAdapter = {\n get: async (key) => {\n const raw = area().getItem(key);\n return raw === null ? undefined : parse(raw);\n },\n list: async (prefix) => listNow(prefix),\n listSync: listNow,\n set: async (key, value) => {\n if (!warned.has(key) && !isJsonSafe(value)) {\n warned.add(key);\n console.warn(\n `[storage] \"${key}\" holds a value JSON cannot keep exactly; ${name} stores what JSON keeps. Use IndexedDB for binary or non-JSON state.`,\n );\n }\n area().setItem(key, JSON.stringify(value));\n },\n delete: async (key) => {\n area().removeItem(key);\n },\n };\n if (crossTab) {\n adapter.subscribe = (prefix, on) => {\n if (typeof window === 'undefined') return () => {};\n const handler = (e: StorageEvent): void => {\n if (e.key === null || !e.key.startsWith(prefix)) return;\n if (e.storageArea !== area()) return;\n on([[e.key, e.newValue === null ? undefined : parse(e.newValue)]]);\n };\n window.addEventListener('storage', handler);\n return () => window.removeEventListener('storage', handler);\n };\n }\n return adapter;\n}\n\n/** Persist to `localStorage`, one JSON value per record. Survives a reload and\n * reaches other tabs through the `storage` event. */\nexport const localStorageAdapter: SyncStorageAdapter = webStorageAdapter(\n () => localStorage,\n 'localStorage',\n true,\n);\n\n/** Persist to `sessionStorage` — survives a reload but not a new tab. */\nexport const sessionStorageAdapter: SyncStorageAdapter = webStorageAdapter(\n () => sessionStorage,\n 'sessionStorage',\n false,\n);\n","import { indexedDbAdapter } from './indexedDb';\nimport type { StorageAdapter } from './types';\nimport { localStorageAdapter } from './webStorage';\n\n/** `preferred` if it opens, otherwise `fallback` — with a warning. */\nexport async function fallbackStorage(\n preferred: StorageAdapter,\n fallback: StorageAdapter = localStorageAdapter,\n label = 'preferred storage',\n): Promise<StorageAdapter> {\n try {\n await preferred.list(' ');\n return preferred;\n } catch (error) {\n console.warn(`[storage] ${label} would not open; using the fallback instead`, error);\n return fallback;\n }\n}\n\n/** A page-wide default: `preferred`, or localStorage where it will not open,\n * chosen on first call and kept until `resetDefaultStorage` (tests only). */\nexport function createDefaultStorage(\n preferred: StorageAdapter,\n label?: string,\n): { defaultStorage: () => Promise<StorageAdapter>; resetDefaultStorage: () => void } {\n let resolved: Promise<StorageAdapter> | null = null;\n return {\n defaultStorage: () => (resolved ??= fallbackStorage(preferred, localStorageAdapter, label)),\n resetDefaultStorage: () => {\n resolved = null;\n },\n };\n}\n\n/** IndexedDB under the default database, 'weasel', or localStorage where it\n * will not open. Resolved once per page. */\nexport const { defaultStorage, resetDefaultStorage } = createDefaultStorage(indexedDbAdapter, 'IndexedDB');\n","import type { StorageAdapter, StorageChange, SyncStorageAdapter } from './types';\n\ninterface MemorySubscriber {\n owner: StorageAdapter;\n prefix: string;\n on: (changes: StorageChange[]) => void;\n}\n\nconst memorySubscribers = new WeakMap<Map<string, unknown>, Set<MemorySubscriber>>();\n\n/** An in-memory store, discarded on reload. Values are copied in and out, as a\n * real substrate would. Adapters built over one shared `backing` hear each\n * other's writes — two tabs, for a test. */\nexport function createMemoryAdapter(backing: Map<string, unknown> = new Map()): SyncStorageAdapter {\n let subscribers = memorySubscribers.get(backing);\n if (!subscribers) {\n subscribers = new Set();\n memorySubscribers.set(backing, subscribers);\n }\n const peers = subscribers;\n const notify = (key: string, value: unknown): void => {\n for (const sub of peers) {\n if (sub.owner === adapter || !key.startsWith(sub.prefix)) continue;\n const copy = value === undefined ? undefined : structuredClone(value);\n queueMicrotask(() => sub.on([[key, copy]]));\n }\n };\n const listNow = (prefix: string): [string, unknown][] =>\n [...backing]\n .filter(([key]) => key.startsWith(prefix))\n .map(([key, value]): [string, unknown] => [key, structuredClone(value)]);\n const adapter: SyncStorageAdapter = {\n get: async (key) => (backing.has(key) ? structuredClone(backing.get(key)) : undefined),\n list: async (prefix) => listNow(prefix),\n listSync: listNow,\n set: async (key, value) => {\n const copy = structuredClone(value);\n backing.set(key, copy);\n notify(key, copy);\n },\n delete: async (key) => {\n backing.delete(key);\n notify(key, undefined);\n },\n subscribe: (prefix, on) => {\n const sub = { owner: adapter, prefix, on };\n peers.add(sub);\n return () => {\n peers.delete(sub);\n };\n },\n };\n return adapter;\n}\n\n/** Persists nothing and reads back nothing. */\nexport const noneAdapter: SyncStorageAdapter = {\n get: async () => undefined,\n list: async () => [],\n listSync: () => [],\n set: async () => {},\n delete: async () => {},\n};\n","/** Encode a string for the URL fragment. */\nexport function encodeUrlHash(value: string): string {\n return btoa(encodeURIComponent(value));\n}\n\n/** Decode a URL fragment written by `encodeUrlHash`, or `null` if it is\n * malformed. */\nexport function decodeUrlHash(hash: string): string | null {\n if (!hash) return null;\n try {\n return decodeURIComponent(atob(hash));\n } catch {\n return null;\n }\n}\n","import { parse } from './json';\nimport type { StorageAdapter, StorageChange } from './types';\nimport { decodeUrlHash, encodeUrlHash } from './urlHash';\n\nconst URL_HASH_GUARD = typeof window !== 'undefined';\n\nfunction readHashMap(): Record<string, string> {\n if (!URL_HASH_GUARD) return {};\n const raw = decodeUrlHash(window.location.hash.replace(/^#/, ''));\n if (!raw) return {};\n try {\n return JSON.parse(raw) as Record<string, string>;\n } catch {\n return {};\n }\n}\n\n/** What each live subscription last saw, refreshed on this page's own writes\n * so `hashchange` reports only somebody else's. */\nconst hashSubscribers = new Set<{ last: Record<string, string> }>();\n\nfunction writeHashMap(map: Record<string, string>): void {\n if (!URL_HASH_GUARD) return;\n const encoded = encodeUrlHash(JSON.stringify(map));\n window.history.replaceState(null, '', `#${encoded}`);\n for (const sub of hashSubscribers) sub.last = { ...map };\n}\n\n/** Persist into the URL fragment, so the page's link carries its state and can\n * be shared or bookmarked. Changes to the fragment made elsewhere — a pasted\n * link, back and forward — arrive through `hashchange`. */\nexport const urlHashAdapter: StorageAdapter = {\n get: async (key) => {\n const raw = readHashMap()[key];\n return raw === undefined ? undefined : parse(raw);\n },\n list: async (prefix) =>\n Object.entries(readHashMap())\n .filter(([key]) => key.startsWith(prefix))\n .map(([key, raw]): [string, unknown] => [key, parse(raw)]),\n set: async (key, value) => {\n const map = readHashMap();\n map[key] = JSON.stringify(value);\n writeHashMap(map);\n },\n delete: async (key) => {\n const map = readHashMap();\n delete map[key];\n writeHashMap(map);\n },\n subscribe: (prefix, on) => {\n if (!URL_HASH_GUARD) return () => {};\n const sub = { last: readHashMap() };\n hashSubscribers.add(sub);\n const handler = (): void => {\n const next = readHashMap();\n const changes: StorageChange[] = [];\n for (const key of new Set([...Object.keys(sub.last), ...Object.keys(next)])) {\n if (!key.startsWith(prefix) || sub.last[key] === next[key]) continue;\n const raw = next[key];\n changes.push([key, raw === undefined ? undefined : parse(raw)]);\n }\n sub.last = next;\n if (changes.length > 0) on(changes);\n };\n window.addEventListener('hashchange', handler);\n return () => {\n hashSubscribers.delete(sub);\n window.removeEventListener('hashchange', handler);\n };\n },\n};\n","import type { StorageAdapter, StorageChange, SyncStorageAdapter } from './types';\n\n/** One record changing, as a cache listener hears it. `value` is `undefined`\n * for a delete. `local` changes were made through this cache; `remote` ones\n * came from another writer. */\nexport interface RecordChange {\n name: string;\n value: unknown;\n origin: 'local' | 'remote';\n}\n\n/**\n * Every record under one prefix, held in memory: the only thing that talks to\n * a `StorageAdapter`. Names are keys with the prefix removed.\n *\n * Writes land in memory at once and reach storage on a debounce. The newest\n * write to a record wins: another writer's change to a record with a write\n * still queued here is ignored, since the queued write lands after it.\n */\nexport interface RecordCache {\n readonly prefix: string;\n /** False once writing is off — the records could not be read, or the\n * opener marked them read-only — so nothing here can overwrite them. */\n readonly writable: boolean;\n has(name: string): boolean;\n get(name: string): unknown;\n /** Every record whose name starts with `namePrefix`. */\n entries(namePrefix?: string): [string, unknown][];\n set(name: string, value: unknown): void;\n delete(name: string): void;\n subscribe(listener: (changes: RecordChange[]) => void): () => void;\n /** Send queued writes now. Resolves true when every queued write landed or\n * none was queued; false when a write threw (its value stays in memory) or\n * writing is off and writes were queued. */\n flush(): Promise<boolean>;\n /** Send queued writes and stop hearing other writers. */\n close(): Promise<void>;\n}\n\n/** The parts of a cache only its opener uses. */\nexport interface OwnedRecordCache extends RecordCache {\n /** Hold these records without writing them back. */\n seed(entries: Iterable<[string, unknown]>): void;\n stopWriting(): void;\n}\n\n/** Options for `createRecordCache` and `openRecords`. */\nexport interface RecordCacheOptions {\n storage: StorageAdapter;\n prefix: string;\n /** Trailing debounce on writes. Default 300 ms. */\n debounceMs?: number;\n /** The longest a write waits under continuous change. Default 1000 ms. */\n maxWaitMs?: number;\n}\n\nconst DELETED = Symbol('deleted');\n\n/** A cache holding `initial`, with nothing read from storage. */\nexport function createRecordCache(\n options: RecordCacheOptions & { initial?: Iterable<[string, unknown]>; writable?: boolean },\n): OwnedRecordCache {\n const { storage, prefix } = options;\n const debounceMs = options.debounceMs ?? 300;\n const maxWaitMs = options.maxWaitMs ?? 1000;\n const values = new Map<string, unknown>(options.initial ?? []);\n const queued = new Map<string, unknown>();\n const listeners = new Set<(changes: RecordChange[]) => void>();\n let writable = options.writable ?? true;\n let closed = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n let firstQueuedAt: number | null = null;\n\n const emit = (changes: RecordChange[]): void => {\n for (const listener of [...listeners]) listener(changes);\n };\n\n const schedule = (): void => {\n if (!writable || closed) return;\n const now = Date.now();\n firstQueuedAt ??= now;\n if (timer) clearTimeout(timer);\n const wait = Math.max(0, Math.min(debounceMs, firstQueuedAt + maxWaitMs - now));\n timer = setTimeout(() => {\n timer = null;\n void flush();\n }, wait);\n };\n\n async function flush(): Promise<boolean> {\n if (timer) clearTimeout(timer);\n timer = null;\n firstQueuedAt = null;\n if (queued.size === 0) return true;\n if (!writable) return false;\n const batch = [...queued];\n queued.clear();\n const landed = await Promise.all(\n batch.map(async ([name, value]) => {\n const key = prefix + name;\n try {\n if (value === DELETED) await storage.delete(key);\n else await storage.set(key, value);\n return true;\n } catch (error) {\n console.warn(`[storage] could not write \"${key}\"; keeping it in memory`, error);\n return false;\n }\n }),\n );\n return landed.every(Boolean);\n }\n\n const applyRemote = (changes: StorageChange[]): void => {\n const applied: RecordChange[] = [];\n for (const [key, value] of changes) {\n if (!key.startsWith(prefix)) continue;\n const name = key.slice(prefix.length);\n if (queued.has(name)) continue;\n if (value === undefined) {\n if (!values.has(name)) continue;\n values.delete(name);\n } else {\n values.set(name, value);\n }\n applied.push({ name, value, origin: 'remote' });\n }\n if (applied.length > 0) emit(applied);\n };\n\n const unsubscribe = writable ? storage.subscribe?.(prefix, applyRemote) : undefined;\n\n const onPageHide = (): void => {\n void flush();\n };\n const onVisibility = (): void => {\n if (document.visibilityState === 'hidden') void flush();\n };\n if (typeof window !== 'undefined') {\n window.addEventListener('pagehide', onPageHide);\n document.addEventListener('visibilitychange', onVisibility);\n }\n\n const cache: OwnedRecordCache & { applyRemote: typeof applyRemote } = {\n prefix,\n get writable() {\n return writable;\n },\n has: (name) => values.has(name),\n get: (name) => values.get(name),\n entries: (namePrefix = '') => [...values].filter(([name]) => name.startsWith(namePrefix)),\n set: (name, value) => {\n values.set(name, value);\n queued.set(name, value);\n emit([{ name, value, origin: 'local' }]);\n schedule();\n },\n delete: (name) => {\n if (!values.has(name) && !queued.has(name)) return;\n values.delete(name);\n queued.set(name, DELETED);\n emit([{ name, value: undefined, origin: 'local' }]);\n schedule();\n },\n subscribe: (listener) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n flush,\n close: async () => {\n if (closed) return;\n await flush();\n closed = true;\n unsubscribe?.();\n if (typeof window !== 'undefined') {\n window.removeEventListener('pagehide', onPageHide);\n document.removeEventListener('visibilitychange', onVisibility);\n }\n },\n seed: (entries) => {\n for (const [name, value] of entries) values.set(name, value);\n },\n stopWriting: () => {\n writable = false;\n queued.clear();\n if (timer) clearTimeout(timer);\n timer = null;\n unsubscribe?.();\n },\n applyRemote,\n };\n return cache;\n}\n\nfunction stripPrefix(prefix: string, rows: [string, unknown][]): [string, unknown][] {\n return rows.map(([key, value]): [string, unknown] => [key.slice(prefix.length), value]);\n}\n\nfunction unreadable(options: RecordCacheOptions, error: unknown): OwnedRecordCache {\n console.warn(\n `[storage] could not read \"${options.prefix}\"; opening empty and not persisting`,\n error,\n );\n return createRecordCache({ ...options, writable: false });\n}\n\n/** Read every record under `prefix` and hold them. A failed read opens the\n * cache empty with writing off, so it never overwrites what it could not\n * read. */\nexport async function openRecords(options: RecordCacheOptions): Promise<OwnedRecordCache> {\n // Listen before listing, so a change landing between the two is not lost.\n const early: StorageChange[] = [];\n const stopEarly = options.storage.subscribe?.(options.prefix, (c) => early.push(...c));\n let listed: [string, unknown][];\n try {\n listed = await options.storage.list(options.prefix);\n } catch (error) {\n stopEarly?.();\n return unreadable(options, error);\n }\n const cache = createRecordCache({\n ...options,\n initial: stripPrefix(options.prefix, listed),\n });\n stopEarly?.();\n (cache as unknown as { applyRemote: (c: StorageChange[]) => void }).applyRemote(early);\n return cache;\n}\n\n/** `openRecords` for an adapter that reads synchronously: the cache is ready\n * on return. A failed read opens it empty with writing off. */\nexport function openRecordsSync(\n options: RecordCacheOptions & { storage: SyncStorageAdapter },\n): OwnedRecordCache {\n let listed: [string, unknown][];\n try {\n listed = options.storage.listSync(options.prefix);\n } catch (error) {\n return unreadable(options, error);\n }\n return createRecordCache({\n ...options,\n initial: stripPrefix(options.prefix, listed),\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/indexedDb.ts","../src/json.ts","../src/webStorage.ts","../src/defaultStorage.ts","../src/memory.ts","../src/urlHash.ts","../src/urlHashAdapter.ts","../src/records.ts"],"names":["landed"],"mappings":"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","file":"index.js","sourcesContent":["import type { StorageAdapter, StorageChange } from './types';\n\n/** Options for `createIndexedDbAdapter`. */\nexport interface IndexedDbAdapterOptions {\n /** Default `'weasel'`. */\n database?: string;\n /** Default `'records'`. One object store per database: a store added to a\n * database that already exists is not created. */\n store?: string;\n}\n\ninterface NodeChannel extends BroadcastChannel {\n unref?: () => void;\n}\n\n/** Persist to IndexedDB, which stores structured-clone values natively — typed\n * arrays, `Map`s, `Blob`s — and holds far more than localStorage. Other tabs\n * hear writes through a `BroadcastChannel`. The database opens on first use. */\nexport function createIndexedDbAdapter({\n database = 'weasel',\n store = 'records',\n}: IndexedDbAdapterOptions = {}): StorageAdapter {\n let opened: Promise<IDBDatabase> | null = null;\n let connection: IDBDatabase | null = null;\n const forget = (db: IDBDatabase): void => {\n if (connection !== db) return;\n connection = null;\n opened = null;\n };\n const open = (): Promise<IDBDatabase> => {\n if (opened) return opened;\n const mine = new Promise<IDBDatabase>((resolve, reject) => {\n if (typeof indexedDB === 'undefined') {\n reject(new Error('[storage] IndexedDB is not available here'));\n return;\n }\n // No `onblocked`: a blocked open is still pending, and lands once the\n // connections in its way close.\n const request = indexedDB.open(database, 1);\n request.onupgradeneeded = () => {\n if (!request.result.objectStoreNames.contains(store)) {\n request.result.createObjectStore(store);\n }\n };\n request.onsuccess = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(store)) {\n db.close();\n reject(new Error(`[storage] IndexedDB database \"${database}\" has no store \"${store}\"`));\n return;\n }\n connection = db;\n // The browser closing the connection: site data cleared, or evicted.\n db.onclose = () => forget(db);\n // Another connection wants to delete or upgrade the database, and\n // waits for as long as this one stays open.\n db.onversionchange = () => {\n db.close();\n forget(db);\n };\n resolve(db);\n };\n request.onerror = () => reject(request.error);\n });\n opened = mine;\n // A failed open is not kept, so the next call tries again.\n mine.catch(() => {\n if (opened === mine) opened = null;\n });\n return mine;\n };\n\n const run = async <T>(\n mode: IDBTransactionMode,\n body: (s: IDBObjectStore) => () => T,\n ): Promise<T> => {\n let db = await open();\n let tx: IDBTransaction;\n try {\n tx = db.transaction(store, mode);\n } catch {\n // A connection that closed without saying so. One more, on a new one.\n forget(db);\n db = await open();\n tx = db.transaction(store, mode);\n }\n return new Promise<T>((resolve, reject) => {\n const result = body(tx.objectStore(store));\n tx.oncomplete = () => resolve(result());\n tx.onerror = () => reject(tx.error);\n tx.onabort = () => reject(tx.error);\n });\n };\n\n let channel: NodeChannel | null = null;\n const getChannel = (): NodeChannel | null => {\n if (typeof BroadcastChannel === 'undefined') return null;\n if (!channel) {\n channel = new BroadcastChannel(`weasel-storage:${database}:${store}`) as NodeChannel;\n // Node keeps its event loop alive for an open channel; a browser has no unref.\n channel.unref?.();\n }\n return channel;\n };\n const announce = (changes: StorageChange[]): void => {\n getChannel()?.postMessage(changes);\n };\n\n const adapter: StorageAdapter = {\n get: (key) =>\n run('readonly', (s) => {\n const request = s.get(key);\n return () => request.result as unknown;\n }),\n list: (prefix) =>\n run('readonly', (s) => {\n const range = IDBKeyRange.bound(prefix, `${prefix}`);\n const keys = s.getAllKeys(range);\n const values = s.getAll(range);\n return () =>\n keys.result.map((key, i): [string, unknown] => [String(key), values.result[i]]);\n }),\n set: async (key, value) => {\n await run('readwrite', (s) => {\n s.put(value, key);\n return () => undefined;\n });\n announce([[key, value]]);\n },\n delete: async (key) => {\n await run('readwrite', (s) => {\n s.delete(key);\n return () => undefined;\n });\n announce([[key, undefined]]);\n },\n };\n if (typeof BroadcastChannel !== 'undefined') {\n adapter.subscribe = (prefix, on) => {\n const ch = getChannel();\n if (!ch) return () => {};\n const handler = (e: MessageEvent<StorageChange[]>): void => {\n const mine = e.data.filter(([key]) => key.startsWith(prefix));\n if (mine.length > 0) on(mine);\n };\n ch.addEventListener('message', handler);\n return () => ch.removeEventListener('message', handler);\n };\n }\n return adapter;\n}\n\n/** IndexedDB under the default database and store. */\nexport const indexedDbAdapter: StorageAdapter = createIndexedDbAdapter();\n","/** Raw strings written before records existed come back as the string they\n * are. */\nexport function parse(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n}\n\n/** Whether `JSON.stringify` would keep `value` exactly. An object key holding\n * `undefined` counts as kept: JSON drops the key, and reading back an absent\n * key yields the same `undefined`. */\nexport function isJsonSafe(value: unknown, seen: Set<object> = new Set()): boolean {\n if (value === null) return true;\n switch (typeof value) {\n case 'string':\n case 'boolean':\n return true;\n case 'number':\n return Number.isFinite(value);\n case 'object': {\n if (seen.has(value)) return false;\n seen.add(value);\n const proto = Object.getPrototypeOf(value);\n const ok = Array.isArray(value)\n ? value.every((v) => isJsonSafe(v, seen))\n : (proto === Object.prototype || proto === null) &&\n Object.values(value).every((v) => v === undefined || isJsonSafe(v, seen));\n seen.delete(value);\n return ok;\n }\n default:\n return false;\n }\n}\n","import { isJsonSafe, parse } from './json';\nimport type { SyncStorageAdapter } from './types';\n\nfunction webStorageAdapter(area: () => Storage, name: string, crossTab: boolean): SyncStorageAdapter {\n const warned = new Set<string>();\n const keysNow = (prefix: string): string[] => {\n const store = area();\n const out: string[] = [];\n for (let i = 0; i < store.length; i++) {\n const key = store.key(i);\n if (key?.startsWith(prefix)) out.push(key);\n }\n return out;\n };\n const listNow = (prefix: string): [string, unknown][] => {\n const store = area();\n const out: [string, unknown][] = [];\n for (const key of keysNow(prefix)) {\n const raw = store.getItem(key);\n if (raw !== null) out.push([key, parse(raw)]);\n }\n return out;\n };\n // The keys each live subscription knows to exist. `clear()` in another tab\n // arrives as one event naming no key, so this is what says what went.\n const subscribers = new Set<{ prefix: string; keys: Set<string> }>();\n const note = (key: string, held: boolean): void => {\n for (const sub of subscribers) {\n if (!key.startsWith(sub.prefix)) continue;\n if (held) sub.keys.add(key);\n else sub.keys.delete(key);\n }\n };\n const adapter: SyncStorageAdapter = {\n get: async (key) => {\n const raw = area().getItem(key);\n return raw === null ? undefined : parse(raw);\n },\n list: async (prefix) => listNow(prefix),\n listSync: listNow,\n set: async (key, value) => {\n if (!warned.has(key) && !isJsonSafe(value)) {\n warned.add(key);\n console.warn(\n `[storage] \"${key}\" holds a value JSON cannot keep exactly; ${name} stores what JSON keeps. Use IndexedDB for binary or non-JSON state.`,\n );\n }\n area().setItem(key, JSON.stringify(value));\n note(key, true);\n },\n delete: async (key) => {\n area().removeItem(key);\n note(key, false);\n },\n };\n if (crossTab) {\n adapter.subscribe = (prefix, on) => {\n if (typeof window === 'undefined') return () => {};\n const sub = { prefix, keys: new Set(keysNow(prefix)) };\n subscribers.add(sub);\n const handler = (e: StorageEvent): void => {\n const store = area();\n if (e.storageArea !== store) return;\n if (e.key === null) {\n const gone = [...sub.keys].filter((key) => store.getItem(key) === null);\n for (const key of gone) sub.keys.delete(key);\n if (gone.length > 0) on(gone.map((key): [string, unknown] => [key, undefined]));\n return;\n }\n if (!e.key.startsWith(prefix)) return;\n if (e.newValue === null) sub.keys.delete(e.key);\n else sub.keys.add(e.key);\n on([[e.key, e.newValue === null ? undefined : parse(e.newValue)]]);\n };\n window.addEventListener('storage', handler);\n return () => {\n subscribers.delete(sub);\n window.removeEventListener('storage', handler);\n };\n };\n }\n return adapter;\n}\n\n/** Persist to `localStorage`, one JSON value per record. Survives a reload and\n * reaches other tabs through the `storage` event. */\nexport const localStorageAdapter: SyncStorageAdapter = webStorageAdapter(\n () => localStorage,\n 'localStorage',\n true,\n);\n\n/** Persist to `sessionStorage` — survives a reload but not a new tab. */\nexport const sessionStorageAdapter: SyncStorageAdapter = webStorageAdapter(\n () => sessionStorage,\n 'sessionStorage',\n false,\n);\n","import { indexedDbAdapter } from './indexedDb';\nimport type { StorageAdapter } from './types';\nimport { localStorageAdapter } from './webStorage';\n\n/** `preferred` if it opens, otherwise `fallback` — with a warning. */\nexport async function fallbackStorage(\n preferred: StorageAdapter,\n fallback: StorageAdapter = localStorageAdapter,\n label = 'preferred storage',\n): Promise<StorageAdapter> {\n try {\n await preferred.list(' ');\n return preferred;\n } catch (error) {\n console.warn(`[storage] ${label} would not open; using the fallback instead`, error);\n return fallback;\n }\n}\n\n/** A page-wide default: `preferred`, or localStorage where it will not open,\n * chosen on first call and kept until `resetDefaultStorage` (tests only). */\nexport function createDefaultStorage(\n preferred: StorageAdapter,\n label?: string,\n): { defaultStorage: () => Promise<StorageAdapter>; resetDefaultStorage: () => void } {\n let resolved: Promise<StorageAdapter> | null = null;\n return {\n defaultStorage: () => (resolved ??= fallbackStorage(preferred, localStorageAdapter, label)),\n resetDefaultStorage: () => {\n resolved = null;\n },\n };\n}\n\n/** IndexedDB under the default database, 'weasel', or localStorage where it\n * will not open. Resolved once per page. */\nexport const { defaultStorage, resetDefaultStorage } = createDefaultStorage(indexedDbAdapter, 'IndexedDB');\n","import type { StorageAdapter, StorageChange, SyncStorageAdapter } from './types';\n\ninterface MemorySubscriber {\n owner: StorageAdapter;\n prefix: string;\n on: (changes: StorageChange[]) => void;\n}\n\nconst memorySubscribers = new WeakMap<Map<string, unknown>, Set<MemorySubscriber>>();\n\n/** An in-memory store, discarded on reload. Values are copied in and out, as a\n * real substrate would. Adapters built over one shared `backing` hear each\n * other's writes — two tabs, for a test. */\nexport function createMemoryAdapter(backing: Map<string, unknown> = new Map()): SyncStorageAdapter {\n let subscribers = memorySubscribers.get(backing);\n if (!subscribers) {\n subscribers = new Set();\n memorySubscribers.set(backing, subscribers);\n }\n const peers = subscribers;\n const notify = (key: string, value: unknown): void => {\n for (const sub of peers) {\n if (sub.owner === adapter || !key.startsWith(sub.prefix)) continue;\n const copy = value === undefined ? undefined : structuredClone(value);\n queueMicrotask(() => sub.on([[key, copy]]));\n }\n };\n const listNow = (prefix: string): [string, unknown][] =>\n [...backing]\n .filter(([key]) => key.startsWith(prefix))\n .map(([key, value]): [string, unknown] => [key, structuredClone(value)]);\n const adapter: SyncStorageAdapter = {\n get: async (key) => (backing.has(key) ? structuredClone(backing.get(key)) : undefined),\n list: async (prefix) => listNow(prefix),\n listSync: listNow,\n set: async (key, value) => {\n const copy = structuredClone(value);\n backing.set(key, copy);\n notify(key, copy);\n },\n delete: async (key) => {\n backing.delete(key);\n notify(key, undefined);\n },\n subscribe: (prefix, on) => {\n const sub = { owner: adapter, prefix, on };\n peers.add(sub);\n return () => {\n peers.delete(sub);\n };\n },\n };\n return adapter;\n}\n\n/** Persists nothing and reads back nothing. */\nexport const noneAdapter: SyncStorageAdapter = {\n get: async () => undefined,\n list: async () => [],\n listSync: () => [],\n set: async () => {},\n delete: async () => {},\n};\n","/** Encode a string for the URL fragment. */\nexport function encodeUrlHash(value: string): string {\n return btoa(encodeURIComponent(value));\n}\n\n/** Decode a URL fragment written by `encodeUrlHash`, or `null` if it is\n * malformed. */\nexport function decodeUrlHash(hash: string): string | null {\n if (!hash) return null;\n try {\n return decodeURIComponent(atob(hash));\n } catch {\n return null;\n }\n}\n","import { parse } from './json';\nimport type { StorageAdapter, StorageChange } from './types';\nimport { decodeUrlHash, encodeUrlHash } from './urlHash';\n\nconst URL_HASH_GUARD = typeof window !== 'undefined';\n\n/** The fragment's records, each still the JSON text it is stored as. Anyone\n * can write a fragment, so whatever is not a map of strings reads as absent.\n * No prototype, so a record may be named for anything an object inherits. */\nfunction readHashMap(): Record<string, string> {\n const map = Object.create(null) as Record<string, string>;\n if (!URL_HASH_GUARD) return map;\n const raw = decodeUrlHash(window.location.hash.replace(/^#/, ''));\n if (!raw) return map;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return map;\n }\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return map;\n for (const [key, value] of Object.entries(parsed)) {\n if (typeof value === 'string') map[key] = value;\n }\n return map;\n}\n\n/** What each live subscription last saw, refreshed on this page's own writes\n * so `hashchange` reports only somebody else's. */\nconst hashSubscribers = new Set<{ last: Record<string, string> }>();\n\nfunction writeHashMap(map: Record<string, string>): void {\n if (!URL_HASH_GUARD) return;\n const encoded = encodeUrlHash(JSON.stringify(map));\n window.history.replaceState(window.history.state, '', `#${encoded}`);\n for (const sub of hashSubscribers) sub.last = Object.assign(Object.create(null), map);\n}\n\n/** Persist into the URL fragment, so the page's link carries its state and can\n * be shared or bookmarked. Changes to the fragment made elsewhere — a pasted\n * link, back and forward — arrive through `hashchange`. */\nexport const urlHashAdapter: StorageAdapter = {\n get: async (key) => {\n const raw = readHashMap()[key];\n return raw === undefined ? undefined : parse(raw);\n },\n list: async (prefix) =>\n Object.entries(readHashMap())\n .filter(([key]) => key.startsWith(prefix))\n .map(([key, raw]): [string, unknown] => [key, parse(raw)]),\n set: async (key, value) => {\n const map = readHashMap();\n map[key] = JSON.stringify(value);\n writeHashMap(map);\n },\n delete: async (key) => {\n const map = readHashMap();\n delete map[key];\n writeHashMap(map);\n },\n subscribe: (prefix, on) => {\n if (!URL_HASH_GUARD) return () => {};\n const sub = { last: readHashMap() };\n hashSubscribers.add(sub);\n const handler = (): void => {\n const next = readHashMap();\n const changes: StorageChange[] = [];\n for (const key of new Set([...Object.keys(sub.last), ...Object.keys(next)])) {\n if (!key.startsWith(prefix) || sub.last[key] === next[key]) continue;\n const raw = next[key];\n changes.push([key, raw === undefined ? undefined : parse(raw)]);\n }\n sub.last = next;\n if (changes.length > 0) on(changes);\n };\n window.addEventListener('hashchange', handler);\n return () => {\n hashSubscribers.delete(sub);\n window.removeEventListener('hashchange', handler);\n };\n },\n};\n","import type { StorageAdapter, StorageChange, SyncStorageAdapter } from './types';\n\n/** One record changing, as a cache listener hears it. `value` is `undefined`\n * for a delete. `local` changes were made through this cache; `remote` ones\n * came from another writer. */\nexport interface RecordChange {\n name: string;\n value: unknown;\n origin: 'local' | 'remote';\n}\n\n/**\n * Every record under one prefix, held in memory: the only thing that talks to\n * a `StorageAdapter`. Names are keys with the prefix removed.\n *\n * Writes land in memory at once and reach storage on a debounce. The newest\n * write to a record wins: another writer's change to a record with a write\n * still queued or on its way here is ignored, since that write lands after\n * it. A write storage rejects stays queued and is sent again on a backoff.\n */\nexport interface RecordCache {\n readonly prefix: string;\n /** False while writing is off, so nothing here can overwrite the records:\n * until a first read that failed lands on a later try, and for good once\n * the opener marks them read-only. */\n readonly writable: boolean;\n has(name: string): boolean;\n get(name: string): unknown;\n /** Every record whose name starts with `namePrefix`. */\n entries(namePrefix?: string): [string, unknown][];\n /** `undefined` is no value a record can hold: setting it deletes. */\n set(name: string, value: unknown): void;\n delete(name: string): void;\n subscribe(listener: (changes: RecordChange[]) => void): () => void;\n /** Send queued writes now. Resolves true when every queued write landed or\n * none was queued; false when a write threw (it stays queued) or writing\n * is off and writes were queued. */\n flush(): Promise<boolean>;\n /** Try a failed first read again now, ahead of the backoff. Resolves\n * whether the records have been read. */\n read(): Promise<boolean>;\n /** Resolves once the records have been read: at once for a cache that\n * opened with them, and when a later read lands for one whose first read\n * failed. Never resolves for a cache closed or stopped before that. */\n whenRead(): Promise<void>;\n /** Send queued writes, stop hearing other writers, and stop retrying. */\n close(): Promise<void>;\n}\n\n/** The parts of a cache only its opener uses. */\nexport interface OwnedRecordCache extends RecordCache {\n /** Hold these records without writing them back. */\n seed(entries: Iterable<[string, unknown]>): void;\n /** Hear another writer's changes this cache did not take, because its own\n * write to the record was queued or on its way: each a name and the value\n * the other writer set, `undefined` for a delete. */\n onOverruled(listener: (changes: [string, unknown][]) => void): () => void;\n stopWriting(): void;\n}\n\n/** Options for `createRecordCache` and `openRecords`. */\nexport interface RecordCacheOptions {\n storage: StorageAdapter;\n prefix: string;\n /** Trailing debounce on writes. Default 300 ms. */\n debounceMs?: number;\n /** The longest a write waits under continuous change. Default 1000 ms. */\n maxWaitMs?: number;\n /** The wait before a failed first read, or a write storage rejected, is\n * tried again, doubling after each failure. Default 1000 ms; `false` never\n * tries again on its own, leaving a rejected write for the next flush. */\n retryMs?: number | false;\n /** The longest wait between those tries. Default 30000 ms. */\n retryMaxMs?: number;\n}\n\ntype CreateOptions = RecordCacheOptions & {\n initial?: Iterable<[string, unknown]>;\n writable?: boolean;\n};\n\nconst DELETED = Symbol('deleted');\n\n/** A cache holding `initial`, with nothing read from storage. */\nexport function createRecordCache(options: CreateOptions): OwnedRecordCache {\n return buildCache(options, false).cache;\n}\n\n/** `startUnread` leaves the cache waiting on a first read: `cache.read()`\n * makes it, and `failed` reports one made elsewhere that threw. */\nfunction buildCache(\n options: CreateOptions,\n startUnread: boolean,\n): { cache: OwnedRecordCache; failed: (error: unknown) => void } {\n const { storage, prefix } = options;\n const debounceMs = options.debounceMs ?? 300;\n const maxWaitMs = options.maxWaitMs ?? 1000;\n const retryMs = options.retryMs ?? 1000;\n const retryMaxMs = options.retryMaxMs ?? 30_000;\n const values = new Map<string, unknown>(options.initial ?? []);\n const queued = new Map<string, unknown>();\n // How many writes to each name are on their way to storage.\n const inFlight = new Map<string, number>();\n const listeners = new Set<(changes: RecordChange[]) => void>();\n const overruledListeners = new Set<(changes: [string, unknown][]) => void>();\n // Two reasons writing is off. A read landing clears `unread`; nothing\n // clears `stopped`, which is the opener's choice.\n let unread = startUnread;\n let stopped = !(options.writable ?? true);\n const writable = (): boolean => !unread && !stopped;\n let closed = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n let firstQueuedAt: number | null = null;\n let retryTimer: ReturnType<typeof setTimeout> | null = null;\n const firstRetryWait = retryMs === false ? 0 : retryMs;\n let retryWait = firstRetryWait;\n let reading: Promise<boolean> | null = null;\n let warned = false;\n let writeRetryTimer: ReturnType<typeof setTimeout> | null = null;\n let writeRetryWait = retryWait;\n let writeWarned = false;\n let sending: Promise<boolean> | null = null;\n let landed = (): void => {};\n const hasRead = new Promise<void>((resolve) => {\n if (startUnread) landed = resolve;\n else resolve();\n });\n\n const emit = (changes: RecordChange[]): void => {\n for (const listener of [...listeners]) listener(changes);\n };\n\n const schedule = (): void => {\n if (!writable() || closed) return;\n const now = Date.now();\n firstQueuedAt ??= now;\n if (timer) clearTimeout(timer);\n const wait = Math.max(0, Math.min(debounceMs, firstQueuedAt + maxWaitMs - now));\n timer = setTimeout(() => {\n timer = null;\n void flush();\n }, wait);\n };\n\n // One batch is on its way at a time. A rejected write is back in the queue\n // before the next batch is taken, so a newer write to its record stands.\n function flush(): Promise<boolean> {\n if (timer) clearTimeout(timer);\n timer = null;\n firstQueuedAt = null;\n // With nothing on its way the batch leaves in this tick, which a flush\n // on `pagehide` depends on.\n const mine = sending ? sending.then(send) : send();\n sending = mine;\n void mine.finally(() => {\n if (sending === mine) sending = null;\n });\n return mine;\n }\n\n async function send(): Promise<boolean> {\n if (queued.size === 0) return true;\n if (!writable()) return false;\n stopRetryingWrites();\n const batch = [...queued];\n queued.clear();\n for (const [name] of batch) inFlight.set(name, (inFlight.get(name) ?? 0) + 1);\n const landed = await Promise.all(\n batch.map(async ([name, value]) => {\n const key = prefix + name;\n try {\n if (value === DELETED) await storage.delete(key);\n else await storage.set(key, value);\n return true;\n } catch (error) {\n if (!writeWarned) {\n writeWarned = true;\n console.warn(`[storage] could not write \"${key}\"; trying again`, error);\n }\n // A write queued since is newer, and stands.\n if (!queued.has(name) && !stopped) queued.set(name, value);\n return false;\n } finally {\n const count = inFlight.get(name) ?? 1;\n if (count > 1) inFlight.set(name, count - 1);\n else inFlight.delete(name);\n }\n }),\n );\n if (landed.every(Boolean)) {\n writeWarned = false;\n writeRetryWait = firstRetryWait;\n return true;\n }\n retryWrites();\n return false;\n }\n\n function stopRetryingWrites(): void {\n if (writeRetryTimer) clearTimeout(writeRetryTimer);\n writeRetryTimer = null;\n }\n\n function retryWrites(): void {\n stopRetryingWrites();\n if (retryMs === false || closed || stopped || queued.size === 0) return;\n writeRetryTimer = setTimeout(() => {\n writeRetryTimer = null;\n void flush();\n }, writeRetryWait);\n writeRetryWait = Math.min(writeRetryWait * 2, retryMaxMs);\n }\n\n const applyRemote = (changes: StorageChange[]): void => {\n const applied: RecordChange[] = [];\n const overruled: [string, unknown][] = [];\n for (const [key, value] of changes) {\n if (!key.startsWith(prefix)) continue;\n const name = key.slice(prefix.length);\n if (queued.has(name) || inFlight.has(name)) {\n overruled.push([name, value]);\n continue;\n }\n if (value === undefined) {\n if (!values.has(name)) continue;\n values.delete(name);\n } else {\n values.set(name, value);\n }\n applied.push({ name, value, origin: 'remote' });\n }\n if (overruled.length > 0) for (const listener of [...overruledListeners]) listener(overruled);\n if (applied.length > 0) emit(applied);\n };\n\n let unsubscribe = writable() ? storage.subscribe?.(prefix, applyRemote) : undefined;\n\n const stopRetrying = (): void => {\n if (retryTimer) clearTimeout(retryTimer);\n retryTimer = null;\n };\n\n const failed = (error: unknown): void => {\n if (!warned) {\n warned = true;\n console.warn(\n `[storage] could not read \"${prefix}\"; opening empty and holding writes until a read lands`,\n error,\n );\n }\n if (retryMs === false || closed || stopped) return;\n retryTimer = setTimeout(() => {\n retryTimer = null;\n void read();\n }, retryWait);\n retryWait = Math.min(retryWait * 2, retryMaxMs);\n };\n\n async function attempt(): Promise<boolean> {\n stopRetrying();\n // Listen before listing, so a change landing between the two is not lost.\n const early: StorageChange[] = [];\n const stopEarly = storage.subscribe?.(prefix, (c) => early.push(...c));\n let listed: StorageChange[];\n try {\n listed = await storage.list(prefix);\n } catch (error) {\n stopEarly?.();\n failed(error);\n return false;\n }\n if (closed || stopped) {\n stopEarly?.();\n return false;\n }\n unread = false;\n unsubscribe = storage.subscribe?.(prefix, applyRemote);\n stopEarly?.();\n // Skips every name with a write queued while unread, so those win.\n applyRemote([...listed, ...early]);\n if (queued.size > 0) schedule();\n landed();\n return true;\n }\n\n function read(): Promise<boolean> {\n if (!unread || stopped || closed) return Promise.resolve(!unread);\n reading ??= attempt().finally(() => {\n reading = null;\n });\n return reading;\n }\n\n const onPageHide = (): void => {\n void flush();\n };\n const onVisibility = (): void => {\n if (document.visibilityState === 'hidden') void flush();\n };\n if (typeof window !== 'undefined') {\n window.addEventListener('pagehide', onPageHide);\n document.addEventListener('visibilitychange', onVisibility);\n }\n\n const remove = (name: string): void => {\n // An unread cache cannot tell what storage holds, so it queues the delete.\n if (!unread && !values.has(name) && !queued.has(name)) return;\n values.delete(name);\n queued.set(name, DELETED);\n emit([{ name, value: undefined, origin: 'local' }]);\n schedule();\n };\n\n const cache: OwnedRecordCache = {\n prefix,\n get writable() {\n return writable();\n },\n has: (name) => values.has(name),\n get: (name) => values.get(name),\n entries: (namePrefix = '') => [...values].filter(([name]) => name.startsWith(namePrefix)),\n set: (name, value) => {\n if (value === undefined) return remove(name);\n values.set(name, value);\n queued.set(name, value);\n emit([{ name, value, origin: 'local' }]);\n schedule();\n },\n delete: remove,\n subscribe: (listener) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n flush,\n read,\n whenRead: () => hasRead,\n close: async () => {\n if (closed) return;\n stopRetrying();\n await flush();\n closed = true;\n stopRetryingWrites();\n unsubscribe?.();\n if (typeof window !== 'undefined') {\n window.removeEventListener('pagehide', onPageHide);\n document.removeEventListener('visibilitychange', onVisibility);\n }\n },\n onOverruled: (listener) => {\n overruledListeners.add(listener);\n return () => {\n overruledListeners.delete(listener);\n };\n },\n seed: (entries) => {\n for (const [name, value] of entries) values.set(name, value);\n },\n stopWriting: () => {\n stopped = true;\n queued.clear();\n if (timer) clearTimeout(timer);\n timer = null;\n stopRetrying();\n stopRetryingWrites();\n unsubscribe?.();\n },\n };\n return { cache, failed };\n}\n\n/** Read every record under `prefix` and hold them. A failed read opens the\n * cache empty with writing off, so it never overwrites what it could not\n * read, and tries again on a backoff. When a read lands, the cache holds it,\n * reports it as remote changes, and starts writing, sending first whatever\n * was written meanwhile. */\nexport async function openRecords(options: RecordCacheOptions): Promise<OwnedRecordCache> {\n const { cache } = buildCache(options, true);\n await cache.read();\n return cache;\n}\n\n/** `openRecords` for an adapter that reads synchronously: the cache is ready\n * on return. A failed read opens it empty with writing off, and it recovers\n * the way `openRecords` does. */\nexport function openRecordsSync(\n options: RecordCacheOptions & { storage: SyncStorageAdapter },\n): OwnedRecordCache {\n let listed: [string, unknown][];\n try {\n listed = options.storage.listSync(options.prefix);\n } catch (error) {\n const { cache, failed } = buildCache(options, true);\n failed(error);\n return cache;\n }\n const initial = listed.map(([key, value]): [string, unknown] => [\n key.slice(options.prefix.length),\n value,\n ]);\n return createRecordCache({ ...options, initial });\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weasel-js/storage",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.4",
|
|
4
4
|
"description": "Keyed async storage behind one adapter interface — IndexedDB, localStorage, the URL hash, memory — and an in-memory record cache that writes behind and hears other tabs. No React, no weasel deps.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|