@sweidos/eidos 2.0.0 → 2.2.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
@@ -4,12 +4,12 @@
4
4
  [![npm downloads](https://img.shields.io/npm/dm/@sweidos/eidos?color=22C55E)](https://www.npmjs.com/package/@sweidos/eidos)
5
5
  [![bundle size](https://deno.bundlejs.com/badge?q=@sweidos/eidos&badge=detailed&color=22C55E)](https://bundlejs.com/?q=@sweidos/eidos)
6
6
  [![TypeScript](https://img.shields.io/badge/TypeScript-strict-22C55E)](https://www.typescriptlang.org/)
7
- [![CI](https://github.com/iamadi11/eidos/actions/workflows/deploy.yml/badge.svg)](https://github.com/iamadi11/eidos/actions)
7
+ [![CI](https://github.com/sweidos/eidos/actions/workflows/deploy.yml/badge.svg)](https://github.com/sweidos/eidos/actions)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-22C55E.svg)](LICENSE)
9
9
 
10
- > **Describe intent. The runtime figures out how.**
10
+ > **Never lose a write.**
11
11
 
12
- Declare what your app needs offline. Eidos picks the cache strategy, registers the Service Worker, and persists your action queue to IndexedDB — automatically.
12
+ Declare what your app needs offline. Eidos picks the cache strategy, registers the Service Worker, and persists your action queue to IndexedDB — with idempotency keys and cross-tab replay locks built in, so a queued mutation runs exactly once.
13
13
 
14
14
  ```ts
15
15
  import { resource, action } from '@sweidos/eidos';
@@ -135,16 +135,19 @@ if ('queued' in result) {
135
135
 
136
136
  ## Framework support
137
137
 
138
- | Framework | Import path | Notes |
139
- | ---------------------- | ----------------------------- | -------------------------------------------------------------- |
140
- | **React** | `@sweidos/eidos` | Hooks + `EidosProvider` |
141
- | **Next.js App Router** | `@sweidos/eidos/nextjs` | Pre-marked `'use client'` — no wrapper needed |
142
- | **SvelteKit** | `@sweidos/eidos/sveltekit` | `initEidosSvelteKit()` in `onMount`, framework-agnostic stores |
143
- | **Vue** | `@sweidos/eidos` | Framework-agnostic stores via `eidosStatus.subscribe()` |
144
- | **React Native** | `@sweidos/eidos/react-native` | AsyncStorage-backed queue, same `action()` API |
145
- | **Vanilla JS** | `@sweidos/eidos` | `eidosStatus`, `eidosQueue`, `eidosQueueStats` stores |
146
- | **Vite** | `@sweidos/eidos/vite` | Plugin auto-copies `eidos-sw.js` on every build |
147
- | **TanStack Query** | `@sweidos/eidos/query` | `useEidosQuery`, `useEidosMutation`, `withEidosQueryClient` |
138
+ | Framework | Import path | Notes |
139
+ | -------------------------- | ----------------------------- | -------------------------------------------------------------- |
140
+ | **React** | `@sweidos/eidos` | Hooks + `EidosProvider` |
141
+ | **Next.js App Router** | `@sweidos/eidos/nextjs` | Pre-marked `'use client'` — no wrapper needed |
142
+ | **Next.js Server Actions** | `@eidos/next` | `serverAction()` neverLose wrapper + idempotency context |
143
+ | **SvelteKit** | `@sweidos/eidos/sveltekit` | `initEidosSvelteKit()` in `onMount`, framework-agnostic stores |
144
+ | **Vue** | `@sweidos/eidos` | Framework-agnostic stores via `eidosStatus.subscribe()` |
145
+ | **React Native** | `@sweidos/eidos/react-native` | AsyncStorage-backed queue, same `action()` API |
146
+ | **Vanilla JS** | `@sweidos/eidos` | `eidosStatus`, `eidosQueue`, `eidosQueueStats` stores |
147
+ | **Vite** | `@sweidos/eidos/vite` | Plugin auto-copies `eidos-sw.js` on every build |
148
+ | **CRDT merge (Yjs)** | `@eidos/crdt-yjs` | `createYjsMergeResolver()` for `conflict.strategy: 'merge'` |
149
+ | **TanStack Query** | `@sweidos/eidos/query` | `useEidosQuery`, `useEidosMutation`, `withEidosQueryClient` |
150
+ | **Tauri / Electron** | `@eidos/sqlite-storage` | SQLite-backed `QueueStorage`, same `action()` API |
148
151
 
149
152
  ---
150
153
 
@@ -160,6 +163,9 @@ const products = resource('/api/products', {
160
163
  strategy?: 'cache-first' | 'stale-while-revalidate' | 'network-first',
161
164
  cacheName?: string, // custom cache bucket
162
165
  maxAge?: number, // TTL in ms — re-fetch after expiry
166
+ version?: string | number, // bump when the response shape changes —
167
+ // appended to cacheName (e.g. 'eidos-resources-v1-v2')
168
+ // so old-shaped cache entries aren't served
163
169
  })
164
170
 
165
171
  await products.fetch() // Promise<Response>
@@ -211,6 +217,13 @@ const createOrder = action(async (payload: OrderPayload, ctx: ActionContext) =>
211
217
 
212
218
  // ctx.idempotencyKey is stable across retries — forward as e.g. an
213
219
  // `Idempotency-Key` header so the server can dedupe replayed writes.
220
+
221
+ // Module-level helpers (used by the devtools queue inspector, and usable
222
+ // directly): cancel/remove a queue item by idempotency key, or reset a
223
+ // 'failed' item back to 'pending' for the next replayQueue().
224
+ import { cancelByIdempotencyKey, requeueItem } from '@sweidos/eidos'
225
+ await cancelByIdempotencyKey(idempotencyKey) // true if cancelled/removed
226
+ await requeueItem(queueItemId) // true if it was 'failed'
214
227
  ```
215
228
 
216
229
  ### React hooks
@@ -221,6 +234,9 @@ const { pending, failed } = useEidosQueueStats();
221
234
  const entry = useEidosResource('/api/products');
222
235
  const item = useEidosAction(queuedResult.id);
223
236
  useEidosOnDrain(() => toast('All offline actions synced!'));
237
+
238
+ // Cumulative neverLose outcome counters (queued/succeeded/failed/retried/conflicted/cancelled)
239
+ const { queued, succeeded, failed: failedCount } = useEidosReliabilityStats();
224
240
  ```
225
241
 
226
242
  ### Framework-agnostic stores
@@ -231,8 +247,25 @@ eidosStatus.subscribe(({ isOnline }) => { ... })
231
247
  eidosQueue.subscribe((queue) => { ... })
232
248
  eidosQueueStats.getState() // { pending, failed, replaying, total }
233
249
  eidosResource('/api/products').getState() // ResourceEntry | undefined
250
+ onQueueDrain(() => toast('All offline actions synced!')) // returns unsubscribe
251
+ eidosReliabilityStats.getState() // { queued, succeeded, failed, retried, conflicted, cancelled }
252
+ ```
253
+
254
+ ### Reliability telemetry
255
+
256
+ Opt in to periodic reporting of cumulative `neverLose` queue outcomes — wire it
257
+ up to your analytics backend:
258
+
259
+ ```ts
260
+ initEidos({
261
+ onReliabilityReport: (stats) => analytics.track('eidos_reliability', stats),
262
+ reliabilityReportInterval: 60_000, // default
263
+ });
234
264
  ```
235
265
 
266
+ The same counters are visible live in `<EidosDevtools />` under the
267
+ "Reliability" tab.
268
+
236
269
  ---
237
270
 
238
271
  ## TanStack Query
@@ -392,10 +425,16 @@ Panel shows: live queue state · cache entries · SW status · offline simulatio
392
425
 
393
426
  **Next.js** — import from `@sweidos/eidos/nextjs`. Pre-marked `'use client'`, works in App Router layouts without a wrapper.
394
427
 
428
+ **Next.js Server Actions** — `@eidos/next`'s `serverAction()` wraps a `'use server'` function with `action()` (`reliability: 'neverLose'` by default), keyed by `config.name` + `config.namespace`. `getActionContext()` / `idempotencyHeaders()` recover the `idempotencyKey`/`attempt` inside the action body.
429
+
395
430
  **SvelteKit** — `initEidosSvelteKit()` inside `onMount`. Framework-agnostic stores (`$eidosQueue`, `$eidosStatus`) work with Svelte's `$` auto-subscribe.
396
431
 
397
432
  **React Native** — `@sweidos/eidos/react-native` with AsyncStorage-backed queue. Same `action()` API surface, no Service Worker dependency.
398
433
 
434
+ **Tauri / Electron** — `@eidos/sqlite-storage` with a SQLite-backed `QueueStorage`. Pass a `@tauri-apps/plugin-sql` `Database` directly, or wrap `better-sqlite3` with the `SqliteLike` interface. Same `action()` API surface, no Service Worker dependency.
435
+
436
+ **CRDT merge (Yjs)** — `@eidos/crdt-yjs`'s `createYjsMergeResolver()` builds a `conflict.resolve` for the `'merge'`/`'custom'` strategy that applies the server's Yjs state and the queued local update to a `Y.Doc`, then rewrites the queued args with the merged update — automatic, loss-free reconciliation of concurrent edits instead of a hand-written `resolve()`.
437
+
399
438
  ---
400
439
 
401
440
  ## Known limitations
@@ -418,7 +457,7 @@ Panel shows: live queue state · cache entries · SW status · offline simulatio
418
457
  | Offline writes | IndexedDB queue, auto-replay + backoff via `action()` | Background Sync, you wire it | No built-in mutation queue |
419
458
  | Framework support | React, Svelte, Vue, Next.js, React Native, vanilla JS | Framework-agnostic (SW only) | Per-library |
420
459
  | TanStack Query bridge | `@sweidos/eidos/query` adapter | — | Native |
421
- | Bundle size (core) | ~6 kB brotli | ~3-6 kB (modular) | ~13 kB |
460
+ | Bundle size (core) | ~6.5 kB brotli | ~3-6 kB (modular) | ~13 kB |
422
461
 
423
462
  Not a TanStack Query replacement — `@sweidos/eidos/query` is a thin adapter so
424
463
  you keep TQ's cache/devtools while Eidos owns the offline layer. Workbox is a
package/dist/action.js CHANGED
@@ -1,104 +1,120 @@
1
- import { useEidosStore as f } from "./store.js";
2
- import { getSwRegistration as _ } from "./sw-bridge.js";
3
- import { idbQueueStorage as A } from "./idb.js";
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
4
  import { _getQueueStorage as S } from "./queue-storage.js";
5
- import { broadcastQueueSync as d } from "./queue-sync.js";
6
- var m = /* @__PURE__ */ new Map(), k = /* @__PURE__ */ new Map(), Q = /* @__PURE__ */ new Map(), x = /* @__PURE__ */ new Map(), y = /* @__PURE__ */ new Map();
5
+ import { broadcastQueueSync as s } from "./queue-sync.js";
6
+ var g = /* @__PURE__ */ new Map(), I = /* @__PURE__ */ new Map(), Q = /* @__PURE__ */ new Map(), k = /* @__PURE__ */ new Map(), p = /* @__PURE__ */ new Map();
7
7
  function c() {
8
- return S() ?? A;
8
+ return S() ?? C;
9
9
  }
10
- function b() {
10
+ function m() {
11
11
  return crypto.randomUUID();
12
12
  }
13
- function h(e, t, o) {
14
- return e(...t, o);
13
+ function v(e, t, n) {
14
+ return e(...t, n);
15
15
  }
16
- function F(e, t) {
17
- const o = t.name || e.name || b(), a = t.namespace ? `${t.namespace}::${o}` : o;
18
- if (m.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
- m.set(a, e), x.set(a, t), t.onRollback && k.set(a, t.onRollback), t.conflict && Q.set(a, t.conflict);
20
- const r = async (...n) => {
21
- const { isOnline: s } = f.getState(), u = b();
22
- let p;
16
+ function B(e, t) {
17
+ const n = t.name || e.name || m(), a = t.namespace ? `${t.namespace}::${n}` : n;
18
+ if (g.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
+ g.set(a, e), k.set(a, t), t.onRollback && I.set(a, t.onRollback), t.conflict && Q.set(a, t.conflict);
20
+ const i = async (...r) => {
21
+ const { isOnline: o } = l.getState(), u = m();
22
+ let b;
23
23
  if (t.cancellable) {
24
- const l = new AbortController();
25
- y.set(u, l), p = l.signal;
24
+ const d = new AbortController();
25
+ p.set(u, d), b = d.signal;
26
26
  }
27
- const w = {
27
+ const y = {
28
28
  idempotencyKey: u,
29
29
  attempt: 0,
30
- signal: p
30
+ signal: b
31
31
  };
32
- t.onOptimistic?.(...n, w);
32
+ t.onOptimistic?.(...r, y);
33
33
  try {
34
34
  if (t.reliability === "neverLose") {
35
- if (!s) return R(a, a, n, t, u);
35
+ if (!o) return h(a, a, r, t, u);
36
36
  try {
37
- return await h(e, n, w);
38
- } catch (l) {
39
- if (C(l)) throw l;
40
- return R(a, a, n, t, u);
37
+ return await v(e, r, y);
38
+ } catch (d) {
39
+ if (x(d)) throw d;
40
+ return h(a, a, r, t, u);
41
41
  }
42
42
  }
43
43
  try {
44
- return await h(e, n, w);
45
- } catch (l) {
46
- throw t.onRollback?.(...n, w), l;
44
+ return await v(e, r, y);
45
+ } catch (d) {
46
+ throw t.onRollback?.(...r, y), d;
47
47
  }
48
48
  } finally {
49
- t.cancellable && y.delete(u);
49
+ t.cancellable && p.delete(u);
50
50
  }
51
- }, i = async (n) => {
52
- const s = y.get(n);
53
- if (s)
54
- return s.abort(), !0;
55
- const u = (await c().getAll()).find((p) => p.idempotencyKey === n && p.status === "pending");
56
- return u ? (f.getState().removeQueueItem(u.id), d({
57
- type: "remove",
58
- id: u.id
59
- }), await c().remove(u.id), !0) : !1;
60
51
  };
61
- return Object.defineProperty(r, "id", {
52
+ return Object.defineProperty(i, "id", {
62
53
  value: a,
63
54
  writable: !1
64
- }), Object.defineProperty(r, "config", {
55
+ }), Object.defineProperty(i, "config", {
65
56
  value: t,
66
57
  writable: !1
67
- }), Object.defineProperty(r, "cancel", {
68
- value: i,
58
+ }), Object.defineProperty(i, "cancel", {
59
+ value: _,
69
60
  writable: !1
70
- }), r;
61
+ }), i;
62
+ }
63
+ async function _(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), s({
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), s({
83
+ type: "update",
84
+ id: e,
85
+ update: n
86
+ }), await c().update(e, n), !0;
71
87
  }
72
- async function R(e, t, o, a, r) {
73
- const i = b(), n = {
88
+ async function h(e, t, n, a, i) {
89
+ const r = m(), o = {
74
90
  schemaVersion: 2,
75
- id: i,
91
+ id: r,
76
92
  actionId: e,
77
93
  actionName: t,
78
- idempotencyKey: r,
79
- args: o,
94
+ idempotencyKey: i,
95
+ args: n,
80
96
  queuedAt: Date.now(),
81
97
  retryCount: 0,
82
98
  maxRetries: a.maxRetries ?? 3,
83
99
  status: "pending",
84
100
  priority: a.priority ?? "normal"
85
101
  };
86
- await c().add(n), f.getState().addQueueItem(n);
102
+ await c().add(o), l.getState().addQueueItem(o), l.getState().recordReliabilityEvent("queued");
87
103
  try {
88
- const s = _();
89
- s && "sync" in s && await s.sync.register("eidos-queue-replay");
104
+ const u = A();
105
+ u && "sync" in u && await u.sync.register("eidos-queue-replay");
90
106
  } catch {
91
107
  }
92
108
  return {
93
109
  queued: !0,
94
- id: i,
110
+ id: r,
95
111
  message: `"${t}" queued — will execute when online`
96
112
  };
97
113
  }
98
- function C(e) {
114
+ function x(e) {
99
115
  return e instanceof DOMException && e.name === "AbortError";
100
116
  }
101
- function K(e) {
117
+ function E(e) {
102
118
  if (e instanceof Response) return e.status >= 400 && e.status < 500;
103
119
  if (typeof e == "object" && e !== null) {
104
120
  const t = e.status;
@@ -106,10 +122,10 @@ function K(e) {
106
122
  }
107
123
  return !1;
108
124
  }
109
- function M(e) {
125
+ function K(e) {
110
126
  return Math.min(2e3 * 2 ** e, 3e5) * (0.8 + Math.random() * 0.4);
111
127
  }
112
- function g() {
128
+ function f() {
113
129
  return {
114
130
  attempted: 0,
115
131
  succeeded: 0,
@@ -120,171 +136,173 @@ function g() {
120
136
  cancelled: 0
121
137
  };
122
138
  }
123
- var v = !1, O = "eidos-queue-replay";
139
+ var w = !1, q = "eidos-queue-replay";
124
140
  async function V() {
125
- const e = f.getState();
126
- if (!e.isOnline) return g();
127
- if (typeof navigator < "u" && navigator.locks) return navigator.locks.request(O, { ifAvailable: !0 }, async (t) => t ? I(e) : g());
128
- if (v) return g();
129
- v = !0;
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 ? R(e) : f());
144
+ if (w) return f();
145
+ w = !0;
130
146
  try {
131
- return await I(e);
147
+ return await R(e);
132
148
  } finally {
133
- v = !1;
149
+ w = !1;
134
150
  }
135
151
  }
136
- async function q(e, t) {
137
- const o = Date.now();
152
+ async function M(e, t) {
153
+ const n = Date.now();
138
154
  t.updateQueueItem(e.id, {
139
155
  status: "succeeded",
140
- completedAt: o
141
- }), d({
156
+ completedAt: n
157
+ }), t.recordReliabilityEvent("succeeded"), s({
142
158
  type: "update",
143
159
  id: e.id,
144
160
  update: {
145
161
  status: "succeeded",
146
- completedAt: o
162
+ completedAt: n
147
163
  }
148
164
  }), await c().update(e.id, {
149
165
  status: "succeeded",
150
- completedAt: o
166
+ completedAt: n
151
167
  }), setTimeout(() => {
152
- t.removeQueueItem(e.id), d({
168
+ t.removeQueueItem(e.id), s({
153
169
  type: "remove",
154
170
  id: e.id
155
171
  }), c().remove(e.id);
156
172
  }, 3e3);
157
173
  }
158
- async function E(e, t, o) {
174
+ async function O(e, t, n) {
159
175
  const a = Q.get(e.actionId);
160
- let r;
176
+ let i;
161
177
  if (a) switch (a.strategy) {
162
178
  case "serverWins":
163
- r = "skip";
179
+ i = "skip";
164
180
  break;
165
181
  case "clientWins":
166
- r = "retry";
182
+ i = "retry";
167
183
  break;
168
184
  case "merge":
169
185
  case "custom": {
170
- const i = {
171
- error: o,
186
+ const r = {
187
+ error: n,
172
188
  args: e.args,
173
189
  attempt: e.retryCount,
174
190
  idempotencyKey: e.idempotencyKey
175
191
  };
176
- r = a.resolve?.(i) ?? "retry";
192
+ i = a.resolve?.(r) ?? "retry";
177
193
  break;
178
194
  }
179
195
  }
180
- if (r === "skip")
181
- return t.removeQueueItem(e.id), d({
196
+ if (i === "skip")
197
+ return t.removeQueueItem(e.id), t.recordReliabilityEvent("conflicted"), s({
182
198
  type: "remove",
183
199
  id: e.id
184
200
  }), await c().remove(e.id), "conflicted";
185
- r && typeof r == "object" && (e.args = r.resolved, t.updateQueueItem(e.id, { args: r.resolved }), d({
201
+ i && typeof i == "object" && (e.args = i.resolved, t.updateQueueItem(e.id, { args: i.resolved }), s({
186
202
  type: "update",
187
203
  id: e.id,
188
- update: { args: r.resolved }
189
- }), await c().update(e.id, { args: r.resolved }));
204
+ update: { args: i.resolved }
205
+ }), await c().update(e.id, { args: i.resolved }));
190
206
  }
191
- async function P(e, t, o) {
207
+ async function P(e, t, n) {
192
208
  const a = e.retryCount + 1;
193
209
  if (a >= e.maxRetries) {
194
- const i = {
210
+ const r = {
195
211
  status: "failed",
196
- error: String(o),
212
+ error: String(n),
197
213
  retryCount: a
198
214
  };
199
- t.updateQueueItem(e.id, i), d({
215
+ t.updateQueueItem(e.id, r), t.recordReliabilityEvent("failed"), s({
200
216
  type: "update",
201
217
  id: e.id,
202
- update: i
203
- }), await c().update(e.id, i);
204
- const n = {
218
+ update: r
219
+ }), await c().update(e.id, r);
220
+ const o = {
205
221
  idempotencyKey: e.idempotencyKey,
206
222
  attempt: a
207
223
  };
208
- return k.get(e.actionId)?.(...e.args, n), "failed";
224
+ return I.get(e.actionId)?.(...e.args, o), "failed";
209
225
  }
210
- const r = {
226
+ const i = {
211
227
  status: "pending",
212
228
  retryCount: a,
213
- nextRetryAt: Date.now() + M(a)
229
+ nextRetryAt: Date.now() + K(a)
214
230
  };
215
- return t.updateQueueItem(e.id, r), d({
231
+ return t.updateQueueItem(e.id, i), t.recordReliabilityEvent("retried"), s({
216
232
  type: "update",
217
233
  id: e.id,
218
- update: r
219
- }), await c().update(e.id, r), "retrying";
234
+ update: i
235
+ }), await c().update(e.id, i), "retrying";
220
236
  }
221
237
  async function D(e, t) {
222
- const o = m.get(e.actionId);
223
- if (!o) return "skipped";
224
- const a = x.get(e.actionId)?.cancellable;
225
- let r;
238
+ const n = g.get(e.actionId);
239
+ if (!n) return "skipped";
240
+ const a = k.get(e.actionId)?.cancellable;
241
+ let i;
226
242
  if (a) {
227
- const n = new AbortController();
228
- y.set(e.idempotencyKey, n), r = n.signal;
243
+ const o = new AbortController();
244
+ p.set(e.idempotencyKey, o), i = o.signal;
229
245
  }
230
- const i = {
246
+ const r = {
231
247
  idempotencyKey: e.idempotencyKey,
232
248
  attempt: e.retryCount,
233
- signal: r
249
+ signal: i
234
250
  };
235
251
  try {
236
- return await h(o, e.args, i), await q(e, t), "succeeded";
237
- } catch (n) {
238
- if (C(n))
239
- return t.removeQueueItem(e.id), d({
252
+ return await v(n, e.args, r), await M(e, t), "succeeded";
253
+ } catch (o) {
254
+ if (x(o))
255
+ return t.removeQueueItem(e.id), t.recordReliabilityEvent("cancelled"), s({
240
256
  type: "remove",
241
257
  id: e.id
242
258
  }), await c().remove(e.id), "cancelled";
243
- if (K(n)) {
244
- const s = await E(e, t, n);
245
- if (s) return s;
259
+ if (E(o)) {
260
+ const u = await O(e, t, o);
261
+ if (u) return u;
246
262
  }
247
- return P(e, t, n);
263
+ return P(e, t, o);
248
264
  } finally {
249
- a && y.delete(e.idempotencyKey);
265
+ a && p.delete(e.idempotencyKey);
250
266
  }
251
267
  }
252
- async function j(e, t, o) {
268
+ async function j(e, t, n) {
253
269
  if (e.length === 0) return;
254
- const a = e.filter((i) => m.has(i.actionId));
255
- if (o.skipped += e.length - a.length, a.length > 0) {
256
- const i = a.map((n) => ({
257
- id: n.id,
270
+ const a = e.filter((r) => g.has(r.actionId));
271
+ if (n.skipped += e.length - a.length, a.length > 0) {
272
+ const r = a.map((o) => ({
273
+ id: o.id,
258
274
  update: { status: "replaying" }
259
275
  }));
260
- t.batchUpdateQueueItems(i), d({
276
+ t.batchUpdateQueueItems(r), s({
261
277
  type: "batchUpdate",
262
- updates: i
278
+ updates: r
263
279
  });
264
- for (const n of a) c().update(n.id, { status: "replaying" });
280
+ for (const o of a) c().update(o.id, { status: "replaying" });
265
281
  }
266
- const r = await Promise.allSettled(a.map((i) => D(i, t)));
267
- for (const i of r) {
268
- const n = i.status === "fulfilled" ? i.value : "failed";
269
- n === "skipped" ? o.skipped++ : n === "conflicted" ? o.conflicted++ : n === "cancelled" ? o.cancelled++ : (o.attempted++, o[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]++);
270
286
  }
271
287
  }
272
- async function I(e) {
273
- const t = await c().getPending(), o = Date.now(), a = t.filter((i) => i.retryCount < i.maxRetries && (!i.nextRetryAt || i.nextRetryAt <= o)), r = g();
274
- for (const i of [
288
+ async function R(e) {
289
+ const t = await c().getPending(), n = Date.now(), a = t.filter((r) => r.retryCount < r.maxRetries && (!r.nextRetryAt || r.nextRetryAt <= n)), i = f();
290
+ for (const r of [
275
291
  "high",
276
292
  "normal",
277
293
  "low"
278
- ]) await j(a.filter((n) => (n.priority ?? "normal") === i), e, r);
279
- return r;
294
+ ]) await j(a.filter((o) => (o.priority ?? "normal") === r), e, i);
295
+ return i;
280
296
  }
281
297
  async function Y() {
282
- await c().clear(), f.getState().hydrateQueue([]);
298
+ await c().clear(), l.getState().hydrateQueue([]);
283
299
  }
284
300
  export {
285
- F as action,
301
+ B as action,
302
+ _ as cancelByIdempotencyKey,
286
303
  Y as clearQueue,
287
- V as replayQueue
304
+ V as replayQueue,
305
+ F as requeueItem
288
306
  };
289
307
 
290
308
  //# sourceMappingURL=action.js.map