arkormx 0.1.7 → 0.1.8

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.
Files changed (3) hide show
  1. package/dist/index.cjs +4681 -21
  2. package/dist/index.mjs +4592 -21
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -1,32 +1,1802 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`fs`),l=require(`path`);l=s(l);let u=require(`module`),d=require(`node:fs`),f=require(`node:path`),p=require(`node:child_process`),m=require(`@h3ravel/support`),h=require(`url`),ee=require(`@h3ravel/shared`),g=require(`@h3ravel/musket`),_=require(`node:url`),te=require(`@h3ravel/collect.js`);const ne={string:{get:e=>e==null?e:String(e),set:e=>e==null?e:String(e)},number:{get:e=>e==null?e:Number(e),set:e=>e==null?e:Number(e)},boolean:{get:e=>e==null?e:!!e,set:e=>e==null?e:!!e},date:{get:e=>e==null||e instanceof Date?e:new Date(String(e)),set:e=>e==null||e instanceof Date?e:new Date(String(e))},json:{get:e=>{if(e==null||typeof e!=`string`)return e;try{return JSON.parse(e)}catch{return e}},set:e=>e==null||typeof e==`string`?e:JSON.stringify(e)},array:{get:e=>{if(Array.isArray(e))return e;if(typeof e==`string`)try{let t=JSON.parse(e);return Array.isArray(t)?t:[t]}catch{return[e]}return e==null?e:[e]},set:e=>e==null||Array.isArray(e)?e:[e]}};function v(e){return typeof e==`string`?ne[e]:e}var y=class extends Error{constructor(e){super(e),this.name=`ArkormException`}},b=class{},x=class{columns=[];dropColumnNames=[];indexes=[];latestColumnName;primary(e,t){let n=typeof e==`string`?{columnName:e,...t??{}}:e??{},r=this.resolveColumn(n.columnName);return r.primary=!0,typeof n.autoIncrement==`boolean`&&(r.autoIncrement=n.autoIncrement),Object.prototype.hasOwnProperty.call(n,`default`)&&(r.default=n.default),this}id(e=`id`,t=`id`){return this.column(e,t,{primary:!0})}uuid(e,t={}){return this.column(e,`uuid`,t)}string(e,t={}){return this.column(e,`string`,t)}text(e,t={}){return this.column(e,`text`,t)}integer(e,t={}){return this.column(e,`integer`,t)}bigInteger(e,t={}){return this.column(e,`bigInteger`,t)}float(e,t={}){return this.column(e,`float`,t)}boolean(e,t={}){return this.column(e,`boolean`,t)}json(e,t={}){return this.column(e,`json`,t)}date(e,t={}){return this.column(e,`date`,t)}morphs(e,t=!1){return this.string(`${e}Type`,{nullable:t}),this.integer(`${e}Id`,{nullable:t}),this}nullableMorphs(e){return this.morphs(e,!0)}timestamp(e,t={}){return this.column(e,`timestamp`,t)}timestamps(){return this.timestamp(`createdAt`,{nullable:!1}),this.timestamp(`updatedAt`,{nullable:!1}),this}softDeletes(e=`deletedAt`){return this.timestamp(e,{nullable:!0}),this}dropColumn(e){return this.dropColumnNames.push(e),this}nullable(e){let t=this.resolveColumn(e);return t.nullable=!0,this}after(e,t){let n=this.resolveColumn(t);return n.after=e,this}map(e,t){let n=this.resolveColumn(t);return n.map=e,this}index(e,t){let n=Array.isArray(e)?e:typeof e==`string`?[e]:[this.resolveColumn().name];return this.indexes.push({columns:[...n],name:t}),this}getColumns(){return this.columns.map(e=>({...e}))}getDropColumns(){return[...this.dropColumnNames]}getIndexes(){return this.indexes.map(e=>({...e,columns:[...e.columns]}))}column(e,t,n){return this.columns.push({name:e,type:t,map:n.map,nullable:n.nullable,unique:n.unique,primary:n.primary,autoIncrement:n.autoIncrement,after:n.after,default:n.default}),this.latestColumnName=e,this}resolveColumn(e){let t=e??this.latestColumnName;if(!t)throw Error(`No column available for this operation.`);let n=this.columns.find(e=>e.name===t);if(!n)throw Error(`Column [${t}] was not found in the table definition.`);return n}},re=class{operations=[];createTable(e,t){let n=new x;return t(n),this.operations.push({type:`createTable`,table:e,columns:n.getColumns(),indexes:n.getIndexes()}),this}alterTable(e,t){let n=new x;return t(n),this.operations.push({type:`alterTable`,table:e,addColumns:n.getColumns(),dropColumns:n.getDropColumns(),addIndexes:n.getIndexes()}),this}dropTable(e){return this.operations.push({type:`dropTable`,table:e}),this}getOperations(){return this.operations.map(e=>e.type===`createTable`?{...e,columns:e.columns.map(e=>({...e})),indexes:e.indexes.map(e=>({...e,columns:[...e.columns]}))}:e.type===`alterTable`?{...e,addColumns:e.addColumns.map(e=>({...e})),dropColumns:[...e.dropColumns],addIndexes:e.addIndexes.map(e=>({...e,columns:[...e.columns]}))}:{...e})}};const ie=/model\s+(\w+)\s*\{[\s\S]*?\n\}/g,S=e=>{let t=e.replace(/[^a-zA-Z0-9]+/g,` `).trim(),n=(t.endsWith(`s`)&&t.length>1?t.slice(0,-1):t).split(/\s+/g).filter(Boolean);return n.length===0?`GeneratedModel`:n.map(e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}`).join(``)},C=e=>e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`),ae=e=>e.type===`id`?`Int`:e.type===`uuid`||e.type===`string`||e.type===`text`?`String`:e.type===`integer`?`Int`:e.type===`bigInteger`?`BigInt`:e.type===`float`?`Float`:e.type===`boolean`?`Boolean`:e.type===`json`?`Json`:`DateTime`,w=e=>{if(e!=null){if(typeof e==`string`)return`@default("${e.replace(/"/g,`\\"`)}")`;if(typeof e==`number`||typeof e==`bigint`)return`@default(${e})`;if(typeof e==`boolean`)return`@default(${e?`true`:`false`})`}},T=e=>{if(e.type===`id`){let t=e.primary===!1?``:` @id`,n=typeof e.map==`string`&&e.map.trim().length>0?` @map("${e.map.replace(/"/g,`\\"`)}")`:``,r=w(e.default),i=e.autoIncrement??e.primary!==!1,a=r?` ${r}`:i&&t?` @default(autoincrement())`:``;return` ${e.name} Int${t}${a}${n}`}let t=ae(e),n=e.nullable?`?`:``,r=e.unique?` @unique`:``,i=e.primary?` @id`:``,a=typeof e.map==`string`&&e.map.trim().length>0?` @map("${e.map.replace(/"/g,`\\"`)}")`:``,o=w(e.default)??(e.type===`uuid`&&e.primary?`@default(uuid())`:void 0),s=o?` ${o}`:``;return` ${e.name} ${t}${n}${i}${r}${s}${a}`},E=e=>` @@index([${e.columns.join(`, `)}]${typeof e.name==`string`&&e.name.trim().length>0?`, name: "${e.name.replace(/"/g,`\\"`)}"`:``})`,oe=e=>{let t=S(e.table),n=e.table!==t.toLowerCase(),r=e.columns.map(T),i=[...(e.indexes??[]).map(E),...n?[` @@map("${(0,m.str)(e.table).snake()}")`]:[]];return`model ${t} {\n${(i.length>0?[...r,``,...i]:r).join(`
2
- `)}\n}`},D=(e,t)=>{let n=[...e.matchAll(ie)],r=RegExp(`@@map\\("${C(t)}"\\)`);for(let e of n){let n=e[0],i=e[1],a=e.index??0,o=a+n.length;if(r.test(n)||i.toLowerCase()===t.toLowerCase()||i.toLowerCase()===S(t).toLowerCase())return{modelName:i,block:n,start:a,end:o}}return null},O=(e,t)=>{if(D(e,t.table))throw new y(`Prisma model for table [${t.table}] already exists.`);let n=oe(t);return`${e.trimEnd()}\n\n${n}\n`},k=(e,t)=>{let n=D(e,t.table);if(!n)throw new y(`Prisma model for table [${t.table}] was not found.`);let r=n.block,i=r.split(`
3
- `);return t.dropColumns.forEach(e=>{let t=RegExp(`^\\s*${C(e)}\\s+`);for(let e=0;e<i.length;e+=1)if(t.test(i[e])){i.splice(e,1);return}}),t.addColumns.forEach(e=>{let t=T(e),n=RegExp(`^\\s*${C(e.name)}\\s+`);if(i.some(e=>n.test(e)))return;let r=Math.max(1,i.length-1),a=typeof e.after==`string`&&e.after.length>0?i.findIndex(t=>RegExp(`^\\s*${C(e.after)}\\s+`).test(t)):-1,o=a>0?Math.min(a+1,r):r;i.splice(o,0,t)}),(t.addIndexes??[]).forEach(e=>{let t=E(e);if(i.some(e=>e.trim()===t.trim()))return;let n=Math.max(1,i.length-1);i.splice(n,0,t)}),r=i.join(`
4
- `),`${e.slice(0,n.start)}${r}${e.slice(n.end)}`},A=(e,t)=>{let n=D(e,t.table);if(!n)return e;let r=e.slice(0,n.start).trimEnd(),i=e.slice(n.end).trimStart();return`${r}${r&&i?`
5
-
6
- `:``}${i}`},se=(e,t)=>t.reduce((e,t)=>t.type===`createTable`?O(e,t):t.type===`alterTable`?k(e,t):A(e,t),e),j=(e,t)=>{let n=(0,p.spawnSync)(`npx`,[`prisma`,...e],{cwd:t,encoding:`utf-8`});if(n.status===0)return;let r=[n.stdout,n.stderr].filter(Boolean).join(`
7
- `).trim();throw new y(r?`Prisma command failed: prisma ${e.join(` `)}\n${r}`:`Prisma command failed: prisma ${e.join(` `)}`)},ce=e=>{let t=e.replace(/[^a-zA-Z0-9]+/g,` `).trim();return t?`${t.split(/\s+/g).map(e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}`).join(``)}Migration`:`GeneratedMigration`},M=e=>String(e).padStart(2,`0`),N=(e=new Date)=>`${e.getFullYear()}${M(e.getMonth()+1)}${M(e.getDate())}${M(e.getHours())}${M(e.getMinutes())}${M(e.getSeconds())}`,le=e=>e.trim().toLowerCase().replace(/[^a-z0-9]+/g,`_`).replace(/^_+|_+$/g,``)||`migration`,ue=(e,t=`ts`)=>t===`js`?[`import { Migration } from 'arkormx'`,``,`export default class ${e} extends Migration {`,` /**`,` * @param {import('arkormx').SchemaBuilder} schema`,` * @returns {Promise<void>}`,` */`,` async up (schema) {`,` }`,``,` /**`,` * @param {import('arkormx').SchemaBuilder} schema`,` * @returns {Promise<void>}`,` */`,` async down (schema) {`,` }`,`}`,``].join(`
8
- `):[`import { Migration, SchemaBuilder } from 'arkormx'`,``,`export default class ${e} extends Migration {`,` public async up (schema: SchemaBuilder): Promise<void> {`,` }`,``,` public async down (schema: SchemaBuilder): Promise<void> {`,` }`,`}`,``].join(`
9
- `),de=(e,t={})=>{let n=N(new Date),r=le(e),i=ce(e),a=t.extension??`ts`,o=t.directory??(0,f.join)(process.cwd(),`database`,`migrations`),s=`${n}_${r}.${a}`,c=(0,f.join)(o,s),l=ue(i,a);if(t.write??!0){if((0,d.existsSync)(o)||(0,d.mkdirSync)(o,{recursive:!0}),(0,d.existsSync)(c))throw new y(`Migration file already exists: ${c}`);(0,d.writeFileSync)(c,l)}return{fileName:s,filePath:c,className:i,content:l}},fe=async(e,t=`up`)=>{let n=e instanceof b?e:new e,r=new re;return t===`up`?await n.up(r):await n.down(r),r.getOperations()},P=async(e,t={})=>{let n=t.schemaPath??(0,f.join)(process.cwd(),`prisma`,`schema.prisma`);if(!(0,d.existsSync)(n))throw new y(`Prisma schema file not found: ${n}`);let r=(0,d.readFileSync)(n,`utf-8`),i=await fe(e,`up`),a=se(r,i);return(t.write??!0)&&(0,d.writeFileSync)(n,a),{schema:a,schemaPath:n,operations:i}},pe=async(e,t={})=>{let n=t.cwd??process.cwd(),r=await P(e,{schemaPath:t.schemaPath??(0,f.join)(n,`prisma`,`schema.prisma`),write:t.write}),i=t.runGenerate??!0,a=t.runMigrate??!0,o=t.migrateMode??`dev`;return i&&j([`generate`],n),a&&j(o===`deploy`?[`migrate`,`deploy`]:[`migrate`,`dev`,`--name`,t.migrationName??`arkorm_${N()}`],n),{schemaPath:r.schemaPath,operations:r.operations}},me=()=>{let e=l.default.dirname((0,h.fileURLToPath)(require(`url`).pathToFileURL(__filename).href));for(;;){let t=l.default.join(e,`package.json`),n=l.default.join(e,`stubs`);if((0,c.existsSync)(t)&&(0,c.existsSync)(n))return n;let r=l.default.dirname(e);if(r===e)break;e=r}return l.default.join(process.cwd(),`stubs`)},F={paths:{stubs:me(),seeders:l.default.join(process.cwd(),`database`,`seeders`),models:l.default.join(process.cwd(),`src`,`models`),migrations:l.default.join(process.cwd(),`database`,`migrations`),factories:l.default.join(process.cwd(),`database`,`factories`),buildOutput:l.default.join(process.cwd(),`dist`)},outputExt:`ts`},I={...F,paths:{...F.paths??{}}};let L=!1,R,z,B;const he=e=>{let t=F.paths??{},n=I.paths??{},r=Object.entries(e??{}).reduce((e,[t,n])=>(typeof n==`string`&&n.trim().length>0&&(e[t]=n),e),{});return{...t,...n,...r}},ge=e=>e,V=e=>e?I[e]:I,H=(e,t={})=>{let n={...I,prisma:e,paths:he(t.paths)};t.pagination!==void 0&&(n.pagination=t.pagination),t.outputExt!==void 0&&(n.outputExt=t.outputExt),Object.assign(I,{...n}),z=e,B=n.pagination?.urlDriver},_e=()=>{Object.assign(I,{...F,paths:{...F.paths??{}}}),L=!1,R=void 0,z=void 0,B=void 0},ve=e=>{if(!e)return;let t=typeof e==`function`?e():e;if(!(!t||typeof t!=`object`))return t},ye=e=>{let t=e?.default??e;!t||typeof t!=`object`||!t.prisma||(H(t.prisma,{pagination:t.pagination,paths:t.paths,outputExt:t.outputExt}),L=!0)},be=e=>import(`${(0,h.pathToFileURL)(e).href}?arkorm_runtime=${Date.now()}`),U=()=>{let e=(0,u.createRequire)(e(`url`).pathToFileURL(__filename).href),t=[l.default.join(process.cwd(),`arkormx.config.cjs`)];for(let n of t)if((0,c.existsSync)(n))try{return ye(e(n)),!0}catch{continue}return!1},W=async()=>{if(!L){if(R)return await R;U()||(R=(async()=>{let e=[l.default.join(process.cwd(),`arkormx.config.js`),l.default.join(process.cwd(),`arkormx.config.ts`)];for(let t of e)if((0,c.existsSync)(t))try{ye(await be(t));return}catch{continue}L=!0})(),await R)}},xe=()=>{L||R||W()},G=()=>me(),Se=()=>(L||U(),ve(z)),K=()=>(L||U(),B),q=e=>{if(!e||typeof e!=`object`)return!1;let t=e;return[`findMany`,`findFirst`,`create`,`update`,`delete`,`count`].every(e=>typeof t[e]==`function`)};W();var Ce=class{command;config={};constructor(){this.config=V()}getConfig=V;ensureDirectory(e){let t=(0,l.dirname)(e);(0,c.existsSync)(t)||(0,c.mkdirSync)(t,{recursive:!0})}formatPathForLog(e){let t=(0,l.relative)(process.cwd(),e);return t?t.startsWith(`..`)?e:t:`.`}splitLogger(e,t){return t=t.includes(process.cwd())?this.formatPathForLog(t):t,ee.Logger.twoColumnDetail(e+` `,` `+t,!1).join(``)}hasTypeScriptInstalled(){try{return(0,u.createRequire)(require(`url`).pathToFileURL(__filename).href).resolve(`typescript`,{paths:[process.cwd()]}),!0}catch{return!1}}resolveOutputExt(){let e=this.getConfig(`outputExt`)===`js`?`js`:`ts`;return e===`ts`&&!this.hasTypeScriptInstalled()?`js`:e}stripKnownSourceExtension(e){return e.replace(/\.(ts|tsx|mts|cts|js|mjs|cjs)$/i,``)}resolveRuntimeDirectoryPath(e){if((0,c.existsSync)(e))return e;let{buildOutput:t}=this.getConfig(`paths`)||{};if(typeof t!=`string`||t.trim().length===0)return e;let n=(0,l.relative)(process.cwd(),e);if(!n||n.startsWith(`..`))return e;let r=(0,l.join)(t,n);return(0,c.existsSync)(r)?r:e}resolveRuntimeScriptPath(e){let t=(0,l.extname)(e).toLowerCase(),n=t===`.ts`||t===`.mts`||t===`.cts`,r=[];if(n){let n=e.slice(0,-t.length);r.push(`${n}.js`,`${n}.cjs`,`${n}.mjs`)}let{buildOutput:i}=this.getConfig(`paths`)??{};if(typeof i==`string`&&i.trim().length>0){let t=(0,l.relative)(process.cwd(),e);if(t&&!t.startsWith(`..`)){let e=(0,l.join)(i,t),n=(0,l.extname)(e).toLowerCase();if(n===`.ts`||n===`.mts`||n===`.cts`){let t=e.slice(0,-n.length);r.push(`${t}.js`,`${t}.cjs`,`${t}.mjs`)}else r.push(e)}}return r.find(e=>(0,c.existsSync)(e))||e}generateFile(e,t,n,r){(0,c.existsSync)(t)&&!r?.force?(this.command.error(`Error: ${this.formatPathForLog(t)} already exists.`),process.exit(1)):(0,c.existsSync)(t)&&r?.force&&(0,c.rmSync)(t);let i=(0,c.readFileSync)(e,`utf-8`);for(let[e,t]of Object.entries(n))i=i.replace(RegExp(`{{${e}}}`,`g`),t);return this.ensureDirectory(t),(0,c.writeFileSync)(t,i),t}resolveConfigPath(e,t){let{[e]:n}=this.getConfig(`paths`)??{};return typeof n==`string`&&n.trim().length>0?n:t}resolveStubPath(e){return(0,l.join)(this.resolveConfigPath(`stubs`,G()),e)}makeFactory(e,t={}){let n=(0,m.str)(e.replace(/Factory$/,``)).pascal(),r=`${n}Factory`,i=t.modelName?(0,m.str)(t.modelName).pascal():n,a=this.resolveOutputExt(),o=(0,l.join)(this.resolveConfigPath(`factories`,(0,l.join)(process.cwd(),`database`,`factories`)),`${r}.${a}`),s=(0,l.join)(this.resolveConfigPath(`models`,(0,l.join)(process.cwd(),`src`,`models`)),`${i}.${a}`),c=t.modelImportPath??`./${this.stripKnownSourceExtension((0,l.relative)((0,l.dirname)(o),s).replace(/\\/g,`/`))}${a===`js`?`.js`:``}`,u=this.resolveStubPath(a===`js`?`factory.js.stub`:`factory.stub`);return{name:r,path:this.generateFile(u,o,{FactoryName:r,ModelName:i.toString(),ModelImportPath:c.startsWith(`.`)?c:`./${c}`},t)}}makeSeeder(e,t={}){let n=`${(0,m.str)(e.replace(/Seeder$/,``)).pascal()}Seeder`,r=this.resolveOutputExt(),i=(0,l.join)(this.resolveConfigPath(`seeders`,(0,l.join)(process.cwd(),`database`,`seeders`)),`${n}.${r}`),a=this.resolveStubPath(r===`js`?`seeder.js.stub`:`seeder.stub`);return{name:n,path:this.generateFile(a,i,{SeederName:n},t)}}makeMigration(e){let t=de(e,{directory:this.resolveConfigPath(`migrations`,(0,l.join)(process.cwd(),`database`,`migrations`)),extension:this.resolveOutputExt()});return{name:t.className,path:t.filePath}}makeModel(e,t={}){let n=(0,m.str)(e.replace(/Model$/,``)).pascal().toString(),r=`${n}`,i=(0,m.str)(n).camel().plural().toString(),a=this.resolveOutputExt(),o=(0,l.join)(this.resolveConfigPath(`models`,(0,l.join)(process.cwd(),`src`,`models`)),`${r}.${a}`),s=t.all||t.factory,c=t.all||t.seeder,u=t.all||t.migration,d=`${n}Factory`,f=(0,l.join)(this.resolveConfigPath(`factories`,(0,l.join)(process.cwd(),`database`,`factories`)),`${d}.${a}`),p=`./${(0,l.relative)((0,l.dirname)(o),f).replace(/\\/g,`/`).replace(/\.(ts|tsx|mts|cts|js|mjs|cjs)$/i,``)}${a===`js`?`.js`:``}`,h=this.resolveStubPath(a===`js`?`model.js.stub`:`model.stub`),ee=this.generateFile(h,o,{ModelName:r,DelegateName:i,FactoryImport:s?`import { ${d} } from '${p}'\n`:``,FactoryLink:s?a===`js`?`\n static factoryClass = ${d}`:`\n protected static override factoryClass = ${d}`:``},t),g=this.ensurePrismaModelEntry(r,i),_={model:{name:r,path:ee},prisma:g,factory:void 0,seeder:void 0,migration:void 0};return s&&(_.factory=this.makeFactory(n,{force:t.force,modelName:r,modelImportPath:`./${(0,l.relative)((0,l.dirname)(f),o).replace(/\\/g,`/`).replace(/\.(ts|tsx|mts|cts|js|mjs|cjs)$/i,``)}${a===`js`?`.js`:``}`})),c&&(_.seeder=this.makeSeeder(n,{force:t.force})),u&&(_.migration=this.makeMigration(`create ${i} table`)),_}ensurePrismaModelEntry(e,t){let n=(0,l.join)(process.cwd(),`prisma`,`schema.prisma`);if(!(0,c.existsSync)(n))return{path:n,updated:!1};let r=(0,c.readFileSync)(n,`utf-8`),i=D(r,t),a=RegExp(`model\\s+${e}\\s*\\{`,`m`).test(r);return i||a?{path:n,updated:!1}:((0,c.writeFileSync)(n,O(r,{type:`createTable`,table:t,columns:[{name:`id`,type:`id`,primary:!0}]})),{path:n,updated:!0})}prismaTypeToTs(e){return e===`Int`||e===`Float`||e===`Decimal`?`number`:e===`BigInt`?`bigint`:e===`String`?`string`:e===`Boolean`?`boolean`:e===`DateTime`?`Date`:e===`Json`?`Record<string, unknown>`:e===`Bytes`?`Buffer`:`unknown`}parsePrismaModels(e){let t=[],n=/model\s+(\w+)\s*\{([\s\S]*?)\n\}/g,r=new Set([`Int`,`Float`,`Decimal`,`BigInt`,`String`,`Boolean`,`DateTime`,`Json`,`Bytes`]);for(let i of e.matchAll(n)){let e=i[1],n=i[2],a=n.match(/@@map\("([^"]+)"\)/)?.[1]??`${e.charAt(0).toLowerCase()}${e.slice(1)}s`,o=[];n.split(`
10
- `).forEach(e=>{let t=e.trim();if(!t||t.startsWith(`@@`)||t.startsWith(`//`))return;let n=t.match(/^(\w+)\s+([A-Za-z]+)(\?)?\b/);if(!n)return;let i=n[2];r.has(i)&&o.push({name:n[1],type:this.prismaTypeToTs(i),optional:!!n[3]})}),t.push({name:e,table:a,fields:o})}return t}syncModelDeclarations(e,t){let n=e.split(`
11
- `),r=n.findIndex(e=>/export\s+class\s+\w+\s+extends\s+Model<.+>\s*\{/.test(e));if(r<0)return{content:e,updated:!1};let i=-1,a=0;for(let e=r;e<n.length;e+=1){let t=n[e];if(a+=(t.match(/\{/g)||[]).length,a-=(t.match(/\}/g)||[]).length,a===0){i=e;break}}if(i<0)return{content:e,updated:!1};let o=n.slice(r+1,i).filter(e=>!/^\s*declare\s+\w+\??:\s*[^\n]+$/.test(e)),s=[...t.map(e=>` ${e}`),...o],c=[...n.slice(0,r+1),...s,...n.slice(i)].join(`
12
- `);return{content:c,updated:c!==e}}syncModelsFromPrisma(e={}){let t=e.schemaPath??(0,l.join)(process.cwd(),`prisma`,`schema.prisma`),n=e.modelsDir??this.resolveConfigPath(`models`,(0,l.join)(process.cwd(),`src`,`models`));if(!(0,c.existsSync)(t))throw Error(`Prisma schema file not found: ${t}`);if(!(0,c.existsSync)(n))throw Error(`Models directory not found: ${n}`);let r=(0,c.readFileSync)(t,`utf-8`),i=this.parsePrismaModels(r),a=(0,c.readdirSync)(n).filter(e=>e.endsWith(`.ts`)),o=[],s=[];return a.forEach(e=>{let t=(0,l.join)(n,e),r=(0,c.readFileSync)(t,`utf-8`),a=r.match(/export\s+class\s+(\w+)\s+extends\s+Model<'([^']+)'>/);if(!a){s.push(t);return}let u=a[1],d=a[2],f=i.find(e=>e.table===d)??i.find(e=>e.name===u);if(!f||f.fields.length===0){s.push(t);return}let p=f.fields.map(e=>`declare ${e.name}${e.optional?`?`:``}: ${e.type}`),m=this.syncModelDeclarations(r,p);if(!m.updated){s.push(t);return}(0,c.writeFileSync)(t,m.content),o.push(t)}),{schemaPath:t,modelsDir:n,total:a.length,updated:o,skipped:s}}},we=class extends g.Command{signature=`init
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
12
+ key = keys[i];
13
+ if (!__hasOwnProp.call(to, key) && key !== except) {
14
+ __defProp(to, key, {
15
+ get: ((k) => from[k]).bind(null, key),
16
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
17
+ });
18
+ }
19
+ }
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
24
+ value: mod,
25
+ enumerable: true
26
+ }) : target, mod));
27
+
28
+ //#endregion
29
+ let fs = require("fs");
30
+ let path = require("path");
31
+ path = __toESM(path);
32
+ let module$1 = require("module");
33
+ let node_fs = require("node:fs");
34
+ let node_path = require("node:path");
35
+ let node_child_process = require("node:child_process");
36
+ let _h3ravel_support = require("@h3ravel/support");
37
+ let url = require("url");
38
+ let _h3ravel_shared = require("@h3ravel/shared");
39
+ let _h3ravel_musket = require("@h3ravel/musket");
40
+ let node_url = require("node:url");
41
+ let _h3ravel_collect_js = require("@h3ravel/collect.js");
42
+
43
+ //#region src/casts.ts
44
+ const builtinCasts = {
45
+ string: {
46
+ get: (value) => value == null ? value : String(value),
47
+ set: (value) => value == null ? value : String(value)
48
+ },
49
+ number: {
50
+ get: (value) => value == null ? value : Number(value),
51
+ set: (value) => value == null ? value : Number(value)
52
+ },
53
+ boolean: {
54
+ get: (value) => value == null ? value : Boolean(value),
55
+ set: (value) => value == null ? value : Boolean(value)
56
+ },
57
+ date: {
58
+ get: (value) => {
59
+ if (value == null || value instanceof Date) return value;
60
+ return new Date(String(value));
61
+ },
62
+ set: (value) => {
63
+ if (value == null || value instanceof Date) return value;
64
+ return new Date(String(value));
65
+ }
66
+ },
67
+ json: {
68
+ get: (value) => {
69
+ if (value == null || typeof value !== "string") return value;
70
+ try {
71
+ return JSON.parse(value);
72
+ } catch {
73
+ return value;
74
+ }
75
+ },
76
+ set: (value) => {
77
+ if (value == null || typeof value === "string") return value;
78
+ return JSON.stringify(value);
79
+ }
80
+ },
81
+ array: {
82
+ get: (value) => {
83
+ if (Array.isArray(value)) return value;
84
+ if (typeof value === "string") try {
85
+ const parsed = JSON.parse(value);
86
+ return Array.isArray(parsed) ? parsed : [parsed];
87
+ } catch {
88
+ return [value];
89
+ }
90
+ if (value == null) return value;
91
+ return [value];
92
+ },
93
+ set: (value) => {
94
+ if (value == null) return value;
95
+ return Array.isArray(value) ? value : [value];
96
+ }
97
+ }
98
+ };
99
+ function resolveCast(definition) {
100
+ if (typeof definition === "string") return builtinCasts[definition];
101
+ return definition;
102
+ }
103
+
104
+ //#endregion
105
+ //#region src/Exceptions/ArkormException.ts
106
+ /**
107
+ * The ArkormException class is a custom error type for handling
108
+ * exceptions specific to the Arkormˣ.
109
+ *
110
+ * @author Legacy (3m1n3nc3)
111
+ * @since 0.1.0
112
+ */
113
+ var ArkormException = class extends Error {
114
+ constructor(message) {
115
+ super(message);
116
+ this.name = "ArkormException";
117
+ }
118
+ };
119
+
120
+ //#endregion
121
+ //#region src/database/Migration.ts
122
+ /**
123
+ * The Migration class serves as a base for defining database migrations, requiring
124
+ * the implementation of `up` and `down` methods to specify the changes to be
125
+ * applied or reverted in the database schema.
126
+ *
127
+ * @author Legacy (3m1n3nc3)
128
+ * @since 0.1.0
129
+ */
130
+ var Migration = class {};
131
+
132
+ //#endregion
133
+ //#region src/database/TableBuilder.ts
134
+ /**
135
+ * The TableBuilder class provides a fluent interface for defining
136
+ * the structure of a database table in a migration, including columns to add or drop.
137
+ *
138
+ * @author Legacy (3m1n3nc3)
139
+ * @since 0.1.0
140
+ */
141
+ var TableBuilder = class {
142
+ columns = [];
143
+ dropColumnNames = [];
144
+ indexes = [];
145
+ latestColumnName;
146
+ /**
147
+ * Defines a primary key column in the table.
148
+ *
149
+ * @param columnNameOrOptions
150
+ * @param options
151
+ * @returns
152
+ */
153
+ primary(columnNameOrOptions, options) {
154
+ const config = typeof columnNameOrOptions === "string" ? {
155
+ columnName: columnNameOrOptions,
156
+ ...options ?? {}
157
+ } : columnNameOrOptions ?? {};
158
+ const column = this.resolveColumn(config.columnName);
159
+ column.primary = true;
160
+ if (typeof config.autoIncrement === "boolean") column.autoIncrement = config.autoIncrement;
161
+ if (Object.prototype.hasOwnProperty.call(config, "default")) column.default = config.default;
162
+ return this;
163
+ }
164
+ /**
165
+ * Defines an auto-incrementing primary key column.
166
+ *
167
+ * @param name The name of the primary key column.
168
+ * @default 'id'
169
+ * @returns The current TableBuilder instance for chaining.
170
+ */
171
+ id(name = "id", type = "id") {
172
+ return this.column(name, type, { primary: true });
173
+ }
174
+ /**
175
+ * Defines a UUID column in the table.
176
+ *
177
+ * @param name The name of the UUID column.
178
+ * @param options Additional options for the UUID column.
179
+ * @returns The current TableBuilder instance for chaining.
180
+ */
181
+ uuid(name, options = {}) {
182
+ return this.column(name, "uuid", options);
183
+ }
184
+ /**
185
+ * Defines a string column in the table.
186
+ *
187
+ * @param name The name of the string column.
188
+ * @param options Additional options for the string column.
189
+ * @returns The current TableBuilder instance for chaining.
190
+ */
191
+ string(name, options = {}) {
192
+ return this.column(name, "string", options);
193
+ }
194
+ /**
195
+ * Defines a text column in the table.
196
+ *
197
+ * @param name The name of the text column.
198
+ * @param options Additional options for the text column.
199
+ * @returns The current TableBuilder instance for chaining.
200
+ */
201
+ text(name, options = {}) {
202
+ return this.column(name, "text", options);
203
+ }
204
+ /**
205
+ * Defines an integer column in the table.
206
+ *
207
+ * @param name The name of the integer column.
208
+ * @param options Additional options for the integer column.
209
+ * @returns
210
+ */
211
+ integer(name, options = {}) {
212
+ return this.column(name, "integer", options);
213
+ }
214
+ /**
215
+ * Defines a big integer column in the table.
216
+ *
217
+ * @param name The name of the big integer column.
218
+ * @param options Additional options for the big integer column.
219
+ * @returns
220
+ */
221
+ bigInteger(name, options = {}) {
222
+ return this.column(name, "bigInteger", options);
223
+ }
224
+ /**
225
+ * Defines a float column in the table.
226
+ *
227
+ * @param name The name of the float column.
228
+ * @param options Additional options for the float column.
229
+ * @returns
230
+ */
231
+ float(name, options = {}) {
232
+ return this.column(name, "float", options);
233
+ }
234
+ /**
235
+ * Defines a boolean column in the table.
236
+ *
237
+ * @param name The name of the boolean column.
238
+ * @param options Additional options for the boolean column.
239
+ * @returns
240
+ */
241
+ boolean(name, options = {}) {
242
+ return this.column(name, "boolean", options);
243
+ }
244
+ /**
245
+ * Defines a JSON column in the table.
246
+ *
247
+ * @param name The name of the JSON column.
248
+ * @param options Additional options for the JSON column.
249
+ * @returns
250
+ */
251
+ json(name, options = {}) {
252
+ return this.column(name, "json", options);
253
+ }
254
+ /**
255
+ * Defines a date column in the table.
256
+ *
257
+ * @param name The name of the date column.
258
+ * @param options Additional options for the date column.
259
+ * @returns
260
+ */
261
+ date(name, options = {}) {
262
+ return this.column(name, "date", options);
263
+ }
264
+ /**
265
+ * Defines colonns for a polymorphic relationship in the table.
266
+ *
267
+ * @param name The base name for the polymorphic relationship columns.
268
+ * @returns
269
+ */
270
+ morphs(name, nullable = false) {
271
+ this.string(`${name}Type`, { nullable });
272
+ this.integer(`${name}Id`, { nullable });
273
+ return this;
274
+ }
275
+ /**
276
+ * Defines nullable columns for a polymorphic relationship in the table.
277
+ *
278
+ * @param name The base name for the polymorphic relationship columns.
279
+ * @returns
280
+ */
281
+ nullableMorphs(name) {
282
+ return this.morphs(name, true);
283
+ }
284
+ /**
285
+ * Defines a timestamp column in the table.
286
+ *
287
+ * @param name The name of the timestamp column.
288
+ * @param options Additional options for the timestamp column.
289
+ * @returns
290
+ */
291
+ timestamp(name, options = {}) {
292
+ return this.column(name, "timestamp", options);
293
+ }
294
+ /**
295
+ * Defines both createdAt and updatedAt timestamp columns in the table.
296
+ *
297
+ * @returns
298
+ */
299
+ timestamps() {
300
+ this.timestamp("createdAt", { nullable: false });
301
+ this.timestamp("updatedAt", { nullable: false });
302
+ return this;
303
+ }
304
+ /**
305
+ * Defines a soft delete timestamp column in the table.
306
+ *
307
+ * @param column The name of the soft delete column.
308
+ * @returns
309
+ */
310
+ softDeletes(column = "deletedAt") {
311
+ this.timestamp(column, { nullable: true });
312
+ return this;
313
+ }
314
+ /**
315
+ * Defines a column to be dropped from the table in an alterTable operation.
316
+ *
317
+ * @param name The name of the column to drop.
318
+ * @returns
319
+ */
320
+ dropColumn(name) {
321
+ this.dropColumnNames.push(name);
322
+ return this;
323
+ }
324
+ /**
325
+ * Marks a column as nullable.
326
+ *
327
+ * @param columnName Optional explicit column name. When omitted, applies to the latest defined column.
328
+ * @returns The current TableBuilder instance for chaining.
329
+ */
330
+ nullable(columnName) {
331
+ const column = this.resolveColumn(columnName);
332
+ column.nullable = true;
333
+ return this;
334
+ }
335
+ /**
336
+ * Sets the column position to appear after another column when possible.
337
+ *
338
+ * @param referenceColumn The column that the target column should be placed after.
339
+ * @param columnName Optional explicit target column name. When omitted, applies to the latest defined column.
340
+ * @returns The current TableBuilder instance for chaining.
341
+ */
342
+ after(referenceColumn, columnName) {
343
+ const column = this.resolveColumn(columnName);
344
+ column.after = referenceColumn;
345
+ return this;
346
+ }
347
+ /**
348
+ * Maps the column to a custom database column name.
349
+ *
350
+ * @param name The custom database column name.
351
+ * @param columnName Optional explicit target column name. When omitted, applies to the latest defined column.
352
+ * @returns The current TableBuilder instance for chaining.
353
+ */
354
+ map(name, columnName) {
355
+ const column = this.resolveColumn(columnName);
356
+ column.map = name;
357
+ return this;
358
+ }
359
+ /**
360
+ * Defines an index on one or more columns.
361
+ *
362
+ * @param columns Optional target columns. When omitted, applies to the latest defined column.
363
+ * @param name Optional index name.
364
+ * @returns The current TableBuilder instance for chaining.
365
+ */
366
+ index(columns, name) {
367
+ const columnList = Array.isArray(columns) ? columns : typeof columns === "string" ? [columns] : [this.resolveColumn().name];
368
+ this.indexes.push({
369
+ columns: [...columnList],
370
+ name
371
+ });
372
+ return this;
373
+ }
374
+ /**
375
+ * Returns a deep copy of the defined columns for the table.
376
+ *
377
+ * @returns
378
+ */
379
+ getColumns() {
380
+ return this.columns.map((column) => ({ ...column }));
381
+ }
382
+ /**
383
+ * Returns a copy of the defined column names to be dropped from the table.
384
+ *
385
+ * @returns
386
+ */
387
+ getDropColumns() {
388
+ return [...this.dropColumnNames];
389
+ }
390
+ /**
391
+ * Returns a deep copy of the defined indexes for the table.
392
+ *
393
+ * @returns
394
+ */
395
+ getIndexes() {
396
+ return this.indexes.map((index) => ({
397
+ ...index,
398
+ columns: [...index.columns]
399
+ }));
400
+ }
401
+ /**
402
+ * Defines a column in the table with the given name.
403
+ *
404
+ * @param name The name of the column.
405
+ * @param type The type of the column.
406
+ * @param options Additional options for the column.
407
+ * @returns
408
+ */
409
+ column(name, type, options) {
410
+ this.columns.push({
411
+ name,
412
+ type,
413
+ map: options.map,
414
+ nullable: options.nullable,
415
+ unique: options.unique,
416
+ primary: options.primary,
417
+ autoIncrement: options.autoIncrement,
418
+ after: options.after,
419
+ default: options.default
420
+ });
421
+ this.latestColumnName = name;
422
+ return this;
423
+ }
424
+ /**
425
+ * Resolve a target column by name or fallback to the latest defined column.
426
+ *
427
+ * @param columnName
428
+ * @returns
429
+ */
430
+ resolveColumn(columnName) {
431
+ const targetName = columnName ?? this.latestColumnName;
432
+ if (!targetName) throw new Error("No column available for this operation.");
433
+ const column = this.columns.find((item) => item.name === targetName);
434
+ if (!column) throw new Error(`Column [${targetName}] was not found in the table definition.`);
435
+ return column;
436
+ }
437
+ };
438
+
439
+ //#endregion
440
+ //#region src/database/SchemaBuilder.ts
441
+ /**
442
+ * The SchemaBuilder class provides methods for defining the operations to be
443
+ * performed in a migration, such as creating, altering, or dropping tables.
444
+ *
445
+ * @author Legacy (3m1n3nc3)
446
+ * @since 0.1.0
447
+ */
448
+ var SchemaBuilder = class {
449
+ operations = [];
450
+ /**
451
+ * Defines a new table to be created in the migration.
452
+ *
453
+ * @param table The name of the table to create.
454
+ * @param callback A callback function to define the table's columns and structure.
455
+ * @returns The current SchemaBuilder instance for chaining.
456
+ */
457
+ createTable(table, callback) {
458
+ const builder = new TableBuilder();
459
+ callback(builder);
460
+ this.operations.push({
461
+ type: "createTable",
462
+ table,
463
+ columns: builder.getColumns(),
464
+ indexes: builder.getIndexes()
465
+ });
466
+ return this;
467
+ }
468
+ /**
469
+ * Defines alterations to an existing table in the migration.
470
+ *
471
+ * @param table The name of the table to alter.
472
+ * @param callback A callback function to define the alterations to the table's columns and structure.
473
+ * @returns The current SchemaBuilder instance for chaining.
474
+ */
475
+ alterTable(table, callback) {
476
+ const builder = new TableBuilder();
477
+ callback(builder);
478
+ this.operations.push({
479
+ type: "alterTable",
480
+ table,
481
+ addColumns: builder.getColumns(),
482
+ dropColumns: builder.getDropColumns(),
483
+ addIndexes: builder.getIndexes()
484
+ });
485
+ return this;
486
+ }
487
+ /**
488
+ * Defines a table to be dropped in the migration.
489
+ *
490
+ * @param table The name of the table to drop.
491
+ * @returns The current SchemaBuilder instance for chaining.
492
+ */
493
+ dropTable(table) {
494
+ this.operations.push({
495
+ type: "dropTable",
496
+ table
497
+ });
498
+ return this;
499
+ }
500
+ /**
501
+ * Returns a deep copy of the defined schema operations for the migration/
502
+ *
503
+ * @returns An array of schema operations for the migration.
504
+ */
505
+ getOperations() {
506
+ return this.operations.map((operation) => {
507
+ if (operation.type === "createTable") return {
508
+ ...operation,
509
+ columns: operation.columns.map((column) => ({ ...column })),
510
+ indexes: operation.indexes.map((index) => ({
511
+ ...index,
512
+ columns: [...index.columns]
513
+ }))
514
+ };
515
+ if (operation.type === "alterTable") return {
516
+ ...operation,
517
+ addColumns: operation.addColumns.map((column) => ({ ...column })),
518
+ dropColumns: [...operation.dropColumns],
519
+ addIndexes: operation.addIndexes.map((index) => ({
520
+ ...index,
521
+ columns: [...index.columns]
522
+ }))
523
+ };
524
+ return { ...operation };
525
+ });
526
+ }
527
+ };
528
+
529
+ //#endregion
530
+ //#region src/helpers/migrations.ts
531
+ const PRISMA_MODEL_REGEX = /model\s+(\w+)\s*\{[\s\S]*?\n\}/g;
532
+ /**
533
+ * Convert a table name to a PascalCase model name, with basic singularization.
534
+ *
535
+ * @param tableName The name of the table to convert.
536
+ * @returns The corresponding PascalCase model name.
537
+ */
538
+ const toModelName = (tableName) => {
539
+ const normalized = tableName.replace(/[^a-zA-Z0-9]+/g, " ").trim();
540
+ const parts = (normalized.endsWith("s") && normalized.length > 1 ? normalized.slice(0, -1) : normalized).split(/\s+/g).filter(Boolean);
541
+ if (parts.length === 0) return "GeneratedModel";
542
+ return parts.map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`).join("");
543
+ };
544
+ /**
545
+ * Escape special characters in a string for use in a regular expression.
546
+ *
547
+ * @param value
548
+ * @returns
549
+ */
550
+ const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
551
+ /**
552
+ * Convert a SchemaColumn definition to a Prisma field type string, including modifiers.
553
+ *
554
+ * @param column
555
+ * @returns
556
+ */
557
+ const resolvePrismaType = (column) => {
558
+ if (column.type === "id") return "Int";
559
+ if (column.type === "uuid") return "String";
560
+ if (column.type === "string" || column.type === "text") return "String";
561
+ if (column.type === "integer") return "Int";
562
+ if (column.type === "bigInteger") return "BigInt";
563
+ if (column.type === "float") return "Float";
564
+ if (column.type === "boolean") return "Boolean";
565
+ if (column.type === "json") return "Json";
566
+ return "DateTime";
567
+ };
568
+ /**
569
+ * Format a default value for inclusion in a Prisma schema field definition, based on its type.
570
+ *
571
+ * @param value
572
+ * @returns
573
+ */
574
+ const formatDefaultValue = (value) => {
575
+ if (value == null) return void 0;
576
+ if (typeof value === "string") return `@default("${value.replace(/"/g, "\\\"")}")`;
577
+ if (typeof value === "number" || typeof value === "bigint") return `@default(${value})`;
578
+ if (typeof value === "boolean") return `@default(${value ? "true" : "false"})`;
579
+ };
580
+ /**
581
+ * Build a single line of a Prisma model field definition based on a SchemaColumn, including type and modifiers.
582
+ *
583
+ * @param column
584
+ * @returns
585
+ */
586
+ const buildFieldLine = (column) => {
587
+ if (column.type === "id") {
588
+ const primary = column.primary === false ? "" : " @id";
589
+ const mapped = typeof column.map === "string" && column.map.trim().length > 0 ? ` @map("${column.map.replace(/"/g, "\\\"")}")` : "";
590
+ const configuredDefault = formatDefaultValue(column.default);
591
+ const shouldAutoIncrement = column.autoIncrement ?? column.primary !== false;
592
+ const defaultSuffix = configuredDefault ? ` ${configuredDefault}` : shouldAutoIncrement && primary ? " @default(autoincrement())" : "";
593
+ return ` ${column.name} Int${primary}${defaultSuffix}${mapped}`;
594
+ }
595
+ const scalar = resolvePrismaType(column);
596
+ const nullable = column.nullable ? "?" : "";
597
+ const unique = column.unique ? " @unique" : "";
598
+ const primary = column.primary ? " @id" : "";
599
+ const mapped = typeof column.map === "string" && column.map.trim().length > 0 ? ` @map("${column.map.replace(/"/g, "\\\"")}")` : "";
600
+ const defaultValue = formatDefaultValue(column.default) ?? (column.type === "uuid" && column.primary ? "@default(uuid())" : void 0);
601
+ const defaultSuffix = defaultValue ? ` ${defaultValue}` : "";
602
+ return ` ${column.name} ${scalar}${nullable}${primary}${unique}${defaultSuffix}${mapped}`;
603
+ };
604
+ /**
605
+ * Build a Prisma model-level @@index definition line.
606
+ *
607
+ * @param index
608
+ * @returns
609
+ */
610
+ const buildIndexLine = (index) => {
611
+ return ` @@index([${index.columns.join(", ")}]${typeof index.name === "string" && index.name.trim().length > 0 ? `, name: "${index.name.replace(/"/g, "\\\"")}"` : ""})`;
612
+ };
613
+ /**
614
+ * Build a Prisma model block string based on a SchemaTableCreateOperation, including
615
+ * all fields and any necessary mapping.
616
+ *
617
+ * @param operation The schema table create operation to convert.
618
+ * @returns The corresponding Prisma model block string.
619
+ */
620
+ const buildModelBlock = (operation) => {
621
+ const modelName = toModelName(operation.table);
622
+ const mapped = operation.table !== modelName.toLowerCase();
623
+ const fields = operation.columns.map(buildFieldLine);
624
+ const metadata = [...(operation.indexes ?? []).map(buildIndexLine), ...mapped ? [` @@map("${(0, _h3ravel_support.str)(operation.table).snake()}")`] : []];
625
+ return `model ${modelName} {\n${(metadata.length > 0 ? [
626
+ ...fields,
627
+ "",
628
+ ...metadata
629
+ ] : fields).join("\n")}\n}`;
630
+ };
631
+ /**
632
+ * Find the Prisma model block in a schema string that corresponds to a given
633
+ * table name, using both explicit mapping and naming conventions.
634
+ *
635
+ * @param schema
636
+ * @param table
637
+ * @returns
638
+ */
639
+ const findModelBlock = (schema, table) => {
640
+ const candidates = [...schema.matchAll(PRISMA_MODEL_REGEX)];
641
+ const explicitMapRegex = new RegExp(`@@map\\("${escapeRegex(table)}"\\)`);
642
+ for (const match of candidates) {
643
+ const block = match[0];
644
+ const modelName = match[1];
645
+ const start = match.index ?? 0;
646
+ const end = start + block.length;
647
+ if (explicitMapRegex.test(block)) return {
648
+ modelName,
649
+ block,
650
+ start,
651
+ end
652
+ };
653
+ if (modelName.toLowerCase() === table.toLowerCase()) return {
654
+ modelName,
655
+ block,
656
+ start,
657
+ end
658
+ };
659
+ if (modelName.toLowerCase() === toModelName(table).toLowerCase()) return {
660
+ modelName,
661
+ block,
662
+ start,
663
+ end
664
+ };
665
+ }
666
+ return null;
667
+ };
668
+ /**
669
+ * Apply a create table operation to a Prisma schema string, adding a new model
670
+ * block for the specified table and fields.
671
+ *
672
+ * @param schema The current Prisma schema string.
673
+ * @param operation The schema table create operation to apply.
674
+ * @returns The updated Prisma schema string with the new model block.
675
+ */
676
+ const applyCreateTableOperation = (schema, operation) => {
677
+ if (findModelBlock(schema, operation.table)) throw new ArkormException(`Prisma model for table [${operation.table}] already exists.`);
678
+ const block = buildModelBlock(operation);
679
+ return `${schema.trimEnd()}\n\n${block}\n`;
680
+ };
681
+ /**
682
+ * Apply an alter table operation to a Prisma schema string, modifying the model
683
+ * block for the specified table by adding and removing fields as needed.
684
+ *
685
+ * @param schema The current Prisma schema string.
686
+ * @param operation The schema table alter operation to apply.
687
+ * @returns The updated Prisma schema string with the modified model block.
688
+ */
689
+ const applyAlterTableOperation = (schema, operation) => {
690
+ const model = findModelBlock(schema, operation.table);
691
+ if (!model) throw new ArkormException(`Prisma model for table [${operation.table}] was not found.`);
692
+ let block = model.block;
693
+ const bodyLines = block.split("\n");
694
+ operation.dropColumns.forEach((column) => {
695
+ const columnRegex = new RegExp(`^\\s*${escapeRegex(column)}\\s+`);
696
+ for (let index = 0; index < bodyLines.length; index += 1) if (columnRegex.test(bodyLines[index])) {
697
+ bodyLines.splice(index, 1);
698
+ return;
699
+ }
700
+ });
701
+ operation.addColumns.forEach((column) => {
702
+ const fieldLine = buildFieldLine(column);
703
+ const columnRegex = new RegExp(`^\\s*${escapeRegex(column.name)}\\s+`);
704
+ if (bodyLines.some((line) => columnRegex.test(line))) return;
705
+ const defaultInsertIndex = Math.max(1, bodyLines.length - 1);
706
+ const afterInsertIndex = typeof column.after === "string" && column.after.length > 0 ? bodyLines.findIndex((line) => new RegExp(`^\\s*${escapeRegex(column.after)}\\s+`).test(line)) : -1;
707
+ const insertIndex = afterInsertIndex > 0 ? Math.min(afterInsertIndex + 1, defaultInsertIndex) : defaultInsertIndex;
708
+ bodyLines.splice(insertIndex, 0, fieldLine);
709
+ });
710
+ (operation.addIndexes ?? []).forEach((index) => {
711
+ const indexLine = buildIndexLine(index);
712
+ if (bodyLines.some((line) => line.trim() === indexLine.trim())) return;
713
+ const insertIndex = Math.max(1, bodyLines.length - 1);
714
+ bodyLines.splice(insertIndex, 0, indexLine);
715
+ });
716
+ block = bodyLines.join("\n");
717
+ return `${schema.slice(0, model.start)}${block}${schema.slice(model.end)}`;
718
+ };
719
+ /**
720
+ * Apply a drop table operation to a Prisma schema string, removing the model block
721
+ * for the specified table.
722
+ */
723
+ const applyDropTableOperation = (schema, operation) => {
724
+ const model = findModelBlock(schema, operation.table);
725
+ if (!model) return schema;
726
+ const before = schema.slice(0, model.start).trimEnd();
727
+ const after = schema.slice(model.end).trimStart();
728
+ return `${before}${before && after ? "\n\n" : ""}${after}`;
729
+ };
730
+ /**
731
+ * The SchemaBuilder class provides a fluent interface for defining
732
+ * database schema operations in a migration, such as creating, altering, and
733
+ * dropping tables.
734
+ *
735
+ * @param schema The current Prisma schema string.
736
+ * @param operations The list of schema operations to apply.
737
+ * @returns The updated Prisma schema string after applying all operations.
738
+ */
739
+ const applyOperationsToPrismaSchema = (schema, operations) => {
740
+ return operations.reduce((current, operation) => {
741
+ if (operation.type === "createTable") return applyCreateTableOperation(current, operation);
742
+ if (operation.type === "alterTable") return applyAlterTableOperation(current, operation);
743
+ return applyDropTableOperation(current, operation);
744
+ }, schema);
745
+ };
746
+ /**
747
+ * Run a Prisma CLI command using npx, capturing and throwing any errors that occur.
748
+ *
749
+ * @param args The arguments to pass to the Prisma CLI command.
750
+ * @param cwd The current working directory to run the command in.
751
+ * @returns void
752
+ */
753
+ const runPrismaCommand = (args, cwd) => {
754
+ const command = (0, node_child_process.spawnSync)("npx", ["prisma", ...args], {
755
+ cwd,
756
+ encoding: "utf-8"
757
+ });
758
+ if (command.status === 0) return;
759
+ const errorOutput = [command.stdout, command.stderr].filter(Boolean).join("\n").trim();
760
+ throw new ArkormException(errorOutput ? `Prisma command failed: prisma ${args.join(" ")}\n${errorOutput}` : `Prisma command failed: prisma ${args.join(" ")}`);
761
+ };
762
+ /**
763
+ * Generate a new migration file with a given name and options, including
764
+ * writing the file to disk if specified.
765
+ *
766
+ * @param name
767
+ * @returns
768
+ */
769
+ const resolveMigrationClassName = (name) => {
770
+ const cleaned = name.replace(/[^a-zA-Z0-9]+/g, " ").trim();
771
+ if (!cleaned) return "GeneratedMigration";
772
+ return `${cleaned.split(/\s+/g).map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`).join("")}Migration`;
773
+ };
774
+ /**
775
+ * Pad a number with leading zeros to ensure it is at least two digits, for
776
+ * use in migration timestamps.
777
+ *
778
+ * @param value
779
+ * @returns
780
+ */
781
+ const pad = (value) => String(value).padStart(2, "0");
782
+ /**
783
+ * Create a timestamp string in the format YYYYMMDDHHMMSS for use in migration
784
+ * file names, based on the current date and time or a provided date.
785
+ *
786
+ * @param date
787
+ * @returns
788
+ */
789
+ const createMigrationTimestamp = (date = /* @__PURE__ */ new Date()) => {
790
+ return `${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}${pad(date.getHours())}${pad(date.getMinutes())}${pad(date.getSeconds())}`;
791
+ };
792
+ /**
793
+ * Convert a migration name to a slug suitable for use in a file name, by
794
+ * lowercasing and replacing non-alphanumeric characters with underscores.
795
+ *
796
+ * @param name
797
+ * @returns
798
+ */
799
+ const toMigrationFileSlug = (name) => {
800
+ return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "migration";
801
+ };
802
+ /**
803
+ * Build the source code for a new migration file based on a given class
804
+ * name, using a template with empty up and down methods.
805
+ *
806
+ * @param className
807
+ * @returns
808
+ */
809
+ const buildMigrationSource = (className, extension = "ts") => {
810
+ if (extension === "js") return [
811
+ "import { Migration } from 'arkormx'",
812
+ "",
813
+ `export default class ${className} extends Migration {`,
814
+ " /**",
815
+ " * @param {import('arkormx').SchemaBuilder} schema",
816
+ " * @returns {Promise<void>}",
817
+ " */",
818
+ " async up (schema) {",
819
+ " }",
820
+ "",
821
+ " /**",
822
+ " * @param {import('arkormx').SchemaBuilder} schema",
823
+ " * @returns {Promise<void>}",
824
+ " */",
825
+ " async down (schema) {",
826
+ " }",
827
+ "}",
828
+ ""
829
+ ].join("\n");
830
+ return [
831
+ "import { Migration, SchemaBuilder } from 'arkormx'",
832
+ "",
833
+ `export default class ${className} extends Migration {`,
834
+ " public async up (schema: SchemaBuilder): Promise<void> {",
835
+ " }",
836
+ "",
837
+ " public async down (schema: SchemaBuilder): Promise<void> {",
838
+ " }",
839
+ "}",
840
+ ""
841
+ ].join("\n");
842
+ };
843
+ /**
844
+ * Generate a new migration file with a given name and options, including
845
+ * writing the file to disk if specified, and return the details of the generated file.
846
+ *
847
+ * @param name
848
+ * @param options
849
+ * @returns
850
+ */
851
+ const generateMigrationFile = (name, options = {}) => {
852
+ const timestamp = createMigrationTimestamp(/* @__PURE__ */ new Date());
853
+ const fileSlug = toMigrationFileSlug(name);
854
+ const className = resolveMigrationClassName(name);
855
+ const extension = options.extension ?? "ts";
856
+ const directory = options.directory ?? (0, node_path.join)(process.cwd(), "database", "migrations");
857
+ const fileName = `${timestamp}_${fileSlug}.${extension}`;
858
+ const filePath = (0, node_path.join)(directory, fileName);
859
+ const content = buildMigrationSource(className, extension);
860
+ if (options.write ?? true) {
861
+ if (!(0, node_fs.existsSync)(directory)) (0, node_fs.mkdirSync)(directory, { recursive: true });
862
+ if ((0, node_fs.existsSync)(filePath)) throw new ArkormException(`Migration file already exists: ${filePath}`);
863
+ (0, node_fs.writeFileSync)(filePath, content);
864
+ }
865
+ return {
866
+ fileName,
867
+ filePath,
868
+ className,
869
+ content
870
+ };
871
+ };
872
+ /**
873
+ * Get the list of schema operations that would be performed by a given migration class when run in a specified direction (up or down), without actually applying them.
874
+ *
875
+ * @param migration The migration class or instance to analyze.
876
+ * @param direction The direction of the migration to plan for ('up' or 'down').
877
+ * @returns A promise that resolves to an array of schema operations that would be performed.
878
+ */
879
+ const getMigrationPlan = async (migration, direction = "up") => {
880
+ const instance = migration instanceof Migration ? migration : new migration();
881
+ const schema = new SchemaBuilder();
882
+ if (direction === "up") await instance.up(schema);
883
+ else await instance.down(schema);
884
+ return schema.getOperations();
885
+ };
886
+ /**
887
+ * Apply the schema operations defined in a migration to a Prisma schema
888
+ * file, updating the file on disk if specified, and return the updated
889
+ * schema and list of operations applied.
890
+ *
891
+ * @param migration The migration class or instance to apply.
892
+ * @param options Options for applying the migration, including schema path and write flag.
893
+ * @returns A promise that resolves to an object containing the updated schema, schema path, and list of operations applied.
894
+ */
895
+ const applyMigrationToPrismaSchema = async (migration, options = {}) => {
896
+ const schemaPath = options.schemaPath ?? (0, node_path.join)(process.cwd(), "prisma", "schema.prisma");
897
+ if (!(0, node_fs.existsSync)(schemaPath)) throw new ArkormException(`Prisma schema file not found: ${schemaPath}`);
898
+ const source = (0, node_fs.readFileSync)(schemaPath, "utf-8");
899
+ const operations = await getMigrationPlan(migration, "up");
900
+ const schema = applyOperationsToPrismaSchema(source, operations);
901
+ if (options.write ?? true) (0, node_fs.writeFileSync)(schemaPath, schema);
902
+ return {
903
+ schema,
904
+ schemaPath,
905
+ operations
906
+ };
907
+ };
908
+ /**
909
+ * Run a migration by applying its schema operations to a Prisma schema
910
+ * file, optionally generating Prisma client code and running migrations after
911
+ * applying the schema changes.
912
+ *
913
+ * @param migration The migration class or instance to run.
914
+ * @param options Options for running the migration, including schema path, write flag, and Prisma commands.
915
+ * @returns A promise that resolves to an object containing the schema path and list of operations applied.
916
+ */
917
+ const runMigrationWithPrisma = async (migration, options = {}) => {
918
+ const cwd = options.cwd ?? process.cwd();
919
+ const applied = await applyMigrationToPrismaSchema(migration, {
920
+ schemaPath: options.schemaPath ?? (0, node_path.join)(cwd, "prisma", "schema.prisma"),
921
+ write: options.write
922
+ });
923
+ const shouldGenerate = options.runGenerate ?? true;
924
+ const shouldMigrate = options.runMigrate ?? true;
925
+ const mode = options.migrateMode ?? "dev";
926
+ if (shouldGenerate) runPrismaCommand(["generate"], cwd);
927
+ if (shouldMigrate) if (mode === "deploy") runPrismaCommand(["migrate", "deploy"], cwd);
928
+ else runPrismaCommand([
929
+ "migrate",
930
+ "dev",
931
+ "--name",
932
+ options.migrationName ?? `arkorm_${createMigrationTimestamp()}`
933
+ ], cwd);
934
+ return {
935
+ schemaPath: applied.schemaPath,
936
+ operations: applied.operations
937
+ };
938
+ };
939
+
940
+ //#endregion
941
+ //#region src/helpers/runtime-config.ts
942
+ const resolveDefaultStubsPath = () => {
943
+ let current = path.default.dirname((0, url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
944
+ while (true) {
945
+ const packageJsonPath = path.default.join(current, "package.json");
946
+ const stubsPath = path.default.join(current, "stubs");
947
+ if ((0, fs.existsSync)(packageJsonPath) && (0, fs.existsSync)(stubsPath)) return stubsPath;
948
+ const parent = path.default.dirname(current);
949
+ if (parent === current) break;
950
+ current = parent;
951
+ }
952
+ return path.default.join(process.cwd(), "stubs");
953
+ };
954
+ const baseConfig = {
955
+ paths: {
956
+ stubs: resolveDefaultStubsPath(),
957
+ seeders: path.default.join(process.cwd(), "database", "seeders"),
958
+ models: path.default.join(process.cwd(), "src", "models"),
959
+ migrations: path.default.join(process.cwd(), "database", "migrations"),
960
+ factories: path.default.join(process.cwd(), "database", "factories"),
961
+ buildOutput: path.default.join(process.cwd(), "dist")
962
+ },
963
+ outputExt: "ts"
964
+ };
965
+ const userConfig = {
966
+ ...baseConfig,
967
+ paths: { ...baseConfig.paths ?? {} }
968
+ };
969
+ let runtimeConfigLoaded = false;
970
+ let runtimeConfigLoadingPromise;
971
+ let runtimeClientResolver;
972
+ let runtimePaginationURLDriverFactory;
973
+ const mergePathConfig = (paths) => {
974
+ const defaults = baseConfig.paths ?? {};
975
+ const current = userConfig.paths ?? {};
976
+ const incoming = Object.entries(paths ?? {}).reduce((all, [key, value]) => {
977
+ if (typeof value === "string" && value.trim().length > 0) all[key] = value;
978
+ return all;
979
+ }, {});
980
+ return {
981
+ ...defaults,
982
+ ...current,
983
+ ...incoming
984
+ };
985
+ };
986
+ /**
987
+ * Define the ArkORM runtime configuration. This function can be used to provide.
988
+ *
989
+ * @param config The ArkORM configuration object.
990
+ * @returns The same configuration object.
991
+ */
992
+ const defineConfig = (config) => {
993
+ return config;
994
+ };
995
+ /**
996
+ * Get the user-provided ArkORM configuration.
997
+ *
998
+ * @returns The user-provided ArkORM configuration object.
999
+ */
1000
+ const getUserConfig = (key) => {
1001
+ if (key) return userConfig[key];
1002
+ return userConfig;
1003
+ };
1004
+ /**
1005
+ * Configure the ArkORM runtime with the provided Prisma client resolver and
1006
+ * delegate mapping resolver.
1007
+ *
1008
+ * @param prisma
1009
+ * @param mapping
1010
+ */
1011
+ const configureArkormRuntime = (prisma, options = {}) => {
1012
+ const nextConfig = {
1013
+ ...userConfig,
1014
+ prisma,
1015
+ paths: mergePathConfig(options.paths)
1016
+ };
1017
+ if (options.pagination !== void 0) nextConfig.pagination = options.pagination;
1018
+ if (options.outputExt !== void 0) nextConfig.outputExt = options.outputExt;
1019
+ Object.assign(userConfig, { ...nextConfig });
1020
+ runtimeClientResolver = prisma;
1021
+ runtimePaginationURLDriverFactory = nextConfig.pagination?.urlDriver;
1022
+ };
1023
+ /**
1024
+ * Reset the ArkORM runtime configuration.
1025
+ * This is primarily intended for testing purposes.
1026
+ */
1027
+ const resetArkormRuntimeForTests = () => {
1028
+ Object.assign(userConfig, {
1029
+ ...baseConfig,
1030
+ paths: { ...baseConfig.paths ?? {} }
1031
+ });
1032
+ runtimeConfigLoaded = false;
1033
+ runtimeConfigLoadingPromise = void 0;
1034
+ runtimeClientResolver = void 0;
1035
+ runtimePaginationURLDriverFactory = void 0;
1036
+ };
1037
+ /**
1038
+ * Resolve a Prisma client instance from the provided resolver, which can be either
1039
+ * a direct client instance or a function that returns a client instance.
1040
+ *
1041
+ * @param resolver
1042
+ * @returns
1043
+ */
1044
+ const resolveClient = (resolver) => {
1045
+ if (!resolver) return void 0;
1046
+ const client = typeof resolver === "function" ? resolver() : resolver;
1047
+ if (!client || typeof client !== "object") return void 0;
1048
+ return client;
1049
+ };
1050
+ /**
1051
+ * Resolve and apply the ArkORM configuration from an imported module.
1052
+ * This function checks for a default export and falls back to the module itself, then validates
1053
+ * the configuration object and applies it to the runtime if valid.
1054
+ *
1055
+ * @param imported
1056
+ * @returns
1057
+ */
1058
+ const resolveAndApplyConfig = (imported) => {
1059
+ const config = imported?.default ?? imported;
1060
+ if (!config || typeof config !== "object" || !config.prisma) return;
1061
+ configureArkormRuntime(config.prisma, {
1062
+ pagination: config.pagination,
1063
+ paths: config.paths,
1064
+ outputExt: config.outputExt
1065
+ });
1066
+ runtimeConfigLoaded = true;
1067
+ };
1068
+ /**
1069
+ * Dynamically import a configuration file.
1070
+ * A cache-busting query parameter is appended to ensure the latest version is loaded.
1071
+ *
1072
+ * @param configPath
1073
+ * @returns A promise that resolves to the imported configuration module.
1074
+ */
1075
+ const importConfigFile = (configPath) => {
1076
+ return import(`${(0, url.pathToFileURL)(configPath).href}?arkorm_runtime=${Date.now()}`);
1077
+ };
1078
+ const loadRuntimeConfigSync = () => {
1079
+ const require = (0, module$1.createRequire)(require("url").pathToFileURL(__filename).href);
1080
+ const syncConfigPaths = [path.default.join(process.cwd(), "arkormx.config.cjs")];
1081
+ for (const configPath of syncConfigPaths) {
1082
+ if (!(0, fs.existsSync)(configPath)) continue;
1083
+ try {
1084
+ resolveAndApplyConfig(require(configPath));
1085
+ return true;
1086
+ } catch {
1087
+ continue;
1088
+ }
1089
+ }
1090
+ return false;
1091
+ };
1092
+ /**
1093
+ * Load the ArkORM configuration by searching for configuration files in the
1094
+ * current working directory.
1095
+ * @returns
1096
+ */
1097
+ const loadArkormConfig = async () => {
1098
+ if (runtimeConfigLoaded) return;
1099
+ if (runtimeConfigLoadingPromise) return await runtimeConfigLoadingPromise;
1100
+ if (loadRuntimeConfigSync()) return;
1101
+ runtimeConfigLoadingPromise = (async () => {
1102
+ const configPaths = [path.default.join(process.cwd(), "arkormx.config.js"), path.default.join(process.cwd(), "arkormx.config.ts")];
1103
+ for (const configPath of configPaths) {
1104
+ if (!(0, fs.existsSync)(configPath)) continue;
1105
+ try {
1106
+ resolveAndApplyConfig(await importConfigFile(configPath));
1107
+ return;
1108
+ } catch {
1109
+ continue;
1110
+ }
1111
+ }
1112
+ runtimeConfigLoaded = true;
1113
+ })();
1114
+ await runtimeConfigLoadingPromise;
1115
+ };
1116
+ /**
1117
+ * Ensure that the ArkORM configuration is loaded.
1118
+ * This function can be called to trigger the loading process if it hasn't already been initiated.
1119
+ * If the configuration is already loaded, it will return immediately.
1120
+ *
1121
+ * @returns
1122
+ */
1123
+ const ensureArkormConfigLoading = () => {
1124
+ if (runtimeConfigLoaded) return;
1125
+ if (!runtimeConfigLoadingPromise) loadArkormConfig();
1126
+ };
1127
+ const getDefaultStubsPath = () => {
1128
+ return resolveDefaultStubsPath();
1129
+ };
1130
+ /**
1131
+ * Get the runtime Prisma client.
1132
+ * This function will trigger the loading of the ArkORM configuration if
1133
+ * it hasn't already been loaded.
1134
+ *
1135
+ * @returns
1136
+ */
1137
+ const getRuntimePrismaClient = () => {
1138
+ if (!runtimeConfigLoaded) loadRuntimeConfigSync();
1139
+ return resolveClient(runtimeClientResolver);
1140
+ };
1141
+ /**
1142
+ * Get the configured pagination URL driver factory from runtime config.
1143
+ *
1144
+ * @returns
1145
+ */
1146
+ const getRuntimePaginationURLDriverFactory = () => {
1147
+ if (!runtimeConfigLoaded) loadRuntimeConfigSync();
1148
+ return runtimePaginationURLDriverFactory;
1149
+ };
1150
+ /**
1151
+ * Check if a given value is a Prisma delegate-like object
1152
+ * by verifying the presence of common delegate methods.
1153
+ *
1154
+ * @param value The value to check.
1155
+ * @returns True if the value is a Prisma delegate-like object, false otherwise.
1156
+ */
1157
+ const isDelegateLike = (value) => {
1158
+ if (!value || typeof value !== "object") return false;
1159
+ const candidate = value;
1160
+ return [
1161
+ "findMany",
1162
+ "findFirst",
1163
+ "create",
1164
+ "update",
1165
+ "delete",
1166
+ "count"
1167
+ ].every((method) => typeof candidate[method] === "function");
1168
+ };
1169
+ loadArkormConfig();
1170
+
1171
+ //#endregion
1172
+ //#region src/cli/CliApp.ts
1173
+ /**
1174
+ * Main application class for the Arkormˣ CLI.
1175
+ *
1176
+ * @author Legacy (3m1n3nc3)
1177
+ * @since 0.1.0
1178
+ */
1179
+ var CliApp = class {
1180
+ command;
1181
+ config = {};
1182
+ constructor() {
1183
+ this.config = getUserConfig();
1184
+ }
1185
+ /**
1186
+ * Get the current configuration object or a specific configuration value.
1187
+ *
1188
+ * @param key Optional specific configuration key to retrieve
1189
+ * @returns The entire configuration object or the value of the specified key
1190
+ */
1191
+ getConfig = getUserConfig;
1192
+ /**
1193
+ * Utility to ensure directory exists
1194
+ *
1195
+ * @param filePath
1196
+ */
1197
+ ensureDirectory(filePath) {
1198
+ const dir = (0, path.dirname)(filePath);
1199
+ if (!(0, fs.existsSync)(dir)) (0, fs.mkdirSync)(dir, { recursive: true });
1200
+ }
1201
+ /**
1202
+ * Convert absolute paths under current working directory into relative display paths.
1203
+ *
1204
+ * @param filePath
1205
+ * @returns
1206
+ */
1207
+ formatPathForLog(filePath) {
1208
+ const relPath = (0, path.relative)(process.cwd(), filePath);
1209
+ if (!relPath) return ".";
1210
+ if (relPath.startsWith("..")) return filePath;
1211
+ return relPath;
1212
+ }
1213
+ /**
1214
+ * Utility to format a value for logging, converting absolute paths under current
1215
+ * working directory into relative display paths.
1216
+ *
1217
+ * @param name
1218
+ * @param value
1219
+ * @returns
1220
+ */
1221
+ splitLogger(name, value) {
1222
+ value = value.includes(process.cwd()) ? this.formatPathForLog(value) : value;
1223
+ return _h3ravel_shared.Logger.twoColumnDetail(name + " ", " " + value, false).join("");
1224
+ }
1225
+ hasTypeScriptInstalled() {
1226
+ try {
1227
+ (0, module$1.createRequire)(require("url").pathToFileURL(__filename).href).resolve("typescript", { paths: [process.cwd()] });
1228
+ return true;
1229
+ } catch {
1230
+ return false;
1231
+ }
1232
+ }
1233
+ resolveOutputExt() {
1234
+ const preferred = this.getConfig("outputExt") === "js" ? "js" : "ts";
1235
+ if (preferred === "ts" && !this.hasTypeScriptInstalled()) return "js";
1236
+ return preferred;
1237
+ }
1238
+ stripKnownSourceExtension(value) {
1239
+ return value.replace(/\.(ts|tsx|mts|cts|js|mjs|cjs)$/i, "");
1240
+ }
1241
+ /**
1242
+ * Resolve a directory path to runtime output when the source path is unavailable.
1243
+ *
1244
+ * @param directoryPath
1245
+ * @returns
1246
+ */
1247
+ resolveRuntimeDirectoryPath(directoryPath) {
1248
+ if ((0, fs.existsSync)(directoryPath)) return directoryPath;
1249
+ const { buildOutput } = this.getConfig("paths") || {};
1250
+ if (typeof buildOutput !== "string" || buildOutput.trim().length === 0) return directoryPath;
1251
+ const relativeSource = (0, path.relative)(process.cwd(), directoryPath);
1252
+ if (!relativeSource || relativeSource.startsWith("..")) return directoryPath;
1253
+ const mappedDirectory = (0, path.join)(buildOutput, relativeSource);
1254
+ return (0, fs.existsSync)(mappedDirectory) ? mappedDirectory : directoryPath;
1255
+ }
1256
+ /**
1257
+ * Resolve a script file path for runtime execution.
1258
+ * If a .ts file is provided, tries equivalent .js/.cjs/.mjs files first.
1259
+ * Also attempts mapped paths inside paths.buildOutput preserving structure.
1260
+ *
1261
+ * @param filePath
1262
+ * @returns
1263
+ */
1264
+ resolveRuntimeScriptPath(filePath) {
1265
+ const extension = (0, path.extname)(filePath).toLowerCase();
1266
+ const isTsFile = extension === ".ts" || extension === ".mts" || extension === ".cts";
1267
+ const candidates = [];
1268
+ if (isTsFile) {
1269
+ const base = filePath.slice(0, -extension.length);
1270
+ candidates.push(`${base}.js`, `${base}.cjs`, `${base}.mjs`);
1271
+ }
1272
+ const { buildOutput } = this.getConfig("paths") ?? {};
1273
+ if (typeof buildOutput === "string" && buildOutput.trim().length > 0) {
1274
+ const relativeSource = (0, path.relative)(process.cwd(), filePath);
1275
+ if (relativeSource && !relativeSource.startsWith("..")) {
1276
+ const mappedFile = (0, path.join)(buildOutput, relativeSource);
1277
+ const mappedExtension = (0, path.extname)(mappedFile).toLowerCase();
1278
+ if (mappedExtension === ".ts" || mappedExtension === ".mts" || mappedExtension === ".cts") {
1279
+ const mappedBase = mappedFile.slice(0, -mappedExtension.length);
1280
+ candidates.push(`${mappedBase}.js`, `${mappedBase}.cjs`, `${mappedBase}.mjs`);
1281
+ } else candidates.push(mappedFile);
1282
+ }
1283
+ }
1284
+ const runtimeMatch = candidates.find((path$2) => (0, fs.existsSync)(path$2));
1285
+ if (runtimeMatch) return runtimeMatch;
1286
+ return filePath;
1287
+ }
1288
+ /**
1289
+ * Utility to generate file from stub
1290
+ *
1291
+ * @param stubPath
1292
+ * @param outputPath
1293
+ * @param replacements
1294
+ */
1295
+ generateFile(stubPath, outputPath, replacements, options) {
1296
+ if ((0, fs.existsSync)(outputPath) && !options?.force) {
1297
+ this.command.error(`Error: ${this.formatPathForLog(outputPath)} already exists.`);
1298
+ process.exit(1);
1299
+ } else if ((0, fs.existsSync)(outputPath) && options?.force) (0, fs.rmSync)(outputPath);
1300
+ let content = (0, fs.readFileSync)(stubPath, "utf-8");
1301
+ for (const [key, value] of Object.entries(replacements)) content = content.replace(new RegExp(`{{${key}}}`, "g"), value);
1302
+ this.ensureDirectory(outputPath);
1303
+ (0, fs.writeFileSync)(outputPath, content);
1304
+ return outputPath;
1305
+ }
1306
+ /**
1307
+ * Resolve a configuration path with a fallback default
1308
+ *
1309
+ * @param key The configuration key to resolve
1310
+ * @param fallback The fallback value if the configuration key is not set
1311
+ * @returns The resolved configuration path
1312
+ */
1313
+ resolveConfigPath(key, fallback) {
1314
+ const { [key]: configured } = this.getConfig("paths") ?? {};
1315
+ if (typeof configured === "string" && configured.trim().length > 0) return configured;
1316
+ return fallback;
1317
+ }
1318
+ /**
1319
+ * Resolve the path to a stub file based on configuration
1320
+ *
1321
+ * @param stubName
1322
+ * @returns
1323
+ */
1324
+ resolveStubPath(stubName) {
1325
+ return (0, path.join)(this.resolveConfigPath("stubs", getDefaultStubsPath()), stubName);
1326
+ }
1327
+ /**
1328
+ * Generate a factory file for a given model name.
1329
+ *
1330
+ * @param name
1331
+ * @param options
1332
+ * @returns
1333
+ */
1334
+ makeFactory(name, options = {}) {
1335
+ const baseName = (0, _h3ravel_support.str)(name.replace(/Factory$/, "")).pascal();
1336
+ const factoryName = `${baseName}Factory`;
1337
+ const modelName = options.modelName ? (0, _h3ravel_support.str)(options.modelName).pascal() : baseName;
1338
+ const outputExt = this.resolveOutputExt();
1339
+ const outputPath = (0, path.join)(this.resolveConfigPath("factories", (0, path.join)(process.cwd(), "database", "factories")), `${factoryName}.${outputExt}`);
1340
+ const modelPath = (0, path.join)(this.resolveConfigPath("models", (0, path.join)(process.cwd(), "src", "models")), `${modelName}.${outputExt}`);
1341
+ const relativeImport = options.modelImportPath ?? `./${this.stripKnownSourceExtension((0, path.relative)((0, path.dirname)(outputPath), modelPath).replace(/\\/g, "/"))}${outputExt === "js" ? ".js" : ""}`;
1342
+ const stubPath = this.resolveStubPath(outputExt === "js" ? "factory.js.stub" : "factory.stub");
1343
+ return {
1344
+ name: factoryName,
1345
+ path: this.generateFile(stubPath, outputPath, {
1346
+ FactoryName: factoryName,
1347
+ ModelName: modelName.toString(),
1348
+ ModelImportPath: relativeImport.startsWith(".") ? relativeImport : `./${relativeImport}`
1349
+ }, options)
1350
+ };
1351
+ }
1352
+ /**
1353
+ * Generate a seeder file for a given name.
1354
+ *
1355
+ * @param name
1356
+ * @param options
1357
+ * @returns
1358
+ */
1359
+ makeSeeder(name, options = {}) {
1360
+ const seederName = `${(0, _h3ravel_support.str)(name.replace(/Seeder$/, "")).pascal()}Seeder`;
1361
+ const outputExt = this.resolveOutputExt();
1362
+ const outputPath = (0, path.join)(this.resolveConfigPath("seeders", (0, path.join)(process.cwd(), "database", "seeders")), `${seederName}.${outputExt}`);
1363
+ const stubPath = this.resolveStubPath(outputExt === "js" ? "seeder.js.stub" : "seeder.stub");
1364
+ return {
1365
+ name: seederName,
1366
+ path: this.generateFile(stubPath, outputPath, { SeederName: seederName }, options)
1367
+ };
1368
+ }
1369
+ /**
1370
+ * Generate a migration file for a given name.
1371
+ *
1372
+ * @param name The name of the migration.
1373
+ * @returns An object containing the name and path of the generated migration file.
1374
+ */
1375
+ makeMigration(name) {
1376
+ const generated = generateMigrationFile(name, {
1377
+ directory: this.resolveConfigPath("migrations", (0, path.join)(process.cwd(), "database", "migrations")),
1378
+ extension: this.resolveOutputExt()
1379
+ });
1380
+ return {
1381
+ name: generated.className,
1382
+ path: generated.filePath
1383
+ };
1384
+ }
1385
+ /**
1386
+ * Generate a model file along with optional factory, seeder, and migration files.
1387
+ *
1388
+ * @param name
1389
+ * @param options
1390
+ * @returns
1391
+ */
1392
+ makeModel(name, options = {}) {
1393
+ const baseName = (0, _h3ravel_support.str)(name.replace(/Model$/, "")).pascal().toString();
1394
+ const modelName = `${baseName}`;
1395
+ const delegateName = (0, _h3ravel_support.str)(baseName).camel().plural().toString();
1396
+ const outputExt = this.resolveOutputExt();
1397
+ const outputPath = (0, path.join)(this.resolveConfigPath("models", (0, path.join)(process.cwd(), "src", "models")), `${modelName}.${outputExt}`);
1398
+ const shouldBuildFactory = options.all || options.factory;
1399
+ const shouldBuildSeeder = options.all || options.seeder;
1400
+ const shouldBuildMigration = options.all || options.migration;
1401
+ const factoryName = `${baseName}Factory`;
1402
+ const factoryPath = (0, path.join)(this.resolveConfigPath("factories", (0, path.join)(process.cwd(), "database", "factories")), `${factoryName}.${outputExt}`);
1403
+ const factoryImportPath = `./${(0, path.relative)((0, path.dirname)(outputPath), factoryPath).replace(/\\/g, "/").replace(/\.(ts|tsx|mts|cts|js|mjs|cjs)$/i, "")}${outputExt === "js" ? ".js" : ""}`;
1404
+ const stubPath = this.resolveStubPath(outputExt === "js" ? "model.js.stub" : "model.stub");
1405
+ const modelPath = this.generateFile(stubPath, outputPath, {
1406
+ ModelName: modelName,
1407
+ DelegateName: delegateName,
1408
+ FactoryImport: shouldBuildFactory ? `import { ${factoryName} } from '${factoryImportPath}'\n` : "",
1409
+ FactoryLink: shouldBuildFactory ? outputExt === "js" ? `\n static factoryClass = ${factoryName}` : `\n protected static override factoryClass = ${factoryName}` : ""
1410
+ }, options);
1411
+ const prisma = this.ensurePrismaModelEntry(modelName, delegateName);
1412
+ const created = {
1413
+ model: {
1414
+ name: modelName,
1415
+ path: modelPath
1416
+ },
1417
+ prisma,
1418
+ factory: void 0,
1419
+ seeder: void 0,
1420
+ migration: void 0
1421
+ };
1422
+ if (shouldBuildFactory) created.factory = this.makeFactory(baseName, {
1423
+ force: options.force,
1424
+ modelName,
1425
+ modelImportPath: `./${(0, path.relative)((0, path.dirname)(factoryPath), outputPath).replace(/\\/g, "/").replace(/\.(ts|tsx|mts|cts|js|mjs|cjs)$/i, "")}${outputExt === "js" ? ".js" : ""}`
1426
+ });
1427
+ if (shouldBuildSeeder) created.seeder = this.makeSeeder(baseName, { force: options.force });
1428
+ if (shouldBuildMigration) created.migration = this.makeMigration(`create ${delegateName} table`);
1429
+ return created;
1430
+ }
1431
+ /**
1432
+ * Ensure that the Prisma schema has a model entry for the given model
1433
+ * and delegate names.
1434
+ * If the entry does not exist, it will be created with a default `id` field.
1435
+ *
1436
+ * @param modelName The name of the model to ensure in the Prisma schema.
1437
+ * @param delegateName The name of the delegate (table) to ensure in the Prisma schema.
1438
+ */
1439
+ ensurePrismaModelEntry(modelName, delegateName) {
1440
+ const schemaPath = (0, path.join)(process.cwd(), "prisma", "schema.prisma");
1441
+ if (!(0, fs.existsSync)(schemaPath)) return {
1442
+ path: schemaPath,
1443
+ updated: false
1444
+ };
1445
+ const source = (0, fs.readFileSync)(schemaPath, "utf-8");
1446
+ const existingByTable = findModelBlock(source, delegateName);
1447
+ const existingByName = new RegExp(`model\\s+${modelName}\\s*\\{`, "m").test(source);
1448
+ if (existingByTable || existingByName) return {
1449
+ path: schemaPath,
1450
+ updated: false
1451
+ };
1452
+ (0, fs.writeFileSync)(schemaPath, applyCreateTableOperation(source, {
1453
+ type: "createTable",
1454
+ table: delegateName,
1455
+ columns: [{
1456
+ name: "id",
1457
+ type: "id",
1458
+ primary: true
1459
+ }]
1460
+ }));
1461
+ return {
1462
+ path: schemaPath,
1463
+ updated: true
1464
+ };
1465
+ }
1466
+ /**
1467
+ * Convert a Prisma scalar type to its corresponding TypeScript type.
1468
+ *
1469
+ * @param value The Prisma scalar type.
1470
+ * @returns The corresponding TypeScript type.
1471
+ */
1472
+ prismaTypeToTs(value) {
1473
+ if (value === "Int" || value === "Float" || value === "Decimal") return "number";
1474
+ if (value === "BigInt") return "bigint";
1475
+ if (value === "String") return "string";
1476
+ if (value === "Boolean") return "boolean";
1477
+ if (value === "DateTime") return "Date";
1478
+ if (value === "Json") return "Record<string, unknown>";
1479
+ if (value === "Bytes") return "Buffer";
1480
+ return "unknown";
1481
+ }
1482
+ /**
1483
+ * Parse the Prisma schema to extract model definitions and their fields, focusing
1484
+ * on scalar types.
1485
+ *
1486
+ * @param schema The Prisma schema as a string.
1487
+ * @returns An array of model definitions with their fields.
1488
+ */
1489
+ parsePrismaModels(schema) {
1490
+ const models = [];
1491
+ const modelRegex = /model\s+(\w+)\s*\{([\s\S]*?)\n\}/g;
1492
+ const scalarTypes = new Set([
1493
+ "Int",
1494
+ "Float",
1495
+ "Decimal",
1496
+ "BigInt",
1497
+ "String",
1498
+ "Boolean",
1499
+ "DateTime",
1500
+ "Json",
1501
+ "Bytes"
1502
+ ]);
1503
+ for (const match of schema.matchAll(modelRegex)) {
1504
+ const name = match[1];
1505
+ const body = match[2];
1506
+ const table = body.match(/@@map\("([^"]+)"\)/)?.[1] ?? `${name.charAt(0).toLowerCase()}${name.slice(1)}s`;
1507
+ const fields = [];
1508
+ body.split("\n").forEach((rawLine) => {
1509
+ const line = rawLine.trim();
1510
+ if (!line || line.startsWith("@@") || line.startsWith("//")) return;
1511
+ const fieldMatch = line.match(/^(\w+)\s+([A-Za-z]+)(\?)?\b/);
1512
+ if (!fieldMatch) return;
1513
+ const fieldType = fieldMatch[2];
1514
+ if (!scalarTypes.has(fieldType)) return;
1515
+ fields.push({
1516
+ name: fieldMatch[1],
1517
+ type: this.prismaTypeToTs(fieldType),
1518
+ optional: Boolean(fieldMatch[3])
1519
+ });
1520
+ });
1521
+ models.push({
1522
+ name,
1523
+ table,
1524
+ fields
1525
+ });
1526
+ }
1527
+ return models;
1528
+ }
1529
+ /**
1530
+ * Sync model attribute declarations in a model file based on the
1531
+ * provided declarations.
1532
+ * This method takes the source code of a model file and a list of
1533
+ * attribute declarations,
1534
+ *
1535
+ * @param modelSource The source code of the model file.
1536
+ * @param declarations A list of attribute declarations to sync.
1537
+ * @returns An object containing the updated content and a flag indicating if it was updated.
1538
+ */
1539
+ syncModelDeclarations(modelSource, declarations) {
1540
+ const lines = modelSource.split("\n");
1541
+ const classIndex = lines.findIndex((line) => /export\s+class\s+\w+\s+extends\s+Model<.+>\s*\{/.test(line));
1542
+ if (classIndex < 0) return {
1543
+ content: modelSource,
1544
+ updated: false
1545
+ };
1546
+ let classEndIndex = -1;
1547
+ let depth = 0;
1548
+ for (let index = classIndex; index < lines.length; index += 1) {
1549
+ const line = lines[index];
1550
+ depth += (line.match(/\{/g) || []).length;
1551
+ depth -= (line.match(/\}/g) || []).length;
1552
+ if (depth === 0) {
1553
+ classEndIndex = index;
1554
+ break;
1555
+ }
1556
+ }
1557
+ if (classEndIndex < 0) return {
1558
+ content: modelSource,
1559
+ updated: false
1560
+ };
1561
+ const withoutDeclares = lines.slice(classIndex + 1, classEndIndex).filter((line) => !/^\s*declare\s+\w+\??:\s*[^\n]+$/.test(line));
1562
+ const rebuiltClass = [...declarations.map((declaration) => ` ${declaration}`), ...withoutDeclares];
1563
+ const content = [
1564
+ ...lines.slice(0, classIndex + 1),
1565
+ ...rebuiltClass,
1566
+ ...lines.slice(classEndIndex)
1567
+ ].join("\n");
1568
+ return {
1569
+ content,
1570
+ updated: content !== modelSource
1571
+ };
1572
+ }
1573
+ /**
1574
+ * Sync model attribute declarations in model files based on the Prisma schema.
1575
+ * This method reads the Prisma schema to extract model definitions and their
1576
+ * scalar fields, then updates the corresponding model files to include `declare`
1577
+ * statements for these fields. It returns an object containing the paths of the
1578
+ * schema and models, the total number of model files processed, and lists of
1579
+ * updated and skipped files.
1580
+ *
1581
+ * @param options Optional parameters to specify custom paths for the Prisma schema and models directory.
1582
+ * @returns An object with details about the synchronization process, including updated and skipped files.
1583
+ */
1584
+ syncModelsFromPrisma(options = {}) {
1585
+ const schemaPath = options.schemaPath ?? (0, path.join)(process.cwd(), "prisma", "schema.prisma");
1586
+ const modelsDir = options.modelsDir ?? this.resolveConfigPath("models", (0, path.join)(process.cwd(), "src", "models"));
1587
+ if (!(0, fs.existsSync)(schemaPath)) throw new Error(`Prisma schema file not found: ${schemaPath}`);
1588
+ if (!(0, fs.existsSync)(modelsDir)) throw new Error(`Models directory not found: ${modelsDir}`);
1589
+ const schema = (0, fs.readFileSync)(schemaPath, "utf-8");
1590
+ const prismaModels = this.parsePrismaModels(schema);
1591
+ const modelFiles = (0, fs.readdirSync)(modelsDir).filter((file) => file.endsWith(".ts"));
1592
+ const updated = [];
1593
+ const skipped = [];
1594
+ modelFiles.forEach((file) => {
1595
+ const filePath = (0, path.join)(modelsDir, file);
1596
+ const source = (0, fs.readFileSync)(filePath, "utf-8");
1597
+ const classMatch = source.match(/export\s+class\s+(\w+)\s+extends\s+Model<'([^']+)'>/);
1598
+ if (!classMatch) {
1599
+ skipped.push(filePath);
1600
+ return;
1601
+ }
1602
+ const className = classMatch[1];
1603
+ const delegate = classMatch[2];
1604
+ const prismaModel = prismaModels.find((model) => model.table === delegate) ?? prismaModels.find((model) => model.name === className);
1605
+ if (!prismaModel || prismaModel.fields.length === 0) {
1606
+ skipped.push(filePath);
1607
+ return;
1608
+ }
1609
+ const declarations = prismaModel.fields.map((field) => `declare ${field.name}${field.optional ? "?" : ""}: ${field.type}`);
1610
+ const synced = this.syncModelDeclarations(source, declarations);
1611
+ if (!synced.updated) {
1612
+ skipped.push(filePath);
1613
+ return;
1614
+ }
1615
+ (0, fs.writeFileSync)(filePath, synced.content);
1616
+ updated.push(filePath);
1617
+ });
1618
+ return {
1619
+ schemaPath,
1620
+ modelsDir,
1621
+ total: modelFiles.length,
1622
+ updated,
1623
+ skipped
1624
+ };
1625
+ }
1626
+ };
1627
+
1628
+ //#endregion
1629
+ //#region src/cli/commands/InitCommand.ts
1630
+ /**
1631
+ * The InitCommand class implements the CLI command for initializing Arkormˣ by creating
1632
+ * a default config file in the current directory.
1633
+ *
1634
+ * @author Legacy (3m1n3nc3)
1635
+ * @since 0.1.0
1636
+ */
1637
+ var InitCommand = class extends _h3ravel_musket.Command {
1638
+ signature = `init
13
1639
  {--force : Force overwrite if config file already exists (existing file will be backed up) }
14
- `;description=`Initialize Arkormˣ by creating a default config file in the current directory`;async handle(){this.app.command=this;let e=(0,f.join)(process.cwd(),`arkormx.config.js`),{stubs:t}=V(`paths`)??{},n=typeof t==`string`&&t.trim().length>0?t:G(),r=(0,f.join)(n,`arkormx.config.stub`),i=(0,f.join)(n,`arkorm.config.stub`),a=(0,c.existsSync)(r)?r:i;(0,c.existsSync)(e)&&!this.option(`force`)&&(this.error(`Error: Arkormˣ has already been initialized. Use --force to reinitialize.`),process.exit(1)),this.app.ensureDirectory(e),(0,c.existsSync)(e)&&this.option(`force`)&&(0,c.copyFileSync)(e,e.replace(/\.js$/,`.backup.${Date.now()}.js`)),(0,c.existsSync)(a)||(this.error(`Error: Missing config stub at ${r} (or ${i})`),process.exit(1)),(0,c.writeFileSync)(e,(0,c.readFileSync)(a,`utf-8`)),this.success(`Arkormˣ initialized successfully!`)}},Te=class extends g.Command{signature=`make:factory
1640
+ `;
1641
+ description = "Initialize Arkormˣ by creating a default config file in the current directory";
1642
+ /**
1643
+ * Command handler for the init command.
1644
+ */
1645
+ async handle() {
1646
+ this.app.command = this;
1647
+ const outputDir = (0, node_path.join)(process.cwd(), "arkormx.config.js");
1648
+ const { stubs } = getUserConfig("paths") ?? {};
1649
+ const stubsDir = typeof stubs === "string" && stubs.trim().length > 0 ? stubs : getDefaultStubsPath();
1650
+ const preferredStubPath = (0, node_path.join)(stubsDir, "arkormx.config.stub");
1651
+ const legacyStubPath = (0, node_path.join)(stubsDir, "arkorm.config.stub");
1652
+ const stubPath = (0, fs.existsSync)(preferredStubPath) ? preferredStubPath : legacyStubPath;
1653
+ if ((0, fs.existsSync)(outputDir) && !this.option("force")) {
1654
+ this.error("Error: Arkormˣ has already been initialized. Use --force to reinitialize.");
1655
+ process.exit(1);
1656
+ }
1657
+ this.app.ensureDirectory(outputDir);
1658
+ if ((0, fs.existsSync)(outputDir) && this.option("force")) (0, fs.copyFileSync)(outputDir, outputDir.replace(/\.js$/, `.backup.${Date.now()}.js`));
1659
+ if (!(0, fs.existsSync)(stubPath)) {
1660
+ this.error(`Error: Missing config stub at ${preferredStubPath} (or ${legacyStubPath})`);
1661
+ process.exit(1);
1662
+ }
1663
+ (0, fs.writeFileSync)(outputDir, (0, fs.readFileSync)(stubPath, "utf-8"));
1664
+ this.success("Arkormˣ initialized successfully!");
1665
+ }
1666
+ };
1667
+
1668
+ //#endregion
1669
+ //#region src/cli/commands/MakeFactoryCommand.ts
1670
+ /**
1671
+ * The MakeFactoryCommand class implements the CLI command for creating new factory classes.
1672
+ *
1673
+ * @author Legacy (3m1n3nc3)
1674
+ * @since 0.1.0
1675
+ */
1676
+ var MakeFactoryCommand = class extends _h3ravel_musket.Command {
1677
+ signature = `make:factory
15
1678
  {name : Name of the factory to create}
16
1679
  {--f|force : Overwrite existing file}
17
- `;description=`Create a new model factory class`;async handle(){this.app.command=this;let e=this.argument(`name`);if(!e)return void this.error(`Error: Name argument is required.`);let t=this.app.makeFactory(e,{force:this.option(`force`)});this.success(`Created factory: ${this.app.formatPathForLog(t.path)}`)}},Ee=class extends g.Command{signature=`make:migration
1680
+ `;
1681
+ description = "Create a new model factory class";
1682
+ /**
1683
+ * Command handler for the make:factory command.
1684
+ *
1685
+ * @returns
1686
+ */
1687
+ async handle() {
1688
+ this.app.command = this;
1689
+ const name = this.argument("name");
1690
+ if (!name) return void this.error("Error: Name argument is required.");
1691
+ const created = this.app.makeFactory(name, { force: this.option("force") });
1692
+ this.success(`Created factory: ${this.app.formatPathForLog(created.path)}`);
1693
+ }
1694
+ };
1695
+
1696
+ //#endregion
1697
+ //#region src/cli/commands/MakeMigrationCommand.ts
1698
+ /**
1699
+ * The MakeMigrationCommand class implements the CLI command for creating new migration classes.
1700
+ *
1701
+ * @author Legacy (3m1n3nc3)
1702
+ * @since 0.1.0
1703
+ */
1704
+ var MakeMigrationCommand = class extends _h3ravel_musket.Command {
1705
+ signature = `make:migration
18
1706
  {name : Name of the migration to create}
19
- `;description=`Create a new migration class file`;async handle(){this.app.command=this;let e=this.argument(`name`);if(!e)return void this.error(`Error: Name argument is required.`);let t=this.app.makeMigration(e);this.success(`Created migration: ${this.app.formatPathForLog(t.path)}`)}},De=class extends g.Command{signature=`make:model
1707
+ `;
1708
+ description = "Create a new migration class file";
1709
+ /**
1710
+ * Command handler for the make:migration command.
1711
+ *
1712
+ * @returns
1713
+ */
1714
+ async handle() {
1715
+ this.app.command = this;
1716
+ const name = this.argument("name");
1717
+ if (!name) return void this.error("Error: Name argument is required.");
1718
+ const created = this.app.makeMigration(name);
1719
+ this.success(`Created migration: ${this.app.formatPathForLog(created.path)}`);
1720
+ }
1721
+ };
1722
+
1723
+ //#endregion
1724
+ //#region src/cli/commands/MakeModelCommand.ts
1725
+ /**
1726
+ * The MakeModelCommand class implements the CLI command for creating new model
1727
+ * classes along with optional linked resources such as factories, seeders, and migrations.
1728
+ *
1729
+ * @author Legacy (3m1n3nc3)
1730
+ * @since 0.1.0
1731
+ */
1732
+ var MakeModelCommand = class extends _h3ravel_musket.Command {
1733
+ signature = `make:model
20
1734
  {name : Name of the model to create}
21
1735
  {--f|force : Overwrite existing files}
22
1736
  {--factory : Create and link a factory}
23
1737
  {--seeder : Create a seeder}
24
1738
  {--migration : Create a migration}
25
1739
  {--all : Create and link factory, seeder, and migration}
26
- `;description=`Create a new model and optional linked resources`;async handle(){this.app.command=this;let e=this.argument(`name`);if(!e)return void this.error(`Error: Name argument is required.`);let t=this.app.makeModel(e,this.options());this.success(`Created files:`),[[`Model`,t.model.path],[`Prisma schema ${t.prisma.updated?`(updated)`:`(already up to date)`}`,t.prisma.path],t.factory?[`Factory`,t.factory.path]:``,t.seeder?[`Seeder`,t.seeder.path]:``,t.migration?[`Migration`,t.migration.path]:``].filter(Boolean).map(([e,t])=>this.success(this.app.splitLogger(e,t)))}},Oe=class extends g.Command{signature=`make:seeder
1740
+ `;
1741
+ description = "Create a new model and optional linked resources";
1742
+ /**
1743
+ * Command handler for the make:model command.
1744
+ *
1745
+ * @returns
1746
+ */
1747
+ async handle() {
1748
+ this.app.command = this;
1749
+ const name = this.argument("name");
1750
+ if (!name) return void this.error("Error: Name argument is required.");
1751
+ const created = this.app.makeModel(name, this.options());
1752
+ this.success("Created files:");
1753
+ [
1754
+ ["Model", created.model.path],
1755
+ [`Prisma schema ${created.prisma.updated ? "(updated)" : "(already up to date)"}`, created.prisma.path],
1756
+ created.factory ? ["Factory", created.factory.path] : "",
1757
+ created.seeder ? ["Seeder", created.seeder.path] : "",
1758
+ created.migration ? ["Migration", created.migration.path] : ""
1759
+ ].filter(Boolean).map(([name, path]) => this.success(this.app.splitLogger(name, path)));
1760
+ }
1761
+ };
1762
+
1763
+ //#endregion
1764
+ //#region src/cli/commands/MakeSeederCommand.ts
1765
+ /**
1766
+ * The MakeSeederCommand class implements the CLI command for creating new seeder classes.
1767
+ *
1768
+ * @author Legacy (3m1n3nc3)
1769
+ * @since 0.1.0
1770
+ */
1771
+ var MakeSeederCommand = class extends _h3ravel_musket.Command {
1772
+ signature = `make:seeder
27
1773
  {name : Name of the seeder to create}
28
1774
  {--f|force : Overwrite existing file}
29
- `;description=`Create a new seeder class`;async handle(){this.app.command=this;let e=this.argument(`name`);if(!e)return void this.error(`Error: Name argument is required.`);let t=this.app.makeSeeder(e,this.options());this.success(`Created seeder: ${this.app.formatPathForLog(t.path)}`)}},ke=class extends g.Command{signature=`migrate
1775
+ `;
1776
+ description = "Create a new seeder class";
1777
+ /**
1778
+ * Command handler for the make:seeder command.
1779
+ */
1780
+ async handle() {
1781
+ this.app.command = this;
1782
+ const name = this.argument("name");
1783
+ if (!name) return void this.error("Error: Name argument is required.");
1784
+ const created = this.app.makeSeeder(name, this.options());
1785
+ this.success(`Created seeder: ${this.app.formatPathForLog(created.path)}`);
1786
+ }
1787
+ };
1788
+
1789
+ //#endregion
1790
+ //#region src/cli/commands/MigrateCommand.ts
1791
+ /**
1792
+ * The MigrateCommand class implements the CLI command for applying migration
1793
+ * classes to the Prisma schema and running the Prisma workflow.
1794
+ *
1795
+ * @author Legacy (3m1n3nc3)
1796
+ * @since 0.1.0
1797
+ */
1798
+ var MigrateCommand = class extends _h3ravel_musket.Command {
1799
+ signature = `migrate
30
1800
  {name? : Migration class or file name}
31
1801
  {--all : Run all migrations from the configured migrations directory}
32
1802
  {--deploy : Use prisma migrate deploy instead of migrate dev}
@@ -34,17 +1804,2907 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.
34
1804
  {--skip-migrate : Skip prisma migrate command}
35
1805
  {--schema= : Explicit prisma schema path}
36
1806
  {--migration-name= : Name for prisma migrate dev}
37
- `;description=`Apply migration classes to schema.prisma and run Prisma workflow`;async handle(){this.app.command=this;let e=this.app.getConfig(`paths`)?.migrations??(0,f.join)(process.cwd(),`database`,`migrations`),t=this.app.resolveRuntimeDirectoryPath(e);if(!(0,d.existsSync)(t))return void this.error(`Error: Migrations directory not found: ${this.app.formatPathForLog(e)}`);let n=this.option(`schema`)?(0,f.resolve)(String(this.option(`schema`))):(0,f.join)(process.cwd(),`prisma`,`schema.prisma`),r=this.option(`all`)?await this.loadAllMigrations(t):(await this.loadNamedMigration(t,this.argument(`name`))).filter(([e])=>e!==void 0);if(r.length===0)return void this.error(`Error: No migration classes found to run.`);for(let[e]of r)await P(e,{schemaPath:n,write:!0});this.option(`skip-generate`)||j([`generate`],process.cwd()),this.option(`skip-migrate`)||(this.option(`deploy`)?j([`migrate`,`deploy`],process.cwd()):j([`migrate`,`dev`,`--name`,this.option(`migration-name`)?String(this.option(`migration-name`)):`arkorm_cli_${Date.now()}`],process.cwd())),this.success(`Applied ${r.length} migration(s).`),r.forEach(([e,t])=>this.success(this.app.splitLogger(`Migrated`,t)))}async loadAllMigrations(e){let t=(0,d.readdirSync)(e).filter(e=>/\.(ts|js|mjs|cjs)$/i.test(e)).sort((e,t)=>e.localeCompare(t)).map(t=>this.app.resolveRuntimeScriptPath((0,f.join)(e,t)));return(await Promise.all(t.map(async e=>(await this.loadMigrationClassesFromFile(e)).map(t=>[t,e])))).flat()}async loadNamedMigration(e,t){if(!t)return[[void 0,``]];let n=t.replace(/Migration$/,``),r=[`${t}.ts`,`${t}.js`,`${t}.mjs`,`${t}.cjs`,`${n}Migration.ts`,`${n}Migration.js`,`${n}Migration.mjs`,`${n}Migration.cjs`].map(t=>(0,f.join)(e,t)).find(e=>(0,d.existsSync)(e));if(!r)return[[void 0,t]];let i=this.app.resolveRuntimeScriptPath(r);return(await this.loadMigrationClassesFromFile(i)).map(e=>[e,i])}async loadMigrationClassesFromFile(e){let t=await import(`${(0,_.pathToFileURL)((0,f.resolve)(e)).href}?arkorm_migrate=${Date.now()}`);return Object.values(t).filter(e=>typeof e==`function`?e.prototype instanceof b:!1)}},Ae=class extends g.Command{signature=`models:sync
1807
+ `;
1808
+ description = "Apply migration classes to schema.prisma and run Prisma workflow";
1809
+ /**
1810
+ * Command handler for the migrate command.
1811
+ * This method is responsible for orchestrating the migration
1812
+ * process, including loading migration classes, applying them to
1813
+ * the Prisma schema, and running the appropriate Prisma commands
1814
+ * based on the provided options.
1815
+ *
1816
+ * @returns
1817
+ */
1818
+ async handle() {
1819
+ this.app.command = this;
1820
+ const configuredMigrationsDir = this.app.getConfig("paths")?.migrations ?? (0, node_path.join)(process.cwd(), "database", "migrations");
1821
+ const migrationsDir = this.app.resolveRuntimeDirectoryPath(configuredMigrationsDir);
1822
+ if (!(0, node_fs.existsSync)(migrationsDir)) return void this.error(`Error: Migrations directory not found: ${this.app.formatPathForLog(configuredMigrationsDir)}`);
1823
+ const schemaPath = this.option("schema") ? (0, node_path.resolve)(String(this.option("schema"))) : (0, node_path.join)(process.cwd(), "prisma", "schema.prisma");
1824
+ const classes = this.option("all") ? await this.loadAllMigrations(migrationsDir) : (await this.loadNamedMigration(migrationsDir, this.argument("name"))).filter(([cls]) => cls !== void 0);
1825
+ if (classes.length === 0) return void this.error("Error: No migration classes found to run.");
1826
+ for (const [MigrationClassItem] of classes) await applyMigrationToPrismaSchema(MigrationClassItem, {
1827
+ schemaPath,
1828
+ write: true
1829
+ });
1830
+ if (!this.option("skip-generate")) runPrismaCommand(["generate"], process.cwd());
1831
+ if (!this.option("skip-migrate")) if (this.option("deploy")) runPrismaCommand(["migrate", "deploy"], process.cwd());
1832
+ else runPrismaCommand([
1833
+ "migrate",
1834
+ "dev",
1835
+ "--name",
1836
+ this.option("migration-name") ? String(this.option("migration-name")) : `arkorm_cli_${Date.now()}`
1837
+ ], process.cwd());
1838
+ this.success(`Applied ${classes.length} migration(s).`);
1839
+ classes.forEach(([_, file]) => this.success(this.app.splitLogger("Migrated", file)));
1840
+ }
1841
+ /**
1842
+ * Load all migration classes from the specified directory.
1843
+ *
1844
+ * @param migrationsDir The directory to load migration classes from.
1845
+ */
1846
+ async loadAllMigrations(migrationsDir) {
1847
+ const files = (0, node_fs.readdirSync)(migrationsDir).filter((file) => /\.(ts|js|mjs|cjs)$/i.test(file)).sort((left, right) => left.localeCompare(right)).map((file) => this.app.resolveRuntimeScriptPath((0, node_path.join)(migrationsDir, file)));
1848
+ return (await Promise.all(files.map(async (file) => (await this.loadMigrationClassesFromFile(file)).map((cls) => [cls, file])))).flat();
1849
+ }
1850
+ /**
1851
+ * Load migration classes from a specific file or by class name.
1852
+ *
1853
+ * @param migrationsDir
1854
+ * @param name
1855
+ * @returns
1856
+ */
1857
+ async loadNamedMigration(migrationsDir, name) {
1858
+ if (!name) return [[void 0, ""]];
1859
+ const base = name.replace(/Migration$/, "");
1860
+ const target = [
1861
+ `${name}.ts`,
1862
+ `${name}.js`,
1863
+ `${name}.mjs`,
1864
+ `${name}.cjs`,
1865
+ `${base}Migration.ts`,
1866
+ `${base}Migration.js`,
1867
+ `${base}Migration.mjs`,
1868
+ `${base}Migration.cjs`
1869
+ ].map((file) => (0, node_path.join)(migrationsDir, file)).find((file) => (0, node_fs.existsSync)(file));
1870
+ if (!target) return [[void 0, name]];
1871
+ const runtimeTarget = this.app.resolveRuntimeScriptPath(target);
1872
+ return (await this.loadMigrationClassesFromFile(runtimeTarget)).map((cls) => [cls, runtimeTarget]);
1873
+ }
1874
+ /**
1875
+ * Load migration classes from a given file path.
1876
+ *
1877
+ * @param filePath
1878
+ * @returns
1879
+ */
1880
+ async loadMigrationClassesFromFile(filePath) {
1881
+ const imported = await import(`${(0, node_url.pathToFileURL)((0, node_path.resolve)(filePath)).href}?arkorm_migrate=${Date.now()}`);
1882
+ return Object.values(imported).filter((value) => {
1883
+ if (typeof value !== "function") return false;
1884
+ return value.prototype instanceof Migration;
1885
+ });
1886
+ }
1887
+ };
1888
+
1889
+ //#endregion
1890
+ //#region src/cli/commands/ModelsSyncCommand.ts
1891
+ var ModelsSyncCommand = class extends _h3ravel_musket.Command {
1892
+ signature = `models:sync
38
1893
  {--schema= : Path to prisma schema file}
39
1894
  {--models= : Path to models directory}
40
- `;description=`Sync model declare attributes from prisma schema for all model files`;async handle(){this.app.command=this;let e=this.app.syncModelsFromPrisma({schemaPath:this.option(`schema`)?(0,f.resolve)(String(this.option(`schema`))):void 0,modelsDir:this.option(`models`)?(0,f.resolve)(String(this.option(`models`))):void 0}),t=e.updated.length===0?[this.app.splitLogger(`Updated`,`none`)]:e.updated.map(e=>this.app.splitLogger(`Updated`,e));this.success(`SUCCESS: Model sync completed with the following results:`),[this.app.splitLogger(`Schema`,e.schemaPath),this.app.splitLogger(`Models`,e.modelsDir),this.app.splitLogger(`Processed`,String(e.total)),...t,this.app.splitLogger(`Skipped`,String(e.skipped.length))].map(e=>this.success(e))}},je=class e{async call(...t){await e.runSeeders(...t)}static toSeederInstance(t){return t instanceof e?t:new t}static async runSeeders(...e){let t=e.reduce((e,t)=>Array.isArray(t)?[...e,...t]:(e.push(t),e),[]);for(let e of t)await this.toSeederInstance(e).run()}},Me=class extends g.Command{signature=`seed
1895
+ `;
1896
+ description = "Sync model declare attributes from prisma schema for all model files";
1897
+ async handle() {
1898
+ this.app.command = this;
1899
+ const result = this.app.syncModelsFromPrisma({
1900
+ schemaPath: this.option("schema") ? (0, node_path.resolve)(String(this.option("schema"))) : void 0,
1901
+ modelsDir: this.option("models") ? (0, node_path.resolve)(String(this.option("models"))) : void 0
1902
+ });
1903
+ const updatedLines = result.updated.length === 0 ? [this.app.splitLogger("Updated", "none")] : result.updated.map((path) => this.app.splitLogger("Updated", path));
1904
+ this.success("SUCCESS: Model sync completed with the following results:");
1905
+ [
1906
+ this.app.splitLogger("Schema", result.schemaPath),
1907
+ this.app.splitLogger("Models", result.modelsDir),
1908
+ this.app.splitLogger("Processed", String(result.total)),
1909
+ ...updatedLines,
1910
+ this.app.splitLogger("Skipped", String(result.skipped.length))
1911
+ ].map((line) => this.success(line));
1912
+ }
1913
+ };
1914
+
1915
+ //#endregion
1916
+ //#region src/database/Seeder.ts
1917
+ /**
1918
+ * The Seeder class serves as a base for defining database seeders, which are
1919
+ * used to populate the database with initial or test data.
1920
+ *
1921
+ * @author Legacy (3m1n3nc3)
1922
+ * @since 0.1.0
1923
+ */
1924
+ var Seeder = class Seeder {
1925
+ /**
1926
+ * Runs one or more seeders.
1927
+ *
1928
+ * @param seeders The seeders to be run.
1929
+ */
1930
+ async call(...seeders) {
1931
+ await Seeder.runSeeders(...seeders);
1932
+ }
1933
+ /**
1934
+ * Converts a SeederInput into a Seeder instance.
1935
+ *
1936
+ * @param input The SeederInput to convert.
1937
+ * @returns A Seeder instance.
1938
+ */
1939
+ static toSeederInstance(input) {
1940
+ if (input instanceof Seeder) return input;
1941
+ return new input();
1942
+ }
1943
+ /**
1944
+ * Runs the given seeders in sequence.
1945
+ *
1946
+ * @param seeders The seeders to be run.
1947
+ */
1948
+ static async runSeeders(...seeders) {
1949
+ const queue = seeders.reduce((all, current) => {
1950
+ if (Array.isArray(current)) return [...all, ...current];
1951
+ all.push(current);
1952
+ return all;
1953
+ }, []);
1954
+ for (const seeder of queue) await this.toSeederInstance(seeder).run();
1955
+ }
1956
+ };
1957
+
1958
+ //#endregion
1959
+ //#region src/cli/commands/SeedCommand.ts
1960
+ /**
1961
+ * The SeedCommand class implements the CLI command for running seeder classes.
1962
+ *
1963
+ * @author Legacy (3m1n3nc3)
1964
+ * @since 0.1.0
1965
+ */
1966
+ var SeedCommand = class extends _h3ravel_musket.Command {
1967
+ signature = `seed
41
1968
  {name? : Seeder class or file name}
42
1969
  {--all : Run all seeders in the configured seeders directory}
43
- `;description=`Run one or more seeders`;async handle(){this.app.command=this;let e=this.app.getConfig(`paths`)?.seeders??(0,f.join)(process.cwd(),`database`,`seeders`),t=this.app.resolveRuntimeDirectoryPath(e);if(!(0,d.existsSync)(t))return void this.error(`ERROR: Seeders directory not found: ${this.app.formatPathForLog(e)}`);let n=this.option(`all`)?await this.loadAllSeeders(t):await this.loadNamedSeeder(t,this.argument(`name`)??`DatabaseSeeder`);if(n.length===0)return void this.error(`ERROR: No seeder classes found to run.`);for(let e of n)await new e().run();this.success(`Database seeding completed`),n.forEach(e=>this.success(this.app.splitLogger(`Seeded`,e.name)))}async loadAllSeeders(e){let t=(0,d.readdirSync)(e).filter(e=>/\.(ts|js|mjs|cjs)$/i.test(e)).map(t=>this.app.resolveRuntimeScriptPath((0,f.join)(e,t)));return(await Promise.all(t.map(async e=>await this.loadSeederClassesFromFile(e)))).flat()}async loadNamedSeeder(e,t){let n=t.replace(/Seeder$/,``),r=[`${t}.ts`,`${t}.js`,`${t}.mjs`,`${t}.cjs`,`${n}Seeder.ts`,`${n}Seeder.js`,`${n}Seeder.mjs`,`${n}Seeder.cjs`].map(t=>(0,f.join)(e,t)).find(e=>(0,d.existsSync)(e));if(!r)return[];let i=this.app.resolveRuntimeScriptPath(r);return await this.loadSeederClassesFromFile(i)}async loadSeederClassesFromFile(e){let t=await import(`${(0,_.pathToFileURL)((0,f.resolve)(e)).href}?arkorm_seed=${Date.now()}`);return Object.values(t).filter(e=>typeof e==`function`?e.prototype instanceof je:!1)}};String.raw`
1970
+ `;
1971
+ description = "Run one or more seeders";
1972
+ /**
1973
+ * Command handler for the seed command.
1974
+ *
1975
+ * @returns
1976
+ */
1977
+ async handle() {
1978
+ this.app.command = this;
1979
+ const configuredSeedersDir = this.app.getConfig("paths")?.seeders ?? (0, node_path.join)(process.cwd(), "database", "seeders");
1980
+ const seedersDir = this.app.resolveRuntimeDirectoryPath(configuredSeedersDir);
1981
+ if (!(0, node_fs.existsSync)(seedersDir)) return void this.error(`ERROR: Seeders directory not found: ${this.app.formatPathForLog(configuredSeedersDir)}`);
1982
+ const classes = this.option("all") ? await this.loadAllSeeders(seedersDir) : await this.loadNamedSeeder(seedersDir, this.argument("name") ?? "DatabaseSeeder");
1983
+ if (classes.length === 0) return void this.error("ERROR: No seeder classes found to run.");
1984
+ for (const SeederClassItem of classes) await new SeederClassItem().run();
1985
+ this.success("Database seeding completed");
1986
+ classes.forEach((cls) => this.success(this.app.splitLogger("Seeded", cls.name)));
1987
+ }
1988
+ /**
1989
+ * Load all seeder classes from the specified directory.
1990
+ *
1991
+ * @param seedersDir
1992
+ * @returns
1993
+ */
1994
+ async loadAllSeeders(seedersDir) {
1995
+ const files = (0, node_fs.readdirSync)(seedersDir).filter((file) => /\.(ts|js|mjs|cjs)$/i.test(file)).map((file) => this.app.resolveRuntimeScriptPath((0, node_path.join)(seedersDir, file)));
1996
+ return (await Promise.all(files.map(async (file) => await this.loadSeederClassesFromFile(file)))).flat();
1997
+ }
1998
+ /**
1999
+ * Load seeder classes from a specific file or by class name.
2000
+ *
2001
+ * @param seedersDir
2002
+ * @param name
2003
+ * @returns
2004
+ */
2005
+ async loadNamedSeeder(seedersDir, name) {
2006
+ const base = name.replace(/Seeder$/, "");
2007
+ const target = [
2008
+ `${name}.ts`,
2009
+ `${name}.js`,
2010
+ `${name}.mjs`,
2011
+ `${name}.cjs`,
2012
+ `${base}Seeder.ts`,
2013
+ `${base}Seeder.js`,
2014
+ `${base}Seeder.mjs`,
2015
+ `${base}Seeder.cjs`
2016
+ ].map((file) => (0, node_path.join)(seedersDir, file)).find((file) => (0, node_fs.existsSync)(file));
2017
+ if (!target) return [];
2018
+ const runtimeTarget = this.app.resolveRuntimeScriptPath(target);
2019
+ return await this.loadSeederClassesFromFile(runtimeTarget);
2020
+ }
2021
+ /**
2022
+ * Load seeder classes from a given file path.
2023
+ *
2024
+ * @param filePath The path to the file containing seeder classes.
2025
+ * @returns An array of seeder classes.
2026
+ */
2027
+ async loadSeederClassesFromFile(filePath) {
2028
+ const imported = await import(`${(0, node_url.pathToFileURL)((0, node_path.resolve)(filePath)).href}?arkorm_seed=${Date.now()}`);
2029
+ return Object.values(imported).filter((value) => {
2030
+ if (typeof value !== "function") return false;
2031
+ return value.prototype instanceof Seeder;
2032
+ });
2033
+ }
2034
+ };
2035
+
2036
+ //#endregion
2037
+ //#region src/cli/logo.ts
2038
+ var logo_default = String.raw`
44
2039
  __/^^^^^^^^^^^^^^^^\__
45
2040
  ▄▄▄/ \▄▄
46
2041
  ▄██▀▀██▄ ▄▄
47
2042
  ███ ███ ████▄ ██ ▄█▀ ▄███▄ ████▄ ███▄███▄
48
2043
  ███▀▀███ ██ ▀▀ ████ ██ ██ ██ ▀▀ ██ ██ ██
49
2044
  ███ ███ ██ ██ ▀█▄ ▀███▀ ██ ██ ██ ██
50
- `;var J=class extends te.Collection{},Ne=class{amount=1;sequence=0;states=[];count(e){return this.amount=Math.max(1,Math.floor(e)),this}state(e){return this.states.push(e),this}make(e={}){let t=this.buildAttributes(e);return new this.model(t)}makeMany(e=this.amount,t={}){let n=Math.max(1,Math.floor(e));return Array.from({length:n},()=>this.make(t))}async create(e={}){let t=this.make(e);if(typeof t.save!=`function`)throw Error(`Factory model does not support save().`);return await t.save()}async createMany(e=this.amount,t={}){let n=this.makeMany(e,t);return await Promise.all(n.map(async e=>{if(typeof e.save!=`function`)throw Error(`Factory model does not support save().`);return await e.save()}))}buildAttributes(e){let t=this.sequence;this.sequence+=1;let n=this.definition(t);for(let e of this.states)n=e(n,t);return{...n,...e}}},Pe=class extends Ne{model;constructor(e,t){super(),this.resolver=t,this.model=e}definition(e){return this.resolver(e)}};const Fe=(e,t)=>new Pe(e,t);var Y=class extends y{constructor(e=`No query results for the given model.`){super(e),this.name=`ModelNotFoundException`}};function Ie(e){return Object.entries(e).reduce((e,[t,n])=>(q(n)&&(e[t]=n),e),{})}function Le(e){return Ie(e)}function Re(e){return`${e.charAt(0).toLowerCase()}${e.slice(1)}s`}var X=class{constraint=null;constrain(e){if(!this.constraint)return this.constraint=e,this;let t=this.constraint;return this.constraint=n=>{let r=t(n)??n;return e(r)??r},this}where(e){return this.constrain(t=>t.where(e))}whereKey(e,t){return this.constrain(n=>n.whereKey(e,t))}whereIn(e,t){return this.constrain(n=>n.whereIn(e,t))}orderBy(e){return this.constrain(t=>t.orderBy(e))}include(e){return this.constrain(t=>t.include(e))}with(e){return this.constrain(t=>t.with(e))}select(e){return this.constrain(t=>t.select(e))}skip(e){return this.constrain(t=>t.skip(e))}take(e){return this.constrain(t=>t.take(e))}withTrashed(){return this.constrain(e=>e.withTrashed())}onlyTrashed(){return this.constrain(e=>e.onlyTrashed())}withoutTrashed(){return this.constrain(e=>e.withoutTrashed())}scope(e,...t){return this.constrain(n=>n.scope(e,...t))}applyConstraint(e){return this.constraint?this.constraint(e)??e:e}async get(){return this.getResults()}async first(){let e=await this.getResults();return e instanceof J?e.all()[0]??null:e}},ze=class extends X{constructor(e,t,n,r,i,a,o){super(),this.parent=e,this.related=t,this.throughDelegate=n,this.foreignPivotKey=r,this.relatedPivotKey=i,this.parentKey=a,this.relatedKey=o}async getResults(){let e=this.parent.getAttribute(this.parentKey),t=(await this.related.getDelegate(this.throughDelegate).findMany({where:{[this.foreignPivotKey]:e}})).map(e=>e[this.relatedPivotKey]);return this.applyConstraint(this.related.query().where({[this.relatedKey]:{in:t}})).get()}},Be=class extends X{constructor(e,t,n,r){super(),this.parent=e,this.related=t,this.foreignKey=n,this.ownerKey=r}async getResults(){let e=this.parent.getAttribute(this.foreignKey);return this.applyConstraint(this.related.query().where({[this.ownerKey]:e})).first()}},Ve=class extends X{constructor(e,t,n,r){super(),this.parent=e,this.related=t,this.foreignKey=n,this.localKey=r}async getResults(){let e=this.parent.getAttribute(this.localKey);return this.applyConstraint(this.related.query().where({[this.foreignKey]:e})).get()}},He=class extends X{constructor(e,t,n,r,i,a,o){super(),this.parent=e,this.related=t,this.throughDelegate=n,this.firstKey=r,this.secondKey=i,this.localKey=a,this.secondLocalKey=o}async getResults(){let e=this.parent.getAttribute(this.localKey),t=(await this.related.getDelegate(this.throughDelegate).findMany({where:{[this.firstKey]:e}})).map(e=>e[this.secondLocalKey]);return this.applyConstraint(this.related.query().where({[this.secondKey]:{in:t}})).get()}},Ue=class extends X{constructor(e,t,n,r){super(),this.parent=e,this.related=t,this.foreignKey=n,this.localKey=r}async getResults(){let e=this.parent.getAttribute(this.localKey);return this.applyConstraint(this.related.query().where({[this.foreignKey]:e})).first()}},We=class extends X{constructor(e,t,n,r,i,a,o){super(),this.parent=e,this.related=t,this.throughDelegate=n,this.firstKey=r,this.secondKey=i,this.localKey=a,this.secondLocalKey=o}async getResults(){let e=this.parent.getAttribute(this.localKey),t=await this.related.getDelegate(this.throughDelegate).findFirst({where:{[this.firstKey]:e}});return t?this.applyConstraint(this.related.query().where({[this.secondKey]:t[this.secondLocalKey]})).first():null}},Ge=class extends X{constructor(e,t,n,r){super(),this.parent=e,this.related=t,this.morphName=n,this.localKey=r}async getResults(){let e=this.parent.getAttribute(this.localKey),t=this.parent.constructor.name;return this.applyConstraint(this.related.query().where({[`${this.morphName}Id`]:e,[`${this.morphName}Type`]:t})).get()}},Ke=class extends X{constructor(e,t,n,r){super(),this.parent=e,this.related=t,this.morphName=n,this.localKey=r}async getResults(){let e=this.parent.getAttribute(this.localKey),t=this.parent.constructor.name;return this.applyConstraint(this.related.query().where({[`${this.morphName}Id`]:e,[`${this.morphName}Type`]:t})).first()}},qe=class extends X{constructor(e,t,n,r,i,a,o){super(),this.parent=e,this.related=t,this.throughDelegate=n,this.morphName=r,this.relatedPivotKey=i,this.parentKey=a,this.relatedKey=o}async getResults(){let e=this.parent.getAttribute(this.parentKey),t=this.parent.constructor.name,n=(await this.related.getDelegate(this.throughDelegate).findMany({where:{[`${this.morphName}Id`]:e,[`${this.morphName}Type`]:t}})).map(e=>e[this.relatedPivotKey]);return this.applyConstraint(this.related.query().where({[this.relatedKey]:{in:n}})).get()}},Z=class e{static DEFAULT_PAGE_NAME=`page`;path;query;fragment;pageName;constructor(t={}){this.path=t.path??`/`,this.query=t.query??{},this.fragment=t.fragment??``,this.pageName=t.pageName??e.DEFAULT_PAGE_NAME}getPageName(){return this.pageName}url(e){let t=Math.max(1,e),[n,r=``]=this.path.split(`?`),i=new URLSearchParams(r);Object.entries(this.query).forEach(([e,t])=>{if(t==null){i.delete(e);return}i.set(e,String(t))}),i.set(this.pageName,String(t));let a=i.toString(),o=this.fragment.replace(/^#/,``);return!a&&!o?n:o?a?`${n}?${a}#${o}`:`${n}#${o}`:`${n}?${a}`}},Q=class{data;meta;urlDriver;constructor(e,t,n,r,i={}){let a=Math.max(1,Math.ceil(t/n)),o=t===0?null:(r-1)*n+1,s=t===0?null:Math.min(r*n,t);this.data=e;let c=K();this.urlDriver=c?c(i):new Z(i),this.meta={total:t,perPage:n,currentPage:r,lastPage:a,from:o,to:s}}getPageName(){return this.urlDriver.getPageName()}url(e){return this.urlDriver.url(e)}nextPageUrl(){return this.meta.currentPage>=this.meta.lastPage?null:this.url(this.meta.currentPage+1)}previousPageUrl(){return this.meta.currentPage<=1?null:this.url(this.meta.currentPage-1)}firstPageUrl(){return this.url(1)}lastPageUrl(){return this.url(this.meta.lastPage)}toJSON(){return{data:this.data,meta:this.meta,links:{first:this.firstPageUrl(),last:this.lastPageUrl(),prev:this.previousPageUrl(),next:this.nextPageUrl()}}}},$=class{data;meta;urlDriver;constructor(e,t,n,r,i={}){let a=e.all().length,o=a===0?null:(n-1)*t+1,s=a===0?null:(o??1)+a-1;this.data=e;let c=K();this.urlDriver=c?c(i):new Z(i),this.meta={perPage:t,currentPage:n,from:o,to:s,hasMorePages:r}}getPageName(){return this.urlDriver.getPageName()}url(e){return this.urlDriver.url(e)}nextPageUrl(){return this.meta.hasMorePages?this.url(this.meta.currentPage+1):null}previousPageUrl(){return this.meta.currentPage<=1?null:this.url(this.meta.currentPage-1)}toJSON(){return{data:this.data,meta:this.meta,links:{prev:this.previousPageUrl(),next:this.nextPageUrl()}}}},Je=class e{args={};eagerLoads={};includeTrashed=!1;onlyTrashedRecords=!1;randomOrderEnabled=!1;relationFilters=[];relationAggregates=[];constructor(e,t){this.delegate=e,this.model=t}where(e){return this.addLogicalWhere(`AND`,e)}orWhere(e){return this.addLogicalWhere(`OR`,e)}whereNot(e){return this.where({NOT:e})}orWhereNot(e){return this.orWhere({NOT:e})}whereNull(e){return this.where({[e]:null})}whereNotNull(e){return this.where({[e]:{not:null}})}whereBetween(e,t){let[n,r]=t;return this.where({[e]:{gte:n,lte:r}})}whereDate(e,t){let n=this.coerceDate(t),r=new Date(Date.UTC(n.getUTCFullYear(),n.getUTCMonth(),n.getUTCDate())),i=new Date(r);return i.setUTCDate(i.getUTCDate()+1),this.where({[e]:{gte:r,lt:i}})}whereMonth(e,t,n=new Date().getUTCFullYear()){let r=Math.min(12,Math.max(1,t)),i=new Date(Date.UTC(n,r-1,1)),a=new Date(Date.UTC(n,r,1));return this.where({[e]:{gte:i,lt:a}})}whereYear(e,t){let n=new Date(Date.UTC(t,0,1)),r=new Date(Date.UTC(t+1,0,1));return this.where({[e]:{gte:n,lt:r}})}whereKeyNot(e,t){return this.where({[e]:{not:t}})}orWhereIn(e,t){return this.orWhere({[e]:{in:t}})}whereNotIn(e,t){return this.where({[e]:{notIn:t}})}orWhereNotIn(e,t){return this.orWhere({[e]:{notIn:t}})}async firstWhere(e,t,n){let r=n!==void 0,i=r?t:`=`,a=r?n:t;return this.clone().where(this.buildComparisonWhere(e,i,a)).first()}addLogicalWhere(e,t){return this.args.where?(this.args.where={[e]:[this.args.where,t]},this):(this.args.where=t,this)}buildComparisonWhere(e,t,n){return t===`=`?{[e]:n}:t===`!=`?{[e]:{not:n}}:t===`>`?{[e]:{gt:n}}:t===`>=`?{[e]:{gte:n}}:t===`<`?{[e]:{lt:n}}:{[e]:{lte:n}}}coerceDate(e){let t=e instanceof Date?new Date(e.getTime()):new Date(e);if(Number.isNaN(t.getTime()))throw new y(`Invalid date value for date-based query helper.`);return t}whereKey(e,t){return this.where({[e]:t})}whereIn(e,t){return this.where({[e]:{in:t}})}orderBy(e){return this.randomOrderEnabled=!1,this.args.orderBy=e,this}inRandomOrder(){return this.randomOrderEnabled=!0,this}reorder(e,t=`asc`){return this.args.orderBy=void 0,this.randomOrderEnabled=!1,e?this.orderBy({[e]:t}):this}latest(e=`createdAt`){return this.orderBy({[e]:`desc`})}oldest(e=`createdAt`){return this.orderBy({[e]:`asc`})}include(e){return this.args.include=e,this}with(e){let t=this.normalizeWith(e),n=Object.keys(t);return this.args.include={...this.args.include||{},...n.reduce((e,t)=>(e[t]=!0,e),{})},Object.entries(t).forEach(([e,t])=>{this.eagerLoads[e]=t}),this}has(e,t=`>=`,n=1,r){return this.relationFilters.push({relation:e,callback:r,operator:t,count:n,boolean:`AND`}),this}orHas(e,t=`>=`,n=1){return this.relationFilters.push({relation:e,operator:t,count:n,boolean:`OR`}),this}doesntHave(e,t){return this.has(e,`<`,1,t)}orDoesntHave(e){return this.orHas(e,`<`,1)}whereHas(e,t,n=`>=`,r=1){return this.has(e,n,r,t)}orWhereHas(e,t,n=`>=`,r=1){return this.relationFilters.push({relation:e,callback:t,operator:n,count:r,boolean:`OR`}),this}whereDoesntHave(e,t){return this.whereHas(e,t,`<`,1)}orWhereDoesntHave(e,t){return this.orWhereHas(e,t,`<`,1)}withCount(e){return(Array.isArray(e)?e:[e]).forEach(e=>{this.relationAggregates.push({type:`count`,relation:e})}),this}withExists(e){return(Array.isArray(e)?e:[e]).forEach(e=>{this.relationAggregates.push({type:`exists`,relation:e})}),this}withSum(e,t){return this.relationAggregates.push({type:`sum`,relation:e,column:t}),this}withAvg(e,t){return this.relationAggregates.push({type:`avg`,relation:e,column:t}),this}withMin(e,t){return this.relationAggregates.push({type:`min`,relation:e,column:t}),this}withMax(e,t){return this.relationAggregates.push({type:`max`,relation:e,column:t}),this}withTrashed(){return this.includeTrashed=!0,this.onlyTrashedRecords=!1,this}onlyTrashed(){return this.onlyTrashedRecords=!0,this.includeTrashed=!1,this}withoutTrashed(){return this.includeTrashed=!1,this.onlyTrashedRecords=!1,this}scope(e,...t){let n=`scope${e.charAt(0).toUpperCase()}${e.slice(1)}`,r=this.model.prototype?.[n];if(typeof r!=`function`)throw new y(`Scope [${e}] is not defined.`);let i=r.call(void 0,this,...t);return i&&i!==this?i:this}when(e,t,n){let r=typeof e==`function`?e():e;return r?t(this,r):n?n(this,r):this}unless(e,t,n){let r=typeof e==`function`?e():e;return r?n?n(this,r):this:t(this,r)}tap(e){return e(this),this}pipe(e){return e(this)}select(e){return this.args.select=e,this}skip(e){return this.args.skip=e,this}offset(e){return this.skip(e)}take(e){return this.args.take=e,this}limit(e){return this.take(e)}forPage(e,t=15){let n=Math.max(1,e),r=Math.max(1,t);return this.skip((n-1)*r).take(r)}async get(){let e=new WeakMap,t=await this.delegate.findMany(this.buildFindArgs()),n=this.randomOrderEnabled?this.shuffleRows(t):t,r=this.model.hydrateMany(n),i=r;if(this.hasRelationFilters())if(this.hasOrRelationFilters()&&this.args.where){let t=new Set(r.map(e=>this.getModelId(e)).filter(e=>e!=null)),n=await this.delegate.findMany({...this.args,where:this.buildSoftDeleteOnlyWhere()}),a=this.model.hydrateMany(n);i=await this.filterModelsByRelationConstraints(a,e,t)}else i=await this.filterModelsByRelationConstraints(r,e);return this.hasRelationAggregates()&&await this.applyRelationAggregates(i,e),await Promise.all(i.map(async e=>{await e.load(this.eagerLoads)})),new J(i)}async first(){if(this.hasRelationFilters()||this.hasRelationAggregates())return(await this.get()).all()[0]??null;if(this.randomOrderEnabled){let e=await this.delegate.findMany(this.buildFindArgs());if(e.length===0)return null;let t=this.shuffleRows(e)[0];if(!t)return null;let n=this.model.hydrate(t);return await n.load(this.eagerLoads),n}let e=await this.delegate.findFirst(this.buildFindArgs());if(!e)return null;let t=this.model.hydrate(e);return await t.load(this.eagerLoads),t}async firstOrFail(){let e=await this.first();if(!e)throw new Y(`Record not found.`);return e}async find(e,t=`id`){return this.where({[t]:e}).first()}async findOr(e,t,n){let r=typeof t==`string`?t:`id`,i=typeof t==`function`?t:n;if(!i)throw new y(`findOr requires a fallback callback.`);return await this.find(e,r)||i()}async value(e){let t=await this.delegate.findFirst(this.buildFindArgs());return t?t[e]??null:null}async valueOrFail(e){let t=await this.value(e);if(t==null)throw new Y(`Record not found.`);return t}async pluck(e,t){let n=await this.delegate.findMany(this.buildFindArgs());return t?new J(n.sort((e,n)=>String(e[t]).localeCompare(String(n[t]))).map(t=>t[e])):new J(n.map(t=>t[e]))}async create(e){let t=await this.delegate.create({data:e});return this.model.hydrate(t)}async update(e){let t=this.buildWhere();if(!t)throw new y(`Update requires a where clause.`);let n=await this.resolveUniqueWhere(t),r=await this.delegate.update({where:n,data:e});return this.model.hydrate(r)}async delete(){let e=this.buildWhere();if(!e)throw new y(`Delete requires a where clause.`);let t=await this.resolveUniqueWhere(e),n=await this.delegate.delete({where:t});return this.model.hydrate(n)}async count(){return this.hasRelationFilters()?(await this.get()).all().length:this.delegate.count({where:this.buildWhere()})}async exists(){return this.hasRelationFilters()?await this.count()>0:await this.delegate.findFirst(this.buildFindArgs())!=null}async doesntExist(){return!await this.exists()}async existsOr(e){return await this.exists()?!0:e()}async doesntExistOr(e){return await this.doesntExist()?!0:e()}async min(e){let t=await this.delegate.findMany(this.buildFindArgs());if(t.length===0)return null;let n=t.map(t=>t[e]).filter(e=>e!=null);return n.length===0?null:n.reduce((e,t)=>t<e?t:e)}async max(e){let t=await this.delegate.findMany(this.buildFindArgs());if(t.length===0)return null;let n=t.map(t=>t[e]).filter(e=>e!=null);return n.length===0?null:n.reduce((e,t)=>t>e?t:e)}async sum(e){return(await this.delegate.findMany(this.buildFindArgs())).reduce((t,n)=>{let r=n[e],i=typeof r==`number`?r:Number(r);return Number.isFinite(i)?t+i:t},0)}async avg(e){let t=(await this.delegate.findMany(this.buildFindArgs())).map(t=>{let n=t[e];return typeof n==`number`?n:Number(n)}).filter(e=>Number.isFinite(e));return t.length===0?null:t.reduce((e,t)=>e+t,0)/t.length}whereRaw(e,t=[]){let n=this.delegate;if(typeof n.applyRawWhere!=`function`)throw new y(`Raw where clauses are not supported by the current adapter.`);return this.args.where=n.applyRawWhere(this.buildWhere(),e,t),this}orWhereRaw(e,t=[]){let n=this.delegate;if(typeof n.applyRawWhere!=`function`)throw new y(`Raw where clauses are not supported by the current adapter.`);let r=n.applyRawWhere(void 0,e,t);return this.orWhere(r)}async paginate(e=1,t=15,n={}){if(this.hasRelationFilters()||this.hasRelationAggregates()){let r=Math.max(1,e),i=Math.max(1,t),a=(await this.get()).all(),o=(r-1)*i;return new Q(new J(a.slice(o,o+i)),a.length,i,r,n)}let r=Math.max(1,e),i=Math.max(1,t),a=await this.count();return new Q(await this.clone().skip((r-1)*i).take(i).get(),a,i,r,n)}async simplePaginate(e=15,t=1,n={}){if(this.hasRelationFilters()||this.hasRelationAggregates()){let r=Math.max(1,t),i=Math.max(1,e),a=(await this.get()).all(),o=(r-1)*i,s=a.slice(o,o+i),c=o+i<a.length;return new $(new J(s),i,r,c,n)}let r=Math.max(1,t),i=Math.max(1,e),a=await this.clone().skip((r-1)*i).take(i+1).get(),o=a.all().length>i;return new $(o?new J(a.all().slice(0,i)):a,i,r,o,n)}clone(){let t=new e(this.delegate,this.model);return t.args.where=this.args.where,t.args.include=this.args.include,t.args.orderBy=this.args.orderBy,t.args.select=this.args.select,t.args.skip=this.args.skip,t.args.take=this.args.take,t.includeTrashed=this.includeTrashed,t.onlyTrashedRecords=this.onlyTrashedRecords,t.randomOrderEnabled=this.randomOrderEnabled,this.relationFilters.forEach(e=>{t.relationFilters.push({...e})}),this.relationAggregates.forEach(e=>{t.relationAggregates.push({...e})}),Object.entries(this.eagerLoads).forEach(([e,n])=>{t.eagerLoads[e]=n}),t}normalizeWith(e){return typeof e==`string`?{[e]:void 0}:Array.isArray(e)?e.reduce((e,t)=>(e[t]=void 0,e),{}):e}buildWhere(){let e=this.model.getSoftDeleteConfig();if(!e.enabled||this.includeTrashed)return this.args.where;let t=this.onlyTrashedRecords?{[e.column]:{not:null}}:{[e.column]:null};return this.args.where?{AND:[this.args.where,t]}:t}buildFindArgs(){return{...this.args,where:this.buildWhere()}}async resolveUniqueWhere(e){if(this.isUniqueWhere(e))return e;let t=await this.delegate.findFirst({where:e});if(!t)throw new y(`Record not found for update/delete operation.`);let n=t;if(!Object.prototype.hasOwnProperty.call(n,`id`))throw new y(`Unable to resolve a unique identifier for update/delete operation. Include an id in the query constraints.`);return{id:n.id}}isUniqueWhere(e){return Object.keys(e).length===1&&Object.prototype.hasOwnProperty.call(e,`id`)}shuffleRows(e){let t=[...e];for(let e=t.length-1;e>0;e--){let n=Math.floor(Math.random()*(e+1)),r=t[e];t[e]=t[n],t[n]=r}return t}hasRelationFilters(){return this.relationFilters.length>0}hasOrRelationFilters(){return this.relationFilters.some(e=>e.boolean===`OR`)}hasRelationAggregates(){return this.relationAggregates.length>0}async filterModelsByRelationConstraints(e,t,n){return(await Promise.all(e.map(async e=>{let r=null;n&&(r=n.has(this.getModelId(e)));for(let n of this.relationFilters){let i=await this.resolveRelatedCount(e,n.relation,t,n.callback),a=this.compareCount(i,n.operator,n.count);r=r==null?a:n.boolean===`AND`?r&&a:r||a}return{model:e,passes:r??!0}}))).filter(e=>e.passes).map(e=>e.model)}getModelId(e){let t=e;if(typeof t.getAttribute!=`function`)return null;let n=t.getAttribute(`id`);return typeof n==`number`||typeof n==`string`?n:null}buildSoftDeleteOnlyWhere(){let e=this.model.getSoftDeleteConfig();if(e.enabled&&!this.includeTrashed)return this.onlyTrashedRecords?{[e.column]:{not:null}}:{[e.column]:null}}async applyRelationAggregates(e,t){let n=t??new WeakMap;await Promise.all(e.map(async e=>{for(let t of this.relationAggregates){let r=await this.resolveRelatedResults(e,t.relation,n),i=Array.isArray(r)?r:r?[r]:[],a=this.buildAggregateAttributeKey(t);if(t.type===`count`){this.assignAggregate(e,a,i.length);continue}if(t.type===`exists`){this.assignAggregate(e,a,i.length>0);continue}let o=i.map(e=>e.getAttribute(t.column)).filter(e=>e!=null);if(t.type===`sum`){let t=o.reduce((e,t)=>{let n=typeof t==`number`?t:Number(t);return Number.isFinite(n)?e+n:e},0);this.assignAggregate(e,a,t);continue}if(t.type===`avg`){let t=o.map(e=>typeof e==`number`?e:Number(e)).filter(e=>Number.isFinite(e)),n=t.length===0?null:t.reduce((e,t)=>e+t,0)/t.length;this.assignAggregate(e,a,n);continue}if(t.type===`min`){let t=o.length===0?null:o.reduce((e,t)=>t<e?t:e);this.assignAggregate(e,a,t);continue}let s=o.length===0?null:o.reduce((e,t)=>t>e?t:e);this.assignAggregate(e,a,s)}}))}async resolveRelatedCount(e,t,n,r){let i=await this.resolveRelatedResults(e,t,n,r);return Array.isArray(i)?i.length:i?1:0}async resolveRelatedResults(e,t,n,r){let i=e,a=r??`__none__`,o=n.get(i);o||(o=new Map,n.set(i,o));let s=o.get(t);s||(s=new Map,o.set(t,s));let c=s.get(a);if(c)return await c;let l=(async()=>{let n=e[t];if(typeof n!=`function`)throw new y(`Relation [${t}] is not defined on the model.`);let i=n.call(e);if(r&&typeof i.constrain==`function`&&i.constrain(e=>r(e)??e),typeof i.get==`function`){let e=await i.get();return e instanceof J?e.all():e}if(typeof i.getResults==`function`){let e=await i.getResults();return e instanceof J?e.all():e}throw new y(`Relation [${t}] does not support result resolution.`)})();return s.set(a,l),await l}compareCount(e,t,n){return t===`>=`?e>=n:t===`>`?e>n:t===`=`?e===n:t===`!=`?e!==n:t===`<=`?e<=n:e<n}buildAggregateAttributeKey(e){let t=e.relation;if(e.type===`count`)return`${t}Count`;if(e.type===`exists`)return`${t}Exists`;let n=e.column?`${e.column.charAt(0).toUpperCase()}${e.column.slice(1)}`:``;return`${t}${`${e.type.charAt(0).toUpperCase()}${e.type.slice(1)}`}${n}`}assignAggregate(e,t,n){let r=e;if(typeof r.setAttribute==`function`){r.setAttribute(t,n);return}e[t]=n}},Ye=class e{static factoryClass;static client;static delegate;static softDeletes=!1;static deletedAtColumn=`deletedAt`;static globalScopes={};static eventListeners={};casts={};hidden=[];visible=[];appends=[];attributes;constructor(e={}){return this.attributes={},this.fill(e),new Proxy(this,{get:(e,t,n)=>typeof t!=`string`||t in e?Reflect.get(e,t,n):e.getAttribute(t),set:(e,t,n,r)=>typeof t!=`string`||t in e?Reflect.set(e,t,n,r):(e.setAttribute(t,n),!0)})}static setClient(e){this.client=e}static setFactory(e){this.factoryClass=e}static factory(e){let t=this.factoryClass;if(!t)throw new y(`Factory is not configured for model [${this.name}].`);let n=new t;return typeof e==`number`&&n.count(e),n}static addGlobalScope(e,t){this.ensureOwnGlobalScopes(),this.globalScopes[e]=t}static removeGlobalScope(e){this.ensureOwnGlobalScopes(),delete this.globalScopes[e]}static clearGlobalScopes(){this.globalScopes={}}static on(e,t){this.ensureOwnEventListeners(),this.eventListeners[e]||(this.eventListeners[e]=[]),this.eventListeners[e]?.push(t)}static off(e,t){if(this.ensureOwnEventListeners(),!t){delete this.eventListeners[e];return}this.eventListeners[e]=(this.eventListeners[e]||[]).filter(e=>e!==t)}static clearEventListeners(){this.eventListeners={}}static getDelegate(e){xe();let t=e||this.delegate||`${(0,m.str)(this.name).camel().plural()}`,n=[t,`${(0,m.str)(t).camel()}`,`${(0,m.str)(t).singular()}`,`${(0,m.str)(t).camel().singular()}`],r=Se(),i=n.map(e=>this.client?.[e]??r?.[e]).find(e=>q(e));if(!i)throw new y(`Database delegate [${t}] is not configured.`);return i}static query(){let e=new Je(this.getDelegate(),this),t=this;return t.ensureOwnGlobalScopes(),Object.values(t.globalScopes).forEach(t=>{let n=t(e);n&&n!==e&&(e=n)}),e}static withTrashed(){return this.query().withTrashed()}static onlyTrashed(){return this.query().onlyTrashed()}static scope(e,...t){return this.query().scope(e,...t)}static getSoftDeleteConfig(){return{enabled:this.softDeletes,column:this.deletedAtColumn}}static hydrate(e){return new this(e)}static hydrateMany(e){return e.map(e=>new this(e))}fill(e){return Object.entries(e).forEach(([e,t])=>{this.setAttribute(e,t)}),this}getAttribute(e){let t=this.resolveGetMutator(e),n=this.casts[e],r=this.attributes[e];return n&&(r=v(n).get(r)),t?t.call(this,r):r}setAttribute(e,t){let n=this.resolveSetMutator(e),r=this.casts[e],i=t;return n&&(i=n.call(this,i)),r&&(i=v(r).set(i)),this.attributes[e]=i,this}async save(){let t=this.getAttribute(`id`),n=this.getRawAttributes(),r=this.constructor;if(t==null){await e.dispatchEvent(r,`saving`,this),await e.dispatchEvent(r,`creating`,this);let t=await r.query().create(n);return this.fill(t.getRawAttributes()),await e.dispatchEvent(r,`created`,this),await e.dispatchEvent(r,`saved`,this),this}await e.dispatchEvent(r,`saving`,this),await e.dispatchEvent(r,`updating`,this);let i=await r.query().where({id:t}).update(n);return this.fill(i.getRawAttributes()),await e.dispatchEvent(r,`updated`,this),await e.dispatchEvent(r,`saved`,this),this}async delete(){let t=this.getAttribute(`id`);if(t==null)throw new y(`Cannot delete a model without an id.`);let n=this.constructor;await e.dispatchEvent(n,`deleting`,this);let r=n.getSoftDeleteConfig();if(r.enabled){let i=await n.query().where({id:t}).update({[r.column]:new Date});return this.fill(i.getRawAttributes()),await e.dispatchEvent(n,`deleted`,this),this}let i=await n.query().where({id:t}).delete();return this.fill(i.getRawAttributes()),await e.dispatchEvent(n,`deleted`,this),this}async forceDelete(){let t=this.getAttribute(`id`);if(t==null)throw new y(`Cannot force delete a model without an id.`);let n=this.constructor;await e.dispatchEvent(n,`forceDeleting`,this),await e.dispatchEvent(n,`deleting`,this);let r=await n.query().withTrashed().where({id:t}).delete();return this.fill(r.getRawAttributes()),await e.dispatchEvent(n,`deleted`,this),await e.dispatchEvent(n,`forceDeleted`,this),this}async restore(){let t=this.getAttribute(`id`);if(t==null)throw new y(`Cannot restore a model without an id.`);let n=this.constructor,r=n.getSoftDeleteConfig();if(!r.enabled)return this;await e.dispatchEvent(n,`restoring`,this);let i=await n.query().withTrashed().where({id:t}).update({[r.column]:null});return this.fill(i.getRawAttributes()),await e.dispatchEvent(n,`restored`,this),this}async load(e){let t=this.normalizeRelationMap(e);return await Promise.all(Object.entries(t).map(async([e,t])=>{let n=this[e];if(typeof n!=`function`)return;let r=n.call(this);t&&r.constrain(t);let i=await r.getResults();this.attributes[e]=i})),this}getRawAttributes(){return{...this.attributes}}toObject(){let e=(this.visible.length>0?this.visible:Object.keys(this.attributes).filter(e=>!this.hidden.includes(e))).reduce((e,t)=>{let n=this.getAttribute(t);return n instanceof Date&&(n=n.toISOString()),e[t]=n,e},{});return this.appends.forEach(t=>{e[t]=this.getAttribute(t)}),e}toJSON(){return this.toObject()}hasOne(e,t,n=`id`){return new Ue(this,e,t,n)}hasMany(e,t,n=`id`){return new Ve(this,e,t,n)}belongsTo(e,t,n=`id`){return new Be(this,e,t,n)}belongsToMany(e,t,n,r,i=`id`,a=`id`){return new ze(this,e,t,n,r,i,a)}hasOneThrough(e,t,n,r,i=`id`,a=`id`){return new We(this,e,t,n,r,i,a)}hasManyThrough(e,t,n,r,i=`id`,a=`id`){return new He(this,e,t,n,r,i,a)}morphOne(e,t,n=`id`){return new Ke(this,e,t,n)}morphMany(e,t,n=`id`){return new Ge(this,e,t,n)}morphToMany(e,t,n,r,i=`id`,a=`id`){return new qe(this,e,t,n,r,i,a)}resolveGetMutator(e){let t=`get${(0,m.str)(e).studly()}Attribute`,n=this[t];return typeof n==`function`?n:null}resolveSetMutator(e){let t=`set${(0,m.str)(e).studly()}Attribute`,n=this[t];return typeof n==`function`?n:null}static ensureOwnGlobalScopes(){Object.prototype.hasOwnProperty.call(this,`globalScopes`)||(this.globalScopes={...this.globalScopes||{}})}static ensureOwnEventListeners(){Object.prototype.hasOwnProperty.call(this,`eventListeners`)||(this.eventListeners={...this.eventListeners||{}})}static async dispatchEvent(e,t,n){e.ensureOwnEventListeners();let r=e.eventListeners[t]||[];for(let e of r)await e(n)}normalizeRelationMap(e){return typeof e==`string`?{[e]:void 0}:Array.isArray(e)?e.reduce((e,t)=>(e[t]=void 0,e),{}):e}};exports.ArkormCollection=J,exports.ArkormException=y,exports.CliApp=Ce,exports.InitCommand=we,exports.InlineFactory=Pe,exports.LengthAwarePaginator=Q,exports.MakeFactoryCommand=Te,exports.MakeMigrationCommand=Ee,exports.MakeModelCommand=De,exports.MakeSeederCommand=Oe,exports.MigrateCommand=ke,exports.Migration=b,exports.Model=Ye,exports.ModelFactory=Ne,exports.ModelNotFoundException=Y,exports.ModelsSyncCommand=Ae,exports.PRISMA_MODEL_REGEX=ie,exports.Paginator=$,exports.QueryBuilder=Je,exports.SchemaBuilder=re,exports.SeedCommand=Me,exports.Seeder=je,exports.TableBuilder=x,exports.URLDriver=Z,exports.applyAlterTableOperation=k,exports.applyCreateTableOperation=O,exports.applyDropTableOperation=A,exports.applyMigrationToPrismaSchema=P,exports.applyOperationsToPrismaSchema=se,exports.buildFieldLine=T,exports.buildIndexLine=E,exports.buildMigrationSource=ue,exports.buildModelBlock=oe,exports.configureArkormRuntime=H,exports.createMigrationTimestamp=N,exports.createPrismaAdapter=Ie,exports.createPrismaDelegateMap=Le,exports.defineConfig=ge,exports.defineFactory=Fe,exports.ensureArkormConfigLoading=xe,exports.escapeRegex=C,exports.findModelBlock=D,exports.formatDefaultValue=w,exports.generateMigrationFile=de,exports.getDefaultStubsPath=G,exports.getMigrationPlan=fe,exports.getRuntimePaginationURLDriverFactory=K,exports.getRuntimePrismaClient=Se,exports.getUserConfig=V,exports.inferDelegateName=Re,exports.isDelegateLike=q,exports.loadArkormConfig=W,exports.pad=M,exports.resetArkormRuntimeForTests=_e,exports.resolveCast=v,exports.resolveMigrationClassName=ce,exports.resolvePrismaType=ae,exports.runMigrationWithPrisma=pe,exports.runPrismaCommand=j,exports.toMigrationFileSlug=le,exports.toModelName=S;
2045
+ `;
2046
+
2047
+ //#endregion
2048
+ //#region src/Collection.ts
2049
+ var ArkormCollection = class extends _h3ravel_collect_js.Collection {};
2050
+
2051
+ //#endregion
2052
+ //#region src/database/factories.ts
2053
+ /**
2054
+ * Base class for defining model factories.
2055
+ * Not meant to be used directly.
2056
+ *
2057
+ * @template TModel The type of model the factory creates.
2058
+ * @template TAttributes The type of attributes used to create the model.
2059
+ * @author Legacy (3m1n3nc3)
2060
+ * @since 0.1.0
2061
+ */
2062
+ var ModelFactory = class {
2063
+ amount = 1;
2064
+ sequence = 0;
2065
+ states = [];
2066
+ /**
2067
+ * Set the number of models to create.
2068
+ *
2069
+ * @param amount
2070
+ * @returns
2071
+ */
2072
+ count(amount) {
2073
+ this.amount = Math.max(1, Math.floor(amount));
2074
+ return this;
2075
+ }
2076
+ /**
2077
+ * Define a state transformation for the factory.
2078
+ * States are applied in the order they were defined.
2079
+ *
2080
+ * @param resolver A function that takes the current attributes and sequence number, and returns the transformed attributes.
2081
+ * @returns The factory instance for chaining.
2082
+ */
2083
+ state(resolver) {
2084
+ this.states.push(resolver);
2085
+ return this;
2086
+ }
2087
+ /**
2088
+ * Create a new model instance without saving it to the database.
2089
+ *
2090
+ * @param overrides
2091
+ * @returns
2092
+ */
2093
+ make(overrides = {}) {
2094
+ const attributes = this.buildAttributes(overrides);
2095
+ return new this.model(attributes);
2096
+ }
2097
+ /**
2098
+ * Create multiple model instances without saving them to the database.
2099
+ *
2100
+ * @param amount
2101
+ * @param overrides
2102
+ * @returns
2103
+ */
2104
+ makeMany(amount = this.amount, overrides = {}) {
2105
+ const total = Math.max(1, Math.floor(amount));
2106
+ return Array.from({ length: total }, () => this.make(overrides));
2107
+ }
2108
+ /**
2109
+ * Create a new model instance and save it to the database.
2110
+ *
2111
+ * @param overrides
2112
+ * @returns
2113
+ */
2114
+ async create(overrides = {}) {
2115
+ const model = this.make(overrides);
2116
+ if (typeof model.save !== "function") throw new Error("Factory model does not support save().");
2117
+ return await model.save();
2118
+ }
2119
+ /**
2120
+ * Create multiple model instances and save them to the database.
2121
+ *
2122
+ * @param amount
2123
+ * @param overrides
2124
+ * @returns
2125
+ */
2126
+ async createMany(amount = this.amount, overrides = {}) {
2127
+ const models = this.makeMany(amount, overrides);
2128
+ return await Promise.all(models.map(async (model) => {
2129
+ if (typeof model.save !== "function") throw new Error("Factory model does not support save().");
2130
+ return await model.save();
2131
+ }));
2132
+ }
2133
+ /**
2134
+ * Build the attributes for a model instance, applying the factory
2135
+ * definition and any defined states, and merging in any overrides.
2136
+ *
2137
+ * @param overrides
2138
+ * @returns
2139
+ */
2140
+ buildAttributes(overrides) {
2141
+ const sequence = this.sequence;
2142
+ this.sequence += 1;
2143
+ let resolved = this.definition(sequence);
2144
+ for (const state of this.states) resolved = state(resolved, sequence);
2145
+ return {
2146
+ ...resolved,
2147
+ ...overrides
2148
+ };
2149
+ }
2150
+ };
2151
+ /**
2152
+ * A helper class for defining factories using an inline definition
2153
+ * function, without needing to create a separate factory class.
2154
+ *
2155
+ * @template TModel
2156
+ * @template TAttributes
2157
+ * @author Legacy (3m1n3nc3)
2158
+ * @since 0.1.0
2159
+ */
2160
+ var InlineFactory = class extends ModelFactory {
2161
+ model;
2162
+ constructor(model, resolver) {
2163
+ super();
2164
+ this.resolver = resolver;
2165
+ this.model = model;
2166
+ }
2167
+ definition(sequence) {
2168
+ return this.resolver(sequence);
2169
+ }
2170
+ };
2171
+ /**
2172
+ * Define a factory for a given model using an inline definition function.
2173
+ *
2174
+ * @template TModel The type of model the factory creates.
2175
+ * @template TAttributes The type of attributes used to create the model.
2176
+ * @param model The model constructor.
2177
+ * @param definition The factory definition function.
2178
+ * @returns A new instance of the model factory.
2179
+ */
2180
+ const defineFactory = (model, definition) => {
2181
+ return new InlineFactory(model, definition);
2182
+ };
2183
+
2184
+ //#endregion
2185
+ //#region src/Exceptions/ModelNotFoundException.ts
2186
+ /**
2187
+ * The ModelNotFoundException class is a custom error type for handling
2188
+ * cases where a requested model instance cannot be found in the database.
2189
+ *
2190
+ * @author Legacy (3m1n3nc3)
2191
+ * @since 0.1.0
2192
+ */
2193
+ var ModelNotFoundException = class extends ArkormException {
2194
+ constructor(message = "No query results for the given model.") {
2195
+ super(message);
2196
+ this.name = "ModelNotFoundException";
2197
+ }
2198
+ };
2199
+
2200
+ //#endregion
2201
+ //#region src/helpers/prisma.ts
2202
+ /**
2203
+ * Create an adapter to convert a Prisma client instance into a format
2204
+ * compatible with ArkORM's expectations.
2205
+ *
2206
+ * @param prisma The Prisma client instance to adapt.
2207
+ * @param mapping An optional mapping of Prisma delegate names to ArkORM delegate names.
2208
+ * @returns A record of adapted Prisma delegates compatible with ArkORM.
2209
+ */
2210
+ function createPrismaAdapter(prisma) {
2211
+ return Object.entries(prisma).reduce((accumulator, [key, value]) => {
2212
+ if (!isDelegateLike(value)) return accumulator;
2213
+ accumulator[key] = value;
2214
+ return accumulator;
2215
+ }, {});
2216
+ }
2217
+ /**
2218
+ * Create a delegate mapping record for Model.setClient() from a Prisma client.
2219
+ *
2220
+ * @param prisma The Prisma client instance.
2221
+ * @param mapping Optional mapping of Arkormˣ delegate names to Prisma delegate names.
2222
+ * @returns A delegate map keyed by Arkormˣ delegate names.
2223
+ */
2224
+ function createPrismaDelegateMap(prisma) {
2225
+ return createPrismaAdapter(prisma);
2226
+ }
2227
+ /**
2228
+ * Infer the Prisma delegate name for a given model name using a simple convention.
2229
+ *
2230
+ * @param modelName The name of the model to infer the delegate name for.
2231
+ * @returns The inferred Prisma delegate name.
2232
+ */
2233
+ function inferDelegateName(modelName) {
2234
+ return `${modelName.charAt(0).toLowerCase()}${modelName.slice(1)}s`;
2235
+ }
2236
+
2237
+ //#endregion
2238
+ //#region src/relationship/Relation.ts
2239
+ /**
2240
+ * Base class for all relationship types. Not meant to be used directly.
2241
+ *
2242
+ * @author Legacy (3m1n3nc3)
2243
+ * @since 0.1.0
2244
+ */
2245
+ var Relation = class {
2246
+ constraint = null;
2247
+ /**
2248
+ * Apply a constraint to the relationship query.
2249
+ *
2250
+ * @param constraint The constraint function to apply to the query.
2251
+ * @returns The current relation instance.
2252
+ */
2253
+ constrain(constraint) {
2254
+ if (!this.constraint) {
2255
+ this.constraint = constraint;
2256
+ return this;
2257
+ }
2258
+ const previousConstraint = this.constraint;
2259
+ this.constraint = (query) => {
2260
+ const constrained = previousConstraint(query) ?? query;
2261
+ return constraint(constrained) ?? constrained;
2262
+ };
2263
+ return this;
2264
+ }
2265
+ /**
2266
+ * Add a where clause to the relationship query.
2267
+ *
2268
+ * @param where
2269
+ * @returns
2270
+ */
2271
+ where(where) {
2272
+ return this.constrain((query) => query.where(where));
2273
+ }
2274
+ /**
2275
+ * Add a strongly-typed where key clause to the relationship query.
2276
+ *
2277
+ * @param key
2278
+ * @param value
2279
+ * @returns
2280
+ */
2281
+ whereKey(key, value) {
2282
+ return this.constrain((query) => query.whereKey(key, value));
2283
+ }
2284
+ /**
2285
+ * Add a strongly-typed where in clause to the relationship query.
2286
+ *
2287
+ * @param key
2288
+ * @param values
2289
+ * @returns
2290
+ */
2291
+ whereIn(key, values) {
2292
+ return this.constrain((query) => query.whereIn(key, values));
2293
+ }
2294
+ /**
2295
+ * Add an order by clause to the relationship query.
2296
+ *
2297
+ * @param orderBy
2298
+ * @returns
2299
+ */
2300
+ orderBy(orderBy) {
2301
+ return this.constrain((query) => query.orderBy(orderBy));
2302
+ }
2303
+ /**
2304
+ * Add an include clause to the relationship query.
2305
+ *
2306
+ * @param include
2307
+ * @returns
2308
+ */
2309
+ include(include) {
2310
+ return this.constrain((query) => query.include(include));
2311
+ }
2312
+ /**
2313
+ * Add eager loading relations to the relationship query.
2314
+ *
2315
+ * @param relations
2316
+ * @returns
2317
+ */
2318
+ with(relations) {
2319
+ return this.constrain((query) => query.with(relations));
2320
+ }
2321
+ /**
2322
+ * Add a select clause to the relationship query.
2323
+ *
2324
+ * @param select
2325
+ * @returns
2326
+ */
2327
+ select(select) {
2328
+ return this.constrain((query) => query.select(select));
2329
+ }
2330
+ /**
2331
+ * Add a skip clause to the relationship query.
2332
+ *
2333
+ * @param skip
2334
+ * @returns
2335
+ */
2336
+ skip(skip) {
2337
+ return this.constrain((query) => query.skip(skip));
2338
+ }
2339
+ /**
2340
+ * Add a take clause to the relationship query.
2341
+ *
2342
+ * @param take
2343
+ * @returns
2344
+ */
2345
+ take(take) {
2346
+ return this.constrain((query) => query.take(take));
2347
+ }
2348
+ /**
2349
+ * Include soft-deleted records in the relationship query.
2350
+ *
2351
+ * @returns
2352
+ */
2353
+ withTrashed() {
2354
+ return this.constrain((query) => query.withTrashed());
2355
+ }
2356
+ /**
2357
+ * Limit relationship query to only soft-deleted records.
2358
+ *
2359
+ * @returns
2360
+ */
2361
+ onlyTrashed() {
2362
+ return this.constrain((query) => query.onlyTrashed());
2363
+ }
2364
+ /**
2365
+ * Exclude soft-deleted records from the relationship query.
2366
+ *
2367
+ * @returns
2368
+ */
2369
+ withoutTrashed() {
2370
+ return this.constrain((query) => query.withoutTrashed());
2371
+ }
2372
+ /**
2373
+ * Apply a scope to the relationship query.
2374
+ *
2375
+ * @param name
2376
+ * @param args
2377
+ * @returns
2378
+ */
2379
+ scope(name, ...args) {
2380
+ return this.constrain((query) => query.scope(name, ...args));
2381
+ }
2382
+ /**
2383
+ * Apply the defined constraint to the given query, if any.
2384
+ *
2385
+ * @param query The query builder instance to apply the constraint to.
2386
+ *
2387
+ * @returns The query builder instance with the constraint applied, if any.
2388
+ */
2389
+ applyConstraint(query) {
2390
+ if (!this.constraint) return query;
2391
+ return this.constraint(query) ?? query;
2392
+ }
2393
+ /**
2394
+ * Execute the relationship query and return relation results.
2395
+ *
2396
+ * @returns
2397
+ */
2398
+ async get() {
2399
+ return this.getResults();
2400
+ }
2401
+ /**
2402
+ * Execute the relationship query and return the first related model.
2403
+ *
2404
+ * @returns
2405
+ */
2406
+ async first() {
2407
+ const results = await this.getResults();
2408
+ if (results instanceof ArkormCollection) return results.all()[0] ?? null;
2409
+ return results;
2410
+ }
2411
+ };
2412
+
2413
+ //#endregion
2414
+ //#region src/relationship/BelongsToManyRelation.ts
2415
+ /**
2416
+ * Defines a many-to-many relationship.
2417
+ *
2418
+ * @author Legacy (3m1n3nc3)
2419
+ * @since 0.1.0
2420
+ */
2421
+ var BelongsToManyRelation = class extends Relation {
2422
+ constructor(parent, related, throughDelegate, foreignPivotKey, relatedPivotKey, parentKey, relatedKey) {
2423
+ super();
2424
+ this.parent = parent;
2425
+ this.related = related;
2426
+ this.throughDelegate = throughDelegate;
2427
+ this.foreignPivotKey = foreignPivotKey;
2428
+ this.relatedPivotKey = relatedPivotKey;
2429
+ this.parentKey = parentKey;
2430
+ this.relatedKey = relatedKey;
2431
+ }
2432
+ /**
2433
+ * Fetches the related models for this relationship.
2434
+ *
2435
+ * @returns
2436
+ */
2437
+ async getResults() {
2438
+ const parentValue = this.parent.getAttribute(this.parentKey);
2439
+ const ids = (await this.related.getDelegate(this.throughDelegate).findMany({ where: { [this.foreignPivotKey]: parentValue } })).map((row) => row[this.relatedPivotKey]);
2440
+ return this.applyConstraint(this.related.query().where({ [this.relatedKey]: { in: ids } })).get();
2441
+ }
2442
+ };
2443
+
2444
+ //#endregion
2445
+ //#region src/relationship/BelongsToRelation.ts
2446
+ /**
2447
+ * Defines an inverse one-to-one or many relationship.
2448
+ *
2449
+ * @author Legacy (3m1n3nc3)
2450
+ * @since 0.1.0
2451
+ */
2452
+ var BelongsToRelation = class extends Relation {
2453
+ constructor(parent, related, foreignKey, ownerKey) {
2454
+ super();
2455
+ this.parent = parent;
2456
+ this.related = related;
2457
+ this.foreignKey = foreignKey;
2458
+ this.ownerKey = ownerKey;
2459
+ }
2460
+ /**
2461
+ * Fetches the related models for this relationship.
2462
+ *
2463
+ * @returns
2464
+ */
2465
+ async getResults() {
2466
+ const foreignValue = this.parent.getAttribute(this.foreignKey);
2467
+ return this.applyConstraint(this.related.query().where({ [this.ownerKey]: foreignValue })).first();
2468
+ }
2469
+ };
2470
+
2471
+ //#endregion
2472
+ //#region src/relationship/HasManyRelation.ts
2473
+ /**
2474
+ * Defines a one-to-many relationship.
2475
+ *
2476
+ * @author Legacy (3m1n3nc3)
2477
+ * @since 0.1.0
2478
+ */
2479
+ var HasManyRelation = class extends Relation {
2480
+ constructor(parent, related, foreignKey, localKey) {
2481
+ super();
2482
+ this.parent = parent;
2483
+ this.related = related;
2484
+ this.foreignKey = foreignKey;
2485
+ this.localKey = localKey;
2486
+ }
2487
+ /**
2488
+ * Fetches the related models for this relationship.
2489
+ *
2490
+ * @returns
2491
+ */
2492
+ async getResults() {
2493
+ const localValue = this.parent.getAttribute(this.localKey);
2494
+ return this.applyConstraint(this.related.query().where({ [this.foreignKey]: localValue })).get();
2495
+ }
2496
+ };
2497
+
2498
+ //#endregion
2499
+ //#region src/relationship/HasManyThroughRelation.ts
2500
+ /**
2501
+ * Defines a has-many-through relationship, which provides a convenient way to access
2502
+ * distant relations via an intermediate relation.
2503
+ *
2504
+ * @author Legacy (3m1n3nc3)
2505
+ * @since 0.1.0
2506
+ */
2507
+ var HasManyThroughRelation = class extends Relation {
2508
+ constructor(parent, related, throughDelegate, firstKey, secondKey, localKey, secondLocalKey) {
2509
+ super();
2510
+ this.parent = parent;
2511
+ this.related = related;
2512
+ this.throughDelegate = throughDelegate;
2513
+ this.firstKey = firstKey;
2514
+ this.secondKey = secondKey;
2515
+ this.localKey = localKey;
2516
+ this.secondLocalKey = secondLocalKey;
2517
+ }
2518
+ /**
2519
+ * Fetches the related models for this relationship.
2520
+ *
2521
+ * @returns
2522
+ */
2523
+ async getResults() {
2524
+ const localValue = this.parent.getAttribute(this.localKey);
2525
+ const keys = (await this.related.getDelegate(this.throughDelegate).findMany({ where: { [this.firstKey]: localValue } })).map((row) => row[this.secondLocalKey]);
2526
+ return this.applyConstraint(this.related.query().where({ [this.secondKey]: { in: keys } })).get();
2527
+ }
2528
+ };
2529
+
2530
+ //#endregion
2531
+ //#region src/relationship/HasOneRelation.ts
2532
+ /**
2533
+ * Represents a "has one" relationship between two models.
2534
+ *
2535
+ * @author Legacy (3m1n3nc3)
2536
+ * @since 0.1.0
2537
+ */
2538
+ var HasOneRelation = class extends Relation {
2539
+ constructor(parent, related, foreignKey, localKey) {
2540
+ super();
2541
+ this.parent = parent;
2542
+ this.related = related;
2543
+ this.foreignKey = foreignKey;
2544
+ this.localKey = localKey;
2545
+ }
2546
+ /**
2547
+ * Fetches the related models for this relationship.
2548
+ *
2549
+ * @returns
2550
+ */
2551
+ async getResults() {
2552
+ const localValue = this.parent.getAttribute(this.localKey);
2553
+ return this.applyConstraint(this.related.query().where({ [this.foreignKey]: localValue })).first();
2554
+ }
2555
+ };
2556
+
2557
+ //#endregion
2558
+ //#region src/relationship/HasOneThroughRelation.ts
2559
+ /**
2560
+ * Represents a "has one through" relationship, where the parent model is related
2561
+ * to exactly one instance of the related model through an intermediate model.
2562
+ *
2563
+ * @author Legacy (3m1n3nc3)
2564
+ * @since 0.1.0
2565
+ */
2566
+ var HasOneThroughRelation = class extends Relation {
2567
+ constructor(parent, related, throughDelegate, firstKey, secondKey, localKey, secondLocalKey) {
2568
+ super();
2569
+ this.parent = parent;
2570
+ this.related = related;
2571
+ this.throughDelegate = throughDelegate;
2572
+ this.firstKey = firstKey;
2573
+ this.secondKey = secondKey;
2574
+ this.localKey = localKey;
2575
+ this.secondLocalKey = secondLocalKey;
2576
+ }
2577
+ /**
2578
+ * Fetches the related models for this relationship.
2579
+ *
2580
+ * @returns
2581
+ */
2582
+ async getResults() {
2583
+ const localValue = this.parent.getAttribute(this.localKey);
2584
+ const intermediate = await this.related.getDelegate(this.throughDelegate).findFirst({ where: { [this.firstKey]: localValue } });
2585
+ if (!intermediate) return null;
2586
+ return this.applyConstraint(this.related.query().where({ [this.secondKey]: intermediate[this.secondLocalKey] })).first();
2587
+ }
2588
+ };
2589
+
2590
+ //#endregion
2591
+ //#region src/relationship/MorphManyRelation.ts
2592
+ /**
2593
+ * Defines a polymorphic one-to-many relationship.
2594
+ *
2595
+ * @author Legacy (3m1n3nc3)
2596
+ * @since 0.1.0
2597
+ */
2598
+ var MorphManyRelation = class extends Relation {
2599
+ constructor(parent, related, morphName, localKey) {
2600
+ super();
2601
+ this.parent = parent;
2602
+ this.related = related;
2603
+ this.morphName = morphName;
2604
+ this.localKey = localKey;
2605
+ }
2606
+ /**
2607
+ * Fetches the related models for this relationship.
2608
+ *
2609
+ * @returns
2610
+ */
2611
+ async getResults() {
2612
+ const id = this.parent.getAttribute(this.localKey);
2613
+ const type = this.parent.constructor.name;
2614
+ return this.applyConstraint(this.related.query().where({
2615
+ [`${this.morphName}Id`]: id,
2616
+ [`${this.morphName}Type`]: type
2617
+ })).get();
2618
+ }
2619
+ };
2620
+
2621
+ //#endregion
2622
+ //#region src/relationship/MorphOneRelation.ts
2623
+ /**
2624
+ * Defines a polymorphic one-to-one relationship.
2625
+ *
2626
+ * @author Legacy (3m1n3nc3)
2627
+ * @since 0.1.0
2628
+ */
2629
+ var MorphOneRelation = class extends Relation {
2630
+ constructor(parent, related, morphName, localKey) {
2631
+ super();
2632
+ this.parent = parent;
2633
+ this.related = related;
2634
+ this.morphName = morphName;
2635
+ this.localKey = localKey;
2636
+ }
2637
+ /**
2638
+ * Fetches the related models for this relationship.
2639
+ *
2640
+ * @returns
2641
+ */
2642
+ async getResults() {
2643
+ const id = this.parent.getAttribute(this.localKey);
2644
+ const type = this.parent.constructor.name;
2645
+ return this.applyConstraint(this.related.query().where({
2646
+ [`${this.morphName}Id`]: id,
2647
+ [`${this.morphName}Type`]: type
2648
+ })).first();
2649
+ }
2650
+ };
2651
+
2652
+ //#endregion
2653
+ //#region src/relationship/MorphToManyRelation.ts
2654
+ /**
2655
+ * Defines a polymorphic many-to-many relationship.
2656
+ *
2657
+ * @author Legacy (3m1n3nc3)
2658
+ * @since 0.1.0
2659
+ */
2660
+ var MorphToManyRelation = class extends Relation {
2661
+ constructor(parent, related, throughDelegate, morphName, relatedPivotKey, parentKey, relatedKey) {
2662
+ super();
2663
+ this.parent = parent;
2664
+ this.related = related;
2665
+ this.throughDelegate = throughDelegate;
2666
+ this.morphName = morphName;
2667
+ this.relatedPivotKey = relatedPivotKey;
2668
+ this.parentKey = parentKey;
2669
+ this.relatedKey = relatedKey;
2670
+ }
2671
+ /**
2672
+ * Fetches the related models for this relationship.
2673
+ *
2674
+ * @returns
2675
+ */
2676
+ async getResults() {
2677
+ const parentValue = this.parent.getAttribute(this.parentKey);
2678
+ const morphType = this.parent.constructor.name;
2679
+ const ids = (await this.related.getDelegate(this.throughDelegate).findMany({ where: {
2680
+ [`${this.morphName}Id`]: parentValue,
2681
+ [`${this.morphName}Type`]: morphType
2682
+ } })).map((row) => row[this.relatedPivotKey]);
2683
+ return this.applyConstraint(this.related.query().where({ [this.relatedKey]: { in: ids } })).get();
2684
+ }
2685
+ };
2686
+
2687
+ //#endregion
2688
+ //#region src/URLDriver.ts
2689
+ /**
2690
+ * URLDriver builds pagination URLs from paginator options.
2691
+ *
2692
+ * @author Legacy (3m1n3nc3)
2693
+ * @since 0.1.0
2694
+ */
2695
+ var URLDriver = class URLDriver {
2696
+ static DEFAULT_PAGE_NAME = "page";
2697
+ path;
2698
+ query;
2699
+ fragment;
2700
+ pageName;
2701
+ constructor(options = {}) {
2702
+ this.path = options.path ?? "/";
2703
+ this.query = options.query ?? {};
2704
+ this.fragment = options.fragment ?? "";
2705
+ this.pageName = options.pageName ?? URLDriver.DEFAULT_PAGE_NAME;
2706
+ }
2707
+ getPageName() {
2708
+ return this.pageName;
2709
+ }
2710
+ url(page) {
2711
+ const targetPage = Math.max(1, page);
2712
+ const [basePath, pathQuery = ""] = this.path.split("?");
2713
+ const search = new URLSearchParams(pathQuery);
2714
+ Object.entries(this.query).forEach(([key, value]) => {
2715
+ if (value == null) {
2716
+ search.delete(key);
2717
+ return;
2718
+ }
2719
+ search.set(key, String(value));
2720
+ });
2721
+ search.set(this.pageName, String(targetPage));
2722
+ const queryString = search.toString();
2723
+ const normalizedFragment = this.fragment.replace(/^#/, "");
2724
+ if (!queryString && !normalizedFragment) return basePath;
2725
+ if (!normalizedFragment) return `${basePath}?${queryString}`;
2726
+ if (!queryString) return `${basePath}#${normalizedFragment}`;
2727
+ return `${basePath}?${queryString}#${normalizedFragment}`;
2728
+ }
2729
+ };
2730
+
2731
+ //#endregion
2732
+ //#region src/Paginator.ts
2733
+ /**
2734
+ * The LengthAwarePaginator class encapsulates paginated results with full
2735
+ * metadata including the total result count and last page.
2736
+ *
2737
+ * @template T The type of the data being paginated.
2738
+ * @author Legacy (3m1n3nc3)
2739
+ * @since 0.1.0
2740
+ */
2741
+ var LengthAwarePaginator = class {
2742
+ data;
2743
+ meta;
2744
+ urlDriver;
2745
+ /**
2746
+ * Creates a new LengthAwarePaginator instance.
2747
+ *
2748
+ * @param data The collection of data being paginated.
2749
+ * @param total The total number of items.
2750
+ * @param perPage The number of items per page.
2751
+ * @param currentPage The current page number.
2752
+ * @param options URL generation options.
2753
+ */
2754
+ constructor(data, total, perPage, currentPage, options = {}) {
2755
+ const lastPage = Math.max(1, Math.ceil(total / perPage));
2756
+ const from = total === 0 ? null : (currentPage - 1) * perPage + 1;
2757
+ const to = total === 0 ? null : Math.min(currentPage * perPage, total);
2758
+ this.data = data;
2759
+ const urlDriverFactory = getRuntimePaginationURLDriverFactory();
2760
+ this.urlDriver = urlDriverFactory ? urlDriverFactory(options) : new URLDriver(options);
2761
+ this.meta = {
2762
+ total,
2763
+ perPage,
2764
+ currentPage,
2765
+ lastPage,
2766
+ from,
2767
+ to
2768
+ };
2769
+ }
2770
+ getPageName() {
2771
+ return this.urlDriver.getPageName();
2772
+ }
2773
+ url(page) {
2774
+ return this.urlDriver.url(page);
2775
+ }
2776
+ nextPageUrl() {
2777
+ if (this.meta.currentPage >= this.meta.lastPage) return null;
2778
+ return this.url(this.meta.currentPage + 1);
2779
+ }
2780
+ previousPageUrl() {
2781
+ if (this.meta.currentPage <= 1) return null;
2782
+ return this.url(this.meta.currentPage - 1);
2783
+ }
2784
+ firstPageUrl() {
2785
+ return this.url(1);
2786
+ }
2787
+ lastPageUrl() {
2788
+ return this.url(this.meta.lastPage);
2789
+ }
2790
+ /**
2791
+ * Converts the paginator instance to a JSON-serializable object.
2792
+ *
2793
+ * @returns
2794
+ */
2795
+ toJSON() {
2796
+ return {
2797
+ data: this.data,
2798
+ meta: this.meta,
2799
+ links: {
2800
+ first: this.firstPageUrl(),
2801
+ last: this.lastPageUrl(),
2802
+ prev: this.previousPageUrl(),
2803
+ next: this.nextPageUrl()
2804
+ }
2805
+ };
2806
+ }
2807
+ };
2808
+ /**
2809
+ * The Paginator class encapsulates simple pagination results without total count.
2810
+ *
2811
+ * @template T The type of the data being paginated.
2812
+ */
2813
+ var Paginator = class {
2814
+ data;
2815
+ meta;
2816
+ urlDriver;
2817
+ /**
2818
+ * Creates a new simple Paginator instance.
2819
+ *
2820
+ * @param data The collection of data being paginated.
2821
+ * @param perPage The number of items per page.
2822
+ * @param currentPage The current page number.
2823
+ * @param hasMorePages Indicates whether additional pages exist.
2824
+ * @param options URL generation options.
2825
+ */
2826
+ constructor(data, perPage, currentPage, hasMorePages, options = {}) {
2827
+ const count = data.all().length;
2828
+ const from = count === 0 ? null : (currentPage - 1) * perPage + 1;
2829
+ const to = count === 0 ? null : (from ?? 1) + count - 1;
2830
+ this.data = data;
2831
+ const urlDriverFactory = getRuntimePaginationURLDriverFactory();
2832
+ this.urlDriver = urlDriverFactory ? urlDriverFactory(options) : new URLDriver(options);
2833
+ this.meta = {
2834
+ perPage,
2835
+ currentPage,
2836
+ from,
2837
+ to,
2838
+ hasMorePages
2839
+ };
2840
+ }
2841
+ getPageName() {
2842
+ return this.urlDriver.getPageName();
2843
+ }
2844
+ url(page) {
2845
+ return this.urlDriver.url(page);
2846
+ }
2847
+ nextPageUrl() {
2848
+ if (!this.meta.hasMorePages) return null;
2849
+ return this.url(this.meta.currentPage + 1);
2850
+ }
2851
+ previousPageUrl() {
2852
+ if (this.meta.currentPage <= 1) return null;
2853
+ return this.url(this.meta.currentPage - 1);
2854
+ }
2855
+ toJSON() {
2856
+ return {
2857
+ data: this.data,
2858
+ meta: this.meta,
2859
+ links: {
2860
+ prev: this.previousPageUrl(),
2861
+ next: this.nextPageUrl()
2862
+ }
2863
+ };
2864
+ }
2865
+ };
2866
+
2867
+ //#endregion
2868
+ //#region src/QueryBuilder.ts
2869
+ /**
2870
+ * The QueryBuilder class provides a fluent interface for building and
2871
+ * executing database queries.
2872
+ *
2873
+ * @template TModel The type of the model being queried.
2874
+ * @author Legacy (3m1n3nc3)
2875
+ * @since 0.1.0
2876
+ */
2877
+ var QueryBuilder = class QueryBuilder {
2878
+ args = {};
2879
+ eagerLoads = {};
2880
+ includeTrashed = false;
2881
+ onlyTrashedRecords = false;
2882
+ randomOrderEnabled = false;
2883
+ relationFilters = [];
2884
+ relationAggregates = [];
2885
+ /**
2886
+ * Creates a new QueryBuilder instance.
2887
+ *
2888
+ * @param delegate
2889
+ * @param model
2890
+ */
2891
+ constructor(delegate, model) {
2892
+ this.delegate = delegate;
2893
+ this.model = model;
2894
+ }
2895
+ /**
2896
+ * Adds a where clause to the query. Multiple calls to where will combine
2897
+ * the clauses with AND logic.
2898
+ *
2899
+ * @param where
2900
+ * @returns
2901
+ */
2902
+ where(where) {
2903
+ return this.addLogicalWhere("AND", where);
2904
+ }
2905
+ /**
2906
+ * Adds an OR where clause to the query.
2907
+ *
2908
+ * @param where
2909
+ * @returns
2910
+ */
2911
+ orWhere(where) {
2912
+ return this.addLogicalWhere("OR", where);
2913
+ }
2914
+ /**
2915
+ * Adds a NOT where clause to the query.
2916
+ *
2917
+ * @param where
2918
+ * @returns
2919
+ */
2920
+ whereNot(where) {
2921
+ return this.where({ NOT: where });
2922
+ }
2923
+ /**
2924
+ * Adds an OR NOT where clause to the query.
2925
+ *
2926
+ * @param where
2927
+ * @returns
2928
+ */
2929
+ orWhereNot(where) {
2930
+ return this.orWhere({ NOT: where });
2931
+ }
2932
+ /**
2933
+ * Adds a null check for a key.
2934
+ *
2935
+ * @param key
2936
+ * @returns
2937
+ */
2938
+ whereNull(key) {
2939
+ return this.where({ [key]: null });
2940
+ }
2941
+ /**
2942
+ * Adds a not-null check for a key.
2943
+ *
2944
+ * @param key
2945
+ * @returns
2946
+ */
2947
+ whereNotNull(key) {
2948
+ return this.where({ [key]: { not: null } });
2949
+ }
2950
+ /**
2951
+ * Adds a between range clause for a key.
2952
+ *
2953
+ * @param key
2954
+ * @param range
2955
+ * @returns
2956
+ */
2957
+ whereBetween(key, range) {
2958
+ const [min, max] = range;
2959
+ return this.where({ [key]: {
2960
+ gte: min,
2961
+ lte: max
2962
+ } });
2963
+ }
2964
+ /**
2965
+ * Adds a date-only equality clause for a date-like key.
2966
+ *
2967
+ * @param key
2968
+ * @param value
2969
+ * @returns
2970
+ */
2971
+ whereDate(key, value) {
2972
+ const target = this.coerceDate(value);
2973
+ const start = new Date(Date.UTC(target.getUTCFullYear(), target.getUTCMonth(), target.getUTCDate()));
2974
+ const end = new Date(start);
2975
+ end.setUTCDate(end.getUTCDate() + 1);
2976
+ return this.where({ [key]: {
2977
+ gte: start,
2978
+ lt: end
2979
+ } });
2980
+ }
2981
+ /**
2982
+ * Adds a month clause for a date-like key.
2983
+ *
2984
+ * @param key
2985
+ * @param month
2986
+ * @param year
2987
+ * @returns
2988
+ */
2989
+ whereMonth(key, month, year = (/* @__PURE__ */ new Date()).getUTCFullYear()) {
2990
+ const normalizedMonth = Math.min(12, Math.max(1, month));
2991
+ const start = new Date(Date.UTC(year, normalizedMonth - 1, 1));
2992
+ const end = new Date(Date.UTC(year, normalizedMonth, 1));
2993
+ return this.where({ [key]: {
2994
+ gte: start,
2995
+ lt: end
2996
+ } });
2997
+ }
2998
+ /**
2999
+ * Adds a year clause for a date-like key.
3000
+ *
3001
+ * @param key
3002
+ * @param year
3003
+ * @returns
3004
+ */
3005
+ whereYear(key, year) {
3006
+ const start = new Date(Date.UTC(year, 0, 1));
3007
+ const end = new Date(Date.UTC(year + 1, 0, 1));
3008
+ return this.where({ [key]: {
3009
+ gte: start,
3010
+ lt: end
3011
+ } });
3012
+ }
3013
+ /**
3014
+ * Adds a strongly-typed inequality where clause for a single attribute key.
3015
+ *
3016
+ * @param key
3017
+ * @param value
3018
+ * @returns
3019
+ */
3020
+ whereKeyNot(key, value) {
3021
+ return this.where({ [key]: { not: value } });
3022
+ }
3023
+ /**
3024
+ * Adds a strongly-typed OR IN where clause for a single attribute key.
3025
+ *
3026
+ * @param key
3027
+ * @param values
3028
+ * @returns
3029
+ */
3030
+ orWhereIn(key, values) {
3031
+ return this.orWhere({ [key]: { in: values } });
3032
+ }
3033
+ /**
3034
+ * Adds a strongly-typed NOT IN where clause for a single attribute key.
3035
+ *
3036
+ * @param key
3037
+ * @param values
3038
+ * @returns
3039
+ */
3040
+ whereNotIn(key, values) {
3041
+ return this.where({ [key]: { notIn: values } });
3042
+ }
3043
+ /**
3044
+ * Adds a strongly-typed OR NOT IN where clause for a single attribute key.
3045
+ *
3046
+ * @param key
3047
+ * @param values
3048
+ * @returns
3049
+ */
3050
+ orWhereNotIn(key, values) {
3051
+ return this.orWhere({ [key]: { notIn: values } });
3052
+ }
3053
+ async firstWhere(key, operatorOrValue, maybeValue) {
3054
+ const hasOperator = maybeValue !== void 0;
3055
+ const operator = hasOperator ? operatorOrValue : "=";
3056
+ const value = hasOperator ? maybeValue : operatorOrValue;
3057
+ return this.clone().where(this.buildComparisonWhere(key, operator, value)).first();
3058
+ }
3059
+ addLogicalWhere(operator, where) {
3060
+ if (!this.args.where) {
3061
+ this.args.where = where;
3062
+ return this;
3063
+ }
3064
+ this.args.where = { [operator]: [this.args.where, where] };
3065
+ return this;
3066
+ }
3067
+ buildComparisonWhere(key, operator, value) {
3068
+ if (operator === "=") return { [key]: value };
3069
+ if (operator === "!=") return { [key]: { not: value } };
3070
+ if (operator === ">") return { [key]: { gt: value } };
3071
+ if (operator === ">=") return { [key]: { gte: value } };
3072
+ if (operator === "<") return { [key]: { lt: value } };
3073
+ return { [key]: { lte: value } };
3074
+ }
3075
+ coerceDate(value) {
3076
+ const parsed = value instanceof Date ? new Date(value.getTime()) : new Date(value);
3077
+ if (Number.isNaN(parsed.getTime())) throw new ArkormException("Invalid date value for date-based query helper.");
3078
+ return parsed;
3079
+ }
3080
+ /**
3081
+ * Adds a strongly-typed equality where clause for a single attribute key.
3082
+ *
3083
+ * @param key
3084
+ * @param value
3085
+ * @returns
3086
+ */
3087
+ whereKey(key, value) {
3088
+ return this.where({ [key]: value });
3089
+ }
3090
+ /**
3091
+ * Adds a strongly-typed IN where clause for a single attribute key.
3092
+ *
3093
+ * @param key
3094
+ * @param values
3095
+ * @returns
3096
+ */
3097
+ whereIn(key, values) {
3098
+ return this.where({ [key]: { in: values } });
3099
+ }
3100
+ /**
3101
+ * Adds an orderBy clause to the query. This will overwrite any existing orderBy clause.
3102
+ *
3103
+ * @param orderBy
3104
+ * @returns
3105
+ */
3106
+ orderBy(orderBy) {
3107
+ this.randomOrderEnabled = false;
3108
+ this.args.orderBy = orderBy;
3109
+ return this;
3110
+ }
3111
+ /**
3112
+ * Puts the query results in random order.
3113
+ *
3114
+ * @returns
3115
+ */
3116
+ inRandomOrder() {
3117
+ this.randomOrderEnabled = true;
3118
+ return this;
3119
+ }
3120
+ /**
3121
+ * Removes existing order clauses and optionally applies a new one.
3122
+ *
3123
+ * @param column
3124
+ * @param direction
3125
+ * @returns
3126
+ */
3127
+ reorder(column, direction = "asc") {
3128
+ this.args.orderBy = void 0;
3129
+ this.randomOrderEnabled = false;
3130
+ if (!column) return this;
3131
+ return this.orderBy({ [column]: direction });
3132
+ }
3133
+ /**
3134
+ * Adds an orderBy descending clause for a timestamp-like column.
3135
+ *
3136
+ * @param column
3137
+ * @returns
3138
+ */
3139
+ latest(column = "createdAt") {
3140
+ return this.orderBy({ [column]: "desc" });
3141
+ }
3142
+ /**
3143
+ * Adds an orderBy ascending clause for a timestamp-like column.
3144
+ *
3145
+ * @param column
3146
+ * @returns
3147
+ */
3148
+ oldest(column = "createdAt") {
3149
+ return this.orderBy({ [column]: "asc" });
3150
+ }
3151
+ /**
3152
+ * Adds an include clause to the query. This will overwrite any existing include clause.
3153
+ *
3154
+ * @param include
3155
+ * @returns
3156
+ */
3157
+ include(include) {
3158
+ this.args.include = include;
3159
+ return this;
3160
+ }
3161
+ /**
3162
+ * Adds eager loading for the specified relations.
3163
+ * This will merge with any existing include clause.
3164
+ *
3165
+ * @param relations
3166
+ * @returns
3167
+ */
3168
+ with(relations) {
3169
+ const relationMap = this.normalizeWith(relations);
3170
+ const names = Object.keys(relationMap);
3171
+ this.args.include = {
3172
+ ...this.args.include || {},
3173
+ ...names.reduce((accumulator, name) => {
3174
+ accumulator[name] = true;
3175
+ return accumulator;
3176
+ }, {})
3177
+ };
3178
+ Object.entries(relationMap).forEach(([name, constraint]) => {
3179
+ this.eagerLoads[name] = constraint;
3180
+ });
3181
+ return this;
3182
+ }
3183
+ /**
3184
+ * Add a relationship count/existence constraint.
3185
+ *
3186
+ * @param relation
3187
+ * @param operator
3188
+ * @param count
3189
+ * @param callback
3190
+ * @returns
3191
+ */
3192
+ has(relation, operator = ">=", count = 1, callback) {
3193
+ this.relationFilters.push({
3194
+ relation,
3195
+ callback,
3196
+ operator,
3197
+ count,
3198
+ boolean: "AND"
3199
+ });
3200
+ return this;
3201
+ }
3202
+ /**
3203
+ * Add an OR relationship count/existence constraint.
3204
+ *
3205
+ * @param relation
3206
+ * @param operator
3207
+ * @param count
3208
+ * @returns
3209
+ */
3210
+ orHas(relation, operator = ">=", count = 1) {
3211
+ this.relationFilters.push({
3212
+ relation,
3213
+ operator,
3214
+ count,
3215
+ boolean: "OR"
3216
+ });
3217
+ return this;
3218
+ }
3219
+ /**
3220
+ * Add a relationship does-not-have constraint.
3221
+ *
3222
+ * @param relation
3223
+ * @param callback
3224
+ * @returns
3225
+ */
3226
+ doesntHave(relation, callback) {
3227
+ return this.has(relation, "<", 1, callback);
3228
+ }
3229
+ /**
3230
+ * Add an OR relationship does-not-have constraint.
3231
+ *
3232
+ * @param relation
3233
+ * @returns
3234
+ */
3235
+ orDoesntHave(relation) {
3236
+ return this.orHas(relation, "<", 1);
3237
+ }
3238
+ /**
3239
+ * Add a constrained relationship has clause.
3240
+ *
3241
+ * @param relation
3242
+ * @param callback
3243
+ * @param operator
3244
+ * @param count
3245
+ * @returns
3246
+ */
3247
+ whereHas(relation, callback, operator = ">=", count = 1) {
3248
+ return this.has(relation, operator, count, callback);
3249
+ }
3250
+ /**
3251
+ * Add an OR constrained relationship has clause.
3252
+ *
3253
+ * @param relation
3254
+ * @param callback
3255
+ * @param operator
3256
+ * @param count
3257
+ * @returns
3258
+ */
3259
+ orWhereHas(relation, callback, operator = ">=", count = 1) {
3260
+ this.relationFilters.push({
3261
+ relation,
3262
+ callback,
3263
+ operator,
3264
+ count,
3265
+ boolean: "OR"
3266
+ });
3267
+ return this;
3268
+ }
3269
+ /**
3270
+ * Add a constrained relationship does-not-have clause.
3271
+ *
3272
+ * @param relation
3273
+ * @param callback
3274
+ * @returns
3275
+ */
3276
+ whereDoesntHave(relation, callback) {
3277
+ return this.whereHas(relation, callback, "<", 1);
3278
+ }
3279
+ /**
3280
+ * Add an OR constrained relationship does-not-have clause.
3281
+ *
3282
+ * @param relation
3283
+ * @param callback
3284
+ * @returns
3285
+ */
3286
+ orWhereDoesntHave(relation, callback) {
3287
+ return this.orWhereHas(relation, callback, "<", 1);
3288
+ }
3289
+ /**
3290
+ * Add relationship count aggregate attributes.
3291
+ *
3292
+ * @param relations
3293
+ * @returns
3294
+ */
3295
+ withCount(relations) {
3296
+ (Array.isArray(relations) ? relations : [relations]).forEach((relation) => {
3297
+ this.relationAggregates.push({
3298
+ type: "count",
3299
+ relation
3300
+ });
3301
+ });
3302
+ return this;
3303
+ }
3304
+ /**
3305
+ * Add relationship existence aggregate attributes.
3306
+ *
3307
+ * @param relations
3308
+ * @returns
3309
+ */
3310
+ withExists(relations) {
3311
+ (Array.isArray(relations) ? relations : [relations]).forEach((relation) => {
3312
+ this.relationAggregates.push({
3313
+ type: "exists",
3314
+ relation
3315
+ });
3316
+ });
3317
+ return this;
3318
+ }
3319
+ /**
3320
+ * Add relationship sum aggregate attribute.
3321
+ *
3322
+ * @param relation
3323
+ * @param column
3324
+ * @returns
3325
+ */
3326
+ withSum(relation, column) {
3327
+ this.relationAggregates.push({
3328
+ type: "sum",
3329
+ relation,
3330
+ column
3331
+ });
3332
+ return this;
3333
+ }
3334
+ /**
3335
+ * Add relationship average aggregate attribute.
3336
+ *
3337
+ * @param relation
3338
+ * @param column
3339
+ * @returns
3340
+ */
3341
+ withAvg(relation, column) {
3342
+ this.relationAggregates.push({
3343
+ type: "avg",
3344
+ relation,
3345
+ column
3346
+ });
3347
+ return this;
3348
+ }
3349
+ /**
3350
+ * Add relationship minimum aggregate attribute.
3351
+ *
3352
+ * @param relation
3353
+ * @param column
3354
+ * @returns
3355
+ */
3356
+ withMin(relation, column) {
3357
+ this.relationAggregates.push({
3358
+ type: "min",
3359
+ relation,
3360
+ column
3361
+ });
3362
+ return this;
3363
+ }
3364
+ /**
3365
+ * Add relationship maximum aggregate attribute.
3366
+ *
3367
+ * @param relation
3368
+ * @param column
3369
+ * @returns
3370
+ */
3371
+ withMax(relation, column) {
3372
+ this.relationAggregates.push({
3373
+ type: "max",
3374
+ relation,
3375
+ column
3376
+ });
3377
+ return this;
3378
+ }
3379
+ /**
3380
+ * Includes soft-deleted records in the query results.
3381
+ * This method is only applicable if the model has soft delete enabled.
3382
+ *
3383
+ * @returns
3384
+ */
3385
+ withTrashed() {
3386
+ this.includeTrashed = true;
3387
+ this.onlyTrashedRecords = false;
3388
+ return this;
3389
+ }
3390
+ /**
3391
+ * Limits the query results to only soft-deleted records.
3392
+ * This method is only applicable if the model has soft delete enabled.
3393
+ *
3394
+ * @returns
3395
+ */
3396
+ onlyTrashed() {
3397
+ this.onlyTrashedRecords = true;
3398
+ this.includeTrashed = false;
3399
+ return this;
3400
+ }
3401
+ /**
3402
+ * Excludes soft-deleted records from the query results.
3403
+ * This is the default behavior, but this method can be used to explicitly
3404
+ * enforce it after using withTrashed or onlyTrashed.
3405
+ *
3406
+ * @returns
3407
+ */
3408
+ withoutTrashed() {
3409
+ this.includeTrashed = false;
3410
+ this.onlyTrashedRecords = false;
3411
+ return this;
3412
+ }
3413
+ /**
3414
+ * Applies a named scope to the query. A scope is a reusable query constraint
3415
+ * defined as a static method on the model. The scope method will look for a
3416
+ * method with the name `scope{Name}` on the model's prototype.
3417
+ * If found, it will call that method with the current query builder
3418
+ * instance and any additional arguments provided.
3419
+ *
3420
+ * @param name
3421
+ * @param args
3422
+ * @returns
3423
+ */
3424
+ scope(name, ...args) {
3425
+ const methodName = `scope${name.charAt(0).toUpperCase()}${name.slice(1)}`;
3426
+ const scope = this.model.prototype?.[methodName];
3427
+ if (typeof scope !== "function") throw new ArkormException(`Scope [${name}] is not defined.`);
3428
+ const scoped = scope.call(void 0, this, ...args);
3429
+ if (scoped && scoped !== this) return scoped;
3430
+ return this;
3431
+ }
3432
+ /**
3433
+ * Apply the callback when value is truthy.
3434
+ *
3435
+ * @param value
3436
+ * @param callback
3437
+ * @param defaultCallback
3438
+ * @returns
3439
+ */
3440
+ when(value, callback, defaultCallback) {
3441
+ const resolved = typeof value === "function" ? value() : value;
3442
+ if (resolved) return callback(this, resolved);
3443
+ if (defaultCallback) return defaultCallback(this, resolved);
3444
+ return this;
3445
+ }
3446
+ /**
3447
+ * Apply the callback when value is falsy.
3448
+ *
3449
+ * @param value
3450
+ * @param callback
3451
+ * @param defaultCallback
3452
+ * @returns
3453
+ */
3454
+ unless(value, callback, defaultCallback) {
3455
+ const resolved = typeof value === "function" ? value() : value;
3456
+ if (!resolved) return callback(this, resolved);
3457
+ if (defaultCallback) return defaultCallback(this, resolved);
3458
+ return this;
3459
+ }
3460
+ /**
3461
+ * Pass the query builder into a callback and return this.
3462
+ *
3463
+ * @param callback
3464
+ * @returns
3465
+ */
3466
+ tap(callback) {
3467
+ callback(this);
3468
+ return this;
3469
+ }
3470
+ /**
3471
+ * Pass the query builder into a callback and return callback result.
3472
+ *
3473
+ * @param callback
3474
+ * @returns
3475
+ */
3476
+ pipe(callback) {
3477
+ return callback(this);
3478
+ }
3479
+ /**
3480
+ * Adds a select clause to the query. This will overwrite any existing select clause.
3481
+ *
3482
+ * @param select
3483
+ * @returns
3484
+ */
3485
+ select(select) {
3486
+ this.args.select = select;
3487
+ return this;
3488
+ }
3489
+ /**
3490
+ * Adds a skip clause to the query for pagination.
3491
+ * This will overwrite any existing skip clause.
3492
+ *
3493
+ * @param skip
3494
+ * @returns
3495
+ */
3496
+ skip(skip) {
3497
+ this.args.skip = skip;
3498
+ return this;
3499
+ }
3500
+ /**
3501
+ * Alias for skip.
3502
+ *
3503
+ * @param value
3504
+ * @returns
3505
+ */
3506
+ offset(value) {
3507
+ return this.skip(value);
3508
+ }
3509
+ /**
3510
+ * Adds a take clause to the query for pagination.
3511
+ *
3512
+ * @param take
3513
+ * @returns
3514
+ */
3515
+ take(take) {
3516
+ this.args.take = take;
3517
+ return this;
3518
+ }
3519
+ /**
3520
+ * Alias for take.
3521
+ *
3522
+ * @param value
3523
+ * @returns
3524
+ */
3525
+ limit(value) {
3526
+ return this.take(value);
3527
+ }
3528
+ /**
3529
+ * Sets offset/limit for a 1-based page.
3530
+ *
3531
+ * @param page
3532
+ * @param perPage
3533
+ * @returns
3534
+ */
3535
+ forPage(page, perPage = 15) {
3536
+ const currentPage = Math.max(1, page);
3537
+ const pageSize = Math.max(1, perPage);
3538
+ return this.skip((currentPage - 1) * pageSize).take(pageSize);
3539
+ }
3540
+ /**
3541
+ * Executes the query and returns the results as a collection of model instances.
3542
+ *
3543
+ * @returns
3544
+ */
3545
+ async get() {
3546
+ const relationCache = /* @__PURE__ */ new WeakMap();
3547
+ const rows = await this.delegate.findMany(this.buildFindArgs());
3548
+ const normalizedRows = this.randomOrderEnabled ? this.shuffleRows(rows) : rows;
3549
+ const models = this.model.hydrateMany(normalizedRows);
3550
+ let filteredModels = models;
3551
+ if (this.hasRelationFilters()) if (this.hasOrRelationFilters() && this.args.where) {
3552
+ const baseIds = new Set(models.map((model) => this.getModelId(model)).filter((id) => id != null));
3553
+ const allRows = await this.delegate.findMany({
3554
+ ...this.args,
3555
+ where: this.buildSoftDeleteOnlyWhere()
3556
+ });
3557
+ const allModels = this.model.hydrateMany(allRows);
3558
+ filteredModels = await this.filterModelsByRelationConstraints(allModels, relationCache, baseIds);
3559
+ } else filteredModels = await this.filterModelsByRelationConstraints(models, relationCache);
3560
+ if (this.hasRelationAggregates()) await this.applyRelationAggregates(filteredModels, relationCache);
3561
+ await Promise.all(filteredModels.map(async (model) => {
3562
+ await model.load(this.eagerLoads);
3563
+ }));
3564
+ return new ArkormCollection(filteredModels);
3565
+ }
3566
+ /**
3567
+ * Executes the query and returns the first result as a model
3568
+ * instance, or null if no results are found.
3569
+ *
3570
+ * @returns
3571
+ */
3572
+ async first() {
3573
+ if (this.hasRelationFilters() || this.hasRelationAggregates()) return (await this.get()).all()[0] ?? null;
3574
+ if (this.randomOrderEnabled) {
3575
+ const rows = await this.delegate.findMany(this.buildFindArgs());
3576
+ if (rows.length === 0) return null;
3577
+ const row = this.shuffleRows(rows)[0];
3578
+ if (!row) return null;
3579
+ const model = this.model.hydrate(row);
3580
+ await model.load(this.eagerLoads);
3581
+ return model;
3582
+ }
3583
+ const row = await this.delegate.findFirst(this.buildFindArgs());
3584
+ if (!row) return null;
3585
+ const model = this.model.hydrate(row);
3586
+ await model.load(this.eagerLoads);
3587
+ return model;
3588
+ }
3589
+ /**
3590
+ * Executes the query and returns the first result as a model instance.
3591
+ *
3592
+ * @returns
3593
+ */
3594
+ async firstOrFail() {
3595
+ const model = await this.first();
3596
+ if (!model) throw new ModelNotFoundException("Record not found.");
3597
+ return model;
3598
+ }
3599
+ async find(value, key = "id") {
3600
+ return this.where({ [key]: value }).first();
3601
+ }
3602
+ async findOr(value, keyOrCallback, maybeCallback) {
3603
+ const key = typeof keyOrCallback === "string" ? keyOrCallback : "id";
3604
+ const callback = typeof keyOrCallback === "function" ? keyOrCallback : maybeCallback;
3605
+ if (!callback) throw new ArkormException("findOr requires a fallback callback.");
3606
+ const found = await this.find(value, key);
3607
+ if (found) return found;
3608
+ return callback();
3609
+ }
3610
+ /**
3611
+ * Returns a single column value from the first record.
3612
+ *
3613
+ * @param column
3614
+ * @returns
3615
+ */
3616
+ async value(column) {
3617
+ const row = await this.delegate.findFirst(this.buildFindArgs());
3618
+ if (!row) return null;
3619
+ return row[column] ?? null;
3620
+ }
3621
+ /**
3622
+ * Returns a single column value from the first record or throws.
3623
+ *
3624
+ * @param column
3625
+ * @returns
3626
+ */
3627
+ async valueOrFail(column) {
3628
+ const result = await this.value(column);
3629
+ if (result == null) throw new ModelNotFoundException("Record not found.");
3630
+ return result;
3631
+ }
3632
+ /**
3633
+ * Returns a collection with values for the given column.
3634
+ *
3635
+ * @param column
3636
+ * @param key
3637
+ * @returns
3638
+ */
3639
+ async pluck(column, key) {
3640
+ const rows = await this.delegate.findMany(this.buildFindArgs());
3641
+ if (!key) return new ArkormCollection(rows.map((row) => row[column]));
3642
+ return new ArkormCollection(rows.sort((leftRow, rightRow) => String(leftRow[key]).localeCompare(String(rightRow[key]))).map((row) => row[column]));
3643
+ }
3644
+ /**
3645
+ * Creates a new record with the specified data and returns it as a model instance.
3646
+ *
3647
+ * @param data
3648
+ * @returns
3649
+ */
3650
+ async create(data) {
3651
+ const created = await this.delegate.create({ data });
3652
+ return this.model.hydrate(created);
3653
+ }
3654
+ /**
3655
+ * Updates records matching the current query constraints with the
3656
+ * specified data and returns the updated record(s) as model instance(s).
3657
+ *
3658
+ * @param data
3659
+ * @returns
3660
+ */
3661
+ async update(data) {
3662
+ const where = this.buildWhere();
3663
+ if (!where) throw new ArkormException("Update requires a where clause.");
3664
+ const uniqueWhere = await this.resolveUniqueWhere(where);
3665
+ const updated = await this.delegate.update({
3666
+ where: uniqueWhere,
3667
+ data
3668
+ });
3669
+ return this.model.hydrate(updated);
3670
+ }
3671
+ /**
3672
+ * Deletes records matching the current query constraints and returns
3673
+ * the deleted record(s) as model instance(s).
3674
+ *
3675
+ * @returns
3676
+ */
3677
+ async delete() {
3678
+ const where = this.buildWhere();
3679
+ if (!where) throw new ArkormException("Delete requires a where clause.");
3680
+ const uniqueWhere = await this.resolveUniqueWhere(where);
3681
+ const deleted = await this.delegate.delete({ where: uniqueWhere });
3682
+ return this.model.hydrate(deleted);
3683
+ }
3684
+ /**
3685
+ * Counts the number of records matching the current query constraints.
3686
+ *
3687
+ * @returns
3688
+ */
3689
+ async count() {
3690
+ if (this.hasRelationFilters()) return (await this.get()).all().length;
3691
+ return this.delegate.count({ where: this.buildWhere() });
3692
+ }
3693
+ /**
3694
+ * Determines if any records exist for the current query constraints.
3695
+ *
3696
+ * @returns
3697
+ */
3698
+ async exists() {
3699
+ if (this.hasRelationFilters()) return await this.count() > 0;
3700
+ return await this.delegate.findFirst(this.buildFindArgs()) != null;
3701
+ }
3702
+ /**
3703
+ * Determines if no records exist for the current query constraints.
3704
+ *
3705
+ * @returns
3706
+ */
3707
+ async doesntExist() {
3708
+ return !await this.exists();
3709
+ }
3710
+ /**
3711
+ * Execute callback when no records exist.
3712
+ *
3713
+ * @param callback
3714
+ * @returns
3715
+ */
3716
+ async existsOr(callback) {
3717
+ if (await this.exists()) return true;
3718
+ return callback();
3719
+ }
3720
+ /**
3721
+ * Execute callback when records exist.
3722
+ *
3723
+ * @param callback
3724
+ * @returns
3725
+ */
3726
+ async doesntExistOr(callback) {
3727
+ if (await this.doesntExist()) return true;
3728
+ return callback();
3729
+ }
3730
+ /**
3731
+ * Returns minimum value for a column.
3732
+ *
3733
+ * @param column
3734
+ * @returns
3735
+ */
3736
+ async min(column) {
3737
+ const rows = await this.delegate.findMany(this.buildFindArgs());
3738
+ if (rows.length === 0) return null;
3739
+ const values = rows.map((row) => row[column]).filter((value) => value != null);
3740
+ if (values.length === 0) return null;
3741
+ return values.reduce((minValue, currentValue) => currentValue < minValue ? currentValue : minValue);
3742
+ }
3743
+ /**
3744
+ * Returns maximum value for a column.
3745
+ *
3746
+ * @param column
3747
+ * @returns
3748
+ */
3749
+ async max(column) {
3750
+ const rows = await this.delegate.findMany(this.buildFindArgs());
3751
+ if (rows.length === 0) return null;
3752
+ const values = rows.map((row) => row[column]).filter((value) => value != null);
3753
+ if (values.length === 0) return null;
3754
+ return values.reduce((maxValue, currentValue) => currentValue > maxValue ? currentValue : maxValue);
3755
+ }
3756
+ /**
3757
+ * Returns sum of numeric values for a column.
3758
+ *
3759
+ * @param column
3760
+ * @returns
3761
+ */
3762
+ async sum(column) {
3763
+ return (await this.delegate.findMany(this.buildFindArgs())).reduce((total, row) => {
3764
+ const value = row[column];
3765
+ const numeric = typeof value === "number" ? value : Number(value);
3766
+ return Number.isFinite(numeric) ? total + numeric : total;
3767
+ }, 0);
3768
+ }
3769
+ /**
3770
+ * Returns average of numeric values for a column.
3771
+ *
3772
+ * @param column
3773
+ * @returns
3774
+ */
3775
+ async avg(column) {
3776
+ const values = (await this.delegate.findMany(this.buildFindArgs())).map((row) => {
3777
+ const value = row[column];
3778
+ return typeof value === "number" ? value : Number(value);
3779
+ }).filter((value) => Number.isFinite(value));
3780
+ if (values.length === 0) return null;
3781
+ return values.reduce((total, value) => total + value, 0) / values.length;
3782
+ }
3783
+ /**
3784
+ * Adds a raw where clause when supported by the adapter.
3785
+ *
3786
+ * @param sql
3787
+ * @param bindings
3788
+ * @returns
3789
+ */
3790
+ whereRaw(sql, bindings = []) {
3791
+ const delegate = this.delegate;
3792
+ if (typeof delegate.applyRawWhere !== "function") throw new ArkormException("Raw where clauses are not supported by the current adapter.");
3793
+ this.args.where = delegate.applyRawWhere(this.buildWhere(), sql, bindings);
3794
+ return this;
3795
+ }
3796
+ /**
3797
+ * Adds a raw OR where clause when supported by the adapter.
3798
+ *
3799
+ * @param sql
3800
+ * @param bindings
3801
+ * @returns
3802
+ */
3803
+ orWhereRaw(sql, bindings = []) {
3804
+ const delegate = this.delegate;
3805
+ if (typeof delegate.applyRawWhere !== "function") throw new ArkormException("Raw where clauses are not supported by the current adapter.");
3806
+ const rawWhere = delegate.applyRawWhere(void 0, sql, bindings);
3807
+ return this.orWhere(rawWhere);
3808
+ }
3809
+ /**
3810
+ * Paginates the query results and returns a LengthAwarePaginator instance
3811
+ * containing data and total-aware pagination metadata.
3812
+ *
3813
+ * @param page
3814
+ * @param perPage
3815
+ * @param options
3816
+ * @returns
3817
+ */
3818
+ async paginate(page = 1, perPage = 15, options = {}) {
3819
+ if (this.hasRelationFilters() || this.hasRelationAggregates()) {
3820
+ const currentPage = Math.max(1, page);
3821
+ const pageSize = Math.max(1, perPage);
3822
+ const rows = (await this.get()).all();
3823
+ const start = (currentPage - 1) * pageSize;
3824
+ return new LengthAwarePaginator(new ArkormCollection(rows.slice(start, start + pageSize)), rows.length, pageSize, currentPage, options);
3825
+ }
3826
+ const currentPage = Math.max(1, page);
3827
+ const pageSize = Math.max(1, perPage);
3828
+ const total = await this.count();
3829
+ return new LengthAwarePaginator(await this.clone().skip((currentPage - 1) * pageSize).take(pageSize).get(), total, pageSize, currentPage, options);
3830
+ }
3831
+ /**
3832
+ * Paginates results without calculating total row count.
3833
+ *
3834
+ * @param perPage
3835
+ * @param page
3836
+ * @returns
3837
+ */
3838
+ async simplePaginate(perPage = 15, page = 1, options = {}) {
3839
+ if (this.hasRelationFilters() || this.hasRelationAggregates()) {
3840
+ const currentPage = Math.max(1, page);
3841
+ const pageSize = Math.max(1, perPage);
3842
+ const rows = (await this.get()).all();
3843
+ const start = (currentPage - 1) * pageSize;
3844
+ const pageRows = rows.slice(start, start + pageSize);
3845
+ const hasMorePages = start + pageSize < rows.length;
3846
+ return new Paginator(new ArkormCollection(pageRows), pageSize, currentPage, hasMorePages, options);
3847
+ }
3848
+ const currentPage = Math.max(1, page);
3849
+ const pageSize = Math.max(1, perPage);
3850
+ const items = await this.clone().skip((currentPage - 1) * pageSize).take(pageSize + 1).get();
3851
+ const hasMorePages = items.all().length > pageSize;
3852
+ return new Paginator(hasMorePages ? new ArkormCollection(items.all().slice(0, pageSize)) : items, pageSize, currentPage, hasMorePages, options);
3853
+ }
3854
+ /**
3855
+ * Creates a clone of the current query builder instance with the same state.
3856
+ *
3857
+ * @returns
3858
+ */
3859
+ clone() {
3860
+ const builder = new QueryBuilder(this.delegate, this.model);
3861
+ builder.args.where = this.args.where;
3862
+ builder.args.include = this.args.include;
3863
+ builder.args.orderBy = this.args.orderBy;
3864
+ builder.args.select = this.args.select;
3865
+ builder.args.skip = this.args.skip;
3866
+ builder.args.take = this.args.take;
3867
+ builder.includeTrashed = this.includeTrashed;
3868
+ builder.onlyTrashedRecords = this.onlyTrashedRecords;
3869
+ builder.randomOrderEnabled = this.randomOrderEnabled;
3870
+ this.relationFilters.forEach((filter) => {
3871
+ builder.relationFilters.push({ ...filter });
3872
+ });
3873
+ this.relationAggregates.forEach((aggregate) => {
3874
+ builder.relationAggregates.push({ ...aggregate });
3875
+ });
3876
+ Object.entries(this.eagerLoads).forEach(([key, value]) => {
3877
+ builder.eagerLoads[key] = value;
3878
+ });
3879
+ return builder;
3880
+ }
3881
+ /**
3882
+ * Normalizes the input for eager loading relations into a consistent format.
3883
+ *
3884
+ * @param relations
3885
+ * @returns
3886
+ */
3887
+ normalizeWith(relations) {
3888
+ if (typeof relations === "string") return { [relations]: void 0 };
3889
+ if (Array.isArray(relations)) return relations.reduce((accumulator, relation) => {
3890
+ accumulator[relation] = void 0;
3891
+ return accumulator;
3892
+ }, {});
3893
+ return relations;
3894
+ }
3895
+ /**
3896
+ * Builds the where clause for the query, taking into account soft delete
3897
+ * settings if applicable.
3898
+ *
3899
+ * @returns
3900
+ */
3901
+ buildWhere() {
3902
+ const softDeleteConfig = this.model.getSoftDeleteConfig();
3903
+ if (!softDeleteConfig.enabled) return this.args.where;
3904
+ if (this.includeTrashed) return this.args.where;
3905
+ const softDeleteClause = this.onlyTrashedRecords ? { [softDeleteConfig.column]: { not: null } } : { [softDeleteConfig.column]: null };
3906
+ if (!this.args.where) return softDeleteClause;
3907
+ return { AND: [this.args.where, softDeleteClause] };
3908
+ }
3909
+ /**
3910
+ * Builds the arguments for the findMany delegate method, including the where clause.
3911
+ *
3912
+ * @returns
3913
+ */
3914
+ buildFindArgs() {
3915
+ return {
3916
+ ...this.args,
3917
+ where: this.buildWhere()
3918
+ };
3919
+ }
3920
+ /**
3921
+ * Resolves a unique where clause for update and delete operations.
3922
+ *
3923
+ * @param where
3924
+ * @returns
3925
+ */
3926
+ async resolveUniqueWhere(where) {
3927
+ if (this.isUniqueWhere(where)) return where;
3928
+ const row = await this.delegate.findFirst({ where });
3929
+ if (!row) throw new ArkormException("Record not found for update/delete operation.");
3930
+ const record = row;
3931
+ if (!Object.prototype.hasOwnProperty.call(record, "id")) throw new ArkormException("Unable to resolve a unique identifier for update/delete operation. Include an id in the query constraints.");
3932
+ return { id: record.id };
3933
+ }
3934
+ /**
3935
+ * Checks if the provided where clause is already a unique
3936
+ * identifier (i.e., contains only an 'id' field).
3937
+ *
3938
+ * @param where
3939
+ * @returns
3940
+ */
3941
+ isUniqueWhere(where) {
3942
+ return Object.keys(where).length === 1 && Object.prototype.hasOwnProperty.call(where, "id");
3943
+ }
3944
+ shuffleRows(rows) {
3945
+ const shuffled = [...rows];
3946
+ for (let index = shuffled.length - 1; index > 0; index--) {
3947
+ const swapIndex = Math.floor(Math.random() * (index + 1));
3948
+ const current = shuffled[index];
3949
+ shuffled[index] = shuffled[swapIndex];
3950
+ shuffled[swapIndex] = current;
3951
+ }
3952
+ return shuffled;
3953
+ }
3954
+ hasRelationFilters() {
3955
+ return this.relationFilters.length > 0;
3956
+ }
3957
+ hasOrRelationFilters() {
3958
+ return this.relationFilters.some((filter) => filter.boolean === "OR");
3959
+ }
3960
+ hasRelationAggregates() {
3961
+ return this.relationAggregates.length > 0;
3962
+ }
3963
+ async filterModelsByRelationConstraints(models, relationCache, baseIds) {
3964
+ return (await Promise.all(models.map(async (model) => {
3965
+ let result = null;
3966
+ if (baseIds) result = baseIds.has(this.getModelId(model));
3967
+ for (const filter of this.relationFilters) {
3968
+ const relatedCount = await this.resolveRelatedCount(model, filter.relation, relationCache, filter.callback);
3969
+ const condition = this.compareCount(relatedCount, filter.operator, filter.count);
3970
+ if (result == null) result = condition;
3971
+ else result = filter.boolean === "AND" ? result && condition : result || condition;
3972
+ }
3973
+ return {
3974
+ model,
3975
+ passes: result ?? true
3976
+ };
3977
+ }))).filter((entry) => entry.passes).map((entry) => entry.model);
3978
+ }
3979
+ getModelId(model) {
3980
+ const readable = model;
3981
+ if (typeof readable.getAttribute !== "function") return null;
3982
+ const id = readable.getAttribute("id");
3983
+ if (typeof id === "number" || typeof id === "string") return id;
3984
+ return null;
3985
+ }
3986
+ buildSoftDeleteOnlyWhere() {
3987
+ const softDeleteConfig = this.model.getSoftDeleteConfig();
3988
+ if (!softDeleteConfig.enabled) return void 0;
3989
+ if (this.includeTrashed) return void 0;
3990
+ return this.onlyTrashedRecords ? { [softDeleteConfig.column]: { not: null } } : { [softDeleteConfig.column]: null };
3991
+ }
3992
+ async applyRelationAggregates(models, relationCache) {
3993
+ const cache = relationCache ?? /* @__PURE__ */ new WeakMap();
3994
+ await Promise.all(models.map(async (model) => {
3995
+ for (const aggregate of this.relationAggregates) {
3996
+ const results = await this.resolveRelatedResults(model, aggregate.relation, cache);
3997
+ const list = Array.isArray(results) ? results : results ? [results] : [];
3998
+ const attributeKey = this.buildAggregateAttributeKey(aggregate);
3999
+ if (aggregate.type === "count") {
4000
+ this.assignAggregate(model, attributeKey, list.length);
4001
+ continue;
4002
+ }
4003
+ if (aggregate.type === "exists") {
4004
+ this.assignAggregate(model, attributeKey, list.length > 0);
4005
+ continue;
4006
+ }
4007
+ const values = list.map((item) => item.getAttribute(aggregate.column)).filter((value) => value != null);
4008
+ if (aggregate.type === "sum") {
4009
+ const sum = values.reduce((total, value) => {
4010
+ const numeric = typeof value === "number" ? value : Number(value);
4011
+ return Number.isFinite(numeric) ? total + numeric : total;
4012
+ }, 0);
4013
+ this.assignAggregate(model, attributeKey, sum);
4014
+ continue;
4015
+ }
4016
+ if (aggregate.type === "avg") {
4017
+ const numericValues = values.map((value) => typeof value === "number" ? value : Number(value)).filter((value) => Number.isFinite(value));
4018
+ const avg = numericValues.length === 0 ? null : numericValues.reduce((total, value) => total + value, 0) / numericValues.length;
4019
+ this.assignAggregate(model, attributeKey, avg);
4020
+ continue;
4021
+ }
4022
+ if (aggregate.type === "min") {
4023
+ const min = values.length === 0 ? null : values.reduce((left, right) => right < left ? right : left);
4024
+ this.assignAggregate(model, attributeKey, min);
4025
+ continue;
4026
+ }
4027
+ const max = values.length === 0 ? null : values.reduce((left, right) => right > left ? right : left);
4028
+ this.assignAggregate(model, attributeKey, max);
4029
+ }
4030
+ }));
4031
+ }
4032
+ async resolveRelatedCount(model, relation, relationCache, callback) {
4033
+ const results = await this.resolveRelatedResults(model, relation, relationCache, callback);
4034
+ if (Array.isArray(results)) return results.length;
4035
+ return results ? 1 : 0;
4036
+ }
4037
+ async resolveRelatedResults(model, relation, relationCache, callback) {
4038
+ const modelCacheKey = model;
4039
+ const callbackCacheKey = callback ?? "__none__";
4040
+ let relationMap = relationCache.get(modelCacheKey);
4041
+ if (!relationMap) {
4042
+ relationMap = /* @__PURE__ */ new Map();
4043
+ relationCache.set(modelCacheKey, relationMap);
4044
+ }
4045
+ let callbackMap = relationMap.get(relation);
4046
+ if (!callbackMap) {
4047
+ callbackMap = /* @__PURE__ */ new Map();
4048
+ relationMap.set(relation, callbackMap);
4049
+ }
4050
+ const cached = callbackMap.get(callbackCacheKey);
4051
+ if (cached) return await cached;
4052
+ const resolver = (async () => {
4053
+ const relationMethod = model[relation];
4054
+ if (typeof relationMethod !== "function") throw new ArkormException(`Relation [${relation}] is not defined on the model.`);
4055
+ const relationInstance = relationMethod.call(model);
4056
+ if (callback && typeof relationInstance.constrain === "function") relationInstance.constrain((query) => {
4057
+ return callback(query) ?? query;
4058
+ });
4059
+ if (typeof relationInstance.get === "function") {
4060
+ const results = await relationInstance.get();
4061
+ if (results instanceof ArkormCollection) return results.all();
4062
+ return results;
4063
+ }
4064
+ if (typeof relationInstance.getResults === "function") {
4065
+ const results = await relationInstance.getResults();
4066
+ if (results instanceof ArkormCollection) return results.all();
4067
+ return results;
4068
+ }
4069
+ throw new ArkormException(`Relation [${relation}] does not support result resolution.`);
4070
+ })();
4071
+ callbackMap.set(callbackCacheKey, resolver);
4072
+ return await resolver;
4073
+ }
4074
+ compareCount(left, operator, right) {
4075
+ if (operator === ">=") return left >= right;
4076
+ if (operator === ">") return left > right;
4077
+ if (operator === "=") return left === right;
4078
+ if (operator === "!=") return left !== right;
4079
+ if (operator === "<=") return left <= right;
4080
+ return left < right;
4081
+ }
4082
+ buildAggregateAttributeKey(aggregate) {
4083
+ const relationName = aggregate.relation;
4084
+ if (aggregate.type === "count") return `${relationName}Count`;
4085
+ if (aggregate.type === "exists") return `${relationName}Exists`;
4086
+ const columnName = aggregate.column ? `${aggregate.column.charAt(0).toUpperCase()}${aggregate.column.slice(1)}` : "";
4087
+ return `${relationName}${`${aggregate.type.charAt(0).toUpperCase()}${aggregate.type.slice(1)}`}${columnName}`;
4088
+ }
4089
+ assignAggregate(model, key, value) {
4090
+ const assignable = model;
4091
+ if (typeof assignable.setAttribute === "function") {
4092
+ assignable.setAttribute(key, value);
4093
+ return;
4094
+ }
4095
+ model[key] = value;
4096
+ }
4097
+ };
4098
+
4099
+ //#endregion
4100
+ //#region src/Model.ts
4101
+ /**
4102
+ * Base model class that all models should extend.
4103
+ *
4104
+ * @template TModel The type of the model extending this base class.
4105
+ *
4106
+ * @author Legacy (3m1n3nc3)
4107
+ * @since 0.1.0
4108
+ */
4109
+ var Model = class Model {
4110
+ static factoryClass;
4111
+ static client;
4112
+ static delegate;
4113
+ static softDeletes = false;
4114
+ static deletedAtColumn = "deletedAt";
4115
+ static globalScopes = {};
4116
+ static eventListeners = {};
4117
+ casts = {};
4118
+ hidden = [];
4119
+ visible = [];
4120
+ appends = [];
4121
+ attributes;
4122
+ constructor(attributes = {}) {
4123
+ this.attributes = {};
4124
+ this.fill(attributes);
4125
+ return new Proxy(this, {
4126
+ get: (target, key, receiver) => {
4127
+ if (typeof key !== "string" || key in target) return Reflect.get(target, key, receiver);
4128
+ return target.getAttribute(key);
4129
+ },
4130
+ set: (target, key, value, receiver) => {
4131
+ if (typeof key !== "string" || key in target) return Reflect.set(target, key, value, receiver);
4132
+ target.setAttribute(key, value);
4133
+ return true;
4134
+ }
4135
+ });
4136
+ }
4137
+ /**
4138
+ * Set the Prisma client delegates for all models.
4139
+ *
4140
+ * @param client
4141
+ */
4142
+ static setClient(client) {
4143
+ this.client = client;
4144
+ }
4145
+ static setFactory(factoryClass) {
4146
+ this.factoryClass = factoryClass;
4147
+ }
4148
+ static factory(count) {
4149
+ const factoryClass = this.factoryClass;
4150
+ if (!factoryClass) throw new ArkormException(`Factory is not configured for model [${this.name}].`);
4151
+ const factory = new factoryClass();
4152
+ if (typeof count === "number") factory.count(count);
4153
+ return factory;
4154
+ }
4155
+ /**
4156
+ * Register a global scope for the model.
4157
+ *
4158
+ * @param name
4159
+ * @param scope
4160
+ */
4161
+ static addGlobalScope(name, scope) {
4162
+ this.ensureOwnGlobalScopes();
4163
+ this.globalScopes[name] = scope;
4164
+ }
4165
+ /**
4166
+ * Remove a global scope by name.
4167
+ *
4168
+ * @param name
4169
+ */
4170
+ static removeGlobalScope(name) {
4171
+ this.ensureOwnGlobalScopes();
4172
+ delete this.globalScopes[name];
4173
+ }
4174
+ /**
4175
+ * Clear all global scopes for the model.
4176
+ */
4177
+ static clearGlobalScopes() {
4178
+ this.globalScopes = {};
4179
+ }
4180
+ /**
4181
+ * Register an event listener for a model lifecycle event.
4182
+ *
4183
+ * @param event
4184
+ * @param listener
4185
+ */
4186
+ static on(event, listener) {
4187
+ this.ensureOwnEventListeners();
4188
+ if (!this.eventListeners[event]) this.eventListeners[event] = [];
4189
+ this.eventListeners[event]?.push(listener);
4190
+ }
4191
+ /**
4192
+ * Remove listeners for an event. If listener is omitted, all listeners for that event are removed.
4193
+ *
4194
+ * @param event
4195
+ * @param listener
4196
+ */
4197
+ static off(event, listener) {
4198
+ this.ensureOwnEventListeners();
4199
+ if (!listener) {
4200
+ delete this.eventListeners[event];
4201
+ return;
4202
+ }
4203
+ this.eventListeners[event] = (this.eventListeners[event] || []).filter((registered) => registered !== listener);
4204
+ }
4205
+ /**
4206
+ * Clears all event listeners for the model.
4207
+ */
4208
+ static clearEventListeners() {
4209
+ this.eventListeners = {};
4210
+ }
4211
+ /**
4212
+ * Get the Prisma delegate for the model.
4213
+ * If a delegate name is provided, it will attempt to resolve that delegate.
4214
+ * Otherwise, it will attempt to resolve a delegate based on the model's name or
4215
+ * the static `delegate` property.
4216
+ *
4217
+ * @param delegate
4218
+ * @returns
4219
+ */
4220
+ static getDelegate(delegate) {
4221
+ ensureArkormConfigLoading();
4222
+ const key = delegate || this.delegate || `${(0, _h3ravel_support.str)(this.name).camel().plural()}`;
4223
+ const candidates = [
4224
+ key,
4225
+ `${(0, _h3ravel_support.str)(key).camel()}`,
4226
+ `${(0, _h3ravel_support.str)(key).singular()}`,
4227
+ `${(0, _h3ravel_support.str)(key).camel().singular()}`
4228
+ ];
4229
+ const runtimeClient = getRuntimePrismaClient();
4230
+ const resolved = candidates.map((name) => this.client?.[name] ?? runtimeClient?.[name]).find((candidate) => isDelegateLike(candidate));
4231
+ if (!resolved) throw new ArkormException(`Database delegate [${key}] is not configured.`);
4232
+ return resolved;
4233
+ }
4234
+ /**
4235
+ * Get a new query builder instance for the model.
4236
+ *
4237
+ * @param this
4238
+ * @returns
4239
+ */
4240
+ static query() {
4241
+ let builder = new QueryBuilder(this.getDelegate(), this);
4242
+ const modelClass = this;
4243
+ modelClass.ensureOwnGlobalScopes();
4244
+ Object.values(modelClass.globalScopes).forEach((scope) => {
4245
+ const scoped = scope(builder);
4246
+ if (scoped && scoped !== builder) builder = scoped;
4247
+ });
4248
+ return builder;
4249
+ }
4250
+ /**
4251
+ * Get a query builder instance that includes soft-deleted records.
4252
+ *
4253
+ * @param this
4254
+ * @returns
4255
+ */
4256
+ static withTrashed() {
4257
+ return this.query().withTrashed();
4258
+ }
4259
+ /**
4260
+ * Get a query builder instance that only includes soft-deleted records.
4261
+ *
4262
+ * @param this
4263
+ * @returns
4264
+ */
4265
+ static onlyTrashed() {
4266
+ return this.query().onlyTrashed();
4267
+ }
4268
+ /**
4269
+ * Get a query builder instance that excludes soft-deleted records.
4270
+ * This is the default behavior of the query builder, but this method can be used
4271
+ * to explicitly specify it after using `withTrashed` or `onlyTrashed`.
4272
+ *
4273
+ * @param this
4274
+ * @param name
4275
+ * @param args
4276
+ * @returns
4277
+ */
4278
+ static scope(name, ...args) {
4279
+ return this.query().scope(name, ...args);
4280
+ }
4281
+ /**
4282
+ * Get the soft delete configuration for the model, including whether
4283
+ * soft deletes are enabled and the name of the deleted at column.
4284
+ *
4285
+ * @returns
4286
+ */
4287
+ static getSoftDeleteConfig() {
4288
+ return {
4289
+ enabled: this.softDeletes,
4290
+ column: this.deletedAtColumn
4291
+ };
4292
+ }
4293
+ /**
4294
+ * Hydrate a model instance from a plain object of attributes.
4295
+ *
4296
+ * @param this
4297
+ * @param attributes
4298
+ * @returns
4299
+ */
4300
+ static hydrate(attributes) {
4301
+ return new this(attributes);
4302
+ }
4303
+ /**
4304
+ * Hydrate multiple model instances from an array of plain objects of attributes.
4305
+ *
4306
+ * @param this
4307
+ * @param attributes
4308
+ * @returns
4309
+ */
4310
+ static hydrateMany(attributes) {
4311
+ return attributes.map((attribute) => new this(attribute));
4312
+ }
4313
+ fill(attributes) {
4314
+ Object.entries(attributes).forEach(([key, value]) => {
4315
+ this.setAttribute(key, value);
4316
+ });
4317
+ return this;
4318
+ }
4319
+ getAttribute(key) {
4320
+ const mutator = this.resolveGetMutator(key);
4321
+ const cast = this.casts[key];
4322
+ let value = this.attributes[key];
4323
+ if (cast) value = resolveCast(cast).get(value);
4324
+ if (mutator) return mutator.call(this, value);
4325
+ return value;
4326
+ }
4327
+ setAttribute(key, value) {
4328
+ const mutator = this.resolveSetMutator(key);
4329
+ const cast = this.casts[key];
4330
+ let resolved = value;
4331
+ if (mutator) resolved = mutator.call(this, resolved);
4332
+ if (cast) resolved = resolveCast(cast).set(resolved);
4333
+ this.attributes[key] = resolved;
4334
+ return this;
4335
+ }
4336
+ /**
4337
+ * Save the model to the database.
4338
+ * If the model has an identifier (id), it will perform an update.
4339
+ * Otherwise, it will perform a create.
4340
+ *
4341
+ * @returns
4342
+ */
4343
+ async save() {
4344
+ const identifier = this.getAttribute("id");
4345
+ const payload = this.getRawAttributes();
4346
+ const constructor = this.constructor;
4347
+ if (identifier == null) {
4348
+ await Model.dispatchEvent(constructor, "saving", this);
4349
+ await Model.dispatchEvent(constructor, "creating", this);
4350
+ const model = await constructor.query().create(payload);
4351
+ this.fill(model.getRawAttributes());
4352
+ await Model.dispatchEvent(constructor, "created", this);
4353
+ await Model.dispatchEvent(constructor, "saved", this);
4354
+ return this;
4355
+ }
4356
+ await Model.dispatchEvent(constructor, "saving", this);
4357
+ await Model.dispatchEvent(constructor, "updating", this);
4358
+ const model = await constructor.query().where({ id: identifier }).update(payload);
4359
+ this.fill(model.getRawAttributes());
4360
+ await Model.dispatchEvent(constructor, "updated", this);
4361
+ await Model.dispatchEvent(constructor, "saved", this);
4362
+ return this;
4363
+ }
4364
+ /**
4365
+ * Delete the model from the database.
4366
+ * If soft deletes are enabled, it will perform a soft delete by
4367
+ * setting the deleted at column to the current date.
4368
+ * Otherwise, it will perform a hard delete.
4369
+ *
4370
+ * @returns
4371
+ */
4372
+ async delete() {
4373
+ const identifier = this.getAttribute("id");
4374
+ if (identifier == null) throw new ArkormException("Cannot delete a model without an id.");
4375
+ const constructor = this.constructor;
4376
+ await Model.dispatchEvent(constructor, "deleting", this);
4377
+ const softDeleteConfig = constructor.getSoftDeleteConfig();
4378
+ if (softDeleteConfig.enabled) {
4379
+ const model = await constructor.query().where({ id: identifier }).update({ [softDeleteConfig.column]: /* @__PURE__ */ new Date() });
4380
+ this.fill(model.getRawAttributes());
4381
+ await Model.dispatchEvent(constructor, "deleted", this);
4382
+ return this;
4383
+ }
4384
+ const deleted = await constructor.query().where({ id: identifier }).delete();
4385
+ this.fill(deleted.getRawAttributes());
4386
+ await Model.dispatchEvent(constructor, "deleted", this);
4387
+ return this;
4388
+ }
4389
+ /**
4390
+ * Permanently delete the model from the database, regardless of whether soft
4391
+ * deletes are enabled.
4392
+ *
4393
+ * @returns
4394
+ */
4395
+ async forceDelete() {
4396
+ const identifier = this.getAttribute("id");
4397
+ if (identifier == null) throw new ArkormException("Cannot force delete a model without an id.");
4398
+ const constructor = this.constructor;
4399
+ await Model.dispatchEvent(constructor, "forceDeleting", this);
4400
+ await Model.dispatchEvent(constructor, "deleting", this);
4401
+ const deleted = await constructor.query().withTrashed().where({ id: identifier }).delete();
4402
+ this.fill(deleted.getRawAttributes());
4403
+ await Model.dispatchEvent(constructor, "deleted", this);
4404
+ await Model.dispatchEvent(constructor, "forceDeleted", this);
4405
+ return this;
4406
+ }
4407
+ /**
4408
+ * Restore a soft-deleted model by setting the deleted at column to null.
4409
+ *
4410
+ * @returns
4411
+ */
4412
+ async restore() {
4413
+ const identifier = this.getAttribute("id");
4414
+ if (identifier == null) throw new ArkormException("Cannot restore a model without an id.");
4415
+ const constructor = this.constructor;
4416
+ const softDeleteConfig = constructor.getSoftDeleteConfig();
4417
+ if (!softDeleteConfig.enabled) return this;
4418
+ await Model.dispatchEvent(constructor, "restoring", this);
4419
+ const model = await constructor.query().withTrashed().where({ id: identifier }).update({ [softDeleteConfig.column]: null });
4420
+ this.fill(model.getRawAttributes());
4421
+ await Model.dispatchEvent(constructor, "restored", this);
4422
+ return this;
4423
+ }
4424
+ /**
4425
+ * Load related models onto the current model instance.
4426
+ *
4427
+ * @param relations
4428
+ * @returns
4429
+ */
4430
+ async load(relations) {
4431
+ const relationMap = this.normalizeRelationMap(relations);
4432
+ await Promise.all(Object.entries(relationMap).map(async ([name, constraint]) => {
4433
+ const resolver = this[name];
4434
+ if (typeof resolver !== "function") return;
4435
+ const relation = resolver.call(this);
4436
+ if (constraint) relation.constrain(constraint);
4437
+ const results = await relation.getResults();
4438
+ this.attributes[name] = results;
4439
+ }));
4440
+ return this;
4441
+ }
4442
+ /**
4443
+ * Get the raw attributes of the model without applying any mutators or casts.
4444
+ *
4445
+ * @returns
4446
+ */
4447
+ getRawAttributes() {
4448
+ return { ...this.attributes };
4449
+ }
4450
+ /**
4451
+ * Convert the model instance to a plain object, applying visibility
4452
+ * rules, appends, and mutators.
4453
+ *
4454
+ * @returns
4455
+ */
4456
+ toObject() {
4457
+ const object = (this.visible.length > 0 ? this.visible : Object.keys(this.attributes).filter((key) => !this.hidden.includes(key))).reduce((accumulator, key) => {
4458
+ let value = this.getAttribute(key);
4459
+ if (value instanceof Date) value = value.toISOString();
4460
+ accumulator[key] = value;
4461
+ return accumulator;
4462
+ }, {});
4463
+ this.appends.forEach((attribute) => {
4464
+ object[attribute] = this.getAttribute(attribute);
4465
+ });
4466
+ return object;
4467
+ }
4468
+ /**
4469
+ * Convert the model instance to JSON by first converting it to a plain object.
4470
+ *
4471
+ * @returns
4472
+ */
4473
+ toJSON() {
4474
+ return this.toObject();
4475
+ }
4476
+ /**
4477
+ * Define a has one relationship.
4478
+ *
4479
+ * @param related
4480
+ * @param foreignKey
4481
+ * @param localKey
4482
+ * @returns
4483
+ */
4484
+ hasOne(related, foreignKey, localKey = "id") {
4485
+ return new HasOneRelation(this, related, foreignKey, localKey);
4486
+ }
4487
+ /**
4488
+ * Define a has many relationship.
4489
+ *
4490
+ * @param related
4491
+ * @param foreignKey
4492
+ * @param localKey
4493
+ * @returns
4494
+ */
4495
+ hasMany(related, foreignKey, localKey = "id") {
4496
+ return new HasManyRelation(this, related, foreignKey, localKey);
4497
+ }
4498
+ /**
4499
+ * Define a belongs to relationship.
4500
+ *
4501
+ * @param related
4502
+ * @param foreignKey
4503
+ * @param ownerKey
4504
+ * @returns
4505
+ */
4506
+ belongsTo(related, foreignKey, ownerKey = "id") {
4507
+ return new BelongsToRelation(this, related, foreignKey, ownerKey);
4508
+ }
4509
+ /**
4510
+ * Define a belongs to many relationship.
4511
+ *
4512
+ * @param related
4513
+ * @param throughDelegate
4514
+ * @param foreignPivotKey
4515
+ * @param relatedPivotKey
4516
+ * @param parentKey
4517
+ * @param relatedKey
4518
+ * @returns
4519
+ */
4520
+ belongsToMany(related, throughDelegate, foreignPivotKey, relatedPivotKey, parentKey = "id", relatedKey = "id") {
4521
+ return new BelongsToManyRelation(this, related, throughDelegate, foreignPivotKey, relatedPivotKey, parentKey, relatedKey);
4522
+ }
4523
+ /**
4524
+ * Define a has one through relationship.
4525
+ *
4526
+ * @param related
4527
+ * @param throughDelegate
4528
+ * @param firstKey
4529
+ * @param secondKey
4530
+ * @param localKey
4531
+ * @param secondLocalKey
4532
+ * @returns
4533
+ */
4534
+ hasOneThrough(related, throughDelegate, firstKey, secondKey, localKey = "id", secondLocalKey = "id") {
4535
+ return new HasOneThroughRelation(this, related, throughDelegate, firstKey, secondKey, localKey, secondLocalKey);
4536
+ }
4537
+ /**
4538
+ * Define a has many through relationship.
4539
+ *
4540
+ * @param related
4541
+ * @param throughDelegate
4542
+ * @param firstKey
4543
+ * @param secondKey
4544
+ * @param localKey
4545
+ * @param secondLocalKey
4546
+ * @returns
4547
+ */
4548
+ hasManyThrough(related, throughDelegate, firstKey, secondKey, localKey = "id", secondLocalKey = "id") {
4549
+ return new HasManyThroughRelation(this, related, throughDelegate, firstKey, secondKey, localKey, secondLocalKey);
4550
+ }
4551
+ /**
4552
+ * Define a polymorphic one to one relationship.
4553
+ *
4554
+ * @param related
4555
+ * @param morphName
4556
+ * @param localKey
4557
+ * @returns
4558
+ */
4559
+ morphOne(related, morphName, localKey = "id") {
4560
+ return new MorphOneRelation(this, related, morphName, localKey);
4561
+ }
4562
+ /**
4563
+ * Define a polymorphic one to many relationship.
4564
+ *
4565
+ * @param related
4566
+ * @param morphName
4567
+ * @param localKey
4568
+ * @returns
4569
+ */
4570
+ morphMany(related, morphName, localKey = "id") {
4571
+ return new MorphManyRelation(this, related, morphName, localKey);
4572
+ }
4573
+ /**
4574
+ * Define a polymorphic many to many relationship.
4575
+ *
4576
+ * @param related
4577
+ * @param throughDelegate
4578
+ * @param morphName
4579
+ * @param relatedPivotKey
4580
+ * @param parentKey
4581
+ * @param relatedKey
4582
+ * @returns
4583
+ */
4584
+ morphToMany(related, throughDelegate, morphName, relatedPivotKey, parentKey = "id", relatedKey = "id") {
4585
+ return new MorphToManyRelation(this, related, throughDelegate, morphName, relatedPivotKey, parentKey, relatedKey);
4586
+ }
4587
+ /**
4588
+ * Resolve a get mutator method for a given attribute key, if it exists.
4589
+ *
4590
+ * @param key
4591
+ * @returns
4592
+ */
4593
+ resolveGetMutator(key) {
4594
+ const methodName = `get${(0, _h3ravel_support.str)(key).studly()}Attribute`;
4595
+ const method = this[methodName];
4596
+ return typeof method === "function" ? method : null;
4597
+ }
4598
+ /**
4599
+ * Resolve a set mutator method for a given attribute key, if it exists.
4600
+ *
4601
+ * @param key
4602
+ * @returns
4603
+ */
4604
+ resolveSetMutator(key) {
4605
+ const methodName = `set${(0, _h3ravel_support.str)(key).studly()}Attribute`;
4606
+ const method = this[methodName];
4607
+ return typeof method === "function" ? method : null;
4608
+ }
4609
+ /**
4610
+ * Ensures global scopes are own properties on subclass constructors.
4611
+ */
4612
+ static ensureOwnGlobalScopes() {
4613
+ if (!Object.prototype.hasOwnProperty.call(this, "globalScopes")) this.globalScopes = { ...this.globalScopes || {} };
4614
+ }
4615
+ /**
4616
+ * Ensures event listeners are own properties on subclass constructors.
4617
+ */
4618
+ static ensureOwnEventListeners() {
4619
+ if (!Object.prototype.hasOwnProperty.call(this, "eventListeners")) this.eventListeners = { ...this.eventListeners || {} };
4620
+ }
4621
+ /**
4622
+ * Dispatches lifecycle events to registered listeners.
4623
+ *
4624
+ * @param modelClass
4625
+ * @param event
4626
+ * @param model
4627
+ */
4628
+ static async dispatchEvent(modelClass, event, model) {
4629
+ modelClass.ensureOwnEventListeners();
4630
+ const listeners = modelClass.eventListeners[event] || [];
4631
+ for (const listener of listeners) await listener(model);
4632
+ }
4633
+ /**
4634
+ * Normalize the relation map for eager loading.
4635
+ *
4636
+ * @param relations
4637
+ * @returns
4638
+ */
4639
+ normalizeRelationMap(relations) {
4640
+ if (typeof relations === "string") return { [relations]: void 0 };
4641
+ if (Array.isArray(relations)) return relations.reduce((accumulator, relation) => {
4642
+ accumulator[relation] = void 0;
4643
+ return accumulator;
4644
+ }, {});
4645
+ return relations;
4646
+ }
4647
+ };
4648
+
4649
+ //#endregion
4650
+ exports.ArkormCollection = ArkormCollection;
4651
+ exports.ArkormException = ArkormException;
4652
+ exports.CliApp = CliApp;
4653
+ exports.InitCommand = InitCommand;
4654
+ exports.InlineFactory = InlineFactory;
4655
+ exports.LengthAwarePaginator = LengthAwarePaginator;
4656
+ exports.MakeFactoryCommand = MakeFactoryCommand;
4657
+ exports.MakeMigrationCommand = MakeMigrationCommand;
4658
+ exports.MakeModelCommand = MakeModelCommand;
4659
+ exports.MakeSeederCommand = MakeSeederCommand;
4660
+ exports.MigrateCommand = MigrateCommand;
4661
+ exports.Migration = Migration;
4662
+ exports.Model = Model;
4663
+ exports.ModelFactory = ModelFactory;
4664
+ exports.ModelNotFoundException = ModelNotFoundException;
4665
+ exports.ModelsSyncCommand = ModelsSyncCommand;
4666
+ exports.PRISMA_MODEL_REGEX = PRISMA_MODEL_REGEX;
4667
+ exports.Paginator = Paginator;
4668
+ exports.QueryBuilder = QueryBuilder;
4669
+ exports.SchemaBuilder = SchemaBuilder;
4670
+ exports.SeedCommand = SeedCommand;
4671
+ exports.Seeder = Seeder;
4672
+ exports.TableBuilder = TableBuilder;
4673
+ exports.URLDriver = URLDriver;
4674
+ exports.applyAlterTableOperation = applyAlterTableOperation;
4675
+ exports.applyCreateTableOperation = applyCreateTableOperation;
4676
+ exports.applyDropTableOperation = applyDropTableOperation;
4677
+ exports.applyMigrationToPrismaSchema = applyMigrationToPrismaSchema;
4678
+ exports.applyOperationsToPrismaSchema = applyOperationsToPrismaSchema;
4679
+ exports.buildFieldLine = buildFieldLine;
4680
+ exports.buildIndexLine = buildIndexLine;
4681
+ exports.buildMigrationSource = buildMigrationSource;
4682
+ exports.buildModelBlock = buildModelBlock;
4683
+ exports.configureArkormRuntime = configureArkormRuntime;
4684
+ exports.createMigrationTimestamp = createMigrationTimestamp;
4685
+ exports.createPrismaAdapter = createPrismaAdapter;
4686
+ exports.createPrismaDelegateMap = createPrismaDelegateMap;
4687
+ exports.defineConfig = defineConfig;
4688
+ exports.defineFactory = defineFactory;
4689
+ exports.ensureArkormConfigLoading = ensureArkormConfigLoading;
4690
+ exports.escapeRegex = escapeRegex;
4691
+ exports.findModelBlock = findModelBlock;
4692
+ exports.formatDefaultValue = formatDefaultValue;
4693
+ exports.generateMigrationFile = generateMigrationFile;
4694
+ exports.getDefaultStubsPath = getDefaultStubsPath;
4695
+ exports.getMigrationPlan = getMigrationPlan;
4696
+ exports.getRuntimePaginationURLDriverFactory = getRuntimePaginationURLDriverFactory;
4697
+ exports.getRuntimePrismaClient = getRuntimePrismaClient;
4698
+ exports.getUserConfig = getUserConfig;
4699
+ exports.inferDelegateName = inferDelegateName;
4700
+ exports.isDelegateLike = isDelegateLike;
4701
+ exports.loadArkormConfig = loadArkormConfig;
4702
+ exports.pad = pad;
4703
+ exports.resetArkormRuntimeForTests = resetArkormRuntimeForTests;
4704
+ exports.resolveCast = resolveCast;
4705
+ exports.resolveMigrationClassName = resolveMigrationClassName;
4706
+ exports.resolvePrismaType = resolvePrismaType;
4707
+ exports.runMigrationWithPrisma = runMigrationWithPrisma;
4708
+ exports.runPrismaCommand = runPrismaCommand;
4709
+ exports.toMigrationFileSlug = toMigrationFileSlug;
4710
+ exports.toModelName = toModelName;