2 lines
219 KiB
JavaScript
2 lines
219 KiB
JavaScript
!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"],n=Object.freeze({name:"metona-sqlark",mode:"hybrid",diskEngine:"opfs",version:1,maxRowsPerQuery:0,debug:!1,multiTabSync:!1,aria:void 0});class s extends Error{constructor(e,t,n){super(e),this.code=t,this.details=n,this.name="DatabaseError"}}const i="0.8.0";class r{constructor(e,t=()=>{},n=()=>{}){this.listeners=new Set,this.inner=e,this.onListenerError=t,this.onBroadcast=n}getInner(){return this.inner}addListener(e){return this.listeners.add(e),()=>this.listeners.delete(e)}async dispatch(e){try{this.onBroadcast(e.table)}catch{}for(const t of[...this.listeners])try{await t(e)}catch(e){this.onListenerError(e)}}async insert(e,t){const n=await this.inner.insert(e,t);for(let s=0;s<n.length;s++)await this.dispatch({type:"insert",table:e,key:n[s],row:s<t.length?t[s]:void 0,count:1});return n}async update(e,t,n){const s=await this.snapshotAffected(e,t),i=await this.inner.update(e,t,n);if(i>0&&s.length>0)for(const t of s){const s={...t,...a(n)};await this.dispatch({type:"update",table:e,row:s,count:1})}else i>0&&await this.dispatch({type:"update",table:e,count:i});return i}async delete(e,t){const n=await this.snapshotAffected(e,t),s=await this.inner.delete(e,t);if(s>0&&n.length>0)for(const t of n)await this.dispatch({type:"delete",table:e,row:t,count:1});else s>0&&await this.dispatch({type:"delete",table:e,count:s});return s}async clear(e){await this.inner.clear(e),await this.dispatch({type:"clear",table:e})}async createTable(e){await this.inner.createTable(e),await this.dispatch({type:"ddl",table:e.name})}async dropTable(e){await this.inner.dropTable(e),await this.dispatch({type:"ddl",table:e})}async alterTable(e,t,n){await this.requireCapability("alterTable")(e,t,n),await this.dispatch({type:"ddl",table:e})}async createIndex(e,t,n){await this.requireCapability("createIndex")(e,t,n),await this.dispatch({type:"ddl",table:e})}async dropIndex(e,t,n){await this.requireCapability("dropIndex")(e,t,n),await this.dispatch({type:"ddl",table:e})}get name(){return this.inner.name}open(e,t){return this.inner.open(e,t)}close(){return this.inner.close()}isOpen(){return this.inner.isOpen()}hasTable(e){return this.inner.hasTable(e)}getTableNames(){return this.inner.getTableNames()}getTableSchema(e){return this.inner.getTableSchema(e)}find(e,t){return this.inner.find(e,t)}count(e,t){return this.inner.count(e,t)}async findStream(e,t,n){return this.requireCapability("findStream")(e,t,n)}beginTransaction(){return this.inner.beginTransaction()}commitTransaction(){return this.inner.commitTransaction()}rollbackTransaction(){return this.inner.rollbackTransaction()}savepoint(e){return this.requireCapability("savepoint")(e)}rollbackToSavepoint(e){return this.requireCapability("rollbackToSavepoint")(e)}releaseSavepoint(e){return this.requireCapability("releaseSavepoint")(e)}backup(){return this.requireCapability("backup")()}repair(){return this.requireCapability("repair")()}clearAll(){return this.requireCapability("clearAll")()}analyzeTable(e){return this.requireOptionalMethod("analyzeTable")(e)}reindexTable(e){return this.requireOptionalMethod("reindexTable")(e)}vacuum(){return this.requireOptionalMethod("vacuum")()}getMeta(e){const t=this.inner.getMeta;return"function"!=typeof t?Promise.resolve(null):t.call(this.inner,e)}setMeta(e,t){const n=this.inner.setMeta;return"function"!=typeof n?Promise.resolve():n.call(this.inner,e,t)}requireCapability(e){const t=this.inner[e];return"function"!=typeof t?()=>{throw new s(`Engine "${this.inner.name}" does not support ${String(e)}`,"NOT_SUPPORTED")}:t.bind(this.inner)}requireOptionalMethod(e){const t=this.inner[e];return"function"!=typeof t?()=>{throw new s(`Engine "${this.inner.name}" does not support ${e}`,"NOT_SUPPORTED")}:t.bind(this.inner)}async snapshotAffected(e,t){try{return await this.inner.find(e,{table:e,where:t.where})}catch{return[]}}}function a(e){const t={};for(const[n,s]of Object.entries(e))void 0!==s&&(t[n]=s);return t}function o(e){if(null===e||"object"!=typeof e)return e;if("function"==typeof structuredClone)try{return structuredClone(e)}catch{}return c(e)}function c(e){if(null===e||"object"!=typeof e)return e;if(e instanceof Date)return new Date(e.getTime());if(Array.isArray(e))return e.map(e=>c(e));if(e instanceof Uint8Array)return new Uint8Array(e);if(e instanceof ArrayBuffer)return e.slice(0);const t={};for(const[n,s]of Object.entries(e))t[n]=c(s);return t}const h=Object.freeze({TRUE:"TRUE",FALSE:"FALSE",UNKNOWN:"UNKNOWN"});function l(e){return null==e}const u=Symbol("sqlark.unresolved");function f(e){return e===u}function d(e){if(null==e)return"z";switch(typeof e){case"string":{const t=e;return`s${t.length}:${t}`}case"number":{const t=e;return Number.isNaN(t)?"nNaN":`n${Object.is(t,-0)?0:t}`}case"boolean":return"b"+(e?1:0);case"bigint":return`i${String(e)}`;case"object":return e instanceof Date?`d${e.getTime()}`:Array.isArray(e)?`a${JSON.stringify(e)}`:`o${JSON.stringify(e)}`;default:return`x${String(e)}`}}function p(e,t){const n=m(e,t);return null===n?h.UNKNOWN:0===n?h.TRUE:h.FALSE}function y(e,t){return p(e,t)}function m(e,t){if(f(e)||f(t))return null;if(l(e)||l(t))return null;if("number"==typeof e&&"number"==typeof t)return Number.isNaN(e)||Number.isNaN(t)?null:e===t?0:e<t?-1:1;if("boolean"==typeof e&&"boolean"==typeof t)return e===t?0:!1===e?-1:1;if("string"==typeof e&&"string"==typeof t)return e===t?0:e<t?-1:1;if(e instanceof Date&&t instanceof Date){const n=e.getTime(),s=t.getTime();return Number.isNaN(n)||Number.isNaN(s)?null:n===s?0:n<s?-1:1}const n=T(e),s=T(t);return null!==n&&null!==s?n===s?0:n<s?-1:1:null}function w(e,t){return e===h.FALSE||t===h.FALSE?h.FALSE:e===h.UNKNOWN||t===h.UNKNOWN?h.UNKNOWN:h.TRUE}function g(e,t){return e===h.TRUE||t===h.TRUE?h.TRUE:e===h.UNKNOWN||t===h.UNKNOWN?h.UNKNOWN:h.FALSE}function b(e){return e===h.TRUE?h.FALSE:e===h.FALSE?h.TRUE:h.UNKNOWN}function T(e){if("number"==typeof e)return Number.isNaN(e)?null:e;if("boolean"==typeof e)return e?1:0;if("string"==typeof e){const t=e.trim();if(""===t)return null;const n=Number(t);return Number.isNaN(n)?null:n}return null}function E(e,t){if(f(e))return h.UNKNOWN;let n=!1;for(const s of t){const t=y(e,s);if(t===h.TRUE)return h.TRUE;t===h.UNKNOWN&&(n=!0)}return n?h.UNKNOWN:h.FALSE}const k=new Map;function S(e,t,n={}){return O({row:e,options:n},t)===h.TRUE}function x(e){if(!e)return!1;for(const[t,n]of Object.entries(e))if(I(t)){if((Array.isArray(n)?n:[n]).some(e=>x(e)))return!0}else{if("$exists"===t)return!0;if(R(n)&&A(n))return!0}return!1}function A(e){for(const[t,n]of Object.entries(e))if("$and"!==t&&"$or"!==t){if("$not"!==t){if("$subquery"===t||"$col"===t)return!0;if(R(n)&&("$subquery"in n||"$col"in n))return!0}else if(R(n)&&A(n))return!0}else if((Array.isArray(n)?n:[n]).some(e=>R(e)&&A(e)))return!0;return!1}function I(e){return"$and"===e||"$or"===e||"$not"===e}function R(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function O(e,t){let n=h.TRUE;for(const[s,i]of Object.entries(t)){let t;if(t="$and"===s?N(e,i,"and"):"$or"===s?N(e,i,"or"):"$not"===s?b(O(e,i)):"$exists"===s||"$caseResult"===s?!0===i?h.TRUE:h.FALSE:$(e,C(e,s),i),n=w(n,t),n===h.FALSE)return h.FALSE}return n}function N(e,t,n){const s=Array.isArray(t)?t:[t];let i="and"===n?h.TRUE:h.FALSE;for(const t of s){const s=O(e,t);if(i="and"===n?w(i,s):g(i,s),"and"===n&&i===h.FALSE)return h.FALSE;if("or"===n&&i===h.TRUE)return h.TRUE}return i}function C(e,t){if(t in e.row)return e.row[t];let n=u,s=0;for(const i of Object.keys(e.row))i.endsWith(`.${t}`)&&(n=e.row[i],s+=1);return 1===s?n:u}function $(e,t,n){return R(n)?L(e,t,n):Array.isArray(n)?E(t,n):_(t,n)}function L(e,t,n){const s=Object.keys(n);if(1===s.length&&"$col"===s[0]&&e.options.$col)return _(t,C(e,String(n.$col)));let i=h.TRUE;for(const[s,r]of Object.entries(n)){let n;if(n="$and"===s?v(e,t,r,"and"):"$or"===s?v(e,t,r,"or"):"$not"===s?b(U(e,t,r)):D(e,t,s,r),i=w(i,n),i===h.FALSE)return h.FALSE}return i}function v(e,t,n,s){const i=Array.isArray(n)?n:[n];let r="and"===s?h.TRUE:h.FALSE;for(const n of i){const i=U(e,t,n);if(r="and"===s?w(r,i):g(r,i),"and"===s&&r===h.FALSE)return h.FALSE;if("or"===s&&r===h.TRUE)return h.TRUE}return r}function U(e,t,n){return R(n)?Object.keys(n).some(e=>!e.startsWith("$")||I(e)||"$exists"===e||"$caseResult"===e)?O(e,n):L(e,t,n):$(e,t,n)}function D(e,t,n,i){const r=function(e,t){if(!R(t))return t;const n=Object.keys(t);return 1!==n.length?t:"$col"===n[0]?e.options.$col?C(e,String(t.$col)):u:"$subquery"===n[0]?u:t}(e,i);switch(n){case"$eq":return _(t,r);case"$ne":return b(_(t,r));case"$gt":return M(t,r,e=>e>0);case"$gte":return M(t,r,e=>e>=0);case"$lt":return M(t,r,e=>e<0);case"$lte":return M(t,r,e=>e<=0);case"$in":return Array.isArray(r)?E(t,r):h.FALSE;case"$nin":return Array.isArray(r)?b(E(t,r)):h.FALSE;case"$like":return l(t)||l(r)||f(t)||f(r)?h.UNKNOWN:function(e){const t=k.get(e);if(t)return t;const n=e.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/%/g,".*").replace(/_/g,"."),s=new RegExp(`^${n}$`,"i");return k.set(e,s),s}(String(r)).test(String(t))?h.TRUE:h.FALSE;case"$isNull":return l(t)?h.TRUE:h.FALSE;case"$isNotNull":return l(t)?h.FALSE:h.TRUE;case"$col":return e.options.$col?_(t,C(e,String(i))):h.UNKNOWN;case"$subquery":return h.UNKNOWN;default:throw new s(`Unknown where operator "${n}"`,"QUERY_ERROR")}}function _(e,t){return p(e,t)===h.TRUE?h.TRUE:l(e)||l(t)||f(e)||f(t)?h.UNKNOWN:h.FALSE}function M(e,t,n){const s=m(e,t);return null===s?h.UNKNOWN:n(s)?h.TRUE:h.FALSE}function P(e,t){return[...e].sort((e,n)=>{for(const{column:s,direction:i,nulls:r}of t){const t=null===e[s]||void 0===e[s],a=null===n[s]||void 0===n[s];if(r&&(t||a)){if(t&&a)continue;return"first"===r?t?-1:1:t?1:-1}const o=B(e[s],n[s]);if(0!==o)return"desc"===i?-o:o}return 0})}function B(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 K(e,t){const n={};for(const s of t){const t=q(s);if(t in e){n[t]=e[t];continue}let i,r=0;for(const n of Object.keys(e))(n.endsWith(`.${t}`)||n===t)&&(i=e[n],r+=1);r>=1&&(n[t]=i)}return n}function q(e){const t=e.trim();return t.length>=2&&t.startsWith('"')&&t.endsWith('"')?t.slice(1,-1).replace(/""/g,'"'):t}function F(e){const t=e.name,n=Object.entries(e.columns),i=new Set(n.map(([e])=>e));return{table:t,columns:i,validateRow:function(e,r){!function(e,n){const r=n?new Set(n):i,a=[];for(const t of Object.keys(e))"__proto__"!==t&&(r.has(t)||a.push(t));if(a.length>0)throw new s(`Unknown column${a.length>1?"s":""} ${a.map(e=>`"${e}"`).join(", ")} in table "${t}". Known columns: ${[...r].join(", ")}`,"COLUMN_NOT_FOUND")}(e,r);const a={};for(const[s,i]of n){let n=e[s];void 0===n&&void 0!==i.default&&(n=i.default),z(t,s,i,n),null!=n&&(j(t,s,n),V(t,s,i.type,n,i)),a[s]=void 0===n?null:n}return a},validatePartial:function(n){const i={},r=[];for(const[a,o]of Object.entries(n)){if("__proto__"===a)throw new s('Column name "__proto__" is not allowed',"VALIDATION_ERROR");const n=e.columns[a];n?void 0!==o&&(z(t,a,n,o),null!==o&&(j(t,a,o),V(t,a,n.type,o,n)),i[a]=o):r.push(a)}return{values:i,unknown:r}}}}function j(e,t,n){if("number"==typeof n&&!Number.isFinite(n))throw new s(`Column "${t}" in table "${e}" cannot store ${Number.isNaN(n)?"NaN":String(n)}: it is not representable in JSON and would be silently read back as null`,"VALIDATION_ERROR")}function z(e,t,n,i){if(n.required&&null==i)throw new s(`Column "${t}" is required in table "${e}"`,"VALIDATION_ERROR");if(n.primaryKey&&null==i)throw new s(`Primary key column "${t}" in table "${e}" cannot be null or undefined`,"VALIDATION_ERROR")}function V(e,t,n,i,r){const a=typeof i;switch(n){case"string":if("string"!==a)throw new s(`Column "${t}" in table "${e}" expects string, got ${a}`,"TYPE_ERROR");if(void 0!==r?.maxLength&&i.length>r.maxLength)throw new s(`Column "${t}" in table "${e}" exceeds max length ${r.maxLength}`,"VALIDATION_ERROR");break;case"number":if("number"!==a)throw new s(`Column "${t}" in table "${e}" expects number, got ${a}`,"TYPE_ERROR");if(void 0!==r?.min&&i<r.min)throw new s(`Column "${t}" in table "${e}" value ${i} below minimum ${r.min}`,"VALIDATION_ERROR");if(void 0!==r?.max&&i>r.max)throw new s(`Column "${t}" in table "${e}" value ${i} above maximum ${r.max}`,"VALIDATION_ERROR");break;case"boolean":if("boolean"!==a)throw new s(`Column "${t}" in table "${e}" expects boolean, got ${a}`,"TYPE_ERROR");break;case"date":if("string"!==a||isNaN(Date.parse(i)))throw new s(`Column "${t}" in table "${e}" expects valid date string, got ${typeof i}`,"TYPE_ERROR");break;case"json":if("object"!==a)throw new s(`Column "${t}" in table "${e}" expects object/array, got ${a}`,"TYPE_ERROR")}}function W(e,n){return function(e){if(0===Object.keys(e).length)throw new s("Table must have at least one column","SCHEMA_ERROR");let n=0;for(const[i,r]of Object.entries(e)){if("__proto__"===i)throw new s('Column name "__proto__" is not allowed',"SCHEMA_ERROR");if(!t.includes(r.type))throw new s(`Invalid type "${r.type}" for column "${i}". Valid types: ${t.join(", ")}`,"SCHEMA_ERROR");r.primaryKey&&n++}if(0===n)throw new s("Table must have at least one primary key column","SCHEMA_ERROR");if(n>1)throw new s(`Composite primary keys are not supported yet: table has ${n} primary key columns. Use a single primary key column (or a unique column combination) instead.`,"SCHEMA_ERROR")}(n),{name:e,columns:n}}function G(e){const t={};for(const[n,s]of Object.entries(e))void 0!==s&&(t[n]=s);return t}function H(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}}class Q{constructor(){this.name="memory",this.tables=new Map,this.schemas=new Map,this.indexes=new Map,this.opened=!1,this.metaStore=new Map,this.uniqueIndexCols=new Set,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 s(`Table "${e.name}" already exists`,"TABLE_EXISTS");const t={name:e.name,columns:{}};for(const[n,s]of Object.entries(e.columns))t.columns[n]={...s};this.schemas.set(e.name,t),this.tables.set(e.name,new Map);const n=new Map;for(const[e,s]of Object.entries(t.columns))(s.index||s.unique)&&n.set(e,new Map);this.indexes.set(e.name,n)}async dropTable(e){this.ensureTable(e),this.schemas.delete(e),this.tables.delete(e),this.indexes.delete(e);const t=`${e}:`;for(const e of this.uniqueIndexCols)e.startsWith(t)&&this.uniqueIndexCols.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,n){if(this.snapshot)throw new s("ALTER TABLE is not supported inside a transaction (MemoryEngine DDL is not transactional)","NOT_SUPPORTED");this.ensureTable(e);const i=this.schemas.get(e);if("ADD"===t){if(i.columns[n.name])throw new s(`Column "${n.name}" already exists in table "${e}"`,"COLUMN_EXISTS");if(i.columns[n.name]=n,n.index||n.unique){this.indexes.has(e)||this.indexes.set(e,new Map);const t=this.indexes.get(e);t.has(n.name)||t.set(n.name,new Map);const r=t.get(n.name),a=this.tables.get(e),o=new Set;for(const[c,h]of a){const a=h[n.name];if(null==a)continue;if(n.unique&&o.has(a))throw t.delete(n.name),delete i.columns[n.name],new s(`Duplicate value "${String(a)}" for UNIQUE column "${n.name}" in table "${e}"`,"UNIQUE_VIOLATION");o.add(a);let l=r.get(a);l||(l=new Set,r.set(a,l)),l.add(c)}}const t=this.tables.get(e);for(const e of t.values())n.name in e||(e[n.name]=null);return}if(!i.columns[n.name])throw new s(`Column "${n.name}" does not exist in table "${e}"`,"COLUMN_NOT_FOUND");(i.columns[n.name].index||i.columns[n.name].unique)&&this.indexes.get(e)?.delete(n.name),delete i.columns[n.name];const r=this.tables.get(e);for(const e of r.values())n.name in e&&delete e[n.name]}async insert(e,t){this.ensureTable(e);const n=this.schemas.get(e),i=this.tables.get(e),r=this.getPrimaryKey(n),a=[],o=[],c=new Set,h=new Map;for(const a of t){const t=this.validateRow(n,a),l=String(t[r]);if(i.has(l)||c.has(l))throw new s(`Duplicate primary key "${l}" in table "${e}"`,"DUPLICATE_KEY");c.add(l),this.checkInsertUniqueness(n,e,t,h),o.push(t)}for(const t of o){const n=String(t[r]);i.set(n,t),this.updateIndexes(e,t,n),a.push(n)}return a}getRow(e,t){const n=this.tables.get(e);if(!n)return null;const s=n.get(t);return s?o(s):null}async find(e,t){this.ensureTable(e);const n=this.tables.get(e);let s=this.tryIndexLookup(e,n,t);t.where&&Object.keys(t.where).length>0&&(s=s.filter(e=>S(e,t.where))),t.orderBy&&t.orderBy.length>0&&(s=P(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=>K(e,t.columns))),s.map(e=>o(e))}async findStream(e,t,n){this.ensureTable(e);const s=this.tables.get(e),i=!!(t.where&&Object.keys(t.where).length>0),r=t.limit??1/0,a=t.offset??0,c=t.columns&&t.columns.length>0&&"*"!==t.columns[0]?e=>K(e,t.columns):null;let h=0,l=0;for(const e of s.values())if(!i||S(e,t.where))if(l<a)l++;else if(n(c?c(e):o(e)),h++,h>=r)break;return h}async update(e,t,n){this.ensureTable(e);const i=this.schemas.get(e),r=this.tables.get(e),a=this.getPrimaryKey(i),o=G(n);for(const t of Object.keys(o))if(!i.columns[t])throw new s(`Column "${t}" does not exist in table "${e}"`,"COLUMN_NOT_FOUND");const c=[],h=new Map,l=new Set;for(const[n,u]of r){if(t.where&&Object.keys(t.where).length>0&&!S(u,t.where))continue;const f={...u,...o};this.validateRow(i,f),this.checkUpdateUniqueness(i,e,n,f,h);const d=String(f[a]);if(d!==n&&r.has(d))throw new s(`Duplicate primary key "${d}" in table "${e}" (cannot update key to existing value)`,"DUPLICATE_KEY");if(d!==n){if(l.has(d))throw new s(`Duplicate primary key "${d}" in table "${e}" (multiple rows in the same statement update to the same key)`,"DUPLICATE_KEY");l.add(d)}c.push({pk:n,row:u,updated:f,newPk:d})}for(const t of c)t.newPk!==t.pk&&this.checkUpdateRestrict(e,t.pk);let u=0;for(const{pk:t,row:n,updated:s,newPk:i}of c)this.removeIndexEntries(e,n,t),i!==t&&await this.applyUpdateCascade(e,t,i),r.delete(t),r.set(i,s),this.updateIndexes(e,s,i),u++;return u}checkInsertUniqueness(e,t,n,i){const r=this.indexes.get(t);for(const[t,a]of Object.entries(e.columns)){if(!a.unique)continue;const o=n[t];if(null==o)continue;let c=i.get(t);if(c||(c=new Set,i.set(t,c)),c.has(o))throw new s(`Unique constraint violation on column "${t}" in table "${e.name}"`,"UNIQUE_VIOLATION");if(c.add(o),!r)continue;const h=r.get(t);if(h&&h.has(o))throw new s(`Unique constraint violation on column "${t}" in table "${e.name}"`,"UNIQUE_VIOLATION")}}checkUpdateUniqueness(e,t,n,i,r){const a=this.indexes.get(t);for(const[t,o]of Object.entries(e.columns)){if(!o.unique)continue;const c=i[t];if(null==c)continue;let h=r.get(t);if(h||(h=new Set,r.set(t,h)),h.has(c))throw new s(`Unique constraint violation on column "${t}" in table "${e.name}"`,"UNIQUE_VIOLATION");if(h.add(c),!a)continue;const l=a.get(t);if(l&&l.has(c)){const i=l.get(c);if(1!==i.size||!i.has(n))throw new s(`Unique constraint violation on column "${t}" in table "${e.name}"`,"UNIQUE_VIOLATION")}}}checkUpdateRestrict(e,t){for(const[n,i]of this.schemas)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)continue;let c=!1;for(const[,i]of o)if(String(i[r])===t&&(c=!0,"RESTRICT"===a.onUpdate))throw new s(`Cannot update "${e}" key "${t}": foreign key "${r}" in "${n}" has dependent rows`,"FOREIGN_KEY_VIOLATION");if(c&&"SET NULL"===a.onUpdate&&a.required)throw new s(`Cannot update "${e}" key "${t}": foreign key "${r}" in "${n}" is required (SET NULL violates constraint)`,"FOREIGN_KEY_VIOLATION")}}async applyUpdateCascade(e,t,n){for(const[s,i]of this.schemas)for(const[r,a]of Object.entries(i.columns)){if(!a.references||!a.onUpdate)continue;const[o]=a.references.split(".");if(o!==e)continue;const c=this.tables.get(s);if(!c)continue;if("CASCADE"!==a.onUpdate&&"SET NULL"!==a.onUpdate)continue;const h=this.rowValidator(i);for(const[e,i]of c){if(String(i[r])!==t)continue;const o="CASCADE"===a.onUpdate?n:null,{values:c}=h.validatePartial({[r]:o});this.removeIndexEntries(s,i,e),i[r]=c[r],this.updateIndexes(s,i,e)}}}async delete(e,t){this.ensureTable(e);const n=this.tables.get(e),s=[];for(const[e,i]of n)t.where&&0!==Object.keys(t.where).length&&!S(i,t.where)||s.push({pk:e,row:i});const i=new Set;for(const{pk:t}of s)this.checkCascadeRestrict(e,t,i);let r=0;for(const{pk:t,row:n}of s)this.removeIndexEntries(e,n,t),r+=await this.cascadeDelete(e,t,n);for(const{pk:e}of s)n.delete(e);return s.length+r}checkCascadeRestrict(e,t,n){const i=`${e}:${t}`;if(!n.has(i)){n.add(i);for(const[i,r]of this.schemas)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 c=this.tables.get(i);if(!c)continue;const h=[];for(const[e,n]of c)String(n[a])===t&&h.push(e);if("RESTRICT"===o.onDelete&&h.length>0)throw new s(`Cannot delete from "${e}": foreign key "${a}" in "${i}" has dependent rows`,"FOREIGN_KEY_VIOLATION");if("SET NULL"===o.onDelete&&o.required&&h.length>0)throw new s(`Cannot delete from "${e}": foreign key "${a}" in "${i}" is required (SET NULL violates constraint)`,"FOREIGN_KEY_VIOLATION");if("CASCADE"===o.onDelete)for(const e of h)this.checkCascadeRestrict(i,e,n)}}}async count(e,t){this.ensureTable(e);const n=this.tables.get(e);if(!t?.where||0===Object.keys(t.where).length)return n.size;let s=0;for(const e of n.values())S(e,t.where)&&s++;return s}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,n){if(this.snapshot)throw new s("CREATE INDEX is not supported inside a transaction (MemoryEngine DDL is not transactional)","NOT_SUPPORTED");this.ensureTable(e);const i=this.schemas.get(e).columns[t];if(!i)throw new s(`Column "${t}" does not exist in table "${e}"`,"COLUMN_NOT_FOUND");if(i.index||i.unique)return;const r=this.indexes.get(e);r.has(t)||r.set(t,new Map);const a=r.get(t),o=this.tables.get(e);try{for(const[i,r]of o){const o=r[t];if(null!=o){if(n&&a.has(o))throw new s(`Unique index on column "${t}" in table "${e}" cannot be created: duplicate value "${String(o)}"`,"UNIQUE_VIOLATION");a.has(o)||a.set(o,new Set),a.get(o).add(i)}}}catch(e){throw r.delete(t),e}i.index=!0,n&&(i.unique=!0,this.uniqueIndexCols.add(`${e}:${t}`))}async dropIndex(e,t,n){if(this.snapshot)throw new s("DROP INDEX is not supported inside a transaction (MemoryEngine DDL is not transactional)","NOT_SUPPORTED");this.ensureTable(e);const i=this.schemas.get(e).columns[t];if(!i)throw new s(`Column "${t}" does not exist in table "${e}"`,"COLUMN_NOT_FOUND");if(!i.index&&!i.unique)throw new s(`Index on column "${t}" does not exist in table "${e}"`,"INDEX_NOT_FOUND");const r=`${e}:${t}`;if(i.unique&&!this.uniqueIndexCols.has(r))throw new s(`Cannot drop index on column "${t}" in table "${e}": UNIQUE constraint defined at table creation must be removed by recreating the table`,"NOT_SUPPORTED");i.index=!1,i.unique=!1,this.uniqueIndexCols.delete(r);const a=this.indexes.get(e);a&&a.delete(t)}async beginTransaction(){if(this.snapshot)throw new s("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 s("No active transaction","TX_NONE");this.snapshot=null}async rollbackTransaction(){if(!this.snapshot)throw new s("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[n,s]of e){const e=new Map;for(const[t,n]of s)e.set(t,{...n});t.set(n,e)}return t}deepCloneIndexes(e){const t=new Map;for(const[n,s]of e){const e=new Map;for(const[t,n]of s){const s=new Map;for(const[e,t]of n)s.set(e,new Set(t));e.set(t,s)}t.set(n,e)}return t}ensureTable(e){if(!this.tables.has(e))throw new s(`Table "${e}" does not exist`,"TABLE_NOT_FOUND")}getPrimaryKey(e){for(const[t,n]of Object.entries(e.columns))if(n.primaryKey)return t;return Object.keys(e.columns)[0]}validateRow(e,t){return this.rowValidator(e).validateRow(t)}rowValidator(e){return F(e)}async validatePayload(e,t,n="insert"){this.ensureTable(e);const s=this.schemas.get(e),i=this.rowValidator(s);for(const e of t)"update"===n?i.validatePartial(G(e)):i.validateRow(e)}tryIndexLookup(e,t,n){const s=this.indexes.get(e);if(!s||!n.where)return Array.from(t.values());const i=[],r=e=>{for(const[t,n]of Object.entries(e))if("$and"!==t)"$or"!==t&&"$not"!==t&&i.push([t,n]);else for(const e of n)r(e)};r(n.where);for(const[e,n]of i){let i;if("object"!=typeof n||null===n)i=n;else{if(!("$eq"in n)||1!==Object.keys(n).length)continue;i=n.$eq}if(null==i)continue;if("object"==typeof i)continue;const r=s.get(e);if(r){const e=r.get(i);if(e){const n=[];for(const s of e){const e=t.get(s);e&&n.push(e)}return n}return[]}}return Array.from(t.values())}updateIndexes(e,t,n){const s=this.indexes.get(e);if(s)for(const[e,i]of s){const s=t[e];null!=s&&(i.has(s)||i.set(s,new Set),i.get(s).add(n))}}removeIndexEntries(e,t,n){const s=this.indexes.get(e);if(s)for(const[e,i]of s){const s=t[e];if(null!=s){const e=i.get(s);e&&(e.delete(n),0===e.size&&i.delete(s))}}}async cascadeDelete(e,t,n,i=new Set){const r=`${e}:${t}`;if(i.has(r))return 0;i.add(r);let a=0;for(const[n,r]of this.schemas)for(const[o,c]of Object.entries(r.columns)){if(!c.references||!c.onDelete)continue;const[r]=c.references.split(".");if(r!==e)continue;const h=this.tables.get(n);if(!h)continue;const l=[];for(const[e,n]of h)String(n[o])===t&&l.push(e);if("RESTRICT"===c.onDelete&&l.length>0)throw new s(`Cannot delete from "${e}": foreign key "${o}" in "${n}" has dependent rows`,"FOREIGN_KEY_VIOLATION");if("CASCADE"===c.onDelete)for(const e of l){const t=h.get(e);t&&(this.removeIndexEntries(n,t,e),a+=await this.cascadeDelete(n,e,t,i)),h.has(e)&&(h.delete(e),a++)}else if("SET NULL"===c.onDelete)for(const e of l){const t=h.get(e);t&&(this.removeIndexEntries(n,t,e),t[o]=null,this.updateIndexes(n,t,e))}}return a}}const X=[".crswap",".tmp"];class J{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[n]of e.entries())X.some(e=>n.endsWith(e))&&t.push(n);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),n=await t.getFile();return await n.arrayBuffer()}catch{return null}}async write(e,t){if(!this.dbDir)return;const n=this.writeQueue.then(async()=>{const n=await this.dbDir.getFileHandle(e,{create:!0}),s=await n.createWritable();await s.write(t),await s.close()});return this.writeQueue=n.then(()=>{},()=>{}),n}async append(e,t){if(!this.dbDir)return;const n=this.writeQueue.then(async()=>{const n=await this.dbDir.getFileHandle(e,{create:!0}),s=await n.getFile(),i=await n.createWritable({keepExistingData:!0});await i.write({type:"write",position:s.size,data:t}),await i.close()});return this.writeQueue=n.then(()=>{},()=>{}),n}async writeMany(e){if(!this.dbDir)return;const t=this.writeQueue.then(async()=>{for(const[t,n]of Object.entries(e)){const e=await this.dbDir.getFileHandle(t,{create:!0}),s=await e.createWritable();await s.write(n),await s.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[n]of t.entries())e.push(n);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;let Z=0,ee=new Map;class te{constructor(){this.dbName="",this.store=null}static clearRegistry(){Y.clear(),Z+=1,ee=new Map}async open(e){this.dbName=e,this.store=function(e){const t=ee.get(e);if(t&&t.epoch===Z)return t.store;const n=Y.get(e);if(n)return ee.set(e,{epoch:Z,store:n}),n;const s={chunks:new Map,materialized:new Map};return Y.set(e,s),ee.set(e,{epoch:Z,store:s}),s}(e)}async close(){this.store=null}isOpen(){return""!==this.dbName&&null!==this.store}db(){return this.store||(this.store={chunks:new Map,materialized:new Map}),this.store}async read(e){const t=this.db();if(t.materialized.has(e))return t.materialized.get(e)??null;const n=t.chunks.get(e);if(!n||0===n.length)return t.materialized.set(e,null),null;if(1===n.length)return t.materialized.set(e,n[0]),n[0];const s=n.reduce((e,t)=>e+t.byteLength,0),i=new Uint8Array(s);let r=0;for(const e of n)i.set(new Uint8Array(e),r),r+=e.byteLength;const a=i.buffer;return t.materialized.set(e,a),a}async write(e,t){const n=this.db();n.chunks.set(e,[t]),n.materialized.set(e,t)}async append(e,t){const n=this.db(),s=n.chunks.get(e);s?s.push(t):n.chunks.set(e,[t]),n.materialized.delete(e)}async writeMany(e){const t=this.db();for(const[n,s]of Object.entries(e))t.chunks.set(n,[s]),t.materialized.set(n,s)}async delete(e){const t=this.db();t.chunks.delete(e),t.materialized.set(e,null)}async deleteMany(e){const t=this.db();for(const n of e)t.chunks.delete(n),t.materialized.set(n,null)}async listKeys(){return Array.from(this.db().chunks.keys())}async exists(e){return this.db().chunks.has(e)}async clear(){const e=this.db();e.chunks.clear(),e.materialized.clear()}}const ne=(()=>{const e=new Uint32Array(256);for(let t=0;t<256;t++){let n=t;for(let e=0;e<8;e++)n=1&n?3988292384^n>>>1:n>>>1;e[t]=n>>>0}return e})(),se=4294967295;function ie(e,t=0){const n=function(e,t){let n=e>>>0;for(let e=0;e<t.byteLength;e++)n=(ne[255&(n^t[e])]^n>>>8)>>>0;return n>>>0}(t^se,e);return function(e){return(e^se)>>>0}(n)}var re;!function(e){e[e.PUT=1]="PUT",e[e.DELETE=2]="DELETE",e[e.APPEND=3]="APPEND"}(re||(re={}));const ae=1263948622;function oe(e,t){const n=new TextEncoder,s=Array.from(t.keys()),i=[];let r=12;for(const e of s){const s=n.encode(e),a=new Uint8Array(t.get(e));i.push({key:s,value:a}),r+=4+s.byteLength+4+a.byteLength}r+=4;const a=new Uint8Array(r),o=new DataView(a.buffer);let c=0;o.setUint32(c,ae,!1),c+=4,o.setUint32(c,e,!1),c+=4,o.setUint32(c,i.length,!1),c+=4;for(const e of i)o.setUint32(c,e.key.byteLength,!1),c+=4,a.set(e.key,c),c+=e.key.byteLength,o.setUint32(c,e.value.byteLength,!1),c+=4,a.set(e.value,c),c+=e.value.byteLength;const h=ie(a.subarray(0,r-4));return o.setUint32(r-4,h,!1),a}function ce(e){if(e.byteLength<16)return null;const t=new DataView(e.buffer,e.byteOffset,e.byteLength);if(t.getUint32(0,!1)!==ae)return null;if(t.getUint32(e.byteLength-4,!1)!==ie(e.subarray(0,e.byteLength-4)))return null;const n=new TextDecoder,s=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;r<a;r++){if(i+4>e.byteLength-4)return null;const r=t.getUint32(i,!1);if(i+=4,i+r+4>e.byteLength-4)return null;const a=n.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 c=e.slice(i,i+o).buffer;i+=o,s.set(a,c)}return{seq:r,entries:s}}const he="__kv_log",le="__kv_snapshot",ue="__kv_meta";class fe{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.instanceId="",this.stale=!1,this.medium=e??function(){const e=globalThis.navigator;return void 0!==e&&e.storage&&"function"==typeof e.storage.getDirectory?new J:new te}(),this.checkpointThreshold=t}isOpen(){return this.opened}async open(e){if(this.opened)return;this.dbName=e,this.lastBackgroundError=null,this.stale=!1,await this.medium.open(e),await this.claimOwnership(),this.index=new Map,this.seq=0,this.logBytes=0;let t=0;const n=await this.medium.read(le);if(n){const e=ce(new Uint8Array(n));e?(this.index=new Map(e.entries),this.seq=e.seq,t=e.seq):(this.index=new Map,this.seq=0,t=0)}const s=await this.medium.read(he);if(s&&s.byteLength>0){const e=new Uint8Array(s),n=t,i=[];(function(e,t,n){let s=0,i=0;const r=new DataView(e.buffer,e.byteOffset,e.byteLength),a=new TextDecoder;for(;s+4<=e.byteLength;){const o=r.getUint32(s,!1);if(o<12||s+4+o>e.byteLength){if(n&&!n(s))break;break}const c=s,h=s+4+o,l=e.subarray(c,h),u=new DataView(e.buffer,e.byteOffset+c,o+4);if(u.getUint32(o,!1)!==ie(l.subarray(0,o))){if(n&&!n(c))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;e<p;e++){if(f+1+4>o+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 n=a.decode(l.subarray(f,f+t));f+=t;const s=u.getUint32(f,!1);if(f+=4,f+s>o+4){m=!1;break}const i=l.slice(f,f+s).buffer;f+=s,y.push({op:e,key:n,value:i})}if(!m){if(n&&!n(c))break;break}t({seq:d,entries:y,raw:l}),i++,s=h}return i}(e,e=>{e.seq<=n||(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.truncateLogTo(this.findValidLogLength(e))}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.lastBackgroundError=null,this.instanceId="",this.stale=!1,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}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 writeBatch(e,t){0===Object.keys(e).length&&0===t.length||await this.enqueue(async()=>{await this.appendRecord(e,t)})}async appendValue(e,t){0!==t.byteLength&&await this.enqueue(async()=>{await this.appendRecord({},[],{[e]:t})})}async checkpoint(){await this.enqueue(async()=>{if(await this.assertOwnership(),null!==this.lastBackgroundError){const e=this.lastBackgroundError;throw this.lastBackgroundError=null,new s("KVStore background write failed","KV_BACKGROUND_ERROR",e)}if(0===this.logBytes&&0===this.index.size)return;const e=oe(this.seq,this.index);await this.medium.write(le,e.buffer),await this.writeMeta(),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(ue,(new TextEncoder).encode(JSON.stringify({seq:0})).buffer)})}async repair(){return this.enqueue(async()=>{let e=0;const t=await this.medium.read(le);t&&!ce(new Uint8Array(t))&&(await this.medium.delete(le),e++);const n=await this.medium.read(he);if(n&&n.byteLength>0){const t=new Uint8Array(n),s=this.findValidLogLength(t);if(s<t.byteLength){e+=t.byteLength-s;const n=t.subarray(0,s).slice().buffer;await this.medium.write(he,n)}}return e})}enqueue(e){const t=this.opQueue.then(e,e);return this.opQueue=t.then(()=>{},()=>{}),t}async appendRecord(e,t,n={}){if(this.stale)throw new s("KVStore instance has been superseded by another instance on the same database; re-open the database to continue","STALE_INSTANCE");this.seq++;const i=function(e,t,n=[],s={}){const i=new TextEncoder,r=[];for(const[e,n]of Object.entries(t))r.push({op:re.PUT,key:e,value:n});for(const e of n)r.push({op:re.DELETE,key:e,value:new ArrayBuffer(0)});for(const[e,t]of Object.entries(s))r.push({op:re.APPEND,key:e,value:t});const a=[];let o=12;for(const e of r){const t=i.encode(e.key),n=new Uint8Array(e.value);a.push({op:e.op,key:t,value:n}),o+=5+t.byteLength+4+n.byteLength}o+=4;const c=new Uint8Array(o),h=new DataView(c.buffer);let l=0;h.setUint32(l,o-4,!1),l+=4,h.setUint32(l,e,!1),l+=4,h.setUint32(l,a.length,!1),l+=4;for(const e of a)h.setUint8(l,e.op),l+=1,h.setUint32(l,e.key.byteLength,!1),l+=4,c.set(e.key,l),l+=e.key.byteLength,h.setUint32(l,e.value.byteLength,!1),l+=4,c.set(e.value,l),l+=e.value.byteLength;const u=ie(c.subarray(0,o-4));return h.setUint32(o-4,u,!1),c}(this.seq,e,t,n);try{const e=i.buffer.slice(i.byteOffset,i.byteOffset+i.byteLength);if("function"==typeof this.medium.append)await this.medium.append(he,e);else{const t=await this.medium.read(he);if(t){const n=new Uint8Array(t.byteLength+e.byteLength);n.set(new Uint8Array(t),0),n.set(new Uint8Array(e),t.byteLength),await this.medium.write(he,n.buffer)}else await this.medium.write(he,e)}}catch(e){throw this.seq--,this.lastBackgroundError=e,new s("KVStore log append failed","KV_LOG_ERROR",e)}for(const[t,n]of Object.entries(e))this.index.set(t,n);for(const e of t)this.index.delete(e);for(const[e,t]of Object.entries(n)){const n=this.index.get(e);if(n){const s=new Uint8Array(n.byteLength+t.byteLength);s.set(new Uint8Array(n),0),s.set(new Uint8Array(t),n.byteLength),this.index.set(e,s.buffer)}else this.index.set(e,t)}this.logBytes+=i.byteLength,this.checkpointThreshold>0&&this.logBytes>=this.checkpointThreshold&&await this.autoCheckpoint()}async autoCheckpoint(){try{await this.assertOwnership(),await this.medium.write(le,oe(this.seq,this.index).buffer),await this.writeMeta(),await this.truncateLog()}catch(e){this.stale||(this.lastBackgroundError=e)}}async truncateLog(){try{await this.medium.write(he,new ArrayBuffer(0))}catch{}this.logBytes=0}async truncateLogTo(e){try{const t=await this.medium.read(he);if(!t)return;if(e>=t.byteLength)return;const n=new Uint8Array(t).subarray(0,e).slice();await this.medium.write(he,n.buffer),this.logBytes=e}catch{}}applyRecord(e){for(const t of e)if(t.op===re.PUT)this.index.set(t.key,t.value);else if(t.op===re.APPEND){const e=this.index.get(t.key);if(e){const n=new Uint8Array(e.byteLength+t.value.byteLength);n.set(new Uint8Array(e),0),n.set(new Uint8Array(t.value),e.byteLength),this.index.set(t.key,n.buffer)}else this.index.set(t.key,t.value)}else this.index.delete(t.key)}newInstanceId(){const e=Math.random().toString(36).slice(2);return`kv-${Date.now().toString(36)}-${e}`}async readMeta(){try{const e=await this.medium.read(ue);return e&&0!==e.byteLength?JSON.parse((new TextDecoder).decode(e)):null}catch{return null}}async writeMeta(){const e={seq:this.seq,owner:this.instanceId};await this.medium.write(ue,(new TextEncoder).encode(JSON.stringify(e)).buffer)}async claimOwnership(){this.instanceId=this.newInstanceId(),await this.writeMeta()}async assertOwnership(){const e=await this.readMeta();if(e?.owner&&this.instanceId&&e.owner!==this.instanceId)throw this.stale=!0,new s("KVStore instance has been superseded by another instance on the same database (refusing to commit a stale snapshot, which would discard the newer instance's writes); re-open the database to continue","STALE_INSTANCE")}isStale(){return this.stale}findValidLogLength(e){let t=0;const n=new DataView(e.buffer,e.byteOffset,e.byteLength);for(;t+4<=e.byteLength;){const s=n.getUint32(t,!1);if(s<12||t+4+s>e.byteLength)break;const i=e.subarray(t,t+4+s),r=n.getUint32(t+s,!1);if(ie(i.subarray(0,s))!==r)break;t+=4+s}return t}}const de="__schema",pe="t:",ye=e=>(new TextEncoder).encode(e).buffer,me=e=>(new TextDecoder).decode(e);class we{constructor(e,t){this.name="kv",this.memory=new Q,this.dbName="",this.version=1,this.opened=!1,this.txActive=!1,this.txDirtyTables=new Set,this.txSchemaChanged=!1,this.txChanges=new Map,this.txFullTables=new Set,this.txClearedTables=new Set,this.kv=new fe(e,t)}rowKey(e,t){return`${pe}${e}:${t}`}rowPrefix(e){return`${pe}${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 n=await this.kv.get(de);if(n)try{const e=JSON.parse(me(n));for(const t of Object.values(e))await this.memory.createTable(t)}catch{throw new s("Corrupted schema in KVStore","KV_SCHEMA_ERROR")}const i=await this.kv.getAll();for(const[e,t]of i){if(!e.startsWith(pe))continue;const n=e.indexOf(":",2);if(n<0)continue;const s=e.slice(2,n);if(await this.memory.hasTable(s))try{const e=JSON.parse(me(t));await this.memory.insert(s,[e])}catch{}}const r=await this.memory.getTableNames();for(const e of r){const t=await this.memory.getTableSchema(e);if(t)for(const[n,s]of Object.entries(t.columns))(s.index||s.unique)&&await this.memory.createIndex(e,n,s.unique)}this.opened=!0}async close(){if(this.opened){if(this.txActive)try{await this.rollbackTransaction()}catch{}try{await this.kv.checkpoint()}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?me(t):null}async setMeta(e,t){await this.kv.put(`__meta:${e}`,ye(t))}async createTable(e){if(this.ensureOpen(),await this.memory.createTable(e),this.txActive)return this.txDirtyTables.add(e.name),void(this.txSchemaChanged=!0);await this.persistSchema()}async dropTable(e){if(this.ensureOpen(),await this.memory.dropTable(e),this.txActive)return this.txDirtyTables.add(e),void(this.txSchemaChanged=!0);await this.persistSchema();const t=await this.collectTableDiff(e);await this.kv.writeBatch(t.puts,t.deletes)}async hasTable(e){return this.ensureOpen(),this.memory.hasTable(e)}async getTableNames(){return this.ensureOpen(),this.memory.getTableNames()}async getTableSchema(e){return this.ensureOpen(),this.memory.getTableSchema(e)}async alterTable(e,t,n){if(this.ensureOpen(),this.txActive)throw new s("ALTER TABLE is not supported inside a transaction (KVStoreEngine DDL is not transactional)","NOT_SUPPORTED");if(await this.memory.alterTable(e,t,n),this.txActive)return this.txDirtyTables.add(e),void(this.txSchemaChanged=!0);if(await this.persistSchema(),"DROP"===t){const t=await this.collectTableDiff(e);await this.kv.writeBatch(t.puts,t.deletes)}}async validatePayload(e,t,n="insert"){return this.ensureOpen(),this.memory.validatePayload(e,t,n)}async insert(e,t){this.ensureOpen();const n=await this.memory.insert(e,t);if(this.txActive){if(this.txDirtyTables.add(e),this.txClearedTables.has(e))return this.txClearedTables.delete(e),this.txFullTables.add(e),n;let t=this.txChanges.get(e);t||(t=new Map,this.txChanges.set(e,t));for(const e of n)t.set(e,"put");return n}if(!await this.memory.getTableSchema(e))throw new s(`Table "${e}" does not exist`,"TABLE_NOT_FOUND");const i={};for(const t of n){const n=this.memory.getRow(e,t);n&&(i[this.rowKey(e,t)]=ye(JSON.stringify(n)))}return await this.kv.putMany(i),n}async find(e,t){return this.ensureOpen(),this.memory.find(e,t)}async findStream(e,t,n){return this.ensureOpen(),this.memory.findStream(e,t,n)}async update(e,t,n){this.ensureOpen();const i=await this.memory.getTableSchema(e);if(!i)throw new s(`Table "${e}" does not exist`,"TABLE_NOT_FOUND");const r=this.getPK(i),a=G(n),o=r in a,c=o?[]:await this.collectMatchingPks(e,t),h=await this.memory.update(e,t,a);if(this.txActive){if(this.txDirtyTables.add(e),o)for(const t of await this.affectedTables(e))this.txFullTables.add(t),this.txDirtyTables.add(t);else{let t=this.txChanges.get(e);t||(t=new Map,this.txChanges.set(e,t));for(const e of c)t.set(e,"put");for(const t of await this.affectedTables(e))t!==e&&(this.txFullTables.add(t),this.txDirtyTables.add(t))}return h}const l={},u=[];if(o)for(const t of await this.affectedTables(e)){const e=await this.collectTableDiff(t);Object.assign(l,e.puts),u.push(...e.deletes)}else{const t=new Set(c),n=await this.memory.find(e,{table:e});for(const s of n){const n=String(s[r]);t.has(n)&&(l[this.rowKey(e,n)]=ye(JSON.stringify(s)),t.delete(n))}for(const n of t)u.push(this.rowKey(e,n));for(const t of await this.affectedTables(e)){if(t===e)continue;const n=await this.collectTableDiff(t);Object.assign(l,n.puts),u.push(...n.deletes)}}return await this.kv.writeBatch(l,u),h}async delete(e,t){this.ensureOpen();const n=await this.collectMatchingPks(e,t),s=await this.memory.delete(e,t);if(this.txActive){this.txDirtyTables.add(e);let t=this.txChanges.get(e);t||(t=new Map,this.txChanges.set(e,t));for(const e of n)t.set(e,"delete");for(const t of await this.affectedTables(e))t!==e&&(this.txFullTables.add(t),this.txDirtyTables.add(t));return s}const i={},r=n.map(t=>this.rowKey(e,t));for(const t of await this.affectedTables(e)){if(t===e)continue;const n=await this.collectTableDiff(t);Object.assign(i,n.puts),r.push(...n.deletes)}return await this.kv.writeBatch(i,r),s}async count(e,t){return this.ensureOpen(),this.memory.count(e,t)}async clear(e){if(this.ensureOpen(),await this.memory.clear(e),this.txActive)return this.txDirtyTables.add(e),this.txClearedTables.add(e),void this.txChanges.delete(e);const t=await this.collectTableDiff(e);await this.kv.writeBatch(t.puts,t.deletes)}async createIndex(e,t,n){if(this.ensureOpen(),this.txActive)throw new s("CREATE INDEX is not supported inside a transaction (KVStoreEngine DDL is not transactional)","NOT_SUPPORTED");if(await this.memory.createIndex(e,t,n),this.txActive)return this.txDirtyTables.add(e),void(this.txSchemaChanged=!0);await this.persistSchema()}async dropIndex(e,t,n){if(this.ensureOpen(),this.txActive)throw new s("DROP INDEX is not supported inside a transaction (KVStoreEngine DDL is not transactional)","NOT_SUPPORTED");if(await this.memory.dropIndex(e,t,n),this.txActive)return this.txDirtyTables.add(e),void(this.txSchemaChanged=!0);await this.persistSchema()}async beginTransaction(){this.ensureOpen(),await this.memory.beginTransaction(),this.txActive=!0,this.txDirtyTables=new Set,this.txSchemaChanged=!1,this.txChanges=new Map,this.txFullTables=new Set,this.txClearedTables=new Set}async commitTransaction(){if(this.ensureOpen(),!this.txActive)throw new s("No active transaction","TX_NONE");const e={},t=[];for(const n of this.txDirtyTables){if(!await this.memory.hasTable(n)){const e=await this.kv.getAll(),s=this.rowPrefix(n);for(const[n]of e)n.startsWith(s)&&t.push(n);continue}if(this.txClearedTables.has(n)){const e=await this.kv.getAll(),s=this.rowPrefix(n);for(const[n]of e)n.startsWith(s)&&t.push(n);continue}if(this.txFullTables.has(n)){const s=await this.collectTableDiff(n);Object.assign(e,s.puts),t.push(...s.deletes);continue}const s=this.txChanges.get(n);if(s&&s.size>0){const i=await this.memory.getTableSchema(n);if(!i)continue;const r=this.getPK(i),a=new Map(s),o=await this.memory.find(n,{table:n});for(const s of o){const i=String(s[r]),o=a.get(i);void 0!==o&&("put"===o?e[this.rowKey(n,i)]=ye(JSON.stringify(s)):t.push(this.rowKey(n,i)),a.delete(i))}for(const[e,s]of a)"delete"===s&&t.push(this.rowKey(n,e))}}await this.kv.writeBatch(e,t),this.txSchemaChanged&&await this.persistSchema(),await this.memory.commitTransaction(),this.txActive=!1,this.txDirtyTables=new Set,this.txChanges=new Map,this.txFullTables=new Set,this.txClearedTables=new Set}async rollbackTransaction(){if(this.ensureOpen(),!this.txActive)throw new s("No active transaction","TX_NONE");await this.memory.rollbackTransaction(),this.txActive=!1,this.txDirtyTables=new Set,this.txSchemaChanged=!1,this.txChanges=new Map,this.txFullTables=new Set,this.txClearedTables=new Set}ensureOpen(){if(!this.opened)throw new s("Database not opened","DB_NOT_OPEN")}getPK(e){for(const[t,n]of Object.entries(e.columns))if(n.primaryKey)return t;return Object.keys(e.columns)[0]}async collectMatchingPks(e,t){const n=await this.memory.getTableSchema(e);if(!n)throw new s(`Table "${e}" does not exist`,"TABLE_NOT_FOUND");const i=this.getPK(n);return(await this.memory.find(e,t)).map(e=>String(e[i]))}async affectedTables(e){const t=new Set([e]);let n=!0;for(;n;){n=!1;for(const e of await this.memory.getTableNames()){if(t.has(e))continue;const s=await this.memory.getTableSchema(e);if(s)for(const i of Object.values(s.columns))if(i.references){const s=i.references.split(".")[0];if(t.has(s)){t.add(e),n=!0;break}}}}return t}async persistSchema(){const e={};for(const t of await this.memory.getTableNames()){const n=await this.memory.getTableSchema(t);n&&(e[t]=n)}await this.kv.put(de,ye(JSON.stringify(e)))}async collectTableDiff(e){const t=this.rowPrefix(e),n={},s=[],i=await this.memory.getTableSchema(e);if(i){const r=this.getPK(i),a=await this.memory.find(e,{table:e}),o=new Set;for(const t of a){const s=this.rowKey(e,String(t[r]));o.add(s),n[s]=ye(JSON.stringify(t))}const c=await this.kv.getAll();for(const[e]of c)e.startsWith(t)&&!o.has(e)&&s.push(e)}else{const e=await this.kv.getAll();for(const[n]of e)n.startsWith(t)&&s.push(n)}return{puts:n,deletes:s}}}const ge=4096;var be;!function(e){e[e.DATA=1]="DATA",e[e.INDEX=2]="INDEX",e[e.OVERFLOW=3]="OVERFLOW",e[e.META=4]="META"}(be||(be={}));const Te=4194304;var Ee,ke;!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",e[e.SAVEPOINT_ROLLBACK=9]="SAVEPOINT_ROLLBACK",e[e.ALTER_TABLE=10]="ALTER_TABLE"}(Ee||(Ee={})),function(e){e[e.ACTIVE=1]="ACTIVE",e[e.COMMITTED=2]="COMMITTED",e[e.ABORTED=3]="ABORTED"}(ke||(ke={}));const Se={pageSize:ge,bufferPoolPages:256,memtableSizeThreshold:Te,levelSizeMultiplier:10,bloomFilterBitsPerKey:10,walEnabled:!0,walSyncMode:"full",checkpointInterval:1e3,compression:!1,storageBackend:"opfs",walSizeThreshold:16777216,maxMemoryMB:64,encryption:void 0,pageStorage:void 0,testBackend:void 0};var xe;!function(e){e[e.RED=0]="RED",e[e.BLACK=1]="BLACK"}(xe||(xe={}));class Ae{constructor(e,t){this.color=xe.RED,this.left=null,this.right=null,this.parent=null,this.key=e,this.value=t}}class Ie{constructor(){this.root=null,this._size=0}get size(){return this._size}insert(e,t){const n=new Ae(e,t);if(!this.root)return this.root=n,n.color=xe.BLACK,void this._size++;let s=null,i=this.root;for(;i;)if(s=i,e<i.key)i=i.left;else{if(!(e>i.key))return void(i.value=t);i=i.right}n.parent=s,e<s.key?s.left=n:s.right=n,this._size++,this.fixInsert(n)}find(e){let t=this.root;for(;t;)if(e<t.key)t=t.left;else{if(!(e>t.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,n){this._rangeScan(this.root,e,t,n)}*scanLazy(e,t){const n=[];let s=this.root;for(;s;)s.key>=e?(n.push(s),s=s.left):s=s.right;for(;n.length>0;){const i=n.pop();if(i.key>t)break;for(i.key>=e&&(yield[i.key,i.value]),s=i.right;s;)n.push(s),s=s.left}}getAllEntries(){const e=[];return this.inorder((t,n)=>e.push([t,n])),e}clear(){this.root=null,this._size=0}findNode(e){let t=this.root;for(;t;)if(e<t.key)t=t.left;else{if(!(e>t.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),n=t.right,s=t.parent;s!==e&&(this.transplant(t,n),t.right=e.right,t.right.parent=t),this.transplant(e,t),t.left=e.left,t.left.parent=t;const i=t.color;if(t.color=e.color,i===xe.BLACK){const i=n,r=i?i.parent:s===e?t:s;this.fixDelete(i,r)}}else this.transplant(e,e.left),e.color===xe.BLACK&&this.fixDelete(e.left,e.left.parent);else this.transplant(e,e.right),e.color===xe.BLACK&&this.fixDelete(e.right,e.right.parent);else this.transplant(e,null),e.color===xe.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===xe.RED;){const t=e.parent,n=t.parent;if(!n)break;if(t===n.left){const s=n.right;s&&s.color===xe.RED?(t.color=xe.BLACK,s.color=xe.BLACK,n.color=xe.RED,e=n):(e===t.right&&(e=t,this.rotateLeft(e)),e.parent&&(e.parent.color=xe.BLACK),e.parent?.parent&&(e.parent.parent.color=xe.RED),e.parent?.parent&&this.rotateRight(e.parent.parent))}else{const s=n.left;s&&s.color===xe.RED?(t.color=xe.BLACK,s.color=xe.BLACK,n.color=xe.RED,e=n):(e===t.left&&(e=t,this.rotateRight(e)),e.parent&&(e.parent.color=xe.BLACK),e.parent?.parent&&(e.parent.parent.color=xe.RED),e.parent?.parent&&this.rotateLeft(e.parent.parent))}}this.root&&(this.root.color=xe.BLACK)}fixDelete(e,t){let n=e,s=t;for(;(!n||n.color===xe.BLACK)&&n!==this.root&&s;)if(n===s.left){let e=s.right;if(!e)break;if(e.color===xe.RED&&(e.color=xe.BLACK,s.color=xe.RED,this.rotateLeft(s),e=s.right,!e))break;const t=e.left,i=e.right;if(t&&t.color!==xe.BLACK||i&&i.color!==xe.BLACK){if(!(i&&i.color!==xe.BLACK||(t&&(t.color=xe.BLACK),e.color=xe.RED,this.rotateRight(e),e=s.right,e)))break;e.color=s.color,s.color=xe.BLACK,e.right&&(e.right.color=xe.BLACK),this.rotateLeft(s),n=this.root}else e.color=xe.RED,n=s,s=n.parent}else{let e=s.left;if(!e)break;if(e.color===xe.RED&&(e.color=xe.BLACK,s.color=xe.RED,this.rotateRight(s),e=s.left,!e))break;const t=e.left,i=e.right;if(t&&t.color!==xe.BLACK||i&&i.color!==xe.BLACK){if(!(t&&t.color!==xe.BLACK||(i&&(i.color=xe.BLACK),e.color=xe.RED,this.rotateLeft(e),e=s.left,e)))break;e.color=s.color,s.color=xe.BLACK,e.left&&(e.left.color=xe.BLACK),this.rotateRight(s),n=this.root}else e.color=xe.RED,n=s,s=n.parent}n&&(n.color=xe.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,n,s){e&&(e.key>t&&this._rangeScan(e.left,t,n,s),e.key>=t&&e.key<=n&&s(e.key,e.value),e.key<n&&this._rangeScan(e.right,t,n,s))}}class Re{constructor(e=4194304){this._estimatedSize=0,this.tree=new Ie,this.maxSize=e}put(e,t){const n=this.estimateEntrySize(e,this.tree.find(e)),s=this.estimateEntrySize(e,t);this.tree.insert(e,t),this._estimatedSize+=s-n}get(e){return this.tree.find(e)}delete(e){const t=this.tree.find(e);return t&&(this._estimatedSize-=this.estimateEntrySize(e,t)),this.tree.delete(e)}shouldFlush(){return this._estimatedSize>=this.maxSize}getAllEntries(){return this.tree.getAllEntries()}rangeScan(e,t){const n=[];return this.tree.rangeScan(e,t,(e,t)=>n.push([e,t])),n}scanLazy(e,t){return this.tree.scanLazy(e,t)}getEntryCount(){return this.tree.size}getEstimatedSize(){return this._estimatedSize}clear(){this.tree.clear(),this._estimatedSize=0}estimateEntrySize(e,t){if(!t)return 0;let n=2*e.length;for(const e of Object.entries(t)){n+=2*e[0].length;const t=e[1];n+="string"==typeof t?2*t.length:"number"==typeof t?8:"boolean"==typeof t||null==t?1:16}return n}}class Oe{constructor(e,t=10){this._inserted=0;const n=Math.max(64,e*t),s=Math.ceil(n/8);this.bits=new Uint8Array(s),this.numHashes=Math.max(1,Math.floor(.69*t))}static fromData(e,t){const n=new Oe(1);return n.bits=e,n.numHashes=t,n}insert(e){const t=this.getHashes(e);for(const e of t){const t=Math.floor(e/8),n=e%8;this.bits[t]|=1<<n}this._inserted++}mayContain(e){const t=this.getHashes(e);for(const e of t){const t=Math.floor(e/8),n=e%8;if(!(this.bits[t]&1<<n))return!1}return!0}serialize(){return this.bits}getHashCount(){return this.numHashes}getHashes(e){const t=8*this.bits.byteLength,n=this.fnv1a(e),s=this.murmurSimple(e),i=[];for(let e=0;e<this.numHashes;e++){const r=Math.abs((n+e*s)%t);i.push(r)}return i}fnv1a(e){let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t=16777619*t>>>0;return t}murmurSimple(e){let t=0;for(let n=0;n<e.length;n++)t=(t<<5)-t+e.charCodeAt(n)|0,t=(t^t>>>16)>>>0;return Math.abs(t)}}const Ne=1397969987;class Ce{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,n])=>({key:t,keyBytes:e.encode(t),valueBytes:e.encode(JSON.stringify(n))})),n=this.splitIntoBlocks(t),s=new Oe(this.entries.length);let i=0;const r=[];for(const e of n)r.push(i),i+=this.computeBlockSize(e);const a=[];for(let e=0;e<n.length;e++){const t=n[e];a.push({key:t[t.length-1].key,blockOffset:r[e],blockSize:this.computeBlockSize(t)})}const o=this.estimateIndexBlockSize(a),c=s.serialize(),h=c.byteLength,l=new ArrayBuffer(i+o+h+32),u=new DataView(l);let f=0;for(const e of n)f=this.writeDataBlock(u,f,e,s);const d=f;f=this.writeIndexBlock(u,f,a);const p=f;new Uint8Array(u.buffer).set(c,f),f+=h;const y=f;u.setUint32(y,d,!1),u.setUint32(y+4,o,!1),u.setUint32(y+8,p,!1),u.setUint32(y+12,h,!1),u.setUint32(y+16,s.getHashCount(),!1),u.setUint32(y+20,this.entries.length,!1),u.setUint32(y+24,Ne,!1),u.setUint32(y+28,0,!1);const m=new Uint8Array(l);let w=ie(m.subarray(0,m.byteLength-4));return 0===w&&(w=1),u.setUint32(y+28,w,!1),{sstableData:new Uint8Array(l),indexEntries:a}}getEntryCount(){return this.entries.length}splitIntoBlocks(e){const t=[];let n=[];for(const s of e)n.push(s),this.computeBlockSize(n)>=this.blockSizeLimit&&n.length>1&&(t.push(n.slice(0,-1)),n=[s]);return n.length>0&&t.push(n),t}computeBlockSize(e){let t=4;for(const n of e)t+=4+n.keyBytes.length+4+n.valueBytes.length;return t}writeDataBlock(e,t,n,s){e.setUint32(t,n.length,!1),t+=4;for(const i of n){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,s.insert(i.key)}return t}estimateIndexBlockSize(e){let t=4;const n=new TextEncoder;for(const s of e)t+=4+n.encode(s.key).byteLength+8;return t}writeIndexBlock(e,t,n){e.setUint32(t,n.length,!1),t+=4;for(const s of n){const n=(new TextEncoder).encode(s.key);e.setUint32(t,n.length,!1),t+=4,new Uint8Array(e.buffer).set(n,t),t+=n.length,e.setUint32(t,s.blockOffset,!1),t+=4,e.setUint32(t,s.blockSize,!1),t+=4}return t}}class $e{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)&&ie(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 n=this.indexEntries[t],s=this.getBlockData(n);if(!s)return null;const i=new DataView(s.buffer,s.byteOffset,s.byteLength),r=this.lenFieldSize(),a=i.getUint32(0,!1);let o=4;for(let t=0;t<a&&!(o+r>s.byteLength);t++){const t=2===this.format?i.getUint32(o,!1):i.getUint16(o,!1);if(o+=r,o+t+r>s.byteLength)break;const n=(new TextDecoder).decode(s.slice(o,o+t));o+=t;const a=2===this.format?i.getUint32(o,!1):i.getUint16(o,!1);if(o+=r,o+a>s.byteLength)break;const c=s.slice(o,o+a);if(o+=a,n===e)try{return JSON.parse((new TextDecoder).decode(c))}catch{return null}}return null}rangeScan(e,t,n){for(const[s,i]of this.scanLazy(e,t))n(s,i)}*scanLazy(e,t){if(0===this.indexEntries.length)return;const n=Math.max(0,this.locateBlockGE(e)),s=Math.min(this.indexEntries.length-1,this.locateBlockLE(t)+1);if(n<0||s<0||n>s)return;const i=this.lenFieldSize();for(let r=n;r<=s&&r>=0;r++){const n=this.indexEntries[r],s=this.getBlockData(n);if(!s)continue;const a=new DataView(s.buffer,s.byteOffset,s.byteLength),o=a.getUint32(0,!1);let c=4;for(let n=0;n<o&&!(c+i>s.byteLength);n++){const n=2===this.format?a.getUint32(c,!1):a.getUint16(c,!1);if(c+=i,c+n+i>s.byteLength)break;const r=(new TextDecoder).decode(s.slice(c,c+n));c+=n;const o=2===this.format?a.getUint32(c,!1):a.getUint16(c,!1);if(c+=i,c+o>s.byteLength)break;const h=s.slice(c,c+o);if(c+=o,r>=e&&r<=t)try{const e=JSON.parse((new TextDecoder).decode(h));yield[r,e]}catch{}}}}scanAll(e){const t=this.lenFieldSize();for(const n of this.indexEntries){const s=this.getBlockData(n);if(!s)continue;const i=new DataView(s.buffer,s.byteOffset,s.byteLength),r=i.getUint32(0,!1);let a=4;for(let n=0;n<r&&!(a+t>s.byteLength);n++){const n=2===this.format?i.getUint32(a,!1):i.getUint16(a,!1);if(a+=t,a+n+t>s.byteLength)break;const r=(new TextDecoder).decode(s.slice(a,a+n));a+=n;const o=2===this.format?i.getUint32(a,!1):i.getUint16(a,!1);if(a+=t,a+o>s.byteLength)break;const c=s.slice(a,a+o);a+=o;try{e(r,JSON.parse((new TextDecoder).decode(c)))}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!==Ne)throw new Error(`Invalid SSTable magic: expected 1397969986 or 1397969987, got ${t}`);this.format=2}const n=this.view.getUint32(e,!1),s=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),!(n+4>this.data.byteLength||n+s>this.data.byteLength)&&(this.parseIndexBlock(n,s),i>0&&r>0&&i+r<=this.data.byteLength))try{const e=this.data.slice(i,i+r);this.bloomFilter=Oe.fromData(e,a||10)}catch{}}parseIndexBlock(e,t){const n=this.view.getUint32(e,!1);e+=4;const s=this.lenFieldSize();for(let t=0;t<n&&!(e+s>this.data.byteLength);t++){const t=this.readLen(e);if((e+=s)+t+8>this.data.byteLength)break;const n=(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:n,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,n=this.indexEntries.length-1;for(;t<=n;){const s=Math.floor((t+n)/2);if(e<=this.indexEntries[s].key){if(e>(0===s?"":this.indexEntries[s-1].key))return s;n=s-1}else t=s+1}return-1}locateBlockGE(e){let t=0,n=this.indexEntries.length;for(;t<n;){const s=t+n>>1;this.indexEntries[s].key<e?t=s+1:n=s}return t<this.indexEntries.length?t:this.indexEntries.length-1}locateBlockLE(e){let t=0,n=this.indexEntries.length;for(;t<n;){const s=t+n>>1;this.indexEntries[s].key<=e?t=s+1:n=s}return t>0?t-1:0}}class Le{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 ve{constructor(e){this.iter=e}next(){const e=this.iter.next();return e.done?null:e.value}reset(){}}class Ue{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 n=e;const s=2*e+1,i=2*e+2;if(s<t&&this.heap[s].key<this.heap[n].key&&(n=s),i<t&&this.heap[i].key<this.heap[n].key&&(n=i),n===e)break;[this.heap[e],this.heap[n]]=[this.heap[n],this.heap[e]],e=n}}}class De{constructor(){this.sources=[],this.heap=new Ue}addSource(e){this.sources.push(e),this.seedFromSource(this.sources.length-1)}next(){if(0===this.heap.size)return null;const e=this.heap.pop(),t=e.key;let n=e;for(this.seedFromSource(e.sourceIndex);this.heap.peek()&&this.heap.peek().key===t;){const e=this.heap.pop();this.seedFromSource(e.sourceIndex),e.sourceIndex<n.sourceIndex&&(n=e)}return[n.key,n.value]}drain(){const e=[];let t=this.next();for(;t;)e.push(t),t=this.next();return e}seedFromSource(e){const t=this.sources[e].next();t&&this.heap.push({key:t[0],value:t[1],sourceIndex:e})}}class _e{constructor(e){this.immutableMemtable=null,this.frozenMemtables=[],this.levels=[],this.sstableCache=new Map,this.cacheSize=0,this.oversizedSSTables=new Set,this.operationCount=0,this.initialized=!1,this.compacting=!1,this.flushChain=Promise.resolve(),this.lastBackgroundError=null,this.memtableSizeThreshold=e.memtableSizeThreshold??Te,this.memtable=new Re(this.memtableSizeThreshold),this.levelSizeMultiplier=e.levelSizeMultiplier??10,this.blockSize=e.blockSize??4096,this.sstableStore=e.sstableStore,this.cacheLimitBytes=e.cacheLimitBytes??67108864;for(let e=0;e<7;e++)this.levels.push([])}async init(){if(this.initialized)return;const e=await this.sstableStore.listMeta(),t=[];for(const n of e)await this.validateSSTable(n)&&t.push(n);for(const e of t)e.level>=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()}getCacheSize(){return this.cacheSize}getCacheLimit(){return this.cacheLimitBytes}getOversizedCount(){return this.oversizedSSTables.size}setCacheLimit(e){this.cacheLimitBytes=Math.max(0,e),this.trimCache()}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 Re(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 n=await this.sstableStore.allocateId(),s=new Ce(this.blockSize);for(const[e,n]of t)s.add(e,n);const{sstableData:i,indexEntries:r}=s.build(),a={id:n,level:0,minKey:t[0][0],maxKey:t[t.length-1][0],blockCount:r.length,totalSize:i.byteLength,bloomData:null};this.tryCacheSSTable(n,i),this.trimCache();const o=await this.sstableStore.save(n,i);a.totalSize=o.storedSize,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 n=[];for(let s=0;s<7;s++)for(const i of this.levels[s])t<i.minKey||e>i.maxKey||this.sstableCache.has(i.id)||n.push(i.id);for(const e of n){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 n=0;n<7;n++)for(const s of this.levels[n])if(!this.sstableCache.has(s.id))for(const n of e)if(n>=s.minKey&&n<=s.maxKey){t.add(s.id);break}for(const e of t){const t=this.findMetaById(e);await this.preloadSSTable(e,t)}}async prefetchPrefixRanges(e){if(0===e.length)return;const t=new Set,n=[];for(const s of e){const e=`${s[0]}\0${s[1]}`;t.has(e)||(t.add(e),n.push(s))}if(0===n.length)return;await this.drainChain(),this.trimCache();const s=new Set;for(let e=0;e<7;e++)for(const t of this.levels[e])if(!this.sstableCache.has(t.id))for(const[e,i]of n)if(!(i<t.minKey||e>t.maxKey)){s.add(t.id);break}for(const e of s){const t=this.findMetaById(e);await this.preloadSSTable(e,t)}}findMetaById(e){for(let t=0;t<7;t++)for(const n of this.levels[t])if(n.id===e)return n}async get(e){let t=this.memtable.get(e);if(null!==t)return this.unwrapTombstone(t);for(let n=this.frozenMemtables.length-1;n>=0;n--)if(t=this.frozenMemtables[n].get(e),null!==t)return this.unwrapTombstone(t);for(let t=0;t<7;t++)for(const n of this.levels[t]){if(e<n.minKey||e>n.maxKey)continue;const t=await this.loadSSTableReader(n);if(!t)continue;const s=t.get(e);if(null!==s)return this.unwrapTombstone(s)}return null}async rangeScan(e,t){const n=[];return await this.rangeScanLazy(e,t,(e,t)=>{n.push([e,t])}),n}async rangeScanLazy(e,t,n){const s=[];for(let n=0;n<7;n++)for(const i of this.levels[n]){if(t<i.minKey||e>i.maxKey)continue;const n=await this.loadSSTableReader(i);n&&s.push({meta:i,reader:n})}const i=new De;i.addSource(new ve(this.memtable.scanLazy(e,t)));for(let n=this.frozenMemtables.length-1;n>=0;n--)i.addSource(new ve(this.frozenMemtables[n].scanLazy(e,t)));for(const{reader:n}of s)i.addSource(new ve(n.scanLazy(e,t)));let r=i.next();for(;r;){const[e,t]=r;if(!t.__tombstone&&!1===n(e,t))return;r=i.next()}}async compactLevel(e){await this.compactLevelAsync(e,2)}async compactLevelAsync(e,t=4){if(e>=6)return;if(this.levels[e].length<t)return;const n=this.levels[e].splice(0,this.levels[e].length),s=new De,i=[];for(const e of n){let t,n=this.sstableCache.get(e.id)??null;if(!n)try{n=await this.sstableStore.load(e.id)}catch{n=null}if(!n||n.byteLength<32){await this.dropInvalidSSTable(e);continue}try{if(t=new $e(n,e),!t.verifyChecksum()){await this.dropInvalidSSTable(e);continue}}catch{await this.dropInvalidSSTable(e);continue}const r=[];t.scanAll((e,t)=>r.push([e,t])),s.addSource(new Le(r)),i.push(e)}const r=s.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 Ce(this.blockSize);for(const[e,t]of r)o.add(e,t);const{sstableData:c,indexEntries:h}=o.build(),l={id:a,level:e+1,minKey:r[0][0],maxKey:r[r.length-1][0],blockCount:h.length,totalSize:c.byteLength,bloomData:null};this.tryCacheSSTable(a,c),this.trimCache();const u=await this.sstableStore.save(a,c);l.totalSize=u.storedSize,await this.sstableStore.saveMeta(l),this.levels[e+1].unshift(l);for(const e of n)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 s("AriaEngine background flush/compaction failed (data may be inconsistent)","ARIA_BACKGROUND_ERROR",e)}if(await this.drainChain(),this.immutableMemtable){const e=this.immutableMemtable;this.flushChain=this.enqueueOnChain(()=>this.flushImmutableAsync(e)),this.immutableMemtable=null}if(this.memtable.getEntryCount()>0){this.freezeMemtable();const e=this.immutableMemtable;e&&(this.flushChain=this.enqueueOnChain(()=>this.flushImmutableAsync(e)),this.immutableMemtable=null)}await this.drainChain(),this.frozenMemtables=this.frozenMemtables.filter(e=>e.getEntryCount()>0)}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,this.oversizedSSTables.clear()}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 n=[];for(const s of this.levels[t])await this.validateSSTable(s)?n.push(s):(this.sstableCache.delete(s.id),e++);this.levels[t]=n}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 $e(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}async loadSSTableReader(e){let t=this.sstableCache.get(e.id);if(!t){const n=await this.sstableStore.load(e.id);if(!n)return await this.dropInvalidSSTable(e),null;try{if(!new $e(n,e).verifyChecksum())return await this.dropInvalidSSTable(e),null}catch{return await this.dropInvalidSSTable(e),null}this.tryCacheSSTable(e.id,n),t=n}if(this.sstableCache.has(e.id)){const t=this.sstableCache.get(e.id);this.sstableCache.delete(e.id),this.sstableCache.set(e.id,t)}try{return new $e(t,e)}catch{return null}}async preloadSSTable(e,t){if(this.sstableCache.has(e))return;const n=await this.sstableStore.load(e);if(n){if(t)try{if(!new $e(n,t).verifyChecksum())return void await this.dropInvalidSSTable(t)}catch{return void await this.dropInvalidSSTable(t)}this.tryCacheSSTable(e,n)}}tryCacheSSTable(e,t){t.byteLength>this.cacheLimitBytes?this.oversizedSSTables.add(e):this.oversizedSSTables.delete(e),this.cacheSSTable(e,t)}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(){let e=0;const t=this.sstableCache.size;let n=this.sstableCache.keys().next().value;for(;this.cacheSize>this.cacheLimitBytes&&e<t&&void 0!==n;){const t=this.nextCacheKey(n);if(e++,!this.oversizedSSTables.has(n)){const e=this.sstableCache.get(n);e&&(this.cacheSize-=e.byteLength,this.sstableCache.delete(n))}n=t}}nextCacheKey(e){let t=!1;for(const n of this.sstableCache.keys()){if(t)return n;n===e&&(t=!0)}}}class Me{constructor(e,t=!0,n="batch"){this.lsn=0,this.buffer=[],this.bufferedBytes=0,this.store=e,this.enabled=t,this.syncMode=n}async append(e){if(!this.enabled)return;this.lsn++;const t={...e,lsn:this.lsn,checksum:0},n=this.encodeRecord(t);"full"===this.syncMode?(await this.store.append(n),this.bufferedBytes+=n.byteLength):"batch"===this.syncMode&&(this.buffer.push(n),this.bufferedBytes+=n.byteLength)}async appendBatch(e){if(!this.enabled||0===e.length)return;const t=[];for(const n of e)this.lsn++,t.push(this.encodeRecord({...n,lsn:this.lsn,checksum:0}));const n=this.mergeChunks(t);"full"===this.syncMode?(await this.store.append(n),this.bufferedBytes+=n.byteLength):"batch"===this.syncMode&&(this.buffer.push(n),this.bufferedBytes+=n.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),n=new Uint8Array(t);let s=0;for(const t of e)n.set(t,s),s+=t.byteLength;return n}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 n=this.decodeAllRecords(t);for(const t of n)e(t);return this.lsn=n.length>0?n[n.length-1].lsn:0,n.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 n=0;n<e.length;n++)t=(t<<5)-t+e[n]|0;return t>>>0}encodeRecord(e){const t=new TextEncoder,n=t.encode(e.tableName),s=t.encode(e.key),i=e.data?JSON.stringify(e.data):"",r=t.encode(i),a=11+n.length+2+s.length+4+r.length+4,o=new ArrayBuffer(a),c=new DataView(o);let h=0;c.setUint32(h,e.lsn,!1),h+=4,c.setUint8(h,e.type),h+=1,c.setUint32(h,e.txnId,!1),h+=4,c.setUint16(h,n.length,!1),h+=2,new Uint8Array(o).set(n,h),h+=n.length,c.setUint16(h,s.length,!1),h+=2,new Uint8Array(o).set(s,h),h+=s.length,c.setUint32(h,r.length,!1),h+=4,new Uint8Array(o).set(r,h),h+=r.length;const l=ie(new Uint8Array(o,0,h));return c.setUint32(h,l,!1),new Uint8Array(o)}decodeAllRecords(e){const t=[],n=new DataView(e.buffer,e.byteOffset,e.byteLength);let s=0;for(;s+15<=e.byteLength;)try{const i=s,r=n.getUint32(s,!1);s+=4;const a=n.getUint8(s);s+=1;const o=n.getUint32(s,!1);s+=4;const c=n.getUint16(s,!1);if(s+=2,s+c>e.byteLength)break;const h=(new TextDecoder).decode(e.slice(s,s+c));s+=c;const l=n.getUint16(s,!1);if(s+=2,s+l>e.byteLength)break;const u=(new TextDecoder).decode(e.slice(s,s+l));s+=l;const f=n.getUint32(s,!1);if(s+=4,s+f>e.byteLength)break;let d;if(f>0){const t=(new TextDecoder).decode(e.slice(s,s+f));try{d=JSON.parse(t)}catch{}}s+=f;const p=n.getUint32(s,!1);s+=4;const y=e.slice(i,s-4),m=ie(y),w=this.legacyChecksum(y);if(m>>>0!==p&&w>>>0!==p)continue;t.push({lsn:r,type:a,txnId:o,tableName:h,key:u,data:d,checksum:p})}catch{break}return t}}const Pe="__wal_",Be=/^__wal_(\d{6})\.bin$/,Ke=/^__wal_(\d+)$/,qe="__wal_count";class Fe{constructor(e,t=4194304){this.backend=e,this.currentSegment=0,this.currentSize=0,this.segmentSize=t}segmentKey(e){return`${Pe}${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),n=e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength);if("function"==typeof this.backend.append)await this.backend.append(t,n);else{const e=await this.backend.read(t);if(e){const s=new ArrayBuffer(e.byteLength+n.byteLength);new Uint8Array(s).set(new Uint8Array(e),0),new Uint8Array(s).set(new Uint8Array(n),e.byteLength),await this.backend.write(t,s)}else await this.backend.write(t,n)}this.currentSize+=e.byteLength}async readAll(){const e=await this.backend.listKeys(),t=e.filter(e=>Be.test(e)).map(e=>({seq:Number(e.match(Be)[1]),key:e})).sort((e,t)=>e.seq-t.seq);let n=0;for(let e=0;e<t.length&&t[e].seq===e;e++)n=e+1;const s=t.slice(0,n),i=e.filter(e=>Ke.test(e)).map(e=>({seq:Number(e.match(Ke)[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 s){const t=await this.backend.read(e);t&&r.push(new Uint8Array(t))}if(s.length>0){this.currentSegment=s[s.length-1].seq;const e=await this.backend.read(s[s.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 c=0;for(const e of r)o.set(e,c),c+=e.byteLength;return o}async truncate(){const e=(await this.backend.listKeys()).filter(e=>e.startsWith(Pe)||e===qe);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(Pe)||e===qe)}}class je{constructor(){this.acquired=!1,this.supported=!1,this.releaseResolve=null,this.releasePromise=null}async acquire(e){const t=globalThis.navigator,n=t?.locks;return n&&"function"==typeof n.request?(this.supported=!0,await new Promise((t,i)=>{n.request.bind(n)(function(e){return`metona-sqlark:${e}`}(e),{ifAvailable:!0,mode:"exclusive"},async n=>{if(!n)return void i(new s(`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 ze{constructor(e,t,n=null,s=1e3,i=16777216){this.opCount=0,this.lsm=e,this.wal=t,this.flushable=n,this.interval=s,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 Ve{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,n]of Object.entries(e))this.store.set(t,n)}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()}}class We{constructor(e,t){this.kv=new fe(e,t)}getKV(){return this.kv}async open(e){await this.kv.open(e)}async close(){await this.kv.close()}isOpen(){return this.kv.isOpen()}async read(e){return this.kv.get(e)}async write(e,t){await this.kv.put(e,t)}async append(e,t){await this.kv.appendValue(e,t)}async writeMany(e){await this.kv.putMany(e)}async delete(e){await this.kv.delete(e)}async deleteMany(e){await this.kv.deleteMany(e)}async listKeys(){return this.kv.listKeys()}async exists(e){return this.kv.exists(e)}async clear(){await this.kv.clear()}}const Ge="AES-GCM";class He{constructor(){this.cryptoKey=null,this._enabled=!1}get enabled(){return this._enabled}async init(e,t){const n=new TextEncoder,s=await crypto.subtle.importKey("raw",n.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"},s,{name:Ge,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)),n=e instanceof Uint8Array?e:new Uint8Array(e);return{iv:t,data:await crypto.subtle.encrypt({name:Ge,iv:t},this.cryptoKey,n)}}async decryptPage(e,t){if(!this.cryptoKey)throw new Error("Crypto not initialized");const n=t instanceof Uint8Array?t:new Uint8Array(t);return crypto.subtle.decrypt({name:Ge,iv:e},this.cryptoKey,n)}close(){this.cryptoKey=null,this._enabled=!1}}const Qe=12,Xe="__aria_keymeta";function Je(e){if("undefined"!=typeof Buffer)return Buffer.from(e).toString("base64");let t="";for(let n=0;n<e.byteLength;n++)t+=String.fromCharCode(e[n]);return btoa(t)}function Ye(e){if("undefined"!=typeof Buffer)return new Uint8Array(Buffer.from(e,"base64"));const t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return n}class Ze{constructor(e,t){if(this.inner=e,this.password=t,this.crypto=new He,this.opened=!1,!t||0===t.length)throw new s("Encryption password must not be empty","ARIA_ENCRYPT_CONFIG_ERROR")}getInner(){return this.inner}isCryptoReady(){return this.crypto.enabled}async open(e){this.inner.isOpen()||await this.inner.open(e);const t=await this.inner.read(Xe);if(t){let e;try{e=JSON.parse((new TextDecoder).decode(t))}catch{throw new s("Corrupted encryption key metadata","ARIA_DECRYPT_ERROR")}if(!e.salt||!e.verifier)throw new s("Corrupted encryption key metadata","ARIA_DECRYPT_ERROR");const n=Ye(e.salt);await this.crypto.init(this.password,n);const i=Ye(e.verifier);if(i.byteLength<=Qe)throw new s("Corrupted encryption key metadata","ARIA_DECRYPT_ERROR");const r=i.subarray(0,Qe),a=i.subarray(Qe);try{await this.crypto.decryptPage(r,a)}catch{throw new s("Decryption failed: wrong password or corrupted key metadata","ARIA_DECRYPT_ERROR")}}else{if((await this.inner.listKeys()).some(e=>e!==Xe))throw new s("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),n=new Uint8Array(Qe+t.data.byteLength);n.set(t.iv,0),n.set(new Uint8Array(t.data),Qe);const i=JSON.stringify({salt:Je(e),verifier:Je(n)});await this.inner.write(Xe,(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[n,s]of Object.entries(e))t[n]=await this.encrypt(s);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!==Xe);e.length>0&&await this.inner.deleteMany(e)}ensureReady(){if(!this.opened)throw new s("EncryptedBackend not opened","ARIA_DB_NOT_OPEN");if(!this.crypto.enabled)throw new s("EncryptedBackend key not initialized","ARIA_DECRYPT_ERROR")}async encrypt(e){const t=await this.crypto.encryptPage(e),n=new ArrayBuffer(Qe+t.data.byteLength),s=new Uint8Array(n);return s.set(t.iv,0),s.set(new Uint8Array(t.data),Qe),n}async decrypt(e){const t=new Uint8Array(e);if(t.byteLength<=Qe)throw new s("Corrupted encrypted data block (too short)","ARIA_DECRYPT_ERROR");const n=t.subarray(0,Qe),i=t.subarray(Qe);try{return await this.crypto.decryptPage(n,i)}catch(e){if(e instanceof s)throw e;throw new s("Decryption failed (data corrupted or wrong key)","ARIA_DECRYPT_ERROR",e)}}}function et(e){try{return 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,n=t+Math.ceil(t/15)+8,s=new Uint8Array(n);let i=0;const r=new Int32Array(65536).fill(-1),a=new Int32Array(t).fill(-1),o=t=>{const n=(e[t]|e[t+1]<<8|e[t+2]<<16|e[t+3]<<24)>>>0;return Math.imul(n,2654435761)>>>16&65535},c=e=>{if(e+4>t)return;const n=o(e);a[e]=r[n],r[n]=e};let h=0,l=0;const u=()=>{let t=h-l;for(;t>0;){const n=Math.min(t,15);s[i++]=(15&n)<<4;for(let t=0;t<n;t++)s[i++]=e[l+t];t-=n,l+=n}};for(;h<t;){let n=0,f=0;if(h+4<=t){let s=r[o(h)],i=0;for(;s>=0&&i<32;){const r=h-s;if(r>0&&r<=65535&&e[s]===e[h]){let i=0;for(;i<19&&h+i<t&&e[s+i]===e[h+i];)i++;if(i>n&&(n=i,f=r,19===i))break}s=a[s],i++}}if(n>4&&h-l<=15){const t=h-l;s[i++]=(15&t)<<4|n-4&15;for(let n=0;n<t;n++)s[i++]=e[l+n];s[i++]=255&f,s[i++]=f>>8&255;for(let e=0;e<n;e++)c(h+e);h+=n,l=h}else c(h),h++,h-l>=15&&u()}u();const f=new Uint8Array(4+i);return new DataView(f.buffer).setUint32(0,t,!0),f.set(s.subarray(0,i),4),f}(e)}catch{return function(e){const t=Math.ceil(e.byteLength/15),n=Math.max(t,0)+e.byteLength,s=new Uint8Array(4+n);new DataView(s.buffer).setUint32(0,e.byteLength,!0);let i=4,r=0;for(;r<e.byteLength;){const t=Math.min(15,e.byteLength-r);s[i++]=(15&t)<<4;for(let n=0;n<t;n++)s[i++]=e[r+n];r+=t}return i===s.byteLength?s:s.slice(0,i)}(e)}}function tt(e,t){if(e.byteLength<4)throw new Error("LZ4 stream too short: missing header");const n=new DataView(e.buffer,e.byteOffset,e.byteLength).getUint32(0,!0);if(0===n&&4===e.byteLength)return new Uint8Array(0);if(n<=0||n>1073741823)throw new Error("Invalid LZ4 header: bad original size");const s=e.subarray(4),i=new Uint8Array(n);let r=0,a=0;for(;r<s.byteLength&&a<n;){const e=s[r++],t=e>>4&15,o=15&e;for(let e=0;e<t&&r<s.byteLength&&a<n;e++)i[a++]=s[r++];if(0===o)continue;if(r+1>=s.byteLength)break;const c=s[r++]|s[r++]<<8,h=o+4;for(let e=0;e<h&&a<n;e++)i[a]=i[a-c],a++}return i}class nt{constructor(e,t,n=!1){this.fileManager=e,this.bufferPool=t,this.compression=n,this.pageIds=new Map}async save(e,t){const n=this.compression?et(t):t,s=Math.max(1,Math.ceil(n.byteLength/ge)),i=await this.bufferPool.newPages(s,be.DATA),r=[];for(let e=0;e<s;e++){const t=i[e];r.push(t.pageId);const s=new Uint8Array(t.data);s.fill(0);const a=n.subarray(e*ge,Math.min((e+1)*ge,n.byteLength));s.set(a,0),t.dirty=!0,await this.bufferPool.flushPage(t.pageId),this.bufferPool.unpin(t)}return this.pageIds.set(e,r),{storedSize:n.byteLength}}getPageIds(e){return this.pageIds.get(e)}async load(e,t,n){if(0===t.length)return null;const s=[];for(const e of t){const t=await this.bufferPool.getPage(e);if(!t)return null;s.push(new Uint8Array(t.data)),this.bufferPool.unpin(t)}const i=s.reduce((e,t)=>e+t.byteLength,0),r=new Uint8Array(Math.min(i,n));let a=0;for(const e of s){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),this.compression?tt(r):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 st{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");let n=1;t&&t instanceof ArrayBuffer&&t.byteLength>=4&&(n=new DataView(t).getUint32(0,!1));const s=await this.backend.listKeys();for(const e of s)if(e.startsWith("pg_")){const t=Number.parseInt(e.slice(3),10);!Number.isNaN(t)&&t+1>n&&(n=t+1)}this.nextPageId=n,t&&t instanceof ArrayBuffer&&!(t.byteLength<4)&&n===new DataView(t).getUint32(0,!1)||await this.saveMeta(),this.metaLoaded=!0}async readPage(e){const t=`pg_${e}`,n=await this.backend.read(t);if(!n)return null;if(n.byteLength<ge){const e=new ArrayBuffer(ge);return new Uint8Array(e).set(new Uint8Array(n)),e}return n}async writePage(e,t){const n=`pg_${e}`;await this.backend.write(n,t)}async allocatePageId(){const e=this.nextPageId++;return await this.saveMeta(),e}async allocatePageIds(e){if(e<=0)return[];const t=[],n=this.nextPageId;this.nextPageId+=e;for(let s=0;s<e;s++)t.push(n+s);return await this.saveMeta(),t}async freePageId(e){const t=`pg_${e}`;await this.backend.delete(t)}async saveMeta(){const e=new ArrayBuffer(8);new DataView(e).setUint32(0,this.nextPageId,!1),await this.backend.write("__aria_meta",e)}async clearAll(){await this.backend.clear(),this.nextPageId=1,await this.saveMeta()}}class it{constructor(){this.versionStore=new Map,this.activeTxns=new Map,this.nextTxnId=1,this.globalCommitLsn=0,this.txnWriteKeys=new Map}beginTransaction(){const e=this.nextTxnId++;return this.activeTxns.set(e,{txnId:e,state:ke.ACTIVE,snapshotLsn:this.globalCommitLsn,startTime:Date.now()}),this.txnWriteKeys.set(e,new Set),e}commitTransaction(e){const t=this.activeTxns.get(e);if(!t)throw new Error(`Transaction ${e} not found`);t.state=ke.COMMITTED,this.globalCommitLsn++;const n=this.txnWriteKeys.get(e);if(n)for(const t of n){const n=this.versionStore.get(t);if(!n)continue;const s=n.filter(t=>t.txnId!==e);0===s.length?this.versionStore.delete(t):this.versionStore.set(t,s)}this.activeTxns.delete(e),this.txnWriteKeys.delete(e)}rollbackTransaction(e){const t=this.activeTxns.get(e);if(!t)throw new Error(`Transaction ${e} not found`);t.state=ke.ABORTED;const n=this.txnWriteKeys.get(e);if(n)for(const t of n){const n=this.versionStore.get(t);if(!n)continue;const s=n.filter(t=>t.txnId!==e);0===s.length?this.versionStore.delete(t):this.versionStore.set(t,s)}this.activeTxns.delete(e),this.txnWriteKeys.delete(e)}writeVersion(e,t,n,s){const i=`${e}.${t}`,r=this.versionStore.get(i)??[],a={txnId:s,data:n,prevVersion:r.length>0?r[r.length-1]:null,committed:!1};r.push(a),this.versionStore.set(i,r),this.txnWriteKeys.get(s)?.add(i)}deleteVersion(e,t,n){this.writeVersion(e,t,{__mvcc_tombstone:!0},n)}discardVersions(e){const t=this.txnWriteKeys.get(e);if(t)for(const n of t){const t=this.versionStore.get(n);if(!t)continue;const s=t.filter(t=>t.txnId!==e);0===s.length?this.versionStore.delete(n):this.versionStore.set(n,s)}}gc(e=100){for(const[t,n]of this.versionStore){if(n.length<=e)continue;const s=n.slice(n.length-e);this.versionStore.set(t,s)}}getGlobalLSN(){return this.globalCommitLsn}}function rt(e,t){const n=new ArrayBuffer(ge);return function(e,t,n){const s=new DataView(e);s.setUint32(0,t,!1),s.setUint8(4,n),s.setUint16(5,16,!1),s.setUint16(7,e.byteLength,!1),s.setUint16(9,0,!1),s.setUint32(11,0,!1),s.setUint8(15,0)}(n,e,t),{pageId:e,type:t,data:n,dirty:!0,pins:0,prev:null,next:null,lastAccess:Date.now()}}class at{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 ot{constructor(e,t,n){this.lru=new at,this.capacity=e,this.onEvict=t,this.onRemove=n}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),this.onRemove?.(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 ct{constructor(e,t=256){this.pages=new Map,this.nextPageId=0,this.pageIO=e,this.eviction=new ot(t,async e=>{e.dirty&&(await this.pageIO.writePage(e.pageId,e.data),e.dirty=!1)},e=>{this.pages.delete(e.pageId)})}async getPage(e){let t=this.pages.get(e);if(t)return this.eviction.access(t),t.pins++,t;const n=await this.pageIO.readPage(e);return n?(await this.eviction.evictIfNeeded(1),t={pageId:e,type:new DataView(n).getUint8(4),data:n,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=be.DATA){if(e<=0)return[];let n;if("function"==typeof this.pageIO.allocatePageIds)n=await this.pageIO.allocatePageIds(e);else{n=[];for(let t=0;t<e;t++)n.push(await this.pageIO.allocatePageId())}await this.eviction.evictIfNeeded(e);const s=[];for(const e of n){const n=rt(e,t);n.pins=1,this.pages.set(e,n),this.eviction.add(n),s.push(n)}return s}unpin(e){e.pins>0&&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 ht{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.uniqueIndexCols=new Set,this.mvcc=new it,this.currentTxnId=null,this.txnSnapshot=null,this.gcCounter=0,this.savepoints=new Map,this.txnWalRecordCount=0,this.savepointWalBoundary=new Map,this.config={...Se,...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 s)throw t;throw new s(`Failed to open AriaEngine database "${e}"`,"ARIA_OPEN_ERROR",t)}}async openInternal(e){this.dbName=e;const t=new je;let n;if(this.dbLock=t,await t.acquire(e),n=this.config.testBackend?this.config.testBackend:"opfs"===this.config.storageBackend?new J:"kv"===this.config.storageBackend?new We:new Ve,await n.open(e),this.config.encryption?.password)this.backend=new Ze(n,this.config.encryption.password);else if(this.backend=n,await n.exists("__aria_keymeta"))throw await n.close(),new s("Database is encrypted: provide encryption.password to open it","ARIA_ENCRYPT_REQUIRED");await this.backend.open(e),this.fileManager=new st(this.backend),await this.fileManager.init(e),this.bufferPool=new ct(this.fileManager,this.config.bufferPoolPages);const i=this.createSSTableStore("main");this.lsm=new _e({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 Me(new Fe(this.backend),this.config.walEnabled,this.config.walSyncMode),await this.loadSchemas();for(const[e,t]of this.schemas){const n=this.tablePKs.get(e);for(const[s,i]of Object.entries(t.columns))if((i.index||i.unique)&&s!==n){const t=`${e}:idx:${s}`;if(!this.secondaryIndexes.has(t)){const n=new _e({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}_${s}`)});await n.init(),this.secondaryIndexes.set(t,n)}}}await this.lsm.init();const r=new Set,a=[];await this.wal.recover(e=>a.push(e));for(const e of a)e.type===Ee.COMMIT&&r.add(e.txnId),e.type===Ee.ROLLBACK&&r.delete(e.txnId);const o=new Map,c=new Map;for(let e=0;e<a.length;e++){const t=a[e];if(0===t.txnId)continue;const n=c.get(t.txnId)??0;o.set(e,n),c.set(t.txnId,n+1)}const h=new Map;for(let e=0;e<a.length;e++){const t=a[e];if(t.type!==Ee.SAVEPOINT_ROLLBACK)continue;const n=t.data?.replayFromIndex,s="number"==typeof n?n:0,i=o.get(e)??0,r=h.get(t.txnId);h.set(t.txnId,{keepUpTo:r?Math.min(r.keepUpTo,s):s,dropFrom:i})}for(let e=0;e<a.length;e++){const t=a[e];if(0===t.txnId||r.has(t.txnId)){if(t.type===Ee.SAVEPOINT_ROLLBACK)continue;const n=h.get(t.txnId);if(void 0!==n){const t=o.get(e)??0;if(t>=n.keepUpTo&&t<n.dropFrom)continue}t.type===Ee.DROP_TABLE?await this.applyDropTableRecovery(t.tableName):this.applyWALRecord(t)}}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 ze(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();for(const e of this.secondaryIndexes.values())await e.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.uniqueIndexCols.clear(),this.mvcc=new it,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,n=async e=>{const n="main"===e?"__aria_lsm_meta":`__aria_lsm_meta_${e}`,s=await this.backend.read(n);if(s)try{const e=JSON.parse((new TextDecoder).decode(s));for(const n of e)if(n.pageIds)for(const e of n.pageIds)t.add(e)}catch{}};await n("main");for(const[e,t]of this.schemas)for(const[s,i]of Object.entries(t.columns))(i.index||i.unique)&&await n(`idx_${e}_${s}`);const s=e.map(e=>Number(e.slice(3))).filter(e=>!t.has(e));s.length>0&&await this.backend.deleteMany(s.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.uniqueIndexCols.clear(),this.lsm.clear(),this.mvcc=new it,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 s(`Table "${e.name}" already exists`,"TABLE_EXISTS");await this.appendDDLRecord({type:Ee.CREATE_TABLE,txnId:0,tableName:e.name,key:"",data:{schema:JSON.stringify(e)}}),this.schemas.set(e.name,e),this.tablePKs.set(e.name,this.getPK(e));for(const[t,n]of Object.entries(e.columns))if(n.index||n.unique){const n=`${e.name}:idx:${t}`;if(!this.secondaryIndexes.has(n)){const s=new _e({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 s.init(),this.secondaryIndexes.set(n,s)}}await this.persistSchemas()}async appendDDLRecord(e){await this.wal.append(e),await this.wal.flush()}async dropTable(e){this.ensureOpen(),this.ensureNoDDLInTransaction("DROP TABLE"),this.ensureTable(e),await this.appendDDLRecord({type:Ee.DROP_TABLE,txnId:0,tableName:e,key:""});const t=await this.getAllRows(e);for(const n of t){const t=this.tablePKs.get(e);this.lsm.delete(`${e}:${n[t]}`)}await this.cleanupTableIndexes(e);const n=`${e}:`;for(const e of this.uniqueIndexCols)e.startsWith(n)&&this.uniqueIndexCols.delete(e);this.schemas.delete(e),this.tablePKs.delete(e),await this.persistSchemas()}async cleanupTableIndexes(e){const t=`${e}:idx:`,n=[];for(const[e,s]of this.secondaryIndexes)if(e.startsWith(t)){n.push(e);try{await s.clear()}catch{}}for(const e of n)this.secondaryIndexes.delete(e)}async hasTable(e){return this.ensureOpen(),this.schemas.has(e)}async getTableNames(){return this.ensureOpen(),Array.from(this.schemas.keys())}async getTableSchema(e){return this.ensureOpen(),this.schemas.get(e)??null}async insert(e,t){this.ensureOpen(),this.ensureTable(e);const n=this.schemas.get(e),i=this.tablePKs.get(e),r=[],a=[],o=this.uniqueColumns(e,n),c=[];for(const s of t){const t=this.validateRow(n,s),r=String(t[i]);c.push({row:t,pkValue:r,key:`${e}:${r}`})}await this.lsm.prefetchKeys(c.map(e=>e.key));const h=new Set;for(const{pkValue:t,key:n}of c){if(h.has(t))throw new s(`Duplicate primary key "${t}" in table "${e}"`,"DUPLICATE_KEY");h.add(t);const i=this.currentTxnId?this.txnSnapshot?.get(n)??await this.lsm.get(n):await this.lsm.get(n);if(i&&!i.__txn_deleted)throw new s(`Duplicate primary key "${t}" in table "${e}"`,"DUPLICATE_KEY")}for(const t of o){const n=this.secondaryIndexes.get(`${e}:idx:${t}`);await n.prefetchPrefixRanges(c.map(e=>{const n=e.row[t];if(null==n)return null;const s=`${String(n)}:`;return[s,`${s}`]}).filter(e=>null!==e))}const l=new Map;for(const{row:t,pkValue:n}of c)for(const i of o){const r=t[i];if(null==r)continue;const a=String(r);let o=l.get(i);if(o||(o=new Set,l.set(i,o)),o.has(a))throw new s(`Unique constraint violation on column "${i}" in table "${e}"`,"UNIQUE_VIOLATION");o.add(a),await this.checkUnique(e,[i],t,n)}for(const{row:t,pkValue:n,key:s}of c)this.currentTxnId&&this.txnSnapshot?(this.txnSnapshot.set(s,t),this.mvcc.writeVersion(e,n,t,this.currentTxnId)):this.lsm.put(s,t),this.updateSecondaryIndexes(e,n,t,null),r.push(n),a.push({type:Ee.INSERT,txnId:this.currentTxnId??0,tableName:e,key:n,data:t});return await this.wal.appendBatch(a),this.txnWalRecordCount+=a.length,this.opCounter+=t.length,this.checkMemoryBudget(),await this.checkpointManager.tick(),this.tryGC(),r}async find(e,t){let n;this.ensureOpen(),this.ensureTable(e);const s=await this.tryIndexLookup(e,t);n=null!==s?s:await this.getAllRows(e),n=this.mergeTxnSnapshot(e,n),t.where&&Object.keys(t.where).length>0&&(n=n.filter(e=>S(e,t.where))),t.orderBy&&t.orderBy.length>0&&(n=P(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=>K(e,t.columns))),this.trimAllCaches(),n.map(e=>o(e))}async update(e,t,n){this.ensureOpen(),this.ensureTable(e);const i=this.schemas.get(e),r=await this.getAllRows(e);let a=0;const o=[],c=new Set,h=G(n);for(const t of Object.keys(h))if(!i.columns[t])throw new s(`Column "${t}" does not exist in table "${e}"`,"COLUMN_NOT_FOUND");const l=this.uniqueColumns(e,i);for(const t of l){const n=this.secondaryIndexes.get(`${e}:idx:${t}`),s=[];if(void 0!==h[t]&&null!==h[t]){const e=`${String(h[t])}:`;s.push([e,`${e}`])}else if(!(t in h))for(const e of r){const n=e[t];if(null==n)continue;const i=`${String(n)}:`;s.push([i,`${i}`])}await n.prefetchPrefixRanges(s)}const u=[],f=new Map,d=new Set;for(const n of r){const r=this.tablePKs.get(e),a=`${e}:${n[r]}`;if(t.where&&Object.keys(t.where).length>0&&!S(n,t.where))continue;const o={...n,...h};this.validateRow(i,o),this.checkBatchUnique(e,l,o,f),await this.checkUnique(e,l,o,String(n[r]));const c=String(o[r]),p=c!==String(n[r]);if(p){const t=`${e}:${c}`,n=this.currentTxnId?this.txnSnapshot?.get(t)??await this.lsm.get(t):await this.lsm.get(t);if(n&&!n.__txn_deleted)throw new s(`Duplicate primary key "${c}" in table "${e}" (cannot update key to existing value)`,"DUPLICATE_KEY");if(d.has(c))throw new s(`Duplicate primary key "${c}" in table "${e}" (multiple rows in the same statement update to the same key)`,"DUPLICATE_KEY");d.add(c)}u.push({row:n,pk:String(n[r]),key:a,updated:o,newPk:c,pkChanged:p})}for(const t of u)t.pkChanged&&await this.checkForeignKeyUpdateRestrict(e,t.pk,t.newPk);for(const{row:t,pk:n,key:s,updated:i,newPk:r,pkChanged:h}of u)h&&await this.applyForeignKeyUpdateRules(e,n,r,o,c),this.currentTxnId&&this.txnSnapshot?(h&&(this.txnSnapshot.set(s,{__txn_deleted:!0}),this.mvcc.deleteVersion(e,n,this.currentTxnId)),this.txnSnapshot.set(`${e}:${r}`,i),this.mvcc.writeVersion(e,r,i,this.currentTxnId)):(h&&this.lsm.delete(s),this.lsm.put(`${e}:${r}`,i)),a++,h&&o.push({type:Ee.DELETE,txnId:this.currentTxnId??0,tableName:e,key:n}),o.push({type:Ee.UPDATE,txnId:this.currentTxnId??0,tableName:e,key:r,data:i}),this.updateSecondaryIndexes(e,r,i,t);return await this.wal.appendBatch(o),this.txnWalRecordCount+=o.length,this.opCounter+=a,await this.checkpointManager.tick(),this.trimAllCaches(),a}checkBatchUnique(e,t,n,i){for(const r of t){const t=n[r];if(null==t)continue;let a=i.get(r);if(a||(a=new Set,i.set(r,a)),a.has(t))throw new s(`Unique constraint violation on column "${r}" in table "${e}"`,"UNIQUE_VIOLATION");a.add(t)}}async checkForeignKeyUpdateRestrict(e,t,n){for(const[n,i]of this.schemas)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||"SET NULL"===a.onUpdate&&a.required)){const i=await this.getAllRows(n);for(const o of i)if(String(o[r])===t){const i="RESTRICT"===a.onUpdate?`foreign key "${r}" in "${n}" has dependent rows`:`foreign key "${r}" in "${n}" is required (SET NULL violates constraint)`;throw new s(`Cannot update "${e}" key "${t}": ${i}`,"FOREIGN_KEY_VIOLATION")}}}}async applyForeignKeyUpdateRules(e,t,n,s,i){const r=`${e}:${t}`;if(!i.has(r)){i.add(r);for(const[i,r]of this.schemas)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 c=await this.getAllRows(i);for(const e of c){if(String(e[a])!==t)continue;const r=this.tablePKs.get(i),c=String(e[r]),h={...e,[a]:"CASCADE"===o.onUpdate?n:null},l=`${i}:${c}`;this.currentTxnId&&this.txnSnapshot?(this.txnSnapshot.set(l,h),this.mvcc.writeVersion(i,c,h,this.currentTxnId)):this.lsm.put(l,h),this.updateSecondaryIndexes(i,c,h,e),s.push({type:Ee.UPDATE,txnId:this.currentTxnId??0,tableName:i,key:c,data:h})}}}}async delete(e,t){this.ensureOpen(),this.ensureTable(e);const n=await this.getAllRows(e);let s=0;const i=[],r=new Set,a=[];for(const s of n)t.where&&0!==Object.keys(t.where).length&&!S(s,t.where)||a.push(String(s[this.tablePKs.get(e)]));const o=new Set;for(const t of a)await this.checkCascadeRestrict(e,t,o);for(const a of n){const n=this.tablePKs.get(e),o=`${e}:${a[n]}`;t.where&&0!==Object.keys(t.where).length&&!S(a,t.where)||(s+=await this.applyForeignKeyRules(e,String(a[n]),i,r),this.currentTxnId&&this.txnSnapshot?(this.txnSnapshot.set(o,{__txn_deleted:!0}),this.mvcc.deleteVersion(e,String(a[n]),this.currentTxnId)):this.lsm.delete(o),s++,i.push({type:Ee.DELETE,txnId:this.currentTxnId??0,tableName:e,key:String(a[n])}),this.updateSecondaryIndexes(e,String(a[n]),null,a))}return await this.wal.appendBatch(i),this.txnWalRecordCount+=i.length,this.opCounter+=s,await this.checkpointManager.tick(),this.trimAllCaches(),s}async checkCascadeRestrict(e,t,n){const i=`${e}:${t}`;if(!n.has(i)){n.add(i);for(const[i,r]of this.schemas)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 c=(await this.getAllRows(i)).filter(e=>String(e[a])===t);if("RESTRICT"===o.onDelete&&c.length>0)throw new s(`Cannot delete from "${e}": foreign key "${a}" in "${i}" has dependent rows`,"FOREIGN_KEY_VIOLATION");if("SET NULL"===o.onDelete&&o.required&&c.length>0)throw new s(`Cannot delete from "${e}": foreign key "${a}" in "${i}" is required (SET NULL violates constraint)`,"FOREIGN_KEY_VIOLATION");if("CASCADE"===o.onDelete){const e=this.tablePKs.get(i);for(const t of c)await this.checkCascadeRestrict(i,String(t[e]),n)}}}}async applyForeignKeyRules(e,t,n,i){let r=0;const a=`${e}:${t}`;if(i.has(a))return 0;i.add(a);for(const[a,o]of this.schemas)for(const[c,h]of Object.entries(o.columns)){if(!h.references||!h.onDelete)continue;const[o]=h.references.split(".");if(o!==e)continue;const l=(await this.getAllRows(a)).filter(e=>String(e[c])===t);if("RESTRICT"===h.onDelete&&l.length>0)throw new s(`Cannot delete from "${e}": foreign key "${c}" in "${a}" has dependent rows`,"FOREIGN_KEY_VIOLATION");if("CASCADE"===h.onDelete){const e=this.tablePKs.get(a);for(const t of l){const s=String(t[e]);r+=await this.applyForeignKeyRules(a,s,n,i);const o=`${a}:${s}`;this.currentTxnId&&this.txnSnapshot?(this.txnSnapshot.set(o,{__txn_deleted:!0}),this.mvcc.deleteVersion(a,s,this.currentTxnId)):this.lsm.delete(o),this.updateSecondaryIndexes(a,s,null,t),n.push({type:Ee.DELETE,txnId:this.currentTxnId??0,tableName:a,key:s}),r++}}else if("SET NULL"===h.onDelete){const e=this.tablePKs.get(a);for(const t of l){const s=String(t[e]),i={...t,[c]:null},r=`${a}:${s}`;this.currentTxnId&&this.txnSnapshot?(this.txnSnapshot.set(r,i),this.mvcc.writeVersion(a,s,i,this.currentTxnId)):this.lsm.put(r,i),this.updateSecondaryIndexes(a,s,i,t),n.push({type:Ee.UPDATE,txnId:this.currentTxnId??0,tableName:a,key:s,data:i})}}}return r}async findStream(e,t,n){this.ensureOpen(),this.ensureTable(e);const s=!!(t.where&&Object.keys(t.where).length>0),i=t.columns&&t.columns.length>0&&"*"!==t.columns[0]?e=>K(e,t.columns):null,r=t.limit??1/0,a=t.offset??0,c=this.tablePKs.get(e),h=`${e}:`;let l=0,u=0;const f=e=>!(!s||S(e,t.where))||(u<a?(u++,!0):(n(i?i(e):o(e)),l++,l<r));if(this.currentTxnId&&this.txnSnapshot){const s=await this.find(e,{...t,orderBy:void 0,limit:void 0,offset:void 0});for(const e of s)n(i?i(e):o(e));return s.length}const d=await this.tryIndexLookup(e,t);if(null!==d){for(const e of d)if(!f(e))break;return l}return await this.lsm.prefetchRange(h,`${h}`),await this.lsm.rangeScanLazy(h,`${h}`,(e,t)=>{if(l>=r)return!1;const n={...t};return n[c]=e.slice(h.length),f(n)}),l}async count(e,t){this.ensureOpen(),this.ensureTable(e);const n=await this.getAllRows(e);return this.trimAllCaches(),t?.where&&0!==Object.keys(t.where).length?n.filter(e=>S(e,t.where)).length:n.length}async clear(e){this.ensureOpen(),this.ensureTable(e);const t=await this.getAllRows(e),n=[];for(const s of t){const t=this.tablePKs.get(e),i=`${e}:${s[t]}`;this.currentTxnId&&this.txnSnapshot?(this.txnSnapshot.set(i,{__txn_deleted:!0}),this.mvcc.deleteVersion(e,String(s[t]),this.currentTxnId)):this.lsm.delete(i),n.push({type:Ee.DELETE,txnId:this.currentTxnId??0,tableName:e,key:String(s[t])}),this.updateSecondaryIndexes(e,String(s[t]),null,s)}await this.wal.appendBatch(n),this.txnWalRecordCount+=n.length,this.opCounter+=t.length,await this.checkpointManager.tick(),this.tryGC()}async alterTable(e,t,n){this.ensureOpen(),this.ensureNoDDLInTransaction("ALTER TABLE"),this.ensureTable(e);const i=this.schemas.get(e);if("ADD"===t){if(i.columns[n.name])throw new s(`Column "${n.name}" already exists in table "${e}"`,"COLUMN_EXISTS");const t={name:i.name,columns:{...i.columns,[n.name]:n}};return await this.appendDDLRecord({type:Ee.ALTER_TABLE,txnId:0,tableName:e,key:n.name,data:{schema:JSON.stringify(t),action:"ADD"}}),i.columns[n.name]=n,(n.index||n.unique)&&await this.createIndex(e,n.name,!0===n.unique),void await this.persistSchemas()}if(!i.columns[n.name])throw new s(`Column "${n.name}" does not exist in table "${e}"`,"COLUMN_NOT_FOUND");if(i.columns[n.name].index||i.columns[n.name].unique){const t=`${e}:idx:${n.name}`,s=this.secondaryIndexes.get(t);if(s){try{await s.clear()}catch{}this.secondaryIndexes.delete(t)}}{const t={name:i.name,columns:Object.fromEntries(Object.entries(i.columns).filter(([e])=>e!==n.name))};await this.appendDDLRecord({type:Ee.ALTER_TABLE,txnId:0,tableName:e,key:n.name,data:{schema:JSON.stringify(t),action:"DROP"}})}delete i.columns[n.name],await this.persistSchemas();const r=`${e}:`,a=`${r}`;await this.lsm.prefetchRange(r,a);const o=await this.lsm.rangeScan(r,a),c=[];for(const[t,s]of o){if(!(n.name in s))continue;const i={...s};delete i[n.name],this.lsm.put(t,i);const a=t.slice(r.length);this.updateSecondaryIndexes(e,a,i,s),c.push({type:Ee.UPDATE,txnId:this.currentTxnId??0,tableName:e,key:a,data:i})}await this.wal.appendBatch(c),this.txnWalRecordCount+=c.length,this.opCounter+=c.length,await this.checkpointManager.tick(),this.trimAllCaches()}async createIndex(e,t,n){this.ensureOpen(),this.ensureNoDDLInTransaction("CREATE INDEX"),this.ensureTable(e);const i=this.schemas.get(e).columns[t];if(!i)throw new s(`Column "${t}" does not exist in table "${e}"`,"COLUMN_NOT_FOUND");const r=`${e}:idx:${t}`;if(this.secondaryIndexes.has(r))return;const a=new _e({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);try{const i=this.tablePKs.get(e),r=await this.getAllRows(e),o=new Set;for(const c of r){const r=c[t];if(null!=r){const h=String(r);if(n&&o.has(h))throw new s(`Unique index on column "${t}" in table "${e}" cannot be created: duplicate value "${h}"`,"UNIQUE_VIOLATION");o.add(h),a.put(`${h}:${c[i]}`,{pk:c[i]})}}await a.flush()}catch(e){this.secondaryIndexes.delete(r);try{await a.clear()}catch{}throw e}i.index=!0,n&&(i.unique=!0,this.uniqueIndexCols.add(`${e}:${t}`)),await this.persistSchemas()}async dropIndex(e,t,n){this.ensureOpen(),this.ensureNoDDLInTransaction("DROP INDEX"),this.ensureTable(e);const i=this.schemas.get(e).columns[t];if(!i)throw new s(`Column "${t}" does not exist in table "${e}"`,"COLUMN_NOT_FOUND");if(i.primaryKey)throw new s(`Cannot drop primary key index on column "${t}"`,"NOT_SUPPORTED");if(!i.index&&!i.unique&&!this.secondaryIndexes.has(`${e}:idx:${t}`))throw new s(`Index on column "${t}" does not exist in table "${e}"`,"INDEX_NOT_FOUND");const r=`${e}:${t}`;if(i.unique&&!this.uniqueIndexCols.has(r))throw new s(`Cannot drop index on column "${t}" in table "${e}": UNIQUE constraint defined at table creation must be removed by recreating the table`,"NOT_SUPPORTED");i.index=!1,i.unique=!1,this.uniqueIndexCols.delete(r);const a=`${e}:idx:${t}`,o=this.secondaryIndexes.get(a);o&&(await o.clear(),this.secondaryIndexes.delete(a)),await this.persistSchemas()}async beginTransaction(){if(this.currentTxnId)throw new s("Transaction already in progress","TX_ACTIVE");this.currentTxnId=this.mvcc.beginTransaction(),this.txnSnapshot=new Map,this.txnWalRecordCount=0;try{await this.wal.append({type:Ee.BEGIN,txnId:this.currentTxnId,tableName:"",key:""}),this.txnWalRecordCount=1}catch(e){throw this.mvcc.rollbackTransaction(this.currentTxnId),this.currentTxnId=null,this.txnSnapshot=null,e}}async commitTransaction(){if(!this.currentTxnId)throw new s("No active transaction","TX_NONE");if(await this.wal.append({type:Ee.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.savepoints.clear(),this.savepointWalBoundary.clear(),this.txnWalRecordCount=0,this.currentTxnId=null,this.txnSnapshot=null}async rollbackTransaction(){if(!this.currentTxnId)throw new s("No active transaction","TX_NONE");const e=this.currentTxnId;await this.wal.append({type:Ee.ROLLBACK,txnId:e,tableName:"",key:""});const t=new Set;if(this.txnSnapshot)for(const e of this.txnSnapshot.keys()){const n=e.indexOf(":");n>0&&t.add(e.slice(0,n))}this.mvcc.rollbackTransaction(e),this.txnSnapshot=null,this.savepoints.clear(),this.savepointWalBoundary.clear(),this.txnWalRecordCount=0,this.currentTxnId=null;for(const e of t)this.schemas.has(e)&&await this.reindexTable(e)}async savepoint(e){if(!this.currentTxnId)throw new s("No active transaction for savepoint","TX_NONE");if(this.savepoints.has(e))throw new s(`Savepoint "${e}" already exists`,"SAVEPOINT_EXISTS");this.savepoints.set(e,{txnId:this.currentTxnId,snapshot:this.txnSnapshot?new Map(this.txnSnapshot):null}),this.savepointWalBoundary.set(`${this.currentTxnId}:${e}`,this.txnWalRecordCount)}async rollbackToSavepoint(e){const t=this.savepoints.get(e);if(!t)throw new s(`Savepoint "${e}" not found`,"SAVEPOINT_NOT_FOUND");if(t.txnId!==this.currentTxnId)throw new s(`Savepoint "${e}" belongs to a different transaction (stale savepoint)`,"SAVEPOINT_NOT_FOUND");const n=this.savepointWalBoundary.get(`${this.currentTxnId}:${e}`)??0;await this.wal.append({type:Ee.SAVEPOINT_ROLLBACK,txnId:this.currentTxnId,tableName:"",key:"",data:{replayFromIndex:n}}),this.txnWalRecordCount++,this.savepointWalBoundary.set(`${this.currentTxnId}:${e}`,n);const i=new Set;if(this.txnSnapshot)for(const e of this.txnSnapshot.keys()){const t=e.indexOf(":");t>0&&i.add(e.slice(0,t))}this.txnSnapshot=t.snapshot?new Map(t.snapshot):null,this.mvcc.discardVersions(this.currentTxnId);let r=!1;for(const[t]of this.savepoints)t!==e?r&&this.savepoints.delete(t):r=!0;for(const e of i)this.schemas.has(e)&&await this.reindexTable(e)}async releaseSavepoint(e){if(!this.savepoints.has(e))throw new s(`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),n=`${e}:`;await this.lsm.prefetchRange(n,`${n}`);const s=(await this.lsm.rangeScan(n,`${n}`)).map(([e,s])=>{const i=o(s);return i[t]=e.slice(n.length),i});return this.mergeTxnSnapshot(e,s)}mergeTxnSnapshot(e,t){if(!this.currentTxnId||!this.txnSnapshot)return t;const n=this.tablePKs.get(e),s=`${e}:`;for(const[e,i]of this.txnSnapshot){if(!e.startsWith(s))continue;const r=e.slice(s.length),a=i.__txn_deleted,o=t.findIndex(e=>e[n]===r);if(a)o>=0&&t.splice(o,1);else{const e={...i,[n]:r};o>=0?t[o]=e:t.push(e)}}return t}getPK(e){for(const[t,n]of Object.entries(e.columns))if(n.primaryKey)return t;return Object.keys(e.columns)[0]}validateRow(e,t){return F(e).validateRow(t)}async validatePayload(e,t,n="insert"){this.ensureTable(e);const s=F(this.schemas.get(e));for(const e of t)"update"===n?s.validatePartial(G(e)):s.validateRow(e)}async persistSchemas(){const e={};for(const[t,n]of this.schemas)e[t]=n.columns;const t=JSON.stringify(e),n=(new TextEncoder).encode(t).buffer;await this.backend.write("__aria_schemas",n)}async loadSchemas(){const e=await this.backend.read("__aria_schemas");if(e)try{const t=(new TextDecoder).decode(e),n=JSON.parse(t);for(const[e,t]of Object.entries(n)){const n={name:e,columns:t};this.schemas.set(e,n),this.tablePKs.set(e,this.getPK(n))}}catch{}}createSSTableStore(e){const t="main"===e?"sst_":`sst_${e}_`,n="main"===e?"__aria_lsm_meta":`__aria_lsm_meta_${e}`;let s=0,i=!1;const r=this.isPageStorage()?new nt(this.fileManager,this.bufferPool,this.config.compression):null,a=e=>(new TextEncoder).encode(e).buffer,o=async()=>{const e=await this.backend.read(n);if(!e)return[];try{return JSON.parse((new TextDecoder).decode(e))}catch{return[]}};return{save:async(e,n)=>{if(r)return r.save(e,n);let s=n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength);if(this.config.compression){const e=et(new Uint8Array(s));s=e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength)}return await this.backend.write(`${t}${e}`,s),{storedSize:s.byteLength}},load:async e=>{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 n=await this.backend.read(`${t}${e}`);if(!n)return null;let s=new Uint8Array(n);return this.config.compression&&(s=tt(s)),s},delete:async 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();s=e.reduce((e,t)=>Math.max(e,t.id),0),i=!0}return++s},listMeta:o,saveMeta:async e=>{const t=await o(),s=r?.getPageIds(e.id),i=s&&s.length>0?{...e,pageIds:s}:e,c=t.findIndex(t=>t.id===e.id);c>=0?t[c]=i:t.push(i),await this.backend.write(n,a(JSON.stringify(t)))},deleteMeta:async e=>{const t=(await o()).filter(t=>t.id!==e);await this.backend.write(n,a(JSON.stringify(t)))}}}isPageStorage(){return!0===this.config.pageStorage||!1!==this.config.pageStorage&&("opfs"===this.config.storageBackend||"kv"===this.config.storageBackend)}applyWALRecord(e){switch(e.type){case Ee.INSERT:case Ee.UPDATE:e.data&&this.lsm.put(`${e.tableName}:${e.key}`,e.data);break;case Ee.DELETE:this.lsm.delete(`${e.tableName}:${e.key}`);break;case Ee.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{}break;case Ee.ALTER_TABLE:if(e.data?.schema)try{const t=JSON.parse(e.data.schema);this.schemas.set(t.name,t),this.tablePKs.set(t.name,this.getPK(t))}catch{}case Ee.COMMIT:case Ee.ROLLBACK:case Ee.BEGIN:}}async applyDropTableRecovery(e){if(!e)return;await this.cleanupTableIndexes(e),this.schemas.delete(e),this.tablePKs.delete(e);const t=`${e}:`,n=`${t}`;await this.lsm.prefetchRange(t,n);const s=await this.lsm.rangeScan(t,n);for(const[e]of s)this.lsm.delete(e)}uniqueColumns(e,t){const n=[];for(const[s,i]of Object.entries(t.columns))i.unique&&this.secondaryIndexes.has(`${e}:idx:${s}`)&&n.push(s);return n}async checkUnique(e,t,n,i){for(const r of t){const t=n[r];if(null==t)continue;const a=this.secondaryIndexes.get(`${e}:idx:${r}`);if(!a)continue;const o=`${String(t)}:`,c=await a.rangeScan(o,`${o}`);for(const[,t]of c){const n=t.pk;if(void 0!==n&&n!==i)throw new s(`Unique constraint violation on column "${r}" in table "${e}"`,"UNIQUE_VIOLATION")}}}updateSecondaryIndexes(e,t,n,s){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(s){const e=s[r];null!=e&&o.delete(`${String(e)}:${t}`)}if(n){const e=n[r];null!=e&&o.put(`${String(e)}:${t}`,{pk:t})}}}}async tryIndexLookup(e,t){if(!t.where)return null;const n=this.schemas.get(e);if(!n)return null;const s=this.tablePKs.get(e),i=[],r=e=>{for(const[t,n]of Object.entries(e))if("$and"!==t)"$or"!==t&&"$not"!==t&&"$exists"!==t&&i.push([t,n]);else for(const e of n)r(e)};r(t.where);for(const[t,r]of i){const i=n.columns[t];if(!(i&&(i.index||i.unique||i.primaryKey)||t===s))continue;if(t===s){if("object"!=typeof r||null===r){const t=`${e}:${r}`;await this.lsm.prefetchKeys([t]);const n=await this.lsm.get(t);return n?[{...n,[s]:r}]:[]}const t=r;if("$eq"in t){const n=`${e}:${t.$eq}`;await this.lsm.prefetchKeys([n]);const i=await this.lsm.get(n);return i?[{...i,[s]:t.$eq}]:[]}if("$in"in t&&Array.isArray(t.$in)){const n=t.$in.map(t=>`${e}:${t}`);await this.lsm.prefetchKeys(n);const i=[],r=new Set;for(const n of t.$in){const t=String(n);if(r.has(t))continue;const a=await this.lsm.get(`${e}:${t}`);a&&(r.add(t),i.push({...a,[s]: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 n=await this.lsm.rangeScan(t,`${t}`),i=[];for(const[e,a]of n){const n={...a,[s]:e.slice(t.length)};S(n,{[s]:r})&&i.push(n)}return i}}const a=`${e}:idx:${t}`,o=this.secondaryIndexes.get(a);if(!o)continue;if("object"!=typeof r||null===r){if(null===r)continue;return this.indexScanToRows(e,s,o,String(r),String(r))}const c=r;if("$eq"in c){if(null===c.$eq)continue;if("object"==typeof c.$eq)continue;const t=String(c.$eq);return this.indexScanToRows(e,s,o,t,t)}if("$in"in c&&Array.isArray(c.$in)){if(c.$in.some(e=>null===e))continue;if(c.$in.some(e=>"object"==typeof e&&null!==e))continue;const t=c.$in.map(e=>String(e));await o.prefetchPrefixRanges(t.map(e=>[e,`${e}`]));const n=[],i=new Set,r=[];for(const e of t){const t=await o.rangeScan(e,`${e}`);for(const[,e]of t){const t=e.pk;t&&!i.has(t)&&(i.add(t),r.push(t))}}await this.lsm.prefetchKeys(r.map(t=>`${e}:${t}`));for(const t of r){const i=await this.lsm.get(`${e}:${t}`);i&&n.push({...i,[s]:t})}return n}if("$gt"in c||"$gte"in c||"$lt"in c||"$lte"in c)return(await this.indexScanToRows(e,s,o,"","")).filter(e=>S(e,{[t]:r}))}return null}async indexScanToRows(e,t,n,s,i){const r=i.includes("")?i:`${i}`;await n.prefetchRange(s,r);const a=await n.rangeScan(s,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 c=[];for(const n of o){const s=await this.lsm.get(`${e}:${n}`);s&&c.push({...s,[t]:n})}return c}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))}async analyzeTable(e){this.ensureOpen(),this.ensureTable(e);const t=await this.getAllRows(e);let n=this.lsm.getStats().sstableCount,s=this.lsm.getStats().memtableSize,i=this.lsm.getStats().levelCounts.filter(e=>e>0).length;for(const[t,r]of this.secondaryIndexes){if(!t.startsWith(`${e}:idx:`))continue;const a=r.getStats();n+=a.sstableCount,s+=a.memtableSize,i=Math.max(i,a.levelCounts.filter(e=>e>0).length)}const r={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:i,sstableCount:n,memtableSize:s,estimatedMemory:this.lsm.getEstimatedMemory()},a=this.schemas.get(e);if(a&&t.length>0){const e={};for(const n of Object.keys(a.columns)){const s=new Set(t.map(e=>String(e[n])));e[n]={distinctValues:s.size}}r.columnStats=e}return r}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 n=0;const s=this.tablePKs.get(e),i=Object.entries(t.columns).filter(([,e])=>e.index||e.unique);if(0===i.length)return 0;const r=await this.getAllRows(e);for(const[t]of i){const i=`${e}:idx:${t}`,a=this.secondaryIndexes.get(i);if(a){await a.clear(),n++;for(const e of r){const n=e[t];null!=n&&a.put(`${String(n)}:${e[s]}`,{pk:e[s]})}}}return n}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 s("AriaEngine not opened","DB_NOT_OPEN")}ensureNoDDLInTransaction(e){if(this.currentTxnId)throw new s(`${e} is not supported inside a transaction (AriaEngine DDL is not transactional)`,"NOT_SUPPORTED")}ensureTable(e){if(!this.schemas.has(e))throw new s(`Table "${e}" does not exist`,"TABLE_NOT_FOUND")}}class lt{constructor(e="opfs"){this.name="hybrid",this.dbName="",this.version=1,this.memoryEngine=new Q,this.diskEngineType=e,this.diskEngine=new we}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 n=await this.diskEngine.find(e,{table:e});if(n.length>0)try{await this.memoryEngine.insert(e,n)}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);try{await this.diskEngine.createTable(e)}catch(e){await this.recoverMemoryAfterDiskError(e)}}async dropTable(e){await this.memoryEngine.dropTable(e);try{await this.diskEngine.dropTable(e)}catch(e){await this.recoverMemoryAfterDiskError(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,n){await this.memoryEngine.alterTable(e,t,n);try{if("function"==typeof this.diskEngine.alterTable)await this.diskEngine.alterTable(e,t,n);else{const s=await this.diskEngine.getTableSchema(e);s&&"DROP"===t&&delete s.columns[n.name]}}catch(e){await this.recoverMemoryAfterDiskError(e)}}async recoverMemoryAfterDiskError(e){try{await this.reloadMemoryFromDisk()}catch{}throw e}async validatePayload(e,t,n="insert"){return this.memoryEngine.validatePayload(e,t,n)}async insert(e,t){const n=await this.memoryEngine.insert(e,t);try{await this.diskEngine.insert(e,t)}catch(e){await this.recoverMemoryAfterDiskError(e)}return n}async find(e,t){return this.memoryEngine.find(e,t)}async findStream(e,t,n){return this.memoryEngine.findStream(e,t,n)}async update(e,t,n){const s=await this.memoryEngine.update(e,t,n);try{await this.diskEngine.update(e,t,n)}catch(e){await this.recoverMemoryAfterDiskError(e)}return s}async delete(e,t){const n=await this.memoryEngine.delete(e,t);try{await this.diskEngine.delete(e,t)}catch(e){await this.recoverMemoryAfterDiskError(e)}return n}async count(e,t){return this.memoryEngine.count(e,t)}async clear(e){await this.memoryEngine.clear(e);try{await this.diskEngine.clear(e)}catch(e){await this.recoverMemoryAfterDiskError(e)}}async createIndex(e,t,n){await this.memoryEngine.createIndex(e,t,n);try{"function"==typeof this.diskEngine.createIndex&&await this.diskEngine.createIndex(e,t,n)}catch(e){await this.recoverMemoryAfterDiskError(e)}}async dropIndex(e,t,n){await this.memoryEngine.dropIndex(e,t,n);try{"function"==typeof this.diskEngine.dropIndex&&await this.diskEngine.dropIndex(e,t,n)}catch(e){await this.recoverMemoryAfterDiskError(e)}}async beginTransaction(){await this.memoryEngine.beginTransaction();try{await this.diskEngine.beginTransaction()}catch(e){throw await this.memoryEngine.rollbackTransaction(),e}}async commitTransaction(){await this.diskEngine.commitTransaction();try{await this.memoryEngine.commitTransaction()}catch(e){throw new s("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 ut{constructor(e,t,n=["*"]){this.tableName=e,this._columns=n,this._where={},this._orderBy=[],this._joins=[],this._executor=t}as(e){return this._alias=e,this}innerJoin(e,t,n){return this._addJoin("INNER",e,t,n)}leftJoin(e,t,n){return this._addJoin("LEFT",e,t,n)}rightJoin(e,t,n){return this._addJoin("RIGHT",e,t,n)}crossJoin(e,t){return this._addJoin("CROSS",e,{},t)}join(e,t,n){return this._addJoin("INNER",e,t,n)}_addJoin(e,t,n,s){return this._joins.push({type:e,table:t,on:n,alias:s}),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(){return this._executor.execute(this.toAST())}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 ft{constructor(e,t,n,s){this.tableName=e,this._updates=t,this.executor=n,this.hooks=s,this._where={}}where(e){return this._where={...this._where,...e},this}async execute(){const e=this.toAST();await(this.hooks?.before?.(e));const t=await this.executor.execute(e);return await(this.hooks?.after?.(t,e)),t}toAST(){return{type:"UPDATE",table:this.tableName,sets:this._updates,where:this._where}}}class dt{constructor(e,t,n){this.tableName=e,this.executor=t,this.hooks=n,this._where={}}where(e){return this._where={...this._where,...e},this}async execute(){const e=this.toAST();await(this.hooks?.before?.(e));const t=await this.executor.execute(e);return await(this.hooks?.after?.(t,e)),t}toAST(){return{type:"DELETE",from:this.tableName,where:this._where}}}class pt{constructor(e,t,n,s,i){this.schema=null,this.engine=e,this.name=t,this.executor=n,this.onWrite=s,this.onHooks=i}async getSchema(){if(!this.schema){const e=await this.engine.getTableSchema(this.name);if(!e)throw new s(`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 ut(this.name,this.requireExecutor("select"),e)}requireExecutor(e){if(!this.executor)throw new s(`Table.${e}() requires a query executor (obtain the table via db.table())`,"NOT_SUPPORTED");return this.executor}async stream(e,t={}){if("function"!=typeof this.engine.findStream){const n=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 n)e(t);return n.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 ft(this.name,e,this.requireExecutor("update"),{before:async t=>{await(this.onHooks?.("beforeUpdate",[{table:t.table,where:t.where},e]))},after:async(t,n)=>{this.onWrite?.(this.name),await(this.onHooks?.("afterUpdate",[{table:n.table,where:n.where},e,t]))}})}delete(){return new dt(this.name,this.requireExecutor("delete"),{before:async e=>{await(this.onHooks?.("beforeDelete",[{table:e.from,where:e.where}]))},after:async(e,t)=>{this.onWrite?.(this.name),await(this.onHooks?.("afterDelete",[{table:t.from,where:t.where},e]))}})}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 yt(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 s(`Cannot compile statement type "${e.type}" to QueryPlan`,"COMPILE_ERROR")}}var mt;!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.QUOTED_IDENTIFIER="QUOTED_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"}(mt||(mt={}));const wt={SELECT:mt.SELECT,FROM:mt.FROM,WHERE:mt.WHERE,INSERT:mt.INSERT,INTO:mt.INTO,VALUES:mt.VALUES,UPDATE:mt.UPDATE,SET:mt.SET,DELETE:mt.DELETE,CREATE:mt.CREATE,TABLE:mt.TABLE,DROP:mt.DROP,ORDER:mt.ORDER,BY:mt.BY,ASC:mt.ASC,DESC:mt.DESC,LIMIT:mt.LIMIT,OFFSET:mt.OFFSET,AND:mt.AND,OR:mt.OR,NOT:mt.NOT,LIKE:mt.LIKE,IN:mt.IN,PRIMARY:mt.PRIMARY,KEY:mt.KEY,UNIQUE:mt.UNIQUE,DEFAULT:mt.DEFAULT,NULL:mt.NULL,TRUE:mt.TRUE,FALSE:mt.FALSE,REFERENCES:mt.REFERENCES,CASCADE:mt.CASCADE,BETWEEN:mt.BETWEEN,IF:mt.IF,EXISTS:mt.EXISTS,ALTER:mt.ALTER,ADD:mt.ADD,TRUNCATE:mt.TRUNCATE,INNER:mt.INNER,LEFT:mt.LEFT,RIGHT:mt.RIGHT,CROSS:mt.CROSS,JOIN:mt.JOIN,ON:mt.ON,AS:mt.AS,OUTER:mt.OUTER,GROUP:mt.GROUP,HAVING:mt.HAVING,COUNT:mt.COUNT,SUM:mt.SUM,AVG:mt.AVG,MIN:mt.MIN,MAX:mt.MAX,DISTINCT:mt.DISTINCT,BEGIN:mt.BEGIN,COMMIT:mt.COMMIT,ROLLBACK:mt.ROLLBACK,UNION:mt.UNION,ALL:mt.ALL,INDEX:mt.INDEX,CASE:mt.CASE,WHEN:mt.WHEN,THEN:mt.THEN,ELSE:mt.ELSE,END:mt.END,EXPLAIN:mt.EXPLAIN,ANALYZE:mt.ANALYZE,REINDEX:mt.REINDEX,VACUUM:mt.VACUUM,SAVEPOINT:mt.SAVEPOINT,RELEASE:mt.RELEASE,TO:mt.TO};class gt{constructor(e){this.position=0,this.readPosition=0,this.ch="",this.input=e,this.readChar()}nextToken(){this.skipWhitespaceAndComments();const e=this.position;let t;switch(this.ch){case",":t=this.makeToken(mt.COMMA,",",e);break;case"(":t=this.makeToken(mt.LPAREN,"(",e);break;case")":t=this.makeToken(mt.RPAREN,")",e);break;case";":t=this.makeToken(mt.SEMICOLON,";");break;case"*":t=this.makeToken(mt.STAR,"*",e);break;case".":t=this.makeToken(mt.DOT,".",e);break;case"=":t=this.makeToken(mt.EQ,"=",e);break;case"!":"="===this.peekChar()?(this.readChar(),t=this.makeToken(mt.NEQ,"!=",e)):t=this.makeToken(mt.ILLEGAL,"!",e);break;case">":"="===this.peekChar()?(this.readChar(),t=this.makeToken(mt.GTE,">=",e)):t=this.makeToken(mt.GT,">",e);break;case"<":"="===this.peekChar()?(this.readChar(),t=this.makeToken(mt.LTE,"<=",e)):">"===this.peekChar()?(this.readChar(),t=this.makeToken(mt.NEQ,"<>",e)):t=this.makeToken(mt.LT,"<",e);break;case"'":t=this.readString();break;case'"':t=this.readQuotedIdentifier();break;case"":t={type:mt.EOF,value:"",position:this.position};break;default:if(this.isLetter(this.ch)){const n=this.readIdentifier();return t={type:wt[n.toUpperCase()]??mt.IDENTIFIER,value:n,position:e},t}if(this.isDigit(this.ch)||"-"===this.ch&&this.isDigit(this.peekChar())){const n=this.readNumber();return t={type:mt.NUMBER,value:n,position:e},t}t=this.makeToken(mt.ILLEGAL,this.ch,e)}return this.readChar(),t}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]}skipWhitespaceAndComments(){for(;;){for(;" "===this.ch||"\t"===this.ch||"\n"===this.ch||"\r"===this.ch;)this.readChar();if("-"!==this.ch||"-"!==this.peekChar()){if("/"!==this.ch||"*"!==this.peekChar())return;this.skipBlockComment()}else this.skipLineComment()}}skipLineComment(){for(;"\n"!==this.ch&&"\r"!==this.ch&&""!==this.ch;)this.readChar()}skipBlockComment(){const e=this.position;for(this.readChar(),this.readChar();""!==this.ch&&("*"!==this.ch||"/"!==this.peekChar());)this.readChar();if(""===this.ch)throw new s(`Unterminated block comment at position ${e}`,"PARSE_ERROR");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(){const e=this.position,t=this.position+1;this.readChar();let n="";for(;""!==this.ch;){if("'"===this.ch){if("'"===this.peekChar()){n+="'",this.readChar(),this.readChar();continue}break}n+=this.ch,this.readChar()}if(""===this.ch)throw new s(`Unterminated string literal at position ${t}`,"PARSE_ERROR");return{type:mt.STRING,value:n,position:e}}readQuotedIdentifier(){const e=this.position;this.readChar();let t="";for(;""!==this.ch;){if('"'===this.ch){if('"'===this.peekChar()){t+='"',this.readChar(),this.readChar();continue}break}t+=this.ch,this.readChar()}if(""===this.ch)throw new s(`Unterminated quoted identifier at position ${e}`,"PARSE_ERROR");if(0===t.length)throw new s(`Empty quoted identifier at position ${e}`,"PARSE_ERROR");return{type:mt.QUOTED_IDENTIFIER,value:t,position:e}}isLetter(e){return/[a-zA-Z_]/.test(e)}isDigit(e){return/[0-9]/.test(e)}makeToken(e,t,n=this.position){return{type:e,value:t,position:n}}}function bt(e){const t=new gt(e),n=[];let s=t.nextToken();for(;s.type!==mt.EOF;)n.push(s),s=t.nextToken();return n.push(s),n}class Tt{constructor(e){this.sql=e,this.lexer=new gt(e),this.nextToken(),this.nextToken()}parseStatement(){switch(this.curToken.type){case mt.SELECT:return this.parseSelect();case mt.INSERT:return this.parseInsert();case mt.UPDATE:return this.parseUpdate();case mt.DELETE:return this.parseDelete();case mt.CREATE:return this.parseCreateStatement();case mt.DROP:return this.parseDropStatement();case mt.ALTER:return this.parseAlterTable();case mt.TRUNCATE:return this.parseTruncateTable();case mt.BEGIN:return this.parseBegin();case mt.COMMIT:return this.parseCommit();case mt.ROLLBACK:return this.parseRollback();case mt.EXPLAIN:return this.parseExplain();case mt.ANALYZE:return this.parseAnalyze();case mt.REINDEX:return this.parseReindex();case mt.VACUUM:return this.parseVacuum();case mt.SAVEPOINT:case mt.RELEASE:return this.parseSavepoint();default:throw this.error(`Unexpected token "${this.curToken.value}"`)}}parseAllStatements(){const e=[];for(;!this.curTokenIs(mt.EOF);){for(;this.curTokenIs(mt.SEMICOLON);)this.nextToken();if(this.curTokenIs(mt.EOF))break;if(e.push(this.parseStatement()),this.curTokenIs(mt.SEMICOLON))this.nextToken();else if(!this.curTokenIs(mt.EOF))throw this.error(`Expected ';' after statement, got "${this.curToken.value}"`)}return e}parseExplain(){if(this.expect(mt.EXPLAIN),this.curTokenIs(mt.EXPLAIN))throw this.error("Nested EXPLAIN is not allowed");return{type:"EXPLAIN",query:this.parseStatement()}}parseAnalyze(){return this.expect(mt.ANALYZE),this._isKeywordAsIdent()&&"TABLE"===this.curToken.value.toUpperCase()&&this.nextToken(),{type:"ANALYZE",table:this.expectIdentifier("table name")}}parseReindex(){return this.expect(mt.REINDEX),this._isKeywordAsIdent()&&"TABLE"===this.curToken.value.toUpperCase()&&this.nextToken(),{type:"REINDEX",table:this.expectIdentifier("table name")}}parseVacuum(){return this.expect(mt.VACUUM),{type:"VACUUM"}}parseSavepoint(){let e;return this.curTokenIs(mt.RELEASE)?(e="RELEASE",this.nextToken()):(e="SAVE",this.expect(mt.SAVEPOINT)),this.curTokenIs(mt.SAVEPOINT)&&this.nextToken(),{type:"SAVEPOINT",name:this.expectIdentifier("savepoint name"),action:e}}parseBegin(){return this.expect(mt.BEGIN),this._isKeywordAsIdent()&&"TRANSACTION"===this.curToken.value.toUpperCase()&&this.nextToken(),{type:"BEGIN"}}parseCommit(){return this.expect(mt.COMMIT),this._isKeywordAsIdent()&&"TRANSACTION"===this.curToken.value.toUpperCase()&&this.nextToken(),{type:"COMMIT"}}parseRollback(){return this.expect(mt.ROLLBACK),this._isKeywordAsIdent()&&"TRANSACTION"===this.curToken.value.toUpperCase()?(this.nextToken(),{type:"ROLLBACK"}):this.curTokenIs(mt.TO)||this._isKeywordAsIdent()&&"TO"===this.curToken.value.toUpperCase()?(this.nextToken(),(this.curTokenIs(mt.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(mt.CREATE),this.curTokenIs(mt.TABLE))return this.parseCreateTable();if(this.curTokenIs(mt.INDEX)||this._isKeywordAsIdent()&&"INDEX"===this.curToken.value.toUpperCase())return this.parseCreateIndex();if(this.curTokenIs(mt.UNIQUE)&&(this.nextToken(),this.curTokenIs(mt.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(mt.INDEX);const e=this.expectIdentifier("index name");this.expect(mt.ON);const t=this.expectIdentifier("table name");this.expect(mt.LPAREN);const n=this.expectIdentifier("column name");return this.expect(mt.RPAREN),{type:"CREATE_INDEX",name:e,table:t,column:n}}parseDropStatement(){if(this.expect(mt.DROP),this.curTokenIs(mt.TABLE))return this.parseDropTable();if(this.curTokenIs(mt.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(mt.INDEX);const e=this.expectIdentifier("index name");let t="",n="";return this.curTokenIs(mt.ON)&&(this.nextToken(),t=this.expectIdentifier("table name"),this.curTokenIs(mt.LPAREN)&&(this.nextToken(),n=this.expectIdentifier("column name"),this.expect(mt.RPAREN))),{type:"DROP_INDEX",name:e,table:t,column:n}}parseSelect(){this.expect(mt.SELECT);let e=!1;this.curTokenIs(mt.DISTINCT)&&(e=!0,this.nextToken());const t=[];if(this.curTokenIs(mt.STAR))for(t.push("*"),this.nextToken();this.curTokenIs(mt.COMMA);)this.nextToken(),t.push(this.parseColumnWithAlias());else t.push(...this.parseColumnList());let n,s,i="";this.curTokenIs(mt.FROM)&&(this.nextToken(),this.curTokenIs(mt.LPAREN)?(this.nextToken(),n=this.parseSelect(),this.expect(mt.RPAREN),this.curTokenIs(mt.AS)?(this.nextToken(),s=this.expectIdentifier("alias")):this.curToken.type!==mt.IDENTIFIER||this._isReservedAfterFrom()||(s=this.curToken.value,this.nextToken())):(i=this.expectIdentifier("table name"),this.curTokenIs(mt.AS)?(this.nextToken(),s=this.expectIdentifier("alias")):this.curToken.type!==mt.IDENTIFIER||this._isReservedAfterFrom()||(s=this.curToken.value,this.nextToken())));const r={type:"SELECT",columns:t,distinct:e||void 0,from:i,alias:s,where:{}};n&&(r.fromSubquery=n);const a=this.parseJoinClauses();return a.length>0&&(r.joins=a),this.curTokenIs(mt.WHERE)&&(this.nextToken(),r.where=this.parseCondition()),this.curTokenIs(mt.GROUP)&&(this.nextToken(),this.expect(mt.BY),r.groupBy=this.parseGroupByList()),this.curTokenIs(mt.HAVING)&&(this.nextToken(),r.having=this.parseCondition()),this.curTokenIs(mt.ORDER)&&(this.nextToken(),this.expect(mt.BY),r.orderBy=this.parseOrderByList()),this.curTokenIs(mt.LIMIT)&&(this.nextToken(),r.limit=this.expectNumber("LIMIT value")),this.curTokenIs(mt.OFFSET)&&(this.nextToken(),r.offset=this.expectNumber("OFFSET value")),this.curTokenIs(mt.UNION)?this.parseUnion(r):r}parseUnion(e){this.expect(mt.UNION);let t=!1;this.curTokenIs(mt.ALL)&&(t=!0,this.nextToken());const n=this.parseSelect(),s={type:"SELECT_UNION",left:e,right:n,all:t||void 0};return this.adoptTrailingClauses(s,n),this.curTokenIs(mt.UNION)?this.parseUnionChain(s):s}parseUnionChain(e){this.expect(mt.UNION);let t=!1;this.curTokenIs(mt.ALL)&&(t=!0,this.nextToken());const n=this.parseSelect(),s={type:"SELECT_UNION",left:e,right:n,all:t||void 0};return this.adoptTrailingClauses(s,n),this.curTokenIs(mt.UNION)?this.parseUnionChain(s):s}adoptTrailingClauses(e,t){"SELECT"===t.type&&(t.orderBy&&(e.orderBy=t.orderBy,delete t.orderBy),void 0!==t.limit&&(e.limit=t.limit,delete t.limit),void 0!==t.offset&&(e.offset=t.offset,delete t.offset))}parseJoinClauses(){const e=[];for(;this._isJoinKeyword();)e.push(this.parseJoinClause());return e}_isJoinKeyword(){return this.curTokenIs(mt.INNER)||this.curTokenIs(mt.LEFT)||this.curTokenIs(mt.RIGHT)||this.curTokenIs(mt.CROSS)||this.curTokenIs(mt.JOIN)}parseJoinClause(){let e="INNER";this.curTokenIs(mt.INNER)?(e="INNER",this.nextToken()):this.curTokenIs(mt.LEFT)?(e="LEFT",this.nextToken(),this.curTokenIs(mt.OUTER)&&this.nextToken()):this.curTokenIs(mt.RIGHT)?(e="RIGHT",this.nextToken(),this.curTokenIs(mt.OUTER)&&this.nextToken()):this.curTokenIs(mt.CROSS)&&(e="CROSS",this.nextToken()),this.expect(mt.JOIN);const t=this.expectIdentifier("table name");let n;this.curTokenIs(mt.AS)?(this.nextToken(),n=this.expectIdentifier("alias")):this.curToken.type!==mt.IDENTIFIER||this._isJoinReserved()||(n=this.curToken.value,this.nextToken());let s={};return"CROSS"!==e&&this.curTokenIs(mt.ON)&&(this.nextToken(),s=this.parseCondition()),{type:e,table:t,alias:n,on:s}}_isReservedAfterFrom(){return this.curTokenIs(mt.WHERE)||this.curTokenIs(mt.ORDER)||this.curTokenIs(mt.LIMIT)||this.curTokenIs(mt.OFFSET)||this.curTokenIs(mt.GROUP)||this._isJoinKeyword()}_isJoinReserved(){return this.curTokenIs(mt.ON)||this.curTokenIs(mt.WHERE)||this.curTokenIs(mt.ORDER)||this.curTokenIs(mt.LIMIT)||this._isJoinKeyword()}parseInsert(){this.expect(mt.INSERT),this.expect(mt.INTO);const e=this.expectIdentifier("table name");let t;if(this.curTokenIs(mt.LPAREN)&&(this.nextToken(),t=this.parseIdentifierList(),this.expect(mt.RPAREN)),this.curTokenIs(mt.SELECT))return{type:"INSERT",into:e,columns:t,select:this.parseSelect()};this.expect(mt.VALUES);const n=[];do{this.curTokenIs(mt.COMMA)&&this.nextToken(),this.expect(mt.LPAREN);const e=this.parseValueList();this.expect(mt.RPAREN),n.push(e)}while(this.curTokenIs(mt.COMMA));if(t)for(let e=0;e<n.length;e++)if(n[e].length!==t.length)throw this.error(`INSERT column/value count mismatch: ${t.length} column(s) but row ${e+1} has ${n[e].length} value(s)`);return{type:"INSERT",into:e,columns:t,values:n}}newColumnMap(){return Object.create(null)}parseUpdate(){this.expect(mt.UPDATE);const e=this.expectIdentifier("table name");this.expect(mt.SET);const t=this.newColumnMap();do{this.curTokenIs(mt.COMMA)&&this.nextToken();const e=this.expectIdentifier("column name");this.expect(mt.EQ),t[e]=this.parseValue()}while(this.curTokenIs(mt.COMMA));let n=this.newColumnMap();return this.curTokenIs(mt.WHERE)&&(this.nextToken(),n=this.parseCondition()),{type:"UPDATE",table:e,sets:t,where:n}}parseDelete(){this.expect(mt.DELETE),this.expect(mt.FROM);const e=this.expectIdentifier("table name");let t=this.newColumnMap();return this.curTokenIs(mt.WHERE)&&(this.nextToken(),t=this.parseCondition()),{type:"DELETE",from:e,where:t}}parseCreateTable(){this.expect(mt.TABLE);let e=!1;this.curTokenIs(mt.IF)&&(this.nextToken(),this.expect(mt.NOT),this.expect(mt.EXISTS),e=!0);const t=this.expectIdentifier("table name");this.expect(mt.LPAREN);const n=[];do{this.curTokenIs(mt.COMMA)&&this.nextToken(),n.push(this.parseColumnDef())}while(this.curTokenIs(mt.COMMA));return this.expect(mt.RPAREN),{type:"CREATE_TABLE",name:t,columns:n,ifNotExists:e||void 0}}parseColumnDef(){const e={name:this.expectIdentifier("column name"),type:this.expectIdentifier("column type").toLowerCase()};for(;this.curTokenIs(mt.PRIMARY)||this.curTokenIs(mt.UNIQUE)||this.curTokenIs(mt.NOT)||this.curTokenIs(mt.DEFAULT)||this.curTokenIs(mt.REFERENCES);)if(this.curTokenIs(mt.PRIMARY))this.nextToken(),this.expect(mt.KEY),e.primaryKey=!0;else if(this.curTokenIs(mt.UNIQUE))this.nextToken(),e.unique=!0;else if(this.curTokenIs(mt.NOT))this.nextToken(),this.expect(mt.NULL),e.required=!0;else if(this.curTokenIs(mt.DEFAULT))this.nextToken(),e.default=this.parseValue();else{if(!this.curTokenIs(mt.REFERENCES))break;{this.nextToken();const t=this.expectIdentifier("referenced table");this.expect(mt.LPAREN);const n=this.expectIdentifier("referenced column");for(this.expect(mt.RPAREN),e.references=`${t}.${n}`;this.curTokenIs(mt.ON);)if(this.nextToken(),this.curTokenIs(mt.DELETE))this.nextToken(),e.onDelete=this.parseCascadeAction();else{if(!this.curTokenIs(mt.UPDATE))break;this.nextToken(),e.onUpdate=this.parseCascadeAction()}}}return e}parseCascadeAction(){return this.curTokenIs(mt.CASCADE)?(this.nextToken(),"CASCADE"):this.curTokenIs(mt.SET)?(this.nextToken(),this.expect(mt.NULL),"SET NULL"):this.curToken.type===mt.IDENTIFIER&&"RESTRICT"===this.curToken.value.toUpperCase()?(this.nextToken(),"RESTRICT"):"RESTRICT"}parseAlterTable(){this.expect(mt.ALTER),this.expect(mt.TABLE);const e=this.expectIdentifier("table name");let t;if(this.curTokenIs(mt.ADD))return t="ADD",this.nextToken(),this.curToken.type===mt.IDENTIFIER&&"COLUMN"===this.curToken.value.toUpperCase()&&this.nextToken(),{type:"ALTER_TABLE",name:e,action:t,column:this.parseColumnDef()};if(this.curTokenIs(mt.DROP)||this._isKeywordAsIdent()&&"DROP"===this.curToken.value.toUpperCase())return t="DROP",this.nextToken(),this.curToken.type===mt.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(mt.TRUNCATE),this.expect(mt.TABLE),{type:"TRUNCATE_TABLE",name:this.expectIdentifier("table name")}}parseDropTable(){this.expect(mt.TABLE);let e=!1;return this.curTokenIs(mt.IF)&&(this.nextToken(),this.expect(mt.EXISTS),e=!0),{type:"DROP_TABLE",name:this.expectIdentifier("table name"),ifExists:e||void 0}}parseCondition(){return this.parseOrExpression()}parseOrExpression(){const e=[this.parseAndExpression()];for(;this.curTokenIs(mt.OR);)this.nextToken(),e.push(this.parseAndExpression());return 1===e.length?e[0]:{$or:e}}parseAndExpression(){const e=[this.parseSimpleCondition()];for(;this.curTokenIs(mt.AND);)this.nextToken(),e.push(this.parseSimpleCondition());return 1===e.length?e[0]:{$and:e}}parseWhere(){return this.parseCondition()}parseSimpleCondition(){if(this.curTokenIs(mt.EXISTS)||this._isKeywordAsIdent()&&"EXISTS"===this.curToken.value.toUpperCase())return this.nextToken(),this.parseExistsCondition(!1);if(this.curTokenIs(mt.NOT)&&this._peekIsExists())return this.nextToken(),this.nextToken(),this.parseExistsCondition(!0);if(this.curTokenIs(mt.NOT)&&!this._isNotInOrLike())return this.nextToken(),{$not:this.parseSimpleCondition()};if(this.curTokenIs(mt.LPAREN)){this.nextToken();const e=this.parseCondition();return this.expect(mt.RPAREN),e}const e=this.parseColumnRef();if(this.curTokenIs(mt.IDENTIFIER)&&"IS"===this.curToken.value.toUpperCase()){this.nextToken();const t=this.curTokenIs(mt.NOT);t&&this.nextToken(),this.expect(mt.NULL);const n=this.newColumnMap();return n[e]=t?{$isNotNull:!0}:{$isNull:!0},n}if(this.curTokenIs(mt.BETWEEN)){this.nextToken();const t=this.parseValue();this.expect(mt.AND);const n=this.parseValue(),s=this.newColumnMap();return s[e]={$gte:t,$lte:n},s}if(this.curTokenIs(mt.NOT)&&this.peekTokenIs(mt.BETWEEN)){this.nextToken(),this.nextToken();const t=this.parseValue();this.expect(mt.AND);const n=this.parseValue(),s=this.newColumnMap();return s[e]={$or:[{$lt:t},{$gt:n}]},s}if(this.curTokenIs(mt.NOT)){if(this.peekTokenIs(mt.IN)){if(this.nextToken(),this.nextToken(),this.expect(mt.LPAREN),this.curTokenIs(mt.SELECT)){const t=this.parseSelect();this.expect(mt.RPAREN);const n=this.newColumnMap();return n[e]={$nin:{$subquery:t}},n}const t=this.parseValueList();this.expect(mt.RPAREN);const n=this.newColumnMap();return n[e]={$nin:t},n}if(this.peekTokenIs(mt.LIKE)){this.nextToken(),this.nextToken();const t=this.parseValue(),n=this.newColumnMap();return n[e]={$not:{$like:t}},n}}if(this.curTokenIs(mt.LIKE)){this.nextToken();const t=this.parseValue(),n=this.newColumnMap();return n[e]={$like:t},n}if(this.curTokenIs(mt.IN)){if(this.nextToken(),this.expect(mt.LPAREN),this.curTokenIs(mt.SELECT)){const t=this.parseSelect();this.expect(mt.RPAREN);const n=this.newColumnMap();return n[e]={$in:{$subquery:t}},n}const t=this.parseValueList();this.expect(mt.RPAREN);const n=this.newColumnMap();return n[e]={$in:t},n}if(this.curTokenIs(mt.AND)||this.curTokenIs(mt.OR)||this.curTokenIs(mt.RPAREN)||this.curTokenIs(mt.EOF)||this.curToken.type===mt.IDENTIFIER&&["THEN","END","ELSE","NULLS","LIMIT","OFFSET","ORDER","GROUP","HAVING","UNION","WHERE"].includes(this.curToken.value.toUpperCase())){const t=this.newColumnMap();return t[e]={$eq:!0},t}const t=this.parseComparisonOp();if(this.curTokenIs(mt.LPAREN)&&this.peekTokenIs(mt.SELECT)){this.nextToken();const n=this.parseSelect();this.expect(mt.RPAREN);const s=this.newColumnMap();return s[e]={[t]:{$subquery:n}},s}let n;n=this.curToken.type===mt.QUOTED_IDENTIFIER?{$col:this.expectColumnReference()}:this.curToken.type!==mt.IDENTIFIER||this._isReservedKeywordToken()?this.parseValue():{$col:this.expectColumnReference()};const s=this.newColumnMap();return s[e]={[t]:n},s}_isReservedKeywordToken(){return this.curToken.type!==mt.IDENTIFIER&&this._isKeywordAsIdent()}parseExistsCondition(e){this.expect(mt.LPAREN);const t=this.parseSelect();return this.expect(mt.RPAREN),{$exists:{$subquery:t,$negate:e||void 0}}}_peekIsExists(){return this.peekToken.type===mt.EXISTS||this.peekToken.type===mt.IDENTIFIER&&"EXISTS"===this.peekToken.value.toUpperCase()}_isNotInOrLike(){return this.peekTokenIs(mt.IN)||this.peekTokenIs(mt.LIKE)}peekTokenIs(e){return this.peekToken.type===e}expectColumnReference(){const e=this.expectIdentifier("column reference");return this.curTokenIs(mt.DOT)?(this.nextToken(),`${e}.${this.expectIdentifier('column name after "."')}`):e}parseComparisonOp(){switch(this.curToken.type){case mt.EQ:return this.nextToken(),"$eq";case mt.NEQ:return this.nextToken(),"$ne";case mt.GT:return this.nextToken(),"$gt";case mt.GTE:return this.nextToken(),"$gte";case mt.LT:return this.nextToken(),"$lt";case mt.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(mt.COMMA);)this.nextToken(),e.push(this.parseColumnWithAlias());return e}parseColumnWithAlias(){let e=this.parseColumnRef();if(this.curTokenIs(mt.AS))this.nextToken(),e=`${e} AS ${this.expectIdentifier("alias")}`;else if(this.curToken.type===mt.IDENTIFIER&&!this._isReservedAfterFrom()&&!this._isJoinKeyword()){const t=this.curToken.value;this.nextToken(),e=`${e} AS ${t}`}return e}parseColumnRef(){if(this.curTokenIs(mt.CASE))return this.parseCaseExpressionText();if(this.curTokenIs(mt.NUMBER)){const e=this.curToken.value;return this.nextToken(),e}if(this.curTokenIs(mt.STRING)){const e=this.curToken.value;return this.nextToken(),`'${e.replace(/'/g,"''")}'`}if(this.curTokenIs(mt.COUNT)||this.curTokenIs(mt.SUM)||this.curTokenIs(mt.AVG)||this.curTokenIs(mt.MIN)||this.curTokenIs(mt.MAX))return this.parseAggregateCall();if(this.curTokenIs(mt.QUOTED_IDENTIFIER)){const e=this.curToken.value;if(this.nextToken(),this.curTokenIs(mt.DOT)){this.nextToken();const t=this.expectIdentifier('column name after "."');return`"${e.replace(/"/g,'""')}".${t}`}return`"${e.replace(/"/g,'""')}"`}const e=this.expectIdentifier("column name");return this.curTokenIs(mt.DOT)?(this.nextToken(),`${e}.${this.expectIdentifier("column name")}`):e}parseCaseExpressionText(){const e=this.curToken.position;this.nextToken();let t=1,n=e+4;for(;!this.curTokenIs(mt.EOF)&&t>0&&(this.curTokenIs(mt.CASE)&&t++,!this.curTokenIs(mt.END)||(t--,n=this.curToken.position+3,this.nextToken(),0!==t));)n=this.curToken.position+this.curToken.value.length,this.nextToken();let s=this.sql.slice(e,n);return this.curTokenIs(mt.AS)?(this.nextToken(),s+=` AS ${this.expectIdentifier("alias")}`):this.curToken.type!==mt.IDENTIFIER||this.curTokenIs(mt.COMMA)||this._isReservedAfterFrom()||(s+=` AS ${this.curToken.value}`,this.nextToken()),s}parseAggregateCall(){const e=this.curToken.value.toUpperCase();this.nextToken(),this.expect(mt.LPAREN);let t,n=!1;this.curTokenIs(mt.DISTINCT)&&(n=!0,this.nextToken()),this.curTokenIs(mt.STAR)?(t="*",this.nextToken()):t=this.parseColumnRef(),this.expect(mt.RPAREN);let s="";this.curTokenIs(mt.AS)?(this.nextToken(),s=this.expectIdentifier("alias")):this.curToken.type===mt.IDENTIFIER&&this._isAggregateAlias()&&(s=this.curToken.value,this.nextToken());const i=n?`DISTINCT ${t}`:t;return s?`${e}(${i}) AS ${s}`:`${e}(${i})`}_isAggregateAlias(){return!this._isReservedAfterFrom()&&!this._isJoinKeyword()}parseIdentifierList(){const e=[];for(e.push(this.parseIdentifierWithDot());this.curTokenIs(mt.COMMA);)this.nextToken(),e.push(this.parseIdentifierWithDot());return e}parseValueList(){const e=[];for(e.push(this.parseValue());this.curTokenIs(mt.COMMA);)this.nextToken(),e.push(this.parseValue());return e}parseOrderOrGroupKey(){if(this.curTokenIs(mt.NUMBER)){const e=this.curToken.value;if(!/^\d+$/.test(e)||Number(e)<1)throw this.error(`Invalid output column ordinal "${e}" (must be a positive integer)`);return this.nextToken(),e}return this.parseIdentifierWithDot()}parseGroupByList(){const e=[];for(e.push(this.parseOrderOrGroupKey());this.curTokenIs(mt.COMMA);)this.nextToken(),e.push(this.parseOrderOrGroupKey());return e}parseOrderByList(){const e=[];for(e.push(this.parseOrderBy());this.curTokenIs(mt.COMMA);)this.nextToken(),e.push(this.parseOrderBy());return e}parseOrderBy(){const e=this.parseOrderOrGroupKey();let t,n="asc";return this.curTokenIs(mt.ASC)?this.nextToken():this.curTokenIs(mt.DESC)&&(n="desc",this.nextToken()),this.curTokenIs(mt.IDENTIFIER)&&"NULLS"===this.curToken.value.toUpperCase()&&(this.nextToken(),this.curTokenIs(mt.IDENTIFIER)&&"FIRST"===this.curToken.value.toUpperCase()?(t="first",this.nextToken()):this.curTokenIs(mt.IDENTIFIER)&&"LAST"===this.curToken.value.toUpperCase()&&(t="last",this.nextToken())),{column:e,direction:n,...t?{nulls:t}:{}}}parseIdentifierWithDot(){const e=this.expectIdentifier("identifier");return this.curTokenIs(mt.DOT)?(this.nextToken(),`${e}.${this.expectIdentifier("identifier")}`):e}parseValue(){switch(this.curToken.type){case mt.STRING:{const e=this.curToken.value;return this.nextToken(),e}case mt.NUMBER:{const e=Number(this.curToken.value);return this.nextToken(),e}case mt.TRUE:return this.nextToken(),!0;case mt.FALSE:return this.nextToken(),!1;case mt.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===mt.QUOTED_IDENTIFIER){const e=this.curToken.value;return this.nextToken(),e}if(this.curToken.type===mt.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!==mt.EOF&&this.curToken.type!==mt.ILLEGAL&&this.curToken.type!==mt.STRING&&this.curToken.type!==mt.QUOTED_IDENTIFIER&&this.curToken.type!==mt.NUMBER&&this.curToken.type!==mt.COMMA&&this.curToken.type!==mt.LPAREN&&this.curToken.type!==mt.RPAREN&&this.curToken.type!==mt.SEMICOLON&&this.curToken.type!==mt.EQ&&this.curToken.type!==mt.NEQ&&this.curToken.type!==mt.GT&&this.curToken.type!==mt.GTE&&this.curToken.type!==mt.LT&&this.curToken.type!==mt.LTE&&this.curToken.type!==mt.DOT&&this.curToken.type!==mt.STAR}expectNumber(e){if(this.curToken.type===mt.NUMBER){const e=Number(this.curToken.value);return this.nextToken(),e}throw this.error(`Expected ${e}, got "${this.curToken.value}"`)}error(e){return new s(`Parse error at position ${this.curToken.position}: ${e}`,"PARSE_ERROR")}}function Et(e){return new Tt(e).parseAllStatements()}function kt(e){return new Tt(e).parseWhere()}function St(e,t,n){let i,r=t.trim();if(r.length>=2&&r.startsWith('"')&&r.endsWith('"')&&(r=r.slice(1,-1).replace(/""/g,'"')),r in e)return e[r];if(r.includes(".")){const t=r.split(".").pop();if(t in e)return e[t]}let a=0;for(const t of Object.keys(e))t.endsWith(`.${r}`)&&(i=e[t],a+=1);if(1===a)return i;if(n.strict)throw new s(a>1?`Ambiguous column "${r}" in ${n.context}: present in multiple tables`:`Unknown column "${r}" in ${n.context}`,"COLUMN_NOT_FOUND",{column:r});return u}const xt=new Map;function At(e){const t=e.trim();if(!/^\s*CASE\b/i.test(t))return null;const n=xt.get(t);if(n)return n;const i=function(e){const t=bt(e);let n=0;const i=(t,n)=>{throw new s(`${t} (at offset ${n} in "${e}")`,"PARSE_ERROR")};t[n]?.type!==mt.CASE&&i("Expected CASE",t[n]?.position??0),n++;const r=[];let a=null;const o=(n,s)=>{const i=t[n];if(!i)return"";const r=t[s],a=r?r.position:e.length;return e.slice(i.position,a).trim()};for(;n<t.length;){const e=t[n];if(e.type===mt.WHEN){const a=n+1;let c=0,h=-1;for(let e=a;e<t.length;e++){const n=t[e];if(n.type===mt.LPAREN)c++;else if(n.type===mt.RPAREN)c--;else{if(0===c&&n.type===mt.THEN){h=e;break}if(0===c&&(n.type===mt.ELSE||n.type===mt.END))break}}h<0&&i("CASE WHEN without matching THEN",e.position);const l=h+1;let u=0,f=t.length;for(let e=l;e<t.length;e++){const n=t[e];if(n.type===mt.LPAREN)u++;else if(n.type===mt.RPAREN)u--;else{if(0===u&&(n.type===mt.WHEN||n.type===mt.ELSE||n.type===mt.END)){f=e;break}if(0===u&&n.type===mt.CASE){let n=1;for(let s=e+1;s<t.length;s++)if(t[s].type===mt.CASE)n++;else if(t[s].type===mt.END&&(n--,0===n)){e=s;break}}}}const d=o(a,h),p=o(l,f);let y;d||i("CASE WHEN has an empty condition",e.position),p||i("CASE THEN has an empty result",t[l]?.position??e.position);try{y=kt(d)}catch(e){throw new s(`Invalid CASE WHEN condition "${d}": ${e.message}`,"PARSE_ERROR",{condition:d})}r.push({conditionText:d,resultText:p,condition:y}),n=f;continue}if(e.type===mt.ELSE){const s=n+1;let r=0,c=t.length;for(let e=s;e<t.length;e++){const n=t[e];if(n.type===mt.LPAREN)r++;else if(n.type===mt.RPAREN)r--;else{if(0===r&&n.type===mt.END){c=e;break}if(0===r&&n.type===mt.CASE){let n=1;for(let s=e+1;s<t.length;s++)if(t[s].type===mt.CASE)n++;else if(t[s].type===mt.END&&(n--,0===n)){e=s;break}}}}a=o(s,c),a||i("CASE ELSE has an empty result",e.position),n=c;continue}if(e.type===mt.END){n++;break}i(`Unexpected token "${e.value}" in CASE`,e.position)}if(0===r.length)throw new s(`CASE expression has no WHEN branch: "${e}"`,"PARSE_ERROR");let c=null;const h=t.slice(n).filter(e=>e.type!==mt.EOF&&e.type!==mt.SEMICOLON);if(h.length>0){const e=h[0];if(e.type===mt.AS){const e=h[1];!e||e.type!==mt.IDENTIFIER&&e.type!==mt.QUOTED_IDENTIFIER||(c=e.value)}else e.type!==mt.IDENTIFIER&&e.type!==mt.QUOTED_IDENTIFIER||(c=e.value)}return{branches:r,elseText:a,alias:c,source:e}}(t);return xt.size>=512&&xt.clear(),xt.set(t,i),i}function It(e,t){for(const n of e.branches)if(S(t,n.condition,{$col:!0}))return Rt(n.resultText,t);return null!==e.elseText?Rt(e.elseText,t):null}function Rt(e,t){const n=e.trim();if(""===n)throw new s("Empty expression value","PARSE_ERROR");const i=n.match(/^'(.*)'$/s);if(i)return i[1].replace(/''/g,"'");if(/^NULL$/i.test(n))return null;if(/^TRUE$/i.test(n))return!0;if(/^FALSE$/i.test(n))return!1;if(/^-?\d+(\.\d+)?$/.test(n))return Number(n);const r=At(n);if(r)return It(r,t);if(/^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)?$/.test(n)){const e=St(t,n,{strict:!1,context:"CASE result"});if(f(e))throw new s(`Unknown column "${n}" in expression`,"COLUMN_NOT_FOUND",{column:n});return e}throw new s(`Unsupported expression in CASE result: "${n}"`,"NOT_SUPPORTED",{expression:n})}function Ot(e){const t=e.trim().match(/^(COUNT|SUM|AVG|MIN|MAX)\s*\(\s*([\s\S]+?)\s*\)\s*(?:AS\s+([A-Za-z_][A-Za-z0-9_]*))?$/i);if(!t)return null;const n=t[1].toUpperCase();let s=t[2].trim();const i=t[3],r=/^DISTINCT\s+/i.test(s);r&&(s=s.replace(/^\s*DISTINCT\s+/i,"").trim());const a=`${n}(${r?"DISTINCT ":""}${s})`;return{func:n,arg:s,outputKey:i||e.trim(),distinct:r,exprKey:a}}function Nt(e){return null!==Ot(e)}function Ct(e){const t=e.trim();return t.includes(".")?t.split(".").pop():t}function $t(e,t){const n=At(t);return n?It(n,e):St(e,Lt(t),{strict:!0,context:"GROUP BY"})}function Lt(e){const t=e.trim();return t.length>=2&&t.startsWith('"')&&t.endsWith('"')?t.slice(1,-1).replace(/""/g,'"'):t.includes(".")?t.split(".").map(e=>Lt(e)).join("."):t}function vt(e,t){const n=e.trim(),s=n.match(/^'(.*)'$/s);return s?s[1].replace(/''/g,"'"):/^-?\d+(\.\d+)?$/.test(n)?Number(n):!!/^TRUE$/i.test(n)||!/^FALSE$/i.test(n)&&(/^NULL$/i.test(n)?null:St(t,n,{strict:!1,context:"SELECT list"}))}function Ut(e,t){if(0===e.length)return null;let n="SUM"===t||"AVG"===t?0:e[0];for(let s=0;s<e.length;s++){const i=e[s];switch(t){case"SUM":case"AVG":n+=i;break;case"MIN":s>0&&i<n&&(n=i);break;case"MAX":s>0&&i>n&&(n=i)}}return"AVG"===t?n/e.length:n}class Dt{constructor(e,t=0){this.engine=e,this.maxRowsPerQuery=t}async execute(e){try{return await this.dispatch(e)}catch(e){throw e instanceof Error?e:new s(String(e),"QUERY_ERROR")}}dispatch(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 s("Unknown statement type","UNKNOWN_STATEMENT")}}async executeSelectUnion(e,t="result"){const n=await this.executeSelectPart(e.left,t),s=await this.executeSelectPart(e.right,t),i=n.length>0?Object.keys(n[0]):[],r=n.map(e=>e);let a;if(e.all){for(const e of s)r.push(this.projectUnionRow(e,i));a=r}else{const e=new Set;a=[];for(const t of r){const n=Object.values(t).map(d).join("");e.has(n)||(e.add(n),a.push(t))}for(const t of s){const n=this.projectUnionRow(t,i),s=Object.values(n).map(d).join("");e.has(s)||(e.add(s),a.push(n))}}e.orderBy&&e.orderBy.length>0&&(a=this.orderCompoundResult(a,e.orderBy));const o=e.offset??0;if(o>0||void 0!==e.limit){const t=e.limit??a.length;a=a.slice(o,o+t)}return a}orderCompoundResult(e,t){const n=e[0],i=n?Object.keys(n):[];return P(e,t.map(e=>{const t=/^\d+$/.test(e.column.trim())?Number(e.column.trim()):null;if(null!==t){if(t<1||t>i.length)throw new s(`ORDER BY position ${t} is out of range: compound query has ${i.length} output column(s)`,"QUERY_ERROR");return{...e,column:i[t-1]}}const r=e.column.includes(".")?e.column.split(".").pop():e.column;if(i.length>0&&!(e.column in n)&&!(r in n))throw new s(`Unknown column "${e.column}" in ORDER BY of compound query. Output columns: ${i.join(", ")}`,"COLUMN_NOT_FOUND");return{...e,column:e.column in n?e.column:r}}))}async executeSelectPart(e,t="result"){return"SELECT_UNION"===e.type?this.executeSelectUnion(e,t):this.executeSelect(e,t)}projectUnionRow(e,t){if(0===t.length)return e;const n=Object.values(e),s={};for(let e=0;e<t.length;e++)s[t[e]]=e<n.length?n[e]:null;return s}async executeExplain(e){const t=Date.now();let n=null,s=0;if("SELECT"===e.query.type||"SELECT_UNION"===e.query.type){try{n=await this.execute(e.query)}catch{}s=Array.isArray(n)?n.length:0}else if("UPDATE"===e.query.type||"DELETE"===e.query.type)try{await this.resolveWriteWhere(e.query);const t=yt(e.query);t.where=e.query.where,s=await this.engine.count(t.table,t)}catch{s=0}const i=Date.now()-t;let r=null;try{r=yt(e.query)}catch{}let a="none";if(r&&r.table&&r.where&&Object.keys(r.where).length>0)try{const e=await this.engine.getTableSchema(r.table);if(e){const t=n=>{for(const[s,i]of Object.entries(n)){if("$and"===s){for(const e of i){const n=t(e);if(n)return n}continue}if("$or"===s||"$not"===s)continue;const n=e.columns[s];if(n){if(n.primaryKey)return"pk";if(n.index||n.unique)return`index:${s}`}}return null};a=t(r.where)??"none"}}catch{}return{type:e.query.type,table:r?.table,columns:r?.columns,where:r?.where||{},orderBy:r?.orderBy||[],limit:r?.limit,offset:r?.offset,usingIndex:a,estimatedRows:s,actualTimeMs:i}}analyzeSelect(e){const t=!!(e.groupBy&&e.groupBy.length>0),n=!t&&this._hasAggregateColumn(e.columns),s=!!(e.joins&&e.joins.length>0),i=this.hasCaseColumn(e.columns)||!!e.where&&this.whereHasCase(e.where),r=this.orderByUsesSelectAlias(e),a=e.columns.some(e=>/\s+AS\s+\w+$/i.test(e)),o=!(t||n||s||e.fromSubquery||e.distinct||e.having&&Object.keys(e.having).length>0||r||i||a);let c=e.columns.length>0;for(const t of e.columns)if("*"!==t&&!/^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)?$/.test(t)){c=!1;break}return{hasGroupBy:t,hasAggregate:n,isJoinQuery:s,needsRawRows:i,orderByAlias:r,hasSelectAlias:a,limitPushdownSafe:o,engineEquivalentProjection:c,streamable:c&&!t&&!n&&!s&&!e.fromSubquery&&!e.distinct&&!(e.having&&Object.keys(e.having).length>0)&&!(e.orderBy&&e.orderBy.length>0)&&!i&&!r&&!a&&!this.hasCorrelatedRefs(e.where)&&!x(e.where)}}async executeSelect(e,t="result"){const n=this.analyzeSelect(e),{hasGroupBy:s,hasAggregate:i,isJoinQuery:r,needsRawRows:a,orderByAlias:o,hasSelectAlias:c,limitPushdownSafe:h}=n;let l;if(await this.assertCaseColumnsExist(e,r),await this.assertWhereColumnsExist(e,r),e.orderBy&&e.orderBy.length>0&&!e.fromSubquery){const t=this.selectAliasNames(e),n={};for(const s of e.orderBy){const e=Lt(s.column.trim());e&&!/^\d+$/.test(e)&&(At(s.column)||t.has(e)||(n[e]={$exists:!0}))}Object.keys(n).length>0&&await this.validateWhereColumns(e,n,{context:"ORDER BY",rejectAmbiguous:r})}if(e.fromSubquery){const t=await this.executeSelectPart(e.fromSubquery);l=r?await this.executeJoinSelect(e,t.map(t=>this.prefixRow(t,e.alias??""))):t,r||(await this.resolveOutputOrdinals(e),this.normalizeUnprefixedReferences(e,[e.alias??e.from]),e.where&&Object.keys(e.where).length>0&&(e.where=await this.resolveSubqueries(e.where),l=l.filter(t=>S(t,e.where))))}else if(e.from||r)if(r)l=await this.executeJoinSelect(e);else{await this.resolveOutputOrdinals(e);const t=[e.alias??e.from].filter(Boolean);if(this.normalizeUnprefixedReferences(e,t),e.where&&this.hasCorrelatedRefs(e.where)){const n=yt(s||i?{...e,columns:["*"]}:e);n.columns=["*"],o&&(n.orderBy=void 0,n.limit=void 0,n.offset=void 0),l=await this.engine.find(n.table,{...n,where:this.enginePreFilter(e.where)}),l=await this.filterCorrelated(l,e.where,t)}else{e.where&&Object.keys(e.where).length>0&&(e.where=await this.resolveSubqueries(e.where));const t=yt(s||i?{...e,columns:["*"]}:e);(a||c)&&(t.columns=["*"]),h||(t.limit=void 0,t.offset=void 0),o&&(t.orderBy=void 0,t.limit=void 0,t.offset=void 0),l=await this.engine.find(t.table,t)}}else l=[{}];i&&(l=[this.computeSingleAggregate(l,e)]),s&&(l=this.executeGroupBy(l,e));const u=!!e.distinct&&this.distinctNeedsPreProjectionSort(e);if(u&&(l=this.executeDistinct(l)),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 n={};for(const[s,i]of Object.entries(e.having))n[t.get(s)??s]=i;e.having=n}l=l.filter(t=>S(t,e.having))}s&&(l=this.projectGroupedRows(l,e));const f=!!e.orderBy&&e.orderBy.length>0&&(u||!this.orderByReferencesOutputColumns(e));if(e.orderBy&&e.orderBy.length>0&&!o&&f&&(l=P(l,e.orderBy)),i||this.assertProjectionColumnsExist(l,e.columns,e),s||i||!(e.columns.length>0)||1===e.columns.length&&"*"===e.columns[0]||(l=l.map(t=>this.projectRow(t,e.columns))),e.distinct&&!u&&(l=this.executeDistinct(l)),o&&e.orderBy&&e.orderBy.length>0&&(l=P(l,e.orderBy)),!h){const t=e.offset??0,n=e.limit??l.length;l=l.slice(t,t+n)}return"result"===t&&this.maxRowsPerQuery>0&&l.length>this.maxRowsPerQuery&&(l=l.slice(0,this.maxRowsPerQuery)),l}async executeJoinSelect(e,t){for(const t of e.joins??[])t.on&&Object.keys(t.on).length>0&&await this.validateJoinOnColumns(e,t);const n=e.alias??e.from;let s;if(t)s=t;else{const{pushable:t}=this.extractPushableWhere(e.where??{},n);s=(await this.engine.find(e.from,{table:e.from,where:Object.keys(t).length>0?t:void 0})).map(e=>this.prefixRow(e,n))}let i=s;for(const t of e.joins){const e=t.alias??t.table,s=await this.tryHashJoin(i,t,e,n);if(s){i=s;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=>S(t,e.where)))),i}prefixRow(e,t){const n={};for(const[s,i]of Object.entries(e))n[`${t}.${s}`]=i;return n}extractPushableWhere(e,t){const n={};if(!t)return{pushable:n};const s=`${t}.`;for(const[t,i]of Object.entries(e))t.startsWith(s)&&("object"==typeof i&&null!==i&&("$col"in i||"$subquery"in i||"$and"in i||"$or"in i||"$not"in i)||(n[t.slice(s.length)]=i));return{pushable:n}}async tryHashJoin(e,t,n,s){if("CROSS"===t.type||"RIGHT"===t.type)return null;const i=[],r=e=>{for(const[t,n]of Object.entries(e)){if("$and"===t){if(!n.every(r))return!1;continue}if("$or"===t||"$not"===t)return!1;let e=null;if("object"==typeof n&&null!==n){const t=n;"$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=!!s&&t.startsWith(`${s}.`);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(),n=a.columns[t];return n&&(n.primaryKey||n.index||n.unique)});if(!o)return null;const c=o.rightCol.split(".").pop(),h=Array.from(new Set(e.map(e=>e[o.leftCol]).filter(e=>null!=e)));if(0===h.length)return null;const l=await this.engine.find(t.table,{table:t.table,where:{[c]:{$in:h}}}),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 s of e){const e=i.map(e=>String(s[e.leftCol]??"\0")).join(""),r=u.get(e);if(r&&r.length>0)for(const e of r)d.push({...s,...this.prefixRow(e,n)});else"LEFT"===t.type&&d.push({...s,...this.prefixRow(f,n)})}return d}joinRows(e,t,n){if("CROSS"===n.type){const n=[];for(const s of e)for(const e of t)n.push({...s,...e});return n}const s=[];for(const i of e){let e=!1;for(const r of t){const t={...i,...r};S(t,n.on,{$col:!0})&&(s.push(t),e=!0)}if(!e&&"LEFT"===n.type){const e={};for(const n of Object.keys(t[0]??{}))e[n]=null;s.push({...i,...e})}}if("RIGHT"===n.type)for(const i of t)if(!e.some(e=>S({...e,...i},n.on,{$col:!0}))){const t={};for(const n of Object.keys(e[0]??{}))t[n]=null;s.push({...t,...i})}return s}executeGroupBy(e,t){const n=this.resolveGroupByColumns(t),s=new Map;for(const t of e){const e=n.map(e=>d($t(t,e))).join("");s.has(e)||s.set(e,[]),s.get(e).push(t)}const i=this.collectAggregateExpressions(t),r=[],a=new Map;for(const e of s.values()){const s=e[0],o={};for(const e of n){const t=At(e);o[t?.alias??Lt(e)]=$t(s,e)}for(const t of i){const n=t.distinct?this.computeDistinctAggregate(t.func,e,t.arg):this.computeAggregate(t.func,e,t.arg);t.outputKey!==t.exprKey&&a.set(t.exprKey,t.outputKey),o[t.exprKey]=n,t.outputKey!==t.exprKey&&(o[t.outputKey]=n)}for(const e of t.columns){if("*"===e)continue;if(Ot(e))continue;if(/^\s*CASE\b/i.test(e)){const t=At(e),n=t?.alias??e;n in o||(o[n]=t?It(t,s):null);continue}if(e.match(/^(.+?)\s+AS\s+(\w+)$/i))continue;const t=Ct(e);t in o||e in o||(o[t]=St(s,e,{strict:!0,context:"SELECT list"}))}r.push(o)}return t._aggAliasMap=a,r}projectGroupedRows(e,t){const n=this.resolveGroupByColumns(t);return e.map(e=>this.projectGroupedRow(e,n,t.columns))}projectGroupedRow(e,t,n){if(n.some(e=>"*"===e))return e;const s={};for(const n of t)n in e&&(s[n]=e[n]);for(const t of n){if("*"===t)continue;const n=Ot(t);if(n){s[n.outputKey]=e[n.outputKey];continue}const i=At(t);if(i){const n=i.alias??t;s[n]=e[n];continue}const r=t.match(/^(.+?)\s+AS\s+(\w+)$/i);if(r){s[r[2]]=e[r[2]];continue}const a=Lt(Ct(t));s[a]=a in e?e[a]:e[t]}return s}resolveGroupByColumns(e){const t=new Map;for(const n of e.columns){const e=At(n);if(e?.alias){t.set(e.alias,n.trim());continue}const s=n.match(/^(.+?)\s+AS\s+([A-Za-z_][A-Za-z0-9_]*)$/i);s&&t.set(s[2],s[1].trim())}return e.groupBy.map(e=>{const n=t.get(e.trim());return void 0===n||Ot(n)?e:n})}collectAggregateExpressions(e){const t=new Map,n=e=>{const n=Ot(e);n&&(t.get(n.exprKey)||t.set(n.exprKey,n))};for(const t of e.columns)n(t);for(const t of function(e){const t=[];if(!e)return t;const n=e=>{const n=/(?:COUNT|SUM|AVG|MIN|MAX)\s*\([^()]*(?:\([^()]*\)[^()]*)*\)/gi;let s;for(;null!==(s=n.exec(e));)t.push(s[0])},s=e=>{for(const[t,i]of Object.entries(e))if("$and"!==t&&"$or"!==t)if("$not"!==t){if(n(t),"string"==typeof i)n(i);else if(Array.isArray(i))for(const e of i)"string"==typeof e&&n(e);else if(null!==i&&"object"==typeof i)for(const e of Object.values(i))"string"==typeof e&&n(e)}else s(i);else for(const e of Array.isArray(i)?i:[i])s(e)};return s(e),t}(e.having))n(t);return[...t.values()]}computeAggregate(e,t,n){const s=At(n),i=n.trim(),r=t.map(t=>s?It(s,t):"*"===i?t:St(t,i,{strict:!0,context:`aggregate ${e}(${i})`})).filter(e=>null!=e);if("COUNT"===e)return"*"===i?t.length:r.length;const a=r.map(e=>Number(e));switch(e){case"SUM":return Ut(a,"SUM");case"AVG":return Ut(a,"AVG");case"MIN":return Ut(a,"MIN");case"MAX":return Ut(a,"MAX");default:return null}}computeDistinctAggregate(e,t,n){const s=t.map(t=>St(t,n,{strict:!0,context:`aggregate ${e}(DISTINCT ${n})`})).filter(e=>null!=e);if("COUNT"===e)return new Set(s.map(e=>d(e))).size;const i=s.map(e=>Number(e)),r=new Set,a=[];for(const e of i){const t=d(e);r.has(t)||(r.add(t),a.push(e))}switch(e){case"SUM":return Ut(a,"SUM");case"AVG":return Ut(a,"AVG");case"MIN":return Ut(a,"MIN");case"MAX":return Ut(a,"MAX");default:return null}}executeDistinct(e){const t=new Set;return e.filter(e=>{const n=Object.values(e).map(d).join("");return!t.has(n)&&(t.add(n),!0)})}async executeInsert(e){const t=await this.engine.getTableSchema(e.into);if(!t)throw new s(`Table "${e.into}" does not exist`,"TABLE_NOT_FOUND");const n=e.columns??Object.keys(t.columns),i=n.filter(e=>!(e in t.columns));if(i.length>0)throw new s(`Unknown column${i.length>1?"s":""} ${i.map(e=>`"${e}"`).join(", ")} in table "${e.into}". Known columns: ${Object.keys(t.columns).join(", ")}`,"COLUMN_NOT_FOUND");if(e.select){const t=await this.executeSelectPart(e.select,"source");let s=[];const i=e.select;if("SELECT"===i.type)if(i.columns&&i.columns.length>0&&"*"!==i.columns[0])s=i.columns.map(e=>e.split(".").pop());else if(i.from){const e=await this.engine.getTableSchema(i.from);s=e?Object.keys(e.columns):[]}0===s.length&&t.length>0&&(s=Object.keys(t[0]));const r=t.map(e=>{const t={};for(let i=0;i<n.length;i++){const r=i<s.length?s[i]:null;r&&r in e&&(t[n[i]]=e[r])}return t});return this.assertWithinRowLimit(r.length),await(this.engine.validatePayload?.(e.into,r,"insert")),this.engine.insert(e.into,r)}const r=(e.values??[]).map((t,i)=>{if(t.length>n.length)throw new s(`INSERT has ${t.length} value(s) for ${n.length} column(s) in table "${e.into}" (row ${i+1}); too many values`,"VALIDATION_ERROR",{table:e.into,values:t.length,columns:n.length});const r={};for(let e=0;e<n.length;e++)e<t.length&&(r[n[e]]=t[e]);return r});return this.assertWithinRowLimit(r.length),await(this.engine.validatePayload?.(e.into,r,"insert")),this.engine.insert(e.into,r)}assertWithinRowLimit(e){if(this.maxRowsPerQuery>0&&e>this.maxRowsPerQuery)throw new s(`Statement would write ${e} rows, exceeding maxRowsPerQuery (${this.maxRowsPerQuery}). Narrow the source query or raise the limit.`,"QUERY_ERROR",{rows:e,maxRowsPerQuery:this.maxRowsPerQuery})}async executeUpdate(e){await this.resolveWriteWhere(e);const t=yt(e);return t.where=e.where,this.engine.update(t.table,t,e.sets)}async executeDelete(e){await this.resolveWriteWhere(e);const t=yt(e);return t.where=e.where,this.engine.delete(t.table,t)}async resolveWriteWhere(e){const t=e.where;if(!t||0===Object.keys(t).length)return t??{};if(this.hasCorrelatedRefs(t))throw new s("Correlated subqueries and column references are not supported in UPDATE/DELETE WHERE clauses","NOT_SUPPORTED");return e.where=await this.resolveSubqueries(t),e.where}async executeCreateTable(e){if(e.ifNotExists&&await this.engine.hasTable(e.name))return;const t=Object.create(null);for(const n of e.columns)t[n.name]=H(n);return this.engine.createTable(W(e.name,t))}async executeDropTable(e){if(!e.ifExists||await this.engine.hasTable(e.name))return this.engine.dropTable(e.name)}async executeAlterTable(e){if(!await this.engine.hasTable(e.name))throw new s(`Table "${e.name}" does not exist`,"TABLE_NOT_FOUND");const t=await this.engine.getTableSchema(e.name);if(t){if("ADD"===e.action&&e.column.primaryKey&&Object.values(t.columns).some(e=>e.primaryKey))throw new s(`Composite primary keys are not supported yet: table "${e.name}" already has a primary key column`,"SCHEMA_ERROR");if("function"==typeof this.engine.alterTable)return this.engine.alterTable(e.name,e.action,{...H(e.column),name:e.column.name});if("ADD"===e.action){if(t.columns[e.column.name])throw new s(`Column "${e.column.name}" already exists in table "${e.name}"`,"COLUMN_EXISTS");t.columns[e.column.name]=H(e.column)}else if("DROP"===e.action){if(!t.columns[e.column.name])throw new s(`Column "${e.column.name}" does not exist in table "${e.name}"`,"COLUMN_NOT_FOUND");delete t.columns[e.column.name];const n=await this.engine.find(e.name,{table:e.name}),i=e.column.name;for(const e of n)i in e&&delete e[i]}}}async executeTruncateTable(e){if(!await this.engine.hasTable(e.name))throw new s(`Table "${e.name}" does not exist`,"TABLE_NOT_FOUND");return this.engine.clear(e.name)}async executeCreateIndex(e){if(!await this.engine.hasTable(e.table))throw new s(`Table "${e.table}" does not exist`,"TABLE_NOT_FOUND");const t=await this.engine.getTableSchema(e.table);if(t&&!t.columns[e.column])throw new s(`Column "${e.column}" does not exist in table "${e.table}"`,"COLUMN_NOT_FOUND");if("function"!=typeof this.engine.createIndex)throw new s(`Engine "${this.engine.name}" does not support CREATE INDEX`,"NOT_SUPPORTED");return this.engine.createIndex(e.table,e.column,e.unique)}async executeDropIndex(e){if("function"!=typeof this.engine.dropIndex)throw new s(`Engine "${this.engine.name}" does not support DROP INDEX`,"NOT_SUPPORTED");return this.engine.dropIndex(e.table,e.column,e.name)}async executeBegin(){return this.engine.beginTransaction()}async executeCommit(){return this.engine.commitTransaction()}async executeRollback(){return this.engine.rollbackTransaction()}async executeSavepoint(e){const t=this.engine;if("SAVE"===e.action){if("function"!=typeof t.savepoint)throw new s(`Engine "${this.engine.name}" does not support SAVEPOINT`,"NOT_SUPPORTED");return t.savepoint(e.name)}if("ROLLBACK"===e.action){if("function"!=typeof t.rollbackToSavepoint)throw new s(`Engine "${this.engine.name}" does not support ROLLBACK TO SAVEPOINT`,"NOT_SUPPORTED");return t.rollbackToSavepoint(e.name)}if("function"!=typeof t.releaseSavepoint)throw new s(`Engine "${this.engine.name}" does not support RELEASE SAVEPOINT`,"NOT_SUPPORTED");return t.releaseSavepoint(e.name)}async executeAnalyze(e){const t=this.engine;if("function"!=typeof t.analyzeTable)throw new s(`Engine "${this.engine.name}" does not support ANALYZE`,"NOT_SUPPORTED");if(!await this.engine.hasTable(e.table))throw new s(`Table "${e.table}" does not exist`,"TABLE_NOT_FOUND");return t.analyzeTable(e.table)}async executeReindex(e){const t=this.engine;if("function"!=typeof t.reindexTable)throw new s(`Engine "${this.engine.name}" does not support REINDEX`,"NOT_SUPPORTED");if(!await this.engine.hasTable(e.table))throw new s(`Table "${e.table}" does not exist`,"TABLE_NOT_FOUND");return t.reindexTable(e.table)}async executeVacuum(){const e=this.engine;if("function"!=typeof e.vacuum)throw new s(`Engine "${this.engine.name}" does not support VACUUM`,"NOT_SUPPORTED");return e.vacuum()}outputColumnNames(e){const t=new Set;for(const n of e.columns){if("*"===n)return null;const e=Ot(n);if(e){t.add(e.outputKey);continue}const s=At(n);if(s){t.add(s.alias??n);continue}const i=n.match(/^(.+?)\s+AS\s+([A-Za-z_][A-Za-z0-9_]*)$/i);i?t.add(i[2]):t.add(Ct(n))}return t}orderByReferencesOutputColumns(e){if(!e.orderBy||0===e.orderBy.length)return!0;const t=this.outputColumnNames(e);return null===t||e.orderBy.every(e=>t.has(Ct(e.column)))}distinctNeedsPreProjectionSort(e){return!(!e.orderBy||0===e.orderBy.length||this.orderByReferencesOutputColumns(e))}async validateJoinOnColumns(e,t){const n=new Set,i=async(e,t)=>{const s=await this.engine.getTableSchema(e);if(s)for(const e of Object.keys(s.columns))n.add(e),n.add(`${t}.${e}`)};await i(e.from,e.alias??e.from);for(const n of e.joins??[])if(await i(n.table,n.alias??n.table),n===t)break;await i(t.table,t.alias??t.table);const r=[],a=e=>{const t=e.trim();if(!t)return;if(n.has(t))return;const s=t.includes(".")?t.split(".").pop():t;n.has(s)||r.push(t)},o=e=>{if("object"==typeof e&&null!==e)for(const[t,n]of Object.entries(e))"$col"!==t?"object"!=typeof n||null===n||Array.isArray(n)||o(n):a(String(n))};for(const[e,n]of Object.entries(t.on))"$and"!==e&&"$or"!==e&&"$not"!==e&&(a(e),o(n));if(r.length>0)throw new s(`Unknown column${r.length>1?"s":""} ${r.map(e=>`"${e}"`).join(", ")} in JOIN ON of table "${e.from}"`,"COLUMN_NOT_FOUND",{columns:r,from:e.from})}async assertCaseColumnsExist(e,t){if(e.fromSubquery)return;if(!e.from)return;const n=new Set,i=async(e,t)=>{const s=await this.engine.getTableSchema(e);if(s)for(const e of Object.keys(s.columns))n.add(e),n.add(`${t}.${e}`)};if(await i(e.from,e.alias??e.from),t)for(const t of e.joins??[])await i(t.table,t.alias??t.table);const r=(e,t)=>{const i=At(e);i&&function(e,t,n){const i=[],r=e=>{const n=e.trim();if(!n)return;if(t.has(n))return;const s=n.includes(".")?n.split(".").pop():n;t.has(s)||i.push(n)},a=e=>{for(const[t,n]of Object.entries(e))if("$and"!==t&&"$or"!==t)if("$not"!==t){if("$exists"!==t&&!/^\s*CASE\b/i.test(t)&&(r(t),null!==n&&"object"==typeof n))for(const e of Object.values(n))null!==e&&"object"==typeof e&&!Array.isArray(e)&&"$col"in e&&r(String(e.$col))}else a(n);else for(const e of Array.isArray(n)?n:[n])a(e)};for(const t of e.branches)a(t.condition);const o=e=>{const t=e.trim();if(/^'/.test(t)||/^-?\d/.test(t)||/^(NULL|TRUE|FALSE)$/i.test(t))return;const n=At(t);if(n){for(const e of n.branches)a(e.condition);for(const e of n.branches)o(e.resultText);n.elseText&&o(n.elseText)}else/^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)?$/.test(t)&&r(t)};for(const t of e.branches)o(t.resultText);if(e.elseText&&o(e.elseText),i.length>0)throw new s(`Unknown column${i.length>1?"s":""} ${i.map(e=>`"${e}"`).join(", ")} in ${n}`,"COLUMN_NOT_FOUND",{columns:i})}(i,n,t)};for(const t of e.columns)r(t,`CASE expression "${t.trim()}"`);for(const t of e.groupBy??[])r(t,`GROUP BY "${t}"`);for(const t of e.orderBy??[])r(t.column,`ORDER BY "${t.column}"`);for(const t of Object.keys(e.having??{}))r(t,`HAVING "${t}"`)}async assertWhereColumnsExist(e,t){e.where&&0!==Object.keys(e.where).length&&(e.fromSubquery||e.from&&await this.validateWhereColumns(e,e.where,{context:"WHERE",rejectAmbiguous:t}))}async validateWhereColumns(e,t,n){const i=n.rejectAmbiguous,r=new Set,a=new Map,o=async(e,t)=>{const n=await this.engine.getTableSchema(e);if(n)for(const e of Object.keys(n.columns)){r.add(e),r.add(`${t}.${e}`);const n=a.get(e);void 0===n?a.set(e,t):n!==t&&a.set(e,"*")}};if(await o(e.from,e.alias??e.from),i)for(const t of e.joins??[])await o(t.table,t.alias??t.table);const c=[],h=[],l=e=>{const t=Lt(e);if(!t)return;if(r.has(t))return void(!t.includes(".")&&i&&"*"===a.get(t)&&h.push(t));const n=t.includes(".")?t.split(".").pop():t;r.has(n)||c.push(t)},u=e=>{if("object"!=typeof e||null===e)return;const t=e;for(const[e,n]of Object.entries(t))if("$col"!==e)if("object"!=typeof n||null===n||Array.isArray(n)){if(Array.isArray(n))for(const e of n)u(e)}else u(n);else l(String(n))},f=e=>{for(const[t,n]of Object.entries(e))if("$and"!==t&&"$or"!==t)"$not"!==t?"$exists"!==t&&(/^\s*CASE\b/i.test(t)||(l(t),u(n))):f(n);else for(const e of Array.isArray(n)?n:[n])f(e)};if(f(t),h.length>0)throw new s(`Ambiguous column${h.length>1?"s":""} ${h.map(e=>`"${e}"`).join(", ")} in ${n.context}: present in multiple joined tables. Qualify with a table alias.`,"COLUMN_NOT_FOUND",{columns:h});if(c.length>0)throw new s(`Unknown column${c.length>1?"s":""} ${c.map(e=>`"${e}"`).join(", ")} in ${n.context} of table "${e.from}"`,"COLUMN_NOT_FOUND",{columns:c,from:e.from})}hasCaseColumn(e){return e.some(e=>/^\s*CASE\b/i.test(e))}selectAliasNames(e){const t=new Set;for(const n of e.columns){const e=At(n);if(e?.alias){t.add(e.alias);continue}const s=n.match(/\s+AS\s+([A-Za-z_][A-Za-z0-9_]*)\s*$/i);s&&t.add(s[1])}return t}orderByUsesSelectAlias(e){if(!e.orderBy||0===e.orderBy.length)return!1;const t=this.selectAliasNames(e);return 0!==t.size&&e.orderBy.some(e=>t.has(Lt(e.column)))}whereHasCase(e){for(const[t,n]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(n))return!0}else if(n.some(e=>this.whereHasCase(e)))return!0;return!1}getEngine(){return this.engine}assertProjectionColumnsExist(e,t,n){if(0===e.length||0===t.length)return;const i=new Set;for(const t of e)for(const e of Object.keys(t))i.add(e);for(const e of t){const t=e.trim();if("*"===t)continue;if(At(t))continue;let r=t;const a=t.match(/^(.+?)\s+AS\s+\w+$/i);if(a&&(r=a[1].trim()),r=Lt(r),/^'.*'$/s.test(r))continue;if(/^-?\d+(\.\d+)?$/.test(r))continue;if(/^(TRUE|FALSE|NULL)$/i.test(r))continue;if(/^(COUNT|SUM|AVG|MIN|MAX)\s*\(/i.test(r))continue;if(i.has(r))continue;const o=[];for(const e of i)e.endsWith(`.${r}`)&&o.push(e);if(1!==o.length){if(o.length>1){const e=o.map(e=>e.slice(0,e.length-r.length-1)).sort();throw new s(`Ambiguous column "${r}" in SELECT list: present in ${e.join(", ")}`,"COLUMN_NOT_FOUND",{column:r,tables:e,from:n.from})}throw new s(`Unknown column "${r}" in SELECT list`,"COLUMN_NOT_FOUND",{column:r,from:n.from,available:[...i].slice(0,32)})}}}projectRow(e,t){const n=[],s=[],i=[],r=[];let a=!1;for(const o of t){if("*"===o){a=!0;continue}const t=At(o);if(t){i.push({alias:t.alias??o,expr:t});continue}const c=o.match(/^(.+?)\s+AS\s+(\w+)$/i);if(c){s.push({alias:c[2],source:c[1].trim()});continue}const h=o.match(/^'(.*)'$/s);if(h){const e=h[1].replace(/''/g,"'");r.push({key:o,value:e});continue}if(/^-?\d+(\.\d+)?$/.test(o)||/^(TRUE|FALSE|NULL)$/i.test(o)){r.push({key:o,value:vt(o,e)});continue}const l=Lt(o);l===o?n.push(o):n.push(l)}const o=a?{...e}:n.length>0?K(e,n):{};for(const{alias:t,source:n}of s)"*"===n?Object.assign(o,e):o[t]=vt(n,e);for(const{key:e,value:t}of r)o[e]=t;for(const{alias:t,expr:n}of i)o[t]=It(n,e);return o}_hasAggregateColumn(e){return e.some(e=>Nt(e))}computeSingleAggregate(e,t){const n={};for(const s of t.columns){if("*"===s)continue;const t=Ot(s);t?n[t.outputKey]=t.distinct?this.computeDistinctAggregate(t.func,e,t.arg):this.computeAggregate(t.func,e,t.arg):n[s]=e.length>0?St(e[0],s,{strict:!1,context:"SELECT list"}):null}return n}async resolveOutputOrdinals(e){const t=e.columns;let n=null;const i=async()=>{if(n)return n;const t=new Set,s=async e=>{const n=await this.engine.getTableSchema(e);if(n)for(const e of Object.keys(n.columns))t.add(e)};e.from&&await s(e.from);for(const t of e.joins??[])await s(t.table);return n=t,t},r=async e=>{const n=e.trim();return!!t.some(e=>Ct(e)===n||new RegExp(`\\bAS\\s+${n}$`,"i").test(e))||!!(await i()).has(n)&&t.some(e=>e===n)},a=(e,n)=>{if(e<1||e>t.length)throw new s(`${n} position ${e} is out of range: query has ${t.length} output column(s)`,"QUERY_ERROR",{position:e,outputs:t.length});const i=t[e-1],r=At(i);if(r)return r.alias??i;const a=Ot(i);if(a)return a.outputKey;const o=i.match(/^(.+?)\s+AS\s+([A-Za-z_][A-Za-z0-9_]*)$/i);return o?o[2]:Ct(i)};if(e.orderBy){const t=[];for(const n of e.orderBy)/^\d+$/.test(n.column.trim())&&!await r(n.column)?t.push({...n,column:a(Number(n.column.trim()),"ORDER BY")}):t.push(n);e.orderBy=t}if(e.groupBy){const n=[];for(const i of e.groupBy){if(!/^\d+$/.test(i.trim())||await r(i)){n.push(i);continue}const e=Number(i.trim()),o=t[e-1];if(void 0!==o&&Ot(o))throw new s(`GROUP BY position ${e} refers to an aggregate expression ("${o.trim()}")`,"QUERY_ERROR",{position:e});n.push(a(e,"GROUP BY"))}e.groupBy=n}}normalizeUnprefixedReferences(e,t){const n=t.filter(e=>!!e);e.where&&Object.keys(e.where).length>0&&(e.where=this.normalizeWhereColumns(e.where,n)),e.orderBy&&e.orderBy.length>0&&(e.orderBy=e.orderBy.map(e=>({...e,column:Lt(this.stripAlias(e.column,n))}))),e.groupBy&&e.groupBy.length>0&&(e.groupBy=e.groupBy.map(e=>Lt(this.stripAlias(e,n)))),e.columns=e.columns.map(e=>{if("*"===e||Nt(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(),n);return s===t[1].trim()?e:`${s} AS ${t[2]}`}return this.stripAlias(e,n)})}normalizeWhereColumns(e,t){const n={};for(const[s,i]of Object.entries(e))"$and"!==s&&"$or"!==s?"$not"!==s?"$exists"!==s?n[Lt(this.stripAlias(s,t))]=this.normalizeFieldValue(i,t):n[s]=this.normalizeExistsValue(i,t):n.$not=this.normalizeWhereColumns(i,t):n[s]=i.map(e=>this.normalizeWhereColumns(e,t));return n}normalizeExistsValue(e,t){if("object"!=typeof e||null===e)return e;const n=e;if(n.$subquery){const e=n.$subquery,s=[e.alias??e.from,...t].filter(Boolean);return{...n,$subquery:{...e,where:this.normalizeWhereColumns(e.where,s)}}}return e}normalizeFieldValue(e,t){if("object"!=typeof e||null===e||Array.isArray(e))return e;const n={};for(const[s,i]of Object.entries(e))"$and"===s||"$or"===s?n[s]=i.map(e=>this.normalizeWhereColumns(e,t)):"$not"===s&&"object"==typeof i&&null!==i?n[s]=this.normalizeFieldValue(i,t):"$col"===s?n[s]=Lt(this.stripAlias(String(i),t)):"object"==typeof i&&null!==i&&!Array.isArray(i)&&"$col"in i?n[s]={$col:Lt(this.stripAlias(String(i.$col),t))}:n[s]=i;return n}stripAlias(e,t){for(const n of t){if(!n)continue;const t=`${n}.`;if(e.startsWith(t))return e.slice(t.length)}return e}hasCorrelatedRefs(e){for(const[t,n]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(n))return!0}else if("object"==typeof n&&null!==n&&"$subquery"in n)return!0}else if(this.hasCorrelatedRefs(n))return!0}else if(n.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}enginePreFilter(e){const t={};for(const[n,s]of Object.entries(e)){if("$and"===n){const e=s.map(e=>this.enginePreFilter(e)).filter(e=>Object.keys(e).length>0);e.length>0&&(t.$and=e);continue}if("$or"===n){const e=s;if(e.some(e=>!this.isEngineEvaluable(e)))continue;t.$or=e.map(e=>this.enginePreFilter(e));continue}if("$not"===n){const e=s;if(!this.isEngineEvaluable(e))continue;t.$not=this.enginePreFilter(e);continue}"$exists"!==n&&(/^\s*CASE\b/i.test(n)||this.fieldHasColRef(s)||this.fieldHasSubquery(s)||(t[n]=s))}return t}isEngineEvaluable(e){for(const[t,n]of Object.entries(e))if("$and"!==t&&"$or"!==t){if("$not"!==t){if("$exists"===t)return!1;if(/^\s*CASE\b/i.test(t))return!1;if(this.fieldHasColRef(n)||this.fieldHasSubquery(n))return!1}else if(!this.isEngineEvaluable(n))return!1}else if(n.some(e=>!this.isEngineEvaluable(e)))return!1;return!0}fieldHasSubquery(e){if("object"!=typeof e||null===e||Array.isArray(e))return!1;const t=e;if("$subquery"in t)return!0;for(const[,e]of Object.entries(t)){if("object"==typeof e&&null!==e&&!Array.isArray(e)&&"$subquery"in e)return!0;if(Array.isArray(e))for(const t of e)if("object"==typeof t&&null!==t&&"$subquery"in t)return!0}return!1}async filterCorrelated(e,t,n=[]){const s=[];for(const i of e){let e=this.resolveCaseKeys(t,i);e=await this.resolveSubqueries(e,i,n),S(i,e,{$col:!0})&&s.push(i)}return s}resolveCaseKeys(e,t){const n={};for(const[s,i]of Object.entries(e))if("$and"!==s&&"$or"!==s)if("$not"!==s){if(/^\s*CASE\b/i.test(s)){const e=At(s);if(!e)continue;const r=It(e,t);if(!this.caseConditionMatches(r,i))return{$caseResult:!1};n.$caseResult=!0;continue}n[s]=i}else n.$not=this.resolveCaseKeys(i,t);else n[s]=i.map(e=>this.resolveCaseKeys(e,t));return n}caseConditionMatches(e,t){if("object"!=typeof t||null===t||Array.isArray(t))return e===t;const n=t;for(const[t,s]of Object.entries(n))switch(t){case"$eq":if(e!==s)return!1;break;case"$ne":if(e===s)return!1;break;case"$gt":if(!(e>s))return!1;break;case"$gte":if(!(e>=s))return!1;break;case"$lt":if(!(e<s))return!1;break;case"$lte":if(!(e<=s))return!1;break;case"$in":if(!Array.isArray(s)||!s.includes(e))return!1;break;case"$nin":if(Array.isArray(s)&&s.includes(e))return!1}return!0}bindColumnRefs(e,t,n=[]){if("object"!=typeof e||null===e||Array.isArray(e))return e;const s={};for(const[i,r]of Object.entries(e))if("$col"===i)s[i]=this.lookupOuterValue(t,String(r),n);else if("$and"===i||"$or"===i)s[i]=r.map(e=>this.bindWhereRefs(e,t,n));else if("$not"===i&&"object"==typeof r&&null!==r)s[i]=this.bindColumnRefs(r,t,n);else if("object"==typeof r&&null!==r&&!Array.isArray(r)&&"$col"in r)s[i]=this.lookupOuterValue(t,String(r.$col),n);else if("object"==typeof r&&null!==r&&!Array.isArray(r)&&"$subquery"in r){const e=r.$subquery,a=e.where&&this.hasCorrelatedRefs(e.where)?this.bindWhereRefs(e.where,t,n):e.where;s[i]={$subquery:{...e,where:a}}}else s[i]=r;return s}lookupOuterValue(e,t,n){const s=this.stripAlias(t,n);return s in e?e[s]:t in e?e[t]:null}bindWhereRefs(e,t,n=[]){const s={};for(const[i,r]of Object.entries(e))"$and"===i||"$or"===i?s[i]=r.map(e=>this.bindWhereRefs(e,t,n)):"$not"===i?s.$not=this.bindWhereRefs(r,t,n):s[i]="$exists"===i?r:this.bindColumnRefs(r,t,n);return s}async resolveSubqueries(e,t,n=[]){t&&(e=this.bindWhereRefs(e,t,n));const s={};for(const[i,r]of Object.entries(e)){if("$exists"===i&&"object"==typeof r&&null!==r){const e=r,n=e.$subquery,i=!!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!==i;continue}"$and"===i&&Array.isArray(r)?s.$and=await Promise.all(r.map(e=>this.resolveSubqueries(e,t,n))):"$or"===i&&Array.isArray(r)?s.$or=await Promise.all(r.map(e=>this.resolveSubqueries(e,t,n))):"$not"!==i||"object"!=typeof r||null===r?s[i]="object"==typeof r&&null!==r?await this.resolveOperatorSubqueries(r,t,n):r:s.$not=await this.resolveSubqueries(r,t,n)}return s}async resolveOperatorSubqueries(e,t,n=[]){const s={};for(const[i,r]of Object.entries(e))if("$and"===i&&Array.isArray(r))s.$and=await Promise.all(r.map(e=>this.resolveSubqueries(e,t,n)));else if("$or"===i&&Array.isArray(r))s.$or=await Promise.all(r.map(e=>this.resolveSubqueries(e,t,n)));else if("$not"!==i)if("object"==typeof r&&null!==r&&"$subquery"in r){let e=r.$subquery;t&&this.hasCorrelatedRefs(e.where)&&(e={...e,where:this.bindWhereRefs(e.where,t,n)});const a=await this.executeSelect(e);if("$in"===i||"$nin"===i){const e=Object.keys(a[0]||{})[0],t=a.map(t=>t[e]);s[i]=t}else if(0===a.length)s[i]=null;else{const e=Object.keys(a[0])[0];s[i]=a[0][e]}}else s[i]=r;else s.$not="object"==typeof r&&null!==r?await this.resolveOperatorSubqueries(r,t,n):r;return s}}function _t(e){if(null==e)return"NULL";if("number"==typeof e)return Number.isFinite(e)?String(e):"NULL";if("boolean"==typeof e)return e?"TRUE":"FALSE";if("string"==typeof e)return`'${e.replace(/'/g,"''")}'`;throw new s("Object/array query parameters are not supported by SQL binding (pass JSON strings explicitly)","PARAM_ERROR")}function Mt(e,t){if(!t)return e;let n="",i=0,r=0,a=null;for(;i<e.length;){const o=e[i];if(null===a)if("'"!==o&&'"'!==o)if("-"!==o||"-"!==e[i+1])if("/"!==o||"*"!==e[i+1])if("?"!==o)n+=o,i++;else{if(r>=t.length)throw new s(`Too few query parameters: placeholder #${r+1} has no value (got ${t.length} total)`,"PARAM_ERROR");n+=_t(t[r]),r++,i++}else{for(n+=e[i]+e[i+1],i+=2;i<e.length&&("*"!==e[i]||"/"!==e[i+1]);)n+=e[i],i++;i<e.length&&(n+=e[i]+e[i+1],i+=2)}else for(;i<e.length&&"\n"!==e[i]&&"\r"!==e[i];)n+=e[i],i++;else a=o,n+=o,i++;else{if(n+=o,o===a){if(e[i+1]===a){n+=e[i+1],i+=2;continue}a=null}i++}}if(null!==a)throw new s("Unterminated string literal in SQL","PARSE_ERROR");if(r<t.length)throw new s(`Too many query parameters: ${t.length} provided but only ${r} placeholders`,"PARAM_ERROR");return n}class Pt{constructor(e){this.tables=new Map,this.completed=!1,this.engine=e,this.executor=new Dt(e)}table(e){let t=this.tables.get(e);return t||(t=new pt(this.engine,e,this.executor),this.tables.set(e,t)),t}_markCompleted(){this.completed=!0}isCompleted(){return this.completed}}class Bt{constructor(e){this.engine=e}async execute(e){const t=new Pt(this.engine);await this.engine.beginTransaction();try{const n=await e(t);return await this.engine.commitTransaction(),t._markCompleted(),n}catch(e){try{await this.engine.rollbackTransaction()}catch{}if(e instanceof s)throw e;throw new s(`Transaction failed: ${e.message}`,"TRANSACTION_ERROR",e)}}}class Kt{constructor(){this.plugins=[],this.hooks=new Map}register(e,t){const n=e.priority??0,s=this.plugins.findIndex(e=>(e.priority??0)<n);-1===s?this.plugins.push(e):this.plugins.splice(s,0,e),e.install(t)}unregister(e){const t=this.plugins.findIndex(t=>t.name===e);-1!==t&&(this.plugins[t].destroy(),this.plugins.splice(t,1))}getPlugins(){return[...this.plugins]}on(e,t){const n=this.hooks.get(e)??[];n.push(t),this.hooks.set(e,n)}off(e,t){const n=this.hooks.get(e);if(n){const e=n.indexOf(t);-1!==e&&n.splice(e,1)}}async trigger(e,...t){const n=this.hooks.get(e);if(n)for(const e of n)await e(...t)}destroy(){for(const e of this.plugins)try{e.destroy()}catch(e){}this.plugins=[],this.hooks.clear()}}class qt{static async create(e){const t=new qt(e);return await t.init(),t}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.notifier=null,this.listeners=new Map,this.migrations=new Map,this.config=e,this.name=e.name??n.name,this.mode=e.mode??n.mode,this._version=e.version??n.version,this.pluginManager=new Kt,e.multiTabSync&&"undefined"!=typeof BroadcastChannel&&(this.channel=new BroadcastChannel(`metona-sqlark:${this.name}`),this.channel.onmessage=e=>{const t=e.data;if(!t||"change"!==t.type)return;this.emitExternal(t.table??"");const n=this.unwrapEngine();n instanceof lt&&n.reloadMemoryFromDisk().catch(()=>{})})}async init(){if(this.engine=this.createEngine(),await this.engine.open(this.name,this.version),this.engine=new r(this.engine,e=>this._onError(e),e=>this.broadcastChange(e)),this.notifier=this.engine,this.notifier.addListener(async e=>{const t=this.listeners.get(`change:${e.table}`);if(t&&0!==t.size)for(const n of[...t])try{await n(e)}catch(e){this._onError(e)}}),"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 Dt(this.engine,this.maxRowsPerQuery),this.transactionManager=new Bt(this.engine),this.config.plugins){const e=this.config.plugins.map((e,t)=>({plugin:e,index:t})).sort((e,t)=>(t.plugin.priority??0)-(e.plugin.priority??0)||e.index-t.index).map(e=>e.plugin);for(const t of e)this.pluginManager.register(t,this)}this.ready=!0,this.config.onReady&&this.config.onReady(this)}isReady(){return this.ready}async defineTable(e,t){this.ensureReady();const n=W(e,t);try{await this.pluginManager.trigger("beforeCreateTable",n),await this.engine.createTable(n),await this.pluginManager.trigger("afterCreateTable",n)}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 pt(this.engine,e,this.executor,()=>{},(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,t){this.ensureReady();const n=this.debug?Date.now():0;let s;await this.pluginManager.trigger("beforeQuery",e);try{const n=Et(Mt(e,t));for(const e of n)await this.triggerStatementHooks(e,"before"),s=await this.executor.execute(e),await this.triggerStatementHooks(e,"after",s)}catch(e){throw this._onError(e),e}if(await this.pluginManager.trigger("afterQuery",e,s),this.debug){const t=Date.now()-n,i=Array.isArray(s)?s.length:0;this._debug(`query [${t}ms] ${i} rows: ${e.slice(0,100)}`)}return s}async queryStream(e,t){this.ensureReady();const n=Et(e);if(1!==n.length)throw new s("queryStream requires exactly one SELECT statement","PARSE_ERROR");const i=n[0];if(!i||"SELECT"!==i.type)throw new s("queryStream only supports SELECT statements","NOT_SUPPORTED");const r=i,a=this.executor.analyzeSelect(r);if(0===r.limit)return 0;if(this.isAsyncCallback(t)){const n=await this.query(e);if(!Array.isArray(n))return 0;for(const e of n)await t(e);return n.length}if(a.streamable&&"function"==typeof this.engine.findStream){const e=this.normalizeWhereForStream(r),n=[r.alias??r.from].filter(Boolean),s=r.columns.length>0?r.columns.map(e=>this.stripAliasPrefix(e,n)):["*"],i=this.maxRowsPerQuery;let a=0;const o=await this.engine.findStream(r.from,{table:r.from,columns:s,where:e&&Object.keys(e).length>0?e:void 0,limit:r.limit,offset:r.offset},e=>{i>0&&a>=i||(a++,t(e))});return i>0?Math.min(o,i):o}const o=await this.query(e);if(Array.isArray(o)){for(const e of o)await t(e);return o.length}return 0}isAsyncCallback(e){const t=e.constructor?.name;if("AsyncFunction"===t)return!0;try{return/^\s*async\b/.test(Function.prototype.toString.call(e))}catch{return!1}}stripAliasPrefix(e,t){for(const n of t)if(n&&e.startsWith(`${n}.`))return e.slice(n.length+1);return e}normalizeWhereForStream(e){const t=[e.alias??e.from].filter(Boolean),n=e=>{for(const n of t)if(e.startsWith(`${n}.`))return e.slice(n.length+1);return e},s=e=>{const t={};for(const[i,r]of Object.entries(e))"$and"===i||"$or"===i?t[i]=r.map(s):"$not"===i&&"object"==typeof r&&null!==r?t.$not=s(r):t[n(i)]=r;return t};return s(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 n of t)e[n]=await this.engine.find(n,{table:n});return e}async backup(){return this.ensureReady(),"function"==typeof this.engine.backup?this.engine.backup():this.exportAll()}subscribe(e,t){const n=`change:${e}`;return this.listeners.has(n)||this.listeners.set(n,new Set),this.listeners.get(n).add(t),()=>{this.listeners.get(n)?.delete(t)}}async emit(e,t){await this.dispatchChange({table:e,...t})}async emitExternal(e){const t={type:"external",table:e},n=this.listeners.get(`change:${e}`);if(n)for(const e of[...n])try{await e(t)}catch(e){this._onError(e)}}async dispatchChange(e){this.notifier?await this.notifier.dispatch(e):this.broadcastChange(e.table)}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,n){switch(e.type){case"INSERT":{let s=e.columns??[];if(0===s.length)try{const t=await this.engine.getTableSchema(e.into);s=t?Object.keys(t.columns):[]}catch{s=[]}const i=(e.values??[]).map(e=>{const t={};for(let n=0;n<e.length;n++)t[s[n]??String(n)]=e[n];return t});"before"===t?await this.pluginManager.trigger("beforeInsert",i):await this.pluginManager.trigger("afterInsert",i,n);break}case"UPDATE":{const s={table:e.table,where:e.where};"before"===t?await this.pluginManager.trigger("beforeUpdate",s,e.sets):await this.pluginManager.trigger("afterUpdate",s,e.sets,n);break}case"DELETE":{const s={table:e.from,where:e.where};"before"===t?await this.pluginManager.trigger("beforeDelete",s):await this.pluginManager.trigger("afterDelete",s,n);break}}}addMigration(e,t){this.migrations.set(e,t)}async migrateTo(e){this.ensureReady();for(const[t,n]of[...this.migrations.entries()].sort((e,t)=>e[0]-t[0]))t<=e&&t>this._version&&(await n(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(),this.engine&&await this.engine.close(),this.tableCache.clear(),this.ready=!1}getEngine(){return this.unwrapEngine()}unwrapEngine(){return this.notifier?this.notifier.getInner():this.engine}createEngine(){const e=this.mode,t=this.config.diskEngine??"opfs";switch(e){case"memory":return new Q;case"disk":return new we;case"aria":return new ht({storageBackend:"memory"===t?"memory":"kv"===t?"kv":"opfs",...this.config.aria??{}});case"hybrid":return new lt(t);default:throw new s(`Unknown storage mode: ${e}`,"CONFIG_ERROR")}}ensureReady(){if(!this.ready)throw new s("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 Ft=new class{constructor(){this.connections=new Map,this.refCount=new Map}async connect(e){const t=e.name,n=this.connections.get(t);if(n&&n.isReady()){const e=(this.refCount.get(t)??0)+1;return this.refCount.set(t,e),n}const s=new qt(e);return await s.init(),this.connections.set(t,s),this.refCount.set(t,1),s.disconnect=async()=>{await this.release(t)},s}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())}};qt.connect=e=>Ft.connect(e),qt.disconnect=e=>Ft.release(e),qt.disconnectAll=()=>Ft.closeAll(),qt.getActiveConnections=()=>Ft.getActiveConnections();const jt="__metona_schema";function zt(e){return"number"==typeof e?"number":"boolean"==typeof e?"boolean":"object"==typeof e&&null!==e?"json":"string"}function Vt(e,t){const n={};if(0===t.length)return{name:e,columns:n};const s=t[0],i=Object.keys(s),r=i.includes("id")?"id":i.find(e=>"json"!==zt(s[e]));if(!r)return null;for(const e of i)n[e]={type:zt(s[e]),primaryKey:e===r};return{name:e,columns:n}}function Wt(e){return new Promise((t,n)=>{const s=e.getAll();s.onsuccess=()=>t(s.result??[]),s.onerror=()=>n(s.error)})}async function Gt(e){return qt.create(e)}const Ht={VERSION:i,version:i,create:Gt,MetonaSqlark:qt,MeSqlark:qt};"undefined"!=typeof window&&(window.MetonaSqlark=Ht,window.MeSqlark=Ht);const Qt=qt;e.AriaEngine=ht,e.DatabaseError=s,e.HybridEngine=lt,e.KVStoreEngine=we,e.MeSqlark=Qt,e.MemoryEngine=Q,e.MetonaSqlark=qt,e.OPFSBackend=J,e.PluginManager=Kt,e.Table=pt,e.Transaction=Pt,e.TransactionManager=Bt,e.VERSION=i,e.api=Ht,e.bindParameters=Mt,e.create=Gt,e.default=Ht,e.migrateFromIndexedDB=async function(e){if("aria"===e.engine)throw new Error("Migration from AriaEngine IndexedDB backend is not supported (data is stored in engine-private SSTable/WAL format). Only disk-mode IndexedDBEngine databases can be migrated.");const t=e.dbName;let n=null;try{n=await function(e){return new Promise((t,n)=>{const s=indexedDB.open(e);s.onsuccess=()=>t(s.result),s.onerror=()=>n(s.error??new Error(`Failed to open legacy IndexedDB "${e}"`))})}(t)}catch(e){throw new Error(`Legacy IndexedDB database "${t}" not found or unreadable: ${e.message}`)}const s={migratedTables:[],rowCount:0,skippedTables:[]},i=await function(e){return e.objectStoreNames.contains(jt)?new Promise((t,n)=>{const s=e.transaction(jt,"readonly").objectStore(jt).getAll();s.onsuccess=()=>{const e={};for(const t of s.result??[])if(t.schema)try{const n=JSON.parse(t.schema);e[n.name]=n}catch{}t(e)},s.onerror=()=>n(s.error)}):Promise.resolve({})}(n);try{const t=Array.from(n.objectStoreNames).filter(e=>e!==jt);for(let r=0;r<t.length;r++){const a=t[r];e.onProgress?.(r,t.length,a);const o=await Wt(n.transaction(a,"readonly").objectStore(a));if((await e.target.getTableNames()).includes(a)){s.skippedTables.push(a);continue}let c=i[a];if(!c){if(0===o.length){s.skippedTables.push(a);continue}const e=Vt(a,o);if(!e){s.skippedTables.push(a);continue}c=e}await e.target.defineTable(a,c.columns),o.length>0&&await e.target.table(a).insertMany(o),s.migratedTables.push(a),s.rowCount+=o.length}}finally{n.close()}return s},e.parse=function(e){return new Tt(e).parseStatement()},e.parseAll=Et,e.parseWhereCondition=kt,e.tokenize=bt,Object.defineProperty(e,"__esModule",{value:!0})});
|