@sweidos/eidos 2.1.0 → 2.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.
- package/README.md +43 -14
- package/dist/action.js +47 -47
- package/dist/action.js.map +1 -1
- package/dist/devtools.js +169 -20
- package/dist/eidos.cjs +2 -2
- package/dist/eidos.cjs.map +1 -1
- package/dist/index.d.ts +74 -13
- package/dist/index.js +36 -33
- package/dist/react/hooks.js +30 -27
- package/dist/react/hooks.js.map +1 -1
- package/dist/runtime.js +29 -24
- package/dist/runtime.js.map +1 -1
- package/dist/store-slices.js +31 -20
- package/dist/store-slices.js.map +1 -1
- package/dist/store.js +22 -19
- package/dist/store.js.map +1 -1
- package/dist/stores.js +31 -22
- package/dist/stores.js.map +1 -1
- package/dist/testing.cjs +3 -2
- package/dist/testing.js +3 -2
- package/dist/types.js +19 -8
- package/dist/types.js.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -172,7 +172,7 @@ export declare function cancelByIdempotencyKey(idempotencyKey: string): Promise<
|
|
|
172
172
|
/** Remove all items from the action queue (storage + in-memory store). */
|
|
173
173
|
export declare function clearQueue(): Promise<void>;
|
|
174
174
|
|
|
175
|
-
declare interface ConflictConfig {
|
|
175
|
+
export declare interface ConflictConfig {
|
|
176
176
|
/**
|
|
177
177
|
* - `'serverWins'`: drop the queued item, keeping the server's state.
|
|
178
178
|
* - `'clientWins'`: keep retrying — the client's write should eventually
|
|
@@ -188,7 +188,7 @@ declare interface ConflictConfig {
|
|
|
188
188
|
* Passed to `ConflictConfig.resolve` (for `'merge'`/`'custom'` strategies)
|
|
189
189
|
* when a queued action's replay receives a 4xx response.
|
|
190
190
|
*/
|
|
191
|
-
declare interface ConflictContext {
|
|
191
|
+
export declare interface ConflictContext {
|
|
192
192
|
/** Whatever `fn` threw — typically a `Response` or an error with `.status`. */
|
|
193
193
|
error: unknown;
|
|
194
194
|
/** The original arguments the action was queued with. */
|
|
@@ -205,7 +205,7 @@ declare interface ConflictContext {
|
|
|
205
205
|
* - `{ resolved: args }`: replace the queued args and retry immediately
|
|
206
206
|
* on the next replay pass.
|
|
207
207
|
*/
|
|
208
|
-
declare type ConflictResolution = 'retry' | 'skip' | {
|
|
208
|
+
export declare type ConflictResolution = 'retry' | 'skip' | {
|
|
209
209
|
resolved: any[];
|
|
210
210
|
};
|
|
211
211
|
|
|
@@ -224,6 +224,15 @@ export declare interface EidosConfig {
|
|
|
224
224
|
swPath?: string;
|
|
225
225
|
/** Automatically replay the action queue on reconnect. Default: true. */
|
|
226
226
|
autoReplay?: boolean;
|
|
227
|
+
/**
|
|
228
|
+
* Opt-in reliability telemetry. Called with a snapshot of cumulative
|
|
229
|
+
* `neverLose` queue outcome counters (`ReliabilityStats`) every
|
|
230
|
+
* `reliabilityReportInterval` ms — wire this up to your analytics backend.
|
|
231
|
+
* Not called if omitted.
|
|
232
|
+
*/
|
|
233
|
+
onReliabilityReport?: (stats: ReliabilityStats) => void;
|
|
234
|
+
/** Interval (ms) between `onReliabilityReport` calls. Default: 60000. */
|
|
235
|
+
reliabilityReportInterval?: number;
|
|
227
236
|
}
|
|
228
237
|
|
|
229
238
|
/**
|
|
@@ -262,6 +271,13 @@ export declare interface EidosReadable<T> {
|
|
|
262
271
|
getState(): T;
|
|
263
272
|
}
|
|
264
273
|
|
|
274
|
+
/**
|
|
275
|
+
* Cumulative, session-scoped `neverLose` queue outcome counters — opt-in
|
|
276
|
+
* reliability telemetry. Re-emits only when a counter changes. See
|
|
277
|
+
* `EidosConfig.onReliabilityReport` to forward these to an analytics backend.
|
|
278
|
+
*/
|
|
279
|
+
export declare const eidosReliabilityStats: EidosReadable<ReliabilityStats>;
|
|
280
|
+
|
|
265
281
|
/**
|
|
266
282
|
* Live cache state for a single registered resource URL.
|
|
267
283
|
* @example
|
|
@@ -277,6 +293,7 @@ export declare interface EidosState {
|
|
|
277
293
|
swError?: string;
|
|
278
294
|
resources: Record<string, ResourceEntry>;
|
|
279
295
|
queue: ActionQueueItem[];
|
|
296
|
+
reliability: ReliabilityStats;
|
|
280
297
|
}
|
|
281
298
|
|
|
282
299
|
/**
|
|
@@ -289,7 +306,7 @@ export declare const eidosStatus: EidosReadable<{
|
|
|
289
306
|
swError: string | undefined;
|
|
290
307
|
}>;
|
|
291
308
|
|
|
292
|
-
export declare interface EidosStore extends EidosState, ResourceActions, QueueActions {
|
|
309
|
+
export declare interface EidosStore extends EidosState, ResourceActions, QueueActions, ReliabilityActions {
|
|
293
310
|
setOnline: (online: boolean) => void;
|
|
294
311
|
setSwStatus: (status: EidosState['swStatus'], error?: string) => void;
|
|
295
312
|
}
|
|
@@ -321,6 +338,17 @@ export declare function isBgSyncSupported(): boolean;
|
|
|
321
338
|
|
|
322
339
|
declare type Listener = () => void;
|
|
323
340
|
|
|
341
|
+
/**
|
|
342
|
+
* Calls `callback` once each time the action queue drains from non-empty → 0.
|
|
343
|
+
* Framework-agnostic equivalent of `useEidosOnDrain` for Svelte/Vue/vanilla.
|
|
344
|
+
* Returns an unsubscribe function.
|
|
345
|
+
*
|
|
346
|
+
* @example
|
|
347
|
+
* // Svelte
|
|
348
|
+
* onMount(() => onQueueDrain(() => toast.success('All offline actions synced!')))
|
|
349
|
+
*/
|
|
350
|
+
export declare function onQueueDrain(callback: () => void): () => void;
|
|
351
|
+
|
|
324
352
|
/**
|
|
325
353
|
* Handle for a URL pattern (`/api/products/*`, `/api/users/:id`, `**`).
|
|
326
354
|
* The SW intercepts all matching requests automatically — there is no single
|
|
@@ -372,6 +400,32 @@ export declare interface QueueStorage {
|
|
|
372
400
|
|
|
373
401
|
export declare function registerPushCallbacks(handlers: PushHandlers): void;
|
|
374
402
|
|
|
403
|
+
declare interface ReliabilityActions {
|
|
404
|
+
recordReliabilityEvent: (event: keyof ReliabilityStats) => void;
|
|
405
|
+
resetReliabilityStats: () => void;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Cumulative, session-scoped counters for `neverLose` queue outcomes — opt-in
|
|
410
|
+
* telemetry surfaced via `eidosReliabilityStats` / `useEidosReliabilityStats()`
|
|
411
|
+
* and `EidosConfig.onReliabilityReport`. Reset on page reload (not persisted).
|
|
412
|
+
*/
|
|
413
|
+
export declare interface ReliabilityStats {
|
|
414
|
+
[key: string]: number;
|
|
415
|
+
/** Actions persisted to the queue (offline, or online call that threw). */
|
|
416
|
+
queued: number;
|
|
417
|
+
/** Queue items that executed successfully (first attempt or a retry). */
|
|
418
|
+
succeeded: number;
|
|
419
|
+
/** Queue items that exhausted `maxRetries` and moved to `'failed'`. */
|
|
420
|
+
failed: number;
|
|
421
|
+
/** Replay attempts that failed but will retry (haven't exhausted `maxRetries`). */
|
|
422
|
+
retried: number;
|
|
423
|
+
/** Queue items dropped by a `serverWins`/`merge`/`custom` conflict resolution. */
|
|
424
|
+
conflicted: number;
|
|
425
|
+
/** Queue items removed via `handle.cancel(idempotencyKey)` before replay completed. */
|
|
426
|
+
cancelled: number;
|
|
427
|
+
}
|
|
428
|
+
|
|
375
429
|
export declare function replayQueue(): Promise<ReplayResult>;
|
|
376
430
|
|
|
377
431
|
/** Summary returned by replayQueue(). */
|
|
@@ -507,14 +561,6 @@ export declare function useEidos(): EidosStore;
|
|
|
507
561
|
*/
|
|
508
562
|
export declare function useEidosAction(id: string): ActionQueueItem | undefined;
|
|
509
563
|
|
|
510
|
-
/**
|
|
511
|
-
* Calls `callback` once each time the action queue drains from non-empty → 0.
|
|
512
|
-
* Stable callback reference not required — always calls the latest version.
|
|
513
|
-
* Use for "all offline actions synced!" toasts.
|
|
514
|
-
*
|
|
515
|
-
* @example
|
|
516
|
-
* useEidosOnDrain(() => toast.success('All offline actions synced!'))
|
|
517
|
-
*/
|
|
518
564
|
export declare function useEidosOnDrain(callback: () => void): void;
|
|
519
565
|
|
|
520
566
|
/** The current action queue. */
|
|
@@ -532,6 +578,21 @@ export declare function useEidosQueueStats(): {
|
|
|
532
578
|
total: number;
|
|
533
579
|
};
|
|
534
580
|
|
|
581
|
+
/**
|
|
582
|
+
* Calls `callback` once each time the action queue drains from non-empty → 0.
|
|
583
|
+
* Stable callback reference not required — always calls the latest version.
|
|
584
|
+
* Use for "all offline actions synced!" toasts.
|
|
585
|
+
*
|
|
586
|
+
* @example
|
|
587
|
+
* useEidosOnDrain(() => toast.success('All offline actions synced!'))
|
|
588
|
+
*/
|
|
589
|
+
/**
|
|
590
|
+
* Cumulative, session-scoped `neverLose` queue outcome counters — opt-in
|
|
591
|
+
* reliability telemetry for dashboards/devtools. Re-renders only when a
|
|
592
|
+
* counter changes.
|
|
593
|
+
*/
|
|
594
|
+
export declare function useEidosReliabilityStats(): ReliabilityStats;
|
|
595
|
+
|
|
535
596
|
/** Live state for a single registered resource URL. */
|
|
536
597
|
export declare function useEidosResource(url: string): ResourceEntry;
|
|
537
598
|
|
|
@@ -555,7 +616,7 @@ export declare const useEidosStore: {
|
|
|
555
616
|
setState: (partial: Partial<EidosStore> | ((s: EidosStore) => Partial<EidosStore>)) => void;
|
|
556
617
|
};
|
|
557
618
|
|
|
558
|
-
export declare const VERSION = "2.
|
|
619
|
+
export declare const VERSION = "2.2.0";
|
|
559
620
|
|
|
560
621
|
/**
|
|
561
622
|
* Bulk-prefetch an array of resource handles concurrently, warming the cache
|
package/dist/index.js
CHANGED
|
@@ -1,51 +1,54 @@
|
|
|
1
1
|
import { useEidosStore as o } from "./store.js";
|
|
2
|
-
import { getSwRegistration as
|
|
3
|
-
import { resource as d, resourcePattern as
|
|
2
|
+
import { getSwRegistration as i, isBgSyncSupported as s, registerPushCallbacks as t, sendToWorker as u, setOfflineSimulation as a } from "./sw-bridge.js";
|
|
3
|
+
import { resource as d, resourcePattern as n, setQueryInvalidator as c, warmCache as p } from "./resource.js";
|
|
4
4
|
import { _getQueueStorage as f, setQueueStorage as E } from "./queue-storage.js";
|
|
5
|
-
import { action as
|
|
6
|
-
import { subscribeReplayOnReconnect as
|
|
5
|
+
import { action as Q, cancelByIdempotencyKey as y, clearQueue as g, replayQueue as R, requeueItem as b } from "./action.js";
|
|
6
|
+
import { subscribeReplayOnReconnect as O } from "./replay.js";
|
|
7
7
|
import { _resetEidos as P, initEidos as h } from "./runtime.js";
|
|
8
8
|
import { AsyncStorageQueueStorage as v } from "./async-storage-adapter.js";
|
|
9
9
|
import { EidosProvider as B } from "./react/Provider.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
10
|
+
import { eidosAction as D, eidosQueue as _, eidosQueueStats as q, eidosReliabilityStats as x, eidosResource as K, eidosStatus as N, eidosStore as T, onQueueDrain as V } from "./stores.js";
|
|
11
|
+
import { useEidos as j, useEidosAction as z, useEidosOnDrain as F, useEidosQueue as G, useEidosQueueStats as H, useEidosReliabilityStats as J, useEidosResource as L, useEidosResources as M, useEidosStatus as U } from "./react/hooks.js";
|
|
12
|
+
import { VERSION as Y } from "./version.js";
|
|
13
13
|
export {
|
|
14
14
|
v as AsyncStorageQueueStorage,
|
|
15
15
|
B as EidosProvider,
|
|
16
|
-
|
|
16
|
+
Y as VERSION,
|
|
17
17
|
f as _getQueueStorage,
|
|
18
18
|
P as _resetEidos,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
19
|
+
Q as action,
|
|
20
|
+
y as cancelByIdempotencyKey,
|
|
21
|
+
g as clearQueue,
|
|
22
|
+
D as eidosAction,
|
|
23
|
+
_ as eidosQueue,
|
|
24
|
+
q as eidosQueueStats,
|
|
25
|
+
x as eidosReliabilityStats,
|
|
26
|
+
K as eidosResource,
|
|
27
|
+
N as eidosStatus,
|
|
28
|
+
T as eidosStore,
|
|
29
|
+
i as getSwRegistration,
|
|
29
30
|
h as initEidos,
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
s as isBgSyncSupported,
|
|
32
|
+
V as onQueueDrain,
|
|
33
|
+
t as registerPushCallbacks,
|
|
32
34
|
R as replayQueue,
|
|
33
|
-
|
|
35
|
+
b as requeueItem,
|
|
34
36
|
d as resource,
|
|
35
|
-
|
|
37
|
+
n as resourcePattern,
|
|
36
38
|
u as sendToWorker,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
a as setOfflineSimulation,
|
|
40
|
+
c as setQueryInvalidator,
|
|
39
41
|
E as setQueueStorage,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
O as subscribeReplayOnReconnect,
|
|
43
|
+
j as useEidos,
|
|
44
|
+
z as useEidosAction,
|
|
45
|
+
F as useEidosOnDrain,
|
|
46
|
+
G as useEidosQueue,
|
|
47
|
+
H as useEidosQueueStats,
|
|
48
|
+
J as useEidosReliabilityStats,
|
|
49
|
+
L as useEidosResource,
|
|
50
|
+
M as useEidosResources,
|
|
51
|
+
U as useEidosStatus,
|
|
49
52
|
o as useEidosStore,
|
|
50
53
|
p as warmCache
|
|
51
54
|
};
|
package/dist/react/hooks.js
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
|
-
import { useEidosStore as o } from "../store.js";
|
|
2
1
|
import { countQueueByStatus as l } from "../types.js";
|
|
3
|
-
import {
|
|
2
|
+
import { useEidosStore as r } from "../store.js";
|
|
3
|
+
import { onQueueDrain as E } from "../stores.js";
|
|
4
|
+
import { useEffect as s, useRef as p, useSyncExternalStore as S } from "react";
|
|
4
5
|
function u(e) {
|
|
5
6
|
const t = e ?? ((n) => n);
|
|
6
|
-
return
|
|
7
|
+
return S(r.subscribe, () => t(r.getState()));
|
|
7
8
|
}
|
|
8
|
-
function
|
|
9
|
+
function b() {
|
|
9
10
|
return u();
|
|
10
11
|
}
|
|
11
|
-
function
|
|
12
|
+
function w() {
|
|
12
13
|
return u((e) => e.resources);
|
|
13
14
|
}
|
|
14
|
-
function
|
|
15
|
+
function Q(e) {
|
|
15
16
|
return u((t) => t.resources[e]);
|
|
16
17
|
}
|
|
17
|
-
function
|
|
18
|
+
function $() {
|
|
18
19
|
return u((e) => e.queue);
|
|
19
20
|
}
|
|
20
|
-
function
|
|
21
|
+
function q(e) {
|
|
21
22
|
return u((t) => t.queue.find((n) => n.id === e));
|
|
22
23
|
}
|
|
23
24
|
function O() {
|
|
@@ -27,34 +28,36 @@ function O() {
|
|
|
27
28
|
swError: u((e) => e.swError)
|
|
28
29
|
};
|
|
29
30
|
}
|
|
30
|
-
function
|
|
31
|
-
const [e, t, n,
|
|
32
|
-
const { pending:
|
|
33
|
-
return `${f},${a},${d}
|
|
31
|
+
function x() {
|
|
32
|
+
const [e, t, n, i] = u((o) => {
|
|
33
|
+
const { pending: c, failed: f, replaying: a, total: d } = l(o.queue);
|
|
34
|
+
return `${c},${f},${a},${d}`;
|
|
34
35
|
}).split(",");
|
|
35
36
|
return {
|
|
36
37
|
pending: +e,
|
|
37
38
|
failed: +t,
|
|
38
39
|
replaying: +n,
|
|
39
|
-
total: +
|
|
40
|
+
total: +i
|
|
40
41
|
};
|
|
41
42
|
}
|
|
42
|
-
function
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
function D() {
|
|
44
|
+
return u((e) => e.reliability);
|
|
45
|
+
}
|
|
46
|
+
function A(e) {
|
|
47
|
+
const t = p(e);
|
|
48
|
+
s(() => {
|
|
49
|
+
t.current = e;
|
|
50
|
+
}), s(() => E(() => t.current()), []);
|
|
49
51
|
}
|
|
50
52
|
export {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
b as useEidos,
|
|
54
|
+
q as useEidosAction,
|
|
55
|
+
A as useEidosOnDrain,
|
|
56
|
+
$ as useEidosQueue,
|
|
57
|
+
x as useEidosQueueStats,
|
|
58
|
+
D as useEidosReliabilityStats,
|
|
59
|
+
Q as useEidosResource,
|
|
60
|
+
w as useEidosResources,
|
|
58
61
|
O as useEidosStatus
|
|
59
62
|
};
|
|
60
63
|
|
package/dist/react/hooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.js","names":[],"sources":["../../src/react/hooks.ts"],"sourcesContent":["import { useEffect, useRef, useSyncExternalStore } from 'react';\nimport { useEidosStore } from '../store';\nimport type { EidosStore } from '../store';\nimport { countQueueByStatus } from '../types';\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 const { pending, failed, replaying, total } = countQueueByStatus(s.queue);\n return `${pending},${failed},${replaying},${total}`;\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
|
|
1
|
+
{"version":3,"file":"hooks.js","names":[],"sources":["../../src/react/hooks.ts"],"sourcesContent":["import { useEffect, useRef, useSyncExternalStore } from 'react';\nimport { useEidosStore } from '../store';\nimport type { EidosStore } from '../store';\nimport { countQueueByStatus } from '../types';\nimport { onQueueDrain } from '../stores';\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 const { pending, failed, replaying, total } = countQueueByStatus(s.queue);\n return `${pending},${failed},${replaying},${total}`;\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 */\n/**\n * Cumulative, session-scoped `neverLose` queue outcome counters — opt-in\n * reliability telemetry for dashboards/devtools. Re-renders only when a\n * counter changes.\n */\nexport function useEidosReliabilityStats() {\n return useStore((s) => s.reliability);\n}\n\nexport function useEidosOnDrain(callback: () => void) {\n const callbackRef = useRef(callback);\n\n useEffect(() => {\n callbackRef.current = callback;\n });\n\n useEffect(() => onQueueDrain(() => callbackRef.current()), []);\n}\n"],"mappings":";;;;AAQA,SAAS,EAAyB,GAAwC;AACxE,QAAM,IAAK,MAAA,CAAc,MAAkB;AAC3C,SAAO,EAAqB,EAAc,WAAA,MAAiB,EAAG,EAAc,SAAS,CAAC,CAAC;AACzF;AAGA,SAAgB,IAAW;AACzB,SAAO,EAAS;AAClB;AAGA,SAAgB,IAAoB;AAClC,SAAO,EAAA,CAAU,MAAM,EAAE,SAAS;AACpC;AAGA,SAAgB,EAAiB,GAAa;AAC5C,SAAO,EAAA,CAAU,MAAM,EAAE,UAAU,CAAA,CAAI;AACzC;AAGA,SAAgB,IAAgB;AAC9B,SAAO,EAAA,CAAU,MAAM,EAAE,KAAK;AAChC;AAOA,SAAgB,EAAe,GAAY;AACzC,SAAO,EAAA,CAAU,MAAM,EAAE,MAAM,KAAA,CAAM,MAAS,EAAK,OAAO,CAAE,CAAC;AAC/D;AAOA,SAAgB,IAAiB;AAI/B,SAAO;AAAA,IAAE,UAHQ,EAAA,CAAU,MAAM,EAAE,QAG1B;AAAA,IAAU,UAFF,EAAA,CAAU,MAAM,EAAE,QAEhB;AAAA,IAAU,SADb,EAAA,CAAU,MAAM,EAAE,OACL;AAAA,EAAQ;AACvC;AAOA,SAAgB,IAAqB;AAQnC,QAAM,CAAC,GAAG,GAAG,GAAG,CAAA,IAJA,EAAA,CAAU,MAAM;AAC9B,UAAM,EAAE,SAAA,GAAS,QAAA,GAAQ,WAAA,GAAW,OAAA,EAAA,IAAU,EAAmB,EAAE,KAAK;AACxE,WAAO,GAAG,CAAA,IAAW,CAAA,IAAU,CAAA,IAAa,CAAA;AAAA,EAC9C,CACqB,EAAQ,MAAM,GAAG;AACtC,SAAO;AAAA,IAAE,SAAS,CAAC;AAAA,IAAG,QAAQ,CAAC;AAAA,IAAG,WAAW,CAAC;AAAA,IAAG,OAAO,CAAC;AAAA,EAAE;AAC7D;AAeA,SAAgB,IAA2B;AACzC,SAAO,EAAA,CAAU,MAAM,EAAE,WAAW;AACtC;AAEA,SAAgB,EAAgB,GAAsB;AACpD,QAAM,IAAc,EAAO,CAAQ;AAEnC,EAAA,EAAA,MAAgB;AACd,IAAA,EAAY,UAAU;AAAA,EACxB,CAAC,GAED,EAAA,MAAgB,EAAA,MAAmB,EAAY,QAAQ,CAAC,GAAG,CAAC,CAAC;AAC/D"}
|
package/dist/runtime.js
CHANGED
|
@@ -1,50 +1,55 @@
|
|
|
1
|
-
import { useEidosStore as
|
|
2
|
-
import { registerBgSyncHandler as c, registerServiceWorker as
|
|
3
|
-
import { idbGetQueue as
|
|
4
|
-
import { _getQueueStorage as
|
|
5
|
-
import { subscribeQueueSync as
|
|
6
|
-
import { replayQueue as
|
|
7
|
-
import { subscribeReplayOnReconnect as
|
|
8
|
-
async function
|
|
1
|
+
import { useEidosStore as n } from "./store.js";
|
|
2
|
+
import { registerBgSyncHandler as c, registerServiceWorker as y } from "./sw-bridge.js";
|
|
3
|
+
import { idbGetQueue as m, idbQueueStorage as p } from "./idb.js";
|
|
4
|
+
import { _getQueueStorage as d } from "./queue-storage.js";
|
|
5
|
+
import { subscribeQueueSync as f } from "./queue-sync.js";
|
|
6
|
+
import { replayQueue as b } from "./action.js";
|
|
7
|
+
import { subscribeReplayOnReconnect as h } from "./replay.js";
|
|
8
|
+
async function R(e) {
|
|
9
9
|
if (e.schemaVersion === 2 && e.idempotencyKey) return e;
|
|
10
10
|
const t = {
|
|
11
11
|
...e,
|
|
12
12
|
schemaVersion: 2,
|
|
13
13
|
idempotencyKey: e.idempotencyKey ?? crypto.randomUUID()
|
|
14
14
|
};
|
|
15
|
-
return await (
|
|
15
|
+
return await (d() ?? p).update(t.id, {
|
|
16
16
|
schemaVersion: t.schemaVersion,
|
|
17
17
|
idempotencyKey: t.idempotencyKey
|
|
18
18
|
}).catch(() => {
|
|
19
19
|
}), t;
|
|
20
20
|
}
|
|
21
|
-
var
|
|
22
|
-
async function
|
|
23
|
-
if (typeof window > "u" ||
|
|
24
|
-
|
|
25
|
-
const t = e.swPath ?? "/eidos-sw.js",
|
|
21
|
+
var o = !1, s = null, u = null, i = null;
|
|
22
|
+
async function K(e = {}) {
|
|
23
|
+
if (typeof window > "u" || o) return;
|
|
24
|
+
o = !0;
|
|
25
|
+
const t = e.swPath ?? "/eidos-sw.js", l = e.autoReplay ?? !0;
|
|
26
26
|
try {
|
|
27
|
-
const
|
|
28
|
-
if (
|
|
29
|
-
const
|
|
30
|
-
|
|
27
|
+
const r = await m();
|
|
28
|
+
if (r.length > 0) {
|
|
29
|
+
const a = await Promise.all(r.map(R));
|
|
30
|
+
n.getState().hydrateQueue(a);
|
|
31
31
|
}
|
|
32
32
|
} catch {
|
|
33
33
|
}
|
|
34
34
|
try {
|
|
35
|
-
await
|
|
35
|
+
await y(t);
|
|
36
36
|
} catch {
|
|
37
37
|
}
|
|
38
|
-
c(() => {
|
|
39
|
-
|
|
40
|
-
}),
|
|
38
|
+
if (c(() => {
|
|
39
|
+
n.getState().isOnline && setTimeout(b, 200);
|
|
40
|
+
}), l && (s = h()), u = f(), e.onReliabilityReport) {
|
|
41
|
+
const r = e.reliabilityReportInterval ?? 6e4, a = e.onReliabilityReport;
|
|
42
|
+
i = setInterval(() => {
|
|
43
|
+
a(n.getState().reliability);
|
|
44
|
+
}, r);
|
|
45
|
+
}
|
|
41
46
|
}
|
|
42
47
|
function V() {
|
|
43
|
-
|
|
48
|
+
s?.(), s = null, u?.(), u = null, i && clearInterval(i), i = null, o = !1;
|
|
44
49
|
}
|
|
45
50
|
export {
|
|
46
51
|
V as _resetEidos,
|
|
47
|
-
|
|
52
|
+
K as initEidos
|
|
48
53
|
};
|
|
49
54
|
|
|
50
55
|
//# sourceMappingURL=runtime.js.map
|
package/dist/runtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","names":[],"sources":["../src/runtime.ts"],"sourcesContent":["import { registerServiceWorker, registerBgSyncHandler } from './sw-bridge';\nimport { replayQueue } from './action';\nimport { useEidosStore } from './store';\nimport { idbGetQueue, idbQueueStorage } from './idb';\nimport { _getQueueStorage } from './queue-storage';\nimport { subscribeReplayOnReconnect } from './replay';\nimport { subscribeQueueSync } from './queue-sync';\nimport { CURRENT_QUEUE_SCHEMA_VERSION } from './types';\nimport type { ActionQueueItem } from './types';\n\n// Items persisted before idempotencyKey/schemaVersion existed (v1) are migrated\n// in place: assign a fresh idempotencyKey and bump schemaVersion. A fresh key on\n// first replay after upgrade is correct — these items were never sent with one.\nasync function migrateQueueItem(item: ActionQueueItem): Promise<ActionQueueItem> {\n if (item.schemaVersion === CURRENT_QUEUE_SCHEMA_VERSION && item.idempotencyKey) {\n return item;\n }\n const migrated: ActionQueueItem = {\n ...item,\n schemaVersion: CURRENT_QUEUE_SCHEMA_VERSION,\n idempotencyKey: item.idempotencyKey ?? crypto.randomUUID(),\n };\n const storage = _getQueueStorage() ?? idbQueueStorage;\n await storage\n .update(migrated.id, {\n schemaVersion: migrated.schemaVersion,\n idempotencyKey: migrated.idempotencyKey,\n })\n .catch(() => {\n // Best-effort persist — item still gets the migrated shape in memory this session\n });\n return migrated;\n}\n\nexport interface EidosConfig {\n /** Path to the eidos service worker. Defaults to '/eidos-sw.js'. */\n swPath?: string;\n /** Automatically replay the action queue on reconnect. Default: true. */\n autoReplay?: boolean;\n}\n\nlet _initialized = false;\nlet _unsubscribe: (() => void) | null = null;\nlet _unsubscribeQueueSync: (() => void) | null = null;\n\nexport async function initEidos(config: EidosConfig = {}): Promise<void> {\n // Skip silently during SSR — SW, IndexedDB, and window are browser-only.\n if (typeof window === 'undefined') return;\n if (_initialized) return;\n _initialized = true;\n\n const swPath = config.swPath ?? '/eidos-sw.js';\n const autoReplay = config.autoReplay ?? true;\n\n // Restore persisted queue from IndexedDB on startup\n try {\n const persisted = await idbGetQueue();\n if (persisted.length > 0) {\n const migrated = await Promise.all(persisted.map(migrateQueueItem));\n useEidosStore.getState().hydrateQueue(migrated);\n }\n } catch {\n // IndexedDB unavailable (Firefox private browsing) — silent fallback\n }\n\n try {\n await registerServiceWorker(swPath);\n } catch {\n // SW registration failed; app continues without offline support\n }\n\n // When the SW fires the Background Sync tag, replay the queue in the main thread.\n // This path runs even if the user briefly navigated away and back — the browser\n // triggers the sync event on the SW, which wakes up all open clients.\n registerBgSyncHandler(() => {\n if (useEidosStore.getState().isOnline) {\n setTimeout(replayQueue, 200);\n }\n });\n\n if (autoReplay) {\n _unsubscribe = subscribeReplayOnReconnect();\n }\n\n // Apply queue-item status changes broadcast by the replay-lock holder so\n // non-leader tabs reflect live status without waiting for re-hydration.\n _unsubscribeQueueSync = subscribeQueueSync();\n\n if (import.meta.env.DEV) {\n const store = useEidosStore.getState();\n console.groupCollapsed('%c⚡ Eidos', 'color:#22C55E;font-weight:bold');\n console.log('SW path :', swPath);\n console.log('Auto-replay:', autoReplay);\n console.log('SW status :', store.swStatus);\n console.groupEnd();\n }\n}\n\nexport function _resetEidos() {\n _unsubscribe?.();\n _unsubscribe = null;\n _unsubscribeQueueSync?.();\n _unsubscribeQueueSync = null;\n _initialized = false;\n}\n"],"mappings":";;;;;;;AAaA,eAAe,EAAiB,GAAiD;AAC/E,MAAI,EAAK,kBAAA,KAAkD,EAAK,eAC9D,QAAO;AAET,QAAM,IAA4B;AAAA,IAChC,GAAG;AAAA,IACH,eAAA;AAAA,IACA,gBAAgB,EAAK,kBAAkB,OAAO,WAAW;AAAA,EAC3D;AAEA,gBADgB,EAAiB,KAAK,GAEnC,OAAO,EAAS,IAAI;AAAA,IACnB,eAAe,EAAS;AAAA,IACxB,gBAAgB,EAAS;AAAA,EAC3B,CAAC,EACA,MAAA,MAAY;AAAA,EAEb,CAAC,GACI;AACT;
|
|
1
|
+
{"version":3,"file":"runtime.js","names":[],"sources":["../src/runtime.ts"],"sourcesContent":["import { registerServiceWorker, registerBgSyncHandler } from './sw-bridge';\nimport { replayQueue } from './action';\nimport { useEidosStore } from './store';\nimport { idbGetQueue, idbQueueStorage } from './idb';\nimport { _getQueueStorage } from './queue-storage';\nimport { subscribeReplayOnReconnect } from './replay';\nimport { subscribeQueueSync } from './queue-sync';\nimport { CURRENT_QUEUE_SCHEMA_VERSION } from './types';\nimport type { ActionQueueItem, ReliabilityStats } from './types';\n\n// Items persisted before idempotencyKey/schemaVersion existed (v1) are migrated\n// in place: assign a fresh idempotencyKey and bump schemaVersion. A fresh key on\n// first replay after upgrade is correct — these items were never sent with one.\nasync function migrateQueueItem(item: ActionQueueItem): Promise<ActionQueueItem> {\n if (item.schemaVersion === CURRENT_QUEUE_SCHEMA_VERSION && item.idempotencyKey) {\n return item;\n }\n const migrated: ActionQueueItem = {\n ...item,\n schemaVersion: CURRENT_QUEUE_SCHEMA_VERSION,\n idempotencyKey: item.idempotencyKey ?? crypto.randomUUID(),\n };\n const storage = _getQueueStorage() ?? idbQueueStorage;\n await storage\n .update(migrated.id, {\n schemaVersion: migrated.schemaVersion,\n idempotencyKey: migrated.idempotencyKey,\n })\n .catch(() => {\n // Best-effort persist — item still gets the migrated shape in memory this session\n });\n return migrated;\n}\n\nexport interface EidosConfig {\n /** Path to the eidos service worker. Defaults to '/eidos-sw.js'. */\n swPath?: string;\n /** Automatically replay the action queue on reconnect. Default: true. */\n autoReplay?: boolean;\n /**\n * Opt-in reliability telemetry. Called with a snapshot of cumulative\n * `neverLose` queue outcome counters (`ReliabilityStats`) every\n * `reliabilityReportInterval` ms — wire this up to your analytics backend.\n * Not called if omitted.\n */\n onReliabilityReport?: (stats: ReliabilityStats) => void;\n /** Interval (ms) between `onReliabilityReport` calls. Default: 60000. */\n reliabilityReportInterval?: number;\n}\n\nlet _initialized = false;\nlet _unsubscribe: (() => void) | null = null;\nlet _unsubscribeQueueSync: (() => void) | null = null;\nlet _reliabilityReportTimer: ReturnType<typeof setInterval> | null = null;\n\nexport async function initEidos(config: EidosConfig = {}): Promise<void> {\n // Skip silently during SSR — SW, IndexedDB, and window are browser-only.\n if (typeof window === 'undefined') return;\n if (_initialized) return;\n _initialized = true;\n\n const swPath = config.swPath ?? '/eidos-sw.js';\n const autoReplay = config.autoReplay ?? true;\n\n // Restore persisted queue from IndexedDB on startup\n try {\n const persisted = await idbGetQueue();\n if (persisted.length > 0) {\n const migrated = await Promise.all(persisted.map(migrateQueueItem));\n useEidosStore.getState().hydrateQueue(migrated);\n }\n } catch {\n // IndexedDB unavailable (Firefox private browsing) — silent fallback\n }\n\n try {\n await registerServiceWorker(swPath);\n } catch {\n // SW registration failed; app continues without offline support\n }\n\n // When the SW fires the Background Sync tag, replay the queue in the main thread.\n // This path runs even if the user briefly navigated away and back — the browser\n // triggers the sync event on the SW, which wakes up all open clients.\n registerBgSyncHandler(() => {\n if (useEidosStore.getState().isOnline) {\n setTimeout(replayQueue, 200);\n }\n });\n\n if (autoReplay) {\n _unsubscribe = subscribeReplayOnReconnect();\n }\n\n // Apply queue-item status changes broadcast by the replay-lock holder so\n // non-leader tabs reflect live status without waiting for re-hydration.\n _unsubscribeQueueSync = subscribeQueueSync();\n\n if (config.onReliabilityReport) {\n const interval = config.reliabilityReportInterval ?? 60_000;\n const report = config.onReliabilityReport;\n _reliabilityReportTimer = setInterval(() => {\n report(useEidosStore.getState().reliability);\n }, interval);\n }\n\n if (import.meta.env.DEV) {\n const store = useEidosStore.getState();\n console.groupCollapsed('%c⚡ Eidos', 'color:#22C55E;font-weight:bold');\n console.log('SW path :', swPath);\n console.log('Auto-replay:', autoReplay);\n console.log('SW status :', store.swStatus);\n console.groupEnd();\n }\n}\n\nexport function _resetEidos() {\n _unsubscribe?.();\n _unsubscribe = null;\n _unsubscribeQueueSync?.();\n _unsubscribeQueueSync = null;\n if (_reliabilityReportTimer) clearInterval(_reliabilityReportTimer);\n _reliabilityReportTimer = null;\n _initialized = false;\n}\n"],"mappings":";;;;;;;AAaA,eAAe,EAAiB,GAAiD;AAC/E,MAAI,EAAK,kBAAA,KAAkD,EAAK,eAC9D,QAAO;AAET,QAAM,IAA4B;AAAA,IAChC,GAAG;AAAA,IACH,eAAA;AAAA,IACA,gBAAgB,EAAK,kBAAkB,OAAO,WAAW;AAAA,EAC3D;AAEA,gBADgB,EAAiB,KAAK,GAEnC,OAAO,EAAS,IAAI;AAAA,IACnB,eAAe,EAAS;AAAA,IACxB,gBAAgB,EAAS;AAAA,EAC3B,CAAC,EACA,MAAA,MAAY;AAAA,EAEb,CAAC,GACI;AACT;AAkBA,IAAI,IAAe,IACf,IAAoC,MACpC,IAA6C,MAC7C,IAAiE;AAErE,eAAsB,EAAU,IAAsB,CAAC,GAAkB;AAGvE,MADI,OAAO,SAAW,OAClB,EAAc;AAClB,EAAA,IAAe;AAEf,QAAM,IAAS,EAAO,UAAU,gBAC1B,IAAa,EAAO,cAAc;AAGxC,MAAI;AACF,UAAM,IAAY,MAAM,EAAY;AACpC,QAAI,EAAU,SAAS,GAAG;AACxB,YAAM,IAAW,MAAM,QAAQ,IAAI,EAAU,IAAI,CAAgB,CAAC;AAClE,MAAA,EAAc,SAAS,EAAE,aAAa,CAAQ;AAAA,IAChD;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,MAAI;AACF,UAAM,EAAsB,CAAM;AAAA,EACpC,QAAQ;AAAA,EAER;AAmBA,MAdA,EAAA,MAA4B;AAC1B,IAAI,EAAc,SAAS,EAAE,YAC3B,WAAW,GAAa,GAAG;AAAA,EAE/B,CAAC,GAEG,MACF,IAAe,EAA2B,IAK5C,IAAwB,EAAmB,GAEvC,EAAO,qBAAqB;AAC9B,UAAM,IAAW,EAAO,6BAA6B,KAC/C,IAAS,EAAO;AACtB,IAAA,IAA0B,YAAA,MAAkB;AAC1C,MAAA,EAAO,EAAc,SAAS,EAAE,WAAW;AAAA,IAC7C,GAAG,CAAQ;AAAA,EACb;AAUF;AAEA,SAAgB,IAAc;AAC5B,EAAA,IAAe,GACf,IAAe,MACf,IAAwB,GACxB,IAAwB,MACpB,KAAyB,cAAc,CAAuB,GAClE,IAA0B,MAC1B,IAAe;AACjB"}
|
package/dist/store-slices.js
CHANGED
|
@@ -1,43 +1,54 @@
|
|
|
1
|
-
|
|
1
|
+
import { emptyReliabilityStats as o } from "./types.js";
|
|
2
|
+
function s(t) {
|
|
2
3
|
return {
|
|
3
|
-
registerResource: (e, r) =>
|
|
4
|
+
registerResource: (e, r) => t((u) => ({ resources: {
|
|
4
5
|
...u.resources,
|
|
5
6
|
[e]: r
|
|
6
7
|
} })),
|
|
7
|
-
updateResource: (e, r) =>
|
|
8
|
+
updateResource: (e, r) => t((u) => ({ resources: {
|
|
8
9
|
...u.resources,
|
|
9
10
|
[e]: u.resources[e] ? {
|
|
10
11
|
...u.resources[e],
|
|
11
12
|
...r
|
|
12
13
|
} : u.resources[e]
|
|
13
14
|
} })),
|
|
14
|
-
unregisterResource: (e) =>
|
|
15
|
+
unregisterResource: (e) => t((r) => ({ resources: Object.fromEntries(Object.entries(r.resources).filter(([u]) => u !== e)) }))
|
|
15
16
|
};
|
|
16
17
|
}
|
|
17
|
-
function n(
|
|
18
|
+
function n(t) {
|
|
18
19
|
return {
|
|
19
|
-
addQueueItem: (e) =>
|
|
20
|
-
updateQueueItem: (e, r) =>
|
|
21
|
-
...
|
|
20
|
+
addQueueItem: (e) => t((r) => ({ queue: [...r.queue, e] })),
|
|
21
|
+
updateQueueItem: (e, r) => t((u) => ({ queue: u.queue.map((i) => i.id === e ? {
|
|
22
|
+
...i,
|
|
22
23
|
...r
|
|
23
|
-
} :
|
|
24
|
-
batchUpdateQueueItems: (e) =>
|
|
25
|
-
const u = new Map(e.map((
|
|
26
|
-
return { queue: r.queue.map((
|
|
27
|
-
const
|
|
28
|
-
return
|
|
29
|
-
...
|
|
30
|
-
...
|
|
31
|
-
} :
|
|
24
|
+
} : i) })),
|
|
25
|
+
batchUpdateQueueItems: (e) => t((r) => {
|
|
26
|
+
const u = new Map(e.map((i) => [i.id, i.update]));
|
|
27
|
+
return { queue: r.queue.map((i) => {
|
|
28
|
+
const c = u.get(i.id);
|
|
29
|
+
return c ? {
|
|
30
|
+
...i,
|
|
31
|
+
...c
|
|
32
|
+
} : i;
|
|
32
33
|
}) };
|
|
33
34
|
}),
|
|
34
|
-
removeQueueItem: (e) =>
|
|
35
|
-
hydrateQueue: (e) =>
|
|
35
|
+
removeQueueItem: (e) => t((r) => ({ queue: r.queue.filter((u) => u.id !== e) })),
|
|
36
|
+
hydrateQueue: (e) => t(() => ({ queue: e }))
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function l(t) {
|
|
40
|
+
return {
|
|
41
|
+
recordReliabilityEvent: (e) => t((r) => ({ reliability: {
|
|
42
|
+
...r.reliability,
|
|
43
|
+
[e]: r.reliability[e] + 1
|
|
44
|
+
} })),
|
|
45
|
+
resetReliabilityStats: () => t(() => ({ reliability: o() }))
|
|
36
46
|
};
|
|
37
47
|
}
|
|
38
48
|
export {
|
|
39
49
|
n as createQueueActions,
|
|
40
|
-
|
|
50
|
+
l as createReliabilityActions,
|
|
51
|
+
s as createResourceActions
|
|
41
52
|
};
|
|
42
53
|
|
|
43
54
|
//# sourceMappingURL=store-slices.js.map
|
package/dist/store-slices.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-slices.js","names":[],"sources":["../src/store-slices.ts"],"sourcesContent":["import type { ResourceEntry, ActionQueueItem } from './types';\nimport type { EidosStore } from './store';\n\ntype Setter = (updater: (prev: EidosStore) => Partial<EidosStore>) => void;\n\n// ── Resource slice ────────────────────────────────────────────────────────────\n\nexport interface ResourceActions {\n registerResource: (url: string, entry: ResourceEntry) => void;\n updateResource: (url: string, update: Partial<ResourceEntry>) => void;\n unregisterResource: (url: string) => void;\n}\n\nexport function createResourceActions(set: Setter): ResourceActions {\n return {\n registerResource: (url, entry) => set((s) => ({ resources: { ...s.resources, [url]: entry } })),\n\n updateResource: (url, update) =>\n set((s) => ({\n resources: {\n ...s.resources,\n [url]: s.resources[url] ? { ...s.resources[url], ...update } : s.resources[url],\n },\n })),\n\n unregisterResource: (url) =>\n set((s) => ({\n resources: Object.fromEntries(Object.entries(s.resources).filter(([k]) => k !== url)),\n })),\n };\n}\n\n// ── Queue slice ───────────────────────────────────────────────────────────────\n\nexport interface QueueActions {\n addQueueItem: (item: ActionQueueItem) => void;\n updateQueueItem: (id: string, update: Partial<ActionQueueItem>) => void;\n batchUpdateQueueItems: (updates: Array<{ id: string; update: Partial<ActionQueueItem> }>) => void;\n removeQueueItem: (id: string) => void;\n hydrateQueue: (items: ActionQueueItem[]) => void;\n}\n\nexport function createQueueActions(set: Setter): QueueActions {\n return {\n addQueueItem: (item) => set((s) => ({ queue: [...s.queue, item] })),\n\n updateQueueItem: (id, update) =>\n set((s) => ({\n queue: s.queue.map((item) => (item.id === id ? { ...item, ...update } : item)),\n })),\n\n batchUpdateQueueItems: (updates) =>\n set((s) => {\n const map = new Map(updates.map((u) => [u.id, u.update]));\n return {\n queue: s.queue.map((item) => {\n const u = map.get(item.id);\n return u ? { ...item, ...u } : item;\n }),\n };\n }),\n\n removeQueueItem: (id) => set((s) => ({ queue: s.queue.filter((item) => item.id !== id) })),\n\n hydrateQueue: (items) => set(() => ({ queue: items })),\n };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"store-slices.js","names":[],"sources":["../src/store-slices.ts"],"sourcesContent":["import type { ResourceEntry, ActionQueueItem, ReliabilityStats } from './types';\nimport { emptyReliabilityStats } from './types';\nimport type { EidosStore } from './store';\n\ntype Setter = (updater: (prev: EidosStore) => Partial<EidosStore>) => void;\n\n// ── Resource slice ────────────────────────────────────────────────────────────\n\nexport interface ResourceActions {\n registerResource: (url: string, entry: ResourceEntry) => void;\n updateResource: (url: string, update: Partial<ResourceEntry>) => void;\n unregisterResource: (url: string) => void;\n}\n\nexport function createResourceActions(set: Setter): ResourceActions {\n return {\n registerResource: (url, entry) => set((s) => ({ resources: { ...s.resources, [url]: entry } })),\n\n updateResource: (url, update) =>\n set((s) => ({\n resources: {\n ...s.resources,\n [url]: s.resources[url] ? { ...s.resources[url], ...update } : s.resources[url],\n },\n })),\n\n unregisterResource: (url) =>\n set((s) => ({\n resources: Object.fromEntries(Object.entries(s.resources).filter(([k]) => k !== url)),\n })),\n };\n}\n\n// ── Queue slice ───────────────────────────────────────────────────────────────\n\nexport interface QueueActions {\n addQueueItem: (item: ActionQueueItem) => void;\n updateQueueItem: (id: string, update: Partial<ActionQueueItem>) => void;\n batchUpdateQueueItems: (updates: Array<{ id: string; update: Partial<ActionQueueItem> }>) => void;\n removeQueueItem: (id: string) => void;\n hydrateQueue: (items: ActionQueueItem[]) => void;\n}\n\nexport function createQueueActions(set: Setter): QueueActions {\n return {\n addQueueItem: (item) => set((s) => ({ queue: [...s.queue, item] })),\n\n updateQueueItem: (id, update) =>\n set((s) => ({\n queue: s.queue.map((item) => (item.id === id ? { ...item, ...update } : item)),\n })),\n\n batchUpdateQueueItems: (updates) =>\n set((s) => {\n const map = new Map(updates.map((u) => [u.id, u.update]));\n return {\n queue: s.queue.map((item) => {\n const u = map.get(item.id);\n return u ? { ...item, ...u } : item;\n }),\n };\n }),\n\n removeQueueItem: (id) => set((s) => ({ queue: s.queue.filter((item) => item.id !== id) })),\n\n hydrateQueue: (items) => set(() => ({ queue: items })),\n };\n}\n\n// ── Reliability slice ─────────────────────────────────────────────────────────\n\nexport interface ReliabilityActions {\n recordReliabilityEvent: (event: keyof ReliabilityStats) => void;\n resetReliabilityStats: () => void;\n}\n\nexport function createReliabilityActions(set: Setter): ReliabilityActions {\n return {\n recordReliabilityEvent: (event) =>\n set((s) => ({ reliability: { ...s.reliability, [event]: s.reliability[event] + 1 } })),\n\n resetReliabilityStats: () => set(() => ({ reliability: emptyReliabilityStats() })),\n };\n}\n"],"mappings":";AAcA,SAAgB,EAAsB,GAA8B;AAClE,SAAO;AAAA,IACL,kBAAA,CAAmB,GAAK,MAAU,EAAA,CAAK,OAAO,EAAE,WAAW;AAAA,MAAE,GAAG,EAAE;AAAA,OAAY,CAAA,GAAM;AAAA,IAAM,EAAE,EAAE;AAAA,IAE9F,gBAAA,CAAiB,GAAK,MACpB,EAAA,CAAK,OAAO,EACV,WAAW;AAAA,MACT,GAAG,EAAE;AAAA,OACJ,CAAA,GAAM,EAAE,UAAU,CAAA,IAAO;AAAA,QAAE,GAAG,EAAE,UAAU,CAAA;AAAA,QAAM,GAAG;AAAA,MAAO,IAAI,EAAE,UAAU,CAAA;AAAA,IAC7E,EACF,EAAE;AAAA,IAEJ,oBAAA,CAAqB,MACnB,EAAA,CAAK,OAAO,EACV,WAAW,OAAO,YAAY,OAAO,QAAQ,EAAE,SAAS,EAAE,OAAA,CAAQ,CAAC,CAAA,MAAO,MAAM,CAAG,CAAC,EACtF,EAAE;AAAA,EACN;AACF;AAYA,SAAgB,EAAmB,GAA2B;AAC5D,SAAO;AAAA,IACL,cAAA,CAAe,MAAS,EAAA,CAAK,OAAO,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,CAAI,EAAE,EAAE;AAAA,IAElE,iBAAA,CAAkB,GAAI,MACpB,EAAA,CAAK,OAAO,EACV,OAAO,EAAE,MAAM,IAAA,CAAK,MAAU,EAAK,OAAO,IAAK;AAAA,MAAE,GAAG;AAAA,MAAM,GAAG;AAAA,IAAO,IAAI,CAAK,EAC/E,EAAE;AAAA,IAEJ,uBAAA,CAAwB,MACtB,EAAA,CAAK,MAAM;AACT,YAAM,IAAM,IAAI,IAAI,EAAQ,IAAA,CAAK,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AACxD,aAAO,EACL,OAAO,EAAE,MAAM,IAAA,CAAK,MAAS;AAC3B,cAAM,IAAI,EAAI,IAAI,EAAK,EAAE;AACzB,eAAO,IAAI;AAAA,UAAE,GAAG;AAAA,UAAM,GAAG;AAAA,QAAE,IAAI;AAAA,MACjC,CAAC,EACH;AAAA,IACF,CAAC;AAAA,IAEH,iBAAA,CAAkB,MAAO,EAAA,CAAK,OAAO,EAAE,OAAO,EAAE,MAAM,OAAA,CAAQ,MAAS,EAAK,OAAO,CAAE,EAAE,EAAE;AAAA,IAEzF,cAAA,CAAe,MAAU,EAAA,OAAW,EAAE,OAAO,EAAM,EAAE;AAAA,EACvD;AACF;AASA,SAAgB,EAAyB,GAAiC;AACxE,SAAO;AAAA,IACL,wBAAA,CAAyB,MACvB,EAAA,CAAK,OAAO,EAAE,aAAa;AAAA,MAAE,GAAG,EAAE;AAAA,OAAc,CAAA,GAAQ,EAAE,YAAY,CAAA,IAAS;AAAA,IAAE,EAAE,EAAE;AAAA,IAEvF,uBAAA,MAA6B,EAAA,OAAW,EAAE,aAAa,EAAsB,EAAE,EAAE;AAAA,EACnF;AACF"}
|
package/dist/store.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { emptyReliabilityStats as s } from "./types.js";
|
|
2
|
+
import { createQueueActions as a, createReliabilityActions as u, createResourceActions as c } from "./store-slices.js";
|
|
3
|
+
var t, o = /* @__PURE__ */ new Set();
|
|
3
4
|
function r() {
|
|
4
|
-
|
|
5
|
+
o.forEach((e) => e());
|
|
5
6
|
}
|
|
6
|
-
function
|
|
7
|
+
function i(e) {
|
|
7
8
|
t = {
|
|
8
9
|
...t,
|
|
9
10
|
...e(t)
|
|
@@ -15,35 +16,37 @@ t = {
|
|
|
15
16
|
swError: void 0,
|
|
16
17
|
resources: {},
|
|
17
18
|
queue: [],
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
reliability: s(),
|
|
20
|
+
setOnline: (e) => i(() => ({ isOnline: e })),
|
|
21
|
+
setSwStatus: (e, n) => i(() => ({
|
|
20
22
|
swStatus: e,
|
|
21
|
-
swError:
|
|
23
|
+
swError: n
|
|
22
24
|
})),
|
|
23
|
-
...i
|
|
24
|
-
...
|
|
25
|
+
...c(i),
|
|
26
|
+
...a(i),
|
|
27
|
+
...u(i)
|
|
25
28
|
};
|
|
26
|
-
function
|
|
29
|
+
function f() {
|
|
27
30
|
return t;
|
|
28
31
|
}
|
|
29
|
-
function
|
|
30
|
-
return
|
|
31
|
-
|
|
32
|
+
function d(e) {
|
|
33
|
+
return o.add(e), () => {
|
|
34
|
+
o.delete(e);
|
|
32
35
|
};
|
|
33
36
|
}
|
|
34
|
-
var
|
|
35
|
-
getState:
|
|
36
|
-
subscribe:
|
|
37
|
+
var b = {
|
|
38
|
+
getState: f,
|
|
39
|
+
subscribe: d,
|
|
37
40
|
setState: (e) => {
|
|
38
|
-
const
|
|
41
|
+
const n = typeof e == "function" ? e(t) : e;
|
|
39
42
|
t = {
|
|
40
43
|
...t,
|
|
41
|
-
...
|
|
44
|
+
...n
|
|
42
45
|
}, r();
|
|
43
46
|
}
|
|
44
47
|
};
|
|
45
48
|
export {
|
|
46
|
-
|
|
49
|
+
b as useEidosStore
|
|
47
50
|
};
|
|
48
51
|
|
|
49
52
|
//# sourceMappingURL=store.js.map
|