@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.
- package/README.md +91 -3
- package/dist/action.js +64 -58
- package/dist/action.js.map +1 -1
- package/dist/eidos.cjs.js +2 -2
- package/dist/eidos.cjs.js.map +1 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +23 -22
- package/dist/runtime.js +23 -19
- package/dist/runtime.js.map +1 -1
- package/dist/testing.cjs.js +86 -0
- package/dist/testing.d.ts +98 -0
- package/dist/testing.js +86 -0
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +7 -2
|
@@ -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;
|
package/dist/testing.js
ADDED
|
@@ -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
package/dist/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sources":["../src/version.ts"],"sourcesContent":["export const VERSION = '1.0.
|
|
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.
|
|
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",
|