@sweidos/eidos 1.0.33 → 1.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 +108 -81
- package/dist/action.js +119 -86
- package/dist/async-storage-adapter.js +15 -12
- package/dist/devtools.js +953 -555
- package/dist/eidos.cjs +15 -0
- package/dist/idb.js +59 -56
- package/dist/index.d.ts +37 -15
- package/dist/index.js +42 -41
- package/dist/nextjs.js +1 -10
- package/dist/query.cjs +131 -0
- package/dist/query.js +121 -41
- package/dist/queue-storage.js +5 -4
- package/dist/react/Devtools.d.ts +1 -1
- package/dist/react/Provider.js +11 -7
- package/dist/react/hooks.js +48 -38
- package/dist/react-native.js +47 -53
- package/dist/replay.js +15 -0
- package/dist/resource.js +77 -79
- package/dist/runtime.js +22 -28
- package/dist/store-slices.js +43 -0
- package/dist/store.js +32 -49
- package/dist/stores.js +25 -22
- package/dist/sveltekit.js +22 -6
- package/dist/sw-bridge.js +48 -46
- package/dist/testing.cjs +165 -0
- package/dist/testing.js +140 -70
- package/dist/version.js +4 -3
- package/dist/vite.cjs +48 -0
- package/dist/vite.js +45 -29
- package/package.json +48 -27
- package/dist/action.js.map +0 -1
- package/dist/async-storage-adapter.js.map +0 -1
- package/dist/eidos.cjs.js +0 -14
- package/dist/eidos.cjs.js.map +0 -1
- package/dist/idb.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/query.cjs.js +0 -48
- package/dist/queue-storage.js.map +0 -1
- package/dist/react/Provider.js.map +0 -1
- package/dist/react/hooks.js.map +0 -1
- package/dist/resource.js.map +0 -1
- package/dist/runtime.js.map +0 -1
- package/dist/store.js.map +0 -1
- package/dist/stores.js.map +0 -1
- package/dist/sw-bridge.js.map +0 -1
- package/dist/testing.cjs.js +0 -86
- package/dist/version.js.map +0 -1
- package/dist/vite.cjs.js +0 -31
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@sweidos/eidos)
|
|
4
4
|
[](https://www.npmjs.com/package/@sweidos/eidos)
|
|
5
|
-
[](https://bundlejs.com/?q=@sweidos/eidos)
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
|
7
7
|
[](https://github.com/iamadi11/eidos/actions)
|
|
8
8
|
[](LICENSE)
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
Declare what your app needs offline. Eidos picks the cache strategy, registers the Service Worker, and persists your action queue to IndexedDB — automatically.
|
|
13
13
|
|
|
14
14
|
```ts
|
|
15
|
-
import { resource, action } from '@sweidos/eidos'
|
|
15
|
+
import { resource, action } from '@sweidos/eidos';
|
|
16
16
|
|
|
17
|
-
const products = resource('/api/products', { offline: true })
|
|
18
|
-
const createOrder = action(orderApi.create, { reliability: 'neverLose' })
|
|
17
|
+
const products = resource('/api/products', { offline: true });
|
|
18
|
+
const createOrder = action(orderApi.create, { reliability: 'neverLose' });
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
No service worker file to write. No cache strategy to configure. No retry logic to implement.
|
|
@@ -60,12 +60,12 @@ npm install @sweidos/eidos
|
|
|
60
60
|
|
|
61
61
|
```ts
|
|
62
62
|
// vite.config.ts
|
|
63
|
-
import { eidos } from '@sweidos/eidos/vite'
|
|
64
|
-
import { defineConfig } from 'vite'
|
|
63
|
+
import { eidos } from '@sweidos/eidos/vite';
|
|
64
|
+
import { defineConfig } from 'vite';
|
|
65
65
|
|
|
66
66
|
export default defineConfig({
|
|
67
67
|
plugins: [eidos()],
|
|
68
|
-
})
|
|
68
|
+
});
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
> **Without Vite** — copy manually: `cp node_modules/@sweidos/eidos/dist/eidos-sw.js public/`
|
|
@@ -74,39 +74,39 @@ export default defineConfig({
|
|
|
74
74
|
|
|
75
75
|
```tsx
|
|
76
76
|
// main.tsx
|
|
77
|
-
import { EidosProvider } from '@sweidos/eidos'
|
|
78
|
-
import { createRoot } from 'react-dom/client'
|
|
79
|
-
import { App } from './App'
|
|
77
|
+
import { EidosProvider } from '@sweidos/eidos';
|
|
78
|
+
import { createRoot } from 'react-dom/client';
|
|
79
|
+
import { App } from './App';
|
|
80
80
|
|
|
81
81
|
createRoot(document.getElementById('root')!).render(
|
|
82
82
|
<EidosProvider swPath="/eidos-sw.js">
|
|
83
83
|
<App />
|
|
84
|
-
</EidosProvider
|
|
85
|
-
)
|
|
84
|
+
</EidosProvider>,
|
|
85
|
+
);
|
|
86
86
|
```
|
|
87
87
|
|
|
88
88
|
```ts
|
|
89
89
|
// src/lib/eidos.ts ← module scope required for queue replay after reload
|
|
90
|
-
import { resource, action } from '@sweidos/eidos'
|
|
90
|
+
import { resource, action } from '@sweidos/eidos';
|
|
91
91
|
|
|
92
|
-
export const products = resource('/api/products', { offline: true })
|
|
92
|
+
export const products = resource('/api/products', { offline: true });
|
|
93
93
|
|
|
94
94
|
export const createOrder = action(
|
|
95
95
|
async (payload: OrderPayload) => {
|
|
96
|
-
const res = await fetch('/api/orders', { method: 'POST', body: JSON.stringify(payload) })
|
|
97
|
-
return res.json()
|
|
96
|
+
const res = await fetch('/api/orders', { method: 'POST', body: JSON.stringify(payload) });
|
|
97
|
+
return res.json();
|
|
98
98
|
},
|
|
99
99
|
{ reliability: 'neverLose', name: 'createOrder' },
|
|
100
|
-
)
|
|
100
|
+
);
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
```tsx
|
|
104
104
|
// In components — works the same online and offline
|
|
105
|
-
const result = await createOrder({ productId: 1, qty: 2 })
|
|
105
|
+
const result = await createOrder({ productId: 1, qty: 2 });
|
|
106
106
|
|
|
107
107
|
if ('queued' in result) {
|
|
108
108
|
// Saved to IndexedDB — replays automatically on reconnect
|
|
109
|
-
console.log(result.message)
|
|
109
|
+
console.log(result.message);
|
|
110
110
|
}
|
|
111
111
|
```
|
|
112
112
|
|
|
@@ -114,35 +114,35 @@ if ('queued' in result) {
|
|
|
114
114
|
|
|
115
115
|
## What you get
|
|
116
116
|
|
|
117
|
-
| Feature
|
|
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**
|
|
122
|
-
| **Optimistic updates**
|
|
123
|
-
| **Conflict resolution**
|
|
124
|
-
| **Queue prioritization**
|
|
125
|
-
| **Cache warming**
|
|
126
|
-
| **URL patterns**
|
|
127
|
-
| **Background Sync**
|
|
128
|
-
| **Devtools panel**
|
|
129
|
-
| **Testing helpers**
|
|
130
|
-
| **OpenAPI codegen**
|
|
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`, `**` wildcards — SW 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. |
|
|
131
131
|
|
|
132
132
|
---
|
|
133
133
|
|
|
134
134
|
## Framework support
|
|
135
135
|
|
|
136
|
-
| Framework
|
|
137
|
-
|
|
138
|
-
| **React**
|
|
139
|
-
| **Next.js App Router** | `@sweidos/eidos/nextjs`
|
|
140
|
-
| **SvelteKit**
|
|
141
|
-
| **Vue**
|
|
142
|
-
| **React Native**
|
|
143
|
-
| **Vanilla JS**
|
|
144
|
-
| **Vite**
|
|
145
|
-
| **TanStack Query**
|
|
136
|
+
| Framework | Import path | Notes |
|
|
137
|
+
| ---------------------- | ----------------------------- | -------------------------------------------------------------- |
|
|
138
|
+
| **React** | `@sweidos/eidos` | Hooks + `EidosProvider` |
|
|
139
|
+
| **Next.js App Router** | `@sweidos/eidos/nextjs` | Pre-marked `'use client'` — no wrapper needed |
|
|
140
|
+
| **SvelteKit** | `@sweidos/eidos/sveltekit` | `initEidosSvelteKit()` in `onMount`, framework-agnostic stores |
|
|
141
|
+
| **Vue** | `@sweidos/eidos` | Framework-agnostic stores via `eidosStatus.subscribe()` |
|
|
142
|
+
| **React Native** | `@sweidos/eidos/react-native` | AsyncStorage-backed queue, same `action()` API |
|
|
143
|
+
| **Vanilla JS** | `@sweidos/eidos` | `eidosStatus`, `eidosQueue`, `eidosQueueStats` stores |
|
|
144
|
+
| **Vite** | `@sweidos/eidos/vite` | Plugin auto-copies `eidos-sw.js` on every build |
|
|
145
|
+
| **TanStack Query** | `@sweidos/eidos/query` | `useEidosQuery`, `useEidosMutation`, `withEidosQueryClient` |
|
|
146
146
|
|
|
147
147
|
---
|
|
148
148
|
|
|
@@ -169,11 +169,11 @@ products.query() // { queryKey, queryFn } for useQuery
|
|
|
169
169
|
|
|
170
170
|
**Auto-selected strategy:**
|
|
171
171
|
|
|
172
|
-
| Config
|
|
173
|
-
|
|
174
|
-
| `offline: true`
|
|
175
|
-
| `offline: true, strategy: 'cache-first'`
|
|
176
|
-
| `offline: true, strategy: 'network-first'` | NetworkFirst
|
|
172
|
+
| Config | Strategy | Use when |
|
|
173
|
+
| ------------------------------------------ | -------------------- | ----------------------------------- |
|
|
174
|
+
| `offline: true` | StaleWhileRevalidate | Default — fast + background refresh |
|
|
175
|
+
| `offline: true, strategy: 'cache-first'` | CacheFirst | Static assets, config data |
|
|
176
|
+
| `offline: true, strategy: 'network-first'` | NetworkFirst | Always-fresh with offline fallback |
|
|
177
177
|
|
|
178
178
|
URL patterns work on any handle: `/api/products/*`, `/api/users/:id`, `**`
|
|
179
179
|
|
|
@@ -194,11 +194,11 @@ const createOrder = action(async (payload: OrderPayload) => { ... }, {
|
|
|
194
194
|
### React hooks
|
|
195
195
|
|
|
196
196
|
```ts
|
|
197
|
-
const { isOnline, swStatus }
|
|
198
|
-
const { pending, failed }
|
|
199
|
-
const entry
|
|
200
|
-
const item
|
|
201
|
-
useEidosOnDrain(() => toast('All offline actions synced!'))
|
|
197
|
+
const { isOnline, swStatus } = useEidosStatus();
|
|
198
|
+
const { pending, failed } = useEidosQueueStats();
|
|
199
|
+
const entry = useEidosResource('/api/products');
|
|
200
|
+
const item = useEidosAction(queuedResult.id);
|
|
201
|
+
useEidosOnDrain(() => toast('All offline actions synced!'));
|
|
202
202
|
```
|
|
203
203
|
|
|
204
204
|
### Framework-agnostic stores
|
|
@@ -217,18 +217,18 @@ eidosResource('/api/products').getState() // ResourceEntry | undefined
|
|
|
217
217
|
|
|
218
218
|
```ts
|
|
219
219
|
// main.tsx — register once
|
|
220
|
-
withEidosQueryClient(queryClient)
|
|
220
|
+
withEidosQueryClient(queryClient);
|
|
221
221
|
|
|
222
222
|
// In components
|
|
223
|
-
const { data, isPending } = useEidosQuery<Product[]>(products)
|
|
223
|
+
const { data, isPending } = useEidosQuery<Product[]>(products);
|
|
224
224
|
|
|
225
225
|
const mutation = useEidosMutation(createOrder, {
|
|
226
226
|
invalidates: [products], // clears cache + invalidates TQ on success
|
|
227
227
|
onSuccess(data) {
|
|
228
|
-
if ('queued' in data) toast('Saved offline')
|
|
229
|
-
else toast(`Order #${data.id} created`)
|
|
228
|
+
if ('queued' in data) toast('Saved offline');
|
|
229
|
+
else toast(`Order #${data.id} created`);
|
|
230
230
|
},
|
|
231
|
-
})
|
|
231
|
+
});
|
|
232
232
|
```
|
|
233
233
|
|
|
234
234
|
---
|
|
@@ -237,25 +237,30 @@ const mutation = useEidosMutation(createOrder, {
|
|
|
237
237
|
|
|
238
238
|
```ts
|
|
239
239
|
import {
|
|
240
|
-
mockOffline,
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
240
|
+
mockOffline,
|
|
241
|
+
mockOnline,
|
|
242
|
+
drainQueue,
|
|
243
|
+
waitForQueueDrain,
|
|
244
|
+
getCachedEntry,
|
|
245
|
+
clearEidosCache,
|
|
246
|
+
resetEidos,
|
|
247
|
+
getEidosState,
|
|
248
|
+
} from '@sweidos/eidos/testing';
|
|
249
|
+
|
|
250
|
+
beforeEach(() => resetEidos());
|
|
246
251
|
|
|
247
252
|
it('queues action while offline', async () => {
|
|
248
|
-
mockOffline()
|
|
249
|
-
await createOrder({ productId: 1, qty: 2 })
|
|
250
|
-
expect(getEidosState().queue).toHaveLength(1)
|
|
251
|
-
})
|
|
253
|
+
mockOffline();
|
|
254
|
+
await createOrder({ productId: 1, qty: 2 });
|
|
255
|
+
expect(getEidosState().queue).toHaveLength(1);
|
|
256
|
+
});
|
|
252
257
|
|
|
253
258
|
it('replays on reconnect', async () => {
|
|
254
|
-
mockOffline()
|
|
255
|
-
await createOrder({ productId: 1, qty: 2 })
|
|
256
|
-
const result = await drainQueue()
|
|
257
|
-
expect(result.succeeded).toBe(1)
|
|
258
|
-
})
|
|
259
|
+
mockOffline();
|
|
260
|
+
await createOrder({ productId: 1, qty: 2 });
|
|
261
|
+
const result = await drainQueue();
|
|
262
|
+
expect(result.succeeded).toBe(1);
|
|
263
|
+
});
|
|
259
264
|
```
|
|
260
265
|
|
|
261
266
|
---
|
|
@@ -274,10 +279,12 @@ Handles path params, `$ref` resolution, request/response types, DELETE body omis
|
|
|
274
279
|
## Devtools
|
|
275
280
|
|
|
276
281
|
```tsx
|
|
277
|
-
import { EidosDevtools } from '@sweidos/eidos/devtools'
|
|
282
|
+
import { EidosDevtools } from '@sweidos/eidos/devtools';
|
|
278
283
|
|
|
279
284
|
// Drop anywhere — bottom-right floating panel, no CSS import
|
|
280
|
-
{
|
|
285
|
+
{
|
|
286
|
+
process.env.NODE_ENV === 'development' && <EidosDevtools />;
|
|
287
|
+
}
|
|
281
288
|
```
|
|
282
289
|
|
|
283
290
|
Panel shows: live queue state · cache entries · SW status · offline simulation toggle.
|
|
@@ -296,12 +303,32 @@ Panel shows: live queue state · cache entries · SW status · offline simulatio
|
|
|
296
303
|
|
|
297
304
|
## Known limitations
|
|
298
305
|
|
|
299
|
-
| Limitation
|
|
300
|
-
|
|
301
|
-
| GET-only caching
|
|
302
|
-
| Module-scope actions
|
|
303
|
-
| Single SW
|
|
304
|
-
| React Native resources | In-memory only — no Cache API or SW in RN. Action queue fully persists.
|
|
306
|
+
| Limitation | Detail |
|
|
307
|
+
| ---------------------- | ----------------------------------------------------------------------------------------------- |
|
|
308
|
+
| GET-only caching | SW intercepts `GET` only. Mutations go through `action()`. |
|
|
309
|
+
| Module-scope actions | `action()` must be at module scope so functions are registered before a reload triggers replay. |
|
|
310
|
+
| Single SW | Assumes one SW at the configured `swPath`. |
|
|
311
|
+
| React Native resources | In-memory only — no Cache API or SW in RN. Action queue fully persists. |
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## How it compares
|
|
316
|
+
|
|
317
|
+
| | **Eidos** | Workbox | RTK Query / TanStack Query |
|
|
318
|
+
| -------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------ | --------------------------------------------------------------- |
|
|
319
|
+
| Service worker setup | Generated for you — `resource()`/`action()` declarations drive the SW | Hand-write `routing` + `strategies` config | None — no SW |
|
|
320
|
+
| Caching strategy | Auto-derived from intent (`offline: true` → SWR, etc.), inspectable via devtools | Manually chosen per route | Configurable `staleTime`/`gcTime`, no Cache Storage integration |
|
|
321
|
+
| Offline writes | `action()` + `reliability: 'neverLose'` → IndexedDB queue, auto-replay, exponential backoff | Background Sync plugin, you wire the queue | No built-in offline mutation queue |
|
|
322
|
+
| Framework support | React, Svelte, Vue, Next.js, React Native, vanilla JS | Framework-agnostic (SW only) | Per-library (RTK Query = Redux, TanStack = many) |
|
|
323
|
+
| TanStack Query bridge | `@sweidos/eidos/query` — drop-in `useEidosQuery`/`useEidosMutation` | — | Native |
|
|
324
|
+
| Bundle size (core, brotli) | ~5.4 kB | ~3-6 kB (modular) | ~13 kB (TanStack Query core) |
|
|
325
|
+
|
|
326
|
+
Eidos isn't a replacement for TanStack Query — `@sweidos/eidos/query` is a thin
|
|
327
|
+
adapter so you keep TQ's cache/devtools while Eidos owns the offline layer
|
|
328
|
+
(SW caching + IndexedDB write queue). Workbox is a lower-level toolkit Eidos
|
|
329
|
+
generates strategies _for_; Eidos picks and configures the strategy from your
|
|
330
|
+
`resource()`/`action()` declarations instead of you writing `workbox-*` config
|
|
331
|
+
by hand.
|
|
305
332
|
|
|
306
333
|
---
|
|
307
334
|
|
package/dist/action.js
CHANGED
|
@@ -1,69 +1,66 @@
|
|
|
1
|
-
import { useEidosStore as
|
|
2
|
-
import { getSwRegistration as
|
|
3
|
-
import {
|
|
4
|
-
import { _getQueueStorage as
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
getPending: () => I(),
|
|
9
|
-
update: (e, t) => h(e, t),
|
|
10
|
-
remove: (e) => Q(e),
|
|
11
|
-
clear: () => g()
|
|
12
|
-
};
|
|
13
|
-
function o() {
|
|
14
|
-
return v() ?? S;
|
|
1
|
+
import { useEidosStore as s } from "./store.js";
|
|
2
|
+
import { getSwRegistration as w } from "./sw-bridge.js";
|
|
3
|
+
import { idbQueueStorage as g } from "./idb.js";
|
|
4
|
+
import { _getQueueStorage as m } from "./queue-storage.js";
|
|
5
|
+
var d = /* @__PURE__ */ new Map(), p = /* @__PURE__ */ new Map(), f = /* @__PURE__ */ new Map();
|
|
6
|
+
function u() {
|
|
7
|
+
return m() ?? g;
|
|
15
8
|
}
|
|
16
|
-
function
|
|
9
|
+
function y() {
|
|
17
10
|
return crypto.randomUUID();
|
|
18
11
|
}
|
|
19
|
-
function
|
|
20
|
-
const r = t.name || e.name ||
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (!n)
|
|
27
|
-
return p(r, r, a, t);
|
|
12
|
+
function C(e, t) {
|
|
13
|
+
const r = t.name || e.name || y();
|
|
14
|
+
d.set(r, e), t.onRollback && p.set(r, t.onRollback), t.onConflict && f.set(r, t.onConflict);
|
|
15
|
+
const a = async (...i) => {
|
|
16
|
+
const { isOnline: n } = s.getState();
|
|
17
|
+
if (t.onOptimistic?.(...i), t.reliability === "neverLose") {
|
|
18
|
+
if (!n) return l(r, r, i, t);
|
|
28
19
|
try {
|
|
29
|
-
return await e(...
|
|
20
|
+
return await e(...i);
|
|
30
21
|
} catch {
|
|
31
|
-
return
|
|
22
|
+
return l(r, r, i, t);
|
|
32
23
|
}
|
|
33
24
|
}
|
|
34
25
|
try {
|
|
35
|
-
return await e(...
|
|
36
|
-
} catch (
|
|
37
|
-
throw
|
|
26
|
+
return await e(...i);
|
|
27
|
+
} catch (o) {
|
|
28
|
+
throw t.onRollback?.(...i), o;
|
|
38
29
|
}
|
|
39
30
|
};
|
|
40
|
-
return Object.defineProperty(
|
|
31
|
+
return Object.defineProperty(a, "id", {
|
|
32
|
+
value: r,
|
|
33
|
+
writable: !1
|
|
34
|
+
}), Object.defineProperty(a, "config", {
|
|
35
|
+
value: t,
|
|
36
|
+
writable: !1
|
|
37
|
+
}), a;
|
|
41
38
|
}
|
|
42
|
-
async function
|
|
43
|
-
const
|
|
44
|
-
id:
|
|
39
|
+
async function l(e, t, r, a) {
|
|
40
|
+
const i = y(), n = {
|
|
41
|
+
id: i,
|
|
45
42
|
actionId: e,
|
|
46
43
|
actionName: t,
|
|
47
44
|
args: r,
|
|
48
45
|
queuedAt: Date.now(),
|
|
49
46
|
retryCount: 0,
|
|
50
|
-
maxRetries:
|
|
47
|
+
maxRetries: a.maxRetries ?? 3,
|
|
51
48
|
status: "pending",
|
|
52
|
-
priority:
|
|
49
|
+
priority: a.priority ?? "normal"
|
|
53
50
|
};
|
|
54
|
-
await
|
|
51
|
+
await u().add(n), s.getState().addQueueItem(n);
|
|
55
52
|
try {
|
|
56
|
-
const
|
|
57
|
-
|
|
53
|
+
const o = w();
|
|
54
|
+
o && "sync" in o && await o.sync.register("eidos-queue-replay");
|
|
58
55
|
} catch {
|
|
59
56
|
}
|
|
60
57
|
return {
|
|
61
58
|
queued: !0,
|
|
62
|
-
id:
|
|
59
|
+
id: i,
|
|
63
60
|
message: `"${t}" queued — will execute when online`
|
|
64
61
|
};
|
|
65
62
|
}
|
|
66
|
-
function
|
|
63
|
+
function h(e) {
|
|
67
64
|
if (e instanceof Response) return e.status >= 400 && e.status < 500;
|
|
68
65
|
if (typeof e == "object" && e !== null) {
|
|
69
66
|
const t = e.status;
|
|
@@ -71,76 +68,112 @@ function x(e) {
|
|
|
71
68
|
}
|
|
72
69
|
return !1;
|
|
73
70
|
}
|
|
74
|
-
function
|
|
71
|
+
function R(e) {
|
|
75
72
|
return Math.min(2e3 * 2 ** e, 3e5) * (0.8 + Math.random() * 0.4);
|
|
76
73
|
}
|
|
77
|
-
|
|
78
|
-
async function
|
|
79
|
-
const e =
|
|
80
|
-
if (!e.isOnline || c)
|
|
81
|
-
|
|
74
|
+
var c = !1;
|
|
75
|
+
async function _() {
|
|
76
|
+
const e = s.getState();
|
|
77
|
+
if (!e.isOnline || c) return {
|
|
78
|
+
attempted: 0,
|
|
79
|
+
succeeded: 0,
|
|
80
|
+
failed: 0,
|
|
81
|
+
retrying: 0,
|
|
82
|
+
skipped: 0,
|
|
83
|
+
conflicted: 0
|
|
84
|
+
};
|
|
82
85
|
c = !0;
|
|
83
86
|
try {
|
|
84
|
-
return await
|
|
87
|
+
return await Q(e);
|
|
85
88
|
} finally {
|
|
86
89
|
c = !1;
|
|
87
90
|
}
|
|
88
91
|
}
|
|
89
|
-
async function
|
|
90
|
-
|
|
91
|
-
const r = l.get(e.actionId);
|
|
92
|
+
async function b(e, t) {
|
|
93
|
+
const r = d.get(e.actionId);
|
|
92
94
|
if (!r) return "skipped";
|
|
93
95
|
try {
|
|
94
96
|
await r(...e.args);
|
|
95
97
|
const a = Date.now();
|
|
96
|
-
return t.updateQueueItem(e.id, {
|
|
97
|
-
|
|
98
|
+
return t.updateQueueItem(e.id, {
|
|
99
|
+
status: "succeeded",
|
|
100
|
+
completedAt: a
|
|
101
|
+
}), await u().update(e.id, {
|
|
102
|
+
status: "succeeded",
|
|
103
|
+
completedAt: a
|
|
104
|
+
}), setTimeout(() => {
|
|
105
|
+
t.removeQueueItem(e.id), u().remove(e.id);
|
|
98
106
|
}, 3e3), "succeeded";
|
|
99
107
|
} catch (a) {
|
|
100
|
-
if (
|
|
101
|
-
const
|
|
102
|
-
if (
|
|
103
|
-
return t.removeQueueItem(e.id), await
|
|
108
|
+
if (h(a)) {
|
|
109
|
+
const n = f.get(e.actionId);
|
|
110
|
+
if (n && n(a, e.args) === "skip")
|
|
111
|
+
return t.removeQueueItem(e.id), await u().remove(e.id), "conflicted";
|
|
104
112
|
}
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
107
|
-
return t.updateQueueItem(e.id, {
|
|
113
|
+
const i = e.retryCount + 1;
|
|
114
|
+
if (i >= e.maxRetries)
|
|
115
|
+
return t.updateQueueItem(e.id, {
|
|
116
|
+
status: "failed",
|
|
117
|
+
error: String(a),
|
|
118
|
+
retryCount: i
|
|
119
|
+
}), await u().update(e.id, {
|
|
120
|
+
status: "failed",
|
|
121
|
+
error: String(a),
|
|
122
|
+
retryCount: i
|
|
123
|
+
}), p.get(e.actionId)?.(...e.args), "failed";
|
|
108
124
|
{
|
|
109
|
-
const
|
|
110
|
-
return t.updateQueueItem(e.id, {
|
|
125
|
+
const n = Date.now() + R(i);
|
|
126
|
+
return t.updateQueueItem(e.id, {
|
|
127
|
+
status: "pending",
|
|
128
|
+
retryCount: i,
|
|
129
|
+
nextRetryAt: n
|
|
130
|
+
}), await u().update(e.id, {
|
|
131
|
+
status: "pending",
|
|
132
|
+
retryCount: i,
|
|
133
|
+
nextRetryAt: n
|
|
134
|
+
}), "retrying";
|
|
111
135
|
}
|
|
112
136
|
}
|
|
113
137
|
}
|
|
114
|
-
async function
|
|
138
|
+
async function I(e, t, r) {
|
|
115
139
|
if (e.length === 0) return;
|
|
116
|
-
const
|
|
117
|
-
if (r.skipped += e.length -
|
|
118
|
-
t.batchUpdateQueueItems(
|
|
119
|
-
|
|
120
|
-
|
|
140
|
+
const a = e.filter((n) => d.has(n.actionId));
|
|
141
|
+
if (r.skipped += e.length - a.length, a.length > 0) {
|
|
142
|
+
t.batchUpdateQueueItems(a.map((n) => ({
|
|
143
|
+
id: n.id,
|
|
144
|
+
update: { status: "replaying" }
|
|
145
|
+
})));
|
|
146
|
+
for (const n of a) u().update(n.id, { status: "replaying" });
|
|
121
147
|
}
|
|
122
|
-
const
|
|
123
|
-
for (const n of
|
|
124
|
-
const
|
|
125
|
-
|
|
148
|
+
const i = await Promise.allSettled(a.map((n) => b(n, t)));
|
|
149
|
+
for (const n of i) {
|
|
150
|
+
const o = n.status === "fulfilled" ? n.value : "failed";
|
|
151
|
+
o === "skipped" ? r.skipped++ : o === "conflicted" ? r.conflicted++ : (r.attempted++, r[o]++);
|
|
126
152
|
}
|
|
127
153
|
}
|
|
128
|
-
async function
|
|
129
|
-
const t = await
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
154
|
+
async function Q(e) {
|
|
155
|
+
const t = await u().getPending(), r = Date.now(), a = t.filter((n) => n.retryCount < n.maxRetries && (!n.nextRetryAt || n.nextRetryAt <= r)), i = {
|
|
156
|
+
attempted: 0,
|
|
157
|
+
succeeded: 0,
|
|
158
|
+
failed: 0,
|
|
159
|
+
retrying: 0,
|
|
160
|
+
skipped: 0,
|
|
161
|
+
conflicted: 0
|
|
162
|
+
};
|
|
163
|
+
for (const n of [
|
|
164
|
+
"high",
|
|
165
|
+
"normal",
|
|
166
|
+
"low"
|
|
167
|
+
]) await I(a.filter((o) => (o.priority ?? "normal") === n), e, i);
|
|
168
|
+
return i;
|
|
137
169
|
}
|
|
138
|
-
async function
|
|
139
|
-
await
|
|
170
|
+
async function A() {
|
|
171
|
+
await u().clear(), s.getState().hydrateQueue([]);
|
|
140
172
|
}
|
|
141
173
|
export {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
174
|
+
C as action,
|
|
175
|
+
A as clearQueue,
|
|
176
|
+
_ as replayQueue
|
|
145
177
|
};
|
|
146
|
-
|
|
178
|
+
|
|
179
|
+
//# sourceMappingURL=action.js.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
class l {
|
|
1
|
+
var i = "@eidos:queue", l = class {
|
|
3
2
|
constructor(t) {
|
|
4
3
|
this.storage = t;
|
|
5
4
|
}
|
|
@@ -15,28 +14,32 @@ class l {
|
|
|
15
14
|
await this.storage.setItem(i, JSON.stringify(t));
|
|
16
15
|
}
|
|
17
16
|
async add(t) {
|
|
18
|
-
const
|
|
19
|
-
|
|
17
|
+
const a = await this.readAll();
|
|
18
|
+
a.push(t), await this.writeAll(a);
|
|
20
19
|
}
|
|
21
20
|
async getAll() {
|
|
22
21
|
return this.readAll();
|
|
23
22
|
}
|
|
24
23
|
async getPending() {
|
|
25
|
-
return (await this.readAll()).filter((
|
|
24
|
+
return (await this.readAll()).filter((t) => t.status === "pending" || t.status === "failed");
|
|
26
25
|
}
|
|
27
|
-
async update(t,
|
|
28
|
-
const
|
|
29
|
-
s !== -1 && (
|
|
26
|
+
async update(t, a) {
|
|
27
|
+
const e = await this.readAll(), s = e.findIndex((r) => r.id === t);
|
|
28
|
+
s !== -1 && (e[s] = {
|
|
29
|
+
...e[s],
|
|
30
|
+
...a
|
|
31
|
+
}), await this.writeAll(e);
|
|
30
32
|
}
|
|
31
33
|
async remove(t) {
|
|
32
|
-
const
|
|
33
|
-
await this.writeAll(
|
|
34
|
+
const a = await this.readAll();
|
|
35
|
+
await this.writeAll(a.filter((e) => e.id !== t));
|
|
34
36
|
}
|
|
35
37
|
async clear() {
|
|
36
38
|
await this.storage.removeItem(i);
|
|
37
39
|
}
|
|
38
|
-
}
|
|
40
|
+
};
|
|
39
41
|
export {
|
|
40
42
|
l as AsyncStorageQueueStorage
|
|
41
43
|
};
|
|
42
|
-
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=async-storage-adapter.js.map
|