@sweidos/eidos 1.0.22 → 1.0.24

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.
@@ -0,0 +1,98 @@
1
+ import { EidosState, ReplayResult } from '@sweidos/eidos';
2
+
3
+ export interface MockOfflineOptions {
4
+ /**
5
+ * Also stub `globalThis.fetch` to throw a TypeError (simulates a hard
6
+ * network failure for code that calls fetch directly, outside the Eidos
7
+ * resource layer). Default: false.
8
+ */
9
+ stubFetch?: boolean;
10
+ }
11
+ /**
12
+ * Put Eidos into offline mode. Actions with `reliability: 'neverLose'` will
13
+ * be queued instead of executed. Call `mockOnline()` to restore.
14
+ *
15
+ * @example
16
+ * beforeEach(() => mockOffline())
17
+ * afterEach(() => mockOnline())
18
+ */
19
+ export declare function mockOffline(options?: MockOfflineOptions): void;
20
+ /**
21
+ * Restore online mode. Removes any fetch stub installed by `mockOffline`.
22
+ */
23
+ export declare function mockOnline(): void;
24
+ /**
25
+ * Replay the action queue immediately and return the result.
26
+ * Forces `isOnline = true` first so the replay is never skipped.
27
+ *
28
+ * @example
29
+ * mockOffline()
30
+ * await savePost(draft) // queued
31
+ * const result = await drainQueue()
32
+ * expect(result.succeeded).toBe(1)
33
+ */
34
+ export declare function drainQueue(): Promise<ReplayResult>;
35
+ export interface WaitForQueueDrainOptions {
36
+ /** Maximum wait in milliseconds. Default: 5000. */
37
+ timeout?: number;
38
+ /** Poll interval in milliseconds. Default: 50. */
39
+ interval?: number;
40
+ }
41
+ /**
42
+ * Wait until the action queue contains no pending or replaying items.
43
+ * Resolves immediately if the queue is already clear.
44
+ * Rejects with a timeout error if items remain after `timeout` ms.
45
+ *
46
+ * @example
47
+ * mockOnline()
48
+ * await waitForQueueDrain()
49
+ * expect(getEidosState().queue).toHaveLength(0)
50
+ */
51
+ export declare function waitForQueueDrain(options?: WaitForQueueDrainOptions): Promise<void>;
52
+ /** Default Eidos cache namespace written by the service worker. */
53
+ export declare const EIDOS_CACHE_NAME = "eidos-resources-v1";
54
+ /**
55
+ * Read a cached Response for a URL from Eidos Cache Storage.
56
+ * Returns `undefined` if the entry is not cached.
57
+ *
58
+ * Requires the Cache API (`caches` global). In jsdom, provide a mock (e.g.
59
+ * `vitest-fetch-mock`, `jest-fetch-mock`, or a custom `setupFiles` polyfill).
60
+ *
61
+ * @example
62
+ * const res = await getCachedEntry('/api/user/1')
63
+ * expect(res).toBeDefined()
64
+ * expect(await res!.json()).toMatchObject({ id: 1 })
65
+ */
66
+ export declare function getCachedEntry(url: string, cacheName?: string): Promise<Response | undefined>;
67
+ /**
68
+ * Delete all entries from an Eidos cache namespace.
69
+ * Call in `afterEach` to prevent cache state leaking between tests.
70
+ *
71
+ * @example
72
+ * afterEach(() => clearEidosCache())
73
+ */
74
+ export declare function clearEidosCache(cacheName?: string): Promise<void>;
75
+ /**
76
+ * Full reset — call in `beforeEach` to start each test from a clean slate.
77
+ *
78
+ * - Resets runtime initialisation flag (`_resetEidos`) so `initEidos()` can
79
+ * be called again without the duplicate-init guard firing.
80
+ * - Clears IDB action queue and in-memory store queue.
81
+ * - Restores online state and removes any fetch stub from `mockOffline`.
82
+ * - Clears registered resource entries and resets SW status.
83
+ *
84
+ * @example
85
+ * beforeEach(async () => {
86
+ * await resetEidos()
87
+ * })
88
+ */
89
+ export declare function resetEidos(): Promise<void>;
90
+ /**
91
+ * Return a plain-object snapshot of the current Eidos store state.
92
+ * Useful for assertions without importing `useEidosStore` directly.
93
+ *
94
+ * @example
95
+ * expect(getEidosState().isOnline).toBe(false)
96
+ * expect(getEidosState().queue).toHaveLength(1)
97
+ */
98
+ export declare function getEidosState(): EidosState;
@@ -0,0 +1,86 @@
1
+ import { useEidosStore, replayQueue, _resetEidos, clearQueue } from "@sweidos/eidos";
2
+ let _originalFetch = null;
3
+ function mockOffline(options = {}) {
4
+ useEidosStore.getState().setOnline(false);
5
+ if (options.stubFetch && _originalFetch === null) {
6
+ _originalFetch = globalThis.fetch;
7
+ globalThis.fetch = () => Promise.reject(
8
+ new TypeError("Network request failed (stubbed by @sweidos/eidos/testing)")
9
+ );
10
+ }
11
+ }
12
+ function mockOnline() {
13
+ useEidosStore.getState().setOnline(true);
14
+ if (_originalFetch !== null) {
15
+ globalThis.fetch = _originalFetch;
16
+ _originalFetch = null;
17
+ }
18
+ }
19
+ async function drainQueue() {
20
+ useEidosStore.getState().setOnline(true);
21
+ return replayQueue();
22
+ }
23
+ function waitForQueueDrain(options = {}) {
24
+ const timeout = options.timeout ?? 5e3;
25
+ const interval = options.interval ?? 50;
26
+ const deadline = Date.now() + timeout;
27
+ return new Promise((resolve, reject) => {
28
+ function check() {
29
+ const { queue } = useEidosStore.getState();
30
+ const active = queue.filter(
31
+ (q) => q.status === "pending" || q.status === "replaying"
32
+ );
33
+ if (active.length === 0) {
34
+ resolve();
35
+ return;
36
+ }
37
+ if (Date.now() >= deadline) {
38
+ reject(
39
+ new Error(
40
+ `[eidos/testing] waitForQueueDrain timed out after ${timeout}ms. ${active.length} item(s) still active (statuses: ${active.map((q) => q.status).join(", ")}).`
41
+ )
42
+ );
43
+ return;
44
+ }
45
+ setTimeout(check, interval);
46
+ }
47
+ check();
48
+ });
49
+ }
50
+ const EIDOS_CACHE_NAME = "eidos-resources-v1";
51
+ async function getCachedEntry(url, cacheName = EIDOS_CACHE_NAME) {
52
+ if (typeof caches === "undefined") return void 0;
53
+ const cache = await caches.open(cacheName);
54
+ const match = await cache.match(url);
55
+ return match ?? void 0;
56
+ }
57
+ async function clearEidosCache(cacheName = EIDOS_CACHE_NAME) {
58
+ if (typeof caches === "undefined") return;
59
+ await caches.delete(cacheName);
60
+ }
61
+ async function resetEidos() {
62
+ _resetEidos();
63
+ await clearQueue();
64
+ mockOnline();
65
+ useEidosStore.setState((s) => ({
66
+ ...s,
67
+ resources: {},
68
+ swStatus: "idle",
69
+ swError: void 0
70
+ }));
71
+ }
72
+ function getEidosState() {
73
+ const { isOnline, swStatus, swError, resources, queue } = useEidosStore.getState();
74
+ return { isOnline, swStatus, swError, resources, queue };
75
+ }
76
+ export {
77
+ EIDOS_CACHE_NAME,
78
+ clearEidosCache,
79
+ drainQueue,
80
+ getCachedEntry,
81
+ getEidosState,
82
+ mockOffline,
83
+ mockOnline,
84
+ resetEidos,
85
+ waitForQueueDrain
86
+ };
package/dist/version.js CHANGED
@@ -1,4 +1,4 @@
1
- const o = "1.0.12";
1
+ const o = "1.0.24";
2
2
  export {
3
3
  o as VERSION
4
4
  };
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sources":["../src/version.ts"],"sourcesContent":["export const VERSION = '1.0.12'\n"],"names":["VERSION"],"mappings":"AAAO,MAAMA,IAAU;"}
1
+ {"version":3,"file":"version.js","sources":["../src/version.ts"],"sourcesContent":["export const VERSION = '1.0.24'\n"],"names":["VERSION"],"mappings":"AAAO,MAAMA,IAAU;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sweidos/eidos",
3
- "version": "1.0.22",
3
+ "version": "1.0.24",
4
4
  "description": "Describe intent. The runtime figures out how. An abstraction layer for offline-first web apps.",
5
5
  "author": "Aditya Raj",
6
6
  "license": "MIT",
@@ -43,6 +43,11 @@
43
43
  "import": "./dist/query.js",
44
44
  "require": "./dist/query.cjs.js",
45
45
  "types": "./dist/query.d.ts"
46
+ },
47
+ "./testing": {
48
+ "import": "./dist/testing.js",
49
+ "require": "./dist/testing.cjs.js",
50
+ "types": "./dist/testing.d.ts"
46
51
  }
47
52
  },
48
53
  "files": [
@@ -83,7 +88,7 @@
83
88
  "vitest": "^2.1.9"
84
89
  },
85
90
  "scripts": {
86
- "build": "vite build && vite build --config vite.cjs.config.ts && vite build --config vite.plugin.config.ts && vite build --config vite.query.config.ts && node scripts/copy-sw.mjs",
91
+ "build": "vite build && vite build --config vite.cjs.config.ts && vite build --config vite.plugin.config.ts && vite build --config vite.query.config.ts && vite build --config vite.testing.config.ts && node scripts/copy-sw.mjs",
87
92
  "dev": "vite build --watch",
88
93
  "type-check": "tsc --noEmit",
89
94
  "test": "vitest run",