@sweidos/eidos 1.0.19 → 1.0.22
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 +22 -1
- package/dist/action.js +103 -0
- package/dist/action.js.map +1 -0
- package/dist/eidos.cjs.js +4 -752
- package/dist/eidos.cjs.js.map +1 -1
- package/dist/idb.js +87 -0
- package/dist/idb.js.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/react/Provider.js +12 -0
- package/dist/react/Provider.js.map +1 -0
- package/dist/react/hooks.js +42 -0
- package/dist/react/hooks.js.map +1 -0
- package/dist/resource.js +200 -0
- package/dist/resource.js.map +1 -0
- package/dist/runtime.js +34 -0
- package/dist/runtime.js.map +1 -0
- package/dist/store.js +55 -0
- package/dist/store.js.map +1 -0
- package/dist/stores.js +36 -0
- package/dist/stores.js.map +1 -0
- package/dist/sw-bridge.js +105 -0
- package/dist/sw-bridge.js.map +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/package.json +10 -4
- package/dist/eidos.es.js +0 -762
- package/dist/eidos.es.js.map +0 -1
package/README.md
CHANGED
|
@@ -419,13 +419,15 @@ Performance is a first-class concern in Eidos. Every design decision optimises f
|
|
|
419
419
|
| **Network timeout** | 3 s | `NetworkFirst` strategy aborts fetch after 3 s and falls back to cache — no hanging requests |
|
|
420
420
|
| **Pre-activation buffer** | Zero drops | Messages sent before the SW is active are buffered and flushed on activation |
|
|
421
421
|
| **Concurrency safety** | Lock-guarded | `_replaying` flag prevents duplicate replay passes from concurrent online events |
|
|
422
|
+
| **Request deduplication** | 1 request / N callers | Concurrent `handle.fetch()` calls for the same URL share one in-flight network request; each caller gets a cloned `Response` |
|
|
422
423
|
|
|
423
424
|
### Bundle comparison
|
|
424
425
|
|
|
425
426
|
| Version | Raw | Gzip | Change |
|
|
426
427
|
|---------|-----|------|--------|
|
|
427
428
|
| 1.0.5 (with zustand) | 35.0 kB | 7.9 kB | — |
|
|
428
|
-
|
|
|
429
|
+
| 1.0.6 (zero deps) | 18.6 kB | 5.0 kB | −47% |
|
|
430
|
+
| **1.0.21** (minified + dedup) | **19.0 kB** | **5.8 kB** | +0.5% raw vs 1.0.6 (dedup code), smaller than zustand baseline |
|
|
429
431
|
|
|
430
432
|
---
|
|
431
433
|
|
|
@@ -608,6 +610,7 @@ Registers a `QueryClient` with Eidos. After calling this:
|
|
|
608
610
|
| Query string ignored | Resources match by pathname (or full URL for cross-origin). `/api/products?page=2` and `/api/products` share the same SW rule but are cached as separate entries. |
|
|
609
611
|
| Module-scope actions | `action()` must be called at module scope so functions are registered before a page reload triggers queue replay. |
|
|
610
612
|
| Single SW | `EidosProvider` assumes one SW at `/eidos-sw.js`. Multiple registrations are unsupported. |
|
|
613
|
+
| React in main bundle | ~~Fixed in v1.0.22~~ — ESM output uses `preserveModules`; Vue/Svelte/vanilla consumers only pull in the modules they import. React is isolated to `dist/react/hooks.js` and `dist/react/Provider.js`. |
|
|
611
614
|
|
|
612
615
|
---
|
|
613
616
|
|
|
@@ -624,6 +627,24 @@ Registers a `QueryClient` with Eidos. After calling this:
|
|
|
624
627
|
- [x] Vue / Svelte bindings (framework-agnostic reactive stores)
|
|
625
628
|
- [x] TanStack Query integration (`@sweidos/eidos/query` subpath — `useEidosQuery`, `useEidosMutation`, `withEidosQueryClient`)
|
|
626
629
|
|
|
630
|
+
**Core reliability**
|
|
631
|
+
- [ ] Optimistic updates — `onOptimistic` / `onRollback` callbacks on `action()` for instant UI feedback before server confirms
|
|
632
|
+
- [ ] Conflict resolution hook — `onConflict` callback when replaying a queued action returns 4xx; decide per-item: retry, skip, or merge
|
|
633
|
+
- [ ] Queue prioritization — `priority: 'high' | 'normal' | 'low'` on `action()`; high-priority items replay first
|
|
634
|
+
|
|
635
|
+
**DX / Tooling**
|
|
636
|
+
- [ ] Devtools panel component — drop-in `<EidosDevtools />` showing cache entries, queue state, replay status, and offline toggle
|
|
637
|
+
- [ ] Testing utilities (`@sweidos/eidos/testing`) — `mockOffline()`, `drainQueue()`, `getCachedEntry(url)` for Vitest / Playwright
|
|
638
|
+
- [ ] SvelteKit / Next.js adapters — SSR-aware init helpers that skip SW registration server-side
|
|
639
|
+
|
|
640
|
+
**Performance**
|
|
641
|
+
- [x] Request deduplication — multiple simultaneous `resource.fetch()` calls share one in-flight network request; each caller gets an independent cloned `Response`
|
|
642
|
+
- [ ] Cache warming — `warmCache(handles[])` bulk-prefetches a list of resources on init (e.g. on login)
|
|
643
|
+
|
|
644
|
+
**Ecosystem**
|
|
645
|
+
- [ ] React Native support — AsyncStorage + fetch-based backend (no Cache API / SW); same `resource` / `action` API surface
|
|
646
|
+
- [ ] OpenAPI codegen CLI — `npx eidos-gen ./openapi.json` generates typed `resource()` and `action()` declarations
|
|
647
|
+
|
|
627
648
|
---
|
|
628
649
|
|
|
629
650
|
## Contributing
|
package/dist/action.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { useEidosStore as l } from "./store.js";
|
|
2
|
+
import { getSwRegistration as g } from "./sw-bridge.js";
|
|
3
|
+
import { idbClearQueue as Q, idbGetPendingItems as b, idbUpdateQueueItem as c, idbRemoveFromQueue as I, idbAddToQueue as R } from "./idb.js";
|
|
4
|
+
const w = /* @__PURE__ */ new Map();
|
|
5
|
+
function m() {
|
|
6
|
+
return crypto.randomUUID();
|
|
7
|
+
}
|
|
8
|
+
function v(t, n) {
|
|
9
|
+
const r = n.name || t.name || m();
|
|
10
|
+
w.set(r, t);
|
|
11
|
+
const u = async (...a) => {
|
|
12
|
+
const { isOnline: i } = l.getState();
|
|
13
|
+
if (n.reliability === "neverLose") {
|
|
14
|
+
if (!i)
|
|
15
|
+
return f(r, r, a, n);
|
|
16
|
+
try {
|
|
17
|
+
return await t(...a);
|
|
18
|
+
} catch {
|
|
19
|
+
return f(r, r, a, n);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return t(...a);
|
|
23
|
+
};
|
|
24
|
+
return Object.defineProperty(u, "id", { value: r, writable: !1 }), Object.defineProperty(u, "config", { value: n, writable: !1 }), u;
|
|
25
|
+
}
|
|
26
|
+
async function f(t, n, r, u) {
|
|
27
|
+
const a = m(), i = {
|
|
28
|
+
id: a,
|
|
29
|
+
actionId: t,
|
|
30
|
+
actionName: n,
|
|
31
|
+
args: r,
|
|
32
|
+
queuedAt: Date.now(),
|
|
33
|
+
retryCount: 0,
|
|
34
|
+
maxRetries: u.maxRetries ?? 3,
|
|
35
|
+
status: "pending"
|
|
36
|
+
};
|
|
37
|
+
await R(i), l.getState().addQueueItem(i);
|
|
38
|
+
try {
|
|
39
|
+
const e = g();
|
|
40
|
+
e && "sync" in e && await e.sync.register("eidos-queue-replay");
|
|
41
|
+
} catch {
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
queued: !0,
|
|
45
|
+
id: a,
|
|
46
|
+
message: `"${n}" queued — will execute when online`
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function S(t) {
|
|
50
|
+
return Math.min(2e3 * 2 ** t, 3e5) * (0.8 + Math.random() * 0.4);
|
|
51
|
+
}
|
|
52
|
+
let p = !1;
|
|
53
|
+
async function D() {
|
|
54
|
+
const t = l.getState();
|
|
55
|
+
if (!t.isOnline || p)
|
|
56
|
+
return { attempted: 0, succeeded: 0, failed: 0, retrying: 0, skipped: 0 };
|
|
57
|
+
p = !0;
|
|
58
|
+
try {
|
|
59
|
+
return await x(t);
|
|
60
|
+
} finally {
|
|
61
|
+
p = !1;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async function x(t) {
|
|
65
|
+
const n = await b(), r = Date.now(), u = n.filter(
|
|
66
|
+
(e) => !e.nextRetryAt || e.nextRetryAt <= r
|
|
67
|
+
), a = { attempted: 0, succeeded: 0, failed: 0, retrying: 0, skipped: 0 }, i = await Promise.allSettled(
|
|
68
|
+
u.map(async (e) => {
|
|
69
|
+
const d = w.get(e.actionId);
|
|
70
|
+
if (!d) return "skipped";
|
|
71
|
+
t.updateQueueItem(e.id, { status: "replaying" }), await c(e.id, { status: "replaying" });
|
|
72
|
+
try {
|
|
73
|
+
await d(...e.args);
|
|
74
|
+
const o = Date.now();
|
|
75
|
+
return t.updateQueueItem(e.id, { status: "succeeded", completedAt: o }), await c(e.id, { status: "succeeded", completedAt: o }), setTimeout(() => {
|
|
76
|
+
t.removeQueueItem(e.id), I(e.id);
|
|
77
|
+
}, 3e3), "succeeded";
|
|
78
|
+
} catch (o) {
|
|
79
|
+
const s = e.retryCount + 1;
|
|
80
|
+
if (s >= e.maxRetries)
|
|
81
|
+
return t.updateQueueItem(e.id, { status: "failed", error: String(o), retryCount: s }), await c(e.id, { status: "failed", error: String(o), retryCount: s }), "failed";
|
|
82
|
+
{
|
|
83
|
+
const y = Date.now() + S(s);
|
|
84
|
+
return t.updateQueueItem(e.id, { status: "pending", retryCount: s, nextRetryAt: y }), await c(e.id, { status: "pending", retryCount: s, nextRetryAt: y }), "retrying";
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
);
|
|
89
|
+
for (const e of i) {
|
|
90
|
+
const d = e.status === "fulfilled" ? e.value : "failed";
|
|
91
|
+
d === "skipped" ? a.skipped++ : (a.attempted++, a[d]++);
|
|
92
|
+
}
|
|
93
|
+
return a;
|
|
94
|
+
}
|
|
95
|
+
async function q() {
|
|
96
|
+
await Q(), l.getState().hydrateQueue([]);
|
|
97
|
+
}
|
|
98
|
+
export {
|
|
99
|
+
v as action,
|
|
100
|
+
q as clearQueue,
|
|
101
|
+
D as replayQueue
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=action.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action.js","sources":["../src/action.ts"],"sourcesContent":["import { useEidosStore } from './store'\nimport { getSwRegistration } from './sw-bridge'\nimport {\n idbAddToQueue,\n idbGetPendingItems,\n idbUpdateQueueItem,\n idbRemoveFromQueue,\n idbClearQueue,\n} from './idb'\nimport type {\n ActionConfig,\n ActionHandle,\n ActionFn,\n ActionQueueItem,\n QueuedResult,\n ReplayResult,\n} from './types'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst _actionRegistry = new Map<string, ActionFn<any[], any>>()\n\nfunction uid() {\n return crypto.randomUUID()\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function action<TArgs extends any[], TReturn>(\n fn: ActionFn<TArgs, TReturn>,\n config: ActionConfig,\n): ActionHandle<TArgs, TReturn> {\n // || not ?? — fn.name can be '' (anonymous arrow fn) which ?? treats as a\n // valid value, causing all anonymous actions to share actionId ''.\n const actionId = config.name || fn.name || uid()\n\n if (import.meta.env.DEV && config.reliability === 'neverLose' && !config.name && !fn.name) {\n console.warn(\n `[eidos] action() registered with neverLose but no stable name was found (fn.name=\"${fn.name}\"). Pass config.name so queued items survive a page reload and can be replayed.`,\n )\n }\n\n // Registering here means the function is available for replay after\n // the user refreshes the page (actions are defined at module scope).\n _actionRegistry.set(actionId, fn as ActionFn<unknown[], unknown>)\n\n const wrapped = async (...args: TArgs): Promise<TReturn | QueuedResult> => {\n const { isOnline } = useEidosStore.getState()\n\n if (config.reliability === 'neverLose') {\n if (!isOnline) {\n return persistAndQueue(actionId, actionId, args, config)\n }\n // Online + neverLose: execute, queue on failure\n try {\n return await fn(...args)\n } catch {\n return persistAndQueue(actionId, actionId, args, config)\n }\n }\n\n // best-effort: execute directly, no queuing\n return fn(...args)\n }\n\n Object.defineProperty(wrapped, 'id', { value: actionId, writable: false })\n Object.defineProperty(wrapped, 'config', { value: config, writable: false })\n\n return wrapped as unknown as ActionHandle<TArgs, TReturn>\n}\n\nfunction isJsonSerializable(value: unknown): boolean {\n try {\n JSON.stringify(value)\n return true\n } catch {\n return false\n }\n}\n\nasync function persistAndQueue(\n actionId: string,\n actionName: string,\n args: unknown[],\n config: ActionConfig,\n): Promise<QueuedResult> {\n if (import.meta.env.DEV && !isJsonSerializable(args)) {\n console.warn(\n `[eidos] action \"${actionName}\" queued with non-JSON-serializable args. These args will be lost after a page reload. Use plain JSON values for neverLose actions.`,\n args,\n )\n }\n\n const id = uid()\n const item: ActionQueueItem = {\n id,\n actionId,\n actionName,\n args,\n queuedAt: Date.now(),\n retryCount: 0,\n maxRetries: config.maxRetries ?? 3,\n status: 'pending',\n }\n\n await idbAddToQueue(item)\n useEidosStore.getState().addQueueItem(item)\n\n // Register Background Sync tag so the browser can wake up open clients\n // when connectivity returns, even if the user navigated away briefly.\n // Graceful no-op when Background Sync is unsupported.\n try {\n const reg = getSwRegistration()\n if (reg && 'sync' in reg) {\n await (reg as unknown as { sync: { register(tag: string): Promise<void> } }).sync.register('eidos-queue-replay')\n }\n } catch {\n // Background Sync not available — online-event replay remains the fallback\n }\n\n return {\n queued: true,\n id,\n message: `\"${actionName}\" queued — will execute when online`,\n }\n}\n\n// Base delay 2s, doubles per retry, capped at 5 minutes, ±20% jitter\nfunction backoffMs(retryCount: number): number {\n const base = Math.min(2000 * 2 ** retryCount, 300_000)\n return base * (0.8 + Math.random() * 0.4)\n}\n\nlet _replaying = false\n\nexport async function replayQueue(): Promise<ReplayResult> {\n const store = useEidosStore.getState()\n if (!store.isOnline || _replaying) {\n return { attempted: 0, succeeded: 0, failed: 0, retrying: 0, skipped: 0 }\n }\n _replaying = true\n try {\n return await _doReplayQueue(store)\n } finally {\n _replaying = false\n }\n}\n\nasync function _doReplayQueue(store: ReturnType<typeof useEidosStore.getState>): Promise<ReplayResult> {\n\n const candidates = await idbGetPendingItems()\n const now = Date.now()\n const pending = candidates.filter(\n (item) => !item.nextRetryAt || item.nextRetryAt <= now,\n )\n\n const result: ReplayResult = { attempted: 0, succeeded: 0, failed: 0, retrying: 0, skipped: 0 }\n\n const outcomes = await Promise.allSettled(\n pending.map(async (item): Promise<'succeeded' | 'failed' | 'retrying' | 'skipped'> => {\n const fn = _actionRegistry.get(item.actionId)\n if (!fn) return 'skipped'\n\n store.updateQueueItem(item.id, { status: 'replaying' })\n await idbUpdateQueueItem(item.id, { status: 'replaying' })\n\n try {\n await fn(...(item.args as unknown[]))\n const completedAt = Date.now()\n store.updateQueueItem(item.id, { status: 'succeeded', completedAt })\n await idbUpdateQueueItem(item.id, { status: 'succeeded', completedAt })\n\n // Remove from queue after a delay so the UI can show the success state\n setTimeout(() => {\n store.removeQueueItem(item.id)\n idbRemoveFromQueue(item.id)\n }, 3000)\n return 'succeeded'\n } catch (err) {\n const retryCount = item.retryCount + 1\n if (retryCount >= item.maxRetries) {\n store.updateQueueItem(item.id, { status: 'failed', error: String(err), retryCount })\n await idbUpdateQueueItem(item.id, { status: 'failed', error: String(err), retryCount })\n return 'failed'\n } else {\n const nextRetryAt = Date.now() + backoffMs(retryCount)\n store.updateQueueItem(item.id, { status: 'pending', retryCount, nextRetryAt })\n await idbUpdateQueueItem(item.id, { status: 'pending', retryCount, nextRetryAt })\n return 'retrying'\n }\n }\n }),\n )\n\n for (const o of outcomes) {\n const outcome = o.status === 'fulfilled' ? o.value : 'failed'\n if (outcome === 'skipped') { result.skipped++ }\n else { result.attempted++; result[outcome]++ }\n }\n\n return result\n}\n\n/** Remove all items from the action queue (IDB + in-memory store). */\nexport async function clearQueue(): Promise<void> {\n await idbClearQueue()\n useEidosStore.getState().hydrateQueue([])\n}\n"],"names":["_actionRegistry","uid","action","fn","config","actionId","wrapped","args","isOnline","useEidosStore","persistAndQueue","actionName","id","item","idbAddToQueue","reg","getSwRegistration","backoffMs","retryCount","_replaying","replayQueue","store","_doReplayQueue","candidates","idbGetPendingItems","now","pending","result","outcomes","idbUpdateQueueItem","completedAt","idbRemoveFromQueue","err","nextRetryAt","o","outcome","clearQueue","idbClearQueue"],"mappings":"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"}
|