@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.
Files changed (48) hide show
  1. package/README.md +96 -89
  2. package/dist/action.js +119 -86
  3. package/dist/async-storage-adapter.js +15 -12
  4. package/dist/devtools.js +953 -555
  5. package/dist/eidos.cjs +15 -0
  6. package/dist/idb.js +59 -56
  7. package/dist/index.d.ts +37 -15
  8. package/dist/index.js +42 -41
  9. package/dist/nextjs.js +1 -10
  10. package/dist/query.cjs +131 -0
  11. package/dist/query.js +121 -41
  12. package/dist/queue-storage.js +5 -4
  13. package/dist/react/Devtools.d.ts +1 -1
  14. package/dist/react/Provider.js +11 -7
  15. package/dist/react/hooks.js +48 -38
  16. package/dist/react-native.js +47 -53
  17. package/dist/replay.js +15 -0
  18. package/dist/resource.js +77 -79
  19. package/dist/runtime.js +22 -28
  20. package/dist/store-slices.js +43 -0
  21. package/dist/store.js +32 -49
  22. package/dist/stores.js +25 -22
  23. package/dist/sveltekit.js +22 -6
  24. package/dist/sw-bridge.js +48 -46
  25. package/dist/testing.cjs +165 -0
  26. package/dist/testing.js +140 -70
  27. package/dist/version.js +4 -3
  28. package/dist/vite.cjs +48 -0
  29. package/dist/vite.js +45 -29
  30. package/package.json +48 -27
  31. package/dist/action.js.map +0 -1
  32. package/dist/async-storage-adapter.js.map +0 -1
  33. package/dist/eidos.cjs.js +0 -14
  34. package/dist/eidos.cjs.js.map +0 -1
  35. package/dist/idb.js.map +0 -1
  36. package/dist/index.js.map +0 -1
  37. package/dist/query.cjs.js +0 -48
  38. package/dist/queue-storage.js.map +0 -1
  39. package/dist/react/Provider.js.map +0 -1
  40. package/dist/react/hooks.js.map +0 -1
  41. package/dist/resource.js.map +0 -1
  42. package/dist/runtime.js.map +0 -1
  43. package/dist/store.js.map +0 -1
  44. package/dist/stores.js.map +0 -1
  45. package/dist/sw-bridge.js.map +0 -1
  46. package/dist/testing.cjs.js +0 -86
  47. package/dist/version.js.map +0 -1
  48. package/dist/vite.cjs.js +0 -31
package/dist/query.js CHANGED
@@ -1,48 +1,128 @@
1
- import { useQuery, useQueryClient, useMutation } from "@tanstack/react-query";
1
+ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
2
2
  import { setQueryInvalidator } from "@sweidos/eidos";
3
- let _globalClient = null;
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
- _globalClient = client;
6
- setQueryInvalidator((queryKey) => {
7
- client.invalidateQueries({ queryKey });
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
- return useQuery({
12
- networkMode: "always",
13
- retry: false,
14
- ...options,
15
- ...handle.query()
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
- let contextClient = null;
20
- try {
21
- contextClient = useQueryClient();
22
- } catch {
23
- }
24
- const { invalidates, onSuccess, ...rest } = options ?? {};
25
- return useMutation({
26
- networkMode: "always",
27
- ...rest,
28
- mutationFn: (arg) => handle(arg),
29
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
- onSuccess: async (...args) => {
31
- const [data] = args;
32
- if (invalidates == null ? void 0 : invalidates.length) {
33
- await Promise.all(invalidates.map((h) => h.invalidate()));
34
- if (!_globalClient && contextClient) {
35
- invalidates.forEach((h) => {
36
- contextClient.invalidateQueries({ queryKey: h.query().queryKey });
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
- export {
45
- useEidosMutation,
46
- useEidosQuery,
47
- withEidosQueryClient
48
- };
127
+ //#endregion
128
+ export { useEidosMutation, useEidosQuery, withEidosQueryClient };
@@ -1,12 +1,13 @@
1
- let e = null;
1
+ var e = null;
2
2
  function u(t) {
3
3
  e = t;
4
4
  }
5
- function n() {
5
+ function r() {
6
6
  return e;
7
7
  }
8
8
  export {
9
- n as _getQueueStorage,
9
+ r as _getQueueStorage,
10
10
  u as setQueueStorage
11
11
  };
12
- //# sourceMappingURL=queue-storage.js.map
12
+
13
+ //# sourceMappingURL=queue-storage.js.map
@@ -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;
@@ -1,12 +1,16 @@
1
- import { jsx as t, Fragment as m } from "react/jsx-runtime";
2
- import { useEffect as e } from "react";
3
- import { initEidos as f } from "../runtime.js";
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 e(() => {
6
- f({ swPath: o, autoReplay: i });
7
- }, []), /* @__PURE__ */ t(m, { children: r });
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
- //# sourceMappingURL=Provider.js.map
15
+
16
+ //# sourceMappingURL=Provider.js.map
@@ -1,51 +1,61 @@
1
- import { useRef as a, useEffect as E, useSyncExternalStore as p } from "react";
2
- import { useEidosStore as l } from "../store.js";
3
- function s(e) {
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 p(l.subscribe, () => t(l.getState()));
5
+ return E(a.subscribe, () => t(a.getState()));
6
+ }
7
+ function S() {
8
+ return u();
6
9
  }
7
10
  function q() {
8
- return s();
11
+ return u((e) => e.resources);
9
12
  }
10
- function R() {
11
- return s((e) => e.resources);
13
+ function R(e) {
14
+ return u((t) => t.resources[e]);
12
15
  }
13
- function m(e) {
14
- return s((t) => t.resources[e]);
16
+ function m() {
17
+ return u((e) => e.queue);
15
18
  }
16
- function w() {
17
- return s((e) => e.queue);
19
+ function w(e) {
20
+ return u((t) => t.queue.find((n) => n.id === e));
18
21
  }
19
- function y(e) {
20
- return s((t) => t.queue.find((n) => n.id === e));
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 = s((u) => u.isOnline), t = s((u) => u.swStatus), n = s((u) => u.swError);
24
- return { isOnline: e, swStatus: t, swError: n };
25
- }
26
- function O() {
27
- const e = s((i) => {
28
- let c = 0, f = 0, d = 0;
29
- for (const o of i.queue)
30
- o.status === "pending" ? c++ : o.status === "failed" ? f++ : o.status === "replaying" && d++;
31
- return `${c},${f},${d},${i.queue.length}`;
32
- }), [t, n, u, r] = e.split(",");
33
- return { pending: +t, failed: +n, replaying: +u, total: +r };
34
- }
35
- function b(e) {
36
- const t = s((r) => r.queue.length), n = a(0), u = a(e);
37
- u.current = e, E(() => {
38
- n.current > 0 && t === 0 && u.current(), n.current = t;
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
- q as useEidos,
43
- y as useEidosAction,
44
- b as useEidosOnDrain,
45
- w as useEidosQueue,
46
- O as useEidosQueueStats,
47
- m as useEidosResource,
48
- R as useEidosResources,
49
- $ as useEidosStatus
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
- //# sourceMappingURL=hooks.js.map
60
+
61
+ //# sourceMappingURL=hooks.js.map
@@ -1,59 +1,53 @@
1
- import { jsx, Fragment } from "react/jsx-runtime";
2
- import { useRef, useEffect } from "react";
3
- import { useEidosStore, AsyncStorageQueueStorage, setQueueStorage, replayQueue } from "@sweidos/eidos";
4
- import { AsyncStorageQueueStorage as AsyncStorageQueueStorage2, setQueueStorage as setQueueStorage2 } from "@sweidos/eidos";
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
- const prevRef = useRef(void 0);
7
- useEffect(() => {
8
- const online = isConnected !== false;
9
- if (online !== prevRef.current) {
10
- prevRef.current = online;
11
- useEidosStore.getState().setOnline(online);
12
- }
13
- }, [isConnected]);
14
- return /* @__PURE__ */ jsx(Fragment, { children });
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
- let _initialized = false;
17
- let _unsubscribe = null;
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
- if (_initialized) return;
20
- _initialized = true;
21
- const { storage, autoReplay = true } = config;
22
- const queueStorage = new AsyncStorageQueueStorage(storage);
23
- setQueueStorage(queueStorage);
24
- try {
25
- const persisted = await queueStorage.getAll();
26
- if (persisted.length > 0) {
27
- useEidosStore.getState().hydrateQueue(persisted);
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
- _unsubscribe == null ? void 0 : _unsubscribe();
50
- _unsubscribe = null;
51
- _initialized = false;
48
+ _unsubscribe?.();
49
+ _unsubscribe = null;
50
+ _initialized = false;
52
51
  }
53
- export {
54
- AsyncStorageQueueStorage2 as AsyncStorageQueueStorage,
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