@sweidos/eidos 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/eidos-sw.js CHANGED
@@ -1,213 +1,308 @@
1
- const CACHE_VERSION = "v1";
2
- const CACHE_PREFIX = "eidos";
3
- const runtimeConfig = {
4
- resources: /* @__PURE__ */ new Map(),
5
- simulateOffline: false
1
+ //#region ../core/src/internal/url-base64.ts
2
+ /** Decodes a base64url string (e.g. a VAPID public key) into raw bytes. */
3
+ function urlBase64ToUint8Array(base64Url) {
4
+ const base64 = (base64Url + "=".repeat((4 - base64Url.length % 4) % 4)).replace(/-/g, "+").replace(/_/g, "/");
5
+ const raw = atob(base64);
6
+ return Uint8Array.from(raw, (c) => c.charCodeAt(0));
7
+ }
8
+ //#endregion
9
+ //#region src/sw.ts
10
+ var CACHE_VERSION = "v1";
11
+ var CACHE_PREFIX = "eidos";
12
+ var runtimeConfig = {
13
+ resources: /* @__PURE__ */ new Map(),
14
+ simulateOffline: false
6
15
  };
7
16
  self.addEventListener("install", (event) => {
8
- event.waitUntil(self.skipWaiting());
17
+ event.waitUntil(self.skipWaiting());
9
18
  });
10
19
  self.addEventListener("activate", (event) => {
11
- event.waitUntil(
12
- Promise.all([
13
- self.clients.claim(),
14
- // Purge stale caches from previous versions
15
- caches.keys().then(
16
- (keys) => Promise.all(
17
- keys.filter((k) => k.startsWith(CACHE_PREFIX) && !k.endsWith(CACHE_VERSION)).map((k) => caches.delete(k))
18
- )
19
- )
20
- ])
21
- );
20
+ event.waitUntil(Promise.all([self.clients.claim(), caches.keys().then((keys) => Promise.all(keys.filter((k) => k.startsWith(CACHE_PREFIX) && !k.endsWith(CACHE_VERSION)).map((k) => caches.delete(k))))]));
22
21
  });
23
22
  self.addEventListener("message", (event) => {
24
- const data = event.data;
25
- if (!data?.type) return;
26
- switch (data.type) {
27
- case "EIDOS_REGISTER_RESOURCE": {
28
- const url = data.url;
29
- const patternSrc = data.pattern;
30
- runtimeConfig.resources.set(url, {
31
- strategy: data.strategy,
32
- cacheName: data.cacheName ?? `${CACHE_PREFIX}-resources-${CACHE_VERSION}`,
33
- ...patternSrc !== void 0 && { pattern: new RegExp(patternSrc) }
34
- });
35
- event.source?.postMessage({ type: "EIDOS_RESOURCE_REGISTERED", url });
36
- break;
37
- }
38
- case "EIDOS_UNREGISTER_RESOURCE": {
39
- runtimeConfig.resources.delete(data.url);
40
- break;
41
- }
42
- case "EIDOS_SIMULATE_OFFLINE": {
43
- runtimeConfig.simulateOffline = data.enabled;
44
- break;
45
- }
46
- case "EIDOS_CLEAR_CACHE": {
47
- const targetUrl = data.url;
48
- const reg = targetUrl ? runtimeConfig.resources.get(targetUrl) : void 0;
49
- const cacheName = reg?.cacheName ?? `${CACHE_PREFIX}-resources-${CACHE_VERSION}`;
50
- caches.open(cacheName).then(async (cache) => {
51
- if (targetUrl) {
52
- const keys = await cache.keys();
53
- const isCrossOrigin = targetUrl.startsWith("http");
54
- await Promise.all(
55
- keys.filter((req) => {
56
- const reqUrl = req.url;
57
- const p = new URL(reqUrl).pathname;
58
- if (reg?.pattern) {
59
- return reg.pattern.test(isCrossOrigin ? reqUrl : p);
60
- }
61
- return isCrossOrigin ? reqUrl === targetUrl : p === targetUrl;
62
- }).map((req) => cache.delete(req))
63
- );
64
- } else {
65
- await cache.keys().then((keys) => Promise.all(keys.map((k) => cache.delete(k))));
66
- }
67
- notifyClients({ type: "EIDOS_CACHE_CLEARED", url: targetUrl });
68
- });
69
- break;
70
- }
71
- case "EIDOS_PING":
72
- event.source?.postMessage({ type: "EIDOS_PONG" });
73
- break;
74
- }
23
+ const data = event.data;
24
+ if (!data?.type) return;
25
+ switch (data.type) {
26
+ case "EIDOS_REGISTER_RESOURCE": {
27
+ const url = data.url;
28
+ const patternSrc = data.pattern;
29
+ runtimeConfig.resources.set(url, {
30
+ strategy: data.strategy,
31
+ cacheName: data.cacheName ?? `${CACHE_PREFIX}-resources-${CACHE_VERSION}`,
32
+ ...patternSrc !== void 0 && { pattern: new RegExp(patternSrc) }
33
+ });
34
+ event.source?.postMessage({
35
+ type: "EIDOS_RESOURCE_REGISTERED",
36
+ url
37
+ });
38
+ break;
39
+ }
40
+ case "EIDOS_UNREGISTER_RESOURCE":
41
+ runtimeConfig.resources.delete(data.url);
42
+ break;
43
+ case "EIDOS_SIMULATE_OFFLINE":
44
+ runtimeConfig.simulateOffline = data.enabled;
45
+ break;
46
+ case "EIDOS_CLEAR_CACHE": {
47
+ const targetUrl = data.url;
48
+ const reg = targetUrl ? runtimeConfig.resources.get(targetUrl) : void 0;
49
+ const cacheName = reg?.cacheName ?? `${CACHE_PREFIX}-resources-${CACHE_VERSION}`;
50
+ caches.open(cacheName).then(async (cache) => {
51
+ if (targetUrl) {
52
+ const keys = await cache.keys();
53
+ const isCrossOrigin = targetUrl.startsWith("http");
54
+ await Promise.all(keys.filter((req) => {
55
+ const reqUrl = req.url;
56
+ const p = new URL(reqUrl).pathname;
57
+ if (reg?.pattern) return reg.pattern.test(isCrossOrigin ? reqUrl : p);
58
+ return isCrossOrigin ? reqUrl === targetUrl : p === targetUrl;
59
+ }).map((req) => cache.delete(req)));
60
+ } else await cache.keys().then((keys) => Promise.all(keys.map((k) => cache.delete(k))));
61
+ notifyClients({
62
+ type: "EIDOS_CACHE_CLEARED",
63
+ url: targetUrl
64
+ });
65
+ });
66
+ break;
67
+ }
68
+ case "EIDOS_PING":
69
+ event.source?.postMessage({ type: "EIDOS_PONG" });
70
+ break;
71
+ case "EIDOS_CACHE_VAPID_KEY":
72
+ idbSet("vapidPublicKey", data.key);
73
+ break;
74
+ }
75
75
  });
76
76
  self.addEventListener("fetch", (event) => {
77
- if (event.request.method !== "GET") return;
78
- const requestUrl = event.request.url;
79
- const pathname = new URL(requestUrl).pathname;
80
- let reg = runtimeConfig.resources.get(requestUrl) ?? runtimeConfig.resources.get(pathname);
81
- if (!reg) {
82
- for (const [key, registration] of runtimeConfig.resources) {
83
- if (!registration.pattern) continue;
84
- const target = key.startsWith("http") ? requestUrl : pathname;
85
- if (registration.pattern.test(target)) {
86
- reg = registration;
87
- break;
88
- }
89
- }
90
- }
91
- if (!reg) return;
92
- if (reg.strategy === "stale-while-revalidate" && !runtimeConfig.simulateOffline) {
93
- event.respondWith(staleWhileRevalidate(event, event.request, pathname, reg.cacheName));
94
- return;
95
- }
96
- event.respondWith(handleFetch(event.request, pathname, reg));
77
+ if (event.request.method !== "GET") return;
78
+ const requestUrl = event.request.url;
79
+ const pathname = new URL(requestUrl).pathname;
80
+ let reg = runtimeConfig.resources.get(requestUrl) ?? runtimeConfig.resources.get(pathname);
81
+ if (!reg) for (const [key, registration] of runtimeConfig.resources) {
82
+ if (!registration.pattern) continue;
83
+ const target = key.startsWith("http") ? requestUrl : pathname;
84
+ if (registration.pattern.test(target)) {
85
+ reg = registration;
86
+ break;
87
+ }
88
+ }
89
+ if (!reg) return;
90
+ if (reg.strategy === "stale-while-revalidate" && !runtimeConfig.simulateOffline) {
91
+ event.respondWith(staleWhileRevalidate(event, event.request, pathname, reg.cacheName));
92
+ return;
93
+ }
94
+ event.respondWith(handleFetch(event.request, pathname, reg));
97
95
  });
98
96
  async function handleFetch(request, pathname, reg) {
99
- if (runtimeConfig.simulateOffline) {
100
- return serveOffline(request, pathname, reg.cacheName);
101
- }
102
- switch (reg.strategy) {
103
- case "cache-first":
104
- return cacheFirst(request, pathname, reg.cacheName);
105
- case "stale-while-revalidate":
106
- return staleWhileRevalidate(null, request, pathname, reg.cacheName);
107
- case "network-first":
108
- return networkFirst(request, pathname, reg.cacheName);
109
- default:
110
- return fetch(request);
111
- }
97
+ if (runtimeConfig.simulateOffline) return serveOffline(request, pathname, reg.cacheName);
98
+ switch (reg.strategy) {
99
+ case "cache-first": return cacheFirst(request, pathname, reg.cacheName);
100
+ case "stale-while-revalidate": return staleWhileRevalidate(null, request, pathname, reg.cacheName);
101
+ case "network-first": return networkFirst(request, pathname, reg.cacheName);
102
+ default: return fetch(request);
103
+ }
112
104
  }
113
105
  async function cacheFirst(request, pathname, cacheName) {
114
- const cache = await caches.open(cacheName);
115
- const cached = await cache.match(request);
116
- if (cached) {
117
- notifyClients({ type: "EIDOS_CACHE_HIT", url: pathname, strategy: "cache-first" });
118
- return cached;
119
- }
120
- try {
121
- const response = await fetch(request);
122
- if (response.ok) {
123
- await cache.put(request, response.clone());
124
- notifyClients({ type: "EIDOS_CACHE_UPDATED", url: pathname, strategy: "cache-first" });
125
- }
126
- return response;
127
- } catch {
128
- notifyClients({ type: "EIDOS_NETWORK_ERROR", url: pathname });
129
- return offlineErrorResponse(pathname);
130
- }
106
+ const cache = await caches.open(cacheName);
107
+ const cached = await cache.match(request);
108
+ if (cached) {
109
+ notifyClients({
110
+ type: "EIDOS_CACHE_HIT",
111
+ url: pathname,
112
+ strategy: "cache-first"
113
+ });
114
+ return cached;
115
+ }
116
+ try {
117
+ const response = await fetch(request);
118
+ if (response.ok) {
119
+ await cache.put(request, response.clone());
120
+ notifyClients({
121
+ type: "EIDOS_CACHE_UPDATED",
122
+ url: pathname,
123
+ strategy: "cache-first"
124
+ });
125
+ }
126
+ return response;
127
+ } catch {
128
+ notifyClients({
129
+ type: "EIDOS_NETWORK_ERROR",
130
+ url: pathname
131
+ });
132
+ return offlineErrorResponse(pathname);
133
+ }
131
134
  }
132
135
  async function staleWhileRevalidate(event, request, pathname, cacheName) {
133
- const cache = await caches.open(cacheName);
134
- const cached = await cache.match(request);
135
- const revalidatePromise = fetch(request).then(async (response) => {
136
- if (response.ok) {
137
- await cache.put(request, response.clone());
138
- notifyClients({
139
- type: "EIDOS_CACHE_UPDATED",
140
- url: pathname,
141
- strategy: "stale-while-revalidate"
142
- });
143
- }
144
- return response;
145
- }).catch(() => {
146
- notifyClients({ type: "EIDOS_NETWORK_ERROR", url: pathname, strategy: "stale-while-revalidate" });
147
- });
148
- if (cached) {
149
- event?.waitUntil(revalidatePromise);
150
- notifyClients({
151
- type: "EIDOS_CACHE_HIT",
152
- url: pathname,
153
- strategy: "stale-while-revalidate"
154
- });
155
- return cached;
156
- }
157
- const fresh = await revalidatePromise;
158
- return fresh ?? offlineErrorResponse(pathname);
136
+ const cache = await caches.open(cacheName);
137
+ const cached = await cache.match(request);
138
+ const revalidatePromise = fetch(request).then(async (response) => {
139
+ if (response.ok) {
140
+ await cache.put(request, response.clone());
141
+ notifyClients({
142
+ type: "EIDOS_CACHE_UPDATED",
143
+ url: pathname,
144
+ strategy: "stale-while-revalidate"
145
+ });
146
+ }
147
+ return response;
148
+ }).catch(() => {
149
+ notifyClients({
150
+ type: "EIDOS_NETWORK_ERROR",
151
+ url: pathname,
152
+ strategy: "stale-while-revalidate"
153
+ });
154
+ });
155
+ if (cached) {
156
+ event?.waitUntil(revalidatePromise);
157
+ notifyClients({
158
+ type: "EIDOS_CACHE_HIT",
159
+ url: pathname,
160
+ strategy: "stale-while-revalidate"
161
+ });
162
+ return cached;
163
+ }
164
+ return await revalidatePromise ?? offlineErrorResponse(pathname);
159
165
  }
160
166
  async function networkFirst(request, pathname, cacheName) {
161
- const cache = await caches.open(cacheName);
162
- try {
163
- const response = await fetch(request, { signal: AbortSignal.timeout(3e3) });
164
- if (response.ok) {
165
- await cache.put(request, response.clone());
166
- notifyClients({ type: "EIDOS_CACHE_UPDATED", url: pathname, strategy: "network-first" });
167
- }
168
- return response;
169
- } catch {
170
- const cached = await cache.match(request);
171
- if (cached) {
172
- notifyClients({ type: "EIDOS_CACHE_HIT", url: pathname, strategy: "network-first" });
173
- return cached;
174
- }
175
- notifyClients({ type: "EIDOS_NETWORK_ERROR", url: pathname });
176
- return offlineErrorResponse(pathname);
177
- }
167
+ const cache = await caches.open(cacheName);
168
+ try {
169
+ const response = await fetch(request, { signal: AbortSignal.timeout(3e3) });
170
+ if (response.ok) {
171
+ await cache.put(request, response.clone());
172
+ notifyClients({
173
+ type: "EIDOS_CACHE_UPDATED",
174
+ url: pathname,
175
+ strategy: "network-first"
176
+ });
177
+ }
178
+ return response;
179
+ } catch {
180
+ const cached = await cache.match(request);
181
+ if (cached) {
182
+ notifyClients({
183
+ type: "EIDOS_CACHE_HIT",
184
+ url: pathname,
185
+ strategy: "network-first"
186
+ });
187
+ return cached;
188
+ }
189
+ notifyClients({
190
+ type: "EIDOS_NETWORK_ERROR",
191
+ url: pathname
192
+ });
193
+ return offlineErrorResponse(pathname);
194
+ }
178
195
  }
179
196
  async function serveOffline(request, pathname, cacheName) {
180
- const cache = await caches.open(cacheName);
181
- const cached = await cache.match(request);
182
- if (cached) {
183
- notifyClients({ type: "EIDOS_CACHE_HIT", url: pathname, strategy: "offline-simulation", simulated: true });
184
- return cached;
185
- }
186
- return offlineErrorResponse(pathname);
197
+ const cached = await (await caches.open(cacheName)).match(request);
198
+ if (cached) {
199
+ notifyClients({
200
+ type: "EIDOS_CACHE_HIT",
201
+ url: pathname,
202
+ strategy: "offline-simulation",
203
+ simulated: true
204
+ });
205
+ return cached;
206
+ }
207
+ return offlineErrorResponse(pathname);
187
208
  }
188
209
  function offlineErrorResponse(pathname) {
189
- return new Response(
190
- JSON.stringify({
191
- error: "offline",
192
- message: `No cached response available for ${pathname}`,
193
- eidos: true
194
- }),
195
- {
196
- status: 503,
197
- headers: {
198
- "Content-Type": "application/json",
199
- "X-Eidos-Offline": "true"
200
- }
201
- }
202
- );
210
+ return new Response(JSON.stringify({
211
+ error: "offline",
212
+ message: `No cached response available for ${pathname}`,
213
+ eidos: true
214
+ }), {
215
+ status: 503,
216
+ headers: {
217
+ "Content-Type": "application/json",
218
+ "X-Eidos-Offline": "true"
219
+ }
220
+ });
203
221
  }
204
222
  async function notifyClients(message) {
205
- const clients = await self.clients.matchAll({ includeUncontrolled: true });
206
- clients.forEach((client) => client.postMessage(message));
223
+ (await self.clients.matchAll({ includeUncontrolled: true })).forEach((client) => client.postMessage(message));
207
224
  }
208
225
  self.addEventListener("sync", (event) => {
209
- const syncEvent = event;
210
- if (syncEvent.tag === "eidos-queue-replay") {
211
- syncEvent.waitUntil(notifyClients({ type: "EIDOS_BACKGROUND_SYNC" }));
212
- }
226
+ const syncEvent = event;
227
+ if (syncEvent.tag === "eidos-queue-replay") syncEvent.waitUntil(notifyClients({ type: "EIDOS_BACKGROUND_SYNC" }));
228
+ });
229
+ self.addEventListener("push", (event) => {
230
+ let payload = null;
231
+ try {
232
+ payload = event.data?.json();
233
+ } catch {
234
+ return;
235
+ }
236
+ if (!payload?.title) return;
237
+ event.waitUntil(self.registration.showNotification(payload.title, {
238
+ body: payload.body,
239
+ icon: payload.icon,
240
+ badge: payload.badge,
241
+ tag: payload.tag,
242
+ data: payload.data
243
+ }));
244
+ });
245
+ self.addEventListener("notificationclick", (event) => {
246
+ event.notification.close();
247
+ const data = event.notification.data ?? {};
248
+ event.waitUntil(self.clients.matchAll({
249
+ type: "window",
250
+ includeUncontrolled: true
251
+ }).then((clients) => {
252
+ const client = clients[0];
253
+ if (client) {
254
+ client.focus();
255
+ client.postMessage({
256
+ type: "EIDOS_NOTIFICATION_CLICK",
257
+ data
258
+ });
259
+ return;
260
+ }
261
+ if (data.url) return self.clients.openWindow(data.url);
262
+ }));
263
+ });
264
+ self.addEventListener("pushsubscriptionchange", (event) => {
265
+ const psEvent = event;
266
+ psEvent.waitUntil((async () => {
267
+ const vapidPublicKey = await idbGet("vapidPublicKey");
268
+ if (!vapidPublicKey) return;
269
+ await notifyClients({
270
+ type: "EIDOS_SUBSCRIPTION_EXPIRED",
271
+ subscription: (psEvent.newSubscription ?? await self.registration.pushManager.subscribe({
272
+ userVisibleOnly: true,
273
+ applicationServerKey: urlBase64ToUint8Array(vapidPublicKey)
274
+ })).toJSON()
275
+ });
276
+ })());
213
277
  });
278
+ var META_DB = "eidos-sw-meta";
279
+ var META_STORE = "kv";
280
+ function openMetaDb() {
281
+ return new Promise((resolve, reject) => {
282
+ const req = indexedDB.open(META_DB, 1);
283
+ req.onupgradeneeded = () => req.result.createObjectStore(META_STORE);
284
+ req.onsuccess = () => resolve(req.result);
285
+ req.onerror = () => reject(req.error);
286
+ });
287
+ }
288
+ async function idbSet(key, value) {
289
+ const db = await openMetaDb();
290
+ await new Promise((resolve, reject) => {
291
+ const tx = db.transaction(META_STORE, "readwrite");
292
+ tx.objectStore(META_STORE).put(value, key);
293
+ tx.oncomplete = () => resolve();
294
+ tx.onerror = () => reject(tx.error);
295
+ });
296
+ db.close();
297
+ }
298
+ async function idbGet(key) {
299
+ const db = await openMetaDb();
300
+ const value = await new Promise((resolve, reject) => {
301
+ const req = db.transaction(META_STORE, "readonly").objectStore(META_STORE).get(key);
302
+ req.onsuccess = () => resolve(req.result);
303
+ req.onerror = () => reject(req.error);
304
+ });
305
+ db.close();
306
+ return value;
307
+ }
308
+ //#endregion
package/dist/eidos.cjs CHANGED
@@ -1,4 +1,4 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});let v=require("react"),j=require("react/jsx-runtime");function X(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 Z(e){return{addQueueItem:t=>e(n=>({queue:[...n.queue,t]})),updateQueueItem:(t,n)=>e(r=>({queue:r.queue.map(s=>s.id===t?{...s,...n}:s)})),batchUpdateQueueItems:t=>e(n=>{const r=new Map(t.map(s=>[s.id,s.update]));return{queue:n.queue.map(s=>{const a=r.get(s.id);return a?{...s,...a}:s})}}),removeQueueItem:t=>e(n=>({queue:n.queue.filter(r=>r.id!==t)})),hydrateQueue:t=>e(()=>({queue:t}))}}var g,x=new Set;function W(){x.forEach(e=>e())}function b(e){g={...g,...e(g)},W()}g={isOnline:typeof navigator>"u"||navigator.onLine!==!1,swStatus:"idle",swError:void 0,resources:{},queue:[],setOnline:e=>b(()=>({isOnline:e})),setSwStatus:(e,t)=>b(()=>({swStatus:e,swError:t})),...X(b),...Z(b)};function ee(){return g}function te(e){return x.add(e),()=>{x.delete(e)}}var o={getState:ee,subscribe:te,setState:e=>{const t=typeof e=="function"?e(g):e;g={...g,...t},W()}},y=null,C=[];function ne(){return y}async function re(e){if(typeof navigator>"u"||!("serviceWorker"in navigator)){o.getState().setSwStatus("unsupported");return}const t=o.getState();t.setSwStatus("registering");try{y=await navigator.serviceWorker.register(e,{scope:"/"}),await se(y),t.setSwStatus("active"),navigator.serviceWorker.addEventListener("message",oe),window.addEventListener("online",()=>t.setOnline(!0)),window.addEventListener("offline",()=>t.setOnline(!1)),ue()}catch(n){t.setSwStatus("error",String(n))}}function se(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 s(){n.state==="activated"&&(clearTimeout(r),n.removeEventListener("statechange",s),t())})})}function k(e){const t=y?.active;t?t.postMessage(e):C.push(e)}var H=null;function ae(e){H=e}function ie(){try{return typeof navigator<"u"&&"serviceWorker"in navigator&&y!==null&&"sync"in y}catch{return!1}}function oe(e){const t=e.data;if(!t?.type)return;const n=o.getState(),{type:r,url:s}=t;if(r==="EIDOS_BACKGROUND_SYNC"){H?.();return}if(s)switch(r){case"EIDOS_CACHE_HIT":{const a=n.resources[s];n.updateResource(s,{status:"fresh",lastEvent:"cache-hit",cacheHits:(a?.cacheHits??0)+1});break}case"EIDOS_CACHE_UPDATED":n.updateResource(s,{status:"fresh",lastEvent:"cache-updated",cachedAt:Date.now()});break;case"EIDOS_NETWORK_ERROR":n.updateResource(s,{status:"error",lastEvent:"network-error"});break}}function ce(e){k({type:"EIDOS_SIMULATE_OFFLINE",enabled:e}),o.getState().setOnline(!e)}function ue(){const e=y?.active;if(e){for(const t of C)e.postMessage(t);C=[]}}var R=new Map,_=new Map,$=null;function de(e){$=e}function S(e){return e.includes("*")||/:[^/]+/.test(e)}function le(e){return"^"+e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*\*/g,".+").replace(/\*/g,"[^/]+").replace(/:[^/]+/g,"[^/]+")+"$"}function O(e,t){return new Error(`[eidos] resource('${e}') is a URL pattern — ${t}() is not supported on pattern handles. The SW intercepts matching requests automatically; call fetch(specificUrl) directly in your app code.`)}function fe(e,t){if(R.has(e))return R.get(e);const n=pe(t),r=S(e)?le(e):void 0,s={url:e,config:t,strategy:n,status:"idle",cacheHits:0,cacheMisses:0};o.getState().registerResource(e,s),k({type:"EIDOS_REGISTER_RESOURCE",url:e,strategy:n.swStrategy,cacheName:n.cacheName,...r!==void 0&&{pattern:r}});const a={url:e,config:t,strategy:n,fetch:async()=>{if(S(e))throw O(e,"fetch");const i=_.get(e);if(i)return i.then(u=>u.clone());const c=he(e,t,n);return _.set(e,c),c.finally(()=>_.delete(e)).catch(()=>{}),c.then(u=>u.clone())},json:async()=>{if(S(e))throw O(e,"json");return(await a.fetch()).json()},query:()=>{if(S(e))throw O(e,"query");return{queryKey:["eidos",e],queryFn:()=>a.json()}},prefetch:async()=>{if(S(e))throw O(e,"prefetch");await a.fetch()},invalidate:async()=>{k({type:"EIDOS_CLEAR_CACHE",url:e});const i=await caches.open(n.cacheName).catch(()=>null);if(i){const c=await i.keys(),u=r?new RegExp(r):null,p=e.startsWith("http");await Promise.all(c.filter(w=>{const l=w.url,T=new URL(l).pathname;return u?u.test(p?l:T):p?l===e:l===e||T===e}).map(w=>i.delete(w)))}S(e)||o.getState().updateResource(e,{status:"stale",cachedAt:void 0,lastEvent:"cache-cleared",cacheHits:0,cacheMisses:0}),$?.(["eidos",e])},unregister:()=>{R.delete(e),k({type:"EIDOS_UNREGISTER_RESOURCE",url:e}),o.getState().unregisterResource(e)}};return R.set(e,a),a}async function he(e,t,n){const r=o.getState();r.updateResource(e,{status:"fetching",fetchedAt:Date.now()});const s=await caches.open(n.cacheName).catch(()=>null);try{if(n.swStrategy!=="network-first"){const c=s?await s.match(e).catch(()=>null):null,u=o.getState().resources[e],p=t.maxAge!==void 0&&u?.cachedAt!==void 0&&Date.now()-u.cachedAt>t.maxAge;if(c&&!p)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 l=>{l.ok&&s&&(await s.put(e,l.clone()),o.getState().updateResource(e,{cachedAt:Date.now(),lastEvent:"cache-updated"}))}).catch(()=>{}),c;const w=o.getState().resources[e];r.updateResource(e,{cacheMisses:(w?.cacheMisses??0)+1})}const a=await fetch(e);if(a.ok)return s&&await s.put(e,a.clone()),r.updateResource(e,{status:"fresh",cachedAt:Date.now(),lastEvent:"cache-updated"}),a;r.updateResource(e,{status:a.status===503?"offline":"error"});const i=a.headers.get("X-Eidos-Offline")==="true";throw new Error(i?`offline: no cached response for ${e}`:`${a.status} ${a.statusText}`)}catch(a){const i=s?await s.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"}),a}}function pe(e){const t=e.strategy;return e.offline?M(t??"stale-while-revalidate",e.cacheName):M(t??"network-first",e.cacheName)}var ge={"stale-while-revalidate":"StaleWhileRevalidate","cache-first":"CacheFirst","network-first":"NetworkFirst"},we={"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
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});let S=require("react"),H=require("react/jsx-runtime");function pe(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 w,D=new Set;function G(){D.forEach(e=>e())}function k(e){w={...w,...e(w)},G()}w={isOnline:typeof navigator>"u"||navigator.onLine!==!1,swStatus:"idle",swError:void 0,resources:{},queue:[],setOnline:e=>k(()=>({isOnline:e})),setSwStatus:(e,t)=>k(()=>({swStatus:e,swError:t})),...pe(k),...he(k)};function ye(){return w}function ge(e){return D.add(e),()=>{D.delete(e)}}var o={getState:ye,subscribe:ge,setState:e=>{const t=typeof e=="function"?e(w):e;w={...w,...t},G()}},m=null,T=[];function Y(){return m}async function we(e){if(typeof navigator>"u"||!("serviceWorker"in navigator)){o.getState().setSwStatus("unsupported");return}const t=o.getState();t.setSwStatus("registering");try{m=await navigator.serviceWorker.register(e,{scope:"/"}),await ve(m),t.setSwStatus("active"),navigator.serviceWorker.addEventListener("message",Ee),window.addEventListener("online",()=>t.setOnline(!0)),window.addEventListener("offline",()=>t.setOnline(!1)),_e()}catch(r){t.setSwStatus("error",String(r))}}function ve(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 _(e){const t=m?.active;t?t.postMessage(e):T.push(e)}var J=null;function me(e){J=e}function Se(){try{return typeof navigator<"u"&&"serviceWorker"in navigator&&m!==null&&"sync"in m}catch{return!1}}var M={};function be(e){M=e}function Ee(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 Re(e){_({type:"EIDOS_SIMULATE_OFFLINE",enabled:e}),o.getState().setOnline(!e)}function _e(){const e=m?.active;if(e){for(const t of T)e.postMessage(t);T=[]}}var v=new Map,C=new Map,X=null;function ke(e){X=e}function q(e){return e.includes("*")||/:[^/]+/.test(e)}function Ie(e){return"^"+e.replace(/[.+?^${}()|[\]\\]/g,"\\$&").replace(/\*\*/g,".+").replace(/\*/g,"[^/]+").replace(/:[^/]+/g,"[^/]+")+"$"}function z(e,t){const r=qe(t),n=q(e)?Ie(e):void 0,a={url:e,config:t,strategy:r,status:"idle",cacheHits:0,cacheMisses:0};return o.getState().registerResource(e,a),_({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()=>{_({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,d=new URL(u).pathname;return s?s.test(i?u:d):i?u===e:u===e||d===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()=>{v.delete(e),_({type:"EIDOS_UNREGISTER_RESOURCE",url:e}),o.getState().unregisterResource(e)}}function Oe(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(v.has(e))return v.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 v.set(e,n),n}function Ae(e,t){if(!q(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:r,regexStr:n}=z(e,t),a={url:e,config:t,strategy:r,invalidate:Z(e,r,n),unregister:ee(e)};return v.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],d=t.maxAge!==void 0&&u?.cachedAt!==void 0&&Date.now()-u.cachedAt>t.maxAge;if(c&&!d)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 l=>{l.ok&&a&&(await a.put(e,l.clone()),o.getState().updateResource(e,{cachedAt:Date.now(),lastEvent:"cache-updated"}))}).catch(()=>{}),c;const g=o.getState().resources[e];n.updateResource(e,{cacheMisses:(g?.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 qe(e){const t=e.strategy;return e.offline?B(t??"stale-while-revalidate",e.cacheName):B(t??"network-first",e.cacheName)}var Ce={"stale-while-revalidate":"StaleWhileRevalidate","cache-first":"CacheFirst","network-first":"NetworkFirst"},xe={"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 M(e,t){const n=we[e];return{name:ge[e],swStrategy:e,cacheName:t??"eidos-resources-v1",reasoning:n.reasoning,behavior:n.behavior,equivalentCode:""}}async function ye(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 Se="eidos",ve=1,d="action-queue",A=null;function m(){return A?Promise.resolve(A):new Promise((e,t)=>{const n=indexedDB.open(Se,ve);n.onupgradeneeded=r=>{const s=r.target.result;if(!s.objectStoreNames.contains(d)){const a=s.createObjectStore(d,{keyPath:"id"});a.createIndex("status","status",{unique:!1}),a.createIndex("actionId","actionId",{unique:!1})}},n.onsuccess=()=>{A=n.result,e(n.result)},n.onerror=()=>t(n.error)})}async function me(e){const t=await m();return new Promise((n,r)=>{const s=t.transaction(d,"readwrite");s.objectStore(d).add(e),s.oncomplete=()=>n(),s.onerror=()=>r(s.error)})}async function L(){const e=await m();return new Promise((t,n)=>{const r=e.transaction(d,"readonly").objectStore(d).getAll();r.onsuccess=()=>t(r.result),r.onerror=()=>n(r.error)})}async function Ee(e,t){const n=await m();return new Promise((r,s)=>{const a=n.transaction(d,"readwrite"),i=a.objectStore(d),c=i.get(e);c.onsuccess=()=>{c.result&&i.put({...c.result,...t})},a.oncomplete=()=>r(),a.onerror=()=>s(a.error)})}async function be(e){const t=await m();return new Promise((n,r)=>{const s=t.transaction(d,"readwrite");s.objectStore(d).delete(e),s.oncomplete=()=>n(),s.onerror=()=>r(s.error)})}async function Re(){const e=await m();function t(s){return new Promise((a,i)=>{const c=e.transaction(d,"readonly").objectStore(d).index("status"),u=[],p=c.openCursor(IDBKeyRange.only(s));p.onsuccess=w=>{const l=w.target.result;l?(u.push(l.value),l.continue()):a(u)},p.onerror=()=>i(p.error)})}const[n,r]=await Promise.all([t("pending"),t("failed")]);return[...n,...r]}async function Oe(){const e=await m();return new Promise((t,n)=>{const r=e.transaction(d,"readwrite");r.objectStore(d).clear(),r.oncomplete=()=>t(),r.onerror=()=>n(r.error)})}var ke={add:me,getAll:L,getPending:Re,update:Ee,remove:be,clear:Oe},B=null;function qe(e){B=e}function F(){return B}var N=new Map,G=new Map,K=new Map;function h(){return F()??ke}function V(){return crypto.randomUUID()}function _e(e,t){const n=t.name||e.name||V();N.set(n,e),t.onRollback&&G.set(n,t.onRollback),t.onConflict&&K.set(n,t.onConflict);const r=async(...s)=>{const{isOnline:a}=o.getState();if(t.onOptimistic?.(...s),t.reliability==="neverLose"){if(!a)return U(n,n,s,t);try{return await e(...s)}catch{return U(n,n,s,t)}}try{return await e(...s)}catch(i){throw t.onRollback?.(...s),i}};return Object.defineProperty(r,"id",{value:n,writable:!1}),Object.defineProperty(r,"config",{value:t,writable:!1}),r}async function U(e,t,n,r){const s=V(),a={id:s,actionId:e,actionName:t,args:n,queuedAt:Date.now(),retryCount:0,maxRetries:r.maxRetries??3,status:"pending",priority:r.priority??"normal"};await h().add(a),o.getState().addQueueItem(a);try{const i=ne();i&&"sync"in i&&await i.sync.register("eidos-queue-replay")}catch{}return{queued:!0,id:s,message:`"${t}" queued — will execute when online`}}function Ae(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 Ie(e){return Math.min(2e3*2**e,3e5)*(.8+Math.random()*.4)}var I=!1;async function q(){const e=o.getState();if(!e.isOnline||I)return{attempted:0,succeeded:0,failed:0,retrying:0,skipped:0,conflicted:0};I=!0;try{return await Ce(e)}finally{I=!1}}async function Qe(e,t){const n=N.get(e.actionId);if(!n)return"skipped";try{await n(...e.args);const r=Date.now();return t.updateQueueItem(e.id,{status:"succeeded",completedAt:r}),await h().update(e.id,{status:"succeeded",completedAt:r}),setTimeout(()=>{t.removeQueueItem(e.id),h().remove(e.id)},3e3),"succeeded"}catch(r){if(Ae(r)){const a=K.get(e.actionId);if(a&&a(r,e.args)==="skip")return t.removeQueueItem(e.id),await h().remove(e.id),"conflicted"}const s=e.retryCount+1;if(s>=e.maxRetries)return t.updateQueueItem(e.id,{status:"failed",error:String(r),retryCount:s}),await h().update(e.id,{status:"failed",error:String(r),retryCount:s}),G.get(e.actionId)?.(...e.args),"failed";{const a=Date.now()+Ie(s);return t.updateQueueItem(e.id,{status:"pending",retryCount:s,nextRetryAt:a}),await h().update(e.id,{status:"pending",retryCount:s,nextRetryAt:a}),"retrying"}}}async function xe(e,t,n){if(e.length===0)return;const r=e.filter(a=>N.has(a.actionId));if(n.skipped+=e.length-r.length,r.length>0){t.batchUpdateQueueItems(r.map(a=>({id:a.id,update:{status:"replaying"}})));for(const a of r)h().update(a.id,{status:"replaying"})}const s=await Promise.allSettled(r.map(a=>Qe(a,t)));for(const a of s){const i=a.status==="fulfilled"?a.value:"failed";i==="skipped"?n.skipped++:i==="conflicted"?n.conflicted++:(n.attempted++,n[i]++)}}async function Ce(e){const t=await h().getPending(),n=Date.now(),r=t.filter(a=>a.retryCount<a.maxRetries&&(!a.nextRetryAt||a.nextRetryAt<=n)),s={attempted:0,succeeded:0,failed:0,retrying:0,skipped:0,conflicted:0};for(const a of["high","normal","low"])await xe(r.filter(i=>(i.priority??"normal")===a),e,s);return s}async function Pe(){await h().clear(),o.getState().hydrateQueue([])}function Y(){let e=o.getState().isOnline;const t=o.subscribe(()=>{const{isOnline:s}=o.getState(),a=s&&!e;e=s,a&&setTimeout(q,600)}),n=o.getState(),r=n.queue.some(s=>s.status==="pending");return n.isOnline&&r&&setTimeout(q,1200),t}var P=!1,D=null;async function J(e={}){if(typeof window>"u"||P)return;P=!0;const t=e.swPath??"/eidos-sw.js",n=e.autoReplay??!0;try{const r=await L();r.length>0&&o.getState().hydrateQueue(r)}catch{}try{await re(t)}catch{}ae(()=>{o.getState().isOnline&&setTimeout(q,200)}),n&&(D=Y())}function De(){D?.(),D=null,P=!1}var Q="@eidos:queue",Ne=class{constructor(e){this.storage=e}async readAll(){try{const e=await this.storage.getItem(Q);return e?JSON.parse(e):[]}catch{return[]}}async writeAll(e){await this.storage.setItem(Q,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(s=>s.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(Q)}};function Te({children:e,swPath:t,autoReplay:n}){return(0,v.useEffect)(()=>{J({swPath:t,autoReplay:n})},[]),(0,j.jsx)(j.Fragment,{children:e})}function f(e){const t=e??(n=>n);return(0,v.useSyncExternalStore)(o.subscribe,()=>t(o.getState()))}function je(){return f()}function Me(){return f(e=>e.resources)}function Ue(e){return f(t=>t.resources[e])}function We(){return f(e=>e.queue)}function He(e){return f(t=>t.queue.find(n=>n.id===e))}function $e(){return{isOnline:f(e=>e.isOnline),swStatus:f(e=>e.swStatus),swError:f(e=>e.swError)}}function Le(){const[e,t,n,r]=f(s=>{let a=0,i=0,c=0;for(const u of s.queue)u.status==="pending"?a++:u.status==="failed"?i++:u.status==="replaying"&&c++;return`${a},${i},${c},${s.queue.length}`}).split(",");return{pending:+e,failed:+t,replaying:+n,total:+r}}function Be(e){const t=f(s=>s.queue.length),n=(0,v.useRef)(0),r=(0,v.useRef)(e);(0,v.useEffect)(()=>{r.current=e}),(0,v.useEffect)(()=>{n.current>0&&t===0&&r.current(),n.current=t},[t])}var Fe="1.1.0";function Ge(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 z(e,t){return Ge(e,t)}function E(e,t=Object.is){return{subscribe(n){let r=e(o.getState());return n(r),o.subscribe(()=>{const s=e(o.getState());t(r,s)||(r=s,n(s))})},getState(){return e(o.getState())}}}var Ke=E(e=>e),Ve=E(e=>e.queue),Ye=E(e=>({isOnline:e.isOnline,swStatus:e.swStatus,swError:e.swError}),z),Je=E(e=>{let t=0,n=0,r=0;for(const s of e.queue)s.status==="pending"?t++:s.status==="failed"?n++:s.status==="replaying"&&r++;return{pending:t,failed:n,replaying:r,total:e.queue.length}},z);function ze(e){return E(t=>t.resources[e])}function Xe(e){return E(t=>t.queue.find(n=>n.id===e))}exports.AsyncStorageQueueStorage=Ne;exports.EidosProvider=Te;exports.VERSION=Fe;exports._getQueueStorage=F;exports._resetEidos=De;exports.action=_e;exports.clearQueue=Pe;exports.eidosAction=Xe;exports.eidosQueue=Ve;exports.eidosQueueStats=Je;exports.eidosResource=ze;exports.eidosStatus=Ye;exports.eidosStore=Ke;exports.initEidos=J;exports.isBgSyncSupported=ie;exports.replayQueue=q;exports.resource=fe;exports.setOfflineSimulation=ce;exports.setQueryInvalidator=de;exports.setQueueStorage=qe;exports.subscribeReplayOnReconnect=Y;exports.useEidos=je;exports.useEidosAction=He;exports.useEidosOnDrain=Be;exports.useEidosQueue=We;exports.useEidosQueueStats=Le;exports.useEidosResource=Ue;exports.useEidosResources=Me;exports.useEidosStatus=$e;exports.useEidosStore=o;exports.warmCache=ye;
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;
14
14
 
15
15
  //# sourceMappingURL=eidos.cjs.map