@sweidos/eidos 2.2.0 → 2.3.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/index.js CHANGED
@@ -1,45 +1,48 @@
1
1
  import { useEidosStore as o } from "./store.js";
2
- import { getSwRegistration as i, isBgSyncSupported as s, registerPushCallbacks as t, sendToWorker as u, setOfflineSimulation as a } from "./sw-bridge.js";
3
- import { resource as d, resourcePattern as n, setQueryInvalidator as c, warmCache as p } from "./resource.js";
4
- import { _getQueueStorage as f, setQueueStorage as E } from "./queue-storage.js";
5
- import { action as Q, cancelByIdempotencyKey as y, clearQueue as g, replayQueue as R, requeueItem as b } from "./action.js";
6
- import { subscribeReplayOnReconnect as O } from "./replay.js";
7
- import { _resetEidos as P, initEidos as h } from "./runtime.js";
8
- import { AsyncStorageQueueStorage as v } from "./async-storage-adapter.js";
2
+ import { getSwRegistration as i, isBgSyncSupported as t, registerPushCallbacks as s, sendToWorker as u, setOfflineSimulation as m, triggerSwUpdate as a } from "./sw-bridge.js";
3
+ import { resource as n, resourcePattern as p, setQueryInvalidator as c, warmCache as S } from "./resource.js";
4
+ import { _getQueueStorage as E, setQueueStorage as l } from "./queue-storage.js";
5
+ import { action as Q, cancelByIdempotencyKey as y, clearQueue as R, replayQueue as b, requeueItem as I } from "./action.js";
6
+ import { subscribeReplayOnReconnect as w } from "./replay.js";
7
+ import { _resetEidos as D, initEidos as P } from "./runtime.js";
8
+ import { AsyncStorageQueueStorage as k } from "./async-storage-adapter.js";
9
9
  import { EidosProvider as B } from "./react/Provider.js";
10
- import { eidosAction as D, eidosQueue as _, eidosQueueStats as q, eidosReliabilityStats as x, eidosResource as K, eidosStatus as N, eidosStore as T, onQueueDrain as V } from "./stores.js";
11
- import { useEidos as j, useEidosAction as z, useEidosOnDrain as F, useEidosQueue as G, useEidosQueueStats as H, useEidosReliabilityStats as J, useEidosResource as L, useEidosResources as M, useEidosStatus as U } from "./react/hooks.js";
12
- import { VERSION as Y } from "./version.js";
10
+ import { eidosAction as _, eidosQueue as q, eidosQueueStats as x, eidosReliabilityStats as K, eidosResource as N, eidosStatus as T, eidosStore as U, onQueueDrain as V } from "./stores.js";
11
+ import { useEidos as j, useEidosAction as z, useEidosOnDrain as F, useEidosQueue as G, useEidosQueueStats as H, useEidosReliabilityStats as J, useEidosResource as L, useEidosResources as M, useEidosStatus as X } from "./react/hooks.js";
12
+ import { VERSION as Z } from "./version.js";
13
+ import { eidosDebug as ee } from "./debug.js";
13
14
  export {
14
- v as AsyncStorageQueueStorage,
15
+ k as AsyncStorageQueueStorage,
15
16
  B as EidosProvider,
16
- Y as VERSION,
17
- f as _getQueueStorage,
18
- P as _resetEidos,
17
+ Z as VERSION,
18
+ E as _getQueueStorage,
19
+ D as _resetEidos,
19
20
  Q as action,
20
21
  y as cancelByIdempotencyKey,
21
- g as clearQueue,
22
- D as eidosAction,
23
- _ as eidosQueue,
24
- q as eidosQueueStats,
25
- x as eidosReliabilityStats,
26
- K as eidosResource,
27
- N as eidosStatus,
28
- T as eidosStore,
22
+ R as clearQueue,
23
+ _ as eidosAction,
24
+ ee as eidosDebug,
25
+ q as eidosQueue,
26
+ x as eidosQueueStats,
27
+ K as eidosReliabilityStats,
28
+ N as eidosResource,
29
+ T as eidosStatus,
30
+ U as eidosStore,
29
31
  i as getSwRegistration,
30
- h as initEidos,
31
- s as isBgSyncSupported,
32
+ P as initEidos,
33
+ t as isBgSyncSupported,
32
34
  V as onQueueDrain,
33
- t as registerPushCallbacks,
34
- R as replayQueue,
35
- b as requeueItem,
36
- d as resource,
37
- n as resourcePattern,
35
+ s as registerPushCallbacks,
36
+ b as replayQueue,
37
+ I as requeueItem,
38
+ n as resource,
39
+ p as resourcePattern,
38
40
  u as sendToWorker,
39
- a as setOfflineSimulation,
41
+ m as setOfflineSimulation,
40
42
  c as setQueryInvalidator,
41
- E as setQueueStorage,
42
- O as subscribeReplayOnReconnect,
43
+ l as setQueueStorage,
44
+ w as subscribeReplayOnReconnect,
45
+ a as triggerSwUpdate,
43
46
  j as useEidos,
44
47
  z as useEidosAction,
45
48
  F as useEidosOnDrain,
@@ -48,7 +51,7 @@ export {
48
51
  J as useEidosReliabilityStats,
49
52
  L as useEidosResource,
50
53
  M as useEidosResources,
51
- U as useEidosStatus,
54
+ X as useEidosStatus,
52
55
  o as useEidosStore,
53
- p as warmCache
56
+ S as warmCache
54
57
  };
@@ -0,0 +1,2 @@
1
+ /** Decodes a base64url string (e.g. a VAPID public key) into raw bytes. */
2
+ export declare function urlBase64ToUint8Array(base64Url: string): Uint8Array;
package/dist/query.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { UseQueryOptions, UseQueryResult, UseMutationOptions, UseMutationResult, QueryClient } from '@tanstack/react-query';
2
- import { ResourceHandle, AnyResourceHandle, ActionHandle, QueuedResult } from '@sweidos/eidos';
3
-
2
+ import { ResourceHandle, AnyResourceHandle, ActionHandle, QueuedResult } from './index.ts';
4
3
  /**
5
4
  * Register a QueryClient with Eidos.
6
5
  *
@@ -0,0 +1,12 @@
1
+ import { ActionQueueItem } from './types';
2
+ export interface QueueStorage {
3
+ add(item: ActionQueueItem): Promise<void>;
4
+ getAll(): Promise<ActionQueueItem[]>;
5
+ getPending(): Promise<ActionQueueItem[]>;
6
+ update(id: string, patch: Partial<ActionQueueItem>): Promise<void>;
7
+ remove(id: string): Promise<void>;
8
+ clear(): Promise<void>;
9
+ }
10
+ /** Override the default IndexedDB queue with a custom storage backend (e.g. AsyncStorage for React Native). */
11
+ export declare function setQueueStorage(s: QueueStorage): void;
12
+ export declare function _getQueueStorage(): QueueStorage | null;
@@ -0,0 +1,32 @@
1
+ import { ActionQueueItem } from './types';
2
+ type QueueSyncMessage = {
3
+ type: 'update';
4
+ id: string;
5
+ update: Partial<ActionQueueItem>;
6
+ } | {
7
+ type: 'batchUpdate';
8
+ updates: Array<{
9
+ id: string;
10
+ update: Partial<ActionQueueItem>;
11
+ }>;
12
+ } | {
13
+ type: 'remove';
14
+ id: string;
15
+ };
16
+ /**
17
+ * Broadcasts a queue-item status change to other tabs sharing the same
18
+ * IndexedDB queue. The replay-lock holder (see `replayQueue` in action.ts)
19
+ * is the only tab that mutates queue-item status, so non-leader tabs would
20
+ * otherwise show stale status until their own store re-hydrates.
21
+ *
22
+ * No-ops in environments without BroadcastChannel (React Native, old Safari).
23
+ */
24
+ export declare function broadcastQueueSync(message: QueueSyncMessage): void;
25
+ /**
26
+ * Applies queue-item status updates broadcast by the replay-lock holder to
27
+ * this tab's store. Returns an unsubscribe function.
28
+ */
29
+ export declare function subscribeQueueSync(): () => void;
30
+ /** Test-only: reset the cached channel so each test gets a fresh instance. */
31
+ export declare function _resetQueueSyncChannel(): void;
32
+ export {};
@@ -0,0 +1,16 @@
1
+ import { ReactNode } from 'react';
2
+ import { EidosConfig } from '../runtime';
3
+ interface EidosProviderProps extends EidosConfig {
4
+ children: ReactNode;
5
+ }
6
+ /**
7
+ * Mount once at the root of your application.
8
+ * Registers the service worker and initialises the Eidos runtime.
9
+ *
10
+ * @example
11
+ * <EidosProvider swPath="/eidos-sw.js">
12
+ * <App />
13
+ * </EidosProvider>
14
+ */
15
+ export declare function EidosProvider({ children, swPath, autoReplay }: EidosProviderProps): import("react").JSX.Element;
16
+ export {};
@@ -1,5 +1,4 @@
1
1
  import { default as React } from 'react';
2
-
3
2
  export interface EidosProviderRNProps {
4
3
  children: React.ReactNode;
5
4
  /**
@@ -0,0 +1,51 @@
1
+ import { EidosStore } from '../store';
2
+ /** Full Eidos store — prefer the narrower hooks below for performance. */
3
+ export declare function useEidos(): EidosStore;
4
+ /** All registered resources — only re-renders when the resources map changes, not on queue mutations. */
5
+ export declare function useEidosResources(): Record<string, import('..').ResourceEntry>;
6
+ /** Live state for a single registered resource URL. */
7
+ export declare function useEidosResource(url: string): import('..').ResourceEntry;
8
+ /** The current action queue. */
9
+ export declare function useEidosQueue(): import('..').ActionQueueItem[];
10
+ /**
11
+ * Live state for a single queue item by ID. Only re-renders when that specific
12
+ * item changes — cheaper than `useEidosQueue().find(id)` which re-renders on
13
+ * any queue mutation.
14
+ */
15
+ export declare function useEidosAction(id: string): import('..').ActionQueueItem | undefined;
16
+ /**
17
+ * Online + SW status — cheap subscription, safe to use in header components.
18
+ * Three separate primitive selectors so each only triggers a re-render when
19
+ * its own value changes (no object-reference churn from a combined selector).
20
+ */
21
+ export declare function useEidosStatus(): {
22
+ isOnline: boolean;
23
+ swStatus: "idle" | "error" | "registering" | "active" | "unsupported";
24
+ swError: string | undefined;
25
+ };
26
+ /**
27
+ * Queue counts — single subscription, single loop. Re-renders only when a
28
+ * count changes, not on every queue mutation. Use for badges and status bars
29
+ * instead of `useEidosQueue()` when you only need numbers, not full items.
30
+ */
31
+ export declare function useEidosQueueStats(): {
32
+ pending: number;
33
+ failed: number;
34
+ replaying: number;
35
+ total: number;
36
+ };
37
+ /**
38
+ * Calls `callback` once each time the action queue drains from non-empty → 0.
39
+ * Stable callback reference not required — always calls the latest version.
40
+ * Use for "all offline actions synced!" toasts.
41
+ *
42
+ * @example
43
+ * useEidosOnDrain(() => toast.success('All offline actions synced!'))
44
+ */
45
+ /**
46
+ * Cumulative, session-scoped `neverLose` queue outcome counters — opt-in
47
+ * reliability telemetry for dashboards/devtools. Re-renders only when a
48
+ * counter changes.
49
+ */
50
+ export declare function useEidosReliabilityStats(): import('..').ReliabilityStats;
51
+ export declare function useEidosOnDrain(callback: () => void): void;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Subscribe to online/offline transitions and trigger replayQueue() on
3
+ * reconnect, plus replay any pending items left over from a previous session.
4
+ *
5
+ * Shared by the web (runtime.ts) and React Native (runtime-rn.ts) init paths.
6
+ *
7
+ * WHY subscribe to the store instead of window.addEventListener('online'):
8
+ * setOfflineSimulation() updates the store directly but never fires a real
9
+ * browser `online` event. Watching the store catches both:
10
+ * • Real network reconnects (sw-bridge updates store on window.online)
11
+ * • Simulation toggled off (setOfflineSimulation(false) → store.setOnline(true))
12
+ *
13
+ * Returns an unsubscribe function.
14
+ */
15
+ export declare function subscribeReplayOnReconnect(): () => void;
@@ -0,0 +1,32 @@
1
+ import { ResourceConfig, ResourceHandle, PatternResourceHandle, WarmCacheResult } from './types';
2
+ type QueryInvalidator = (queryKey: [string, string]) => void;
3
+ /** @internal Called by @sweidos/eidos/query. */
4
+ export declare function setQueryInvalidator(fn: QueryInvalidator): void;
5
+ /**
6
+ * Registers a concrete-URL resource. For URL patterns (`/api/products/*`,
7
+ * `/api/users/:id`, `**`), use `resourcePattern()` instead.
8
+ */
9
+ export declare function resource<T = unknown>(url: string, config: ResourceConfig): ResourceHandle<T>;
10
+ /**
11
+ * Registers a URL pattern (`/api/products/*`, `/api/users/:id`, `**`). The SW
12
+ * intercepts all matching requests automatically — there is no single URL to
13
+ * fetch/cache directly, so the returned handle only supports cache management
14
+ * (`invalidate`/`unregister`). For a fetchable resource, use `resource()`.
15
+ */
16
+ export declare function resourcePattern(url: string, config: ResourceConfig): PatternResourceHandle;
17
+ /**
18
+ * Bulk-prefetch an array of resource handles concurrently, warming the cache
19
+ * for each one. Useful on login / app init when you know which resources the
20
+ * user will need offline.
21
+ *
22
+ * Pattern handles (containing `*`, `**`, or `:param`) are silently skipped —
23
+ * they match multiple URLs so there is no single URL to prefetch.
24
+ *
25
+ * @example
26
+ * import { warmCache } from '@sweidos/eidos'
27
+ *
28
+ * // In EidosProvider's onReady, or after login:
29
+ * const { warmed, failed } = await warmCache([products, userProfile, settings])
30
+ */
31
+ export declare function warmCache(handles: ResourceHandle[]): Promise<WarmCacheResult>;
32
+ export {};
package/dist/resource.js CHANGED
@@ -1,79 +1,81 @@
1
1
  import { useEidosStore as h } from "./store.js";
2
- import { sendToWorker as p } from "./sw-bridge.js";
3
- var d = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), g = null;
4
- function _(e) {
5
- g = e;
2
+ import { sendToWorker as g } from "./sw-bridge.js";
3
+ var d = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), v = null;
4
+ function q(e) {
5
+ v = e;
6
6
  }
7
- function f(e) {
7
+ function u(e) {
8
8
  return e.includes("*") || /:[^/]+/.test(e);
9
9
  }
10
- function k(e) {
10
+ function R(e) {
11
11
  return "^" + e.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, ".+").replace(/\*/g, "[^/]+").replace(/:[^/]+/g, "[^/]+") + "$";
12
12
  }
13
- function m(e, s) {
14
- const a = b(s), t = f(e) ? k(e) : void 0, r = {
13
+ function w(e, a) {
14
+ const s = k(a), t = u(e) ? R(e) : void 0, r = {
15
15
  url: e,
16
- config: s,
17
- strategy: a,
16
+ config: a,
17
+ strategy: s,
18
18
  status: "idle",
19
19
  cacheHits: 0,
20
20
  cacheMisses: 0
21
21
  };
22
- return h.getState().registerResource(e, r), p({
22
+ return h.getState().registerResource(e, r), g({
23
23
  type: "EIDOS_REGISTER_RESOURCE",
24
24
  url: e,
25
- strategy: a.swStrategy,
26
- cacheName: a.cacheName,
27
- ...t !== void 0 && { pattern: t }
25
+ strategy: s.swStrategy,
26
+ cacheName: s.cacheName,
27
+ ...t !== void 0 && { pattern: t },
28
+ ...a.maxAge !== void 0 && { maxAge: a.maxAge },
29
+ ...a.maxEntries !== void 0 && { maxEntries: a.maxEntries }
28
30
  }), {
29
- strategy: a,
31
+ strategy: s,
30
32
  regexStr: t
31
33
  };
32
34
  }
33
- function y(e, s, a) {
35
+ function y(e, a, s) {
34
36
  return async () => {
35
- p({
37
+ g({
36
38
  type: "EIDOS_CLEAR_CACHE",
37
39
  url: e
38
40
  });
39
- const t = await caches.open(s.cacheName).catch(() => null);
41
+ const t = await caches.open(a.cacheName).catch(() => null);
40
42
  if (t) {
41
- const r = await t.keys(), n = a ? new RegExp(a) : null, c = e.startsWith("http");
42
- await Promise.all(r.filter((i) => {
43
- const o = i.url, u = new URL(o).pathname;
44
- return n ? n.test(c ? o : u) : c ? o === e : o === e || u === e;
45
- }).map((i) => t.delete(i)));
43
+ const r = await t.keys(), n = s ? new RegExp(s) : null, i = e.startsWith("http");
44
+ await Promise.all(r.filter((c) => {
45
+ const o = c.url, f = new URL(o).pathname;
46
+ return n ? n.test(i ? o : f) : i ? o === e : o === e || f === e;
47
+ }).map((c) => t.delete(c)));
46
48
  }
47
- f(e) || h.getState().updateResource(e, {
49
+ u(e) || h.getState().updateResource(e, {
48
50
  status: "stale",
49
51
  cachedAt: void 0,
50
52
  lastEvent: "cache-cleared",
51
53
  cacheHits: 0,
52
54
  cacheMisses: 0
53
- }), g?.(["eidos", e]);
55
+ }), v?.(["eidos", e]);
54
56
  };
55
57
  }
56
58
  function E(e) {
57
59
  return () => {
58
- d.delete(e), p({
60
+ d.delete(e), g({
59
61
  type: "EIDOS_UNREGISTER_RESOURCE",
60
62
  url: e
61
63
  }), h.getState().unregisterResource(e);
62
64
  };
63
65
  }
64
- function A(e, s) {
65
- if (f(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.`);
66
+ function _(e, a) {
67
+ if (u(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.`);
66
68
  if (d.has(e)) return d.get(e);
67
- const { strategy: a } = m(e, s), t = {
69
+ const { strategy: s } = w(e, a), t = {
68
70
  url: e,
69
- config: s,
70
- strategy: a,
71
+ config: a,
72
+ strategy: s,
71
73
  fetch: async () => {
72
74
  const r = l.get(e);
73
- if (r) return r.then((c) => c.clone());
74
- const n = S(e, s, a);
75
+ if (r) return r.then((i) => i.clone());
76
+ const n = S(e, a, s);
75
77
  return l.set(e, n), n.finally(() => l.delete(e)).catch(() => {
76
- }), n.then((c) => c.clone());
78
+ }), n.then((i) => i.clone());
77
79
  },
78
80
  json: async () => (await t.fetch()).json(),
79
81
  query: () => ({
@@ -83,47 +85,47 @@ function A(e, s) {
83
85
  prefetch: async () => {
84
86
  await t.fetch();
85
87
  },
86
- invalidate: y(e, a, void 0),
88
+ invalidate: y(e, s, void 0),
87
89
  unregister: E(e)
88
90
  };
89
91
  return d.set(e, t), t;
90
92
  }
91
- function $(e, s) {
92
- if (!f(e)) throw new Error(`[eidos] resourcePattern('${e}') is not a URL pattern — use resource('${e}', config) instead.`);
93
+ function $(e, a) {
94
+ if (!u(e)) throw new Error(`[eidos] resourcePattern('${e}') is not a URL pattern — use resource('${e}', config) instead.`);
93
95
  if (d.has(e)) return d.get(e);
94
- const { strategy: a, regexStr: t } = m(e, s), r = {
96
+ const { strategy: s, regexStr: t } = w(e, a), r = {
95
97
  url: e,
96
- config: s,
97
- strategy: a,
98
- invalidate: y(e, a, t),
98
+ config: a,
99
+ strategy: s,
100
+ invalidate: y(e, s, t),
99
101
  unregister: E(e)
100
102
  };
101
103
  return d.set(e, r), r;
102
104
  }
103
- async function S(e, s, a) {
105
+ async function S(e, a, s) {
104
106
  const t = h.getState();
105
107
  t.updateResource(e, {
106
108
  status: "fetching",
107
109
  fetchedAt: Date.now()
108
110
  });
109
- const r = await caches.open(a.cacheName).catch(() => null);
111
+ const r = await caches.open(s.cacheName).catch(() => null);
110
112
  try {
111
- if (a.swStrategy !== "network-first") {
112
- const i = r ? await r.match(e).catch(() => null) : null, o = h.getState().resources[e], u = s.maxAge !== void 0 && o?.cachedAt !== void 0 && Date.now() - o.cachedAt > s.maxAge;
113
- if (i && !u)
113
+ if (s.swStrategy !== "network-first") {
114
+ const c = r ? await r.match(e).catch(() => null) : null, o = h.getState().resources[e], f = a.maxAge !== void 0 && o?.cachedAt !== void 0 && Date.now() - o.cachedAt > a.maxAge;
115
+ if (c && !f)
114
116
  return t.updateResource(e, {
115
117
  status: "fresh",
116
118
  lastEvent: "cache-hit",
117
119
  cacheHits: (o?.cacheHits ?? 0) + 1
118
- }), a.swStrategy === "stale-while-revalidate" && fetch(e, { signal: AbortSignal.timeout(5e3) }).then(async (w) => {
119
- w.ok && r && (await r.put(e, w.clone()), h.getState().updateResource(e, {
120
+ }), s.swStrategy === "stale-while-revalidate" && fetch(e, { signal: AbortSignal.timeout(5e3) }).then(async (m) => {
121
+ m.ok && r && (await r.put(e, m.clone()), h.getState().updateResource(e, {
120
122
  cachedAt: Date.now(),
121
123
  lastEvent: "cache-updated"
122
124
  }));
123
125
  }).catch(() => {
124
- }), i;
125
- const R = h.getState().resources[e];
126
- t.updateResource(e, { cacheMisses: (R?.cacheMisses ?? 0) + 1 });
126
+ }), c;
127
+ const x = h.getState().resources[e];
128
+ t.updateResource(e, { cacheMisses: (x?.cacheMisses ?? 0) + 1 });
127
129
  }
128
130
  const n = await fetch(e);
129
131
  if (n.ok)
@@ -133,30 +135,30 @@ async function S(e, s, a) {
133
135
  lastEvent: "cache-updated"
134
136
  }), n;
135
137
  t.updateResource(e, { status: n.status === 503 ? "offline" : "error" });
136
- const c = n.headers.get("X-Eidos-Offline") === "true";
137
- throw new Error(c ? `offline: no cached response for ${e}` : `${n.status} ${n.statusText}`);
138
+ const i = n.headers.get("X-Eidos-Offline") === "true";
139
+ throw new Error(i ? `offline: no cached response for ${e}` : `${n.status} ${n.statusText}`);
138
140
  } catch (n) {
139
- const c = r ? await r.match(e).catch(() => null) : null;
140
- if (c) {
141
- const i = h.getState().resources[e];
141
+ const i = r ? await r.match(e).catch(() => null) : null;
142
+ if (i) {
143
+ const c = h.getState().resources[e];
142
144
  return t.updateResource(e, {
143
145
  status: "fresh",
144
146
  lastEvent: "cache-hit",
145
- cacheHits: (i?.cacheHits ?? 0) + 1
146
- }), c;
147
+ cacheHits: (c?.cacheHits ?? 0) + 1
148
+ }), i;
147
149
  }
148
150
  throw t.updateResource(e, { status: "error" }), n;
149
151
  }
150
152
  }
151
- function b(e) {
152
- const s = e.strategy;
153
- return e.offline ? v(s ?? "stale-while-revalidate", e.cacheName, e.version) : v(s ?? "network-first", e.cacheName, e.version);
153
+ function k(e) {
154
+ const a = e.strategy;
155
+ return e.offline ? p(a ?? "stale-while-revalidate", e.cacheName, e.version) : p(a ?? "network-first", e.cacheName, e.version);
154
156
  }
155
- var x = {
157
+ var A = {
156
158
  "stale-while-revalidate": "StaleWhileRevalidate",
157
159
  "cache-first": "CacheFirst",
158
160
  "network-first": "NetworkFirst"
159
- }, C = {
161
+ }, b = {
160
162
  "stale-while-revalidate": {
161
163
  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.",
162
164
  behavior: [
@@ -165,10 +167,10 @@ var x = {
165
167
  "Offline → return cached version if available, 503 if not",
166
168
  "Reconnect → next request triggers a background refresh"
167
169
  ],
168
- equivalentCode: `// Workbox equivalent
170
+ equivalentCode: `// Workbox equivalent (maxEntries/maxAge are configured via ResourceConfig)
169
171
  new StaleWhileRevalidate({
170
172
  cacheName: 'eidos-resources-v1',
171
- plugins: [new ExpirationPlugin({ maxEntries: 60 })],
173
+ plugins: [new ExpirationPlugin({ maxEntries: config.maxEntries, maxAgeSeconds: config.maxAge && config.maxAge / 1000 })],
172
174
  })`
173
175
  },
174
176
  "cache-first": {
@@ -177,12 +179,12 @@ new StaleWhileRevalidate({
177
179
  "Cache hit → return immediately, no network request made",
178
180
  "Cache miss → fetch from network, cache the response, return it",
179
181
  "Offline → return cached version, 503 if cache is empty",
180
- "Cache never expires unless explicitly invalidated"
182
+ "Cache never expires unless maxAge is set or explicitly invalidated"
181
183
  ],
182
- equivalentCode: `// Workbox equivalent
184
+ equivalentCode: `// Workbox equivalent (maxEntries/maxAge are configured via ResourceConfig)
183
185
  new CacheFirst({
184
186
  cacheName: 'eidos-resources-v1',
185
- plugins: [new ExpirationPlugin({ maxEntries: 60 })],
187
+ plugins: [new ExpirationPlugin({ maxEntries: config.maxEntries, maxAgeSeconds: config.maxAge && config.maxAge / 1000 })],
186
188
  })`
187
189
  },
188
190
  "network-first": {
@@ -200,29 +202,29 @@ new NetworkFirst({
200
202
  })`
201
203
  }
202
204
  };
203
- function v(e, s, a) {
204
- const t = C[e], r = s ?? "eidos-resources-v1";
205
+ function p(e, a, s) {
206
+ const t = b[e], r = a ?? "eidos-resources-v1";
205
207
  return {
206
- name: x[e],
208
+ name: A[e],
207
209
  swStrategy: e,
208
- cacheName: a !== void 0 ? `${r}-v${a}` : r,
210
+ cacheName: s !== void 0 ? `${r}-v${s}` : r,
209
211
  reasoning: t.reasoning,
210
212
  behavior: t.behavior,
211
213
  equivalentCode: ""
212
214
  };
213
215
  }
214
216
  async function O(e) {
215
- const s = await Promise.allSettled(e.map((t) => t.prefetch())), a = s.filter((t) => t.status === "rejected").map((t) => t.reason);
217
+ const a = await Promise.allSettled(e.map((t) => t.prefetch())), s = a.filter((t) => t.status === "rejected").map((t) => t.reason);
216
218
  return {
217
- warmed: s.filter((t) => t.status === "fulfilled").length,
218
- failed: a.length,
219
- errors: a
219
+ warmed: a.filter((t) => t.status === "fulfilled").length,
220
+ failed: s.length,
221
+ errors: s
220
222
  };
221
223
  }
222
224
  export {
223
- A as resource,
225
+ _ as resource,
224
226
  $ as resourcePattern,
225
- _ as setQueryInvalidator,
227
+ q as setQueryInvalidator,
226
228
  O as warmCache
227
229
  };
228
230