!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).MetonaSqlark={})}(this,function(e){"use strict";const t=["string","number","boolean","date","json"],s=Object.freeze({name:"metona-sqlark",mode:"hybrid",diskEngine:"indexeddb",version:1});class n extends Error{constructor(e,t,s){super(e),this.code=t,this.details=s,this.name="DatabaseError"}}const i="0.1.14",r=new Map;function o(e,t,s={}){for(const[n,i]of Object.entries(t))if("$and"!==n){if("$or"!==n){if(!a(e[n],i,e,s))return!1}else if(!i.some(t=>o(e,t,s)))return!1}else if(!i.every(t=>o(e,t,s)))return!1;return!0}function a(e,t,s,n){if("object"==typeof t&&null!==t&&"$and"in t)return t.$and.every(e=>o(s,e,n));if("object"==typeof t&&null!==t&&"$or"in t)return t.$or.some(e=>o(s,e,n));if("object"==typeof t&&null!==t&&"$not"in t)return!a(e,t.$not,s,n);if("object"!=typeof t||null===t||Array.isArray(t))return e===t;const i=t;if(n.$col&&"$col"in i&&1===Object.keys(i).length)return e===s[i.$col];for(const[t,r]of Object.entries(i)){let i=r;if(n.$col&&"object"==typeof r&&null!==r&&"$col"in r&&(i=s[r.$col]),!h(e,t,i))return!1}return!0}function h(e,t,s){switch(t){case"$eq":return e===s;case"$ne":return e!==s;case"$gt":return e>s;case"$gte":return e>=s;case"$lt":return e{for(const{column:n,direction:i}of t){const t=u(e[n],s[n]);if(0!==t)return"desc"===i?-t:t}return 0})}function u(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 l(e,t){const s={};for(const n of t)if(n in e)s[n]=e[n];else for(const t of Object.keys(e))if(t.endsWith(`.${n}`)||t===n){s[n]=e[t];break}return s}class T{constructor(){this.name="memory",this.tables=new Map,this.schemas=new Map,this.indexes=new Map,this.opened=!1,this.snapshot=null}async open(e,t){this.opened||(this.opened=!0)}async close(){this.tables.clear(),this.schemas.clear(),this.indexes.clear(),this.opened=!1}isOpen(){return this.opened}async createTable(e){if(this.schemas.has(e.name))throw new n(`Table "${e.name}" already exists`,"TABLE_EXISTS");this.schemas.set(e.name,e),this.tables.set(e.name,new Map);const t=new Map;for(const[s,n]of Object.entries(e.columns))(n.index||n.unique)&&t.set(s,new Map);this.indexes.set(e.name,t)}async dropTable(e){this.ensureTable(e),this.schemas.delete(e),this.tables.delete(e),this.indexes.delete(e)}async hasTable(e){return this.schemas.has(e)}async getTableNames(){return Array.from(this.schemas.keys())}async getTableSchema(e){return this.schemas.get(e)??null}async insert(e,t){this.ensureTable(e);const s=this.schemas.get(e),i=this.tables.get(e),r=this.getPrimaryKey(s),o=[];for(const a of t){const t=this.validateRow(s,a),h=String(t[r]);if(i.has(h))throw new n(`Duplicate primary key "${h}" in table "${e}"`,"DUPLICATE_KEY");this.checkUniqueness(s,t),i.set(h,t),this.updateIndexes(e,t,h),o.push(h)}return o}async find(e,t){this.ensureTable(e);const s=this.tables.get(e);let n=this.tryIndexLookup(e,s,t);t.where&&Object.keys(t.where).length>0&&(n=n.filter(e=>o(e,t.where))),t.orderBy&&t.orderBy.length>0&&(n=c(n,t.orderBy));const i=t.offset??0,r=t.limit??n.length;return n=n.slice(i,i+r),t.columns&&t.columns.length>0&&"*"!==t.columns[0]&&(n=n.map(e=>l(e,t.columns))),n}async update(e,t,s){this.ensureTable(e);const n=this.schemas.get(e),i=this.tables.get(e);let r=0;for(const[e,a]of i)if(!t.where||0===Object.keys(t.where).length||o(a,t.where)){const t={...a,...s};this.validateRow(n,t),i.set(e,t),r++}return r}async delete(e,t){this.ensureTable(e);const s=this.tables.get(e),n=[];for(const[e,i]of s)t.where&&0!==Object.keys(t.where).length&&!o(i,t.where)||n.push(e);let i=0;for(const t of n){const n=s.get(t);n&&(i+=await this.cascadeDelete(e,t,n))}for(const e of n)s.delete(e);return n.length+i}async count(e,t){this.ensureTable(e);const s=this.tables.get(e);if(!t?.where||0===Object.keys(t.where).length)return s.size;let n=0;for(const e of s.values())o(e,t.where)&&n++;return n}async clear(e){this.ensureTable(e),this.tables.get(e).clear();const t=this.indexes.get(e);if(t)for(const e of t.values())e.clear()}async beginTransaction(){if(this.snapshot)throw new n("Transaction already in progress","TX_ACTIVE");this.snapshot={tables:this.deepCloneMapMap(this.tables),schemas:new Map(this.schemas),indexes:this.deepCloneIndexes(this.indexes)}}async commitTransaction(){if(!this.snapshot)throw new n("No active transaction","TX_NONE");this.snapshot=null}async rollbackTransaction(){if(!this.snapshot)throw new n("No active transaction","TX_NONE");this.tables=this.snapshot.tables,this.schemas=this.snapshot.schemas,this.indexes=this.snapshot.indexes,this.snapshot=null}deepCloneMapMap(e){const t=new Map;for(const[s,n]of e){const e=new Map;for(const[t,s]of n)e.set(t,{...s});t.set(s,e)}return t}deepCloneIndexes(e){const t=new Map;for(const[s,n]of e){const e=new Map;for(const[t,s]of n){const n=new Map;for(const[e,t]of s)n.set(e,new Set(t));e.set(t,n)}t.set(s,e)}return t}ensureTable(e){if(!this.tables.has(e))throw new n(`Table "${e}" does not exist`,"TABLE_NOT_FOUND")}getPrimaryKey(e){for(const[t,s]of Object.entries(e.columns))if(s.primaryKey)return t;return Object.keys(e.columns)[0]}validateRow(e,t){const s={};for(const[i,r]of Object.entries(e.columns)){let o=t[i];if(void 0===o&&void 0!==r.default&&(o=r.default),r.required&&null==o)throw new n(`Column "${i}" is required in table "${e.name}"`,"VALIDATION_ERROR");null!=o&&this.checkType(i,r.type,o),void 0!==o&&(s[i]=o)}return s}checkType(e,t,s){const i=typeof s;switch(t){case"string":if("string"!==i)throw new n(`Column "${e}" expects string, got ${i}`,"TYPE_ERROR");break;case"number":if("number"!==i)throw new n(`Column "${e}" expects number, got ${i}`,"TYPE_ERROR");break;case"boolean":if("boolean"!==i)throw new n(`Column "${e}" expects boolean, got ${i}`,"TYPE_ERROR");break;case"date":if("string"!==i||isNaN(Date.parse(s)))throw new n(`Column "${e}" expects valid date`,"TYPE_ERROR");break;case"json":if("object"!==i)throw new n(`Column "${e}" expects object/array, got ${i}`,"TYPE_ERROR")}}checkUniqueness(e,t){const s=this.indexes.get(e.name);if(s)for(const[i,r]of Object.entries(e.columns)){if(!r.unique||void 0===t[i]||null===t[i])continue;const o=s.get(i);if(o&&o.has(t[i]))throw new n(`Unique constraint violation on column "${i}" in table "${e.name}"`,"UNIQUE_VIOLATION")}}tryIndexLookup(e,t,s){const n=this.indexes.get(e);if(!n||!s.where)return Array.from(t.values());for(const[e,i]of Object.entries(s.where))if("object"!=typeof i||null===i){const s=n.get(e);if(s){const e=s.get(i);if(e){const s=[];for(const n of e){const e=t.get(n);e&&s.push(e)}return s}return[]}}return Array.from(t.values())}updateIndexes(e,t,s){const n=this.indexes.get(e);if(n)for(const[e,i]of n){const n=t[e];null!=n&&(i.has(n)||i.set(n,new Set),i.get(n).add(s))}}async cascadeDelete(e,t,s){let n=0;for(const[s,i]of this.schemas)if(s!==e)for(const[r,o]of Object.entries(i.columns)){if(!o.references||!o.onDelete||"RESTRICT"===o.onDelete)continue;const[i,a]=o.references.split(".");if(i!==e)continue;const h=this.tables.get(s);if(!h)continue;const c=[];for(const[e,s]of h)String(s[r])===t&&c.push(e);if("CASCADE"===o.onDelete)for(const e of c){const t=h.get(e);t&&(n+=await this.cascadeDelete(s,e,t)),h.delete(e),n++}else if("SET NULL"===o.onDelete)for(const e of c){const t=h.get(e);t&&(t[r]=null)}}return n}}class p{constructor(){this.name="indexeddb",this.db=null,this.dbName="",this.version=1,this.memoryCache=new T,this.txActive=!1}async open(e,t){return this.dbName=e,this.version=t,await this.memoryCache.open(e,t),new Promise((s,i)=>{const r=indexedDB.open(e,t);r.onsuccess=()=>{this.db=r.result,this.db.onversionchange=()=>{this.db&&(this.db.close(),this.db=null)},s()},r.onerror=()=>i(new n(`Failed to open IndexedDB "${e}"`,"IDB_OPEN_ERROR",r.error)),r.onblocked=()=>i(new n(`IndexedDB "${e}" is blocked`,"IDB_BLOCKED"))})}async close(){this.db&&(this.db.onversionchange=null,this.db.close(),this.db=null),await this.memoryCache.close()}isOpen(){return null!==this.db}async createTable(e){await this.memoryCache.createTable(e),this.txActive||await this.idbCreateTable(e)}async dropTable(e){await this.memoryCache.dropTable(e),this.txActive||await this.idbDropTable(e)}async hasTable(e){return this.ensureDB().objectStoreNames.contains(e)}async getTableNames(){return Array.from(this.ensureDB().objectStoreNames)}async getTableSchema(e){return this.memoryCache.getTableSchema(e)}async insert(e,t){const s=await this.memoryCache.insert(e,t);return this.txActive||await this.idbInsert(e,t),s}async find(e,t){return this.txActive?this.memoryCache.find(e,t):this.idbFind(e,t)}async update(e,t,s){const n=await this.memoryCache.update(e,t,s);return this.txActive||await this.idbUpdate(e,t,s),n}async delete(e,t){const s=await this.memoryCache.delete(e,t);return this.txActive||await this.idbDelete(e,t),s}async count(e,t){return this.txActive?this.memoryCache.count(e,t):(await this.idbFind(e,{table:e,where:t?.where??{}})).length}async clear(e){await this.memoryCache.clear(e),this.txActive||await this.idbClear(e)}async beginTransaction(){if(this.txActive)throw new n("Transaction already in progress","TX_ACTIVE");this.txActive=!0,await this.memoryCache.beginTransaction()}async commitTransaction(){if(!this.txActive)throw new n("No active transaction","TX_NONE");await this.memoryCache.commitTransaction(),await this.flushToIDB(),this.txActive=!1}async rollbackTransaction(){if(!this.txActive)throw new n("No active transaction","TX_NONE");await this.memoryCache.rollbackTransaction(),this.txActive=!1}async idbCreateTable(e){const t=this.ensureDB();return new Promise((s,i)=>{const r=t.version+1;t.close();const o=indexedDB.open(this.dbName,r);o.onupgradeneeded=t=>{const s=t.target.result,n=Object.entries(e.columns).find(([,e])=>e.primaryKey)?.[0]??Object.keys(e.columns)[0],i=s.createObjectStore(e.name,{keyPath:n});for(const[t,s]of Object.entries(e.columns))s.index&&t!==n&&i.createIndex(`idx_${t}`,t,{unique:s.unique??!1})},o.onsuccess=()=>{this.db=o.result,s()},o.onerror=()=>i(new n(`Failed to create table "${e.name}"`,"IDB_UPGRADE_ERROR",o.error))})}async idbDropTable(e){const t=this.ensureDB();return new Promise((s,i)=>{const r=t.version+1;t.close();const o=indexedDB.open(this.dbName,r);o.onupgradeneeded=t=>{const s=t.target.result;s.objectStoreNames.contains(e)&&s.deleteObjectStore(e)},o.onsuccess=()=>{this.db=o.result,s()},o.onerror=()=>i(new n(`Failed to drop table "${e}"`,"IDB_UPGRADE_ERROR",o.error))})}async idbInsert(e,t){const s=this.ensureDB();return new Promise((i,r)=>{const o=s.transaction(e,"readwrite"),a=o.objectStore(e);for(const e of t)a.add(e);o.oncomplete=()=>i(),o.onerror=()=>r(new n(`Insert failed for "${e}"`,"IDB_TX_ERROR",o.error))})}async idbFind(e,t){const s=this.ensureDB();return new Promise((i,r)=>{const a=s.transaction(e,"readonly").objectStore(e).getAll();a.onsuccess=()=>{let e=a.result??[];t.where&&Object.keys(t.where).length>0&&(e=e.filter(e=>o(e,t.where))),t.orderBy&&t.orderBy.length>0&&(e=c(e,t.orderBy));const s=t.offset??0,n=t.limit??e.length;e=e.slice(s,s+n),t.columns&&t.columns.length>0&&"*"!==t.columns[0]&&(e=e.map(e=>l(e,t.columns))),i(e)},a.onerror=()=>r(new n(`Find failed for "${e}"`,"IDB_READ_ERROR",a.error))})}async idbUpdate(e,t,s){const i=this.ensureDB();return new Promise((r,a)=>{const h=i.transaction(e,"readwrite"),c=h.objectStore(e),u=c.getAll();u.onsuccess=()=>{for(const e of u.result??[])t.where&&0!==Object.keys(t.where).length&&!o(e,t.where)||(Object.assign(e,s),c.put(e))},h.oncomplete=()=>r(),h.onerror=()=>a(new n(`Update failed for "${e}"`,"IDB_TX_ERROR",h.error))})}async idbDelete(e,t){const s=this.ensureDB(),i=await this.memoryCache.getTableSchema(e);if(!i)throw new n(`Table "${e}" not found`,"TABLE_NOT_FOUND");const r=Object.entries(i.columns).find(([,e])=>e.primaryKey)?.[0]??Object.keys(i.columns)[0];return new Promise((i,a)=>{const h=s.transaction(e,"readwrite"),c=h.objectStore(e),u=c.getAll();u.onsuccess=()=>{for(const e of u.result??[])t.where&&0!==Object.keys(t.where).length&&!o(e,t.where)||c.delete(e[r])},h.oncomplete=()=>i(),h.onerror=()=>a(new n(`Delete failed for "${e}"`,"IDB_TX_ERROR",h.error))})}async idbClear(e){const t=this.ensureDB();return new Promise((s,i)=>{const r=t.transaction(e,"readwrite");r.objectStore(e).clear(),r.oncomplete=()=>s(),r.onerror=()=>i(new n(`Clear failed for "${e}"`,"IDB_TX_ERROR",r.error))})}async flushToIDB(){const e=await this.memoryCache.getTableNames(),t=this.ensureDB();for(const s of e){const e=await this.memoryCache.find(s,{table:s});await new Promise((i,r)=>{const o=t.transaction(s,"readwrite"),a=o.objectStore(s);a.clear();for(const t of e)a.add(t);o.oncomplete=()=>i(),o.onerror=()=>r(new n(`Flush failed for "${s}"`,"IDB_FLUSH_ERROR",o.error))})}}ensureDB(){if(!this.db)throw new n("Database not opened","DB_NOT_OPEN");return this.db}}class d{constructor(){this.name="opfs",this.root=null,this.tablesDir=null,this.dbName="",this.memoryCache=new T}async open(e,t){this.dbName=e,await this.memoryCache.open(e,t),this.root=await navigator.storage.getDirectory(),this.tablesDir=await this.root.getDirectoryHandle(e,{create:!0})}async close(){this.root=null,this.tablesDir=null,await this.memoryCache.close()}isOpen(){return null!==this.tablesDir}async createTable(e){await this.memoryCache.createTable(e),await this.writeTableData(e.name,[])}async dropTable(e){if(await this.memoryCache.dropTable(e),this.tablesDir)try{await this.tablesDir.removeEntry(`${e}.json`)}catch{}}async hasTable(e){if(!this.tablesDir)return!1;try{return await this.tablesDir.getFileHandle(`${e}.json`),!0}catch{return!1}}async getTableNames(){if(!this.tablesDir)return[];const e=[];for await(const[t]of this.tablesDir.entries())t.endsWith(".json")&&e.push(t.replace(".json",""));return e}async getTableSchema(e){return this.memoryCache.getTableSchema(e)}async insert(e,t){const s=await this.memoryCache.insert(e,t),n=await this.memoryCache.find(e,{table:e});return await this.writeTableData(e,n),s}async find(e,t){return this.memoryCache.find(e,t)}async update(e,t,s){const n=await this.memoryCache.update(e,t,s),i=await this.memoryCache.find(e,{table:e});return await this.writeTableData(e,i),n}async delete(e,t){const s=await this.memoryCache.delete(e,t),n=await this.memoryCache.find(e,{table:e});return await this.writeTableData(e,n),s}async count(e,t){return this.memoryCache.count(e,t)}async clear(e){await this.memoryCache.clear(e),await this.writeTableData(e,[])}async beginTransaction(){await this.memoryCache.beginTransaction()}async commitTransaction(){await this.memoryCache.commitTransaction();const e=await this.memoryCache.getTableNames();for(const t of e){const e=await this.memoryCache.find(t,{table:t});await this.writeTableData(t,e)}}async rollbackTransaction(){await this.memoryCache.rollbackTransaction()}ensureDir(){if(!this.tablesDir)throw new n("Database not opened","DB_NOT_OPEN");return this.tablesDir}async writeTableData(e,t){const s=this.ensureDir(),n=`${e}.json`,i=await s.getFileHandle(n,{create:!0}),r=await i.createWritable();await r.write(JSON.stringify(t)),await r.close()}async readTableData(e){const t=this.ensureDir(),s=`${e}.json`;try{const e=await t.getFileHandle(s),n=await e.getFile(),i=await n.text();return JSON.parse(i)}catch{return[]}}async loadTableIntoMemory(e,t){await this.memoryCache.createTable(t);const s=await this.readTableData(e);if(s.length>0)for(const t of s)await this.memoryCache.insert(e,[t])}}class m{constructor(e="indexeddb"){this.name="hybrid",this.memoryEngine=new T,this.diskEngineType=e,this.diskEngine="opfs"===e?new d:new p}async open(e,t){await this.diskEngine.open(e,t),await this.memoryEngine.open(e,t);const s=await this.diskEngine.getTableNames();for(const e of s){const t=await this.diskEngine.getTableSchema(e);if(!t)continue;await this.memoryEngine.createTable(t);const s=await this.diskEngine.find(e,{table:e});if(s.length>0)try{await this.memoryEngine.insert(e,s)}catch(e){}}}async close(){await this.memoryEngine.close(),await this.diskEngine.close()}isOpen(){return this.memoryEngine.isOpen()&&this.diskEngine.isOpen()}async createTable(e){await this.memoryEngine.createTable(e),await this.diskEngine.createTable(e)}async dropTable(e){await this.memoryEngine.dropTable(e),await this.diskEngine.dropTable(e)}async hasTable(e){return this.memoryEngine.hasTable(e)}async getTableNames(){return this.memoryEngine.getTableNames()}async getTableSchema(e){return this.memoryEngine.getTableSchema(e)}async insert(e,t){const s=await this.memoryEngine.insert(e,t);return await this.diskEngine.insert(e,t),s}async find(e,t){return this.memoryEngine.find(e,t)}async update(e,t,s){const n=await this.memoryEngine.update(e,t,s);return await this.diskEngine.update(e,t,s),n}async delete(e,t){const s=await this.memoryEngine.delete(e,t);return await this.diskEngine.delete(e,t),s}async count(e,t){return this.memoryEngine.count(e,t)}async clear(e){await this.memoryEngine.clear(e),await this.diskEngine.clear(e)}async beginTransaction(){await this.memoryEngine.beginTransaction(),await this.diskEngine.beginTransaction()}async commitTransaction(){await this.memoryEngine.commitTransaction(),await this.diskEngine.commitTransaction()}async rollbackTransaction(){await this.memoryEngine.rollbackTransaction(),await this.diskEngine.rollbackTransaction()}getDiskEngineType(){return this.diskEngineType}getMemoryEngine(){return this.memoryEngine}}class f{constructor(e,t,s=["*"],n){this.engine=e,this.tableName=t,this._columns=s,this._where={},this._orderBy=[],this._joins=[],this._executor=n}as(e){return this._alias=e,this}innerJoin(e,t,s){return this._addJoin("INNER",e,t,s)}leftJoin(e,t,s){return this._addJoin("LEFT",e,t,s)}rightJoin(e,t,s){return this._addJoin("RIGHT",e,t,s)}crossJoin(e,t){return this._addJoin("CROSS",e,{},t)}join(e,t,s){return this._addJoin("INNER",e,t,s)}_addJoin(e,t,s,n){return this._joins.push({type:e,table:t,on:s,alias:n}),this}where(e){return this._where={...this._where,...e},this}orderBy(e,t="asc"){return this._orderBy.push({column:e,direction:t}),this}limit(e){return this._limit=e,this}offset(e){return this._offset=e,this}async execute(){if(this._joins.length>0&&this._executor){const e=this.toAST();return this._executor.execute(e)}return this.engine.find(this.tableName,{table:this.tableName,columns:this._columns,where:this._where,orderBy:this._orderBy.length>0?this._orderBy:void 0,limit:this._limit,offset:this._offset})}toAST(){return{type:"SELECT",columns:this._columns,from:this.tableName,alias:this._alias,joins:this._joins.length>0?[...this._joins]:void 0,where:this._where,orderBy:this._orderBy.length>0?this._orderBy:void 0,limit:this._limit,offset:this._offset}}}class E{constructor(e,t,s){this.engine=e,this.tableName=t,this._updates=s,this._where={}}where(e){return this._where={...this._where,...e},this}async execute(){return this.engine.update(this.tableName,{table:this.tableName,where:this._where},this._updates)}toAST(){return{type:"UPDATE",table:this.tableName,sets:this._updates,where:this._where}}}class y{constructor(e,t){this.engine=e,this.tableName=t,this._where={}}where(e){return this._where={...this._where,...e},this}async execute(){return this.engine.delete(this.tableName,{table:this.tableName,where:this._where})}toAST(){return{type:"DELETE",from:this.tableName,where:this._where}}}class g{constructor(e,t,s){this.schema=null,this.engine=e,this.name=t,this.executor=s}async getSchema(){if(!this.schema){const e=await this.engine.getTableSchema(this.name);if(!e)throw new n(`Table "${this.name}" does not exist`,"TABLE_NOT_FOUND");this.schema=e}return this.schema}async insert(e){return(await this.engine.insert(this.name,[e]))[0]}async insertMany(e){return this.engine.insert(this.name,e)}select(e=["*"]){return new f(this.engine,this.name,e,this.executor)}update(e){return new E(this.engine,this.name,e)}delete(){return new y(this.engine,this.name)}async count(e){return this.engine.count(this.name,e?{table:this.name,where:e}:void 0)}async clear(){return this.engine.clear(this.name)}async drop(){return this.engine.dropTable(this.name)}}function k(e,s){return function(e){if(0===Object.keys(e).length)throw new n("Table must have at least one column","SCHEMA_ERROR");let s=0;for(const[i,r]of Object.entries(e)){if(!t.includes(r.type))throw new n(`Invalid type "${r.type}" for column "${i}". Valid types: ${t.join(", ")}`,"SCHEMA_ERROR");r.primaryKey&&s++}if(0===s)throw new n("Table must have at least one primary key column","SCHEMA_ERROR")}(s),{name:e,columns:s}}function w(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}}function b(e){switch(e.type){case"SELECT":return function(e){return{table:e.from,columns:e.columns,where:e.where,orderBy:e.orderBy?.length?e.orderBy:void 0,limit:e.limit,offset:e.offset}}(e);case"DELETE":return function(e){return{table:e.from,where:e.where}}(e);case"UPDATE":return function(e){return{table:e.table,where:e.where}}(e);default:throw new n(`Cannot compile statement type "${e.type}" to QueryPlan`,"COMPILE_ERROR")}}class I{constructor(e){this.engine=e}async execute(e){switch(e.type){case"SELECT":return this.executeSelect(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);default:throw new n("Unknown statement type","UNKNOWN_STATEMENT")}}async executeSelect(e){e.where&&Object.keys(e.where).length>0&&(e.where=await this.resolveSubqueries(e.where));const t=!!(e.groupBy&&e.groupBy.length>0),s=!t&&this._hasAggregateColumn(e.columns);let n;if(e.joins&&0!==e.joins.length)n=await this.executeJoinSelect(e);else{const i=b(t||s?{...e,columns:["*"]}:e);n=await this.engine.find(i.table,i)}s&&(n=[this.computeSingleAggregate(n,e)]),t&&(n=this.executeGroupBy(n,e)),e.distinct&&(n=this.executeDistinct(n)),e.having&&Object.keys(e.having).length>0&&(n=n.filter(t=>o(t,e.having))),e.orderBy&&e.orderBy.length>0&&(n=c(n,e.orderBy));const i=e.offset??0,r=e.limit??n.length;return n=n.slice(i,i+r),!t&&e.columns.length>0&&"*"!==e.columns[0]&&(n=n.map(t=>l(t,e.columns))),n}async executeJoinSelect(e){const t=e.alias??e.from;let s=(await this.engine.find(e.from,{table:e.from})).map(e=>this.prefixRow(e,t));for(const t of e.joins){const e=t.alias??t.table,n=(await this.engine.find(t.table,{table:t.table})).map(t=>this.prefixRow(t,e));s=this.joinRows(s,n,t)}return e.where&&Object.keys(e.where).length>0&&(s=s.filter(t=>o(t,e.where))),s}prefixRow(e,t){const s={};for(const[n,i]of Object.entries(e))s[`${t}.${n}`]=i;return s}joinRows(e,t,s){if("CROSS"===s.type){const s=[];for(const n of e)for(const e of t)s.push({...n,...e});return s}const n=[];for(const i of e){let e=!1;for(const r of t){const t={...i,...r};o(t,s.on,{$col:!0})&&(n.push(t),e=!0)}if(!e&&"LEFT"===s.type){const e={};for(const s of Object.keys(t[0]??{}))e[s]=null;n.push({...i,...e})}}if("RIGHT"===s.type)for(const i of t)if(!e.some(e=>o({...e,...i},s.on,{$col:!0}))){const t={};for(const s of Object.keys(e[0]??{}))t[s]=null;n.push({...t,...i})}return n}executeGroupBy(e,t){const s=new Map;for(const n of e){const e=t.groupBy.map(e=>String(n[e]??"null")).join("|");s.has(e)||s.set(e,[]),s.get(e).push(n)}const n=[];for(const e of s.values()){const s={};for(const n of t.groupBy)s[n]=e[0][n];for(const n of t.columns){if("*"===n)continue;const i=n.match(/^(COUNT|SUM|AVG|MIN|MAX)\((.+?)\)(?:\s+AS\s+(\w+))?$/i);if(i){const[,t,r,o]=i;s[o||n]=this.computeAggregate(t.toUpperCase(),e,r.trim())}else t.groupBy.includes(n)||(s[n]=e[0][n])}n.push(s)}return n}computeAggregate(e,t,s){const n=t.map(e=>e[s]).filter(e=>null!=e).map(Number);switch(e){case"COUNT":return"*"===s?t.length:n.length;case"SUM":return n.reduce((e,t)=>e+t,0);case"AVG":return 0===n.length?0:n.reduce((e,t)=>e+t,0)/n.length;case"MIN":return 0===n.length?0:Math.min(...n);case"MAX":return 0===n.length?0:Math.max(...n);default:return 0}}executeDistinct(e){const t=new Set;return e.filter(e=>{const s=Object.values(e).map(e=>String(e??"\0")).join("");return!t.has(s)&&(t.add(s),!0)})}async executeInsert(e){const t=await this.engine.getTableSchema(e.into);if(!t)throw new n(`Table "${e.into}" does not exist`,"TABLE_NOT_FOUND");const s=e.columns??Object.keys(t.columns),i=e.values.map(e=>{const t={};for(let n=0;n/^(COUNT|SUM|AVG|MIN|MAX)\(/i.test(e))}computeSingleAggregate(e,t){const s={};for(const n of t.columns){if("*"===n)continue;const t=n.match(/^(COUNT|SUM|AVG|MIN|MAX)\((.+?)\)(?:\s+AS\s+(\w+))?$/i);if(t){const[,i,r,o]=t;s[o||n]=this.computeAggregate(i.toUpperCase(),e,r.trim())}else s[n]=e.length>0?e[0][n]:null}return s}async resolveSubqueries(e){const t={};for(const[s,n]of Object.entries(e))"$and"===s&&Array.isArray(n)?t.$and=await Promise.all(n.map(e=>this.resolveSubqueries(e))):"$or"===s&&Array.isArray(n)?t.$or=await Promise.all(n.map(e=>this.resolveSubqueries(e))):"$not"!==s||"object"!=typeof n||null===n?t[s]="object"==typeof n&&null!==n?await this.resolveOperatorSubqueries(n):n:t.$not=await this.resolveSubqueries(n);return t}async resolveOperatorSubqueries(e){const t={};for(const[s,n]of Object.entries(e))if("$and"===s&&Array.isArray(n))t.$and=await Promise.all(n.map(e=>this.resolveSubqueries(e)));else if("$or"===s&&Array.isArray(n))t.$or=await Promise.all(n.map(e=>this.resolveSubqueries(e)));else if("$not"!==s)if("object"==typeof n&&null!==n&&"$subquery"in n){const e=n.$subquery,i=await this.executeSelect(e);if("$in"===s||"$nin"===s){const e=Object.keys(i[0]||{})[0],n=i.map(t=>t[e]);t[s]=n}else if(0===i.length)t[s]=null;else{const e=Object.keys(i[0])[0];t[s]=i[0][e]}}else t[s]=n;else t.$not="object"==typeof n&&null!==n?await this.resolveOperatorSubqueries(n):n;return t}}var R;!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.FALSE="FALSE",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.IDENTIFIER="IDENTIFIER",e.STRING="STRING",e.NUMBER="NUMBER",e.COMMA="COMMA",e.LPAREN="LPAREN",e.RPAREN="RPAREN",e.SEMICOLON="SEMICOLON",e.EQ="EQ",e.NEQ="NEQ",e.GT="GT",e.GTE="GTE",e.LT="LT",e.LTE="LTE",e.STAR="STAR",e.DOT="DOT",e.EOF="EOF",e.ILLEGAL="ILLEGAL"}(R||(R={}));const C={SELECT:R.SELECT,FROM:R.FROM,WHERE:R.WHERE,INSERT:R.INSERT,INTO:R.INTO,VALUES:R.VALUES,UPDATE:R.UPDATE,SET:R.SET,DELETE:R.DELETE,CREATE:R.CREATE,TABLE:R.TABLE,DROP:R.DROP,ORDER:R.ORDER,BY:R.BY,ASC:R.ASC,DESC:R.DESC,LIMIT:R.LIMIT,OFFSET:R.OFFSET,AND:R.AND,OR:R.OR,NOT:R.NOT,LIKE:R.LIKE,IN:R.IN,PRIMARY:R.PRIMARY,KEY:R.KEY,UNIQUE:R.UNIQUE,DEFAULT:R.DEFAULT,NULL:R.NULL,TRUE:R.TRUE,FALSE:R.FALSE,REFERENCES:R.REFERENCES,CASCADE:R.CASCADE,INNER:R.INNER,LEFT:R.LEFT,RIGHT:R.RIGHT,CROSS:R.CROSS,JOIN:R.JOIN,ON:R.ON,AS:R.AS,OUTER:R.OUTER,GROUP:R.GROUP,HAVING:R.HAVING,COUNT:R.COUNT,SUM:R.SUM,AVG:R.AVG,MIN:R.MIN,MAX:R.MAX,DISTINCT:R.DISTINCT};class x{constructor(e){this.position=0,this.readPosition=0,this.ch="",this.input=e,this.readChar()}nextToken(){let e;switch(this.skipWhitespace(),this.ch){case",":e=this.makeToken(R.COMMA,",");break;case"(":e=this.makeToken(R.LPAREN,"(");break;case")":e=this.makeToken(R.RPAREN,")");break;case";":e=this.makeToken(R.SEMICOLON,";");break;case"*":e=this.makeToken(R.STAR,"*");break;case".":e=this.makeToken(R.DOT,".");break;case"=":e=this.makeToken(R.EQ,"=");break;case"!":"="===this.peekChar()?(this.readChar(),e=this.makeToken(R.NEQ,"!=")):e=this.makeToken(R.ILLEGAL,"!");break;case">":"="===this.peekChar()?(this.readChar(),e=this.makeToken(R.GTE,">=")):e=this.makeToken(R.GT,">");break;case"<":"="===this.peekChar()?(this.readChar(),e=this.makeToken(R.LTE,"<=")):">"===this.peekChar()?(this.readChar(),e=this.makeToken(R.NEQ,"<>")):e=this.makeToken(R.LT,"<");break;case"'":case'"':e=this.readString(this.ch);break;case"":e={type:R.EOF,value:"",position:this.position};break;default:if("-"===this.ch&&"-"===this.peekChar())return this.skipLineComment(),this.nextToken();if("/"===this.ch&&"*"===this.peekChar())return this.skipBlockComment(),this.nextToken();if(this.isLetter(this.ch)){const t=this.readIdentifier();return e={type:C[t.toUpperCase()]??R.IDENTIFIER,value:t,position:this.position-t.length},e}if(this.isDigit(this.ch)||"-"===this.ch&&this.isDigit(this.peekChar())){const t=this.readNumber();return e={type:R.NUMBER,value:t,position:this.position-t.length},e}e=this.makeToken(R.ILLEGAL,this.ch)}return this.readChar(),e}readChar(){this.readPosition>=this.input.length?this.ch="":this.ch=this.input[this.readPosition],this.position=this.readPosition,this.readPosition++}peekChar(){return this.readPosition>=this.input.length?"":this.input[this.readPosition]}skipWhitespace(){for(;" "===this.ch||"\t"===this.ch||"\n"===this.ch||"\r"===this.ch;)this.readChar()}skipLineComment(){for(;"\n"!==this.ch&&"\r"!==this.ch&&""!==this.ch;)this.readChar()}skipBlockComment(){for(this.readChar(),this.readChar();""!==this.ch&&("*"!==this.ch||"/"!==this.peekChar());)this.readChar();""!==this.ch&&(this.readChar(),this.readChar())}readIdentifier(){const e=this.position;for(;this.isLetter(this.ch)||this.isDigit(this.ch)||"_"===this.ch;)this.readChar();return this.input.slice(e,this.position)}readNumber(){const e=this.position;for("-"===this.ch&&this.readChar();this.isDigit(this.ch);)this.readChar();if("."===this.ch&&this.isDigit(this.peekChar()))for(this.readChar();this.isDigit(this.ch);)this.readChar();return this.input.slice(e,this.position)}readString(e){const t=this.position+1;this.readChar();let s="";for(;this.ch!==e&&""!==this.ch;)"\\"===this.ch&&this.peekChar()===e?(this.readChar(),s+=e):s+=this.ch,this.readChar();return{type:R.STRING,value:s,position:t}}isLetter(e){return/[a-zA-Z_]/.test(e)}isDigit(e){return/[0-9]/.test(e)}makeToken(e,t){return{type:e,value:t,position:this.position}}}class N{constructor(e){this.lexer=new x(e),this.nextToken(),this.nextToken()}parseStatement(){switch(this.curToken.type){case R.SELECT:return this.parseSelect();case R.INSERT:return this.parseInsert();case R.UPDATE:return this.parseUpdate();case R.DELETE:return this.parseDelete();case R.CREATE:return this.parseCreateTable();case R.DROP:return this.parseDropTable();default:throw this.error(`Unexpected token "${this.curToken.value}"`)}}parseSelect(){this.expect(R.SELECT);let e=!1;this.curTokenIs(R.DISTINCT)&&(e=!0,this.nextToken());const t=[];this.curTokenIs(R.STAR)?(t.push("*"),this.nextToken()):t.push(...this.parseColumnList()),this.expect(R.FROM);const s=this.expectIdentifier("table name");let n;this.curTokenIs(R.AS)?(this.nextToken(),n=this.expectIdentifier("alias")):this.curToken.type!==R.IDENTIFIER||this._isReservedAfterFrom()||(n=this.curToken.value,this.nextToken());const i={type:"SELECT",columns:t,distinct:e||void 0,from:s,alias:n,where:{}},r=this.parseJoinClauses();return r.length>0&&(i.joins=r),this.curTokenIs(R.WHERE)&&(this.nextToken(),i.where=this.parseCondition()),this.curTokenIs(R.GROUP)&&(this.nextToken(),this.expect(R.BY),i.groupBy=this.parseIdentifierList()),this.curTokenIs(R.HAVING)&&(this.nextToken(),i.having=this.parseCondition()),this.curTokenIs(R.ORDER)&&(this.nextToken(),this.expect(R.BY),i.orderBy=this.parseOrderByList()),this.curTokenIs(R.LIMIT)&&(this.nextToken(),i.limit=this.expectNumber("LIMIT value")),this.curTokenIs(R.OFFSET)&&(this.nextToken(),i.offset=this.expectNumber("OFFSET value")),i}parseJoinClauses(){const e=[];for(;this._isJoinKeyword();)e.push(this.parseJoinClause());return e}_isJoinKeyword(){return this.curTokenIs(R.INNER)||this.curTokenIs(R.LEFT)||this.curTokenIs(R.RIGHT)||this.curTokenIs(R.CROSS)||this.curTokenIs(R.JOIN)}parseJoinClause(){let e="INNER";this.curTokenIs(R.INNER)?(e="INNER",this.nextToken()):this.curTokenIs(R.LEFT)?(e="LEFT",this.nextToken(),this.curTokenIs(R.OUTER)&&this.nextToken()):this.curTokenIs(R.RIGHT)?(e="RIGHT",this.nextToken(),this.curTokenIs(R.OUTER)&&this.nextToken()):this.curTokenIs(R.CROSS)&&(e="CROSS",this.nextToken()),this.expect(R.JOIN);const t=this.expectIdentifier("table name");let s;this.curTokenIs(R.AS)?(this.nextToken(),s=this.expectIdentifier("alias")):this.curToken.type!==R.IDENTIFIER||this._isJoinReserved()||(s=this.curToken.value,this.nextToken());let n={};return"CROSS"!==e&&this.curTokenIs(R.ON)&&(this.nextToken(),n=this.parseCondition()),{type:e,table:t,alias:s,on:n}}_isReservedAfterFrom(){return this.curTokenIs(R.WHERE)||this.curTokenIs(R.ORDER)||this.curTokenIs(R.LIMIT)||this.curTokenIs(R.OFFSET)||this.curTokenIs(R.GROUP)||this._isJoinKeyword()}_isJoinReserved(){return this.curTokenIs(R.ON)||this.curTokenIs(R.WHERE)||this.curTokenIs(R.ORDER)||this.curTokenIs(R.LIMIT)||this._isJoinKeyword()}parseInsert(){this.expect(R.INSERT),this.expect(R.INTO);const e=this.expectIdentifier("table name");let t;this.curTokenIs(R.LPAREN)&&(this.nextToken(),t=this.parseIdentifierList(),this.expect(R.RPAREN)),this.expect(R.VALUES);const s=[];do{this.curTokenIs(R.COMMA)&&this.nextToken(),this.expect(R.LPAREN);const e=this.parseValueList();this.expect(R.RPAREN),s.push(e)}while(this.curTokenIs(R.COMMA));return{type:"INSERT",into:e,columns:t,values:s}}parseUpdate(){this.expect(R.UPDATE);const e=this.expectIdentifier("table name");this.expect(R.SET);const t={};do{this.curTokenIs(R.COMMA)&&this.nextToken();const e=this.expectIdentifier("column name");this.expect(R.EQ),t[e]=this.parseValue()}while(this.curTokenIs(R.COMMA));let s={};return this.curTokenIs(R.WHERE)&&(this.nextToken(),s=this.parseCondition()),{type:"UPDATE",table:e,sets:t,where:s}}parseDelete(){this.expect(R.DELETE),this.expect(R.FROM);const e=this.expectIdentifier("table name");let t={};return this.curTokenIs(R.WHERE)&&(this.nextToken(),t=this.parseCondition()),{type:"DELETE",from:e,where:t}}parseCreateTable(){this.expect(R.CREATE),this.expect(R.TABLE);const e=this.expectIdentifier("table name");this.expect(R.LPAREN);const t=[];do{this.curTokenIs(R.COMMA)&&this.nextToken(),t.push(this.parseColumnDef())}while(this.curTokenIs(R.COMMA));return this.expect(R.RPAREN),{type:"CREATE_TABLE",name:e,columns:t}}parseColumnDef(){const e={name:this.expectIdentifier("column name"),type:this.expectIdentifier("column type").toLowerCase()};for(;this.curTokenIs(R.PRIMARY)||this.curTokenIs(R.UNIQUE)||this.curTokenIs(R.NOT)||this.curTokenIs(R.DEFAULT)||this.curTokenIs(R.REFERENCES);)if(this.curTokenIs(R.PRIMARY))this.nextToken(),this.expect(R.KEY),e.primaryKey=!0;else if(this.curTokenIs(R.UNIQUE))this.nextToken(),e.unique=!0;else if(this.curTokenIs(R.NOT))this.nextToken(),this.expect(R.NULL),e.required=!0;else if(this.curTokenIs(R.DEFAULT))this.nextToken(),e.default=this.parseValue();else{if(!this.curTokenIs(R.REFERENCES))break;{this.nextToken();const t=this.expectIdentifier("referenced table");this.expect(R.LPAREN);const s=this.expectIdentifier("referenced column");for(this.expect(R.RPAREN),e.references=`${t}.${s}`;this.curTokenIs(R.ON);)if(this.nextToken(),this.curTokenIs(R.DELETE))this.nextToken(),e.onDelete=this.parseCascadeAction();else{if(!this.curTokenIs(R.UPDATE))break;this.nextToken(),e.onUpdate=this.parseCascadeAction()}}}return e}parseCascadeAction(){return this.curTokenIs(R.CASCADE)?(this.nextToken(),"CASCADE"):this.curTokenIs(R.SET)?(this.nextToken(),this.expect(R.NULL),"SET NULL"):this.curToken.type===R.IDENTIFIER&&"RESTRICT"===this.curToken.value.toUpperCase()?(this.nextToken(),"RESTRICT"):"RESTRICT"}parseDropTable(){return this.expect(R.DROP),this.expect(R.TABLE),{type:"DROP_TABLE",name:this.expectIdentifier("table name")}}parseCondition(){let e=this.parseSimpleCondition();for(;this.curTokenIs(R.AND)||this.curTokenIs(R.OR);){const t=this.curTokenIs(R.AND);this.nextToken();const s=this.parseSimpleCondition();e=t?{$and:[e,s]}:{$or:[e,s]}}return e}parseSimpleCondition(){if(this.curTokenIs(R.NOT)&&!this._isNotInOrLike())return this.nextToken(),{$not:this.parseSimpleCondition()};if(this.curTokenIs(R.LPAREN)){this.nextToken();const e=this.parseCondition();return this.expect(R.RPAREN),e}const e=this.parseColumnRef();if(this.curTokenIs(R.IDENTIFIER)&&"IS"===this.curToken.value.toUpperCase()){this.nextToken();const t=this.curTokenIs(R.NOT);t&&this.nextToken(),this.expect(R.NULL);const s={};return s[e]=t?{$ne:null}:{$eq:null},s}if(this.curTokenIs(R.NOT)){if(this.peekTokenIs(R.IN)){if(this.nextToken(),this.nextToken(),this.expect(R.LPAREN),this.curTokenIs(R.SELECT)){const t=this.parseSelect();this.expect(R.RPAREN);const s={};return s[e]={$nin:{$subquery:t}},s}const t=this.parseValueList();this.expect(R.RPAREN);const s={};return s[e]={$nin:t},s}if(this.peekTokenIs(R.LIKE)){this.nextToken(),this.nextToken();const t=this.parseValue(),s={};return s[e]={$not:{$like:t}},s}}if(this.curTokenIs(R.LIKE)){this.nextToken();const t=this.parseValue(),s={};return s[e]={$like:t},s}if(this.curTokenIs(R.IN)){if(this.nextToken(),this.expect(R.LPAREN),this.curTokenIs(R.SELECT)){const t=this.parseSelect();this.expect(R.RPAREN);const s={};return s[e]={$in:{$subquery:t}},s}const t=this.parseValueList();this.expect(R.RPAREN);const s={};return s[e]={$in:t},s}const t=this.parseComparisonOp();if(this.curTokenIs(R.LPAREN)&&this.peekTokenIs(R.SELECT)){this.nextToken();const s=this.parseSelect();this.expect(R.RPAREN);const n={};return n[e]={[t]:{$subquery:s}},n}let s;s=(this.curToken.type===R.IDENTIFIER||this._isKeywordAsIdent())&&this.peekTokenIs(R.DOT)?{$col:this.parseColumnRef()}:this.parseValue();const n={};return n[e]={[t]:s},n}_isNotInOrLike(){return this.peekTokenIs(R.IN)||this.peekTokenIs(R.LIKE)}peekTokenIs(e){return this.peekToken.type===e}parseComparisonOp(){switch(this.curToken.type){case R.EQ:return this.nextToken(),"$eq";case R.NEQ:return this.nextToken(),"$ne";case R.GT:return this.nextToken(),"$gt";case R.GTE:return this.nextToken(),"$gte";case R.LT:return this.nextToken(),"$lt";case R.LTE:return this.nextToken(),"$lte";default:throw this.error(`Expected comparison operator, got "${this.curToken.value}"`)}}parseColumnList(){const e=[];for(e.push(this.parseColumnRef());this.curTokenIs(R.COMMA);)this.nextToken(),e.push(this.parseColumnRef());return e}parseColumnRef(){if(this.curTokenIs(R.COUNT)||this.curTokenIs(R.SUM)||this.curTokenIs(R.AVG)||this.curTokenIs(R.MIN)||this.curTokenIs(R.MAX))return this.parseAggregateCall();const e=this.expectIdentifier("column name");return this.curTokenIs(R.DOT)?(this.nextToken(),`${e}.${this.expectIdentifier("column name")}`):e}parseAggregateCall(){const e=this.curToken.value.toUpperCase();let t;this.nextToken(),this.expect(R.LPAREN),this.curTokenIs(R.STAR)?(t="*",this.nextToken()):t=this.parseColumnRef(),this.expect(R.RPAREN);let s="";return this.curTokenIs(R.AS)?(this.nextToken(),s=this.expectIdentifier("alias")):this.curToken.type===R.IDENTIFIER&&this._isAggregateAlias()&&(s=this.curToken.value,this.nextToken()),s?`${e}(${t}) AS ${s}`:`${e}(${t})`}_isAggregateAlias(){return!this._isReservedAfterFrom()&&!this._isJoinKeyword()}parseIdentifierList(){const e=[];for(e.push(this.expectIdentifier("identifier"));this.curTokenIs(R.COMMA);)this.nextToken(),e.push(this.expectIdentifier("identifier"));return e}parseValueList(){const e=[];for(e.push(this.parseValue());this.curTokenIs(R.COMMA);)this.nextToken(),e.push(this.parseValue());return e}parseOrderByList(){const e=[];for(e.push(this.parseOrderBy());this.curTokenIs(R.COMMA);)this.nextToken(),e.push(this.parseOrderBy());return e}parseOrderBy(){const e=this.expectIdentifier("column name");let t="asc";return this.curTokenIs(R.ASC)?this.nextToken():this.curTokenIs(R.DESC)&&(t="desc",this.nextToken()),{column:e,direction:t}}parseValue(){switch(this.curToken.type){case R.STRING:{const e=this.curToken.value;return this.nextToken(),e}case R.NUMBER:{const e=Number(this.curToken.value);return this.nextToken(),e}case R.TRUE:return this.nextToken(),!0;case R.FALSE:return this.nextToken(),!1;case R.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===R.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!==R.EOF&&this.curToken.type!==R.ILLEGAL&&this.curToken.type!==R.STRING&&this.curToken.type!==R.NUMBER&&this.curToken.type!==R.COMMA&&this.curToken.type!==R.LPAREN&&this.curToken.type!==R.RPAREN&&this.curToken.type!==R.SEMICOLON&&this.curToken.type!==R.EQ&&this.curToken.type!==R.NEQ&&this.curToken.type!==R.GT&&this.curToken.type!==R.GTE&&this.curToken.type!==R.LT&&this.curToken.type!==R.LTE&&this.curToken.type!==R.DOT&&this.curToken.type!==R.STAR}expectNumber(e){if(this.curToken.type===R.NUMBER){const e=Number(this.curToken.value);return this.nextToken(),e}throw this.error(`Expected ${e}, got "${this.curToken.value}"`)}error(e){return new n(`Parse error at position ${this.curToken.position}: ${e}`,"PARSE_ERROR")}}function A(e){return new N(e).parseStatement()}class O{constructor(e){this.tables=new Map,this.completed=!1,this.engine=e}table(e){let t=this.tables.get(e);return t||(t=new g(this.engine,e),this.tables.set(e,t)),t}_markCompleted(){this.completed=!0}isCompleted(){return this.completed}}class S{constructor(e){this.engine=e}async execute(e){const t=new O(this.engine);await this.engine.beginTransaction();try{const s=await e(t);return await this.engine.commitTransaction(),t._markCompleted(),s}catch(e){if(await this.engine.rollbackTransaction(),e instanceof n)throw e;throw new n(`Transaction failed: ${e.message}`,"TRANSACTION_ERROR",e)}}}class D{constructor(){this.plugins=[],this.hooks=new Map}register(e){const t=e.priority??0,s=this.plugins.findIndex(e=>(e.priority??0)t.name===e);-1!==t&&(this.plugins[t].destroy(),this.plugins.splice(t,1))}getPlugins(){return[...this.plugins]}on(e,t){const s=this.hooks.get(e)??[];s.push(t),this.hooks.set(e,s)}off(e,t){const s=this.hooks.get(e);if(s){const e=s.indexOf(t);-1!==e&&s.splice(e,1)}}async trigger(e,...t){const s=this.hooks.get(e);if(s)for(const e of s)await e(...t)}destroy(){for(const e of this.plugins)try{e.destroy()}catch(e){}this.plugins=[],this.hooks.clear()}}class L{get version(){return this._version}constructor(e){this.ready=!1,this.tableCache=new Map,this.listeners=new Map,this.migrations=new Map,this.config=e,this.name=e.name??s.name,this.mode=e.mode??s.mode,this._version=e.version??s.version,this.pluginManager=new D}async init(){if(this.engine=this.createEngine(),await this.engine.open(this.name,this.version),this.executor=new I(this.engine),this.transactionManager=new S(this.engine),this.config.plugins)for(const e of this.config.plugins)this.pluginManager.register(e);this.ready=!0,this.config.onReady&&this.config.onReady(this)}isReady(){return this.ready}async defineTable(e,t){this.ensureReady();const s=k(e,t);await this.pluginManager.trigger("beforeCreateTable",s),await this.engine.createTable(s),await this.pluginManager.trigger("afterCreateTable",s),this.tableCache.delete(e)}table(e){this.ensureReady();let t=this.tableCache.get(e);return t||(t=new g(this.engine,e,this.executor),this.tableCache.set(e,t)),t}async dropTable(e){this.ensureReady(),await this.pluginManager.trigger("beforeDropTable",e),await this.engine.dropTable(e),await this.pluginManager.trigger("afterDropTable",e),this.tableCache.delete(e)}async getTableNames(){return this.ensureReady(),this.engine.getTableNames()}async query(e){this.ensureReady(),await this.pluginManager.trigger("beforeQuery",e);const t=A(e),s=await this.executor.execute(t);return await this.pluginManager.trigger("afterQuery",e,s),s}async transaction(e){this.ensureReady(),await this.pluginManager.trigger("beforeTransaction");const t=await this.transactionManager.execute(e);return await this.pluginManager.trigger("afterTransaction"),t}async exportTable(e){return this.ensureReady(),this.engine.find(e,{table:e})}async importTable(e,t){return this.ensureReady(),this.engine.insert(e,t)}async exportAll(){this.ensureReady();const e={},t=await this.engine.getTableNames();for(const s of t)e[s]=await this.engine.find(s,{table:s});return e}subscribe(e,t){const s=`change:${e}`;return this.listeners.has(s)||this.listeners.set(s,new Set),this.listeners.get(s).add(t),()=>this.listeners.get(s)?.delete(t)}emit(e,t){const s=`change:${e}`;this.listeners.get(s)?.forEach(e=>e(t))}addMigration(e,t){this.migrations.set(e,t)}async migrateTo(e){this.ensureReady();for(const[t,s]of[...this.migrations.entries()].sort((e,t)=>e[0]-t[0]))t<=e&&t>this.version&&(await s(this),this._version=t)}getPluginManager(){return this.pluginManager}on(e,t){this.pluginManager.on(e,t)}async close(){this.pluginManager.destroy(),await this.engine.close(),this.tableCache.clear(),this.ready=!1}getEngine(){return this.engine}createEngine(){const e=this.mode,t=this.config.diskEngine??"indexeddb";switch(e){case"memory":return new T;case"disk":return"opfs"===t?new d:new p;case"hybrid":return new m(t);default:throw new n(`Unknown storage mode: ${e}`,"CONFIG_ERROR")}}ensureReady(){if(!this.ready)throw new n("Database not initialized. Call await db.init() first.","DB_NOT_READY")}}const M=new class{constructor(){this.connections=new Map,this.refCount=new Map}async connect(e){const t=e.name,s=this.connections.get(t);if(s&&s.isReady()){const e=(this.refCount.get(t)??0)+1;return this.refCount.set(t,e),s}const n=new L(e);return await n.init(),this.connections.set(t,n),this.refCount.set(t,1),n.disconnect=async()=>{await this.release(t)},n}async release(e){const t=(this.refCount.get(e)??1)-1;if(t<=0){const t=this.connections.get(e);t&&(await t.close(),this.connections.delete(e)),this.refCount.delete(e)}else this.refCount.set(e,t)}async forceClose(e){const t=this.connections.get(e);t&&(await t.close(),this.connections.delete(e)),this.refCount.delete(e)}async closeAll(){for(const[,e]of this.connections)try{await e.close()}catch{}this.connections.clear(),this.refCount.clear()}getActiveConnections(){return Array.from(this.connections.keys())}getRefCount(e){return this.refCount.get(e)??0}},_=L;async function $(e){const t=new L(e);return await t.init(),t}_.connect=e=>M.connect(e),_.disconnect=e=>M.release(e),_.disconnectAll=()=>M.closeAll(),_.getActiveConnections=()=>M.getActiveConnections();const U={VERSION:i,version:i,create:$,MetonaSqlark:L,MeSqlark:L};"undefined"!=typeof window&&(window.MetonaSqlark=U,window.MeSqlark=U);const v=L;e.HybridEngine=m,e.IndexedDBEngine=p,e.MeSqlark=v,e.MemoryEngine=T,e.MetonaSqlark=L,e.OPFSEngine=d,e.Table=g,e.VERSION=i,e.api=U,e.create=$,e.default=U,e.parse=A,e.tokenize=function(e){const t=new x(e),s=[];let n=t.nextToken();for(;n.type!==R.EOF;)s.push(n),n=t.nextToken();return s.push(n),s},Object.defineProperty(e,"__esModule",{value:!0})});