@sweidos/eidos 2.3.0 → 2.3.1

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 CHANGED
@@ -164,6 +164,7 @@ const products = resource('/api/products', {
164
164
  cacheName?: string, // custom cache bucket
165
165
  maxAge?: number, // TTL in ms — enforced by the SW on all requests (not just handle.fetch())
166
166
  maxEntries?: number, // max cache entries; oldest evicted (FIFO) when exceeded
167
+ networkTimeoutMs?: number, // ms before falling back to cache (network-first & SWR). Default: 3000
167
168
  version?: string | number, // bump when the response shape changes —
168
169
  // appended to cacheName (e.g. 'eidos-resources-v1-v2')
169
170
  // so old-shaped cache entries aren't served.
@@ -658,6 +659,12 @@ Eidos emits plain-English `console.warn` messages in development (`import.meta.e
658
659
  | `Service worker registration failed: …` | Unexpected registration error | Check `eidosDebug().swError` for the full browser error message |
659
660
  | `Service workers are not supported in this context` | Old browser, or SW API absent | No fix needed — Eidos degrades gracefully; only SW-side caching is disabled |
660
661
 
662
+ **[→ Getting started guide](docs/guides/getting-started.md)** — zero-jargon walkthrough: install → Vite plugin → wrap app → first resource + action → offline status UI.
663
+
664
+ **[→ Full troubleshooting guide](docs/guides/troubleshooting.md)** — per-warning copy-pasteable fixes, runtime issues (stuck SW, maxAge, networkTimeoutMs), and `eidosDebug()` field reference.
665
+
666
+ **[→ Glossary](docs/guides/glossary.md)** — plain-language definitions of service worker, cache strategy, idempotency key, replay queue, and more.
667
+
661
668
  ---
662
669
 
663
670
  ## SSR adapters
package/dist/eidos-sw.js CHANGED
@@ -29,7 +29,8 @@ self.addEventListener("message", (event) => {
29
29
  cacheName: data.cacheName ?? `${CACHE_PREFIX}-resources-${CACHE_VERSION}`,
30
30
  ...patternSrc !== void 0 && { pattern: new RegExp(patternSrc) },
31
31
  ...data.maxAge !== void 0 && { maxAge: data.maxAge },
32
- ...data.maxEntries !== void 0 && { maxEntries: data.maxEntries }
32
+ ...data.maxEntries !== void 0 && { maxEntries: data.maxEntries },
33
+ ...data.networkTimeoutMs !== void 0 && { networkTimeoutMs: data.networkTimeoutMs }
33
34
  });
34
35
  event.source?.postMessage({
35
36
  type: "EIDOS_RESOURCE_REGISTERED",
@@ -203,10 +204,10 @@ async function staleWhileRevalidate(event, request, pathname, reg) {
203
204
  return await revalidatePromise ?? offlineErrorResponse(pathname);
204
205
  }
205
206
  async function networkFirst(request, pathname, reg) {
206
- const { cacheName, maxAge, maxEntries } = reg;
207
+ const { cacheName, maxAge, maxEntries, networkTimeoutMs = 3e3 } = reg;
207
208
  const cache = await caches.open(cacheName);
208
209
  try {
209
- const response = await fetch(request, { signal: AbortSignal.timeout(3e3) });
210
+ const response = await fetch(request, { signal: AbortSignal.timeout(networkTimeoutMs) });
210
211
  if (response.ok) {
211
212
  await putCached(cache, request, response.clone());
212
213
  await evictIfNeeded(cache, maxEntries);
package/dist/eidos.cjs CHANGED
@@ -1,4 +1,4 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});let R=require("react"),V=require("react/jsx-runtime");function X(){return{queued:0,succeeded:0,failed:0,retried:0,conflicted:0,cancelled:0}}function z(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 ve(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 me(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 Se(e){return{recordReliabilityEvent:t=>e(n=>({reliability:{...n.reliability,[t]:n.reliability[t]+1}})),resetReliabilityStats:()=>e(()=>({reliability:X()}))}}var w,M=new Set;function Z(){M.forEach(e=>e())}function E(e){w={...w,...e(w)},Z()}w={isOnline:typeof navigator>"u"||navigator.onLine!==!1,swStatus:"idle",swError:void 0,resources:{},queue:[],reliability:X(),setOnline:e=>E(()=>({isOnline:e})),setSwStatus:(e,t)=>E(()=>({swStatus:e,swError:t})),...ve(E),...me(E),...Se(E)};function be(){return w}function Ee(e){return M.add(e),()=>{M.delete(e)}}var o={getState:be,subscribe:Ee,setState:e=>{const t=typeof e=="function"?e(w):e;w={...w,...t},Z()}},h=null,T=[];function H(){return h}async function Re(e,t={skipWaiting:!0}){if(typeof navigator>"u"||!("serviceWorker"in navigator)){o.getState().setSwStatus("unsupported");return}const n=o.getState();n.setSwStatus("registering");try{h=await navigator.serviceWorker.register(e,{scope:"/"}),await Ae(h),n.setSwStatus("active"),navigator.serviceWorker.addEventListener("message",Oe),window.addEventListener("online",()=>n.setOnline(!0)),window.addEventListener("offline",()=>n.setOnline(!1)),qe(),Ce(h,t)}catch(r){n.setSwStatus("error",String(r))}}function Ae(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 I(e){const t=h?.active;t?t.postMessage(e):T.push(e)}var ee=null;function Ie(e){ee=e}function _e(){try{return typeof navigator<"u"&&"serviceWorker"in navigator&&h!==null&&"sync"in h}catch{return!1}}var U={};function ke(e){U=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"){U.onNotificationClick?.(t.data);return}if(r==="EIDOS_SUBSCRIPTION_EXPIRED"){U.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 xe(e){I({type:"EIDOS_SIMULATE_OFFLINE",enabled:e}),o.getState().setOnline(!e)}function qe(){const e=h?.active;if(e){for(const t of T)e.postMessage(t);T=[]}}function Ce(e,t){const n=r=>{t.skipWaiting?r.waiting?.postMessage({type:"EIDOS_SKIP_WAITING"}):t.onUpdateAvailable?.(r)};e.waiting&&navigator.serviceWorker.controller&&n(e),e.addEventListener("updatefound",()=>{const r=e.installing;r&&r.addEventListener("statechange",()=>{r.state==="installed"&&navigator.serviceWorker.controller&&n(e)})})}function Qe(){h?.waiting?.postMessage({type:"EIDOS_SKIP_WAITING"})}var v=new Map,Q=new Map,te=null;function Pe(e){te=e}function C(e){return e.includes("*")||/:[^/]+/.test(e)}function De(e){return"^"+e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*\*/g,".+").replace(/\*/g,"[^/]+").replace(/:[^/]+/g,"[^/]+")+"$"}function ne(e,t){const n=Ue(t),r=C(e)?De(e):void 0,a={url:e,config:t,strategy:n,status:"idle",cacheHits:0,cacheMisses:0};return o.getState().registerResource(e,a),I({type:"EIDOS_REGISTER_RESOURCE",url:e,strategy:n.swStrategy,cacheName:n.cacheName,...r!==void 0&&{pattern:r},...t.maxAge!==void 0&&{maxAge:t.maxAge},...t.maxEntries!==void 0&&{maxEntries:t.maxEntries}}),{strategy:n,regexStr:r}}function re(e,t,n){return async()=>{I({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()=>{v.delete(e),I({type:"EIDOS_UNREGISTER_RESOURCE",url:e}),o.getState().unregisterResource(e)}}function Ne(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(v.has(e))return v.get(e);const{strategy:n}=ne(e,t),r={url:e,config:t,strategy:n,fetch:async()=>{const a=Q.get(e);if(a)return a.then(i=>i.clone());const s=Te(e,t,n);return Q.set(e,s),s.finally(()=>Q.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 v.set(e,r),r}function Me(e,t){if(!C(e))throw new Error(`[eidos] resourcePattern('${e}') is not a URL pattern — use resource('${e}', config) instead.`);if(v.has(e))return v.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 v.set(e,a),a}async function Te(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 Ue(e){const t=e.strategy;return e.offline?G(t??"stale-while-revalidate",e.cacheName,e.version):G(t??"network-first",e.cacheName,e.version)}var je={"stale-while-revalidate":"StaleWhileRevalidate","cache-first":"CacheFirst","network-first":"NetworkFirst"},Ke={"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 (maxEntries/maxAge are configured via ResourceConfig)
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});let R=require("react"),V=require("react/jsx-runtime");function X(){return{queued:0,succeeded:0,failed:0,retried:0,conflicted:0,cancelled:0}}function z(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 ve(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 me(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 Se(e){return{recordReliabilityEvent:t=>e(n=>({reliability:{...n.reliability,[t]:n.reliability[t]+1}})),resetReliabilityStats:()=>e(()=>({reliability:X()}))}}var w,M=new Set;function Z(){M.forEach(e=>e())}function E(e){w={...w,...e(w)},Z()}w={isOnline:typeof navigator>"u"||navigator.onLine!==!1,swStatus:"idle",swError:void 0,resources:{},queue:[],reliability:X(),setOnline:e=>E(()=>({isOnline:e})),setSwStatus:(e,t)=>E(()=>({swStatus:e,swError:t})),...ve(E),...me(E),...Se(E)};function be(){return w}function Ee(e){return M.add(e),()=>{M.delete(e)}}var o={getState:be,subscribe:Ee,setState:e=>{const t=typeof e=="function"?e(w):e;w={...w,...t},Z()}},h=null,T=[];function H(){return h}async function Re(e,t={skipWaiting:!0}){if(typeof navigator>"u"||!("serviceWorker"in navigator)){o.getState().setSwStatus("unsupported");return}const n=o.getState();n.setSwStatus("registering");try{h=await navigator.serviceWorker.register(e,{scope:"/"}),await Ae(h),n.setSwStatus("active"),navigator.serviceWorker.addEventListener("message",Oe),window.addEventListener("online",()=>n.setOnline(!0)),window.addEventListener("offline",()=>n.setOnline(!1)),qe(),Ce(h,t)}catch(r){n.setSwStatus("error",String(r))}}function Ae(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 I(e){const t=h?.active;t?t.postMessage(e):T.push(e)}var ee=null;function Ie(e){ee=e}function ke(){try{return typeof navigator<"u"&&"serviceWorker"in navigator&&h!==null&&"sync"in h}catch{return!1}}var U={};function _e(e){U=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"){U.onNotificationClick?.(t.data);return}if(r==="EIDOS_SUBSCRIPTION_EXPIRED"){U.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 xe(e){I({type:"EIDOS_SIMULATE_OFFLINE",enabled:e}),o.getState().setOnline(!e)}function qe(){const e=h?.active;if(e){for(const t of T)e.postMessage(t);T=[]}}function Ce(e,t){const n=r=>{t.skipWaiting?r.waiting?.postMessage({type:"EIDOS_SKIP_WAITING"}):t.onUpdateAvailable?.(r)};e.waiting&&navigator.serviceWorker.controller&&n(e),e.addEventListener("updatefound",()=>{const r=e.installing;r&&r.addEventListener("statechange",()=>{r.state==="installed"&&navigator.serviceWorker.controller&&n(e)})})}function Qe(){h?.waiting?.postMessage({type:"EIDOS_SKIP_WAITING"})}var v=new Map,Q=new Map,te=null;function Pe(e){te=e}function C(e){return e.includes("*")||/:[^/]+/.test(e)}function De(e){return"^"+e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*\*/g,".+").replace(/\*/g,"[^/]+").replace(/:[^/]+/g,"[^/]+")+"$"}function ne(e,t){const n=Ue(t),r=C(e)?De(e):void 0,a={url:e,config:t,strategy:n,status:"idle",cacheHits:0,cacheMisses:0};return o.getState().registerResource(e,a),I({type:"EIDOS_REGISTER_RESOURCE",url:e,strategy:n.swStrategy,cacheName:n.cacheName,...r!==void 0&&{pattern:r},...t.maxAge!==void 0&&{maxAge:t.maxAge},...t.maxEntries!==void 0&&{maxEntries:t.maxEntries},...t.networkTimeoutMs!==void 0&&{networkTimeoutMs:t.networkTimeoutMs}}),{strategy:n,regexStr:r}}function re(e,t,n){return async()=>{I({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()=>{v.delete(e),I({type:"EIDOS_UNREGISTER_RESOURCE",url:e}),o.getState().unregisterResource(e)}}function Ne(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(v.has(e))return v.get(e);const{strategy:n}=ne(e,t),r={url:e,config:t,strategy:n,fetch:async()=>{const a=Q.get(e);if(a)return a.then(i=>i.clone());const s=Te(e,t,n);return Q.set(e,s),s.finally(()=>Q.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 v.set(e,r),r}function Me(e,t){if(!C(e))throw new Error(`[eidos] resourcePattern('${e}') is not a URL pattern — use resource('${e}', config) instead.`);if(v.has(e))return v.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 v.set(e,a),a}async function Te(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(t.networkTimeoutMs??3e3)}).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 Ue(e){const t=e.strategy;return e.offline?G(t??"stale-while-revalidate",e.cacheName,e.version):G(t??"network-first",e.cacheName,e.version)}var je={"stale-while-revalidate":"StaleWhileRevalidate","cache-first":"CacheFirst","network-first":"NetworkFirst"},Ke={"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 (maxEntries/maxAge are configured via ResourceConfig)
2
2
  new StaleWhileRevalidate({
3
3
  cacheName: 'eidos-resources-v1',
4
4
  plugins: [new ExpirationPlugin({ maxEntries: config.maxEntries, maxAgeSeconds: config.maxAge && config.maxAge / 1000 })],
@@ -9,7 +9,7 @@ new CacheFirst({
9
9
  })`},"network-first":{reasoning:"network-first prioritises fresh data. Cache acts as a safety net when offline. Best for frequently-updated resources where stale data causes problems.",behavior:["Always try network first","Network success → update cache, return fresh response","Network failure → fall back to cached version","Offline with empty cache → return 503 error response"],equivalentCode:`// Workbox equivalent
10
10
  new NetworkFirst({
11
11
  cacheName: 'eidos-resources-v1',
12
- networkTimeoutSeconds: 3,
13
- })`}};function G(e,t,n){const r=Ke[e],a=t??"eidos-resources-v1";return{name:je[e],swStrategy:e,cacheName:n!==void 0?`${a}-v${n}`:a,reasoning:r.reasoning,behavior:r.behavior,equivalentCode:""}}async function We(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 Le="eidos",$e=1,l="action-queue",P=null;function b(){return P?Promise.resolve(P):new Promise((e,t)=>{const n=indexedDB.open(Le,$e);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 He(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 Be(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 Fe(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 Ve(){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 Ge(){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:He,getAll:se,getPending:Ve,update:Be,remove:Fe,clear:Ge},oe=null;function Ye(e){oe=e}function B(){return oe}var Je="eidos-queue-sync",_;function ce(){return _!==void 0||(_=typeof BroadcastChannel>"u"?null:new BroadcastChannel(Je)),_}function g(e){ce()?.postMessage(e)}function Xe(){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 x=new Map,ue=new Map,de=new Map,le=new Map,A=new Map;function d(){return B()??ie}function j(){return crypto.randomUUID()}function K(e,t,n){return e(...t,n)}function ze(e,t){const n=t.name||e.name||j(),r=t.namespace?`${t.namespace}::${n}`:n;if(x.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.`);x.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=j();let u;if(t.cancellable){const p=new AbortController;A.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 Y(r,r,s,t,c);try{return await K(e,s,f)}catch(p){if(pe(p))throw p;return Y(r,r,s,t,c)}}try{return await K(e,s,f)}catch(p){throw t.onRollback?.(...s,f),p}}finally{t.cancellable&&A.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=A.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),g({type:"remove",id:n.id}),await d().remove(n.id),!0):!1}async function Ze(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),g({type:"update",id:e,update:n}),await d().update(e,n),!0}async function Y(e,t,n,r,a){const s=j(),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=H();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 et(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 tt(e){return Math.min(2e3*2**e,3e5)*(.8+Math.random()*.4)}function k(){return{attempted:0,succeeded:0,failed:0,retrying:0,skipped:0,conflicted:0,cancelled:0}}var D=!1,nt="eidos-queue-replay";async function q(){const e=o.getState();if(!e.isOnline)return k();if(typeof navigator<"u"&&navigator.locks)return navigator.locks.request(nt,{ifAvailable:!0},async t=>t?J(e):k());if(D)return k();D=!0;try{return await J(e)}finally{D=!1}}async function rt(e,t){const n=Date.now();t.updateQueueItem(e.id,{status:"succeeded",completedAt:n}),t.recordReliabilityEvent("succeeded"),g({type:"update",id:e.id,update:{status:"succeeded",completedAt:n}}),await d().update(e.id,{status:"succeeded",completedAt:n}),setTimeout(()=>{t.removeQueueItem(e.id),g({type:"remove",id:e.id}),d().remove(e.id)},3e3)}async function at(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"),g({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}),g({type:"update",id:e.id,update:{args:a.resolved}}),await d().update(e.id,{args:a.resolved}))}async function st(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"),g({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()+tt(r)};return t.updateQueueItem(e.id,a),t.recordReliabilityEvent("retried"),g({type:"update",id:e.id,update:a}),await d().update(e.id,a),"retrying"}async function it(e,t){const n=x.get(e.actionId);if(!n)return"skipped";const r=le.get(e.actionId)?.cancellable;let a;if(r){const i=new AbortController;A.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 rt(e,t),"succeeded"}catch(i){if(pe(i))return t.removeQueueItem(e.id),t.recordReliabilityEvent("cancelled"),g({type:"remove",id:e.id}),await d().remove(e.id),"cancelled";if(et(i)){const c=await at(e,t,i);if(c)return c}return st(e,t,i)}finally{r&&A.delete(e.idempotencyKey)}}async function ot(e,t,n){if(e.length===0)return;const r=e.filter(s=>x.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),g({type:"batchUpdate",updates:s});for(const i of r)d().update(i.id,{status:"replaying"})}const a=await Promise.allSettled(r.map(s=>it(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 J(e){const t=await d().getPending(),n=Date.now(),r=t.filter(s=>s.retryCount<s.maxRetries&&(!s.nextRetryAt||s.nextRetryAt<=n)),a=k();for(const s of["high","normal","low"])await ot(r.filter(i=>(i.priority??"normal")===s),e,a);return a}async function ct(){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 ut(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 W=!1,L=null,$=null,O=null;async function ge(e={}){if(typeof window>"u"||W)return;W=!0;const t=e.swPath??"/eidos-sw.js",n=e.autoReplay??!0,r=e.skipWaiting??!0;try{const a=await se();if(a.length>0){const s=await Promise.all(a.map(ut));o.getState().hydrateQueue(s)}}catch{}try{await Re(t,{skipWaiting:r,onUpdateAvailable:e.onUpdateAvailable})}catch{}if(Ie(()=>{o.getState().isOnline&&setTimeout(q,200)}),n&&(L=ye()),$=Xe(),e.onReliabilityReport){const a=e.reliabilityReportInterval??6e4,s=e.onReliabilityReport;O=setInterval(()=>{s(o.getState().reliability)},a)}}function dt(){L?.(),L=null,$?.(),$=null,O&&clearInterval(O),O=null,W=!1}var N="@eidos:queue",lt=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 ft({children:e,swPath:t,autoReplay:n}){return(0,R.useEffect)(()=>{ge({swPath:t,autoReplay:n})},[]),(0,V.jsx)(V.Fragment,{children:e})}function pt(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 F(e,t){return pt(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 yt=m(e=>e),gt=m(e=>e.queue),ht=m(e=>({isOnline:e.isOnline,swStatus:e.swStatus,swError:e.swError}),F),wt=m(e=>z(e.queue),F),vt=m(e=>e.reliability,F);function mt(e){return m(t=>t.resources[e])}function St(e){return m(t=>t.queue.find(n=>n.id===e))}function he(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 bt(){return y()}function Et(){return y(e=>e.resources)}function Rt(e){return y(t=>t.resources[e])}function At(){return y(e=>e.queue)}function It(e){return y(t=>t.queue.find(n=>n.id===e))}function _t(){return{isOnline:y(e=>e.isOnline),swStatus:y(e=>e.swStatus),swError:y(e=>e.swError)}}function kt(){const[e,t,n,r]=y(a=>{const{pending:s,failed:i,replaying:c,total:u}=z(a.queue);return`${s},${i},${c},${u}`}).split(",");return{pending:+e,failed:+t,replaying:+n,total:+r}}function Ot(){return y(e=>e.reliability)}function xt(e){const t=(0,R.useRef)(e);(0,R.useEffect)(()=>{t.current=e}),(0,R.useEffect)(()=>he(()=>t.current()),[])}var we="2.3.0";function qt(){const e=o.getState(),t=H();return{version:we,swStatus:e.swStatus,...e.swError!==void 0&&{swError:e.swError},isOnline:e.isOnline,resourceCount:Object.keys(e.resources).length,resources:Object.fromEntries(Object.entries(e.resources).map(([n,r])=>[n,{url:r.url,strategy:r.strategy.swStrategy,status:r.status,cacheHits:r.cacheHits,cacheMisses:r.cacheMisses,...r.cachedAt!==void 0&&{cachedAt:r.cachedAt}}])),queue:e.queue.map(n=>({id:n.id,actionId:n.actionId,actionName:n.actionName,status:n.status,retryCount:n.retryCount,maxRetries:n.maxRetries,idempotencyKey:n.idempotencyKey,schemaVersion:n.schemaVersion,queuedAt:n.queuedAt})),reliability:{...e.reliability},swRegistration:t?{scope:t.scope,scriptURL:(t.active??t.waiting??t.installing)?.scriptURL??"",state:t.installing?"installing":t.waiting?"waiting":t.active?"active":null}:null}}exports.AsyncStorageQueueStorage=lt;exports.EidosProvider=ft;exports.VERSION=we;exports._getQueueStorage=B;exports._resetEidos=dt;exports.action=ze;exports.cancelByIdempotencyKey=fe;exports.clearQueue=ct;exports.eidosAction=St;exports.eidosDebug=qt;exports.eidosQueue=gt;exports.eidosQueueStats=wt;exports.eidosReliabilityStats=vt;exports.eidosResource=mt;exports.eidosStatus=ht;exports.eidosStore=yt;exports.getSwRegistration=H;exports.initEidos=ge;exports.isBgSyncSupported=_e;exports.onQueueDrain=he;exports.registerPushCallbacks=ke;exports.replayQueue=q;exports.requeueItem=Ze;exports.resource=Ne;exports.resourcePattern=Me;exports.sendToWorker=I;exports.setOfflineSimulation=xe;exports.setQueryInvalidator=Pe;exports.setQueueStorage=Ye;exports.subscribeReplayOnReconnect=ye;exports.triggerSwUpdate=Qe;exports.useEidos=bt;exports.useEidosAction=It;exports.useEidosOnDrain=xt;exports.useEidosQueue=At;exports.useEidosQueueStats=kt;exports.useEidosReliabilityStats=Ot;exports.useEidosResource=Rt;exports.useEidosResources=Et;exports.useEidosStatus=_t;exports.useEidosStore=o;exports.warmCache=We;
12
+ networkTimeoutSeconds: 3, // controlled via ResourceConfig.networkTimeoutMs (default 3000)
13
+ })`}};function G(e,t,n){const r=Ke[e],a=t??"eidos-resources-v1";return{name:je[e],swStrategy:e,cacheName:n!==void 0?`${a}-v${n}`:a,reasoning:r.reasoning,behavior:r.behavior,equivalentCode:""}}async function We(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 Le="eidos",$e=1,l="action-queue",P=null;function b(){return P?Promise.resolve(P):new Promise((e,t)=>{const n=indexedDB.open(Le,$e);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 He(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 Be(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 Fe(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 Ve(){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 Ge(){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:He,getAll:se,getPending:Ve,update:Be,remove:Fe,clear:Ge},oe=null;function Ye(e){oe=e}function B(){return oe}var Je="eidos-queue-sync",k;function ce(){return k!==void 0||(k=typeof BroadcastChannel>"u"?null:new BroadcastChannel(Je)),k}function g(e){ce()?.postMessage(e)}function Xe(){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 x=new Map,ue=new Map,de=new Map,le=new Map,A=new Map;function d(){return B()??ie}function j(){return crypto.randomUUID()}function K(e,t,n){return e(...t,n)}function ze(e,t){const n=t.name||e.name||j(),r=t.namespace?`${t.namespace}::${n}`:n;if(x.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.`);x.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=j();let u;if(t.cancellable){const p=new AbortController;A.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 Y(r,r,s,t,c);try{return await K(e,s,f)}catch(p){if(pe(p))throw p;return Y(r,r,s,t,c)}}try{return await K(e,s,f)}catch(p){throw t.onRollback?.(...s,f),p}}finally{t.cancellable&&A.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=A.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),g({type:"remove",id:n.id}),await d().remove(n.id),!0):!1}async function Ze(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),g({type:"update",id:e,update:n}),await d().update(e,n),!0}async function Y(e,t,n,r,a){const s=j(),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=H();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 et(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 tt(e){return Math.min(2e3*2**e,3e5)*(.8+Math.random()*.4)}function _(){return{attempted:0,succeeded:0,failed:0,retrying:0,skipped:0,conflicted:0,cancelled:0}}var D=!1,nt="eidos-queue-replay";async function q(){const e=o.getState();if(!e.isOnline)return _();if(typeof navigator<"u"&&navigator.locks)return navigator.locks.request(nt,{ifAvailable:!0},async t=>t?J(e):_());if(D)return _();D=!0;try{return await J(e)}finally{D=!1}}async function rt(e,t){const n=Date.now();t.updateQueueItem(e.id,{status:"succeeded",completedAt:n}),t.recordReliabilityEvent("succeeded"),g({type:"update",id:e.id,update:{status:"succeeded",completedAt:n}}),await d().update(e.id,{status:"succeeded",completedAt:n}),setTimeout(()=>{t.removeQueueItem(e.id),g({type:"remove",id:e.id}),d().remove(e.id)},3e3)}async function at(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"),g({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}),g({type:"update",id:e.id,update:{args:a.resolved}}),await d().update(e.id,{args:a.resolved}))}async function st(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"),g({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()+tt(r)};return t.updateQueueItem(e.id,a),t.recordReliabilityEvent("retried"),g({type:"update",id:e.id,update:a}),await d().update(e.id,a),"retrying"}async function it(e,t){const n=x.get(e.actionId);if(!n)return"skipped";const r=le.get(e.actionId)?.cancellable;let a;if(r){const i=new AbortController;A.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 rt(e,t),"succeeded"}catch(i){if(pe(i))return t.removeQueueItem(e.id),t.recordReliabilityEvent("cancelled"),g({type:"remove",id:e.id}),await d().remove(e.id),"cancelled";if(et(i)){const c=await at(e,t,i);if(c)return c}return st(e,t,i)}finally{r&&A.delete(e.idempotencyKey)}}async function ot(e,t,n){if(e.length===0)return;const r=e.filter(s=>x.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),g({type:"batchUpdate",updates:s});for(const i of r)d().update(i.id,{status:"replaying"})}const a=await Promise.allSettled(r.map(s=>it(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 J(e){const t=await d().getPending(),n=Date.now(),r=t.filter(s=>s.retryCount<s.maxRetries&&(!s.nextRetryAt||s.nextRetryAt<=n)),a=_();for(const s of["high","normal","low"])await ot(r.filter(i=>(i.priority??"normal")===s),e,a);return a}async function ct(){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 ut(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 W=!1,L=null,$=null,O=null;async function ge(e={}){if(typeof window>"u"||W)return;W=!0;const t=e.swPath??"/eidos-sw.js",n=e.autoReplay??!0,r=e.skipWaiting??!0;try{const a=await se();if(a.length>0){const s=await Promise.all(a.map(ut));o.getState().hydrateQueue(s)}}catch{}try{await Re(t,{skipWaiting:r,onUpdateAvailable:e.onUpdateAvailable})}catch{}if(Ie(()=>{o.getState().isOnline&&setTimeout(q,200)}),n&&(L=ye()),$=Xe(),e.onReliabilityReport){const a=e.reliabilityReportInterval??6e4,s=e.onReliabilityReport;O=setInterval(()=>{s(o.getState().reliability)},a)}}function dt(){L?.(),L=null,$?.(),$=null,O&&clearInterval(O),O=null,W=!1}var N="@eidos:queue",lt=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 ft({children:e,swPath:t,autoReplay:n}){return(0,R.useEffect)(()=>{ge({swPath:t,autoReplay:n})},[]),(0,V.jsx)(V.Fragment,{children:e})}function pt(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 F(e,t){return pt(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 yt=m(e=>e),gt=m(e=>e.queue),ht=m(e=>({isOnline:e.isOnline,swStatus:e.swStatus,swError:e.swError}),F),wt=m(e=>z(e.queue),F),vt=m(e=>e.reliability,F);function mt(e){return m(t=>t.resources[e])}function St(e){return m(t=>t.queue.find(n=>n.id===e))}function he(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 bt(){return y()}function Et(){return y(e=>e.resources)}function Rt(e){return y(t=>t.resources[e])}function At(){return y(e=>e.queue)}function It(e){return y(t=>t.queue.find(n=>n.id===e))}function kt(){return{isOnline:y(e=>e.isOnline),swStatus:y(e=>e.swStatus),swError:y(e=>e.swError)}}function _t(){const[e,t,n,r]=y(a=>{const{pending:s,failed:i,replaying:c,total:u}=z(a.queue);return`${s},${i},${c},${u}`}).split(",");return{pending:+e,failed:+t,replaying:+n,total:+r}}function Ot(){return y(e=>e.reliability)}function xt(e){const t=(0,R.useRef)(e);(0,R.useEffect)(()=>{t.current=e}),(0,R.useEffect)(()=>he(()=>t.current()),[])}var we="2.3.1";function qt(){const e=o.getState(),t=H();return{version:we,swStatus:e.swStatus,...e.swError!==void 0&&{swError:e.swError},isOnline:e.isOnline,resourceCount:Object.keys(e.resources).length,resources:Object.fromEntries(Object.entries(e.resources).map(([n,r])=>[n,{url:r.url,strategy:r.strategy.swStrategy,status:r.status,cacheHits:r.cacheHits,cacheMisses:r.cacheMisses,...r.cachedAt!==void 0&&{cachedAt:r.cachedAt}}])),queue:e.queue.map(n=>({id:n.id,actionId:n.actionId,actionName:n.actionName,status:n.status,retryCount:n.retryCount,maxRetries:n.maxRetries,idempotencyKey:n.idempotencyKey,schemaVersion:n.schemaVersion,queuedAt:n.queuedAt})),reliability:{...e.reliability},swRegistration:t?{scope:t.scope,scriptURL:(t.active??t.waiting??t.installing)?.scriptURL??"",state:t.installing?"installing":t.waiting?"waiting":t.active?"active":null}:null}}exports.AsyncStorageQueueStorage=lt;exports.EidosProvider=ft;exports.VERSION=we;exports._getQueueStorage=B;exports._resetEidos=dt;exports.action=ze;exports.cancelByIdempotencyKey=fe;exports.clearQueue=ct;exports.eidosAction=St;exports.eidosDebug=qt;exports.eidosQueue=gt;exports.eidosQueueStats=wt;exports.eidosReliabilityStats=vt;exports.eidosResource=mt;exports.eidosStatus=ht;exports.eidosStore=yt;exports.getSwRegistration=H;exports.initEidos=ge;exports.isBgSyncSupported=ke;exports.onQueueDrain=he;exports.registerPushCallbacks=_e;exports.replayQueue=q;exports.requeueItem=Ze;exports.resource=Ne;exports.resourcePattern=Me;exports.sendToWorker=I;exports.setOfflineSimulation=xe;exports.setQueryInvalidator=Pe;exports.setQueueStorage=Ye;exports.subscribeReplayOnReconnect=ye;exports.triggerSwUpdate=Qe;exports.useEidos=bt;exports.useEidosAction=It;exports.useEidosOnDrain=xt;exports.useEidosQueue=At;exports.useEidosQueueStats=_t;exports.useEidosReliabilityStats=Ot;exports.useEidosResource=Rt;exports.useEidosResources=Et;exports.useEidosStatus=kt;exports.useEidosStore=o;exports.warmCache=We;
14
14
 
15
15
  //# sourceMappingURL=eidos.cjs.map