@sweidos/eidos 2.1.0 → 2.2.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/README.md +43 -14
- package/dist/action.js +47 -47
- package/dist/action.js.map +1 -1
- package/dist/devtools.js +169 -20
- package/dist/eidos.cjs +2 -2
- package/dist/eidos.cjs.map +1 -1
- package/dist/index.d.ts +74 -13
- package/dist/index.js +36 -33
- package/dist/react/hooks.js +30 -27
- package/dist/react/hooks.js.map +1 -1
- package/dist/runtime.js +29 -24
- package/dist/runtime.js.map +1 -1
- package/dist/store-slices.js +31 -20
- package/dist/store-slices.js.map +1 -1
- package/dist/store.js +22 -19
- package/dist/store.js.map +1 -1
- package/dist/stores.js +31 -22
- package/dist/stores.js.map +1 -1
- package/dist/testing.cjs +3 -2
- package/dist/testing.js +3 -2
- package/dist/types.js +19 -8
- package/dist/types.js.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +3 -3
package/dist/devtools.js
CHANGED
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useCallback, useState, useSyncExternalStore } from "react";
|
|
3
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/types.ts
|
|
5
|
+
function emptyReliabilityStats() {
|
|
6
|
+
return {
|
|
7
|
+
queued: 0,
|
|
8
|
+
succeeded: 0,
|
|
9
|
+
failed: 0,
|
|
10
|
+
retried: 0,
|
|
11
|
+
conflicted: 0,
|
|
12
|
+
cancelled: 0
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/** Single pass over the queue — avoids separate .filter() calls per status. */
|
|
16
|
+
function countQueueByStatus(queue) {
|
|
17
|
+
let pending = 0, failed = 0, replaying = 0;
|
|
18
|
+
for (const q of queue) if (q.status === "pending") pending++;
|
|
19
|
+
else if (q.status === "failed") failed++;
|
|
20
|
+
else if (q.status === "replaying") replaying++;
|
|
21
|
+
return {
|
|
22
|
+
pending,
|
|
23
|
+
failed,
|
|
24
|
+
replaying,
|
|
25
|
+
total: queue.length
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
4
29
|
//#region src/store-slices.ts
|
|
5
30
|
function createResourceActions(set) {
|
|
6
31
|
return {
|
|
@@ -39,6 +64,15 @@ function createQueueActions(set) {
|
|
|
39
64
|
hydrateQueue: (items) => set(() => ({ queue: items }))
|
|
40
65
|
};
|
|
41
66
|
}
|
|
67
|
+
function createReliabilityActions(set) {
|
|
68
|
+
return {
|
|
69
|
+
recordReliabilityEvent: (event) => set((s) => ({ reliability: {
|
|
70
|
+
...s.reliability,
|
|
71
|
+
[event]: s.reliability[event] + 1
|
|
72
|
+
} })),
|
|
73
|
+
resetReliabilityStats: () => set(() => ({ reliability: emptyReliabilityStats() }))
|
|
74
|
+
};
|
|
75
|
+
}
|
|
42
76
|
//#endregion
|
|
43
77
|
//#region src/store.ts
|
|
44
78
|
var _state;
|
|
@@ -59,13 +93,15 @@ _state = {
|
|
|
59
93
|
swError: void 0,
|
|
60
94
|
resources: {},
|
|
61
95
|
queue: [],
|
|
96
|
+
reliability: emptyReliabilityStats(),
|
|
62
97
|
setOnline: (isOnline) => _set(() => ({ isOnline })),
|
|
63
98
|
setSwStatus: (swStatus, swError) => _set(() => ({
|
|
64
99
|
swStatus,
|
|
65
100
|
swError
|
|
66
101
|
})),
|
|
67
102
|
...createResourceActions(_set),
|
|
68
|
-
...createQueueActions(_set)
|
|
103
|
+
...createQueueActions(_set),
|
|
104
|
+
...createReliabilityActions(_set)
|
|
69
105
|
};
|
|
70
106
|
function _getState() {
|
|
71
107
|
return _state;
|
|
@@ -89,21 +125,6 @@ var useEidosStore = {
|
|
|
89
125
|
}
|
|
90
126
|
};
|
|
91
127
|
//#endregion
|
|
92
|
-
//#region src/types.ts
|
|
93
|
-
/** Single pass over the queue — avoids separate .filter() calls per status. */
|
|
94
|
-
function countQueueByStatus(queue) {
|
|
95
|
-
let pending = 0, failed = 0, replaying = 0;
|
|
96
|
-
for (const q of queue) if (q.status === "pending") pending++;
|
|
97
|
-
else if (q.status === "failed") failed++;
|
|
98
|
-
else if (q.status === "replaying") replaying++;
|
|
99
|
-
return {
|
|
100
|
-
pending,
|
|
101
|
-
failed,
|
|
102
|
-
replaying,
|
|
103
|
-
total: queue.length
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
//#endregion
|
|
107
128
|
//#region src/react/hooks.ts
|
|
108
129
|
function useStore(selector) {
|
|
109
130
|
const fn = selector ?? ((s) => s);
|
|
@@ -146,6 +167,22 @@ function useEidosQueueStats() {
|
|
|
146
167
|
total: +t
|
|
147
168
|
};
|
|
148
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Calls `callback` once each time the action queue drains from non-empty → 0.
|
|
172
|
+
* Stable callback reference not required — always calls the latest version.
|
|
173
|
+
* Use for "all offline actions synced!" toasts.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* useEidosOnDrain(() => toast.success('All offline actions synced!'))
|
|
177
|
+
*/
|
|
178
|
+
/**
|
|
179
|
+
* Cumulative, session-scoped `neverLose` queue outcome counters — opt-in
|
|
180
|
+
* reliability telemetry for dashboards/devtools. Re-renders only when a
|
|
181
|
+
* counter changes.
|
|
182
|
+
*/
|
|
183
|
+
function useEidosReliabilityStats() {
|
|
184
|
+
return useStore((s) => s.reliability);
|
|
185
|
+
}
|
|
149
186
|
//#endregion
|
|
150
187
|
//#region src/sw-bridge.ts
|
|
151
188
|
var _registration = null;
|
|
@@ -403,6 +440,7 @@ async function _markSucceeded(item, store) {
|
|
|
403
440
|
status: "succeeded",
|
|
404
441
|
completedAt
|
|
405
442
|
});
|
|
443
|
+
store.recordReliabilityEvent("succeeded");
|
|
406
444
|
broadcastQueueSync({
|
|
407
445
|
type: "update",
|
|
408
446
|
id: item.id,
|
|
@@ -453,6 +491,7 @@ async function _resolveConflict(item, store, err) {
|
|
|
453
491
|
}
|
|
454
492
|
if (resolution === "skip") {
|
|
455
493
|
store.removeQueueItem(item.id);
|
|
494
|
+
store.recordReliabilityEvent("conflicted");
|
|
456
495
|
broadcastQueueSync({
|
|
457
496
|
type: "remove",
|
|
458
497
|
id: item.id
|
|
@@ -480,6 +519,7 @@ async function _scheduleRetryOrFail(item, store, err) {
|
|
|
480
519
|
retryCount
|
|
481
520
|
};
|
|
482
521
|
store.updateQueueItem(item.id, update);
|
|
522
|
+
store.recordReliabilityEvent("failed");
|
|
483
523
|
broadcastQueueSync({
|
|
484
524
|
type: "update",
|
|
485
525
|
id: item.id,
|
|
@@ -499,6 +539,7 @@ async function _scheduleRetryOrFail(item, store, err) {
|
|
|
499
539
|
nextRetryAt: Date.now() + backoffMs(retryCount)
|
|
500
540
|
};
|
|
501
541
|
store.updateQueueItem(item.id, update);
|
|
542
|
+
store.recordReliabilityEvent("retried");
|
|
502
543
|
broadcastQueueSync({
|
|
503
544
|
type: "update",
|
|
504
545
|
id: item.id,
|
|
@@ -529,6 +570,7 @@ async function _replayItem(item, store) {
|
|
|
529
570
|
} catch (err) {
|
|
530
571
|
if (isAbortError(err)) {
|
|
531
572
|
store.removeQueueItem(item.id);
|
|
573
|
+
store.recordReliabilityEvent("cancelled");
|
|
532
574
|
broadcastQueueSync({
|
|
533
575
|
type: "remove",
|
|
534
576
|
id: item.id
|
|
@@ -709,7 +751,8 @@ var ICONS = {
|
|
|
709
751
|
arrowDown: "M12 5v14M19 12l-7 7-7-7",
|
|
710
752
|
clock: "M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20zM12 6v6l4 2",
|
|
711
753
|
x: "M18 6 6 18M6 6l12 12",
|
|
712
|
-
rotateCcw: "M3 12a9 9 0 1 0 2.6-6.4M3 12V5m0 7h7"
|
|
754
|
+
rotateCcw: "M3 12a9 9 0 1 0 2.6-6.4M3 12V5m0 7h7",
|
|
755
|
+
activity: "M22 12h-4l-3 9L9 3l-3 9H2"
|
|
713
756
|
};
|
|
714
757
|
function positionStyle(p) {
|
|
715
758
|
const base = {
|
|
@@ -746,6 +789,7 @@ function EidosDevtools({ position = "bottom-right", defaultOpen = false }) {
|
|
|
746
789
|
const { pending, failed, replaying } = useEidosQueueStats();
|
|
747
790
|
const resources = useEidosResources();
|
|
748
791
|
const resourceList = Object.values(resources);
|
|
792
|
+
const reliability = useEidosReliabilityStats();
|
|
749
793
|
const badgeCount = pending + failed + replaying;
|
|
750
794
|
const toggleOffline = useCallback(() => {
|
|
751
795
|
const next = !simOffline;
|
|
@@ -949,7 +993,11 @@ function EidosDevtools({ position = "bottom-right", defaultOpen = false }) {
|
|
|
949
993
|
flexShrink: 0,
|
|
950
994
|
background: C.surface
|
|
951
995
|
},
|
|
952
|
-
children: [
|
|
996
|
+
children: [
|
|
997
|
+
"queue",
|
|
998
|
+
"cache",
|
|
999
|
+
"reliability"
|
|
1000
|
+
].map((t) => /* @__PURE__ */ jsx("button", {
|
|
953
1001
|
role: "tab",
|
|
954
1002
|
"aria-selected": tab === t,
|
|
955
1003
|
onClick: () => setTab(t),
|
|
@@ -970,7 +1018,7 @@ function EidosDevtools({ position = "bottom-right", defaultOpen = false }) {
|
|
|
970
1018
|
letterSpacing: "0.05em",
|
|
971
1019
|
transition: "color 0.15s, border-color 0.15s"
|
|
972
1020
|
},
|
|
973
|
-
children: t === "queue" ? `Queue (${queue.length})` : `Cache (${resourceList.length})`
|
|
1021
|
+
children: t === "queue" ? `Queue (${queue.length})` : t === "cache" ? `Cache (${resourceList.length})` : "Reliability"
|
|
974
1022
|
}, t))
|
|
975
1023
|
}),
|
|
976
1024
|
/* @__PURE__ */ jsx("div", {
|
|
@@ -983,7 +1031,7 @@ function EidosDevtools({ position = "bottom-right", defaultOpen = false }) {
|
|
|
983
1031
|
queue,
|
|
984
1032
|
onReplay: handleReplay,
|
|
985
1033
|
onClear: handleClear
|
|
986
|
-
}) : /* @__PURE__ */ jsx(CacheTab, { resources: resourceList })
|
|
1034
|
+
}) : tab === "cache" ? /* @__PURE__ */ jsx(CacheTab, { resources: resourceList }) : /* @__PURE__ */ jsx(ReliabilityTab, { stats: reliability })
|
|
987
1035
|
})
|
|
988
1036
|
]
|
|
989
1037
|
}), toggleBtn]
|
|
@@ -1235,5 +1283,106 @@ function CacheTab({ resources }) {
|
|
|
1235
1283
|
]
|
|
1236
1284
|
}, res.url)) });
|
|
1237
1285
|
}
|
|
1286
|
+
function ReliabilityTab({ stats }) {
|
|
1287
|
+
const rows = [
|
|
1288
|
+
{
|
|
1289
|
+
label: "Queued",
|
|
1290
|
+
key: "queued",
|
|
1291
|
+
color: C.blue
|
|
1292
|
+
},
|
|
1293
|
+
{
|
|
1294
|
+
label: "Succeeded",
|
|
1295
|
+
key: "succeeded",
|
|
1296
|
+
color: C.green
|
|
1297
|
+
},
|
|
1298
|
+
{
|
|
1299
|
+
label: "Retried",
|
|
1300
|
+
key: "retried",
|
|
1301
|
+
color: C.yellow
|
|
1302
|
+
},
|
|
1303
|
+
{
|
|
1304
|
+
label: "Failed",
|
|
1305
|
+
key: "failed",
|
|
1306
|
+
color: C.red
|
|
1307
|
+
},
|
|
1308
|
+
{
|
|
1309
|
+
label: "Conflicted",
|
|
1310
|
+
key: "conflicted",
|
|
1311
|
+
color: C.purple
|
|
1312
|
+
},
|
|
1313
|
+
{
|
|
1314
|
+
label: "Cancelled",
|
|
1315
|
+
key: "cancelled",
|
|
1316
|
+
color: C.muted
|
|
1317
|
+
}
|
|
1318
|
+
];
|
|
1319
|
+
const total = stats.queued;
|
|
1320
|
+
const successRate = total > 0 ? Math.round(stats.succeeded / total * 100) : null;
|
|
1321
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
1322
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1323
|
+
style: {
|
|
1324
|
+
display: "flex",
|
|
1325
|
+
alignItems: "center",
|
|
1326
|
+
gap: 8,
|
|
1327
|
+
padding: "8px 12px",
|
|
1328
|
+
borderBottom: `1px solid ${C.border}`
|
|
1329
|
+
},
|
|
1330
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1331
|
+
style: {
|
|
1332
|
+
color: C.muted,
|
|
1333
|
+
display: "inline-flex"
|
|
1334
|
+
},
|
|
1335
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
1336
|
+
path: ICONS.activity,
|
|
1337
|
+
size: 12
|
|
1338
|
+
})
|
|
1339
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
1340
|
+
style: {
|
|
1341
|
+
color: C.muted,
|
|
1342
|
+
fontSize: 10
|
|
1343
|
+
},
|
|
1344
|
+
children: successRate === null ? "No queued actions yet this session" : `${successRate}% succeeded`
|
|
1345
|
+
})]
|
|
1346
|
+
}),
|
|
1347
|
+
rows.map(({ label, key, color }) => /* @__PURE__ */ jsxs("div", {
|
|
1348
|
+
style: {
|
|
1349
|
+
display: "flex",
|
|
1350
|
+
alignItems: "center",
|
|
1351
|
+
justifyContent: "space-between",
|
|
1352
|
+
padding: "7px 12px",
|
|
1353
|
+
borderBottom: `1px solid ${C.border}`
|
|
1354
|
+
},
|
|
1355
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1356
|
+
style: { color: C.text },
|
|
1357
|
+
children: label
|
|
1358
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
1359
|
+
style: pill(color),
|
|
1360
|
+
children: stats[key]
|
|
1361
|
+
})]
|
|
1362
|
+
}, key)),
|
|
1363
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1364
|
+
style: {
|
|
1365
|
+
padding: "8px 12px",
|
|
1366
|
+
color: C.muted,
|
|
1367
|
+
fontSize: 10
|
|
1368
|
+
},
|
|
1369
|
+
children: [
|
|
1370
|
+
"Session-only counters — reset on reload. Wire up",
|
|
1371
|
+
" ",
|
|
1372
|
+
/* @__PURE__ */ jsx("code", {
|
|
1373
|
+
style: { color: C.cyan },
|
|
1374
|
+
children: "onReliabilityReport"
|
|
1375
|
+
}),
|
|
1376
|
+
" in",
|
|
1377
|
+
" ",
|
|
1378
|
+
/* @__PURE__ */ jsx("code", {
|
|
1379
|
+
style: { color: C.cyan },
|
|
1380
|
+
children: "initEidos()"
|
|
1381
|
+
}),
|
|
1382
|
+
" to forward these to analytics."
|
|
1383
|
+
]
|
|
1384
|
+
})
|
|
1385
|
+
] });
|
|
1386
|
+
}
|
|
1238
1387
|
//#endregion
|
|
1239
1388
|
export { EidosDevtools };
|
package/dist/eidos.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});let
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});let R=require("react"),F=require("react/jsx-runtime");function J(){return{queued:0,succeeded:0,failed:0,retried:0,conflicted:0,cancelled:0}}function X(e){let t=0,n=0,r=0;for(const a of e)a.status==="pending"?t++:a.status==="failed"?n++:a.status==="replaying"&&r++;return{pending:t,failed:n,replaying:r,total:e.length}}function we(e){return{registerResource:(t,n)=>e(r=>({resources:{...r.resources,[t]:n}})),updateResource:(t,n)=>e(r=>({resources:{...r.resources,[t]:r.resources[t]?{...r.resources[t],...n}:r.resources[t]}})),unregisterResource:t=>e(n=>({resources:Object.fromEntries(Object.entries(n.resources).filter(([r])=>r!==t))}))}}function ve(e){return{addQueueItem:t=>e(n=>({queue:[...n.queue,t]})),updateQueueItem:(t,n)=>e(r=>({queue:r.queue.map(a=>a.id===t?{...a,...n}:a)})),batchUpdateQueueItems:t=>e(n=>{const r=new Map(t.map(a=>[a.id,a.update]));return{queue:n.queue.map(a=>{const s=r.get(a.id);return s?{...a,...s}:a})}}),removeQueueItem:t=>e(n=>({queue:n.queue.filter(r=>r.id!==t)})),hydrateQueue:t=>e(()=>({queue:t}))}}function me(e){return{recordReliabilityEvent:t=>e(n=>({reliability:{...n.reliability,[t]:n.reliability[t]+1}})),resetReliabilityStats:()=>e(()=>({reliability:J()}))}}var g,T=new Set;function z(){T.forEach(e=>e())}function E(e){g={...g,...e(g)},z()}g={isOnline:typeof navigator>"u"||navigator.onLine!==!1,swStatus:"idle",swError:void 0,resources:{},queue:[],reliability:J(),setOnline:e=>E(()=>({isOnline:e})),setSwStatus:(e,t)=>E(()=>({swStatus:e,swError:t})),...we(E),...ve(E),...me(E)};function Se(){return g}function be(e){return T.add(e),()=>{T.delete(e)}}var o={getState:Se,subscribe:be,setState:e=>{const t=typeof e=="function"?e(g):e;g={...g,...t},z()}},v=null,M=[];function Z(){return v}async function Ee(e){if(typeof navigator>"u"||!("serviceWorker"in navigator)){o.getState().setSwStatus("unsupported");return}const t=o.getState();t.setSwStatus("registering");try{v=await navigator.serviceWorker.register(e,{scope:"/"}),await Re(v),t.setSwStatus("active"),navigator.serviceWorker.addEventListener("message",Oe),window.addEventListener("online",()=>t.setOnline(!0)),window.addEventListener("offline",()=>t.setOnline(!1)),qe()}catch(n){t.setSwStatus("error",String(n))}}function Re(e){return new Promise(t=>{if(e.active){t();return}const n=e.installing??e.waiting;if(!n){t();return}const r=setTimeout(t,1e4);n.addEventListener("statechange",function a(){n.state==="activated"&&(clearTimeout(r),n.removeEventListener("statechange",a),t())})})}function _(e){const t=v?.active;t?t.postMessage(e):M.push(e)}var ee=null;function Ie(e){ee=e}function _e(){try{return typeof navigator<"u"&&"serviceWorker"in navigator&&v!==null&&"sync"in v}catch{return!1}}var j={};function ke(e){j=e}function Oe(e){const t=e.data;if(!t?.type)return;const n=o.getState(),{type:r,url:a}=t;if(r==="EIDOS_BACKGROUND_SYNC"){ee?.();return}if(r==="EIDOS_NOTIFICATION_CLICK"){j.onNotificationClick?.(t.data);return}if(r==="EIDOS_SUBSCRIPTION_EXPIRED"){j.onSubscriptionExpired?.(t.subscription);return}if(a)switch(r){case"EIDOS_CACHE_HIT":{const s=n.resources[a];n.updateResource(a,{status:"fresh",lastEvent:"cache-hit",cacheHits:(s?.cacheHits??0)+1});break}case"EIDOS_CACHE_UPDATED":n.updateResource(a,{status:"fresh",lastEvent:"cache-updated",cachedAt:Date.now()});break;case"EIDOS_NETWORK_ERROR":n.updateResource(a,{status:"error",lastEvent:"network-error"});break}}function Ae(e){_({type:"EIDOS_SIMULATE_OFFLINE",enabled:e}),o.getState().setOnline(!e)}function qe(){const e=v?.active;if(e){for(const t of M)e.postMessage(t);M=[]}}var w=new Map,x=new Map,te=null;function Qe(e){te=e}function C(e){return e.includes("*")||/:[^/]+/.test(e)}function Ce(e){return"^"+e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*\*/g,".+").replace(/\*/g,"[^/]+").replace(/:[^/]+/g,"[^/]+")+"$"}function ne(e,t){const n=Ne(t),r=C(e)?Ce(e):void 0,a={url:e,config:t,strategy:n,status:"idle",cacheHits:0,cacheMisses:0};return o.getState().registerResource(e,a),_({type:"EIDOS_REGISTER_RESOURCE",url:e,strategy:n.swStrategy,cacheName:n.cacheName,...r!==void 0&&{pattern:r}}),{strategy:n,regexStr:r}}function re(e,t,n){return async()=>{_({type:"EIDOS_CLEAR_CACHE",url:e});const r=await caches.open(t.cacheName).catch(()=>null);if(r){const a=await r.keys(),s=n?new RegExp(n):null,i=e.startsWith("http");await Promise.all(a.filter(c=>{const u=c.url,f=new URL(u).pathname;return s?s.test(i?u:f):i?u===e:u===e||f===e}).map(c=>r.delete(c)))}C(e)||o.getState().updateResource(e,{status:"stale",cachedAt:void 0,lastEvent:"cache-cleared",cacheHits:0,cacheMisses:0}),te?.(["eidos",e])}}function ae(e){return()=>{w.delete(e),_({type:"EIDOS_UNREGISTER_RESOURCE",url:e}),o.getState().unregisterResource(e)}}function xe(e,t){if(C(e))throw new Error(`[eidos] resource('${e}') is a URL pattern — use resourcePattern('${e}', config) instead. Pattern handles only support invalidate()/unregister(); the SW intercepts matching requests automatically.`);if(w.has(e))return w.get(e);const{strategy:n}=ne(e,t),r={url:e,config:t,strategy:n,fetch:async()=>{const a=x.get(e);if(a)return a.then(i=>i.clone());const s=De(e,t,n);return x.set(e,s),s.finally(()=>x.delete(e)).catch(()=>{}),s.then(i=>i.clone())},json:async()=>(await r.fetch()).json(),query:()=>({queryKey:["eidos",e],queryFn:()=>r.json()}),prefetch:async()=>{await r.fetch()},invalidate:re(e,n,void 0),unregister:ae(e)};return w.set(e,r),r}function Pe(e,t){if(!C(e))throw new Error(`[eidos] resourcePattern('${e}') is not a URL pattern — use resource('${e}', config) instead.`);if(w.has(e))return w.get(e);const{strategy:n,regexStr:r}=ne(e,t),a={url:e,config:t,strategy:n,invalidate:re(e,n,r),unregister:ae(e)};return w.set(e,a),a}async function De(e,t,n){const r=o.getState();r.updateResource(e,{status:"fetching",fetchedAt:Date.now()});const a=await caches.open(n.cacheName).catch(()=>null);try{if(n.swStrategy!=="network-first"){const c=a?await a.match(e).catch(()=>null):null,u=o.getState().resources[e],f=t.maxAge!==void 0&&u?.cachedAt!==void 0&&Date.now()-u.cachedAt>t.maxAge;if(c&&!f)return r.updateResource(e,{status:"fresh",lastEvent:"cache-hit",cacheHits:(u?.cacheHits??0)+1}),n.swStrategy==="stale-while-revalidate"&&fetch(e,{signal:AbortSignal.timeout(5e3)}).then(async S=>{S.ok&&a&&(await a.put(e,S.clone()),o.getState().updateResource(e,{cachedAt:Date.now(),lastEvent:"cache-updated"}))}).catch(()=>{}),c;const p=o.getState().resources[e];r.updateResource(e,{cacheMisses:(p?.cacheMisses??0)+1})}const s=await fetch(e);if(s.ok)return a&&await a.put(e,s.clone()),r.updateResource(e,{status:"fresh",cachedAt:Date.now(),lastEvent:"cache-updated"}),s;r.updateResource(e,{status:s.status===503?"offline":"error"});const i=s.headers.get("X-Eidos-Offline")==="true";throw new Error(i?`offline: no cached response for ${e}`:`${s.status} ${s.statusText}`)}catch(s){const i=a?await a.match(e).catch(()=>null):null;if(i){const c=o.getState().resources[e];return r.updateResource(e,{status:"fresh",lastEvent:"cache-hit",cacheHits:(c?.cacheHits??0)+1}),i}throw r.updateResource(e,{status:"error"}),s}}function Ne(e){const t=e.strategy;return e.offline?V(t??"stale-while-revalidate",e.cacheName,e.version):V(t??"network-first",e.cacheName,e.version)}var Te={"stale-while-revalidate":"StaleWhileRevalidate","cache-first":"CacheFirst","network-first":"NetworkFirst"},Me={"stale-while-revalidate":{reasoning:"offline: true signals resilience. SWR returns cached data instantly while revalidating in the background — the best tradeoff between speed and freshness for offline-capable resources.",behavior:["Cache hit → return immediately, kick off background revalidation","Cache miss → fetch from network, cache the response, return it","Offline → return cached version if available, 503 if not","Reconnect → next request triggers a background refresh"],equivalentCode:`// Workbox equivalent
|
|
2
2
|
new StaleWhileRevalidate({
|
|
3
3
|
cacheName: 'eidos-resources-v1',
|
|
4
4
|
plugins: [new ExpirationPlugin({ maxEntries: 60 })],
|
|
@@ -10,6 +10,6 @@ new CacheFirst({
|
|
|
10
10
|
new NetworkFirst({
|
|
11
11
|
cacheName: 'eidos-resources-v1',
|
|
12
12
|
networkTimeoutSeconds: 3,
|
|
13
|
-
})`}};function H(e,t,r){const n=Pe[e],a=t??"eidos-resources-v1";return{name:xe[e],swStrategy:e,cacheName:r!==void 0?`${a}-v${r}`:a,reasoning:n.reasoning,behavior:n.behavior,equivalentCode:""}}async function Ne(e){const t=await Promise.allSettled(e.map(n=>n.prefetch())),r=t.filter(n=>n.status==="rejected").map(n=>n.reason);return{warmed:t.filter(n=>n.status==="fulfilled").length,failed:r.length,errors:r}}var De="eidos",Te=1,l="action-queue",x=null;function b(){return x?Promise.resolve(x):new Promise((e,t)=>{const r=indexedDB.open(De,Te);r.onupgradeneeded=n=>{const a=n.target.result;if(!a.objectStoreNames.contains(l)){const s=a.createObjectStore(l,{keyPath:"id"});s.createIndex("status","status",{unique:!1}),s.createIndex("actionId","actionId",{unique:!1})}},r.onsuccess=()=>{x=r.result,e(r.result)},r.onerror=()=>t(r.error)})}async function Me(e){const t=await b();return new Promise((r,n)=>{const a=t.transaction(l,"readwrite");a.objectStore(l).add(e),a.oncomplete=()=>r(),a.onerror=()=>n(a.error)})}async function te(){const e=await b();return new Promise((t,r)=>{const n=e.transaction(l,"readonly").objectStore(l).getAll();n.onsuccess=()=>t(n.result),n.onerror=()=>r(n.error)})}async function je(e,t){const r=await b();return new Promise((n,a)=>{const s=r.transaction(l,"readwrite"),i=s.objectStore(l),c=i.get(e);c.onsuccess=()=>{c.result&&i.put({...c.result,...t})},s.oncomplete=()=>n(),s.onerror=()=>a(s.error)})}async function Ue(e){const t=await b();return new Promise((r,n)=>{const a=t.transaction(l,"readwrite");a.objectStore(l).delete(e),a.oncomplete=()=>r(),a.onerror=()=>n(a.error)})}async function Ke(){const e=await b();function t(a){return new Promise((s,i)=>{const c=e.transaction(l,"readonly").objectStore(l).index("status"),u=[],f=c.openCursor(IDBKeyRange.only(a));f.onsuccess=p=>{const m=p.target.result;m?(u.push(m.value),m.continue()):s(u)},f.onerror=()=>i(f.error)})}const[r,n]=await Promise.all([t("pending"),t("failed")]);return[...r,...n]}async function $e(){const e=await b();return new Promise((t,r)=>{const n=e.transaction(l,"readwrite");n.objectStore(l).clear(),n.oncomplete=()=>t(),n.onerror=()=>r(n.error)})}var ne={add:Me,getAll:te,getPending:Ke,update:je,remove:Ue,clear:$e},re=null;function Le(e){re=e}function W(){return re}var We="eidos-queue-sync",k;function ae(){return k!==void 0||(k=typeof BroadcastChannel>"u"?null:new BroadcastChannel(We)),k}function h(e){ae()?.postMessage(e)}function Be(){const e=ae();if(!e)return()=>{};const t=r=>{const n=o.getState(),a=r.data;switch(a.type){case"update":n.updateQueueItem(a.id,a.update);break;case"batchUpdate":n.batchUpdateQueueItems(a.updates);break;case"remove":n.removeQueueItem(a.id);break}};return e.addEventListener("message",t),()=>e.removeEventListener("message",t)}function se(e){let t=0,r=0,n=0;for(const a of e)a.status==="pending"?t++:a.status==="failed"?r++:a.status==="replaying"&&n++;return{pending:t,failed:r,replaying:n,total:e.length}}var A=new Map,ie=new Map,oe=new Map,ce=new Map,R=new Map;function d(){return W()??ne}function j(){return crypto.randomUUID()}function U(e,t,r){return e(...t,r)}function He(e,t){const r=t.name||e.name||j(),n=t.namespace?`${t.namespace}::${r}`:r;if(A.has(n))throw new Error(`[eidos] duplicate action id "${n}" — an action with this id is already registered. Pass a unique config.name or config.namespace.`);A.set(n,e),ce.set(n,t),t.onRollback&&ie.set(n,t.onRollback),t.conflict&&oe.set(n,t.conflict);const a=async(...s)=>{const{isOnline:i}=o.getState(),c=j();let u;if(t.cancellable){const p=new AbortController;R.set(c,p),u=p.signal}const f={idempotencyKey:c,attempt:0,signal:u};t.onOptimistic?.(...s,f);try{if(t.reliability==="neverLose"){if(!i)return F(n,n,s,t,c);try{return await U(e,s,f)}catch(p){if(de(p))throw p;return F(n,n,s,t,c)}}try{return await U(e,s,f)}catch(p){throw t.onRollback?.(...s,f),p}}finally{t.cancellable&&R.delete(c)}};return Object.defineProperty(a,"id",{value:n,writable:!1}),Object.defineProperty(a,"config",{value:t,writable:!1}),Object.defineProperty(a,"cancel",{value:ue,writable:!1}),a}async function ue(e){const t=R.get(e);if(t)return t.abort(),!0;const r=(await d().getAll()).find(n=>n.idempotencyKey===e&&n.status==="pending");return r?(o.getState().removeQueueItem(r.id),h({type:"remove",id:r.id}),await d().remove(r.id),!0):!1}async function Fe(e){const t=(await d().getAll()).find(n=>n.id===e);if(!t||t.status!=="failed")return!1;const r={status:"pending",error:void 0,nextRetryAt:void 0,retryCount:0};return o.getState().updateQueueItem(e,r),h({type:"update",id:e,update:r}),await d().update(e,r),!0}async function F(e,t,r,n,a){const s=j(),i={schemaVersion:2,id:s,actionId:e,actionName:t,idempotencyKey:a,args:r,queuedAt:Date.now(),retryCount:0,maxRetries:n.maxRetries??3,status:"pending",priority:n.priority??"normal"};await d().add(i),o.getState().addQueueItem(i);try{const c=Y();c&&"sync"in c&&await c.sync.register("eidos-queue-replay")}catch{}return{queued:!0,id:s,message:`"${t}" queued — will execute when online`}}function de(e){return e instanceof DOMException&&e.name==="AbortError"}function Ve(e){if(e instanceof Response)return e.status>=400&&e.status<500;if(typeof e=="object"&&e!==null){const t=e.status;if(typeof t=="number")return t>=400&&t<500}return!1}function Ge(e){return Math.min(2e3*2**e,3e5)*(.8+Math.random()*.4)}function O(){return{attempted:0,succeeded:0,failed:0,retrying:0,skipped:0,conflicted:0,cancelled:0}}var P=!1,Ye="eidos-queue-replay";async function q(){const e=o.getState();if(!e.isOnline)return O();if(typeof navigator<"u"&&navigator.locks)return navigator.locks.request(Ye,{ifAvailable:!0},async t=>t?V(e):O());if(P)return O();P=!0;try{return await V(e)}finally{P=!1}}async function Je(e,t){const r=Date.now();t.updateQueueItem(e.id,{status:"succeeded",completedAt:r}),h({type:"update",id:e.id,update:{status:"succeeded",completedAt:r}}),await d().update(e.id,{status:"succeeded",completedAt:r}),setTimeout(()=>{t.removeQueueItem(e.id),h({type:"remove",id:e.id}),d().remove(e.id)},3e3)}async function Xe(e,t,r){const n=oe.get(e.actionId);let a;if(n)switch(n.strategy){case"serverWins":a="skip";break;case"clientWins":a="retry";break;case"merge":case"custom":{const s={error:r,args:e.args,attempt:e.retryCount,idempotencyKey:e.idempotencyKey};a=n.resolve?.(s)??"retry";break}}if(a==="skip")return t.removeQueueItem(e.id),h({type:"remove",id:e.id}),await d().remove(e.id),"conflicted";a&&typeof a=="object"&&(e.args=a.resolved,t.updateQueueItem(e.id,{args:a.resolved}),h({type:"update",id:e.id,update:{args:a.resolved}}),await d().update(e.id,{args:a.resolved}))}async function ze(e,t,r){const n=e.retryCount+1;if(n>=e.maxRetries){const s={status:"failed",error:String(r),retryCount:n};t.updateQueueItem(e.id,s),h({type:"update",id:e.id,update:s}),await d().update(e.id,s);const i={idempotencyKey:e.idempotencyKey,attempt:n};return ie.get(e.actionId)?.(...e.args,i),"failed"}const a={status:"pending",retryCount:n,nextRetryAt:Date.now()+Ge(n)};return t.updateQueueItem(e.id,a),h({type:"update",id:e.id,update:a}),await d().update(e.id,a),"retrying"}async function Ze(e,t){const r=A.get(e.actionId);if(!r)return"skipped";const n=ce.get(e.actionId)?.cancellable;let a;if(n){const i=new AbortController;R.set(e.idempotencyKey,i),a=i.signal}const s={idempotencyKey:e.idempotencyKey,attempt:e.retryCount,signal:a};try{return await U(r,e.args,s),await Je(e,t),"succeeded"}catch(i){if(de(i))return t.removeQueueItem(e.id),h({type:"remove",id:e.id}),await d().remove(e.id),"cancelled";if(Ve(i)){const c=await Xe(e,t,i);if(c)return c}return ze(e,t,i)}finally{n&&R.delete(e.idempotencyKey)}}async function et(e,t,r){if(e.length===0)return;const n=e.filter(s=>A.has(s.actionId));if(r.skipped+=e.length-n.length,n.length>0){const s=n.map(i=>({id:i.id,update:{status:"replaying"}}));t.batchUpdateQueueItems(s),h({type:"batchUpdate",updates:s});for(const i of n)d().update(i.id,{status:"replaying"})}const a=await Promise.allSettled(n.map(s=>Ze(s,t)));for(const s of a){const i=s.status==="fulfilled"?s.value:"failed";i==="skipped"?r.skipped++:i==="conflicted"?r.conflicted++:i==="cancelled"?r.cancelled++:(r.attempted++,r[i]++)}}async function V(e){const t=await d().getPending(),r=Date.now(),n=t.filter(s=>s.retryCount<s.maxRetries&&(!s.nextRetryAt||s.nextRetryAt<=r)),a=O();for(const s of["high","normal","low"])await et(n.filter(i=>(i.priority??"normal")===s),e,a);return a}async function tt(){await d().clear(),o.getState().hydrateQueue([])}function le(){let e=o.getState().isOnline;const t=o.subscribe(()=>{const{isOnline:a}=o.getState(),s=a&&!e;e=a,s&&setTimeout(q,600)}),r=o.getState(),n=r.queue.some(a=>a.status==="pending");return r.isOnline&&n&&setTimeout(q,1200),t}async function nt(e){if(e.schemaVersion===2&&e.idempotencyKey)return e;const t={...e,schemaVersion:2,idempotencyKey:e.idempotencyKey??crypto.randomUUID()};return await(W()??ne).update(t.id,{schemaVersion:t.schemaVersion,idempotencyKey:t.idempotencyKey}).catch(()=>{}),t}var K=!1,$=null,L=null;async function fe(e={}){if(typeof window>"u"||K)return;K=!0;const t=e.swPath??"/eidos-sw.js",r=e.autoReplay??!0;try{const n=await te();if(n.length>0){const a=await Promise.all(n.map(nt));o.getState().hydrateQueue(a)}}catch{}try{await ve(t)}catch{}Se(()=>{o.getState().isOnline&&setTimeout(q,200)}),r&&($=le()),L=Be()}function rt(){$?.(),$=null,L?.(),L=null,K=!1}var N="@eidos:queue",at=class{constructor(e){this.storage=e}async readAll(){try{const e=await this.storage.getItem(N);return e?JSON.parse(e):[]}catch{return[]}}async writeAll(e){await this.storage.setItem(N,JSON.stringify(e))}async add(e){const t=await this.readAll();t.push(e),await this.writeAll(t)}async getAll(){return this.readAll()}async getPending(){return(await this.readAll()).filter(e=>e.status==="pending"||e.status==="failed")}async update(e,t){const r=await this.readAll(),n=r.findIndex(a=>a.id===e);n!==-1&&(r[n]={...r[n],...t}),await this.writeAll(r)}async remove(e){const t=await this.readAll();await this.writeAll(t.filter(r=>r.id!==e))}async clear(){await this.storage.removeItem(N)}};function st({children:e,swPath:t,autoReplay:r}){return(0,S.useEffect)(()=>{fe({swPath:t,autoReplay:r})},[]),(0,B.jsx)(B.Fragment,{children:e})}function y(e){const t=e??(r=>r);return(0,S.useSyncExternalStore)(o.subscribe,()=>t(o.getState()))}function it(){return y()}function ot(){return y(e=>e.resources)}function ct(e){return y(t=>t.resources[e])}function ut(){return y(e=>e.queue)}function dt(e){return y(t=>t.queue.find(r=>r.id===e))}function lt(){return{isOnline:y(e=>e.isOnline),swStatus:y(e=>e.swStatus),swError:y(e=>e.swError)}}function ft(){const[e,t,r,n]=y(a=>{const{pending:s,failed:i,replaying:c,total:u}=se(a.queue);return`${s},${i},${c},${u}`}).split(",");return{pending:+e,failed:+t,replaying:+r,total:+n}}function pt(e){const t=y(a=>a.queue.length),r=(0,S.useRef)(0),n=(0,S.useRef)(e);(0,S.useEffect)(()=>{n.current=e}),(0,S.useEffect)(()=>{r.current>0&&t===0&&n.current(),r.current=t},[t])}var yt="2.1.0";function ht(e,t){const r=Object.keys(e);if(r.length!==Object.keys(t).length)return!1;for(const n of r)if(e[n]!==t[n])return!1;return!0}function pe(e,t){return ht(e,t)}function E(e,t=Object.is){return{subscribe(r){let n=e(o.getState());return r(n),o.subscribe(()=>{const a=e(o.getState());t(n,a)||(n=a,r(a))})},getState(){return e(o.getState())}}}var gt=E(e=>e),wt=E(e=>e.queue),vt=E(e=>({isOnline:e.isOnline,swStatus:e.swStatus,swError:e.swError}),pe),mt=E(e=>se(e.queue),pe);function St(e){return E(t=>t.resources[e])}function bt(e){return E(t=>t.queue.find(r=>r.id===e))}exports.AsyncStorageQueueStorage=at;exports.EidosProvider=st;exports.VERSION=yt;exports._getQueueStorage=W;exports._resetEidos=rt;exports.action=He;exports.cancelByIdempotencyKey=ue;exports.clearQueue=tt;exports.eidosAction=bt;exports.eidosQueue=wt;exports.eidosQueueStats=mt;exports.eidosResource=St;exports.eidosStatus=vt;exports.eidosStore=gt;exports.getSwRegistration=Y;exports.initEidos=fe;exports.isBgSyncSupported=be;exports.registerPushCallbacks=Ee;exports.replayQueue=q;exports.requeueItem=Fe;exports.resource=Ae;exports.resourcePattern=qe;exports.sendToWorker=I;exports.setOfflineSimulation=Ie;exports.setQueryInvalidator=ke;exports.setQueueStorage=Le;exports.subscribeReplayOnReconnect=le;exports.useEidos=it;exports.useEidosAction=dt;exports.useEidosOnDrain=pt;exports.useEidosQueue=ut;exports.useEidosQueueStats=ft;exports.useEidosResource=ct;exports.useEidosResources=ot;exports.useEidosStatus=lt;exports.useEidosStore=o;exports.warmCache=Ne;
|
|
13
|
+
})`}};function V(e,t,n){const r=Me[e],a=t??"eidos-resources-v1";return{name:Te[e],swStrategy:e,cacheName:n!==void 0?`${a}-v${n}`:a,reasoning:r.reasoning,behavior:r.behavior,equivalentCode:""}}async function je(e){const t=await Promise.allSettled(e.map(r=>r.prefetch())),n=t.filter(r=>r.status==="rejected").map(r=>r.reason);return{warmed:t.filter(r=>r.status==="fulfilled").length,failed:n.length,errors:n}}var Ue="eidos",Ke=1,l="action-queue",P=null;function b(){return P?Promise.resolve(P):new Promise((e,t)=>{const n=indexedDB.open(Ue,Ke);n.onupgradeneeded=r=>{const a=r.target.result;if(!a.objectStoreNames.contains(l)){const s=a.createObjectStore(l,{keyPath:"id"});s.createIndex("status","status",{unique:!1}),s.createIndex("actionId","actionId",{unique:!1})}},n.onsuccess=()=>{P=n.result,e(n.result)},n.onerror=()=>t(n.error)})}async function $e(e){const t=await b();return new Promise((n,r)=>{const a=t.transaction(l,"readwrite");a.objectStore(l).add(e),a.oncomplete=()=>n(),a.onerror=()=>r(a.error)})}async function se(){const e=await b();return new Promise((t,n)=>{const r=e.transaction(l,"readonly").objectStore(l).getAll();r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error)})}async function Le(e,t){const n=await b();return new Promise((r,a)=>{const s=n.transaction(l,"readwrite"),i=s.objectStore(l),c=i.get(e);c.onsuccess=()=>{c.result&&i.put({...c.result,...t})},s.oncomplete=()=>r(),s.onerror=()=>a(s.error)})}async function We(e){const t=await b();return new Promise((n,r)=>{const a=t.transaction(l,"readwrite");a.objectStore(l).delete(e),a.oncomplete=()=>n(),a.onerror=()=>r(a.error)})}async function Be(){const e=await b();function t(a){return new Promise((s,i)=>{const c=e.transaction(l,"readonly").objectStore(l).index("status"),u=[],f=c.openCursor(IDBKeyRange.only(a));f.onsuccess=p=>{const S=p.target.result;S?(u.push(S.value),S.continue()):s(u)},f.onerror=()=>i(f.error)})}const[n,r]=await Promise.all([t("pending"),t("failed")]);return[...n,...r]}async function He(){const e=await b();return new Promise((t,n)=>{const r=e.transaction(l,"readwrite");r.objectStore(l).clear(),r.oncomplete=()=>t(),r.onerror=()=>n(r.error)})}var ie={add:$e,getAll:se,getPending:Be,update:Le,remove:We,clear:He},oe=null;function Fe(e){oe=e}function B(){return oe}var Ve="eidos-queue-sync",k;function ce(){return k!==void 0||(k=typeof BroadcastChannel>"u"?null:new BroadcastChannel(Ve)),k}function h(e){ce()?.postMessage(e)}function Ge(){const e=ce();if(!e)return()=>{};const t=n=>{const r=o.getState(),a=n.data;switch(a.type){case"update":r.updateQueueItem(a.id,a.update);break;case"batchUpdate":r.batchUpdateQueueItems(a.updates);break;case"remove":r.removeQueueItem(a.id);break}};return e.addEventListener("message",t),()=>e.removeEventListener("message",t)}var q=new Map,ue=new Map,de=new Map,le=new Map,I=new Map;function d(){return B()??ie}function U(){return crypto.randomUUID()}function K(e,t,n){return e(...t,n)}function Ye(e,t){const n=t.name||e.name||U(),r=t.namespace?`${t.namespace}::${n}`:n;if(q.has(r))throw new Error(`[eidos] duplicate action id "${r}" — an action with this id is already registered. Pass a unique config.name or config.namespace.`);q.set(r,e),le.set(r,t),t.onRollback&&ue.set(r,t.onRollback),t.conflict&&de.set(r,t.conflict);const a=async(...s)=>{const{isOnline:i}=o.getState(),c=U();let u;if(t.cancellable){const p=new AbortController;I.set(c,p),u=p.signal}const f={idempotencyKey:c,attempt:0,signal:u};t.onOptimistic?.(...s,f);try{if(t.reliability==="neverLose"){if(!i)return G(r,r,s,t,c);try{return await K(e,s,f)}catch(p){if(pe(p))throw p;return G(r,r,s,t,c)}}try{return await K(e,s,f)}catch(p){throw t.onRollback?.(...s,f),p}}finally{t.cancellable&&I.delete(c)}};return Object.defineProperty(a,"id",{value:r,writable:!1}),Object.defineProperty(a,"config",{value:t,writable:!1}),Object.defineProperty(a,"cancel",{value:fe,writable:!1}),a}async function fe(e){const t=I.get(e);if(t)return t.abort(),!0;const n=(await d().getAll()).find(r=>r.idempotencyKey===e&&r.status==="pending");return n?(o.getState().removeQueueItem(n.id),h({type:"remove",id:n.id}),await d().remove(n.id),!0):!1}async function Je(e){const t=(await d().getAll()).find(r=>r.id===e);if(!t||t.status!=="failed")return!1;const n={status:"pending",error:void 0,nextRetryAt:void 0,retryCount:0};return o.getState().updateQueueItem(e,n),h({type:"update",id:e,update:n}),await d().update(e,n),!0}async function G(e,t,n,r,a){const s=U(),i={schemaVersion:2,id:s,actionId:e,actionName:t,idempotencyKey:a,args:n,queuedAt:Date.now(),retryCount:0,maxRetries:r.maxRetries??3,status:"pending",priority:r.priority??"normal"};await d().add(i),o.getState().addQueueItem(i),o.getState().recordReliabilityEvent("queued");try{const c=Z();c&&"sync"in c&&await c.sync.register("eidos-queue-replay")}catch{}return{queued:!0,id:s,message:`"${t}" queued — will execute when online`}}function pe(e){return e instanceof DOMException&&e.name==="AbortError"}function Xe(e){if(e instanceof Response)return e.status>=400&&e.status<500;if(typeof e=="object"&&e!==null){const t=e.status;if(typeof t=="number")return t>=400&&t<500}return!1}function ze(e){return Math.min(2e3*2**e,3e5)*(.8+Math.random()*.4)}function O(){return{attempted:0,succeeded:0,failed:0,retrying:0,skipped:0,conflicted:0,cancelled:0}}var D=!1,Ze="eidos-queue-replay";async function Q(){const e=o.getState();if(!e.isOnline)return O();if(typeof navigator<"u"&&navigator.locks)return navigator.locks.request(Ze,{ifAvailable:!0},async t=>t?Y(e):O());if(D)return O();D=!0;try{return await Y(e)}finally{D=!1}}async function et(e,t){const n=Date.now();t.updateQueueItem(e.id,{status:"succeeded",completedAt:n}),t.recordReliabilityEvent("succeeded"),h({type:"update",id:e.id,update:{status:"succeeded",completedAt:n}}),await d().update(e.id,{status:"succeeded",completedAt:n}),setTimeout(()=>{t.removeQueueItem(e.id),h({type:"remove",id:e.id}),d().remove(e.id)},3e3)}async function tt(e,t,n){const r=de.get(e.actionId);let a;if(r)switch(r.strategy){case"serverWins":a="skip";break;case"clientWins":a="retry";break;case"merge":case"custom":{const s={error:n,args:e.args,attempt:e.retryCount,idempotencyKey:e.idempotencyKey};a=r.resolve?.(s)??"retry";break}}if(a==="skip")return t.removeQueueItem(e.id),t.recordReliabilityEvent("conflicted"),h({type:"remove",id:e.id}),await d().remove(e.id),"conflicted";a&&typeof a=="object"&&(e.args=a.resolved,t.updateQueueItem(e.id,{args:a.resolved}),h({type:"update",id:e.id,update:{args:a.resolved}}),await d().update(e.id,{args:a.resolved}))}async function nt(e,t,n){const r=e.retryCount+1;if(r>=e.maxRetries){const s={status:"failed",error:String(n),retryCount:r};t.updateQueueItem(e.id,s),t.recordReliabilityEvent("failed"),h({type:"update",id:e.id,update:s}),await d().update(e.id,s);const i={idempotencyKey:e.idempotencyKey,attempt:r};return ue.get(e.actionId)?.(...e.args,i),"failed"}const a={status:"pending",retryCount:r,nextRetryAt:Date.now()+ze(r)};return t.updateQueueItem(e.id,a),t.recordReliabilityEvent("retried"),h({type:"update",id:e.id,update:a}),await d().update(e.id,a),"retrying"}async function rt(e,t){const n=q.get(e.actionId);if(!n)return"skipped";const r=le.get(e.actionId)?.cancellable;let a;if(r){const i=new AbortController;I.set(e.idempotencyKey,i),a=i.signal}const s={idempotencyKey:e.idempotencyKey,attempt:e.retryCount,signal:a};try{return await K(n,e.args,s),await et(e,t),"succeeded"}catch(i){if(pe(i))return t.removeQueueItem(e.id),t.recordReliabilityEvent("cancelled"),h({type:"remove",id:e.id}),await d().remove(e.id),"cancelled";if(Xe(i)){const c=await tt(e,t,i);if(c)return c}return nt(e,t,i)}finally{r&&I.delete(e.idempotencyKey)}}async function at(e,t,n){if(e.length===0)return;const r=e.filter(s=>q.has(s.actionId));if(n.skipped+=e.length-r.length,r.length>0){const s=r.map(i=>({id:i.id,update:{status:"replaying"}}));t.batchUpdateQueueItems(s),h({type:"batchUpdate",updates:s});for(const i of r)d().update(i.id,{status:"replaying"})}const a=await Promise.allSettled(r.map(s=>rt(s,t)));for(const s of a){const i=s.status==="fulfilled"?s.value:"failed";i==="skipped"?n.skipped++:i==="conflicted"?n.conflicted++:i==="cancelled"?n.cancelled++:(n.attempted++,n[i]++)}}async function Y(e){const t=await d().getPending(),n=Date.now(),r=t.filter(s=>s.retryCount<s.maxRetries&&(!s.nextRetryAt||s.nextRetryAt<=n)),a=O();for(const s of["high","normal","low"])await at(r.filter(i=>(i.priority??"normal")===s),e,a);return a}async function st(){await d().clear(),o.getState().hydrateQueue([])}function ye(){let e=o.getState().isOnline;const t=o.subscribe(()=>{const{isOnline:a}=o.getState(),s=a&&!e;e=a,s&&setTimeout(Q,600)}),n=o.getState(),r=n.queue.some(a=>a.status==="pending");return n.isOnline&&r&&setTimeout(Q,1200),t}async function it(e){if(e.schemaVersion===2&&e.idempotencyKey)return e;const t={...e,schemaVersion:2,idempotencyKey:e.idempotencyKey??crypto.randomUUID()};return await(B()??ie).update(t.id,{schemaVersion:t.schemaVersion,idempotencyKey:t.idempotencyKey}).catch(()=>{}),t}var $=!1,L=null,W=null,A=null;async function he(e={}){if(typeof window>"u"||$)return;$=!0;const t=e.swPath??"/eidos-sw.js",n=e.autoReplay??!0;try{const r=await se();if(r.length>0){const a=await Promise.all(r.map(it));o.getState().hydrateQueue(a)}}catch{}try{await Ee(t)}catch{}if(Ie(()=>{o.getState().isOnline&&setTimeout(Q,200)}),n&&(L=ye()),W=Ge(),e.onReliabilityReport){const r=e.reliabilityReportInterval??6e4,a=e.onReliabilityReport;A=setInterval(()=>{a(o.getState().reliability)},r)}}function ot(){L?.(),L=null,W?.(),W=null,A&&clearInterval(A),A=null,$=!1}var N="@eidos:queue",ct=class{constructor(e){this.storage=e}async readAll(){try{const e=await this.storage.getItem(N);return e?JSON.parse(e):[]}catch{return[]}}async writeAll(e){await this.storage.setItem(N,JSON.stringify(e))}async add(e){const t=await this.readAll();t.push(e),await this.writeAll(t)}async getAll(){return this.readAll()}async getPending(){return(await this.readAll()).filter(e=>e.status==="pending"||e.status==="failed")}async update(e,t){const n=await this.readAll(),r=n.findIndex(a=>a.id===e);r!==-1&&(n[r]={...n[r],...t}),await this.writeAll(n)}async remove(e){const t=await this.readAll();await this.writeAll(t.filter(n=>n.id!==e))}async clear(){await this.storage.removeItem(N)}};function ut({children:e,swPath:t,autoReplay:n}){return(0,R.useEffect)(()=>{he({swPath:t,autoReplay:n})},[]),(0,F.jsx)(F.Fragment,{children:e})}function dt(e,t){const n=Object.keys(e);if(n.length!==Object.keys(t).length)return!1;for(const r of n)if(e[r]!==t[r])return!1;return!0}function H(e,t){return dt(e,t)}function m(e,t=Object.is){return{subscribe(n){let r=e(o.getState());return n(r),o.subscribe(()=>{const a=e(o.getState());t(r,a)||(r=a,n(a))})},getState(){return e(o.getState())}}}var lt=m(e=>e),ft=m(e=>e.queue),pt=m(e=>({isOnline:e.isOnline,swStatus:e.swStatus,swError:e.swError}),H),yt=m(e=>X(e.queue),H),ht=m(e=>e.reliability,H);function gt(e){return m(t=>t.resources[e])}function wt(e){return m(t=>t.queue.find(n=>n.id===e))}function ge(e){let t=o.getState().queue.length;return o.subscribe(()=>{const n=o.getState().queue.length;t>0&&n===0&&e(),t=n})}function y(e){const t=e??(n=>n);return(0,R.useSyncExternalStore)(o.subscribe,()=>t(o.getState()))}function vt(){return y()}function mt(){return y(e=>e.resources)}function St(e){return y(t=>t.resources[e])}function bt(){return y(e=>e.queue)}function Et(e){return y(t=>t.queue.find(n=>n.id===e))}function Rt(){return{isOnline:y(e=>e.isOnline),swStatus:y(e=>e.swStatus),swError:y(e=>e.swError)}}function It(){const[e,t,n,r]=y(a=>{const{pending:s,failed:i,replaying:c,total:u}=X(a.queue);return`${s},${i},${c},${u}`}).split(",");return{pending:+e,failed:+t,replaying:+n,total:+r}}function _t(){return y(e=>e.reliability)}function kt(e){const t=(0,R.useRef)(e);(0,R.useEffect)(()=>{t.current=e}),(0,R.useEffect)(()=>ge(()=>t.current()),[])}var Ot="2.2.0";exports.AsyncStorageQueueStorage=ct;exports.EidosProvider=ut;exports.VERSION=Ot;exports._getQueueStorage=B;exports._resetEidos=ot;exports.action=Ye;exports.cancelByIdempotencyKey=fe;exports.clearQueue=st;exports.eidosAction=wt;exports.eidosQueue=ft;exports.eidosQueueStats=yt;exports.eidosReliabilityStats=ht;exports.eidosResource=gt;exports.eidosStatus=pt;exports.eidosStore=lt;exports.getSwRegistration=Z;exports.initEidos=he;exports.isBgSyncSupported=_e;exports.onQueueDrain=ge;exports.registerPushCallbacks=ke;exports.replayQueue=Q;exports.requeueItem=Je;exports.resource=xe;exports.resourcePattern=Pe;exports.sendToWorker=_;exports.setOfflineSimulation=Ae;exports.setQueryInvalidator=Qe;exports.setQueueStorage=Fe;exports.subscribeReplayOnReconnect=ye;exports.useEidos=vt;exports.useEidosAction=Et;exports.useEidosOnDrain=kt;exports.useEidosQueue=bt;exports.useEidosQueueStats=It;exports.useEidosReliabilityStats=_t;exports.useEidosResource=St;exports.useEidosResources=mt;exports.useEidosStatus=Rt;exports.useEidosStore=o;exports.warmCache=je;
|
|
14
14
|
|
|
15
15
|
//# sourceMappingURL=eidos.cjs.map
|