@lunora/replica 1.0.0-alpha.50 → 1.0.0-alpha.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/better-sqlite3.mjs +1 -1
- package/dist/adapters/sqlite-wasm.mjs +1 -1
- package/dist/adapters/sqljs.mjs +1 -1
- package/dist/index.d.mts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/{EventEmitter-ovTsLeAj.mjs → EventEmitter-uo75adUL.mjs} +1 -1
- package/dist/packem_shared/EventLog-B1-yhArT.mjs +1 -0
- package/dist/packem_shared/EventLogDO-CaZvpgJN.mjs +1 -0
- package/dist/packem_shared/EventLogDOClient-DGDMj5L5.mjs +1 -0
- package/dist/packem_shared/EventSource-BC0hKJSA.mjs +1 -0
- package/dist/packem_shared/LocalMirror-DMzMW0Mc.mjs +4 -0
- package/dist/packem_shared/MaterializerRuntime-D0HOAe24.mjs +1 -0
- package/dist/packem_shared/{SubscriptionManager-CzEXcqvp.mjs → SubscriptionManager-AhPw3lFc.mjs} +1 -1
- package/dist/packem_shared/applyDiff-Da160K7_.mjs +1 -0
- package/dist/packem_shared/applyDiffToDb-BEot1Jiz.mjs +1 -0
- package/dist/packem_shared/classifyChanges-ioMfjjbU.mjs +1 -0
- package/dist/packem_shared/defineEvents-DHo-VK7G.mjs +1 -0
- package/dist/packem_shared/{isClientSeq-DSXBJskD.mjs → isClientSeq-D2Xm0_lj.mjs} +1 -1
- package/dist/packem_shared/subscribeToMirror-CX10AaP3.mjs +1 -0
- package/dist/react.mjs +1 -1
- package/package.json +1 -1
- package/dist/packem_shared/EventLog-DmlRY_4Z.mjs +0 -1
- package/dist/packem_shared/EventLogDO-q80QYMLe.mjs +0 -1
- package/dist/packem_shared/EventLogDOClient-lkVhWras.mjs +0 -1
- package/dist/packem_shared/EventSource-ArQhJa6P.mjs +0 -1
- package/dist/packem_shared/LocalMirror-DCjNXIyf.mjs +0 -4
- package/dist/packem_shared/MaterializerRuntime-CqGGjSxl.mjs +0 -1
- package/dist/packem_shared/applyDiff-BUzddc6r.mjs +0 -1
- package/dist/packem_shared/applyDiffToDb-DSnSZmL4.mjs +0 -1
- package/dist/packem_shared/classifyChanges-IoOGDVVx.mjs +0 -1
- package/dist/packem_shared/defineEvents-D3OcpXb_.mjs +0 -1
- package/dist/packem_shared/subscribeToMirror-Dru_AYBu.mjs +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const c=t=>({exec(e,r){r&&r.length>0?t.prepare(e).run([...r]):t.exec(e)},query(e,r){const n=t.prepare(e);return r&&r.length>0?n.all([...r]):n.all()},transaction(e){t.transaction(e)()},lastInsertRowId(){const e=t.prepare("SELECT last_insert_rowid() AS id").get();return Number(e?.id??-1)},close(){t.close()}});export{c as createBetterSqlite3Adapter};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const n=t=>({exec(e,r){r&&r.length>0?t.exec(e,{bind:[...r]}):t.exec(e)},query(e,r){return t.exec(e,{bind:r&&r.length>0?[...r]:void 0,returnValue:"resultRows",rowMode:"object"})??[]},transaction(e){t.exec("BEGIN");try{e(),t.exec("COMMIT")}catch(r){throw t.exec("ROLLBACK"),r}},lastInsertRowId(){const e=t.selectValue("SELECT last_insert_rowid()");return typeof e=="number"?e:typeof e=="bigint"?Number(e):-1},close(){t.close()}});export{n as createSqliteWasmAdapter};
|
package/dist/adapters/sqljs.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const h=n=>({exec(e
|
|
1
|
+
const h=n=>({exec(r,e){e&&e.length>0?n.run(r,[...e]):n.exec(r)},query(r,e){const o=(e&&e.length>0?n.exec(r,[...e]):n.exec(r))[0];if(!o)return[];const l=o.columns,c=[];for(const s of o.values){const u={};for(const[i,f]of l.entries())u[f]=s[i];c.push(u)}return c},transaction(r){n.run("BEGIN");try{r(),n.run("COMMIT")}catch(e){throw n.run("ROLLBACK"),e}},lastInsertRowId(){const r=n.exec("SELECT last_insert_rowid() AS id"),e=r[0];if(r.length===0||!e||e.values.length===0)return-1;const t=e.values[0];return!t||t.length===0?-1:Number(t[0])},close(){n.close()}});export{h as createSqlJsAdapter};
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {
|
|
2
|
+
/**
|
|
3
|
+
* \@lunora/replica — Event sourcing runtime + local SQLite mirror for Lunora
|
|
4
|
+
*
|
|
5
|
+
* ## Modules
|
|
6
|
+
*
|
|
7
|
+
* - **EventEmitter** — type-safe event emitter with typed + wildcard listeners.
|
|
8
|
+
* - **EventSource** — event-sourcing runtime that derives state from an
|
|
9
|
+
* append-only log via a reducer.
|
|
10
|
+
* - **SubscriptionManager** — manages state and event-type subscriptions.
|
|
11
|
+
* - **SnapshotStore** — interface + in-memory store for persisting
|
|
12
|
+
* event-sourced state snapshots.
|
|
13
|
+
* - **LocalMirror** — local SQLite mirror that applies typed table diffs.
|
|
14
|
+
* - **EventLog** — append-only log for events and catch-up replication.
|
|
15
|
+
*/
|
|
16
|
+
createBetterSqlite3Adapter } from "./adapters/better-sqlite3.mjs";
|
|
2
17
|
export { createSqliteWasmAdapter } from "./adapters/sqlite-wasm.mjs";
|
|
3
18
|
export { createSqlJsAdapter } from "./adapters/sqljs.mjs";
|
|
4
19
|
import { S as SqliteAdapter } from "./packem_shared/types.d-CkMkSwLJ.mjs";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {
|
|
2
|
+
/**
|
|
3
|
+
* \@lunora/replica — Event sourcing runtime + local SQLite mirror for Lunora
|
|
4
|
+
*
|
|
5
|
+
* ## Modules
|
|
6
|
+
*
|
|
7
|
+
* - **EventEmitter** — type-safe event emitter with typed + wildcard listeners.
|
|
8
|
+
* - **EventSource** — event-sourcing runtime that derives state from an
|
|
9
|
+
* append-only log via a reducer.
|
|
10
|
+
* - **SubscriptionManager** — manages state and event-type subscriptions.
|
|
11
|
+
* - **SnapshotStore** — interface + in-memory store for persisting
|
|
12
|
+
* event-sourced state snapshots.
|
|
13
|
+
* - **LocalMirror** — local SQLite mirror that applies typed table diffs.
|
|
14
|
+
* - **EventLog** — append-only log for events and catch-up replication.
|
|
15
|
+
*/
|
|
16
|
+
createBetterSqlite3Adapter } from "./adapters/better-sqlite3.js";
|
|
2
17
|
export { createSqliteWasmAdapter } from "./adapters/sqlite-wasm.js";
|
|
3
18
|
export { createSqlJsAdapter } from "./adapters/sqljs.js";
|
|
4
19
|
import { S as SqliteAdapter } from "./packem_shared/types.d-CkMkSwLJ.js";
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createBetterSqlite3Adapter as o}from"./adapters/better-sqlite3.mjs";import{createSqliteWasmAdapter as f}from"./adapters/sqlite-wasm.mjs";import{createSqlJsAdapter as i}from"./adapters/sqljs.mjs";import{applyDiff as m,applyDiffToSnapshot as n,applyDiffs as x}from"./packem_shared/applyDiff-
|
|
1
|
+
import{createBetterSqlite3Adapter as o}from"./adapters/better-sqlite3.mjs";import{createSqliteWasmAdapter as f}from"./adapters/sqlite-wasm.mjs";import{createSqlJsAdapter as i}from"./adapters/sqljs.mjs";import{applyDiff as m,applyDiffToSnapshot as n,applyDiffs as x}from"./packem_shared/applyDiff-Da160K7_.mjs";import{defineEvents as l}from"./packem_shared/defineEvents-DHo-VK7G.mjs";import{MaterializerRuntime as S,defineMaterializer as E}from"./packem_shared/MaterializerRuntime-D0HOAe24.mjs";import{applyDiffToDb as v,applyDiffsToDb as y}from"./packem_shared/applyDiffToDb-BEot1Jiz.mjs";import{EventEmitter as d}from"./packem_shared/EventEmitter-uo75adUL.mjs";import{EventLog as M}from"./packem_shared/EventLog-B1-yhArT.mjs";import{EventLogDO as u}from"./packem_shared/EventLogDO-CaZvpgJN.mjs";import{EventLogDOClient as T}from"./packem_shared/EventLogDOClient-DGDMj5L5.mjs";import{EventSource as C,UNHANDLED as h}from"./packem_shared/EventSource-BC0hKJSA.mjs";import{eventsContext as I}from"./packem_shared/eventsContext-Dxow9Y7S.mjs";import{LocalMirror as O}from"./packem_shared/LocalMirror-DMzMW0Mc.mjs";import{isClientSeq as G,isGlobalSeq as H,isInputEvent as J}from"./packem_shared/isClientSeq-D2Xm0_lj.mjs";import{InMemorySnapshotStore as U}from"./packem_shared/InMemorySnapshotStore-C4taIG5K.mjs";import{subscribeToMirror as j}from"./packem_shared/subscribeToMirror-CX10AaP3.mjs";import{SubscriptionManager as w}from"./packem_shared/SubscriptionManager-AhPw3lFc.mjs";import{EventsSync as K}from"./packem_shared/EventsSync-DWyGGZQZ.mjs";import{classifyChanges as Q,createTableDiff as V,diffSize as X,isDiffEmpty as Y,mergeDiffs as Z}from"./packem_shared/classifyChanges-ioMfjjbU.mjs";export{d as EventEmitter,M as EventLog,u as EventLogDO,T as EventLogDOClient,C as EventSource,K as EventsSync,U as InMemorySnapshotStore,O as LocalMirror,S as MaterializerRuntime,w as SubscriptionManager,h as UNHANDLED,m as applyDiff,v as applyDiffToDb,n as applyDiffToSnapshot,x as applyDiffs,y as applyDiffsToDb,Q as classifyChanges,o as createBetterSqlite3Adapter,i as createSqlJsAdapter,f as createSqliteWasmAdapter,V as createTableDiff,l as defineEvents,E as defineMaterializer,X as diffSize,I as eventsContext,G as isClientSeq,Y as isDiffEmpty,H as isGlobalSeq,J as isInputEvent,Z as mergeDiffs,j as subscribeToMirror};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
class
|
|
1
|
+
class h{#t=new Map;#e=new Set;on(t,s){let e=this.#t.get(t);return e||(e=new Set,this.#t.set(t,e)),e.add(s),()=>{this.off(t,s)}}off(t,s){this.#t.get(t)?.delete(s)}onAny(t){return this.#e.add(t),()=>{this.#e.delete(t)}}offAny(t){this.#e.delete(t)}emit(t,s){let e=!1;const r=this.#t.get(t);if(r)for(const i of r)try{i(s),e=!0}catch{}for(const i of this.#e)try{i(t,s),e=!0}catch{}return e}hasListeners(t){return(this.#t.get(t)?.size??0)>0||this.#e.size>0}listenerCount(t){return this.#t.get(t)?.size??0}clear(){this.#t.clear(),this.#e.clear()}}export{h as EventEmitter};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class l{#e=[];#t=0;#i=0;#s=null;#n;constructor(t){this.#n=l.#o(t?.maxEntries)}static#o(t){if(t!==void 0){if(!Number.isSafeInteger(t)||t<0)throw new RangeError("maxEntries must be a non-negative safe integer");return t}}#r(){return this.#e.slice(this.#t)}#h(){if(this.#n===void 0)return;const e=this.#e.length-this.#t-this.#n;e>0&&(this.#t+=e),this.#t>0&&this.#t>=this.#e.length-this.#t&&(this.#e=this.#e.slice(this.#t),this.#t=0)}append(t,e,s,n){const r=typeof t=="string"?t:t.type,o=typeof t=="string"?e:t.payload;let h,i;typeof t=="string"?(h=s,i=n):(h=void 0,i=e);const a=i?.parentSeqNum??this.#s??void 0,f=this.#i;this.#i+=1;const c={seq:f,type:r,payload:o,timestamp:i?.timestamp??Date.now(),tableDiffs:h,clientId:i?.clientId,sessionId:i?.sessionId,parentSeqNum:a};return this.#e.push(c),this.#s=c.seq,this.#h(),c}commitAll(t){if(t.length===0)return[];const e=[];for(const s of t){const{type:n}=s,r="payload"in s?s.payload:void 0,o="timestamp"in s?s.timestamp:Date.now(),h=e.at(-1)?.seq??this.#s??void 0,i=this.#i;this.#i+=1;const a={seq:i,type:n,payload:r,timestamp:o,parentSeqNum:h};this.#e.push(a),e.push(a)}return this.#s=e.at(-1)?.seq??this.#s,this.#h(),e}load(t){this.#e=[...t.entries],this.#t=0,this.#i=t.nextSeq,this.#s=t.headSeq,this.#h()}getSince(t){const e=this.#r();if(t<=0)return e;const s=e.findIndex(n=>n.seq>=t);return s===-1?[]:e.slice(s)}getFrom(t,e=50){const s=this.#e,n=this.#t,r=s.findIndex((h,i)=>i>=n&&h.seq>=t);if(r===-1)return{entries:[],hasMore:!1};const o=Math.min(r+e,s.length);return{entries:s.slice(r,o),hasMore:o<s.length}}snapshot(){return{entries:this.#r(),nextSeq:this.#i,headSeq:this.#s}}get size(){return this.#e.length-this.#t}get nextSeq(){return this.#i}get isEmpty(){return this.size===0}get headSeq(){return this.#s}clear(){this.#e=[],this.#t=0,this.#i=0,this.#s=null}truncateBelow(t){if(!Number.isSafeInteger(t)||t<0)throw new RangeError("floorSeq must be a non-negative safe integer");const e=this.#r(),s=e.findIndex(n=>n.seq>=t);this.#e=s===-1?[]:e.slice(s),this.#t=0}async*events(t=0){const e=this.getSince(t);for(const s of e)yield s}}export{l as EventLog};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const T=Symbol("lunora.replica.event-log-do.idempotency-conflict"),q=n=>{const t=new Error(n);return Object.defineProperty(t,T,{value:!0}),t},N=n=>n instanceof Error&&T in n,h=n=>{if(Array.isArray(n))return n.map(t=>h(t));if(n!==null&&typeof n=="object"){const t=n,e=Object.keys(t);e.sort();const s={};for(const r of e)s[r]=h(t[r]);return s}return n},S=async n=>{const t=JSON.stringify(h(n)),e=new TextEncoder().encode(t),s=await crypto.subtle.digest("SHA-256",e);return[...new Uint8Array(s)].map(r=>r.toString(16).padStart(2,"0")).join("")},l=(n,t=200)=>Response.json(n,{status:t,headers:{"content-type":"application/json"}}),u=(n,t,e)=>l({error:{code:t,message:e}},n),E=n=>typeof n.toArray=="function"?n.toArray():typeof n[Symbol.iterator]=="function"?[...n]:[],f=n=>E(n).map(e=>({seq:e.seq,type:e.type,payload:JSON.parse(e.payload),timestamp:e.timestamp,clientId:e.client_id??void 0,sessionId:e.session_id??void 0,parentSeqNum:e.parent_seq??void 0}));class p{state;env;#t=!1;constructor(t,e){this.state=t,this.env=e}async fetch(t){this.#p();const e=new URL(t.url);try{if(t.method==="POST"&&e.pathname==="/append")return await this.#e(t);if(t.method==="GET"&&e.pathname==="/since")return this.#i(e);if(t.method==="GET"&&e.pathname==="/range")return this.#a(e);if(t.method==="GET"&&e.pathname==="/size")return this.#o();if(t.method==="GET"&&e.pathname==="/state")return this.#c()}catch(s){return console.error("[event-log-do] request failed:",s),u(500,"INTERNAL_ERROR","internal error")}return u(404,"NOT_FOUND","unknown route")}async#e(t){let e;try{e=await t.json()}catch{return u(400,"BAD_REQUEST","invalid JSON body")}const s=p.#n(e);if(s)return u(400,"BAD_REQUEST",s);const{sql:r}=this.state.storage,{batchId:i}=e,o=()=>p.#s(r,e,i),{transaction:a}=this.state.storage;let c;try{c=typeof a=="function"?await a(o):await o()}catch(m){if(N(m))return u(409,"CONFLICT",m.message);throw m}return l({entries:c})}static async#s(t,e,s){let r;if(typeof s=="string"){r=await S(e.events);const a=p.#u(t,s);if(a){if(a.fingerprint!==r)throw q(`batchId "${s}" was already used for a different event batch`);return a.entries}}const i=Date.now(),o=[];for(const a of e.events){const d={seq:p.#l(t),type:a.type,payload:a.payload,timestamp:a.timestamp??i,clientId:a.clientId,sessionId:a.sessionId,parentSeqNum:a.parentSeqNum};p.#m(t,d),o.push(d)}if(typeof s=="string"&&r!==void 0){const a=o[0]?.seq,c=o.at(-1)?.seq;a!==void 0&&c!==void 0&&p.#d(t,s,a,c,r)}return o}static#n(t){if(!Array.isArray(t.events)||t.events.length===0)return"events[] with a non-empty string `type` required";if(t.batchId!==void 0&&(typeof t.batchId!="string"||t.batchId.length===0))return"batchId must be a non-empty string";for(const e of t.events){const s=p.#r(e);if(s!==void 0)return s}}static#r(t){if(typeof t.type!="string"||t.type.length===0)return"events[] with a non-empty string `type` required";if(t.timestamp!==void 0&&!Number.isFinite(t.timestamp))return"events[].timestamp must be a finite number";if(t.clientId!==void 0&&typeof t.clientId!="string")return"events[].clientId must be a string";if(t.sessionId!==void 0&&typeof t.sessionId!="string")return"events[].sessionId must be a string";if(t.parentSeqNum!==void 0&&(typeof t.parentSeqNum!="number"||!Number.isInteger(t.parentSeqNum)||t.parentSeqNum<0))return"events[].parentSeqNum must be a non-negative integer"}#i(t){const e=t.searchParams.get("seq"),s=e===null?0:Number(e);if(!Number.isFinite(s)||s<0)return u(400,"BAD_REQUEST","invalid seq");const{sql:r}=this.state.storage,i=r.exec("SELECT seq, type, payload, timestamp, client_id, session_id, parent_seq FROM events WHERE seq >= ? ORDER BY seq ASC",s);return l({entries:f(i)})}#a(t){const e=t.searchParams.get("from"),s=e===null?0:Number(e),r=t.searchParams.get("limit"),i=r===null?50:Number(r);if(!Number.isFinite(s)||s<0||!Number.isFinite(i)||i<1||i>1e3)return u(400,"BAD_REQUEST","invalid from/limit");const{sql:o}=this.state.storage,a=o.exec("SELECT seq, type, payload, timestamp, client_id, session_id, parent_seq FROM events WHERE seq >= ? ORDER BY seq ASC LIMIT ?",s,i+1),c=f(a),d=c.length>i,y={entries:d?c.slice(0,i):c,hasMore:d};return l(y)}#o(){const{sql:t}=this.state.storage,e=t.exec("SELECT COUNT(*) AS count FROM events"),r=E(e)[0]?.count??0;return l({count:r})}#c(){const{sql:t}=this.state.storage,e=t.exec("SELECT seq, type, payload, timestamp, client_id, session_id, parent_seq FROM events ORDER BY seq ASC"),s=f(e),r=(s.at(-1)?.seq??-1)+1;return l({entries:s,nextSeq:r})}#p(){if(this.#t)return;const{sql:t}=this.state.storage;t.exec("CREATE TABLE IF NOT EXISTS events (seq INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT NOT NULL, payload TEXT NOT NULL, timestamp INTEGER NOT NULL, client_id TEXT, session_id TEXT, parent_seq INTEGER)"),t.exec("CREATE TABLE IF NOT EXISTS event_batches (batch_id TEXT PRIMARY KEY, first_seq INTEGER NOT NULL, last_seq INTEGER NOT NULL, fingerprint TEXT NOT NULL)"),this.#t=!0}static#u(t,e){const s=t.exec("SELECT first_seq, last_seq, fingerprint FROM event_batches WHERE batch_id = ?",e),i=E(s)[0];if(!i)return;const o=t.exec("SELECT seq, type, payload, timestamp, client_id, session_id, parent_seq FROM events WHERE seq >= ? AND seq <= ? ORDER BY seq ASC",i.first_seq,i.last_seq);return{entries:f(o),fingerprint:i.fingerprint}}static#d(t,e,s,r,i){t.exec("INSERT INTO event_batches (batch_id, first_seq, last_seq, fingerprint) VALUES (?, ?, ?, ?)",e,s,r,i)}static#l(t){const e=t.exec("SELECT COALESCE(MAX(seq), -1) + 1 AS next_seq FROM events");return E(e)[0]?.next_seq??0}static#m(t,e){const s=typeof e.parentSeqNum=="number"?e.parentSeqNum:null;t.exec("INSERT INTO events (seq, type, payload, timestamp, client_id, session_id, parent_seq) VALUES (?, ?, ?, ?, ?, ?, ?)",e.seq,e.type,JSON.stringify(e.payload),e.timestamp,e.clientId??null,e.sessionId??null,s)}}export{p as EventLogDO};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class s{#a;constructor(t){this.#a=t.fetch}async append(t,a){const e=JSON.stringify({events:t,batchId:a?.batchId}),n=await this.#a(new Request("https://do/append",{method:"POST",headers:{"content-type":"application/json"},body:e}));if(!n.ok)throw await s.#e(n,"append");return(await n.json()).entries}async getSince(t){return(await this.#t(`/since?seq=${String(t)}`,"getSince")).entries}async getRange(t,a=50){return this.#t(`/range?from=${String(t)}&limit=${String(a)}`,"getRange")}async getSize(){return(await this.#t("/size","getSize")).count}async getState(){return this.#t("/state","getState")}async#t(t,a){const e=await this.#a(new Request(`https://do${t}`));if(!e.ok)throw await s.#e(e,a);return await e.json()}static async#e(t,a){try{const n=(await t.json()).error?.message??t.statusText;return new Error(`EventLogDO.${a} failed (${String(t.status)}): ${n}`)}catch{return new Error(`EventLogDO.${a} failed (${String(t.status)}): ${t.statusText}`)}}}export{s as EventLogDOClient};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{EventEmitter as p}from"./EventEmitter-uo75adUL.mjs";import{EventLog as f}from"./EventLog-B1-yhArT.mjs";const d=Symbol("lunora.replica.event-source.unhandled");class w{emitter=new p;log;#e;#t;#n=!1;#i;#r=-1;constructor(e,t,n){this.#e={...e},this.#t=t,this.#i=n?.unknownEventHandling??"warn",this.log=new f({maxEntries:n?.maxLogEntries})}get state(){return this.#e}get replayed(){return this.#n}applyEvent(e,t,n){let r,o,i;typeof e=="string"?(r=e,o=t,i=n):(r=e.type,o=e.payload,i=t);const s={seq:this.log.nextSeq,type:r,payload:o,timestamp:Date.now(),clientId:i?.clientId,sessionId:i?.sessionId,parentSeqNum:i?.parentSeqNum??this.log.headSeq??void 0};let a;try{a=this.#t(this.#e,s)}catch(h){const u=h instanceof Error?h:new Error(String(h));throw this.emitter.emit("replay-error",{entry:s,error:u}),u}let c=!0;a===d&&(c=this.#s(s));const l=this.log.append(r,o,void 0,{...i,timestamp:s.timestamp});return a===d?(c&&this.emitter.emit("state-changed",{state:this.#e,entry:l}),l):(this.#e=a,this.emitter.emit("state-changed",{state:this.#e,entry:l}),l)}replayFromLog(e){const t=e.getSince(this.#r+1);for(const n of t)try{const r=this.#t(this.#e,n);r!==d&&(this.#e=r),this.log.append(n.type,n.payload,n.tableDiffs,{clientId:n.clientId,sessionId:n.sessionId,parentSeqNum:n.parentSeqNum})}catch(r){this.emitter.emit("replay-error",{entry:n,error:r instanceof Error?r:new Error(String(r))})}finally{this.#r=n.seq}this.#n=!0,this.emitter.emit("ready",{entryCount:this.log.size})}reset(e,t=-1){this.#e={...e},this.#r=t,this.#n=t>=0}async*events(e){const t=[];let n;const r=this.emitter.on("state-changed",({entry:i})=>{t.push(i),n?.()}),o=()=>{n?.()};e?.addEventListener("abort",o,{once:!0});try{let i=0;for(;i<this.log.size;){if(e?.aborted)return;for(const s of this.log.getSince(i))yield s;i=this.log.nextSeq}for(;!e?.aborted;){for(;t.length>0;){const s=t.shift();s&&(yield s)}t.length===0&&await new Promise(s=>{n=s})}}finally{r(),e?.removeEventListener("abort",o)}}#s(e){const t=this.#i;if(typeof t=="function")return t(e);switch(t){case"ignore":return!1;case"fail":throw new Error(`EventSource: unhandled event type "${e.type}" (seq ${String(e.seq)}). Configure \`unknownEventHandling\` to handle this event or change the strategy.`);default:return console.warn(`[EventSource] unhandled event type "${e.type}" (seq ${String(e.seq)}). The event was skipped. Configure \`unknownEventHandling\` if this is expected.`),!1}}}export{w as EventSource,d as UNHANDLED};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{createSqlJsAdapter as u}from"../adapters/sqljs.mjs";import{applyDiffToDb as f,escapeIdentifier as o}from"./applyDiffToDb-BEot1Jiz.mjs";import{EventLog as m}from"./EventLog-B1-yhArT.mjs";const l="__lunora_mirror_meta",y=a=>{a.exec(`CREATE TABLE IF NOT EXISTS ${l} (
|
|
2
|
+
key TEXT PRIMARY KEY NOT NULL,
|
|
3
|
+
value TEXT NOT NULL
|
|
4
|
+
)`)},h="schema_version",T=2,b=a=>typeof a=="bigint"||typeof a=="boolean"?"INTEGER":typeof a=="number"?Number.isInteger(a)?"INTEGER":"REAL":"TEXT";class E{#e;#t;#s;#n=new Set;#i=0;static create(e,t){const s=u(e);return new E({db:s,tables:t?.tables})}constructor(e){this.#e=e.db,this.#t={...e.tables},this.#s=new m({maxEntries:e.maxEventLogEntries}),y(this.#e),this.#a()}onChange(e){return this.#n.add(e),()=>{this.#n.delete(e)}}get eventLog(){return this.#s}get db(){return this.#e}get version(){return this.#i}applyDiff(e){if(e.changes.length===0)return;const t=this.#t[e.table]?.primaryKey??"id";this.#l(e),f(this.#e,e,t),this.#s.append("table-diff",e,[e]),this.#r()}query(e,t){return this.#e.query(e,t)}clearData(){const e=this.#o();this.#e.transaction(()=>{for(const{name:t}of e)this.#e.exec(`DELETE FROM ${o(t)}`)}),this.#r()}#r(){this.#i+=1;for(const e of this.#n)try{e()}catch{}}close(){this.#e.close(),this.#s.clear()}registerTable(e,t){this.#t[e]=t}get mirroredTables(){return Object.keys(this.#t)}#o(){return this.#e.query(String.raw`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE '\_\_lunora\_%' ESCAPE '\' AND name NOT LIKE 'sqlite\_%' ESCAPE '\'`)}#a(){if(this.#e.query(`SELECT key, value FROM ${l}`).find(n=>n.key===h)?.value===String(T))return;const s=this.#o();this.#e.transaction(()=>{for(const{name:n}of s)this.#e.exec(`DROP TABLE IF EXISTS ${o(n)}`);this.#e.exec(`INSERT OR REPLACE INTO ${l} (key, value) VALUES (?, ?)`,[h,String(T)])})}static#c(e,t){const s=new Set;for(const n of e.changes)if(n.type!=="delete")for(const c of Object.keys(n.data))c!==t&&s.add(c);return s}static#E(e,t,s){const n=new Map;for(const c of e.changes)if(!(c.type==="delete"||n.size===s.size))for(const r of s){if(r===t||n.has(r))continue;const i=c.data[r];i!=null&&n.set(r,b(i))}return n}#l(e){const t=this.#t[e.table]?.primaryKey??"id",s=E.#c(e,t),n=E.#E(e,t,s);if(this.#e.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?",[e.table]).length===0){let r=`${o(t)} TEXT PRIMARY KEY NOT NULL`;for(const i of s)r+=`, ${o(i)} ${n.get(i)??"TEXT"}`;this.#e.exec(`CREATE TABLE IF NOT EXISTS ${o(e.table)} (${r})`)}else if(s.size>0){const r=new Set(this.#e.query(`PRAGMA table_info(${o(e.table)})`).map(i=>i.name));for(const i of s)r.has(i)||this.#e.exec(`ALTER TABLE ${o(e.table)} ADD COLUMN ${o(i)} ${n.get(i)??"TEXT"}`)}}}export{E as LocalMirror};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const c=a=>{let e=a.initial();return{def:a,get state(){return Object.freeze(e)},setState(t){e=t},apply(t){e=a.handle(e,t)},reset(){e=a.initial()}}};class u{#t;#n;#i;#s;#e;constructor(e,t={}){this.#t=[...e],this.#e=this.#t.map(()=>0),this.#n=t.snapshotStore,this.#i=t.doClient,this.#s=t.unknownEventHandling??"warn"}get appliedSeq(){return this.#e.length>0?Math.min(...this.#e):0}applyEntries(e){let t=0;for(const n of e){let s=!1,i=!1;const r=[];for(const[o,h]of this.#t.entries()){const l=this.#e[o]??0;if(n.seq<l)continue;const p=h.state;h.apply(n),s=!0,h.state!==p&&(i=!0),r.push(o)}if(s){i||this.#r(n);for(const o of r)this.#e[o]=n.seq+1;t+=1}}return t}#r(e){const t=this.#s;if(typeof t=="function"){t(e);return}switch(t){case"ignore":return;case"fail":throw new Error(`MaterializerRuntime: unhandled event type "${e.type}" (seq ${String(e.seq)}). Configure \`unknownEventHandling\` to handle this event or change the strategy.`);default:console.warn(`[MaterializerRuntime] unhandled event type "${e.type}" (seq ${String(e.seq)}). The event was skipped. Configure \`unknownEventHandling\` if this is expected.`)}}async recoverFromSnapshots(){if(!this.#n)return 0;let e=0;for(const[t,n]of this.#t.entries()){const s=await this.#n.load(n.def.name);if(s!==null&&typeof s=="object"){const i=s;if(Number.isSafeInteger(i.appliedSeq)&&i.appliedSeq>=0&&i.state!==void 0){const r=i.appliedSeq;n.setState(i.state),this.#e[t]=r,r>e&&(e=r)}}}return e}async persistSnapshots(){if(this.#n)for(const[e,t]of this.#t.entries())await this.#n.save(t.def.name,{appliedSeq:this.#e[e]??0,state:t.state})}async initialize(){if(!this.#i)return 0;await this.recoverFromSnapshots();const e=this.#e.length>0?Math.min(...this.#e):0,t=await this.#i.getSince(e);return t.length===0?0:this.applyEntries(t)}async appendEvent(e){if(!this.#i)throw new Error("MaterializerRuntime.appendEvent requires a doClient — pass one in the constructor options.");const n=(await this.#i.append([e]))[0];if(!n)throw new Error("MaterializerRuntime.appendEvent: DO returned empty result");return this.applyEntries([n]),n}reset(){for(const[e,t]of this.#t.entries())this.#e[e]=0,t.reset()}get materializers(){return this.#t}}export{u as MaterializerRuntime,c as defineMaterializer};
|
package/dist/packem_shared/{SubscriptionManager-CzEXcqvp.mjs → SubscriptionManager-AhPw3lFc.mjs}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
class
|
|
1
|
+
class i{#t=new Map;#e=0;#s(e){const t=String(this.#e);return this.#e+=1,this.#t.set(t,{...e,id:t}),()=>{this.#t.delete(t)}}onStateChange(e){return this.#s({kind:"state",callback:e})}onEvent(e,t){return this.#s({kind:"event",eventType:e,callback:t})}notifyState(e){for(const t of this.#t.values())if(t.kind==="state")try{t.callback(e)}catch{}}notifyEvent(e){for(const t of this.#t.values())if(t.kind==="event"&&t.eventType===e.type)try{t.callback(e)}catch{}}get size(){return this.#t.size}clear(){this.#t.clear()}}export{i as SubscriptionManager};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const a=t=>{if(Array.isArray(t))return t.map(n=>a(n));if(t!==null&&typeof t=="object"){const n=t,e=Object.keys(n);e.sort();const o={};for(const s of e)o[s]=a(n[s]);return o}return t},r=t=>t.toString(16).padStart(4,"0"),u=t=>{let n=8997,e=33826,o=40164,s=52210;for(let c=0;c<t.length;c+=1){const i=t.codePointAt(c)??0;n^=i&65535,e^=i>>>16&65535;const p=n*435,y=e*435,h=o*435+n*256,b=s*435+e*256,d=y+(p>>>16),f=h+(d>>>16),g=b+(f>>>16);n=p&65535,e=d&65535,o=f&65535,s=g&65535}return r(s)+r(o)+r(e)+r(n)},x=(t,n,e)=>{const o=t.id??String(t.timestamp),s=`${t.table}::${o}::${String(n)}::${JSON.stringify(a(e))}`;return`row-${u(s)}`},l=(t,n)=>{for(const[e,o]of n.changes.entries())switch(o.type){case"delete":{t.delete(o.id);break}case"insert":{const s=o.data.id,c=typeof s=="string"||typeof s=="number"?String(s):x(n,e,o.data);t.set(c,{...o.data,id:c});break}case"update":{const s=t.get(o.id);s&&t.set(o.id,{...s,...o.data});break}}},w=(t,n)=>{const e=new Map(t);return l(e,n),e},S=(t,n)=>{const e=new Map(t);for(const o of n)l(e,o);return e},I=(t,n)=>{const e=new Map(t),o=e.get(n.table)??new Map;return e.set(n.table,w(o,n)),e};export{w as applyDiff,I as applyDiffToSnapshot,S as applyDiffs,a as canonicalizeForHash,x as deriveInsertId,u as fnv1a64Hex};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const c=t=>`\`${t.replaceAll("`","``")}\``,E=t=>t.map(n=>`${c(n)} = ?`).join(", "),$=t=>`(${t.map(n=>c(n)).join(", ")})`,b=t=>`(${Array.from({length:t}).fill("?").join(", ")})`,y=t=>t==null?null:typeof t=="number"||typeof t=="bigint"||typeof t=="string"?t:typeof t=="boolean"?t?1:0:t instanceof Uint8Array?t:typeof t=="object"?JSON.stringify(t):String(t),g=(t,n,o)=>{if(n.changes.length===0)return;const r=c(n.table),l=c(o);for(const s of n.changes)switch(s.type){case"delete":{t.exec(`DELETE FROM ${r} WHERE ${l} = ?`,[s.id]);break}case"insert":{const{data:i}=s,e=Object.keys(i);if(e.length===0)continue;const f=`INSERT OR REPLACE INTO ${r} ${$(e)} VALUES ${b(e.length)}`,a=e.map(p=>y(i[p]));t.exec(f,a);break}case"update":{const{data:i}=s,e=Object.keys(i);if(e.length===0)continue;const f=`UPDATE ${r} SET ${E(e)} WHERE ${l} = ?`,a=[...e.map(p=>y(i[p])),s.id];t.exec(f,a);break}}},D=(t,n,o)=>{t.transaction(()=>{g(t,n,o??"id")})},h=(t,n)=>{n.length!==0&&t.transaction(()=>{for(const o of n)g(t,o,"id")})};export{D as applyDiffToDb,h as applyDiffsToDb,c as escapeIdentifier,y as normalizeBindValue};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{fnv1a64Hex as a}from"./applyDiff-Da160K7_.mjs";let s=0;const c=()=>{if(typeof crypto<"u"&&typeof crypto.randomUUID=="function")return crypto.randomUUID();s+=1;const t=typeof crypto<"u"&&typeof crypto.getRandomValues=="function"?Array.from(crypto.getRandomValues(new Uint8Array(8)),n=>n.toString(16).padStart(2,"0")).join(""):Math.random().toString(16).slice(2,12);return`id_${Date.now().toString(36)}_${s.toString(36)}_${t}`},i=(t,n,o,r)=>({table:t,changes:n,timestamp:o??Date.now(),id:r??c()}),g=t=>t.changes.length===0,m=t=>t.changes.length,u=t=>{const n=[],o=[],r=[];for(const e of t.changes)e.type==="insert"?n.push(e):e.type==="update"?o.push(e):r.push(e);return{inserts:n,updates:o,deletes:r}},f=t=>{if(t.length===0)return null;const n=t[0],o=t[t.length-1],r=`merge:${a(t.map(e=>e.id??String(e.timestamp)).join("|"))}`;return i(n.table,t.flatMap(e=>e.changes),o.timestamp,r)};export{u as classifyChanges,i as createTableDiff,m as diffSize,g as isDiffEmpty,f as mergeDiffs};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const v=(a,n)=>{const e={},s={},i=n?.version?`${n.version}.`:"";for(const[o,p]of Object.entries(a)){const c={};for(const r of Object.keys(p)){const t=`${i}${o}.${r}`;s[t]=void 0;const f=Object.assign(y=>({type:t,payload:y,timestamp:Date.now()}),{type:t});c[r]=f}e[o]=c}return e._types=s,e};export{v as defineEvents};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const n=t=>typeof t=="number",e=t=>typeof t!="number"&&"rebaseGeneration"in t,
|
|
1
|
+
const n=t=>typeof t=="number",e=t=>typeof t!="number"&&"rebaseGeneration"in t,o=t=>typeof t=="object"&&t!==null&&"type"in t&&typeof t.type=="string"&&"payload"in t&&"timestamp"in t;export{e as isClientSeq,n as isGlobalSeq,o as isInputEvent};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const y=e=>`fn_${e.replaceAll(/[/:.]/g,"_")}`,w=e=>Array.isArray(e)?e:e!==null&&typeof e=="object"?[e]:[],d=(e,o,c,a,p)=>{const i=y(c.__lunoraRef);o.registerTable(i,{});let f=new Set;return e.subscribe(c,a,u=>{const b=w(u),n=new Set,r=[];for(const t of b){const l=t,s=l.id;(typeof s=="string"||typeof s=="number")&&n.add(String(s)),r.push({type:"insert",data:l})}for(const t of f)n.has(t)||r.push({type:"delete",id:t});f=n,r.length!==0&&o.applyDiff({table:i,changes:r,timestamp:Date.now()})},{shardKey:p})};export{d as subscribeToMirror};
|
package/dist/react.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{useSyncExternalStore as
|
|
1
|
+
import{useSyncExternalStore as i,useMemo as y}from"react";const u=t=>JSON.stringify(t??[],(r,n)=>typeof n=="bigint"?`${n.toString()}n`:n),p=(t,r,n,g)=>{const s=e=>t.onChange(e),o=()=>t.version,c=i(s,o,o),a=u(n);return y(()=>{try{return{data:t.query(r,n)}}catch(e){return{error:e instanceof Error?e:new Error(String(e))}}},[t,c,r,a])};export{p as useLocalQuery};
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
class c{#s=[];#t=0;#i=0;#e=null;#n;constructor(t){this.#n=c.#o(t?.maxEntries)}static#o(t){if(t!==void 0){if(!Number.isSafeInteger(t)||t<0)throw new RangeError("maxEntries must be a non-negative safe integer");return t}}#h(){return this.#s.slice(this.#t)}#r(){if(this.#n===void 0)return;const t=this.#s.length-this.#t-this.#n;t>0&&(this.#t+=t),this.#t>0&&this.#t>=this.#s.length-this.#t&&(this.#s=this.#s.slice(this.#t),this.#t=0)}append(t,e,s,n){const h=typeof t=="string"?t:t.type,o=typeof t=="string"?e:t.payload;let r,i;typeof t=="string"?(r=s,i=n):(r=void 0,i=e);const a=i?.parentSeqNum??this.#e??void 0,p=this.#i;this.#i+=1;const l={seq:p,type:h,payload:o,timestamp:i?.timestamp??Date.now(),tableDiffs:r,clientId:i?.clientId,sessionId:i?.sessionId,parentSeqNum:a};return this.#s.push(l),this.#e=l.seq,this.#r(),l}commitAll(t){if(t.length===0)return[];const e=[];for(const s of t){const{type:n}=s,h="payload"in s?s.payload:void 0,o="timestamp"in s?s.timestamp:Date.now(),r=e.at(-1)?.seq??this.#e??void 0,i=this.#i;this.#i+=1;const a={seq:i,type:n,payload:h,timestamp:o,parentSeqNum:r};this.#s.push(a),e.push(a)}return this.#e=e.at(-1)?.seq??this.#e,this.#r(),e}load(t){this.#s=[...t.entries],this.#t=0,this.#i=t.nextSeq,this.#e=t.headSeq,this.#r()}getSince(t){const e=this.#h();if(t<=0)return e;const s=e.findIndex(n=>n.seq>=t);return s===-1?[]:e.slice(s)}getFrom(t,e=50){const s=this.#s,n=this.#t,h=s.findIndex((r,i)=>i>=n&&r.seq>=t);if(h===-1)return{entries:[],hasMore:!1};const o=Math.min(h+e,s.length);return{entries:s.slice(h,o),hasMore:o<s.length}}snapshot(){return{entries:this.#h(),nextSeq:this.#i,headSeq:this.#e}}get size(){return this.#s.length-this.#t}get nextSeq(){return this.#i}get isEmpty(){return this.size===0}get headSeq(){return this.#e}clear(){this.#s=[],this.#t=0,this.#i=0,this.#e=null}truncateBelow(t){if(!Number.isSafeInteger(t)||t<0)throw new RangeError("floorSeq must be a non-negative safe integer");const e=this.#h(),s=e.findIndex(n=>n.seq>=t);this.#s=s===-1?[]:e.slice(s),this.#t=0}async*events(t=0){const e=this.getSince(t);for(const s of e)yield s}}export{c as EventLog};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const y=Symbol("lunora.replica.event-log-do.idempotency-conflict"),T=r=>{const e=new Error(r);return Object.defineProperty(e,y,{value:!0}),e},h=r=>r instanceof Error&&y in r,f=r=>{if(Array.isArray(r))return r.map(e=>f(e));if(r!==null&&typeof r=="object"){const e=r,t=Object.keys(e);t.sort();const s={};for(const n of t)s[n]=f(e[n]);return s}return r},N=async r=>{const e=JSON.stringify(f(r)),t=new TextEncoder().encode(e),s=await crypto.subtle.digest("SHA-256",t);return[...new Uint8Array(s)].map(n=>n.toString(16).padStart(2,"0")).join("")},m=(r,e=200)=>Response.json(r,{status:e,headers:{"content-type":"application/json"}}),u=(r,e,t)=>m({error:{code:e,message:t}},r),E=r=>typeof r.toArray=="function"?r.toArray():typeof r[Symbol.iterator]=="function"?[...r]:[],l=r=>E(r).map(e=>({seq:e.seq,type:e.type,payload:JSON.parse(e.payload),timestamp:e.timestamp,clientId:e.client_id??void 0,sessionId:e.session_id??void 0,parentSeqNum:e.parent_seq??void 0}));class p{state;env;#e=!1;constructor(e,t){this.state=e,this.env=t}async fetch(e){this.#p();const t=new URL(e.url);try{if(e.method==="POST"&&t.pathname==="/append")return await this.#t(e);if(e.method==="GET"&&t.pathname==="/since")return this.#i(t);if(e.method==="GET"&&t.pathname==="/range")return this.#a(t);if(e.method==="GET"&&t.pathname==="/size")return this.#o();if(e.method==="GET"&&t.pathname==="/state")return this.#c()}catch(s){return console.error("[event-log-do] request failed:",s),u(500,"INTERNAL_ERROR","internal error")}return u(404,"NOT_FOUND","unknown route")}async#t(e){let t;try{t=await e.json()}catch{return u(400,"BAD_REQUEST","invalid JSON body")}const s=p.#n(t);if(s)return u(400,"BAD_REQUEST",s);const{sql:n}=this.state.storage,{batchId:a}=t,c=()=>p.#s(n,t,a),{transaction:i}=this.state.storage;let o;try{o=typeof i=="function"?await i(c):await c()}catch(d){if(h(d))return u(409,"CONFLICT",d.message);throw d}return m({entries:o})}static async#s(e,t,s){let n;if(typeof s=="string"){n=await N(t.events);const i=p.#u(e,s);if(i){if(i.fingerprint!==n)throw T(`batchId "${s}" was already used for a different event batch`);return i.entries}}const a=Date.now(),c=[];for(const i of t.events){const o={seq:p.#m(e),type:i.type,payload:i.payload,timestamp:i.timestamp??a,clientId:i.clientId,sessionId:i.sessionId,parentSeqNum:i.parentSeqNum};p.#l(e,o),c.push(o)}if(typeof s=="string"&&n!==void 0){const i=c[0]?.seq,o=c.at(-1)?.seq;i!==void 0&&o!==void 0&&p.#d(e,s,i,o,n)}return c}static#n(e){if(!Array.isArray(e.events)||e.events.length===0)return"events[] with a non-empty string `type` required";if(e.batchId!==void 0&&(typeof e.batchId!="string"||e.batchId.length===0))return"batchId must be a non-empty string";for(const t of e.events){const s=p.#r(t);if(s!==void 0)return s}}static#r(e){if(typeof e.type!="string"||e.type.length===0)return"events[] with a non-empty string `type` required";if(e.timestamp!==void 0&&!Number.isFinite(e.timestamp))return"events[].timestamp must be a finite number";if(e.clientId!==void 0&&typeof e.clientId!="string")return"events[].clientId must be a string";if(e.sessionId!==void 0&&typeof e.sessionId!="string")return"events[].sessionId must be a string";if(e.parentSeqNum!==void 0&&(typeof e.parentSeqNum!="number"||!Number.isInteger(e.parentSeqNum)||e.parentSeqNum<0))return"events[].parentSeqNum must be a non-negative integer"}#i(e){const t=e.searchParams.get("seq"),s=t===null?0:Number(t);if(!Number.isFinite(s)||s<0)return u(400,"BAD_REQUEST","invalid seq");const{sql:n}=this.state.storage,a=n.exec("SELECT seq, type, payload, timestamp, client_id, session_id, parent_seq FROM events WHERE seq >= ? ORDER BY seq ASC",s);return m({entries:l(a)})}#a(e){const t=e.searchParams.get("from"),s=t===null?0:Number(t),n=e.searchParams.get("limit"),a=n===null?50:Number(n);if(!Number.isFinite(s)||s<0||!Number.isFinite(a)||a<1||a>1e3)return u(400,"BAD_REQUEST","invalid from/limit");const{sql:c}=this.state.storage,i=c.exec("SELECT seq, type, payload, timestamp, client_id, session_id, parent_seq FROM events WHERE seq >= ? ORDER BY seq ASC LIMIT ?",s,a+1),o=l(i),d=o.length>a,q={entries:d?o.slice(0,a):o,hasMore:d};return m(q)}#o(){const{sql:e}=this.state.storage,t=e.exec("SELECT COUNT(*) AS count FROM events"),s=E(t)[0]?.count??0;return m({count:s})}#c(){const{sql:e}=this.state.storage,t=e.exec("SELECT seq, type, payload, timestamp, client_id, session_id, parent_seq FROM events ORDER BY seq ASC"),s=l(t),n=(s.at(-1)?.seq??-1)+1;return m({entries:s,nextSeq:n})}#p(){if(this.#e)return;const{sql:e}=this.state.storage;e.exec("CREATE TABLE IF NOT EXISTS events (seq INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT NOT NULL, payload TEXT NOT NULL, timestamp INTEGER NOT NULL, client_id TEXT, session_id TEXT, parent_seq INTEGER)"),e.exec("CREATE TABLE IF NOT EXISTS event_batches (batch_id TEXT PRIMARY KEY, first_seq INTEGER NOT NULL, last_seq INTEGER NOT NULL, fingerprint TEXT NOT NULL)"),this.#e=!0}static#u(e,t){const s=e.exec("SELECT first_seq, last_seq, fingerprint FROM event_batches WHERE batch_id = ?",t),n=E(s)[0];if(!n)return;const a=e.exec("SELECT seq, type, payload, timestamp, client_id, session_id, parent_seq FROM events WHERE seq >= ? AND seq <= ? ORDER BY seq ASC",n.first_seq,n.last_seq);return{entries:l(a),fingerprint:n.fingerprint}}static#d(e,t,s,n,a){e.exec("INSERT INTO event_batches (batch_id, first_seq, last_seq, fingerprint) VALUES (?, ?, ?, ?)",t,s,n,a)}static#m(e){const t=e.exec("SELECT COALESCE(MAX(seq), -1) + 1 AS next_seq FROM events");return E(t)[0]?.next_seq??0}static#l(e,t){const s=typeof t.parentSeqNum=="number"?t.parentSeqNum:null;e.exec("INSERT INTO events (seq, type, payload, timestamp, client_id, session_id, parent_seq) VALUES (?, ?, ?, ?, ?, ?, ?)",t.seq,t.type,JSON.stringify(t.payload),t.timestamp,t.clientId??null,t.sessionId??null,s)}}export{p as EventLogDO};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
class s{#e;constructor(t){this.#e=t.fetch}async append(t,e){const n=JSON.stringify({events:t,batchId:e?.batchId}),a=await this.#e(new Request("https://do/append",{method:"POST",headers:{"content-type":"application/json"},body:n}));if(!a.ok)throw await s.#n(a,"append");return(await a.json()).entries}async getSince(t){return(await this.#t(`/since?seq=${String(t)}`,"getSince")).entries}async getRange(t,e=50){return this.#t(`/range?from=${String(t)}&limit=${String(e)}`,"getRange")}async getSize(){return(await this.#t("/size","getSize")).count}async getState(){return this.#t("/state","getState")}async#t(t,e){const n=await this.#e(new Request(`https://do${t}`));if(!n.ok)throw await s.#n(n,e);return await n.json()}static async#n(t,e){try{const n=(await t.json()).error?.message??t.statusText;return new Error(`EventLogDO.${e} failed (${String(t.status)}): ${n}`)}catch{return new Error(`EventLogDO.${e} failed (${String(t.status)}): ${t.statusText}`)}}}export{s as EventLogDOClient};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{EventEmitter as g}from"./EventEmitter-ovTsLeAj.mjs";import{EventLog as y}from"./EventLog-DmlRY_4Z.mjs";const c=Symbol("lunora.replica.event-source.unhandled");class v{emitter=new g;log;#t;#e;#n=!1;#s;#r=-1;constructor(t,e,n){this.#t={...t},this.#e=e,this.#s=n?.unknownEventHandling??"warn",this.log=new y({maxEntries:n?.maxLogEntries})}get state(){return this.#t}get replayed(){return this.#n}applyEvent(t,e,n){let r,o,s;typeof t=="string"?(r=t,o=e,s=n):(r=t.type,o=t.payload,s=e);const i={seq:this.log.nextSeq,type:r,payload:o,timestamp:Date.now(),clientId:s?.clientId,sessionId:s?.sessionId,parentSeqNum:s?.parentSeqNum??this.log.headSeq??void 0};let a;try{a=this.#e(this.#t,i)}catch(l){const p=l instanceof Error?l:new Error(String(l));throw this.emitter.emit("replay-error",{entry:i,error:p}),p}let d=!0;a===c&&(d=this.#i(i));const h=this.log.append(r,o,void 0,{...s,timestamp:i.timestamp});return a===c?(d&&this.emitter.emit("state-changed",{state:this.#t,entry:h}),h):(this.#t=a,this.emitter.emit("state-changed",{state:this.#t,entry:h}),h)}replayFromLog(t){const e=t.getSince(this.#r+1);for(const n of e)try{const r=this.#e(this.#t,n);r!==c&&(this.#t=r),this.log.append(n.type,n.payload,n.tableDiffs,{clientId:n.clientId,sessionId:n.sessionId,parentSeqNum:n.parentSeqNum})}catch(r){this.emitter.emit("replay-error",{entry:n,error:r instanceof Error?r:new Error(String(r))})}finally{this.#r=n.seq}this.#n=!0,this.emitter.emit("ready",{entryCount:this.log.size})}reset(t,e=-1){this.#t={...t},this.#r=e,this.#n=e>=0}async*events(t){const e=[];let n;const r=this.emitter.on("state-changed",({entry:s})=>{e.push(s),n?.()}),o=()=>{n?.()};t?.addEventListener("abort",o,{once:!0});try{let s=0;for(;s<this.log.size;){if(t?.aborted)return;for(const i of this.log.getSince(s))yield i;s=this.log.nextSeq}for(;!t?.aborted;){for(;e.length>0;){const i=e.shift();i&&(yield i)}e.length===0&&await new Promise(i=>{n=i})}}finally{r(),t?.removeEventListener("abort",o)}}#i(t){const e=this.#s;if(typeof e=="function")return e(t);switch(e){case"ignore":return!1;case"fail":throw new Error(`EventSource: unhandled event type "${t.type}" (seq ${String(t.seq)}). Configure \`unknownEventHandling\` to handle this event or change the strategy.`);default:return console.warn(`[EventSource] unhandled event type "${t.type}" (seq ${String(t.seq)}). The event was skipped. Configure \`unknownEventHandling\` if this is expected.`),!1}}}export{v as EventSource,c as UNHANDLED};
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{createSqlJsAdapter as T}from"../adapters/sqljs.mjs";import{applyDiffToDb as u,escapeIdentifier as a}from"./applyDiffToDb-DSnSZmL4.mjs";import{EventLog as m}from"./EventLog-DmlRY_4Z.mjs";const c="__lunora_mirror_meta",y=o=>{o.exec(`CREATE TABLE IF NOT EXISTS ${c} (
|
|
2
|
-
key TEXT PRIMARY KEY NOT NULL,
|
|
3
|
-
value TEXT NOT NULL
|
|
4
|
-
)`)},l="schema_version",f=2,b=o=>typeof o=="bigint"||typeof o=="boolean"?"INTEGER":typeof o=="number"?Number.isInteger(o)?"INTEGER":"REAL":"TEXT";class E{#t;#e;#s;#r=new Set;#i=0;static create(t,e){const r=T(t);return new E({db:r,tables:e?.tables})}constructor(t){this.#t=t.db,this.#e={...t.tables},this.#s=new m({maxEntries:t.maxEventLogEntries}),y(this.#t),this.#o()}onChange(t){return this.#r.add(t),()=>{this.#r.delete(t)}}get eventLog(){return this.#s}get db(){return this.#t}get version(){return this.#i}applyDiff(t){if(t.changes.length===0)return;const e=this.#e[t.table]?.primaryKey??"id";this.#h(t),u(this.#t,t,e),this.#s.append("table-diff",t,[t]),this.#n()}query(t,e){return this.#t.query(t,e)}clearData(){const t=this.#a();this.#t.transaction(()=>{for(const{name:e}of t)this.#t.exec(`DELETE FROM ${a(e)}`)}),this.#n()}#n(){this.#i+=1;for(const t of this.#r)try{t()}catch{}}close(){this.#t.close(),this.#s.clear()}registerTable(t,e){this.#e[t]=e}get mirroredTables(){return Object.keys(this.#e)}#a(){return this.#t.query(String.raw`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE '\_\_lunora\_%' ESCAPE '\' AND name NOT LIKE 'sqlite\_%' ESCAPE '\'`)}#o(){if(this.#t.query(`SELECT key, value FROM ${c}`).find(e=>e.key===l)?.value===String(f))return;const t=this.#a();this.#t.transaction(()=>{for(const{name:e}of t)this.#t.exec(`DROP TABLE IF EXISTS ${a(e)}`);this.#t.exec(`INSERT OR REPLACE INTO ${c} (key, value) VALUES (?, ?)`,[l,String(f)])})}static#E(t,e){const r=new Set;for(const i of t.changes)if(i.type!=="delete")for(const n of Object.keys(i.data))n!==e&&r.add(n);return r}static#c(t,e,r){const i=new Map;for(const n of t.changes)if(!(n.type==="delete"||i.size===r.size))for(const s of r){if(s===e||i.has(s))continue;const h=n.data[s];h!=null&&i.set(s,b(h))}return i}#h(t){const e=this.#e[t.table]?.primaryKey??"id",r=E.#E(t,e),i=E.#c(t,e,r);if(this.#t.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?",[t.table]).length===0){let n=`${a(e)} TEXT PRIMARY KEY NOT NULL`;for(const s of r)n+=`, ${a(s)} ${i.get(s)??"TEXT"}`;this.#t.exec(`CREATE TABLE IF NOT EXISTS ${a(t.table)} (${n})`)}else if(r.size>0){const n=new Set(this.#t.query(`PRAGMA table_info(${a(t.table)})`).map(s=>s.name));for(const s of r)n.has(s)||this.#t.exec(`ALTER TABLE ${a(t.table)} ADD COLUMN ${a(s)} ${i.get(s)??"TEXT"}`)}}}export{E as LocalMirror};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const c=a=>{let e=a.initial();return{def:a,get state(){return Object.freeze(e)},setState(t){e=t},apply(t){e=a.handle(e,t)},reset(){e=a.initial()}}};class f{#e;#n;#i;#s;#t;constructor(e,t={}){this.#e=[...e],this.#t=this.#e.map(()=>0),this.#n=t.snapshotStore,this.#i=t.doClient,this.#s=t.unknownEventHandling??"warn"}get appliedSeq(){return this.#t.length>0?Math.min(...this.#t):0}applyEntries(e){let t=0;for(const i of e){let s=!1,n=!1;const r=[];for(const[o,h]of this.#e.entries()){const p=this.#t[o]??0;if(i.seq<p)continue;const l=h.state;h.apply(i),s=!0,h.state!==l&&(n=!0),r.push(o)}if(s){n||this.#r(i);for(const o of r)this.#t[o]=i.seq+1;t+=1}}return t}#r(e){const t=this.#s;if(typeof t=="function"){t(e);return}switch(t){case"ignore":return;case"fail":throw new Error(`MaterializerRuntime: unhandled event type "${e.type}" (seq ${String(e.seq)}). Configure \`unknownEventHandling\` to handle this event or change the strategy.`);default:console.warn(`[MaterializerRuntime] unhandled event type "${e.type}" (seq ${String(e.seq)}). The event was skipped. Configure \`unknownEventHandling\` if this is expected.`)}}async recoverFromSnapshots(){if(!this.#n)return 0;let e=0;for(const[t,i]of this.#e.entries()){const s=await this.#n.load(i.def.name);if(s!==null&&typeof s=="object"){const n=s;if(Number.isSafeInteger(n.appliedSeq)&&n.appliedSeq>=0&&n.state!==void 0){const r=n.appliedSeq;i.setState(n.state),this.#t[t]=r,r>e&&(e=r)}}}return e}async persistSnapshots(){if(this.#n)for(const[e,t]of this.#e.entries())await this.#n.save(t.def.name,{appliedSeq:this.#t[e]??0,state:t.state})}async initialize(){if(!this.#i)return 0;await this.recoverFromSnapshots();const e=this.#t.length>0?Math.min(...this.#t):0,t=await this.#i.getSince(e);return t.length===0?0:this.applyEntries(t)}async appendEvent(e){if(!this.#i)throw new Error("MaterializerRuntime.appendEvent requires a doClient — pass one in the constructor options.");const t=(await this.#i.append([e]))[0];if(!t)throw new Error("MaterializerRuntime.appendEvent: DO returned empty result");return this.applyEntries([t]),t}reset(){for(const[e,t]of this.#e.entries())this.#t[e]=0,t.reset()}get materializers(){return this.#e}}export{f as MaterializerRuntime,c as defineMaterializer};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const i=t=>{if(Array.isArray(t))return t.map(e=>i(e));if(t!==null&&typeof t=="object"){const e=t,r=Object.keys(e);r.sort();const a={};for(const s of r)a[s]=i(e[s]);return a}return t},n=t=>t.toString(16).padStart(4,"0"),w=t=>{let e=8997,r=33826,a=40164,s=52210;for(let o=0;o<t.length;o+=1){const c=t.codePointAt(o)??0;e^=c&65535,r^=c>>>16&65535;const f=e*435,u=r*435,y=a*435+e*256,g=s*435+r*256,p=u+(f>>>16),d=y+(p>>>16),b=g+(d>>>16);e=f&65535,r=p&65535,a=d&65535,s=b&65535}return n(s)+n(a)+n(r)+n(e)},S=(t,e,r)=>{const a=t.id??String(t.timestamp),s=`${t.table}::${a}::${String(e)}::${JSON.stringify(i(r))}`;return`row-${w(s)}`},l=(t,e)=>{for(const[r,a]of e.changes.entries())switch(a.type){case"delete":{t.delete(a.id);break}case"insert":{const s=a.data.id,o=typeof s=="string"||typeof s=="number"?String(s):S(e,r,a.data);t.set(o,{...a.data,id:o});break}case"update":{const s=t.get(a.id);s&&t.set(a.id,{...s,...a.data});break}}},h=(t,e)=>{const r=new Map(t);return l(r,e),r},k=(t,e)=>{const r=new Map(t);for(const a of e)l(r,a);return r},M=(t,e)=>{const r=new Map(t),a=r.get(e.table)??new Map;return r.set(e.table,h(a,e)),r};export{h as applyDiff,M as applyDiffToSnapshot,k as applyDiffs,i as canonicalizeForHash,S as deriveInsertId,w as fnv1a64Hex};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const t=e=>`\`${e.replaceAll("`","``")}\``,g=e=>e.map(n=>`${t(n)} = ?`).join(", "),E=e=>`(${e.map(n=>t(n)).join(", ")})`,$=e=>`(${Array.from({length:e}).fill("?").join(", ")})`,y=e=>e==null?null:typeof e=="number"||typeof e=="bigint"||typeof e=="string"?e:typeof e=="boolean"?e?1:0:e instanceof Uint8Array?e:typeof e=="object"?JSON.stringify(e):String(e),b=(e,n,o)=>{if(n.changes.length===0)return;const c=t(n.table),p=t(o);for(const i of n.changes)switch(i.type){case"delete":{e.exec(`DELETE FROM ${c} WHERE ${p} = ?`,[i.id]);break}case"insert":{const{data:s}=i,a=Object.keys(s);if(a.length===0)continue;const r=`INSERT OR REPLACE INTO ${c} ${E(a)} VALUES ${$(a.length)}`,f=a.map(l=>y(s[l]));e.exec(r,f);break}case"update":{const{data:s}=i,a=Object.keys(s);if(a.length===0)continue;const r=`UPDATE ${c} SET ${g(a)} WHERE ${p} = ?`,f=[...a.map(l=>y(s[l])),i.id];e.exec(r,f);break}}},d=(e,n,o)=>{e.transaction(()=>{b(e,n,o??"id")})},h=(e,n)=>{n.length!==0&&e.transaction(()=>{for(const o of n)b(e,o,"id")})};export{d as applyDiffToDb,h as applyDiffsToDb,t as escapeIdentifier,y as normalizeBindValue};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{fnv1a64Hex as s}from"./applyDiff-BUzddc6r.mjs";let o=0;const i=()=>{if(typeof crypto<"u"&&typeof crypto.randomUUID=="function")return crypto.randomUUID();o+=1;const t=typeof crypto<"u"&&typeof crypto.getRandomValues=="function"?Array.from(crypto.getRandomValues(new Uint8Array(8)),n=>n.toString(16).padStart(2,"0")).join(""):Math.random().toString(16).slice(2,12);return`id_${Date.now().toString(36)}_${o.toString(36)}_${t}`},p=(t,n,a,r)=>({table:t,changes:n,timestamp:a??Date.now(),id:r??i()}),c=t=>t.changes.length===0,g=t=>t.changes.length,m=t=>{const n=[],a=[],r=[];for(const e of t.changes)e.type==="insert"?n.push(e):e.type==="update"?a.push(e):r.push(e);return{inserts:n,updates:a,deletes:r}},l=t=>{if(t.length===0)return null;const n=t[0],a=t[t.length-1],r=`merge:${s(t.map(e=>e.id??String(e.timestamp)).join("|"))}`;return p(n.table,t.flatMap(e=>e.changes),a.timestamp,r)};export{m as classifyChanges,p as createTableDiff,g as diffSize,c as isDiffEmpty,l as mergeDiffs};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const d=(i,o)=>{const e={},s={},f=o?.version?`${o.version}.`:"";for(const[n,p]of Object.entries(i)){const c={};for(const r of Object.keys(p)){const t=`${f}${n}.${r}`;s[t]=void 0;const y=Object.assign(a=>({type:t,payload:a,timestamp:Date.now()}),{type:t});c[r]=y}e[n]=c}return e._types=s,e};export{d as defineEvents};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const d=e=>`fn_${e.replaceAll(/[/:.]/g,"_")}`,u=e=>Array.isArray(e)?e:e!==null&&typeof e=="object"?[e]:[],g=(e,a,n,f,i)=>{const l=d(n.__lunoraRef);a.registerTable(l,{});let p=new Set;return e.subscribe(n,f,b=>{const y=u(b),o=new Set,t=[];for(const r of y){const c=r,s=c.id;(typeof s=="string"||typeof s=="number")&&o.add(String(s)),t.push({type:"insert",data:c})}for(const r of p)o.has(r)||t.push({type:"delete",id:r});p=o,t.length!==0&&a.applyDiff({table:l,changes:t,timestamp:Date.now()})},{shardKey:i})};export{g as subscribeToMirror};
|