@iadev93/zuno 0.0.11 → 0.0.13

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.
@@ -1,13 +1,14 @@
1
- var p=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;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");this.maxEvents=n,this.maxStateBytes=r;}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)}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;}},S=(t={})=>new p(t),d=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=S(this.serverOptions),this.servers.set(e,n)),n}delete(e){return this.servers.delete(e)}clear(){this.servers.clear();}},b=(t={})=>new d(t),o=S(),x=t=>o.getUniverseRecord(t),I=t=>o.updateUniverseState(t),Z=()=>o.getUniverseState(),w=t=>o.appendEvent(t),O=t=>o.getEventsAfter(t),N=()=>o.getLastEventId(),T=t=>o.subscribeToStateEvents(t),L=t=>o.publishToStateEvent(t);var f=t=>typeof t=="object"&&t!==null&&!Array.isArray(t);function E(t,e=512*1024){if(!f(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&&(!f(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 l(t,e=o){let n=E(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 a={...r,version:s.version+1};return e.updateUniverseState(a),e.appendEvent(a),e.publishToStateEvent(a),{ok:true,event:a}}function A(t,e,n=o){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 j=(t,e,n,r=o)=>{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"),a=Number.parseInt(Array.isArray(s)?s[0]:s??"0",10)||0,i=[],c=true,u=v=>{e.write(`id: ${v.eventId}
2
- `),e.write(`event: state
3
- `),e.write(`data: ${JSON.stringify(v)}
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}
2
+ event: state
3
+ data: ${JSON.stringify(o)}
4
4
 
5
- `);},g=r.subscribeToStateEvents(v=>{c?i.push(v):u(v);});if(a>0){let v=r.getEventsAfter(a);for(let y of v)u(y);}else e.write(`event: snapshot
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()}
6
+ `),e.write(`event: snapshot
6
7
  `),e.write(`data: ${JSON.stringify(r.getUniverseState())}
7
8
 
8
- `);for(c=false;i.length>0;){let v=i.shift();v&&u(v);}let m=setInterval(()=>{e.write(`: ping ${Date.now()}
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
10
 
10
- `);},15e3);e.write(`: connected
11
+ `);},15e3),e.write(`: connected
11
12
 
12
- `),t.on("close",()=>{clearInterval(m),g(),e.end();});},h=(t,e,n=o)=>{let s="";t.on("data",a=>{s+=a.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 a=JSON.parse(s||"{}"),i=l(a,n);if(!i.ok){i.reason==="VERSION_CONFLICT"?(e.writeHead(409,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:!1,reason:"VERSION_CONFLICT",current:i.current}))):(e.writeHead(400,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:!1,reason:i.reason,errors:i.errors})));return}e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:!0,event:i.event}));}catch{e.writeHead(400,{"Content-Type":"application/json"}),e.end(JSON.stringify({ok:false,reason:"INVALID_JSON"}));}});},B=(t,e,n=o)=>h(t,e,n);export{d as ZunoServerRegistry,p as ZunoServerState,w as appendEvent,l as applyStateEvent,j as createSSEConnection,b as createZunoServerRegistry,S as createZunoServerState,o as defaultZunoServerState,O as getEventsAfter,N as getLastEventId,x as getUniverseRecord,Z as getUniverseState,L as publishToStateEvent,A as sendSnapshot,B as setUniverseState,T as subscribeToStateEvents,h as syncUniverseState,I as updateUniverseState,E as validateStateEvent};//# sourceMappingURL=index.js.map
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
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","storeKey","event","current","nextVersion","lastEventId","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","isSyncing","writeEvent","unsubscribe","missed","heartbeat","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};\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\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\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\tthis.maxEvents = maxEvents;\n\t\tthis.maxStateBytes = maxStateBytes;\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\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\tlet isSyncing = true;\n\n\tconst writeEvent = (event: ZunoStateEvent) => {\n\t\tres.write(`id: ${event.eventId}\\n`);\n\t\tres.write(`event: state\\n`);\n\t\tres.write(`data: ${JSON.stringify(event)}\\n\\n`);\n\t};\n\n\tconst unsubscribe = server.subscribeToStateEvents((event: ZunoStateEvent) => {\n\t\tif (isSyncing) {\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) {\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\tres.write(`event: snapshot\\n`);\n\t\tres.write(`data: ${JSON.stringify(server.getUniverseState())}\\n\\n`);\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\n\tconst heartbeat = setInterval(() => {\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\tclearInterval(heartbeat);\n\t\tunsubscribe();\n\t\tres.end();\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/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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iadev93/zuno",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "author": "Ibrahim Aftab",
5
5
  "license": "MIT",
6
6
  "type": "module",