@sweidos/eidos 1.0.34 → 1.1.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 +96 -89
- package/dist/action.js +119 -86
- package/dist/async-storage-adapter.js +15 -12
- package/dist/devtools.js +953 -555
- package/dist/eidos.cjs +15 -0
- package/dist/idb.js +59 -56
- package/dist/index.d.ts +37 -15
- package/dist/index.js +42 -41
- package/dist/nextjs.js +1 -10
- package/dist/query.cjs +131 -0
- package/dist/query.js +121 -41
- package/dist/queue-storage.js +5 -4
- package/dist/react/Devtools.d.ts +1 -1
- package/dist/react/Provider.js +11 -7
- package/dist/react/hooks.js +48 -38
- package/dist/react-native.js +47 -53
- package/dist/replay.js +15 -0
- package/dist/resource.js +77 -79
- package/dist/runtime.js +22 -28
- package/dist/store-slices.js +43 -0
- package/dist/store.js +32 -49
- package/dist/stores.js +25 -22
- package/dist/sveltekit.js +22 -6
- package/dist/sw-bridge.js +48 -46
- package/dist/testing.cjs +165 -0
- package/dist/testing.js +140 -70
- package/dist/version.js +4 -3
- package/dist/vite.cjs +48 -0
- package/dist/vite.js +45 -29
- package/package.json +48 -27
- package/dist/action.js.map +0 -1
- package/dist/async-storage-adapter.js.map +0 -1
- package/dist/eidos.cjs.js +0 -14
- package/dist/eidos.cjs.js.map +0 -1
- package/dist/idb.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/query.cjs.js +0 -48
- package/dist/queue-storage.js.map +0 -1
- package/dist/react/Provider.js.map +0 -1
- package/dist/react/hooks.js.map +0 -1
- package/dist/resource.js.map +0 -1
- package/dist/runtime.js.map +0 -1
- package/dist/store.js.map +0 -1
- package/dist/stores.js.map +0 -1
- package/dist/sw-bridge.js.map +0 -1
- package/dist/testing.cjs.js +0 -86
- package/dist/version.js.map +0 -1
- package/dist/vite.cjs.js +0 -31
package/dist/runtime.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","sources":["../src/runtime.ts"],"sourcesContent":["import { registerServiceWorker, registerBgSyncHandler } from './sw-bridge'\nimport { replayQueue } from './action'\nimport { useEidosStore } from './store'\nimport { idbGetQueue } from './idb'\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\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 useEidosStore.getState().hydrateQueue(persisted)\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 // ── Subscribe to the store instead of window.addEventListener('online')\n //\n // WHY: setOfflineSimulation() updates the store directly but never fires a\n // real browser `online` event. Watching the store catches both:\n // • Real network reconnects (sw-bridge updates store on window.online)\n // • Simulation toggled off (setOfflineSimulation(false) → store.setOnline(true))\n //\n let prevIsOnline = useEidosStore.getState().isOnline\n\n _unsubscribe = useEidosStore.subscribe(() => {\n const { isOnline } = useEidosStore.getState()\n const justCameOnline = isOnline && !prevIsOnline\n prevIsOnline = isOnline\n\n if (justCameOnline) {\n // Small delay so the connection (or simulation reset) settles first\n setTimeout(replayQueue, 600)\n }\n })\n\n // Replay any pending items that survived a page reload.\n // 'failed' items have already exhausted maxRetries and are never\n // re-replayed (see _doReplayQueue), so they don't count here.\n const store = useEidosStore.getState()\n const hasPending = store.queue.some((q) => q.status === 'pending')\n if (store.isOnline && hasPending) {\n setTimeout(replayQueue, 1200)\n }\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 _initialized = false\n}\n"],"names":["_initialized","_unsubscribe","initEidos","config","swPath","autoReplay","persisted","idbGetQueue","useEidosStore","registerServiceWorker","registerBgSyncHandler","replayQueue","prevIsOnline","isOnline","justCameOnline","store","hasPending","q","_resetEidos"],"mappings":";;;;AAYA,IAAIA,IAAe,IACfC,IAAoC;AAExC,eAAsBC,EAAUC,IAAsB,IAAmB;AAGvE,MADI,OAAO,SAAW,OAClBH,EAAc;AAClB,EAAAA,IAAe;AAEf,QAAMI,IAASD,EAAO,UAAU,gBAC1BE,IAAaF,EAAO,cAAc;AAGxC,MAAI;AACF,UAAMG,IAAY,MAAMC,EAAA;AACxB,IAAID,EAAU,SAAS,KACrBE,EAAc,SAAA,EAAW,aAAaF,CAAS;AAAA,EAEnD,QAAQ;AAAA,EAER;AAEA,MAAI;AACF,UAAMG,EAAsBL,CAAM;AAAA,EACpC,QAAQ;AAAA,EAER;AAWA,MANAM,EAAsB,MAAM;AAC1B,IAAIF,EAAc,SAAA,EAAW,YAC3B,WAAWG,GAAa,GAAG;AAAA,EAE/B,CAAC,GAEGN,GAAY;AAQd,QAAIO,IAAeJ,EAAc,SAAA,EAAW;AAE5C,IAAAP,IAAeO,EAAc,UAAU,MAAM;AAC3C,YAAM,EAAE,UAAAK,EAAA,IAAaL,EAAc,SAAA,GAC7BM,IAAiBD,KAAY,CAACD;AACpC,MAAAA,IAAeC,GAEXC,KAEF,WAAWH,GAAa,GAAG;AAAA,IAE/B,CAAC;AAKD,UAAMI,IAAQP,EAAc,SAAA,GACtBQ,IAAaD,EAAM,MAAM,KAAK,CAACE,MAAMA,EAAE,WAAW,SAAS;AACjE,IAAIF,EAAM,YAAYC,KACpB,WAAWL,GAAa,IAAI;AAAA,EAEhC;AAUF;AAEO,SAASO,IAAc;AAC5B,EAAAjB,KAAA,QAAAA,KACAA,IAAe,MACfD,IAAe;AACjB;"}
|
package/dist/store.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sources":["../src/store.ts"],"sourcesContent":["import type { EidosState, ResourceEntry, ActionQueueItem } from './types'\n\nexport interface EidosStore extends EidosState {\n // Online\n setOnline: (online: boolean) => void\n // SW\n setSwStatus: (status: EidosState['swStatus'], error?: string) => void\n // Resources\n registerResource: (url: string, entry: ResourceEntry) => void\n updateResource: (url: string, update: Partial<ResourceEntry>) => void\n unregisterResource: (url: string) => void\n // Queue\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\ntype Listener = () => void\n\nlet _state: EidosStore\nconst _listeners = new Set<Listener>()\n\nfunction _notify() {\n _listeners.forEach((fn) => fn())\n}\n\nfunction _set(updater: (prev: EidosStore) => Partial<EidosStore>) {\n _state = { ..._state, ...updater(_state) }\n _notify()\n}\n\n_state = {\n // navigator.onLine is undefined in React Native — default to true unless explicitly false\n isOnline: typeof navigator === 'undefined' || navigator.onLine !== false,\n swStatus: 'idle',\n swError: undefined,\n resources: {},\n queue: [],\n\n setOnline: (isOnline) => _set(() => ({ isOnline })),\n\n setSwStatus: (swStatus, swError) => _set(() => ({ swStatus, swError })),\n\n registerResource: (url, entry) =>\n _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(\n Object.entries(s.resources).filter(([k]) => k !== url),\n ),\n })),\n\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\nfunction _getState() {\n return _state\n}\n\nfunction _subscribe(listener: Listener) {\n _listeners.add(listener)\n return () => { _listeners.delete(listener) }\n}\n\nexport const useEidosStore = {\n getState: _getState,\n subscribe: _subscribe,\n // Test/devtools helper — merges partial state, preserves action methods.\n setState: (partial: Partial<EidosStore> | ((s: EidosStore) => Partial<EidosStore>)) => {\n const update = typeof partial === 'function' ? partial(_state) : partial\n _state = { ..._state, ...update }\n _notify()\n },\n}\n"],"names":["_state","_listeners","_notify","fn","_set","updater","isOnline","swStatus","swError","url","entry","s","update","k","item","id","updates","map","u","items","_getState","_subscribe","listener","useEidosStore","partial"],"mappings":"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"}
|
package/dist/stores.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"stores.js","sources":["../src/stores.ts"],"sourcesContent":["/**\n * Framework-agnostic reactive stores — compatible with Svelte's store protocol,\n * Vue's watchEffect, RxJS, and vanilla JS. Zero framework dependencies.\n *\n * Svelte: use the `$` prefix — `$eidosQueue`, `$eidosStatus`, etc.\n * Vue: call `.subscribe()` inside a composable with `onUnmounted` cleanup.\n * Vanilla: call `.subscribe(run)` directly; the return value unsubscribes.\n *\n * Each store calls its subscriber whenever any part of the Eidos state changes.\n * For fine-grained subscriptions, use `.getState()` to read the current snapshot\n * and compare manually in the subscriber callback.\n */\n\nimport { useEidosStore } from './store'\nimport type { EidosStore } from './store'\nimport type { ActionQueueItem, ResourceEntry } from './types'\n\n// ── Readable<T> — compatible with Svelte's Readable interface ─────────────────\n\nexport interface EidosReadable<T> {\n /** Subscribe to value changes. Returns an unsubscribe function. */\n subscribe(run: (value: T) => void): () => void\n /** Read the current value synchronously without subscribing. */\n getState(): T\n}\n\nfunction shallowEqual<T extends Record<string, unknown>>(a: T, b: T): boolean {\n const keys = Object.keys(a) as (keyof T)[]\n if (keys.length !== Object.keys(b).length) return false\n for (const k of keys) if (a[k] !== b[k]) return false\n return true\n}\n\n// Typed comparator alias so call sites don't need inline casts.\nfunction shallowEq<T extends Record<string, unknown>>(a: T, b: T): boolean {\n return shallowEqual(a, b)\n}\n\nfunction readable<T>(\n selector: (s: EidosStore) => T,\n equal: (a: T, b: T) => boolean = Object.is,\n): EidosReadable<T> {\n return {\n subscribe(run) {\n // Emit current value immediately (Svelte store contract)\n let last = selector(useEidosStore.getState())\n run(last)\n return useEidosStore.subscribe(() => {\n const next = selector(useEidosStore.getState())\n if (!equal(last, next)) {\n last = next\n run(next)\n }\n })\n },\n getState() {\n return selector(useEidosStore.getState())\n },\n }\n}\n\n// ── Static stores (created once at module scope) ──────────────────────────────\n\n/** Full Eidos state snapshot. Prefer the narrower stores below. */\nexport const eidosStore: EidosReadable<EidosStore> = readable((s) => s)\n\n/** The action queue. Re-notifies on every queue mutation. */\nexport const eidosQueue: EidosReadable<ActionQueueItem[]> = readable((s) => s.queue)\n\n/**\n * Online status + SW lifecycle.\n * Only re-emits when isOnline, swStatus, or swError actually changes.\n */\nexport const eidosStatus: EidosReadable<{\n isOnline: boolean\n swStatus: EidosStore['swStatus']\n swError: string | undefined\n}> = readable(\n (s) => ({ isOnline: s.isOnline, swStatus: s.swStatus, swError: s.swError }),\n shallowEq,\n)\n\n/**\n * Queue counts. Re-emits only when a count actually changes, not on every\n * queue mutation (e.g. a status transition that doesn't change counts is skipped).\n */\nexport const eidosQueueStats: EidosReadable<{\n pending: number\n failed: number\n replaying: number\n total: number\n}> = readable(\n (s) => {\n // Single pass over the queue — avoids three separate .filter() calls.\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, total: s.queue.length }\n },\n shallowEq,\n)\n\n// ── Dynamic stores (created per URL / ID) ─────────────────────────────────────\n\n/**\n * Live cache state for a single registered resource URL.\n * @example\n * // Svelte\n * const entry = eidosResource('/api/products')\n * $: hits = $entry?.cacheHits ?? 0\n */\nexport function eidosResource(url: string): EidosReadable<ResourceEntry | undefined> {\n return readable((s) => s.resources[url])\n}\n\n/**\n * Live state for a single queue item by ID. Returns `undefined` once the item\n * is removed from the queue (after a successful replay or `clearQueue()`).\n * @example\n * // Svelte\n * const item = eidosAction(queuedResult.id)\n * $: status = $item?.status // 'pending' | 'replaying' | 'succeeded' | 'failed' | undefined\n */\nexport function eidosAction(id: string): EidosReadable<ActionQueueItem | undefined> {\n return readable((s) => s.queue.find((item) => item.id === id))\n}\n"],"names":["shallowEqual","a","b","keys","k","shallowEq","readable","selector","equal","run","last","useEidosStore","next","eidosStore","s","eidosQueue","eidosStatus","eidosQueueStats","pending","failed","replaying","q","eidosResource","url","eidosAction","id","item"],"mappings":";AA0BA,SAASA,EAAgDC,GAAMC,GAAe;AAC5E,QAAMC,IAAO,OAAO,KAAKF,CAAC;AAC1B,MAAIE,EAAK,WAAW,OAAO,KAAKD,CAAC,EAAE,OAAQ,QAAO;AAClD,aAAWE,KAAKD,EAAM,KAAIF,EAAEG,CAAC,MAAMF,EAAEE,CAAC,EAAG,QAAO;AAChD,SAAO;AACT;AAGA,SAASC,EAA6CJ,GAAMC,GAAe;AACzE,SAAOF,EAAaC,GAAGC,CAAC;AAC1B;AAEA,SAASI,EACPC,GACAC,IAAiC,OAAO,IACtB;AAClB,SAAO;AAAA,IACL,UAAUC,GAAK;AAEb,UAAIC,IAAOH,EAASI,EAAc,SAAA,CAAU;AAC5C,aAAAF,EAAIC,CAAI,GACDC,EAAc,UAAU,MAAM;AACnC,cAAMC,IAAOL,EAASI,EAAc,SAAA,CAAU;AAC9C,QAAKH,EAAME,GAAME,CAAI,MACnBF,IAAOE,GACPH,EAAIG,CAAI;AAAA,MAEZ,CAAC;AAAA,IACH;AAAA,IACA,WAAW;AACT,aAAOL,EAASI,EAAc,UAAU;AAAA,IAC1C;AAAA,EAAA;AAEJ;AAKO,MAAME,IAAwCP,EAAS,CAACQ,MAAMA,CAAC,GAGzDC,IAA+CT,EAAS,CAACQ,MAAMA,EAAE,KAAK,GAMtEE,IAIRV;AAAA,EACH,CAACQ,OAAO,EAAE,UAAUA,EAAE,UAAU,UAAUA,EAAE,UAAU,SAASA,EAAE,QAAA;AAAA,EACjET;AACF,GAMaY,IAKRX;AAAA,EACH,CAACQ,MAAM;AAEL,QAAII,IAAU,GAAGC,IAAS,GAAGC,IAAY;AACzC,eAAWC,KAAKP,EAAE;AAChB,MAAIO,EAAE,WAAW,YAAWH,MACnBG,EAAE,WAAW,WAAUF,MACvBE,EAAE,WAAW,eAAaD;AAErC,WAAO,EAAE,SAAAF,GAAS,QAAAC,GAAQ,WAAAC,GAAW,OAAON,EAAE,MAAM,OAAA;AAAA,EACtD;AAAA,EACAT;AACF;AAWO,SAASiB,EAAcC,GAAuD;AACnF,SAAOjB,EAAS,CAACQ,MAAMA,EAAE,UAAUS,CAAG,CAAC;AACzC;AAUO,SAASC,EAAYC,GAAwD;AAClF,SAAOnB,EAAS,CAACQ,MAAMA,EAAE,MAAM,KAAK,CAACY,MAASA,EAAK,OAAOD,CAAE,CAAC;AAC/D;"}
|
package/dist/sw-bridge.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sw-bridge.js","sources":["../src/sw-bridge.ts"],"sourcesContent":["import { useEidosStore } from './store'\n\nlet _registration: ServiceWorkerRegistration | null = null\n// Messages sent before the SW activates are buffered here and flushed once\n// the SW is ready. Covers resource registrations, cache clears, offline\n// simulation — anything sent at module scope before EidosProvider mounts.\nlet _pendingMessages: Record<string, unknown>[] = []\n\nexport function getSwRegistration() {\n return _registration\n}\n\nexport async function registerServiceWorker(swPath: string): Promise<void> {\n if (typeof navigator === 'undefined' || !('serviceWorker' in navigator)) {\n useEidosStore.getState().setSwStatus('unsupported')\n return\n }\n\n const store = useEidosStore.getState()\n store.setSwStatus('registering')\n\n try {\n _registration = await navigator.serviceWorker.register(swPath, { scope: '/' })\n\n await waitForActivation(_registration)\n\n store.setSwStatus('active')\n\n // Receive messages from SW\n navigator.serviceWorker.addEventListener('message', onSwMessage)\n\n // Track online/offline\n window.addEventListener('online', () => store.setOnline(true))\n window.addEventListener('offline', () => store.setOnline(false))\n\n flushPendingMessages()\n } catch (err) {\n store.setSwStatus('error', String(err))\n }\n}\n\nfunction waitForActivation(reg: ServiceWorkerRegistration): Promise<void> {\n return new Promise((resolve) => {\n if (reg.active) { resolve(); return }\n const sw = reg.installing ?? reg.waiting\n if (!sw) { resolve(); return }\n\n // Resolve after 10s regardless — another tab may be blocking activation\n const timer = setTimeout(resolve, 10_000)\n\n sw.addEventListener('statechange', function handler() {\n if (sw.state === 'activated') {\n clearTimeout(timer)\n sw.removeEventListener('statechange', handler)\n resolve()\n }\n })\n })\n}\n\nexport function sendToWorker(message: Record<string, unknown>): void {\n const sw = _registration?.active\n if (sw) {\n sw.postMessage(message)\n } else {\n _pendingMessages.push(message)\n }\n}\n\nlet _bgSyncHandler: (() => void) | null = null\n\nexport function registerBgSyncHandler(fn: () => void): void {\n _bgSyncHandler = fn\n}\n\nexport function isBgSyncSupported(): boolean {\n try {\n return (\n typeof navigator !== 'undefined' &&\n 'serviceWorker' in navigator &&\n _registration !== null &&\n 'sync' in _registration\n )\n } catch {\n return false\n }\n}\n\nfunction onSwMessage(event: MessageEvent): void {\n const data = event.data as { type: string; url?: string; strategy?: string }\n if (!data?.type) return\n\n const store = useEidosStore.getState()\n const { type, url } = data\n\n if (type === 'EIDOS_BACKGROUND_SYNC') {\n _bgSyncHandler?.()\n return\n }\n\n if (!url) return\n\n switch (type) {\n case 'EIDOS_CACHE_HIT': {\n const current = store.resources[url]\n store.updateResource(url, {\n status: 'fresh',\n lastEvent: 'cache-hit',\n cacheHits: (current?.cacheHits ?? 0) + 1,\n })\n break\n }\n case 'EIDOS_CACHE_UPDATED': {\n store.updateResource(url, {\n status: 'fresh',\n lastEvent: 'cache-updated',\n cachedAt: Date.now(),\n })\n break\n }\n case 'EIDOS_NETWORK_ERROR': {\n store.updateResource(url, {\n status: 'error',\n lastEvent: 'network-error',\n })\n break\n }\n }\n}\n\nexport function setOfflineSimulation(enabled: boolean): void {\n sendToWorker({ type: 'EIDOS_SIMULATE_OFFLINE', enabled })\n useEidosStore.getState().setOnline(!enabled)\n}\n\nfunction flushPendingMessages(): void {\n const sw = _registration?.active\n if (!sw) return\n for (const msg of _pendingMessages) sw.postMessage(msg)\n _pendingMessages = []\n}\n"],"names":["_registration","_pendingMessages","getSwRegistration","registerServiceWorker","swPath","useEidosStore","store","waitForActivation","onSwMessage","flushPendingMessages","err","reg","resolve","sw","timer","handler","sendToWorker","message","_bgSyncHandler","registerBgSyncHandler","fn","isBgSyncSupported","event","data","type","url","current","setOfflineSimulation","enabled","msg"],"mappings":"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"}
|
package/dist/testing.cjs.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const eidos = require("@sweidos/eidos");
|
|
4
|
-
let _originalFetch = null;
|
|
5
|
-
function mockOffline(options = {}) {
|
|
6
|
-
eidos.useEidosStore.getState().setOnline(false);
|
|
7
|
-
if (options.stubFetch && _originalFetch === null) {
|
|
8
|
-
_originalFetch = globalThis.fetch;
|
|
9
|
-
globalThis.fetch = () => Promise.reject(
|
|
10
|
-
new TypeError("Network request failed (stubbed by @sweidos/eidos/testing)")
|
|
11
|
-
);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
function mockOnline() {
|
|
15
|
-
eidos.useEidosStore.getState().setOnline(true);
|
|
16
|
-
if (_originalFetch !== null) {
|
|
17
|
-
globalThis.fetch = _originalFetch;
|
|
18
|
-
_originalFetch = null;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
async function drainQueue() {
|
|
22
|
-
eidos.useEidosStore.getState().setOnline(true);
|
|
23
|
-
return eidos.replayQueue();
|
|
24
|
-
}
|
|
25
|
-
function waitForQueueDrain(options = {}) {
|
|
26
|
-
const timeout = options.timeout ?? 5e3;
|
|
27
|
-
const interval = options.interval ?? 50;
|
|
28
|
-
const deadline = Date.now() + timeout;
|
|
29
|
-
return new Promise((resolve, reject) => {
|
|
30
|
-
function check() {
|
|
31
|
-
const { queue } = eidos.useEidosStore.getState();
|
|
32
|
-
const active = queue.filter(
|
|
33
|
-
(q) => q.status === "pending" || q.status === "replaying"
|
|
34
|
-
);
|
|
35
|
-
if (active.length === 0) {
|
|
36
|
-
resolve();
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
if (Date.now() >= deadline) {
|
|
40
|
-
reject(
|
|
41
|
-
new Error(
|
|
42
|
-
`[eidos/testing] waitForQueueDrain timed out after ${timeout}ms. ${active.length} item(s) still active (statuses: ${active.map((q) => q.status).join(", ")}).`
|
|
43
|
-
)
|
|
44
|
-
);
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
setTimeout(check, interval);
|
|
48
|
-
}
|
|
49
|
-
check();
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
const EIDOS_CACHE_NAME = "eidos-resources-v1";
|
|
53
|
-
async function getCachedEntry(url, cacheName = EIDOS_CACHE_NAME) {
|
|
54
|
-
if (typeof caches === "undefined") return void 0;
|
|
55
|
-
const cache = await caches.open(cacheName);
|
|
56
|
-
const match = await cache.match(url);
|
|
57
|
-
return match ?? void 0;
|
|
58
|
-
}
|
|
59
|
-
async function clearEidosCache(cacheName = EIDOS_CACHE_NAME) {
|
|
60
|
-
if (typeof caches === "undefined") return;
|
|
61
|
-
await caches.delete(cacheName);
|
|
62
|
-
}
|
|
63
|
-
async function resetEidos() {
|
|
64
|
-
eidos._resetEidos();
|
|
65
|
-
await eidos.clearQueue();
|
|
66
|
-
mockOnline();
|
|
67
|
-
eidos.useEidosStore.setState((s) => ({
|
|
68
|
-
...s,
|
|
69
|
-
resources: {},
|
|
70
|
-
swStatus: "idle",
|
|
71
|
-
swError: void 0
|
|
72
|
-
}));
|
|
73
|
-
}
|
|
74
|
-
function getEidosState() {
|
|
75
|
-
const { isOnline, swStatus, swError, resources, queue } = eidos.useEidosStore.getState();
|
|
76
|
-
return { isOnline, swStatus, swError, resources, queue };
|
|
77
|
-
}
|
|
78
|
-
exports.EIDOS_CACHE_NAME = EIDOS_CACHE_NAME;
|
|
79
|
-
exports.clearEidosCache = clearEidosCache;
|
|
80
|
-
exports.drainQueue = drainQueue;
|
|
81
|
-
exports.getCachedEntry = getCachedEntry;
|
|
82
|
-
exports.getEidosState = getEidosState;
|
|
83
|
-
exports.mockOffline = mockOffline;
|
|
84
|
-
exports.mockOnline = mockOnline;
|
|
85
|
-
exports.resetEidos = resetEidos;
|
|
86
|
-
exports.waitForQueueDrain = waitForQueueDrain;
|
package/dist/version.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sources":["../src/version.ts"],"sourcesContent":["export const VERSION = '1.0.34'\n"],"names":["VERSION"],"mappings":"AAAO,MAAMA,IAAU;"}
|
package/dist/vite.cjs.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const fs = require("fs");
|
|
4
|
-
const path = require("path");
|
|
5
|
-
function eidos(options) {
|
|
6
|
-
const swDest = (options == null ? void 0 : options.swDest) ?? "public/eidos-sw.js";
|
|
7
|
-
function copySW(root) {
|
|
8
|
-
const src = path.resolve(root, "node_modules/@sweidos/eidos/dist/eidos-sw.js");
|
|
9
|
-
if (!fs.existsSync(src)) {
|
|
10
|
-
console.warn(
|
|
11
|
-
"[eidos-vite] Could not locate eidos-sw.js in node_modules. Make sure @sweidos/eidos is installed."
|
|
12
|
-
);
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
const dest = path.resolve(root, swDest);
|
|
16
|
-
const destDir = path.dirname(dest);
|
|
17
|
-
if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
|
|
18
|
-
fs.copyFileSync(src, dest);
|
|
19
|
-
console.log(`[eidos-vite] eidos-sw.js → ${swDest} ✓`);
|
|
20
|
-
}
|
|
21
|
-
return {
|
|
22
|
-
name: "eidos",
|
|
23
|
-
buildStart() {
|
|
24
|
-
copySW(process.cwd());
|
|
25
|
-
},
|
|
26
|
-
configureServer(server) {
|
|
27
|
-
copySW(server.config.root);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
exports.eidos = eidos;
|