@sweidos/eidos 1.2.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 CHANGED
@@ -114,20 +114,22 @@ if ('queued' in result) {
114
114
 
115
115
  ## What you get
116
116
 
117
- | Feature | Description |
118
- | --------------------------- | ------------------------------------------------------------------------------------ |
119
- | **Auto strategy selection** | `offline: true` → StaleWhileRevalidate. No config needed. Override when you want. |
120
- | **Persistent action queue** | Failed writes go to IndexedDB and replay with exponential backoff on reconnect. |
121
- | **Request deduplication** | Concurrent `resource.fetch()` calls share one in-flight request. |
122
- | **Optimistic updates** | `onOptimistic` / `onRollback` callbacks for instant UI feedback. |
123
- | **Conflict resolution** | `onConflict` decides per 4xx whether to retry or drop a queued action. |
124
- | **Queue prioritization** | `priority: 'high' \| 'normal' \| 'low'` — high items replay before normal. |
125
- | **Cache warming** | `warmCache(handles[])` bulk-prefetches resources on login/init. |
126
- | **URL patterns** | `/api/products/*`, `/api/users/:id`, `**` wildcardsSW intercepts all matches. |
127
- | **Background Sync** | Registers a `sync` tag so queued actions replay even after tab close. |
128
- | **Devtools panel** | `<EidosDevtools />` live queue, cache state, offline toggle, no CSS import. |
129
- | **Testing helpers** | `mockOffline`, `drainQueue`, `resetEidos`, `getCachedEntry` for Vitest/Jest. |
130
- | **OpenAPI codegen** | `npx eidos-gen openapi.json` generates typed `resource()` + `action()` declarations. |
117
+ | Feature | Description |
118
+ | --------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
119
+ | **Auto strategy selection** | `offline: true` → StaleWhileRevalidate. No config needed. Override when you want. |
120
+ | **Persistent action queue** | Failed writes go to IndexedDB and replay with exponential backoff on reconnect. |
121
+ | **Request deduplication** | Concurrent `resource.fetch()` calls share one in-flight request. |
122
+ | **Optimistic updates** | `onOptimistic` / `onRollback` callbacks for instant UI feedback. |
123
+ | **Conflict resolution** | `conflict: { strategy: 'serverWins' \| 'clientWins' \| 'merge' \| 'custom' }` on 4xx replay responses. |
124
+ | **Idempotent replay** | Stable `idempotencyKey` per invocation, forwarded to `fn` via `ActionContext` safe retries even after a dropped response. |
125
+ | **Cancellable actions** | `cancellable: true` `AbortSignal` per call, plus `handle.cancel(idempotencyKey)`. |
126
+ | **Queue prioritization** | `priority: 'high' \| 'normal' \| 'low'` high items replay before normal. |
127
+ | **Cache warming** | `warmCache(handles[])` bulk-prefetches resources on login/init. |
128
+ | **URL patterns** | `/api/products/*`, `/api/users/:id`, `**` wildcards SW intercepts all matches. |
129
+ | **Background Sync** | Registers a `sync` tag so queued actions replay even after tab close. |
130
+ | **Devtools panel** | `<EidosDevtools />` live queue, cache state, offline toggle, no CSS import. |
131
+ | **Testing helpers** | `mockOffline`, `drainQueue`, `resetEidos`, `getCachedEntry` for Vitest/Jest. |
132
+ | **OpenAPI codegen** | `npx eidos-gen openapi.json` generates typed `resource()` + `action()` declarations. |
131
133
 
132
134
  ---
133
135
 
@@ -158,6 +160,9 @@ const products = resource('/api/products', {
158
160
  strategy?: 'cache-first' | 'stale-while-revalidate' | 'network-first',
159
161
  cacheName?: string, // custom cache bucket
160
162
  maxAge?: number, // TTL in ms — re-fetch after expiry
163
+ version?: string | number, // bump when the response shape changes —
164
+ // appended to cacheName (e.g. 'eidos-resources-v1-v2')
165
+ // so old-shaped cache entries aren't served
161
166
  })
162
167
 
163
168
  await products.fetch() // Promise<Response>
@@ -175,20 +180,47 @@ products.query() // { queryKey, queryFn } for useQuery
175
180
  | `offline: true, strategy: 'cache-first'` | CacheFirst | Static assets, config data |
176
181
  | `offline: true, strategy: 'network-first'` | NetworkFirst | Always-fresh with offline fallback |
177
182
 
178
- URL patterns work on any handle: `/api/products/*`, `/api/users/:id`, `**`
183
+ ### `resourcePattern(pattern, config)`
184
+
185
+ For URL patterns — `/api/products/*`, `/api/users/:id`, `**` — the SW intercepts
186
+ all matching requests automatically, so there's no single URL to fetch. Use
187
+ `resourcePattern()` instead of `resource()`; it returns a handle with only
188
+ `invalidate()` and `unregister()`:
189
+
190
+ ```ts
191
+ const productPattern = resourcePattern('/api/products/*', { offline: true });
192
+
193
+ await productPattern.invalidate(); // clear all cached entries matching the pattern
194
+ productPattern.unregister();
195
+ ```
179
196
 
180
197
  ### `action(fn, config)`
181
198
 
182
199
  ```ts
183
- const createOrder = action(async (payload: OrderPayload) => { ... }, {
200
+ const createOrder = action(async (payload: OrderPayload, ctx: ActionContext) => { ... }, {
184
201
  reliability: 'neverLose', // persist to IDB + replay on reconnect
185
202
  name: 'createOrder', // stable name for post-reload replay
203
+ namespace?: string, // prefix actionId — avoids collisions across modules
186
204
  maxRetries?: number, // default: 3
187
205
  priority?: 'high' | 'normal' | 'low',
206
+ cancellable?: boolean, // adds AbortSignal to ctx, enables handle.cancel(key)
188
207
  onOptimistic?: (...args) => void, // instant UI update
189
208
  onRollback?: (...args) => void, // revert on permanent failure
190
- onConflict?: (error, args) => 'retry' | 'skip', // 4xx handler
209
+ conflict?: { // 4xx replay handling
210
+ strategy: 'serverWins' | 'clientWins' | 'merge' | 'custom',
211
+ resolve?: (ctx) => 'retry' | 'skip' | { resolved: args },
212
+ },
191
213
  })
214
+
215
+ // ctx.idempotencyKey is stable across retries — forward as e.g. an
216
+ // `Idempotency-Key` header so the server can dedupe replayed writes.
217
+
218
+ // Module-level helpers (used by the devtools queue inspector, and usable
219
+ // directly): cancel/remove a queue item by idempotency key, or reset a
220
+ // 'failed' item back to 'pending' for the next replayQueue().
221
+ import { cancelByIdempotencyKey, requeueItem } from '@sweidos/eidos'
222
+ await cancelByIdempotencyKey(idempotencyKey) // true if cancelled/removed
223
+ await requeueItem(queueItemId) // true if it was 'failed'
192
224
  ```
193
225
 
194
226
  ### React hooks
@@ -389,21 +421,19 @@ Panel shows: live queue state · cache entries · SW status · offline simulatio
389
421
 
390
422
  ## How it compares
391
423
 
392
- | | **Eidos** | Workbox | RTK Query / TanStack Query |
393
- | -------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------ | --------------------------------------------------------------- |
394
- | Service worker setup | Generated for you — `resource()`/`action()` declarations drive the SW | Hand-write `routing` + `strategies` config | None — no SW |
395
- | Caching strategy | Auto-derived from intent (`offline: true` → SWR, etc.), inspectable via devtools | Manually chosen per route | Configurable `staleTime`/`gcTime`, no Cache Storage integration |
396
- | Offline writes | `action()` + `reliability: 'neverLose'` → IndexedDB queue, auto-replay, exponential backoff | Background Sync plugin, you wire the queue | No built-in offline mutation queue |
397
- | Framework support | React, Svelte, Vue, Next.js, React Native, vanilla JS | Framework-agnostic (SW only) | Per-library (RTK Query = Redux, TanStack = many) |
398
- | TanStack Query bridge | `@sweidos/eidos/query` — drop-in `useEidosQuery`/`useEidosMutation` | — | Native |
399
- | Bundle size (core, brotli) | ~5.4 kB | ~3-6 kB (modular) | ~13 kB (TanStack Query core) |
400
-
401
- Eidos isn't a replacement for TanStack Query — `@sweidos/eidos/query` is a thin
402
- adapter so you keep TQ's cache/devtools while Eidos owns the offline layer
403
- (SW caching + IndexedDB write queue). Workbox is a lower-level toolkit Eidos
404
- generates strategies _for_; Eidos picks and configures the strategy from your
405
- `resource()`/`action()` declarations instead of you writing `workbox-*` config
406
- by hand.
424
+ | | **Eidos** | Workbox | RTK Query / TanStack Query |
425
+ | --------------------- | ----------------------------------------------------- | ---------------------------- | -------------------------- |
426
+ | Service worker setup | Generated from `resource()`/`action()` declarations | Hand-written routing config | None — no SW |
427
+ | Caching strategy | Auto-derived from intent, inspectable via devtools | Manually chosen per route | `staleTime`/`gcTime` only |
428
+ | Offline writes | IndexedDB queue, auto-replay + backoff via `action()` | Background Sync, you wire it | No built-in mutation queue |
429
+ | Framework support | React, Svelte, Vue, Next.js, React Native, vanilla JS | Framework-agnostic (SW only) | Per-library |
430
+ | TanStack Query bridge | `@sweidos/eidos/query` adapter | — | Native |
431
+ | Bundle size (core) | ~6.3 kB brotli | ~3-6 kB (modular) | ~13 kB |
432
+
433
+ Not a TanStack Query replacement — `@sweidos/eidos/query` is a thin adapter so
434
+ you keep TQ's cache/devtools while Eidos owns the offline layer. Workbox is a
435
+ lower-level toolkit; Eidos picks and configures strategies for you instead of
436
+ hand-written `workbox-*` config.
407
437
 
408
438
  ---
409
439
 
package/dist/action.js CHANGED
@@ -1,86 +1,107 @@
1
- import { useEidosStore as y } from "./store.js";
2
- import { getSwRegistration as _ } from "./sw-bridge.js";
3
- import { idbQueueStorage as S } from "./idb.js";
4
- import { _getQueueStorage as M } from "./queue-storage.js";
5
- var h = /* @__PURE__ */ new Map(), k = /* @__PURE__ */ new Map(), C = /* @__PURE__ */ new Map(), Q = /* @__PURE__ */ new Map(), x = /* @__PURE__ */ new Map(), p = /* @__PURE__ */ new Map();
6
- function u() {
7
- return M() ?? S;
1
+ import { useEidosStore as l } from "./store.js";
2
+ import { getSwRegistration as A } from "./sw-bridge.js";
3
+ import { idbQueueStorage as C } from "./idb.js";
4
+ import { _getQueueStorage as _ } from "./queue-storage.js";
5
+ import { broadcastQueueSync as u } from "./queue-sync.js";
6
+ var w = /* @__PURE__ */ new Map(), R = /* @__PURE__ */ new Map(), Q = /* @__PURE__ */ new Map(), k = /* @__PURE__ */ new Map(), p = /* @__PURE__ */ new Map();
7
+ function c() {
8
+ return _() ?? C;
8
9
  }
9
10
  function m() {
10
11
  return crypto.randomUUID();
11
12
  }
12
- function v(e, t, i) {
13
- return e(...t, i);
13
+ function v(e, t, n) {
14
+ return e(...t, n);
14
15
  }
15
- function U(e, t) {
16
- const i = t.name || e.name || m(), a = t.namespace ? `${t.namespace}::${i}` : i;
17
- h.set(a, e), x.set(a, t), t.onRollback && k.set(a, t.onRollback), t.onConflict && C.set(a, t.onConflict), t.conflict && Q.set(a, t.conflict);
18
- const c = async (...n) => {
19
- const { isOnline: s } = y.getState(), l = t.reliability === "neverLose" || t.cancellable, o = l ? m() : "";
20
- let d;
16
+ function B(e, t) {
17
+ const n = t.name || e.name || m(), a = t.namespace ? `${t.namespace}::${n}` : n;
18
+ if (w.has(a)) throw new Error(`[eidos] duplicate action id "${a}" an action with this id is already registered. Pass a unique config.name or config.namespace.`);
19
+ w.set(a, e), k.set(a, t), t.onRollback && R.set(a, t.onRollback), t.conflict && Q.set(a, t.conflict);
20
+ const i = async (...r) => {
21
+ const { isOnline: o } = l.getState(), s = m();
22
+ let b;
21
23
  if (t.cancellable) {
22
- const f = new AbortController();
23
- p.set(o, f), d = f.signal;
24
+ const d = new AbortController();
25
+ p.set(s, d), b = d.signal;
24
26
  }
25
- const g = {
26
- idempotencyKey: o,
27
+ const y = {
28
+ idempotencyKey: s,
27
29
  attempt: 0,
28
- signal: d
30
+ signal: b
29
31
  };
30
- t.onOptimistic?.(...n, g);
32
+ t.onOptimistic?.(...r, y);
31
33
  try {
32
34
  if (t.reliability === "neverLose") {
33
- if (!s) return R(a, a, n, t, o);
35
+ if (!o) return h(a, a, r, t, s);
34
36
  try {
35
- return await v(e, n, g);
36
- } catch (f) {
37
- if (A(f)) throw f;
38
- return R(a, a, n, t, o);
37
+ return await v(e, r, y);
38
+ } catch (d) {
39
+ if (x(d)) throw d;
40
+ return h(a, a, r, t, s);
39
41
  }
40
42
  }
41
43
  try {
42
- return l ? await v(e, n, g) : await e(...n);
43
- } catch (f) {
44
- throw t.onRollback?.(...n), f;
44
+ return await v(e, r, y);
45
+ } catch (d) {
46
+ throw t.onRollback?.(...r, y), d;
45
47
  }
46
48
  } finally {
47
- t.cancellable && p.delete(o);
49
+ t.cancellable && p.delete(s);
48
50
  }
49
- }, r = async (n) => {
50
- const s = p.get(n);
51
- if (s)
52
- return s.abort(), !0;
53
- const l = (await u().getAll()).find((o) => o.idempotencyKey === n && o.status === "pending");
54
- return l ? (y.getState().removeQueueItem(l.id), await u().remove(l.id), !0) : !1;
55
51
  };
56
- return Object.defineProperty(c, "id", {
52
+ return Object.defineProperty(i, "id", {
57
53
  value: a,
58
54
  writable: !1
59
- }), Object.defineProperty(c, "config", {
55
+ }), Object.defineProperty(i, "config", {
60
56
  value: t,
61
57
  writable: !1
62
- }), Object.defineProperty(c, "cancel", {
63
- value: r,
58
+ }), Object.defineProperty(i, "cancel", {
59
+ value: S,
64
60
  writable: !1
65
- }), c;
61
+ }), i;
66
62
  }
67
- async function R(e, t, i, a, c) {
68
- const r = m(), n = {
63
+ async function S(e) {
64
+ const t = p.get(e);
65
+ if (t)
66
+ return t.abort(), !0;
67
+ const n = (await c().getAll()).find((a) => a.idempotencyKey === e && a.status === "pending");
68
+ return n ? (l.getState().removeQueueItem(n.id), u({
69
+ type: "remove",
70
+ id: n.id
71
+ }), await c().remove(n.id), !0) : !1;
72
+ }
73
+ async function F(e) {
74
+ const t = (await c().getAll()).find((a) => a.id === e);
75
+ if (!t || t.status !== "failed") return !1;
76
+ const n = {
77
+ status: "pending",
78
+ error: void 0,
79
+ nextRetryAt: void 0,
80
+ retryCount: 0
81
+ };
82
+ return l.getState().updateQueueItem(e, n), u({
83
+ type: "update",
84
+ id: e,
85
+ update: n
86
+ }), await c().update(e, n), !0;
87
+ }
88
+ async function h(e, t, n, a, i) {
89
+ const r = m(), o = {
69
90
  schemaVersion: 2,
70
91
  id: r,
71
92
  actionId: e,
72
93
  actionName: t,
73
- idempotencyKey: c,
74
- args: i,
94
+ idempotencyKey: i,
95
+ args: n,
75
96
  queuedAt: Date.now(),
76
97
  retryCount: 0,
77
98
  maxRetries: a.maxRetries ?? 3,
78
99
  status: "pending",
79
100
  priority: a.priority ?? "normal"
80
101
  };
81
- await u().add(n), y.getState().addQueueItem(n);
102
+ await c().add(o), l.getState().addQueueItem(o);
82
103
  try {
83
- const s = _();
104
+ const s = A();
84
105
  s && "sync" in s && await s.sync.register("eidos-queue-replay");
85
106
  } catch {
86
107
  }
@@ -90,7 +111,7 @@ async function R(e, t, i, a, c) {
90
111
  message: `"${t}" queued — will execute when online`
91
112
  };
92
113
  }
93
- function A(e) {
114
+ function x(e) {
94
115
  return e instanceof DOMException && e.name === "AbortError";
95
116
  }
96
117
  function K(e) {
@@ -101,10 +122,10 @@ function K(e) {
101
122
  }
102
123
  return !1;
103
124
  }
104
- function O(e) {
125
+ function M(e) {
105
126
  return Math.min(2e3 * 2 ** e, 3e5) * (0.8 + Math.random() * 0.4);
106
127
  }
107
- function w() {
128
+ function f() {
108
129
  return {
109
130
  attempted: 0,
110
131
  succeeded: 0,
@@ -115,138 +136,173 @@ function w() {
115
136
  cancelled: 0
116
137
  };
117
138
  }
118
- var b = !1, q = "eidos-queue-replay";
119
- async function $() {
120
- const e = y.getState();
121
- if (!e.isOnline) return w();
122
- if (typeof navigator < "u" && navigator.locks) return navigator.locks.request(q, { ifAvailable: !0 }, async (t) => t ? I(e) : w());
123
- if (b) return w();
124
- b = !0;
139
+ var g = !1, q = "eidos-queue-replay";
140
+ async function V() {
141
+ const e = l.getState();
142
+ if (!e.isOnline) return f();
143
+ if (typeof navigator < "u" && navigator.locks) return navigator.locks.request(q, { ifAvailable: !0 }, async (t) => t ? I(e) : f());
144
+ if (g) return f();
145
+ g = !0;
125
146
  try {
126
147
  return await I(e);
127
148
  } finally {
128
- b = !1;
149
+ g = !1;
129
150
  }
130
151
  }
131
- async function E(e, t) {
132
- const i = h.get(e.actionId);
133
- if (!i) return "skipped";
134
- const a = x.get(e.actionId)?.cancellable;
135
- let c;
152
+ async function O(e, t) {
153
+ const n = Date.now();
154
+ t.updateQueueItem(e.id, {
155
+ status: "succeeded",
156
+ completedAt: n
157
+ }), u({
158
+ type: "update",
159
+ id: e.id,
160
+ update: {
161
+ status: "succeeded",
162
+ completedAt: n
163
+ }
164
+ }), await c().update(e.id, {
165
+ status: "succeeded",
166
+ completedAt: n
167
+ }), setTimeout(() => {
168
+ t.removeQueueItem(e.id), u({
169
+ type: "remove",
170
+ id: e.id
171
+ }), c().remove(e.id);
172
+ }, 3e3);
173
+ }
174
+ async function E(e, t, n) {
175
+ const a = Q.get(e.actionId);
176
+ let i;
177
+ if (a) switch (a.strategy) {
178
+ case "serverWins":
179
+ i = "skip";
180
+ break;
181
+ case "clientWins":
182
+ i = "retry";
183
+ break;
184
+ case "merge":
185
+ case "custom": {
186
+ const r = {
187
+ error: n,
188
+ args: e.args,
189
+ attempt: e.retryCount,
190
+ idempotencyKey: e.idempotencyKey
191
+ };
192
+ i = a.resolve?.(r) ?? "retry";
193
+ break;
194
+ }
195
+ }
196
+ if (i === "skip")
197
+ return t.removeQueueItem(e.id), u({
198
+ type: "remove",
199
+ id: e.id
200
+ }), await c().remove(e.id), "conflicted";
201
+ i && typeof i == "object" && (e.args = i.resolved, t.updateQueueItem(e.id, { args: i.resolved }), u({
202
+ type: "update",
203
+ id: e.id,
204
+ update: { args: i.resolved }
205
+ }), await c().update(e.id, { args: i.resolved }));
206
+ }
207
+ async function P(e, t, n) {
208
+ const a = e.retryCount + 1;
209
+ if (a >= e.maxRetries) {
210
+ const r = {
211
+ status: "failed",
212
+ error: String(n),
213
+ retryCount: a
214
+ };
215
+ t.updateQueueItem(e.id, r), u({
216
+ type: "update",
217
+ id: e.id,
218
+ update: r
219
+ }), await c().update(e.id, r);
220
+ const o = {
221
+ idempotencyKey: e.idempotencyKey,
222
+ attempt: a
223
+ };
224
+ return R.get(e.actionId)?.(...e.args, o), "failed";
225
+ }
226
+ const i = {
227
+ status: "pending",
228
+ retryCount: a,
229
+ nextRetryAt: Date.now() + M(a)
230
+ };
231
+ return t.updateQueueItem(e.id, i), u({
232
+ type: "update",
233
+ id: e.id,
234
+ update: i
235
+ }), await c().update(e.id, i), "retrying";
236
+ }
237
+ async function D(e, t) {
238
+ const n = w.get(e.actionId);
239
+ if (!n) return "skipped";
240
+ const a = k.get(e.actionId)?.cancellable;
241
+ let i;
136
242
  if (a) {
137
- const n = new AbortController();
138
- p.set(e.idempotencyKey, n), c = n.signal;
243
+ const o = new AbortController();
244
+ p.set(e.idempotencyKey, o), i = o.signal;
139
245
  }
140
246
  const r = {
141
247
  idempotencyKey: e.idempotencyKey,
142
248
  attempt: e.retryCount,
143
- signal: c
249
+ signal: i
144
250
  };
145
251
  try {
146
- await v(i, e.args, r);
147
- const n = Date.now();
148
- return t.updateQueueItem(e.id, {
149
- status: "succeeded",
150
- completedAt: n
151
- }), await u().update(e.id, {
152
- status: "succeeded",
153
- completedAt: n
154
- }), setTimeout(() => {
155
- t.removeQueueItem(e.id), u().remove(e.id);
156
- }, 3e3), "succeeded";
157
- } catch (n) {
158
- if (A(n))
159
- return t.removeQueueItem(e.id), await u().remove(e.id), "cancelled";
160
- if (K(n)) {
161
- const l = Q.get(e.actionId);
162
- let o;
163
- if (l) switch (l.strategy) {
164
- case "serverWins":
165
- o = "skip";
166
- break;
167
- case "clientWins":
168
- case "lastWriteWins":
169
- o = "retry";
170
- break;
171
- case "merge":
172
- case "custom": {
173
- const d = {
174
- error: n,
175
- args: e.args,
176
- attempt: e.retryCount,
177
- idempotencyKey: e.idempotencyKey
178
- };
179
- o = l.resolve?.(d) ?? "retry";
180
- break;
181
- }
182
- }
183
- else {
184
- const d = C.get(e.actionId);
185
- d && (o = d(n, e.args));
186
- }
187
- if (o === "skip")
188
- return t.removeQueueItem(e.id), await u().remove(e.id), "conflicted";
189
- o && typeof o == "object" && (e.args = o.resolved, t.updateQueueItem(e.id, { args: o.resolved }), await u().update(e.id, { args: o.resolved }));
190
- }
191
- const s = e.retryCount + 1;
192
- if (s >= e.maxRetries)
193
- return t.updateQueueItem(e.id, {
194
- status: "failed",
195
- error: String(n),
196
- retryCount: s
197
- }), await u().update(e.id, {
198
- status: "failed",
199
- error: String(n),
200
- retryCount: s
201
- }), k.get(e.actionId)?.(...e.args), "failed";
202
- {
203
- const l = Date.now() + O(s);
204
- return t.updateQueueItem(e.id, {
205
- status: "pending",
206
- retryCount: s,
207
- nextRetryAt: l
208
- }), await u().update(e.id, {
209
- status: "pending",
210
- retryCount: s,
211
- nextRetryAt: l
212
- }), "retrying";
252
+ return await v(n, e.args, r), await O(e, t), "succeeded";
253
+ } catch (o) {
254
+ if (x(o))
255
+ return t.removeQueueItem(e.id), u({
256
+ type: "remove",
257
+ id: e.id
258
+ }), await c().remove(e.id), "cancelled";
259
+ if (K(o)) {
260
+ const s = await E(e, t, o);
261
+ if (s) return s;
213
262
  }
263
+ return P(e, t, o);
214
264
  } finally {
215
265
  a && p.delete(e.idempotencyKey);
216
266
  }
217
267
  }
218
- async function D(e, t, i) {
268
+ async function j(e, t, n) {
219
269
  if (e.length === 0) return;
220
- const a = e.filter((r) => h.has(r.actionId));
221
- if (i.skipped += e.length - a.length, a.length > 0) {
222
- t.batchUpdateQueueItems(a.map((r) => ({
223
- id: r.id,
270
+ const a = e.filter((r) => w.has(r.actionId));
271
+ if (n.skipped += e.length - a.length, a.length > 0) {
272
+ const r = a.map((o) => ({
273
+ id: o.id,
224
274
  update: { status: "replaying" }
225
- })));
226
- for (const r of a) u().update(r.id, { status: "replaying" });
275
+ }));
276
+ t.batchUpdateQueueItems(r), u({
277
+ type: "batchUpdate",
278
+ updates: r
279
+ });
280
+ for (const o of a) c().update(o.id, { status: "replaying" });
227
281
  }
228
- const c = await Promise.allSettled(a.map((r) => E(r, t)));
229
- for (const r of c) {
230
- const n = r.status === "fulfilled" ? r.value : "failed";
231
- n === "skipped" ? i.skipped++ : n === "conflicted" ? i.conflicted++ : n === "cancelled" ? i.cancelled++ : (i.attempted++, i[n]++);
282
+ const i = await Promise.allSettled(a.map((r) => D(r, t)));
283
+ for (const r of i) {
284
+ const o = r.status === "fulfilled" ? r.value : "failed";
285
+ o === "skipped" ? n.skipped++ : o === "conflicted" ? n.conflicted++ : o === "cancelled" ? n.cancelled++ : (n.attempted++, n[o]++);
232
286
  }
233
287
  }
234
288
  async function I(e) {
235
- const t = await u().getPending(), i = Date.now(), a = t.filter((r) => r.retryCount < r.maxRetries && (!r.nextRetryAt || r.nextRetryAt <= i)), c = w();
289
+ const t = await c().getPending(), n = Date.now(), a = t.filter((r) => r.retryCount < r.maxRetries && (!r.nextRetryAt || r.nextRetryAt <= n)), i = f();
236
290
  for (const r of [
237
291
  "high",
238
292
  "normal",
239
293
  "low"
240
- ]) await D(a.filter((n) => (n.priority ?? "normal") === r), e, c);
241
- return c;
294
+ ]) await j(a.filter((o) => (o.priority ?? "normal") === r), e, i);
295
+ return i;
242
296
  }
243
- async function T() {
244
- await u().clear(), y.getState().hydrateQueue([]);
297
+ async function Y() {
298
+ await c().clear(), l.getState().hydrateQueue([]);
245
299
  }
246
300
  export {
247
- U as action,
248
- T as clearQueue,
249
- $ as replayQueue
301
+ B as action,
302
+ S as cancelByIdempotencyKey,
303
+ Y as clearQueue,
304
+ V as replayQueue,
305
+ F as requeueItem
250
306
  };
251
307
 
252
308
  //# sourceMappingURL=action.js.map