@sweidos/eidos 1.0.34 → 1.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.
Files changed (53) hide show
  1. package/README.md +171 -89
  2. package/dist/action.js +197 -91
  3. package/dist/async-storage-adapter.js +15 -12
  4. package/dist/cli.js +102 -0
  5. package/dist/devtools.js +1009 -551
  6. package/dist/eidos-sw.js +280 -188
  7. package/dist/eidos.cjs +15 -0
  8. package/dist/idb.js +59 -56
  9. package/dist/index.d.ts +135 -18
  10. package/dist/index.js +46 -42
  11. package/dist/nextjs.js +1 -10
  12. package/dist/push.cjs +120 -0
  13. package/dist/push.d.ts +28 -0
  14. package/dist/push.js +113 -0
  15. package/dist/query.cjs +131 -0
  16. package/dist/query.js +121 -41
  17. package/dist/queue-storage.js +5 -4
  18. package/dist/react/Devtools.d.ts +1 -1
  19. package/dist/react/Provider.js +11 -7
  20. package/dist/react/hooks.js +48 -38
  21. package/dist/react-native.js +47 -53
  22. package/dist/replay.js +15 -0
  23. package/dist/resource.js +77 -79
  24. package/dist/runtime.js +39 -28
  25. package/dist/store-slices.js +43 -0
  26. package/dist/store.js +32 -49
  27. package/dist/stores.js +25 -22
  28. package/dist/sveltekit.js +22 -6
  29. package/dist/sw-bridge.js +64 -49
  30. package/dist/testing.cjs +165 -0
  31. package/dist/testing.js +140 -70
  32. package/dist/version.js +4 -3
  33. package/dist/vite.cjs +48 -0
  34. package/dist/vite.js +45 -29
  35. package/package.json +57 -28
  36. package/dist/action.js.map +0 -1
  37. package/dist/async-storage-adapter.js.map +0 -1
  38. package/dist/eidos.cjs.js +0 -14
  39. package/dist/eidos.cjs.js.map +0 -1
  40. package/dist/idb.js.map +0 -1
  41. package/dist/index.js.map +0 -1
  42. package/dist/query.cjs.js +0 -48
  43. package/dist/queue-storage.js.map +0 -1
  44. package/dist/react/Provider.js.map +0 -1
  45. package/dist/react/hooks.js.map +0 -1
  46. package/dist/resource.js.map +0 -1
  47. package/dist/runtime.js.map +0 -1
  48. package/dist/store.js.map +0 -1
  49. package/dist/stores.js.map +0 -1
  50. package/dist/sw-bridge.js.map +0 -1
  51. package/dist/testing.cjs.js +0 -86
  52. package/dist/version.js.map +0 -1
  53. package/dist/vite.cjs.js +0 -31
package/dist/push.js ADDED
@@ -0,0 +1,113 @@
1
+ import { getSwRegistration, registerPushCallbacks, sendToWorker } from "@sweidos/eidos";
2
+ //#region src/push.ts
3
+ /**
4
+ * @sweidos/eidos/push
5
+ *
6
+ * Web Push integration. Framework-agnostic: register click/expiry handlers
7
+ * once at app init (any tab), and trigger subscription from a user gesture.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { registerPushHandlers, subscribeToPush } from '@sweidos/eidos/push'
12
+ *
13
+ * // App init — every tab, no permission prompt
14
+ * registerPushHandlers({
15
+ * onNotificationClick: (data) => router.push(data.url),
16
+ * onSubscriptionExpired: (sub) => fetch('/api/push-subscribe', { method: 'POST', body: JSON.stringify(sub) }),
17
+ * })
18
+ *
19
+ * // User gesture (button click)
20
+ * const result = await subscribeToPush({
21
+ * vapidPublicKey: import.meta.env.VITE_EIDOS_VAPID_PUBLIC_KEY,
22
+ * onSubscribe: (sub) => fetch('/api/push-subscribe', { method: 'POST', body: JSON.stringify(sub) }),
23
+ * })
24
+ * ```
25
+ */
26
+ /** Why push is unavailable on this device, or null if it's supported. */
27
+ function getPushUnsupportedReason() {
28
+ if (typeof window === "undefined") return "no-push-api";
29
+ if (/iPhone|iPad|iPod/.test(navigator.userAgent) && !navigator.standalone) return "ios-not-installed";
30
+ if (!("serviceWorker" in navigator) || !("PushManager" in window) || !("Notification" in window)) return "no-push-api";
31
+ return null;
32
+ }
33
+ function isPushSupported() {
34
+ return getPushUnsupportedReason() === null;
35
+ }
36
+ function getPushPermissionState() {
37
+ if (!isPushSupported()) return "unsupported";
38
+ return Notification.permission;
39
+ }
40
+ function registerPushHandlers(handlers) {
41
+ registerPushCallbacks(handlers);
42
+ }
43
+ async function subscribeToPush(config) {
44
+ if (!isPushSupported()) return { status: "unsupported" };
45
+ let permission;
46
+ try {
47
+ permission = await Notification.requestPermission();
48
+ } catch (error) {
49
+ return {
50
+ status: "error",
51
+ error
52
+ };
53
+ }
54
+ if (permission !== "granted") return { status: "denied" };
55
+ const registration = getSwRegistration();
56
+ if (!registration) return { status: "sw-not-ready" };
57
+ try {
58
+ const applicationServerKey = urlBase64ToUint8Array(config.vapidPublicKey);
59
+ let subscription = await registration.pushManager.getSubscription();
60
+ if (subscription && !subscriptionKeyMatches(subscription, config.vapidPublicKey)) {
61
+ await subscription.unsubscribe();
62
+ subscription = null;
63
+ }
64
+ if (!subscription) subscription = await registration.pushManager.subscribe({
65
+ userVisibleOnly: true,
66
+ applicationServerKey
67
+ });
68
+ sendToWorker({
69
+ type: "EIDOS_CACHE_VAPID_KEY",
70
+ key: config.vapidPublicKey
71
+ });
72
+ const json = subscription.toJSON();
73
+ config.onSubscribe?.(json);
74
+ return {
75
+ status: "subscribed",
76
+ subscription: json
77
+ };
78
+ } catch (error) {
79
+ return {
80
+ status: "error",
81
+ error
82
+ };
83
+ }
84
+ }
85
+ async function unsubscribeFromPush() {
86
+ const registration = getSwRegistration();
87
+ if (!registration) return;
88
+ await (await registration.pushManager.getSubscription())?.unsubscribe();
89
+ }
90
+ async function getCurrentPushSubscription() {
91
+ const registration = getSwRegistration();
92
+ if (!registration) return null;
93
+ const subscription = await registration.pushManager.getSubscription();
94
+ return subscription ? subscription.toJSON() : null;
95
+ }
96
+ function urlBase64ToUint8Array(base64Url) {
97
+ const base64 = (base64Url + "=".repeat((4 - base64Url.length % 4) % 4)).replace(/-/g, "+").replace(/_/g, "/");
98
+ const raw = atob(base64);
99
+ return Uint8Array.from(raw, (c) => c.charCodeAt(0));
100
+ }
101
+ function uint8ArrayToUrlBase64(bytes) {
102
+ let binary = "";
103
+ for (const byte of bytes) binary += String.fromCharCode(byte);
104
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
105
+ }
106
+ /** Compares an existing subscription's key against the configured VAPID public key. */
107
+ function subscriptionKeyMatches(subscription, vapidPublicKey) {
108
+ const key = subscription.options.applicationServerKey;
109
+ if (!key) return false;
110
+ return uint8ArrayToUrlBase64(new Uint8Array(key)) === uint8ArrayToUrlBase64(urlBase64ToUint8Array(vapidPublicKey));
111
+ }
112
+ //#endregion
113
+ export { getCurrentPushSubscription, getPushPermissionState, getPushUnsupportedReason, isPushSupported, registerPushHandlers, subscribeToPush, unsubscribeFromPush };
package/dist/query.cjs ADDED
@@ -0,0 +1,131 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _tanstack_react_query = require("@tanstack/react-query");
3
+ let _sweidos_eidos = require("@sweidos/eidos");
4
+ //#region src/query.ts
5
+ /**
6
+ * @sweidos/eidos/query
7
+ *
8
+ * TanStack Query (React Query) integration for Eidos.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * // Register once (e.g. alongside new QueryClient())
13
+ * import { withEidosQueryClient } from '@sweidos/eidos/query'
14
+ * withEidosQueryClient(queryClient)
15
+ *
16
+ * // In components
17
+ * import { useEidosQuery, useEidosMutation } from '@sweidos/eidos/query'
18
+ *
19
+ * const { data, isPending } = useEidosQuery(products)
20
+ *
21
+ * const mutation = useEidosMutation(createOrder, {
22
+ * invalidates: [products],
23
+ * })
24
+ * ```
25
+ */
26
+ var _globalClient = null;
27
+ /**
28
+ * Register a QueryClient with Eidos.
29
+ *
30
+ * Once called, `handle.invalidate()` will also call
31
+ * `queryClient.invalidateQueries({ queryKey: ['eidos', url] })`, keeping
32
+ * TanStack Query's cache in sync with Eidos's Cache Storage.
33
+ *
34
+ * Call this once, before rendering — e.g. alongside `new QueryClient()`.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * const queryClient = new QueryClient()
39
+ * withEidosQueryClient(queryClient)
40
+ *
41
+ * // Wrap your app as usual
42
+ * <QueryClientProvider client={queryClient}>
43
+ * <App />
44
+ * </QueryClientProvider>
45
+ * ```
46
+ */
47
+ function withEidosQueryClient(client) {
48
+ _globalClient = client;
49
+ (0, _sweidos_eidos.setQueryInvalidator)((queryKey) => {
50
+ client.invalidateQueries({ queryKey });
51
+ });
52
+ }
53
+ /**
54
+ * Wraps `useQuery` with Eidos-smart defaults.
55
+ *
56
+ * Key differences from plain `useQuery`:
57
+ * - `networkMode: 'always'` — Eidos owns offline logic; queries run even when
58
+ * `navigator.onLine` is false (the SW cache or IndexedDB serves the data).
59
+ * - `retry: false` — Eidos handles retries at the SW / replay layer; TQ
60
+ * retrying on top would double-fire and fight Eidos's backoff.
61
+ *
62
+ * @example
63
+ * ```ts
64
+ * const products = resource('/api/products', { offline: true })
65
+ *
66
+ * // Automatically typed as UseQueryResult<Product[]>
67
+ * const { data, isPending, isError } = useEidosQuery<Product[]>(products)
68
+ *
69
+ * // Override any TQ option
70
+ * const { data } = useEidosQuery(products, { staleTime: 30_000 })
71
+ * ```
72
+ */
73
+ function useEidosQuery(handle, options) {
74
+ return (0, _tanstack_react_query.useQuery)({
75
+ networkMode: "always",
76
+ retry: false,
77
+ ...options,
78
+ ...handle.query()
79
+ });
80
+ }
81
+ /**
82
+ * Wraps `useMutation` for a single-argument Eidos action handle.
83
+ *
84
+ * Key differences from plain `useMutation`:
85
+ * - `networkMode: 'always'` — action executes (or queues) even when offline.
86
+ * - `invalidates` — shorthand to clear resource caches on success. Triggers
87
+ * both Eidos Cache Storage and TanStack Query invalidation (requires
88
+ * `withEidosQueryClient` for the TQ half).
89
+ * - Return type is `TData | QueuedResult`. Narrow with `'queued' in data` to
90
+ * detect the offline-queued case.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * const mutation = useEidosMutation(createOrder, {
95
+ * invalidates: [products],
96
+ * onSuccess(data) {
97
+ * if ('queued' in data) toast('Saved offline — will sync when back online')
98
+ * else toast(`Order #${data.id} created!`)
99
+ * },
100
+ * })
101
+ *
102
+ * // Trigger
103
+ * mutation.mutate({ productId: 1, qty: 2 })
104
+ * ```
105
+ */
106
+ function useEidosMutation(handle, options) {
107
+ let contextClient = null;
108
+ try {
109
+ contextClient = (0, _tanstack_react_query.useQueryClient)();
110
+ } catch {}
111
+ const { invalidates, onSuccess, ...rest } = options ?? {};
112
+ return (0, _tanstack_react_query.useMutation)({
113
+ networkMode: "always",
114
+ ...rest,
115
+ mutationFn: (arg) => handle(arg),
116
+ onSuccess: async (...args) => {
117
+ const [data] = args;
118
+ if (invalidates?.length) {
119
+ await Promise.all(invalidates.map((h) => h.invalidate()));
120
+ if (!_globalClient && contextClient) invalidates.forEach((h) => {
121
+ contextClient.invalidateQueries({ queryKey: h.query().queryKey });
122
+ });
123
+ }
124
+ if (onSuccess) await onSuccess(...args);
125
+ }
126
+ });
127
+ }
128
+ //#endregion
129
+ exports.useEidosMutation = useEidosMutation;
130
+ exports.useEidosQuery = useEidosQuery;
131
+ exports.withEidosQueryClient = withEidosQueryClient;
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 };