@iadev93/zuno 0.0.6 → 0.0.7
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/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';function f(
|
|
1
|
+
'use strict';function f(r,o,n){let{clientId:t,versions:a}=n;if(o.origin!==t){if(typeof o.version=="number"){let i=a.get(o.storeKey)??0;if(o.version<=i)return;a.set(o.storeKey,o.version);}r.getStore(o.storeKey,()=>o.state).set(o.state);}}function h(r){let{url:o,syncUrl:n,universe:t,clientId:a,versions:i,getLastEventId:g}=r,v=null,d=0;function y(){let p=g(),l=new URL(o,globalThis.location?.href);p>0&&l.searchParams.set("lastEventId",String(p)),v=new EventSource(l.toString()),v.addEventListener("snapshot",S=>{try{let e=JSON.parse(S.data);for(let[s,c]of Object.entries(e)){let u=c;i.set(s,Math.max(i.get(s)??0,u.version)),t.getStore(s,()=>u.state).set(u.state);}}catch(e){console.error("[Zuno] Failed to parse snapshot",e);}}),v.addEventListener("state",S=>{try{let e=JSON.parse(S.data);if(e.origin===a)return;if(typeof e.version=="number"){let s=i.get(e.storeKey)??0;if(e.version<=s)return;i.set(e.storeKey,e.version);}t.getStore(e.storeKey,()=>e.state).set(e.state);}catch(e){console.error("[Zuno] Failed to parse SSE event",e);}}),v.onopen=()=>{d=0,r.onOpen?.();},v.onerror=()=>{v?.close(),r.onClose?.();let S=Math.min(1e3*Math.pow(2,d),3e4);d++,setTimeout(y,S);};}return y(),{dispatch:async p=>{try{r.optimistic&&t.getStore(p.storeKey,()=>p.state).set(p.state);let l=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(p)});if(l.status===409){let e=await l.json();if(e.current){let{state:s,version:c}=e.current;i.set(p.storeKey,c),t.getStore(p.storeKey,()=>s).set(s);}return {ok:!1,status:409,json:e,reason:"CONFLICT"}}if(!l.ok)return {ok:!1,status:l.status,json:await l.json()};let S=await l.json();if(S.event){let{state:e,version:s}=S.event;typeof s=="number"&&i.set(p.storeKey,s);}return {ok:!0,status:200,json:S}}catch(l){return {ok:false,status:500,json:l,reason:"NETWORK_ERROR"}}},unsubscribe:()=>{v?.close();}}}function T(r){let{channelName:o,clientId:n,onEvent:t,getSnapshot:a,onSnapshot:i}=r,g=new BroadcastChannel(o);return g.onmessage=v=>{let d=v.data;d.origin!==n&&(d.type==="event"&&t(d.event),d.type==="hello"&&g.postMessage({type:"snapshot",snapshot:a(),origin:n}),d.type==="snapshot"&&i(d.snapshot));},{publish:v=>g.postMessage({type:"event",event:v,origin:n}),hello:()=>g.postMessage({type:"hello",origin:n}),stop:()=>g.close()}}var m=r=>{let o=r,n=new Set;return {get:()=>o,set:t=>{let a=typeof t=="function"?t(o):t;Object.is(a,o)||(o=a,n.forEach(i=>i(o)));},subscribe:t=>(n.add(t),()=>n.delete(t))}},E=()=>{let r=new Map;return {getStore(n,t){return r.has(n)||r.set(n,m(t())),r.get(n)},snapshot(){let n={};for(let[t,a]of r.entries())n[t]=a.get();return n},restore(n){for(let[t,a]of Object.entries(n)){let i=r.get(t);i?i.set(a):r.set(t,m(a));}},delete(n){r.delete(n);},clear(){r.clear();},hydrateSnapshot(n){let t={};for(let[a,i]of Object.entries(n.state))t[a]=i.state;this.restore(t);}}},w=(r={})=>{let n=new Map,t=r.universe??E(),a=r.clientId??globalThis.crypto?.randomUUID?.()??String(Math.random()),i=false,g=0;function v(e){let s={};for(let[c,u]of Object.entries(e.state))s[c]=u.state,n.set(c,u.version);t.restore(s),g=e.lastEventId;}let d=e=>{typeof e.eventId=="number"&&(g=Math.max(g,e.eventId)),f(t,e,{clientId:a,versions:n});},y=r.sseUrl&&r.syncUrl?h({universe:t,url:r.sseUrl,syncUrl:r.syncUrl,optimistic:r.optimistic??true,clientId:a,versions:n,getLastEventId:()=>g,onOpen:()=>{i=true;},onClose:()=>{i=false;}}):null,p=r.channelName?T({channelName:r.channelName,clientId:a,onEvent:d,getSnapshot:()=>{let e=t.snapshot(),s={};for(let[c,u]of Object.entries(e))s[c]={state:u,version:n.get(c)??0};return s},onSnapshot:e=>{for(let[s,c]of Object.entries(e))d({storeKey:s,state:c.state,version:c.version});}}):null;setTimeout(()=>p?.hello(),0);let l=async e=>{if(y&&i)return await y.dispatch({...e,origin:a,baseVersion:n.get(e.storeKey)??0});let s=(n.get(e.storeKey)??0)+1;return d({...e,version:s}),n.set(e.storeKey,s),p&&p.publish({...e,version:s,origin:a}),{ok:true,status:200,json:null}};return {universe:t,clientId:a,store:(e,s)=>{let c=t.getStore(e,s);return {key:e,raw:()=>c,get:()=>c.get(),subscribe:u=>c.subscribe(u),set:u=>{let b=c.get(),Z=typeof u=="function"?u(b):u;return l({storeKey:e,state:Z})}}},getStore:t.getStore.bind(t),get:(e,s)=>t.getStore(e,s??(()=>{})).get(),set:async(e,s,c)=>{let u=t.getStore(e,c??(()=>{})),b=typeof s=="function"?s(u.get()):s;return l({storeKey:e,state:b})},subscribe:(e,s,c)=>t.getStore(e,s).subscribe(c),dispatch:l,stop:()=>{y?.unsubscribe?.(),p?.stop?.();},hydrateSnapshot:v,getLastEventId:()=>g,setLastEventId:e=>{g=e;}}};function I(r){return {getSnapshot:()=>r.get(),subscribe:o=>r.subscribe(()=>o())}}exports.applyIncomingEvent=f;exports.createStore=m;exports.createUniverse=E;exports.createZuno=w;exports.startBroadcastChannel=T;exports.startSSE=h;exports.toReadable=I;//# sourceMappingURL=index.cjs.map
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sync/index.ts","../src/core/index.ts","../src/shared/readable.ts"],"names":["applyIncomingEvent","universe","event","context","clientId","versions","current","startSSE","opts","url","syncUrl","getLastEventId","es","retryCount","connect","lastId","connectUrl","e","snap","key","rec","r","err","delay","res","data","state","version","startBroadcastChannel","channelName","onEvent","getSnapshot","onSnapshot","channel","msg","createStore","initial","listeners","next","value","l","listener","createUniverse","stores","init","out","store","existing","snapshot","plain","k","createZuno","sseReady","lastEventId","hydrateSnapshot","apply","sse","bc","storeKey","dispatch","nextVersion","rawStore","cb","prev","s","id","toReadable","onChange"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Universe } from \"../core\";\n\n// --- Types ---\n\n/**\n * Authoritative state event.\n */\nexport type ZunoStateEvent = {\n storeKey: string;\n state: any;\n version?: number;\n baseVersion?: number;\n origin?: string;\n ts?: number;\n eventId?: number;\n};\n\n/**\n * Generic transport status.\n */\nexport type TransportStatus = {\n ok: boolean;\n status: number;\n json: any;\n reason?: string;\n};\n\n/**\n * Client transport interface.\n */\nexport interface ZunoTransport {\n dispatch(event: ZunoStateEvent): Promise<TransportStatus>;\n unsubscribe?(): void;\n}\n\n/**\n * Apply incoming event to the universe and local bookkeeping.\n */\nexport function applyIncomingEvent(\n universe: Universe,\n event: ZunoStateEvent,\n context: {\n clientId: string;\n localState: Map<string, unknown>;\n versions: Map<string, number>;\n }\n) {\n const { clientId, versions } = context;\n\n // 1. Loopback suppression\n if (event.origin === clientId) return;\n\n // 2. Version check (if provided by transport)\n if (typeof event.version === \"number\") {\n const current = versions.get(event.storeKey) ?? 0;\n if (event.version <= current) return; // Stale\n versions.set(event.storeKey, event.version);\n }\n\n // 3. Apply to universe\n universe.getStore(event.storeKey, () => event.state).set(event.state);\n}\n\n// --- SSE Client ---\n\nexport type SSEOptions = {\n universe: Universe;\n url: string;\n syncUrl: string;\n optimistic: boolean;\n clientId: string;\n versions: Map<string, number>;\n getLastEventId: () => number;\n onOpen?: () => void;\n onClose?: () => void;\n};\n\nexport function startSSE(opts: SSEOptions): ZunoTransport {\n const { url, syncUrl, universe, clientId, versions, getLastEventId } = opts;\n let es: EventSource | null = null;\n let retryCount = 0;\n\n function connect() {\n const lastId = getLastEventId();\n const connectUrl = new URL(url, globalThis.location?.href);\n if (lastId > 0) connectUrl.searchParams.set(\"lastEventId\", String(lastId));\n\n es = new EventSource(connectUrl.toString());\n\n es.addEventListener(\"snapshot\", (e: any) => {\n try {\n const snap = JSON.parse(e.data);\n for (const [key, rec] of Object.entries(snap)) {\n const r = rec as { state: any; version: number };\n versions.set(key, Math.max(versions.get(key) ?? 0, r.version));\n universe.getStore(key, () => r.state).set(r.state);\n }\n } catch (err) {\n console.error(\"[Zuno] Failed to parse snapshot\", err);\n }\n });\n\n es.addEventListener(\"state\", (e: any) => {\n try {\n const event = JSON.parse(e.data) as ZunoStateEvent;\n if (event.origin === clientId) return;\n\n if (typeof event.version === \"number\") {\n const current = versions.get(event.storeKey) ?? 0;\n if (event.version <= current) return;\n versions.set(event.storeKey, event.version);\n }\n\n universe.getStore(event.storeKey, () => event.state).set(event.state);\n } catch (err) {\n console.error(\"[Zuno] Failed to parse SSE event\", err);\n }\n });\n\n es.onopen = () => {\n retryCount = 0;\n opts.onOpen?.();\n };\n\n es.onerror = () => {\n es?.close();\n opts.onClose?.();\n const delay = Math.min(1000 * Math.pow(2, retryCount), 30000);\n retryCount++;\n setTimeout(connect, delay);\n };\n }\n\n connect();\n\n return {\n dispatch: async (event) => {\n try {\n const res = await fetch(syncUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(event),\n });\n\n if (res.status === 409) {\n const data = await res.json();\n if (data.current) {\n const { state, version } = data.current;\n versions.set(event.storeKey, version);\n universe.getStore(event.storeKey, () => state).set(state);\n }\n return { ok: false, status: 409, json: data, reason: \"CONFLICT\" };\n }\n\n if (!res.ok) return { ok: false, status: res.status, json: await res.json() };\n\n return { ok: true, status: 200, json: await res.json() };\n } catch (err) {\n return { ok: false, status: 500, json: err, reason: \"NETWORK_ERROR\" };\n }\n },\n unsubscribe: () => {\n es?.close();\n },\n };\n}\n\n// --- BroadcastChannel ---\n\nexport type BCOptions = {\n channelName: string;\n clientId: string;\n onEvent: (event: ZunoStateEvent) => void;\n getSnapshot: () => Record<string, { state: unknown; version: number }>;\n onSnapshot: (snap: Record<string, { state: unknown; version: number }>) => void;\n};\n\nexport function startBroadcastChannel(opts: BCOptions) {\n const { channelName, clientId, onEvent, getSnapshot, onSnapshot } = opts;\n const channel = new BroadcastChannel(channelName);\n\n channel.onmessage = (e) => {\n const msg = e.data;\n if (msg.origin === clientId) return;\n\n if (msg.type === \"event\") onEvent(msg.event);\n if (msg.type === \"hello\") channel.postMessage({ type: \"snapshot\", snapshot: getSnapshot(), origin: clientId });\n if (msg.type === \"snapshot\") onSnapshot(msg.snapshot);\n };\n\n return {\n publish: (event: ZunoStateEvent) => channel.postMessage({ type: \"event\", event, origin: clientId }),\n hello: () => channel.postMessage({ type: \"hello\", origin: clientId }),\n stop: () => channel.close(),\n };\n}\n","import { startSSE, startBroadcastChannel, applyIncomingEvent } from \"../sync\";\nimport type { ZunoStateEvent } from \"../sync\";\n\n// --- Types ---\n\n/**\n * Authoritative snapshot of the entire universe.\n */\nexport type ZunoSnapshot = {\n state: Record<string, { state: unknown; version: number }>;\n lastEventId: number;\n};\n\n/**\n * A simple state container for a single keyed value.\n */\nexport interface Store<T> {\n get(): T;\n set(next: T | ((prev: T) => T)): void;\n subscribe(listener: (state: T) => void): () => void;\n}\n\n/**\n * A Universe manages many stores.\n */\nexport interface Universe {\n getStore<T>(key: string, init: () => T): Store<T>;\n snapshot(): Record<string, unknown>;\n restore(data: Record<string, unknown>): void;\n delete(key: string): void;\n clear(): void;\n hydrateSnapshot(snapshot: ZunoSnapshot): void;\n}\n\n/**\n * Options for creating a Zuno instance.\n */\nexport type CreateZunoOptions = {\n /** Optional pre-existing universe. */\n universe?: Universe;\n /** BroadcastChannel name for local tab sync. */\n channelName?: string;\n /** SSE endpoint URL. */\n sseUrl?: string;\n /** Sync endpoint URL (required if sseUrl is provided). */\n syncUrl?: string;\n /** Apply updates locally before server confirmation (default: true). */\n optimistic?: boolean;\n /** Unique client identifier (default: random UUID). */\n clientId?: string;\n};\n\n/**\n * An extended interface for a Zuno store that includes methods for setting state\n * and a unique key identifier. This represents a store that has been \"bound\" or registered.\n */\nexport type BoundStore<T> = {\n key: string;\n get: () => T;\n set: (next: T | ((prev: T) => T)) => Promise<any>;\n subscribe: (cb: (state: T) => void) => () => void;\n raw: () => Store<T>;\n};\n\n// --- Store Implementation ---\n\n/**\n * Creates a raw ZUNO state management store.\n */\nexport const createStore = <T>(initial: T): Store<T> => {\n let state = initial;\n const listeners = new Set<(state: T) => void>();\n\n return {\n get: () => state,\n set: (next) => {\n const value = typeof next === \"function\" ? (next as (prev: T) => T)(state) : next;\n if (Object.is(value, state)) return;\n state = value;\n listeners.forEach((l) => l(state));\n },\n subscribe: (listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n};\n\n// --- Universe Implementation ---\n\n/**\n * Creates a ZUNO Universe to manage multiple stores.\n */\nexport const createUniverse = (): Universe => {\n const stores = new Map<string, Store<any>>();\n\n const universe: Universe = {\n getStore<T>(key: string, init: () => T): Store<T> {\n if (!stores.has(key)) {\n stores.set(key, createStore(init()));\n }\n return stores.get(key)! as Store<T>;\n },\n snapshot(): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, store] of stores.entries()) {\n out[key] = store.get();\n }\n return out;\n },\n restore(data: Record<string, unknown>): void {\n for (const [key, value] of Object.entries(data)) {\n const existing = stores.get(key);\n if (existing) {\n existing.set(value as any);\n } else {\n stores.set(key, createStore(value as any));\n }\n }\n },\n delete(key: string): void {\n stores.delete(key);\n },\n clear(): void {\n stores.clear();\n },\n hydrateSnapshot(snapshot: ZunoSnapshot) {\n const plain: Record<string, unknown> = {};\n for (const [k, rec] of Object.entries(snapshot.state)) {\n plain[k] = rec.state;\n }\n this.restore(plain);\n },\n };\n\n return universe;\n};\n\n// --- Main Zuno Factory ---\n\n/**\n * Creates a Zuno instance for distributed state synchronization.\n */\nexport const createZuno = (opts: CreateZunoOptions = {}) => {\n const localState = new Map<string, unknown>();\n const versions = new Map<string, number>();\n const universe = opts.universe ?? createUniverse();\n const clientId = opts.clientId ?? (globalThis.crypto?.randomUUID?.() ?? String(Math.random()));\n let sseReady = false;\n let lastEventId = 0;\n\n function hydrateSnapshot(snapshot: ZunoSnapshot) {\n const plain: Record<string, unknown> = {};\n for (const [k, rec] of Object.entries(snapshot.state)) {\n plain[k] = rec.state;\n versions.set(k, rec.version);\n }\n universe.restore(plain);\n lastEventId = snapshot.lastEventId;\n }\n\n const apply = (event: ZunoStateEvent) => {\n if (typeof event.eventId === \"number\") {\n lastEventId = Math.max(lastEventId, event.eventId);\n }\n applyIncomingEvent(universe, event, { clientId, localState, versions });\n };\n\n const sse = opts.sseUrl && opts.syncUrl\n ? startSSE({\n universe,\n url: opts.sseUrl,\n syncUrl: opts.syncUrl,\n optimistic: opts.optimistic ?? true,\n clientId,\n versions,\n getLastEventId: () => lastEventId,\n onOpen: () => { sseReady = true; },\n onClose: () => { sseReady = false; },\n })\n : null;\n\n const bc = opts.channelName\n ? startBroadcastChannel({\n channelName: opts.channelName,\n clientId,\n onEvent: apply,\n getSnapshot: () => {\n const snap = universe.snapshot();\n const out: Record<string, { state: unknown; version: number }> = {};\n for (const [storeKey, state] of Object.entries(snap)) {\n out[storeKey] = { state, version: versions.get(storeKey) ?? 0 };\n }\n return out;\n },\n onSnapshot: (snap) => {\n for (const [storeKey, rec] of Object.entries(snap)) {\n apply({ storeKey, state: rec.state, version: rec.version });\n }\n },\n })\n : null;\n\n setTimeout(() => bc?.hello(), 0);\n\n const dispatch = async (event: ZunoStateEvent) => {\n if (sse && sseReady) {\n return await sse.dispatch({\n ...event,\n origin: clientId,\n baseVersion: versions.get(event.storeKey) ?? 0,\n });\n }\n\n const nextVersion = (versions.get(event.storeKey) ?? 0) + 1;\n apply({ ...event, version: nextVersion });\n versions.set(event.storeKey, nextVersion);\n\n if (bc) {\n bc.publish({ ...event, version: nextVersion, origin: clientId });\n }\n\n return { ok: true, status: 200, json: null };\n };\n\n const store = <T,>(storeKey: string, init: () => T): BoundStore<T> => {\n const rawStore = universe.getStore<T>(storeKey, init);\n return {\n key: storeKey,\n raw: () => rawStore,\n get: () => rawStore.get(),\n subscribe: (cb) => rawStore.subscribe(cb),\n set: (next) => {\n const prev = rawStore.get();\n const state = typeof next === \"function\" ? (next as (prev: T) => T)(prev) : next;\n return dispatch({ storeKey, state });\n },\n };\n };\n\n return {\n universe,\n clientId,\n store,\n getStore: universe.getStore.bind(universe),\n get: <T,>(key: string, init?: () => T) => universe.getStore<T>(key, init ?? (() => undefined as any)).get(),\n set: async <T,>(key: string, next: T | ((prev: T) => T), init?: () => T) => {\n const s = universe.getStore<T>(key, init ?? (() => undefined as any));\n const state = typeof next === \"function\" ? (next as (prev: T) => T)(s.get()) : next;\n return dispatch({ storeKey: key, state });\n },\n subscribe: <T,>(key: string, init: () => T, cb: (state: T) => void) => universe.getStore<T>(key, init).subscribe(cb),\n dispatch,\n stop: () => {\n sse?.unsubscribe?.();\n bc?.stop?.();\n },\n hydrateSnapshot,\n getLastEventId: () => lastEventId,\n setLastEventId: (id: number) => { lastEventId = id; },\n };\n};\n","/** Universal UI adapter contract */\nexport type ZunoReadable<T> = {\n /** Read current value (sync) */\n getSnapshot(): T;\n\n /**\n * Subscribe to changes.\n * Call `onChange()` whenever snapshot may have changed.\n * Return unsubscribe.\n */\n subscribe(onChange: () => void): () => void;\n\n /** Optional: React SSR (server snapshot) */\n getServerSnapshot?: () => T;\n};\n\n/** Minimal store shape that can be adapted into a readable */\nexport type ZunoSubscribableStore<T> = {\n get(): T;\n subscribe(cb: (state: T) => void): () => void;\n};\n\n/** Adapter helper: convert store => readable */\nexport function toReadable<T>(store: ZunoSubscribableStore<T>): ZunoReadable<T> {\n return {\n getSnapshot: () => store.get(),\n subscribe: (onChange) => store.subscribe(() => onChange()),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/sync/index.ts","../src/core/index.ts","../src/shared/readable.ts"],"names":["applyIncomingEvent","universe","event","context","clientId","versions","current","startSSE","opts","url","syncUrl","getLastEventId","es","retryCount","connect","lastId","connectUrl","e","snap","key","rec","r","err","delay","res","data","state","version","json","startBroadcastChannel","channelName","onEvent","getSnapshot","onSnapshot","channel","msg","createStore","initial","listeners","next","value","l","listener","createUniverse","stores","init","out","store","existing","snapshot","plain","k","createZuno","sseReady","lastEventId","hydrateSnapshot","apply","sse","bc","storeKey","dispatch","nextVersion","rawStore","cb","prev","s","id","toReadable","onChange"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Universe } from \"../core\";\n\n// --- Types ---\n\n/**\n * Authoritative state event.\n */\nexport type ZunoStateEvent = {\n storeKey: string;\n state: any;\n version?: number;\n baseVersion?: number;\n origin?: string;\n ts?: number;\n eventId?: number;\n};\n\n/**\n * Generic transport status.\n */\nexport type TransportStatus = {\n ok: boolean;\n status: number;\n json: any;\n reason?: string;\n};\n\n/**\n * Client transport interface.\n */\nexport interface ZunoTransport {\n dispatch(event: ZunoStateEvent): Promise<TransportStatus>;\n unsubscribe?(): void;\n}\n\n/**\n * Apply incoming event to the universe and local bookkeeping.\n */\nexport function applyIncomingEvent(\n universe: Universe,\n event: ZunoStateEvent,\n context: {\n clientId: string;\n localState: Map<string, unknown>;\n versions: Map<string, number>;\n }\n) {\n const { clientId, versions } = context;\n\n // 1. Loopback suppression\n if (event.origin === clientId) return;\n\n // 2. Version check (if provided by transport)\n if (typeof event.version === \"number\") {\n const current = versions.get(event.storeKey) ?? 0;\n if (event.version <= current) return; // Stale\n versions.set(event.storeKey, event.version);\n }\n\n // 3. Apply to universe\n universe.getStore(event.storeKey, () => event.state).set(event.state);\n}\n\n// --- SSE Client ---\n\nexport type SSEOptions = {\n universe: Universe;\n url: string;\n syncUrl: string;\n optimistic: boolean;\n clientId: string;\n versions: Map<string, number>;\n getLastEventId: () => number;\n onOpen?: () => void;\n onClose?: () => void;\n};\n\nexport function startSSE(opts: SSEOptions): ZunoTransport {\n const { url, syncUrl, universe, clientId, versions, getLastEventId } = opts;\n let es: EventSource | null = null;\n let retryCount = 0;\n\n function connect() {\n const lastId = getLastEventId();\n const connectUrl = new URL(url, globalThis.location?.href);\n if (lastId > 0) connectUrl.searchParams.set(\"lastEventId\", String(lastId));\n\n es = new EventSource(connectUrl.toString());\n\n es.addEventListener(\"snapshot\", (e: any) => {\n try {\n const snap = JSON.parse(e.data);\n for (const [key, rec] of Object.entries(snap)) {\n const r = rec as { state: any; version: number };\n versions.set(key, Math.max(versions.get(key) ?? 0, r.version));\n universe.getStore(key, () => r.state).set(r.state);\n }\n } catch (err) {\n console.error(\"[Zuno] Failed to parse snapshot\", err);\n }\n });\n\n es.addEventListener(\"state\", (e: any) => {\n try {\n const event = JSON.parse(e.data) as ZunoStateEvent;\n if (event.origin === clientId) return;\n\n if (typeof event.version === \"number\") {\n const current = versions.get(event.storeKey) ?? 0;\n if (event.version <= current) return;\n versions.set(event.storeKey, event.version);\n }\n\n universe.getStore(event.storeKey, () => event.state).set(event.state);\n } catch (err) {\n console.error(\"[Zuno] Failed to parse SSE event\", err);\n }\n });\n\n es.onopen = () => {\n retryCount = 0;\n opts.onOpen?.();\n };\n\n es.onerror = () => {\n es?.close();\n opts.onClose?.();\n const delay = Math.min(1000 * Math.pow(2, retryCount), 30000);\n retryCount++;\n setTimeout(connect, delay);\n };\n }\n\n connect();\n\n return {\n dispatch: async (event) => {\n try {\n if (opts.optimistic) {\n universe.getStore(event.storeKey, () => event.state).set(event.state);\n }\n\n const res = await fetch(syncUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(event),\n });\n\n if (res.status === 409) {\n const data = await res.json();\n if (data.current) {\n const { state, version } = data.current;\n versions.set(event.storeKey, version);\n universe.getStore(event.storeKey, () => state).set(state);\n }\n return { ok: false, status: 409, json: data, reason: \"CONFLICT\" };\n }\n\n if (!res.ok) return { ok: false, status: res.status, json: await res.json() };\n\n const json = await res.json();\n if (json.event) {\n const { state, version } = json.event;\n if (typeof version === \"number\") {\n versions.set(event.storeKey, version);\n }\n // We don't strictly need to set the store here because we already did it optimistically?\n // BUT, if the server modified it (e.g. server-side logic), we should.\n // However, for pure optimistic updates, we rely on the fact that we already set it.\n // The critical part is THE VERSION.\n // Let's just update the version to avoid the 409.\n }\n\n return { ok: true, status: 200, json };\n } catch (err) {\n return { ok: false, status: 500, json: err, reason: \"NETWORK_ERROR\" };\n }\n },\n unsubscribe: () => {\n es?.close();\n },\n };\n}\n\n// --- BroadcastChannel ---\n\nexport type BCOptions = {\n channelName: string;\n clientId: string;\n onEvent: (event: ZunoStateEvent) => void;\n getSnapshot: () => Record<string, { state: unknown; version: number }>;\n onSnapshot: (snap: Record<string, { state: unknown; version: number }>) => void;\n};\n\nexport function startBroadcastChannel(opts: BCOptions) {\n const { channelName, clientId, onEvent, getSnapshot, onSnapshot } = opts;\n const channel = new BroadcastChannel(channelName);\n\n channel.onmessage = (e) => {\n const msg = e.data;\n if (msg.origin === clientId) return;\n\n if (msg.type === \"event\") onEvent(msg.event);\n if (msg.type === \"hello\") channel.postMessage({ type: \"snapshot\", snapshot: getSnapshot(), origin: clientId });\n if (msg.type === \"snapshot\") onSnapshot(msg.snapshot);\n };\n\n return {\n publish: (event: ZunoStateEvent) => channel.postMessage({ type: \"event\", event, origin: clientId }),\n hello: () => channel.postMessage({ type: \"hello\", origin: clientId }),\n stop: () => channel.close(),\n };\n}\n","import { startSSE, startBroadcastChannel, applyIncomingEvent } from \"../sync\";\nimport type { ZunoStateEvent } from \"../sync\";\n\n// --- Types ---\n\n/**\n * Authoritative snapshot of the entire universe.\n */\nexport type ZunoSnapshot = {\n state: Record<string, { state: unknown; version: number }>;\n lastEventId: number;\n};\n\n/**\n * A simple state container for a single keyed value.\n */\nexport interface Store<T> {\n get(): T;\n set(next: T | ((prev: T) => T)): void;\n subscribe(listener: (state: T) => void): () => void;\n}\n\n/**\n * A Universe manages many stores.\n */\nexport interface Universe {\n getStore<T>(key: string, init: () => T): Store<T>;\n snapshot(): Record<string, unknown>;\n restore(data: Record<string, unknown>): void;\n delete(key: string): void;\n clear(): void;\n hydrateSnapshot(snapshot: ZunoSnapshot): void;\n}\n\n/**\n * Options for creating a Zuno instance.\n */\nexport type CreateZunoOptions = {\n /** Optional pre-existing universe. */\n universe?: Universe;\n /** BroadcastChannel name for local tab sync. */\n channelName?: string;\n /** SSE endpoint URL. */\n sseUrl?: string;\n /** Sync endpoint URL (required if sseUrl is provided). */\n syncUrl?: string;\n /** Apply updates locally before server confirmation (default: true). */\n optimistic?: boolean;\n /** Unique client identifier (default: random UUID). */\n clientId?: string;\n};\n\n/**\n * An extended interface for a Zuno store that includes methods for setting state\n * and a unique key identifier. This represents a store that has been \"bound\" or registered.\n */\nexport type BoundStore<T> = {\n key: string;\n get: () => T;\n set: (next: T | ((prev: T) => T)) => Promise<any>;\n subscribe: (cb: (state: T) => void) => () => void;\n raw: () => Store<T>;\n};\n\n// --- Store Implementation ---\n\n/**\n * Creates a raw ZUNO state management store.\n */\nexport const createStore = <T>(initial: T): Store<T> => {\n let state = initial;\n const listeners = new Set<(state: T) => void>();\n\n return {\n get: () => state,\n set: (next) => {\n const value = typeof next === \"function\" ? (next as (prev: T) => T)(state) : next;\n if (Object.is(value, state)) return;\n state = value;\n listeners.forEach((l) => l(state));\n },\n subscribe: (listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n};\n\n// --- Universe Implementation ---\n\n/**\n * Creates a ZUNO Universe to manage multiple stores.\n */\nexport const createUniverse = (): Universe => {\n const stores = new Map<string, Store<any>>();\n\n const universe: Universe = {\n getStore<T>(key: string, init: () => T): Store<T> {\n if (!stores.has(key)) {\n stores.set(key, createStore(init()));\n }\n return stores.get(key)! as Store<T>;\n },\n snapshot(): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, store] of stores.entries()) {\n out[key] = store.get();\n }\n return out;\n },\n restore(data: Record<string, unknown>): void {\n for (const [key, value] of Object.entries(data)) {\n const existing = stores.get(key);\n if (existing) {\n existing.set(value as any);\n } else {\n stores.set(key, createStore(value as any));\n }\n }\n },\n delete(key: string): void {\n stores.delete(key);\n },\n clear(): void {\n stores.clear();\n },\n hydrateSnapshot(snapshot: ZunoSnapshot) {\n const plain: Record<string, unknown> = {};\n for (const [k, rec] of Object.entries(snapshot.state)) {\n plain[k] = rec.state;\n }\n this.restore(plain);\n },\n };\n\n return universe;\n};\n\n// --- Main Zuno Factory ---\n\n/**\n * Creates a Zuno instance for distributed state synchronization.\n */\nexport const createZuno = (opts: CreateZunoOptions = {}) => {\n const localState = new Map<string, unknown>();\n const versions = new Map<string, number>();\n const universe = opts.universe ?? createUniverse();\n const clientId = opts.clientId ?? (globalThis.crypto?.randomUUID?.() ?? String(Math.random()));\n let sseReady = false;\n let lastEventId = 0;\n\n function hydrateSnapshot(snapshot: ZunoSnapshot) {\n const plain: Record<string, unknown> = {};\n for (const [k, rec] of Object.entries(snapshot.state)) {\n plain[k] = rec.state;\n versions.set(k, rec.version);\n }\n universe.restore(plain);\n lastEventId = snapshot.lastEventId;\n }\n\n const apply = (event: ZunoStateEvent) => {\n if (typeof event.eventId === \"number\") {\n lastEventId = Math.max(lastEventId, event.eventId);\n }\n applyIncomingEvent(universe, event, { clientId, localState, versions });\n };\n\n const sse = opts.sseUrl && opts.syncUrl\n ? startSSE({\n universe,\n url: opts.sseUrl,\n syncUrl: opts.syncUrl,\n optimistic: opts.optimistic ?? true,\n clientId,\n versions,\n getLastEventId: () => lastEventId,\n onOpen: () => { sseReady = true; },\n onClose: () => { sseReady = false; },\n })\n : null;\n\n const bc = opts.channelName\n ? startBroadcastChannel({\n channelName: opts.channelName,\n clientId,\n onEvent: apply,\n getSnapshot: () => {\n const snap = universe.snapshot();\n const out: Record<string, { state: unknown; version: number }> = {};\n for (const [storeKey, state] of Object.entries(snap)) {\n out[storeKey] = { state, version: versions.get(storeKey) ?? 0 };\n }\n return out;\n },\n onSnapshot: (snap) => {\n for (const [storeKey, rec] of Object.entries(snap)) {\n apply({ storeKey, state: rec.state, version: rec.version });\n }\n },\n })\n : null;\n\n setTimeout(() => bc?.hello(), 0);\n\n const dispatch = async (event: ZunoStateEvent) => {\n if (sse && sseReady) {\n return await sse.dispatch({\n ...event,\n origin: clientId,\n baseVersion: versions.get(event.storeKey) ?? 0,\n });\n }\n\n const nextVersion = (versions.get(event.storeKey) ?? 0) + 1;\n apply({ ...event, version: nextVersion });\n versions.set(event.storeKey, nextVersion);\n\n if (bc) {\n bc.publish({ ...event, version: nextVersion, origin: clientId });\n }\n\n return { ok: true, status: 200, json: null };\n };\n\n const store = <T,>(storeKey: string, init: () => T): BoundStore<T> => {\n const rawStore = universe.getStore<T>(storeKey, init);\n return {\n key: storeKey,\n raw: () => rawStore,\n get: () => rawStore.get(),\n subscribe: (cb) => rawStore.subscribe(cb),\n set: (next) => {\n const prev = rawStore.get();\n const state = typeof next === \"function\" ? (next as (prev: T) => T)(prev) : next;\n return dispatch({ storeKey, state });\n },\n };\n };\n\n return {\n universe,\n clientId,\n store,\n getStore: universe.getStore.bind(universe),\n get: <T,>(key: string, init?: () => T) => universe.getStore<T>(key, init ?? (() => undefined as any)).get(),\n set: async <T,>(key: string, next: T | ((prev: T) => T), init?: () => T) => {\n const s = universe.getStore<T>(key, init ?? (() => undefined as any));\n const state = typeof next === \"function\" ? (next as (prev: T) => T)(s.get()) : next;\n return dispatch({ storeKey: key, state });\n },\n subscribe: <T,>(key: string, init: () => T, cb: (state: T) => void) => universe.getStore<T>(key, init).subscribe(cb),\n dispatch,\n stop: () => {\n sse?.unsubscribe?.();\n bc?.stop?.();\n },\n hydrateSnapshot,\n getLastEventId: () => lastEventId,\n setLastEventId: (id: number) => { lastEventId = id; },\n };\n};\n","/** Universal UI adapter contract */\nexport type ZunoReadable<T> = {\n /** Read current value (sync) */\n getSnapshot(): T;\n\n /**\n * Subscribe to changes.\n * Call `onChange()` whenever snapshot may have changed.\n * Return unsubscribe.\n */\n subscribe(onChange: () => void): () => void;\n\n /** Optional: React SSR (server snapshot) */\n getServerSnapshot?: () => T;\n};\n\n/** Minimal store shape that can be adapted into a readable */\nexport type ZunoSubscribableStore<T> = {\n get(): T;\n subscribe(cb: (state: T) => void): () => void;\n};\n\n/** Adapter helper: convert store => readable */\nexport function toReadable<T>(store: ZunoSubscribableStore<T>): ZunoReadable<T> {\n return {\n getSnapshot: () => store.get(),\n subscribe: (onChange) => store.subscribe(() => onChange()),\n };\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function f(
|
|
1
|
+
function f(r,o,n){let{clientId:t,versions:a}=n;if(o.origin!==t){if(typeof o.version=="number"){let i=a.get(o.storeKey)??0;if(o.version<=i)return;a.set(o.storeKey,o.version);}r.getStore(o.storeKey,()=>o.state).set(o.state);}}function h(r){let{url:o,syncUrl:n,universe:t,clientId:a,versions:i,getLastEventId:g}=r,v=null,d=0;function y(){let p=g(),l=new URL(o,globalThis.location?.href);p>0&&l.searchParams.set("lastEventId",String(p)),v=new EventSource(l.toString()),v.addEventListener("snapshot",S=>{try{let e=JSON.parse(S.data);for(let[s,c]of Object.entries(e)){let u=c;i.set(s,Math.max(i.get(s)??0,u.version)),t.getStore(s,()=>u.state).set(u.state);}}catch(e){console.error("[Zuno] Failed to parse snapshot",e);}}),v.addEventListener("state",S=>{try{let e=JSON.parse(S.data);if(e.origin===a)return;if(typeof e.version=="number"){let s=i.get(e.storeKey)??0;if(e.version<=s)return;i.set(e.storeKey,e.version);}t.getStore(e.storeKey,()=>e.state).set(e.state);}catch(e){console.error("[Zuno] Failed to parse SSE event",e);}}),v.onopen=()=>{d=0,r.onOpen?.();},v.onerror=()=>{v?.close(),r.onClose?.();let S=Math.min(1e3*Math.pow(2,d),3e4);d++,setTimeout(y,S);};}return y(),{dispatch:async p=>{try{r.optimistic&&t.getStore(p.storeKey,()=>p.state).set(p.state);let l=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(p)});if(l.status===409){let e=await l.json();if(e.current){let{state:s,version:c}=e.current;i.set(p.storeKey,c),t.getStore(p.storeKey,()=>s).set(s);}return {ok:!1,status:409,json:e,reason:"CONFLICT"}}if(!l.ok)return {ok:!1,status:l.status,json:await l.json()};let S=await l.json();if(S.event){let{state:e,version:s}=S.event;typeof s=="number"&&i.set(p.storeKey,s);}return {ok:!0,status:200,json:S}}catch(l){return {ok:false,status:500,json:l,reason:"NETWORK_ERROR"}}},unsubscribe:()=>{v?.close();}}}function T(r){let{channelName:o,clientId:n,onEvent:t,getSnapshot:a,onSnapshot:i}=r,g=new BroadcastChannel(o);return g.onmessage=v=>{let d=v.data;d.origin!==n&&(d.type==="event"&&t(d.event),d.type==="hello"&&g.postMessage({type:"snapshot",snapshot:a(),origin:n}),d.type==="snapshot"&&i(d.snapshot));},{publish:v=>g.postMessage({type:"event",event:v,origin:n}),hello:()=>g.postMessage({type:"hello",origin:n}),stop:()=>g.close()}}var m=r=>{let o=r,n=new Set;return {get:()=>o,set:t=>{let a=typeof t=="function"?t(o):t;Object.is(a,o)||(o=a,n.forEach(i=>i(o)));},subscribe:t=>(n.add(t),()=>n.delete(t))}},E=()=>{let r=new Map;return {getStore(n,t){return r.has(n)||r.set(n,m(t())),r.get(n)},snapshot(){let n={};for(let[t,a]of r.entries())n[t]=a.get();return n},restore(n){for(let[t,a]of Object.entries(n)){let i=r.get(t);i?i.set(a):r.set(t,m(a));}},delete(n){r.delete(n);},clear(){r.clear();},hydrateSnapshot(n){let t={};for(let[a,i]of Object.entries(n.state))t[a]=i.state;this.restore(t);}}},w=(r={})=>{let n=new Map,t=r.universe??E(),a=r.clientId??globalThis.crypto?.randomUUID?.()??String(Math.random()),i=false,g=0;function v(e){let s={};for(let[c,u]of Object.entries(e.state))s[c]=u.state,n.set(c,u.version);t.restore(s),g=e.lastEventId;}let d=e=>{typeof e.eventId=="number"&&(g=Math.max(g,e.eventId)),f(t,e,{clientId:a,versions:n});},y=r.sseUrl&&r.syncUrl?h({universe:t,url:r.sseUrl,syncUrl:r.syncUrl,optimistic:r.optimistic??true,clientId:a,versions:n,getLastEventId:()=>g,onOpen:()=>{i=true;},onClose:()=>{i=false;}}):null,p=r.channelName?T({channelName:r.channelName,clientId:a,onEvent:d,getSnapshot:()=>{let e=t.snapshot(),s={};for(let[c,u]of Object.entries(e))s[c]={state:u,version:n.get(c)??0};return s},onSnapshot:e=>{for(let[s,c]of Object.entries(e))d({storeKey:s,state:c.state,version:c.version});}}):null;setTimeout(()=>p?.hello(),0);let l=async e=>{if(y&&i)return await y.dispatch({...e,origin:a,baseVersion:n.get(e.storeKey)??0});let s=(n.get(e.storeKey)??0)+1;return d({...e,version:s}),n.set(e.storeKey,s),p&&p.publish({...e,version:s,origin:a}),{ok:true,status:200,json:null}};return {universe:t,clientId:a,store:(e,s)=>{let c=t.getStore(e,s);return {key:e,raw:()=>c,get:()=>c.get(),subscribe:u=>c.subscribe(u),set:u=>{let b=c.get(),Z=typeof u=="function"?u(b):u;return l({storeKey:e,state:Z})}}},getStore:t.getStore.bind(t),get:(e,s)=>t.getStore(e,s??(()=>{})).get(),set:async(e,s,c)=>{let u=t.getStore(e,c??(()=>{})),b=typeof s=="function"?s(u.get()):s;return l({storeKey:e,state:b})},subscribe:(e,s,c)=>t.getStore(e,s).subscribe(c),dispatch:l,stop:()=>{y?.unsubscribe?.(),p?.stop?.();},hydrateSnapshot:v,getLastEventId:()=>g,setLastEventId:e=>{g=e;}}};function I(r){return {getSnapshot:()=>r.get(),subscribe:o=>r.subscribe(()=>o())}}export{f as applyIncomingEvent,m as createStore,E as createUniverse,w as createZuno,T as startBroadcastChannel,h as startSSE,I as toReadable};//# sourceMappingURL=index.js.map
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sync/index.ts","../src/core/index.ts","../src/shared/readable.ts"],"names":["applyIncomingEvent","universe","event","context","clientId","versions","current","startSSE","opts","url","syncUrl","getLastEventId","es","retryCount","connect","lastId","connectUrl","e","snap","key","rec","r","err","delay","res","data","state","version","startBroadcastChannel","channelName","onEvent","getSnapshot","onSnapshot","channel","msg","createStore","initial","listeners","next","value","l","listener","createUniverse","stores","init","out","store","existing","snapshot","plain","k","createZuno","sseReady","lastEventId","hydrateSnapshot","apply","sse","bc","storeKey","dispatch","nextVersion","rawStore","cb","prev","s","id","toReadable","onChange"],"mappings":"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","file":"index.js","sourcesContent":["import type { Universe } from \"../core\";\n\n// --- Types ---\n\n/**\n * Authoritative state event.\n */\nexport type ZunoStateEvent = {\n storeKey: string;\n state: any;\n version?: number;\n baseVersion?: number;\n origin?: string;\n ts?: number;\n eventId?: number;\n};\n\n/**\n * Generic transport status.\n */\nexport type TransportStatus = {\n ok: boolean;\n status: number;\n json: any;\n reason?: string;\n};\n\n/**\n * Client transport interface.\n */\nexport interface ZunoTransport {\n dispatch(event: ZunoStateEvent): Promise<TransportStatus>;\n unsubscribe?(): void;\n}\n\n/**\n * Apply incoming event to the universe and local bookkeeping.\n */\nexport function applyIncomingEvent(\n universe: Universe,\n event: ZunoStateEvent,\n context: {\n clientId: string;\n localState: Map<string, unknown>;\n versions: Map<string, number>;\n }\n) {\n const { clientId, versions } = context;\n\n // 1. Loopback suppression\n if (event.origin === clientId) return;\n\n // 2. Version check (if provided by transport)\n if (typeof event.version === \"number\") {\n const current = versions.get(event.storeKey) ?? 0;\n if (event.version <= current) return; // Stale\n versions.set(event.storeKey, event.version);\n }\n\n // 3. Apply to universe\n universe.getStore(event.storeKey, () => event.state).set(event.state);\n}\n\n// --- SSE Client ---\n\nexport type SSEOptions = {\n universe: Universe;\n url: string;\n syncUrl: string;\n optimistic: boolean;\n clientId: string;\n versions: Map<string, number>;\n getLastEventId: () => number;\n onOpen?: () => void;\n onClose?: () => void;\n};\n\nexport function startSSE(opts: SSEOptions): ZunoTransport {\n const { url, syncUrl, universe, clientId, versions, getLastEventId } = opts;\n let es: EventSource | null = null;\n let retryCount = 0;\n\n function connect() {\n const lastId = getLastEventId();\n const connectUrl = new URL(url, globalThis.location?.href);\n if (lastId > 0) connectUrl.searchParams.set(\"lastEventId\", String(lastId));\n\n es = new EventSource(connectUrl.toString());\n\n es.addEventListener(\"snapshot\", (e: any) => {\n try {\n const snap = JSON.parse(e.data);\n for (const [key, rec] of Object.entries(snap)) {\n const r = rec as { state: any; version: number };\n versions.set(key, Math.max(versions.get(key) ?? 0, r.version));\n universe.getStore(key, () => r.state).set(r.state);\n }\n } catch (err) {\n console.error(\"[Zuno] Failed to parse snapshot\", err);\n }\n });\n\n es.addEventListener(\"state\", (e: any) => {\n try {\n const event = JSON.parse(e.data) as ZunoStateEvent;\n if (event.origin === clientId) return;\n\n if (typeof event.version === \"number\") {\n const current = versions.get(event.storeKey) ?? 0;\n if (event.version <= current) return;\n versions.set(event.storeKey, event.version);\n }\n\n universe.getStore(event.storeKey, () => event.state).set(event.state);\n } catch (err) {\n console.error(\"[Zuno] Failed to parse SSE event\", err);\n }\n });\n\n es.onopen = () => {\n retryCount = 0;\n opts.onOpen?.();\n };\n\n es.onerror = () => {\n es?.close();\n opts.onClose?.();\n const delay = Math.min(1000 * Math.pow(2, retryCount), 30000);\n retryCount++;\n setTimeout(connect, delay);\n };\n }\n\n connect();\n\n return {\n dispatch: async (event) => {\n try {\n const res = await fetch(syncUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(event),\n });\n\n if (res.status === 409) {\n const data = await res.json();\n if (data.current) {\n const { state, version } = data.current;\n versions.set(event.storeKey, version);\n universe.getStore(event.storeKey, () => state).set(state);\n }\n return { ok: false, status: 409, json: data, reason: \"CONFLICT\" };\n }\n\n if (!res.ok) return { ok: false, status: res.status, json: await res.json() };\n\n return { ok: true, status: 200, json: await res.json() };\n } catch (err) {\n return { ok: false, status: 500, json: err, reason: \"NETWORK_ERROR\" };\n }\n },\n unsubscribe: () => {\n es?.close();\n },\n };\n}\n\n// --- BroadcastChannel ---\n\nexport type BCOptions = {\n channelName: string;\n clientId: string;\n onEvent: (event: ZunoStateEvent) => void;\n getSnapshot: () => Record<string, { state: unknown; version: number }>;\n onSnapshot: (snap: Record<string, { state: unknown; version: number }>) => void;\n};\n\nexport function startBroadcastChannel(opts: BCOptions) {\n const { channelName, clientId, onEvent, getSnapshot, onSnapshot } = opts;\n const channel = new BroadcastChannel(channelName);\n\n channel.onmessage = (e) => {\n const msg = e.data;\n if (msg.origin === clientId) return;\n\n if (msg.type === \"event\") onEvent(msg.event);\n if (msg.type === \"hello\") channel.postMessage({ type: \"snapshot\", snapshot: getSnapshot(), origin: clientId });\n if (msg.type === \"snapshot\") onSnapshot(msg.snapshot);\n };\n\n return {\n publish: (event: ZunoStateEvent) => channel.postMessage({ type: \"event\", event, origin: clientId }),\n hello: () => channel.postMessage({ type: \"hello\", origin: clientId }),\n stop: () => channel.close(),\n };\n}\n","import { startSSE, startBroadcastChannel, applyIncomingEvent } from \"../sync\";\nimport type { ZunoStateEvent } from \"../sync\";\n\n// --- Types ---\n\n/**\n * Authoritative snapshot of the entire universe.\n */\nexport type ZunoSnapshot = {\n state: Record<string, { state: unknown; version: number }>;\n lastEventId: number;\n};\n\n/**\n * A simple state container for a single keyed value.\n */\nexport interface Store<T> {\n get(): T;\n set(next: T | ((prev: T) => T)): void;\n subscribe(listener: (state: T) => void): () => void;\n}\n\n/**\n * A Universe manages many stores.\n */\nexport interface Universe {\n getStore<T>(key: string, init: () => T): Store<T>;\n snapshot(): Record<string, unknown>;\n restore(data: Record<string, unknown>): void;\n delete(key: string): void;\n clear(): void;\n hydrateSnapshot(snapshot: ZunoSnapshot): void;\n}\n\n/**\n * Options for creating a Zuno instance.\n */\nexport type CreateZunoOptions = {\n /** Optional pre-existing universe. */\n universe?: Universe;\n /** BroadcastChannel name for local tab sync. */\n channelName?: string;\n /** SSE endpoint URL. */\n sseUrl?: string;\n /** Sync endpoint URL (required if sseUrl is provided). */\n syncUrl?: string;\n /** Apply updates locally before server confirmation (default: true). */\n optimistic?: boolean;\n /** Unique client identifier (default: random UUID). */\n clientId?: string;\n};\n\n/**\n * An extended interface for a Zuno store that includes methods for setting state\n * and a unique key identifier. This represents a store that has been \"bound\" or registered.\n */\nexport type BoundStore<T> = {\n key: string;\n get: () => T;\n set: (next: T | ((prev: T) => T)) => Promise<any>;\n subscribe: (cb: (state: T) => void) => () => void;\n raw: () => Store<T>;\n};\n\n// --- Store Implementation ---\n\n/**\n * Creates a raw ZUNO state management store.\n */\nexport const createStore = <T>(initial: T): Store<T> => {\n let state = initial;\n const listeners = new Set<(state: T) => void>();\n\n return {\n get: () => state,\n set: (next) => {\n const value = typeof next === \"function\" ? (next as (prev: T) => T)(state) : next;\n if (Object.is(value, state)) return;\n state = value;\n listeners.forEach((l) => l(state));\n },\n subscribe: (listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n};\n\n// --- Universe Implementation ---\n\n/**\n * Creates a ZUNO Universe to manage multiple stores.\n */\nexport const createUniverse = (): Universe => {\n const stores = new Map<string, Store<any>>();\n\n const universe: Universe = {\n getStore<T>(key: string, init: () => T): Store<T> {\n if (!stores.has(key)) {\n stores.set(key, createStore(init()));\n }\n return stores.get(key)! as Store<T>;\n },\n snapshot(): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, store] of stores.entries()) {\n out[key] = store.get();\n }\n return out;\n },\n restore(data: Record<string, unknown>): void {\n for (const [key, value] of Object.entries(data)) {\n const existing = stores.get(key);\n if (existing) {\n existing.set(value as any);\n } else {\n stores.set(key, createStore(value as any));\n }\n }\n },\n delete(key: string): void {\n stores.delete(key);\n },\n clear(): void {\n stores.clear();\n },\n hydrateSnapshot(snapshot: ZunoSnapshot) {\n const plain: Record<string, unknown> = {};\n for (const [k, rec] of Object.entries(snapshot.state)) {\n plain[k] = rec.state;\n }\n this.restore(plain);\n },\n };\n\n return universe;\n};\n\n// --- Main Zuno Factory ---\n\n/**\n * Creates a Zuno instance for distributed state synchronization.\n */\nexport const createZuno = (opts: CreateZunoOptions = {}) => {\n const localState = new Map<string, unknown>();\n const versions = new Map<string, number>();\n const universe = opts.universe ?? createUniverse();\n const clientId = opts.clientId ?? (globalThis.crypto?.randomUUID?.() ?? String(Math.random()));\n let sseReady = false;\n let lastEventId = 0;\n\n function hydrateSnapshot(snapshot: ZunoSnapshot) {\n const plain: Record<string, unknown> = {};\n for (const [k, rec] of Object.entries(snapshot.state)) {\n plain[k] = rec.state;\n versions.set(k, rec.version);\n }\n universe.restore(plain);\n lastEventId = snapshot.lastEventId;\n }\n\n const apply = (event: ZunoStateEvent) => {\n if (typeof event.eventId === \"number\") {\n lastEventId = Math.max(lastEventId, event.eventId);\n }\n applyIncomingEvent(universe, event, { clientId, localState, versions });\n };\n\n const sse = opts.sseUrl && opts.syncUrl\n ? startSSE({\n universe,\n url: opts.sseUrl,\n syncUrl: opts.syncUrl,\n optimistic: opts.optimistic ?? true,\n clientId,\n versions,\n getLastEventId: () => lastEventId,\n onOpen: () => { sseReady = true; },\n onClose: () => { sseReady = false; },\n })\n : null;\n\n const bc = opts.channelName\n ? startBroadcastChannel({\n channelName: opts.channelName,\n clientId,\n onEvent: apply,\n getSnapshot: () => {\n const snap = universe.snapshot();\n const out: Record<string, { state: unknown; version: number }> = {};\n for (const [storeKey, state] of Object.entries(snap)) {\n out[storeKey] = { state, version: versions.get(storeKey) ?? 0 };\n }\n return out;\n },\n onSnapshot: (snap) => {\n for (const [storeKey, rec] of Object.entries(snap)) {\n apply({ storeKey, state: rec.state, version: rec.version });\n }\n },\n })\n : null;\n\n setTimeout(() => bc?.hello(), 0);\n\n const dispatch = async (event: ZunoStateEvent) => {\n if (sse && sseReady) {\n return await sse.dispatch({\n ...event,\n origin: clientId,\n baseVersion: versions.get(event.storeKey) ?? 0,\n });\n }\n\n const nextVersion = (versions.get(event.storeKey) ?? 0) + 1;\n apply({ ...event, version: nextVersion });\n versions.set(event.storeKey, nextVersion);\n\n if (bc) {\n bc.publish({ ...event, version: nextVersion, origin: clientId });\n }\n\n return { ok: true, status: 200, json: null };\n };\n\n const store = <T,>(storeKey: string, init: () => T): BoundStore<T> => {\n const rawStore = universe.getStore<T>(storeKey, init);\n return {\n key: storeKey,\n raw: () => rawStore,\n get: () => rawStore.get(),\n subscribe: (cb) => rawStore.subscribe(cb),\n set: (next) => {\n const prev = rawStore.get();\n const state = typeof next === \"function\" ? (next as (prev: T) => T)(prev) : next;\n return dispatch({ storeKey, state });\n },\n };\n };\n\n return {\n universe,\n clientId,\n store,\n getStore: universe.getStore.bind(universe),\n get: <T,>(key: string, init?: () => T) => universe.getStore<T>(key, init ?? (() => undefined as any)).get(),\n set: async <T,>(key: string, next: T | ((prev: T) => T), init?: () => T) => {\n const s = universe.getStore<T>(key, init ?? (() => undefined as any));\n const state = typeof next === \"function\" ? (next as (prev: T) => T)(s.get()) : next;\n return dispatch({ storeKey: key, state });\n },\n subscribe: <T,>(key: string, init: () => T, cb: (state: T) => void) => universe.getStore<T>(key, init).subscribe(cb),\n dispatch,\n stop: () => {\n sse?.unsubscribe?.();\n bc?.stop?.();\n },\n hydrateSnapshot,\n getLastEventId: () => lastEventId,\n setLastEventId: (id: number) => { lastEventId = id; },\n };\n};\n","/** Universal UI adapter contract */\nexport type ZunoReadable<T> = {\n /** Read current value (sync) */\n getSnapshot(): T;\n\n /**\n * Subscribe to changes.\n * Call `onChange()` whenever snapshot may have changed.\n * Return unsubscribe.\n */\n subscribe(onChange: () => void): () => void;\n\n /** Optional: React SSR (server snapshot) */\n getServerSnapshot?: () => T;\n};\n\n/** Minimal store shape that can be adapted into a readable */\nexport type ZunoSubscribableStore<T> = {\n get(): T;\n subscribe(cb: (state: T) => void): () => void;\n};\n\n/** Adapter helper: convert store => readable */\nexport function toReadable<T>(store: ZunoSubscribableStore<T>): ZunoReadable<T> {\n return {\n getSnapshot: () => store.get(),\n subscribe: (onChange) => store.subscribe(() => onChange()),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/sync/index.ts","../src/core/index.ts","../src/shared/readable.ts"],"names":["applyIncomingEvent","universe","event","context","clientId","versions","current","startSSE","opts","url","syncUrl","getLastEventId","es","retryCount","connect","lastId","connectUrl","e","snap","key","rec","r","err","delay","res","data","state","version","json","startBroadcastChannel","channelName","onEvent","getSnapshot","onSnapshot","channel","msg","createStore","initial","listeners","next","value","l","listener","createUniverse","stores","init","out","store","existing","snapshot","plain","k","createZuno","sseReady","lastEventId","hydrateSnapshot","apply","sse","bc","storeKey","dispatch","nextVersion","rawStore","cb","prev","s","id","toReadable","onChange"],"mappings":"AAsCO,SAASA,CAAAA,CACdC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CAKA,CACA,GAAM,CAAE,QAAA,CAAAC,CAAAA,CAAU,SAAAC,CAAS,CAAA,CAAIF,CAAAA,CAG/B,GAAID,EAAM,MAAA,GAAWE,CAAAA,CAGrB,CAAA,GAAI,OAAOF,CAAAA,CAAM,OAAA,EAAY,QAAA,CAAU,CACrC,IAAMI,CAAAA,CAAUD,CAAAA,CAAS,GAAA,CAAIH,CAAAA,CAAM,QAAQ,CAAA,EAAK,CAAA,CAChD,GAAIA,CAAAA,CAAM,SAAWI,CAAAA,CAAS,OAC9BD,CAAAA,CAAS,GAAA,CAAIH,CAAAA,CAAM,QAAA,CAAUA,CAAAA,CAAM,OAAO,EAC5C,CAGAD,CAAAA,CAAS,QAAA,CAASC,CAAAA,CAAM,SAAU,IAAMA,CAAAA,CAAM,KAAK,CAAA,CAAE,IAAIA,CAAAA,CAAM,KAAK,EAAA,CACtE,CAgBO,SAASK,CAAAA,CAASC,CAAAA,CAAiC,CACxD,GAAM,CAAE,GAAA,CAAAC,CAAAA,CAAK,OAAA,CAAAC,EAAS,QAAA,CAAAT,CAAAA,CAAU,QAAA,CAAAG,CAAAA,CAAU,SAAAC,CAAAA,CAAU,cAAA,CAAAM,CAAe,CAAA,CAAIH,CAAAA,CACnEI,CAAAA,CAAyB,IAAA,CACzBC,CAAAA,CAAa,EAEjB,SAASC,CAAAA,EAAU,CACjB,IAAMC,EAASJ,CAAAA,EAAe,CACxBK,CAAAA,CAAa,IAAI,IAAIP,CAAAA,CAAK,UAAA,CAAW,QAAA,EAAU,IAAI,CAAA,CACrDM,CAAAA,CAAS,CAAA,EAAGC,CAAAA,CAAW,aAAa,GAAA,CAAI,aAAA,CAAe,MAAA,CAAOD,CAAM,CAAC,CAAA,CAEzEH,CAAAA,CAAK,IAAI,WAAA,CAAYI,EAAW,QAAA,EAAU,CAAA,CAE1CJ,CAAAA,CAAG,gBAAA,CAAiB,UAAA,CAAaK,CAAAA,EAAW,CAC1C,GAAI,CACF,IAAMC,CAAAA,CAAO,IAAA,CAAK,MAAMD,CAAAA,CAAE,IAAI,CAAA,CAC9B,IAAA,GAAW,CAACE,CAAAA,CAAKC,CAAG,CAAA,GAAK,MAAA,CAAO,OAAA,CAAQF,CAAI,CAAA,CAAG,CAC7C,IAAMG,CAAAA,CAAID,CAAAA,CACVf,CAAAA,CAAS,GAAA,CAAIc,EAAK,IAAA,CAAK,GAAA,CAAId,CAAAA,CAAS,GAAA,CAAIc,CAAG,CAAA,EAAK,CAAA,CAAGE,CAAAA,CAAE,OAAO,CAAC,CAAA,CAC7DpB,CAAAA,CAAS,QAAA,CAASkB,EAAK,IAAME,CAAAA,CAAE,KAAK,CAAA,CAAE,IAAIA,CAAAA,CAAE,KAAK,EACnD,CACF,OAASC,CAAAA,CAAK,CACZ,OAAA,CAAQ,KAAA,CAAM,iCAAA,CAAmCA,CAAG,EACtD,CACF,CAAC,CAAA,CAEDV,CAAAA,CAAG,gBAAA,CAAiB,OAAA,CAAUK,GAAW,CACvC,GAAI,CACF,IAAMf,EAAQ,IAAA,CAAK,KAAA,CAAMe,CAAAA,CAAE,IAAI,CAAA,CAC/B,GAAIf,CAAAA,CAAM,MAAA,GAAWE,EAAU,OAE/B,GAAI,OAAOF,CAAAA,CAAM,SAAY,QAAA,CAAU,CACrC,IAAMI,CAAAA,CAAUD,EAAS,GAAA,CAAIH,CAAAA,CAAM,QAAQ,CAAA,EAAK,CAAA,CAChD,GAAIA,CAAAA,CAAM,OAAA,EAAWI,EAAS,OAC9BD,CAAAA,CAAS,GAAA,CAAIH,CAAAA,CAAM,SAAUA,CAAAA,CAAM,OAAO,EAC5C,CAEAD,EAAS,QAAA,CAASC,CAAAA,CAAM,QAAA,CAAU,IAAMA,CAAAA,CAAM,KAAK,CAAA,CAAE,GAAA,CAAIA,EAAM,KAAK,EACtE,CAAA,MAASoB,CAAAA,CAAK,CACZ,OAAA,CAAQ,KAAA,CAAM,kCAAA,CAAoCA,CAAG,EACvD,CACF,CAAC,CAAA,CAEDV,CAAAA,CAAG,MAAA,CAAS,IAAM,CAChBC,CAAAA,CAAa,EACbL,CAAAA,CAAK,MAAA,KACP,CAAA,CAEAI,EAAG,OAAA,CAAU,IAAM,CACjBA,CAAAA,EAAI,OAAM,CACVJ,CAAAA,CAAK,OAAA,IAAU,CACf,IAAMe,CAAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,IAAO,IAAA,CAAK,GAAA,CAAI,CAAA,CAAGV,CAAU,EAAG,GAAK,CAAA,CAC5DA,CAAAA,EAAAA,CACA,UAAA,CAAWC,EAASS,CAAK,EAC3B,EACF,CAEA,OAAAT,CAAAA,EAAQ,CAED,CACL,SAAU,MAAOZ,CAAAA,EAAU,CACzB,GAAI,CACEM,CAAAA,CAAK,UAAA,EACPP,CAAAA,CAAS,QAAA,CAASC,EAAM,QAAA,CAAU,IAAMA,CAAAA,CAAM,KAAK,CAAA,CAAE,GAAA,CAAIA,CAAAA,CAAM,KAAK,EAGtE,IAAMsB,CAAAA,CAAM,MAAM,KAAA,CAAMd,EAAS,CAC/B,MAAA,CAAQ,MAAA,CACR,OAAA,CAAS,CAAE,cAAA,CAAgB,kBAAmB,CAAA,CAC9C,IAAA,CAAM,IAAA,CAAK,SAAA,CAAUR,CAAK,CAC5B,CAAC,CAAA,CAED,GAAIsB,CAAAA,CAAI,MAAA,GAAW,IAAK,CACtB,IAAMC,CAAAA,CAAO,MAAMD,EAAI,IAAA,EAAK,CAC5B,GAAIC,CAAAA,CAAK,OAAA,CAAS,CAChB,GAAM,CAAE,MAAAC,CAAAA,CAAO,OAAA,CAAAC,CAAQ,CAAA,CAAIF,EAAK,OAAA,CAChCpB,CAAAA,CAAS,GAAA,CAAIH,CAAAA,CAAM,SAAUyB,CAAO,CAAA,CACpC1B,CAAAA,CAAS,QAAA,CAASC,CAAAA,CAAM,QAAA,CAAU,IAAMwB,CAAK,EAAE,GAAA,CAAIA,CAAK,EAC1D,CACA,OAAO,CAAE,EAAA,CAAI,CAAA,CAAA,CAAO,MAAA,CAAQ,IAAK,IAAA,CAAMD,CAAAA,CAAM,MAAA,CAAQ,UAAW,CAClE,CAEA,GAAI,CAACD,EAAI,EAAA,CAAI,OAAO,CAAE,EAAA,CAAI,GAAO,MAAA,CAAQA,CAAAA,CAAI,MAAA,CAAQ,IAAA,CAAM,MAAMA,CAAAA,CAAI,IAAA,EAAO,CAAA,CAE5E,IAAMI,CAAAA,CAAO,MAAMJ,CAAAA,CAAI,MAAK,CAC5B,GAAII,CAAAA,CAAK,KAAA,CAAO,CACd,GAAM,CAAE,KAAA,CAAAF,CAAAA,CAAO,QAAAC,CAAQ,CAAA,CAAIC,CAAAA,CAAK,KAAA,CAC5B,OAAOD,CAAAA,EAAY,QAAA,EACrBtB,CAAAA,CAAS,IAAIH,CAAAA,CAAM,QAAA,CAAUyB,CAAO,EAOxC,CAEA,OAAO,CAAE,EAAA,CAAI,CAAA,CAAA,CAAM,OAAQ,GAAA,CAAK,IAAA,CAAAC,CAAK,CACvC,CAAA,MAASN,CAAAA,CAAK,CACZ,OAAO,CAAE,EAAA,CAAI,KAAA,CAAO,MAAA,CAAQ,GAAA,CAAK,KAAMA,CAAAA,CAAK,MAAA,CAAQ,eAAgB,CACtE,CACF,CAAA,CACA,WAAA,CAAa,IAAM,CACjBV,CAAAA,EAAI,KAAA,GACN,CACF,CACF,CAYO,SAASiB,CAAAA,CAAsBrB,CAAAA,CAAiB,CACrD,GAAM,CAAE,WAAA,CAAAsB,CAAAA,CAAa,SAAA1B,CAAAA,CAAU,OAAA,CAAA2B,CAAAA,CAAS,WAAA,CAAAC,CAAAA,CAAa,UAAA,CAAAC,CAAW,CAAA,CAAIzB,EAC9D0B,CAAAA,CAAU,IAAI,gBAAA,CAAiBJ,CAAW,EAEhD,OAAAI,CAAAA,CAAQ,SAAA,CAAajB,CAAAA,EAAM,CACzB,IAAMkB,CAAAA,CAAMlB,CAAAA,CAAE,IAAA,CACVkB,CAAAA,CAAI,MAAA,GAAW/B,CAAAA,GAEf+B,CAAAA,CAAI,OAAS,OAAA,EAASJ,CAAAA,CAAQI,CAAAA,CAAI,KAAK,EACvCA,CAAAA,CAAI,IAAA,GAAS,OAAA,EAASD,CAAAA,CAAQ,YAAY,CAAE,IAAA,CAAM,UAAA,CAAY,QAAA,CAAUF,CAAAA,EAAY,CAAG,MAAA,CAAQ5B,CAAS,CAAC,CAAA,CACzG+B,CAAAA,CAAI,IAAA,GAAS,UAAA,EAAYF,EAAWE,CAAAA,CAAI,QAAQ,CAAA,EACtD,CAAA,CAEO,CACL,OAAA,CAAUjC,CAAAA,EAA0BgC,CAAAA,CAAQ,WAAA,CAAY,CAAE,IAAA,CAAM,OAAA,CAAS,KAAA,CAAAhC,EAAO,MAAA,CAAQE,CAAS,CAAC,CAAA,CAClG,MAAO,IAAM8B,CAAAA,CAAQ,WAAA,CAAY,CAAE,KAAM,OAAA,CAAS,MAAA,CAAQ9B,CAAS,CAAC,CAAA,CACpE,IAAA,CAAM,IAAM8B,CAAAA,CAAQ,OACtB,CACF,CC/IO,IAAME,EAAkBC,CAAAA,EAAyB,CACtD,IAAIX,CAAAA,CAAQW,EACNC,CAAAA,CAAY,IAAI,GAAA,CAEtB,OAAO,CACL,GAAA,CAAK,IAAMZ,CAAAA,CACX,IAAMa,CAAAA,EAAS,CACb,IAAMC,CAAAA,CAAQ,OAAOD,CAAAA,EAAS,UAAA,CAAcA,CAAAA,CAAwBb,CAAK,EAAIa,CAAAA,CACzE,MAAA,CAAO,EAAA,CAAGC,CAAAA,CAAOd,CAAK,CAAA,GAC1BA,CAAAA,CAAQc,CAAAA,CACRF,EAAU,OAAA,CAASG,CAAAA,EAAMA,CAAAA,CAAEf,CAAK,CAAC,CAAA,EACnC,CAAA,CACA,SAAA,CAAYgB,CAAAA,GACVJ,EAAU,GAAA,CAAII,CAAQ,CAAA,CACf,IAAMJ,CAAAA,CAAU,MAAA,CAAOI,CAAQ,CAAA,CAE1C,CACF,CAAA,CAOaC,CAAAA,CAAiB,IAAgB,CAC5C,IAAMC,CAAAA,CAAS,IAAI,GAAA,CAyCnB,OAvC2B,CACzB,QAAA,CAAYzB,CAAAA,CAAa0B,CAAAA,CAAyB,CAChD,OAAKD,CAAAA,CAAO,GAAA,CAAIzB,CAAG,GACjByB,CAAAA,CAAO,GAAA,CAAIzB,CAAAA,CAAKiB,CAAAA,CAAYS,GAAM,CAAC,CAAA,CAE9BD,CAAAA,CAAO,IAAIzB,CAAG,CACvB,CAAA,CACA,QAAA,EAAoC,CAClC,IAAM2B,CAAAA,CAA+B,GACrC,IAAA,GAAW,CAAC3B,CAAAA,CAAK4B,CAAK,IAAKH,CAAAA,CAAO,OAAA,EAAQ,CACxCE,CAAAA,CAAI3B,CAAG,CAAA,CAAI4B,CAAAA,CAAM,GAAA,EAAI,CAEvB,OAAOD,CACT,CAAA,CACA,OAAA,CAAQrB,EAAqC,CAC3C,IAAA,GAAW,CAACN,CAAAA,CAAKqB,CAAK,CAAA,GAAK,MAAA,CAAO,OAAA,CAAQf,CAAI,EAAG,CAC/C,IAAMuB,CAAAA,CAAWJ,CAAAA,CAAO,GAAA,CAAIzB,CAAG,CAAA,CAC3B6B,CAAAA,CACFA,EAAS,GAAA,CAAIR,CAAY,CAAA,CAEzBI,CAAAA,CAAO,IAAIzB,CAAAA,CAAKiB,CAAAA,CAAYI,CAAY,CAAC,EAE7C,CACF,CAAA,CACA,MAAA,CAAOrB,CAAAA,CAAmB,CACxByB,CAAAA,CAAO,MAAA,CAAOzB,CAAG,EACnB,CAAA,CACA,KAAA,EAAc,CACZyB,CAAAA,CAAO,QACT,CAAA,CACA,eAAA,CAAgBK,CAAAA,CAAwB,CACtC,IAAMC,CAAAA,CAAiC,EAAC,CACxC,IAAA,GAAW,CAACC,CAAAA,CAAG/B,CAAG,IAAK,MAAA,CAAO,OAAA,CAAQ6B,CAAAA,CAAS,KAAK,EAClDC,CAAAA,CAAMC,CAAC,CAAA,CAAI/B,CAAAA,CAAI,MAEjB,IAAA,CAAK,OAAA,CAAQ8B,CAAK,EACpB,CACF,CAGF,CAAA,CAOaE,CAAAA,CAAa,CAAC5C,CAAAA,CAA0B,EAAC,GAAM,KAEpDH,CAAAA,CAAW,IAAI,GAAA,CACfJ,CAAAA,CAAWO,CAAAA,CAAK,QAAA,EAAYmC,CAAAA,EAAe,CAC3CvC,CAAAA,CAAWI,CAAAA,CAAK,UAAa,UAAA,CAAW,MAAA,EAAQ,UAAA,IAAa,EAAK,OAAO,IAAA,CAAK,MAAA,EAAQ,CAAA,CACxF6C,EAAW,KAAA,CACXC,CAAAA,CAAc,EAElB,SAASC,CAAAA,CAAgBN,CAAAA,CAAwB,CAC/C,IAAMC,EAAiC,EAAC,CACxC,IAAA,GAAW,CAACC,EAAG/B,CAAG,CAAA,GAAK,MAAA,CAAO,OAAA,CAAQ6B,EAAS,KAAK,CAAA,CAClDC,CAAAA,CAAMC,CAAC,CAAA,CAAI/B,CAAAA,CAAI,KAAA,CACff,CAAAA,CAAS,IAAI8C,CAAAA,CAAG/B,CAAAA,CAAI,OAAO,CAAA,CAE7BnB,EAAS,OAAA,CAAQiD,CAAK,CAAA,CACtBI,CAAAA,CAAcL,EAAS,YACzB,CAEA,IAAMO,CAAAA,CAAStD,CAAAA,EAA0B,CACnC,OAAOA,CAAAA,CAAM,SAAY,QAAA,GAC3BoD,CAAAA,CAAc,IAAA,CAAK,GAAA,CAAIA,EAAapD,CAAAA,CAAM,OAAO,CAAA,CAAA,CAEnDF,CAAAA,CAAmBC,EAAUC,CAAAA,CAAO,CAAE,QAAA,CAAAE,CAAAA,CAAsB,QAAA,CAAAC,CAAS,CAAC,EACxE,CAAA,CAEMoD,CAAAA,CAAMjD,CAAAA,CAAK,QAAUA,CAAAA,CAAK,OAAA,CAC5BD,CAAAA,CAAS,CACP,SAAAN,CAAAA,CACA,GAAA,CAAKO,CAAAA,CAAK,MAAA,CACV,OAAA,CAASA,CAAAA,CAAK,OAAA,CACd,UAAA,CAAYA,EAAK,UAAA,EAAc,IAAA,CAC/B,QAAA,CAAAJ,CAAAA,CACA,SAAAC,CAAAA,CACA,cAAA,CAAgB,IAAMiD,CAAAA,CACtB,OAAQ,IAAM,CAAED,CAAAA,CAAW,KAAM,CAAA,CACjC,OAAA,CAAS,IAAM,CAAEA,EAAW,MAAO,CACrC,CAAC,CAAA,CACD,KAEEK,CAAAA,CAAKlD,CAAAA,CAAK,WAAA,CACZqB,CAAAA,CAAsB,CACpB,WAAA,CAAarB,CAAAA,CAAK,WAAA,CAClB,QAAA,CAAAJ,CAAAA,CACA,OAAA,CAASoD,CAAAA,CACT,WAAA,CAAa,IAAM,CACjB,IAAMtC,CAAAA,CAAOjB,CAAAA,CAAS,UAAS,CACzB6C,CAAAA,CAA2D,EAAC,CAClE,OAAW,CAACa,CAAAA,CAAUjC,CAAK,CAAA,GAAK,MAAA,CAAO,OAAA,CAAQR,CAAI,CAAA,CACjD4B,EAAIa,CAAQ,CAAA,CAAI,CAAE,KAAA,CAAAjC,EAAO,OAAA,CAASrB,CAAAA,CAAS,GAAA,CAAIsD,CAAQ,GAAK,CAAE,CAAA,CAEhE,OAAOb,CACT,CAAA,CACA,UAAA,CAAa5B,CAAAA,EAAS,CACpB,OAAW,CAACyC,CAAAA,CAAUvC,CAAG,CAAA,GAAK,OAAO,OAAA,CAAQF,CAAI,CAAA,CAC/CsC,CAAAA,CAAM,CAAE,QAAA,CAAAG,CAAAA,CAAU,KAAA,CAAOvC,CAAAA,CAAI,KAAA,CAAO,OAAA,CAASA,CAAAA,CAAI,OAAQ,CAAC,EAE9D,CACF,CAAC,CAAA,CACD,KAEJ,UAAA,CAAW,IAAMsC,CAAAA,EAAI,KAAA,GAAS,CAAC,CAAA,CAE/B,IAAME,CAAAA,CAAW,MAAO1D,CAAAA,EAA0B,CAChD,GAAIuD,GAAOJ,CAAAA,CACT,OAAO,MAAMI,CAAAA,CAAI,SAAS,CACxB,GAAGvD,CAAAA,CACH,MAAA,CAAQE,EACR,WAAA,CAAaC,CAAAA,CAAS,GAAA,CAAIH,CAAAA,CAAM,QAAQ,CAAA,EAAK,CAC/C,CAAC,EAGH,IAAM2D,CAAAA,CAAAA,CAAexD,CAAAA,CAAS,GAAA,CAAIH,EAAM,QAAQ,CAAA,EAAK,CAAA,EAAK,CAAA,CAC1D,OAAAsD,CAAAA,CAAM,CAAE,GAAGtD,CAAAA,CAAO,OAAA,CAAS2D,CAAY,CAAC,CAAA,CACxCxD,EAAS,GAAA,CAAIH,CAAAA,CAAM,QAAA,CAAU2D,CAAW,EAEpCH,CAAAA,EACFA,CAAAA,CAAG,OAAA,CAAQ,CAAE,GAAGxD,CAAAA,CAAO,OAAA,CAAS2D,CAAAA,CAAa,MAAA,CAAQzD,CAAS,CAAC,CAAA,CAG1D,CAAE,GAAI,IAAA,CAAM,MAAA,CAAQ,GAAA,CAAK,IAAA,CAAM,IAAK,CAC7C,CAAA,CAiBA,OAAO,CACL,SAAAH,CAAAA,CACA,QAAA,CAAAG,CAAAA,CACA,KAAA,CAlBY,CAAKuD,CAAAA,CAAkBd,CAAAA,GAAiC,CACpE,IAAMiB,CAAAA,CAAW7D,CAAAA,CAAS,QAAA,CAAY0D,CAAAA,CAAUd,CAAI,CAAA,CACpD,OAAO,CACL,GAAA,CAAKc,EACL,GAAA,CAAK,IAAMG,CAAAA,CACX,GAAA,CAAK,IAAMA,CAAAA,CAAS,GAAA,EAAI,CACxB,UAAYC,CAAAA,EAAOD,CAAAA,CAAS,SAAA,CAAUC,CAAE,EACxC,GAAA,CAAMxB,CAAAA,EAAS,CACb,IAAMyB,EAAOF,CAAAA,CAAS,GAAA,EAAI,CACpBpC,CAAAA,CAAQ,OAAOa,CAAAA,EAAS,UAAA,CAAcA,CAAAA,CAAwByB,CAAI,CAAA,CAAIzB,CAAAA,CAC5E,OAAOqB,CAAAA,CAAS,CAAE,QAAA,CAAAD,CAAAA,CAAU,KAAA,CAAAjC,CAAM,CAAC,CACrC,CACF,CACF,CAAA,CAME,QAAA,CAAUzB,CAAAA,CAAS,QAAA,CAAS,IAAA,CAAKA,CAAQ,CAAA,CACzC,GAAA,CAAK,CAAKkB,CAAAA,CAAa0B,IAAmB5C,CAAAA,CAAS,QAAA,CAAYkB,CAAAA,CAAK0B,CAAAA,GAAS,IAAG,CAAA,CAAA,CAAoB,CAAA,CAAE,GAAA,EAAI,CAC1G,GAAA,CAAK,MAAW1B,CAAAA,CAAaoB,CAAAA,CAA4BM,IAAmB,CAC1E,IAAMoB,CAAAA,CAAIhE,CAAAA,CAAS,SAAYkB,CAAAA,CAAK0B,CAAAA,GAAS,IAAG,CAAA,CAAA,CAAoB,EAC9DnB,CAAAA,CAAQ,OAAOa,CAAAA,EAAS,UAAA,CAAcA,CAAAA,CAAwB0B,CAAAA,CAAE,GAAA,EAAK,EAAI1B,CAAAA,CAC/E,OAAOqB,CAAAA,CAAS,CAAE,SAAUzC,CAAAA,CAAK,KAAA,CAAAO,CAAM,CAAC,CAC1C,CAAA,CACA,SAAA,CAAW,CAAKP,CAAAA,CAAa0B,CAAAA,CAAekB,CAAAA,GAA2B9D,CAAAA,CAAS,QAAA,CAAYkB,EAAK0B,CAAI,CAAA,CAAE,SAAA,CAAUkB,CAAE,EACnH,QAAA,CAAAH,CAAAA,CACA,IAAA,CAAM,IAAM,CACVH,CAAAA,EAAK,WAAA,IAAc,CACnBC,CAAAA,EAAI,IAAA,KACN,CAAA,CACA,eAAA,CAAAH,EACA,cAAA,CAAgB,IAAMD,CAAAA,CACtB,cAAA,CAAiBY,GAAe,CAAEZ,CAAAA,CAAcY,EAAI,CACtD,CACF,EC9OO,SAASC,CAAAA,CAAcpB,CAAAA,CAAkD,CAC9E,OAAO,CACL,WAAA,CAAa,IAAMA,EAAM,GAAA,EAAI,CAC7B,SAAA,CAAYqB,CAAAA,EAAarB,EAAM,SAAA,CAAU,IAAMqB,CAAAA,EAAU,CAC3D,CACF","file":"index.js","sourcesContent":["import type { Universe } from \"../core\";\n\n// --- Types ---\n\n/**\n * Authoritative state event.\n */\nexport type ZunoStateEvent = {\n storeKey: string;\n state: any;\n version?: number;\n baseVersion?: number;\n origin?: string;\n ts?: number;\n eventId?: number;\n};\n\n/**\n * Generic transport status.\n */\nexport type TransportStatus = {\n ok: boolean;\n status: number;\n json: any;\n reason?: string;\n};\n\n/**\n * Client transport interface.\n */\nexport interface ZunoTransport {\n dispatch(event: ZunoStateEvent): Promise<TransportStatus>;\n unsubscribe?(): void;\n}\n\n/**\n * Apply incoming event to the universe and local bookkeeping.\n */\nexport function applyIncomingEvent(\n universe: Universe,\n event: ZunoStateEvent,\n context: {\n clientId: string;\n localState: Map<string, unknown>;\n versions: Map<string, number>;\n }\n) {\n const { clientId, versions } = context;\n\n // 1. Loopback suppression\n if (event.origin === clientId) return;\n\n // 2. Version check (if provided by transport)\n if (typeof event.version === \"number\") {\n const current = versions.get(event.storeKey) ?? 0;\n if (event.version <= current) return; // Stale\n versions.set(event.storeKey, event.version);\n }\n\n // 3. Apply to universe\n universe.getStore(event.storeKey, () => event.state).set(event.state);\n}\n\n// --- SSE Client ---\n\nexport type SSEOptions = {\n universe: Universe;\n url: string;\n syncUrl: string;\n optimistic: boolean;\n clientId: string;\n versions: Map<string, number>;\n getLastEventId: () => number;\n onOpen?: () => void;\n onClose?: () => void;\n};\n\nexport function startSSE(opts: SSEOptions): ZunoTransport {\n const { url, syncUrl, universe, clientId, versions, getLastEventId } = opts;\n let es: EventSource | null = null;\n let retryCount = 0;\n\n function connect() {\n const lastId = getLastEventId();\n const connectUrl = new URL(url, globalThis.location?.href);\n if (lastId > 0) connectUrl.searchParams.set(\"lastEventId\", String(lastId));\n\n es = new EventSource(connectUrl.toString());\n\n es.addEventListener(\"snapshot\", (e: any) => {\n try {\n const snap = JSON.parse(e.data);\n for (const [key, rec] of Object.entries(snap)) {\n const r = rec as { state: any; version: number };\n versions.set(key, Math.max(versions.get(key) ?? 0, r.version));\n universe.getStore(key, () => r.state).set(r.state);\n }\n } catch (err) {\n console.error(\"[Zuno] Failed to parse snapshot\", err);\n }\n });\n\n es.addEventListener(\"state\", (e: any) => {\n try {\n const event = JSON.parse(e.data) as ZunoStateEvent;\n if (event.origin === clientId) return;\n\n if (typeof event.version === \"number\") {\n const current = versions.get(event.storeKey) ?? 0;\n if (event.version <= current) return;\n versions.set(event.storeKey, event.version);\n }\n\n universe.getStore(event.storeKey, () => event.state).set(event.state);\n } catch (err) {\n console.error(\"[Zuno] Failed to parse SSE event\", err);\n }\n });\n\n es.onopen = () => {\n retryCount = 0;\n opts.onOpen?.();\n };\n\n es.onerror = () => {\n es?.close();\n opts.onClose?.();\n const delay = Math.min(1000 * Math.pow(2, retryCount), 30000);\n retryCount++;\n setTimeout(connect, delay);\n };\n }\n\n connect();\n\n return {\n dispatch: async (event) => {\n try {\n if (opts.optimistic) {\n universe.getStore(event.storeKey, () => event.state).set(event.state);\n }\n\n const res = await fetch(syncUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(event),\n });\n\n if (res.status === 409) {\n const data = await res.json();\n if (data.current) {\n const { state, version } = data.current;\n versions.set(event.storeKey, version);\n universe.getStore(event.storeKey, () => state).set(state);\n }\n return { ok: false, status: 409, json: data, reason: \"CONFLICT\" };\n }\n\n if (!res.ok) return { ok: false, status: res.status, json: await res.json() };\n\n const json = await res.json();\n if (json.event) {\n const { state, version } = json.event;\n if (typeof version === \"number\") {\n versions.set(event.storeKey, version);\n }\n // We don't strictly need to set the store here because we already did it optimistically?\n // BUT, if the server modified it (e.g. server-side logic), we should.\n // However, for pure optimistic updates, we rely on the fact that we already set it.\n // The critical part is THE VERSION.\n // Let's just update the version to avoid the 409.\n }\n\n return { ok: true, status: 200, json };\n } catch (err) {\n return { ok: false, status: 500, json: err, reason: \"NETWORK_ERROR\" };\n }\n },\n unsubscribe: () => {\n es?.close();\n },\n };\n}\n\n// --- BroadcastChannel ---\n\nexport type BCOptions = {\n channelName: string;\n clientId: string;\n onEvent: (event: ZunoStateEvent) => void;\n getSnapshot: () => Record<string, { state: unknown; version: number }>;\n onSnapshot: (snap: Record<string, { state: unknown; version: number }>) => void;\n};\n\nexport function startBroadcastChannel(opts: BCOptions) {\n const { channelName, clientId, onEvent, getSnapshot, onSnapshot } = opts;\n const channel = new BroadcastChannel(channelName);\n\n channel.onmessage = (e) => {\n const msg = e.data;\n if (msg.origin === clientId) return;\n\n if (msg.type === \"event\") onEvent(msg.event);\n if (msg.type === \"hello\") channel.postMessage({ type: \"snapshot\", snapshot: getSnapshot(), origin: clientId });\n if (msg.type === \"snapshot\") onSnapshot(msg.snapshot);\n };\n\n return {\n publish: (event: ZunoStateEvent) => channel.postMessage({ type: \"event\", event, origin: clientId }),\n hello: () => channel.postMessage({ type: \"hello\", origin: clientId }),\n stop: () => channel.close(),\n };\n}\n","import { startSSE, startBroadcastChannel, applyIncomingEvent } from \"../sync\";\nimport type { ZunoStateEvent } from \"../sync\";\n\n// --- Types ---\n\n/**\n * Authoritative snapshot of the entire universe.\n */\nexport type ZunoSnapshot = {\n state: Record<string, { state: unknown; version: number }>;\n lastEventId: number;\n};\n\n/**\n * A simple state container for a single keyed value.\n */\nexport interface Store<T> {\n get(): T;\n set(next: T | ((prev: T) => T)): void;\n subscribe(listener: (state: T) => void): () => void;\n}\n\n/**\n * A Universe manages many stores.\n */\nexport interface Universe {\n getStore<T>(key: string, init: () => T): Store<T>;\n snapshot(): Record<string, unknown>;\n restore(data: Record<string, unknown>): void;\n delete(key: string): void;\n clear(): void;\n hydrateSnapshot(snapshot: ZunoSnapshot): void;\n}\n\n/**\n * Options for creating a Zuno instance.\n */\nexport type CreateZunoOptions = {\n /** Optional pre-existing universe. */\n universe?: Universe;\n /** BroadcastChannel name for local tab sync. */\n channelName?: string;\n /** SSE endpoint URL. */\n sseUrl?: string;\n /** Sync endpoint URL (required if sseUrl is provided). */\n syncUrl?: string;\n /** Apply updates locally before server confirmation (default: true). */\n optimistic?: boolean;\n /** Unique client identifier (default: random UUID). */\n clientId?: string;\n};\n\n/**\n * An extended interface for a Zuno store that includes methods for setting state\n * and a unique key identifier. This represents a store that has been \"bound\" or registered.\n */\nexport type BoundStore<T> = {\n key: string;\n get: () => T;\n set: (next: T | ((prev: T) => T)) => Promise<any>;\n subscribe: (cb: (state: T) => void) => () => void;\n raw: () => Store<T>;\n};\n\n// --- Store Implementation ---\n\n/**\n * Creates a raw ZUNO state management store.\n */\nexport const createStore = <T>(initial: T): Store<T> => {\n let state = initial;\n const listeners = new Set<(state: T) => void>();\n\n return {\n get: () => state,\n set: (next) => {\n const value = typeof next === \"function\" ? (next as (prev: T) => T)(state) : next;\n if (Object.is(value, state)) return;\n state = value;\n listeners.forEach((l) => l(state));\n },\n subscribe: (listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n};\n\n// --- Universe Implementation ---\n\n/**\n * Creates a ZUNO Universe to manage multiple stores.\n */\nexport const createUniverse = (): Universe => {\n const stores = new Map<string, Store<any>>();\n\n const universe: Universe = {\n getStore<T>(key: string, init: () => T): Store<T> {\n if (!stores.has(key)) {\n stores.set(key, createStore(init()));\n }\n return stores.get(key)! as Store<T>;\n },\n snapshot(): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, store] of stores.entries()) {\n out[key] = store.get();\n }\n return out;\n },\n restore(data: Record<string, unknown>): void {\n for (const [key, value] of Object.entries(data)) {\n const existing = stores.get(key);\n if (existing) {\n existing.set(value as any);\n } else {\n stores.set(key, createStore(value as any));\n }\n }\n },\n delete(key: string): void {\n stores.delete(key);\n },\n clear(): void {\n stores.clear();\n },\n hydrateSnapshot(snapshot: ZunoSnapshot) {\n const plain: Record<string, unknown> = {};\n for (const [k, rec] of Object.entries(snapshot.state)) {\n plain[k] = rec.state;\n }\n this.restore(plain);\n },\n };\n\n return universe;\n};\n\n// --- Main Zuno Factory ---\n\n/**\n * Creates a Zuno instance for distributed state synchronization.\n */\nexport const createZuno = (opts: CreateZunoOptions = {}) => {\n const localState = new Map<string, unknown>();\n const versions = new Map<string, number>();\n const universe = opts.universe ?? createUniverse();\n const clientId = opts.clientId ?? (globalThis.crypto?.randomUUID?.() ?? String(Math.random()));\n let sseReady = false;\n let lastEventId = 0;\n\n function hydrateSnapshot(snapshot: ZunoSnapshot) {\n const plain: Record<string, unknown> = {};\n for (const [k, rec] of Object.entries(snapshot.state)) {\n plain[k] = rec.state;\n versions.set(k, rec.version);\n }\n universe.restore(plain);\n lastEventId = snapshot.lastEventId;\n }\n\n const apply = (event: ZunoStateEvent) => {\n if (typeof event.eventId === \"number\") {\n lastEventId = Math.max(lastEventId, event.eventId);\n }\n applyIncomingEvent(universe, event, { clientId, localState, versions });\n };\n\n const sse = opts.sseUrl && opts.syncUrl\n ? startSSE({\n universe,\n url: opts.sseUrl,\n syncUrl: opts.syncUrl,\n optimistic: opts.optimistic ?? true,\n clientId,\n versions,\n getLastEventId: () => lastEventId,\n onOpen: () => { sseReady = true; },\n onClose: () => { sseReady = false; },\n })\n : null;\n\n const bc = opts.channelName\n ? startBroadcastChannel({\n channelName: opts.channelName,\n clientId,\n onEvent: apply,\n getSnapshot: () => {\n const snap = universe.snapshot();\n const out: Record<string, { state: unknown; version: number }> = {};\n for (const [storeKey, state] of Object.entries(snap)) {\n out[storeKey] = { state, version: versions.get(storeKey) ?? 0 };\n }\n return out;\n },\n onSnapshot: (snap) => {\n for (const [storeKey, rec] of Object.entries(snap)) {\n apply({ storeKey, state: rec.state, version: rec.version });\n }\n },\n })\n : null;\n\n setTimeout(() => bc?.hello(), 0);\n\n const dispatch = async (event: ZunoStateEvent) => {\n if (sse && sseReady) {\n return await sse.dispatch({\n ...event,\n origin: clientId,\n baseVersion: versions.get(event.storeKey) ?? 0,\n });\n }\n\n const nextVersion = (versions.get(event.storeKey) ?? 0) + 1;\n apply({ ...event, version: nextVersion });\n versions.set(event.storeKey, nextVersion);\n\n if (bc) {\n bc.publish({ ...event, version: nextVersion, origin: clientId });\n }\n\n return { ok: true, status: 200, json: null };\n };\n\n const store = <T,>(storeKey: string, init: () => T): BoundStore<T> => {\n const rawStore = universe.getStore<T>(storeKey, init);\n return {\n key: storeKey,\n raw: () => rawStore,\n get: () => rawStore.get(),\n subscribe: (cb) => rawStore.subscribe(cb),\n set: (next) => {\n const prev = rawStore.get();\n const state = typeof next === \"function\" ? (next as (prev: T) => T)(prev) : next;\n return dispatch({ storeKey, state });\n },\n };\n };\n\n return {\n universe,\n clientId,\n store,\n getStore: universe.getStore.bind(universe),\n get: <T,>(key: string, init?: () => T) => universe.getStore<T>(key, init ?? (() => undefined as any)).get(),\n set: async <T,>(key: string, next: T | ((prev: T) => T), init?: () => T) => {\n const s = universe.getStore<T>(key, init ?? (() => undefined as any));\n const state = typeof next === \"function\" ? (next as (prev: T) => T)(s.get()) : next;\n return dispatch({ storeKey: key, state });\n },\n subscribe: <T,>(key: string, init: () => T, cb: (state: T) => void) => universe.getStore<T>(key, init).subscribe(cb),\n dispatch,\n stop: () => {\n sse?.unsubscribe?.();\n bc?.stop?.();\n },\n hydrateSnapshot,\n getLastEventId: () => lastEventId,\n setLastEventId: (id: number) => { lastEventId = id; },\n };\n};\n","/** Universal UI adapter contract */\nexport type ZunoReadable<T> = {\n /** Read current value (sync) */\n getSnapshot(): T;\n\n /**\n * Subscribe to changes.\n * Call `onChange()` whenever snapshot may have changed.\n * Return unsubscribe.\n */\n subscribe(onChange: () => void): () => void;\n\n /** Optional: React SSR (server snapshot) */\n getServerSnapshot?: () => T;\n};\n\n/** Minimal store shape that can be adapted into a readable */\nexport type ZunoSubscribableStore<T> = {\n get(): T;\n subscribe(cb: (state: T) => void): () => void;\n};\n\n/** Adapter helper: convert store => readable */\nexport function toReadable<T>(store: ZunoSubscribableStore<T>): ZunoReadable<T> {\n return {\n getSnapshot: () => store.get(),\n subscribe: (onChange) => store.subscribe(() => onChange()),\n };\n}\n"]}
|