@isdk/kvsqlite
Version:
SQlite(>=3.45.0) key/value Document store
1 lines • 28.7 kB
JavaScript
var t="名",e="name";import s from"path/posix";import i from"path";import n from"fs";import{fileURLToPath as r}from"url";import{mimeType as o}from"mime-type/with-db";import E from"better-sqlite3";import{cloneDeep as c,defaultsDeep as T,omit as f,pick as a}from"lodash-es";function h(t,e){void 0===e&&(e={});for(const s in t)t.hasOwnProperty(s)&&("object"==typeof t[s]?(("object"!=typeof e[s]||Array.isArray(e[s]))&&(e[s]=Array.isArray(t[s])?[]:{}),h(t[s],e[s])):void 0!==t[s]&&(e[s]=t[s]));return e}function $(t,e){return t?`'${e}'`:String(e)}function d(t,e){return t.map((t=>R(t,e))).join(" AND ")}function u(t,e){return t.map((t=>R(t,e))).join(" OR ")}function l(t,e,s){const i=[];return Object.keys(e).forEach((s=>{const n=e[s];let r="string"==typeof e[s];switch(s){case"$lt":case"<":i.push(`${t} < ${$(r,n)}`);break;case"<=":case"$lte":i.push(`${t} <= ${$(r,n)}`);break;case"$gt":case">":i.push(`${t} > ${$(r,n)}`);break;case"$gte":case">=":i.push(`${t} >= ${$(r,n)}`);break;case"$ne":case"!=":i.push(`${t} != ${$(r,n)}`);break;case"=":case"$eq":i.push(`${t} = ${$(r,n)}`);break;case"$in":i.push(`${t} IN (${n.map((t=>$(!0,t))).join(", ")})`);break;case"$nin":i.push(`${t} NOT IN (${n.map((t=>$(!0,t))).join(", ")})`);break;case"$regex":i.push(`${t} REGEXP '${n.source}'`);break;case"$like":i.push(`${t} LIKE '${n}'`);break;case"$nlike":i.push(`${t} NOT LIKE '${n}'`);break;case"$glob":i.push(`${t} GLOB '${n}'`);break;case"$nglob":i.push(`${t} NOT GLOB '${n}'`);break;case"$or":if(Array.isArray(n)){s=s.slice(1).toUpperCase(),i.push(`(${n.map((e=>`${t}='${$(r,e)}'`)).join(` ${s} `)})`);break}throw new Error(`${s} is not an array for ${t}`);default:throw new Error(`Unsupported condition operator: ${s} for ${t}`)}})),i.join(" AND ")}function R(t,e){const s=[];if("function"!=typeof e&&(e=t=>t),Array.isArray(t))s.push(d(t,e));else for(const[i,n]of Object.entries(t))if("$and"===i)s.push(`(${d(t[i],e)})`);else if("$or"===i)s.push(`(${u(t[i],e)})`);else{const t=typeof n;if(null==n)s.push(`${e(i)} IS NULL`);else if("object"!==t||Array.isArray(n))if("string"===t)s.push(`${e(i)}='${n}'`);else{if("number"!==t&&"boolean"!==t)throw new Error(`Unsupported value type for key ${i}`);s.push(`${e(i)}=${n}`)}else s.push(l(e(i),n))}return s.length>1?s.join(" AND "):s[0]}function N(t,e){let s="";if(Array.isArray(t))s="string"==typeof e?"("+t.map((t=>N(t,e))).join(") || (")+")":"("+t.map((t=>N(t,e))).join(") (")+")";else if("object"==typeof t&&null!==t){const i=Object.keys(t);if(1!==i.length)throw new Error("Unsupported JSON object structure for FTS5 query.");const n=i[0],r=t[n];if("$or"===n)s=e?r.map((t=>N(t,e))).join(" || ' OR ' || "):r.map((t=>N(t,e))).join(" OR ");else if("$and"===n)s=e?r.map((t=>N(t,e))).join(" || "):r.map((t=>N(t,e))).join(" ");else{if("$near"!==n)throw new Error(`Unsupported operator: ${n}`);if(Array.isArray(r))s="string"==typeof e?"'NEAR(' || "+r.map((t=>N(t,e))).join(" || ")+" || ')'":"NEAR("+r.map((t=>N(t,e))).join(" ")+")";else{if(!r||!Array.isArray(r.items))throw new Error(`Invalid value for $near operator: ${r}`);{const t=r.items;s="string"==typeof e?"'NEAR(' || "+t.map((t=>N(t,e))).join(" || "):"NEAR("+t.map((t=>N(t,e))).join(" "),r.distance&&(s+="string"==typeof e?` || ', ${r.distance}`:`, ${r.distance}`),s+="string"==typeof e?")'":")"}}}}else s=e?"string"==typeof e?`${e}('${t}')`:`'${t}'`:`"${t}"`;return s}var I=r(import.meta.url),S=i.dirname(I),A=2,O="kv",_="_sys_kv",p="值",b="型",y="value",L="type",F={[p]:y},m=Object.fromEntries(Object.entries(F).map((([t,e])=>[e,t])));function D(t,e){F[t]=e,m[e]=t}var C="schema",M={zh:{name:"zh",plugin:{path:"zh",jiebaDict:"dict",load(t,e){if(e?.jiebaDict){const s=i.isAbsolute(e.jiebaDict)?e.jiebaDict:i.resolve(S,"..","plugins",e.jiebaDict);t.prepare("select jieba_dict(?)").run(s)}}},tokenize:"simple"},en:{name:"en",tokenize:"porter unicode61"}};function g(t,e){if(null==t)return"NULL";if("string"==typeof t)return P(t)?t:`'${t}'`;if("number"==typeof t)return`${t}`;if("boolean"==typeof t)return t?"1":"0";if("object"==typeof t){return`${e&&!1===e.usingJsonb?"json":"jsonb"}('${(e?.serialize||JSON.stringify)(t)}')`}}function j(t,e,s){if(null==t)return null;switch(e){case"INTEGER":{const e=typeof t;if("boolean"===e)t=t?1:0;else if("string"===e){const e=parseInt(t);if(isNaN(e))throw new Error(`${t} is not a number`);t=e}return t}case"REAL":{const e=typeof t;if("boolean"===e)t=t?1:0;else if("string"===e){const e=parseFloat(t);if(isNaN(e))throw new Error(`${t} is not a number`);t=e}return t}case"BLOB":if(!Buffer.isBuffer(t)){if("string"===typeof t)t=Buffer.from(t,"utf8");else{if(!(Array.isArray(t)||t.length>=0))throw new Error(`${t} can not convert to a buffer`);t=Buffer.from(t)}}return t;case"TEXT":return""+t;case"JSON":case"JSONB":return(s?.serialize??JSON.stringify)(t)}}function x(t){for(const[e,s]of Object.entries(t)){const i=F[e];i&&(t[i]=s,delete t[e])}return t}function U(t){for(const[e,s]of Object.entries(t)){const i=m[e];i&&(t[i]=s,delete t[e])}return t}function v(t,e,s){const i={};for(let n in t){const r=t[n],o=F[n];if(e){const t=e[n]??e[o]??H(r.default);if(null==t&&r.notNull)throw new Error(`field ${n} is not nullable`);o&&(n=o),i[n]=j(t,r.type,s)}else{o&&(n=o);let t="@"+n;switch(r.type){case"JSON":t=`json(${t})`;break;case"JSONB":t=`jsonb(${t})`}i[n]=t}}return i}function W(t){const e=[];for(let s in t){const i=t[s];F[s]&&(s=F[s]),"JSON"!==i.type&&"JSONB"!==i.type||(s=`json(${s}) as ${s}`),e.push(s)}return e}function w(t,e,s){const i=s?.deserialize??JSON.parse;for(let s in e){const n=m[s],r=e[s],o=t[s];"string"!=typeof o||"JSON"!==r.type&&"JSONB"!==r.type||(t[s]=i(o)),n&&void 0!==o&&(t[n]=o,delete t[s])}return t}var G=class extends E{constructor(t,e){if("string"==typeof t&&":"!=t[0]){const e=i.dirname(t);e&&!n.existsSync(e)&&n.mkdirSync(e,{recursive:!0})}if(super(t,e),this.ftsLoaded={},this.collections={},this.serdeOptions={},this.preIsExists=this.prepare("SELECT COUNT(*) FROM sqlite_master WHERE type = ? AND name= ?").pluck(),e?.serialize&&(this.serdeOptions.serialize=e.serialize),e?.deserialize&&(this.serdeOptions.deserialize=e.deserialize),e?.id&&(this.id=e.id),!this.readonly){const t=e?.collections||[],s=t.indexOf(_);let i;if(s>=0){const e=t.splice(s,1);"string"!=typeof e&&(i=e)}this.create(_,i),t.includes(O)||t.push(O),e?.collection&&!t.includes(e.collection)&&t.push(e.collection),t.length&&this.createCollections(t,e),this.tryUpgradeVer()}}createCollections(t,e){t.forEach((t=>{let s;"string"==typeof t?(s=t,t=e):s=t.name,s&&!this.collections[s]&&this.create(s,t)}))}tryUpgradeVer(){let t;const e=this.collections[_];if(e&&(t=e.getExtend(_,"ver")),!t||t<1){const t=this.tableInfo(O);t?.key&&this.exec(`ALTER TABLE ${O} RENAME COLUMN "key" TO "_id"`)}else if(t<2)for(const t of Object.values(this.collections)){const e=t.tableInfo(),s=t.name,i=e.val?"JSONB"===e.val.type||"BLOB"===e.val.type?"jsonb":"json":"";e.createdAt?(this.exec(`DROP TRIGGER IF EXISTS tr_${s}_createdAt;`),this.prepare(`CREATE TRIGGER IF NOT EXISTS tr_${s}_createdAt AFTER INSERT ON ${s} BEGIN UPDATE ${s} SET createdAt = strftime('%FT%TZ', CURRENT_TIMESTAMP) WHERE _id = NEW._id AND NEW.createdAt IS NULL; END`).run()):i&&(this.exec(`DROP TRIGGER IF EXISTS tr_${s}_createdAt;`),this.prepare(`CREATE TRIGGER IF NOT EXISTS tr_${s}_createdAt AFTER INSERT ON ${s} BEGIN UPDATE ${s} SET val = ${i}_insert(New.val, '$.createdAt', strftime('%FT%TZ', CURRENT_TIMESTAMP)) WHERE _id = NEW._id AND NEW.val->>'$.createdAt' IS NULL; END`).run()),e.updatedAt?(this.exec(`DROP TRIGGER IF EXISTS tr_${s}_updatedAt;`),this.prepare(`CREATE TRIGGER IF NOT EXISTS tr_${s}_updatedAt AFTER UPDATE ON ${s} BEGIN UPDATE ${s} SET updatedAt = strftime('%FT%TZ', CURRENT_TIMESTAMP) WHERE _id = NEW._id AND NEW.updatedAt IS NULL; END`).run()):i&&(this.exec(`DROP TRIGGER IF EXISTS tr_${s}_updatedAt;`),this.prepare(`CREATE TRIGGER IF NOT EXISTS tr_${s}_updatedAt AFTER UPDATE ON ${s} BEGIN UPDATE ${s} SET val = ${i}_set(New.val, '$.updatedAt', strftime('%FT%TZ', CURRENT_TIMESTAMP)) WHERE _id = NEW._id AND NEW.val->>'$.updatedAt' IS NULL; END`).run())}2!==t&&e.setExtend(_,"ver",2)}usingJsonb(t){let e;t||(t=O);const s=this.collections[_];if(s&&(e=s.getExtend(t,"usingJsonb")),void 0===e){const s=this.tableInfo(t);s?.val&&(e="JSONB"===s.val.type||"BLOB"===s.val.type)}return e}getSysConfig(t,e){const s=this.collections[_];if(s)return e||(e=O),s.getExtend(e,t)}setSysConfig(t,e,s){const i=this.collections[_];if(i)return s||(s=O),i.setExtend(s,t,e)}getSerdeOptions(t){const e={};return t?.serialize?e.serialize=t.serialize:this.serdeOptions.serialize&&(e.serialize=this.serdeOptions.serialize),t?.deserialize?e.deserialize=t.deserialize:this.serdeOptions.deserialize&&(e.deserialize=this.serdeOptions.deserialize),e}create(t,e){if(!this.collections[t])return this.collections[t]=new X(t,this,e)}getCollection(t){let e=this.collections[t];return!e&&this.isCollectionExists(t)&&(e=this.collections[t]=new X(t,this)),e}drop(t){if(this.collections[t]){if(t===_||t===O)throw new Error("Cannot drop system collection");this.exec(`DROP TABLE IF EXISTS ${t}_fts`),this.exec(`DROP TABLE IF EXISTS ${t}`);const e=this.collections[_];e&&(e.delExtend(t,"%"),e.del(t)),delete this.collections[t]}}set(t,e,s){const i=("object"===typeof t?e?.collection:s?.collection)||O;return this.collections[i]?.set(t,e,s)}setExtend(t,e,s,i){const n=i?.collection||O;return this.collections[n]?.setExtend(t,e,s,i)}setExtends(t,e,s){const i=s?.collection||O;return this.collections[i]?.setExtends(t,e,s)}bulkDocs(t,e){const s=e?.collection||O;return this.collections[s]?.bulkDocs(t,e)}get(t,e){const s=e?.collection||O;return this.collections[s]?.get(t)}getExtend(t,e,s){const i=s?.collection||O;return this.collections[i]?.getExtend(t,e)}getExtends(t,e,s){const i=s?.collection||O;return this.collections[i]?.getExtends(t,e,s)}del(t,e){const s=e?.collection||O;return this.collections[s]?.del(t)}isExists(t,e){const s=e?.collection||O;return this.collections[s]?.isExists(t)}count(t,e){const s=e?.collection||O;return this.collections[s]?.count(t)}list(t,e){t&&"object"==typeof t&&(t=(e=t).query);const s=e?.collection||O;return this.collections[s]?.list(t,e)}createJsonIndex(t,e,s){const i=s?.collection||O;return this.collections[i]?.createJsonIndex(t,e)}createIndex(t,e,s){const i=s?.collection||O;return this.collections[i]?.createTableIndex(t,e)}searchEx(t,e){const s=e?.collection||O;return this.collections[s]?.searchEx(t,e)}search(t,e){const s=e?.collection||O;return this.collections[s]?.search(t,e)}buildWhereClause(t,e){const s=e?.collection||O;return this.collections[s]?.buildWhereClause(t,e)}isTypeExists(t,e){return this.preIsExists.get(t,e)}isCollectionExists(t){return this.isTypeExists("table",t)}loadFtsLanguage(t){const e=t?.language||"en",s=e&&"object"==typeof e?e.name:e;if(!this.ftsLoaded[s]){const t="object"==typeof e?T({},e,M[s]):M[s];if(t&&t.plugin){const e=t.plugin;if(e.path){const t=i.isAbsolute(e.path)?e.path:i.resolve(S,"..","plugins",e.path);this.loadExtension(t,"sqlite3_simple_init")}"function"==typeof e.load&&e.load(this,e)}this.ftsLoaded[s]=!0}}tableInfo(t,e){return t&&"object"==typeof t&&(t=(e=t).collection),t=t||O,this.collections[t]?.tableInfo(e)}enableFts(t,e){return t=t||O,this.collections[t]?.enableFts(e)}searchFts(t,e){const s=e?.collection||O;return this.collections[s]?.searchFts(t,e)}},X=class{constructor(t,e,s={}){this.name=t,this.db=e;const i=e.usingJsonb(t)??s.usingJsonb??e.usingJsonb(_)??!0,n=i?"jsonb":"json";let r;this.jsonb=n,s=c(s);e.isCollectionExists(t)?(r=this.tableInfo({collection:t,mapped:!1}),s.fields=r):(s.fields?s.fields=function(t){return Array.isArray(t)?t=t.reduce(((t,e)=>{let s;if("string"==typeof e)s=e,e={};else{if(!e||"object"!=typeof e)throw new Error(`${e} is not a valid field name`);s=e.name}const i=F[s];return i&&(s=i),t[s]=e,t}),{}):x(t),t}(s.fields):s.fields={},r=s.fields,r._id&&r._id.type?r._id.primary=!0:r._id={name:"_id",type:"TEXT",primary:!0},r.val?r.val.type=i?"JSONB":"JSON":r.val={name:"val",type:i?"JSONB":"JSON"},!0===s.fts&&(s.fts={unIndexed:["_id"]}));const o=v(r,void 0,e.getSerdeOptions(s)),E=W(r);e.readonly||this.createTable(t,s),this.preExists=e.prepare("SELECT 1 FROM "+t+" WHERE _id = ?").pluck(),this.preAdd=e.prepare(`INSERT INTO ${t} (${Object.keys(o).join(",")}) VALUES (${Object.values(o).join(",")})`),this.preUpdate=e.prepare(`UPDATE ${t} SET ${Object.entries(o).map((([t,e])=>`${t} = ${e}`)).join(",")} WHERE _id=@_id`),this.preGet=e.prepare(`SELECT ${E.join(",")} FROM ${t} WHERE _id = ?`),this.preDel=e.prepare("DELETE FROM "+t+" WHERE _id = ?"),this.preDelAll=e.prepare("DELETE FROM "+t),this.preCount=e.prepare("SELECT Count(*) as count FROM "+t).pluck(),this.preCountW=e.prepare("SELECT Count(*) as count FROM "+t+" WHERE _id LIKE ?").pluck(),this.preSearchKey=e.prepare(`SELECT ${E.join(",")} FROM ${t} WHERE _id LIKE @query LIMIT @size OFFSET @offset`),this.preSearchKeyAll=e.prepare(`SELECT ${E.join(",")} FROM ${t} WHERE _id LIKE @query`),this.preAll=e.prepare(`SELECT ${E.join(",")} FROM ${t}`),this.preAllLimit=e.prepare(`SELECT ${E.join(",")} FROM ${t} LIMIT @size OFFSET @offset`)}createIndexAndTriggerForAutoTimestamp(t,e,s){const i=e[t];let n,r=this.jsonb;i?(this.createTableIndex(t,t),n=`UPDATE ${this.name} SET ${t} = strftime('%FT%TZ', CURRENT_TIMESTAMP) WHERE _id = NEW._id AND NEW.${t} IS NULL;`):(this.createJsonIndex(t,t),r+="UPDATE"===s?"_set":"_insert",n=`UPDATE ${this.name} SET val = ${r}(New.val, '$.${t}', strftime('%FT%TZ', CURRENT_TIMESTAMP)) WHERE _id = NEW._id AND NEW.val->>'$.${t}' IS NULL;`),this.createTrigger({name:t,triggerTiming:"AFTER",event:s,action:n})}_set(t,e){const s=t._id;t={...t};let i,n=this.get(s);if(n){if(e?.ignoreExists)return{changes:0,lastInsertRowid:-1};const s=!e||!1!==e.overwrite;i=this.preUpdate,s?n=t:t&&"object"==typeof t&&n&&"object"==typeof n?h(t,n):n=t}else i=this.preAdd,n=t;const r=this.tableInfo(),o=[...Object.keys(r),...Object.keys(f(m,[y]))];let E=f(n,o);1===Object.keys(E).length&&null!=E[p]&&"object"!=typeof E[p]&&(E=E[p]);const c=a(n,o);null!=E&&("object"==typeof E&&x(E),c.val=E);const T=v(r,c,this.db.getSerdeOptions(e));return i.run(T)}set(t,e,s){const i=typeof t;return"object"===i?(s=e,e=t):"string"!==i&&"number"!==i||(e._id=t),this.db.transaction((()=>this._set(e,s)))()}_setExtend(t,e,i,n){e.startsWith(".")||(e="."+e);const r={_id:t=s.join(""+t,e),[p]:i};return n?.[b]&&(r[b]=n[b]),this._set(r,n)}setExtend(t,e,s,i){return this.db.transaction((()=>this._setExtend(t,e,s,i)))()}setExtends(t,e,s){return this.db.transaction((()=>Object.keys(e).map((i=>this._setExtend(t,i,e[i],s)))))()}bulkDocs(t,e){return this.db.transaction((()=>t.map((t=>this._set(t,e)))))()}get(t){let e=this.getEx(t);return e&&"object"==typeof e?e._id=t:void 0!==e&&(e={[p]:e,_id:t}),e}getEx(t){let e=this.preGet.get(t+"");if(e){w(e,this.tableInfo({mapped:!1}),this.db.getSerdeOptions());const t=e.val;t&&"object"==typeof t?(delete e.val,e={...e,...U(t)}):2===Object.keys(e).length?e=t:(delete e.val,void 0!==t&&(e[p]=t))}return e}getExtend(t,e){e&&(t=k(t,e));const s=this.get(t);return s?.[p]}isExistsExtend(t,e){return e&&(t=k(t,e)),this.isExists(t)}delExtend(t,e){return e&&(t=k(t,e)),this.del(t)}getExtends(t,e,i){const n=i?.singleValue;e?"string"==typeof e&&(e=[e]):e=["%"];const r=(e=e.map((e=>k(t,e)))).map((t=>t.lastIndexOf("%")>=0?this.list(t):this.get(t))).flat();let o=r.reduce(((t,e)=>{if(e){const i=e[p];t[s.basename(e._id+"").slice(1)]=i}return t}),{_id:t});return n&&(o=1===r.length?r[0][p]:o),o}del(t){if(Array.isArray(t))return this.db.transaction((()=>t.map((t=>this.preDel.run(t)))))();if(t&&"object"==typeof t){let e=R(t);return e="DELETE FROM "+this.name+" WHERE "+e,this.db.prepare(e).run()}return t?this.preDel.run(t):this.preDelAll.run()}isExists(t){return this.preExists.get(t+"")}count(t){let e;if(t){"object"==typeof t&&(t=this.buildWhereClause(t));const s="SELECT Count(*) as count FROM "+this.name+" WHERE "+t;e=this.db.prepare(s).pluck()}else e=this.preCount;return e.get()}list(t,e){t&&"object"==typeof t&&(t=(e=t).query);const s=this.tableInfo({mapped:!1}),i=e?.size,n=e?.page||0;let r,o=e?.sort;if(o){const E=W(s),c=e?.order;Array.isArray(o)||(o=[o]),o=o.map((t=>`val->>'$.${t}'`)),"DESC"===c&&(o=o.map((t=>`${t} DESC`))),o=" ORDER BY "+o.join(", ");const T=i?` LIMIT ${i} OFFSET ${n*i}`:"";t=t?` WHERE _id LIKE ${t}`:"",r=this.db.prepare(`SELECT ${E.join(",")} FROM ${this.name}`+t+o+T).all()}else r=t?i?this.preSearchKey.all({query:t,size:i,offset:n*i}):this.preSearchKeyAll.all({query:t}):i?this.preAllLimit.all({size:i,offset:n*i}):this.preAll.all();const E=this.db.getSerdeOptions(e).deserialize??JSON.parse;return r.map((t=>{const i=E(t.val);return delete t.val,i&&"object"==typeof i?{...w(f(t,["val"]),s,this.db.getSerdeOptions(e)),...U(i)}:{...w(f(t,["val"]),s,this.db.getSerdeOptions(e)),[p]:i}}))}getSchema(){const t=this.db.collections[_];return t?.getExtend(this.name,C)}createSchema(t){const e=this.db;if(t.name&&t.fields&&t.name!==_){const s=e.collections[_];if(s){const e=t.overwrite;delete t.overwrite,e?s.setExtend(t.name,C,t,{overwrite:e}):s.isExistsExtend(t.name,C)||s.setExtend(t.name,C,t)}}}createTable(t,e){const s=this.db,i=e.fields,n=e.fts;s.prepare(function(t,e){const s=e.fields,i=new Set,n=Object.keys(s).map((t=>{const n=s[t];F[t]&&(t=F[t]);const r=(n.type||"TEXT")+" ";let o=n.constraint||"";n.notNull&&!o.toUpperCase().includes("NOT NULL")&&(o+="NOT NULL "),n.primary&&!o.toUpperCase().includes("PRIMARY KEY")&&(o+="PRIMARY KEY "),n.unique&&!o.toUpperCase().includes("UNIQUE")&&(o+="UNIQUE ");const E=`${t} ${r}${o}${null!=n.default?`DEFAULT ${g(n.default,e)}`:""}`.trim(),c=n.foreignKey;if(c){const{reference:e,isJson:s,onUpdate:n,onDelete:r}=c;let o=c.referenceField;if(!e)throw new Error("foreignKey.referenceTable is required");o||(o="_id"),s&&(o=`val->>'$.${o}'`);let E=`FOREIGN KEY (${t}) REFERENCES ${e}(${o})`;E+=` ON UPDATE ${n||"CASCADE"}`,E+=` ON DELETE ${r||"CASCADE"}`,i.add(E)}return E}));i.size&&n.push(...i);let r=`CREATE TABLE IF NOT EXISTS ${t} (`;return r+=n.join(", "),r+=");",r}(t,{...e,...s.getSerdeOptions(e)})).run(),this.createIndexAndTriggerForAutoTimestamp("createdAt",i,"INSERT"),this.createIndexAndTriggerForAutoTimestamp("updatedAt",i,"UPDATE"),e.indexes&&this.createIndexes(e),n&&this.enableFts(n);const r=e.triggers;Array.isArray(r)?r.forEach((t=>{this.createTrigger(t,e)})):"object"==typeof r&&this.createTrigger(r,e),e.name=t,this.createSchema(e)}createJsonIndex(t,e){return t.startsWith("ix_"+this.name+"_")||(t="ix_"+this.name+"_"+t),Array.isArray(e)||(e=[e]),e=e.map((t=>`val->>'$.${t}'`)),this.db.prepare("CREATE INDEX IF NOT EXISTS "+t+" ON "+this.name+" ("+e.join(",")+")").run()}createTableIndex(t,e){return t.startsWith("ix_"+this.name+"_")||(t="ix_"+this.name+"_"+t),Array.isArray(e)||(e=[e]),e=e.map((t=>{const e=t.trim().split(" ");return F[e[0]]&&(e[0]=F[e[0]],t=e.join(" ")),t})),this.db.prepare("CREATE INDEX IF NOT EXISTS "+t+" ON "+this.name+" ("+e.join(",")+")").run()}createIndex(t,e){let s=t.fields;if(!s)throw new Error("index fields is required");const i=e?.fields||this.tableInfo({mapped:!1});let n=t.name;n.startsWith("ix_"+this.name+"_")||(n="ix_"+this.name+"_"+n),s=s.map((t=>{const e=t.trim().split(" "),s=F[e[0]]??e[0];return i[s]||(e[0]=`val->>'$.${s}'`,t=e.join(" ")),t}));let r="CREATE "+(t.unique?"UNIQUE ":"")+"INDEX IF NOT EXISTS "+n+" ON "+this.name+" ("+s.join(",")+")";return t.partial&&(r+=" WHERE "+t.partial),this.db.prepare(r).run()}createIndexes(t){let e=t.indexes;if(!e)throw new Error("indexes is required");return Array.isArray(e)||(e=[e]),this.db.transaction((()=>e.map((e=>this.createIndex(e,t)))))()}createTrigger(t,e){const s=t.condition||"";let i=t.name;i?.startsWith("tr_"+this.name+"_")||(i="tr_"+this.name+"_"+i);const n=`\n CREATE TRIGGER IF NOT EXISTS ${i} ${t.triggerTiming} ${t.event} ON ${this.name}\n ${s}\n BEGIN\n ${t.action}\n END;`;return this.db.prepare(n).run()}searchEx(t,e){const s=e?.fieldInfos??this.tableInfo({mapped:!1});"string"!=typeof t&&(t=Object.entries(t).map((([t,i])=>(F[t]&&(t=F[t]),(void 0===s[t]?`val->>'$.${t}'`:t)+`=${g(i,{...e,...this.db.getSerdeOptions(e)})}`))).join(" AND "));let i=e?.sort;const n=e?.order,r=e?.size,o=e?.page||0;i?(Array.isArray(i)||(i=[i]),i=i.map((t=>(F[t]&&(t=F[t]),void 0===s[t]&&(t=`val->>'$.${t}'`),t))),"DESC"===n&&(i=i.map((t=>`${t} DESC`))),i=" ORDER BY "+i.join(", ")):i="";const E=`SELECT ${W(e?.fieldNames?a(s,["_id","val",...e.fieldNames]):s).join(",")} FROM `+this.name+" WHERE "+t+i,c=this.db.prepare(E+" LIMIT @size OFFSET @offset"),T=this.db.prepare(E),f=r?c.all({size:r,offset:o*r}):T.all(),h=this.db.getSerdeOptions(e).deserialize??JSON.parse;return f.map((t=>{let i=h(t.val);return delete t.val,i&&"object"==typeof i?U(i):i={[p]:i},{...w(t,s,this.db.getSerdeOptions(e)),...i}}))}search(t,e){const s=e?.fieldInfos??this.tableInfo({mapped:!1}),i=this.buildWhereClause(t,{...e,fieldInfos:s});return this.searchEx(i,{...e,fieldInfos:s})}buildWhereClause(t,e){const s=e?.fieldInfos??this.tableInfo({mapped:!1});return R(t,(t=>{const e=F[t];return e&&(t=e),void 0===s[t]?`val->>'$.${t}'`:t}))}tableInfo(t){const e=t?.collection||this.name,s=t?.mapped??!0,i=this.db.prepare(`PRAGMA table_info(${e})`).all();if(i){const t={};i.forEach((e=>{let i=e.name;s&&m[i]&&(i=m[i]);const n=t[i]={name:i,type:e.type,notNull:!!e.notnull,primary:!!e.pk};null!==e.dflt_value&&(n.default=e.dflt_value)}));const n=this.db.prepare(`PRAGMA index_list(${e})`).all();return n?.length&&n.forEach((e=>{const i=this.db.prepare(`PRAGMA index_xinfo(${e.name})`).all().map((t=>t.name)).filter((t=>t));i.length&&i.forEach((i=>{s&&m[i]&&(i=m[i]),t[i].index={name:e.name,unique:!!e.unique,partial:!!e.partial}}))})),t}}enableFts(t={}){const e=this.db,s=this.name,i=this.tableInfo({mapped:!1});let n=t.fields;if(!n){const i=e.getSysConfig("fts",s);i?.fields&&(t.fields=n=i.fields)}if(i&&n?.length&&Object.keys(i).forEach((t=>{n.includes(t)||delete i[t]})),!i||0===Object.keys(i).length)throw new Error(`Missing fields to add fts for collection ${s}`);const r=t.exclude?new Set(t.exclude):void 0;let o=t.language;"string"==typeof o?o=M[o]??M.en:o&&o.name&&M[o.name]?T(o,M[o.name]):o&&"object"==typeof o||(o=M.en),t.language=o,e.loadFtsLanguage(t);const E=[],c=[],f=(t.unIndexed??["_id"]).map((t=>F[t]??t));if(Object.keys(i).forEach((e=>{r&&r.has(e)||(c.push(e),(f.includes(e)||!1!==t.skipIndexed&&i[e]?.index)&&(e+=" UNINDEXED"),E.push(e))})),0===E.length)throw new Error("fts fields is empty");if(o.tokenize){let t=o.options||"";t&&(t=" "+t),E.push(`tokenize="${o.tokenize}${t}"`)}E.push(`content='${s}'`),e.transaction((()=>{const n=(t,e)=>"jsonb"===i[t].type?`json(${e})`:e;e.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS ${s}_fts USING fts5(${E.join(",")})`),e.exec(`\n CREATE TRIGGER IF NOT EXISTS tr_${s}_fts_ai AFTER INSERT ON ${s}\n BEGIN\n INSERT INTO ${s}_fts(${c.join(",")}) VALUES (${c.map((t=>n(t,"NEW."+t))).join(",")});\n END;\n `),e.exec(`\n CREATE TRIGGER IF NOT EXISTS tr_${s}_fts_au AFTER UPDATE ON ${s}\n BEGIN\n INSERT INTO ${s}_fts (${s}_fts, oid, ${c.join(",")}) VALUES ('delete', OLD.oid, ${c.map((t=>"OLD."+t)).join(",")});\n INSERT INTO ${s}_fts (oid, ${c.join(",")}) VALUES (NEW.oid, ${c.map((t=>n(t,"NEW."+t))).join(",")});\n END;\n `),e.exec(`\n CREATE TRIGGER IF NOT EXISTS tr_${s}_fts_ad AFTER DELETE ON ${s}\n BEGIN\n INSERT INTO ${s}_fts (${s}_fts, oid, ${c.join(",")}) VALUES ('delete', OLD.oid, ${c.map((t=>"OLD."+t)).join(",")});\n END;\n `),e.setSysConfig("fts",{...t,language:o.name,fields:c,unIndexed:f},s)}))()}searchFts(t,e){const s=e?.collection||this.name,i=this.db;i.loadFtsLanguage(e);const n=e?.fieldInfos??this.tableInfo({mapped:!1});let r=e?.fieldNames;if(!r){const t=i.getSysConfig("fts",s);r=W(t?.fields?a(n,t.fields):n)}r.includes("_id")||r.push("_id");let o=e?.sort;const E=e?.order,c=e?.size,T="number"==typeof c&&c>0?`LIMIT ${c} OFFSET ${(e?.page||0)*c}`:"";o?(Array.isArray(o)||(o=[o]),o.unshift("rank"),"DESC"===E&&(o=o.map((t=>`${t} DESC`))),o=" ORDER BY "+o.join(", ")):(o="ORDER BY rank","DESC"===E&&(o+=" DESC"));let f,h=`SELECT ${r} FROM ${s}_fts`;if("string"==typeof t){let n=e?.ftsQueryStyle;"string"==typeof n?(n=n.trim(),n.endsWith(")")||(n+="(?)"),h+=` WHERE ${s}_fts MATCH ${n} ${o} ${T}`,f=i.prepare(h).all(t)):!0!==n?(h+=` WHERE ${s}_fts MATCH ? ${o} ${T}`,f=i.prepare(h).all(t)):(h+=` WHERE ${s}_fts MATCH ${t} ${o} ${T}`,f=i.prepare(h).all())}else if(t&&"object"==typeof t){const n=e?.ftsQueryStyle;t=N(t,n),n||(t=`'${t}'`),h+=` WHERE ${s}_fts MATCH ${t} ${o} ${T}`,f=i.prepare(h).all()}else f=i.prepare(h).all();if(f){const t=this.db.getSerdeOptions(e).deserialize??JSON.parse;f=f.map((e=>{const s=e._id,i=e.val?t(e.val):e;let n;return null!=i?"object"==typeof i?(n=i,n._id=s):n={_id:s,[p]:i}:n={_id:s},Object.entries(e).forEach((([t,e])=>{"_id"!==t&&"val"!==t&&(m[t]&&(t=m[t]),n[t]=e)})),n}))}return f}drop(){this.db.drop(this.name)}},B=class{constructor(t,e){this.name=t,this.db=e,t+="_attach",e.readonly||(e.prepare("CREATE TABLE IF NOT EXISTS "+t+" (_id TEXT PRIMARY KEY,filename TEXT, isText BOOLEAN, size INTEGER, mime TEXT, content BLOB, createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP)").run(),e.prepare(`CREATE INDEX IF NOT EXISTS ix_${t}_createdAt ON ${t} (createdAt)`).run(),e.prepare(`CREATE INDEX IF NOT EXISTS ix_${t}_updatedAt ON ${t} (updatedAt)`).run(),e.prepare(`CREATE INDEX IF NOT EXISTS ix_${t}_filename ON ${t} (filename)`).run(),e.prepare(`CREATE INDEX IF NOT EXISTS ix_${t}_isText ON ${t} (isText)`).run(),e.prepare(`CREATE INDEX IF NOT EXISTS ix_${t}_size ON ${t} (size)`).run(),e.prepare(`CREATE TRIGGER IF NOT EXISTS tr_${t}_updatedAt AFTER UPDATE ON ${t} BEGIN UPDATE ${t} SET updatedAt = CURRENT_TIMESTAMP WHERE key = NEW.key; END`).run()),this.preAdd=e.prepare("INSERT INTO "+t+" (_id, filename, isText, mime, content) VALUES (@_id, @filename, @isText, @mime, @content))"),this.preUpdate=e.prepare("UPDATE "+t+" SET _id = @_id, content = @content WHERE _id=@_id"),this.preExists=e.prepare("SELECT 1 FROM "+t+" WHERE _id = ?").pluck(),this.preGet=e.prepare("SELECT filename, content FROM "+t+" WHERE _id = ?").pluck(),this.preDel=e.prepare("DELETE FROM "+t+" WHERE _id = ?"),this.preDelAll=e.prepare("DELETE FROM "+t+" WHERE _id like ?"),this.preCount=e.prepare("SELECT Count(*) as count FROM "+t).pluck(),this.preCountW=e.prepare("SELECT Count(*) as count FROM "+t+" WHERE _id LIKE ?").pluck(),this.preSearchKey=e.prepare("SELECT _id, filename, isText, mime, size, createdAt, updatedAt FROM "+t+" WHERE _id LIKE @query LIMIT @size OFFSET @offset"),this.preSearchKeyAll=e.prepare("SELECT _id, filename, isText, mime, size, createdAt, updatedAt FROM "+t+" WHERE _id LIKE @query"),this.preAll=e.prepare("SELECT _id, filename, isText, mime, size, createdAt, updatedAt FROM "+t),this.preAllLimit=e.prepare("SELECT _id, filename, isText, mime, size, createdAt, updatedAt FROM "+t+" LIMIT @size OFFSET @offset"),this.preCalcSize=e.prepare("SELECT length(content) FROM "+t+" WHERE _id = ?").pluck()}get(t,e){const s=t+"/"+e;return this.preGet.get(s)}list(t,e=""){return this.preSearchKeyAll.all({query:t+`/${e}%`})}add(t,e,s,i={}){const n=t+"/"+e;if(!i.mime){const t=o.lookup(e);if(t&&(i.mime=t,void 0===i.isText)){const e=t.includes("text")||t.includes("xml")||t.includes("json")||t.includes("script");e&&(i.isText=e)}}return this.preAdd.run({...i,_id:n,filename:e,content:s})}update(t,e,s,i={}){const n=t+"/"+e;if(!i.mime){const t=o.lookup(e);if(t&&(i.mime=t,void 0===i.isText)){const e=t.includes("text")||t.includes("xml")||t.includes("json")||t.includes("script");e&&(i.isText=e)}}let r=["_id = @_id","content = @content",`filename = '${e}'`];void 0!==i.isText&&r.push("isText = "+(i.isText?"true":"false")),i.mime&&r.push(`mime = '${i.mime}'`);let E="UPDATE "+this.name+" SET "+r.join(", ")+"WHERE _id = @_id";return this.db.prepare(E).run({_id:n,content:s})}del(t,e){return String(t).endsWith("/")||(t+="/"),e?Array.isArray(e)?this.db.transaction((()=>e.map((e=>this.preDel.run(t+e)))))():this.preDel.run(t+e):this.preDelAll.run(t+"%")}};function P(t){return('"'===t[0]||"'"===t[0])&&t[t.length-1]===t[0]}function H(t){return t&&P(t)&&(t=t.slice(1,-1)),t}function k(t,e){return e&&(e.startsWith(".")||(e="."+e),t=s.join(""+t,e)),t}export{O as DefaultKVCollection,A as KVFileCurrentVer,G as KVSqlite,B as KVSqliteAttachments,X as KVSqliteCollection,F as KV_FIELD_SYMBOL_MAP,m as KV_FIELD_SYMBOL_MAP_REVERSE,e as KV_NAME_FIELD_NAME,t as KV_NAME_SYMBOL,L as KV_TYPE_FIELD_NAME,b as KV_TYPE_SYMBOL,y as KV_VALUE_FIELD_NAME,p as KV_VALUE_SYMBOL,_ as SYS_KV_COLLECTION,D as updateKVFieldSymbol};