@sweidos/eidos 2.0.0 → 2.1.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 +11 -1
- package/dist/action.js +147 -129
- package/dist/action.js.map +1 -1
- package/dist/devtools.js +85 -1
- package/dist/eidos.cjs +2 -2
- package/dist/eidos.cjs.map +1 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +32 -30
- package/dist/resource.js +22 -22
- package/dist/resource.js.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
package/dist/devtools.js
CHANGED
|
@@ -306,6 +306,55 @@ function qs() {
|
|
|
306
306
|
function callWithContext(fn, args, ctx) {
|
|
307
307
|
return fn(...args, ctx);
|
|
308
308
|
}
|
|
309
|
+
/**
|
|
310
|
+
* Cancel an invocation by its `idempotencyKey` (from `ActionContext` /
|
|
311
|
+
* `onOptimistic`). Aborts the in-flight call if `cancellable: true` and
|
|
312
|
+
* still running, otherwise removes a not-yet-replayed queued item.
|
|
313
|
+
* Returns `true` if something was cancelled/removed.
|
|
314
|
+
*
|
|
315
|
+
* Shared by every `ActionHandle.cancel()` and by devtools, which can't
|
|
316
|
+
* address a specific handle from a queue item alone.
|
|
317
|
+
*/
|
|
318
|
+
async function cancelByIdempotencyKey(idempotencyKey) {
|
|
319
|
+
const controller = _inflightControllers.get(idempotencyKey);
|
|
320
|
+
if (controller) {
|
|
321
|
+
controller.abort();
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
const item = (await qs().getAll()).find((i) => i.idempotencyKey === idempotencyKey && i.status === "pending");
|
|
325
|
+
if (!item) return false;
|
|
326
|
+
useEidosStore.getState().removeQueueItem(item.id);
|
|
327
|
+
broadcastQueueSync({
|
|
328
|
+
type: "remove",
|
|
329
|
+
id: item.id
|
|
330
|
+
});
|
|
331
|
+
await qs().remove(item.id);
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Reset a `'failed'` queue item back to `'pending'` so the next
|
|
336
|
+
* `replayQueue()` retries it — clears `error`/`nextRetryAt` and resets
|
|
337
|
+
* `retryCount` to 0. Returns `true` if the item existed and was failed.
|
|
338
|
+
* Used by devtools' per-item "Retry" action.
|
|
339
|
+
*/
|
|
340
|
+
async function requeueItem(id) {
|
|
341
|
+
const item = (await qs().getAll()).find((i) => i.id === id);
|
|
342
|
+
if (!item || item.status !== "failed") return false;
|
|
343
|
+
const update = {
|
|
344
|
+
status: "pending",
|
|
345
|
+
error: void 0,
|
|
346
|
+
nextRetryAt: void 0,
|
|
347
|
+
retryCount: 0
|
|
348
|
+
};
|
|
349
|
+
useEidosStore.getState().updateQueueItem(id, update);
|
|
350
|
+
broadcastQueueSync({
|
|
351
|
+
type: "update",
|
|
352
|
+
id,
|
|
353
|
+
update
|
|
354
|
+
});
|
|
355
|
+
await qs().update(id, update);
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
309
358
|
function isAbortError(err) {
|
|
310
359
|
return err instanceof DOMException && err.name === "AbortError";
|
|
311
360
|
}
|
|
@@ -658,7 +707,9 @@ var ICONS = {
|
|
|
658
707
|
trash: "M3 6h18M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2m3 0-1 14a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1L4 6h16z",
|
|
659
708
|
arrowUp: "M12 19V5M5 12l7-7 7 7",
|
|
660
709
|
arrowDown: "M12 5v14M19 12l-7 7-7-7",
|
|
661
|
-
clock: "M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20zM12 6v6l4 2"
|
|
710
|
+
clock: "M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20zM12 6v6l4 2",
|
|
711
|
+
x: "M18 6 6 18M6 6l12 12",
|
|
712
|
+
rotateCcw: "M3 12a9 9 0 1 0 2.6-6.4M3 12V5m0 7h7"
|
|
662
713
|
};
|
|
663
714
|
function positionStyle(p) {
|
|
664
715
|
const base = {
|
|
@@ -1022,6 +1073,7 @@ function QueueTab({ queue, onReplay, onClear }) {
|
|
|
1022
1073
|
children: item.priority
|
|
1023
1074
|
}),
|
|
1024
1075
|
/* @__PURE__ */ jsx("span", {
|
|
1076
|
+
title: `idempotencyKey: ${item.idempotencyKey}`,
|
|
1025
1077
|
style: {
|
|
1026
1078
|
flex: 1,
|
|
1027
1079
|
overflow: "hidden",
|
|
@@ -1043,6 +1095,38 @@ function QueueTab({ queue, onReplay, onClear }) {
|
|
|
1043
1095
|
"/",
|
|
1044
1096
|
item.maxRetries
|
|
1045
1097
|
]
|
|
1098
|
+
}),
|
|
1099
|
+
item.status === "pending" && /* @__PURE__ */ jsx("button", {
|
|
1100
|
+
onClick: () => void cancelByIdempotencyKey(item.idempotencyKey),
|
|
1101
|
+
title: "Cancel — remove this item before it's replayed",
|
|
1102
|
+
"aria-label": `Cancel ${item.actionName}`,
|
|
1103
|
+
...withFocusRing(),
|
|
1104
|
+
style: {
|
|
1105
|
+
...btn("ghost"),
|
|
1106
|
+
padding: "2px 4px",
|
|
1107
|
+
minHeight: 18,
|
|
1108
|
+
color: C.red
|
|
1109
|
+
},
|
|
1110
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
1111
|
+
path: ICONS.x,
|
|
1112
|
+
size: 11
|
|
1113
|
+
})
|
|
1114
|
+
}),
|
|
1115
|
+
item.status === "failed" && /* @__PURE__ */ jsx("button", {
|
|
1116
|
+
onClick: () => void requeueItem(item.id),
|
|
1117
|
+
title: "Retry — reset for the next replay",
|
|
1118
|
+
"aria-label": `Retry ${item.actionName}`,
|
|
1119
|
+
...withFocusRing(),
|
|
1120
|
+
style: {
|
|
1121
|
+
...btn("ghost"),
|
|
1122
|
+
padding: "2px 4px",
|
|
1123
|
+
minHeight: 18,
|
|
1124
|
+
color: C.blue
|
|
1125
|
+
},
|
|
1126
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
1127
|
+
path: ICONS.rotateCcw,
|
|
1128
|
+
size: 11
|
|
1129
|
+
})
|
|
1046
1130
|
})
|
|
1047
1131
|
]
|
|
1048
1132
|
}, item.id))
|
package/dist/eidos.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});let S=require("react"),
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});let S=require("react"),B=require("react/jsx-runtime");function ye(e){return{registerResource:(t,r)=>e(n=>({resources:{...n.resources,[t]:r}})),updateResource:(t,r)=>e(n=>({resources:{...n.resources,[t]:n.resources[t]?{...n.resources[t],...r}:n.resources[t]}})),unregisterResource:t=>e(r=>({resources:Object.fromEntries(Object.entries(r.resources).filter(([n])=>n!==t))}))}}function he(e){return{addQueueItem:t=>e(r=>({queue:[...r.queue,t]})),updateQueueItem:(t,r)=>e(n=>({queue:n.queue.map(a=>a.id===t?{...a,...r}:a)})),batchUpdateQueueItems:t=>e(r=>{const n=new Map(t.map(a=>[a.id,a.update]));return{queue:r.queue.map(a=>{const s=n.get(a.id);return s?{...a,...s}:a})}}),removeQueueItem:t=>e(r=>({queue:r.queue.filter(n=>n.id!==t)})),hydrateQueue:t=>e(()=>({queue:t}))}}var g,D=new Set;function G(){D.forEach(e=>e())}function _(e){g={...g,...e(g)},G()}g={isOnline:typeof navigator>"u"||navigator.onLine!==!1,swStatus:"idle",swError:void 0,resources:{},queue:[],setOnline:e=>_(()=>({isOnline:e})),setSwStatus:(e,t)=>_(()=>({swStatus:e,swError:t})),...ye(_),...he(_)};function ge(){return g}function we(e){return D.add(e),()=>{D.delete(e)}}var o={getState:ge,subscribe:we,setState:e=>{const t=typeof e=="function"?e(g):e;g={...g,...t},G()}},v=null,T=[];function Y(){return v}async function ve(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 me(v),t.setSwStatus("active"),navigator.serviceWorker.addEventListener("message",Re),window.addEventListener("online",()=>t.setOnline(!0)),window.addEventListener("offline",()=>t.setOnline(!1)),_e()}catch(r){t.setSwStatus("error",String(r))}}function me(e){return new Promise(t=>{if(e.active){t();return}const r=e.installing??e.waiting;if(!r){t();return}const n=setTimeout(t,1e4);r.addEventListener("statechange",function a(){r.state==="activated"&&(clearTimeout(n),r.removeEventListener("statechange",a),t())})})}function I(e){const t=v?.active;t?t.postMessage(e):T.push(e)}var J=null;function Se(e){J=e}function be(){try{return typeof navigator<"u"&&"serviceWorker"in navigator&&v!==null&&"sync"in v}catch{return!1}}var M={};function Ee(e){M=e}function Re(e){const t=e.data;if(!t?.type)return;const r=o.getState(),{type:n,url:a}=t;if(n==="EIDOS_BACKGROUND_SYNC"){J?.();return}if(n==="EIDOS_NOTIFICATION_CLICK"){M.onNotificationClick?.(t.data);return}if(n==="EIDOS_SUBSCRIPTION_EXPIRED"){M.onSubscriptionExpired?.(t.subscription);return}if(a)switch(n){case"EIDOS_CACHE_HIT":{const s=r.resources[a];r.updateResource(a,{status:"fresh",lastEvent:"cache-hit",cacheHits:(s?.cacheHits??0)+1});break}case"EIDOS_CACHE_UPDATED":r.updateResource(a,{status:"fresh",lastEvent:"cache-updated",cachedAt:Date.now()});break;case"EIDOS_NETWORK_ERROR":r.updateResource(a,{status:"error",lastEvent:"network-error"});break}}function Ie(e){I({type:"EIDOS_SIMULATE_OFFLINE",enabled:e}),o.getState().setOnline(!e)}function _e(){const e=v?.active;if(e){for(const t of T)e.postMessage(t);T=[]}}var w=new Map,C=new Map,X=null;function ke(e){X=e}function Q(e){return e.includes("*")||/:[^/]+/.test(e)}function Oe(e){return"^"+e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*\*/g,".+").replace(/\*/g,"[^/]+").replace(/:[^/]+/g,"[^/]+")+"$"}function z(e,t){const r=Ce(t),n=Q(e)?Oe(e):void 0,a={url:e,config:t,strategy:r,status:"idle",cacheHits:0,cacheMisses:0};return o.getState().registerResource(e,a),I({type:"EIDOS_REGISTER_RESOURCE",url:e,strategy:r.swStrategy,cacheName:r.cacheName,...n!==void 0&&{pattern:n}}),{strategy:r,regexStr:n}}function Z(e,t,r){return async()=>{I({type:"EIDOS_CLEAR_CACHE",url:e});const n=await caches.open(t.cacheName).catch(()=>null);if(n){const a=await n.keys(),s=r?new RegExp(r):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=>n.delete(c)))}Q(e)||o.getState().updateResource(e,{status:"stale",cachedAt:void 0,lastEvent:"cache-cleared",cacheHits:0,cacheMisses:0}),X?.(["eidos",e])}}function ee(e){return()=>{w.delete(e),I({type:"EIDOS_UNREGISTER_RESOURCE",url:e}),o.getState().unregisterResource(e)}}function Ae(e,t){if(Q(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:r}=z(e,t),n={url:e,config:t,strategy:r,fetch:async()=>{const a=C.get(e);if(a)return a.then(i=>i.clone());const s=Qe(e,t,r);return C.set(e,s),s.finally(()=>C.delete(e)).catch(()=>{}),s.then(i=>i.clone())},json:async()=>(await n.fetch()).json(),query:()=>({queryKey:["eidos",e],queryFn:()=>n.json()}),prefetch:async()=>{await n.fetch()},invalidate:Z(e,r,void 0),unregister:ee(e)};return w.set(e,n),n}function qe(e,t){if(!Q(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:r,regexStr:n}=z(e,t),a={url:e,config:t,strategy:r,invalidate:Z(e,r,n),unregister:ee(e)};return w.set(e,a),a}async function Qe(e,t,r){const n=o.getState();n.updateResource(e,{status:"fetching",fetchedAt:Date.now()});const a=await caches.open(r.cacheName).catch(()=>null);try{if(r.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 n.updateResource(e,{status:"fresh",lastEvent:"cache-hit",cacheHits:(u?.cacheHits??0)+1}),r.swStrategy==="stale-while-revalidate"&&fetch(e,{signal:AbortSignal.timeout(5e3)}).then(async m=>{m.ok&&a&&(await a.put(e,m.clone()),o.getState().updateResource(e,{cachedAt:Date.now(),lastEvent:"cache-updated"}))}).catch(()=>{}),c;const p=o.getState().resources[e];n.updateResource(e,{cacheMisses:(p?.cacheMisses??0)+1})}const s=await fetch(e);if(s.ok)return a&&await a.put(e,s.clone()),n.updateResource(e,{status:"fresh",cachedAt:Date.now(),lastEvent:"cache-updated"}),s;n.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 n.updateResource(e,{status:"fresh",lastEvent:"cache-hit",cacheHits:(c?.cacheHits??0)+1}),i}throw n.updateResource(e,{status:"error"}),s}}function Ce(e){const t=e.strategy;return e.offline?H(t??"stale-while-revalidate",e.cacheName,e.version):H(t??"network-first",e.cacheName,e.version)}var xe={"stale-while-revalidate":"StaleWhileRevalidate","cache-first":"CacheFirst","network-first":"NetworkFirst"},Pe={"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 B(e,t){const r=xe[e];return{name:Ce[e],swStrategy:e,cacheName:t??"eidos-resources-v1",reasoning:r.reasoning,behavior:r.behavior,equivalentCode:""}}async function Pe(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 Ne="eidos",De=1,f="action-queue",x=null;function b(){return x?Promise.resolve(x):new Promise((e,t)=>{const r=indexedDB.open(Ne,De);r.onupgradeneeded=n=>{const a=n.target.result;if(!a.objectStoreNames.contains(f)){const s=a.createObjectStore(f,{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 Te(e){const t=await b();return new Promise((r,n)=>{const a=t.transaction(f,"readwrite");a.objectStore(f).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(f,"readonly").objectStore(f).getAll();n.onsuccess=()=>t(n.result),n.onerror=()=>r(n.error)})}async function Me(e,t){const r=await b();return new Promise((n,a)=>{const s=r.transaction(f,"readwrite"),i=s.objectStore(f),c=i.get(e);c.onsuccess=()=>{c.result&&i.put({...c.result,...t})},s.oncomplete=()=>n(),s.onerror=()=>a(s.error)})}async function je(e){const t=await b();return new Promise((r,n)=>{const a=t.transaction(f,"readwrite");a.objectStore(f).delete(e),a.oncomplete=()=>r(),a.onerror=()=>n(a.error)})}async function Ue(){const e=await b();function t(a){return new Promise((s,i)=>{const c=e.transaction(f,"readonly").objectStore(f).index("status"),u=[],d=c.openCursor(IDBKeyRange.only(a));d.onsuccess=g=>{const l=g.target.result;l?(u.push(l.value),l.continue()):s(u)},d.onerror=()=>i(d.error)})}const[r,n]=await Promise.all([t("pending"),t("failed")]);return[...r,...n]}async function Ke(){const e=await b();return new Promise((t,r)=>{const n=e.transaction(f,"readwrite");n.objectStore(f).clear(),n.oncomplete=()=>t(),n.onerror=()=>r(n.error)})}var ne={add:Te,getAll:te,getPending:Ue,update:Me,remove:je,clear:Ke},re=null;function Le(e){re=e}function $(){return re}var We="eidos-queue-sync",I;function ae(){return I!==void 0||(I=typeof BroadcastChannel>"u"?null:new BroadcastChannel(We)),I}function y(e){ae()?.postMessage(e)}function $e(){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 p(){return $()??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(...i)=>{const{isOnline:c}=o.getState(),u=j();let d;if(t.cancellable){const l=new AbortController;R.set(u,l),d=l.signal}const g={idempotencyKey:u,attempt:0,signal:d};t.onOptimistic?.(...i,g);try{if(t.reliability==="neverLose"){if(!c)return F(n,n,i,t,u);try{return await U(e,i,g)}catch(l){if(ue(l))throw l;return F(n,n,i,t,u)}}try{return await U(e,i,g)}catch(l){throw t.onRollback?.(...i,g),l}}finally{t.cancellable&&R.delete(u)}},s=async i=>{const c=R.get(i);if(c)return c.abort(),!0;const u=(await p().getAll()).find(d=>d.idempotencyKey===i&&d.status==="pending");return u?(o.getState().removeQueueItem(u.id),y({type:"remove",id:u.id}),await p().remove(u.id),!0):!1};return Object.defineProperty(a,"id",{value:n,writable:!1}),Object.defineProperty(a,"config",{value:t,writable:!1}),Object.defineProperty(a,"cancel",{value:s,writable:!1}),a}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 p().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 ue(e){return e instanceof DOMException&&e.name==="AbortError"}function Be(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 Fe(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,Ve="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(Ve,{ifAvailable:!0},async t=>t?V(e):O());if(P)return O();P=!0;try{return await V(e)}finally{P=!1}}async function Ge(e,t){const r=Date.now();t.updateQueueItem(e.id,{status:"succeeded",completedAt:r}),y({type:"update",id:e.id,update:{status:"succeeded",completedAt:r}}),await p().update(e.id,{status:"succeeded",completedAt:r}),setTimeout(()=>{t.removeQueueItem(e.id),y({type:"remove",id:e.id}),p().remove(e.id)},3e3)}async function Ye(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),y({type:"remove",id:e.id}),await p().remove(e.id),"conflicted";a&&typeof a=="object"&&(e.args=a.resolved,t.updateQueueItem(e.id,{args:a.resolved}),y({type:"update",id:e.id,update:{args:a.resolved}}),await p().update(e.id,{args:a.resolved}))}async function Je(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),y({type:"update",id:e.id,update:s}),await p().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()+Fe(n)};return t.updateQueueItem(e.id,a),y({type:"update",id:e.id,update:a}),await p().update(e.id,a),"retrying"}async function Xe(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 Ge(e,t),"succeeded"}catch(i){if(ue(i))return t.removeQueueItem(e.id),y({type:"remove",id:e.id}),await p().remove(e.id),"cancelled";if(Be(i)){const c=await Ye(e,t,i);if(c)return c}return Je(e,t,i)}finally{n&&R.delete(e.idempotencyKey)}}async function ze(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),y({type:"batchUpdate",updates:s});for(const i of n)p().update(i.id,{status:"replaying"})}const a=await Promise.allSettled(n.map(s=>Xe(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 p().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 ze(n.filter(i=>(i.priority??"normal")===s),e,a);return a}async function Ze(){await p().clear(),o.getState().hydrateQueue([])}function de(){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 et(e){if(e.schemaVersion===2&&e.idempotencyKey)return e;const t={...e,schemaVersion:2,idempotencyKey:e.idempotencyKey??crypto.randomUUID()};return await($()??ne).update(t.id,{schemaVersion:t.schemaVersion,idempotencyKey:t.idempotencyKey}).catch(()=>{}),t}var K=!1,L=null,W=null;async function le(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(et));o.getState().hydrateQueue(a)}}catch{}try{await we(t)}catch{}me(()=>{o.getState().isOnline&&setTimeout(Q,200)}),r&&(L=de()),W=$e()}function tt(){L?.(),L=null,W?.(),W=null,K=!1}var N="@eidos:queue",nt=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 rt({children:e,swPath:t,autoReplay:r}){return(0,S.useEffect)(()=>{le({swPath:t,autoReplay:r})},[]),(0,H.jsx)(H.Fragment,{children:e})}function h(e){const t=e??(r=>r);return(0,S.useSyncExternalStore)(o.subscribe,()=>t(o.getState()))}function at(){return h()}function st(){return h(e=>e.resources)}function it(e){return h(t=>t.resources[e])}function ot(){return h(e=>e.queue)}function ct(e){return h(t=>t.queue.find(r=>r.id===e))}function ut(){return{isOnline:h(e=>e.isOnline),swStatus:h(e=>e.swStatus),swError:h(e=>e.swError)}}function dt(){const[e,t,r,n]=h(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 lt(e){const t=h(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 ft="2.0.0";function pt(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 fe(e,t){return pt(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 ht=E(e=>e),yt=E(e=>e.queue),gt=E(e=>({isOnline:e.isOnline,swStatus:e.swStatus,swError:e.swError}),fe),wt=E(e=>se(e.queue),fe);function vt(e){return E(t=>t.resources[e])}function mt(e){return E(t=>t.queue.find(r=>r.id===e))}exports.AsyncStorageQueueStorage=nt;exports.EidosProvider=rt;exports.VERSION=ft;exports._getQueueStorage=$;exports._resetEidos=tt;exports.action=He;exports.clearQueue=Ze;exports.eidosAction=mt;exports.eidosQueue=yt;exports.eidosQueueStats=wt;exports.eidosResource=vt;exports.eidosStatus=gt;exports.eidosStore=ht;exports.getSwRegistration=Y;exports.initEidos=le;exports.isBgSyncSupported=Se;exports.registerPushCallbacks=be;exports.replayQueue=Q;exports.resource=Oe;exports.resourcePattern=Ae;exports.sendToWorker=_;exports.setOfflineSimulation=Re;exports.setQueryInvalidator=ke;exports.setQueueStorage=Le;exports.subscribeReplayOnReconnect=de;exports.useEidos=at;exports.useEidosAction=ct;exports.useEidosOnDrain=lt;exports.useEidosQueue=ot;exports.useEidosQueueStats=dt;exports.useEidosResource=it;exports.useEidosResources=st;exports.useEidosStatus=ut;exports.useEidosStore=o;exports.warmCache=Pe;
|
|
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;
|
|
14
14
|
|
|
15
15
|
//# sourceMappingURL=eidos.cjs.map
|