@contentrain/query 4.0.0 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +253 -260
- package/dist/cli.cjs +78 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +81 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/generate-C5Qz8qKt.mjs +855 -0
- package/dist/generate-C5Qz8qKt.mjs.map +1 -0
- package/dist/generate-CPKYh6ZU.cjs +858 -0
- package/dist/generator/generate.cjs +3 -0
- package/dist/generator/generate.d.cts +14 -0
- package/dist/generator/generate.d.cts.map +1 -0
- package/dist/generator/generate.d.mts +14 -0
- package/dist/generator/generate.d.mts.map +1 -0
- package/dist/generator/generate.mjs +2 -0
- package/dist/index.cjs +356 -0
- package/dist/index.d.cts +108 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +108 -565
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +349 -30
- package/dist/index.mjs.map +1 -0
- package/package.json +53 -38
- package/dist/index.d.ts +0 -565
- package/dist/index.js +0 -30
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/runtime/query.ts","../src/runtime/singleton.ts","../src/runtime/dictionary.ts","../src/runtime/document.ts","../src/index.ts"],"mappings":";;;UAAiB,YAAA;EACf,MAAA;EACA,KAAA;AAAA;AAAA,KAGU,gBAAA,IAAoB,KAAA,UAAe,EAAA,UAAY,MAAA,oBAA0B,MAAA;AAAA,cAExE,YAAA;EAAA,QACH,KAAA;EAAA,QACA,OAAA;EAAA,QACA,QAAA;EAAA,QACA,UAAA;EAAA,QACA,UAAA;EAAA,QACA,MAAA;EAAA,QACA,OAAA;EAAA,QACA,SAAA;EAAA,QACA,aAAA;EAAA,QACA,SAAA;EAAA,QACA,cAAA;cAGN,IAAA,EAAM,GAAA,SAAY,CAAA,KAClB,YAAA,GAAe,MAAA,SAAe,YAAA,GAC9B,QAAA,GAAW,gBAAA,EACX,aAAA;EAQF,MAAA,CAAO,IAAA;EAKP,KAAA,0BAA+B,CAAA,CAAA,CAAG,KAAA,EAAO,CAAA,EAAG,KAAA,EAAO,CAAA,CAAE,CAAA;EASrD,IAAA,0BAA8B,CAAA,CAAA,CAAG,KAAA,EAAO,CAAA,EAAG,KAAA;EAM3C,KAAA,CAAM,CAAA;EAKN,MAAA,CAAO,CAAA;EAKP,OAAA,CAAA,GAAW,MAAA;EAKX,GAAA,CAAA,GAAO,CAAA;EAsCP,KAAA,CAAA,GAAS,CAAA;EAAA,QAID,YAAA;EAAA,QAeA,gBAAA;EAAA,QAoCA,UAAA;AAAA;;;cChKG,iBAAA,WAA4B,MAAA;EAAA,QAC/B,KAAA;EAAA,QACA,OAAA;EAAA,QACA,cAAA;EAAA,QACA,SAAA;EAAA,QACA,aAAA;EAAA,QACA,aAAA;cAGN,IAAA,EAAM,GAAA,SAAY,CAAA,GAClB,aAAA,WACA,YAAA,GAAe,MAAA;IAAiB,MAAA;IAA2B,KAAA;EAAA,IAC3D,YAAA,IAAgB,KAAA,UAAe,EAAA,UAAY,MAAA;EAQ7C,MAAA,CAAO,IAAA;EAKP,OAAA,CAAA,GAAW,MAAA;EAKX,GAAA,CAAA,GAAO,CAAA;EAAA,QA4BC,gBAAA;EAAA,QAuCA,UAAA;AAAA;;;cCjGG,kBAAA;EAAA,QACH,KAAA;EAAA,QACA,OAAA;EAAA,QACA,cAAA;cAEI,IAAA,EAAM,GAAA,SAAY,MAAA,mBAAyB,aAAA;EAKvD,MAAA,CAAO,IAAA;EAKP,GAAA,CAAA,GAAO,MAAA;EACP,GAAA,CAAI,GAAA;EACJ,GAAA,CAAI,GAAA,UAAa,MAAA,EAAQ,MAAA;EAAA,QAUjB,YAAA;AAAA;;;cCzBG,aAAA;EAAA,QACH,KAAA;EAAA,QACA,OAAA;EAAA,QACA,QAAA;EAAA,QACA,SAAA;EAAA,QACA,aAAA;EAAA,QACA,SAAA;EAAA,QACA,cAAA;cAGN,IAAA,EAAM,GAAA,SAAY,CAAA,KAClB,YAAA,GAAe,MAAA,SAAe,YAAA,GAC9B,QAAA,GAAW,gBAAA,EACX,aAAA;EAQF,MAAA,CAAO,IAAA;EAKP,KAAA,0BAA+B,CAAA,CAAA,CAAG,KAAA,EAAO,CAAA,EAAG,KAAA,EAAO,CAAA,CAAE,CAAA;EAKrD,OAAA,CAAA,GAAW,MAAA;EAKX,MAAA,CAAO,IAAA,WAAe,CAAA;EAStB,GAAA,CAAA,GAAO,CAAA;EAWP,KAAA,CAAA,GAAS,CAAA;EAAA,QAID,YAAA;EAAA,QAeA,gBAAA;EAAA,QAoCA,UAAA;AAAA;;;;;AH5GV;;;;;;;;;iBIuBsB,uBAAA,CACpB,WAAA,YACC,OAAA;EACD,KAAA,GAAQ,KAAA,aAAkE,YAAA,CAAN,MAAA;EACpE,SAAA,GAAY,KAAA,aAA2E,iBAAA,CAAN,MAAA;EACjF,UAAA,GAAa,KAAA,aADmE,kBAAA;EAEhF,QAAA,GAAW,KAAA,aAAsE,aAAA,CAAN,MAAA;AAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,30 +1,349 @@
|
|
|
1
|
-
import {lru}from'tiny-lru';import Pe from'node:process';import Me from'debug';import {readFile,readdir}from'node:fs/promises';import {join}from'node:path';import ze from'better-sqlite3';var Qe=Object.defineProperty;var c=(N,t)=>Qe(N,"name",{value:t,configurable:true});var ne=class ne{constructor(t={}){this.stats={hits:0,misses:0,size:0,lastCleanup:Date.now()};this.options={maxSize:100,defaultTTL:60*1e3,...t};let e=Math.max(1,Math.floor(Math.max(1,this.options.maxSize)*1024*1024/1e3));this.cache=lru(e);}calculateSize(t){let e=JSON.stringify(t);return new TextEncoder().encode(e).length}async set(t,e,r){await this.cleanupCache();let o=this.calculateSize(e),a=Date.now(),s=a+(r||this.options.defaultTTL);for(;o+this.stats.size>this.options.maxSize*1024*1024;){let d=this.findOldestKey();if(!d)break;await this.delete(d);}let n={data:e,expireAt:s,size:o,createdAt:a},l=this.cache.get(t);l&&(this.stats.size-=l.size),this.cache.set(t,n),this.stats.size+=o;}findOldestKey(){let t=null,e=1/0;for(let r of this.cache.keys()){let o=this.cache.get(r);o.createdAt<e&&(e=o.createdAt,t=r);}return t}async get(t){let e=this.cache.get(t);return e?Date.now()>=e.expireAt?(await this.delete(t),this.stats.misses++,null):(this.stats.hits++,e.data):(this.stats.misses++,null)}async delete(t){let e=this.cache.get(t);e&&(this.stats.size-=e.size,this.cache.delete(t));}async clear(){this.cache.clear(),this.stats={hits:0,misses:0,size:0,lastCleanup:Date.now()};}async cleanupCache(){let t=Date.now(),e=[],r=0;for(let o of this.cache.keys()){let a=this.cache.get(o);a.expireAt<=t?e.push(o):r+=a.size;}for(let o of e)await this.delete(o);for(;r>this.options.maxSize*1024*1024;){let o=this.findOldestKey();if(!o)break;let a=this.cache.get(o);await this.delete(o),r-=a.size;}this.stats.lastCleanup=t;}getStats(){return {...this.stats}}};c(ne,"MemoryCache");var A=ne;var Ae=Pe.env.NODE_ENV!=="production",L={CACHE:"cache",QUERY:"query",LOADER:"loader",SQLITE:"sqlite",RELATION:"relation",TRANSLATION:"translation",EXECUTOR:"executor",DEFAULT:"default"};function I(N){let t=Me(`contentrain:query:${N}`);return {debug:c((e,r)=>{Ae&&t({message:e,...r});},"debug"),info:c((e,r)=>{t({message:e,...r});},"info"),warn:c((e,r)=>{t({message:e,...r});},"warn"),error:c((e,r)=>{t({message:e,...r});},"error")}}c(I,"createLogger");var g={cache:I(L.CACHE),query:I(L.QUERY),loader:I(L.LOADER),sqlite:I(L.SQLITE),relation:I(L.RELATION),translation:I(L.TRANSLATION),executor:I(L.EXECUTOR),default:I(L.DEFAULT)};Ae&&Me.enable("contentrain:query:*");var R=g.loader,se=class se{constructor(t,e){this.contentDir=t;this.defaultLocale=e;this.modelConfigCache=new Map;this.contentCache=new Map;}getContentCacheKey(t,e){return `${t}:${e}`}async loadModelConfig(t){let e=this.modelConfigCache.get(t);if(e)return e;try{let r=join(this.contentDir,"models","metadata.json"),o=await readFile(r,"utf-8"),a=JSON.parse(o),s=a.find(u=>u.modelId===t);if(!s)throw R.error("Model metadata not found:",{modelId:t,metadataPath:r,availableModels:a.map(u=>u.modelId),context:{contentDir:this.contentDir,operation:"loadModelConfig"}}),new Error(`Model metadata not found for ${t}`);let n=join(this.contentDir,"models",`${t}.json`),l=await readFile(n,"utf-8"),d;try{d=JSON.parse(l);}catch(u){throw R.error("Failed to parse model fields:",{modelId:t,modelPath:n,error:u?.message||"Unknown error",content:l.slice(0,100),context:{contentDir:this.contentDir,operation:"loadModelConfig"}}),new Error(`Invalid JSON format in model fields: ${n}`)}let i={metadata:s,fields:d};return this.modelConfigCache.set(t,i),i}catch(r){throw R.error("Failed to load model config:",{modelId:t,error:r?.message||"Unknown error",stack:r?.stack,context:{contentDir:this.contentDir,operation:"loadModelConfig"}}),r}}async loadModelContent(t,e="default"){let r=this.getContentCacheKey(t,e),o=this.contentCache.get(r);if(o)return o;try{let a=await this.loadModelConfig(t),s,n=e;if(a.metadata.localization){if(e==="default"){if(!this.defaultLocale)throw R.error("Default locale required:",{modelId:t,locale:e,isLocalized:!0,context:{contentDir:this.contentDir,operation:"loadModelContent"}}),new Error(`Default locale is required for localized model "${t}"`);n=this.defaultLocale;}s=join(this.contentDir,t,`${n}.json`);}else s=join(this.contentDir,t,`${t}.json`);let l=await readFile(s,"utf-8"),d;try{if(d=JSON.parse(l),!Array.isArray(d))throw new TypeError("Content must be an array");let i={model:t,locale:a.metadata.localization?n:void 0,data:d};return this.contentCache.set(r,i),i}catch(i){throw R.error("JSON parse error:",{modelId:t,locale:n,contentPath:s,error:i?.message||"Unknown error",content:l.slice(0,100),context:{contentDir:this.contentDir,operation:"loadModelContent"}}),new Error(`Failed to load content: Invalid JSON format in ${s}`)}}catch(a){throw a.message.includes("Invalid JSON format")||R.error("Content load error:",{modelId:t,locale:e,error:a?.message||"Unknown error",stack:a?.stack,context:{contentDir:this.contentDir,operation:"loadModelContent"}}),a}}async loadAssets(){try{let t=join(this.contentDir,"assets.json"),e=await readFile(t,"utf-8");return JSON.parse(e)}catch(t){return R.warn("Assets file not found or cannot be read:",{error:t?.message||"Unknown error",path:join(this.contentDir,"assets.json"),context:{contentDir:this.contentDir,operation:"loadAssets"}}),[]}}async getModelLocales(t){try{if(!(await this.loadModelConfig(t)).metadata.localization)return ["default"];let r=join(this.contentDir,t),o=await readdir(r),a=o.filter(s=>s.endsWith(".json")).map(s=>s.replace(".json","")).filter(s=>s!==t);if(a.length===0)throw R.error("No locale files found:",{modelId:t,modelDir:r,files:o}),new Error(`No locale files found for localized model "${t}"`);return a}catch(e){throw R.error("Failed to read locales:",{modelId:t,error:e?.message||"Unknown error",stack:e?.stack,context:{contentDir:this.contentDir,operation:"getModelLocales"}}),e}}async clearCache(){this.modelConfigCache.clear(),this.contentCache.clear();}getCacheStats(){return {modelConfigs:this.modelConfigCache.size,contents:this.contentCache.size}}};c(se,"JSONContentManager");var $=se;var ke=g.default,ie=class ie extends Error{constructor(t,e){super(t),this.name="ContentrainError",this.code=e.code,this.operation=e.operation,this.severity=e.severity||"error",this.context=e.context,this.timestamp=e.timestamp||Date.now(),ke.error(t,{name:this.name,code:this.code,operation:this.operation,severity:this.severity,context:this.context,timestamp:this.timestamp,stack:this.stack}),Error.captureStackTrace(this,this.constructor);}toJSON(){return {name:this.name,message:this.message,code:this.code,operation:this.operation,severity:this.severity,context:this.context,timestamp:this.timestamp,stack:this.stack}}};c(ie,"ContentrainError");var O=ie;var le=class le extends O{constructor(t,e,r){let o={code:"DATABASE_ERROR",operation:e,severity:"error",context:r};super(t,o),this.name="DatabaseError";}};c(le,"DatabaseError");var T=le;var ce=class ce extends O{constructor(t,e,r){let o={code:"LOADER_ERROR",operation:e,severity:"error",context:r};super(t,o),this.name="LoaderError";}};c(ce,"LoaderError");var Y=ce;var de=class de extends O{constructor(t,e,r){let o={code:"RELATION_ERROR",operation:e,severity:"error",context:r};super(t,o),this.name="RelationError";}};c(de,"RelationError");var y=de,he=class he extends O{constructor(t,e,r){let o={code:"TRANSLATION_ERROR",operation:e,severity:"error",context:r};super(t,o),this.name="TranslationError";}};c(he,"TranslationError");var D=he;var ue=class ue extends O{constructor(t,e,r){let o={code:"QUERY_ERROR",operation:e,severity:"error",context:r};super(t,o),this.name="QueryBuilderError";}};c(ue,"QueryBuilderError");var m=ue,ge=class ge extends O{constructor(t,e,r){let o={code:"QUERY_ERROR",operation:e,severity:"error",context:r};super(t,o),this.name="QueryExecutorError";}};c(ge,"QueryExecutorError");var w=ge;var M=g.loader,pe=class pe extends ${constructor(e,r){super(e,r);this.relationCache=new Map;}async loadRelations(e){try{let r=this.relationCache.get(e);if(r)return r;let s=(await this.loadModelConfig(e)).fields.filter(n=>n.fieldType==="relation").map(n=>{let l=n.options,d=l?.reference?.form?.reference?.value;if(!d)throw M.error("Reference not found for relation field",{modelId:e,fieldName:n.name,fieldId:n.fieldId,options:l}),new y(`Reference not found for relation field: ${n.name}`,"load",{modelId:e,fieldName:n.name,fieldId:n.fieldId});return {model:d,type:n.componentId==="one-to-one"?"one-to-one":"one-to-many",foreignKey:n.fieldId}});return this.relationCache.set(e,s),s}catch(r){throw M.error("Failed to load relations",{modelId:e,error:r?.message,stack:r?.stack}),new y("Failed to load relations","load",{modelId:e,originalError:r?.message})}}async resolveRelation(e,r,o,a){try{let s=await this.loadRelations(e),n=s.find(i=>i.foreignKey===r);if(!n)throw M.error("Relation not found",{modelId:e,relationField:String(r),availableFields:s.map(i=>i.foreignKey)}),new y(`No relation found for field: ${String(r)}`,"resolve",{modelId:e,relationField:String(r)});let l=a||this.defaultLocale||"default",d=await this.loadModelContent(n.model,l);if(n.type==="one-to-one")return o.filter(h=>h[r]!==void 0&&h[r]!==null).map(h=>{let p=d.data.find(f=>f.ID===h[r]);if(!p)throw M.error("Related item not found",{modelId:e,relationField:String(r),sourceId:h.ID,targetId:h[r]}),new y(`Failed to resolve relation: No matching item found for ID ${String(h[r])}`,"resolve",{modelId:e,relationField:String(r),sourceId:h.ID,targetId:h[r]});return p});{let i=new Set(o.flatMap(h=>h[r]!==void 0&&h[r]!==null?Array.isArray(h[r])?h[r]:[h[r]]:[])),u=Array.from(i).map(h=>d.data.find(p=>p.ID===h)).filter(h=>h!==void 0);if(u.length!==i.size){let h=u.map(f=>f.ID),p=Array.from(i).filter(f=>!h.includes(f));throw M.error("Some related items not found",{modelId:e,relationField:String(r),missingIds:p,totalExpected:i.size,totalFound:u.length}),new y("Failed to resolve relation: Some related items not found","resolve",{modelId:e,relationField:String(r),missingIds:p})}return u}}catch(s){throw M.error("Failed to resolve relation",{modelId:e,relationField:String(r),error:s?.message,stack:s?.stack}),new y("Failed to resolve relation","resolve",{modelId:e,relationField:String(r),originalError:s?.message})}}async clearCache(){try{this.relationCache.clear(),await super.clearCache();}catch(e){throw M.error("Failed to clear relation cache",{error:e?.message,stack:e?.stack}),e}}};c(pe,"JSONRelationManager");var G=pe;var me=g.loader,fe=class fe extends ${constructor(e){super(e.contentDir,e.defaultLocale);this.options=e;this.modelConfigs=new Map;this.relationManager=new G(e.contentDir,e.defaultLocale),e.cache&&(this.cache=new A({maxSize:e.maxCacheSize||100,defaultTTL:e.ttl||60*1e3}));}getCacheKey(e){return `json:${e}`}getModelTTL(e){return this.options.modelTTL?.[e]||this.options.ttl||0}async load(e){let r=this.getCacheKey(e);if(this.options.cache&&this.cache){let o=await this.cache.get(r);if(o)return o}try{let o=this.modelConfigs.get(e);o||(o=await this.loadModelConfig(e),this.modelConfigs.set(e,o));let a=await this.getModelLocales(e),s={};if(o.metadata.localization)for(let d of a)try{let i=await this.loadModelContent(e,d);s[d]=i.data;}catch(i){if(me.warn("Failed to load content for locale:",{locale:d,modelId:e,error:i?.message||"Unknown error",stack:i?.stack}),d===this.options.defaultLocale)throw i}else {let d=await this.loadModelContent(e);s.default=d.data;}let n=await this.loadAssets(),l={model:o,content:s,assets:n};if(this.options.cache&&this.cache){let d=this.getModelTTL(e);await this.cache.set(r,l,d);}return l}catch(o){throw me.error("Load error:",{modelId:e,error:o?.message||"Unknown error",stack:o?.stack,context:{options:this.options,operation:"load"}}),o}}async resolveRelations(e,r,o){try{return await this.relationManager.resolveRelation(e,r,o,this.options.defaultLocale)}catch(a){throw me.error("Resolve relations error:",{modelId:e,relationKey:String(r),error:a?.message||"Unknown error",stack:a?.stack,context:{options:this.options,operation:"resolveRelations"}}),a}}async clearCache(){this.cache&&(await this.cache.clear(),this.modelConfigs.clear(),await this.relationManager.clearCache());}async refreshCache(e){let r=this.getCacheKey(e);this.cache&&(await this.cache.delete(r),this.modelConfigs.delete(e),await this.load(e));}getCacheStats(){let{modelConfigs:e,contents:r}=super.getCacheStats();return {modelConfigs:e,contents:r,cache:this.cache?.getStats()||{size:0,hits:0,misses:0,lastCleanup:0}}}};c(fe,"JSONLoader");var Q=fe;function C(N){return `tbl_${N.replace(/[-_]/g,"_")}`}c(C,"normalizeTableName");function x(N){return `tbl_${N.replace(/[-_]/g,"_")}_translations`}c(x,"normalizeTranslationTableName");var q=g.sqlite,ye=class ye{constructor(t){try{this.db=new ze(t,{readonly:!0,fileMustExist:!0}),this.setupDatabase();}catch(e){throw q.error("Failed to establish database connection",{databasePath:t,error:e?.message,code:e?.code}),new T("Failed to establish database connection","constructor",{databasePath:t,originalError:e?.message,errorCode:e?.code})}}setupDatabase(){try{this.db.pragma("journal_mode = WAL"),this.db.pragma("synchronous = NORMAL"),this.db.pragma("foreign_keys = ON");}catch(t){throw q.error("Failed to setup database configuration",{error:t?.message,code:t?.code}),new T("Failed to setup database configuration","constructor",{originalError:t?.message,errorCode:t?.code})}}async query(t,e=[]){try{return await new Promise((r,o)=>{try{let a=this.db.prepare(t).all(e);r(a);}catch(a){o(a);}})}catch(r){throw q.error("Failed to execute query",{sql:t,params:e,error:r?.message,code:r?.code}),new T("Failed to execute query","query",{sql:t,params:e,originalError:r?.message,errorCode:r?.code})}}async get(t,e=[]){try{return await new Promise((r,o)=>{try{let a=this.db.prepare(t).get(e);r(a);}catch(a){o(a);}})}catch(r){throw q.error("Failed to execute single row query",{sql:t,params:e,error:r?.message,code:r?.code}),new T("Failed to execute single row query","query",{sql:t,params:e,originalError:r?.message,errorCode:r?.code})}}async close(){try{return await new Promise((t,e)=>{try{this.db.close(),t();}catch(r){e(r);}})}catch(t){throw q.error("Failed to close database connection",{error:t?.message,code:t?.code}),new T("Failed to close database connection","constructor",{originalError:t?.message,errorCode:t?.code})}}};c(ye,"SQLiteConnection");var X=ye;var B=g.sqlite,we=class we{constructor(t){this.databasePath=t;try{this.connection=new X(t);}catch(e){throw B.error("Failed to initialize content manager",{databasePath:t,error:e?.message,code:e?.code}),new T("Failed to initialize content manager","initialize",{databasePath:t,originalError:e?.message,errorCode:e?.code})}}async query(t,e=[]){try{return await this.connection.query(t,e)}catch(r){throw B.error("Query error:",{sql:t,params:e,error:r?.message,code:r?.code}),r}}async findById(t,e){let r=C(t);try{return await this.connection.get(`SELECT * FROM ${r} WHERE id = ?`,[e])}catch(o){throw B.error("Failed to find record by ID",{model:t,id:e,error:o?.message,code:o?.code}),new T("Failed to find record by ID","read",{model:t,id:e,originalError:o?.message,errorCode:o?.code})}}async findAll(t,e={}){let r=C(t);try{let o=Object.keys(e).length?`WHERE ${Object.keys(e).map(s=>`${s} = ?`).join(" AND ")}`:"";return await this.connection.query(`SELECT * FROM ${r} ${o}`,Object.values(e))}catch(o){throw B.error("Failed to find records",{model:t,conditions:e,error:o?.message,code:o?.code}),new T("Failed to find records","read",{model:t,conditions:e,originalError:o?.message,errorCode:o?.code})}}async getTableCount(){try{return (await this.connection.query("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'")).length}catch(t){return B.error("Failed to get table count",{error:t?.message,code:t?.code}),0}}async close(){try{await this.connection.close();}catch(t){throw B.error("Failed to close content manager",{error:t?.message,code:t?.code}),new T("Failed to close content manager","disconnect",{originalError:t?.message,errorCode:t?.code})}}};c(we,"SQLiteContentManager");var b=we;var Te=g.sqlite,Ce=class Ce extends b{constructor(t){super(t);}async hasTranslations(t){try{let e=x(t);return !!await this.connection.get(`SELECT name FROM sqlite_master
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
`,[t])).map(o=>o.field_id)}catch(e){throw W.error("Failed to get relation fields",{model:t,error:e?.message,code:e?.code}),new y("Failed to get relation fields","read",{model:t,originalError:e?.message,errorCode:e?.code})}}};c(Se,"SQLiteRelationManager");var Z=Se;var Ee=class Ee{constructor(t){this.options=t;this.modelTTL={};this.modelConfigs=new Map;this.contentManager=new b(t.databasePath),this.relationManager=new Z(t.databasePath),this.translationManager=new P(t.databasePath),t.cache&&(this.cache=new A({maxSize:t.maxCacheSize,defaultTTL:typeof t.modelTTL=="number"?t.modelTTL:undefined})),t.modelTTL&&Object.assign(this.modelTTL,t.modelTTL);}async query(t,e=[]){return this.contentManager.query(t,e)}async findById(t,e){return this.contentManager.findById(t,e)}async findAll(t,e={}){return this.contentManager.findAll(t,e)}getCacheKey(t){return `sqlite:${t}`}getModelTTL(t){return this.modelTTL[t]||0}async loadWithTranslations(t,e){let r=await this.translationManager.hasTranslations(t),o=await this.getModelFields(t);if(!r)return {model:{metadata:{modelId:t,name:t,type:"SQLite",localization:false,isServerless:false},fields:o},content:{default:e,translations:{}}};let a=await this.translationManager.getLocales(t),s={};for(let n of a){let l=await this.translationManager.loadTranslations(t,e.map(d=>d.id),n);s[n]=Object.values(l);}return {model:{metadata:{modelId:t,name:t,type:"SQLite",localization:true,isServerless:false},fields:o},content:{default:e,translations:s}}}async getModelFields(t){let e=await this.translationManager.getMainColumns(t),r=await this.relationManager.getRelationFields(t),a=await this.translationManager.hasTranslations(t)?await this.translationManager.getTranslationColumns(t):[];return [...this.mapSystemFields(e),...this.mapRelationFields(r),...this.mapTranslationFields(a)]}mapSystemFields(t){return t.map(e=>({name:e,fieldId:e,type:this.getFieldType(e)}))}mapRelationFields(t){return t.map(e=>({name:e,fieldId:e,type:"relation"}))}mapTranslationFields(t){return t.map(e=>({name:e,fieldId:e,type:this.getFieldType(e)}))}getFieldType(t){return t==="id"||t.endsWith("_id")?"string":t==="created_at"||t==="updated_at"?"date":t==="status"?"string":t==="field_order"?"number":"string"}async resolveRelations(t,e,r){let o=await this.relationManager.loadRelations(t,r.map(a=>a.id),e);return this.relationManager.loadRelatedContent(o,this.options.defaultLocale)}async load(t){let e=this.getCacheKey(t);if(this.options.cache&&this.cache){let r=await this.cache.get(e);if(r)return r}try{let r=await this.findAll(t),o=await this.loadWithTranslations(t,r);if(this.options.cache&&this.cache){let a=this.getModelTTL(t);await this.cache.set(e,o,a);}return o}catch(r){throw new Y("Failed to load content","load",{model:t,originalError:r?.message,errorCode:r?.code})}}async clearCache(){this.cache&&await this.cache.clear();}async refreshCache(t){let e=this.getCacheKey(t);this.cache&&(await this.cache.delete(e),await this.load(t));}async getCacheStats(){return {modelConfigs:this.modelConfigs.size,contents:await this.contentManager.getTableCount(),cache:this.cache?.getStats()||undefined}}async close(){await this.contentManager.close(),await this.relationManager.close(),await this.translationManager.close();}};c(Ee,"SQLiteLoader");var J=Ee;var Re=g.query,Oe=class Oe{constructor(t){this.model=t;this.filters=[];this.includes={};this.sorting=[];this.pagination={};this.options={};}where(t,e,r){try{return this.filters.push({field:String(t),operator:e,value:r}),this}catch(o){throw Re.error("Failed to add filter",{field:String(t),operator:e,value:r,error:o?.message}),new m("Failed to add filter","filter",{field:String(t),operator:e,value:r,originalError:o?.message})}}orderBy(t,e="asc"){try{return this.sorting.push({field:String(t),direction:e}),this}catch(r){throw Re.error("Failed to add sort",{field:String(t),direction:e,error:r?.message}),new m("Failed to add sort","sort",{field:String(t),direction:e,originalError:r?.message})}}limit(t){if(t<0)throw new m("Limit cannot be negative","validate",{count:t});return this.pagination.limit=t,this}offset(t){if(t<0)throw new m("Offset cannot be negative","validate",{count:t});return this.pagination.offset=t,this}cache(t){try{return this.options.cache=!0,t&&(this.options.ttl=t),this}catch(e){throw Re.error("Failed to set cache",{ttl:t,error:e?.message}),new m("Failed to set cache","cache",{ttl:t,originalError:e?.message})}}noCache(){return this.options.cache=false,this}bypassCache(){return this.options.cache=false,this.options.ttl=0,this}toJSON(){return {model:this.model,filters:this.filters,includes:this.includes,sorting:this.sorting,pagination:this.pagination,options:this.options}}};c(Oe,"BaseQueryBuilder");var k=Oe;var z=g.executor,De=class De{applyStringOperation(t,e,r){try{switch(e){case "eq":return t===r;case "ne":return t!==r;case "contains":return t.toLowerCase().includes(r.toLowerCase());case "startsWith":return t.toLowerCase().startsWith(r.toLowerCase());case "endsWith":return t.toLowerCase().endsWith(r.toLowerCase());default:return e}}catch(o){throw z.error("Failed to apply string operation",{operator:e,error:o?.message}),new w("Failed to apply string operation","filter",{operator:e,originalError:o?.message})}}async resolveIncludes(t,e,r,o){try{let a=[...e];for(let[s,n]of Object.entries(r)){let l=await this.resolveRelation(t,s,a,o);n.include&&l.length&&await this.resolveIncludes(s,l,n.include,o),a.forEach(d=>{let i=d[s],u=l.filter(h=>Array.isArray(i)?i.includes(h.ID):h.ID===i);d._relations||(d._relations={}),d._relations[s]=Array.isArray(i)?u:u[0];});}return a}catch(a){throw z.error("Failed to resolve includes",{model:t,error:a?.message,errorCode:a?.code}),new w("Failed to resolve includes","resolve",{model:t,originalError:a?.message,errorCode:a?.code})}}applyFilters(t,e=[]){try{return t.filter(o=>e.every(({field:a,operator:s,value:n})=>{let l=o[a];return this.compareValues(l,s,n)}))}catch(r){throw z.error("Failed to apply filters",{error:r?.message,errorCode:r?.code}),new w("Failed to apply filters","filter",{originalError:r?.message,errorCode:r?.code})}}applySorting(t,e=[]){try{return [...t].sort((o,a)=>{for(let{field:s,direction:n}of e){let l=o[s],d=a[s];if(l===d)continue;let i=l<d?-1:1;return n==="asc"?i:-i}return 0})}catch(r){throw z.error("Failed to apply sorting",{error:r?.message,errorCode:r?.code}),new w("Failed to apply sorting","sort",{originalError:r?.message,errorCode:r?.code})}}applyPagination(t,e,r=0){try{return e?t.slice(r,r+e):t.slice(r)}catch(o){throw z.error("Failed to apply pagination",{limit:e,offset:r,error:o?.message,errorCode:o?.code}),new w("Failed to apply pagination","paginate",{limit:e,offset:r,originalError:o?.message,errorCode:o?.code})}}compareValues(t,e,r){try{switch(e){case "eq":return t===r;case "ne":return t!==r;case "gt":return t>r;case "gte":return t>=r;case "lt":return t<r;case "lte":return t<=r;case "in":return Array.isArray(r)?r.includes(t):!1;case "nin":return Array.isArray(r)?!r.includes(t):!1;case "contains":return typeof t=="string"&&typeof r=="string"?this.applyStringOperation(t,"contains",r):!1;case "startsWith":return typeof t=="string"&&typeof r=="string"?this.applyStringOperation(t,"startsWith",r):!1;case "endsWith":return typeof t=="string"&&typeof r=="string"?this.applyStringOperation(t,"endsWith",r):!1;default:return !1}}catch(o){throw z.error("Failed to compare values",{operator:e,error:o?.message}),new w("Failed to compare values","filter",{operator:e,originalError:o?.message})}}getPaginationInfo(t,e){if(t?.limit)return {limit:t.limit,offset:t.offset||0,hasMore:(t.offset||0)+t.limit<e}}};c(De,"BaseQueryExecutor");var j=De;var V=g.query,Le=class Le extends j{constructor(e){super();this.loader=e;}async resolveRelation(e,r,o,a){try{return await this.loader.resolveRelations(e,r,o)}catch(s){throw V.error("Failed to resolve relation",{model:e,field:r,error:s?.message,stack:s?.stack}),new w("Failed to resolve relation","resolve",{model:e,field:r,originalError:s?.message})}}async execute(e){try{let r=await this.loader.load(e.model),o=e.options?.locale||"default",a=this.getContent(r,o);e.filters?.length&&(a=this.applyFilters(a,e.filters)),e.includes&&Object.keys(e.includes).length&&(a=await this.resolveIncludes(e.model,a,e.includes,e.options||{})),e.sorting?.length&&(a=this.applySorting(a,e.sorting));let s=a.length;return e.pagination&&(a=this.applyPagination(a,e.pagination.limit,e.pagination.offset)),{data:a,total:s,pagination:this.getPaginationInfo(e.pagination,s)}}catch(r){throw V.error("Query execution failed",{model:e.model,error:r?.message,stack:r?.stack}),new w("Failed to execute query","execute",{model:e.model,originalError:r?.message})}}getContent(e,r){if(e.model.metadata.localization){let o=e.content[r];return o||(V.warn("Content not found for locale, falling back to en",{locale:r}),e.content.en||[])}return e.content.en||[]}applySorting(e,r=[]){try{return [...e].sort((a,s)=>{for(let{field:n,direction:l}of r){if(!(n in a)||!(n in s))throw new w(`Invalid sort field: ${n}`,"sort",{field:n,availableFields:Object.keys(a)});let d=a[n],i=s[n];if(d===i)continue;let u=d<i?-1:1;return l==="asc"?u:-u}return 0})}catch(o){throw V.error("Failed to apply sorting",{error:o?.message,stack:o?.stack,operation:"sort"}),o}}};c(Le,"JSONQueryExecutor");var ee=Le;var E=g.query,Ie=class Ie extends k{constructor(t,e){super(t);try{this.executor=new ee(e),E.info("Query builder initialized successfully");}catch(r){throw E.error("Failed to initialize query builder",{model:t,error:r?.message,stack:r?.stack}),r}}where(t,e,r){try{return this.validateField(t),this.validateOperator(e),this.filters.push({field:String(t),operator:e,value:r}),this}catch(o){throw E.error("Failed to add filter",{field:String(t),operator:e,value:r,error:o?.message}),o}}include(t){try{return typeof t=="string"?this.includes[t]={}:t.forEach(e=>{this.includes[e]={};}),this}catch(e){throw E.error("Failed to add relation",{relations:t,error:e?.message,errorCode:e?.code}),new m("Failed to add relation","include",{relations:t,originalError:e?.message,errorCode:e?.code})}}orderBy(t,e="asc"){try{return this.validateField(t),this.sorting.push({field:String(t),direction:e}),this}catch(r){throw E.error("Failed to add sort",{field:String(t),direction:e,error:r?.message}),r}}limit(t){try{if(t<0)throw new m("Limit cannot be negative","validate",{count:t});return this.pagination.limit=t,this}catch(e){throw E.error("Failed to set limit",{count:t,error:e?.message}),e}}offset(t){try{if(t<0)throw new m("Offset cannot be negative","validate",{count:t});return this.pagination.offset=t,this}catch(e){throw E.error("Failed to set offset",{count:t,error:e?.message}),e}}locale(t){try{if(!t)throw new m("Locale code cannot be empty","validate",{code:t});return this.options.locale=t,this}catch(e){throw E.error("Failed to set locale",{code:t,error:e?.message}),e}}validateField(t){if(!t)throw new m("Field cannot be empty","validate",{field:t});if(typeof t!="string")throw new m("Field must be a string","validate",{field:t,type:typeof t})}validateOperator(t){if(typeof t!="string")throw new m("Operator must be a string","validate",{operator:t,type:typeof t});let e=["eq","ne","gt","gte","lt","lte","in","nin","contains","startsWith","endsWith"];if(!e.includes(t))throw new m(`Invalid operator: ${t}`,"validate",{operator:t,validOperators:e})}async get(){try{return await this.executor.execute({model:this.model,filters:this.filters,includes:this.includes,sorting:this.sorting,pagination:this.pagination,options:this.options})}catch(t){throw E.error("Failed to execute query",{model:this.model,error:t?.message,stack:t?.stack}),t}}async first(){try{return (await this.limit(1).get()).data[0]||null}catch(t){throw E.error("Failed to get first record",{model:this.model,error:t?.message}),t}}async count(){try{return (await this.get()).total}catch(t){throw E.error("Failed to get record count",{model:this.model,error:t?.message}),t}}};c(Ie,"JSONQueryBuilder");var te=Ie;var F=g.query,xe=class xe extends k{constructor(e,r){super(e);this.model=e;this.executor=r;this.options={};}setLoader(e){return this.executor.setLoader(e),this}include(e){try{return typeof e=="string"?this.options.includes=[{relation:e}]:Array.isArray(e)?this.options.includes=e.map(r=>typeof r=="string"?{relation:r}:r):this.options.includes=[e],this}catch(r){throw F.error("Failed to add relation",{relations:e,error:r?.message}),new m("Failed to add relation","include",{relations:e,originalError:r?.message})}}locale(e){try{return this.options.locale=e,this}catch(r){throw F.error("Failed to set locale",{code:e,error:r?.message}),new m("Failed to set locale","localize",{code:e,originalError:r?.message})}}orderBy(e,r="asc"){try{return this.sorting.push({field:e,direction:r}),this}catch(o){throw F.error("Failed to add sorting",{field:e,direction:r,error:o?.message}),new m("Failed to add sorting","sort",{field:e,direction:r,originalError:o?.message})}}limit(e){try{if(e<0)throw new Error("Limit count must be positive");return this.pagination.limit=e,this}catch(r){throw F.error("Failed to set limit",{count:e,error:r?.message}),new m("Failed to set limit","paginate",{count:e,originalError:r?.message})}}offset(e){try{if(e<0)throw new Error("Offset count must be positive");return this.pagination.offset=e,this}catch(r){throw F.error("Failed to set offset",{count:e,error:r?.message}),new m("Failed to set offset","paginate",{count:e,originalError:r?.message})}}async get(){try{return await this.executor.execute({model:this.model,filters:this.filters,sorting:this.sorting,pagination:this.pagination,options:this.options})}catch(e){throw F.error("Failed to execute query",{model:this.model,error:e?.message}),new m("Failed to execute query","execute",{model:this.model,originalError:e?.message})}}async first(){try{return (await this.limit(1).get()).data[0]||null}catch(e){throw F.error("Failed to get first record",{model:this.model,error:e?.message}),new m("Failed to get first record","query",{model:this.model,originalError:e?.message})}}async count(){try{return (await this.get()).total}catch(e){throw F.error("Failed to get record count",{model:this.model,error:e?.message}),new m("Failed to get record count","query",{model:this.model,originalError:e?.message})}}};c(xe,"SQLiteQueryBuilder");var re=xe;var _=g.query,be=class be extends j{constructor(e){super();this.loader=e;this.query=null;this.mainColumns=[];this.translationColumns=[];}setLoader(e){this.loader=e;}async resolveRelation(e,r,o,a){try{let n=(await this.loader.relationManager.getRelationTypes(e))[r];if(!n)return _.warn("No relation type found",{model:e,field:r}),o;let l=await this.loader.relationManager.loadRelations(e,o.map(u=>u.id),r);if(!l.length)return o;let d=await this.loader.relationManager.loadRelatedContent(l,a?.locale),i={};return l.forEach(u=>{i[u.source_id]||(i[u.source_id]=[]);let h=d.find(p=>p.id===u.target_id);h&&i[u.source_id].push(h);}),o.forEach(u=>{u._relations||(u._relations={});let h=i[u.id]||[];if(u._relations[r]=n==="one-to-many"?h:h[0],h.length>0){let p=h[0];Object.keys(p).forEach(f=>{f!=="id"&&f!=="created_at"&&f!=="updated_at"&&f!=="status"&&(u[`${r}_${f}`]=p[f]);});}}),o}catch(s){if(s instanceof y)return _.warn("Relation not found",{model:e,field:r,error:s.message}),o;throw _.error("Failed to resolve relation",{error:s}),new w("Failed to resolve relation","resolve",{model:e,field:r,originalError:s?.message})}}async execute(e){try{let r=await this.buildSQLQuery(e),o=this.buildSQL(r),a=await this.loader.query(o,r.parameters);if(e.options?.includes?.length)for(let n of e.options.includes){let l=await this.loader.relationManager.loadRelations(e.model,a.map(p=>p.id),n.relation);if(!l.length)continue;let i=l[0].type==="one-to-many",u=await this.loader.relationManager.loadRelatedContent(l,n.locale||e.options?.locale||void 0),h={};l.forEach(p=>{h[p.source_id]||(h[p.source_id]=[]);let f=u.find(ae=>ae.id===p.target_id);f&&h[p.source_id].push(f);}),a.forEach(p=>{p._relations||(p._relations={});let f=h[p.id]||[];p._relations[n.relation]=i?f:f[0];});}let s=await this.getTotal(r);return {data:a,total:s,pagination:this.getPaginationInfo(e.pagination,s)}}catch(r){throw _.error("Query execution failed",{operation:"execute",model:e.model,error:r?.message,stack:r?.stack}),new w("Failed to execute query","execute",{model:e.model,originalError:r?.message})}}async buildSQLQuery(e){let r=C(e.model),o={select:[],from:r,joins:[],where:[],orderBy:e.sorting||[],parameters:[],pagination:e.pagination,options:e.options};if(this.query=o,await this.loader.translationManager.hasTranslations(e.model)?(this.mainColumns=await this.loader.translationManager.getMainColumns(e.model),this.translationColumns=await this.loader.translationManager.getTranslationColumns(e.model),o.select.push(...this.mainColumns.map(s=>`m.${s}`)),e.options?.locale&&(e.sorting?.some(n=>this.translationColumns.includes(n.field))||e.options?.translations!==false)&&await this.addTranslationJoin(o,e.model,e.options.locale)):(this.mainColumns=await this.loader.translationManager.getAllColumns(e.model),this.translationColumns=[],o.select.push(...this.mainColumns.map(s=>`m.${s}`))),e.options?.includes?.length){let s=await this.loader.relationManager.getRelationTypes(e.model);for(let n of e.options.includes){if(!s[n.relation]){_.warn("No relation type found",{model:e.model,field:n.relation});continue}let d=await this.loader.relationManager.loadRelations(e.model,[],n.relation);if(d.length>0){let i=d[0],u=C(i.target_model),h=`r_${n.relation}`;o.joins.push({type:"LEFT",table:u,alias:h,conditions:[`m.${n.relation}_id = ${h}.id`]});let p=await this.loader.translationManager.getMainColumns(i.target_model);if(o.select.push(...p.map(f=>`${h}.${f} as ${n.relation}_${f}`)),n.locale&&await this.loader.translationManager.hasTranslations(i.target_model)){let ae=x(i.target_model),Ne=await this.loader.translationManager.getTranslationColumns(i.target_model);if(Ne.length>0){let K=`t_${n.relation}`;o.joins.push({type:"LEFT",table:ae,alias:K,conditions:[`${h}.id = ${K}.id`,`${K}.locale = ?`]}),o.select.push(...Ne.map(ve=>`${K}.${ve} as ${n.relation}_${ve}`)),o.parameters.push(n.locale);}}}}}return e.filters?.length&&(o.where=e.filters.map(s=>{let n=s.field.endsWith("_id")?s.field:`${s.field}_id`;return {...s,field:this.mainColumns.includes(n)?n:s.field}}),o.where.forEach(s=>{s.value!==null&&(Array.isArray(s.value)?o.parameters.push(...s.value):s.operator==="startsWith"?o.parameters.push(`${String(s.value)}%`):s.operator==="contains"?o.parameters.push(`%${String(s.value)}%`):s.operator==="endsWith"?o.parameters.push(`%${String(s.value)}`):o.parameters.push(s.value));})),o}buildSQL(e){let r=[];if(e.pagination?.limit){if(r.push("WITH paginated_base AS ("),r.push(" SELECT DISTINCT m.id"),r.push(` FROM ${e.from} AS m`),e.joins.length&&r.push(e.joins.map(o=>`${o.type} JOIN ${o.table} AS ${o.alias} ON ${o.conditions.join(" AND ")}`).join(" ")),e.options?.locale&&e.parameters.push(e.options.locale),e.where.length){let o=e.where.map(a=>this.buildConditionWithoutParams(a));r.push(` WHERE ${o.join(" AND ")}`);}if(e.orderBy.length){let o=e.orderBy.map(a=>{if(["id","created_at","updated_at","status"].includes(a.field))return `m.${a.field} ${a.direction.toUpperCase()}`;let n=this.translationColumns?.includes(a.field),l=!!e.options?.locale;return n&&l?`COALESCE(t.${a.field}, '') COLLATE NOCASE ${a.direction.toUpperCase()}`:`COALESCE(m.${a.field}, '') COLLATE NOCASE ${a.direction.toUpperCase()}`});r.push(` ORDER BY ${o.join(", ")}`);}r.push(` LIMIT ${e.pagination.limit}`),e.pagination.offset&&r.push(` OFFSET ${e.pagination.offset}`),r.push(")"),r.push(`SELECT ${e.select.join(", ")}`),r.push("FROM paginated_base pb"),r.push(`JOIN ${e.from} AS m ON pb.id = m.id`),e.joins.length&&r.push(e.joins.map(o=>`${o.type} JOIN ${o.table} AS ${o.alias} ON ${o.conditions.join(" AND ")}`).join(" "));}else {if(r.push(`SELECT ${e.select.join(", ")}`),r.push(`FROM ${e.from} AS m`),e.joins.length&&r.push(e.joins.map(o=>`${o.type} JOIN ${o.table} AS ${o.alias} ON ${o.conditions.join(" AND ")}`).join(" ")),e.where.length){let o=e.where.map(a=>this.buildConditionWithoutParams(a));r.push(`WHERE ${o.join(" AND ")}`);}if(e.orderBy.length){let o=e.orderBy.map(a=>{if(["id","created_at","updated_at","status"].includes(a.field))return `m.${a.field} ${a.direction.toUpperCase()}`;let n=this.translationColumns?.includes(a.field),l=!!e.options?.locale;return n&&l?`COALESCE(t.${a.field}, '') COLLATE NOCASE ${a.direction.toUpperCase()}`:`COALESCE(m.${a.field}, '') COLLATE NOCASE ${a.direction.toUpperCase()}`});r.push(`ORDER BY ${o.join(", ")}`);}}return r.join(`
|
|
30
|
-
|
|
1
|
+
//#region src/runtime/query.ts
|
|
2
|
+
var QueryBuilder = class {
|
|
3
|
+
_data;
|
|
4
|
+
_locale = null;
|
|
5
|
+
_filters = [];
|
|
6
|
+
_sortField = null;
|
|
7
|
+
_sortOrder = "asc";
|
|
8
|
+
_limit = null;
|
|
9
|
+
_offset = 0;
|
|
10
|
+
_includes = [];
|
|
11
|
+
_relationMeta;
|
|
12
|
+
_resolver;
|
|
13
|
+
_defaultLocale;
|
|
14
|
+
constructor(data, relationMeta, resolver, defaultLocale) {
|
|
15
|
+
this._data = data;
|
|
16
|
+
this._relationMeta = relationMeta ?? {};
|
|
17
|
+
this._resolver = resolver ?? null;
|
|
18
|
+
this._defaultLocale = defaultLocale ?? null;
|
|
19
|
+
}
|
|
20
|
+
locale(lang) {
|
|
21
|
+
this._locale = lang;
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
where(field, value) {
|
|
25
|
+
this._filters.push((item) => {
|
|
26
|
+
const v = item[field];
|
|
27
|
+
if (Array.isArray(v)) return v.includes(value);
|
|
28
|
+
return v === value;
|
|
29
|
+
});
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
sort(field, order = "asc") {
|
|
33
|
+
this._sortField = field;
|
|
34
|
+
this._sortOrder = order;
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
limit(n) {
|
|
38
|
+
this._limit = n;
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
offset(n) {
|
|
42
|
+
this._offset = n;
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
include(...fields) {
|
|
46
|
+
this._includes.push(...fields);
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
all() {
|
|
50
|
+
let items = this._resolveData();
|
|
51
|
+
for (const filter of this._filters) items = items.filter(filter);
|
|
52
|
+
if (this._sortField) {
|
|
53
|
+
const field = this._sortField;
|
|
54
|
+
const dir = this._sortOrder === "asc" ? 1 : -1;
|
|
55
|
+
items.sort((a, b) => {
|
|
56
|
+
const va = a[field];
|
|
57
|
+
const vb = b[field];
|
|
58
|
+
if (va == null && vb == null) return 0;
|
|
59
|
+
if (va == null) return dir;
|
|
60
|
+
if (vb == null) return -dir;
|
|
61
|
+
if (va < vb) return -dir;
|
|
62
|
+
if (va > vb) return dir;
|
|
63
|
+
return 0;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (this._offset > 0 || this._limit !== null) {
|
|
67
|
+
const end = this._limit !== null ? this._offset + this._limit : void 0;
|
|
68
|
+
items = items.slice(this._offset, end);
|
|
69
|
+
}
|
|
70
|
+
if (this._includes.length > 0 && this._resolver) items = items.map((item) => this._resolveIncludes(item));
|
|
71
|
+
return items;
|
|
72
|
+
}
|
|
73
|
+
first() {
|
|
74
|
+
return this.all()[0];
|
|
75
|
+
}
|
|
76
|
+
_resolveData() {
|
|
77
|
+
if (this._locale) return [...this._data.get(this._locale) ?? []];
|
|
78
|
+
if (this._defaultLocale) {
|
|
79
|
+
const defaultData = this._data.get(this._defaultLocale);
|
|
80
|
+
if (defaultData) return [...defaultData];
|
|
81
|
+
}
|
|
82
|
+
const firstKey = this._data.keys().next().value;
|
|
83
|
+
if (firstKey !== void 0) return [...this._data.get(firstKey) ?? []];
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
_resolveIncludes(item) {
|
|
87
|
+
const resolved = { ...item };
|
|
88
|
+
const src = item;
|
|
89
|
+
const dst = resolved;
|
|
90
|
+
for (const field of this._includes) {
|
|
91
|
+
const meta = this._relationMeta[field];
|
|
92
|
+
if (!meta) continue;
|
|
93
|
+
const targets = Array.isArray(meta.target) ? meta.target : [meta.target];
|
|
94
|
+
if (meta.multi) {
|
|
95
|
+
const ids = src[field];
|
|
96
|
+
if (Array.isArray(ids)) dst[field] = ids.map((id) => {
|
|
97
|
+
if (typeof id === "string") return this._resolveId(targets, id) ?? id;
|
|
98
|
+
if (typeof id === "object" && id !== null && "model" in id && "ref" in id) {
|
|
99
|
+
const polyObj = id;
|
|
100
|
+
return this._resolveId([polyObj.model], polyObj.ref) ?? id;
|
|
101
|
+
}
|
|
102
|
+
return id;
|
|
103
|
+
});
|
|
104
|
+
} else {
|
|
105
|
+
const id = src[field];
|
|
106
|
+
if (typeof id === "string") dst[field] = this._resolveId(targets, id) ?? id;
|
|
107
|
+
else if (typeof id === "object" && id !== null && "model" in id && "ref" in id) {
|
|
108
|
+
const polyObj = id;
|
|
109
|
+
dst[field] = this._resolveId([polyObj.model], polyObj.ref) ?? id;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return resolved;
|
|
114
|
+
}
|
|
115
|
+
_resolveId(targets, id) {
|
|
116
|
+
for (const target of targets) {
|
|
117
|
+
const result = this._resolver(target, id, this._locale);
|
|
118
|
+
if (result) return result;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region src/runtime/singleton.ts
|
|
124
|
+
var SingletonAccessor = class {
|
|
125
|
+
_data;
|
|
126
|
+
_locale = null;
|
|
127
|
+
_defaultLocale;
|
|
128
|
+
_includes = [];
|
|
129
|
+
_relationMeta = {};
|
|
130
|
+
_resolveEntry;
|
|
131
|
+
constructor(data, defaultLocale, relationMeta, resolveEntry) {
|
|
132
|
+
this._data = data;
|
|
133
|
+
this._defaultLocale = defaultLocale ?? null;
|
|
134
|
+
if (relationMeta) this._relationMeta = relationMeta;
|
|
135
|
+
this._resolveEntry = resolveEntry;
|
|
136
|
+
}
|
|
137
|
+
locale(lang) {
|
|
138
|
+
this._locale = lang;
|
|
139
|
+
return this;
|
|
140
|
+
}
|
|
141
|
+
include(...fields) {
|
|
142
|
+
this._includes.push(...fields);
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
get() {
|
|
146
|
+
if (this._locale) {
|
|
147
|
+
const d = this._data.get(this._locale);
|
|
148
|
+
if (!d) throw new Error(`No data for locale "${this._locale}"`);
|
|
149
|
+
if (this._includes.length > 0) return this._resolveIncludes(d, this._locale);
|
|
150
|
+
return d;
|
|
151
|
+
}
|
|
152
|
+
const locale = this._defaultLocale ?? void 0;
|
|
153
|
+
let data;
|
|
154
|
+
if (locale) data = this._data.get(locale);
|
|
155
|
+
if (!data) {
|
|
156
|
+
const firstKey = this._data.keys().next().value;
|
|
157
|
+
if (firstKey !== void 0) data = this._data.get(firstKey);
|
|
158
|
+
}
|
|
159
|
+
if (!data) throw new Error("No data available");
|
|
160
|
+
if (this._includes.length > 0) return this._resolveIncludes(data, locale ?? "en");
|
|
161
|
+
return data;
|
|
162
|
+
}
|
|
163
|
+
_resolveIncludes(item, locale) {
|
|
164
|
+
if (!this._resolveEntry || this._includes.length === 0) return item;
|
|
165
|
+
const resolved = { ...item };
|
|
166
|
+
const src = item;
|
|
167
|
+
const dst = resolved;
|
|
168
|
+
for (const field of this._includes) {
|
|
169
|
+
const meta = this._relationMeta[field];
|
|
170
|
+
if (!meta) continue;
|
|
171
|
+
const targets = Array.isArray(meta.target) ? meta.target : [meta.target];
|
|
172
|
+
if (meta.multi) {
|
|
173
|
+
const ids = src[field];
|
|
174
|
+
if (Array.isArray(ids)) dst[field] = ids.map((id) => {
|
|
175
|
+
if (typeof id === "string") return this._resolveId(targets, id, locale) ?? id;
|
|
176
|
+
if (typeof id === "object" && id !== null && "model" in id && "ref" in id) {
|
|
177
|
+
const polyObj = id;
|
|
178
|
+
return this._resolveId([polyObj.model], polyObj.ref, locale) ?? id;
|
|
179
|
+
}
|
|
180
|
+
return id;
|
|
181
|
+
});
|
|
182
|
+
} else {
|
|
183
|
+
const id = src[field];
|
|
184
|
+
if (typeof id === "string") dst[field] = this._resolveId(targets, id, locale) ?? id;
|
|
185
|
+
else if (typeof id === "object" && id !== null && "model" in id && "ref" in id) {
|
|
186
|
+
const polyObj = id;
|
|
187
|
+
dst[field] = this._resolveId([polyObj.model], polyObj.ref, locale) ?? id;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return resolved;
|
|
192
|
+
}
|
|
193
|
+
_resolveId(targets, id, locale) {
|
|
194
|
+
if (!this._resolveEntry) return void 0;
|
|
195
|
+
for (const model of targets) {
|
|
196
|
+
const result = this._resolveEntry(model, id, locale);
|
|
197
|
+
if (result) return result;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/runtime/dictionary.ts
|
|
203
|
+
var DictionaryAccessor = class {
|
|
204
|
+
_data;
|
|
205
|
+
_locale = null;
|
|
206
|
+
_defaultLocale;
|
|
207
|
+
constructor(data, defaultLocale) {
|
|
208
|
+
this._data = data;
|
|
209
|
+
this._defaultLocale = defaultLocale ?? null;
|
|
210
|
+
}
|
|
211
|
+
locale(lang) {
|
|
212
|
+
this._locale = lang;
|
|
213
|
+
return this;
|
|
214
|
+
}
|
|
215
|
+
get(key, params) {
|
|
216
|
+
const dict = this._resolveData();
|
|
217
|
+
if (key === void 0) return dict;
|
|
218
|
+
const value = dict[key];
|
|
219
|
+
if (value === void 0) return void 0;
|
|
220
|
+
if (params) return interpolate(value, params);
|
|
221
|
+
return value;
|
|
222
|
+
}
|
|
223
|
+
_resolveData() {
|
|
224
|
+
if (this._locale) return this._data.get(this._locale) ?? {};
|
|
225
|
+
if (this._defaultLocale) {
|
|
226
|
+
const d = this._data.get(this._defaultLocale);
|
|
227
|
+
if (d) return d;
|
|
228
|
+
}
|
|
229
|
+
const firstKey = this._data.keys().next().value;
|
|
230
|
+
if (firstKey !== void 0) return this._data.get(firstKey) ?? {};
|
|
231
|
+
return {};
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
function interpolate(template, params) {
|
|
235
|
+
return template.replace(/\{(\w+)\}/g, (match, key) => {
|
|
236
|
+
const val = params[key];
|
|
237
|
+
return val !== void 0 ? String(val) : match;
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
//#endregion
|
|
241
|
+
//#region src/runtime/document.ts
|
|
242
|
+
var DocumentQuery = class {
|
|
243
|
+
_data;
|
|
244
|
+
_locale = null;
|
|
245
|
+
_filters = [];
|
|
246
|
+
_includes = [];
|
|
247
|
+
_relationMeta;
|
|
248
|
+
_resolver;
|
|
249
|
+
_defaultLocale;
|
|
250
|
+
constructor(data, relationMeta, resolver, defaultLocale) {
|
|
251
|
+
this._data = data;
|
|
252
|
+
this._relationMeta = relationMeta ?? {};
|
|
253
|
+
this._resolver = resolver ?? null;
|
|
254
|
+
this._defaultLocale = defaultLocale ?? null;
|
|
255
|
+
}
|
|
256
|
+
locale(lang) {
|
|
257
|
+
this._locale = lang;
|
|
258
|
+
return this;
|
|
259
|
+
}
|
|
260
|
+
where(field, value) {
|
|
261
|
+
this._filters.push((item) => item[field] === value);
|
|
262
|
+
return this;
|
|
263
|
+
}
|
|
264
|
+
include(...fields) {
|
|
265
|
+
this._includes.push(...fields);
|
|
266
|
+
return this;
|
|
267
|
+
}
|
|
268
|
+
bySlug(slug) {
|
|
269
|
+
const item = this._resolveData().find((i) => i["slug"] === slug);
|
|
270
|
+
if (item && this._includes.length > 0 && this._resolver) return this._resolveIncludes(item);
|
|
271
|
+
return item;
|
|
272
|
+
}
|
|
273
|
+
all() {
|
|
274
|
+
let items = this._resolveData();
|
|
275
|
+
for (const filter of this._filters) items = items.filter(filter);
|
|
276
|
+
if (this._includes.length > 0 && this._resolver) items = items.map((item) => this._resolveIncludes(item));
|
|
277
|
+
return items;
|
|
278
|
+
}
|
|
279
|
+
first() {
|
|
280
|
+
return this.all()[0];
|
|
281
|
+
}
|
|
282
|
+
_resolveData() {
|
|
283
|
+
if (this._locale) return [...this._data.get(this._locale) ?? []];
|
|
284
|
+
if (this._defaultLocale) {
|
|
285
|
+
const defaultData = this._data.get(this._defaultLocale);
|
|
286
|
+
if (defaultData) return [...defaultData];
|
|
287
|
+
}
|
|
288
|
+
const firstKey = this._data.keys().next().value;
|
|
289
|
+
if (firstKey !== void 0) return [...this._data.get(firstKey) ?? []];
|
|
290
|
+
return [];
|
|
291
|
+
}
|
|
292
|
+
_resolveIncludes(item) {
|
|
293
|
+
const resolved = { ...item };
|
|
294
|
+
const src = item;
|
|
295
|
+
const dst = resolved;
|
|
296
|
+
for (const field of this._includes) {
|
|
297
|
+
const meta = this._relationMeta[field];
|
|
298
|
+
if (!meta) continue;
|
|
299
|
+
const targets = Array.isArray(meta.target) ? meta.target : [meta.target];
|
|
300
|
+
if (meta.multi) {
|
|
301
|
+
const ids = src[field];
|
|
302
|
+
if (Array.isArray(ids)) dst[field] = ids.map((id) => {
|
|
303
|
+
if (typeof id === "string") return this._resolveId(targets, id) ?? id;
|
|
304
|
+
if (typeof id === "object" && id !== null && "model" in id && "ref" in id) {
|
|
305
|
+
const polyObj = id;
|
|
306
|
+
return this._resolveId([polyObj.model], polyObj.ref) ?? id;
|
|
307
|
+
}
|
|
308
|
+
return id;
|
|
309
|
+
});
|
|
310
|
+
} else {
|
|
311
|
+
const id = src[field];
|
|
312
|
+
if (typeof id === "string") dst[field] = this._resolveId(targets, id) ?? id;
|
|
313
|
+
else if (typeof id === "object" && id !== null && "model" in id && "ref" in id) {
|
|
314
|
+
const polyObj = id;
|
|
315
|
+
dst[field] = this._resolveId([polyObj.model], polyObj.ref) ?? id;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return resolved;
|
|
320
|
+
}
|
|
321
|
+
_resolveId(targets, id) {
|
|
322
|
+
for (const target of targets) {
|
|
323
|
+
const result = this._resolver(target, id, this._locale);
|
|
324
|
+
if (result) return result;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
//#endregion
|
|
329
|
+
//#region src/index.ts
|
|
330
|
+
/**
|
|
331
|
+
* Factory for framework SDK authors.
|
|
332
|
+
* Returns the generated client module loaded from .contentrain/client/.
|
|
333
|
+
*
|
|
334
|
+
* Usage (Nuxt composable):
|
|
335
|
+
* ```ts
|
|
336
|
+
* import { createContentrainClient } from '@contentrain/query'
|
|
337
|
+
* const client = createContentrainClient()
|
|
338
|
+
* const posts = client.query('blog-post').locale('en').all()
|
|
339
|
+
* ```
|
|
340
|
+
*/
|
|
341
|
+
async function createContentrainClient(projectRoot) {
|
|
342
|
+
const { resolve } = await import("node:path");
|
|
343
|
+
const { pathToFileURL } = await import("node:url");
|
|
344
|
+
return await import(pathToFileURL(resolve(projectRoot ?? process.cwd(), ".contentrain", "client", "index.mjs")).href);
|
|
345
|
+
}
|
|
346
|
+
//#endregion
|
|
347
|
+
export { DictionaryAccessor, DocumentQuery, QueryBuilder, SingletonAccessor, createContentrainClient, createContentrainClient as default };
|
|
348
|
+
|
|
349
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/runtime/query.ts","../src/runtime/singleton.ts","../src/runtime/dictionary.ts","../src/runtime/document.ts","../src/index.ts"],"sourcesContent":["export interface RelationMeta {\n target: string | string[]\n multi: boolean\n}\n\nexport type RelationResolver = (model: string, id: string, locale: string | null) => Record<string, unknown> | undefined\n\nexport class QueryBuilder<T extends object> {\n private _data: Map<string, T[]>\n private _locale: string | null = null\n private _filters: Array<(item: T) => boolean> = []\n private _sortField: string | null = null\n private _sortOrder: 'asc' | 'desc' = 'asc'\n private _limit: number | null = null\n private _offset = 0\n private _includes: string[] = []\n private _relationMeta: Record<string, RelationMeta>\n private _resolver: RelationResolver | null\n private _defaultLocale: string | null\n\n constructor(\n data: Map<string, T[]>,\n relationMeta?: Record<string, RelationMeta>,\n resolver?: RelationResolver,\n defaultLocale?: string,\n ) {\n this._data = data\n this._relationMeta = relationMeta ?? {}\n this._resolver = resolver ?? null\n this._defaultLocale = defaultLocale ?? null\n }\n\n locale(lang: string): this {\n this._locale = lang\n return this\n }\n\n where<K extends string & keyof T>(field: K, value: T[K]): this {\n this._filters.push((item) => {\n const v = item[field]\n if (Array.isArray(v)) return v.includes(value)\n return v === value\n })\n return this\n }\n\n sort<K extends string & keyof T>(field: K, order: 'asc' | 'desc' = 'asc'): this {\n this._sortField = field\n this._sortOrder = order\n return this\n }\n\n limit(n: number): this {\n this._limit = n\n return this\n }\n\n offset(n: number): this {\n this._offset = n\n return this\n }\n\n include(...fields: string[]): this {\n this._includes.push(...fields)\n return this\n }\n\n all(): T[] {\n let items = this._resolveData()\n\n // Filter\n for (const filter of this._filters) {\n items = items.filter(filter)\n }\n\n // Sort\n if (this._sortField) {\n const field = this._sortField\n const dir = this._sortOrder === 'asc' ? 1 : -1\n items.sort((a, b) => {\n const va = (a as Record<string, unknown>)[field]\n const vb = (b as Record<string, unknown>)[field]\n if (va == null && vb == null) return 0\n if (va == null) return dir\n if (vb == null) return -dir\n if (va < vb) return -dir\n if (va > vb) return dir\n return 0\n })\n }\n\n // Pagination\n if (this._offset > 0 || this._limit !== null) {\n const end = this._limit !== null ? this._offset + this._limit : undefined\n items = items.slice(this._offset, end)\n }\n\n // Resolve relations (after pagination for efficiency)\n if (this._includes.length > 0 && this._resolver) {\n items = items.map(item => this._resolveIncludes(item))\n }\n\n return items\n }\n\n first(): T | undefined {\n return this.all()[0]\n }\n\n private _resolveData(): T[] {\n if (this._locale) {\n return [...(this._data.get(this._locale) ?? [])]\n }\n if (this._defaultLocale) {\n const defaultData = this._data.get(this._defaultLocale)\n if (defaultData) return [...defaultData]\n }\n const firstKey = this._data.keys().next().value\n if (firstKey !== undefined) {\n return [...(this._data.get(firstKey) ?? [])]\n }\n return []\n }\n\n private _resolveIncludes(item: T): T {\n const resolved = { ...item }\n const src = item as Record<string, unknown>\n const dst = resolved as Record<string, unknown>\n for (const field of this._includes) {\n const meta = this._relationMeta[field]\n if (!meta) continue\n const targets = Array.isArray(meta.target) ? meta.target : [meta.target]\n\n if (meta.multi) {\n const ids = src[field]\n if (Array.isArray(ids)) {\n dst[field] = ids.map((id) => {\n if (typeof id === 'string') {\n return this._resolveId(targets, id) ?? id\n }\n if (typeof id === 'object' && id !== null && 'model' in id && 'ref' in id) {\n const polyObj = id as { model: string; ref: string }\n return this._resolveId([polyObj.model], polyObj.ref) ?? id\n }\n return id\n })\n }\n } else {\n const id = src[field]\n if (typeof id === 'string') {\n dst[field] = this._resolveId(targets, id) ?? id\n } else if (typeof id === 'object' && id !== null && 'model' in id && 'ref' in id) {\n const polyObj = id as { model: string; ref: string }\n dst[field] = this._resolveId([polyObj.model], polyObj.ref) ?? id\n }\n }\n }\n return resolved\n }\n\n private _resolveId(targets: string[], id: string): Record<string, unknown> | undefined {\n for (const target of targets) {\n const result = this._resolver!(target, id, this._locale)\n if (result) return result\n }\n return undefined\n }\n}\n","export class SingletonAccessor<T extends Record<string, unknown>> {\n private _data: Map<string, T>\n private _locale: string | null = null\n private _defaultLocale: string | null\n private _includes: string[] = []\n private _relationMeta: Record<string, { target: string | string[]; multi: boolean }> = {}\n private _resolveEntry?: (model: string, id: string, locale: string) => unknown\n\n constructor(\n data: Map<string, T>,\n defaultLocale?: string,\n relationMeta?: Record<string, { target: string | string[]; multi: boolean }>,\n resolveEntry?: (model: string, id: string, locale: string) => unknown,\n ) {\n this._data = data\n this._defaultLocale = defaultLocale ?? null\n if (relationMeta) this._relationMeta = relationMeta\n this._resolveEntry = resolveEntry\n }\n\n locale(lang: string): this {\n this._locale = lang\n return this\n }\n\n include(...fields: string[]): this {\n this._includes.push(...fields)\n return this\n }\n\n get(): T {\n if (this._locale) {\n const d = this._data.get(this._locale)\n if (!d) throw new Error(`No data for locale \"${this._locale}\"`)\n if (this._includes.length > 0) return this._resolveIncludes(d, this._locale)\n return d\n }\n\n const locale = this._defaultLocale ?? undefined\n let data: T | undefined\n\n if (locale) {\n data = this._data.get(locale)\n }\n if (!data) {\n const firstKey = this._data.keys().next().value\n if (firstKey !== undefined) {\n data = this._data.get(firstKey)\n }\n }\n if (!data) throw new Error('No data available')\n\n if (this._includes.length > 0) {\n return this._resolveIncludes(data, locale ?? 'en')\n }\n return data\n }\n\n private _resolveIncludes(item: T, locale: string): T {\n if (!this._resolveEntry || this._includes.length === 0) return item\n const resolved = { ...item }\n const src = item as Record<string, unknown>\n const dst = resolved as Record<string, unknown>\n\n for (const field of this._includes) {\n const meta = this._relationMeta[field]\n if (!meta) continue\n const targets = Array.isArray(meta.target) ? meta.target : [meta.target]\n\n if (meta.multi) {\n const ids = src[field]\n if (Array.isArray(ids)) {\n dst[field] = ids.map((id) => {\n if (typeof id === 'string') {\n return this._resolveId(targets, id, locale) ?? id\n }\n if (typeof id === 'object' && id !== null && 'model' in id && 'ref' in id) {\n const polyObj = id as { model: string; ref: string }\n return this._resolveId([polyObj.model], polyObj.ref, locale) ?? id\n }\n return id\n })\n }\n } else {\n const id = src[field]\n if (typeof id === 'string') {\n dst[field] = this._resolveId(targets, id, locale) ?? id\n } else if (typeof id === 'object' && id !== null && 'model' in id && 'ref' in id) {\n const polyObj = id as { model: string; ref: string }\n dst[field] = this._resolveId([polyObj.model], polyObj.ref, locale) ?? id\n }\n }\n }\n\n return resolved\n }\n\n private _resolveId(targets: string[], id: string, locale: string): unknown {\n if (!this._resolveEntry) return undefined\n for (const model of targets) {\n const result = this._resolveEntry(model, id, locale)\n if (result) return result\n }\n return undefined\n }\n}\n","export class DictionaryAccessor {\n private _data: Map<string, Record<string, string>>\n private _locale: string | null = null\n private _defaultLocale: string | null\n\n constructor(data: Map<string, Record<string, string>>, defaultLocale?: string) {\n this._data = data\n this._defaultLocale = defaultLocale ?? null\n }\n\n locale(lang: string): this {\n this._locale = lang\n return this\n }\n\n get(): Record<string, string>\n get(key: string): string | undefined\n get(key: string, params: Record<string, string | number>): string\n get(key?: string, params?: Record<string, string | number>): Record<string, string> | string | undefined {\n const dict = this._resolveData()\n if (key === undefined) return dict\n const value = dict[key]\n if (value === undefined) return undefined\n if (params) return interpolate(value, params)\n return value\n }\n\n private _resolveData(): Record<string, string> {\n if (this._locale) {\n return this._data.get(this._locale) ?? {}\n }\n if (this._defaultLocale) {\n const d = this._data.get(this._defaultLocale)\n if (d) return d\n }\n const firstKey = this._data.keys().next().value\n if (firstKey !== undefined) {\n return this._data.get(firstKey) ?? {}\n }\n return {}\n }\n}\n\nfunction interpolate(template: string, params: Record<string, string | number>): string {\n return template.replace(/\\{(\\w+)\\}/g, (match, key: string) => {\n const val = params[key]\n return val !== undefined ? String(val) : match\n })\n}\n","import type { RelationMeta, RelationResolver } from './query.js'\n\nexport class DocumentQuery<T extends object> {\n private _data: Map<string, T[]>\n private _locale: string | null = null\n private _filters: Array<(item: T) => boolean> = []\n private _includes: string[] = []\n private _relationMeta: Record<string, RelationMeta>\n private _resolver: RelationResolver | null\n private _defaultLocale: string | null\n\n constructor(\n data: Map<string, T[]>,\n relationMeta?: Record<string, RelationMeta>,\n resolver?: RelationResolver,\n defaultLocale?: string,\n ) {\n this._data = data\n this._relationMeta = relationMeta ?? {}\n this._resolver = resolver ?? null\n this._defaultLocale = defaultLocale ?? null\n }\n\n locale(lang: string): this {\n this._locale = lang\n return this\n }\n\n where<K extends string & keyof T>(field: K, value: T[K]): this {\n this._filters.push((item) => item[field] === value)\n return this\n }\n\n include(...fields: string[]): this {\n this._includes.push(...fields)\n return this\n }\n\n bySlug(slug: string): T | undefined {\n const items = this._resolveData()\n const item = items.find(i => (i as Record<string, unknown>)['slug'] === slug)\n if (item && this._includes.length > 0 && this._resolver) {\n return this._resolveIncludes(item)\n }\n return item\n }\n\n all(): T[] {\n let items = this._resolveData()\n for (const filter of this._filters) {\n items = items.filter(filter)\n }\n if (this._includes.length > 0 && this._resolver) {\n items = items.map(item => this._resolveIncludes(item))\n }\n return items\n }\n\n first(): T | undefined {\n return this.all()[0]\n }\n\n private _resolveData(): T[] {\n if (this._locale) {\n return [...(this._data.get(this._locale) ?? [])]\n }\n if (this._defaultLocale) {\n const defaultData = this._data.get(this._defaultLocale)\n if (defaultData) return [...defaultData]\n }\n const firstKey = this._data.keys().next().value\n if (firstKey !== undefined) {\n return [...(this._data.get(firstKey) ?? [])]\n }\n return []\n }\n\n private _resolveIncludes(item: T): T {\n const resolved = { ...item }\n const src = item as Record<string, unknown>\n const dst = resolved as Record<string, unknown>\n for (const field of this._includes) {\n const meta = this._relationMeta[field]\n if (!meta) continue\n const targets = Array.isArray(meta.target) ? meta.target : [meta.target]\n\n if (meta.multi) {\n const ids = src[field]\n if (Array.isArray(ids)) {\n dst[field] = ids.map((id) => {\n if (typeof id === 'string') {\n return this._resolveId(targets, id) ?? id\n }\n if (typeof id === 'object' && id !== null && 'model' in id && 'ref' in id) {\n const polyObj = id as { model: string; ref: string }\n return this._resolveId([polyObj.model], polyObj.ref) ?? id\n }\n return id\n })\n }\n } else {\n const id = src[field]\n if (typeof id === 'string') {\n dst[field] = this._resolveId(targets, id) ?? id\n } else if (typeof id === 'object' && id !== null && 'model' in id && 'ref' in id) {\n const polyObj = id as { model: string; ref: string }\n dst[field] = this._resolveId([polyObj.model], polyObj.ref) ?? id\n }\n }\n }\n return resolved\n }\n\n private _resolveId(targets: string[], id: string): Record<string, unknown> | undefined {\n for (const target of targets) {\n const result = this._resolver!(target, id, this._locale)\n if (result) return result\n }\n return undefined\n }\n}\n","// Runtime exports — used by framework SDK authors and generated client\nexport { QueryBuilder } from './runtime/query.js'\nexport type { RelationMeta, RelationResolver } from './runtime/query.js'\nexport { SingletonAccessor } from './runtime/singleton.js'\nexport { DictionaryAccessor } from './runtime/dictionary.js'\nexport { DocumentQuery } from './runtime/document.js'\n\n// Re-export types from @contentrain/types that SDK consumers need\nexport type {\n FieldType,\n ModelKind,\n ContentStatus,\n FieldDef,\n ModelDefinition,\n ContentrainConfig,\n} from '@contentrain/types'\n\n/**\n * Factory for framework SDK authors.\n * Returns the generated client module loaded from .contentrain/client/.\n *\n * Usage (Nuxt composable):\n * ```ts\n * import { createContentrainClient } from '@contentrain/query'\n * const client = createContentrainClient()\n * const posts = client.query('blog-post').locale('en').all()\n * ```\n */\nexport async function createContentrainClient(\n projectRoot?: string,\n): Promise<{\n query: (model: string) => import('./runtime/query.js').QueryBuilder<Record<string, unknown>>\n singleton: (model: string) => import('./runtime/singleton.js').SingletonAccessor<Record<string, unknown>>\n dictionary: (model: string) => import('./runtime/dictionary.js').DictionaryAccessor\n document: (model: string) => import('./runtime/document.js').DocumentQuery<Record<string, unknown>>\n}> {\n const { resolve } = await import('node:path')\n const { pathToFileURL } = await import('node:url')\n const root = projectRoot ?? process.cwd()\n const clientPath = resolve(root, '.contentrain', 'client', 'index.mjs')\n const client = await import(pathToFileURL(clientPath).href)\n return client\n}\n\n// Sync factory for CJS — requires generated client to be pre-loaded\nexport { createContentrainClient as default }\n"],"mappings":";AAOA,IAAa,eAAb,MAA4C;CAC1C;CACA,UAAiC;CACjC,WAAgD,EAAE;CAClD,aAAoC;CACpC,aAAqC;CACrC,SAAgC;CAChC,UAAkB;CAClB,YAA8B,EAAE;CAChC;CACA;CACA;CAEA,YACE,MACA,cACA,UACA,eACA;AACA,OAAK,QAAQ;AACb,OAAK,gBAAgB,gBAAgB,EAAE;AACvC,OAAK,YAAY,YAAY;AAC7B,OAAK,iBAAiB,iBAAiB;;CAGzC,OAAO,MAAoB;AACzB,OAAK,UAAU;AACf,SAAO;;CAGT,MAAkC,OAAU,OAAmB;AAC7D,OAAK,SAAS,MAAM,SAAS;GAC3B,MAAM,IAAI,KAAK;AACf,OAAI,MAAM,QAAQ,EAAE,CAAE,QAAO,EAAE,SAAS,MAAM;AAC9C,UAAO,MAAM;IACb;AACF,SAAO;;CAGT,KAAiC,OAAU,QAAwB,OAAa;AAC9E,OAAK,aAAa;AAClB,OAAK,aAAa;AAClB,SAAO;;CAGT,MAAM,GAAiB;AACrB,OAAK,SAAS;AACd,SAAO;;CAGT,OAAO,GAAiB;AACtB,OAAK,UAAU;AACf,SAAO;;CAGT,QAAQ,GAAG,QAAwB;AACjC,OAAK,UAAU,KAAK,GAAG,OAAO;AAC9B,SAAO;;CAGT,MAAW;EACT,IAAI,QAAQ,KAAK,cAAc;AAG/B,OAAK,MAAM,UAAU,KAAK,SACxB,SAAQ,MAAM,OAAO,OAAO;AAI9B,MAAI,KAAK,YAAY;GACnB,MAAM,QAAQ,KAAK;GACnB,MAAM,MAAM,KAAK,eAAe,QAAQ,IAAI;AAC5C,SAAM,MAAM,GAAG,MAAM;IACnB,MAAM,KAAM,EAA8B;IAC1C,MAAM,KAAM,EAA8B;AAC1C,QAAI,MAAM,QAAQ,MAAM,KAAM,QAAO;AACrC,QAAI,MAAM,KAAM,QAAO;AACvB,QAAI,MAAM,KAAM,QAAO,CAAC;AACxB,QAAI,KAAK,GAAI,QAAO,CAAC;AACrB,QAAI,KAAK,GAAI,QAAO;AACpB,WAAO;KACP;;AAIJ,MAAI,KAAK,UAAU,KAAK,KAAK,WAAW,MAAM;GAC5C,MAAM,MAAM,KAAK,WAAW,OAAO,KAAK,UAAU,KAAK,SAAS,KAAA;AAChE,WAAQ,MAAM,MAAM,KAAK,SAAS,IAAI;;AAIxC,MAAI,KAAK,UAAU,SAAS,KAAK,KAAK,UACpC,SAAQ,MAAM,KAAI,SAAQ,KAAK,iBAAiB,KAAK,CAAC;AAGxD,SAAO;;CAGT,QAAuB;AACrB,SAAO,KAAK,KAAK,CAAC;;CAGpB,eAA4B;AAC1B,MAAI,KAAK,QACP,QAAO,CAAC,GAAI,KAAK,MAAM,IAAI,KAAK,QAAQ,IAAI,EAAE,CAAE;AAElD,MAAI,KAAK,gBAAgB;GACvB,MAAM,cAAc,KAAK,MAAM,IAAI,KAAK,eAAe;AACvD,OAAI,YAAa,QAAO,CAAC,GAAG,YAAY;;EAE1C,MAAM,WAAW,KAAK,MAAM,MAAM,CAAC,MAAM,CAAC;AAC1C,MAAI,aAAa,KAAA,EACf,QAAO,CAAC,GAAI,KAAK,MAAM,IAAI,SAAS,IAAI,EAAE,CAAE;AAE9C,SAAO,EAAE;;CAGX,iBAAyB,MAAY;EACnC,MAAM,WAAW,EAAE,GAAG,MAAM;EAC5B,MAAM,MAAM;EACZ,MAAM,MAAM;AACZ,OAAK,MAAM,SAAS,KAAK,WAAW;GAClC,MAAM,OAAO,KAAK,cAAc;AAChC,OAAI,CAAC,KAAM;GACX,MAAM,UAAU,MAAM,QAAQ,KAAK,OAAO,GAAG,KAAK,SAAS,CAAC,KAAK,OAAO;AAExE,OAAI,KAAK,OAAO;IACd,MAAM,MAAM,IAAI;AAChB,QAAI,MAAM,QAAQ,IAAI,CACpB,KAAI,SAAS,IAAI,KAAK,OAAO;AAC3B,SAAI,OAAO,OAAO,SAChB,QAAO,KAAK,WAAW,SAAS,GAAG,IAAI;AAEzC,SAAI,OAAO,OAAO,YAAY,OAAO,QAAQ,WAAW,MAAM,SAAS,IAAI;MACzE,MAAM,UAAU;AAChB,aAAO,KAAK,WAAW,CAAC,QAAQ,MAAM,EAAE,QAAQ,IAAI,IAAI;;AAE1D,YAAO;MACP;UAEC;IACL,MAAM,KAAK,IAAI;AACf,QAAI,OAAO,OAAO,SAChB,KAAI,SAAS,KAAK,WAAW,SAAS,GAAG,IAAI;aACpC,OAAO,OAAO,YAAY,OAAO,QAAQ,WAAW,MAAM,SAAS,IAAI;KAChF,MAAM,UAAU;AAChB,SAAI,SAAS,KAAK,WAAW,CAAC,QAAQ,MAAM,EAAE,QAAQ,IAAI,IAAI;;;;AAIpE,SAAO;;CAGT,WAAmB,SAAmB,IAAiD;AACrF,OAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,SAAS,KAAK,UAAW,QAAQ,IAAI,KAAK,QAAQ;AACxD,OAAI,OAAQ,QAAO;;;;;;ACnKzB,IAAa,oBAAb,MAAkE;CAChE;CACA,UAAiC;CACjC;CACA,YAA8B,EAAE;CAChC,gBAAuF,EAAE;CACzF;CAEA,YACE,MACA,eACA,cACA,cACA;AACA,OAAK,QAAQ;AACb,OAAK,iBAAiB,iBAAiB;AACvC,MAAI,aAAc,MAAK,gBAAgB;AACvC,OAAK,gBAAgB;;CAGvB,OAAO,MAAoB;AACzB,OAAK,UAAU;AACf,SAAO;;CAGT,QAAQ,GAAG,QAAwB;AACjC,OAAK,UAAU,KAAK,GAAG,OAAO;AAC9B,SAAO;;CAGT,MAAS;AACP,MAAI,KAAK,SAAS;GAChB,MAAM,IAAI,KAAK,MAAM,IAAI,KAAK,QAAQ;AACtC,OAAI,CAAC,EAAG,OAAM,IAAI,MAAM,uBAAuB,KAAK,QAAQ,GAAG;AAC/D,OAAI,KAAK,UAAU,SAAS,EAAG,QAAO,KAAK,iBAAiB,GAAG,KAAK,QAAQ;AAC5E,UAAO;;EAGT,MAAM,SAAS,KAAK,kBAAkB,KAAA;EACtC,IAAI;AAEJ,MAAI,OACF,QAAO,KAAK,MAAM,IAAI,OAAO;AAE/B,MAAI,CAAC,MAAM;GACT,MAAM,WAAW,KAAK,MAAM,MAAM,CAAC,MAAM,CAAC;AAC1C,OAAI,aAAa,KAAA,EACf,QAAO,KAAK,MAAM,IAAI,SAAS;;AAGnC,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,oBAAoB;AAE/C,MAAI,KAAK,UAAU,SAAS,EAC1B,QAAO,KAAK,iBAAiB,MAAM,UAAU,KAAK;AAEpD,SAAO;;CAGT,iBAAyB,MAAS,QAAmB;AACnD,MAAI,CAAC,KAAK,iBAAiB,KAAK,UAAU,WAAW,EAAG,QAAO;EAC/D,MAAM,WAAW,EAAE,GAAG,MAAM;EAC5B,MAAM,MAAM;EACZ,MAAM,MAAM;AAEZ,OAAK,MAAM,SAAS,KAAK,WAAW;GAClC,MAAM,OAAO,KAAK,cAAc;AAChC,OAAI,CAAC,KAAM;GACX,MAAM,UAAU,MAAM,QAAQ,KAAK,OAAO,GAAG,KAAK,SAAS,CAAC,KAAK,OAAO;AAExE,OAAI,KAAK,OAAO;IACd,MAAM,MAAM,IAAI;AAChB,QAAI,MAAM,QAAQ,IAAI,CACpB,KAAI,SAAS,IAAI,KAAK,OAAO;AAC3B,SAAI,OAAO,OAAO,SAChB,QAAO,KAAK,WAAW,SAAS,IAAI,OAAO,IAAI;AAEjD,SAAI,OAAO,OAAO,YAAY,OAAO,QAAQ,WAAW,MAAM,SAAS,IAAI;MACzE,MAAM,UAAU;AAChB,aAAO,KAAK,WAAW,CAAC,QAAQ,MAAM,EAAE,QAAQ,KAAK,OAAO,IAAI;;AAElE,YAAO;MACP;UAEC;IACL,MAAM,KAAK,IAAI;AACf,QAAI,OAAO,OAAO,SAChB,KAAI,SAAS,KAAK,WAAW,SAAS,IAAI,OAAO,IAAI;aAC5C,OAAO,OAAO,YAAY,OAAO,QAAQ,WAAW,MAAM,SAAS,IAAI;KAChF,MAAM,UAAU;AAChB,SAAI,SAAS,KAAK,WAAW,CAAC,QAAQ,MAAM,EAAE,QAAQ,KAAK,OAAO,IAAI;;;;AAK5E,SAAO;;CAGT,WAAmB,SAAmB,IAAY,QAAyB;AACzE,MAAI,CAAC,KAAK,cAAe,QAAO,KAAA;AAChC,OAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,SAAS,KAAK,cAAc,OAAO,IAAI,OAAO;AACpD,OAAI,OAAQ,QAAO;;;;;;ACrGzB,IAAa,qBAAb,MAAgC;CAC9B;CACA,UAAiC;CACjC;CAEA,YAAY,MAA2C,eAAwB;AAC7E,OAAK,QAAQ;AACb,OAAK,iBAAiB,iBAAiB;;CAGzC,OAAO,MAAoB;AACzB,OAAK,UAAU;AACf,SAAO;;CAMT,IAAI,KAAc,QAAuF;EACvG,MAAM,OAAO,KAAK,cAAc;AAChC,MAAI,QAAQ,KAAA,EAAW,QAAO;EAC9B,MAAM,QAAQ,KAAK;AACnB,MAAI,UAAU,KAAA,EAAW,QAAO,KAAA;AAChC,MAAI,OAAQ,QAAO,YAAY,OAAO,OAAO;AAC7C,SAAO;;CAGT,eAA+C;AAC7C,MAAI,KAAK,QACP,QAAO,KAAK,MAAM,IAAI,KAAK,QAAQ,IAAI,EAAE;AAE3C,MAAI,KAAK,gBAAgB;GACvB,MAAM,IAAI,KAAK,MAAM,IAAI,KAAK,eAAe;AAC7C,OAAI,EAAG,QAAO;;EAEhB,MAAM,WAAW,KAAK,MAAM,MAAM,CAAC,MAAM,CAAC;AAC1C,MAAI,aAAa,KAAA,EACf,QAAO,KAAK,MAAM,IAAI,SAAS,IAAI,EAAE;AAEvC,SAAO,EAAE;;;AAIb,SAAS,YAAY,UAAkB,QAAiD;AACtF,QAAO,SAAS,QAAQ,eAAe,OAAO,QAAgB;EAC5D,MAAM,MAAM,OAAO;AACnB,SAAO,QAAQ,KAAA,IAAY,OAAO,IAAI,GAAG;GACzC;;;;AC7CJ,IAAa,gBAAb,MAA6C;CAC3C;CACA,UAAiC;CACjC,WAAgD,EAAE;CAClD,YAA8B,EAAE;CAChC;CACA;CACA;CAEA,YACE,MACA,cACA,UACA,eACA;AACA,OAAK,QAAQ;AACb,OAAK,gBAAgB,gBAAgB,EAAE;AACvC,OAAK,YAAY,YAAY;AAC7B,OAAK,iBAAiB,iBAAiB;;CAGzC,OAAO,MAAoB;AACzB,OAAK,UAAU;AACf,SAAO;;CAGT,MAAkC,OAAU,OAAmB;AAC7D,OAAK,SAAS,MAAM,SAAS,KAAK,WAAW,MAAM;AACnD,SAAO;;CAGT,QAAQ,GAAG,QAAwB;AACjC,OAAK,UAAU,KAAK,GAAG,OAAO;AAC9B,SAAO;;CAGT,OAAO,MAA6B;EAElC,MAAM,OADQ,KAAK,cAAc,CACd,MAAK,MAAM,EAA8B,YAAY,KAAK;AAC7E,MAAI,QAAQ,KAAK,UAAU,SAAS,KAAK,KAAK,UAC5C,QAAO,KAAK,iBAAiB,KAAK;AAEpC,SAAO;;CAGT,MAAW;EACT,IAAI,QAAQ,KAAK,cAAc;AAC/B,OAAK,MAAM,UAAU,KAAK,SACxB,SAAQ,MAAM,OAAO,OAAO;AAE9B,MAAI,KAAK,UAAU,SAAS,KAAK,KAAK,UACpC,SAAQ,MAAM,KAAI,SAAQ,KAAK,iBAAiB,KAAK,CAAC;AAExD,SAAO;;CAGT,QAAuB;AACrB,SAAO,KAAK,KAAK,CAAC;;CAGpB,eAA4B;AAC1B,MAAI,KAAK,QACP,QAAO,CAAC,GAAI,KAAK,MAAM,IAAI,KAAK,QAAQ,IAAI,EAAE,CAAE;AAElD,MAAI,KAAK,gBAAgB;GACvB,MAAM,cAAc,KAAK,MAAM,IAAI,KAAK,eAAe;AACvD,OAAI,YAAa,QAAO,CAAC,GAAG,YAAY;;EAE1C,MAAM,WAAW,KAAK,MAAM,MAAM,CAAC,MAAM,CAAC;AAC1C,MAAI,aAAa,KAAA,EACf,QAAO,CAAC,GAAI,KAAK,MAAM,IAAI,SAAS,IAAI,EAAE,CAAE;AAE9C,SAAO,EAAE;;CAGX,iBAAyB,MAAY;EACnC,MAAM,WAAW,EAAE,GAAG,MAAM;EAC5B,MAAM,MAAM;EACZ,MAAM,MAAM;AACZ,OAAK,MAAM,SAAS,KAAK,WAAW;GAClC,MAAM,OAAO,KAAK,cAAc;AAChC,OAAI,CAAC,KAAM;GACX,MAAM,UAAU,MAAM,QAAQ,KAAK,OAAO,GAAG,KAAK,SAAS,CAAC,KAAK,OAAO;AAExE,OAAI,KAAK,OAAO;IACd,MAAM,MAAM,IAAI;AAChB,QAAI,MAAM,QAAQ,IAAI,CACpB,KAAI,SAAS,IAAI,KAAK,OAAO;AAC3B,SAAI,OAAO,OAAO,SAChB,QAAO,KAAK,WAAW,SAAS,GAAG,IAAI;AAEzC,SAAI,OAAO,OAAO,YAAY,OAAO,QAAQ,WAAW,MAAM,SAAS,IAAI;MACzE,MAAM,UAAU;AAChB,aAAO,KAAK,WAAW,CAAC,QAAQ,MAAM,EAAE,QAAQ,IAAI,IAAI;;AAE1D,YAAO;MACP;UAEC;IACL,MAAM,KAAK,IAAI;AACf,QAAI,OAAO,OAAO,SAChB,KAAI,SAAS,KAAK,WAAW,SAAS,GAAG,IAAI;aACpC,OAAO,OAAO,YAAY,OAAO,QAAQ,WAAW,MAAM,SAAS,IAAI;KAChF,MAAM,UAAU;AAChB,SAAI,SAAS,KAAK,WAAW,CAAC,QAAQ,MAAM,EAAE,QAAQ,IAAI,IAAI;;;;AAIpE,SAAO;;CAGT,WAAmB,SAAmB,IAAiD;AACrF,OAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,SAAS,KAAK,UAAW,QAAQ,IAAI,KAAK,QAAQ;AACxD,OAAI,OAAQ,QAAO;;;;;;;;;;;;;;;;;ACxFzB,eAAsB,wBACpB,aAMC;CACD,MAAM,EAAE,YAAY,MAAM,OAAO;CACjC,MAAM,EAAE,kBAAkB,MAAM,OAAO;AAIvC,QADe,MAAM,OAAO,cADT,QADN,eAAe,QAAQ,KAAK,EACR,gBAAgB,UAAU,YAAY,CAClB,CAAC"}
|