UNPKG

@isdk/kvsqlite

Version:

[![npm version](https://img.shields.io/npm/v/@isdk/kvsqlite.svg)](https://www.npmjs.com/package/@isdk/kvsqlite) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

1 lines 33.1 kB
var t=5,e="ver",s="kv",i="_sys_kv",n="值",r="型",o="value",E="type",c="createdAt",f="updatedAt",a={[n]:o},l=Object.fromEntries(Object.entries(a).map(([t,e])=>[e,t])),h="名",$="name";import T from"path/posix";import d from"path";import u from"fs";import{fileURLToPath as O}from"url";import{mimeType as R}from"mime-type/with-db";import A from"better-sqlite3";import{cloneDeep as N,defaultsDeep as p,omit as I,pick as S,template as _}from"lodash-es";function b(t,e){void 0===e&&(e={});for(const s in t)t.hasOwnProperty(s)&&(null!==t[s]&&"object"==typeof t[s]?(("object"!=typeof e[s]||Array.isArray(e[s]))&&(e[s]=Array.isArray(t[s])?[]:{}),b(t[s],e[s])):void 0!==t[s]&&(e[s]=t[s]));return e}function m(t,e){return t?`'${e}'`:String(e)}function L(t,e){return t.map(t=>C(t,e)).join(" AND ")}function y(t,e){return t.map(t=>C(t,e)).join(" OR ")}function F(t,e,s){const i=[];return Object.keys(e).forEach(n=>{const r=e[n];let o="string"==typeof e[n];switch(n){case"$lt":case"<":i.push(`${t} < ${m(o,r)}`);break;case"<=":case"$lte":i.push(`${t} <= ${m(o,r)}`);break;case"$gt":case">":i.push(`${t} > ${m(o,r)}`);break;case"$gte":case">=":i.push(`${t} >= ${m(o,r)}`);break;case"$ne":case"!=":i.push(`${t} != ${m(o,r)}`);break;case"=":case"$eq":i.push(`${t} = ${m(o,r)}`);break;case"$in":i.push(`${t} IN (${r.map(t=>m(!0,t)).join(", ")})`);break;case"$nin":i.push(`${t} NOT IN (${r.map(t=>m(!0,t)).join(", ")})`);break;case"$regex":i.push(`${t} REGEXP '${r.source}'`);break;case"$like":i.push(`${t} LIKE '${r}'`);break;case"$nlike":i.push(`${t} NOT LIKE '${r}'`);break;case"$glob":i.push(`${t} GLOB '${r}'`);break;case"$nglob":i.push(`${t} NOT GLOB '${r}'`);break;case"$or":if(Array.isArray(r)){n=n.slice(1).toUpperCase();const e=e=>"string"==typeof e?`${t}='${m(o,e)}'`:F(t,e,s);i.push(`(${r.map(t=>e(t)).join(` ${n} `)})`);break}throw new Error(`${n} is not an array for ${t}`);default:throw new Error(`Unsupported condition operator: ${n} for ${t}`)}}),i.join(" AND ")}function C(t,e){const s=[];let i=e?.wrapKey;if(e||(e={}),"function"!=typeof i&&(e.wrapKey=i=t=>t),Array.isArray(t))s.push(L(t,e));else for(const[n,r]of Object.entries(t))if("$and"===n)s.push(`(${L(t[n],e)})`);else if("$or"===n)s.push(`(${y(t[n],e)})`);else{const t=typeof r;if(null==r)s.push(`${i(n)} IS NULL`);else if("object"!==t||Array.isArray(r))if("string"===t)s.push(`${i(n)}='${r}'`);else{if("number"!==t&&"boolean"!==t)throw new Error(`Unsupported value type for key ${n}`);s.push(`${i(n)}=${r}`)}else s.push(F(i(n),r,e))}return s.length>1?s.join(" AND "):s[0]}function D(t,e){let s="";if(Array.isArray(t))s="string"==typeof e?"("+t.map(t=>D(t,e)).join(") || (")+")":"("+t.map(t=>D(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=>D(t,e)).join(" || ' OR ' || "):r.map(t=>D(t,e)).join(" OR ");else if("$and"===n)s=e?r.map(t=>D(t,e)).join(" || "):r.map(t=>D(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=>D(t,e)).join(" || ")+" || ')'":"NEAR("+r.map(t=>D(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=>D(t,e)).join(" || "):"NEAR("+t.map(t=>D(t,e)).join(" "),r.distance&&(s+="string"==typeof e?` || ', ${r.distance}`:`, ${r.distance}`),s+="string"==typeof e?")'":")"}}}}else s=e?"string"==typeof e?(e=e.trim()).endsWith(")")?e:`${e}('${t}')`:`'${t}'`:`"${t}"`;return s}function g(t,e){let s;return"string"==typeof e?((e=e.trim()).endsWith(")")||(e+="(?)"),t=e,s=!0):!0!==e&&(t="?",s=!0),{query:t,hasParam:s}}import M from"better-sqlite3";var j=O(import.meta.url),w=d.dirname(j);function x(t,e){a[t]=e,l[e]=t}var v=_("CASE WHEN strftime('%Y', NEW.${field}) IS NOT NULL THEN NEW.${field} ELSE ${fallback} END"),U=_("${field} != NEW.${field}"),W="schema",B={zh:{name:"zh",plugin:{path:"zh",jiebaDict:"dict",load(t,e){if(e?.jiebaDict){const s=d.isAbsolute(e.jiebaDict)?e.jiebaDict:d.resolve(w,"..","plugins",e.jiebaDict);t.prepare("select jieba_dict(?)").run(s)}}},tokenize:"simple"},en:{name:"en",tokenize:"porter unicode61"}};function X(t,e){if(null==t)return"NULL";if("string"==typeof t)return t.startsWith("?=")?t=t.slice(2):Q(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 H(t,e,s){if(null==t)return null;switch(e=e.toUpperCase(),["DATE","TIME"].some(t=>e.startsWith(t))&&(e="TEXT"),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 k(t){for(const[e,s]of Object.entries(t)){const i=a[e];i&&(t[i]=s,delete t[e])}return t}function G(t){for(const[e,s]of Object.entries(t)){const i=l[e];i&&(t[i]=s,delete t[e])}return t}function P(t){if("string"==typeof t)return Q(t)?t.slice(1,-1):this.prepare(`SELECT ${t}`).pluck().get()}function q(t,e,s){const i={};for(let n in t){const r=t[n],o=a[n];if(e){const t=e[n]??e[o]??P.call(this,r.default);if(null==t&&r.notNull)throw new Error(`field ${n} is not nullable`);o&&(n=o),i[n]=H(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 J(t){const e=[];for(let s in t){const i=t[s];a[s]&&(s=a[s]),"JSON"!==i.type&&"JSONB"!==i.type||(s=`json(${s}) as ${s}`),e.push(s)}return e}function z(t,e,s){const i=s?.deserialize??JSON.parse;for(let s in e){const n=l[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}function K(t,e){const s=e?.foreignKeys;let i=t.name;if(!i)throw new Error("fieldName is required");a[i]&&(i=a[i]);const n=(t.type||"TEXT")+" ";let r=t.constraint||"";t.notNull&&!r.toUpperCase().includes("NOT NULL")&&(r+="NOT NULL "),t.primary&&!r.toUpperCase().includes("PRIMARY KEY")&&(r+="PRIMARY KEY "),t.unique&&!r.toUpperCase().includes("UNIQUE")&&(r+="UNIQUE ");const o=`${i} ${n}${r}${null!=t.default?`DEFAULT ${X(t.default,e)}`:""}`.trim();if(s){const e=t.foreignKey;if(e){const{reference:t,isJson:n,onUpdate:r,onDelete:o}=e;let E=e.referenceField;if(!t)throw new Error("foreignKey.referenceTable is required");E||(E="_id"),n&&(E=`val->>'$.${E}'`);let c=`FOREIGN KEY (${i}) REFERENCES ${t}(${E})`;c+=` ON UPDATE ${r||"CASCADE"}`,c+=` ON DELETE ${o||"CASCADE"}`,s.add(c)}}return o}var Y=class extends A{constructor(t,n){if("string"==typeof t&&":"!=t[0]){const e=d.dirname(t);e&&!u.existsSync(e)&&u.mkdirSync(e,{recursive:!0})}super(t,n),this.ftsLoaded={},this.collections={},this.serdeOptions={};const r=0===this.prepare("SELECT COUNT(*) FROM sqlite_master").pluck().get();if(this.preIsExists=this.prepare("SELECT COUNT(*) FROM sqlite_master WHERE type = ? AND name= ?").pluck(),n?.serialize&&(this.serdeOptions.serialize=n.serialize),n?.deserialize&&(this.serdeOptions.deserialize=n.deserialize),n?.id&&(this.id=n.id),this.readonly){const t=this.sqlSchema({type:"table"}).map(t=>t.tbl_name);t.length&&this.createCollections(t)}else{const t=n?.collections||[],o=t.indexOf(i);let E;if(o>=0){const e=t.splice(o,1);"string"!=typeof e&&(E=e)}if(this.create(i,E),t.includes(s)||t.push(s),n?.collection&&!t.includes(n.collection)&&t.push(n.collection),t.length&&this.createCollections(t,n),!r){const t=this.getSysConfig(e,i);5!==t&&this.tryUpgradeVer(t)}}}static dump(t){return function(t){let e=!1;if("string"==typeof t){const s={};":memory:"!==t&&(s.readonly=!0),t=new M(t,s),e=!0}if(!(t instanceof M))throw new Error("db must be a string or a Database instance");let s="";try{s+="PRAGMA foreign_keys=OFF;\n",s+="BEGIN TRANSACTION;\n";const e=t.prepare("SELECT type, name, sql FROM sqlite_master WHERE sql IS NOT NULL AND name NOT LIKE 'sqlite_%'").all(),i=e.filter(t=>"table"===t.type&&"sqlite_sequence"!==t.name),n=e.filter(t=>"trigger"===t.type),r=e.filter(t=>"view"===t.type),o=e.filter(t=>"index"===t.type);for(const t of i)s+=`${t.sql.replace(/^CREATE TABLE/,"CREATE TABLE IF NOT EXISTS")};\n`;for(const e of i){const i=t.prepare(`SELECT * FROM "${e.name}"`).all();for(const t of i){const i=Object.keys(t).map(t=>`"${t}"`).join(", "),n=Object.values(t).map(t=>null===t?"NULL":"string"==typeof t?`'${t.replace(/'/g,"''")}'`:"boolean"==typeof t?t?"1":"0":Buffer.isBuffer(t)?`x'${t.toString("hex")}'`:t).join(", ");s+=`INSERT OR IGNORE INTO "${e.name}" (${i}) VALUES (${n});\n`}}for(const t of n)s+=`${t.sql.replace(/^CREATE TRIGGER/,"CREATE TRIGGER IF NOT EXISTS")};\n`;for(const t of r)s+=`${t.sql.replace(/^CREATE VIEW/,"CREATE VIEW IF NOT EXISTS")};\n`;for(const t of o)s+=`${t.sql.replace(/^CREATE INDEX/,"CREATE INDEX IF NOT EXISTS")};\n`;s+="COMMIT;\n"}catch(t){throw t}finally{e&&t.close()}return s}(t)}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(t){const n=this.getCollection(i);if(null==t&&n&&(t=n.getExtend(i,e)),t||(t=0),t<1){const t=this.tableInfo(s);t?.key&&this.exec(`ALTER TABLE ${s} RENAME COLUMN "key" TO "_id"`)}if(t<5)for(const e of Object.values(this.collections)){const s=e.tableInfo({mapped:!1}),i=e.name;t<4?(this.exec(`DROP TRIGGER IF EXISTS tr_${i}_createdAt;`),this.exec(`DROP TRIGGER IF EXISTS tr_${i}_updatedAt;`)):4===t&&(this.exec(`DROP TRIGGER IF EXISTS tr_${i}_auto_timestamp_insert;`),this.exec(`DROP TRIGGER IF EXISTS tr_${i}_auto_timestamp_update;`)),e.createIndexAndTriggerForAutoTimestamp(s)}5!==t&&n?.setExtend(i,e,5)}usingJsonb(t){let e;t||(t=s);const n=this.collections[i];if(n&&(e=n.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 n=this.getCollection(i);if(n)return e||(e=s),n.getExtend(e,t)}setSysConfig(t,e,n){const r=this.getCollection(i);if(r)return n||(n=s),r.setExtend(n,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 V(t,this,e)}getCollection(t){let e=this.collections[t];return!e&&this.isCollectionExists(t)&&(e=this.collections[t]=new V(t,this)),e}drop(t){if(this.isCollectionExists(t)){if(t===i||t===s)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.getCollection(i);e&&(e.delExtend(t,"%"),e.del(t)),delete this.collections[t]}}set(t,e,i){const n=("object"===typeof t?e?.collection:i?.collection)||s,r=this.getCollection(n);return r?.set(t,e,i)}setExtend(t,e,i,n){const r=n?.collection||s,o=this.getCollection(r);return o?.setExtend(t,e,i,n)}setExtends(t,e,i){const n=i?.collection||s,r=this.getCollection(n);return r?.setExtends(t,e,i)}bulkDocs(t,e){const i=e?.collection||s,n=this.getCollection(i);return n?.bulkDocs(t,e)}get(t,e){const i=e?.collection||s,n=this.getCollection(i);return n?.get(t)}getExtend(t,e,i){const n=i?.collection||s,r=this.getCollection(n);return r?.getExtend(t,e)}getExtends(t,e,i){const n=i?.collection||s,r=this.getCollection(n);return r?.getExtends(t,e,i)}del(t,e){const i=e?.collection||s,n=this.getCollection(i);return n?.del(t)}isExists(t,e){const i=e?.collection||s,n=this.getCollection(i);return n?.isExists(t)}count(t,e){const i=e?.collection||s,n=this.getCollection(i);return n?.count(t)}list(t,e){t&&"object"==typeof t&&(t=(e=t).query);const i=e?.collection||s,n=this.getCollection(i);return n?.list(t,e)}createJsonIndex(t,e,i){const n=i?.collection||s,r=this.getCollection(n);return r?.createJsonIndex(t,e)}createIndex(t,e,i){const n=i?.collection||s,r=this.getCollection(n);return r?.createTableIndex(t,e)}searchEx(t,e){const i=e?.collection||s,n=this.getCollection(i);return n?.searchEx(t,e)}search(t,e){const i=e?.collection||s,n=this.getCollection(i);return n?.search(t,e)}buildWhereClause(t,e){const i=e?.collection||s,n=this.getCollection(i);return n?.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?p({},e,B[s]):B[s];if(t&&t.plugin){const e=t.plugin;if(e.path){const t=d.isAbsolute(e.path)?e.path:d.resolve(w,"..","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"string"==typeof t?(e||(e={}),e.collection=t):t&&"object"==typeof t&&(e=t),this._tableInfo(e)}_tableInfo(t){const e=t?.collection||s,i=t?.mapped??!0,n=this.prepare(`PRAGMA table_info(${e})`).all();if(n){const t={};n.forEach(e=>{let s=e.name;i&&l[s]&&(s=l[s]);const n=t[s]={name:s,type:e.type,notNull:!!e.notnull,primary:!!e.pk};null!==e.dflt_value&&(n.default=e.dflt_value)});const s=this.prepare(`PRAGMA index_list(${e})`).all();return s?.length&&s.forEach(e=>{const s=this.prepare(`PRAGMA index_xinfo(${e.name})`).all().map(t=>t.name).filter(t=>t);s.length&&s.forEach(s=>{i&&l[s]&&(s=l[s]),t[s].index={name:e.name,unique:!!e.unique,partial:!!e.partial}})}),t}}sqlSchema(t,e){let s,i,n="",r=!0;t&&"object"==typeof t&&(t=(e=t).name),t&&(n+=`name = '${t}'`),e&&(null!=e.excludeUnmanaged&&(r=e.excludeUnmanaged),i=e.type,i&&(s="virtual_table"===i,s&&(i="table"),n&&(n+=" AND "),n+=`type = '${i}'`)),n&&(n=` WHERE ${n}`),n="SELECT * FROM sqlite_master"+n;let o=this.prepare(n).all();return"table"===i&&(o=o.filter(t=>{if(r&&!this.isManagedTable(t.tbl_name))return!1;let e=t.sql.startsWith("CREATE VIRTUAL TABLE");return s?t.type="virtual_table":e=!e,e})),t&&o.length<=1?o[0]:o}dump(){return this.constructor.dump(this)}isManagedTable(t){const e=this.tableInfo(t,{mapped:!1});if(!e)throw new Error(`Table ${t} not found`);return e._id&&e._id.primary&&e.val}enableFts(t,e){return t=t||s,this.collections[t]?.enableFts(e)}searchFts(t,e){const i=e?.collection||s,n=this.getCollection(i);return n?.searchFts(t,e)}},V=class{constructor(t,e,s={}){this.name=t,this.db=e;const n=e.usingJsonb(t)??s.usingJsonb??e.usingJsonb(i)??!0,r=n?"jsonb":"json";let o;this.jsonb=r,s=N(s);e.isCollectionExists(t)?(o=this.tableInfo({collection:t,mapped:!1}),s.fields=o):(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=a[s];return i&&(s=i),t[s]=e,t},{}):k(t),t}(s.fields):s.fields={},o=s.fields,o._id&&o._id.type?o._id.primary=!0:o._id={name:"_id",type:"TEXT",primary:!0},o.val?o.val.type=n?"JSONB":"JSON":o.val={name:"val",type:n?"JSONB":"JSON"},!0===s.fts&&(s.fts={unIndexed:["_id"]}));const E=q.call(e,o,void 0,e.getSerdeOptions(s)),c=J(o);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(E).join(",")}) VALUES (${Object.values(E).join(",")})`),this.preUpdate=e.prepare(`UPDATE ${t} SET ${Object.entries(E).map(([t,e])=>`${t} = ${e}`).join(",")} WHERE _id=@_id`),this.preGet=e.prepare(`SELECT ${c.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 ${c.join(",")} FROM ${t} WHERE _id LIKE @query LIMIT @size OFFSET @offset`),this.preSearchKeyAll=e.prepare(`SELECT ${c.join(",")} FROM ${t} WHERE _id LIKE @query`),this.preAll=e.prepare(`SELECT ${c.join(",")} FROM ${t}`),this.preAllLimit=e.prepare(`SELECT ${c.join(",")} FROM ${t} LIMIT @size OFFSET @offset`)}createIndexAndTriggerForAutoTimestamp(t){const e=t.val?"JSONB"===t.val.type||"BLOB"===t.val.type?"jsonb":"json":"";t.createdAt?this.createTableIndex("createdAt","createdAt"):e&&this.createJsonIndex("createdAt","createdAt"),t.updatedAt?this.createTableIndex("updatedAt","updatedAt"):e&&this.createJsonIndex("updatedAt","updatedAt");const s=t.createdAt?"createdAt":e?"val->>'$.createdAt'":void 0,i=t.updatedAt?"updatedAt":e?"val->>'$.updatedAt'":void 0,n=t.createdAt?"createdAt = (":e?`val = ${e}_set(New.val, '$.createdAt',`:"",r=t.updatedAt?"updatedAt = (":e?`val = ${e}_set(New.val, '$.updatedAt', `:"";let o="";(s||i)&&(!e||t.createdAt||t.updatedAt?(s&&(o+=`${n} strftime('%FT%H:%M:%fZ', ${v({field:s,fallback:"'now'"})}))`),i&&(o&&(o+=", "),o+=`${r} strftime('%FT%H:%M:%fZ', ${v({field:i,fallback:"'now'"})}))`)):o=`val = ${e}_set(New.val, '$.createdAt', strftime('%FT%H:%M:%fZ', ${v({field:s,fallback:"'now'"})}), '$.updatedAt', strftime('%FT%H:%M:%fZ', ${v({field:i,fallback:"'now'"})}))`,o+=" WHERE _id = NEW._id;",this.createTrigger({name:"auto_timestamp_insert",triggerTiming:"AFTER",event:"INSERT",condition:"FOR EACH ROW",action:`UPDATE ${this.name} SET `+o})),o="",!e||t.createdAt||t.updatedAt?(s&&(o+=`${n} strftime('%FT%H:%M:%fZ', ${v({field:s,fallback:`IFNULL(OLD.${s}, 'now')`})}))`),i&&(o&&(o+=", "),o+=`${r} strftime('%FT%H:%M:%fZ', ${v({field:i,fallback:"'now'"})}))`)):o=`val = ${e}_set(New.val, '$.createdAt', strftime('%FT%H:%M:%fZ', ${v({field:s,fallback:`IFNULL(OLD.${s}, 'now')`})}), '$.updatedAt', strftime('%FT%H:%M:%fZ', ${v({field:i,fallback:"'now'"})}))`,o+=` WHERE _id = NEW._id AND (${i} IS NULL OR ${U({name:this.name,field:i})} OR strftime('%Y', NEW.${i}) IS NULL);`,this.createTrigger({name:"auto_timestamp_update",triggerTiming:"AFTER",event:"UPDATE",condition:"FOR EACH ROW",action:`UPDATE ${this.name} SET `+o})}_set(t,e){const s=t._id;t={...t};let i,r=this.get(s);if(r){if(e?.ignoreExists)return{changes:0,lastInsertRowid:-1};const s=!e||!1!==e.overwrite;i=this.preUpdate,s?r=t:t&&"object"==typeof t&&r&&"object"==typeof r?(delete r.createdAt,delete r.updatedAt,b(t,r)):r=t}else i=this.preAdd,r=t;const E=this.tableInfo(),c=[...Object.keys(E),...Object.keys(I(l,[o]))];let f=I(r,c);1===Object.keys(f).length&&null!=f[n]&&"object"!=typeof f[n]&&(f=f[n]);const a=S(r,c);null!=f&&("object"==typeof f&&k(f),a.val=f);const h=q.call(this.db,E,a,this.db.getSerdeOptions(e));return i.run(h)}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,s,i){e.startsWith(".")||(e="."+e);const o={_id:t=T.join(""+t,e),[n]:s};return i?.[r]&&(o[r]=i[r]),this._set(o,i)}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={[n]:e,_id:t}),e}getEx(t){let e=this.preGet.get(t+"");if(e){z(e,this.tableInfo({mapped:!1}),this.db.getSerdeOptions());const t=e.val;t&&"object"==typeof t?(delete e.val,e={...e,...G(t)}):2===Object.keys(e).length?e=t:(delete e.val,void 0!==t&&(e[n]=t))}return e}getExtend(t,e){e&&(t=tt(t,e));const s=this.get(t);return s?.[n]}isExistsExtend(t,e){return e&&(t=tt(t,e)),this.isExists(t)}delExtend(t,e){return e&&(t=tt(t,e)),this.del(t)}getExtends(t,e,s){const i=s?.singleValue;e?"string"==typeof e&&(e=[e]):e=["%"];const r=(e=e.map(e=>tt(t,e))).map(t=>t.lastIndexOf("%")>=0?this.list(t):this.get(t)).flat();let o=r.reduce((t,e)=>{if(e){const s=e[n];t[T.basename(e._id+"").slice(1)]=s}return t},{_id:t});return i&&(o=1===r.length?r[0][n]: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=C(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,r=e?.page||0;let o,E=e?.sort;if(E){const n=J(s),c=e?.order;Array.isArray(E)||(E=[E]),E=E.map(t=>`val->>'$.${t}'`),"DESC"===c&&(E=E.map(t=>`${t} DESC`)),E=" ORDER BY "+E.join(", ");const f=i?` LIMIT ${i} OFFSET ${r*i}`:"";t=t?` WHERE _id LIKE ${t}`:"",o=this.db.prepare(`SELECT ${n.join(",")} FROM ${this.name}`+t+E+f).all()}else o=t?i?this.preSearchKey.all({query:t,size:i,offset:r*i}):this.preSearchKeyAll.all({query:t}):i?this.preAllLimit.all({size:i,offset:r*i}):this.preAll.all();const c=this.db.getSerdeOptions(e).deserialize??JSON.parse;return o.map(t=>{const i=c(t.val);return delete t.val,i&&"object"==typeof i?{...z(I(t,["val"]),s,this.db.getSerdeOptions(e)),...G(i)}:{...z(I(t,["val"]),s,this.db.getSerdeOptions(e)),[n]:i}})}getSchema(){const t=this.db.collections[i];return t?.getExtend(this.name,W)}createSchema(t){const e=this.db;if(t.name&&t.fields&&t.name!==i){const s=e.collections[i];if(s){const e=t.overwrite;delete t.overwrite,e?s.setExtend(t.name,W,t,{overwrite:e}):s.isExistsExtend(t.name,W)||s.setExtend(t.name,W,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];return n.name||(n.name=t),K(n,{...e,foreignKeys:i})});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(i),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 a[e[0]]&&(e[0]=a[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=a[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()}renameField(t,e,s){const i=this.db;if("string"==typeof e){const s=`ALTER TABLE ${this.name} RENAME COLUMN ${t} TO ${e}`;return i.prepare(s).run()}if(e?.name){const n=s?.fieldInfos??this.tableInfo({mapped:!1});i.transaction(()=>{let r=`__${t}_OlD`,o=0;for(;n?.[r];)r+=o++;i.exec(`ALTER TABLE ${this.name} RENAME COLUMN ${t} TO ${r}`),i.exec(`ALTER TABLE ${this.name} ADD COLUMN ${e.name} ${K(e,s)}`),i.exec(`UPDATE ${this.name} SET ${e.name} = ${r} WHERE ${r} IS NOT NULL`),i.exec(`ALTER TABLE ${this.name} DROP COLUMN ${r}`)}).immediate()}}searchEx(t,e){const s=e?.fieldInfos??this.tableInfo({mapped:!1});"string"!=typeof t&&(t=Object.entries(t).map(([t,i])=>(a[t]&&(t=a[t]),(void 0===s[t]?`val->>'$.${t}'`:t)+`=${X(i,{...e,...this.db.getSerdeOptions(e)})}`)).join(" AND "));let i=e?.sort;const r=e?.order,o=e?.size,E=e?.page||0;i?(Array.isArray(i)||(i=[i]),i=i.map(t=>(a[t]&&(t=a[t]),void 0===s[t]&&(t=`val->>'$.${t}'`),t)),"DESC"===r&&(i=i.map(t=>`${t} DESC`)),i=" ORDER BY "+i.join(", ")):i="";const c=`SELECT ${J(e?.fieldNames?S(s,["_id","val",...e.fieldNames]):s).join(",")} FROM `+this.name+" WHERE "+t+i,f=this.db.prepare(c+" LIMIT @size OFFSET @offset"),l=this.db.prepare(c),h=o?f.all({size:o,offset:E*o}):l.all(),$=this.db.getSerdeOptions(e).deserialize??JSON.parse;return h.map(t=>{let i=$(t.val);return delete t.val,i&&"object"==typeof i?G(i):i={[n]:i},{...z(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 C(t,{wrapKey:t=>{const e=a[t];return e&&(t=e),void 0===s[t]?`val->>'$.${t}'`:t}})}tableInfo(t){const e=this.name;return this.db._tableInfo({...t,collection:e})}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,o=t.include?new Set(t.include):void 0;let E=t.language;"string"==typeof E?E=B[E]??B.en:E&&E.name&&B[E.name]?p(E,B[E.name]):E&&"object"==typeof E||(E=B.en),t.language=E,e.loadFtsLanguage(t);const c=[],f=[],l=(t.unIndexed??["_id"]).map(t=>a[t]??t);if(Object.keys(i).forEach(e=>{r&&r.has(e)||(f.push(e),(l.includes(e)||!1!==t.skipIndexed&&i[e]?.index&&!o?.has(e))&&(e+=" UNINDEXED"),c.push(e))}),0===c.length)throw new Error("fts fields is empty");if(E.tokenize){let t=E.options||"";t&&(t=" "+t),c.push(`tokenize="${E.tokenize}${t}"`)}c.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(${c.join(",")})`),e.exec(`\n CREATE TRIGGER IF NOT EXISTS tr_${s}_fts_ai AFTER INSERT ON ${s}\n BEGIN\n INSERT INTO ${s}_fts(${f.join(",")}) VALUES (${f.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, ${f.join(",")}) VALUES ('delete', OLD.oid, ${f.map(t=>"OLD."+t).join(",")});\n INSERT INTO ${s}_fts (oid, ${f.join(",")}) VALUES (NEW.oid, ${f.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, ${f.join(",")}) VALUES ('delete', OLD.oid, ${f.map(t=>"OLD."+t).join(",")});\n END;\n `),e.setSysConfig("fts",{...t,language:E.name,fields:f,unIndexed:l},s)})()}searchFts(t,e){const s=e?.collection||this.name,i=this.db;i.loadFtsLanguage(e);const r=e?.fieldInfos??this.tableInfo({mapped:!1});let o=e?.fieldNames;if(!o){const t=i.getSysConfig("fts",s);o=J(t?.fields?S(r,t.fields):r)}o.includes("_id")||o.push("_id");let E=e?.sort;const c=e?.order,f=e?.size,a="number"==typeof f&&f>0?`LIMIT ${f} OFFSET ${(e?.page||0)*f}`:"";E?(Array.isArray(E)||(E=[E]),E.unshift("rank"),"DESC"===c&&(E=E.map(t=>`${t} DESC`)),E=" ORDER BY "+E.join(", ")):(E="ORDER BY rank","DESC"===c&&(E+=" DESC"));let h,$,T="",d=e?.query;if("string"==typeof t){const i=g(t,e?.ftsQueryStyle);T+=`${s}_fts MATCH ${i.query}`,$=i.hasParam}else if(t&&"object"==typeof t){let i=e?.ftsQueryStyle,n=t;if(!(Array.isArray(n)||1===Object.keys(n).length&&null==n.$match)){const e=I(n,["$match"]),s=S(n,["$match"]);if(Object.keys(e).length&&(d=p(e,d)),s.$match&&(n=s.$match,"string"==typeof n)){t=n;const e=g(n,i);n=e.query,$=e.hasParam}}"object"==typeof n&&(n=D(n,i),i||(n=`'${n}'`)),T+=`${s}_fts MATCH ${n}`}if(d){let t=d;"object"==typeof t&&(t=this.buildWhereClause(t,{...e,fieldInfos:r})),t&&(T&&(T+=" AND "),T+=t)}if(T&&(T=`WHERE ${T}`),T=`SELECT ${o} FROM ${s}_fts ${T} ${E} ${a}`,h=$?i.prepare(T).all(t):i.prepare(T).all(),h){const t=e?.retrieveFull,s=this.db.getSerdeOptions(e).deserialize??JSON.parse;h=h.map(e=>{const i=e._id;if(t)return this.get(i);const r=e.val?s(e.val):e;let o;return null!=r?"object"==typeof r?(o=r,o._id=i):o={_id:i,[n]:r}:o={_id:i},Object.entries(e).forEach(([t,e])=>{"_id"!==t&&"val"!==t&&(l[t]&&(t=l[t]),o[t]=e)}),o})}return h}drop(){this.db.drop(this.name)}},Z=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=R.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=R.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 o="UPDATE "+this.name+" SET "+r.join(", ")+"WHERE _id = @_id";return this.db.prepare(o).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 Q(t){return('"'===t[0]||"'"===t[0])&&t[t.length-1]===t[0]}function tt(t,e){return e&&(e.startsWith(".")||(e="."+e),t=T.join(""+t,e)),t}export{s as DefaultKVCollection,t as KVFileCurrentVer,Y as KVSqlite,Z as KVSqliteAttachments,V as KVSqliteCollection,c as KV_CREATEDAT_FIELD_NAME,a as KV_FIELD_SYMBOL_MAP,l as KV_FIELD_SYMBOL_MAP_REVERSE,e as KV_FILE_VER_NAME,$ as KV_NAME_FIELD_NAME,h as KV_NAME_SYMBOL,E as KV_TYPE_FIELD_NAME,r as KV_TYPE_SYMBOL,f as KV_UPDATEDAT_FIELD_NAME,o as KV_VALUE_FIELD_NAME,n as KV_VALUE_SYMBOL,i as SYS_KV_COLLECTION,x as updateKVFieldSymbol};