@iadev93/zuno 0.0.13 → 0.0.14

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 CHANGED
@@ -214,6 +214,28 @@ The original module-level helpers remain available and use `defaultZunoServerSta
214
214
 
215
215
  `maxSubscriberBuffer` bounds pending messages per SSE subscriber. A slow subscriber that exhausts its buffer is disconnected and can recover through replay or snapshot fallback on reconnect.
216
216
 
217
+ ### Persistence and multiple server instances
218
+
219
+ Server state accepts a `ZunoServerPersistence` adapter for authoritative state
220
+ and replay history, plus a `ZunoServerEventBus` for live fan-out between server
221
+ instances. All instances for one namespace must share both resources.
222
+
223
+ ```ts
224
+ import {
225
+ createFileZunoServerPersistence,
226
+ createZunoServerState,
227
+ } from "@iadev93/zuno/server";
228
+
229
+ const server = createZunoServerState({
230
+ persistence: createFileZunoServerPersistence("./data/zuno.json"),
231
+ });
232
+ ```
233
+
234
+ Persistence adapters must implement `compareAndSet()` atomically so two server
235
+ processes cannot accept mutations based on the same version. See the
236
+ [server persistence guide](../../docs/server-persistence.md) for the complete
237
+ contract, shared-bus example, and production guidance.
238
+
217
239
  ### Snapshot handler
218
240
 
219
241
  The snapshot handler returns the current universe/store snapshot for new clients.
@@ -1,14 +1,14 @@
1
- 'use strict';var l=class{constructor(e={}){this.universeState=new Map;this.eventLog=[];this.listeners=new Set;this.nextEventId=1;let n=e.maxEvents??1e3,r=e.maxStateBytes??512*1024,s=e.maxSubscriberBuffer??1e3;if(!Number.isInteger(n)||n<1)throw new TypeError("maxEvents must be a positive integer");if(!Number.isInteger(r)||r<1)throw new TypeError("maxStateBytes must be a positive integer");if(!Number.isInteger(s)||s<1)throw new TypeError("maxSubscriberBuffer must be a positive integer");this.maxEvents=n,this.maxStateBytes=r,this.maxSubscriberBuffer=s;}getUniverseRecord(e){return this.universeState.get(e)}updateUniverseState(e){let n=this.universeState.get(e.storeKey)??{version:0},r=typeof e.version=="number"?e.version:n.version+1;this.universeState.set(e.storeKey,{state:e.state,version:r});}getUniverseState(){return Object.fromEntries(this.universeState)}appendEvent(e){return e.eventId=this.nextEventId++,this.eventLog.push(e),this.eventLog.length>this.maxEvents&&this.eventLog.shift(),e}getEventsAfter(e){return this.eventLog.filter(n=>(n.eventId??0)>e)}canReplayAfter(e){let n=this.getLastEventId();if(e===n)return true;let r=this.eventLog[0]?.eventId;return typeof r=="number"&&e>=r-1&&e<n}getLastEventId(){return this.eventLog[this.eventLog.length-1]?.eventId??0}subscribeToStateEvents(e){return this.listeners.add(e),()=>{this.listeners.delete(e);}}publishToStateEvent(e){this.listeners.forEach(n=>{n(e);});}clear(){this.universeState.clear(),this.eventLog.length=0,this.nextEventId=1;}},b=(t={})=>new l(t),g=class{constructor(e={}){this.serverOptions=e;this.servers=new Map;}get(e){if(e.trim().length===0)throw new TypeError("namespace must be a non-empty string");let n=this.servers.get(e);return n||(n=b(this.serverOptions),this.servers.set(e,n)),n}delete(e){return this.servers.delete(e)}clear(){this.servers.clear();}},L=(t={})=>new g(t),a=b(),T=t=>a.getUniverseRecord(t),R=t=>a.updateUniverseState(t),C=()=>a.getUniverseState(),k=t=>a.appendEvent(t),A=t=>a.getEventsAfter(t),B=()=>a.getLastEventId(),U=t=>a.subscribeToStateEvents(t),V=t=>a.publishToStateEvent(t);var x=t=>typeof t=="object"&&t!==null&&!Array.isArray(t);function O(t,e=512*1024){if(!x(t))return [{field:"event",message:"Event must be an object"}];let n=[];if((typeof t.storeKey!="string"||t.storeKey.trim().length===0||t.storeKey.length>256)&&n.push({field:"storeKey",message:"storeKey must be a non-empty string of at most 256 characters"}),!("state"in t))n.push({field:"state",message:"state is required"});else try{let r=JSON.stringify(t.state);r===void 0?n.push({field:"state",message:"state must be JSON-serializable"}):new TextEncoder().encode(r).byteLength>e&&n.push({field:"state",message:`state exceeds the ${e} byte limit`});}catch{n.push({field:"state",message:"state must be JSON-serializable"});}for(let r of ["version","baseVersion","eventId"]){let s=t[r];s!==void 0&&(!Number.isInteger(s)||s<0)&&n.push({field:r,message:`${r} must be a non-negative integer when provided`});}for(let r of ["origin"]){let s=t[r];s!==void 0&&typeof s!="string"&&n.push({field:r,message:`${r} must be a string when provided`});}return t.ts!==void 0&&!Number.isFinite(t.ts)&&n.push({field:"ts",message:"ts must be a finite number when provided"}),t.intent!==void 0&&(!x(t.intent)||typeof t.intent.type!="string"||t.intent.type.trim().length===0)&&n.push({field:"intent",message:"intent must contain a non-empty string type"}),n}function I(t,e=a){let n=O(t,e.maxStateBytes);if(n.length>0)return {ok:false,reason:"INVALID_EVENT",errors:n};let r=t,s=e.getUniverseRecord(r.storeKey)??{state:void 0,version:0};if(typeof r.baseVersion=="number"&&r.baseVersion!==s.version)return {ok:false,reason:"VERSION_CONFLICT",current:s};let i={...r,version:s.version+1};return e.updateUniverseState(i),e.appendEvent(i),e.publishToStateEvent(i),{ok:true,event:i}}function H(t,e,n=a){let r={state:n.getUniverseState(),lastEventId:n.getLastEventId()};"json"in e&&typeof e.json=="function"?e.json(r):(e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify(r)));}var F=(t,e,n,r=a)=>{e.writeHead(200,{"Cache-Control":"no-cache, no-transform","Content-Type":"text/event-stream; charset=utf-8",Connection:"keep-alive","X-Accel-Buffering":"no",...n}),e.flushHeaders?.();let s=t.headers["last-event-id"]||new URL(t.url||"","http://localhost").searchParams.get("lastEventId"),i=Number.parseInt(Array.isArray(s)?s[0]:s??"0",10)||0,u=[],f=[],m=true,c=false,v=false,p=null,y=()=>{},d=()=>{v||(v=true,p&&clearInterval(p),y(),e.end());},h=()=>{for(c=false;!v&&f.length>0;){let o=f.shift();if(o&&!e.write(E(o))){c=true,e.once("drain",h);break}}},E=o=>`id: ${o.eventId}
1
+ 'use strict';var fs=require('fs'),path=require('path');var v=()=>({universe:{},events:[],nextEventId:1}),m=t=>structuredClone(t),y=(t,e,r)=>{let n=t.universe[e.storeKey]??{state:void 0,version:0};if(typeof e.baseVersion=="number"&&e.baseVersion!==n.version)return {ok:false,current:structuredClone(n)};let s={...structuredClone(e),version:n.version+1,eventId:t.nextEventId++};return t.universe[e.storeKey]={state:structuredClone(e.state),version:s.version},t.events.push(s),t.events.length>r&&t.events.splice(0,t.events.length-r),{ok:true,event:structuredClone(s)}},h=class{constructor(e=v()){this.state=m(e);}load(){return m(this.state)}save(e){this.state=m(e);}compareAndSet(e,r){let n=y(this.state,e,r);return structuredClone(n)}},C=t=>new h(t);var E=class{constructor(e={}){this.listeners=new Set;let r=e.maxEvents??1e3,n=e.maxStateBytes??512*1024,s=e.maxSubscriberBuffer??1e3;if(!Number.isInteger(r)||r<1)throw new TypeError("maxEvents must be a positive integer");if(!Number.isInteger(n)||n<1)throw new TypeError("maxStateBytes must be a positive integer");if(!Number.isInteger(s)||s<1)throw new TypeError("maxSubscriberBuffer must be a positive integer");this.maxEvents=r,this.maxStateBytes=n,this.maxSubscriberBuffer=s,this.persistence=e.persistence??C(),this.eventBus=e.eventBus,this.instanceId=e.instanceId??crypto.randomUUID(),this.unsubscribeFromEventBus=this.eventBus?.subscribe(u=>{u.source!==this.instanceId&&this.notifyLocalListeners(u.event);});}getUniverseRecord(e){return this.persistence.load().universe[e]}updateUniverseState(e){let r=this.persistence.load(),n=r.universe[e.storeKey]??{version:0},s=typeof e.version=="number"?e.version:n.version+1;r.universe[e.storeKey]={state:e.state,version:s},this.persistence.save(r);}getUniverseState(){return this.persistence.load().universe}appendEvent(e){let r=this.persistence.load();return e.eventId=r.nextEventId++,r.events.push(e),r.events.length>this.maxEvents&&r.events.splice(0,r.events.length-this.maxEvents),this.persistence.save(r),e}compareAndSet(e){return this.persistence.compareAndSet(e,this.maxEvents)}getEventsAfter(e){return this.persistence.load().events.filter(r=>(r.eventId??0)>e)}canReplayAfter(e){let r=this.getLastEventId();if(e===r)return true;let n=this.persistence.load().events[0]?.eventId;return typeof n=="number"&&e>=n-1&&e<r}getLastEventId(){let e=this.persistence.load().events;return e[e.length-1]?.eventId??0}subscribeToStateEvents(e){return this.listeners.add(e),()=>{this.listeners.delete(e);}}publishToStateEvent(e){this.notifyLocalListeners(e),this.eventBus?.publish({source:this.instanceId,event:e});}notifyLocalListeners(e){this.listeners.forEach(r=>{r(e);});}clear(){this.persistence.save(v());}dispose(){this.unsubscribeFromEventBus?.(),this.unsubscribeFromEventBus=void 0,this.listeners.clear();}},N=(t={})=>new E(t),g=class{constructor(e={}){this.serverOptions=e;this.servers=new Map;}get(e){if(e.trim().length===0)throw new TypeError("namespace must be a non-empty string");let r=this.servers.get(e);return r||(r=N(this.serverOptions),this.servers.set(e,r)),r}delete(e){return this.servers.delete(e)}clear(){this.servers.clear();}},j=(t={})=>new g(t),i=N(),H=t=>i.getUniverseRecord(t),K=t=>i.updateUniverseState(t),Y=()=>i.getUniverseState(),X=t=>i.appendEvent(t),z=t=>i.getEventsAfter(t),W=()=>i.getLastEventId(),G=t=>i.subscribeToStateEvents(t),Q=t=>i.publishToStateEvent(t);var P=t=>typeof t=="object"&&t!==null&&!Array.isArray(t);function R(t,e=512*1024){if(!P(t))return [{field:"event",message:"Event must be an object"}];let r=[];if((typeof t.storeKey!="string"||t.storeKey.trim().length===0||t.storeKey.length>256)&&r.push({field:"storeKey",message:"storeKey must be a non-empty string of at most 256 characters"}),!("state"in t))r.push({field:"state",message:"state is required"});else try{let n=JSON.stringify(t.state);n===void 0?r.push({field:"state",message:"state must be JSON-serializable"}):new TextEncoder().encode(n).byteLength>e&&r.push({field:"state",message:`state exceeds the ${e} byte limit`});}catch{r.push({field:"state",message:"state must be JSON-serializable"});}for(let n of ["version","baseVersion","eventId"]){let s=t[n];s!==void 0&&(!Number.isInteger(s)||s<0)&&r.push({field:n,message:`${n} must be a non-negative integer when provided`});}for(let n of ["origin"]){let s=t[n];s!==void 0&&typeof s!="string"&&r.push({field:n,message:`${n} must be a string when provided`});}return t.ts!==void 0&&!Number.isFinite(t.ts)&&r.push({field:"ts",message:"ts must be a finite number when provided"}),t.intent!==void 0&&(!P(t.intent)||typeof t.intent.type!="string"||t.intent.type.trim().length===0)&&r.push({field:"intent",message:"intent must contain a non-empty string type"}),r}function B(t,e=i){let r=R(t,e.maxStateBytes);if(r.length>0)return {ok:false,reason:"INVALID_EVENT",errors:r};let n=e.compareAndSet(t);if(!n.ok)return {ok:false,reason:"VERSION_CONFLICT",current:n.current};let s=n.event;return e.publishToStateEvent(s),{ok:true,event:s}}var b=class{constructor(){this.listeners=new Set;}publish(e){for(let r of this.listeners)r(structuredClone(e));}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}},re=()=>new b;var x=class{constructor(e,r={}){this.filePath=e;if(this.lockPath=`${e}.lock`,this.lockTimeoutMs=r.lockTimeoutMs??5e3,this.staleLockMs=r.staleLockMs??3e4,!Number.isInteger(this.lockTimeoutMs)||this.lockTimeoutMs<1)throw new TypeError("lockTimeoutMs must be a positive integer");if(!Number.isInteger(this.staleLockMs)||this.staleLockMs<1)throw new TypeError("staleLockMs must be a positive integer");fs.mkdirSync(path.dirname(e),{recursive:true});}read(){try{return JSON.parse(fs.readFileSync(this.filePath,"utf8"))}catch(e){if(e.code==="ENOENT")return v();throw e}}write(e){let r=`${this.filePath}.${process.pid}.${crypto.randomUUID()}.tmp`;try{fs.writeFileSync(r,JSON.stringify(e),"utf8"),fs.renameSync(r,this.filePath);}finally{fs.rmSync(r,{force:true});}}withLock(e){let r=Date.now();for(;;)try{fs.mkdirSync(this.lockPath);break}catch(n){if(n.code!=="EEXIST")throw n;try{if(Date.now()-fs.statSync(this.lockPath).mtimeMs>=this.staleLockMs){fs.rmSync(this.lockPath,{recursive:!0,force:!0});continue}}catch(s){if(s.code==="ENOENT")continue;throw s}if(Date.now()-r>=this.lockTimeoutMs)throw new Error(`Timed out acquiring Zuno lock: ${this.lockPath}`);Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,5);}try{return e()}finally{fs.rmSync(this.lockPath,{recursive:true,force:true});}}load(){return structuredClone(this.read())}save(e){this.withLock(()=>this.write(structuredClone(e)));}compareAndSet(e,r){return this.withLock(()=>{let n=this.read(),s=y(n,e,r);return s.ok&&this.write(n),s})}},ae=(t,e)=>new x(t,e);function ve(t,e,r=i){let n={state:r.getUniverseState(),lastEventId:r.getLastEventId()};"json"in e&&typeof e.json=="function"?e.json(n):(e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify(n)));}var Se=(t,e,r,n=i)=>{e.writeHead(200,{"Cache-Control":"no-cache, no-transform","Content-Type":"text/event-stream; charset=utf-8",Connection:"keep-alive","X-Accel-Buffering":"no",...r}),e.flushHeaders?.();let s=t.headers["last-event-id"]||new URL(t.url||"","http://localhost").searchParams.get("lastEventId"),u=Number.parseInt(Array.isArray(s)?s[0]:s??"0",10)||0,a=[],p=[],w=true,d=false,c=false,l=null,I=()=>{},S=()=>{c||(c=true,l&&clearInterval(l),I(),e.end());},k=()=>{for(d=false;!c&&p.length>0;){let o=p.shift();if(o&&!e.write(T(o))){d=true,e.once("drain",k);break}}},T=o=>`id: ${o.eventId}
2
2
  event: state
3
3
  data: ${JSON.stringify(o)}
4
4
 
5
- `,S=o=>{if(!v){if(c){if(f.length>=r.maxSubscriberBuffer){d();return}f.push(o);return}e.write(E(o))||(c=true,e.once("drain",h));}},w=()=>{e.write(`id: ${r.getLastEventId()}
5
+ `,f=o=>{if(!c){if(d){if(p.length>=n.maxSubscriberBuffer){S();return}p.push(o);return}e.write(T(o))||(d=true,e.once("drain",k));}},O=()=>{e.write(`id: ${n.getLastEventId()}
6
6
  `),e.write(`event: snapshot
7
- `),e.write(`data: ${JSON.stringify(r.getUniverseState())}
7
+ `),e.write(`data: ${JSON.stringify(n.getUniverseState())}
8
8
 
9
- `);};if(y=r.subscribeToStateEvents(o=>{if(m){if(u.length>=r.maxSubscriberBuffer){d();return}u.push(o);}else S(o);}),i>0&&r.canReplayAfter(i)){let o=r.getEventsAfter(i);for(let Z of o)S(Z);}else w();for(m=false;u.length>0;){let o=u.shift();o&&S(o);}v||(p=setInterval(()=>{v||e.write(`: ping ${Date.now()}
9
+ `);};if(I=n.subscribeToStateEvents(o=>{if(w){if(a.length>=n.maxSubscriberBuffer){S();return}a.push(o);}else f(o);}),u>0&&n.canReplayAfter(u)){let o=n.getEventsAfter(u);for(let L of o)f(L);}else O();for(w=false;a.length>0;){let o=a.shift();o&&f(o);}c||(l=setInterval(()=>{c||e.write(`: ping ${Date.now()}
10
10
 
11
11
  `);},15e3),e.write(`: connected
12
12
 
13
- `),t.on("close",()=>{d();}));},N=(t,e,n=a)=>{let s="";t.on("data",i=>{s+=i.toString("utf8"),s.length>524288&&(e.writeHead(413,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:false,reason:"PAYLOAD_TOO_LARGE"})),t.destroy());}),t.on("end",()=>{try{let i=JSON.parse(s||"{}"),u=I(i,n);if(!u.ok){u.reason==="VERSION_CONFLICT"?(e.writeHead(409,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:!1,reason:"VERSION_CONFLICT",current:u.current}))):(e.writeHead(400,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:!1,reason:u.reason,errors:u.errors})));return}e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:!0,event:u.event}));}catch{e.writeHead(400,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:false,reason:"INVALID_JSON"}));}});},Y=(t,e,n=a)=>N(t,e,n);exports.ZunoServerRegistry=g;exports.ZunoServerState=l;exports.appendEvent=k;exports.applyStateEvent=I;exports.createSSEConnection=F;exports.createZunoServerRegistry=L;exports.createZunoServerState=b;exports.defaultZunoServerState=a;exports.getEventsAfter=A;exports.getLastEventId=B;exports.getUniverseRecord=T;exports.getUniverseState=C;exports.publishToStateEvent=V;exports.sendSnapshot=H;exports.setUniverseState=Y;exports.subscribeToStateEvents=U;exports.syncUniverseState=N;exports.updateUniverseState=R;exports.validateStateEvent=O;//# sourceMappingURL=index.cjs.map
13
+ `),t.on("close",()=>{S();}));},_=(t,e,r=i)=>{let s="";t.on("data",u=>{s+=u.toString("utf8"),s.length>524288&&(e.writeHead(413,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:false,reason:"PAYLOAD_TOO_LARGE"})),t.destroy());}),t.on("end",()=>{try{let u=JSON.parse(s||"{}"),a=B(u,r);if(!a.ok){a.reason==="VERSION_CONFLICT"?(e.writeHead(409,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:!1,reason:"VERSION_CONFLICT",current:a.current}))):(e.writeHead(400,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:!1,reason:a.reason,errors:a.errors})));return}e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:!0,event:a.event}));}catch{e.writeHead(400,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:false,reason:"INVALID_JSON"}));}});},fe=(t,e,r=i)=>_(t,e,r);exports.FileZunoServerPersistence=x;exports.MemoryZunoServerEventBus=b;exports.MemoryZunoServerPersistence=h;exports.ZunoServerRegistry=g;exports.ZunoServerState=E;exports.appendEvent=X;exports.applyCompareAndSet=y;exports.applyStateEvent=B;exports.createEmptyPersistedServerState=v;exports.createFileZunoServerPersistence=ae;exports.createMemoryZunoServerEventBus=re;exports.createMemoryZunoServerPersistence=C;exports.createSSEConnection=Se;exports.createZunoServerRegistry=j;exports.createZunoServerState=N;exports.defaultZunoServerState=i;exports.getEventsAfter=z;exports.getLastEventId=W;exports.getUniverseRecord=H;exports.getUniverseState=Y;exports.publishToStateEvent=Q;exports.sendSnapshot=ve;exports.setUniverseState=fe;exports.subscribeToStateEvents=G;exports.syncUniverseState=_;exports.updateUniverseState=K;exports.validateStateEvent=R;//# sourceMappingURL=index.cjs.map
14
14
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/core.ts","../../src/server/apply-state-event.ts","../../src/server/snapshot-handler.ts","../../src/server/sse-handler.ts"],"names":["ZunoServerState","options","maxEvents","maxStateBytes","maxSubscriberBuffer","storeKey","event","current","nextVersion","lastEventId","latest","first","listener","createZunoServerState","ZunoServerRegistry","serverOptions","namespace","server","createZunoServerRegistry","defaultZunoServerState","getUniverseRecord","updateUniverseState","getUniverseState","appendEvent","getEventsAfter","getLastEventId","subscribeToStateEvents","publishToStateEvent","isRecord","value","validateStateEvent","input","errors","serialized","field","applyStateEvent","incoming","authoritativeEvent","sendSnapshot","_req","res","snapshot","createSSEConnection","req","headers","raw","buffer","pendingWrites","isSyncing","backpressured","closed","heartbeat","unsubscribe","closeConnection","flushPendingWrites","formatStateEvent","writeEvent","writeSnapshot","missed","syncUniverseState","body","chunk","result","setUniverseState"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { ZunoStateEvent } from \"../sync\";\n\nexport type UniverseRecord = {\n\tstate: unknown;\n\tversion: number;\n};\n\nexport type ZunoStateListener = (event: ZunoStateEvent) => void;\n\nexport type CreateZunoServerStateOptions = {\n\t/** Maximum number of authoritative events retained for SSE replay. */\n\tmaxEvents?: number;\n\t/** Maximum serialized state size accepted per event. */\n\tmaxStateBytes?: number;\n\t/** Maximum events buffered for a slow SSE subscriber. */\n\tmaxSubscriberBuffer?: number;\n};\n\n/**\n * Isolated authoritative state used by server adapters.\n * Create one instance per application, namespace, or tenant boundary.\n */\nexport class ZunoServerState {\n\treadonly maxEvents: number;\n\treadonly maxStateBytes: number;\n\treadonly maxSubscriberBuffer: number;\n\tprivate readonly universeState = new Map<string, UniverseRecord>();\n\tprivate readonly eventLog: ZunoStateEvent[] = [];\n\tprivate readonly listeners = new Set<ZunoStateListener>();\n\tprivate nextEventId = 1;\n\n\tconstructor(options: CreateZunoServerStateOptions = {}) {\n\t\tconst maxEvents = options.maxEvents ?? 1000;\n\t\tconst maxStateBytes = options.maxStateBytes ?? 512 * 1024;\n\t\tconst maxSubscriberBuffer = options.maxSubscriberBuffer ?? 1000;\n\t\tif (!Number.isInteger(maxEvents) || maxEvents < 1) {\n\t\t\tthrow new TypeError(\"maxEvents must be a positive integer\");\n\t\t}\n\t\tif (!Number.isInteger(maxStateBytes) || maxStateBytes < 1) {\n\t\t\tthrow new TypeError(\"maxStateBytes must be a positive integer\");\n\t\t}\n\t\tif (!Number.isInteger(maxSubscriberBuffer) || maxSubscriberBuffer < 1) {\n\t\t\tthrow new TypeError(\"maxSubscriberBuffer must be a positive integer\");\n\t\t}\n\t\tthis.maxEvents = maxEvents;\n\t\tthis.maxStateBytes = maxStateBytes;\n\t\tthis.maxSubscriberBuffer = maxSubscriberBuffer;\n\t}\n\n\tgetUniverseRecord(storeKey: string): UniverseRecord | undefined {\n\t\treturn this.universeState.get(storeKey);\n\t}\n\n\tupdateUniverseState(event: ZunoStateEvent): void {\n\t\tconst current = this.universeState.get(event.storeKey) ?? {\n\t\t\tstate: undefined,\n\t\t\tversion: 0,\n\t\t};\n\t\tconst nextVersion =\n\t\t\ttypeof event.version === \"number\" ? event.version : current.version + 1;\n\t\tthis.universeState.set(event.storeKey, {\n\t\t\tstate: event.state,\n\t\t\tversion: nextVersion,\n\t\t});\n\t}\n\n\tgetUniverseState(): Record<string, UniverseRecord> {\n\t\treturn Object.fromEntries(this.universeState);\n\t}\n\n\tappendEvent(event: ZunoStateEvent): ZunoStateEvent {\n\t\tevent.eventId = this.nextEventId++;\n\t\tthis.eventLog.push(event);\n\t\tif (this.eventLog.length > this.maxEvents) {\n\t\t\tthis.eventLog.shift();\n\t\t}\n\t\treturn event;\n\t}\n\n\tgetEventsAfter(lastEventId: number): ZunoStateEvent[] {\n\t\treturn this.eventLog.filter((event) => (event.eventId ?? 0) > lastEventId);\n\t}\n\n\tcanReplayAfter(lastEventId: number): boolean {\n\t\tconst latest = this.getLastEventId();\n\t\tif (lastEventId === latest) return true;\n\t\tconst first = this.eventLog[0]?.eventId;\n\t\treturn (\n\t\t\ttypeof first === \"number\" &&\n\t\t\tlastEventId >= first - 1 &&\n\t\t\tlastEventId < latest\n\t\t);\n\t}\n\n\tgetLastEventId(): number {\n\t\treturn this.eventLog[this.eventLog.length - 1]?.eventId ?? 0;\n\t}\n\n\tsubscribeToStateEvents(listener: ZunoStateListener): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => {\n\t\t\tthis.listeners.delete(listener);\n\t\t};\n\t}\n\n\tpublishToStateEvent(event: ZunoStateEvent): void {\n\t\tthis.listeners.forEach((listener) => {\n\t\t\tlistener(event);\n\t\t});\n\t}\n\n\tclear(): void {\n\t\tthis.universeState.clear();\n\t\tthis.eventLog.length = 0;\n\t\tthis.nextEventId = 1;\n\t}\n}\n\nexport const createZunoServerState = (\n\toptions: CreateZunoServerStateOptions = {},\n) => new ZunoServerState(options);\n\n/** Lazily creates isolated server states for application namespaces or tenants. */\nexport class ZunoServerRegistry {\n\tprivate readonly servers = new Map<string, ZunoServerState>();\n\n\tconstructor(\n\t\tprivate readonly serverOptions: CreateZunoServerStateOptions = {},\n\t) {}\n\n\tget(namespace: string): ZunoServerState {\n\t\tif (namespace.trim().length === 0) {\n\t\t\tthrow new TypeError(\"namespace must be a non-empty string\");\n\t\t}\n\t\tlet server = this.servers.get(namespace);\n\t\tif (!server) {\n\t\t\tserver = createZunoServerState(this.serverOptions);\n\t\t\tthis.servers.set(namespace, server);\n\t\t}\n\t\treturn server;\n\t}\n\n\tdelete(namespace: string): boolean {\n\t\treturn this.servers.delete(namespace);\n\t}\n\n\tclear(): void {\n\t\tthis.servers.clear();\n\t}\n}\n\nexport const createZunoServerRegistry = (\n\toptions: CreateZunoServerStateOptions = {},\n) => new ZunoServerRegistry(options);\n\n/** Backward-compatible singleton used by the original module-level helpers. */\nexport const defaultZunoServerState = createZunoServerState();\n\nexport const getUniverseRecord = (storeKey: string) =>\n\tdefaultZunoServerState.getUniverseRecord(storeKey);\nexport const updateUniverseState = (event: ZunoStateEvent) =>\n\tdefaultZunoServerState.updateUniverseState(event);\nexport const getUniverseState = () => defaultZunoServerState.getUniverseState();\nexport const appendEvent = (event: ZunoStateEvent) =>\n\tdefaultZunoServerState.appendEvent(event);\nexport const getEventsAfter = (lastEventId: number) =>\n\tdefaultZunoServerState.getEventsAfter(lastEventId);\nexport const getLastEventId = () => defaultZunoServerState.getLastEventId();\nexport const subscribeToStateEvents = (listener: ZunoStateListener) =>\n\tdefaultZunoServerState.subscribeToStateEvents(listener);\nexport const publishToStateEvent = (event: ZunoStateEvent) =>\n\tdefaultZunoServerState.publishToStateEvent(event);\n","import type { ZunoStateEvent } from \"../sync\";\nimport { defaultZunoServerState, type ZunoServerState } from \"./core\";\n\nexport type EventValidationError = {\n\tfield: string;\n\tmessage: string;\n};\n\nexport type ApplyResult =\n\t| { ok: true; event: ZunoStateEvent }\n\t| {\n\t\t\tok: false;\n\t\t\treason: \"VERSION_CONFLICT\";\n\t\t\tcurrent: { state: unknown; version: number };\n\t\t\terrors?: never;\n\t }\n\t| {\n\t\t\tok: false;\n\t\t\treason: \"INVALID_EVENT\";\n\t\t\terrors: EventValidationError[];\n\t\t\tcurrent?: never;\n\t };\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n\ttypeof value === \"object\" && value !== null && !Array.isArray(value);\n\nexport function validateStateEvent(\n\tinput: unknown,\n\tmaxStateBytes = 512 * 1024,\n): EventValidationError[] {\n\tif (!isRecord(input)) {\n\t\treturn [{ field: \"event\", message: \"Event must be an object\" }];\n\t}\n\n\tconst errors: EventValidationError[] = [];\n\tif (\n\t\ttypeof input.storeKey !== \"string\" ||\n\t\tinput.storeKey.trim().length === 0 ||\n\t\tinput.storeKey.length > 256\n\t) {\n\t\terrors.push({\n\t\t\tfield: \"storeKey\",\n\t\t\tmessage: \"storeKey must be a non-empty string of at most 256 characters\",\n\t\t});\n\t}\n\n\tif (!(\"state\" in input)) {\n\t\terrors.push({ field: \"state\", message: \"state is required\" });\n\t} else {\n\t\ttry {\n\t\t\tconst serialized = JSON.stringify(input.state);\n\t\t\tif (serialized === undefined) {\n\t\t\t\terrors.push({\n\t\t\t\t\tfield: \"state\",\n\t\t\t\t\tmessage: \"state must be JSON-serializable\",\n\t\t\t\t});\n\t\t\t} else if (\n\t\t\t\tnew TextEncoder().encode(serialized).byteLength > maxStateBytes\n\t\t\t) {\n\t\t\t\terrors.push({\n\t\t\t\t\tfield: \"state\",\n\t\t\t\t\tmessage: `state exceeds the ${maxStateBytes} byte limit`,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\terrors.push({\n\t\t\t\tfield: \"state\",\n\t\t\t\tmessage: \"state must be JSON-serializable\",\n\t\t\t});\n\t\t}\n\t}\n\n\tfor (const field of [\"version\", \"baseVersion\", \"eventId\"] as const) {\n\t\tconst value = input[field];\n\t\tif (\n\t\t\tvalue !== undefined &&\n\t\t\t(!Number.isInteger(value) || (value as number) < 0)\n\t\t) {\n\t\t\terrors.push({\n\t\t\t\tfield,\n\t\t\t\tmessage: `${field} must be a non-negative integer when provided`,\n\t\t\t});\n\t\t}\n\t}\n\n\tfor (const field of [\"origin\"] as const) {\n\t\tconst value = input[field];\n\t\tif (value !== undefined && typeof value !== \"string\") {\n\t\t\terrors.push({\n\t\t\t\tfield,\n\t\t\t\tmessage: `${field} must be a string when provided`,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (input.ts !== undefined && !Number.isFinite(input.ts)) {\n\t\terrors.push({\n\t\t\tfield: \"ts\",\n\t\t\tmessage: \"ts must be a finite number when provided\",\n\t\t});\n\t}\n\n\tif (input.intent !== undefined) {\n\t\tif (\n\t\t\t!isRecord(input.intent) ||\n\t\t\ttypeof input.intent.type !== \"string\" ||\n\t\t\tinput.intent.type.trim().length === 0\n\t\t) {\n\t\t\terrors.push({\n\t\t\t\tfield: \"intent\",\n\t\t\t\tmessage: \"intent must contain a non-empty string type\",\n\t\t\t});\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/** Validates and applies a state event to an isolated authoritative server. */\nexport function applyStateEvent(\n\tincoming: unknown,\n\tserver: ZunoServerState = defaultZunoServerState,\n): ApplyResult {\n\tconst errors = validateStateEvent(incoming, server.maxStateBytes);\n\tif (errors.length > 0) {\n\t\treturn { ok: false, reason: \"INVALID_EVENT\", errors };\n\t}\n\n\tconst event = incoming as ZunoStateEvent;\n\tconst current = server.getUniverseRecord(event.storeKey) ?? {\n\t\tstate: undefined,\n\t\tversion: 0,\n\t};\n\n\tif (\n\t\ttypeof event.baseVersion === \"number\" &&\n\t\tevent.baseVersion !== current.version\n\t) {\n\t\treturn { ok: false, reason: \"VERSION_CONFLICT\", current };\n\t}\n\n\tconst authoritativeEvent = { ...event, version: current.version + 1 };\n\tserver.updateUniverseState(authoritativeEvent);\n\tserver.appendEvent(authoritativeEvent);\n\tserver.publishToStateEvent(authoritativeEvent);\n\n\treturn { ok: true, event: authoritativeEvent };\n}\n","import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { defaultZunoServerState, type ZunoServerState } from \"./core\";\n\n/**\n * Sends a snapshot of the current universe state to the response.\n * Compatible with both Express and raw Node.js http.\n */\nexport function sendSnapshot(\n\t_req: IncomingMessage,\n\tres: ServerResponse,\n\tserver: ZunoServerState = defaultZunoServerState,\n) {\n\tconst snapshot = {\n\t\tstate: server.getUniverseState(),\n\t\tlastEventId: server.getLastEventId(),\n\t};\n\n\t// Check for Express-like .json() method\n\t// biome-ignore lint/suspicious/noExplicitAny: Checking for dynamic .json() method on response object\n\tif (\"json\" in res && typeof (res as any).json === \"function\") {\n\t\t// biome-ignore lint/suspicious/noExplicitAny: Calling dynamic .json() method\n\t\t(res as any).json(snapshot);\n\t} else {\n\t\tres.writeHead(200, { \"Content-Type\": \"application/json\" });\n\t\tres.end(JSON.stringify(snapshot));\n\t}\n}\n","import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport type { ZunoStateEvent } from \"../sync\";\nimport { applyStateEvent } from \"./apply-state-event\";\nimport { defaultZunoServerState, type ZunoServerState } from \"./core\";\n\ntype IncomingHeaders = IncomingMessage[\"headers\"];\n\n/**\n * Creates a Server-Sent Events (SSE) connection for Zuno state updates.\n */\nexport const createSSEConnection = (\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\theaders: IncomingHeaders,\n\tserver: ZunoServerState = defaultZunoServerState,\n) => {\n\tres.writeHead(200, {\n\t\t\"Cache-Control\": \"no-cache, no-transform\",\n\t\t\"Content-Type\": \"text/event-stream; charset=utf-8\",\n\t\tConnection: \"keep-alive\",\n\t\t\"X-Accel-Buffering\": \"no\",\n\t\t...headers,\n\t});\n\n\tres.flushHeaders?.();\n\n\tconst raw =\n\t\treq.headers[\"last-event-id\"] ||\n\t\tnew URL(req.url || \"\", \"http://localhost\").searchParams.get(\"lastEventId\");\n\tconst lastEventId =\n\t\tNumber.parseInt(Array.isArray(raw) ? raw[0] : (raw ?? \"0\"), 10) || 0;\n\n\t// 1. Subscribe FIRST and buffer events until snapshot/missed-events are sent\n\tconst buffer: ZunoStateEvent[] = [];\n\tconst pendingWrites: ZunoStateEvent[] = [];\n\tlet isSyncing = true;\n\tlet backpressured = false;\n\tlet closed = false;\n\tlet heartbeat: ReturnType<typeof setInterval> | null = null;\n\tlet unsubscribe = () => {};\n\n\tconst closeConnection = () => {\n\t\tif (closed) return;\n\t\tclosed = true;\n\t\tif (heartbeat) clearInterval(heartbeat);\n\t\tunsubscribe();\n\t\tres.end();\n\t};\n\tconst flushPendingWrites = () => {\n\t\tbackpressured = false;\n\t\twhile (!closed && pendingWrites.length > 0) {\n\t\t\tconst event = pendingWrites.shift();\n\t\t\tif (!event) continue;\n\t\t\tif (!res.write(formatStateEvent(event))) {\n\t\t\t\tbackpressured = true;\n\t\t\t\tres.once(\"drain\", flushPendingWrites);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t};\n\tconst formatStateEvent = (event: ZunoStateEvent) =>\n\t\t`id: ${event.eventId}\\nevent: state\\ndata: ${JSON.stringify(event)}\\n\\n`;\n\tconst writeEvent = (event: ZunoStateEvent) => {\n\t\tif (closed) return;\n\t\tif (backpressured) {\n\t\t\tif (pendingWrites.length >= server.maxSubscriberBuffer) {\n\t\t\t\tcloseConnection();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpendingWrites.push(event);\n\t\t\treturn;\n\t\t}\n\t\tif (!res.write(formatStateEvent(event))) {\n\t\t\tbackpressured = true;\n\t\t\tres.once(\"drain\", flushPendingWrites);\n\t\t}\n\t};\n\tconst writeSnapshot = () => {\n\t\tres.write(`id: ${server.getLastEventId()}\\n`);\n\t\tres.write(\"event: snapshot\\n\");\n\t\tres.write(`data: ${JSON.stringify(server.getUniverseState())}\\n\\n`);\n\t};\n\n\tunsubscribe = server.subscribeToStateEvents((event: ZunoStateEvent) => {\n\t\tif (isSyncing) {\n\t\t\tif (buffer.length >= server.maxSubscriberBuffer) {\n\t\t\t\tcloseConnection();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbuffer.push(event);\n\t\t} else {\n\t\t\twriteEvent(event);\n\t\t}\n\t});\n\n\t// 2. Send missed events or snapshot\n\tif (lastEventId > 0 && server.canReplayAfter(lastEventId)) {\n\t\tconst missed = server.getEventsAfter(lastEventId);\n\t\tfor (const event of missed) {\n\t\t\twriteEvent(event);\n\t\t}\n\t} else {\n\t\twriteSnapshot();\n\t}\n\n\t// 3. Flush buffer and switch to live mode\n\tisSyncing = false;\n\twhile (buffer.length > 0) {\n\t\tconst event = buffer.shift();\n\t\tif (event) writeEvent(event);\n\t}\n\tif (closed) return;\n\n\theartbeat = setInterval(() => {\n\t\tif (closed) return;\n\t\tres.write(`: ping ${Date.now()}\\n\\n`);\n\t}, 15000);\n\n\tres.write(\": connected \\n\\n\");\n\n\treq.on(\"close\", () => {\n\t\tcloseConnection();\n\t});\n};\n\n/**\n * Synchronizes the Zuno universe state by applying an incoming event.\n */\nexport const syncUniverseState = (\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tserver: ZunoServerState = defaultZunoServerState,\n) => {\n\tconst MAX_BODY_BYTES = 512 * 1024; // 512KB safety\n\tlet body = \"\";\n\n\treq.on(\"data\", (chunk: Buffer) => {\n\t\tbody += chunk.toString(\"utf8\");\n\t\tif (body.length > MAX_BODY_BYTES) {\n\t\t\tres.writeHead(413, { \"Content-Type\": \"application/json\" });\n\t\t\tres.end(JSON.stringify({ ok: false, reason: \"PAYLOAD_TOO_LARGE\" }));\n\t\t\treq.destroy();\n\t\t}\n\t});\n\n\treq.on(\"end\", () => {\n\t\ttry {\n\t\t\tconst incoming: ZunoStateEvent = JSON.parse(\n\t\t\t\tbody || \"{}\",\n\t\t\t) as unknown as ZunoStateEvent;\n\t\t\tconst result = applyStateEvent(incoming, server);\n\n\t\t\tif (!result.ok) {\n\t\t\t\tif (result.reason === \"VERSION_CONFLICT\") {\n\t\t\t\t\tres.writeHead(409, { \"Content-Type\": \"application/json\" });\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\treason: \"VERSION_CONFLICT\",\n\t\t\t\t\t\t\tcurrent: result.current,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tres.writeHead(400, { \"Content-Type\": \"application/json\" });\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\treason: result.reason,\n\t\t\t\t\t\t\terrors: result.errors,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tres.writeHead(200, { \"Content-Type\": \"application/json\" });\n\t\t\tres.end(JSON.stringify({ ok: true, event: result.event }));\n\t\t} catch {\n\t\t\tres.writeHead(400, { \"Content-Type\": \"application/json\" });\n\t\t\tres.end(JSON.stringify({ ok: false, reason: \"INVALID_JSON\" }));\n\t\t}\n\t});\n};\n\nexport const setUniverseState = (\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tserver: ZunoServerState = defaultZunoServerState,\n) => {\n\treturn syncUniverseState(req, res, server);\n};\n"]}
1
+ {"version":3,"sources":["../../src/server/persistence.ts","../../src/server/core.ts","../../src/server/apply-state-event.ts","../../src/server/event-bus.ts","../../src/server/file-persistence.ts","../../src/server/snapshot-handler.ts","../../src/server/sse-handler.ts"],"names":["createEmptyPersistedServerState","cloneState","state","applyCompareAndSet","event","maxEvents","current","authoritativeEvent","MemoryZunoServerPersistence","initialState","result","createMemoryZunoServerPersistence","ZunoServerState","options","maxStateBytes","maxSubscriberBuffer","message","storeKey","persisted","nextVersion","lastEventId","latest","first","events","listener","createZunoServerState","ZunoServerRegistry","serverOptions","namespace","server","createZunoServerRegistry","defaultZunoServerState","getUniverseRecord","updateUniverseState","getUniverseState","appendEvent","getEventsAfter","getLastEventId","subscribeToStateEvents","publishToStateEvent","isRecord","value","validateStateEvent","input","errors","serialized","field","applyStateEvent","incoming","MemoryZunoServerEventBus","createMemoryZunoServerEventBus","FileZunoServerPersistence","filePath","mkdirSync","dirname","readFileSync","error","temporaryPath","writeFileSync","renameSync","rmSync","operation","startedAt","statSync","lockError","createFileZunoServerPersistence","sendSnapshot","_req","res","snapshot","createSSEConnection","req","headers","raw","buffer","pendingWrites","isSyncing","backpressured","closed","heartbeat","unsubscribe","closeConnection","flushPendingWrites","formatStateEvent","writeEvent","writeSnapshot","missed","syncUniverseState","body","chunk","setUniverseState"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { ZunoStateEvent } from \"../sync\";\nimport type { UniverseRecord } from \"./core\";\n\nexport type ZunoPersistedServerState = {\n\tuniverse: Record<string, UniverseRecord>;\n\tevents: ZunoStateEvent[];\n\tnextEventId: number;\n};\n\nexport type ZunoCompareAndSetResult =\n\t| { ok: true; event: ZunoStateEvent }\n\t| { ok: false; current: UniverseRecord };\n\n/**\n * Authoritative persistence contract for server state and its replay log.\n * `compareAndSet` must atomically validate baseVersion, update state, assign an\n * event ID, and append/truncate the replay log.\n */\nexport interface ZunoServerPersistence {\n\tload(): ZunoPersistedServerState;\n\tsave(state: ZunoPersistedServerState): void;\n\tcompareAndSet(\n\t\tevent: ZunoStateEvent,\n\t\tmaxEvents: number,\n\t): ZunoCompareAndSetResult;\n}\n\nexport const createEmptyPersistedServerState =\n\t(): ZunoPersistedServerState => ({\n\t\tuniverse: {},\n\t\tevents: [],\n\t\tnextEventId: 1,\n\t});\n\nconst cloneState = (\n\tstate: ZunoPersistedServerState,\n): ZunoPersistedServerState => structuredClone(state);\n\nconst applyCompareAndSet = (\n\tstate: ZunoPersistedServerState,\n\tevent: ZunoStateEvent,\n\tmaxEvents: number,\n): ZunoCompareAndSetResult => {\n\tconst current = state.universe[event.storeKey] ?? {\n\t\tstate: undefined,\n\t\tversion: 0,\n\t};\n\tif (\n\t\ttypeof event.baseVersion === \"number\" &&\n\t\tevent.baseVersion !== current.version\n\t) {\n\t\treturn { ok: false, current: structuredClone(current) };\n\t}\n\n\tconst authoritativeEvent = {\n\t\t...structuredClone(event),\n\t\tversion: current.version + 1,\n\t\teventId: state.nextEventId++,\n\t};\n\tstate.universe[event.storeKey] = {\n\t\tstate: structuredClone(event.state),\n\t\tversion: authoritativeEvent.version,\n\t};\n\tstate.events.push(authoritativeEvent);\n\tif (state.events.length > maxEvents) {\n\t\tstate.events.splice(0, state.events.length - maxEvents);\n\t}\n\treturn { ok: true, event: structuredClone(authoritativeEvent) };\n};\n\n/** In-memory authoritative persistence for development and tests. */\nexport class MemoryZunoServerPersistence implements ZunoServerPersistence {\n\tprivate state: ZunoPersistedServerState;\n\n\tconstructor(initialState = createEmptyPersistedServerState()) {\n\t\tthis.state = cloneState(initialState);\n\t}\n\n\tload(): ZunoPersistedServerState {\n\t\treturn cloneState(this.state);\n\t}\n\n\tsave(state: ZunoPersistedServerState): void {\n\t\tthis.state = cloneState(state);\n\t}\n\n\tcompareAndSet(\n\t\tevent: ZunoStateEvent,\n\t\tmaxEvents: number,\n\t): ZunoCompareAndSetResult {\n\t\tconst result = applyCompareAndSet(this.state, event, maxEvents);\n\t\treturn structuredClone(result);\n\t}\n}\n\nexport const createMemoryZunoServerPersistence = (\n\tinitialState?: ZunoPersistedServerState,\n) => new MemoryZunoServerPersistence(initialState);\n\nexport { applyCompareAndSet };\n","import type { ZunoStateEvent } from \"../sync\";\nimport type { ZunoServerEventBus } from \"./event-bus\";\nimport {\n\tcreateEmptyPersistedServerState,\n\tcreateMemoryZunoServerPersistence,\n\ttype ZunoCompareAndSetResult,\n\ttype ZunoServerPersistence,\n} from \"./persistence\";\n\nexport type UniverseRecord = {\n\tstate: unknown;\n\tversion: number;\n};\n\nexport type ZunoStateListener = (event: ZunoStateEvent) => void;\n\nexport type CreateZunoServerStateOptions = {\n\t/** Maximum number of authoritative events retained for SSE replay. */\n\tmaxEvents?: number;\n\t/** Maximum serialized state size accepted per event. */\n\tmaxStateBytes?: number;\n\t/** Maximum events buffered for a slow SSE subscriber. */\n\tmaxSubscriberBuffer?: number;\n\t/** Authoritative state and replay-log persistence. */\n\tpersistence?: ZunoServerPersistence;\n\t/** Shared fan-out bus for other server instances using the same persistence. */\n\teventBus?: ZunoServerEventBus;\n\t/** Stable identifier used to suppress event-bus loopback. */\n\tinstanceId?: string;\n};\n\n/**\n * Isolated authoritative state used by server adapters.\n * Create one instance per application, namespace, or tenant boundary.\n */\nexport class ZunoServerState {\n\treadonly maxEvents: number;\n\treadonly maxStateBytes: number;\n\treadonly maxSubscriberBuffer: number;\n\treadonly persistence: ZunoServerPersistence;\n\treadonly instanceId: string;\n\tprivate readonly listeners = new Set<ZunoStateListener>();\n\tprivate readonly eventBus?: ZunoServerEventBus;\n\tprivate unsubscribeFromEventBus?: () => void;\n\n\tconstructor(options: CreateZunoServerStateOptions = {}) {\n\t\tconst maxEvents = options.maxEvents ?? 1000;\n\t\tconst maxStateBytes = options.maxStateBytes ?? 512 * 1024;\n\t\tconst maxSubscriberBuffer = options.maxSubscriberBuffer ?? 1000;\n\t\tif (!Number.isInteger(maxEvents) || maxEvents < 1) {\n\t\t\tthrow new TypeError(\"maxEvents must be a positive integer\");\n\t\t}\n\t\tif (!Number.isInteger(maxStateBytes) || maxStateBytes < 1) {\n\t\t\tthrow new TypeError(\"maxStateBytes must be a positive integer\");\n\t\t}\n\t\tif (!Number.isInteger(maxSubscriberBuffer) || maxSubscriberBuffer < 1) {\n\t\t\tthrow new TypeError(\"maxSubscriberBuffer must be a positive integer\");\n\t\t}\n\t\tthis.maxEvents = maxEvents;\n\t\tthis.maxStateBytes = maxStateBytes;\n\t\tthis.maxSubscriberBuffer = maxSubscriberBuffer;\n\t\tthis.persistence =\n\t\t\toptions.persistence ?? createMemoryZunoServerPersistence();\n\t\tthis.eventBus = options.eventBus;\n\t\tthis.instanceId = options.instanceId ?? crypto.randomUUID();\n\t\tthis.unsubscribeFromEventBus = this.eventBus?.subscribe((message) => {\n\t\t\tif (message.source === this.instanceId) return;\n\t\t\tthis.notifyLocalListeners(message.event);\n\t\t});\n\t}\n\n\tgetUniverseRecord(storeKey: string): UniverseRecord | undefined {\n\t\treturn this.persistence.load().universe[storeKey];\n\t}\n\n\tupdateUniverseState(event: ZunoStateEvent): void {\n\t\tconst persisted = this.persistence.load();\n\t\tconst current = persisted.universe[event.storeKey] ?? {\n\t\t\tstate: undefined,\n\t\t\tversion: 0,\n\t\t};\n\t\tconst nextVersion =\n\t\t\ttypeof event.version === \"number\" ? event.version : current.version + 1;\n\t\tpersisted.universe[event.storeKey] = {\n\t\t\tstate: event.state,\n\t\t\tversion: nextVersion,\n\t\t};\n\t\tthis.persistence.save(persisted);\n\t}\n\n\tgetUniverseState(): Record<string, UniverseRecord> {\n\t\treturn this.persistence.load().universe;\n\t}\n\n\tappendEvent(event: ZunoStateEvent): ZunoStateEvent {\n\t\tconst persisted = this.persistence.load();\n\t\tevent.eventId = persisted.nextEventId++;\n\t\tpersisted.events.push(event);\n\t\tif (persisted.events.length > this.maxEvents) {\n\t\t\tpersisted.events.splice(0, persisted.events.length - this.maxEvents);\n\t\t}\n\t\tthis.persistence.save(persisted);\n\t\treturn event;\n\t}\n\n\tcompareAndSet(event: ZunoStateEvent): ZunoCompareAndSetResult {\n\t\treturn this.persistence.compareAndSet(event, this.maxEvents);\n\t}\n\n\tgetEventsAfter(lastEventId: number): ZunoStateEvent[] {\n\t\treturn this.persistence\n\t\t\t.load()\n\t\t\t.events.filter((event) => (event.eventId ?? 0) > lastEventId);\n\t}\n\n\tcanReplayAfter(lastEventId: number): boolean {\n\t\tconst latest = this.getLastEventId();\n\t\tif (lastEventId === latest) return true;\n\t\tconst first = this.persistence.load().events[0]?.eventId;\n\t\treturn (\n\t\t\ttypeof first === \"number\" &&\n\t\t\tlastEventId >= first - 1 &&\n\t\t\tlastEventId < latest\n\t\t);\n\t}\n\n\tgetLastEventId(): number {\n\t\tconst events = this.persistence.load().events;\n\t\treturn events[events.length - 1]?.eventId ?? 0;\n\t}\n\n\tsubscribeToStateEvents(listener: ZunoStateListener): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => {\n\t\t\tthis.listeners.delete(listener);\n\t\t};\n\t}\n\n\tpublishToStateEvent(event: ZunoStateEvent): void {\n\t\tthis.notifyLocalListeners(event);\n\t\tthis.eventBus?.publish({ source: this.instanceId, event });\n\t}\n\n\tprivate notifyLocalListeners(event: ZunoStateEvent): void {\n\t\tthis.listeners.forEach((listener) => {\n\t\t\tlistener(event);\n\t\t});\n\t}\n\n\tclear(): void {\n\t\tthis.persistence.save(createEmptyPersistedServerState());\n\t}\n\n\tdispose(): void {\n\t\tthis.unsubscribeFromEventBus?.();\n\t\tthis.unsubscribeFromEventBus = undefined;\n\t\tthis.listeners.clear();\n\t}\n}\n\nexport const createZunoServerState = (\n\toptions: CreateZunoServerStateOptions = {},\n) => new ZunoServerState(options);\n\n/** Lazily creates isolated server states for application namespaces or tenants. */\nexport class ZunoServerRegistry {\n\tprivate readonly servers = new Map<string, ZunoServerState>();\n\n\tconstructor(\n\t\tprivate readonly serverOptions: CreateZunoServerStateOptions = {},\n\t) {}\n\n\tget(namespace: string): ZunoServerState {\n\t\tif (namespace.trim().length === 0) {\n\t\t\tthrow new TypeError(\"namespace must be a non-empty string\");\n\t\t}\n\t\tlet server = this.servers.get(namespace);\n\t\tif (!server) {\n\t\t\tserver = createZunoServerState(this.serverOptions);\n\t\t\tthis.servers.set(namespace, server);\n\t\t}\n\t\treturn server;\n\t}\n\n\tdelete(namespace: string): boolean {\n\t\treturn this.servers.delete(namespace);\n\t}\n\n\tclear(): void {\n\t\tthis.servers.clear();\n\t}\n}\n\nexport const createZunoServerRegistry = (\n\toptions: CreateZunoServerStateOptions = {},\n) => new ZunoServerRegistry(options);\n\n/** Backward-compatible singleton used by the original module-level helpers. */\nexport const defaultZunoServerState = createZunoServerState();\n\nexport const getUniverseRecord = (storeKey: string) =>\n\tdefaultZunoServerState.getUniverseRecord(storeKey);\nexport const updateUniverseState = (event: ZunoStateEvent) =>\n\tdefaultZunoServerState.updateUniverseState(event);\nexport const getUniverseState = () => defaultZunoServerState.getUniverseState();\nexport const appendEvent = (event: ZunoStateEvent) =>\n\tdefaultZunoServerState.appendEvent(event);\nexport const getEventsAfter = (lastEventId: number) =>\n\tdefaultZunoServerState.getEventsAfter(lastEventId);\nexport const getLastEventId = () => defaultZunoServerState.getLastEventId();\nexport const subscribeToStateEvents = (listener: ZunoStateListener) =>\n\tdefaultZunoServerState.subscribeToStateEvents(listener);\nexport const publishToStateEvent = (event: ZunoStateEvent) =>\n\tdefaultZunoServerState.publishToStateEvent(event);\n","import type { ZunoStateEvent } from \"../sync\";\nimport { defaultZunoServerState, type ZunoServerState } from \"./core\";\n\nexport type EventValidationError = {\n\tfield: string;\n\tmessage: string;\n};\n\nexport type ApplyResult =\n\t| { ok: true; event: ZunoStateEvent }\n\t| {\n\t\t\tok: false;\n\t\t\treason: \"VERSION_CONFLICT\";\n\t\t\tcurrent: { state: unknown; version: number };\n\t\t\terrors?: never;\n\t }\n\t| {\n\t\t\tok: false;\n\t\t\treason: \"INVALID_EVENT\";\n\t\t\terrors: EventValidationError[];\n\t\t\tcurrent?: never;\n\t };\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n\ttypeof value === \"object\" && value !== null && !Array.isArray(value);\n\nexport function validateStateEvent(\n\tinput: unknown,\n\tmaxStateBytes = 512 * 1024,\n): EventValidationError[] {\n\tif (!isRecord(input)) {\n\t\treturn [{ field: \"event\", message: \"Event must be an object\" }];\n\t}\n\n\tconst errors: EventValidationError[] = [];\n\tif (\n\t\ttypeof input.storeKey !== \"string\" ||\n\t\tinput.storeKey.trim().length === 0 ||\n\t\tinput.storeKey.length > 256\n\t) {\n\t\terrors.push({\n\t\t\tfield: \"storeKey\",\n\t\t\tmessage: \"storeKey must be a non-empty string of at most 256 characters\",\n\t\t});\n\t}\n\n\tif (!(\"state\" in input)) {\n\t\terrors.push({ field: \"state\", message: \"state is required\" });\n\t} else {\n\t\ttry {\n\t\t\tconst serialized = JSON.stringify(input.state);\n\t\t\tif (serialized === undefined) {\n\t\t\t\terrors.push({\n\t\t\t\t\tfield: \"state\",\n\t\t\t\t\tmessage: \"state must be JSON-serializable\",\n\t\t\t\t});\n\t\t\t} else if (\n\t\t\t\tnew TextEncoder().encode(serialized).byteLength > maxStateBytes\n\t\t\t) {\n\t\t\t\terrors.push({\n\t\t\t\t\tfield: \"state\",\n\t\t\t\t\tmessage: `state exceeds the ${maxStateBytes} byte limit`,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\terrors.push({\n\t\t\t\tfield: \"state\",\n\t\t\t\tmessage: \"state must be JSON-serializable\",\n\t\t\t});\n\t\t}\n\t}\n\n\tfor (const field of [\"version\", \"baseVersion\", \"eventId\"] as const) {\n\t\tconst value = input[field];\n\t\tif (\n\t\t\tvalue !== undefined &&\n\t\t\t(!Number.isInteger(value) || (value as number) < 0)\n\t\t) {\n\t\t\terrors.push({\n\t\t\t\tfield,\n\t\t\t\tmessage: `${field} must be a non-negative integer when provided`,\n\t\t\t});\n\t\t}\n\t}\n\n\tfor (const field of [\"origin\"] as const) {\n\t\tconst value = input[field];\n\t\tif (value !== undefined && typeof value !== \"string\") {\n\t\t\terrors.push({\n\t\t\t\tfield,\n\t\t\t\tmessage: `${field} must be a string when provided`,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (input.ts !== undefined && !Number.isFinite(input.ts)) {\n\t\terrors.push({\n\t\t\tfield: \"ts\",\n\t\t\tmessage: \"ts must be a finite number when provided\",\n\t\t});\n\t}\n\n\tif (input.intent !== undefined) {\n\t\tif (\n\t\t\t!isRecord(input.intent) ||\n\t\t\ttypeof input.intent.type !== \"string\" ||\n\t\t\tinput.intent.type.trim().length === 0\n\t\t) {\n\t\t\terrors.push({\n\t\t\t\tfield: \"intent\",\n\t\t\t\tmessage: \"intent must contain a non-empty string type\",\n\t\t\t});\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/** Validates and applies a state event to an isolated authoritative server. */\nexport function applyStateEvent(\n\tincoming: unknown,\n\tserver: ZunoServerState = defaultZunoServerState,\n): ApplyResult {\n\tconst errors = validateStateEvent(incoming, server.maxStateBytes);\n\tif (errors.length > 0) {\n\t\treturn { ok: false, reason: \"INVALID_EVENT\", errors };\n\t}\n\n\tconst result = server.compareAndSet(incoming as ZunoStateEvent);\n\tif (!result.ok) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\treason: \"VERSION_CONFLICT\",\n\t\t\tcurrent: result.current,\n\t\t};\n\t}\n\n\tconst authoritativeEvent = result.event;\n\tserver.publishToStateEvent(authoritativeEvent);\n\n\treturn { ok: true, event: authoritativeEvent };\n}\n","import type { ZunoStateEvent } from \"../sync\";\n\nexport type ZunoServerBusMessage = {\n\tsource: string;\n\tevent: ZunoStateEvent;\n};\n\n/** Shared live-event fan-out contract (Redis, NATS, etc. can implement this). */\nexport interface ZunoServerEventBus {\n\tpublish(message: ZunoServerBusMessage): void;\n\tsubscribe(listener: (message: ZunoServerBusMessage) => void): () => void;\n}\n\n/** In-process reference bus used by tests and multi-instance development. */\nexport class MemoryZunoServerEventBus implements ZunoServerEventBus {\n\tprivate readonly listeners = new Set<\n\t\t(message: ZunoServerBusMessage) => void\n\t>();\n\n\tpublish(message: ZunoServerBusMessage): void {\n\t\tfor (const listener of this.listeners) listener(structuredClone(message));\n\t}\n\n\tsubscribe(listener: (message: ZunoServerBusMessage) => void): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => this.listeners.delete(listener);\n\t}\n}\n\nexport const createMemoryZunoServerEventBus = () =>\n\tnew MemoryZunoServerEventBus();\n","import {\n\tmkdirSync,\n\treadFileSync,\n\trenameSync,\n\trmSync,\n\tstatSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport type { ZunoStateEvent } from \"../sync\";\nimport {\n\tapplyCompareAndSet,\n\tcreateEmptyPersistedServerState,\n\ttype ZunoCompareAndSetResult,\n\ttype ZunoPersistedServerState,\n\ttype ZunoServerPersistence,\n} from \"./persistence\";\n\nexport type FileZunoServerPersistenceOptions = {\n\tlockTimeoutMs?: number;\n\tstaleLockMs?: number;\n};\n\n/** Durable JSON reference adapter with an atomic rename and cross-process lock. */\nexport class FileZunoServerPersistence implements ZunoServerPersistence {\n\tprivate readonly lockPath: string;\n\tprivate readonly lockTimeoutMs: number;\n\tprivate readonly staleLockMs: number;\n\n\tconstructor(\n\t\tprivate readonly filePath: string,\n\t\toptions: FileZunoServerPersistenceOptions = {},\n\t) {\n\t\tthis.lockPath = `${filePath}.lock`;\n\t\tthis.lockTimeoutMs = options.lockTimeoutMs ?? 5000;\n\t\tthis.staleLockMs = options.staleLockMs ?? 30000;\n\t\tif (!Number.isInteger(this.lockTimeoutMs) || this.lockTimeoutMs < 1) {\n\t\t\tthrow new TypeError(\"lockTimeoutMs must be a positive integer\");\n\t\t}\n\t\tif (!Number.isInteger(this.staleLockMs) || this.staleLockMs < 1) {\n\t\t\tthrow new TypeError(\"staleLockMs must be a positive integer\");\n\t\t}\n\t\tmkdirSync(dirname(filePath), { recursive: true });\n\t}\n\n\tprivate read(): ZunoPersistedServerState {\n\t\ttry {\n\t\t\treturn JSON.parse(readFileSync(this.filePath, \"utf8\"));\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\treturn createEmptyPersistedServerState();\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate write(state: ZunoPersistedServerState): void {\n\t\tconst temporaryPath = `${this.filePath}.${process.pid}.${crypto.randomUUID()}.tmp`;\n\t\ttry {\n\t\t\twriteFileSync(temporaryPath, JSON.stringify(state), \"utf8\");\n\t\t\trenameSync(temporaryPath, this.filePath);\n\t\t} finally {\n\t\t\trmSync(temporaryPath, { force: true });\n\t\t}\n\t}\n\n\tprivate withLock<T>(operation: () => T): T {\n\t\tconst startedAt = Date.now();\n\t\twhile (true) {\n\t\t\ttry {\n\t\t\t\tmkdirSync(this.lockPath);\n\t\t\t\tbreak;\n\t\t\t} catch (error) {\n\t\t\t\tif ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n\t\t\t\ttry {\n\t\t\t\t\tif (\n\t\t\t\t\t\tDate.now() - statSync(this.lockPath).mtimeMs >=\n\t\t\t\t\t\tthis.staleLockMs\n\t\t\t\t\t) {\n\t\t\t\t\t\trmSync(this.lockPath, { recursive: true, force: true });\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch (lockError) {\n\t\t\t\t\tif ((lockError as NodeJS.ErrnoException).code === \"ENOENT\") continue;\n\t\t\t\t\tthrow lockError;\n\t\t\t\t}\n\t\t\t\tif (Date.now() - startedAt >= this.lockTimeoutMs) {\n\t\t\t\t\tthrow new Error(`Timed out acquiring Zuno lock: ${this.lockPath}`);\n\t\t\t\t}\n\t\t\t\tAtomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 5);\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\treturn operation();\n\t\t} finally {\n\t\t\trmSync(this.lockPath, { recursive: true, force: true });\n\t\t}\n\t}\n\n\tload(): ZunoPersistedServerState {\n\t\treturn structuredClone(this.read());\n\t}\n\n\tsave(state: ZunoPersistedServerState): void {\n\t\tthis.withLock(() => this.write(structuredClone(state)));\n\t}\n\n\tcompareAndSet(\n\t\tevent: ZunoStateEvent,\n\t\tmaxEvents: number,\n\t): ZunoCompareAndSetResult {\n\t\treturn this.withLock(() => {\n\t\t\tconst state = this.read();\n\t\t\tconst result = applyCompareAndSet(state, event, maxEvents);\n\t\t\tif (result.ok) this.write(state);\n\t\t\treturn result;\n\t\t});\n\t}\n}\n\nexport const createFileZunoServerPersistence = (\n\tfilePath: string,\n\toptions?: FileZunoServerPersistenceOptions,\n) => new FileZunoServerPersistence(filePath, options);\n","import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { defaultZunoServerState, type ZunoServerState } from \"./core\";\n\n/**\n * Sends a snapshot of the current universe state to the response.\n * Compatible with both Express and raw Node.js http.\n */\nexport function sendSnapshot(\n\t_req: IncomingMessage,\n\tres: ServerResponse,\n\tserver: ZunoServerState = defaultZunoServerState,\n) {\n\tconst snapshot = {\n\t\tstate: server.getUniverseState(),\n\t\tlastEventId: server.getLastEventId(),\n\t};\n\n\t// Check for Express-like .json() method\n\t// biome-ignore lint/suspicious/noExplicitAny: Checking for dynamic .json() method on response object\n\tif (\"json\" in res && typeof (res as any).json === \"function\") {\n\t\t// biome-ignore lint/suspicious/noExplicitAny: Calling dynamic .json() method\n\t\t(res as any).json(snapshot);\n\t} else {\n\t\tres.writeHead(200, { \"Content-Type\": \"application/json\" });\n\t\tres.end(JSON.stringify(snapshot));\n\t}\n}\n","import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport type { ZunoStateEvent } from \"../sync\";\nimport { applyStateEvent } from \"./apply-state-event\";\nimport { defaultZunoServerState, type ZunoServerState } from \"./core\";\n\ntype IncomingHeaders = IncomingMessage[\"headers\"];\n\n/**\n * Creates a Server-Sent Events (SSE) connection for Zuno state updates.\n */\nexport const createSSEConnection = (\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\theaders: IncomingHeaders,\n\tserver: ZunoServerState = defaultZunoServerState,\n) => {\n\tres.writeHead(200, {\n\t\t\"Cache-Control\": \"no-cache, no-transform\",\n\t\t\"Content-Type\": \"text/event-stream; charset=utf-8\",\n\t\tConnection: \"keep-alive\",\n\t\t\"X-Accel-Buffering\": \"no\",\n\t\t...headers,\n\t});\n\n\tres.flushHeaders?.();\n\n\tconst raw =\n\t\treq.headers[\"last-event-id\"] ||\n\t\tnew URL(req.url || \"\", \"http://localhost\").searchParams.get(\"lastEventId\");\n\tconst lastEventId =\n\t\tNumber.parseInt(Array.isArray(raw) ? raw[0] : (raw ?? \"0\"), 10) || 0;\n\n\t// 1. Subscribe FIRST and buffer events until snapshot/missed-events are sent\n\tconst buffer: ZunoStateEvent[] = [];\n\tconst pendingWrites: ZunoStateEvent[] = [];\n\tlet isSyncing = true;\n\tlet backpressured = false;\n\tlet closed = false;\n\tlet heartbeat: ReturnType<typeof setInterval> | null = null;\n\tlet unsubscribe = () => {};\n\n\tconst closeConnection = () => {\n\t\tif (closed) return;\n\t\tclosed = true;\n\t\tif (heartbeat) clearInterval(heartbeat);\n\t\tunsubscribe();\n\t\tres.end();\n\t};\n\tconst flushPendingWrites = () => {\n\t\tbackpressured = false;\n\t\twhile (!closed && pendingWrites.length > 0) {\n\t\t\tconst event = pendingWrites.shift();\n\t\t\tif (!event) continue;\n\t\t\tif (!res.write(formatStateEvent(event))) {\n\t\t\t\tbackpressured = true;\n\t\t\t\tres.once(\"drain\", flushPendingWrites);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t};\n\tconst formatStateEvent = (event: ZunoStateEvent) =>\n\t\t`id: ${event.eventId}\\nevent: state\\ndata: ${JSON.stringify(event)}\\n\\n`;\n\tconst writeEvent = (event: ZunoStateEvent) => {\n\t\tif (closed) return;\n\t\tif (backpressured) {\n\t\t\tif (pendingWrites.length >= server.maxSubscriberBuffer) {\n\t\t\t\tcloseConnection();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpendingWrites.push(event);\n\t\t\treturn;\n\t\t}\n\t\tif (!res.write(formatStateEvent(event))) {\n\t\t\tbackpressured = true;\n\t\t\tres.once(\"drain\", flushPendingWrites);\n\t\t}\n\t};\n\tconst writeSnapshot = () => {\n\t\tres.write(`id: ${server.getLastEventId()}\\n`);\n\t\tres.write(\"event: snapshot\\n\");\n\t\tres.write(`data: ${JSON.stringify(server.getUniverseState())}\\n\\n`);\n\t};\n\n\tunsubscribe = server.subscribeToStateEvents((event: ZunoStateEvent) => {\n\t\tif (isSyncing) {\n\t\t\tif (buffer.length >= server.maxSubscriberBuffer) {\n\t\t\t\tcloseConnection();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbuffer.push(event);\n\t\t} else {\n\t\t\twriteEvent(event);\n\t\t}\n\t});\n\n\t// 2. Send missed events or snapshot\n\tif (lastEventId > 0 && server.canReplayAfter(lastEventId)) {\n\t\tconst missed = server.getEventsAfter(lastEventId);\n\t\tfor (const event of missed) {\n\t\t\twriteEvent(event);\n\t\t}\n\t} else {\n\t\twriteSnapshot();\n\t}\n\n\t// 3. Flush buffer and switch to live mode\n\tisSyncing = false;\n\twhile (buffer.length > 0) {\n\t\tconst event = buffer.shift();\n\t\tif (event) writeEvent(event);\n\t}\n\tif (closed) return;\n\n\theartbeat = setInterval(() => {\n\t\tif (closed) return;\n\t\tres.write(`: ping ${Date.now()}\\n\\n`);\n\t}, 15000);\n\n\tres.write(\": connected \\n\\n\");\n\n\treq.on(\"close\", () => {\n\t\tcloseConnection();\n\t});\n};\n\n/**\n * Synchronizes the Zuno universe state by applying an incoming event.\n */\nexport const syncUniverseState = (\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tserver: ZunoServerState = defaultZunoServerState,\n) => {\n\tconst MAX_BODY_BYTES = 512 * 1024; // 512KB safety\n\tlet body = \"\";\n\n\treq.on(\"data\", (chunk: Buffer) => {\n\t\tbody += chunk.toString(\"utf8\");\n\t\tif (body.length > MAX_BODY_BYTES) {\n\t\t\tres.writeHead(413, { \"Content-Type\": \"application/json\" });\n\t\t\tres.end(JSON.stringify({ ok: false, reason: \"PAYLOAD_TOO_LARGE\" }));\n\t\t\treq.destroy();\n\t\t}\n\t});\n\n\treq.on(\"end\", () => {\n\t\ttry {\n\t\t\tconst incoming: ZunoStateEvent = JSON.parse(\n\t\t\t\tbody || \"{}\",\n\t\t\t) as unknown as ZunoStateEvent;\n\t\t\tconst result = applyStateEvent(incoming, server);\n\n\t\t\tif (!result.ok) {\n\t\t\t\tif (result.reason === \"VERSION_CONFLICT\") {\n\t\t\t\t\tres.writeHead(409, { \"Content-Type\": \"application/json\" });\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\treason: \"VERSION_CONFLICT\",\n\t\t\t\t\t\t\tcurrent: result.current,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tres.writeHead(400, { \"Content-Type\": \"application/json\" });\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\treason: result.reason,\n\t\t\t\t\t\t\terrors: result.errors,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tres.writeHead(200, { \"Content-Type\": \"application/json\" });\n\t\t\tres.end(JSON.stringify({ ok: true, event: result.event }));\n\t\t} catch {\n\t\t\tres.writeHead(400, { \"Content-Type\": \"application/json\" });\n\t\t\tres.end(JSON.stringify({ ok: false, reason: \"INVALID_JSON\" }));\n\t\t}\n\t});\n};\n\nexport const setUniverseState = (\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tserver: ZunoServerState = defaultZunoServerState,\n) => {\n\treturn syncUniverseState(req, res, server);\n};\n"]}
@@ -1,6 +1,57 @@
1
1
  import { f as ZunoStateEvent } from '../index-BSBSRWd0.cjs';
2
2
  import { IncomingMessage, ServerResponse } from 'node:http';
3
3
 
4
+ type ZunoServerBusMessage = {
5
+ source: string;
6
+ event: ZunoStateEvent;
7
+ };
8
+ /** Shared live-event fan-out contract (Redis, NATS, etc. can implement this). */
9
+ interface ZunoServerEventBus {
10
+ publish(message: ZunoServerBusMessage): void;
11
+ subscribe(listener: (message: ZunoServerBusMessage) => void): () => void;
12
+ }
13
+ /** In-process reference bus used by tests and multi-instance development. */
14
+ declare class MemoryZunoServerEventBus implements ZunoServerEventBus {
15
+ private readonly listeners;
16
+ publish(message: ZunoServerBusMessage): void;
17
+ subscribe(listener: (message: ZunoServerBusMessage) => void): () => void;
18
+ }
19
+ declare const createMemoryZunoServerEventBus: () => MemoryZunoServerEventBus;
20
+
21
+ type ZunoPersistedServerState = {
22
+ universe: Record<string, UniverseRecord>;
23
+ events: ZunoStateEvent[];
24
+ nextEventId: number;
25
+ };
26
+ type ZunoCompareAndSetResult = {
27
+ ok: true;
28
+ event: ZunoStateEvent;
29
+ } | {
30
+ ok: false;
31
+ current: UniverseRecord;
32
+ };
33
+ /**
34
+ * Authoritative persistence contract for server state and its replay log.
35
+ * `compareAndSet` must atomically validate baseVersion, update state, assign an
36
+ * event ID, and append/truncate the replay log.
37
+ */
38
+ interface ZunoServerPersistence {
39
+ load(): ZunoPersistedServerState;
40
+ save(state: ZunoPersistedServerState): void;
41
+ compareAndSet(event: ZunoStateEvent, maxEvents: number): ZunoCompareAndSetResult;
42
+ }
43
+ declare const createEmptyPersistedServerState: () => ZunoPersistedServerState;
44
+ declare const applyCompareAndSet: (state: ZunoPersistedServerState, event: ZunoStateEvent, maxEvents: number) => ZunoCompareAndSetResult;
45
+ /** In-memory authoritative persistence for development and tests. */
46
+ declare class MemoryZunoServerPersistence implements ZunoServerPersistence {
47
+ private state;
48
+ constructor(initialState?: ZunoPersistedServerState);
49
+ load(): ZunoPersistedServerState;
50
+ save(state: ZunoPersistedServerState): void;
51
+ compareAndSet(event: ZunoStateEvent, maxEvents: number): ZunoCompareAndSetResult;
52
+ }
53
+ declare const createMemoryZunoServerPersistence: (initialState?: ZunoPersistedServerState) => MemoryZunoServerPersistence;
54
+
4
55
  type UniverseRecord = {
5
56
  state: unknown;
6
57
  version: number;
@@ -13,6 +64,12 @@ type CreateZunoServerStateOptions = {
13
64
  maxStateBytes?: number;
14
65
  /** Maximum events buffered for a slow SSE subscriber. */
15
66
  maxSubscriberBuffer?: number;
67
+ /** Authoritative state and replay-log persistence. */
68
+ persistence?: ZunoServerPersistence;
69
+ /** Shared fan-out bus for other server instances using the same persistence. */
70
+ eventBus?: ZunoServerEventBus;
71
+ /** Stable identifier used to suppress event-bus loopback. */
72
+ instanceId?: string;
16
73
  };
17
74
  /**
18
75
  * Isolated authoritative state used by server adapters.
@@ -22,21 +79,25 @@ declare class ZunoServerState {
22
79
  readonly maxEvents: number;
23
80
  readonly maxStateBytes: number;
24
81
  readonly maxSubscriberBuffer: number;
25
- private readonly universeState;
26
- private readonly eventLog;
82
+ readonly persistence: ZunoServerPersistence;
83
+ readonly instanceId: string;
27
84
  private readonly listeners;
28
- private nextEventId;
85
+ private readonly eventBus?;
86
+ private unsubscribeFromEventBus?;
29
87
  constructor(options?: CreateZunoServerStateOptions);
30
88
  getUniverseRecord(storeKey: string): UniverseRecord | undefined;
31
89
  updateUniverseState(event: ZunoStateEvent): void;
32
90
  getUniverseState(): Record<string, UniverseRecord>;
33
91
  appendEvent(event: ZunoStateEvent): ZunoStateEvent;
92
+ compareAndSet(event: ZunoStateEvent): ZunoCompareAndSetResult;
34
93
  getEventsAfter(lastEventId: number): ZunoStateEvent[];
35
94
  canReplayAfter(lastEventId: number): boolean;
36
95
  getLastEventId(): number;
37
96
  subscribeToStateEvents(listener: ZunoStateListener): () => void;
38
97
  publishToStateEvent(event: ZunoStateEvent): void;
98
+ private notifyLocalListeners;
39
99
  clear(): void;
100
+ dispose(): void;
40
101
  }
41
102
  declare const createZunoServerState: (options?: CreateZunoServerStateOptions) => ZunoServerState;
42
103
  /** Lazily creates isolated server states for application namespaces or tenants. */
@@ -85,6 +146,26 @@ declare function validateStateEvent(input: unknown, maxStateBytes?: number): Eve
85
146
  /** Validates and applies a state event to an isolated authoritative server. */
86
147
  declare function applyStateEvent(incoming: unknown, server?: ZunoServerState): ApplyResult;
87
148
 
149
+ type FileZunoServerPersistenceOptions = {
150
+ lockTimeoutMs?: number;
151
+ staleLockMs?: number;
152
+ };
153
+ /** Durable JSON reference adapter with an atomic rename and cross-process lock. */
154
+ declare class FileZunoServerPersistence implements ZunoServerPersistence {
155
+ private readonly filePath;
156
+ private readonly lockPath;
157
+ private readonly lockTimeoutMs;
158
+ private readonly staleLockMs;
159
+ constructor(filePath: string, options?: FileZunoServerPersistenceOptions);
160
+ private read;
161
+ private write;
162
+ private withLock;
163
+ load(): ZunoPersistedServerState;
164
+ save(state: ZunoPersistedServerState): void;
165
+ compareAndSet(event: ZunoStateEvent, maxEvents: number): ZunoCompareAndSetResult;
166
+ }
167
+ declare const createFileZunoServerPersistence: (filePath: string, options?: FileZunoServerPersistenceOptions) => FileZunoServerPersistence;
168
+
88
169
  /**
89
170
  * Sends a snapshot of the current universe state to the response.
90
171
  * Compatible with both Express and raw Node.js http.
@@ -102,4 +183,4 @@ declare const createSSEConnection: (req: IncomingMessage, res: ServerResponse, h
102
183
  declare const syncUniverseState: (req: IncomingMessage, res: ServerResponse, server?: ZunoServerState) => void;
103
184
  declare const setUniverseState: (req: IncomingMessage, res: ServerResponse, server?: ZunoServerState) => void;
104
185
 
105
- export { type ApplyResult, type CreateZunoServerStateOptions, type EventValidationError, type UniverseRecord, ZunoServerRegistry, ZunoServerState, type ZunoStateListener, appendEvent, applyStateEvent, createSSEConnection, createZunoServerRegistry, createZunoServerState, defaultZunoServerState, getEventsAfter, getLastEventId, getUniverseRecord, getUniverseState, publishToStateEvent, sendSnapshot, setUniverseState, subscribeToStateEvents, syncUniverseState, updateUniverseState, validateStateEvent };
186
+ export { type ApplyResult, type CreateZunoServerStateOptions, type EventValidationError, FileZunoServerPersistence, type FileZunoServerPersistenceOptions, MemoryZunoServerEventBus, MemoryZunoServerPersistence, type UniverseRecord, type ZunoCompareAndSetResult, type ZunoPersistedServerState, type ZunoServerBusMessage, type ZunoServerEventBus, type ZunoServerPersistence, ZunoServerRegistry, ZunoServerState, type ZunoStateListener, appendEvent, applyCompareAndSet, applyStateEvent, createEmptyPersistedServerState, createFileZunoServerPersistence, createMemoryZunoServerEventBus, createMemoryZunoServerPersistence, createSSEConnection, createZunoServerRegistry, createZunoServerState, defaultZunoServerState, getEventsAfter, getLastEventId, getUniverseRecord, getUniverseState, publishToStateEvent, sendSnapshot, setUniverseState, subscribeToStateEvents, syncUniverseState, updateUniverseState, validateStateEvent };
@@ -1,6 +1,57 @@
1
1
  import { f as ZunoStateEvent } from '../index-BSBSRWd0.js';
2
2
  import { IncomingMessage, ServerResponse } from 'node:http';
3
3
 
4
+ type ZunoServerBusMessage = {
5
+ source: string;
6
+ event: ZunoStateEvent;
7
+ };
8
+ /** Shared live-event fan-out contract (Redis, NATS, etc. can implement this). */
9
+ interface ZunoServerEventBus {
10
+ publish(message: ZunoServerBusMessage): void;
11
+ subscribe(listener: (message: ZunoServerBusMessage) => void): () => void;
12
+ }
13
+ /** In-process reference bus used by tests and multi-instance development. */
14
+ declare class MemoryZunoServerEventBus implements ZunoServerEventBus {
15
+ private readonly listeners;
16
+ publish(message: ZunoServerBusMessage): void;
17
+ subscribe(listener: (message: ZunoServerBusMessage) => void): () => void;
18
+ }
19
+ declare const createMemoryZunoServerEventBus: () => MemoryZunoServerEventBus;
20
+
21
+ type ZunoPersistedServerState = {
22
+ universe: Record<string, UniverseRecord>;
23
+ events: ZunoStateEvent[];
24
+ nextEventId: number;
25
+ };
26
+ type ZunoCompareAndSetResult = {
27
+ ok: true;
28
+ event: ZunoStateEvent;
29
+ } | {
30
+ ok: false;
31
+ current: UniverseRecord;
32
+ };
33
+ /**
34
+ * Authoritative persistence contract for server state and its replay log.
35
+ * `compareAndSet` must atomically validate baseVersion, update state, assign an
36
+ * event ID, and append/truncate the replay log.
37
+ */
38
+ interface ZunoServerPersistence {
39
+ load(): ZunoPersistedServerState;
40
+ save(state: ZunoPersistedServerState): void;
41
+ compareAndSet(event: ZunoStateEvent, maxEvents: number): ZunoCompareAndSetResult;
42
+ }
43
+ declare const createEmptyPersistedServerState: () => ZunoPersistedServerState;
44
+ declare const applyCompareAndSet: (state: ZunoPersistedServerState, event: ZunoStateEvent, maxEvents: number) => ZunoCompareAndSetResult;
45
+ /** In-memory authoritative persistence for development and tests. */
46
+ declare class MemoryZunoServerPersistence implements ZunoServerPersistence {
47
+ private state;
48
+ constructor(initialState?: ZunoPersistedServerState);
49
+ load(): ZunoPersistedServerState;
50
+ save(state: ZunoPersistedServerState): void;
51
+ compareAndSet(event: ZunoStateEvent, maxEvents: number): ZunoCompareAndSetResult;
52
+ }
53
+ declare const createMemoryZunoServerPersistence: (initialState?: ZunoPersistedServerState) => MemoryZunoServerPersistence;
54
+
4
55
  type UniverseRecord = {
5
56
  state: unknown;
6
57
  version: number;
@@ -13,6 +64,12 @@ type CreateZunoServerStateOptions = {
13
64
  maxStateBytes?: number;
14
65
  /** Maximum events buffered for a slow SSE subscriber. */
15
66
  maxSubscriberBuffer?: number;
67
+ /** Authoritative state and replay-log persistence. */
68
+ persistence?: ZunoServerPersistence;
69
+ /** Shared fan-out bus for other server instances using the same persistence. */
70
+ eventBus?: ZunoServerEventBus;
71
+ /** Stable identifier used to suppress event-bus loopback. */
72
+ instanceId?: string;
16
73
  };
17
74
  /**
18
75
  * Isolated authoritative state used by server adapters.
@@ -22,21 +79,25 @@ declare class ZunoServerState {
22
79
  readonly maxEvents: number;
23
80
  readonly maxStateBytes: number;
24
81
  readonly maxSubscriberBuffer: number;
25
- private readonly universeState;
26
- private readonly eventLog;
82
+ readonly persistence: ZunoServerPersistence;
83
+ readonly instanceId: string;
27
84
  private readonly listeners;
28
- private nextEventId;
85
+ private readonly eventBus?;
86
+ private unsubscribeFromEventBus?;
29
87
  constructor(options?: CreateZunoServerStateOptions);
30
88
  getUniverseRecord(storeKey: string): UniverseRecord | undefined;
31
89
  updateUniverseState(event: ZunoStateEvent): void;
32
90
  getUniverseState(): Record<string, UniverseRecord>;
33
91
  appendEvent(event: ZunoStateEvent): ZunoStateEvent;
92
+ compareAndSet(event: ZunoStateEvent): ZunoCompareAndSetResult;
34
93
  getEventsAfter(lastEventId: number): ZunoStateEvent[];
35
94
  canReplayAfter(lastEventId: number): boolean;
36
95
  getLastEventId(): number;
37
96
  subscribeToStateEvents(listener: ZunoStateListener): () => void;
38
97
  publishToStateEvent(event: ZunoStateEvent): void;
98
+ private notifyLocalListeners;
39
99
  clear(): void;
100
+ dispose(): void;
40
101
  }
41
102
  declare const createZunoServerState: (options?: CreateZunoServerStateOptions) => ZunoServerState;
42
103
  /** Lazily creates isolated server states for application namespaces or tenants. */
@@ -85,6 +146,26 @@ declare function validateStateEvent(input: unknown, maxStateBytes?: number): Eve
85
146
  /** Validates and applies a state event to an isolated authoritative server. */
86
147
  declare function applyStateEvent(incoming: unknown, server?: ZunoServerState): ApplyResult;
87
148
 
149
+ type FileZunoServerPersistenceOptions = {
150
+ lockTimeoutMs?: number;
151
+ staleLockMs?: number;
152
+ };
153
+ /** Durable JSON reference adapter with an atomic rename and cross-process lock. */
154
+ declare class FileZunoServerPersistence implements ZunoServerPersistence {
155
+ private readonly filePath;
156
+ private readonly lockPath;
157
+ private readonly lockTimeoutMs;
158
+ private readonly staleLockMs;
159
+ constructor(filePath: string, options?: FileZunoServerPersistenceOptions);
160
+ private read;
161
+ private write;
162
+ private withLock;
163
+ load(): ZunoPersistedServerState;
164
+ save(state: ZunoPersistedServerState): void;
165
+ compareAndSet(event: ZunoStateEvent, maxEvents: number): ZunoCompareAndSetResult;
166
+ }
167
+ declare const createFileZunoServerPersistence: (filePath: string, options?: FileZunoServerPersistenceOptions) => FileZunoServerPersistence;
168
+
88
169
  /**
89
170
  * Sends a snapshot of the current universe state to the response.
90
171
  * Compatible with both Express and raw Node.js http.
@@ -102,4 +183,4 @@ declare const createSSEConnection: (req: IncomingMessage, res: ServerResponse, h
102
183
  declare const syncUniverseState: (req: IncomingMessage, res: ServerResponse, server?: ZunoServerState) => void;
103
184
  declare const setUniverseState: (req: IncomingMessage, res: ServerResponse, server?: ZunoServerState) => void;
104
185
 
105
- export { type ApplyResult, type CreateZunoServerStateOptions, type EventValidationError, type UniverseRecord, ZunoServerRegistry, ZunoServerState, type ZunoStateListener, appendEvent, applyStateEvent, createSSEConnection, createZunoServerRegistry, createZunoServerState, defaultZunoServerState, getEventsAfter, getLastEventId, getUniverseRecord, getUniverseState, publishToStateEvent, sendSnapshot, setUniverseState, subscribeToStateEvents, syncUniverseState, updateUniverseState, validateStateEvent };
186
+ export { type ApplyResult, type CreateZunoServerStateOptions, type EventValidationError, FileZunoServerPersistence, type FileZunoServerPersistenceOptions, MemoryZunoServerEventBus, MemoryZunoServerPersistence, type UniverseRecord, type ZunoCompareAndSetResult, type ZunoPersistedServerState, type ZunoServerBusMessage, type ZunoServerEventBus, type ZunoServerPersistence, ZunoServerRegistry, ZunoServerState, type ZunoStateListener, appendEvent, applyCompareAndSet, applyStateEvent, createEmptyPersistedServerState, createFileZunoServerPersistence, createMemoryZunoServerEventBus, createMemoryZunoServerPersistence, createSSEConnection, createZunoServerRegistry, createZunoServerState, defaultZunoServerState, getEventsAfter, getLastEventId, getUniverseRecord, getUniverseState, publishToStateEvent, sendSnapshot, setUniverseState, subscribeToStateEvents, syncUniverseState, updateUniverseState, validateStateEvent };
@@ -1,14 +1,14 @@
1
- var l=class{constructor(e={}){this.universeState=new Map;this.eventLog=[];this.listeners=new Set;this.nextEventId=1;let n=e.maxEvents??1e3,r=e.maxStateBytes??512*1024,s=e.maxSubscriberBuffer??1e3;if(!Number.isInteger(n)||n<1)throw new TypeError("maxEvents must be a positive integer");if(!Number.isInteger(r)||r<1)throw new TypeError("maxStateBytes must be a positive integer");if(!Number.isInteger(s)||s<1)throw new TypeError("maxSubscriberBuffer must be a positive integer");this.maxEvents=n,this.maxStateBytes=r,this.maxSubscriberBuffer=s;}getUniverseRecord(e){return this.universeState.get(e)}updateUniverseState(e){let n=this.universeState.get(e.storeKey)??{version:0},r=typeof e.version=="number"?e.version:n.version+1;this.universeState.set(e.storeKey,{state:e.state,version:r});}getUniverseState(){return Object.fromEntries(this.universeState)}appendEvent(e){return e.eventId=this.nextEventId++,this.eventLog.push(e),this.eventLog.length>this.maxEvents&&this.eventLog.shift(),e}getEventsAfter(e){return this.eventLog.filter(n=>(n.eventId??0)>e)}canReplayAfter(e){let n=this.getLastEventId();if(e===n)return true;let r=this.eventLog[0]?.eventId;return typeof r=="number"&&e>=r-1&&e<n}getLastEventId(){return this.eventLog[this.eventLog.length-1]?.eventId??0}subscribeToStateEvents(e){return this.listeners.add(e),()=>{this.listeners.delete(e);}}publishToStateEvent(e){this.listeners.forEach(n=>{n(e);});}clear(){this.universeState.clear(),this.eventLog.length=0,this.nextEventId=1;}},b=(t={})=>new l(t),g=class{constructor(e={}){this.serverOptions=e;this.servers=new Map;}get(e){if(e.trim().length===0)throw new TypeError("namespace must be a non-empty string");let n=this.servers.get(e);return n||(n=b(this.serverOptions),this.servers.set(e,n)),n}delete(e){return this.servers.delete(e)}clear(){this.servers.clear();}},L=(t={})=>new g(t),a=b(),T=t=>a.getUniverseRecord(t),R=t=>a.updateUniverseState(t),C=()=>a.getUniverseState(),k=t=>a.appendEvent(t),A=t=>a.getEventsAfter(t),B=()=>a.getLastEventId(),U=t=>a.subscribeToStateEvents(t),V=t=>a.publishToStateEvent(t);var x=t=>typeof t=="object"&&t!==null&&!Array.isArray(t);function O(t,e=512*1024){if(!x(t))return [{field:"event",message:"Event must be an object"}];let n=[];if((typeof t.storeKey!="string"||t.storeKey.trim().length===0||t.storeKey.length>256)&&n.push({field:"storeKey",message:"storeKey must be a non-empty string of at most 256 characters"}),!("state"in t))n.push({field:"state",message:"state is required"});else try{let r=JSON.stringify(t.state);r===void 0?n.push({field:"state",message:"state must be JSON-serializable"}):new TextEncoder().encode(r).byteLength>e&&n.push({field:"state",message:`state exceeds the ${e} byte limit`});}catch{n.push({field:"state",message:"state must be JSON-serializable"});}for(let r of ["version","baseVersion","eventId"]){let s=t[r];s!==void 0&&(!Number.isInteger(s)||s<0)&&n.push({field:r,message:`${r} must be a non-negative integer when provided`});}for(let r of ["origin"]){let s=t[r];s!==void 0&&typeof s!="string"&&n.push({field:r,message:`${r} must be a string when provided`});}return t.ts!==void 0&&!Number.isFinite(t.ts)&&n.push({field:"ts",message:"ts must be a finite number when provided"}),t.intent!==void 0&&(!x(t.intent)||typeof t.intent.type!="string"||t.intent.type.trim().length===0)&&n.push({field:"intent",message:"intent must contain a non-empty string type"}),n}function I(t,e=a){let n=O(t,e.maxStateBytes);if(n.length>0)return {ok:false,reason:"INVALID_EVENT",errors:n};let r=t,s=e.getUniverseRecord(r.storeKey)??{state:void 0,version:0};if(typeof r.baseVersion=="number"&&r.baseVersion!==s.version)return {ok:false,reason:"VERSION_CONFLICT",current:s};let i={...r,version:s.version+1};return e.updateUniverseState(i),e.appendEvent(i),e.publishToStateEvent(i),{ok:true,event:i}}function H(t,e,n=a){let r={state:n.getUniverseState(),lastEventId:n.getLastEventId()};"json"in e&&typeof e.json=="function"?e.json(r):(e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify(r)));}var F=(t,e,n,r=a)=>{e.writeHead(200,{"Cache-Control":"no-cache, no-transform","Content-Type":"text/event-stream; charset=utf-8",Connection:"keep-alive","X-Accel-Buffering":"no",...n}),e.flushHeaders?.();let s=t.headers["last-event-id"]||new URL(t.url||"","http://localhost").searchParams.get("lastEventId"),i=Number.parseInt(Array.isArray(s)?s[0]:s??"0",10)||0,u=[],f=[],m=true,c=false,v=false,p=null,y=()=>{},d=()=>{v||(v=true,p&&clearInterval(p),y(),e.end());},h=()=>{for(c=false;!v&&f.length>0;){let o=f.shift();if(o&&!e.write(E(o))){c=true,e.once("drain",h);break}}},E=o=>`id: ${o.eventId}
1
+ import {mkdirSync,readFileSync,writeFileSync,renameSync,rmSync,statSync}from'fs';import {dirname}from'path';var v=()=>({universe:{},events:[],nextEventId:1}),m=t=>structuredClone(t),y=(t,e,r)=>{let n=t.universe[e.storeKey]??{state:void 0,version:0};if(typeof e.baseVersion=="number"&&e.baseVersion!==n.version)return {ok:false,current:structuredClone(n)};let s={...structuredClone(e),version:n.version+1,eventId:t.nextEventId++};return t.universe[e.storeKey]={state:structuredClone(e.state),version:s.version},t.events.push(s),t.events.length>r&&t.events.splice(0,t.events.length-r),{ok:true,event:structuredClone(s)}},h=class{constructor(e=v()){this.state=m(e);}load(){return m(this.state)}save(e){this.state=m(e);}compareAndSet(e,r){let n=y(this.state,e,r);return structuredClone(n)}},C=t=>new h(t);var E=class{constructor(e={}){this.listeners=new Set;let r=e.maxEvents??1e3,n=e.maxStateBytes??512*1024,s=e.maxSubscriberBuffer??1e3;if(!Number.isInteger(r)||r<1)throw new TypeError("maxEvents must be a positive integer");if(!Number.isInteger(n)||n<1)throw new TypeError("maxStateBytes must be a positive integer");if(!Number.isInteger(s)||s<1)throw new TypeError("maxSubscriberBuffer must be a positive integer");this.maxEvents=r,this.maxStateBytes=n,this.maxSubscriberBuffer=s,this.persistence=e.persistence??C(),this.eventBus=e.eventBus,this.instanceId=e.instanceId??crypto.randomUUID(),this.unsubscribeFromEventBus=this.eventBus?.subscribe(u=>{u.source!==this.instanceId&&this.notifyLocalListeners(u.event);});}getUniverseRecord(e){return this.persistence.load().universe[e]}updateUniverseState(e){let r=this.persistence.load(),n=r.universe[e.storeKey]??{version:0},s=typeof e.version=="number"?e.version:n.version+1;r.universe[e.storeKey]={state:e.state,version:s},this.persistence.save(r);}getUniverseState(){return this.persistence.load().universe}appendEvent(e){let r=this.persistence.load();return e.eventId=r.nextEventId++,r.events.push(e),r.events.length>this.maxEvents&&r.events.splice(0,r.events.length-this.maxEvents),this.persistence.save(r),e}compareAndSet(e){return this.persistence.compareAndSet(e,this.maxEvents)}getEventsAfter(e){return this.persistence.load().events.filter(r=>(r.eventId??0)>e)}canReplayAfter(e){let r=this.getLastEventId();if(e===r)return true;let n=this.persistence.load().events[0]?.eventId;return typeof n=="number"&&e>=n-1&&e<r}getLastEventId(){let e=this.persistence.load().events;return e[e.length-1]?.eventId??0}subscribeToStateEvents(e){return this.listeners.add(e),()=>{this.listeners.delete(e);}}publishToStateEvent(e){this.notifyLocalListeners(e),this.eventBus?.publish({source:this.instanceId,event:e});}notifyLocalListeners(e){this.listeners.forEach(r=>{r(e);});}clear(){this.persistence.save(v());}dispose(){this.unsubscribeFromEventBus?.(),this.unsubscribeFromEventBus=void 0,this.listeners.clear();}},N=(t={})=>new E(t),g=class{constructor(e={}){this.serverOptions=e;this.servers=new Map;}get(e){if(e.trim().length===0)throw new TypeError("namespace must be a non-empty string");let r=this.servers.get(e);return r||(r=N(this.serverOptions),this.servers.set(e,r)),r}delete(e){return this.servers.delete(e)}clear(){this.servers.clear();}},j=(t={})=>new g(t),i=N(),H=t=>i.getUniverseRecord(t),K=t=>i.updateUniverseState(t),Y=()=>i.getUniverseState(),X=t=>i.appendEvent(t),z=t=>i.getEventsAfter(t),W=()=>i.getLastEventId(),G=t=>i.subscribeToStateEvents(t),Q=t=>i.publishToStateEvent(t);var P=t=>typeof t=="object"&&t!==null&&!Array.isArray(t);function R(t,e=512*1024){if(!P(t))return [{field:"event",message:"Event must be an object"}];let r=[];if((typeof t.storeKey!="string"||t.storeKey.trim().length===0||t.storeKey.length>256)&&r.push({field:"storeKey",message:"storeKey must be a non-empty string of at most 256 characters"}),!("state"in t))r.push({field:"state",message:"state is required"});else try{let n=JSON.stringify(t.state);n===void 0?r.push({field:"state",message:"state must be JSON-serializable"}):new TextEncoder().encode(n).byteLength>e&&r.push({field:"state",message:`state exceeds the ${e} byte limit`});}catch{r.push({field:"state",message:"state must be JSON-serializable"});}for(let n of ["version","baseVersion","eventId"]){let s=t[n];s!==void 0&&(!Number.isInteger(s)||s<0)&&r.push({field:n,message:`${n} must be a non-negative integer when provided`});}for(let n of ["origin"]){let s=t[n];s!==void 0&&typeof s!="string"&&r.push({field:n,message:`${n} must be a string when provided`});}return t.ts!==void 0&&!Number.isFinite(t.ts)&&r.push({field:"ts",message:"ts must be a finite number when provided"}),t.intent!==void 0&&(!P(t.intent)||typeof t.intent.type!="string"||t.intent.type.trim().length===0)&&r.push({field:"intent",message:"intent must contain a non-empty string type"}),r}function B(t,e=i){let r=R(t,e.maxStateBytes);if(r.length>0)return {ok:false,reason:"INVALID_EVENT",errors:r};let n=e.compareAndSet(t);if(!n.ok)return {ok:false,reason:"VERSION_CONFLICT",current:n.current};let s=n.event;return e.publishToStateEvent(s),{ok:true,event:s}}var b=class{constructor(){this.listeners=new Set;}publish(e){for(let r of this.listeners)r(structuredClone(e));}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}},re=()=>new b;var x=class{constructor(e,r={}){this.filePath=e;if(this.lockPath=`${e}.lock`,this.lockTimeoutMs=r.lockTimeoutMs??5e3,this.staleLockMs=r.staleLockMs??3e4,!Number.isInteger(this.lockTimeoutMs)||this.lockTimeoutMs<1)throw new TypeError("lockTimeoutMs must be a positive integer");if(!Number.isInteger(this.staleLockMs)||this.staleLockMs<1)throw new TypeError("staleLockMs must be a positive integer");mkdirSync(dirname(e),{recursive:true});}read(){try{return JSON.parse(readFileSync(this.filePath,"utf8"))}catch(e){if(e.code==="ENOENT")return v();throw e}}write(e){let r=`${this.filePath}.${process.pid}.${crypto.randomUUID()}.tmp`;try{writeFileSync(r,JSON.stringify(e),"utf8"),renameSync(r,this.filePath);}finally{rmSync(r,{force:true});}}withLock(e){let r=Date.now();for(;;)try{mkdirSync(this.lockPath);break}catch(n){if(n.code!=="EEXIST")throw n;try{if(Date.now()-statSync(this.lockPath).mtimeMs>=this.staleLockMs){rmSync(this.lockPath,{recursive:!0,force:!0});continue}}catch(s){if(s.code==="ENOENT")continue;throw s}if(Date.now()-r>=this.lockTimeoutMs)throw new Error(`Timed out acquiring Zuno lock: ${this.lockPath}`);Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,5);}try{return e()}finally{rmSync(this.lockPath,{recursive:true,force:true});}}load(){return structuredClone(this.read())}save(e){this.withLock(()=>this.write(structuredClone(e)));}compareAndSet(e,r){return this.withLock(()=>{let n=this.read(),s=y(n,e,r);return s.ok&&this.write(n),s})}},ae=(t,e)=>new x(t,e);function ve(t,e,r=i){let n={state:r.getUniverseState(),lastEventId:r.getLastEventId()};"json"in e&&typeof e.json=="function"?e.json(n):(e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify(n)));}var Se=(t,e,r,n=i)=>{e.writeHead(200,{"Cache-Control":"no-cache, no-transform","Content-Type":"text/event-stream; charset=utf-8",Connection:"keep-alive","X-Accel-Buffering":"no",...r}),e.flushHeaders?.();let s=t.headers["last-event-id"]||new URL(t.url||"","http://localhost").searchParams.get("lastEventId"),u=Number.parseInt(Array.isArray(s)?s[0]:s??"0",10)||0,a=[],p=[],w=true,d=false,c=false,l=null,I=()=>{},S=()=>{c||(c=true,l&&clearInterval(l),I(),e.end());},k=()=>{for(d=false;!c&&p.length>0;){let o=p.shift();if(o&&!e.write(T(o))){d=true,e.once("drain",k);break}}},T=o=>`id: ${o.eventId}
2
2
  event: state
3
3
  data: ${JSON.stringify(o)}
4
4
 
5
- `,S=o=>{if(!v){if(c){if(f.length>=r.maxSubscriberBuffer){d();return}f.push(o);return}e.write(E(o))||(c=true,e.once("drain",h));}},w=()=>{e.write(`id: ${r.getLastEventId()}
5
+ `,f=o=>{if(!c){if(d){if(p.length>=n.maxSubscriberBuffer){S();return}p.push(o);return}e.write(T(o))||(d=true,e.once("drain",k));}},O=()=>{e.write(`id: ${n.getLastEventId()}
6
6
  `),e.write(`event: snapshot
7
- `),e.write(`data: ${JSON.stringify(r.getUniverseState())}
7
+ `),e.write(`data: ${JSON.stringify(n.getUniverseState())}
8
8
 
9
- `);};if(y=r.subscribeToStateEvents(o=>{if(m){if(u.length>=r.maxSubscriberBuffer){d();return}u.push(o);}else S(o);}),i>0&&r.canReplayAfter(i)){let o=r.getEventsAfter(i);for(let Z of o)S(Z);}else w();for(m=false;u.length>0;){let o=u.shift();o&&S(o);}v||(p=setInterval(()=>{v||e.write(`: ping ${Date.now()}
9
+ `);};if(I=n.subscribeToStateEvents(o=>{if(w){if(a.length>=n.maxSubscriberBuffer){S();return}a.push(o);}else f(o);}),u>0&&n.canReplayAfter(u)){let o=n.getEventsAfter(u);for(let L of o)f(L);}else O();for(w=false;a.length>0;){let o=a.shift();o&&f(o);}c||(l=setInterval(()=>{c||e.write(`: ping ${Date.now()}
10
10
 
11
11
  `);},15e3),e.write(`: connected
12
12
 
13
- `),t.on("close",()=>{d();}));},N=(t,e,n=a)=>{let s="";t.on("data",i=>{s+=i.toString("utf8"),s.length>524288&&(e.writeHead(413,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:false,reason:"PAYLOAD_TOO_LARGE"})),t.destroy());}),t.on("end",()=>{try{let i=JSON.parse(s||"{}"),u=I(i,n);if(!u.ok){u.reason==="VERSION_CONFLICT"?(e.writeHead(409,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:!1,reason:"VERSION_CONFLICT",current:u.current}))):(e.writeHead(400,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:!1,reason:u.reason,errors:u.errors})));return}e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:!0,event:u.event}));}catch{e.writeHead(400,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:false,reason:"INVALID_JSON"}));}});},Y=(t,e,n=a)=>N(t,e,n);export{g as ZunoServerRegistry,l as ZunoServerState,k as appendEvent,I as applyStateEvent,F as createSSEConnection,L as createZunoServerRegistry,b as createZunoServerState,a as defaultZunoServerState,A as getEventsAfter,B as getLastEventId,T as getUniverseRecord,C as getUniverseState,V as publishToStateEvent,H as sendSnapshot,Y as setUniverseState,U as subscribeToStateEvents,N as syncUniverseState,R as updateUniverseState,O as validateStateEvent};//# sourceMappingURL=index.js.map
13
+ `),t.on("close",()=>{S();}));},_=(t,e,r=i)=>{let s="";t.on("data",u=>{s+=u.toString("utf8"),s.length>524288&&(e.writeHead(413,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:false,reason:"PAYLOAD_TOO_LARGE"})),t.destroy());}),t.on("end",()=>{try{let u=JSON.parse(s||"{}"),a=B(u,r);if(!a.ok){a.reason==="VERSION_CONFLICT"?(e.writeHead(409,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:!1,reason:"VERSION_CONFLICT",current:a.current}))):(e.writeHead(400,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:!1,reason:a.reason,errors:a.errors})));return}e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:!0,event:a.event}));}catch{e.writeHead(400,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:false,reason:"INVALID_JSON"}));}});},fe=(t,e,r=i)=>_(t,e,r);export{x as FileZunoServerPersistence,b as MemoryZunoServerEventBus,h as MemoryZunoServerPersistence,g as ZunoServerRegistry,E as ZunoServerState,X as appendEvent,y as applyCompareAndSet,B as applyStateEvent,v as createEmptyPersistedServerState,ae as createFileZunoServerPersistence,re as createMemoryZunoServerEventBus,C as createMemoryZunoServerPersistence,Se as createSSEConnection,j as createZunoServerRegistry,N as createZunoServerState,i as defaultZunoServerState,z as getEventsAfter,W as getLastEventId,H as getUniverseRecord,Y as getUniverseState,Q as publishToStateEvent,ve as sendSnapshot,fe as setUniverseState,G as subscribeToStateEvents,_ as syncUniverseState,K as updateUniverseState,R as validateStateEvent};//# sourceMappingURL=index.js.map
14
14
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/core.ts","../../src/server/apply-state-event.ts","../../src/server/snapshot-handler.ts","../../src/server/sse-handler.ts"],"names":["ZunoServerState","options","maxEvents","maxStateBytes","maxSubscriberBuffer","storeKey","event","current","nextVersion","lastEventId","latest","first","listener","createZunoServerState","ZunoServerRegistry","serverOptions","namespace","server","createZunoServerRegistry","defaultZunoServerState","getUniverseRecord","updateUniverseState","getUniverseState","appendEvent","getEventsAfter","getLastEventId","subscribeToStateEvents","publishToStateEvent","isRecord","value","validateStateEvent","input","errors","serialized","field","applyStateEvent","incoming","authoritativeEvent","sendSnapshot","_req","res","snapshot","createSSEConnection","req","headers","raw","buffer","pendingWrites","isSyncing","backpressured","closed","heartbeat","unsubscribe","closeConnection","flushPendingWrites","formatStateEvent","writeEvent","writeSnapshot","missed","syncUniverseState","body","chunk","result","setUniverseState"],"mappings":"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","file":"index.js","sourcesContent":["import type { ZunoStateEvent } from \"../sync\";\n\nexport type UniverseRecord = {\n\tstate: unknown;\n\tversion: number;\n};\n\nexport type ZunoStateListener = (event: ZunoStateEvent) => void;\n\nexport type CreateZunoServerStateOptions = {\n\t/** Maximum number of authoritative events retained for SSE replay. */\n\tmaxEvents?: number;\n\t/** Maximum serialized state size accepted per event. */\n\tmaxStateBytes?: number;\n\t/** Maximum events buffered for a slow SSE subscriber. */\n\tmaxSubscriberBuffer?: number;\n};\n\n/**\n * Isolated authoritative state used by server adapters.\n * Create one instance per application, namespace, or tenant boundary.\n */\nexport class ZunoServerState {\n\treadonly maxEvents: number;\n\treadonly maxStateBytes: number;\n\treadonly maxSubscriberBuffer: number;\n\tprivate readonly universeState = new Map<string, UniverseRecord>();\n\tprivate readonly eventLog: ZunoStateEvent[] = [];\n\tprivate readonly listeners = new Set<ZunoStateListener>();\n\tprivate nextEventId = 1;\n\n\tconstructor(options: CreateZunoServerStateOptions = {}) {\n\t\tconst maxEvents = options.maxEvents ?? 1000;\n\t\tconst maxStateBytes = options.maxStateBytes ?? 512 * 1024;\n\t\tconst maxSubscriberBuffer = options.maxSubscriberBuffer ?? 1000;\n\t\tif (!Number.isInteger(maxEvents) || maxEvents < 1) {\n\t\t\tthrow new TypeError(\"maxEvents must be a positive integer\");\n\t\t}\n\t\tif (!Number.isInteger(maxStateBytes) || maxStateBytes < 1) {\n\t\t\tthrow new TypeError(\"maxStateBytes must be a positive integer\");\n\t\t}\n\t\tif (!Number.isInteger(maxSubscriberBuffer) || maxSubscriberBuffer < 1) {\n\t\t\tthrow new TypeError(\"maxSubscriberBuffer must be a positive integer\");\n\t\t}\n\t\tthis.maxEvents = maxEvents;\n\t\tthis.maxStateBytes = maxStateBytes;\n\t\tthis.maxSubscriberBuffer = maxSubscriberBuffer;\n\t}\n\n\tgetUniverseRecord(storeKey: string): UniverseRecord | undefined {\n\t\treturn this.universeState.get(storeKey);\n\t}\n\n\tupdateUniverseState(event: ZunoStateEvent): void {\n\t\tconst current = this.universeState.get(event.storeKey) ?? {\n\t\t\tstate: undefined,\n\t\t\tversion: 0,\n\t\t};\n\t\tconst nextVersion =\n\t\t\ttypeof event.version === \"number\" ? event.version : current.version + 1;\n\t\tthis.universeState.set(event.storeKey, {\n\t\t\tstate: event.state,\n\t\t\tversion: nextVersion,\n\t\t});\n\t}\n\n\tgetUniverseState(): Record<string, UniverseRecord> {\n\t\treturn Object.fromEntries(this.universeState);\n\t}\n\n\tappendEvent(event: ZunoStateEvent): ZunoStateEvent {\n\t\tevent.eventId = this.nextEventId++;\n\t\tthis.eventLog.push(event);\n\t\tif (this.eventLog.length > this.maxEvents) {\n\t\t\tthis.eventLog.shift();\n\t\t}\n\t\treturn event;\n\t}\n\n\tgetEventsAfter(lastEventId: number): ZunoStateEvent[] {\n\t\treturn this.eventLog.filter((event) => (event.eventId ?? 0) > lastEventId);\n\t}\n\n\tcanReplayAfter(lastEventId: number): boolean {\n\t\tconst latest = this.getLastEventId();\n\t\tif (lastEventId === latest) return true;\n\t\tconst first = this.eventLog[0]?.eventId;\n\t\treturn (\n\t\t\ttypeof first === \"number\" &&\n\t\t\tlastEventId >= first - 1 &&\n\t\t\tlastEventId < latest\n\t\t);\n\t}\n\n\tgetLastEventId(): number {\n\t\treturn this.eventLog[this.eventLog.length - 1]?.eventId ?? 0;\n\t}\n\n\tsubscribeToStateEvents(listener: ZunoStateListener): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => {\n\t\t\tthis.listeners.delete(listener);\n\t\t};\n\t}\n\n\tpublishToStateEvent(event: ZunoStateEvent): void {\n\t\tthis.listeners.forEach((listener) => {\n\t\t\tlistener(event);\n\t\t});\n\t}\n\n\tclear(): void {\n\t\tthis.universeState.clear();\n\t\tthis.eventLog.length = 0;\n\t\tthis.nextEventId = 1;\n\t}\n}\n\nexport const createZunoServerState = (\n\toptions: CreateZunoServerStateOptions = {},\n) => new ZunoServerState(options);\n\n/** Lazily creates isolated server states for application namespaces or tenants. */\nexport class ZunoServerRegistry {\n\tprivate readonly servers = new Map<string, ZunoServerState>();\n\n\tconstructor(\n\t\tprivate readonly serverOptions: CreateZunoServerStateOptions = {},\n\t) {}\n\n\tget(namespace: string): ZunoServerState {\n\t\tif (namespace.trim().length === 0) {\n\t\t\tthrow new TypeError(\"namespace must be a non-empty string\");\n\t\t}\n\t\tlet server = this.servers.get(namespace);\n\t\tif (!server) {\n\t\t\tserver = createZunoServerState(this.serverOptions);\n\t\t\tthis.servers.set(namespace, server);\n\t\t}\n\t\treturn server;\n\t}\n\n\tdelete(namespace: string): boolean {\n\t\treturn this.servers.delete(namespace);\n\t}\n\n\tclear(): void {\n\t\tthis.servers.clear();\n\t}\n}\n\nexport const createZunoServerRegistry = (\n\toptions: CreateZunoServerStateOptions = {},\n) => new ZunoServerRegistry(options);\n\n/** Backward-compatible singleton used by the original module-level helpers. */\nexport const defaultZunoServerState = createZunoServerState();\n\nexport const getUniverseRecord = (storeKey: string) =>\n\tdefaultZunoServerState.getUniverseRecord(storeKey);\nexport const updateUniverseState = (event: ZunoStateEvent) =>\n\tdefaultZunoServerState.updateUniverseState(event);\nexport const getUniverseState = () => defaultZunoServerState.getUniverseState();\nexport const appendEvent = (event: ZunoStateEvent) =>\n\tdefaultZunoServerState.appendEvent(event);\nexport const getEventsAfter = (lastEventId: number) =>\n\tdefaultZunoServerState.getEventsAfter(lastEventId);\nexport const getLastEventId = () => defaultZunoServerState.getLastEventId();\nexport const subscribeToStateEvents = (listener: ZunoStateListener) =>\n\tdefaultZunoServerState.subscribeToStateEvents(listener);\nexport const publishToStateEvent = (event: ZunoStateEvent) =>\n\tdefaultZunoServerState.publishToStateEvent(event);\n","import type { ZunoStateEvent } from \"../sync\";\nimport { defaultZunoServerState, type ZunoServerState } from \"./core\";\n\nexport type EventValidationError = {\n\tfield: string;\n\tmessage: string;\n};\n\nexport type ApplyResult =\n\t| { ok: true; event: ZunoStateEvent }\n\t| {\n\t\t\tok: false;\n\t\t\treason: \"VERSION_CONFLICT\";\n\t\t\tcurrent: { state: unknown; version: number };\n\t\t\terrors?: never;\n\t }\n\t| {\n\t\t\tok: false;\n\t\t\treason: \"INVALID_EVENT\";\n\t\t\terrors: EventValidationError[];\n\t\t\tcurrent?: never;\n\t };\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n\ttypeof value === \"object\" && value !== null && !Array.isArray(value);\n\nexport function validateStateEvent(\n\tinput: unknown,\n\tmaxStateBytes = 512 * 1024,\n): EventValidationError[] {\n\tif (!isRecord(input)) {\n\t\treturn [{ field: \"event\", message: \"Event must be an object\" }];\n\t}\n\n\tconst errors: EventValidationError[] = [];\n\tif (\n\t\ttypeof input.storeKey !== \"string\" ||\n\t\tinput.storeKey.trim().length === 0 ||\n\t\tinput.storeKey.length > 256\n\t) {\n\t\terrors.push({\n\t\t\tfield: \"storeKey\",\n\t\t\tmessage: \"storeKey must be a non-empty string of at most 256 characters\",\n\t\t});\n\t}\n\n\tif (!(\"state\" in input)) {\n\t\terrors.push({ field: \"state\", message: \"state is required\" });\n\t} else {\n\t\ttry {\n\t\t\tconst serialized = JSON.stringify(input.state);\n\t\t\tif (serialized === undefined) {\n\t\t\t\terrors.push({\n\t\t\t\t\tfield: \"state\",\n\t\t\t\t\tmessage: \"state must be JSON-serializable\",\n\t\t\t\t});\n\t\t\t} else if (\n\t\t\t\tnew TextEncoder().encode(serialized).byteLength > maxStateBytes\n\t\t\t) {\n\t\t\t\terrors.push({\n\t\t\t\t\tfield: \"state\",\n\t\t\t\t\tmessage: `state exceeds the ${maxStateBytes} byte limit`,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\terrors.push({\n\t\t\t\tfield: \"state\",\n\t\t\t\tmessage: \"state must be JSON-serializable\",\n\t\t\t});\n\t\t}\n\t}\n\n\tfor (const field of [\"version\", \"baseVersion\", \"eventId\"] as const) {\n\t\tconst value = input[field];\n\t\tif (\n\t\t\tvalue !== undefined &&\n\t\t\t(!Number.isInteger(value) || (value as number) < 0)\n\t\t) {\n\t\t\terrors.push({\n\t\t\t\tfield,\n\t\t\t\tmessage: `${field} must be a non-negative integer when provided`,\n\t\t\t});\n\t\t}\n\t}\n\n\tfor (const field of [\"origin\"] as const) {\n\t\tconst value = input[field];\n\t\tif (value !== undefined && typeof value !== \"string\") {\n\t\t\terrors.push({\n\t\t\t\tfield,\n\t\t\t\tmessage: `${field} must be a string when provided`,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (input.ts !== undefined && !Number.isFinite(input.ts)) {\n\t\terrors.push({\n\t\t\tfield: \"ts\",\n\t\t\tmessage: \"ts must be a finite number when provided\",\n\t\t});\n\t}\n\n\tif (input.intent !== undefined) {\n\t\tif (\n\t\t\t!isRecord(input.intent) ||\n\t\t\ttypeof input.intent.type !== \"string\" ||\n\t\t\tinput.intent.type.trim().length === 0\n\t\t) {\n\t\t\terrors.push({\n\t\t\t\tfield: \"intent\",\n\t\t\t\tmessage: \"intent must contain a non-empty string type\",\n\t\t\t});\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/** Validates and applies a state event to an isolated authoritative server. */\nexport function applyStateEvent(\n\tincoming: unknown,\n\tserver: ZunoServerState = defaultZunoServerState,\n): ApplyResult {\n\tconst errors = validateStateEvent(incoming, server.maxStateBytes);\n\tif (errors.length > 0) {\n\t\treturn { ok: false, reason: \"INVALID_EVENT\", errors };\n\t}\n\n\tconst event = incoming as ZunoStateEvent;\n\tconst current = server.getUniverseRecord(event.storeKey) ?? {\n\t\tstate: undefined,\n\t\tversion: 0,\n\t};\n\n\tif (\n\t\ttypeof event.baseVersion === \"number\" &&\n\t\tevent.baseVersion !== current.version\n\t) {\n\t\treturn { ok: false, reason: \"VERSION_CONFLICT\", current };\n\t}\n\n\tconst authoritativeEvent = { ...event, version: current.version + 1 };\n\tserver.updateUniverseState(authoritativeEvent);\n\tserver.appendEvent(authoritativeEvent);\n\tserver.publishToStateEvent(authoritativeEvent);\n\n\treturn { ok: true, event: authoritativeEvent };\n}\n","import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { defaultZunoServerState, type ZunoServerState } from \"./core\";\n\n/**\n * Sends a snapshot of the current universe state to the response.\n * Compatible with both Express and raw Node.js http.\n */\nexport function sendSnapshot(\n\t_req: IncomingMessage,\n\tres: ServerResponse,\n\tserver: ZunoServerState = defaultZunoServerState,\n) {\n\tconst snapshot = {\n\t\tstate: server.getUniverseState(),\n\t\tlastEventId: server.getLastEventId(),\n\t};\n\n\t// Check for Express-like .json() method\n\t// biome-ignore lint/suspicious/noExplicitAny: Checking for dynamic .json() method on response object\n\tif (\"json\" in res && typeof (res as any).json === \"function\") {\n\t\t// biome-ignore lint/suspicious/noExplicitAny: Calling dynamic .json() method\n\t\t(res as any).json(snapshot);\n\t} else {\n\t\tres.writeHead(200, { \"Content-Type\": \"application/json\" });\n\t\tres.end(JSON.stringify(snapshot));\n\t}\n}\n","import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport type { ZunoStateEvent } from \"../sync\";\nimport { applyStateEvent } from \"./apply-state-event\";\nimport { defaultZunoServerState, type ZunoServerState } from \"./core\";\n\ntype IncomingHeaders = IncomingMessage[\"headers\"];\n\n/**\n * Creates a Server-Sent Events (SSE) connection for Zuno state updates.\n */\nexport const createSSEConnection = (\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\theaders: IncomingHeaders,\n\tserver: ZunoServerState = defaultZunoServerState,\n) => {\n\tres.writeHead(200, {\n\t\t\"Cache-Control\": \"no-cache, no-transform\",\n\t\t\"Content-Type\": \"text/event-stream; charset=utf-8\",\n\t\tConnection: \"keep-alive\",\n\t\t\"X-Accel-Buffering\": \"no\",\n\t\t...headers,\n\t});\n\n\tres.flushHeaders?.();\n\n\tconst raw =\n\t\treq.headers[\"last-event-id\"] ||\n\t\tnew URL(req.url || \"\", \"http://localhost\").searchParams.get(\"lastEventId\");\n\tconst lastEventId =\n\t\tNumber.parseInt(Array.isArray(raw) ? raw[0] : (raw ?? \"0\"), 10) || 0;\n\n\t// 1. Subscribe FIRST and buffer events until snapshot/missed-events are sent\n\tconst buffer: ZunoStateEvent[] = [];\n\tconst pendingWrites: ZunoStateEvent[] = [];\n\tlet isSyncing = true;\n\tlet backpressured = false;\n\tlet closed = false;\n\tlet heartbeat: ReturnType<typeof setInterval> | null = null;\n\tlet unsubscribe = () => {};\n\n\tconst closeConnection = () => {\n\t\tif (closed) return;\n\t\tclosed = true;\n\t\tif (heartbeat) clearInterval(heartbeat);\n\t\tunsubscribe();\n\t\tres.end();\n\t};\n\tconst flushPendingWrites = () => {\n\t\tbackpressured = false;\n\t\twhile (!closed && pendingWrites.length > 0) {\n\t\t\tconst event = pendingWrites.shift();\n\t\t\tif (!event) continue;\n\t\t\tif (!res.write(formatStateEvent(event))) {\n\t\t\t\tbackpressured = true;\n\t\t\t\tres.once(\"drain\", flushPendingWrites);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t};\n\tconst formatStateEvent = (event: ZunoStateEvent) =>\n\t\t`id: ${event.eventId}\\nevent: state\\ndata: ${JSON.stringify(event)}\\n\\n`;\n\tconst writeEvent = (event: ZunoStateEvent) => {\n\t\tif (closed) return;\n\t\tif (backpressured) {\n\t\t\tif (pendingWrites.length >= server.maxSubscriberBuffer) {\n\t\t\t\tcloseConnection();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpendingWrites.push(event);\n\t\t\treturn;\n\t\t}\n\t\tif (!res.write(formatStateEvent(event))) {\n\t\t\tbackpressured = true;\n\t\t\tres.once(\"drain\", flushPendingWrites);\n\t\t}\n\t};\n\tconst writeSnapshot = () => {\n\t\tres.write(`id: ${server.getLastEventId()}\\n`);\n\t\tres.write(\"event: snapshot\\n\");\n\t\tres.write(`data: ${JSON.stringify(server.getUniverseState())}\\n\\n`);\n\t};\n\n\tunsubscribe = server.subscribeToStateEvents((event: ZunoStateEvent) => {\n\t\tif (isSyncing) {\n\t\t\tif (buffer.length >= server.maxSubscriberBuffer) {\n\t\t\t\tcloseConnection();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbuffer.push(event);\n\t\t} else {\n\t\t\twriteEvent(event);\n\t\t}\n\t});\n\n\t// 2. Send missed events or snapshot\n\tif (lastEventId > 0 && server.canReplayAfter(lastEventId)) {\n\t\tconst missed = server.getEventsAfter(lastEventId);\n\t\tfor (const event of missed) {\n\t\t\twriteEvent(event);\n\t\t}\n\t} else {\n\t\twriteSnapshot();\n\t}\n\n\t// 3. Flush buffer and switch to live mode\n\tisSyncing = false;\n\twhile (buffer.length > 0) {\n\t\tconst event = buffer.shift();\n\t\tif (event) writeEvent(event);\n\t}\n\tif (closed) return;\n\n\theartbeat = setInterval(() => {\n\t\tif (closed) return;\n\t\tres.write(`: ping ${Date.now()}\\n\\n`);\n\t}, 15000);\n\n\tres.write(\": connected \\n\\n\");\n\n\treq.on(\"close\", () => {\n\t\tcloseConnection();\n\t});\n};\n\n/**\n * Synchronizes the Zuno universe state by applying an incoming event.\n */\nexport const syncUniverseState = (\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tserver: ZunoServerState = defaultZunoServerState,\n) => {\n\tconst MAX_BODY_BYTES = 512 * 1024; // 512KB safety\n\tlet body = \"\";\n\n\treq.on(\"data\", (chunk: Buffer) => {\n\t\tbody += chunk.toString(\"utf8\");\n\t\tif (body.length > MAX_BODY_BYTES) {\n\t\t\tres.writeHead(413, { \"Content-Type\": \"application/json\" });\n\t\t\tres.end(JSON.stringify({ ok: false, reason: \"PAYLOAD_TOO_LARGE\" }));\n\t\t\treq.destroy();\n\t\t}\n\t});\n\n\treq.on(\"end\", () => {\n\t\ttry {\n\t\t\tconst incoming: ZunoStateEvent = JSON.parse(\n\t\t\t\tbody || \"{}\",\n\t\t\t) as unknown as ZunoStateEvent;\n\t\t\tconst result = applyStateEvent(incoming, server);\n\n\t\t\tif (!result.ok) {\n\t\t\t\tif (result.reason === \"VERSION_CONFLICT\") {\n\t\t\t\t\tres.writeHead(409, { \"Content-Type\": \"application/json\" });\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\treason: \"VERSION_CONFLICT\",\n\t\t\t\t\t\t\tcurrent: result.current,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tres.writeHead(400, { \"Content-Type\": \"application/json\" });\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\treason: result.reason,\n\t\t\t\t\t\t\terrors: result.errors,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tres.writeHead(200, { \"Content-Type\": \"application/json\" });\n\t\t\tres.end(JSON.stringify({ ok: true, event: result.event }));\n\t\t} catch {\n\t\t\tres.writeHead(400, { \"Content-Type\": \"application/json\" });\n\t\t\tres.end(JSON.stringify({ ok: false, reason: \"INVALID_JSON\" }));\n\t\t}\n\t});\n};\n\nexport const setUniverseState = (\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tserver: ZunoServerState = defaultZunoServerState,\n) => {\n\treturn syncUniverseState(req, res, server);\n};\n"]}
1
+ {"version":3,"sources":["../../src/server/persistence.ts","../../src/server/core.ts","../../src/server/apply-state-event.ts","../../src/server/event-bus.ts","../../src/server/file-persistence.ts","../../src/server/snapshot-handler.ts","../../src/server/sse-handler.ts"],"names":["createEmptyPersistedServerState","cloneState","state","applyCompareAndSet","event","maxEvents","current","authoritativeEvent","MemoryZunoServerPersistence","initialState","result","createMemoryZunoServerPersistence","ZunoServerState","options","maxStateBytes","maxSubscriberBuffer","message","storeKey","persisted","nextVersion","lastEventId","latest","first","events","listener","createZunoServerState","ZunoServerRegistry","serverOptions","namespace","server","createZunoServerRegistry","defaultZunoServerState","getUniverseRecord","updateUniverseState","getUniverseState","appendEvent","getEventsAfter","getLastEventId","subscribeToStateEvents","publishToStateEvent","isRecord","value","validateStateEvent","input","errors","serialized","field","applyStateEvent","incoming","MemoryZunoServerEventBus","createMemoryZunoServerEventBus","FileZunoServerPersistence","filePath","mkdirSync","dirname","readFileSync","error","temporaryPath","writeFileSync","renameSync","rmSync","operation","startedAt","statSync","lockError","createFileZunoServerPersistence","sendSnapshot","_req","res","snapshot","createSSEConnection","req","headers","raw","buffer","pendingWrites","isSyncing","backpressured","closed","heartbeat","unsubscribe","closeConnection","flushPendingWrites","formatStateEvent","writeEvent","writeSnapshot","missed","syncUniverseState","body","chunk","setUniverseState"],"mappings":"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","file":"index.js","sourcesContent":["import type { ZunoStateEvent } from \"../sync\";\nimport type { UniverseRecord } from \"./core\";\n\nexport type ZunoPersistedServerState = {\n\tuniverse: Record<string, UniverseRecord>;\n\tevents: ZunoStateEvent[];\n\tnextEventId: number;\n};\n\nexport type ZunoCompareAndSetResult =\n\t| { ok: true; event: ZunoStateEvent }\n\t| { ok: false; current: UniverseRecord };\n\n/**\n * Authoritative persistence contract for server state and its replay log.\n * `compareAndSet` must atomically validate baseVersion, update state, assign an\n * event ID, and append/truncate the replay log.\n */\nexport interface ZunoServerPersistence {\n\tload(): ZunoPersistedServerState;\n\tsave(state: ZunoPersistedServerState): void;\n\tcompareAndSet(\n\t\tevent: ZunoStateEvent,\n\t\tmaxEvents: number,\n\t): ZunoCompareAndSetResult;\n}\n\nexport const createEmptyPersistedServerState =\n\t(): ZunoPersistedServerState => ({\n\t\tuniverse: {},\n\t\tevents: [],\n\t\tnextEventId: 1,\n\t});\n\nconst cloneState = (\n\tstate: ZunoPersistedServerState,\n): ZunoPersistedServerState => structuredClone(state);\n\nconst applyCompareAndSet = (\n\tstate: ZunoPersistedServerState,\n\tevent: ZunoStateEvent,\n\tmaxEvents: number,\n): ZunoCompareAndSetResult => {\n\tconst current = state.universe[event.storeKey] ?? {\n\t\tstate: undefined,\n\t\tversion: 0,\n\t};\n\tif (\n\t\ttypeof event.baseVersion === \"number\" &&\n\t\tevent.baseVersion !== current.version\n\t) {\n\t\treturn { ok: false, current: structuredClone(current) };\n\t}\n\n\tconst authoritativeEvent = {\n\t\t...structuredClone(event),\n\t\tversion: current.version + 1,\n\t\teventId: state.nextEventId++,\n\t};\n\tstate.universe[event.storeKey] = {\n\t\tstate: structuredClone(event.state),\n\t\tversion: authoritativeEvent.version,\n\t};\n\tstate.events.push(authoritativeEvent);\n\tif (state.events.length > maxEvents) {\n\t\tstate.events.splice(0, state.events.length - maxEvents);\n\t}\n\treturn { ok: true, event: structuredClone(authoritativeEvent) };\n};\n\n/** In-memory authoritative persistence for development and tests. */\nexport class MemoryZunoServerPersistence implements ZunoServerPersistence {\n\tprivate state: ZunoPersistedServerState;\n\n\tconstructor(initialState = createEmptyPersistedServerState()) {\n\t\tthis.state = cloneState(initialState);\n\t}\n\n\tload(): ZunoPersistedServerState {\n\t\treturn cloneState(this.state);\n\t}\n\n\tsave(state: ZunoPersistedServerState): void {\n\t\tthis.state = cloneState(state);\n\t}\n\n\tcompareAndSet(\n\t\tevent: ZunoStateEvent,\n\t\tmaxEvents: number,\n\t): ZunoCompareAndSetResult {\n\t\tconst result = applyCompareAndSet(this.state, event, maxEvents);\n\t\treturn structuredClone(result);\n\t}\n}\n\nexport const createMemoryZunoServerPersistence = (\n\tinitialState?: ZunoPersistedServerState,\n) => new MemoryZunoServerPersistence(initialState);\n\nexport { applyCompareAndSet };\n","import type { ZunoStateEvent } from \"../sync\";\nimport type { ZunoServerEventBus } from \"./event-bus\";\nimport {\n\tcreateEmptyPersistedServerState,\n\tcreateMemoryZunoServerPersistence,\n\ttype ZunoCompareAndSetResult,\n\ttype ZunoServerPersistence,\n} from \"./persistence\";\n\nexport type UniverseRecord = {\n\tstate: unknown;\n\tversion: number;\n};\n\nexport type ZunoStateListener = (event: ZunoStateEvent) => void;\n\nexport type CreateZunoServerStateOptions = {\n\t/** Maximum number of authoritative events retained for SSE replay. */\n\tmaxEvents?: number;\n\t/** Maximum serialized state size accepted per event. */\n\tmaxStateBytes?: number;\n\t/** Maximum events buffered for a slow SSE subscriber. */\n\tmaxSubscriberBuffer?: number;\n\t/** Authoritative state and replay-log persistence. */\n\tpersistence?: ZunoServerPersistence;\n\t/** Shared fan-out bus for other server instances using the same persistence. */\n\teventBus?: ZunoServerEventBus;\n\t/** Stable identifier used to suppress event-bus loopback. */\n\tinstanceId?: string;\n};\n\n/**\n * Isolated authoritative state used by server adapters.\n * Create one instance per application, namespace, or tenant boundary.\n */\nexport class ZunoServerState {\n\treadonly maxEvents: number;\n\treadonly maxStateBytes: number;\n\treadonly maxSubscriberBuffer: number;\n\treadonly persistence: ZunoServerPersistence;\n\treadonly instanceId: string;\n\tprivate readonly listeners = new Set<ZunoStateListener>();\n\tprivate readonly eventBus?: ZunoServerEventBus;\n\tprivate unsubscribeFromEventBus?: () => void;\n\n\tconstructor(options: CreateZunoServerStateOptions = {}) {\n\t\tconst maxEvents = options.maxEvents ?? 1000;\n\t\tconst maxStateBytes = options.maxStateBytes ?? 512 * 1024;\n\t\tconst maxSubscriberBuffer = options.maxSubscriberBuffer ?? 1000;\n\t\tif (!Number.isInteger(maxEvents) || maxEvents < 1) {\n\t\t\tthrow new TypeError(\"maxEvents must be a positive integer\");\n\t\t}\n\t\tif (!Number.isInteger(maxStateBytes) || maxStateBytes < 1) {\n\t\t\tthrow new TypeError(\"maxStateBytes must be a positive integer\");\n\t\t}\n\t\tif (!Number.isInteger(maxSubscriberBuffer) || maxSubscriberBuffer < 1) {\n\t\t\tthrow new TypeError(\"maxSubscriberBuffer must be a positive integer\");\n\t\t}\n\t\tthis.maxEvents = maxEvents;\n\t\tthis.maxStateBytes = maxStateBytes;\n\t\tthis.maxSubscriberBuffer = maxSubscriberBuffer;\n\t\tthis.persistence =\n\t\t\toptions.persistence ?? createMemoryZunoServerPersistence();\n\t\tthis.eventBus = options.eventBus;\n\t\tthis.instanceId = options.instanceId ?? crypto.randomUUID();\n\t\tthis.unsubscribeFromEventBus = this.eventBus?.subscribe((message) => {\n\t\t\tif (message.source === this.instanceId) return;\n\t\t\tthis.notifyLocalListeners(message.event);\n\t\t});\n\t}\n\n\tgetUniverseRecord(storeKey: string): UniverseRecord | undefined {\n\t\treturn this.persistence.load().universe[storeKey];\n\t}\n\n\tupdateUniverseState(event: ZunoStateEvent): void {\n\t\tconst persisted = this.persistence.load();\n\t\tconst current = persisted.universe[event.storeKey] ?? {\n\t\t\tstate: undefined,\n\t\t\tversion: 0,\n\t\t};\n\t\tconst nextVersion =\n\t\t\ttypeof event.version === \"number\" ? event.version : current.version + 1;\n\t\tpersisted.universe[event.storeKey] = {\n\t\t\tstate: event.state,\n\t\t\tversion: nextVersion,\n\t\t};\n\t\tthis.persistence.save(persisted);\n\t}\n\n\tgetUniverseState(): Record<string, UniverseRecord> {\n\t\treturn this.persistence.load().universe;\n\t}\n\n\tappendEvent(event: ZunoStateEvent): ZunoStateEvent {\n\t\tconst persisted = this.persistence.load();\n\t\tevent.eventId = persisted.nextEventId++;\n\t\tpersisted.events.push(event);\n\t\tif (persisted.events.length > this.maxEvents) {\n\t\t\tpersisted.events.splice(0, persisted.events.length - this.maxEvents);\n\t\t}\n\t\tthis.persistence.save(persisted);\n\t\treturn event;\n\t}\n\n\tcompareAndSet(event: ZunoStateEvent): ZunoCompareAndSetResult {\n\t\treturn this.persistence.compareAndSet(event, this.maxEvents);\n\t}\n\n\tgetEventsAfter(lastEventId: number): ZunoStateEvent[] {\n\t\treturn this.persistence\n\t\t\t.load()\n\t\t\t.events.filter((event) => (event.eventId ?? 0) > lastEventId);\n\t}\n\n\tcanReplayAfter(lastEventId: number): boolean {\n\t\tconst latest = this.getLastEventId();\n\t\tif (lastEventId === latest) return true;\n\t\tconst first = this.persistence.load().events[0]?.eventId;\n\t\treturn (\n\t\t\ttypeof first === \"number\" &&\n\t\t\tlastEventId >= first - 1 &&\n\t\t\tlastEventId < latest\n\t\t);\n\t}\n\n\tgetLastEventId(): number {\n\t\tconst events = this.persistence.load().events;\n\t\treturn events[events.length - 1]?.eventId ?? 0;\n\t}\n\n\tsubscribeToStateEvents(listener: ZunoStateListener): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => {\n\t\t\tthis.listeners.delete(listener);\n\t\t};\n\t}\n\n\tpublishToStateEvent(event: ZunoStateEvent): void {\n\t\tthis.notifyLocalListeners(event);\n\t\tthis.eventBus?.publish({ source: this.instanceId, event });\n\t}\n\n\tprivate notifyLocalListeners(event: ZunoStateEvent): void {\n\t\tthis.listeners.forEach((listener) => {\n\t\t\tlistener(event);\n\t\t});\n\t}\n\n\tclear(): void {\n\t\tthis.persistence.save(createEmptyPersistedServerState());\n\t}\n\n\tdispose(): void {\n\t\tthis.unsubscribeFromEventBus?.();\n\t\tthis.unsubscribeFromEventBus = undefined;\n\t\tthis.listeners.clear();\n\t}\n}\n\nexport const createZunoServerState = (\n\toptions: CreateZunoServerStateOptions = {},\n) => new ZunoServerState(options);\n\n/** Lazily creates isolated server states for application namespaces or tenants. */\nexport class ZunoServerRegistry {\n\tprivate readonly servers = new Map<string, ZunoServerState>();\n\n\tconstructor(\n\t\tprivate readonly serverOptions: CreateZunoServerStateOptions = {},\n\t) {}\n\n\tget(namespace: string): ZunoServerState {\n\t\tif (namespace.trim().length === 0) {\n\t\t\tthrow new TypeError(\"namespace must be a non-empty string\");\n\t\t}\n\t\tlet server = this.servers.get(namespace);\n\t\tif (!server) {\n\t\t\tserver = createZunoServerState(this.serverOptions);\n\t\t\tthis.servers.set(namespace, server);\n\t\t}\n\t\treturn server;\n\t}\n\n\tdelete(namespace: string): boolean {\n\t\treturn this.servers.delete(namespace);\n\t}\n\n\tclear(): void {\n\t\tthis.servers.clear();\n\t}\n}\n\nexport const createZunoServerRegistry = (\n\toptions: CreateZunoServerStateOptions = {},\n) => new ZunoServerRegistry(options);\n\n/** Backward-compatible singleton used by the original module-level helpers. */\nexport const defaultZunoServerState = createZunoServerState();\n\nexport const getUniverseRecord = (storeKey: string) =>\n\tdefaultZunoServerState.getUniverseRecord(storeKey);\nexport const updateUniverseState = (event: ZunoStateEvent) =>\n\tdefaultZunoServerState.updateUniverseState(event);\nexport const getUniverseState = () => defaultZunoServerState.getUniverseState();\nexport const appendEvent = (event: ZunoStateEvent) =>\n\tdefaultZunoServerState.appendEvent(event);\nexport const getEventsAfter = (lastEventId: number) =>\n\tdefaultZunoServerState.getEventsAfter(lastEventId);\nexport const getLastEventId = () => defaultZunoServerState.getLastEventId();\nexport const subscribeToStateEvents = (listener: ZunoStateListener) =>\n\tdefaultZunoServerState.subscribeToStateEvents(listener);\nexport const publishToStateEvent = (event: ZunoStateEvent) =>\n\tdefaultZunoServerState.publishToStateEvent(event);\n","import type { ZunoStateEvent } from \"../sync\";\nimport { defaultZunoServerState, type ZunoServerState } from \"./core\";\n\nexport type EventValidationError = {\n\tfield: string;\n\tmessage: string;\n};\n\nexport type ApplyResult =\n\t| { ok: true; event: ZunoStateEvent }\n\t| {\n\t\t\tok: false;\n\t\t\treason: \"VERSION_CONFLICT\";\n\t\t\tcurrent: { state: unknown; version: number };\n\t\t\terrors?: never;\n\t }\n\t| {\n\t\t\tok: false;\n\t\t\treason: \"INVALID_EVENT\";\n\t\t\terrors: EventValidationError[];\n\t\t\tcurrent?: never;\n\t };\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n\ttypeof value === \"object\" && value !== null && !Array.isArray(value);\n\nexport function validateStateEvent(\n\tinput: unknown,\n\tmaxStateBytes = 512 * 1024,\n): EventValidationError[] {\n\tif (!isRecord(input)) {\n\t\treturn [{ field: \"event\", message: \"Event must be an object\" }];\n\t}\n\n\tconst errors: EventValidationError[] = [];\n\tif (\n\t\ttypeof input.storeKey !== \"string\" ||\n\t\tinput.storeKey.trim().length === 0 ||\n\t\tinput.storeKey.length > 256\n\t) {\n\t\terrors.push({\n\t\t\tfield: \"storeKey\",\n\t\t\tmessage: \"storeKey must be a non-empty string of at most 256 characters\",\n\t\t});\n\t}\n\n\tif (!(\"state\" in input)) {\n\t\terrors.push({ field: \"state\", message: \"state is required\" });\n\t} else {\n\t\ttry {\n\t\t\tconst serialized = JSON.stringify(input.state);\n\t\t\tif (serialized === undefined) {\n\t\t\t\terrors.push({\n\t\t\t\t\tfield: \"state\",\n\t\t\t\t\tmessage: \"state must be JSON-serializable\",\n\t\t\t\t});\n\t\t\t} else if (\n\t\t\t\tnew TextEncoder().encode(serialized).byteLength > maxStateBytes\n\t\t\t) {\n\t\t\t\terrors.push({\n\t\t\t\t\tfield: \"state\",\n\t\t\t\t\tmessage: `state exceeds the ${maxStateBytes} byte limit`,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\terrors.push({\n\t\t\t\tfield: \"state\",\n\t\t\t\tmessage: \"state must be JSON-serializable\",\n\t\t\t});\n\t\t}\n\t}\n\n\tfor (const field of [\"version\", \"baseVersion\", \"eventId\"] as const) {\n\t\tconst value = input[field];\n\t\tif (\n\t\t\tvalue !== undefined &&\n\t\t\t(!Number.isInteger(value) || (value as number) < 0)\n\t\t) {\n\t\t\terrors.push({\n\t\t\t\tfield,\n\t\t\t\tmessage: `${field} must be a non-negative integer when provided`,\n\t\t\t});\n\t\t}\n\t}\n\n\tfor (const field of [\"origin\"] as const) {\n\t\tconst value = input[field];\n\t\tif (value !== undefined && typeof value !== \"string\") {\n\t\t\terrors.push({\n\t\t\t\tfield,\n\t\t\t\tmessage: `${field} must be a string when provided`,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (input.ts !== undefined && !Number.isFinite(input.ts)) {\n\t\terrors.push({\n\t\t\tfield: \"ts\",\n\t\t\tmessage: \"ts must be a finite number when provided\",\n\t\t});\n\t}\n\n\tif (input.intent !== undefined) {\n\t\tif (\n\t\t\t!isRecord(input.intent) ||\n\t\t\ttypeof input.intent.type !== \"string\" ||\n\t\t\tinput.intent.type.trim().length === 0\n\t\t) {\n\t\t\terrors.push({\n\t\t\t\tfield: \"intent\",\n\t\t\t\tmessage: \"intent must contain a non-empty string type\",\n\t\t\t});\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/** Validates and applies a state event to an isolated authoritative server. */\nexport function applyStateEvent(\n\tincoming: unknown,\n\tserver: ZunoServerState = defaultZunoServerState,\n): ApplyResult {\n\tconst errors = validateStateEvent(incoming, server.maxStateBytes);\n\tif (errors.length > 0) {\n\t\treturn { ok: false, reason: \"INVALID_EVENT\", errors };\n\t}\n\n\tconst result = server.compareAndSet(incoming as ZunoStateEvent);\n\tif (!result.ok) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\treason: \"VERSION_CONFLICT\",\n\t\t\tcurrent: result.current,\n\t\t};\n\t}\n\n\tconst authoritativeEvent = result.event;\n\tserver.publishToStateEvent(authoritativeEvent);\n\n\treturn { ok: true, event: authoritativeEvent };\n}\n","import type { ZunoStateEvent } from \"../sync\";\n\nexport type ZunoServerBusMessage = {\n\tsource: string;\n\tevent: ZunoStateEvent;\n};\n\n/** Shared live-event fan-out contract (Redis, NATS, etc. can implement this). */\nexport interface ZunoServerEventBus {\n\tpublish(message: ZunoServerBusMessage): void;\n\tsubscribe(listener: (message: ZunoServerBusMessage) => void): () => void;\n}\n\n/** In-process reference bus used by tests and multi-instance development. */\nexport class MemoryZunoServerEventBus implements ZunoServerEventBus {\n\tprivate readonly listeners = new Set<\n\t\t(message: ZunoServerBusMessage) => void\n\t>();\n\n\tpublish(message: ZunoServerBusMessage): void {\n\t\tfor (const listener of this.listeners) listener(structuredClone(message));\n\t}\n\n\tsubscribe(listener: (message: ZunoServerBusMessage) => void): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => this.listeners.delete(listener);\n\t}\n}\n\nexport const createMemoryZunoServerEventBus = () =>\n\tnew MemoryZunoServerEventBus();\n","import {\n\tmkdirSync,\n\treadFileSync,\n\trenameSync,\n\trmSync,\n\tstatSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport type { ZunoStateEvent } from \"../sync\";\nimport {\n\tapplyCompareAndSet,\n\tcreateEmptyPersistedServerState,\n\ttype ZunoCompareAndSetResult,\n\ttype ZunoPersistedServerState,\n\ttype ZunoServerPersistence,\n} from \"./persistence\";\n\nexport type FileZunoServerPersistenceOptions = {\n\tlockTimeoutMs?: number;\n\tstaleLockMs?: number;\n};\n\n/** Durable JSON reference adapter with an atomic rename and cross-process lock. */\nexport class FileZunoServerPersistence implements ZunoServerPersistence {\n\tprivate readonly lockPath: string;\n\tprivate readonly lockTimeoutMs: number;\n\tprivate readonly staleLockMs: number;\n\n\tconstructor(\n\t\tprivate readonly filePath: string,\n\t\toptions: FileZunoServerPersistenceOptions = {},\n\t) {\n\t\tthis.lockPath = `${filePath}.lock`;\n\t\tthis.lockTimeoutMs = options.lockTimeoutMs ?? 5000;\n\t\tthis.staleLockMs = options.staleLockMs ?? 30000;\n\t\tif (!Number.isInteger(this.lockTimeoutMs) || this.lockTimeoutMs < 1) {\n\t\t\tthrow new TypeError(\"lockTimeoutMs must be a positive integer\");\n\t\t}\n\t\tif (!Number.isInteger(this.staleLockMs) || this.staleLockMs < 1) {\n\t\t\tthrow new TypeError(\"staleLockMs must be a positive integer\");\n\t\t}\n\t\tmkdirSync(dirname(filePath), { recursive: true });\n\t}\n\n\tprivate read(): ZunoPersistedServerState {\n\t\ttry {\n\t\t\treturn JSON.parse(readFileSync(this.filePath, \"utf8\"));\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\treturn createEmptyPersistedServerState();\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate write(state: ZunoPersistedServerState): void {\n\t\tconst temporaryPath = `${this.filePath}.${process.pid}.${crypto.randomUUID()}.tmp`;\n\t\ttry {\n\t\t\twriteFileSync(temporaryPath, JSON.stringify(state), \"utf8\");\n\t\t\trenameSync(temporaryPath, this.filePath);\n\t\t} finally {\n\t\t\trmSync(temporaryPath, { force: true });\n\t\t}\n\t}\n\n\tprivate withLock<T>(operation: () => T): T {\n\t\tconst startedAt = Date.now();\n\t\twhile (true) {\n\t\t\ttry {\n\t\t\t\tmkdirSync(this.lockPath);\n\t\t\t\tbreak;\n\t\t\t} catch (error) {\n\t\t\t\tif ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n\t\t\t\ttry {\n\t\t\t\t\tif (\n\t\t\t\t\t\tDate.now() - statSync(this.lockPath).mtimeMs >=\n\t\t\t\t\t\tthis.staleLockMs\n\t\t\t\t\t) {\n\t\t\t\t\t\trmSync(this.lockPath, { recursive: true, force: true });\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch (lockError) {\n\t\t\t\t\tif ((lockError as NodeJS.ErrnoException).code === \"ENOENT\") continue;\n\t\t\t\t\tthrow lockError;\n\t\t\t\t}\n\t\t\t\tif (Date.now() - startedAt >= this.lockTimeoutMs) {\n\t\t\t\t\tthrow new Error(`Timed out acquiring Zuno lock: ${this.lockPath}`);\n\t\t\t\t}\n\t\t\t\tAtomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 5);\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\treturn operation();\n\t\t} finally {\n\t\t\trmSync(this.lockPath, { recursive: true, force: true });\n\t\t}\n\t}\n\n\tload(): ZunoPersistedServerState {\n\t\treturn structuredClone(this.read());\n\t}\n\n\tsave(state: ZunoPersistedServerState): void {\n\t\tthis.withLock(() => this.write(structuredClone(state)));\n\t}\n\n\tcompareAndSet(\n\t\tevent: ZunoStateEvent,\n\t\tmaxEvents: number,\n\t): ZunoCompareAndSetResult {\n\t\treturn this.withLock(() => {\n\t\t\tconst state = this.read();\n\t\t\tconst result = applyCompareAndSet(state, event, maxEvents);\n\t\t\tif (result.ok) this.write(state);\n\t\t\treturn result;\n\t\t});\n\t}\n}\n\nexport const createFileZunoServerPersistence = (\n\tfilePath: string,\n\toptions?: FileZunoServerPersistenceOptions,\n) => new FileZunoServerPersistence(filePath, options);\n","import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { defaultZunoServerState, type ZunoServerState } from \"./core\";\n\n/**\n * Sends a snapshot of the current universe state to the response.\n * Compatible with both Express and raw Node.js http.\n */\nexport function sendSnapshot(\n\t_req: IncomingMessage,\n\tres: ServerResponse,\n\tserver: ZunoServerState = defaultZunoServerState,\n) {\n\tconst snapshot = {\n\t\tstate: server.getUniverseState(),\n\t\tlastEventId: server.getLastEventId(),\n\t};\n\n\t// Check for Express-like .json() method\n\t// biome-ignore lint/suspicious/noExplicitAny: Checking for dynamic .json() method on response object\n\tif (\"json\" in res && typeof (res as any).json === \"function\") {\n\t\t// biome-ignore lint/suspicious/noExplicitAny: Calling dynamic .json() method\n\t\t(res as any).json(snapshot);\n\t} else {\n\t\tres.writeHead(200, { \"Content-Type\": \"application/json\" });\n\t\tres.end(JSON.stringify(snapshot));\n\t}\n}\n","import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport type { ZunoStateEvent } from \"../sync\";\nimport { applyStateEvent } from \"./apply-state-event\";\nimport { defaultZunoServerState, type ZunoServerState } from \"./core\";\n\ntype IncomingHeaders = IncomingMessage[\"headers\"];\n\n/**\n * Creates a Server-Sent Events (SSE) connection for Zuno state updates.\n */\nexport const createSSEConnection = (\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\theaders: IncomingHeaders,\n\tserver: ZunoServerState = defaultZunoServerState,\n) => {\n\tres.writeHead(200, {\n\t\t\"Cache-Control\": \"no-cache, no-transform\",\n\t\t\"Content-Type\": \"text/event-stream; charset=utf-8\",\n\t\tConnection: \"keep-alive\",\n\t\t\"X-Accel-Buffering\": \"no\",\n\t\t...headers,\n\t});\n\n\tres.flushHeaders?.();\n\n\tconst raw =\n\t\treq.headers[\"last-event-id\"] ||\n\t\tnew URL(req.url || \"\", \"http://localhost\").searchParams.get(\"lastEventId\");\n\tconst lastEventId =\n\t\tNumber.parseInt(Array.isArray(raw) ? raw[0] : (raw ?? \"0\"), 10) || 0;\n\n\t// 1. Subscribe FIRST and buffer events until snapshot/missed-events are sent\n\tconst buffer: ZunoStateEvent[] = [];\n\tconst pendingWrites: ZunoStateEvent[] = [];\n\tlet isSyncing = true;\n\tlet backpressured = false;\n\tlet closed = false;\n\tlet heartbeat: ReturnType<typeof setInterval> | null = null;\n\tlet unsubscribe = () => {};\n\n\tconst closeConnection = () => {\n\t\tif (closed) return;\n\t\tclosed = true;\n\t\tif (heartbeat) clearInterval(heartbeat);\n\t\tunsubscribe();\n\t\tres.end();\n\t};\n\tconst flushPendingWrites = () => {\n\t\tbackpressured = false;\n\t\twhile (!closed && pendingWrites.length > 0) {\n\t\t\tconst event = pendingWrites.shift();\n\t\t\tif (!event) continue;\n\t\t\tif (!res.write(formatStateEvent(event))) {\n\t\t\t\tbackpressured = true;\n\t\t\t\tres.once(\"drain\", flushPendingWrites);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t};\n\tconst formatStateEvent = (event: ZunoStateEvent) =>\n\t\t`id: ${event.eventId}\\nevent: state\\ndata: ${JSON.stringify(event)}\\n\\n`;\n\tconst writeEvent = (event: ZunoStateEvent) => {\n\t\tif (closed) return;\n\t\tif (backpressured) {\n\t\t\tif (pendingWrites.length >= server.maxSubscriberBuffer) {\n\t\t\t\tcloseConnection();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tpendingWrites.push(event);\n\t\t\treturn;\n\t\t}\n\t\tif (!res.write(formatStateEvent(event))) {\n\t\t\tbackpressured = true;\n\t\t\tres.once(\"drain\", flushPendingWrites);\n\t\t}\n\t};\n\tconst writeSnapshot = () => {\n\t\tres.write(`id: ${server.getLastEventId()}\\n`);\n\t\tres.write(\"event: snapshot\\n\");\n\t\tres.write(`data: ${JSON.stringify(server.getUniverseState())}\\n\\n`);\n\t};\n\n\tunsubscribe = server.subscribeToStateEvents((event: ZunoStateEvent) => {\n\t\tif (isSyncing) {\n\t\t\tif (buffer.length >= server.maxSubscriberBuffer) {\n\t\t\t\tcloseConnection();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbuffer.push(event);\n\t\t} else {\n\t\t\twriteEvent(event);\n\t\t}\n\t});\n\n\t// 2. Send missed events or snapshot\n\tif (lastEventId > 0 && server.canReplayAfter(lastEventId)) {\n\t\tconst missed = server.getEventsAfter(lastEventId);\n\t\tfor (const event of missed) {\n\t\t\twriteEvent(event);\n\t\t}\n\t} else {\n\t\twriteSnapshot();\n\t}\n\n\t// 3. Flush buffer and switch to live mode\n\tisSyncing = false;\n\twhile (buffer.length > 0) {\n\t\tconst event = buffer.shift();\n\t\tif (event) writeEvent(event);\n\t}\n\tif (closed) return;\n\n\theartbeat = setInterval(() => {\n\t\tif (closed) return;\n\t\tres.write(`: ping ${Date.now()}\\n\\n`);\n\t}, 15000);\n\n\tres.write(\": connected \\n\\n\");\n\n\treq.on(\"close\", () => {\n\t\tcloseConnection();\n\t});\n};\n\n/**\n * Synchronizes the Zuno universe state by applying an incoming event.\n */\nexport const syncUniverseState = (\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tserver: ZunoServerState = defaultZunoServerState,\n) => {\n\tconst MAX_BODY_BYTES = 512 * 1024; // 512KB safety\n\tlet body = \"\";\n\n\treq.on(\"data\", (chunk: Buffer) => {\n\t\tbody += chunk.toString(\"utf8\");\n\t\tif (body.length > MAX_BODY_BYTES) {\n\t\t\tres.writeHead(413, { \"Content-Type\": \"application/json\" });\n\t\t\tres.end(JSON.stringify({ ok: false, reason: \"PAYLOAD_TOO_LARGE\" }));\n\t\t\treq.destroy();\n\t\t}\n\t});\n\n\treq.on(\"end\", () => {\n\t\ttry {\n\t\t\tconst incoming: ZunoStateEvent = JSON.parse(\n\t\t\t\tbody || \"{}\",\n\t\t\t) as unknown as ZunoStateEvent;\n\t\t\tconst result = applyStateEvent(incoming, server);\n\n\t\t\tif (!result.ok) {\n\t\t\t\tif (result.reason === \"VERSION_CONFLICT\") {\n\t\t\t\t\tres.writeHead(409, { \"Content-Type\": \"application/json\" });\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\treason: \"VERSION_CONFLICT\",\n\t\t\t\t\t\t\tcurrent: result.current,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tres.writeHead(400, { \"Content-Type\": \"application/json\" });\n\t\t\t\t\tres.end(\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\tok: false,\n\t\t\t\t\t\t\treason: result.reason,\n\t\t\t\t\t\t\terrors: result.errors,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tres.writeHead(200, { \"Content-Type\": \"application/json\" });\n\t\t\tres.end(JSON.stringify({ ok: true, event: result.event }));\n\t\t} catch {\n\t\t\tres.writeHead(400, { \"Content-Type\": \"application/json\" });\n\t\t\tres.end(JSON.stringify({ ok: false, reason: \"INVALID_JSON\" }));\n\t\t}\n\t});\n};\n\nexport const setUniverseState = (\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tserver: ZunoServerState = defaultZunoServerState,\n) => {\n\treturn syncUniverseState(req, res, server);\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iadev93/zuno",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
4
4
  "author": "Ibrahim Aftab",
5
5
  "license": "MIT",
6
6
  "type": "module",