@sweidos/eidos 1.0.25 → 1.0.31
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 +286 -13
- package/dist/action.js +111 -76
- package/dist/action.js.map +1 -1
- package/dist/async-storage-adapter.js +42 -0
- package/dist/async-storage-adapter.js.map +1 -0
- package/dist/devtools.d.ts +2 -0
- package/dist/devtools.js +627 -0
- package/dist/eidos.cjs.js +2 -2
- package/dist/eidos.cjs.js.map +1 -1
- package/dist/idb.js.map +1 -1
- package/dist/index.d.ts +103 -7
- package/dist/index.js +41 -34
- package/dist/index.js.map +1 -1
- package/dist/nextjs.d.ts +2 -0
- package/dist/nextjs.js +12 -0
- package/dist/queue-storage.js +12 -0
- package/dist/queue-storage.js.map +1 -0
- package/dist/react/Devtools.d.ts +7 -0
- package/dist/react/ProviderRN.d.ts +23 -0
- package/dist/react/hooks.js +17 -13
- package/dist/react/hooks.js.map +1 -1
- package/dist/react-native.d.ts +8 -0
- package/dist/react-native.js +59 -0
- package/dist/resource.js +65 -55
- package/dist/resource.js.map +1 -1
- package/dist/runtime-rn.d.ts +18 -0
- package/dist/runtime.js +12 -12
- package/dist/runtime.js.map +1 -1
- package/dist/store.js +27 -17
- package/dist/store.js.map +1 -1
- package/dist/stores.js +34 -21
- package/dist/stores.js.map +1 -1
- package/dist/sveltekit.d.ts +19 -0
- package/dist/sveltekit.js +9 -0
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +18 -2
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,13 @@ export declare interface ActionConfig {
|
|
|
13
13
|
maxRetries?: number;
|
|
14
14
|
/** Human-readable name for the action (used in devtools). */
|
|
15
15
|
name?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Replay order when multiple queued actions are pending.
|
|
18
|
+
* `'high'` items replay before `'normal'`, which replay before `'low'`.
|
|
19
|
+
* Each tier completes fully before the next tier begins.
|
|
20
|
+
* Default: `'normal'`.
|
|
21
|
+
*/
|
|
22
|
+
priority?: 'high' | 'normal' | 'low';
|
|
16
23
|
/**
|
|
17
24
|
* Called immediately before the async function executes, with the same args.
|
|
18
25
|
* Use to apply an optimistic UI update (add item, mark as pending, etc.).
|
|
@@ -26,6 +33,20 @@ export declare interface ActionConfig {
|
|
|
26
33
|
* Use to revert the optimistic update.
|
|
27
34
|
*/
|
|
28
35
|
onRollback?: (...args: any[]) => void;
|
|
36
|
+
/**
|
|
37
|
+
* Called during queue replay when the server responds with a 4xx status code
|
|
38
|
+
* (client error — conflict, gone, unprocessable, etc.).
|
|
39
|
+
*
|
|
40
|
+
* Return `'retry'` to keep the item in the queue and retry per normal backoff.
|
|
41
|
+
* Return `'skip'` to silently remove the item without triggering `onRollback`.
|
|
42
|
+
*
|
|
43
|
+
* If not provided, 4xx errors are treated identically to other errors (retried
|
|
44
|
+
* until `maxRetries` is exhausted, then `onRollback` is called).
|
|
45
|
+
*
|
|
46
|
+
* The `error` argument is whatever `fn` threw — typically a `Response` object
|
|
47
|
+
* or a custom error with a `.status` property.
|
|
48
|
+
*/
|
|
49
|
+
onConflict?: (error: unknown, args: any[]) => 'retry' | 'skip';
|
|
29
50
|
}
|
|
30
51
|
|
|
31
52
|
declare type ActionFn<TArgs extends any[], TReturn> = (...args: TArgs) => Promise<TReturn>;
|
|
@@ -46,15 +67,42 @@ export declare interface ActionQueueItem {
|
|
|
46
67
|
retryCount: number;
|
|
47
68
|
maxRetries: number;
|
|
48
69
|
status: 'pending' | 'replaying' | 'succeeded' | 'failed';
|
|
70
|
+
/** Replay priority. High items replay before normal, normal before low. Default: 'normal'. */
|
|
71
|
+
priority?: 'high' | 'normal' | 'low';
|
|
49
72
|
error?: string;
|
|
50
73
|
completedAt?: number;
|
|
51
74
|
/** Earliest timestamp at which this item should be retried (exponential backoff). */
|
|
52
75
|
nextRetryAt?: number;
|
|
53
76
|
}
|
|
54
77
|
|
|
78
|
+
/** Minimal subset of @react-native-async-storage/async-storage (or any compatible key-value store). */
|
|
79
|
+
export declare interface AsyncStorageLike {
|
|
80
|
+
getItem(key: string): Promise<string | null>;
|
|
81
|
+
setItem(key: string, value: string): Promise<void>;
|
|
82
|
+
removeItem(key: string): Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* QueueStorage implementation backed by any AsyncStorage-compatible API.
|
|
87
|
+
* Pass the AsyncStorage singleton from @react-native-async-storage/async-storage
|
|
88
|
+
* (or MMKV, SQLite, or any store that satisfies AsyncStorageLike).
|
|
89
|
+
*/
|
|
90
|
+
export declare class AsyncStorageQueueStorage implements QueueStorage {
|
|
91
|
+
private readonly storage;
|
|
92
|
+
constructor(storage: AsyncStorageLike);
|
|
93
|
+
private readAll;
|
|
94
|
+
private writeAll;
|
|
95
|
+
add(item: ActionQueueItem): Promise<void>;
|
|
96
|
+
getAll(): Promise<ActionQueueItem[]>;
|
|
97
|
+
getPending(): Promise<ActionQueueItem[]>;
|
|
98
|
+
update(id: string, patch: Partial<ActionQueueItem>): Promise<void>;
|
|
99
|
+
remove(id: string): Promise<void>;
|
|
100
|
+
clear(): Promise<void>;
|
|
101
|
+
}
|
|
102
|
+
|
|
55
103
|
export declare type CacheStrategy = 'cache-first' | 'stale-while-revalidate' | 'network-first';
|
|
56
104
|
|
|
57
|
-
/** Remove all items from the action queue (
|
|
105
|
+
/** Remove all items from the action queue (storage + in-memory store). */
|
|
58
106
|
export declare function clearQueue(): Promise<void>;
|
|
59
107
|
|
|
60
108
|
/**
|
|
@@ -67,7 +115,7 @@ export declare function clearQueue(): Promise<void>;
|
|
|
67
115
|
*/
|
|
68
116
|
export declare function eidosAction(id: string): EidosReadable<ActionQueueItem | undefined>;
|
|
69
117
|
|
|
70
|
-
declare interface EidosConfig {
|
|
118
|
+
export declare interface EidosConfig {
|
|
71
119
|
/** Path to the eidos service worker. Defaults to '/eidos-sw.js'. */
|
|
72
120
|
swPath?: string;
|
|
73
121
|
/** Automatically replay the action queue on reconnect. Default: true. */
|
|
@@ -93,8 +141,8 @@ declare interface EidosProviderProps extends EidosConfig {
|
|
|
93
141
|
export declare const eidosQueue: EidosReadable<ActionQueueItem[]>;
|
|
94
142
|
|
|
95
143
|
/**
|
|
96
|
-
* Queue counts. Re-
|
|
97
|
-
*
|
|
144
|
+
* Queue counts. Re-emits only when a count actually changes, not on every
|
|
145
|
+
* queue mutation (e.g. a status transition that doesn't change counts is skipped).
|
|
98
146
|
*/
|
|
99
147
|
export declare const eidosQueueStats: EidosReadable<{
|
|
100
148
|
pending: number;
|
|
@@ -129,8 +177,7 @@ export declare interface EidosState {
|
|
|
129
177
|
|
|
130
178
|
/**
|
|
131
179
|
* Online status + SW lifecycle.
|
|
132
|
-
*
|
|
133
|
-
* in the subscriber if you need to avoid unnecessary work.
|
|
180
|
+
* Only re-emits when isOnline, swStatus, or swError actually changes.
|
|
134
181
|
*/
|
|
135
182
|
export declare const eidosStatus: EidosReadable<{
|
|
136
183
|
isOnline: boolean;
|
|
@@ -146,6 +193,10 @@ export declare interface EidosStore extends EidosState {
|
|
|
146
193
|
unregisterResource: (url: string) => void;
|
|
147
194
|
addQueueItem: (item: ActionQueueItem) => void;
|
|
148
195
|
updateQueueItem: (id: string, update: Partial<ActionQueueItem>) => void;
|
|
196
|
+
batchUpdateQueueItems: (updates: Array<{
|
|
197
|
+
id: string;
|
|
198
|
+
update: Partial<ActionQueueItem>;
|
|
199
|
+
}>) => void;
|
|
149
200
|
removeQueueItem: (id: string) => void;
|
|
150
201
|
hydrateQueue: (items: ActionQueueItem[]) => void;
|
|
151
202
|
}
|
|
@@ -165,6 +216,8 @@ export declare interface GeneratedStrategy {
|
|
|
165
216
|
equivalentCode: string;
|
|
166
217
|
}
|
|
167
218
|
|
|
219
|
+
export declare function _getQueueStorage(): QueueStorage | null;
|
|
220
|
+
|
|
168
221
|
declare function _getState(): EidosStore;
|
|
169
222
|
|
|
170
223
|
export declare function initEidos(config?: EidosConfig): Promise<void>;
|
|
@@ -181,6 +234,15 @@ export declare interface QueuedResult {
|
|
|
181
234
|
readonly message: string;
|
|
182
235
|
}
|
|
183
236
|
|
|
237
|
+
export declare interface QueueStorage {
|
|
238
|
+
add(item: ActionQueueItem): Promise<void>;
|
|
239
|
+
getAll(): Promise<ActionQueueItem[]>;
|
|
240
|
+
getPending(): Promise<ActionQueueItem[]>;
|
|
241
|
+
update(id: string, patch: Partial<ActionQueueItem>): Promise<void>;
|
|
242
|
+
remove(id: string): Promise<void>;
|
|
243
|
+
clear(): Promise<void>;
|
|
244
|
+
}
|
|
245
|
+
|
|
184
246
|
export declare function replayQueue(): Promise<ReplayResult>;
|
|
185
247
|
|
|
186
248
|
/** Summary returned by replayQueue(). */
|
|
@@ -195,6 +257,8 @@ export declare interface ReplayResult {
|
|
|
195
257
|
retrying: number;
|
|
196
258
|
/** Items whose actionId had no registered function — likely not yet imported. */
|
|
197
259
|
skipped: number;
|
|
260
|
+
/** Items that received a 4xx response and were dropped via `onConflict: () => 'skip'`. */
|
|
261
|
+
conflicted: number;
|
|
198
262
|
}
|
|
199
263
|
|
|
200
264
|
export declare function _resetEidos(): void;
|
|
@@ -245,6 +309,9 @@ export declare function setOfflineSimulation(enabled: boolean): void;
|
|
|
245
309
|
|
|
246
310
|
/* Excluded from this release type: setQueryInvalidator */
|
|
247
311
|
|
|
312
|
+
/** Override the default IndexedDB queue with a custom storage backend (e.g. AsyncStorage for React Native). */
|
|
313
|
+
export declare function setQueueStorage(s: QueueStorage): void;
|
|
314
|
+
|
|
248
315
|
declare function _subscribe(listener: Listener): () => void;
|
|
249
316
|
|
|
250
317
|
/** Full Eidos store — prefer the narrower hooks below for performance. */
|
|
@@ -285,6 +352,9 @@ export declare function useEidosQueueStats(): {
|
|
|
285
352
|
/** Live state for a single registered resource URL. */
|
|
286
353
|
export declare function useEidosResource(url: string): ResourceEntry;
|
|
287
354
|
|
|
355
|
+
/** All registered resources — only re-renders when the resources map changes, not on queue mutations. */
|
|
356
|
+
export declare function useEidosResources(): Record<string, ResourceEntry>;
|
|
357
|
+
|
|
288
358
|
/**
|
|
289
359
|
* Online + SW status — cheap subscription, safe to use in header components.
|
|
290
360
|
* Three separate primitive selectors so each only triggers a re-render when
|
|
@@ -302,6 +372,32 @@ export declare const useEidosStore: {
|
|
|
302
372
|
setState: (partial: Partial<EidosStore> | ((s: EidosStore) => Partial<EidosStore>)) => void;
|
|
303
373
|
};
|
|
304
374
|
|
|
305
|
-
export declare const VERSION = "1.0.
|
|
375
|
+
export declare const VERSION = "1.0.31";
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Bulk-prefetch an array of resource handles concurrently, warming the cache
|
|
379
|
+
* for each one. Useful on login / app init when you know which resources the
|
|
380
|
+
* user will need offline.
|
|
381
|
+
*
|
|
382
|
+
* Pattern handles (containing `*`, `**`, or `:param`) are silently skipped —
|
|
383
|
+
* they match multiple URLs so there is no single URL to prefetch.
|
|
384
|
+
*
|
|
385
|
+
* @example
|
|
386
|
+
* import { warmCache } from '@sweidos/eidos'
|
|
387
|
+
*
|
|
388
|
+
* // In EidosProvider's onReady, or after login:
|
|
389
|
+
* const { warmed, failed } = await warmCache([products, userProfile, settings])
|
|
390
|
+
*/
|
|
391
|
+
export declare function warmCache(handles: ResourceHandle[]): Promise<WarmCacheResult>;
|
|
392
|
+
|
|
393
|
+
/** Summary returned by warmCache(). */
|
|
394
|
+
export declare interface WarmCacheResult {
|
|
395
|
+
/** Resources that were prefetched successfully. */
|
|
396
|
+
warmed: number;
|
|
397
|
+
/** Resources whose prefetch threw (network error, offline, etc.). */
|
|
398
|
+
failed: number;
|
|
399
|
+
/** The raw errors, in input order, for failed handles. */
|
|
400
|
+
errors: unknown[];
|
|
401
|
+
}
|
|
306
402
|
|
|
307
403
|
export { }
|
package/dist/index.js
CHANGED
|
@@ -1,37 +1,44 @@
|
|
|
1
|
-
import { resource as
|
|
2
|
-
import { action as
|
|
3
|
-
import { _resetEidos as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
1
|
+
import { resource as r, setQueryInvalidator as s, warmCache as t } from "./resource.js";
|
|
2
|
+
import { action as i, clearQueue as d, replayQueue as a } from "./action.js";
|
|
3
|
+
import { _resetEidos as S, initEidos as f } from "./runtime.js";
|
|
4
|
+
import { _getQueueStorage as E, setQueueStorage as c } from "./queue-storage.js";
|
|
5
|
+
import { AsyncStorageQueueStorage as x } from "./async-storage-adapter.js";
|
|
6
|
+
import { EidosProvider as g } from "./react/Provider.js";
|
|
7
|
+
import { useEidos as y, useEidosAction as R, useEidosOnDrain as A, useEidosQueue as O, useEidosQueueStats as v, useEidosResource as I, useEidosResources as _, useEidosStatus as h } from "./react/hooks.js";
|
|
8
|
+
import { VERSION as B } from "./version.js";
|
|
9
|
+
import { isBgSyncSupported as D, setOfflineSimulation as N } from "./sw-bridge.js";
|
|
10
|
+
import { useEidosStore as V } from "./store.js";
|
|
11
|
+
import { eidosAction as j, eidosQueue as k, eidosQueueStats as q, eidosResource as z, eidosStatus as F, eidosStore as G } from "./stores.js";
|
|
10
12
|
export {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
r as
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
c as
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
O as
|
|
35
|
-
|
|
13
|
+
x as AsyncStorageQueueStorage,
|
|
14
|
+
g as EidosProvider,
|
|
15
|
+
B as VERSION,
|
|
16
|
+
E as _getQueueStorage,
|
|
17
|
+
S as _resetEidos,
|
|
18
|
+
i as action,
|
|
19
|
+
d as clearQueue,
|
|
20
|
+
j as eidosAction,
|
|
21
|
+
k as eidosQueue,
|
|
22
|
+
q as eidosQueueStats,
|
|
23
|
+
z as eidosResource,
|
|
24
|
+
F as eidosStatus,
|
|
25
|
+
G as eidosStore,
|
|
26
|
+
f as initEidos,
|
|
27
|
+
D as isBgSyncSupported,
|
|
28
|
+
a as replayQueue,
|
|
29
|
+
r as resource,
|
|
30
|
+
N as setOfflineSimulation,
|
|
31
|
+
s as setQueryInvalidator,
|
|
32
|
+
c as setQueueStorage,
|
|
33
|
+
y as useEidos,
|
|
34
|
+
R as useEidosAction,
|
|
35
|
+
A as useEidosOnDrain,
|
|
36
|
+
O as useEidosQueue,
|
|
37
|
+
v as useEidosQueueStats,
|
|
38
|
+
I as useEidosResource,
|
|
39
|
+
_ as useEidosResources,
|
|
40
|
+
h as useEidosStatus,
|
|
41
|
+
V as useEidosStore,
|
|
42
|
+
t as warmCache
|
|
36
43
|
};
|
|
37
44
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;"}
|
package/dist/nextjs.d.ts
ADDED
package/dist/nextjs.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { EidosProvider, useEidos, useEidosAction, useEidosOnDrain, useEidosQueue, useEidosQueueStats, useEidosResource, useEidosStatus } from "@sweidos/eidos";
|
|
3
|
+
export {
|
|
4
|
+
EidosProvider,
|
|
5
|
+
useEidos,
|
|
6
|
+
useEidosAction,
|
|
7
|
+
useEidosOnDrain,
|
|
8
|
+
useEidosQueue,
|
|
9
|
+
useEidosQueueStats,
|
|
10
|
+
useEidosResource,
|
|
11
|
+
useEidosStatus
|
|
12
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-storage.js","sources":["../src/queue-storage.ts"],"sourcesContent":["import type { ActionQueueItem } from './types'\n\nexport interface QueueStorage {\n add(item: ActionQueueItem): Promise<void>\n getAll(): Promise<ActionQueueItem[]>\n getPending(): Promise<ActionQueueItem[]>\n update(id: string, patch: Partial<ActionQueueItem>): Promise<void>\n remove(id: string): Promise<void>\n clear(): Promise<void>\n}\n\nlet _storage: QueueStorage | null = null\n\n/** Override the default IndexedDB queue with a custom storage backend (e.g. AsyncStorage for React Native). */\nexport function setQueueStorage(s: QueueStorage): void {\n _storage = s\n}\n\nexport function _getQueueStorage(): QueueStorage | null {\n return _storage\n}\n"],"names":["_storage","setQueueStorage","s","_getQueueStorage"],"mappings":"AAWA,IAAIA,IAAgC;AAG7B,SAASC,EAAgBC,GAAuB;AACrD,EAAAF,IAAWE;AACb;AAEO,SAASC,IAAwC;AACtD,SAAOH;AACT;"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface EidosDevtoolsProps {
|
|
2
|
+
/** Corner to anchor the panel. Default: 'bottom-right'. */
|
|
3
|
+
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
4
|
+
/** Start expanded. Default: false. */
|
|
5
|
+
defaultOpen?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function EidosDevtools({ position, defaultOpen }: EidosDevtoolsProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface EidosProviderRNProps {
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
/**
|
|
6
|
+
* Current network connectivity state.
|
|
7
|
+
* Pass the value from `useNetInfo()` (@react-native-community/netinfo).
|
|
8
|
+
* Defaults to `true` if omitted.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const netInfo = useNetInfo()
|
|
12
|
+
* <EidosProviderRN isConnected={netInfo.isConnected ?? true}>
|
|
13
|
+
*/
|
|
14
|
+
isConnected?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Connectivity bridge for React Native.
|
|
18
|
+
* Syncs the network state into the Eidos store so `initEidosRN`'s autoReplay
|
|
19
|
+
* subscription fires when the device comes back online.
|
|
20
|
+
*
|
|
21
|
+
* Render this near the root of your app, after calling `initEidosRN()`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function EidosProviderRN({ children, isConnected }: EidosProviderRNProps): React.JSX.Element;
|
package/dist/react/hooks.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { useRef as
|
|
1
|
+
import { useRef as a, useEffect as E, useSyncExternalStore as p } from "react";
|
|
2
2
|
import { useEidosStore as l } from "../store.js";
|
|
3
3
|
function s(e) {
|
|
4
4
|
const t = e ?? ((n) => n);
|
|
5
|
-
return
|
|
5
|
+
return p(l.subscribe, () => t(l.getState()));
|
|
6
6
|
}
|
|
7
7
|
function q() {
|
|
8
8
|
return s();
|
|
9
9
|
}
|
|
10
|
+
function R() {
|
|
11
|
+
return s((e) => e.resources);
|
|
12
|
+
}
|
|
10
13
|
function m(e) {
|
|
11
14
|
return s((t) => t.resources[e]);
|
|
12
15
|
}
|
|
@@ -16,32 +19,33 @@ function w() {
|
|
|
16
19
|
function y(e) {
|
|
17
20
|
return s((t) => t.queue.find((n) => n.id === e));
|
|
18
21
|
}
|
|
19
|
-
function
|
|
22
|
+
function $() {
|
|
20
23
|
const e = s((u) => u.isOnline), t = s((u) => u.swStatus), n = s((u) => u.swError);
|
|
21
24
|
return { isOnline: e, swStatus: t, swError: n };
|
|
22
25
|
}
|
|
23
|
-
function
|
|
26
|
+
function O() {
|
|
24
27
|
const e = s((i) => {
|
|
25
|
-
let c = 0, f = 0,
|
|
28
|
+
let c = 0, f = 0, d = 0;
|
|
26
29
|
for (const o of i.queue)
|
|
27
|
-
o.status === "pending" ? c++ : o.status === "failed" ? f++ : o.status === "replaying" &&
|
|
28
|
-
return `${c},${f},${
|
|
30
|
+
o.status === "pending" ? c++ : o.status === "failed" ? f++ : o.status === "replaying" && d++;
|
|
31
|
+
return `${c},${f},${d},${i.queue.length}`;
|
|
29
32
|
}), [t, n, u, r] = e.split(",");
|
|
30
33
|
return { pending: +t, failed: +n, replaying: +u, total: +r };
|
|
31
34
|
}
|
|
32
|
-
function
|
|
33
|
-
const t = s((r) => r.queue.length), n =
|
|
34
|
-
u.current = e,
|
|
35
|
+
function b(e) {
|
|
36
|
+
const t = s((r) => r.queue.length), n = a(0), u = a(e);
|
|
37
|
+
u.current = e, E(() => {
|
|
35
38
|
n.current > 0 && t === 0 && u.current(), n.current = t;
|
|
36
39
|
}, [t]);
|
|
37
40
|
}
|
|
38
41
|
export {
|
|
39
42
|
q as useEidos,
|
|
40
43
|
y as useEidosAction,
|
|
41
|
-
|
|
44
|
+
b as useEidosOnDrain,
|
|
42
45
|
w as useEidosQueue,
|
|
43
|
-
|
|
46
|
+
O as useEidosQueueStats,
|
|
44
47
|
m as useEidosResource,
|
|
45
|
-
R as
|
|
48
|
+
R as useEidosResources,
|
|
49
|
+
$ as useEidosStatus
|
|
46
50
|
};
|
|
47
51
|
//# sourceMappingURL=hooks.js.map
|
package/dist/react/hooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.js","sources":["../../src/react/hooks.ts"],"sourcesContent":["import { useEffect, useRef, useSyncExternalStore } from 'react'\nimport { useEidosStore } from '../store'\nimport type { EidosStore } from '../store'\n\nfunction useStore(): EidosStore\nfunction useStore<T>(selector: (state: EidosStore) => T): T\nfunction useStore<T = EidosStore>(selector?: (state: EidosStore) => T): T {\n const fn = selector ?? ((s: EidosStore) => s as unknown as T)\n return useSyncExternalStore(useEidosStore.subscribe, () => fn(useEidosStore.getState()))\n}\n\n/** Full Eidos store — prefer the narrower hooks below for performance. */\nexport function useEidos() {\n return useStore()\n}\n\n/** Live state for a single registered resource URL. */\nexport function useEidosResource(url: string) {\n return useStore((s) => s.resources[url])\n}\n\n/** The current action queue. */\nexport function useEidosQueue() {\n return useStore((s) => s.queue)\n}\n\n/**\n * Live state for a single queue item by ID. Only re-renders when that specific\n * item changes — cheaper than `useEidosQueue().find(id)` which re-renders on\n * any queue mutation.\n */\nexport function useEidosAction(id: string) {\n return useStore((s) => s.queue.find((item) => item.id === id))\n}\n\n/**\n * Online + SW status — cheap subscription, safe to use in header components.\n * Three separate primitive selectors so each only triggers a re-render when\n * its own value changes (no object-reference churn from a combined selector).\n */\nexport function useEidosStatus() {\n const isOnline = useStore((s) => s.isOnline)\n const swStatus = useStore((s) => s.swStatus)\n const swError = useStore((s) => s.swError)\n return { isOnline, swStatus, swError }\n}\n\n/**\n * Queue counts — single subscription, single loop. Re-renders only when a\n * count changes, not on every queue mutation. Use for badges and status bars\n * instead of `useEidosQueue()` when you only need numbers, not full items.\n */\nexport function useEidosQueueStats() {\n // Encode as a comma-separated string so useSyncExternalStore's Object.is\n // comparison bails out correctly when counts haven't changed. One loop,\n // one subscription — cheaper than four separate filter() passes.\n const encoded = useStore((s) => {\n let pending = 0, failed = 0, replaying = 0\n for (const q of s.queue) {\n if (q.status === 'pending') pending++\n else if (q.status === 'failed') failed++\n else if (q.status === 'replaying') replaying++\n }\n return `${pending},${failed},${replaying},${s.queue.length}`\n })\n const [p, f, r, t] = encoded.split(',')\n return { pending: +p, failed: +f, replaying: +r, total: +t }\n}\n\n/**\n * Calls `callback` once each time the action queue drains from non-empty → 0.\n * Stable callback reference not required — always calls the latest version.\n * Use for \"all offline actions synced!\" toasts.\n *\n * @example\n * useEidosOnDrain(() => toast.success('All offline actions synced!'))\n */\nexport function useEidosOnDrain(callback: () => void) {\n const total = useStore((s) => s.queue.length)\n const prevRef = useRef(0)\n const callbackRef = useRef(callback)\n callbackRef.current = callback\n\n useEffect(() => {\n if (prevRef.current > 0 && total === 0) {\n callbackRef.current()\n }\n prevRef.current = total\n }, [total])\n}\n"],"names":["useStore","selector","fn","s","useSyncExternalStore","useEidosStore","useEidos","useEidosResource","url","useEidosQueue","useEidosAction","id","item","useEidosStatus","isOnline","swStatus","swError","useEidosQueueStats","encoded","pending","failed","replaying","q","p","f","r","t","useEidosOnDrain","callback","total","prevRef","useRef","callbackRef","useEffect"],"mappings":";;AAMA,SAASA,EAAyBC,GAAwC;AACxE,QAAMC,IAAKD,MAAa,CAACE,MAAkBA;AAC3C,SAAOC,EAAqBC,EAAc,WAAW,MAAMH,EAAGG,EAAc,SAAA,CAAU,CAAC;AACzF;AAGO,SAASC,IAAW;AACzB,SAAON,EAAA;AACT;AAGO,SAASO,EAAiBC,GAAa;AAC5C,
|
|
1
|
+
{"version":3,"file":"hooks.js","sources":["../../src/react/hooks.ts"],"sourcesContent":["import { useEffect, useRef, useSyncExternalStore } from 'react'\nimport { useEidosStore } from '../store'\nimport type { EidosStore } from '../store'\n\nfunction useStore(): EidosStore\nfunction useStore<T>(selector: (state: EidosStore) => T): T\nfunction useStore<T = EidosStore>(selector?: (state: EidosStore) => T): T {\n const fn = selector ?? ((s: EidosStore) => s as unknown as T)\n return useSyncExternalStore(useEidosStore.subscribe, () => fn(useEidosStore.getState()))\n}\n\n/** Full Eidos store — prefer the narrower hooks below for performance. */\nexport function useEidos() {\n return useStore()\n}\n\n/** All registered resources — only re-renders when the resources map changes, not on queue mutations. */\nexport function useEidosResources() {\n return useStore((s) => s.resources)\n}\n\n/** Live state for a single registered resource URL. */\nexport function useEidosResource(url: string) {\n return useStore((s) => s.resources[url])\n}\n\n/** The current action queue. */\nexport function useEidosQueue() {\n return useStore((s) => s.queue)\n}\n\n/**\n * Live state for a single queue item by ID. Only re-renders when that specific\n * item changes — cheaper than `useEidosQueue().find(id)` which re-renders on\n * any queue mutation.\n */\nexport function useEidosAction(id: string) {\n return useStore((s) => s.queue.find((item) => item.id === id))\n}\n\n/**\n * Online + SW status — cheap subscription, safe to use in header components.\n * Three separate primitive selectors so each only triggers a re-render when\n * its own value changes (no object-reference churn from a combined selector).\n */\nexport function useEidosStatus() {\n const isOnline = useStore((s) => s.isOnline)\n const swStatus = useStore((s) => s.swStatus)\n const swError = useStore((s) => s.swError)\n return { isOnline, swStatus, swError }\n}\n\n/**\n * Queue counts — single subscription, single loop. Re-renders only when a\n * count changes, not on every queue mutation. Use for badges and status bars\n * instead of `useEidosQueue()` when you only need numbers, not full items.\n */\nexport function useEidosQueueStats() {\n // Encode as a comma-separated string so useSyncExternalStore's Object.is\n // comparison bails out correctly when counts haven't changed. One loop,\n // one subscription — cheaper than four separate filter() passes.\n const encoded = useStore((s) => {\n let pending = 0, failed = 0, replaying = 0\n for (const q of s.queue) {\n if (q.status === 'pending') pending++\n else if (q.status === 'failed') failed++\n else if (q.status === 'replaying') replaying++\n }\n return `${pending},${failed},${replaying},${s.queue.length}`\n })\n const [p, f, r, t] = encoded.split(',')\n return { pending: +p, failed: +f, replaying: +r, total: +t }\n}\n\n/**\n * Calls `callback` once each time the action queue drains from non-empty → 0.\n * Stable callback reference not required — always calls the latest version.\n * Use for \"all offline actions synced!\" toasts.\n *\n * @example\n * useEidosOnDrain(() => toast.success('All offline actions synced!'))\n */\nexport function useEidosOnDrain(callback: () => void) {\n const total = useStore((s) => s.queue.length)\n const prevRef = useRef(0)\n const callbackRef = useRef(callback)\n callbackRef.current = callback\n\n useEffect(() => {\n if (prevRef.current > 0 && total === 0) {\n callbackRef.current()\n }\n prevRef.current = total\n }, [total])\n}\n"],"names":["useStore","selector","fn","s","useSyncExternalStore","useEidosStore","useEidos","useEidosResources","useEidosResource","url","useEidosQueue","useEidosAction","id","item","useEidosStatus","isOnline","swStatus","swError","useEidosQueueStats","encoded","pending","failed","replaying","q","p","f","r","t","useEidosOnDrain","callback","total","prevRef","useRef","callbackRef","useEffect"],"mappings":";;AAMA,SAASA,EAAyBC,GAAwC;AACxE,QAAMC,IAAKD,MAAa,CAACE,MAAkBA;AAC3C,SAAOC,EAAqBC,EAAc,WAAW,MAAMH,EAAGG,EAAc,SAAA,CAAU,CAAC;AACzF;AAGO,SAASC,IAAW;AACzB,SAAON,EAAA;AACT;AAGO,SAASO,IAAoB;AAClC,SAAOP,EAAS,CAACG,MAAMA,EAAE,SAAS;AACpC;AAGO,SAASK,EAAiBC,GAAa;AAC5C,SAAOT,EAAS,CAACG,MAAMA,EAAE,UAAUM,CAAG,CAAC;AACzC;AAGO,SAASC,IAAgB;AAC9B,SAAOV,EAAS,CAACG,MAAMA,EAAE,KAAK;AAChC;AAOO,SAASQ,EAAeC,GAAY;AACzC,SAAOZ,EAAS,CAACG,MAAMA,EAAE,MAAM,KAAK,CAACU,MAASA,EAAK,OAAOD,CAAE,CAAC;AAC/D;AAOO,SAASE,IAAiB;AAC/B,QAAMC,IAAWf,EAAS,CAACG,MAAMA,EAAE,QAAQ,GACrCa,IAAWhB,EAAS,CAACG,MAAMA,EAAE,QAAQ,GACrCc,IAAUjB,EAAS,CAACG,MAAMA,EAAE,OAAO;AACzC,SAAO,EAAE,UAAAY,GAAU,UAAAC,GAAU,SAAAC,EAAA;AAC/B;AAOO,SAASC,IAAqB;AAInC,QAAMC,IAAUnB,EAAS,CAACG,MAAM;AAC9B,QAAIiB,IAAU,GAAGC,IAAS,GAAGC,IAAY;AACzC,eAAWC,KAAKpB,EAAE;AAChB,MAAIoB,EAAE,WAAW,YAAWH,MACnBG,EAAE,WAAW,WAAUF,MACvBE,EAAE,WAAW,eAAaD;AAErC,WAAO,GAAGF,CAAO,IAAIC,CAAM,IAAIC,CAAS,IAAInB,EAAE,MAAM,MAAM;AAAA,EAC5D,CAAC,GACK,CAACqB,GAAGC,GAAGC,GAAGC,CAAC,IAAIR,EAAQ,MAAM,GAAG;AACtC,SAAO,EAAE,SAAS,CAACK,GAAG,QAAQ,CAACC,GAAG,WAAW,CAACC,GAAG,OAAO,CAACC,EAAA;AAC3D;AAUO,SAASC,EAAgBC,GAAsB;AACpD,QAAMC,IAAW9B,EAAS,CAACG,MAAMA,EAAE,MAAM,MAAM,GACzC4B,IAAWC,EAAO,CAAC,GACnBC,IAAcD,EAAOH,CAAQ;AACnC,EAAAI,EAAY,UAAUJ,GAEtBK,EAAU,MAAM;AACd,IAAIH,EAAQ,UAAU,KAAKD,MAAU,KACnCG,EAAY,QAAA,GAEdF,EAAQ,UAAUD;AAAA,EACpB,GAAG,CAACA,CAAK,CAAC;AACZ;"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { EidosProviderRN } from './react/ProviderRN';
|
|
2
|
+
export type { EidosProviderRNProps } from './react/ProviderRN';
|
|
3
|
+
export { initEidosRN, _resetEidosRN } from './runtime-rn';
|
|
4
|
+
export type { EidosRNConfig } from './runtime-rn';
|
|
5
|
+
export { setQueueStorage } from './index.ts';
|
|
6
|
+
export type { QueueStorage } from './index.ts';
|
|
7
|
+
export { AsyncStorageQueueStorage } from './index.ts';
|
|
8
|
+
export type { AsyncStorageLike } from './index.ts';
|
|
@@ -0,0 +1,59 @@
|
|
|
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";
|
|
5
|
+
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 });
|
|
15
|
+
}
|
|
16
|
+
let _initialized = false;
|
|
17
|
+
let _unsubscribe = null;
|
|
18
|
+
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" || q.status === "failed");
|
|
43
|
+
if (store.isOnline && hasPending) {
|
|
44
|
+
setTimeout(replayQueue, 1200);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function _resetEidosRN() {
|
|
49
|
+
_unsubscribe == null ? void 0 : _unsubscribe();
|
|
50
|
+
_unsubscribe = null;
|
|
51
|
+
_initialized = false;
|
|
52
|
+
}
|
|
53
|
+
export {
|
|
54
|
+
AsyncStorageQueueStorage2 as AsyncStorageQueueStorage,
|
|
55
|
+
EidosProviderRN,
|
|
56
|
+
_resetEidosRN,
|
|
57
|
+
initEidosRN,
|
|
58
|
+
setQueueStorage2 as setQueueStorage
|
|
59
|
+
};
|