@fairfox/polly 0.61.0 → 0.62.0
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/background/index.js +114 -69
- package/dist/src/background/index.js.map +5 -4
- package/dist/src/background/message-router.js +114 -69
- package/dist/src/background/message-router.js.map +5 -4
- package/dist/src/index.js +115 -69
- package/dist/src/index.js.map +5 -4
- package/dist/src/mesh.js +111 -101
- package/dist/src/mesh.js.map +5 -4
- package/dist/src/shared/adapters/index.js +114 -69
- package/dist/src/shared/adapters/index.js.map +5 -4
- package/dist/src/shared/lib/blob-cache.d.ts +1 -6
- package/dist/src/shared/lib/context-helpers.js +114 -69
- package/dist/src/shared/lib/context-helpers.js.map +5 -4
- package/dist/src/shared/lib/idb-helpers.d.ts +38 -0
- package/dist/src/shared/lib/message-bus.js +114 -69
- package/dist/src/shared/lib/message-bus.js.map +5 -4
- package/dist/src/shared/lib/resource.js +109 -65
- package/dist/src/shared/lib/resource.js.map +5 -4
- package/dist/src/shared/lib/state.js +109 -65
- package/dist/src/shared/lib/state.js.map +5 -4
- package/dist/src/shared/lib/storage-adapter.d.ts +3 -3
- package/dist/src/shared/state/app-state.js +109 -65
- package/dist/src/shared/state/app-state.js.map +5 -4
- package/package.json +1 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tiny internal helpers around the raw IndexedDB API.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the patterns that `storage-adapter.ts` and `blob-cache.ts` both
|
|
5
|
+
* re-implement: timed open with retry-on-failure caching, request→promise
|
|
6
|
+
* wrapping, transaction-to-completion, and cursor iteration. Not exported
|
|
7
|
+
* from the public package — internal to `src/shared/lib`.
|
|
8
|
+
*/
|
|
9
|
+
/** Polly#107 post-v0.60: hard timeout on `indexedDB.open()`. Healthy
|
|
10
|
+
* opens land in microseconds; the v0.60.0 fingerprint surfaced a zombie
|
|
11
|
+
* cross-tab connection that left an open request firing no events at all.
|
|
12
|
+
* 5s is two orders of magnitude beyond the normal upper bound and short
|
|
13
|
+
* enough that the operator gets a named failure instead of a hung page
|
|
14
|
+
* when the storage layer wedges. */
|
|
15
|
+
export declare const IDB_OPEN_TIMEOUT_MS = 5000;
|
|
16
|
+
export interface OpenIDBOptions {
|
|
17
|
+
name: string;
|
|
18
|
+
version: number;
|
|
19
|
+
/** Invoked inside `onupgradeneeded`. Create object stores here. */
|
|
20
|
+
upgrade: (db: IDBDatabase, event: IDBVersionChangeEvent) => void;
|
|
21
|
+
}
|
|
22
|
+
/** Open an IndexedDB database with the Polly#107 timeout guard. */
|
|
23
|
+
export declare function openIDB(options: OpenIDBOptions): Promise<IDBDatabase>;
|
|
24
|
+
/** One-shot open caching with retry-on-failure. Caller owns a `{ promise }`
|
|
25
|
+
* cell; first call opens, subsequent calls return the cached promise, and
|
|
26
|
+
* a rejected open clears the cache so the next call can retry instead of
|
|
27
|
+
* being poisoned by one transient failure. */
|
|
28
|
+
export declare function cachedOpen(ref: {
|
|
29
|
+
promise: Promise<IDBDatabase> | null;
|
|
30
|
+
}, options: OpenIDBOptions): Promise<IDBDatabase>;
|
|
31
|
+
/** Promise-wrap a single `IDBRequest` (`get`, `put`, `count`, `delete`…). */
|
|
32
|
+
export declare function runRequest<T>(request: IDBRequest<T>): Promise<T>;
|
|
33
|
+
/** Run a transaction to completion. Resolves on `tx.oncomplete` (write
|
|
34
|
+
* durability), not on individual request success. Throws inside `fn`
|
|
35
|
+
* abort the transaction and reject. */
|
|
36
|
+
export declare function runTx(db: IDBDatabase, storeName: string, mode: IDBTransactionMode, fn: (store: IDBObjectStore) => void): Promise<void>;
|
|
37
|
+
/** Walk every record in a store. */
|
|
38
|
+
export declare function iterateCursor<V>(db: IDBDatabase, storeName: string, visit: (key: IDBValidKey, value: V) => void): Promise<void>;
|
|
@@ -69,6 +69,90 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
69
69
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
+
// src/shared/lib/idb-helpers.ts
|
|
73
|
+
function openIDB(options) {
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
const start = Date.now();
|
|
76
|
+
const request = indexedDB.open(options.name, options.version);
|
|
77
|
+
let settled = false;
|
|
78
|
+
const timer = setTimeout(() => {
|
|
79
|
+
if (settled)
|
|
80
|
+
return;
|
|
81
|
+
settled = true;
|
|
82
|
+
const elapsedMs = Date.now() - start;
|
|
83
|
+
reject(new Error(`Polly IndexedDB open of '${options.name}' timed out after ${elapsedMs}ms (cross-tab lock or zombie connection?)`));
|
|
84
|
+
}, IDB_OPEN_TIMEOUT_MS);
|
|
85
|
+
request.onerror = () => {
|
|
86
|
+
if (settled)
|
|
87
|
+
return;
|
|
88
|
+
settled = true;
|
|
89
|
+
clearTimeout(timer);
|
|
90
|
+
reject(request.error);
|
|
91
|
+
};
|
|
92
|
+
request.onsuccess = () => {
|
|
93
|
+
if (settled)
|
|
94
|
+
return;
|
|
95
|
+
settled = true;
|
|
96
|
+
clearTimeout(timer);
|
|
97
|
+
resolve(request.result);
|
|
98
|
+
};
|
|
99
|
+
request.onupgradeneeded = (event) => {
|
|
100
|
+
const db = event.target.result;
|
|
101
|
+
options.upgrade(db, event);
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
function cachedOpen(ref, options) {
|
|
106
|
+
if (ref.promise)
|
|
107
|
+
return ref.promise;
|
|
108
|
+
const pending = openIDB(options);
|
|
109
|
+
pending.catch(() => {
|
|
110
|
+
if (ref.promise === pending)
|
|
111
|
+
ref.promise = null;
|
|
112
|
+
});
|
|
113
|
+
ref.promise = pending;
|
|
114
|
+
return pending;
|
|
115
|
+
}
|
|
116
|
+
function runRequest(request) {
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
request.onsuccess = () => resolve(request.result);
|
|
119
|
+
request.onerror = () => reject(request.error);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function runTx(db, storeName, mode, fn) {
|
|
123
|
+
return new Promise((resolve, reject) => {
|
|
124
|
+
const tx = db.transaction(storeName, mode);
|
|
125
|
+
const store = tx.objectStore(storeName);
|
|
126
|
+
tx.oncomplete = () => resolve();
|
|
127
|
+
tx.onerror = () => reject(tx.error);
|
|
128
|
+
tx.onabort = () => reject(tx.error);
|
|
129
|
+
try {
|
|
130
|
+
fn(store);
|
|
131
|
+
} catch (err) {
|
|
132
|
+
try {
|
|
133
|
+
tx.abort();
|
|
134
|
+
} catch {}
|
|
135
|
+
reject(err);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
function iterateCursor(db, storeName, visit) {
|
|
140
|
+
return new Promise((resolve, reject) => {
|
|
141
|
+
const tx = db.transaction(storeName, "readonly");
|
|
142
|
+
const store = tx.objectStore(storeName);
|
|
143
|
+
const request = store.openCursor();
|
|
144
|
+
request.onsuccess = () => {
|
|
145
|
+
const cursor = request.result;
|
|
146
|
+
if (!cursor)
|
|
147
|
+
return resolve();
|
|
148
|
+
visit(cursor.key, cursor.value);
|
|
149
|
+
cursor.continue();
|
|
150
|
+
};
|
|
151
|
+
request.onerror = () => reject(request.error);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
var IDB_OPEN_TIMEOUT_MS = 5000;
|
|
155
|
+
|
|
72
156
|
// src/shared/lib/storage-adapter.ts
|
|
73
157
|
var exports_storage_adapter = {};
|
|
74
158
|
__export(exports_storage_adapter, {
|
|
@@ -81,68 +165,32 @@ __export(exports_storage_adapter, {
|
|
|
81
165
|
class IndexedDBAdapter {
|
|
82
166
|
dbName;
|
|
83
167
|
storeName = "state";
|
|
84
|
-
|
|
168
|
+
dbRef = { promise: null };
|
|
85
169
|
constructor(dbName = "polly-state") {
|
|
86
170
|
this.dbName = dbName;
|
|
87
171
|
}
|
|
88
172
|
getDB() {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const request = indexedDB.open(this.dbName, 1);
|
|
94
|
-
let settled = false;
|
|
95
|
-
const timer = setTimeout(() => {
|
|
96
|
-
if (settled)
|
|
97
|
-
return;
|
|
98
|
-
settled = true;
|
|
99
|
-
const elapsedMs = Date.now() - start;
|
|
100
|
-
reject(new Error(`Polly IndexedDB open of '${this.dbName}' timed out after ${elapsedMs}ms (cross-tab lock or zombie connection?)`));
|
|
101
|
-
}, IDB_OPEN_TIMEOUT_MS);
|
|
102
|
-
request.onerror = () => {
|
|
103
|
-
if (settled)
|
|
104
|
-
return;
|
|
105
|
-
settled = true;
|
|
106
|
-
clearTimeout(timer);
|
|
107
|
-
reject(request.error);
|
|
108
|
-
};
|
|
109
|
-
request.onsuccess = () => {
|
|
110
|
-
if (settled)
|
|
111
|
-
return;
|
|
112
|
-
settled = true;
|
|
113
|
-
clearTimeout(timer);
|
|
114
|
-
resolve(request.result);
|
|
115
|
-
};
|
|
116
|
-
request.onupgradeneeded = (event) => {
|
|
117
|
-
const db = event.target.result;
|
|
173
|
+
return cachedOpen(this.dbRef, {
|
|
174
|
+
name: this.dbName,
|
|
175
|
+
version: 1,
|
|
176
|
+
upgrade: (db) => {
|
|
118
177
|
if (!db.objectStoreNames.contains(this.storeName)) {
|
|
119
178
|
db.createObjectStore(this.storeName);
|
|
120
179
|
}
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
pending.catch(() => {
|
|
124
|
-
if (this.dbPromise === pending)
|
|
125
|
-
this.dbPromise = null;
|
|
180
|
+
}
|
|
126
181
|
});
|
|
127
|
-
this.dbPromise = pending;
|
|
128
|
-
return pending;
|
|
129
182
|
}
|
|
130
183
|
async get(keys) {
|
|
131
184
|
try {
|
|
132
185
|
const db = await this.getDB();
|
|
186
|
+
const tx = db.transaction(this.storeName, "readonly");
|
|
187
|
+
const store = tx.objectStore(this.storeName);
|
|
188
|
+
const pairs = await Promise.all(keys.map(async (key) => [key, await runRequest(store.get(key))]));
|
|
133
189
|
const result = {};
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
request.onerror = () => reject(request.error);
|
|
139
|
-
request.onsuccess = () => {
|
|
140
|
-
if (request.result !== undefined) {
|
|
141
|
-
result[key] = request.result;
|
|
142
|
-
}
|
|
143
|
-
resolve();
|
|
144
|
-
};
|
|
145
|
-
})));
|
|
190
|
+
for (const [key, value] of pairs) {
|
|
191
|
+
if (value !== undefined)
|
|
192
|
+
result[key] = value;
|
|
193
|
+
}
|
|
146
194
|
return result;
|
|
147
195
|
} catch (error) {
|
|
148
196
|
console.warn("[Polly] IndexedDB get failed:", error);
|
|
@@ -152,13 +200,11 @@ class IndexedDBAdapter {
|
|
|
152
200
|
async set(items) {
|
|
153
201
|
try {
|
|
154
202
|
const db = await this.getDB();
|
|
155
|
-
await
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
request.onsuccess = () => resolve();
|
|
161
|
-
})));
|
|
203
|
+
await runTx(db, this.storeName, "readwrite", (store) => {
|
|
204
|
+
for (const [key, value] of Object.entries(items)) {
|
|
205
|
+
store.put(value, key);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
162
208
|
} catch (error) {
|
|
163
209
|
console.warn("[Polly] IndexedDB set failed:", error);
|
|
164
210
|
}
|
|
@@ -166,13 +212,10 @@ class IndexedDBAdapter {
|
|
|
166
212
|
async remove(keys) {
|
|
167
213
|
try {
|
|
168
214
|
const db = await this.getDB();
|
|
169
|
-
await
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
request.onerror = () => reject(request.error);
|
|
174
|
-
request.onsuccess = () => resolve();
|
|
175
|
-
})));
|
|
215
|
+
await runTx(db, this.storeName, "readwrite", (store) => {
|
|
216
|
+
for (const key of keys)
|
|
217
|
+
store.delete(key);
|
|
218
|
+
});
|
|
176
219
|
} catch (error) {
|
|
177
220
|
console.warn("[Polly] IndexedDB remove failed:", error);
|
|
178
221
|
}
|
|
@@ -245,7 +288,7 @@ function createStorageAdapter() {
|
|
|
245
288
|
}
|
|
246
289
|
return new MemoryStorageAdapter;
|
|
247
290
|
}
|
|
248
|
-
var
|
|
291
|
+
var init_storage_adapter = () => {};
|
|
249
292
|
|
|
250
293
|
// src/shared/lib/sync-adapter.ts
|
|
251
294
|
var exports_sync_adapter = {};
|
|
@@ -749,6 +792,7 @@ class MessageLoggerAdapter {
|
|
|
749
792
|
}
|
|
750
793
|
|
|
751
794
|
// src/shared/lib/adapter-factory.ts
|
|
795
|
+
init_storage_adapter();
|
|
752
796
|
function createStateAdapters(options = {}) {
|
|
753
797
|
if (options.storage && options.sync) {
|
|
754
798
|
return {
|
|
@@ -769,7 +813,7 @@ function createStateAdapters(options = {}) {
|
|
|
769
813
|
return { storage, sync };
|
|
770
814
|
}
|
|
771
815
|
function createChromeAdapters() {
|
|
772
|
-
const { ChromeStorageAdapter: ChromeStorageAdapter3 } = __toCommonJS(exports_storage_adapter);
|
|
816
|
+
const { ChromeStorageAdapter: ChromeStorageAdapter3 } = (init_storage_adapter(), __toCommonJS(exports_storage_adapter));
|
|
773
817
|
const { ChromeRuntimeSyncAdapter: ChromeRuntimeSyncAdapter2 } = __toCommonJS(exports_sync_adapter);
|
|
774
818
|
return {
|
|
775
819
|
storage: new ChromeStorageAdapter3,
|
|
@@ -777,14 +821,14 @@ function createChromeAdapters() {
|
|
|
777
821
|
};
|
|
778
822
|
}
|
|
779
823
|
function createWebAdapters(options = {}) {
|
|
780
|
-
const { IndexedDBAdapter: IndexedDBAdapter2 } = __toCommonJS(exports_storage_adapter);
|
|
824
|
+
const { IndexedDBAdapter: IndexedDBAdapter2 } = (init_storage_adapter(), __toCommonJS(exports_storage_adapter));
|
|
781
825
|
const { BroadcastChannelSyncAdapter: BroadcastChannelSyncAdapter2, NoOpSyncAdapter: NoOpSyncAdapter2 } = __toCommonJS(exports_sync_adapter);
|
|
782
826
|
const storage = new IndexedDBAdapter2(options.dbName);
|
|
783
827
|
const sync = options.singleTab ? new NoOpSyncAdapter2 : new BroadcastChannelSyncAdapter2(options.channelName);
|
|
784
828
|
return { storage, sync };
|
|
785
829
|
}
|
|
786
830
|
function createNodeAdapters() {
|
|
787
|
-
const { MemoryStorageAdapter: MemoryStorageAdapter2 } = __toCommonJS(exports_storage_adapter);
|
|
831
|
+
const { MemoryStorageAdapter: MemoryStorageAdapter2 } = (init_storage_adapter(), __toCommonJS(exports_storage_adapter));
|
|
788
832
|
const { NoOpSyncAdapter: NoOpSyncAdapter2 } = __toCommonJS(exports_sync_adapter);
|
|
789
833
|
return {
|
|
790
834
|
storage: new MemoryStorageAdapter2,
|
|
@@ -792,7 +836,7 @@ function createNodeAdapters() {
|
|
|
792
836
|
};
|
|
793
837
|
}
|
|
794
838
|
function createMockAdapters() {
|
|
795
|
-
const { MemoryStorageAdapter: MemoryStorageAdapter2 } = __toCommonJS(exports_storage_adapter);
|
|
839
|
+
const { MemoryStorageAdapter: MemoryStorageAdapter2 } = (init_storage_adapter(), __toCommonJS(exports_storage_adapter));
|
|
796
840
|
const { NoOpSyncAdapter: NoOpSyncAdapter2 } = __toCommonJS(exports_sync_adapter);
|
|
797
841
|
return {
|
|
798
842
|
storage: new MemoryStorageAdapter2,
|
|
@@ -801,6 +845,7 @@ function createMockAdapters() {
|
|
|
801
845
|
}
|
|
802
846
|
|
|
803
847
|
// src/shared/adapters/index.ts
|
|
848
|
+
init_storage_adapter();
|
|
804
849
|
function createChromeAdapters2(context, options) {
|
|
805
850
|
const runtime2 = new ChromeRuntimeAdapter;
|
|
806
851
|
return {
|
|
@@ -1590,4 +1635,4 @@ export {
|
|
|
1590
1635
|
MessageBus
|
|
1591
1636
|
};
|
|
1592
1637
|
|
|
1593
|
-
//# debugId=
|
|
1638
|
+
//# debugId=3223EA30A6C6201E64756E2164756E21
|