!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).MetonaSqlark={})}(this,function(e){"use strict";const t=["string","number","boolean","date","json"],s=Object.freeze({name:"metona-sqlark",mode:"hybrid",diskEngine:"opfs",version:1,maxRowsPerQuery:0,debug:!1,multiTabSync:!1,aria:void 0});class n extends Error{constructor(e,t,s){super(e),this.code=t,this.details=s,this.name="DatabaseError"}}const i="0.6.0",r=new Map;function a(e,t,s={}){for(const[n,i]of Object.entries(t))if("$caseResult"!==n){if("$exists"!==n){if("$and"===n){if(!i.every(t=>a(e,t,s)))return!1;continue}if("$or"===n){if(!i.some(t=>a(e,t,s)))return!1;continue}if("$not"!==n){if(!o(e[n],i,e,s))return!1}else if(a(e,i,s))return!1}else if(!0!==i)return!1}else if(!0!==i)return!1;return!0}function o(e,t,s,n){if("object"==typeof t&&null!==t&&"$and"in t)return t.$and.every(e=>a(s,e,n));if("object"==typeof t&&null!==t&&"$or"in t)return t.$or.some(e=>a(s,e,n));if("object"==typeof t&&null!==t&&"$not"in t)return!o(e,t.$not,s,n);if("object"!=typeof t||null===t||Array.isArray(t))return e===t;const i=t;if(n.$col&&"$col"in i&&1===Object.keys(i).length)return e===s[i.$col];for(const[t,r]of Object.entries(i)){let i=r;if(n.$col&&"object"==typeof r&&null!==r&&"$col"in r&&(i=s[r.$col]),!h(e,t,i))return!1}return!0}function h(e,t,s){switch(t){case"$eq":return e===s;case"$ne":return e!==s;case"$gt":return e>s;case"$gte":return e>=s;case"$lt":return e{for(const{column:n,direction:i,nulls:r}of t){const t=null===e[n]||void 0===e[n],a=null===s[n]||void 0===s[n];if(r&&(t||a)){if(t&&a)continue;return"first"===r?t?-1:1:t?1:-1}const o=l(e[n],s[n]);if(0!==o)return"desc"===i?-o:o}return 0})}function l(e,t){return e===t?0:null==e?1:null==t?-1:"string"==typeof e&&"string"==typeof t?e.localeCompare(t):"number"==typeof e&&"number"==typeof t?e-t:String(e).localeCompare(String(t))}function u(e,t){const s={};for(const n of t)if(n in e)s[n]=e[n];else for(const t of Object.keys(e))if(t.endsWith(`.${n}`)||t===n){s[n]=e[t];break}return s}class f{constructor(){this.name="memory",this.tables=new Map,this.schemas=new Map,this.indexes=new Map,this.opened=!1,this.metaStore=new Map,this.snapshot=null}async open(e,t){this.opened||(this.opened=!0)}async close(){this.tables.clear(),this.schemas.clear(),this.indexes.clear(),this.metaStore.clear(),this.opened=!1}isOpen(){return this.opened}async repair(){}async clearAll(){const e=Array.from(this.schemas.keys());for(const t of e)await this.dropTable(t);this.metaStore.clear()}async getMeta(e){return this.metaStore.get(e)??null}async setMeta(e,t){this.metaStore.set(e,t)}async createTable(e){if(this.schemas.has(e.name))throw new n(`Table "${e.name}" already exists`,"TABLE_EXISTS");const t={name:e.name,columns:{}};for(const[s,n]of Object.entries(e.columns))t.columns[s]={...n};this.schemas.set(e.name,t),this.tables.set(e.name,new Map);const s=new Map;for(const[e,n]of Object.entries(t.columns))(n.index||n.unique)&&s.set(e,new Map);this.indexes.set(e.name,s)}async dropTable(e){this.ensureTable(e),this.schemas.delete(e),this.tables.delete(e),this.indexes.delete(e)}async hasTable(e){return this.schemas.has(e)}async getTableNames(){return Array.from(this.schemas.keys())}async getTableSchema(e){return this.schemas.get(e)??null}async alterTable(e,t,s){this.ensureTable(e);const i=this.schemas.get(e);if("ADD"===t){if(i.columns[s.name])throw new n(`Column "${s.name}" already exists in table "${e}"`,"COLUMN_EXISTS");return void(i.columns[s.name]=s)}if(!i.columns[s.name])throw new n(`Column "${s.name}" does not exist in table "${e}"`,"COLUMN_NOT_FOUND");delete i.columns[s.name];const r=this.tables.get(e);for(const e of r.values())s.name in e&&delete e[s.name]}async insert(e,t){this.ensureTable(e);const s=this.schemas.get(e),i=this.tables.get(e),r=this.getPrimaryKey(s),a=[];for(const o of t){const t=this.validateRow(s,o),h=String(t[r]);if(i.has(h))throw new n(`Duplicate primary key "${h}" in table "${e}"`,"DUPLICATE_KEY");this.checkUniqueness(s,t),i.set(h,t),this.updateIndexes(e,t,h),a.push(h)}return a}async find(e,t){this.ensureTable(e);const s=this.tables.get(e);let n=this.tryIndexLookup(e,s,t);t.where&&Object.keys(t.where).length>0&&(n=n.filter(e=>a(e,t.where))),t.orderBy&&t.orderBy.length>0&&(n=c(n,t.orderBy));const i=t.offset??0,r=t.limit??n.length;return n=n.slice(i,i+r),t.columns&&t.columns.length>0&&"*"!==t.columns[0]&&(n=n.map(e=>u(e,t.columns))),n}async findStream(e,t,s){this.ensureTable(e);const n=this.tables.get(e),i=!!(t.where&&Object.keys(t.where).length>0),r=t.limit??1/0,o=t.offset??0,h=t.columns&&t.columns.length>0&&"*"!==t.columns[0]?e=>u(e,t.columns):null;let c=0,l=0;for(const e of n.values())if(!i||a(e,t.where))if(l=r)break;return c}async update(e,t,s){this.ensureTable(e);const n=this.schemas.get(e),i=this.tables.get(e),r=this.getPrimaryKey(n);let o=0;for(const[h,c]of[...i])if(!t.where||0===Object.keys(t.where).length||a(c,t.where)){this.removeIndexEntries(e,c,h);const t={...c,...s};this.validateRow(n,t),this.checkUniqueness(n,t);const a=String(t[r]);a!==h&&await this.applyUpdateCascade(e,h,a),i.delete(h),i.set(a,t),this.updateIndexes(e,t,a),o++}return o}async applyUpdateCascade(e,t,s){for(const[s,i]of this.schemas)if(s!==e)for(const[r,a]of Object.entries(i.columns)){if(!a.references||!a.onUpdate)continue;const[i]=a.references.split(".");if(i!==e)continue;const o=this.tables.get(s);if(o)for(const[,i]of o)if(String(i[r])===t&&"RESTRICT"===a.onUpdate)throw new n(`Cannot update "${e}" key "${t}": foreign key "${r}" in "${s}" has dependent rows`,"FOREIGN_KEY_VIOLATION")}for(const[n,i]of this.schemas)if(n!==e)for(const[r,a]of Object.entries(i.columns)){if(!a.references||!a.onUpdate)continue;const[i]=a.references.split(".");if(i!==e)continue;const o=this.tables.get(n);if(o&&("CASCADE"===a.onUpdate||"SET NULL"===a.onUpdate))for(const[e,i]of o)String(i[r])===t&&(this.removeIndexEntries(n,i,e),i[r]="CASCADE"===a.onUpdate?s:null,this.updateIndexes(n,i,e))}}async delete(e,t){this.ensureTable(e);const s=this.tables.get(e),n=[];for(const[i,r]of s)t.where&&0!==Object.keys(t.where).length&&!a(r,t.where)||(this.removeIndexEntries(e,r,i),n.push(i));let i=0;for(const t of n){const n=s.get(t);n&&(i+=await this.cascadeDelete(e,t,n))}for(const e of n)s.delete(e);return n.length+i}async count(e,t){this.ensureTable(e);const s=this.tables.get(e);if(!t?.where||0===Object.keys(t.where).length)return s.size;let n=0;for(const e of s.values())a(e,t.where)&&n++;return n}async clear(e){this.ensureTable(e),this.tables.get(e).clear();const t=this.indexes.get(e);if(t)for(const e of t.values())e.clear()}async createIndex(e,t,s){this.ensureTable(e);const i=this.schemas.get(e).columns[t];if(!i)throw new n(`Column "${t}" does not exist in table "${e}"`,"COLUMN_NOT_FOUND");if(i.index||i.unique)return;i.index=!0,s&&(i.unique=!0);const r=this.indexes.get(e);r.has(t)||r.set(t,new Map);const a=r.get(t),o=this.tables.get(e);for(const[e,s]of o){const n=s[t];null!=n&&(a.has(n)||a.set(n,new Set),a.get(n).add(e))}}async dropIndex(e,t,s){this.ensureTable(e);const i=this.schemas.get(e).columns[t];if(!i)throw new n(`Column "${t}" does not exist in table "${e}"`,"COLUMN_NOT_FOUND");if(!i.index&&!i.unique)throw new n(`Index on column "${t}" does not exist in table "${e}"`,"INDEX_NOT_FOUND");i.index=!1,i.unique=!1;const r=this.indexes.get(e);r&&r.delete(t)}async beginTransaction(){if(this.snapshot)throw new n("Transaction already in progress","TX_ACTIVE");this.snapshot={tables:this.deepCloneMapMap(this.tables),schemas:new Map(this.schemas),indexes:this.deepCloneIndexes(this.indexes)}}async commitTransaction(){if(!this.snapshot)throw new n("No active transaction","TX_NONE");this.snapshot=null}async rollbackTransaction(){if(!this.snapshot)throw new n("No active transaction","TX_NONE");this.tables=this.snapshot.tables,this.schemas=this.snapshot.schemas,this.indexes=this.snapshot.indexes,this.snapshot=null}deepCloneMapMap(e){const t=new Map;for(const[s,n]of e){const e=new Map;for(const[t,s]of n)e.set(t,{...s});t.set(s,e)}return t}deepCloneIndexes(e){const t=new Map;for(const[s,n]of e){const e=new Map;for(const[t,s]of n){const n=new Map;for(const[e,t]of s)n.set(e,new Set(t));e.set(t,n)}t.set(s,e)}return t}ensureTable(e){if(!this.tables.has(e))throw new n(`Table "${e}" does not exist`,"TABLE_NOT_FOUND")}getPrimaryKey(e){for(const[t,s]of Object.entries(e.columns))if(s.primaryKey)return t;return Object.keys(e.columns)[0]}validateRow(e,t){const s={};for(const[i,r]of Object.entries(e.columns)){let a=t[i];if(void 0===a&&void 0!==r.default&&(a=r.default),r.required&&null==a)throw new n(`Column "${i}" is required in table "${e.name}"`,"VALIDATION_ERROR");null!=a&&this.checkType(i,r.type,a),void 0!==a&&(s[i]=a)}return s}checkType(e,t,s){const i=typeof s;switch(t){case"string":if("string"!==i)throw new n(`Column "${e}" expects string, got ${i}`,"TYPE_ERROR");break;case"number":if("number"!==i)throw new n(`Column "${e}" expects number, got ${i}`,"TYPE_ERROR");break;case"boolean":if("boolean"!==i)throw new n(`Column "${e}" expects boolean, got ${i}`,"TYPE_ERROR");break;case"date":if("string"!==i||isNaN(Date.parse(s)))throw new n(`Column "${e}" expects valid date`,"TYPE_ERROR");break;case"json":if("object"!==i)throw new n(`Column "${e}" expects object/array, got ${i}`,"TYPE_ERROR")}}checkUniqueness(e,t){const s=this.indexes.get(e.name);if(s)for(const[i,r]of Object.entries(e.columns)){if(!r.unique||void 0===t[i]||null===t[i])continue;const a=s.get(i);if(a&&a.has(t[i]))throw new n(`Unique constraint violation on column "${i}" in table "${e.name}"`,"UNIQUE_VIOLATION")}}tryIndexLookup(e,t,s){const n=this.indexes.get(e);if(!n||!s.where)return Array.from(t.values());for(const[e,i]of Object.entries(s.where)){let s;if("object"!=typeof i||null===i)s=i;else{if(!("$eq"in i)||1!==Object.keys(i).length)continue;s=i.$eq}const r=n.get(e);if(r){const e=r.get(s);if(e){const s=[];for(const n of e){const e=t.get(n);e&&s.push(e)}return s}return[]}}return Array.from(t.values())}updateIndexes(e,t,s){const n=this.indexes.get(e);if(n)for(const[e,i]of n){const n=t[e];null!=n&&(i.has(n)||i.set(n,new Set),i.get(n).add(s))}}removeIndexEntries(e,t,s){const n=this.indexes.get(e);if(n)for(const[e,i]of n){const n=t[e];if(null!=n){const e=i.get(n);e&&(e.delete(s),0===e.size&&i.delete(n))}}}async cascadeDelete(e,t,s){let i=0;for(const[s,r]of this.schemas)if(s!==e)for(const[a,o]of Object.entries(r.columns)){if(!o.references||!o.onDelete)continue;const[r]=o.references.split(".");if(r!==e)continue;const h=this.tables.get(s);if(!h)continue;const c=[];for(const[e,s]of h)String(s[a])===t&&c.push(e);if("RESTRICT"===o.onDelete&&c.length>0)throw new n(`Cannot delete from "${e}": foreign key "${a}" in "${s}" has dependent rows`,"FOREIGN_KEY_VIOLATION");if("CASCADE"===o.onDelete)for(const e of c){const t=h.get(e);t&&(this.removeIndexEntries(s,t,e),i+=await this.cascadeDelete(s,e,t)),h.delete(e),i++}else if("SET NULL"===o.onDelete)for(const e of c){const t=h.get(e);if(t){if(void 0!==t[a]&&null!==t[a]){const n=this.indexes.get(s)?.get(a);n&&n.get(t[a])?.delete(e)}t[a]=null}}}return i}}const d=[".crswap",".tmp"];class p{constructor(){this.root=null,this.dbDir=null,this.dbName="",this.writeQueue=Promise.resolve()}async open(e){this.dbName=e,this.root=await navigator.storage.getDirectory(),this.dbDir=await this.root.getDirectoryHandle(e,{create:!0}),await this.cleanupStaleFiles()}async close(){try{await this.writeQueue}catch{}this.dbDir=null,this.root=null}isOpen(){return null!==this.dbDir}async cleanupStaleFiles(){if(this.dbDir)try{const e=this.dbDir,t=[];for await(const[s]of e.entries())d.some(e=>s.endsWith(e))&&t.push(s);for(const e of t)try{await this.dbDir.removeEntry(e)}catch{}}catch{}}async read(e){if(!this.dbDir)return null;try{const t=await this.dbDir.getFileHandle(e),s=await t.getFile();return await s.arrayBuffer()}catch{return null}}async write(e,t){if(!this.dbDir)return;const s=this.writeQueue.then(async()=>{const s=await this.dbDir.getFileHandle(e,{create:!0}),n=await s.createWritable();await n.write(t),await n.close()});return this.writeQueue=s.then(()=>{},()=>{}),s}async append(e,t){if(!this.dbDir)return;const s=this.writeQueue.then(async()=>{const s=await this.dbDir.getFileHandle(e,{create:!0}),n=await s.getFile(),i=await s.createWritable({keepExistingData:!0});await i.write({type:"write",position:n.size,data:t}),await i.close()});return this.writeQueue=s.then(()=>{},()=>{}),s}async writeMany(e){if(!this.dbDir)return;const t=this.writeQueue.then(async()=>{for(const[t,s]of Object.entries(e)){const e=await this.dbDir.getFileHandle(t,{create:!0}),n=await e.createWritable();await n.write(s),await n.close()}});return this.writeQueue=t.then(()=>{},()=>{}),t}async delete(e){if(!this.dbDir)return;const t=this.writeQueue.then(async()=>{try{await this.dbDir.removeEntry(e)}catch{}});return this.writeQueue=t.then(()=>{},()=>{}),t}async deleteMany(e){if(!this.dbDir)return;const t=this.writeQueue.then(async()=>{for(const t of e)try{await this.dbDir.removeEntry(t)}catch{}});return this.writeQueue=t.then(()=>{},()=>{}),t}async listKeys(){if(!this.dbDir)return[];const e=[],t=this.dbDir;for await(const[s]of t.entries())e.push(s);return e}async exists(e){if(!this.dbDir)return!1;try{return await this.dbDir.getFileHandle(e),!0}catch{return!1}}async clear(){if(!this.dbDir)return;const e=this.dbDir;for await(const[t]of e.entries())try{await this.dbDir.removeEntry(t)}catch{}}}const y=new Map;class m{constructor(){this.dbName="",this.store=null}static clearRegistry(){y.clear()}static registrySize(){return y.size}async open(e){this.dbName=e,y.has(e)||y.set(e,new Map),this.store=y.get(e)}async close(){this.store=null}isOpen(){return null!==this.store}async read(e){return this.store?.get(e)??null}async write(e,t){this.store?.set(e,t)}async append(e,t){if(!this.store)return;const s=this.store.get(e);if(s){const n=new Uint8Array(s.byteLength+t.byteLength);n.set(new Uint8Array(s),0),n.set(new Uint8Array(t),s.byteLength),this.store.set(e,n.buffer)}else this.store.set(e,t)}async writeMany(e){if(this.store)for(const[t,s]of Object.entries(e))this.store.set(t,s)}async delete(e){this.store?.delete(e)}async deleteMany(e){if(this.store)for(const t of e)this.store.delete(t)}async listKeys(){return this.store?Array.from(this.store.keys()):[]}async exists(e){return this.store?.has(e)??!1}async clear(){this.store?.clear()}}const g=(()=>{const e=new Uint32Array(256);for(let t=0;t<256;t++){let s=t;for(let e=0;e<8;e++)s=1&s?3988292384^s>>>1:s>>>1;e[t]=s>>>0}return e})(),w=4294967295;function b(e,t=0){const s=function(e,t){let s=e>>>0;for(let e=0;e>>8)>>>0;return s>>>0}(t^w,e);return function(e){return(e^w)>>>0}(s)}var T;!function(e){e[e.PUT=1]="PUT",e[e.DELETE=2]="DELETE"}(T||(T={}));const E=1263948622;function k(e,t){const s=new TextEncoder,n=Array.from(t.keys()),i=[];let r=12;for(const e of n){const n=s.encode(e),a=new Uint8Array(t.get(e));i.push({key:n,value:a}),r+=4+n.byteLength+4+a.byteLength}r+=4;const a=new Uint8Array(r),o=new DataView(a.buffer);let h=0;o.setUint32(h,E,!1),h+=4,o.setUint32(h,e,!1),h+=4,o.setUint32(h,i.length,!1),h+=4;for(const e of i)o.setUint32(h,e.key.byteLength,!1),h+=4,a.set(e.key,h),h+=e.key.byteLength,o.setUint32(h,e.value.byteLength,!1),h+=4,a.set(e.value,h),h+=e.value.byteLength;const c=b(a.subarray(0,r-4));return o.setUint32(r-4,c,!1),a}function x(e){if(e.byteLength<16)return null;const t=new DataView(e.buffer,e.byteOffset,e.byteLength);if(t.getUint32(0,!1)!==E)return null;if(t.getUint32(e.byteLength-4,!1)!==b(e.subarray(0,e.byteLength-4)))return null;const s=new TextDecoder,n=new Map;let i=4;const r=t.getUint32(i,!1);i+=4;const a=t.getUint32(i,!1);i+=4;for(let r=0;re.byteLength-4)return null;const r=t.getUint32(i,!1);if(i+=4,i+r+4>e.byteLength-4)return null;const a=s.decode(e.subarray(i,i+r));i+=r;const o=t.getUint32(i,!1);if(i+=4,i+o>e.byteLength-4)return null;const h=e.slice(i,i+o).buffer;i+=o,n.set(a,h)}return{seq:r,entries:n}}const S="__kv_log",I="__kv_snapshot",A="__kv_meta";class R{constructor(e,t=16777216){this.dbName="",this.opened=!1,this.index=new Map,this.seq=0,this.logBytes=0,this.opQueue=Promise.resolve(),this.lastBackgroundError=null,this.medium=e??function(){const e=globalThis.navigator;return void 0!==e&&e.storage&&"function"==typeof e.storage.getDirectory?new p:new m}(),this.checkpointThreshold=t}getMedium(){return this.medium}isOpen(){return this.opened}async open(e){if(this.opened)return;this.dbName=e,await this.medium.open(e),this.index=new Map,this.seq=0,this.logBytes=0;let t=0;const s=await this.medium.read(A);if(s)try{const e=JSON.parse((new TextDecoder).decode(s));t=Number(e.seq)||0}catch{}let n=0;const i=await this.medium.read(I);if(i){const e=x(new Uint8Array(i));e?(this.index=new Map(e.entries),this.seq=e.seq,n=e.seq):(this.index=new Map,this.seq=0,n=0)}const r=await this.medium.read(S);if(r&&r.byteLength>0){const e=new Uint8Array(r),s=Math.max(t,n),i=[];(function(e,t,s){let n=0,i=0;const r=new DataView(e.buffer,e.byteOffset,e.byteLength),a=new TextDecoder;for(;n+4<=e.byteLength;){const o=r.getUint32(n,!1);if(o<12||n+4+o>e.byteLength){if(s&&!s(n))break;break}const h=n,c=n+4+o,l=e.subarray(h,c),u=new DataView(e.buffer,e.byteOffset+h,o+4);if(u.getUint32(o,!1)!==b(l.subarray(0,o))){if(s&&!s(h))break;break}let f=4;const d=u.getUint32(f,!1);f+=4;const p=u.getUint32(f,!1);f+=4;const y=[];let m=!0;for(let e=0;eo+4){m=!1;break}const e=u.getUint8(f);f+=1;const t=u.getUint32(f,!1);if(f+=4,f+t+4>o+4){m=!1;break}const s=a.decode(l.subarray(f,f+t));f+=t;const n=u.getUint32(f,!1);if(f+=4,f+n>o+4){m=!1;break}const i=l.slice(f,f+n).buffer;f+=n,y.push({op:e,key:s,value:i})}if(!m){if(s&&!s(h))break;break}t({seq:d,entries:y,raw:l}),i++,n=c}return i}(e,e=>{e.seq<=s||(this.applyRecord(e.entries),this.seq=e.seq)},e=>(i.push(e),!0))>0||i.length>0)&&(this.logBytes=e.byteLength),i.length>0&&await this.truncateLog()}this.opened=!0}async reload(){if(this.opened){try{await this.opQueue}catch{}this.index=new Map,this.seq=0,this.logBytes=0,this.opened=!1,await this.open(this.dbName)}}async close(){if(this.opened){try{await this.opQueue}catch{}await this.medium.close(),this.index.clear(),this.seq=0,this.logBytes=0,this.opened=!1}}async get(e){return this.index.get(e)??null}async getAll(){return Array.from(this.index.entries())}async listKeys(){return Array.from(this.index.keys())}async exists(e){return this.index.has(e)}size(){return this.index.size}getSeq(){return this.seq}async put(e,t){await this.enqueue(async()=>{await this.appendRecord({[e]:t},[])})}async putMany(e){0!==Object.keys(e).length&&await this.enqueue(async()=>{await this.appendRecord(e,[])})}async delete(e){await this.enqueue(async()=>{await this.appendRecord({},[e])})}async deleteMany(e){0!==e.length&&await this.enqueue(async()=>{await this.appendRecord({},e)})}async checkpoint(){await this.enqueue(async()=>{if(null!==this.lastBackgroundError){const e=this.lastBackgroundError;throw this.lastBackgroundError=null,new n("KVStore background write failed","KV_BACKGROUND_ERROR",e)}if(0===this.logBytes&&0===this.index.size)return;const e=k(this.seq,this.index);await this.medium.write(I,e.buffer);const t={seq:this.seq};await this.medium.write(A,(new TextEncoder).encode(JSON.stringify(t)).buffer),await this.truncateLog()})}async clear(){await this.enqueue(async()=>{await this.medium.clear(),this.index.clear(),this.seq=0,this.logBytes=0,await this.medium.write(A,(new TextEncoder).encode(JSON.stringify({seq:0})).buffer)})}async repair(){return this.enqueue(async()=>{let e=0;const t=await this.medium.read(I);t&&!x(new Uint8Array(t))&&(await this.medium.delete(I),e++);const s=await this.medium.read(S);if(s&&s.byteLength>0){const t=new Uint8Array(s),n=this.findValidLogLength(t);if(n{},()=>{}),t}async appendRecord(e,t){this.seq++;const s=function(e,t,s=[]){const n=new TextEncoder,i=[];for(const[e,s]of Object.entries(t))i.push({op:T.PUT,key:e,value:s});for(const e of s)i.push({op:T.DELETE,key:e,value:new ArrayBuffer(0)});const r=[];let a=12;for(const e of i){const t=n.encode(e.key),s=new Uint8Array(e.value);r.push({op:e.op,key:t,value:s}),a+=5+t.byteLength+4+s.byteLength}a+=4;const o=new Uint8Array(a),h=new DataView(o.buffer);let c=0;h.setUint32(c,a-4,!1),c+=4,h.setUint32(c,e,!1),c+=4,h.setUint32(c,r.length,!1),c+=4;for(const e of r)h.setUint8(c,e.op),c+=1,h.setUint32(c,e.key.byteLength,!1),c+=4,o.set(e.key,c),c+=e.key.byteLength,h.setUint32(c,e.value.byteLength,!1),c+=4,o.set(e.value,c),c+=e.value.byteLength;const l=b(o.subarray(0,a-4));return h.setUint32(a-4,l,!1),o}(this.seq,e,t);try{const e=s.buffer.slice(s.byteOffset,s.byteOffset+s.byteLength);if("function"==typeof this.medium.append)await this.medium.append(S,e);else{const t=await this.medium.read(S);if(t){const s=new Uint8Array(t.byteLength+e.byteLength);s.set(new Uint8Array(t),0),s.set(new Uint8Array(e),t.byteLength),await this.medium.write(S,s.buffer)}else await this.medium.write(S,e)}}catch(e){throw this.seq--,this.lastBackgroundError=e,new n("KVStore log append failed","KV_LOG_ERROR",e)}for(const[t,s]of Object.entries(e))this.index.set(t,s);for(const e of t)this.index.delete(e);if(this.logBytes+=s.byteLength,this.checkpointThreshold>0&&this.logBytes>=this.checkpointThreshold){await this.medium.write(I,k(this.seq,this.index).buffer);const e={seq:this.seq};await this.medium.write(A,(new TextEncoder).encode(JSON.stringify(e)).buffer),await this.truncateLog()}}async truncateLog(){try{await this.medium.write(S,new ArrayBuffer(0))}catch{}this.logBytes=0}applyRecord(e){for(const t of e)t.op===T.PUT?this.index.set(t.key,t.value):this.index.delete(t.key)}findValidLogLength(e){let t=0;const s=new DataView(e.buffer,e.byteOffset,e.byteLength);for(;t+4<=e.byteLength;){const n=s.getUint32(t,!1);if(n<12||t+4+n>e.byteLength)break;const i=e.subarray(t,t+4+n),r=s.getUint32(t+n,!1);if(b(i.subarray(0,n))!==r)break;t+=4+n}return t}}const C="__schema",N="t:",O=e=>(new TextEncoder).encode(e).buffer,L=e=>(new TextDecoder).decode(e);class v{constructor(e,t){this.name="kv",this.memory=new f,this.dbName="",this.version=1,this.opened=!1,this.txActive=!1,this.txDirtyTables=new Set,this.kv=new R(e,t)}rowKey(e,t){return`${N}${e}:${t}`}rowPrefix(e){return`${N}${e}:`}async open(e,t){if(this.opened)return;this.dbName=e,this.version=t,await this.kv.open(e),await this.memory.open(e,t);const s=await this.kv.get(C);if(s)try{const e=JSON.parse(L(s));for(const t of Object.values(e))await this.memory.createTable(t)}catch{throw new n("Corrupted schema in KVStore","KV_SCHEMA_ERROR")}const i=await this.kv.getAll();for(const[e,t]of i){if(!e.startsWith(N))continue;const s=e.indexOf(":",2);if(s<0)continue;const n=e.slice(2,s);if(await this.memory.hasTable(n))try{const e=JSON.parse(L(t));await this.memory.insert(n,[e])}catch{}}const r=await this.memory.getTableNames();for(const e of r){const t=await this.memory.getTableSchema(e);if(t)for(const[s,n]of Object.entries(t.columns))(n.index||n.unique)&&await this.memory.createIndex(e,s,n.unique)}this.opened=!0}async close(){if(this.opened){if(this.txActive)try{await this.rollbackTransaction()}catch{}await this.kv.close(),await this.memory.close(),this.opened=!1}}isOpen(){return this.opened}async reload(){this.opened&&(await this.kv.reload(),await this.memory.close(),await this.memory.open(this.dbName,this.version),this.opened=!1,await this.open(this.dbName,this.version))}async repair(){this.ensureOpen(),await this.kv.repair(),await this.memory.close(),await this.memory.open(this.dbName,this.version),this.opened=!1,await this.open(this.dbName,this.version)}async clearAll(){this.ensureOpen(),await this.kv.clear(),await this.memory.clearAll()}async getMeta(e){const t=await this.kv.get(`__meta:${e}`);return t?L(t):null}async setMeta(e,t){await this.kv.put(`__meta:${e}`,O(t))}async createTable(e){this.ensureOpen(),await this.memory.createTable(e),this.txActive?this.txDirtyTables.add(e.name):await this.persistSchema()}async dropTable(e){this.ensureOpen(),await this.memory.dropTable(e),this.txActive?this.txDirtyTables.add(e):(await this.persistSchema(),await this.flushTable(e))}async hasTable(e){return this.memory.hasTable(e)}async getTableNames(){return this.memory.getTableNames()}async getTableSchema(e){return this.memory.getTableSchema(e)}async alterTable(e,t,s){this.ensureOpen(),await this.memory.alterTable(e,t,s),this.txActive?this.txDirtyTables.add(e):(await this.persistSchema(),"DROP"===t&&await this.flushTable(e))}async insert(e,t){this.ensureOpen();const s=await this.memory.insert(e,t);if(this.txActive)return this.txDirtyTables.add(e),s;const i=await this.memory.getTableSchema(e);if(!i)throw new n(`Table "${e}" does not exist`,"TABLE_NOT_FOUND");const r=this.getPK(i),a={};return t.forEach((t,n)=>{a[this.rowKey(e,String(s[n]??t[r]))]=O(JSON.stringify(t))}),await this.kv.putMany(a),s}async find(e,t){return this.ensureOpen(),this.memory.find(e,t)}async findStream(e,t,s){return this.ensureOpen(),this.memory.findStream(e,t,s)}async update(e,t,s){this.ensureOpen();const i=await this.memory.getTableSchema(e);if(!i)throw new n(`Table "${e}" does not exist`,"TABLE_NOT_FOUND");const r=this.getPK(i),a=r in s,o=a?[]:await this.collectMatchingPks(e,t),h=await this.memory.update(e,t,s);if(this.txActive)return this.txDirtyTables.add(e),h;if(a)for(const t of await this.affectedTables(e))await this.flushTable(t);else{const t={},s=[];for(const n of o){const i=await this.memory.find(e,{table:e,where:{[r]:n}});i.length>0?t[this.rowKey(e,n)]=O(JSON.stringify(i[0])):s.push(this.rowKey(e,n))}Object.keys(t).length>0&&await this.kv.putMany(t),s.length>0&&await this.kv.deleteMany(s);for(const t of await this.affectedTables(e))t!==e&&await this.flushTable(t)}return h}async delete(e,t){this.ensureOpen();const s=await this.collectMatchingPks(e,t),n=await this.memory.delete(e,t);if(this.txActive)return this.txDirtyTables.add(e),n;const i=s.map(t=>this.rowKey(e,t));i.length>0&&await this.kv.deleteMany(i);for(const t of await this.affectedTables(e))t!==e&&await this.flushTable(t);return n}async count(e,t){return this.ensureOpen(),this.memory.count(e,t)}async clear(e){this.ensureOpen(),await this.memory.clear(e),this.txActive?this.txDirtyTables.add(e):await this.flushTable(e)}async createIndex(e,t,s){this.ensureOpen(),await this.memory.createIndex(e,t,s),this.txActive?this.txDirtyTables.add(e):await this.persistSchema()}async dropIndex(e,t,s){this.ensureOpen(),await this.memory.dropIndex(e,t,s),this.txActive?this.txDirtyTables.add(e):await this.persistSchema()}async beginTransaction(){this.ensureOpen(),await this.memory.beginTransaction(),this.txActive=!0,this.txDirtyTables=new Set}async commitTransaction(){if(this.ensureOpen(),!this.txActive)throw new n("No active transaction","TX_NONE");for(const e of this.txDirtyTables)if(await this.memory.hasTable(e))await this.flushTable(e);else{const t=await this.kv.getAll(),s=this.rowPrefix(e),n=t.filter(([e])=>e.startsWith(s)).map(([e])=>e);n.length>0&&await this.kv.deleteMany(n)}await this.persistSchema(),await this.kv.checkpoint(),await this.memory.commitTransaction(),this.txActive=!1,this.txDirtyTables=new Set}async rollbackTransaction(){if(this.ensureOpen(),!this.txActive)throw new n("No active transaction","TX_NONE");await this.memory.rollbackTransaction(),this.txActive=!1,this.txDirtyTables=new Set}ensureOpen(){if(!this.opened)throw new n("Database not opened","DB_NOT_OPEN")}getPK(e){for(const[t,s]of Object.entries(e.columns))if(s.primaryKey)return t;return Object.keys(e.columns)[0]}async collectMatchingPks(e,t){const s=await this.memory.getTableSchema(e);if(!s)throw new n(`Table "${e}" does not exist`,"TABLE_NOT_FOUND");const i=this.getPK(s);return(await this.memory.find(e,t)).map(e=>String(e[i]))}async affectedTables(e){const t=new Set([e]);let s=!0;for(;s;){s=!1;for(const e of await this.memory.getTableNames()){if(t.has(e))continue;const n=await this.memory.getTableSchema(e);if(n)for(const i of Object.values(n.columns))if(i.references){const n=i.references.split(".")[0];if(t.has(n)){t.add(e),s=!0;break}}}}return t}async persistSchema(){const e={};for(const t of await this.memory.getTableNames()){const s=await this.memory.getTableSchema(t);s&&(e[t]=s)}await this.kv.put(C,O(JSON.stringify(e)))}async flushTable(e){const t=this.rowPrefix(e),s=await this.memory.getTableSchema(e);if(!s){const e=(await this.kv.getAll()).filter(([e])=>e.startsWith(t)).map(([e])=>e);return void(e.length>0&&await this.kv.deleteMany(e))}const n=this.getPK(s),i=await this.memory.find(e,{table:e}),r={},a=new Set;for(const t of i){const s=this.rowKey(e,String(t[n]));a.add(s),r[s]=O(JSON.stringify(t))}const o=await this.kv.getAll(),h=[];for(const[e]of o)e.startsWith(t)&&!a.has(e)&&h.push(e);Object.keys(r).length>0&&await this.kv.putMany(r),h.length>0&&await this.kv.deleteMany(h)}}function $(e,s){return function(e){if(0===Object.keys(e).length)throw new n("Table must have at least one column","SCHEMA_ERROR");let s=0;for(const[i,r]of Object.entries(e)){if(!t.includes(r.type))throw new n(`Invalid type "${r.type}" for column "${i}". Valid types: ${t.join(", ")}`,"SCHEMA_ERROR");r.primaryKey&&s++}if(0===s)throw new n("Table must have at least one primary key column","SCHEMA_ERROR")}(s),{name:e,columns:s}}function D(e){return{type:e.type,primaryKey:e.primaryKey,unique:e.unique,required:e.required,default:e.default,index:e.index,maxLength:e.maxLength,min:e.min,max:e.max,references:e.references,onDelete:e.onDelete,onUpdate:e.onUpdate}}const _=4096;var U;!function(e){e[e.DATA=1]="DATA",e[e.INDEX=2]="INDEX",e[e.OVERFLOW=3]="OVERFLOW",e[e.META=4]="META"}(U||(U={}));const M=4194304;var B,P;!function(e){e[e.INSERT=1]="INSERT",e[e.UPDATE=2]="UPDATE",e[e.DELETE=3]="DELETE",e[e.BEGIN=4]="BEGIN",e[e.COMMIT=5]="COMMIT",e[e.ROLLBACK=6]="ROLLBACK",e[e.CREATE_TABLE=7]="CREATE_TABLE",e[e.DROP_TABLE=8]="DROP_TABLE"}(B||(B={})),function(e){e[e.ACTIVE=1]="ACTIVE",e[e.COMMITTED=2]="COMMITTED",e[e.ABORTED=3]="ABORTED"}(P||(P={}));const K={pageSize:_,bufferPoolPages:256,memtableSizeThreshold:M,levelSizeMultiplier:10,bloomFilterBitsPerKey:10,walEnabled:!0,walSyncMode:"full",checkpointInterval:1e3,compression:!1,storageBackend:"opfs",walSizeThreshold:16777216,maxMemoryMB:64,encryption:void 0,pageStorage:void 0};var z;!function(e){e[e.RED=0]="RED",e[e.BLACK=1]="BLACK"}(z||(z={}));class F{constructor(e,t){this.color=z.RED,this.left=null,this.right=null,this.parent=null,this.key=e,this.value=t}}class j{constructor(){this.root=null,this._size=0}get size(){return this._size}insert(e,t){const s=new F(e,t);if(!this.root)return this.root=s,s.color=z.BLACK,void this._size++;let n=null,i=this.root;for(;i;)if(n=i,ei.key))return void(i.value=t);i=i.right}s.parent=n,et.key))return t.value;t=t.right}return null}delete(e){const t=this.findNode(e);return!!t&&(this.deleteNode(t),this._size--,!0)}inorder(e){this._inorder(this.root,e)}rangeScan(e,t,s){this._rangeScan(this.root,e,t,s)}getAllEntries(){const e=[];return this.inorder((t,s)=>e.push([t,s])),e}clear(){this.root=null,this._size=0}findNode(e){let t=this.root;for(;t;)if(et.key))return t;t=t.right}return null}deleteNode(e){if(e.left||e.right)if(e.left)if(e.right){const t=this.minimum(e.right);t.parent!==e&&(this.transplant(t,t.right),t.right=e.right,t.right.parent=t),this.transplant(e,t),t.left=e.left,t.left.parent=t;const s=t.color;t.color=e.color,s===z.BLACK&&this.fixDelete(t.right,t.right?.parent??null)}else this.transplant(e,e.left),e.color===z.BLACK&&this.fixDelete(e.left,e.left.parent);else this.transplant(e,e.right),e.color===z.BLACK&&this.fixDelete(e.right,e.right.parent);else this.transplant(e,null),e.color===z.BLACK&&this.fixDelete(null,e.parent)}transplant(e,t){e.parent?e===e.parent.left?e.parent.left=t:e.parent.right=t:this.root=t,t&&(t.parent=e.parent)}minimum(e){for(;e.left;)e=e.left;return e}fixInsert(e){for(;e.parent&&e.parent.color===z.RED;){const t=e.parent,s=t.parent;if(!s)break;if(t===s.left){const n=s.right;n&&n.color===z.RED?(t.color=z.BLACK,n.color=z.BLACK,s.color=z.RED,e=s):(e===t.right&&(e=t,this.rotateLeft(e)),e.parent&&(e.parent.color=z.BLACK),e.parent?.parent&&(e.parent.parent.color=z.RED),e.parent?.parent&&this.rotateRight(e.parent.parent))}else{const n=s.left;n&&n.color===z.RED?(t.color=z.BLACK,n.color=z.BLACK,s.color=z.RED,e=s):(e===t.left&&(e=t,this.rotateRight(e)),e.parent&&(e.parent.color=z.BLACK),e.parent?.parent&&(e.parent.parent.color=z.RED),e.parent?.parent&&this.rotateLeft(e.parent.parent))}}this.root&&(this.root.color=z.BLACK)}fixDelete(e,t){let s=e,n=t;for(;(!s||s.color===z.BLACK)&&s!==this.root&&n;)if(s===n.left){let e=n.right;if(!e)break;if(e.color===z.RED&&(e.color=z.BLACK,n.color=z.RED,this.rotateLeft(n),e=n.right,!e))break;const t=e.left,i=e.right;if(t&&t.color!==z.BLACK||i&&i.color!==z.BLACK){if(!(i&&i.color!==z.BLACK||(t&&(t.color=z.BLACK),e.color=z.RED,this.rotateRight(e),e=n.right,e)))break;e.color=n.color,n.color=z.BLACK,e.right&&(e.right.color=z.BLACK),this.rotateLeft(n),s=this.root}else e.color=z.RED,s=n,n=s.parent}else{let e=n.left;if(!e)break;if(e.color===z.RED&&(e.color=z.BLACK,n.color=z.RED,this.rotateRight(n),e=n.left,!e))break;const t=e.left,i=e.right;if(t&&t.color!==z.BLACK||i&&i.color!==z.BLACK){if(!(t&&t.color!==z.BLACK||(i&&(i.color=z.BLACK),e.color=z.RED,this.rotateLeft(e),e=n.left,e)))break;e.color=n.color,n.color=z.BLACK,e.left&&(e.left.color=z.BLACK),this.rotateRight(n),s=this.root}else e.color=z.RED,s=n,n=s.parent}s&&(s.color=z.BLACK)}rotateLeft(e){const t=e.right;t&&(e.right=t.left,t.left&&(t.left.parent=e),t.parent=e.parent,e.parent?e===e.parent.left?e.parent.left=t:e.parent.right=t:this.root=t,t.left=e,e.parent=t)}rotateRight(e){const t=e.left;t&&(e.left=t.right,t.right&&(t.right.parent=e),t.parent=e.parent,e.parent?e===e.parent.right?e.parent.right=t:e.parent.left=t:this.root=t,t.right=e,e.parent=t)}_inorder(e,t){e&&(this._inorder(e.left,t),t(e.key,e.value),this._inorder(e.right,t))}_rangeScan(e,t,s,n){e&&(e.key>t&&this._rangeScan(e.left,t,s,n),e.key>=t&&e.key<=s&&n(e.key,e.value),e.key=this.maxSize}getAllEntries(){return this.tree.getAllEntries()}rangeScan(e,t){const s=[];return this.tree.rangeScan(e,t,(e,t)=>s.push([e,t])),s}getEntryCount(){return this.tree.size}getEstimatedSize(){return this._estimatedSize}clear(){this.tree.clear(),this._estimatedSize=0}estimateEntrySize(e,t){if(!t)return 0;let s=2*e.length;for(const e of Object.entries(t)){s+=2*e[0].length;const t=e[1];s+="string"==typeof t?2*t.length:"number"==typeof t?8:"boolean"==typeof t||null==t?1:16}return s}}class V{constructor(e,t=10){this._inserted=0;const s=Math.max(64,e*t),n=Math.ceil(s/8);this.bits=new Uint8Array(n),this.numHashes=Math.max(1,Math.floor(.69*t))}static fromData(e,t){const s=new V(1);return s.bits=e,s.numHashes=t,s}insert(e){const t=this.getHashes(e);for(const e of t){const t=Math.floor(e/8),s=e%8;this.bits[t]|=1<>>0;return t}murmurSimple(e){let t=0;for(let s=0;s>>16)>>>0;return Math.abs(t)}}const W=1397969987;class H{constructor(e=4096){this.entries=[],this.blockSizeLimit=e}add(e,t){this.entries.push([e,t])}build(){const e=new TextEncoder,t=this.entries.map(([t,s])=>({key:t,keyBytes:e.encode(t),valueBytes:e.encode(JSON.stringify(s))})),s=this.splitIntoBlocks(t),n=new V(this.entries.length);let i=0;const r=[];for(const e of s)r.push(i),i+=this.computeBlockSize(e);const a=[];for(let e=0;e=this.blockSizeLimit&&s.length>1&&(t.push(s.slice(0,-1)),s=[n]);return s.length>0&&t.push(s),t}computeBlockSize(e){let t=4;for(const s of e)t+=4+s.keyBytes.length+4+s.valueBytes.length;return t}writeDataBlock(e,t,s,n){e.setUint32(t,s.length,!1),t+=4;for(const i of s){if(i.keyBytes.length>4294967295||i.valueBytes.length>4294967295)throw new Error("SSTable entry too large (exceeds u32 length field)");e.setUint32(t,i.keyBytes.length,!1),t+=4,new Uint8Array(e.buffer).set(i.keyBytes,t),t+=i.keyBytes.length,e.setUint32(t,i.valueBytes.length,!1),t+=4,new Uint8Array(e.buffer).set(i.valueBytes,t),t+=i.valueBytes.length,n.insert(i.key)}return t}estimateIndexBlockSize(e){let t=4;const s=new TextEncoder;for(const n of e)t+=4+s.encode(n.key).byteLength+8;return t}writeIndexBlock(e,t,s){e.setUint32(t,s.length,!1),t+=4;for(const n of s){const s=(new TextEncoder).encode(n.key);e.setUint32(t,s.length,!1),t+=4,new Uint8Array(e.buffer).set(s,t),t+=s.length,e.setUint32(t,n.blockOffset,!1),t+=4,e.setUint32(t,n.blockSize,!1),t+=4}return t}}class G{constructor(e,t){this.indexEntries=[],this.entryCount=0,this.bloomFilter=null,this.format=2,this.storedChecksum=0,this.data=e,this.view=new DataView(e.buffer,e.byteOffset,e.byteLength),this.meta=t,this.parseFooter()}verifyChecksum(){return 0===this.storedChecksum||!(this.data.byteLength<4)&&b(this.data.subarray(0,this.data.byteLength-4))===this.storedChecksum}lenFieldSize(){return 2===this.format?4:2}readLen(e){return 2===this.format?this.view.getUint32(e,!1):this.view.getUint16(e,!1)}get(e){if(this.bloomFilter&&!this.bloomFilter.mayContain(e))return null;const t=this.locateBlock(e);if(t<0)return null;const s=this.indexEntries[t],n=this.getBlockData(s);if(!n)return null;const i=new DataView(n.buffer,n.byteOffset,n.byteLength),r=this.lenFieldSize(),a=i.getUint32(0,!1);let o=4;for(let t=0;tn.byteLength);t++){const t=2===this.format?i.getUint32(o,!1):i.getUint16(o,!1);if(o+=r,o+t+r>n.byteLength)break;const s=(new TextDecoder).decode(n.slice(o,o+t));o+=t;const a=2===this.format?i.getUint32(o,!1):i.getUint16(o,!1);if(o+=r,o+a>n.byteLength)break;const h=n.slice(o,o+a);if(o+=a,s===e)try{return JSON.parse((new TextDecoder).decode(h))}catch{return null}}return null}rangeScan(e,t,s){if(0===this.indexEntries.length)return;const n=Math.max(0,this.locateBlockGE(e)),i=Math.min(this.indexEntries.length-1,this.locateBlockLE(t));if(n<0||i<0||n>i)return;const r=this.lenFieldSize();for(let a=n;a<=i&&a>=0;a++){const n=this.indexEntries[a],i=this.getBlockData(n);if(!i)continue;const o=new DataView(i.buffer,i.byteOffset,i.byteLength),h=o.getUint32(0,!1);let c=4;for(let n=0;ni.byteLength);n++){const n=2===this.format?o.getUint32(c,!1):o.getUint16(c,!1);if(c+=r,c+n+r>i.byteLength)break;const a=(new TextDecoder).decode(i.slice(c,c+n));c+=n;const h=2===this.format?o.getUint32(c,!1):o.getUint16(c,!1);if(c+=r,c+h>i.byteLength)break;const l=i.slice(c,c+h);if(c+=h,a>=e&&a<=t)try{s(a,JSON.parse((new TextDecoder).decode(l)))}catch{}}}}scanAll(e){const t=this.lenFieldSize();for(const s of this.indexEntries){const n=this.getBlockData(s);if(!n)continue;const i=new DataView(n.buffer,n.byteOffset,n.byteLength),r=i.getUint32(0,!1);let a=4;for(let s=0;sn.byteLength);s++){const s=2===this.format?i.getUint32(a,!1):i.getUint16(a,!1);if(a+=t,a+s+t>n.byteLength)break;const r=(new TextDecoder).decode(n.slice(a,a+s));a+=s;const o=2===this.format?i.getUint32(a,!1):i.getUint16(a,!1);if(a+=t,a+o>n.byteLength)break;const h=n.slice(a,a+o);a+=o;try{e(r,JSON.parse((new TextDecoder).decode(h)))}catch{}}}}parseFooter(){if(this.data.byteLength<32)throw new Error("SSTable too small: missing footer");const e=this.data.byteLength-32,t=this.view.getUint32(e+24,!1);if(1397969986===t)this.format=1;else{if(t!==W)throw new Error(`Invalid SSTable magic: expected 1397969986 or 1397969987, got ${t}`);this.format=2}const s=this.view.getUint32(e,!1),n=this.view.getUint32(e+4,!1),i=this.view.getUint32(e+8,!1),r=this.view.getUint32(e+12,!1),a=this.view.getUint32(e+16,!1);if(this.entryCount=this.view.getUint32(e+20,!1),this.storedChecksum=this.view.getUint32(e+28,!1),!(s+4>this.data.byteLength||s+n>this.data.byteLength)&&(this.parseIndexBlock(s,n),i>0&&r>0&&i+r<=this.data.byteLength))try{const e=this.data.slice(i,i+r);this.bloomFilter=V.fromData(e,a||10)}catch{}}parseIndexBlock(e,t){const s=this.view.getUint32(e,!1);e+=4;const n=this.lenFieldSize();for(let t=0;tthis.data.byteLength);t++){const t=this.readLen(e);if((e+=n)+t+8>this.data.byteLength)break;const s=(new TextDecoder).decode(this.data.slice(e,e+t));e+=t;const i=this.view.getUint32(e,!1);e+=4;const r=this.view.getUint32(e,!1);e+=4,0===r||i+r>this.data.byteLength||this.indexEntries.push({key:s,blockOffset:i,blockSize:r})}}getBlockData(e){return e.blockSize<=0||e.blockOffset<0||e.blockOffset+e.blockSize>this.data.byteLength?null:new Uint8Array(this.data.buffer,this.data.byteOffset+e.blockOffset,e.blockSize)}locateBlock(e){let t=0,s=this.indexEntries.length-1;for(;t<=s;){const n=Math.floor((t+s)/2);if(e<=this.indexEntries[n].key){if(e>(0===n?"":this.indexEntries[n-1].key))return n;s=n-1}else t=n+1}return-1}locateBlockGE(e){let t=0,s=this.indexEntries.length;for(;t>1;this.indexEntries[n].key>1;this.indexEntries[n].key<=e?t=n+1:s=n}return t>0?t-1:0}}class X{constructor(e){this.index=0,this.entries=e}next(){return this.index>=this.entries.length?null:this.entries[this.index++]}reset(){this.index=0}}class J{constructor(){this.heap=[]}push(e){this.heap.push(e),this.bubbleUp(this.heap.length-1)}pop(){if(0===this.heap.length)return null;if(1===this.heap.length)return this.heap.pop();const e=this.heap[0];return this.heap[0]=this.heap.pop(),this.bubbleDown(0),e}peek(){return this.heap.length>0?this.heap[0]:null}get size(){return this.heap.length}bubbleUp(e){for(;e>0;){const t=Math.floor((e-1)/2);if(this.heap[e].key>=this.heap[t].key)break;[this.heap[e],this.heap[t]]=[this.heap[t],this.heap[e]],e=t}}bubbleDown(e){const t=this.heap.length;for(;;){let s=e;const n=2*e+1,i=2*e+2;if(n=0&&e.level<7&&this.levels[e.level].push(e);for(let e=0;e<7;e++)this.levels[e].sort((e,t)=>t.id-e.id);this.initialized=!0}put(e,t){this.levels[0].length>=8&&this.enqueueCompact(0),this.memtable.put(e,t),this.operationCount++,this.memtable.shouldFlush()&&this.freezeMemtable()}delete(e){this.levels[0].length>=8&&this.enqueueCompact(0),this.memtable.put(e,{__tombstone:!0}),this.operationCount++,this.memtable.shouldFlush()&&this.freezeMemtable()}getEstimatedMemory(){let e=this.memtable.getEstimatedSize();for(const t of this.frozenMemtables)e+=t.getEstimatedSize();return e+=this.cacheSize,e}freezeMemtable(){if(this.immutableMemtable){const e=this.immutableMemtable;this.flushChain=this.enqueueOnChain(()=>this.flushImmutableAsync(e))}this.immutableMemtable=this.memtable,this.frozenMemtables.push(this.immutableMemtable),this.memtable=new q(this.memtableSizeThreshold)}enqueueOnChain(e){return this.flushChain.then(e).catch(e=>{this.lastBackgroundError=e})}async drainChain(){for(;;){const e=this.flushChain;if(await e,this.flushChain===e)return}}async flushImmutableAsync(e){const t=e.getAllEntries();if(0===t.length)return void(this.immutableMemtable===e&&(this.immutableMemtable=null));const s=await this.sstableStore.allocateId(),n=new H(this.blockSize);for(const[e,s]of t)n.add(e,s);const{sstableData:i,indexEntries:r}=n.build(),a={id:s,level:0,minKey:t[0][0],maxKey:t[t.length-1][0],blockCount:r.length,totalSize:i.byteLength,bloomData:null};this.cacheSSTable(s,i),this.trimCache(),await this.sstableStore.save(s,i),await this.sstableStore.saveMeta(a),this.immutableMemtable===e&&(this.immutableMemtable=null),this.levels[0].unshift(a),this.frozenMemtables=this.frozenMemtables.filter(t=>t!==e),this.levels[0].length>=4&&!this.compacting&&this.scheduleCompact(0)}scheduleCompact(e){e>=6||this.compacting||(this.compacting=!0,this.flushChain=this.enqueueOnChain(()=>this.compactLevelAsync(e).finally(()=>{this.compacting=!1,this.levels[e].length>=4&&this.scheduleCompact(e),e+1<6&&this.levels[e+1].length>=4&&this.scheduleCompact(e+1)})))}enqueueCompact(e){e>=6||this.compacting||(this.compacting=!0,this.flushChain=this.flushChain.then(async()=>{try{await this.compactLevelAsync(e)}finally{this.compacting=!1}}).catch(e=>{this.compacting=!1,this.lastBackgroundError=e}))}async prefetchRange(e,t){await this.drainChain(),this.trimCache();const s=[];for(let n=0;n<7;n++)for(const i of this.levels[n])ti.maxKey||this.sstableCache.has(i.id)||s.push(i.id);for(const e of s){const t=this.findMetaById(e);await this.preloadSSTable(e,t)}}async prefetchKeys(e){if(0===e.length)return;await this.drainChain(),this.trimCache();const t=new Set;for(let s=0;s<7;s++)for(const n of this.levels[s])if(!this.sstableCache.has(n.id))for(const s of e)if(s>=n.minKey&&s<=n.maxKey){t.add(n.id);break}for(const e of t){const t=this.findMetaById(e);await this.preloadSSTable(e,t)}}findMetaById(e){for(let t=0;t<7;t++)for(const s of this.levels[t])if(s.id===e)return s}get(e){let t=this.memtable.get(e);if(null!==t)return this.unwrapTombstone(t);for(let s=this.frozenMemtables.length-1;s>=0;s--)if(t=this.frozenMemtables[s].get(e),null!==t)return this.unwrapTombstone(t);for(let t=0;t<7;t++)for(const s of this.levels[t]){if(es.maxKey)continue;const t=this.loadSSTableReader(s);if(!t)continue;const n=t.get(e);if(null!==n)return this.unwrapTombstone(n)}return null}rangeScan(e,t){const s=[];return this.rangeScanLazy(e,t,(e,t)=>s.push([e,t])),s}rangeScanLazy(e,t,s){const n=new Y;n.addSource(new X(this.memtable.rangeScan(e,t)));for(let s=this.frozenMemtables.length-1;s>=0;s--)n.addSource(new X(this.frozenMemtables[s].rangeScan(e,t)));for(let s=0;s<7;s++)for(const i of this.levels[s]){if(ti.maxKey)continue;const s=this.loadSSTableReader(i);s&&s.rangeScan(e,t,(e,t)=>{n.addSource(new X([[e,t]]))})}const i=n.drain();for(const[e,t]of i)t.__tombstone||s(e,t)}getAllEntries(){const e=new Map;for(let t=6;t>=0;t--)for(let s=this.levels[t].length-1;s>=0;s--){const n=this.levels[t][s],i=this.loadSSTableReader(n);i&&i.scanAll((t,s)=>e.set(t,s))}for(const[t,s]of this.memtable.getAllEntries())e.set(t,s);for(let t=this.frozenMemtables.length-1;t>=0;t--)for(const[s,n]of this.frozenMemtables[t].getAllEntries())e.set(s,n);return Array.from(e.entries()).filter(([,e])=>!e.__tombstone)}async compactLevel(e){await this.compactLevelAsync(e,2)}async compactLevelAsync(e,t=4){if(e>=6)return;if(this.levels[e].lengthr.push([e,t])),n.addSource(new X(r)),i.push(e)}const r=n.drain();if(0===r.length){for(const t of i)this.levels[e].push(t);return void this.levels[e].sort((e,t)=>t.id-e.id)}const a=await this.sstableStore.allocateId(),o=new H(this.blockSize);for(const[e,t]of r)o.add(e,t);const{sstableData:h,indexEntries:c}=o.build(),l={id:a,level:e+1,minKey:r[0][0],maxKey:r[r.length-1][0],blockCount:c.length,totalSize:h.byteLength,bloomData:null};this.cacheSSTable(a,h),this.trimCache(),await this.sstableStore.save(a,h),await this.sstableStore.saveMeta(l),this.levels[e+1].unshift(l);for(const e of s)this.sstableCache.delete(e.id),await this.sstableStore.delete(e.id),await this.sstableStore.deleteMeta(e.id)}async flush(){if(null!==this.lastBackgroundError){const e=this.lastBackgroundError;throw this.lastBackgroundError=null,new n("AriaEngine background flush/compaction failed (data may be inconsistent)","ARIA_BACKGROUND_ERROR",e)}if(await this.drainChain(),this.immutableMemtable){const e=this.immutableMemtable;await this.flushImmutableAsync(e)}if(this.memtable.getEntryCount()>0){this.freezeMemtable();const e=this.immutableMemtable;e&&await this.flushImmutableAsync(e)}this.frozenMemtables=[],await this.drainChain()}async clear(){await this.drainChain(),this.memtable.clear(),this.immutableMemtable=null,this.frozenMemtables=[];for(const e of this.levels)for(const t of e)this.sstableCache.delete(t.id),this.sstableStore.delete(t.id).catch(()=>{}),this.sstableStore.deleteMeta(t.id).catch(()=>{});this.levels=[];for(let e=0;e<7;e++)this.levels.push([]);this.sstableCache.clear(),this.cacheSize=0}getStats(){return{memtableSize:this.memtable.getEntryCount(),sstableCount:this.levels.reduce((e,t)=>e+t.length,0),levelCounts:this.levels.map(e=>e.length)}}async validateAll(){let e=0;for(let t=0;t<7;t++){const s=[];for(const n of this.levels[t])await this.validateSSTable(n)?s.push(n):(this.sstableCache.delete(n.id),e++);this.levels[t]=s}return e}async validateSSTable(e){try{const t=await this.sstableStore.load(e.id);if(!t)return this.dropInvalidSSTable(e),!1;if(t.byteLength<32)return this.dropInvalidSSTable(e),!1;try{if(!new G(t,e).verifyChecksum())return this.dropInvalidSSTable(e),!1}catch{return this.dropInvalidSSTable(e),!1}return!0}catch{return this.dropInvalidSSTable(e),!1}}async dropInvalidSSTable(e){try{await this.sstableStore.delete(e.id)}catch{}try{await this.sstableStore.deleteMeta(e.id)}catch{}}unwrapTombstone(e){return e?e.__tombstone?null:e:null}loadSSTableReader(e){const t=this.sstableCache.get(e.id);if(!t)return null;this.sstableCache.delete(e.id),this.sstableCache.set(e.id,t);try{return new G(t,e)}catch{return null}}async preloadSSTable(e,t){if(this.sstableCache.has(e))return;const s=await this.sstableStore.load(e);if(s){if(t)try{if(!new G(s,t).verifyChecksum())return void await this.dropInvalidSSTable(t)}catch{return void await this.dropInvalidSSTable(t)}this.cacheSSTable(e,s)}}cacheSSTable(e,t){this.sstableCache.has(e)&&(this.cacheSize-=this.sstableCache.get(e).byteLength,this.sstableCache.delete(e)),this.sstableCache.set(e,t),this.cacheSize+=t.byteLength}trimCache(){for(;this.cacheSize>this.cacheLimitBytes&&this.sstableCache.size>0;){const e=this.sstableCache.keys().next().value,t=this.sstableCache.get(e);this.cacheSize-=t.byteLength,this.sstableCache.delete(e)}}}class Z{constructor(e,t=!0,s="batch"){this.lsn=0,this.buffer=[],this.bufferedBytes=0,this.store=e,this.enabled=t,this.syncMode=s}async append(e){if(!this.enabled)return;this.lsn++;const t={...e,lsn:this.lsn,checksum:0},s=this.encodeRecord(t);if("full"===this.syncMode)try{await this.store.append(s),this.bufferedBytes+=s.byteLength}catch{}else"batch"===this.syncMode&&(this.buffer.push(s),this.bufferedBytes+=s.byteLength)}async appendBatch(e){if(!this.enabled||0===e.length)return;const t=[];for(const s of e)this.lsn++,t.push(this.encodeRecord({...s,lsn:this.lsn,checksum:0}));const s=this.mergeChunks(t);if("full"===this.syncMode)try{await this.store.append(s),this.bufferedBytes+=s.byteLength}catch{}else"batch"===this.syncMode&&(this.buffer.push(s),this.bufferedBytes+=s.byteLength)}async flush(){if(!this.enabled||0===this.buffer.length)return;const e=this.mergeChunks(this.buffer);await this.store.append(e),this.buffer=[]}mergeChunks(e){if(1===e.length)return e[0];const t=e.reduce((e,t)=>e+t.byteLength,0),s=new Uint8Array(t);let n=0;for(const t of e)s.set(t,n),n+=t.byteLength;return s}async recover(e){if(!this.enabled)return 0;if(!await this.store.exists())return 0;const t=await this.store.readAll();if(0===t.byteLength)return 0;const s=this.decodeAllRecords(t);for(const t of s)e(t);return this.lsn=s.length>0?s[s.length-1].lsn:0,s.length}async checkpoint(){this.enabled&&(await this.flush(),await this.store.truncate(),this.lsn=0,this.bufferedBytes=0)}getBufferedCount(){return this.buffer.length}getBufferedBytes(){return this.bufferedBytes}legacyChecksum(e){let t=0;for(let s=0;s>>0}encodeRecord(e){const t=new TextEncoder,s=t.encode(e.tableName),n=t.encode(e.key),i=e.data?JSON.stringify(e.data):"",r=t.encode(i),a=11+s.length+2+n.length+4+r.length+4,o=new ArrayBuffer(a),h=new DataView(o);let c=0;h.setUint32(c,e.lsn,!1),c+=4,h.setUint8(c,e.type),c+=1,h.setUint32(c,e.txnId,!1),c+=4,h.setUint16(c,s.length,!1),c+=2,new Uint8Array(o).set(s,c),c+=s.length,h.setUint16(c,n.length,!1),c+=2,new Uint8Array(o).set(n,c),c+=n.length,h.setUint32(c,r.length,!1),c+=4,new Uint8Array(o).set(r,c),c+=r.length;const l=b(new Uint8Array(o,0,c));return h.setUint32(c,l,!1),new Uint8Array(o)}decodeAllRecords(e){const t=[],s=new DataView(e.buffer,e.byteOffset,e.byteLength);let n=0;for(;n+15<=e.byteLength;)try{const i=n,r=s.getUint32(n,!1);n+=4;const a=s.getUint8(n);n+=1;const o=s.getUint32(n,!1);n+=4;const h=s.getUint16(n,!1);if(n+=2,n+h>e.byteLength)break;const c=(new TextDecoder).decode(e.slice(n,n+h));n+=h;const l=s.getUint16(n,!1);if(n+=2,n+l>e.byteLength)break;const u=(new TextDecoder).decode(e.slice(n,n+l));n+=l;const f=s.getUint32(n,!1);if(n+=4,n+f>e.byteLength)break;let d;if(f>0){const t=(new TextDecoder).decode(e.slice(n,n+f));try{d=JSON.parse(t)}catch{}}n+=f;const p=s.getUint32(n,!1);n+=4;const y=e.slice(i,n-4),m=b(y),g=this.legacyChecksum(y);if(m>>>0!==p&&g>>>0!==p)continue;t.push({lsn:r,type:a,txnId:o,tableName:c,key:u,data:d,checksum:p})}catch{break}return t}}const ee="__wal_",te=/^__wal_(\d{6})\.bin$/,se=/^__wal_(\d+)$/,ne="__wal_count";class ie{constructor(e,t=4194304){this.backend=e,this.currentSegment=0,this.currentSize=0,this.segmentSize=t}segmentKey(e){return`${ee}${String(e).padStart(6,"0")}.bin`}async append(e){if(0===e.byteLength)return;this.currentSize+e.byteLength>this.segmentSize&&(this.currentSegment++,this.currentSize=0);const t=this.segmentKey(this.currentSegment),s=e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength);if("function"==typeof this.backend.append)await this.backend.append(t,s);else{const e=await this.backend.read(t);if(e){const n=new ArrayBuffer(e.byteLength+s.byteLength);new Uint8Array(n).set(new Uint8Array(e),0),new Uint8Array(n).set(new Uint8Array(s),e.byteLength),await this.backend.write(t,n)}else await this.backend.write(t,s)}this.currentSize+=e.byteLength}async readAll(){const e=await this.backend.listKeys(),t=e.filter(e=>te.test(e)).map(e=>({seq:Number(e.match(te)[1]),key:e})).sort((e,t)=>e.seq-t.seq);let s=0;for(let e=0;ese.test(e)).map(e=>({seq:Number(e.match(se)[1]),key:e})).sort((e,t)=>e.seq-t.seq),r=[];for(const{key:e}of i){const t=await this.backend.read(e);t&&r.push(new Uint8Array(t))}for(const{key:e}of n){const t=await this.backend.read(e);t&&r.push(new Uint8Array(t))}if(n.length>0){this.currentSegment=n[n.length-1].seq;const e=await this.backend.read(n[n.length-1].key);this.currentSize=e?e.byteLength:0,i.length>0&&(this.currentSegment++,this.currentSize=0)}else i.length>0&&(this.currentSegment=0,this.currentSize=0);const a=r.reduce((e,t)=>e+t.byteLength,0),o=new Uint8Array(a);let h=0;for(const e of r)o.set(e,h),h+=e.byteLength;return o}async truncate(){const e=(await this.backend.listKeys()).filter(e=>e.startsWith(ee)||e===ne);e.length>0&&await this.backend.deleteMany(e),this.currentSegment=0,this.currentSize=0}async exists(){return(await this.backend.listKeys()).some(e=>e.startsWith(ee)||e===ne)}}class re{constructor(){this.acquired=!1,this.supported=!1,this.releaseResolve=null,this.releasePromise=null}async acquire(e){const t=globalThis.navigator,s=t?.locks;return s&&"function"==typeof s.request?(this.supported=!0,await new Promise((t,i)=>{s.request.bind(s)(function(e){return`metona-sqlark:${e}`}(e),{ifAvailable:!0,mode:"exclusive"},async s=>{if(!s)return void i(new n(`Database "${e}" is already open in another tab (locked)`,"ARIA_LOCKED"));this.acquired=!0;const r=new Promise(e=>{this.releaseResolve=e});this.releasePromise=r,t(),await r})}),!0):(this.supported=!1,!1)}async release(){if(this.acquired){if(this.releaseResolve){const e=this.releaseResolve,t=this.releasePromise;this.releaseResolve=null,this.releasePromise=null,e();try{await t}catch{}}this.acquired=!1}}isAcquired(){return this.acquired}isSupported(){return this.supported}}class ae{constructor(e,t,s=null,n=1e3,i=16777216){this.opCount=0,this.lsm=e,this.wal=t,this.flushable=s,this.interval=n,this.walSizeThreshold=i}async tick(){this.opCount++,(this.opCount>=this.interval||this.getWALEstimatedSize()>=this.walSizeThreshold)&&await this.checkpoint()}getWALEstimatedSize(){const e=this.wal;if("function"==typeof e.getBufferedBytes){const t=e.getBufferedBytes();if(t>0)return t}return 200*("function"==typeof e.getBufferedCount?e.getBufferedCount():0)}async checkpoint(){await this.lsm.flush(),this.flushable&&await this.flushable.flushAll(),await this.wal.checkpoint(),this.opCount=0}}class oe{constructor(){this.store=new Map,this.opened=!1}async open(e){this.opened=!0}async close(){this.store.clear(),this.opened=!1}isOpen(){return this.opened}async read(e){return this.store.get(e)??null}async write(e,t){this.store.set(e,t)}async writeMany(e){for(const[t,s]of Object.entries(e))this.store.set(t,s)}async delete(e){this.store.delete(e)}async deleteMany(e){for(const t of e)this.store.delete(t)}async listKeys(){return Array.from(this.store.keys())}async exists(e){return this.store.has(e)}async clear(){this.store.clear()}}const he="AES-GCM";class ce{constructor(){this.cryptoKey=null,this._enabled=!1}get enabled(){return this._enabled}async init(e,t){const s=new TextEncoder,n=await crypto.subtle.importKey("raw",s.encode(e),"PBKDF2",!1,["deriveKey"]),i=t||crypto.getRandomValues(new Uint8Array(16));return this.cryptoKey=await crypto.subtle.deriveKey({name:"PBKDF2",salt:i,iterations:1e5,hash:"SHA-256"},n,{name:he,length:256},!1,["encrypt","decrypt"]),this._enabled=!0,i}async encryptPage(e){if(!this.cryptoKey)throw new Error("Crypto not initialized");const t=crypto.getRandomValues(new Uint8Array(12)),s=e instanceof Uint8Array?e:new Uint8Array(e);return{iv:t,data:await crypto.subtle.encrypt({name:he,iv:t},this.cryptoKey,s)}}async decryptPage(e,t){if(!this.cryptoKey)throw new Error("Crypto not initialized");const s=t instanceof Uint8Array?t:new Uint8Array(t);return crypto.subtle.decrypt({name:he,iv:e},this.cryptoKey,s)}close(){this.cryptoKey=null,this._enabled=!1}}const le=12,ue="__aria_keymeta";function fe(e){if("undefined"!=typeof Buffer)return Buffer.from(e).toString("base64");let t="";for(let s=0;se!==ue))throw new n("Cannot open with encryption: existing database has no key metadata (database was created without encryption, or key metadata was lost)","ARIA_ENCRYPT_CONFIG_ERROR");const e=await this.crypto.init(this.password),t=await this.crypto.encryptPage((new TextEncoder).encode("metona-sqlark-encryption-verifier").buffer),s=new Uint8Array(le+t.data.byteLength);s.set(t.iv,0),s.set(new Uint8Array(t.data),le);const i=JSON.stringify({salt:fe(e),verifier:fe(s)});await this.inner.write(ue,(new TextEncoder).encode(i).buffer)}this.opened=!0}async close(){await this.inner.close(),this.crypto.close(),this.opened=!1}isOpen(){return this.opened}async read(e){this.ensureReady();const t=await this.inner.read(e);return null===t?null:this.decrypt(t)}async write(e,t){this.ensureReady(),await this.inner.write(e,await this.encrypt(t))}async writeMany(e){this.ensureReady();const t={};for(const[s,n]of Object.entries(e))t[s]=await this.encrypt(n);await this.inner.writeMany(t)}async delete(e){this.ensureReady(),await this.inner.delete(e)}async deleteMany(e){this.ensureReady(),await this.inner.deleteMany(e)}async listKeys(){return this.ensureReady(),this.inner.listKeys()}async exists(e){return this.ensureReady(),this.inner.exists(e)}async clear(){const e=(await this.inner.listKeys()).filter(e=>e!==ue);e.length>0&&await this.inner.deleteMany(e)}ensureReady(){if(!this.opened)throw new n("EncryptedBackend not opened","ARIA_DB_NOT_OPEN");if(!this.crypto.enabled)throw new n("EncryptedBackend key not initialized","ARIA_DECRYPT_ERROR")}async encrypt(e){const t=await this.crypto.encryptPage(e),s=new ArrayBuffer(le+t.data.byteLength),n=new Uint8Array(s);return n.set(t.iv,0),n.set(new Uint8Array(t.data),le),s}async decrypt(e){const t=new Uint8Array(e);if(t.byteLength<=le)throw new n("Corrupted encrypted data block (too short)","ARIA_DECRYPT_ERROR");const s=t.subarray(0,le),i=t.subarray(le);try{return await this.crypto.decryptPage(s,i)}catch(e){if(e instanceof n)throw e;throw new n("Decryption failed (data corrupted or wrong key)","ARIA_DECRYPT_ERROR",e)}}}class ye{constructor(e,t){this.fileManager=e,this.bufferPool=t,this.pageIds=new Map}async save(e,t){const s=Math.max(1,Math.ceil(t.byteLength/_)),n=await this.bufferPool.newPages(s,U.DATA),i=[];for(let e=0;ee+t.byteLength,0),r=new Uint8Array(Math.min(i,s));let a=0;for(const e of n){const t=Math.min(e.byteLength,r.byteLength-a);if(t<=0)break;r.set(e.subarray(0,t),a),a+=t}return this.pageIds.delete(e),r}async delete(e,t){for(const e of t){this.bufferPool.removePage(e);try{await this.fileManager.freePageId(e)}catch{}}this.pageIds.delete(e)}}class me{constructor(e){this.nextPageId=0,this.metaLoaded=!1,this.dbName="",this.backend=e}async init(e){this.dbName=e;const t=await this.backend.read("__aria_meta");if(t&&t instanceof ArrayBuffer&&t.byteLength>=4){const e=new DataView(t);this.nextPageId=e.getUint32(0,!1)}else this.nextPageId=1,await this.saveMeta();this.metaLoaded=!0}async readPage(e){const t=`pg_${e}`,s=await this.backend.read(t);if(!s)return null;if(s.byteLength<_){const e=new ArrayBuffer(_);return new Uint8Array(e).set(new Uint8Array(s)),e}return s}async writePage(e,t){const s=`pg_${e}`;await this.backend.write(s,t)}async allocatePageId(){const e=this.nextPageId++;return await this.saveMeta(),e}async allocatePageIds(e){if(e<=0)return[];const t=[],s=this.nextPageId;this.nextPageId+=e;for(let n=0;nt.txnId!==e);0===n.length?this.versionStore.delete(t):this.versionStore.set(t,n)}this.activeTxns.delete(e),this.txnWriteKeys.delete(e)}writeVersion(e,t,s,n){const i=`${e}.${t}`,r=this.versionStore.get(i)??[],a={txnId:n,data:s,prevVersion:r.length>0?r[r.length-1]:null,committed:!1};r.push(a),this.versionStore.set(i,r),this.txnWriteKeys.get(n)?.add(i)}deleteVersion(e,t,s){this.writeVersion(e,t,{__mvcc_tombstone:!0},s)}discardVersions(e){const t=this.txnWriteKeys.get(e);if(t)for(const s of t){const t=this.versionStore.get(s);if(!t)continue;const n=t.filter(t=>t.txnId!==e);0===n.length?this.versionStore.delete(s):this.versionStore.set(s,n)}}gc(e=100){for(const[t,s]of this.versionStore){if(s.length<=e)continue;const n=s.slice(s.length-e);this.versionStore.set(t,n)}}getGlobalLSN(){return this.globalCommitLsn}}function we(e,t){const s=new ArrayBuffer(_);return function(e,t,s){const n=new DataView(e);n.setUint32(0,t,!1),n.setUint8(4,s),n.setUint16(5,16,!1),n.setUint16(7,e.byteLength,!1),n.setUint16(9,0,!1),n.setUint32(11,0,!1),n.setUint8(15,0)}(s,e,t),{pageId:e,type:t,data:s,dirty:!0,pins:0,prev:null,next:null,lastAccess:Date.now()}}class be{constructor(){this.head=null,this.tail=null,this._size=0}get size(){return this._size}moveToHead(e){this.head!==e&&(null!==e.prev||null!==e.next||this.head===e||this.tail===e?this.detach(e):this._size++,e.prev=null,e.next=this.head,this.head&&(this.head.prev=e),this.head=e,this.tail||(this.tail=e))}remove(e){(null!==e.prev||null!==e.next||this.head===e||this.tail===e)&&(this.detach(e),this._size=Math.max(0,this._size-1))}detach(e){e.prev?e.prev.next=e.next:this.head===e&&(this.head=e.next),e.next?e.next.prev=e.prev:this.tail===e&&(this.tail=e.prev),e.prev=null,e.next=null}clear(){this.head=null,this.tail=null,this._size=0}getLRU(){return this.tail}}class Te{constructor(e,t){this.lru=new be,this.capacity=e,this.onEvict=t}access(e){e.lastAccess=Date.now(),this.lru.moveToHead(e)}add(e){this.access(e)}remove(e){this.lru.remove(e)}async evictIfNeeded(e){let t=0;for(;this.lru.size+e>this.capacity&&this.lru.size>0;){const e=this.findEvictionCandidate();if(!e)break;e.dirty&&(await this.onEvict(e),e.dirty=!1),this.lru.remove(e),t++}return t}findEvictionCandidate(){let e=this.lru.getLRU();for(;e;){if(0===e.pins&&!e.dirty)return e;e=e.prev}for(e=this.lru.getLRU();e;){if(0===e.pins)return e;e=e.prev}return null}clear(){this.lru.clear()}}class Ee{constructor(e,t=256){this.pages=new Map,this.nextPageId=0,this.pageIO=e,this.eviction=new Te(t,async e=>{e.dirty&&(await this.pageIO.writePage(e.pageId,e.data),e.dirty=!1)})}async getPage(e){let t=this.pages.get(e);if(t)return this.eviction.access(t),t.pins++,t;const s=await this.pageIO.readPage(e);return s?(await this.eviction.evictIfNeeded(1),t={pageId:e,type:new DataView(s).getUint8(4),data:s,dirty:!1,pins:1,prev:null,next:null,lastAccess:Date.now()},this.pages.set(e,t),this.eviction.add(t),t):null}async newPages(e,t=U.DATA){if(e<=0)return[];let s;if("function"==typeof this.pageIO.allocatePageIds)s=await this.pageIO.allocatePageIds(e);else{s=[];for(let t=0;t0&&e.pins--}markDirty(e){e.dirty=!0}async flushPage(e){const t=this.pages.get(e);t&&t.dirty&&(await this.pageIO.writePage(e,t.data),t.dirty=!1)}async flushAll(){for(const[,e]of this.pages)e.dirty&&(await this.pageIO.writePage(e.pageId,e.data),e.dirty=!1)}removePage(e){const t=this.pages.get(e);t&&(this.eviction.remove(t),this.pages.delete(e))}async clear(){await this.flushAll(),this.pages.clear(),this.eviction.clear()}}class ke{constructor(e={}){this.name="aria",this.opened=!1,this.dbName="",this.dbLock=null,this.schemas=new Map,this.tablePKs=new Map,this.opCounter=0,this.secondaryIndexes=new Map,this.mvcc=new ge,this.currentTxnId=null,this.txnSnapshot=null,this.gcCounter=0,this.savepoints=new Map,this.config={...K,...e}}async open(e,t){if(!this.opened)try{await this.openInternal(e)}catch(t){if(this.dbLock){try{await this.dbLock.release()}catch{}this.dbLock=null}if(t instanceof n)throw t;throw new n(`Failed to open AriaEngine database "${e}"`,"ARIA_OPEN_ERROR",t)}}async openInternal(e){this.dbName=e;const t=new re;let s;if(this.dbLock=t,await t.acquire(e),s="opfs"===this.config.storageBackend?new p:new oe,await s.open(e),this.config.encryption?.password)this.backend=new pe(s,this.config.encryption.password);else if(this.backend=s,await s.exists("__aria_keymeta"))throw await s.close(),new n("Database is encrypted: provide encryption.password to open it","ARIA_ENCRYPT_REQUIRED");await this.backend.open(e),this.fileManager=new me(this.backend),await this.fileManager.init(e),this.bufferPool=new Ee(this.fileManager,this.config.bufferPoolPages);const i=this.createSSTableStore("main");this.lsm=new Q({memtableSizeThreshold:this.config.memtableSizeThreshold,levelSizeMultiplier:this.config.levelSizeMultiplier,blockSize:this.config.pageSize,bloomBitsPerKey:this.config.bloomFilterBitsPerKey,cacheLimitBytes:this.config.bufferPoolPages*this.config.pageSize,sstableStore:i}),this.wal=new Z(new ie(this.backend),this.config.walEnabled,this.config.walSyncMode),await this.loadSchemas();for(const[e,t]of this.schemas){const s=this.tablePKs.get(e);for(const[n,i]of Object.entries(t.columns))if((i.index||i.unique)&&n!==s){const t=`${e}:idx:${n}`;if(!this.secondaryIndexes.has(t)){const s=new Q({memtableSizeThreshold:this.config.memtableSizeThreshold,levelSizeMultiplier:this.config.levelSizeMultiplier,blockSize:this.config.pageSize,bloomBitsPerKey:this.config.bloomFilterBitsPerKey,cacheLimitBytes:this.config.bufferPoolPages*this.config.pageSize,sstableStore:this.createSSTableStore(`idx_${e}_${n}`)});await s.init(),this.secondaryIndexes.set(t,s)}}}await this.lsm.init();const r=new Set,a=[];await this.wal.recover(e=>a.push(e));for(const e of a)e.type===B.COMMIT&&r.add(e.txnId),e.type===B.ROLLBACK&&r.delete(e.txnId);for(const e of a)(0===e.txnId||r.has(e.txnId))&&(e.type===B.DROP_TABLE?await this.applyDropTableRecovery(e.tableName):this.applyWALRecord(e));if(a.length>0){await this.lsm.flush(),await this.wal.checkpoint();for(const e of this.schemas.keys())await this.reindexTableInternal(e)}this.checkpointManager=new ae(this.lsm,{checkpoint:async()=>{this.currentTxnId||await this.wal.checkpoint()},flush:async()=>{this.currentTxnId||await this.wal.flush()},getBufferedBytes:()=>this.wal.getBufferedBytes(),getBufferedCount:()=>this.wal.getBufferedCount()},{flushAll:async()=>{await this.lsm.flush()}},this.config.checkpointInterval,this.config.walSizeThreshold),this.opened=!0}async close(){if(this.opened){await this.persistSchemas(),await this.lsm.flush();for(const e of this.secondaryIndexes.values())await e.flush();await this.bufferPool.flushAll(),await this.wal.flush(),await this.wal.checkpoint(),await this.backend.close(),this.dbLock&&(await this.dbLock.release(),this.dbLock=null),this.schemas.clear(),this.tablePKs.clear(),this.secondaryIndexes.clear(),this.mvcc=new ge,this.currentTxnId=null,this.txnSnapshot=null,this.savepoints.clear(),this.opCounter=0,this.opened=!1}}async repair(){this.ensureOpen(),await this.bufferPool.clear(),await this.lsm.validateAll(),await this.lsm.flush(),await this.wal.checkpoint();for(const e of this.schemas.keys())await this.reindexTable(e);const e=this.backend;if("function"==typeof e.cleanupStaleFiles)try{await e.cleanupStaleFiles()}catch{}await this.cleanupOrphanPages()}async cleanupOrphanPages(){const e=(await this.backend.listKeys()).filter(e=>/^pg_\d+$/.test(e));if(0===e.length)return;const t=new Set,s=async e=>{const s="main"===e?"__aria_lsm_meta":`__aria_lsm_meta_${e}`,n=await this.backend.read(s);if(n)try{const e=JSON.parse((new TextDecoder).decode(n));for(const s of e)if(s.pageIds)for(const e of s.pageIds)t.add(e)}catch{}};await s("main");for(const[e,t]of this.schemas)for(const[n,i]of Object.entries(t.columns))(i.index||i.unique)&&await s(`idx_${e}_${n}`);const n=e.map(e=>Number(e.slice(3))).filter(e=>!t.has(e));n.length>0&&await this.backend.deleteMany(n.map(e=>`pg_${e}`))}async clearAll(){this.ensureOpen(),await this.backend.clear(),await this.bufferPool.clear(),await this.fileManager.clearAll(),this.schemas.clear(),this.tablePKs.clear(),this.secondaryIndexes.clear(),this.lsm.clear(),this.mvcc=new ge,this.currentTxnId=null,this.txnSnapshot=null,this.savepoints.clear(),this.opCounter=0,await this.persistSchemas(),await this.wal.checkpoint()}isOpen(){return this.opened}async getMeta(e){const t=await this.backend.read(`__meta_${e}`);return t?(new TextDecoder).decode(t):null}async setMeta(e,t){await this.backend.write(`__meta_${e}`,(new TextEncoder).encode(t).buffer)}async createTable(e){if(this.ensureOpen(),this.ensureNoDDLInTransaction("CREATE TABLE"),this.schemas.has(e.name))throw new n(`Table "${e.name}" already exists`,"TABLE_EXISTS");this.schemas.set(e.name,e),this.tablePKs.set(e.name,this.getPK(e));for(const[t,s]of Object.entries(e.columns))if(s.index||s.unique){const s=`${e.name}:idx:${t}`;if(!this.secondaryIndexes.has(s)){const n=new Q({memtableSizeThreshold:this.config.memtableSizeThreshold,levelSizeMultiplier:this.config.levelSizeMultiplier,blockSize:this.config.pageSize,bloomBitsPerKey:this.config.bloomFilterBitsPerKey,cacheLimitBytes:this.config.bufferPoolPages*this.config.pageSize,sstableStore:this.createSSTableStore(`idx_${e.name}_${t}`)});await n.init(),this.secondaryIndexes.set(s,n)}}await this.persistSchemas(),await this.wal.append({type:B.CREATE_TABLE,txnId:0,tableName:e.name,key:"",data:{schema:JSON.stringify(e)}})}async dropTable(e){this.ensureOpen(),this.ensureNoDDLInTransaction("DROP TABLE"),this.ensureTable(e);const t=await this.getAllRows(e);for(const s of t){const t=this.tablePKs.get(e);this.lsm.delete(`${e}:${s[t]}`)}await this.cleanupTableIndexes(e),this.schemas.delete(e),this.tablePKs.delete(e),await this.persistSchemas(),await this.wal.append({type:B.DROP_TABLE,txnId:0,tableName:e,key:""})}async cleanupTableIndexes(e){const t=`${e}:idx:`,s=[];for(const[e,n]of this.secondaryIndexes)if(e.startsWith(t)){s.push(e);try{await n.clear()}catch{}}for(const e of s)this.secondaryIndexes.delete(e)}async hasTable(e){return this.schemas.has(e)}async getTableNames(){return Array.from(this.schemas.keys())}async getTableSchema(e){return this.schemas.get(e)??null}async insert(e,t){this.ensureOpen(),this.ensureTable(e);const s=this.schemas.get(e),i=this.tablePKs.get(e),r=[],a=[];for(const o of t){const t=this.validateRow(s,o),h=String(t[i]),c=`${e}:${h}`;await this.lsm.prefetchKeys([c]);const l=this.currentTxnId?this.txnSnapshot?.get(c)??this.lsm.get(c):this.lsm.get(c);if(l&&!l.__txn_deleted)throw new n(`Duplicate primary key "${h}" in table "${e}"`,"DUPLICATE_KEY");this.currentTxnId&&this.txnSnapshot?(this.txnSnapshot.set(c,t),this.mvcc.writeVersion(e,h,t,this.currentTxnId)):this.lsm.put(c,t),this.updateSecondaryIndexes(e,h,t,null),r.push(h),a.push({type:B.INSERT,txnId:this.currentTxnId??0,tableName:e,key:h,data:t})}return await this.wal.appendBatch(a),this.opCounter+=t.length,this.checkMemoryBudget(),await this.checkpointManager.tick(),this.tryGC(),r}async find(e,t){let s;this.ensureOpen(),this.ensureTable(e);const n=await this.tryIndexLookup(e,t);s=null!==n?n:await this.getAllRows(e),s=this.mergeTxnSnapshot(e,s),t.where&&Object.keys(t.where).length>0&&(s=s.filter(e=>a(e,t.where))),t.orderBy&&t.orderBy.length>0&&(s=c(s,t.orderBy));const i=t.offset??0,r=t.limit??s.length;return s=s.slice(i,i+r),t.columns&&t.columns.length>0&&"*"!==t.columns[0]&&(s=s.map(e=>u(e,t.columns))),this.trimAllCaches(),s}async update(e,t,s){this.ensureOpen(),this.ensureTable(e);const n=this.schemas.get(e),i=await this.getAllRows(e);let r=0;const o=[],h=new Set;for(const c of i){const i=this.tablePKs.get(e),l=`${e}:${c[i]}`;if(!t.where||0===Object.keys(t.where).length||a(c,t.where)){const t={...c,...s};this.validateRow(n,t);const a=String(t[i]),u=a!==String(c[i]);u&&await this.applyForeignKeyUpdateRules(e,String(c[i]),a,o,h),this.currentTxnId&&this.txnSnapshot?(u&&(this.txnSnapshot.set(l,{__txn_deleted:!0}),this.mvcc.deleteVersion(e,String(c[i]),this.currentTxnId)),this.txnSnapshot.set(`${e}:${a}`,t),this.mvcc.writeVersion(e,a,t,this.currentTxnId)):(u&&this.lsm.delete(l),this.lsm.put(`${e}:${a}`,t)),r++,u&&o.push({type:B.DELETE,txnId:this.currentTxnId??0,tableName:e,key:String(c[i])}),o.push({type:B.UPDATE,txnId:this.currentTxnId??0,tableName:e,key:a,data:t}),this.updateSecondaryIndexes(e,a,t,u?c:null)}}return await this.wal.appendBatch(o),this.opCounter+=r,await this.checkpointManager.tick(),this.trimAllCaches(),r}async applyForeignKeyUpdateRules(e,t,s,i,r){const a=`${e}:${t}`;if(!r.has(a)){r.add(a);for(const[s,i]of this.schemas)if(s!==e)for(const[r,a]of Object.entries(i.columns)){if(!a.references||!a.onUpdate)continue;const[i]=a.references.split(".");if(i===e&&("RESTRICT"===a.onUpdate&&(await this.getAllRows(s)).some(e=>String(e[r])===t)))throw new n(`Cannot update "${e}" key "${t}": foreign key "${r}" in "${s}" has dependent rows`,"FOREIGN_KEY_VIOLATION")}for(const[n,r]of this.schemas)if(n!==e)for(const[a,o]of Object.entries(r.columns)){if(!o.references||!o.onUpdate)continue;const[r]=o.references.split(".");if(r!==e)continue;if("CASCADE"!==o.onUpdate&&"SET NULL"!==o.onUpdate)continue;const h=await this.getAllRows(n);for(const e of h){if(String(e[a])!==t)continue;const r=this.tablePKs.get(n),h=String(e[r]),c={...e,[a]:"CASCADE"===o.onUpdate?s:null},l=`${n}:${h}`;this.currentTxnId&&this.txnSnapshot?(this.txnSnapshot.set(l,c),this.mvcc.writeVersion(n,h,c,this.currentTxnId)):this.lsm.put(l,c),this.updateSecondaryIndexes(n,h,c,e),i.push({type:B.UPDATE,txnId:this.currentTxnId??0,tableName:n,key:h,data:c})}}}}async delete(e,t){this.ensureOpen(),this.ensureTable(e);const s=await this.getAllRows(e);let n=0;const i=[],r=new Set;for(const o of s){const s=this.tablePKs.get(e),h=`${e}:${o[s]}`;t.where&&0!==Object.keys(t.where).length&&!a(o,t.where)||(n+=await this.applyForeignKeyRules(e,String(o[s]),i,r),this.currentTxnId&&this.txnSnapshot?(this.txnSnapshot.set(h,{__txn_deleted:!0}),this.mvcc.deleteVersion(e,String(o[s]),this.currentTxnId)):this.lsm.delete(h),n++,i.push({type:B.DELETE,txnId:this.currentTxnId??0,tableName:e,key:String(o[s])}),this.updateSecondaryIndexes(e,String(o[s]),null,o))}return await this.wal.appendBatch(i),this.opCounter+=n,await this.checkpointManager.tick(),this.trimAllCaches(),n}async applyForeignKeyRules(e,t,s,i){let r=0;const a=`${e}:${t}`;if(i.has(a))return 0;i.add(a);for(const[a,o]of this.schemas)if(a!==e)for(const[h,c]of Object.entries(o.columns)){if(!c.references||!c.onDelete)continue;const[o]=c.references.split(".");if(o!==e)continue;const l=(await this.getAllRows(a)).filter(e=>String(e[h])===t);if("RESTRICT"===c.onDelete&&l.length>0)throw new n(`Cannot delete from "${e}": foreign key "${h}" in "${a}" has dependent rows`,"FOREIGN_KEY_VIOLATION");if("CASCADE"===c.onDelete){const e=this.tablePKs.get(a);for(const t of l){const n=String(t[e]);r+=await this.applyForeignKeyRules(a,n,s,i);const o=`${a}:${n}`;this.currentTxnId&&this.txnSnapshot?(this.txnSnapshot.set(o,{__txn_deleted:!0}),this.mvcc.deleteVersion(a,n,this.currentTxnId)):this.lsm.delete(o),this.updateSecondaryIndexes(a,n,null,t),s.push({type:B.DELETE,txnId:this.currentTxnId??0,tableName:a,key:n}),r++}}else if("SET NULL"===c.onDelete){const e=this.tablePKs.get(a);for(const t of l){const n=String(t[e]),i={...t,[h]:null},r=`${a}:${n}`;this.currentTxnId&&this.txnSnapshot?(this.txnSnapshot.set(r,i),this.mvcc.writeVersion(a,n,i,this.currentTxnId)):this.lsm.put(r,i),this.updateSecondaryIndexes(a,n,i,t),s.push({type:B.UPDATE,txnId:this.currentTxnId??0,tableName:a,key:n,data:i})}}}return r}async findStream(e,t,s){this.ensureOpen(),this.ensureTable(e);const n=!!(t.where&&Object.keys(t.where).length>0),i=t.columns&&t.columns.length>0&&"*"!==t.columns[0]?e=>u(e,t.columns):null,r=t.limit??1/0,o=t.offset??0,h=this.tablePKs.get(e),c=`${e}:`;let l=0,f=0;const d=e=>!(!n||a(e,t.where))||(f{if(l>=r)return;const s={...t};s[h]=e.slice(c.length),d(s)}),l}async count(e,t){this.ensureOpen(),this.ensureTable(e);const s=await this.getAllRows(e);return this.trimAllCaches(),t?.where&&0!==Object.keys(t.where).length?s.filter(e=>a(e,t.where)).length:s.length}async clear(e){this.ensureOpen(),this.ensureTable(e);const t=await this.getAllRows(e),s=[];for(const n of t){const t=this.tablePKs.get(e),i=`${e}:${n[t]}`;this.currentTxnId&&this.txnSnapshot?(this.txnSnapshot.set(i,{__txn_deleted:!0}),this.mvcc.deleteVersion(e,String(n[t]),this.currentTxnId)):this.lsm.delete(i),s.push({type:B.DELETE,txnId:this.currentTxnId??0,tableName:e,key:String(n[t])}),this.updateSecondaryIndexes(e,String(n[t]),null,n)}await this.wal.appendBatch(s),this.opCounter+=t.length,await this.checkpointManager.tick(),this.tryGC()}async alterTable(e,t,s){this.ensureOpen(),this.ensureNoDDLInTransaction("ALTER TABLE"),this.ensureTable(e);const i=this.schemas.get(e);if("ADD"===t){if(i.columns[s.name])throw new n(`Column "${s.name}" already exists in table "${e}"`,"COLUMN_EXISTS");return i.columns[s.name]=s,void await this.persistSchemas()}if(!i.columns[s.name])throw new n(`Column "${s.name}" does not exist in table "${e}"`,"COLUMN_NOT_FOUND");if(i.columns[s.name].index||i.columns[s.name].unique){const t=`${e}:idx:${s.name}`,n=this.secondaryIndexes.get(t);if(n){try{await n.clear()}catch{}this.secondaryIndexes.delete(t)}}delete i.columns[s.name],await this.persistSchemas();const r=`${e}:`,a=`${r}ï¿¿`;await this.lsm.prefetchRange(r,a);const o=this.lsm.rangeScan(r,a),h=[];for(const[t,n]of o){if(!(s.name in n))continue;const i={...n};delete i[s.name],this.lsm.put(t,i);const a=t.slice(r.length);this.updateSecondaryIndexes(e,a,i,n),h.push({type:B.UPDATE,txnId:this.currentTxnId??0,tableName:e,key:a,data:i})}await this.wal.appendBatch(h),this.opCounter+=h.length,await this.checkpointManager.tick(),this.trimAllCaches()}async createIndex(e,t,s){this.ensureOpen(),this.ensureNoDDLInTransaction("CREATE INDEX"),this.ensureTable(e);const i=this.schemas.get(e).columns[t];if(!i)throw new n(`Column "${t}" does not exist in table "${e}"`,"COLUMN_NOT_FOUND");const r=`${e}:idx:${t}`;if(this.secondaryIndexes.has(r))return;i.index=!0,s&&(i.unique=!0);const a=new Q({memtableSizeThreshold:this.config.memtableSizeThreshold,levelSizeMultiplier:this.config.levelSizeMultiplier,blockSize:this.config.pageSize,bloomBitsPerKey:this.config.bloomFilterBitsPerKey,cacheLimitBytes:this.config.bufferPoolPages*this.config.pageSize,sstableStore:this.createSSTableStore(`idx_${e}_${t}`)});await a.init(),this.secondaryIndexes.set(r,a);const o=this.tablePKs.get(e),h=await this.getAllRows(e);for(const e of h){const s=e[t];null!=s&&a.put(`${String(s)}:${e[o]}`,{pk:e[o]})}await a.flush(),await this.persistSchemas()}async dropIndex(e,t,s){this.ensureOpen(),this.ensureNoDDLInTransaction("DROP INDEX"),this.ensureTable(e);const i=this.schemas.get(e).columns[t];if(!i)throw new n(`Column "${t}" does not exist in table "${e}"`,"COLUMN_NOT_FOUND");if(i.primaryKey)throw new n(`Cannot drop primary key index on column "${t}"`,"NOT_SUPPORTED");if(!i.index&&!i.unique&&!this.secondaryIndexes.has(`${e}:idx:${t}`))throw new n(`Index on column "${t}" does not exist in table "${e}"`,"INDEX_NOT_FOUND");i.index=!1,i.unique=!1;const r=`${e}:idx:${t}`,a=this.secondaryIndexes.get(r);a&&(await a.clear(),this.secondaryIndexes.delete(r)),await this.persistSchemas()}async beginTransaction(){if(this.currentTxnId)throw new n("Transaction already in progress","TX_ACTIVE");this.currentTxnId=this.mvcc.beginTransaction(),this.txnSnapshot=new Map,await this.wal.append({type:B.BEGIN,txnId:this.currentTxnId,tableName:"",key:""})}async commitTransaction(){if(!this.currentTxnId)throw new n("No active transaction","TX_NONE");if(await this.wal.append({type:B.COMMIT,txnId:this.currentTxnId,tableName:"",key:""}),await this.wal.flush(),this.txnSnapshot)for(const[e,t]of this.txnSnapshot)t.__txn_deleted?this.lsm.delete(e):this.lsm.put(e,t);this.mvcc.commitTransaction(this.currentTxnId),this.currentTxnId=null,this.txnSnapshot=null}async rollbackTransaction(){if(!this.currentTxnId)throw new n("No active transaction","TX_NONE");const e=new Set;if(this.txnSnapshot)for(const t of this.txnSnapshot.keys()){const s=t.indexOf(":");s>0&&e.add(t.slice(0,s))}this.mvcc.rollbackTransaction(this.currentTxnId),this.txnSnapshot=null,await this.wal.append({type:B.ROLLBACK,txnId:this.currentTxnId,tableName:"",key:""}),this.currentTxnId=null;for(const t of e)this.schemas.has(t)&&await this.reindexTable(t)}async savepoint(e){if(!this.currentTxnId)throw new n("No active transaction for savepoint","TX_NONE");if(this.savepoints.has(e))throw new n(`Savepoint "${e}" already exists`,"SAVEPOINT_EXISTS");this.savepoints.set(e,{txnId:this.currentTxnId,snapshot:this.txnSnapshot?new Map(this.txnSnapshot):null})}async rollbackToSavepoint(e){const t=this.savepoints.get(e);if(!t)throw new n(`Savepoint "${e}" not found`,"SAVEPOINT_NOT_FOUND");this.txnSnapshot=t.snapshot?new Map(t.snapshot):null,this.mvcc.discardVersions(this.currentTxnId);let s=!1;for(const[t]of this.savepoints)t!==e?s&&this.savepoints.delete(t):s=!0}async releaseSavepoint(e){if(!this.savepoints.has(e))throw new n(`Savepoint "${e}" not found`,"SAVEPOINT_NOT_FOUND");this.savepoints.delete(e)}async backup(){this.ensureOpen();const e={};for(const t of this.schemas.keys())e[t]=await this.getAllRows(t);return e}async getAllRows(e){const t=this.tablePKs.get(e),s=`${e}:`;await this.lsm.prefetchRange(s,`${s}ï¿¿`);const n=this.lsm.rangeScan(s,`${s}ï¿¿`).map(([e,n])=>{const i={...n};return i[t]=e.slice(s.length),i});return this.mergeTxnSnapshot(e,n)}mergeTxnSnapshot(e,t){if(!this.currentTxnId||!this.txnSnapshot)return t;const s=this.tablePKs.get(e),n=`${e}:`;for(const[e,i]of this.txnSnapshot){if(!e.startsWith(n))continue;const r=e.slice(n.length),a=i.__txn_deleted,o=t.findIndex(e=>e[s]===r);if(a)o>=0&&t.splice(o,1);else{const e={...i,[s]:r};o>=0?t[o]=e:t.push(e)}}return t}getPK(e){for(const[t,s]of Object.entries(e.columns))if(s.primaryKey)return t;return Object.keys(e.columns)[0]}validateRow(e,t){const s={};for(const[i,r]of Object.entries(e.columns)){let a=t[i];if(void 0===a&&void 0!==r.default&&(a=r.default),r.required&&null==a)throw new n(`Column "${i}" is required in table "${e.name}"`,"VALIDATION_ERROR");null!=a&&this.checkType(i,r.type,a,r),void 0!==a&&(s[i]=a)}return s}checkType(e,t,s,i){!function(e,t,s,i,r){const a=typeof i;switch(s){case"string":if("string"!==a)throw new n(`Column "${t}" in table "" expects string, got ${a}`,"TYPE_ERROR");if(void 0!==r?.maxLength&&i.length>r.maxLength)throw new n(`Column "${t}" in table "" exceeds max length ${r.maxLength}`,"VALIDATION_ERROR");break;case"number":if("number"!==a)throw new n(`Column "${t}" in table "" expects number, got ${a}`,"TYPE_ERROR");if(void 0!==r?.min&&ir.max)throw new n(`Column "${t}" in table "" value ${i} above maximum ${r.max}`,"VALIDATION_ERROR");break;case"boolean":if("boolean"!==a)throw new n(`Column "${t}" in table "" expects boolean, got ${a}`,"TYPE_ERROR");break;case"date":if("string"!==a||isNaN(Date.parse(i)))throw new n(`Column "${t}" in table "" expects valid date string, got ${typeof i}`,"TYPE_ERROR");break;case"json":if("object"!==a)throw new n(`Column "${t}" in table "" expects object/array, got ${a}`,"TYPE_ERROR")}}(0,e,t,s,i)}async persistSchemas(){const e={};for(const[t,s]of this.schemas)e[t]=s.columns;const t=JSON.stringify(e),s=(new TextEncoder).encode(t).buffer;await this.backend.write("__aria_schemas",s)}async loadSchemas(){const e=await this.backend.read("__aria_schemas");if(e)try{const t=(new TextDecoder).decode(e),s=JSON.parse(t);for(const[e,t]of Object.entries(s)){const s={name:e,columns:t};this.schemas.set(e,s),this.tablePKs.set(e,this.getPK(s))}}catch{}}createSSTableStore(e){const t="main"===e?"sst_":`sst_${e}_`,s="main"===e?"__aria_lsm_meta":`__aria_lsm_meta_${e}`;let n=0,i=!1;const r=this.isPageStorage()?new ye(this.fileManager,this.bufferPool):null,a=e=>(new TextEncoder).encode(e).buffer,o=async()=>{const e=await this.backend.read(s);if(!e)return[];try{return JSON.parse((new TextDecoder).decode(e))}catch{return[]}};return{save:async(e,s)=>{if(r)return void await r.save(e,s);let n=s.buffer.slice(s.byteOffset,s.byteOffset+s.byteLength);if(this.config.compression){const e=function(e){if(0===e.byteLength){const e=new Uint8Array(4);return new DataView(e.buffer).setUint32(0,0,!0),e}const t=e.byteLength+Math.ceil(e.byteLength/15)+8,s=new Uint8Array(t);let n=0,i=0,r=0;for(;n=4&&i>t&&(t=i,a=n-s)}if(t>4&&n-r<=15){const o=n-r,h=t-4;s[i++]=(15&o)<<4|15&h;for(let t=0;t>8&255,n+=t,r=n}else if(n++,n-r>=15){s[i++]=240;for(let t=0;t<15;t++)s[i++]=e[r+t];r=n}}let a=n-r;for(;a>0;){const t=Math.min(a,15);s[i++]=(15&t)<<4;for(let n=0;n{if(r){const t=(await o()).find(t=>t.id===e);if(t&&t.pageIds&&t.pageIds.length>0)return r.load(e,t.pageIds,t.totalSize)}const s=await this.backend.read(`${t}${e}`);if(!s)return null;let n=new Uint8Array(s);return this.config.compression&&(n=function(e){if(e.byteLength<4)throw new Error("LZ4 stream too short: missing header");const t=new DataView(e.buffer,e.byteOffset,e.byteLength).getUint32(0,!0);if(0===t&&4===e.byteLength)return new Uint8Array(0);if(t<=0||t>1073741823)throw new Error("Invalid LZ4 header: bad original size");const s=e.subarray(4),n=new Uint8Array(t);let i=0,r=0;for(;i>4&15,o=15&e;for(let e=0;e=s.byteLength)break;const h=s[i++]|s[i++]<<8,c=o+4;for(let e=0;e{if(r){const t=(await o()).find(t=>t.id===e);t&&t.pageIds&&t.pageIds.length>0&&await r.delete(e,t.pageIds)}await this.backend.delete(`${t}${e}`)},allocateId:async()=>{if(!i){const e=await o();n=e.reduce((e,t)=>Math.max(e,t.id),0),i=!0}return++n},listMeta:o,saveMeta:async e=>{const t=await o(),n=r?.getPageIds(e.id),i=n&&n.length>0?{...e,pageIds:n}:e,h=t.findIndex(t=>t.id===e.id);h>=0?t[h]=i:t.push(i),await this.backend.write(s,a(JSON.stringify(t)))},deleteMeta:async e=>{const t=(await o()).filter(t=>t.id!==e);await this.backend.write(s,a(JSON.stringify(t)))}}}isPageStorage(){return!0===this.config.pageStorage||!1!==this.config.pageStorage&&"opfs"===this.config.storageBackend}applyWALRecord(e){switch(e.type){case B.INSERT:case B.UPDATE:e.data&&this.lsm.put(`${e.tableName}:${e.key}`,e.data);break;case B.DELETE:this.lsm.delete(`${e.tableName}:${e.key}`);break;case B.CREATE_TABLE:if(e.data?.schema)try{const t=JSON.parse(e.data.schema);this.schemas.has(t.name)||(this.schemas.set(t.name,t),this.tablePKs.set(t.name,this.getPK(t)))}catch{}case B.COMMIT:case B.ROLLBACK:case B.BEGIN:}}async applyDropTableRecovery(e){if(!e)return;await this.cleanupTableIndexes(e),this.schemas.delete(e),this.tablePKs.delete(e);const t=`${e}:`,s=`${t}ï¿¿`;await this.lsm.prefetchRange(t,s);const n=this.lsm.rangeScan(t,s);for(const[e]of n)this.lsm.delete(e)}updateSecondaryIndexes(e,t,s,n){const i=this.schemas.get(e);if(i)for(const[r,a]of Object.entries(i.columns)){if(!a.index&&!a.unique)continue;const i=`${e}:idx:${r}`,o=this.secondaryIndexes.get(i);if(o){if(n){const e=n[r];null!=e&&o.delete(`${String(e)}:${t}`)}if(s){const e=s[r];null!=e&&o.put(`${String(e)}:${t}`,{pk:t})}}}}async tryIndexLookup(e,t){if(!t.where)return null;const s=this.schemas.get(e);if(!s)return null;const n=this.tablePKs.get(e);for(const[i,r]of Object.entries(t.where)){if("$and"===i||"$or"===i||"$not"===i)continue;const t=s.columns[i];if(!(t&&(t.index||t.unique||t.primaryKey)||i===n))continue;if(i===n){if("object"!=typeof r||null===r){const t=`${e}:${r}`;await this.lsm.prefetchKeys([t]);const s=this.lsm.get(t);return s?[{...s,[n]:r}]:[]}const t=r;if("$eq"in t){const s=`${e}:${t.$eq}`;await this.lsm.prefetchKeys([s]);const i=this.lsm.get(s);return i?[{...i,[n]:t.$eq}]:[]}if("$in"in t&&Array.isArray(t.$in)){const s=t.$in.map(t=>`${e}:${t}`);await this.lsm.prefetchKeys(s);const i=[],r=new Set;for(const s of t.$in){const t=String(s);if(r.has(t))continue;const a=this.lsm.get(`${e}:${t}`);a&&(r.add(t),i.push({...a,[n]:t}))}return i}if("$gt"in t||"$gte"in t||"$lt"in t||"$lte"in t){const t=`${e}:`;await this.lsm.prefetchRange(t,`${t}ï¿¿`);const s=this.lsm.rangeScan(t,`${t}ï¿¿`),i=[];for(const[e,o]of s){const s={...o,[n]:e.slice(t.length)};a(s,{[n]:r})&&i.push(s)}return i}}const o=`${e}:idx:${i}`,h=this.secondaryIndexes.get(o);if(!h)continue;if("object"!=typeof r||null===r)return this.indexScanToRows(e,n,h,String(r),String(r));const c=r;if("$eq"in c){const t=String(c.$eq);return this.indexScanToRows(e,n,h,t,t)}if("$in"in c&&Array.isArray(c.$in)){const t=[],s=new Set;for(const i of c.$in){const r=await this.indexScanToRows(e,n,h,String(i),String(i));for(const e of r){const i=String(e[n]);s.has(i)||(s.add(i),t.push(e))}}return t}if("$gt"in c||"$gte"in c||"$lt"in c||"$lte"in c){let t="",s="ï¿¿";return void 0!==c.$gt?t=`${String(Number(c.$gt)+1)}:`:void 0!==c.$gte&&(t=`${String(c.$gte)}:`),void 0!==c.$lt?s=`${String(Number(c.$lt)-1)}:ï¿¿`:void 0!==c.$lte&&(s=`${String(c.$lte)}:ï¿¿`),this.indexScanToRows(e,n,h,t,s)}}return null}async indexScanToRows(e,t,s,n,i){const r=i.includes("ï¿¿")?i:`${i}ï¿¿`;await s.prefetchRange(n,r);const a=s.rangeScan(n,r),o=[];for(const[,e]of a){const t=e.pk;t&&o.push(t)}await this.lsm.prefetchKeys(o.map(t=>`${e}:${t}`));const h=[];for(const s of o){const n=this.lsm.get(`${e}:${s}`);n&&h.push({...n,[t]:s})}return h}tryGC(){this.gcCounter++,this.gcCounter>=10&&(this.mvcc.gc(100),this.gcCounter=0)}trimAllCaches(){this.lsm.trimCache();for(const e of this.secondaryIndexes.values())e.trimCache()}checkMemoryBudget(){const e=1024*this.config.maxMemoryMB*1024;this.lsm.getEstimatedMemory()>e&&(this.lsm.flush().catch(()=>{}),this.mvcc.gc(50))}getWALEstimatedSize(){return 200*this.wal.getBufferedCount()}async analyzeTable(e){this.ensureOpen(),this.ensureTable(e);const t=await this.getAllRows(e),s={table:e,rowCount:t.length,avgRowSize:t.length>0?Math.round(t.reduce((e,t)=>e+JSON.stringify(t).length,0)/t.length):0,indexDepth:this.lsm.getStats().levelCounts.filter(e=>e>0).length,sstableCount:this.lsm.getStats().sstableCount,memtableSize:this.lsm.getStats().memtableSize,estimatedMemory:this.lsm.getEstimatedMemory()},n=this.schemas.get(e);if(n&&t.length>0){const e={};for(const s of Object.keys(n.columns)){const n=new Set(t.map(e=>String(e[s])));e[s]={distinctValues:n.size}}s.columnStats=e}return s}async reindexTable(e){return this.ensureOpen(),this.ensureTable(e),this.reindexTableInternal(e)}async reindexTableInternal(e){const t=this.schemas.get(e);if(!t)return 0;let s=0;for(const[n,i]of Object.entries(t.columns)){if(!i.index&&!i.unique)continue;const t=`${e}:idx:${n}`,r=this.secondaryIndexes.get(t);if(!r)continue;await r.clear(),s++;const a=await this.getAllRows(e);for(const t of a){const s=t[n];null!=s&&r.put(`${String(s)}:${t[this.tablePKs.get(e)]}`,{pk:t[this.tablePKs.get(e)]})}}return s}async vacuum(){this.ensureOpen(),await this.lsm.flush();for(let e=0;e<6;e++)this.lsm.getStats().levelCounts[e]>=2&&await this.lsm.compactLevel(e);const e=this.mvcc.getGlobalLSN();return this.mvcc.gc(10),{compactedLevels:6,gcVersions:e}}ensureOpen(){if(!this.opened)throw new n("AriaEngine not opened","DB_NOT_OPEN")}ensureNoDDLInTransaction(e){if(this.currentTxnId)throw new n(`${e} is not supported inside a transaction (AriaEngine DDL is not transactional)`,"NOT_SUPPORTED")}ensureTable(e){if(!this.schemas.has(e))throw new n(`Table "${e}" does not exist`,"TABLE_NOT_FOUND")}}class xe{constructor(e="opfs"){this.name="hybrid",this.dbName="",this.version=1,this.memoryEngine=new f,this.diskEngineType=e,this.diskEngine=new v}async open(e,t){this.dbName=e,this.version=t,await this.diskEngine.open(e,t),await this.memoryEngine.open(e,t),await this.reloadMemoryFromDisk()}async reloadMemoryFromDisk(){await this.memoryEngine.close(),await this.memoryEngine.open(this.dbName,this.version);const e=this.diskEngine;"function"==typeof e.reload&&await e.reload();const t=await this.diskEngine.getTableNames();for(const e of t){const t=await this.diskEngine.getTableSchema(e);if(!t)continue;await this.memoryEngine.createTable(t);const s=await this.diskEngine.find(e,{table:e});if(s.length>0)try{await this.memoryEngine.insert(e,s)}catch(e){}}}async close(){await this.memoryEngine.close(),await this.diskEngine.close()}isOpen(){return this.memoryEngine.isOpen()&&this.diskEngine.isOpen()}async repair(){"function"==typeof this.diskEngine.repair&&await this.diskEngine.repair(),await this.reloadMemoryFromDisk()}async clearAll(){if("function"==typeof this.diskEngine.clearAll)await this.diskEngine.clearAll();else{const e=await this.diskEngine.getTableNames();for(const t of e)await this.diskEngine.dropTable(t)}if("function"==typeof this.memoryEngine.clearAll)await this.memoryEngine.clearAll();else{const e=await this.memoryEngine.getTableNames();for(const t of e)await this.memoryEngine.dropTable(t)}}async getMeta(e){return"function"==typeof this.diskEngine.getMeta?this.diskEngine.getMeta(e):null}async setMeta(e,t){"function"==typeof this.diskEngine.setMeta&&await this.diskEngine.setMeta(e,t)}async createTable(e){await this.memoryEngine.createTable(e),await this.diskEngine.createTable(e)}async dropTable(e){await this.memoryEngine.dropTable(e),await this.diskEngine.dropTable(e)}async hasTable(e){return this.memoryEngine.hasTable(e)}async getTableNames(){return this.memoryEngine.getTableNames()}async getTableSchema(e){return this.memoryEngine.getTableSchema(e)}async alterTable(e,t,s){if(await this.memoryEngine.alterTable(e,t,s),"function"==typeof this.diskEngine.alterTable)await this.diskEngine.alterTable(e,t,s);else{const n=await this.diskEngine.getTableSchema(e);n&&"DROP"===t&&delete n.columns[s.name]}}async insert(e,t){const s=await this.memoryEngine.insert(e,t);return await this.diskEngine.insert(e,t),s}async find(e,t){return this.memoryEngine.find(e,t)}async findStream(e,t,s){return this.memoryEngine.findStream(e,t,s)}async update(e,t,s){const n=await this.memoryEngine.update(e,t,s);return await this.diskEngine.update(e,t,s),n}async delete(e,t){const s=await this.memoryEngine.delete(e,t);return await this.diskEngine.delete(e,t),s}async count(e,t){return this.memoryEngine.count(e,t)}async clear(e){await this.memoryEngine.clear(e),await this.diskEngine.clear(e)}async createIndex(e,t,s){await this.memoryEngine.createIndex(e,t,s),"function"==typeof this.diskEngine.createIndex&&await this.diskEngine.createIndex(e,t,s)}async dropIndex(e,t,s){await this.memoryEngine.dropIndex(e,t,s),"function"==typeof this.diskEngine.dropIndex&&await this.diskEngine.dropIndex(e,t,s)}async beginTransaction(){await this.memoryEngine.beginTransaction(),await this.diskEngine.beginTransaction()}async commitTransaction(){await this.diskEngine.commitTransaction();try{await this.memoryEngine.commitTransaction()}catch(e){throw new n("Hybrid commit failed: memory engine error after disk commit (disk data is committed)","TX_COMMIT_ERROR",e)}}async rollbackTransaction(){await this.memoryEngine.rollbackTransaction(),await this.diskEngine.rollbackTransaction()}getDiskEngineType(){return this.diskEngineType}getMemoryEngine(){return this.memoryEngine}}class Se{constructor(e,t,s=["*"],n){this.engine=e,this.tableName=t,this._columns=s,this._where={},this._orderBy=[],this._joins=[],this._executor=n}as(e){return this._alias=e,this}innerJoin(e,t,s){return this._addJoin("INNER",e,t,s)}leftJoin(e,t,s){return this._addJoin("LEFT",e,t,s)}rightJoin(e,t,s){return this._addJoin("RIGHT",e,t,s)}crossJoin(e,t){return this._addJoin("CROSS",e,{},t)}join(e,t,s){return this._addJoin("INNER",e,t,s)}_addJoin(e,t,s,n){return this._joins.push({type:e,table:t,on:s,alias:n}),this}where(e){return this._where={...this._where,...e},this}orderBy(e,t="asc"){return this._orderBy.push({column:e,direction:t}),this}limit(e){return this._limit=e,this}offset(e){return this._offset=e,this}async execute(){if(this._joins.length>0&&this._executor){const e=this.toAST();return this._executor.execute(e)}return this.engine.find(this.tableName,{table:this.tableName,columns:this._columns,where:this._where,orderBy:this._orderBy.length>0?this._orderBy:void 0,limit:this._limit,offset:this._offset})}toAST(){return{type:"SELECT",columns:this._columns,from:this.tableName,alias:this._alias,joins:this._joins.length>0?[...this._joins]:void 0,where:this._where,orderBy:this._orderBy.length>0?this._orderBy:void 0,limit:this._limit,offset:this._offset}}}class Ie{constructor(e,t,s,n,i){this.engine=e,this.tableName=t,this._updates=s,this._where={},this.onWrite=n,this.onHooks=i}where(e){return this._where={...this._where,...e},this}async execute(){const e={table:this.tableName,where:this._where};await(this.onHooks?.("beforeUpdate",[e,this._updates]));const t=await this.engine.update(this.tableName,e,this._updates);return this.onWrite?.(this.tableName),await(this.onHooks?.("afterUpdate",[e,this._updates,t])),t}toAST(){return{type:"UPDATE",table:this.tableName,sets:this._updates,where:this._where}}}class Ae{constructor(e,t,s,n){this.engine=e,this.tableName=t,this._where={},this.onWrite=s,this.onHooks=n}where(e){return this._where={...this._where,...e},this}async execute(){const e={table:this.tableName,where:this._where};await(this.onHooks?.("beforeDelete",[e]));const t=await this.engine.delete(this.tableName,e);return this.onWrite?.(this.tableName),await(this.onHooks?.("afterDelete",[e,t])),t}toAST(){return{type:"DELETE",from:this.tableName,where:this._where}}}class Re{constructor(e,t,s,n,i){this.schema=null,this.engine=e,this.name=t,this.executor=s,this.onWrite=n,this.onHooks=i}async getSchema(){if(!this.schema){const e=await this.engine.getTableSchema(this.name);if(!e)throw new n(`Table "${this.name}" does not exist`,"TABLE_NOT_FOUND");this.schema=e}return this.schema}async insert(e){await(this.onHooks?.("beforeInsert",[[e]]));const t=await this.engine.insert(this.name,[e]);return this.onWrite?.(this.name),await(this.onHooks?.("afterInsert",[[e],t])),t[0]}async insertMany(e){await(this.onHooks?.("beforeInsert",[e]));const t=await this.engine.insert(this.name,e);return this.onWrite?.(this.name),await(this.onHooks?.("afterInsert",[e,t])),t}select(e=["*"]){return new Se(this.engine,this.name,e,this.executor)}async stream(e,t={}){if("function"!=typeof this.engine.findStream){const s=await this.engine.find(this.name,{table:this.name,where:t.where,limit:t.limit,offset:t.offset,columns:t.columns});for(const t of s)e(t);return s.length}return this.engine.findStream(this.name,{table:this.name,where:t.where,limit:t.limit,offset:t.offset,columns:t.columns},e)}update(e){return new Ie(this.engine,this.name,e,this.onWrite,this.onHooks)}delete(){return new Ae(this.engine,this.name,this.onWrite,this.onHooks)}async count(e){return this.engine.count(this.name,e?{table:this.name,where:e}:void 0)}async clear(){await this.engine.clear(this.name),this.onWrite?.(this.name)}async drop(){await this.engine.dropTable(this.name),this.onWrite?.(this.name)}}function Ce(e){switch(e.type){case"SELECT":return function(e){return{table:e.from,columns:e.columns,where:e.where,orderBy:e.orderBy?.length?e.orderBy:void 0,limit:e.limit,offset:e.offset}}(e);case"DELETE":return function(e){return{table:e.from,where:e.where}}(e);case"UPDATE":return function(e){return{table:e.table,where:e.where}}(e);default:throw new n(`Cannot compile statement type "${e.type}" to QueryPlan`,"COMPILE_ERROR")}}var Ne;!function(e){e.SELECT="SELECT",e.FROM="FROM",e.WHERE="WHERE",e.INSERT="INSERT",e.INTO="INTO",e.VALUES="VALUES",e.UPDATE="UPDATE",e.SET="SET",e.DELETE="DELETE",e.CREATE="CREATE",e.TABLE="TABLE",e.DROP="DROP",e.ORDER="ORDER",e.BY="BY",e.ASC="ASC",e.DESC="DESC",e.LIMIT="LIMIT",e.OFFSET="OFFSET",e.AND="AND",e.OR="OR",e.NOT="NOT",e.LIKE="LIKE",e.IN="IN",e.PRIMARY="PRIMARY",e.KEY="KEY",e.UNIQUE="UNIQUE",e.DEFAULT="DEFAULT",e.NULL="NULL",e.TRUE="TRUE",e.REFERENCES="REFERENCES",e.CASCADE="CASCADE",e.BETWEEN="BETWEEN",e.IF="IF",e.EXISTS="EXISTS",e.FALSE="FALSE",e.ALTER="ALTER",e.ADD="ADD",e.TRUNCATE="TRUNCATE",e.INNER="INNER",e.LEFT="LEFT",e.RIGHT="RIGHT",e.CROSS="CROSS",e.JOIN="JOIN",e.ON="ON",e.AS="AS",e.OUTER="OUTER",e.GROUP="GROUP",e.HAVING="HAVING",e.COUNT="COUNT",e.SUM="SUM",e.AVG="AVG",e.MIN="MIN",e.MAX="MAX",e.DISTINCT="DISTINCT",e.BEGIN="BEGIN",e.COMMIT="COMMIT",e.ROLLBACK="ROLLBACK",e.UNION="UNION",e.ALL="ALL",e.INDEX="INDEX",e.CASE="CASE",e.WHEN="WHEN",e.THEN="THEN",e.ELSE="ELSE",e.END="END",e.EXPLAIN="EXPLAIN",e.ANALYZE="ANALYZE",e.REINDEX="REINDEX",e.VACUUM="VACUUM",e.SAVEPOINT="SAVEPOINT",e.RELEASE="RELEASE",e.TO="TO",e.IDENTIFIER="IDENTIFIER",e.STRING="STRING",e.NUMBER="NUMBER",e.COMMA="COMMA",e.LPAREN="LPAREN",e.RPAREN="RPAREN",e.SEMICOLON="SEMICOLON",e.EQ="EQ",e.NEQ="NEQ",e.GT="GT",e.GTE="GTE",e.LT="LT",e.LTE="LTE",e.STAR="STAR",e.DOT="DOT",e.EOF="EOF",e.ILLEGAL="ILLEGAL"}(Ne||(Ne={}));const Oe={SELECT:Ne.SELECT,FROM:Ne.FROM,WHERE:Ne.WHERE,INSERT:Ne.INSERT,INTO:Ne.INTO,VALUES:Ne.VALUES,UPDATE:Ne.UPDATE,SET:Ne.SET,DELETE:Ne.DELETE,CREATE:Ne.CREATE,TABLE:Ne.TABLE,DROP:Ne.DROP,ORDER:Ne.ORDER,BY:Ne.BY,ASC:Ne.ASC,DESC:Ne.DESC,LIMIT:Ne.LIMIT,OFFSET:Ne.OFFSET,AND:Ne.AND,OR:Ne.OR,NOT:Ne.NOT,LIKE:Ne.LIKE,IN:Ne.IN,PRIMARY:Ne.PRIMARY,KEY:Ne.KEY,UNIQUE:Ne.UNIQUE,DEFAULT:Ne.DEFAULT,NULL:Ne.NULL,TRUE:Ne.TRUE,FALSE:Ne.FALSE,REFERENCES:Ne.REFERENCES,CASCADE:Ne.CASCADE,BETWEEN:Ne.BETWEEN,IF:Ne.IF,EXISTS:Ne.EXISTS,ALTER:Ne.ALTER,ADD:Ne.ADD,TRUNCATE:Ne.TRUNCATE,INNER:Ne.INNER,LEFT:Ne.LEFT,RIGHT:Ne.RIGHT,CROSS:Ne.CROSS,JOIN:Ne.JOIN,ON:Ne.ON,AS:Ne.AS,OUTER:Ne.OUTER,GROUP:Ne.GROUP,HAVING:Ne.HAVING,COUNT:Ne.COUNT,SUM:Ne.SUM,AVG:Ne.AVG,MIN:Ne.MIN,MAX:Ne.MAX,DISTINCT:Ne.DISTINCT,BEGIN:Ne.BEGIN,COMMIT:Ne.COMMIT,ROLLBACK:Ne.ROLLBACK,UNION:Ne.UNION,ALL:Ne.ALL,INDEX:Ne.INDEX,CASE:Ne.CASE,WHEN:Ne.WHEN,THEN:Ne.THEN,ELSE:Ne.ELSE,END:Ne.END,EXPLAIN:Ne.EXPLAIN,ANALYZE:Ne.ANALYZE,REINDEX:Ne.REINDEX,VACUUM:Ne.VACUUM,SAVEPOINT:Ne.SAVEPOINT,RELEASE:Ne.RELEASE,TO:Ne.TO};class Le{constructor(e){this.position=0,this.readPosition=0,this.ch="",this.input=e,this.readChar()}nextToken(){let e;switch(this.skipWhitespace(),this.ch){case",":e=this.makeToken(Ne.COMMA,",");break;case"(":e=this.makeToken(Ne.LPAREN,"(");break;case")":e=this.makeToken(Ne.RPAREN,")");break;case";":e=this.makeToken(Ne.SEMICOLON,";");break;case"*":e=this.makeToken(Ne.STAR,"*");break;case".":e=this.makeToken(Ne.DOT,".");break;case"=":e=this.makeToken(Ne.EQ,"=");break;case"!":"="===this.peekChar()?(this.readChar(),e=this.makeToken(Ne.NEQ,"!=")):e=this.makeToken(Ne.ILLEGAL,"!");break;case">":"="===this.peekChar()?(this.readChar(),e=this.makeToken(Ne.GTE,">=")):e=this.makeToken(Ne.GT,">");break;case"<":"="===this.peekChar()?(this.readChar(),e=this.makeToken(Ne.LTE,"<=")):">"===this.peekChar()?(this.readChar(),e=this.makeToken(Ne.NEQ,"<>")):e=this.makeToken(Ne.LT,"<");break;case"'":case'"':e=this.readString(this.ch);break;case"":e={type:Ne.EOF,value:"",position:this.position};break;default:if("-"===this.ch&&"-"===this.peekChar())return this.skipLineComment(),this.nextToken();if("/"===this.ch&&"*"===this.peekChar())return this.skipBlockComment(),this.nextToken();if(this.isLetter(this.ch)){const t=this.readIdentifier();return e={type:Oe[t.toUpperCase()]??Ne.IDENTIFIER,value:t,position:this.position-t.length},e}if(this.isDigit(this.ch)||"-"===this.ch&&this.isDigit(this.peekChar())){const t=this.readNumber();return e={type:Ne.NUMBER,value:t,position:this.position-t.length},e}e=this.makeToken(Ne.ILLEGAL,this.ch)}return this.readChar(),e}readChar(){this.readPosition>=this.input.length?this.ch="":this.ch=this.input[this.readPosition],this.position=this.readPosition,this.readPosition++}peekChar(){return this.readPosition>=this.input.length?"":this.input[this.readPosition]}skipWhitespace(){for(;" "===this.ch||"\t"===this.ch||"\n"===this.ch||"\r"===this.ch;)this.readChar()}skipLineComment(){for(;"\n"!==this.ch&&"\r"!==this.ch&&""!==this.ch;)this.readChar()}skipBlockComment(){for(this.readChar(),this.readChar();""!==this.ch&&("*"!==this.ch||"/"!==this.peekChar());)this.readChar();""!==this.ch&&(this.readChar(),this.readChar())}readIdentifier(){const e=this.position;for(;this.isLetter(this.ch)||this.isDigit(this.ch)||"_"===this.ch;)this.readChar();return this.input.slice(e,this.position)}readNumber(){const e=this.position;for("-"===this.ch&&this.readChar();this.isDigit(this.ch);)this.readChar();if("."===this.ch&&this.isDigit(this.peekChar()))for(this.readChar();this.isDigit(this.ch);)this.readChar();return this.input.slice(e,this.position)}readString(e){const t=this.position+1;this.readChar();let s="";for(;""!==this.ch;){if(this.ch===e){if(this.peekChar()===e){s+=e,this.readChar(),this.readChar();continue}break}"\\"!==this.ch||this.peekChar()!==e?(s+=this.ch,this.readChar()):(this.readChar(),s+=e,this.readChar())}return{type:Ne.STRING,value:s,position:t}}isLetter(e){return/[a-zA-Z_]/.test(e)}isDigit(e){return/[0-9]/.test(e)}makeToken(e,t){return{type:e,value:t,position:this.position}}}class ve{constructor(e){this.sql=e,this.lexer=new Le(e),this.nextToken(),this.nextToken()}parseStatement(){switch(this.curToken.type){case Ne.SELECT:return this.parseSelect();case Ne.INSERT:return this.parseInsert();case Ne.UPDATE:return this.parseUpdate();case Ne.DELETE:return this.parseDelete();case Ne.CREATE:return this.parseCreateStatement();case Ne.DROP:return this.parseDropStatement();case Ne.ALTER:return this.parseAlterTable();case Ne.TRUNCATE:return this.parseTruncateTable();case Ne.BEGIN:return this.parseBegin();case Ne.COMMIT:return this.parseCommit();case Ne.ROLLBACK:return this.parseRollback();case Ne.EXPLAIN:return this.parseExplain();case Ne.ANALYZE:return this.parseAnalyze();case Ne.REINDEX:return this.parseReindex();case Ne.VACUUM:return this.parseVacuum();case Ne.SAVEPOINT:case Ne.RELEASE:return this.parseSavepoint();default:throw this.error(`Unexpected token "${this.curToken.value}"`)}}parseAllStatements(){const e=[];for(;!this.curTokenIs(Ne.EOF);){for(;this.curTokenIs(Ne.SEMICOLON);)this.nextToken();if(this.curTokenIs(Ne.EOF))break;if(e.push(this.parseStatement()),this.curTokenIs(Ne.SEMICOLON))this.nextToken();else if(!this.curTokenIs(Ne.EOF))throw this.error(`Expected ';' after statement, got "${this.curToken.value}"`)}return e}parseExplain(){if(this.expect(Ne.EXPLAIN),this.curTokenIs(Ne.EXPLAIN))throw this.error("Nested EXPLAIN is not allowed");return{type:"EXPLAIN",query:this.parseStatement()}}parseAnalyze(){return this.expect(Ne.ANALYZE),this._isKeywordAsIdent()&&"TABLE"===this.curToken.value.toUpperCase()&&this.nextToken(),{type:"ANALYZE",table:this.expectIdentifier("table name")}}parseReindex(){return this.expect(Ne.REINDEX),this._isKeywordAsIdent()&&"TABLE"===this.curToken.value.toUpperCase()&&this.nextToken(),{type:"REINDEX",table:this.expectIdentifier("table name")}}parseVacuum(){return this.expect(Ne.VACUUM),{type:"VACUUM"}}parseSavepoint(){let e;return this.curTokenIs(Ne.RELEASE)?(e="RELEASE",this.nextToken()):(e="SAVE",this.expect(Ne.SAVEPOINT)),this.curTokenIs(Ne.SAVEPOINT)&&this.nextToken(),{type:"SAVEPOINT",name:this.expectIdentifier("savepoint name"),action:e}}parseBegin(){return this.expect(Ne.BEGIN),this._isKeywordAsIdent()&&"TRANSACTION"===this.curToken.value.toUpperCase()&&this.nextToken(),{type:"BEGIN"}}parseCommit(){return this.expect(Ne.COMMIT),this._isKeywordAsIdent()&&"TRANSACTION"===this.curToken.value.toUpperCase()&&this.nextToken(),{type:"COMMIT"}}parseRollback(){return this.expect(Ne.ROLLBACK),this._isKeywordAsIdent()&&"TRANSACTION"===this.curToken.value.toUpperCase()?(this.nextToken(),{type:"ROLLBACK"}):this.curTokenIs(Ne.TO)||this._isKeywordAsIdent()&&"TO"===this.curToken.value.toUpperCase()?(this.nextToken(),(this.curTokenIs(Ne.SAVEPOINT)||this._isKeywordAsIdent()&&"SAVEPOINT"===this.curToken.value.toUpperCase())&&this.nextToken(),{type:"SAVEPOINT",name:this.expectIdentifier("savepoint name"),action:"ROLLBACK"}):{type:"ROLLBACK"}}parseCreateStatement(){if(this.expect(Ne.CREATE),this.curTokenIs(Ne.TABLE))return this.parseCreateTable();if(this.curTokenIs(Ne.INDEX)||this._isKeywordAsIdent()&&"INDEX"===this.curToken.value.toUpperCase())return this.parseCreateIndex();if(this.curTokenIs(Ne.UNIQUE)&&(this.nextToken(),this.curTokenIs(Ne.INDEX)||this._isKeywordAsIdent()&&"INDEX"===this.curToken.value.toUpperCase())){const e=this.parseCreateIndex();return e.unique=!0,e}throw this.error(`Expected TABLE or INDEX after CREATE, got "${this.curToken.value}"`)}parseCreateIndex(){this.expect(Ne.INDEX);const e=this.expectIdentifier("index name");this.expect(Ne.ON);const t=this.expectIdentifier("table name");this.expect(Ne.LPAREN);const s=this.expectIdentifier("column name");return this.expect(Ne.RPAREN),{type:"CREATE_INDEX",name:e,table:t,column:s}}parseDropStatement(){if(this.expect(Ne.DROP),this.curTokenIs(Ne.TABLE))return this.parseDropTable();if(this.curTokenIs(Ne.INDEX)||this._isKeywordAsIdent()&&"INDEX"===this.curToken.value.toUpperCase())return this.parseDropIndex();throw this.error(`Expected TABLE or INDEX after DROP, got "${this.curToken.value}"`)}parseDropIndex(){this.expect(Ne.INDEX);const e=this.expectIdentifier("index name");let t="",s="";return this.curTokenIs(Ne.ON)&&(this.nextToken(),t=this.expectIdentifier("table name"),this.curTokenIs(Ne.LPAREN)&&(this.nextToken(),s=this.expectIdentifier("column name"),this.expect(Ne.RPAREN))),{type:"DROP_INDEX",name:e,table:t,column:s}}parseSelect(){this.expect(Ne.SELECT);let e=!1;this.curTokenIs(Ne.DISTINCT)&&(e=!0,this.nextToken());const t=[];let s;this.curTokenIs(Ne.STAR)?(t.push("*"),this.nextToken()):t.push(...this.parseColumnList());let n,i="";this.curTokenIs(Ne.FROM)&&(this.nextToken(),this.curTokenIs(Ne.LPAREN)?(this.nextToken(),s=this.parseSelect(),this.expect(Ne.RPAREN),this.curTokenIs(Ne.AS)?(this.nextToken(),n=this.expectIdentifier("alias")):this.curToken.type!==Ne.IDENTIFIER||this._isReservedAfterFrom()||(n=this.curToken.value,this.nextToken())):(i=this.expectIdentifier("table name"),this.curTokenIs(Ne.AS)?(this.nextToken(),n=this.expectIdentifier("alias")):this.curToken.type!==Ne.IDENTIFIER||this._isReservedAfterFrom()||(n=this.curToken.value,this.nextToken())));const r={type:"SELECT",columns:t,distinct:e||void 0,from:i,alias:n,where:{}};s&&(r.fromSubquery=s);const a=this.parseJoinClauses();return a.length>0&&(r.joins=a),this.curTokenIs(Ne.WHERE)&&(this.nextToken(),r.where=this.parseCondition()),this.curTokenIs(Ne.GROUP)&&(this.nextToken(),this.expect(Ne.BY),r.groupBy=this.parseIdentifierList()),this.curTokenIs(Ne.HAVING)&&(this.nextToken(),r.having=this.parseCondition()),this.curTokenIs(Ne.ORDER)&&(this.nextToken(),this.expect(Ne.BY),r.orderBy=this.parseOrderByList()),this.curTokenIs(Ne.LIMIT)&&(this.nextToken(),r.limit=this.expectNumber("LIMIT value")),this.curTokenIs(Ne.OFFSET)&&(this.nextToken(),r.offset=this.expectNumber("OFFSET value")),this.curTokenIs(Ne.UNION)?this.parseUnion(r):r}parseUnion(e){this.expect(Ne.UNION);let t=!1;this.curTokenIs(Ne.ALL)&&(t=!0,this.nextToken());const s={type:"SELECT_UNION",left:e,right:this.parseSelect(),all:t||void 0};return this.curTokenIs(Ne.UNION)?this.parseUnionChain(s):s}parseUnionChain(e){this.expect(Ne.UNION);let t=!1;this.curTokenIs(Ne.ALL)&&(t=!0,this.nextToken());const s={type:"SELECT_UNION",left:e,right:this.parseSelect(),all:t||void 0};return this.curTokenIs(Ne.UNION)?this.parseUnionChain(s):s}parseJoinClauses(){const e=[];for(;this._isJoinKeyword();)e.push(this.parseJoinClause());return e}_isJoinKeyword(){return this.curTokenIs(Ne.INNER)||this.curTokenIs(Ne.LEFT)||this.curTokenIs(Ne.RIGHT)||this.curTokenIs(Ne.CROSS)||this.curTokenIs(Ne.JOIN)}parseJoinClause(){let e="INNER";this.curTokenIs(Ne.INNER)?(e="INNER",this.nextToken()):this.curTokenIs(Ne.LEFT)?(e="LEFT",this.nextToken(),this.curTokenIs(Ne.OUTER)&&this.nextToken()):this.curTokenIs(Ne.RIGHT)?(e="RIGHT",this.nextToken(),this.curTokenIs(Ne.OUTER)&&this.nextToken()):this.curTokenIs(Ne.CROSS)&&(e="CROSS",this.nextToken()),this.expect(Ne.JOIN);const t=this.expectIdentifier("table name");let s;this.curTokenIs(Ne.AS)?(this.nextToken(),s=this.expectIdentifier("alias")):this.curToken.type!==Ne.IDENTIFIER||this._isJoinReserved()||(s=this.curToken.value,this.nextToken());let n={};return"CROSS"!==e&&this.curTokenIs(Ne.ON)&&(this.nextToken(),n=this.parseCondition()),{type:e,table:t,alias:s,on:n}}_isReservedAfterFrom(){return this.curTokenIs(Ne.WHERE)||this.curTokenIs(Ne.ORDER)||this.curTokenIs(Ne.LIMIT)||this.curTokenIs(Ne.OFFSET)||this.curTokenIs(Ne.GROUP)||this._isJoinKeyword()}_isJoinReserved(){return this.curTokenIs(Ne.ON)||this.curTokenIs(Ne.WHERE)||this.curTokenIs(Ne.ORDER)||this.curTokenIs(Ne.LIMIT)||this._isJoinKeyword()}parseInsert(){this.expect(Ne.INSERT),this.expect(Ne.INTO);const e=this.expectIdentifier("table name");let t;if(this.curTokenIs(Ne.LPAREN)&&(this.nextToken(),t=this.parseIdentifierList(),this.expect(Ne.RPAREN)),this.curTokenIs(Ne.SELECT))return{type:"INSERT",into:e,columns:t,select:this.parseSelect()};this.expect(Ne.VALUES);const s=[];do{this.curTokenIs(Ne.COMMA)&&this.nextToken(),this.expect(Ne.LPAREN);const e=this.parseValueList();this.expect(Ne.RPAREN),s.push(e)}while(this.curTokenIs(Ne.COMMA));return{type:"INSERT",into:e,columns:t,values:s}}parseUpdate(){this.expect(Ne.UPDATE);const e=this.expectIdentifier("table name");this.expect(Ne.SET);const t={};do{this.curTokenIs(Ne.COMMA)&&this.nextToken();const e=this.expectIdentifier("column name");this.expect(Ne.EQ),t[e]=this.parseValue()}while(this.curTokenIs(Ne.COMMA));let s={};return this.curTokenIs(Ne.WHERE)&&(this.nextToken(),s=this.parseCondition()),{type:"UPDATE",table:e,sets:t,where:s}}parseDelete(){this.expect(Ne.DELETE),this.expect(Ne.FROM);const e=this.expectIdentifier("table name");let t={};return this.curTokenIs(Ne.WHERE)&&(this.nextToken(),t=this.parseCondition()),{type:"DELETE",from:e,where:t}}parseCreateTable(){this.expect(Ne.TABLE);let e=!1;this.curTokenIs(Ne.IF)&&(this.nextToken(),this.expect(Ne.NOT),this.expect(Ne.EXISTS),e=!0);const t=this.expectIdentifier("table name");this.expect(Ne.LPAREN);const s=[];do{this.curTokenIs(Ne.COMMA)&&this.nextToken(),s.push(this.parseColumnDef())}while(this.curTokenIs(Ne.COMMA));return this.expect(Ne.RPAREN),{type:"CREATE_TABLE",name:t,columns:s,ifNotExists:e||void 0}}parseColumnDef(){const e={name:this.expectIdentifier("column name"),type:this.expectIdentifier("column type").toLowerCase()};for(;this.curTokenIs(Ne.PRIMARY)||this.curTokenIs(Ne.UNIQUE)||this.curTokenIs(Ne.NOT)||this.curTokenIs(Ne.DEFAULT)||this.curTokenIs(Ne.REFERENCES);)if(this.curTokenIs(Ne.PRIMARY))this.nextToken(),this.expect(Ne.KEY),e.primaryKey=!0;else if(this.curTokenIs(Ne.UNIQUE))this.nextToken(),e.unique=!0;else if(this.curTokenIs(Ne.NOT))this.nextToken(),this.expect(Ne.NULL),e.required=!0;else if(this.curTokenIs(Ne.DEFAULT))this.nextToken(),e.default=this.parseValue();else{if(!this.curTokenIs(Ne.REFERENCES))break;{this.nextToken();const t=this.expectIdentifier("referenced table");this.expect(Ne.LPAREN);const s=this.expectIdentifier("referenced column");for(this.expect(Ne.RPAREN),e.references=`${t}.${s}`;this.curTokenIs(Ne.ON);)if(this.nextToken(),this.curTokenIs(Ne.DELETE))this.nextToken(),e.onDelete=this.parseCascadeAction();else{if(!this.curTokenIs(Ne.UPDATE))break;this.nextToken(),e.onUpdate=this.parseCascadeAction()}}}return e}parseCascadeAction(){return this.curTokenIs(Ne.CASCADE)?(this.nextToken(),"CASCADE"):this.curTokenIs(Ne.SET)?(this.nextToken(),this.expect(Ne.NULL),"SET NULL"):this.curToken.type===Ne.IDENTIFIER&&"RESTRICT"===this.curToken.value.toUpperCase()?(this.nextToken(),"RESTRICT"):"RESTRICT"}parseAlterTable(){this.expect(Ne.ALTER),this.expect(Ne.TABLE);const e=this.expectIdentifier("table name");let t;if(this.curTokenIs(Ne.ADD))return t="ADD",this.nextToken(),this.curToken.type===Ne.IDENTIFIER&&"COLUMN"===this.curToken.value.toUpperCase()&&this.nextToken(),{type:"ALTER_TABLE",name:e,action:t,column:this.parseColumnDef()};if(this.curTokenIs(Ne.DROP)||this._isKeywordAsIdent()&&"DROP"===this.curToken.value.toUpperCase())return t="DROP",this.nextToken(),this.curToken.type===Ne.IDENTIFIER&&"COLUMN"===this.curToken.value.toUpperCase()&&this.nextToken(),{type:"ALTER_TABLE",name:e,action:t,column:{name:this.expectIdentifier("column name"),type:"string"}};throw this.error("Expected ADD or DROP in ALTER TABLE")}parseTruncateTable(){return this.expect(Ne.TRUNCATE),this.expect(Ne.TABLE),{type:"TRUNCATE_TABLE",name:this.expectIdentifier("table name")}}parseDropTable(){this.expect(Ne.TABLE);let e=!1;return this.curTokenIs(Ne.IF)&&(this.nextToken(),this.expect(Ne.EXISTS),e=!0),{type:"DROP_TABLE",name:this.expectIdentifier("table name"),ifExists:e||void 0}}parseCondition(){let e=this.parseSimpleCondition();for(;this.curTokenIs(Ne.AND)||this.curTokenIs(Ne.OR);){const t=this.curTokenIs(Ne.AND);this.nextToken();const s=this.parseSimpleCondition();e=t?{$and:[e,s]}:{$or:[e,s]}}return e}parseWhere(){return this.parseCondition()}parseSimpleCondition(){if(this.curTokenIs(Ne.EXISTS)||this._isKeywordAsIdent()&&"EXISTS"===this.curToken.value.toUpperCase())return this.nextToken(),this.parseExistsCondition(!1);if(this.curTokenIs(Ne.NOT)&&this._peekIsExists())return this.nextToken(),this.nextToken(),this.parseExistsCondition(!0);if(this.curTokenIs(Ne.NOT)&&!this._isNotInOrLike())return this.nextToken(),{$not:this.parseSimpleCondition()};if(this.curTokenIs(Ne.LPAREN)){this.nextToken();const e=this.parseCondition();return this.expect(Ne.RPAREN),e}const e=this.parseColumnRef();if(this.curTokenIs(Ne.IDENTIFIER)&&"IS"===this.curToken.value.toUpperCase()){this.nextToken();const t=this.curTokenIs(Ne.NOT);t&&this.nextToken(),this.expect(Ne.NULL);const s={};return s[e]=t?{$ne:null}:{$eq:null},s}if(this.curTokenIs(Ne.BETWEEN)){this.nextToken();const t=this.parseValue();this.expect(Ne.AND);const s=this.parseValue(),n={};return n[e]={$gte:t,$lte:s},n}if(this.curTokenIs(Ne.NOT)&&this.peekTokenIs(Ne.BETWEEN)){this.nextToken(),this.nextToken();const t=this.parseValue();this.expect(Ne.AND);const s=this.parseValue(),n={};return n[e]={$not:{$gte:t,$lte:s}},n}if(this.curTokenIs(Ne.NOT)){if(this.peekTokenIs(Ne.IN)){if(this.nextToken(),this.nextToken(),this.expect(Ne.LPAREN),this.curTokenIs(Ne.SELECT)){const t=this.parseSelect();this.expect(Ne.RPAREN);const s={};return s[e]={$nin:{$subquery:t}},s}const t=this.parseValueList();this.expect(Ne.RPAREN);const s={};return s[e]={$nin:t},s}if(this.peekTokenIs(Ne.LIKE)){this.nextToken(),this.nextToken();const t=this.parseValue(),s={};return s[e]={$not:{$like:t}},s}}if(this.curTokenIs(Ne.LIKE)){this.nextToken();const t=this.parseValue(),s={};return s[e]={$like:t},s}if(this.curTokenIs(Ne.IN)){if(this.nextToken(),this.expect(Ne.LPAREN),this.curTokenIs(Ne.SELECT)){const t=this.parseSelect();this.expect(Ne.RPAREN);const s={};return s[e]={$in:{$subquery:t}},s}const t=this.parseValueList();this.expect(Ne.RPAREN);const s={};return s[e]={$in:t},s}if(this.curTokenIs(Ne.AND)||this.curTokenIs(Ne.OR)||this.curTokenIs(Ne.RPAREN)||this.curTokenIs(Ne.EOF)||this.curToken.type===Ne.IDENTIFIER&&["THEN","END","ELSE","NULLS","LIMIT","OFFSET","ORDER","GROUP","HAVING","UNION","WHERE"].includes(this.curToken.value.toUpperCase())){const t={};return t[e]={$eq:!0},t}const t=this.parseComparisonOp();if(this.curTokenIs(Ne.LPAREN)&&this.peekTokenIs(Ne.SELECT)){this.nextToken();const s=this.parseSelect();this.expect(Ne.RPAREN);const n={};return n[e]={[t]:{$subquery:s}},n}let s;s=(this.curToken.type===Ne.IDENTIFIER||this._isKeywordAsIdent())&&this.peekTokenIs(Ne.DOT)?{$col:this.parseColumnRef()}:this.parseValue();const n={};return n[e]={[t]:s},n}parseExistsCondition(e){this.expect(Ne.LPAREN);const t=this.parseSelect();return this.expect(Ne.RPAREN),{$exists:{$subquery:t,$negate:e||void 0}}}_peekIsExists(){return this.peekToken.type===Ne.EXISTS||this.peekToken.type===Ne.IDENTIFIER&&"EXISTS"===this.peekToken.value.toUpperCase()}_isNotInOrLike(){return this.peekTokenIs(Ne.IN)||this.peekTokenIs(Ne.LIKE)}peekTokenIs(e){return this.peekToken.type===e}parseComparisonOp(){switch(this.curToken.type){case Ne.EQ:return this.nextToken(),"$eq";case Ne.NEQ:return this.nextToken(),"$ne";case Ne.GT:return this.nextToken(),"$gt";case Ne.GTE:return this.nextToken(),"$gte";case Ne.LT:return this.nextToken(),"$lt";case Ne.LTE:return this.nextToken(),"$lte";default:throw this.error(`Expected comparison operator, got "${this.curToken.value}"`)}}parseColumnList(){const e=[];for(e.push(this.parseColumnWithAlias());this.curTokenIs(Ne.COMMA);)this.nextToken(),e.push(this.parseColumnWithAlias());return e}parseColumnWithAlias(){let e=this.parseColumnRef();if(this.curTokenIs(Ne.AS))this.nextToken(),e=`${e} AS ${this.expectIdentifier("alias")}`;else if(this.curToken.type===Ne.IDENTIFIER&&!this._isReservedAfterFrom()&&!this._isJoinKeyword()){const t=this.curToken.value;this.nextToken(),e=`${e} AS ${t}`}return e}parseColumnRef(){if(this.curTokenIs(Ne.CASE))return this.parseCaseExpressionText();if(this.curTokenIs(Ne.NUMBER)){const e=this.curToken.value;return this.nextToken(),e}if(this.curTokenIs(Ne.STRING)){const e=this.curToken.value;return this.nextToken(),`'${e}'`}if(this.curTokenIs(Ne.COUNT)||this.curTokenIs(Ne.SUM)||this.curTokenIs(Ne.AVG)||this.curTokenIs(Ne.MIN)||this.curTokenIs(Ne.MAX))return this.parseAggregateCall();const e=this.expectIdentifier("column name");return this.curTokenIs(Ne.DOT)?(this.nextToken(),`${e}.${this.expectIdentifier("column name")}`):e}parseCaseExpressionText(){const e=this.curToken.position;this.nextToken();let t=1,s=e+4;for(;!this.curTokenIs(Ne.EOF)&&t>0&&(this.curTokenIs(Ne.CASE)&&t++,!this.curTokenIs(Ne.END)||(t--,s=this.curToken.position+3,this.nextToken(),0!==t));)s=this.curToken.position+this.curToken.value.length,this.nextToken();let n=this.sql.slice(e,s);return this.curTokenIs(Ne.AS)?(this.nextToken(),n+=` AS ${this.expectIdentifier("alias")}`):this.curToken.type!==Ne.IDENTIFIER||this.curTokenIs(Ne.COMMA)||this._isReservedAfterFrom()||(n+=` AS ${this.curToken.value}`,this.nextToken()),n}parseAggregateCall(){const e=this.curToken.value.toUpperCase();this.nextToken(),this.expect(Ne.LPAREN);let t,s=!1;this.curTokenIs(Ne.DISTINCT)&&(s=!0,this.nextToken()),this.curTokenIs(Ne.STAR)?(t="*",this.nextToken()):t=this.parseColumnRef(),this.expect(Ne.RPAREN);let n="";this.curTokenIs(Ne.AS)?(this.nextToken(),n=this.expectIdentifier("alias")):this.curToken.type===Ne.IDENTIFIER&&this._isAggregateAlias()&&(n=this.curToken.value,this.nextToken());const i=s?`DISTINCT ${t}`:t;return n?`${e}(${i}) AS ${n}`:`${e}(${i})`}_isAggregateAlias(){return!this._isReservedAfterFrom()&&!this._isJoinKeyword()}parseIdentifierList(){const e=[];for(e.push(this.parseIdentifierWithDot());this.curTokenIs(Ne.COMMA);)this.nextToken(),e.push(this.parseIdentifierWithDot());return e}parseValueList(){const e=[];for(e.push(this.parseValue());this.curTokenIs(Ne.COMMA);)this.nextToken(),e.push(this.parseValue());return e}parseOrderByList(){const e=[];for(e.push(this.parseOrderBy());this.curTokenIs(Ne.COMMA);)this.nextToken(),e.push(this.parseOrderBy());return e}parseOrderBy(){const e=this.parseIdentifierWithDot();let t,s="asc";return this.curTokenIs(Ne.ASC)?this.nextToken():this.curTokenIs(Ne.DESC)&&(s="desc",this.nextToken()),this.curTokenIs(Ne.IDENTIFIER)&&"NULLS"===this.curToken.value.toUpperCase()&&(this.nextToken(),this.curTokenIs(Ne.IDENTIFIER)&&"FIRST"===this.curToken.value.toUpperCase()?(t="first",this.nextToken()):this.curTokenIs(Ne.IDENTIFIER)&&"LAST"===this.curToken.value.toUpperCase()&&(t="last",this.nextToken())),{column:e,direction:s,...t?{nulls:t}:{}}}parseIdentifierWithDot(){const e=this.expectIdentifier("identifier");return this.curTokenIs(Ne.DOT)?(this.nextToken(),`${e}.${this.expectIdentifier("identifier")}`):e}parseValue(){switch(this.curToken.type){case Ne.STRING:{const e=this.curToken.value;return this.nextToken(),e}case Ne.NUMBER:{const e=Number(this.curToken.value);return this.nextToken(),e}case Ne.TRUE:return this.nextToken(),!0;case Ne.FALSE:return this.nextToken(),!1;case Ne.NULL:return this.nextToken(),null;default:throw this.error(`Expected value, got "${this.curToken.value}"`)}}nextToken(){this.curToken=this.peekToken,this.peekToken=this.lexer.nextToken()}curTokenIs(e){return this.curToken.type===e}expect(e){if(!this.curTokenIs(e))throw this.error(`Expected ${e}, got "${this.curToken.value}"`);this.nextToken()}expectIdentifier(e){if(this.curToken.type===Ne.IDENTIFIER||this._isKeywordAsIdent()){const e=this.curToken.value;return this.nextToken(),e}throw this.error(`Expected ${e}, got "${this.curToken.value}"`)}_isKeywordAsIdent(){return this.curToken.type!==Ne.EOF&&this.curToken.type!==Ne.ILLEGAL&&this.curToken.type!==Ne.STRING&&this.curToken.type!==Ne.NUMBER&&this.curToken.type!==Ne.COMMA&&this.curToken.type!==Ne.LPAREN&&this.curToken.type!==Ne.RPAREN&&this.curToken.type!==Ne.SEMICOLON&&this.curToken.type!==Ne.EQ&&this.curToken.type!==Ne.NEQ&&this.curToken.type!==Ne.GT&&this.curToken.type!==Ne.GTE&&this.curToken.type!==Ne.LT&&this.curToken.type!==Ne.LTE&&this.curToken.type!==Ne.DOT&&this.curToken.type!==Ne.STAR}expectNumber(e){if(this.curToken.type===Ne.NUMBER){const e=Number(this.curToken.value);return this.nextToken(),e}throw this.error(`Expected ${e}, got "${this.curToken.value}"`)}error(e){return new n(`Parse error at position ${this.curToken.position}: ${e}`,"PARSE_ERROR")}}function $e(e){return new ve(e).parseAllStatements()}function De(e){return new ve(e).parseWhere()}function _e(e){const t=e.match(/^\s*CASE\s+([\s\S]*?)\s+END\s*(?:AS\s+(\w+))?\s*$/i);if(!t)return null;const s=t[1],n=t[2]??null,i=[],r=/WHEN\s+([\s\S]*?)\s+THEN\s+([\s\S]*?)(?=\s+WHEN\s+|\s+ELSE\s+|\s*$)/gi;let a;for(;null!==(a=r.exec(s));){let e=null;try{e=De(a[1].trim())}catch{}i.push({cond:e,value:a[2].trim()})}let o=null;const h=s.match(/\sELSE\s+([\s\S]*)$/i);return h&&(o=h[1].trim()),{whens:i,elseValue:o,alias:n}}function Ue(e,t){const s=e.trim();if("null"===s)return null;if("true"===s)return!0;if("false"===s)return!1;const n=Number(s);if(""!==s&&!isNaN(n))return n;const i=s.match(/^'(.*)'$/s)||s.match(/^"(.*)"$/s);return i?i[1]:/^[a-zA-Z_][a-zA-Z0-9_.]*$/.test(s)?t[s]??null:s}function Me(e,t){for(const{cond:s,value:n}of e.whens)if(s&&a(t,s))return Ue(n,t);return null!==e.elseValue?Ue(e.elseValue,t):null}class Be{constructor(e,t=0){this.engine=e,this.maxRowsPerQuery=t}async execute(e){switch(e.type){case"SELECT":return this.executeSelect(e);case"SELECT_UNION":return this.executeSelectUnion(e);case"EXPLAIN":return this.executeExplain(e);case"INSERT":return this.executeInsert(e);case"UPDATE":return this.executeUpdate(e);case"DELETE":return this.executeDelete(e);case"CREATE_TABLE":return this.executeCreateTable(e);case"DROP_TABLE":return this.executeDropTable(e);case"ALTER_TABLE":return this.executeAlterTable(e);case"TRUNCATE_TABLE":return this.executeTruncateTable(e);case"CREATE_INDEX":return this.executeCreateIndex(e);case"DROP_INDEX":return this.executeDropIndex(e);case"BEGIN":return this.executeBegin();case"COMMIT":return this.executeCommit();case"ROLLBACK":return this.executeRollback();case"SAVEPOINT":return this.executeSavepoint(e);case"ANALYZE":return this.executeAnalyze(e);case"REINDEX":return this.executeReindex(e);case"VACUUM":return this.executeVacuum();default:throw new n("Unknown statement type","UNKNOWN_STATEMENT")}}async executeSelectUnion(e){const t=await this.executeSelectPart(e.left),s=await this.executeSelectPart(e.right),n=t.length>0?Object.keys(t[0]):[],i=t.map(e=>e);if(e.all){for(const e of s)i.push(this.projectUnionRow(e,n));return i}const r=new Set,a=[];for(const e of i){const t=Object.values(e).map(e=>String(e??"\0")).join("");r.has(t)||(r.add(t),a.push(e))}for(const e of s){const t=this.projectUnionRow(e,n),s=Object.values(t).map(e=>String(e??"\0")).join("");r.has(s)||(r.add(s),a.push(t))}return a}async executeSelectPart(e){return"SELECT_UNION"===e.type?this.executeSelectUnion(e):this.executeSelect(e)}projectUnionRow(e,t){if(0===t.length)return e;const s=Object.values(e),n={};for(let e=0;e0),s=!t&&this._hasAggregateColumn(e.columns);let n;const i=!!(e.joins&&e.joins.length>0),r=this.hasCaseColumn(e.columns)||!!e.where&&this.whereHasCase(e.where),o=this.orderByUsesSelectAlias(e),h=e.columns.some(e=>/\s+AS\s+\w+$/i.test(e));if(e.fromSubquery){const t=await this.executeSelectPart(e.fromSubquery);n=i?await this.executeJoinSelect(e,t.map(t=>this.prefixRow(t,e.alias??""))):t,!i&&e.where&&Object.keys(e.where).length>0&&(e.where=await this.resolveSubqueries(e.where),n=n.filter(t=>a(t,e.where)))}else if(e.from||i)if(i)n=await this.executeJoinSelect(e);else{e.where&&Object.keys(e.where).length>0&&(e.where=this.normalizeWhereColumns(e.where,[e.alias??e.from]));const i=[e.alias??e.from].filter(Boolean);if(e.orderBy&&e.orderBy.length>0&&(e.orderBy=e.orderBy.map(e=>({...e,column:this.stripAlias(e.column,i)}))),e.groupBy&&e.groupBy.length>0&&(e.groupBy=e.groupBy.map(e=>this.stripAlias(e,i))),e.columns=e.columns.map(e=>{if("*"===e||/^(COUNT|SUM|AVG|MIN|MAX)\(/i.test(e)||/^\s*CASE\b/i.test(e)||/^'/.test(e))return e;const t=e.match(/^(.+?)\s+AS\s+(\w+)$/i);if(t){const s=this.stripAlias(t[1].trim(),i);return s===t[1].trim()?e:`${s} AS ${t[2]}`}return this.stripAlias(e,i)}),e.where&&this.hasCorrelatedRefs(e.where)){const i=Ce(t||s?{...e,columns:["*"]}:e);i.columns=["*"],o&&(i.orderBy=void 0,i.limit=void 0,i.offset=void 0),n=await this.engine.find(i.table,{...i,where:this.stripCorrelatedExists(e.where)}),n=await this.filterCorrelated(n,e.where)}else{e.where&&Object.keys(e.where).length>0&&(e.where=await this.resolveSubqueries(e.where));const i=Ce(t||s?{...e,columns:["*"]}:e);(r||h)&&(i.columns=["*"]),o&&(i.orderBy=void 0,i.limit=void 0,i.offset=void 0),n=await this.engine.find(i.table,i)}}else n=[{}];if(s&&(n=[this.computeSingleAggregate(n,e)]),t&&(n=this.executeGroupBy(n,e)),e.distinct&&(n=this.executeDistinct(n)),e.having&&Object.keys(e.having).length>0){e.having=await this.resolveSubqueries(e.having);const t=e._aggAliasMap;if(t&&t.size>0){const s={};for(const[n,i]of Object.entries(e.having))s[t.get(n)??n]=i;e.having=s}n=n.filter(t=>a(t,e.having))}e.orderBy&&e.orderBy.length>0&&(n=c(n,e.orderBy)),!t&&!s&&e.columns.length>0&&"*"!==e.columns[0]&&(n=n.map(t=>this.projectRow(t,e.columns))),o&&e.orderBy&&e.orderBy.length>0&&(n=c(n,e.orderBy));const l=e.offset??0,u=e.limit??n.length;return n=n.slice(l,l+u),this.maxRowsPerQuery>0&&n.length>this.maxRowsPerQuery&&(n=n.slice(0,this.maxRowsPerQuery)),n}async executeJoinSelect(e,t){const s=e.alias??e.from;let n;if(t)n=t;else{const{pushable:t}=this.extractPushableWhere(e.where??{},s);n=(await this.engine.find(e.from,{table:e.from,where:Object.keys(t).length>0?t:void 0})).map(e=>this.prefixRow(e,s))}let i=n;for(const t of e.joins){const e=t.alias??t.table,n=await this.tryHashJoin(i,t,e,s);if(n){i=n;continue}const r=(await this.engine.find(t.table,{table:t.table})).map(t=>this.prefixRow(t,e));i=this.joinRows(i,r,t)}return e.where&&Object.keys(e.where).length>0&&(this.hasCorrelatedRefs(e.where)?i=await this.filterCorrelated(i,e.where):(e.where=await this.resolveSubqueries(e.where),i=i.filter(t=>a(t,e.where)))),i}prefixRow(e,t){const s={};for(const[n,i]of Object.entries(e))s[`${t}.${n}`]=i;return s}extractPushableWhere(e,t){const s={};if(!t)return{pushable:s};const n=`${t}.`;for(const[t,i]of Object.entries(e))t.startsWith(n)&&("object"==typeof i&&null!==i&&("$col"in i||"$subquery"in i||"$and"in i||"$or"in i||"$not"in i)||(s[t.slice(n.length)]=i));return{pushable:s}}async tryHashJoin(e,t,s,n){if("CROSS"===t.type||"RIGHT"===t.type)return null;const i=[],r=e=>{for(const[t,s]of Object.entries(e)){if("$and"===t){if(!s.every(r))return!1;continue}if("$or"===t||"$not"===t)return!1;let e=null;if("object"==typeof s&&null!==s){const t=s;"$eq"in t&&"object"==typeof t.$eq&&null!==t.$eq&&"$col"in t.$eq?e=String(t.$eq.$col):"$col"in t&&1===Object.keys(t).length&&(e=String(t.$col))}if(!e)return!1;const a=!!n&&t.startsWith(`${n}.`);i.push({leftCol:a?t:e,rightCol:a?e:t})}return!0};if(!r(t.on))return null;if(0===i.length)return null;const a=await this.engine.getTableSchema(t.table);if(!a)return null;const o=i.find(e=>{const t=e.rightCol.split(".").pop(),s=a.columns[t];return s&&(s.primaryKey||s.index||s.unique)});if(!o)return null;const h=o.rightCol.split(".").pop(),c=Array.from(new Set(e.map(e=>e[o.leftCol]).filter(e=>null!=e)));if(0===c.length)return null;const l=await this.engine.find(t.table,{table:t.table,where:{[h]:{$in:c}}}),u=new Map;for(const e of l){const t=i.map(t=>String(e[t.rightCol.split(".").pop()]??"\0")).join("");u.has(t)||u.set(t,[]),u.get(t).push(e)}const f={};for(const e of Object.keys(a.columns))f[e]=null;const d=[];for(const n of e){const e=i.map(e=>String(n[e.leftCol]??"\0")).join(""),r=u.get(e);if(r&&r.length>0)for(const e of r)d.push({...n,...this.prefixRow(e,s)});else"LEFT"===t.type&&d.push({...n,...this.prefixRow(f,s)})}return d}joinRows(e,t,s){if("CROSS"===s.type){const s=[];for(const n of e)for(const e of t)s.push({...n,...e});return s}const n=[];for(const i of e){let e=!1;for(const r of t){const t={...i,...r};a(t,s.on,{$col:!0})&&(n.push(t),e=!0)}if(!e&&"LEFT"===s.type){const e={};for(const s of Object.keys(t[0]??{}))e[s]=null;n.push({...i,...e})}}if("RIGHT"===s.type)for(const i of t)if(!e.some(e=>a({...e,...i},s.on,{$col:!0}))){const t={};for(const s of Object.keys(e[0]??{}))t[s]=null;n.push({...t,...i})}return n}executeGroupBy(e,t){const s=new Map;for(const n of e){const e=t.groupBy.map(e=>String(n[e]??"null")).join("|");s.has(e)||s.set(e,[]),s.get(e).push(n)}const n=[],i=new Map;for(const e of s.values()){const s={};for(const n of t.groupBy)s[n]=e[0][n];for(const n of t.columns){if("*"===n)continue;const r=n.match(/^(COUNT|SUM|AVG|MIN|MAX)\((.+?)\)(?:\s+AS\s+(\w+))?$/i);if(r){const[,t,a,o]=r,h=this.computeAggregate(t.toUpperCase(),e,a.trim()),c=`${t.toUpperCase()}(${a.trim()})`,l=o||n;l!==c&&i.set(c,l),s[l]=h}else if(/^\s*CASE\b/i.test(n)){const t=_e(n);s[t?.alias??n]=t?Me(t,e[0]):null}else t.groupBy.includes(n)||(s[n]=e[0][n])}n.push(s)}return t._aggAliasMap=i,n}computeAggregate(e,t,s){const n=/^\s*CASE\b/i.test(s)?_e(s):null,i=!n&&/^\s*DISTINCT\s+/i.test(s),r=i?s.replace(/^\s*DISTINCT\s+/i,"").trim():s,a=t.map(e=>n?Me(n,e):e[r]).filter(e=>null!=e);if("COUNT"===e)return"*"===r?t.length:i?new Set(a.map(e=>"object"==typeof e?JSON.stringify(e):String(e))).size:a.length;const o=a.map(Number),h=i?Array.from(new Set(o)):o;switch(e){case"SUM":return h.reduce((e,t)=>e+t,0);case"AVG":return 0===h.length?0:h.reduce((e,t)=>e+t,0)/h.length;case"MIN":return 0===h.length?0:Math.min(...h);case"MAX":return 0===h.length?0:Math.max(...h);default:return 0}}executeDistinct(e){const t=new Set;return e.filter(e=>{const s=Object.values(e).map(e=>String(e??"\0")).join("");return!t.has(s)&&(t.add(s),!0)})}async executeInsert(e){const t=await this.engine.getTableSchema(e.into);if(!t)throw new n(`Table "${e.into}" does not exist`,"TABLE_NOT_FOUND");const s=e.columns??Object.keys(t.columns);if(e.select){const t=await this.executeSelectPart(e.select);let n=[];const i=e.select;if("SELECT"===i.type)if(i.columns&&i.columns.length>0&&"*"!==i.columns[0])n=i.columns.map(e=>e.split(".").pop());else if(i.from){const e=await this.engine.getTableSchema(i.from);n=e?Object.keys(e.columns):[]}0===n.length&&t.length>0&&(n=Object.keys(t[0]));const r=t.map(e=>{const t={};for(let i=0;i{const t={};for(let n=0;n/^\s*CASE\b/i.test(e))}orderByUsesSelectAlias(e){if(!e.orderBy||0===e.orderBy.length)return!1;const t=new Set;for(const s of e.columns){const e=s.match(/\s+AS\s+(\w+)$/i);if(e)t.add(e[1]);else if(/^\s*CASE\b/i.test(s)){const e=_e(s);e?.alias&&t.add(e.alias)}}return 0!==t.size&&e.orderBy.some(e=>t.has(e.column))}whereHasCase(e){for(const[t,s]of Object.entries(e))if("$and"!==t&&"$or"!==t){if("$not"!==t){if(/^\s*CASE\b/i.test(t))return!0}else if(this.whereHasCase(s))return!0}else if(s.some(e=>this.whereHasCase(e)))return!0;return!1}getEngine(){return this.engine}projectRow(e,t){const s=[],n=[],i=[],r=[];for(const e of t){if("*"===e)continue;const t=_e(e);if(t){i.push({alias:t.alias??e,expr:t});continue}const a=e.match(/^(.+?)\s+AS\s+(\w+)$/i);if(a){n.push({alias:a[2],source:a[1].trim()});continue}const o=e.match(/^'(.*)'$/s);if(o){const t=o[1].replace(/\\'/g,"'");r.push({key:e,value:t});continue}s.push(e)}const a=s.length>0?u(e,s):{};for(const{alias:t,source:s}of n)if("*"===s)Object.assign(a,e);else{const n=s.match(/^'(.*)'$/s);a[t]=n?n[1].replace(/\\'/g,"'"):e[s]}for(const{key:e,value:t}of r)a[e]=t;for(const{alias:t,expr:s}of i)a[t]=Me(s,e);return a}_hasAggregateColumn(e){return e.some(e=>/^(COUNT|SUM|AVG|MIN|MAX)\(/i.test(e))}computeSingleAggregate(e,t){const s={};for(const n of t.columns){if("*"===n)continue;const t=n.match(/^(COUNT|SUM|AVG|MIN|MAX)\((.+?)\)(?:\s+AS\s+(\w+))?$/i);if(t){const[,i,r,a]=t;s[a||n]=this.computeAggregate(i.toUpperCase(),e,r.trim())}else s[n]=e.length>0?e[0][n]:null}return s}normalizeWhereColumns(e,t){const s={};for(const[n,i]of Object.entries(e))"$and"!==n&&"$or"!==n?"$not"!==n?"$exists"!==n?s[this.stripAlias(n,t)]=this.normalizeFieldValue(i,t):s[n]=this.normalizeExistsValue(i,t):s.$not=this.normalizeWhereColumns(i,t):s[n]=i.map(e=>this.normalizeWhereColumns(e,t));return s}normalizeExistsValue(e,t){if("object"!=typeof e||null===e)return e;const s=e;if(s.$subquery){const e=s.$subquery,n=[e.alias??e.from,...t].filter(Boolean);return{...s,$subquery:{...e,where:this.normalizeWhereColumns(e.where,n)}}}return e}normalizeFieldValue(e,t){if("object"!=typeof e||null===e||Array.isArray(e))return e;const s={};for(const[n,i]of Object.entries(e))"$and"===n||"$or"===n?s[n]=i.map(e=>this.normalizeWhereColumns(e,t)):"$not"===n&&"object"==typeof i&&null!==i?s[n]=this.normalizeFieldValue(i,t):"$col"===n?s[n]=this.stripAlias(String(i),t):"object"==typeof i&&null!==i&&!Array.isArray(i)&&"$col"in i?s[n]={$col:this.stripAlias(String(i.$col),t)}:s[n]=i;return s}stripAlias(e,t){for(const s of t){if(!s)continue;const t=`${s}.`;if(e.startsWith(t))return e.slice(t.length)}return e}hasCorrelatedRefs(e){for(const[t,s]of Object.entries(e))if("$and"!==t&&"$or"!==t){if("$not"!==t){if("$exists"!==t){if(/^\s*CASE\b/i.test(t))return!0;if(this.fieldHasColRef(s))return!0}else if("object"==typeof s&&null!==s&&"$subquery"in s)return!0}else if(this.hasCorrelatedRefs(s))return!0}else if(s.some(e=>this.hasCorrelatedRefs(e)))return!0;return!1}fieldHasColRef(e){if("object"!=typeof e||null===e||Array.isArray(e))return!1;const t=e;if("$col"in t)return!0;if("$and"in t||"$or"in t)return(t.$and??t.$or).some(e=>this.hasCorrelatedRefs(e));if("$not"in t&&"object"==typeof t.$not&&null!==t.$not)return this.fieldHasColRef(t.$not);for(const[,e]of Object.entries(t))if("object"==typeof e&&null!==e&&!Array.isArray(e)){if("$col"in e)return!0;if(this.fieldHasColRef(e))return!0}return!1}stripCorrelatedExists(e){const t={};for(const[s,n]of Object.entries(e))if("$and"!==s&&"$or"!==s){if("$not"===s){const e=this.stripCorrelatedExists(n);Object.keys(e).length>0&&(t.$not=e);continue}"$exists"!==s&&(/^\s*CASE\b/i.test(s)||(t[s]=n))}else t[s]=n.map(e=>this.stripCorrelatedExists(e));return t}async filterCorrelated(e,t){const s=[];for(const n of e){let e=this.resolveCaseKeys(t,n);e=await this.resolveSubqueries(e,n),a(n,e)&&s.push(n)}return s}resolveCaseKeys(e,t){const s={};for(const[n,i]of Object.entries(e))if("$and"!==n&&"$or"!==n)if("$not"!==n){if(/^\s*CASE\b/i.test(n)){const e=_e(n);if(!e)continue;const r=Me(e,t);if(!this.caseConditionMatches(r,i))return{$caseResult:!1};s.$caseResult=!0;continue}s[n]=i}else s.$not=this.resolveCaseKeys(i,t);else s[n]=i.map(e=>this.resolveCaseKeys(e,t));return s}caseConditionMatches(e,t){if("object"!=typeof t||null===t||Array.isArray(t))return e===t;const s=t;for(const[t,n]of Object.entries(s))switch(t){case"$eq":if(e!==n)return!1;break;case"$ne":if(e===n)return!1;break;case"$gt":if(!(e>n))return!1;break;case"$gte":if(!(e>=n))return!1;break;case"$lt":if(!(ethis.bindWhereRefs(e,t)):"$not"===n&&"object"==typeof i&&null!==i?s[n]=this.bindColumnRefs(i,t):"object"==typeof i&&null!==i&&!Array.isArray(i)&&"$col"in i?s[n]=t[String(i.$col)]??null:s[n]=i;return s}bindWhereRefs(e,t){const s={};for(const[n,i]of Object.entries(e))"$and"===n||"$or"===n?s[n]=i.map(e=>this.bindWhereRefs(e,t)):"$not"===n?s.$not=this.bindWhereRefs(i,t):s[n]="$exists"===n?i:this.bindColumnRefs(i,t);return s}async resolveSubqueries(e,t){t&&(e=this.bindWhereRefs(e,t));const s={};for(const[n,i]of Object.entries(e)){if("$exists"===n&&"object"==typeof i&&null!==i){const e=i,n=e.$subquery,r=!!e.$negate;let a=n.where;this.hasCorrelatedRefs(a)&&(a=this.bindWhereRefs(a,t??{}));const o=await this.executeSelectPart({...n,where:a});s.$exists=o.length>0!==r;continue}"$and"===n&&Array.isArray(i)?s.$and=await Promise.all(i.map(e=>this.resolveSubqueries(e,t))):"$or"===n&&Array.isArray(i)?s.$or=await Promise.all(i.map(e=>this.resolveSubqueries(e,t))):"$not"!==n||"object"!=typeof i||null===i?s[n]="object"==typeof i&&null!==i?await this.resolveOperatorSubqueries(i):i:s.$not=await this.resolveSubqueries(i,t)}return s}async resolveOperatorSubqueries(e){const t={};for(const[s,n]of Object.entries(e))if("$and"===s&&Array.isArray(n))t.$and=await Promise.all(n.map(e=>this.resolveSubqueries(e)));else if("$or"===s&&Array.isArray(n))t.$or=await Promise.all(n.map(e=>this.resolveSubqueries(e)));else if("$not"!==s)if("object"==typeof n&&null!==n&&"$subquery"in n){const e=n.$subquery,i=await this.executeSelect(e);if("$in"===s||"$nin"===s){const e=Object.keys(i[0]||{})[0],n=i.map(t=>t[e]);t[s]=n}else if(0===i.length)t[s]=null;else{const e=Object.keys(i[0])[0];t[s]=i[0][e]}}else t[s]=n;else t.$not="object"==typeof n&&null!==n?await this.resolveOperatorSubqueries(n):n;return t}}class Pe{constructor(e){this.tables=new Map,this.completed=!1,this.engine=e}table(e){let t=this.tables.get(e);return t||(t=new Re(this.engine,e),this.tables.set(e,t)),t}_markCompleted(){this.completed=!0}isCompleted(){return this.completed}}class Ke{constructor(e){this.engine=e}async execute(e){const t=new Pe(this.engine);await this.engine.beginTransaction();try{const s=await e(t);return await this.engine.commitTransaction(),t._markCompleted(),s}catch(e){if(await this.engine.rollbackTransaction(),e instanceof n)throw e;throw new n(`Transaction failed: ${e.message}`,"TRANSACTION_ERROR",e)}}}class ze{constructor(){this.plugins=[],this.hooks=new Map}register(e,t){const s=e.priority??0,n=this.plugins.findIndex(e=>(e.priority??0)t.name===e);-1!==t&&(this.plugins[t].destroy(),this.plugins.splice(t,1))}getPlugins(){return[...this.plugins]}on(e,t){const s=this.hooks.get(e)??[];s.push(t),this.hooks.set(e,s)}off(e,t){const s=this.hooks.get(e);if(s){const e=s.indexOf(t);-1!==e&&s.splice(e,1)}}async trigger(e,...t){const s=this.hooks.get(e);if(s)for(const e of s)await e(...t)}destroy(){for(const e of this.plugins)try{e.destroy()}catch(e){}this.plugins=[],this.hooks.clear()}}class Fe{get version(){return this._version}get maxRowsPerQuery(){return this.config.maxRowsPerQuery??0}get debug(){return this.config.debug??!1}constructor(e){this.ready=!1,this.tableCache=new Map,this.channel=null,this.listeners=new Map,this.migrations=new Map,this.config=e,this.name=e.name??s.name,this.mode=e.mode??s.mode,this._version=e.version??s.version,this.pluginManager=new ze,e.multiTabSync&&"undefined"!=typeof BroadcastChannel&&(this.channel=new BroadcastChannel(`metona-sqlark:${this.name}`),this.channel.onmessage=e=>{const t=e.data;t&&"change"===t.type&&(this.emit(t.table??"",{type:"external",table:t.table??""}),this.engine instanceof xe&&this.engine.reloadMemoryFromDisk().catch(()=>{}))})}async init(){if(this.engine=this.createEngine(),await this.engine.open(this.name,this.version),"function"==typeof this.engine.getMeta)try{const e=await this.engine.getMeta("__metona_version");null!=e&&Number(e)>=1&&(this._version=Math.max(this._version,Math.floor(Number(e))))}catch{}if(this.executor=new Be(this.engine,this.maxRowsPerQuery),this.transactionManager=new Ke(this.engine),this.config.plugins)for(const e of this.config.plugins)this.pluginManager.register(e,this);this.ready=!0,this.config.onReady&&this.config.onReady(this)}isReady(){return this.ready}async defineTable(e,t){this.ensureReady();const s=$(e,t);try{await this.pluginManager.trigger("beforeCreateTable",s),await this.engine.createTable(s),await this.pluginManager.trigger("afterCreateTable",s)}catch(e){throw this._onError(e),e}this.tableCache.delete(e)}table(e){this.ensureReady();let t=this.tableCache.get(e);return t||(t=new Re(this.engine,e,this.executor,e=>this.broadcastChange(e),(e,t)=>this.pluginManager.trigger(e,...t)),this.tableCache.set(e,t)),t}async dropTable(e){this.ensureReady();try{await this.pluginManager.trigger("beforeDropTable",e),await this.engine.dropTable(e),await this.pluginManager.trigger("afterDropTable",e)}catch(e){throw this._onError(e),e}this.tableCache.delete(e)}async getTableNames(){return this.ensureReady(),this.engine.getTableNames()}async query(e){this.ensureReady();const t=this.debug?Date.now():0;let s;await this.pluginManager.trigger("beforeQuery",e);try{const t=$e(e);for(const e of t){await this.triggerStatementHooks(e,"before"),s=await this.executor.execute(e),await this.triggerStatementHooks(e,"after",s);const t=this.writeStatementTable(e);t&&this.broadcastChange(t)}}catch(e){throw this._onError(e),e}if(await this.pluginManager.trigger("afterQuery",e,s),this.debug){const n=Date.now()-t,i=Array.isArray(s)?s.length:0;this._debug(`query [${n}ms] ${i} rows: ${e.slice(0,100)}`)}return s}async queryStream(e,t){this.ensureReady();const s=$e(e)[0];if(!s||"SELECT"!==s.type)throw new n("queryStream only supports SELECT statements","NOT_SUPPORTED");const i=s,r=i.columns.some(e=>/^(COUNT|SUM|AVG|MIN|MAX)\(/i.test(e));if(!(i.joins||i.groupBy||i.having||i.distinct||r||i.orderBy&&i.orderBy.length>0||i.where&&void 0!==i.where.$exists)&&"function"==typeof this.engine.findStream&&"AsyncFunction"!==t.constructor?.name){const e=this.normalizeWhereForStream(i),s=i.columns.filter(e=>!/\s+AS\s+\w+$/i.test(e));return this.engine.findStream(i.from,{table:i.from,columns:s.length>0&&"*"!==s[0]?s:["*"],where:e&&Object.keys(e).length>0?e:void 0,limit:i.limit,offset:i.offset},t)}const a=await this.query(e);if(Array.isArray(a)){for(const e of a)await t(e);return a.length}return 0}normalizeWhereForStream(e){const t=[e.alias??e.from].filter(Boolean),s=e=>{for(const s of t)if(e.startsWith(`${s}.`))return e.slice(s.length+1);return e},n=e=>{const t={};for(const[i,r]of Object.entries(e))"$and"===i||"$or"===i?t[i]=r.map(n):"$not"===i&&"object"==typeof r&&null!==r?t.$not=n(r):t[s(i)]=r;return t};return n(e.where??{})}async transaction(e){this.ensureReady(),await this.pluginManager.trigger("beforeTransaction");try{const t=await this.transactionManager.execute(e);return await this.pluginManager.trigger("afterTransaction"),t}catch(e){throw this._onError(e),e}}async exportTable(e){return this.ensureReady(),this.engine.find(e,{table:e})}async importTable(e,t){this.ensureReady();try{return await this.engine.insert(e,t)}catch(e){throw this._onError(e),e}}async exportAll(){this.ensureReady();const e={},t=await this.engine.getTableNames();for(const s of t)e[s]=await this.engine.find(s,{table:s});return e}async backup(){return this.ensureReady(),"function"==typeof this.engine.backup?this.engine.backup():this.exportAll()}subscribe(e,t){const s=`change:${e}`;return this.listeners.has(s)||this.listeners.set(s,new Set),this.listeners.get(s).add(t),()=>this.listeners.get(s)?.delete(t)}emit(e,t){const s=`change:${e}`;this.listeners.get(s)?.forEach(e=>e(t))}broadcastChange(e){if(this.channel)try{this.channel.postMessage({type:"change",table:e})}catch{}}writeStatementTable(e){switch(e.type){case"INSERT":return e.into;case"UPDATE":case"CREATE_INDEX":case"DROP_INDEX":return e.table;case"DELETE":return e.from;case"CREATE_TABLE":case"DROP_TABLE":case"TRUNCATE_TABLE":case"ALTER_TABLE":return e.name;default:return null}}async triggerStatementHooks(e,t,s){switch(e.type){case"INSERT":{const n=(e.values??[]).map(t=>{const s={},n=e.columns??[];for(let e=0;ee[0]-t[0]))t<=e&&t>this._version&&(await s(this),this._version=t);if("function"==typeof this.engine.setMeta)try{await this.engine.setMeta("__metona_version",String(this._version))}catch{}}async repair(){if(this.ensureReady(),"function"==typeof this.engine.repair)return await this.engine.repair(),void this.tableCache.clear();this.tableCache.clear()}async clearAll(){if(this.ensureReady(),"function"==typeof this.engine.clearAll)await this.engine.clearAll();else{const e=await this.engine.getTableNames();for(const t of e)await this.engine.dropTable(t)}this.tableCache.clear()}getPluginManager(){return this.pluginManager}on(e,t){this.pluginManager.on(e,t)}async close(){this.channel&&(this.channel.close(),this.channel=null),this.pluginManager.destroy(),await this.engine.close(),this.tableCache.clear(),this.ready=!1}getEngine(){return this.engine}createEngine(){const e=this.mode,t=this.config.diskEngine??"opfs";switch(e){case"memory":return new f;case"disk":return new v;case"aria":return new ke({storageBackend:"memory"===t?"memory":"opfs",...this.config.aria??{}});case"hybrid":return new xe(t);default:throw new n(`Unknown storage mode: ${e}`,"CONFIG_ERROR")}}ensureReady(){if(!this.ready)throw new n("Database not initialized. Call await db.init() first.","DB_NOT_READY")}_onError(e){if(this.config.onError)try{this.config.onError(e)}catch{}}_debug(e,...t){this.debug}}const je=new class{constructor(){this.connections=new Map,this.refCount=new Map}async connect(e){const t=e.name,s=this.connections.get(t);if(s&&s.isReady()){const e=(this.refCount.get(t)??0)+1;return this.refCount.set(t,e),s}const n=new Fe(e);return await n.init(),this.connections.set(t,n),this.refCount.set(t,1),n.disconnect=async()=>{await this.release(t)},n}async release(e){const t=(this.refCount.get(e)??1)-1;if(t<=0){const t=this.connections.get(e);t&&(await t.close(),this.connections.delete(e)),this.refCount.delete(e)}else this.refCount.set(e,t)}async forceClose(e){const t=this.connections.get(e);t&&(await t.close(),this.connections.delete(e)),this.refCount.delete(e)}async closeAll(){for(const[,e]of this.connections)try{await e.close()}catch{}this.connections.clear(),this.refCount.clear()}getActiveConnections(){return Array.from(this.connections.keys())}},qe=Fe;async function Ve(e){const t=new Fe(e);return await t.init(),t}qe.connect=e=>je.connect(e),qe.disconnect=e=>je.release(e),qe.disconnectAll=()=>je.closeAll(),qe.getActiveConnections=()=>je.getActiveConnections();const We={VERSION:i,version:i,create:Ve,MetonaSqlark:Fe,MeSqlark:Fe};"undefined"!=typeof window&&(window.MetonaSqlark=We,window.MeSqlark=We);const He=Fe;e.AriaEngine=ke,e.HybridEngine=xe,e.KVStoreEngine=v,e.MeSqlark=He,e.MemoryEngine=f,e.MetonaSqlark=Fe,e.OPFSBackend=p,e.Table=Re,e.VERSION=i,e.api=We,e.create=Ve,e.default=We,e.parse=function(e){return new ve(e).parseStatement()},e.parseAll=$e,e.tokenize=function(e){const t=new Le(e),s=[];let n=t.nextToken();for(;n.type!==Ne.EOF;)s.push(n),n=t.nextToken();return s.push(n),s},Object.defineProperty(e,"__esModule",{value:!0})});