@sweidos/eidos 1.0.34 → 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 +96 -89
- 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/dist/query.js
CHANGED
|
@@ -1,48 +1,128 @@
|
|
|
1
|
-
import { useQuery, useQueryClient
|
|
1
|
+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
2
2
|
import { setQueryInvalidator } from "@sweidos/eidos";
|
|
3
|
-
|
|
3
|
+
//#region src/query.ts
|
|
4
|
+
/**
|
|
5
|
+
* @sweidos/eidos/query
|
|
6
|
+
*
|
|
7
|
+
* TanStack Query (React Query) integration for Eidos.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* // Register once (e.g. alongside new QueryClient())
|
|
12
|
+
* import { withEidosQueryClient } from '@sweidos/eidos/query'
|
|
13
|
+
* withEidosQueryClient(queryClient)
|
|
14
|
+
*
|
|
15
|
+
* // In components
|
|
16
|
+
* import { useEidosQuery, useEidosMutation } from '@sweidos/eidos/query'
|
|
17
|
+
*
|
|
18
|
+
* const { data, isPending } = useEidosQuery(products)
|
|
19
|
+
*
|
|
20
|
+
* const mutation = useEidosMutation(createOrder, {
|
|
21
|
+
* invalidates: [products],
|
|
22
|
+
* })
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
var _globalClient = null;
|
|
26
|
+
/**
|
|
27
|
+
* Register a QueryClient with Eidos.
|
|
28
|
+
*
|
|
29
|
+
* Once called, `handle.invalidate()` will also call
|
|
30
|
+
* `queryClient.invalidateQueries({ queryKey: ['eidos', url] })`, keeping
|
|
31
|
+
* TanStack Query's cache in sync with Eidos's Cache Storage.
|
|
32
|
+
*
|
|
33
|
+
* Call this once, before rendering — e.g. alongside `new QueryClient()`.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* const queryClient = new QueryClient()
|
|
38
|
+
* withEidosQueryClient(queryClient)
|
|
39
|
+
*
|
|
40
|
+
* // Wrap your app as usual
|
|
41
|
+
* <QueryClientProvider client={queryClient}>
|
|
42
|
+
* <App />
|
|
43
|
+
* </QueryClientProvider>
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
4
46
|
function withEidosQueryClient(client) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
47
|
+
_globalClient = client;
|
|
48
|
+
setQueryInvalidator((queryKey) => {
|
|
49
|
+
client.invalidateQueries({ queryKey });
|
|
50
|
+
});
|
|
9
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Wraps `useQuery` with Eidos-smart defaults.
|
|
54
|
+
*
|
|
55
|
+
* Key differences from plain `useQuery`:
|
|
56
|
+
* - `networkMode: 'always'` — Eidos owns offline logic; queries run even when
|
|
57
|
+
* `navigator.onLine` is false (the SW cache or IndexedDB serves the data).
|
|
58
|
+
* - `retry: false` — Eidos handles retries at the SW / replay layer; TQ
|
|
59
|
+
* retrying on top would double-fire and fight Eidos's backoff.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* const products = resource('/api/products', { offline: true })
|
|
64
|
+
*
|
|
65
|
+
* // Automatically typed as UseQueryResult<Product[]>
|
|
66
|
+
* const { data, isPending, isError } = useEidosQuery<Product[]>(products)
|
|
67
|
+
*
|
|
68
|
+
* // Override any TQ option
|
|
69
|
+
* const { data } = useEidosQuery(products, { staleTime: 30_000 })
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
10
72
|
function useEidosQuery(handle, options) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
73
|
+
return useQuery({
|
|
74
|
+
networkMode: "always",
|
|
75
|
+
retry: false,
|
|
76
|
+
...options,
|
|
77
|
+
...handle.query()
|
|
78
|
+
});
|
|
17
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Wraps `useMutation` for a single-argument Eidos action handle.
|
|
82
|
+
*
|
|
83
|
+
* Key differences from plain `useMutation`:
|
|
84
|
+
* - `networkMode: 'always'` — action executes (or queues) even when offline.
|
|
85
|
+
* - `invalidates` — shorthand to clear resource caches on success. Triggers
|
|
86
|
+
* both Eidos Cache Storage and TanStack Query invalidation (requires
|
|
87
|
+
* `withEidosQueryClient` for the TQ half).
|
|
88
|
+
* - Return type is `TData | QueuedResult`. Narrow with `'queued' in data` to
|
|
89
|
+
* detect the offline-queued case.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```ts
|
|
93
|
+
* const mutation = useEidosMutation(createOrder, {
|
|
94
|
+
* invalidates: [products],
|
|
95
|
+
* onSuccess(data) {
|
|
96
|
+
* if ('queued' in data) toast('Saved offline — will sync when back online')
|
|
97
|
+
* else toast(`Order #${data.id} created!`)
|
|
98
|
+
* },
|
|
99
|
+
* })
|
|
100
|
+
*
|
|
101
|
+
* // Trigger
|
|
102
|
+
* mutation.mutate({ productId: 1, qty: 2 })
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
18
105
|
function useEidosMutation(handle, options) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
if (onSuccess) await onSuccess(...args);
|
|
41
|
-
}
|
|
42
|
-
});
|
|
106
|
+
let contextClient = null;
|
|
107
|
+
try {
|
|
108
|
+
contextClient = useQueryClient();
|
|
109
|
+
} catch {}
|
|
110
|
+
const { invalidates, onSuccess, ...rest } = options ?? {};
|
|
111
|
+
return useMutation({
|
|
112
|
+
networkMode: "always",
|
|
113
|
+
...rest,
|
|
114
|
+
mutationFn: (arg) => handle(arg),
|
|
115
|
+
onSuccess: async (...args) => {
|
|
116
|
+
const [data] = args;
|
|
117
|
+
if (invalidates?.length) {
|
|
118
|
+
await Promise.all(invalidates.map((h) => h.invalidate()));
|
|
119
|
+
if (!_globalClient && contextClient) invalidates.forEach((h) => {
|
|
120
|
+
contextClient.invalidateQueries({ queryKey: h.query().queryKey });
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (onSuccess) await onSuccess(...args);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
43
126
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
useEidosQuery,
|
|
47
|
-
withEidosQueryClient
|
|
48
|
-
};
|
|
127
|
+
//#endregion
|
|
128
|
+
export { useEidosMutation, useEidosQuery, withEidosQueryClient };
|
package/dist/queue-storage.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
var e = null;
|
|
2
2
|
function u(t) {
|
|
3
3
|
e = t;
|
|
4
4
|
}
|
|
5
|
-
function
|
|
5
|
+
function r() {
|
|
6
6
|
return e;
|
|
7
7
|
}
|
|
8
8
|
export {
|
|
9
|
-
|
|
9
|
+
r as _getQueueStorage,
|
|
10
10
|
u as setQueueStorage
|
|
11
11
|
};
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=queue-storage.js.map
|
package/dist/react/Devtools.d.ts
CHANGED
|
@@ -4,4 +4,4 @@ export interface EidosDevtoolsProps {
|
|
|
4
4
|
/** Start expanded. Default: false. */
|
|
5
5
|
defaultOpen?: boolean;
|
|
6
6
|
}
|
|
7
|
-
export declare function EidosDevtools({ position, defaultOpen }: EidosDevtoolsProps): import("react").JSX.Element;
|
|
7
|
+
export declare function EidosDevtools({ position, defaultOpen, }: EidosDevtoolsProps): import("react").JSX.Element;
|
package/dist/react/Provider.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useEffect as
|
|
3
|
-
import {
|
|
1
|
+
import { initEidos as t } from "../runtime.js";
|
|
2
|
+
import { useEffect as m } from "react";
|
|
3
|
+
import { Fragment as e, jsx as f } from "react/jsx-runtime";
|
|
4
4
|
function d({ children: r, swPath: o, autoReplay: i }) {
|
|
5
|
-
return
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
return m(() => {
|
|
6
|
+
t({
|
|
7
|
+
swPath: o,
|
|
8
|
+
autoReplay: i
|
|
9
|
+
});
|
|
10
|
+
}, []), /* @__PURE__ */ f(e, { children: r });
|
|
8
11
|
}
|
|
9
12
|
export {
|
|
10
13
|
d as EidosProvider
|
|
11
14
|
};
|
|
12
|
-
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=Provider.js.map
|
package/dist/react/hooks.js
CHANGED
|
@@ -1,51 +1,61 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
function
|
|
1
|
+
import { useEidosStore as a } from "../store.js";
|
|
2
|
+
import { useEffect as d, useRef as l, useSyncExternalStore as E } from "react";
|
|
3
|
+
function u(e) {
|
|
4
4
|
const t = e ?? ((n) => n);
|
|
5
|
-
return
|
|
5
|
+
return E(a.subscribe, () => t(a.getState()));
|
|
6
|
+
}
|
|
7
|
+
function S() {
|
|
8
|
+
return u();
|
|
6
9
|
}
|
|
7
10
|
function q() {
|
|
8
|
-
return
|
|
11
|
+
return u((e) => e.resources);
|
|
9
12
|
}
|
|
10
|
-
function R() {
|
|
11
|
-
return
|
|
13
|
+
function R(e) {
|
|
14
|
+
return u((t) => t.resources[e]);
|
|
12
15
|
}
|
|
13
|
-
function m(
|
|
14
|
-
return
|
|
16
|
+
function m() {
|
|
17
|
+
return u((e) => e.queue);
|
|
15
18
|
}
|
|
16
|
-
function w() {
|
|
17
|
-
return
|
|
19
|
+
function w(e) {
|
|
20
|
+
return u((t) => t.queue.find((n) => n.id === e));
|
|
18
21
|
}
|
|
19
|
-
function y(
|
|
20
|
-
return
|
|
22
|
+
function y() {
|
|
23
|
+
return {
|
|
24
|
+
isOnline: u((e) => e.isOnline),
|
|
25
|
+
swStatus: u((e) => e.swStatus),
|
|
26
|
+
swError: u((e) => e.swError)
|
|
27
|
+
};
|
|
21
28
|
}
|
|
22
29
|
function $() {
|
|
23
|
-
const e
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
function
|
|
36
|
-
const t =
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
const [e, t, n, r] = u((s) => {
|
|
31
|
+
let o = 0, f = 0, c = 0;
|
|
32
|
+
for (const i of s.queue) i.status === "pending" ? o++ : i.status === "failed" ? f++ : i.status === "replaying" && c++;
|
|
33
|
+
return `${o},${f},${c},${s.queue.length}`;
|
|
34
|
+
}).split(",");
|
|
35
|
+
return {
|
|
36
|
+
pending: +e,
|
|
37
|
+
failed: +t,
|
|
38
|
+
replaying: +n,
|
|
39
|
+
total: +r
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function O(e) {
|
|
43
|
+
const t = u((s) => s.queue.length), n = l(0), r = l(e);
|
|
44
|
+
d(() => {
|
|
45
|
+
r.current = e;
|
|
46
|
+
}), d(() => {
|
|
47
|
+
n.current > 0 && t === 0 && r.current(), n.current = t;
|
|
39
48
|
}, [t]);
|
|
40
49
|
}
|
|
41
50
|
export {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
S as useEidos,
|
|
52
|
+
w as useEidosAction,
|
|
53
|
+
O as useEidosOnDrain,
|
|
54
|
+
m as useEidosQueue,
|
|
55
|
+
$ as useEidosQueueStats,
|
|
56
|
+
R as useEidosResource,
|
|
57
|
+
q as useEidosResources,
|
|
58
|
+
y as useEidosStatus
|
|
50
59
|
};
|
|
51
|
-
|
|
60
|
+
|
|
61
|
+
//# sourceMappingURL=hooks.js.map
|
package/dist/react-native.js
CHANGED
|
@@ -1,59 +1,53 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import { AsyncStorageQueueStorage, AsyncStorageQueueStorage as AsyncStorageQueueStorage$1, setQueueStorage, setQueueStorage as setQueueStorage$1, subscribeReplayOnReconnect, useEidosStore } from "@sweidos/eidos";
|
|
3
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/react/ProviderRN.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Connectivity bridge for React Native.
|
|
7
|
+
* Syncs the network state into the Eidos store so `initEidosRN`'s autoReplay
|
|
8
|
+
* subscription fires when the device comes back online.
|
|
9
|
+
*
|
|
10
|
+
* Render this near the root of your app, after calling `initEidosRN()`.
|
|
11
|
+
*/
|
|
5
12
|
function EidosProviderRN({ children, isConnected }) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
const prevRef = useRef(void 0);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const online = isConnected !== false;
|
|
16
|
+
if (online !== prevRef.current) {
|
|
17
|
+
prevRef.current = online;
|
|
18
|
+
useEidosStore.getState().setOnline(online);
|
|
19
|
+
}
|
|
20
|
+
}, [isConnected]);
|
|
21
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
15
22
|
}
|
|
16
|
-
|
|
17
|
-
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/runtime-rn.ts
|
|
25
|
+
var _initialized = false;
|
|
26
|
+
var _unsubscribe = null;
|
|
27
|
+
/**
|
|
28
|
+
* Initialize Eidos for React Native.
|
|
29
|
+
* Call this once at app startup (outside any component) before rendering.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* import AsyncStorage from '@react-native-async-storage/async-storage'
|
|
33
|
+
* await initEidosRN({ storage: AsyncStorage })
|
|
34
|
+
*/
|
|
18
35
|
async function initEidosRN(config) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
} catch {
|
|
30
|
-
}
|
|
31
|
-
if (autoReplay) {
|
|
32
|
-
let prevIsOnline = useEidosStore.getState().isOnline;
|
|
33
|
-
_unsubscribe = useEidosStore.subscribe(() => {
|
|
34
|
-
const { isOnline } = useEidosStore.getState();
|
|
35
|
-
const justCameOnline = isOnline && !prevIsOnline;
|
|
36
|
-
prevIsOnline = isOnline;
|
|
37
|
-
if (justCameOnline) {
|
|
38
|
-
setTimeout(replayQueue, 600);
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
const store = useEidosStore.getState();
|
|
42
|
-
const hasPending = store.queue.some((q) => q.status === "pending");
|
|
43
|
-
if (store.isOnline && hasPending) {
|
|
44
|
-
setTimeout(replayQueue, 1200);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
36
|
+
if (_initialized) return;
|
|
37
|
+
_initialized = true;
|
|
38
|
+
const { storage, autoReplay = true } = config;
|
|
39
|
+
const queueStorage = new AsyncStorageQueueStorage$1(storage);
|
|
40
|
+
setQueueStorage$1(queueStorage);
|
|
41
|
+
try {
|
|
42
|
+
const persisted = await queueStorage.getAll();
|
|
43
|
+
if (persisted.length > 0) useEidosStore.getState().hydrateQueue(persisted);
|
|
44
|
+
} catch {}
|
|
45
|
+
if (autoReplay) _unsubscribe = subscribeReplayOnReconnect();
|
|
47
46
|
}
|
|
48
47
|
function _resetEidosRN() {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
_unsubscribe?.();
|
|
49
|
+
_unsubscribe = null;
|
|
50
|
+
_initialized = false;
|
|
52
51
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
EidosProviderRN,
|
|
56
|
-
_resetEidosRN,
|
|
57
|
-
initEidosRN,
|
|
58
|
-
setQueueStorage2 as setQueueStorage
|
|
59
|
-
};
|
|
52
|
+
//#endregion
|
|
53
|
+
export { AsyncStorageQueueStorage, EidosProviderRN, _resetEidosRN, initEidosRN, setQueueStorage };
|
package/dist/replay.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useEidosStore as t } from "./store.js";
|
|
2
|
+
import { replayQueue as i } from "./action.js";
|
|
3
|
+
function l() {
|
|
4
|
+
let n = t.getState().isOnline;
|
|
5
|
+
const o = t.subscribe(() => {
|
|
6
|
+
const { isOnline: e } = t.getState(), r = e && !n;
|
|
7
|
+
n = e, r && setTimeout(i, 600);
|
|
8
|
+
}), s = t.getState(), u = s.queue.some((e) => e.status === "pending");
|
|
9
|
+
return s.isOnline && u && setTimeout(i, 1200), o;
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
l as subscribeReplayOnReconnect
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=replay.js.map
|