@stacksjs/actions 0.61.24 → 0.63.0

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 (99) hide show
  1. package/dist/add.js +3 -0
  2. package/dist/add.js.map +10 -0
  3. package/dist/build/component-libs.js +3 -0
  4. package/dist/build/component-libs.js.map +10 -0
  5. package/dist/build/core.js +3 -0
  6. package/dist/build/core.js.map +10 -0
  7. package/dist/build/stacks.js +3 -0
  8. package/dist/build/stacks.js.map +10 -0
  9. package/dist/build.js +6 -4
  10. package/dist/build.js.map +14 -0
  11. package/dist/bump.js +7 -4
  12. package/dist/bump.js.map +10 -0
  13. package/dist/changelog.js +3 -0
  14. package/dist/changelog.js.map +10 -0
  15. package/dist/clean.js +3 -0
  16. package/dist/clean.js.map +10 -0
  17. package/dist/commit.js +3 -0
  18. package/dist/commit.js.map +10 -0
  19. package/dist/copy-types.js +3 -0
  20. package/dist/copy-types.js.map +10 -0
  21. package/dist/database/seed.js +3 -0
  22. package/dist/database/seed.js.map +10 -0
  23. package/dist/deploy/index.js +14 -5
  24. package/dist/deploy/index.js.map +10 -0
  25. package/dist/dev/components.js +3 -0
  26. package/dist/dev/components.js.map +10 -0
  27. package/dist/dev/docs.js +3 -0
  28. package/dist/dev/docs.js.map +10 -0
  29. package/dist/dev/index.js +4 -2
  30. package/dist/dev/index.js.map +11 -0
  31. package/dist/examples.js +3 -0
  32. package/dist/examples.js.map +10 -0
  33. package/dist/fresh.js +3 -0
  34. package/dist/fresh.js.map +10 -0
  35. package/dist/generate/component-meta.js +3 -0
  36. package/dist/generate/component-meta.js.map +11 -0
  37. package/dist/generate/ide-helpers.js +5 -2
  38. package/dist/generate/ide-helpers.js.map +12 -0
  39. package/dist/generate/index.js +6 -4
  40. package/dist/generate/index.js.map +13 -0
  41. package/dist/generate/lib-entries.js +3 -0
  42. package/dist/generate/lib-entries.js.map +12 -0
  43. package/dist/generate/vscode-custom-data.js +5 -2
  44. package/dist/generate/vscode-custom-data.js.map +12 -0
  45. package/dist/helpers/component-meta.js +3 -0
  46. package/dist/helpers/component-meta.js.map +10 -0
  47. package/dist/helpers/lib-entries.js +3 -0
  48. package/dist/helpers/lib-entries.js.map +11 -0
  49. package/dist/helpers/package-json.js +3 -0
  50. package/dist/helpers/package-json.js.map +11 -0
  51. package/dist/helpers/utils.js +4 -2
  52. package/dist/helpers/utils.js.map +10 -0
  53. package/dist/helpers/vscode-custom-data.js +5 -2
  54. package/dist/helpers/vscode-custom-data.js.map +11 -0
  55. package/dist/index.js +21 -5
  56. package/dist/index.js.map +18 -0
  57. package/dist/key-generate.js +3 -0
  58. package/dist/key-generate.js.map +10 -0
  59. package/dist/lint/fix.js +3 -0
  60. package/dist/lint/fix.js.map +10 -0
  61. package/dist/lint/index.js +3 -0
  62. package/dist/lint/index.js.map +10 -0
  63. package/dist/make.js +3 -0
  64. package/dist/make.js.map +10 -0
  65. package/dist/prepublish.js +3 -0
  66. package/dist/prepublish.js.map +10 -0
  67. package/dist/release.js +21 -5
  68. package/dist/release.js.map +19 -0
  69. package/dist/test/coverage.js +3 -0
  70. package/dist/test/coverage.js.map +10 -0
  71. package/dist/test/feature.js +3 -0
  72. package/dist/test/feature.js.map +10 -0
  73. package/dist/test/ui.js +3 -0
  74. package/dist/test/ui.js.map +10 -0
  75. package/dist/test/unit.js +3 -0
  76. package/dist/test/unit.js.map +10 -0
  77. package/dist/test.js +3 -0
  78. package/dist/test.js.map +10 -0
  79. package/dist/typecheck.js +3 -0
  80. package/dist/typecheck.js.map +10 -0
  81. package/dist/types.js +3 -0
  82. package/dist/types.js.map +10 -0
  83. package/dist/upgrade/dependencies.js +3 -0
  84. package/dist/upgrade/dependencies.js.map +9 -0
  85. package/dist/upgrade/framework.js +3 -0
  86. package/dist/upgrade/framework.js.map +9 -0
  87. package/dist/upgrade/index.js +4 -2
  88. package/dist/upgrade/index.js.map +11 -0
  89. package/dist/upgrade.js +4 -1
  90. package/dist/upgrade.js.map +10 -0
  91. package/package.json +3 -3
  92. package/src/action.ts +18 -9
  93. package/src/build/server.ts +1 -1
  94. package/src/bump.ts +5 -5
  95. package/src/cdn/invalidate-cache.ts +1 -1
  96. package/src/deploy/index.ts +14 -6
  97. package/src/files/watch.ts +8 -0
  98. package/src/helpers/utils.ts +4 -5
  99. package/src/orm/generate-model.ts +814 -467
@@ -1,10 +1,11 @@
1
1
  import { log } from '@stacksjs/logging'
2
+ import { getModelName, getTableName } from '@stacksjs/orm'
2
3
  import { path } from '@stacksjs/path'
3
4
  import { fs, glob } from '@stacksjs/storage'
4
- import { camelCase, pascalCase } from '@stacksjs/strings'
5
- import type { Model, RelationConfig } from '@stacksjs/types'
6
- import { isString, isBoolean } from '@stacksjs/validation'
7
- import { getModelName, getTableName} from '@stacksjs/orm'
5
+ import { camelCase, pascalCase, plural, singular, snakeCase } from '@stacksjs/strings'
6
+ import type { Attributes, Model, RelationConfig } from '@stacksjs/types'
7
+ import { isString } from '@stacksjs/validation'
8
+
8
9
  export interface FieldArrayElement {
9
10
  entity: string
10
11
  charValue?: string | null
@@ -22,7 +23,7 @@ await initiateModelGeneration()
22
23
  await setKyselyTypes()
23
24
 
24
25
  async function generateApiRoutes(modelFiles: string[]) {
25
- const file = Bun.file(path.projectStoragePath(`framework/orm/routes.ts`))
26
+ const file = Bun.file(path.frameworkPath(`orm/routes.ts`))
26
27
  const writer = file.writer()
27
28
  let routeString = `import { route } from '@stacksjs/router'\n\n\n`
28
29
 
@@ -36,12 +37,14 @@ async function generateApiRoutes(modelFiles: string[]) {
36
37
  if (model.traits?.useApi) {
37
38
  const apiRoutes = model.traits?.useApi?.routes
38
39
  const middlewares = model.traits.useApi?.middleware
40
+ const uri = model.traits.useApi?.uri || tableName
41
+
39
42
  if (middlewares) {
40
43
  middlewareString = `.middleware([`
41
44
  if (middlewares.length) {
42
45
  for (let i = 0; i < middlewares.length; i++) {
43
46
  middlewareString += `'${middlewares[i]}'`
44
-
47
+
45
48
  if (i < middlewares.length - 1) {
46
49
  middlewareString += ','
47
50
  }
@@ -51,24 +54,37 @@ async function generateApiRoutes(modelFiles: string[]) {
51
54
  middlewareString += `])`
52
55
  }
53
56
 
54
- if (apiRoutes?.length) {
55
- for (const apiRoute of apiRoutes) {
56
- await writeOrmActions(apiRoute as string, modelName)
57
+ if (Object.keys(model.traits.useApi.routes).length > 0) {
58
+ if (apiRoutes) {
59
+ for (const apiRoute in apiRoutes) {
60
+ // if (Object.prototype.hasOwnProperty.call(apiRoutes, route)) {
61
+ // console.log(`Route: ${route}, Path: ${apiRoutes[route]}`);
62
+ // }
63
+
64
+ let path: string | null = ''
57
65
 
58
- if (apiRoute === 'index')
59
- routeString += `await route.get('${tableName}', 'Actions/${modelName}IndexOrmAction')${middlewareString}\n\n`
66
+ await writeOrmActions(apiRoute as string, modelName)
60
67
 
61
- if (apiRoute === 'store')
62
- routeString += `await route.post('${tableName}', 'Actions/${modelName}StoreOrmAction')${middlewareString}\n\n`
68
+ path = `${apiRoutes[apiRoute]}.ts`
63
69
 
64
- if (apiRoute === 'update')
65
- routeString += `await route.patch('${tableName}/{id}', 'Actions/${modelName}UpdateOrmAction')${middlewareString}\n\n`
70
+ if (!path.includes('/')) {
71
+ path = await lookupFile(path)
72
+ }
73
+
74
+ if (!path) {
75
+ throw { message: 'Action Not Found!' }
76
+ }
66
77
 
67
- if (apiRoute === 'show')
68
- routeString += `await route.get('${tableName}/{id}', 'Actions/${modelName}ShowOrmAction')${middlewareString}\n\n`
78
+ if (apiRoute === 'index') routeString += `await route.get('${uri}', '${path}')\n\n`
69
79
 
70
- if (apiRoute === 'destroy')
71
- routeString += `await route.delete('${tableName}/{id}', 'Actions/${modelName}DestroyOrmAction')${middlewareString}\n\n`
80
+ if (apiRoute === 'show') routeString += `await route.get('${uri}/{id}', '${path}')\n\n`
81
+
82
+ if (apiRoute === 'store') routeString += `await route.post('${uri}', '${path}')\n\n`
83
+
84
+ if (apiRoute === 'update') routeString += `await route.patch('${uri}/{id}', '${path}')\n\n`
85
+
86
+ if (apiRoute === 'destroy') routeString += `await route.delete('${uri}/{id}', '${path}')\n\n`
87
+ }
72
88
  }
73
89
  }
74
90
  }
@@ -78,18 +94,36 @@ async function generateApiRoutes(modelFiles: string[]) {
78
94
  await writer.end()
79
95
  }
80
96
 
97
+ async function lookupFile(fileName: string): Promise<string | null> {
98
+ const ormDirectory = path.builtUserActionsPath('src')
99
+ const filePath = path.join(ormDirectory, fileName)
100
+ const pathExists = await fs.existsSync(filePath)
101
+
102
+ // Check if the directory exists
103
+ if (pathExists) {
104
+ return filePath
105
+ }
106
+
107
+ const actionDirectory = path.userActionsPath()
108
+ const actionFilePath = path.join(actionDirectory, fileName)
109
+ const fileExists = await fs.existsSync(actionFilePath)
110
+
111
+ if (fileExists) {
112
+ return actionFilePath
113
+ }
114
+
115
+ return null
116
+ }
117
+
81
118
  async function writeModelNames() {
82
119
  const modelFiles = glob.sync(path.userModelsPath('*.ts'))
83
-
84
120
  let fileString = `export type ModelNames = `
85
121
 
86
122
  for (let i = 0; i < modelFiles.length; i++) {
87
123
  const modeFileElement = modelFiles[i] as string
88
-
89
124
  const model = (await import(modeFileElement)).default as Model
90
125
  const modelName = getModelName(model, modeFileElement)
91
-
92
- const typeFile = Bun.file(path.projectStoragePath(`framework/core/types/src/model-names.ts`))
126
+ const typeFile = Bun.file(path.corePath(`types/src/model-names.ts`))
93
127
 
94
128
  fileString += `'${modelName}'`
95
129
 
@@ -103,59 +137,157 @@ async function writeModelNames() {
103
137
  }
104
138
  }
105
139
 
106
- async function writeModelRequests() {
140
+ async function writeModelRequest() {
107
141
  const modelFiles = glob.sync(path.userModelsPath('*.ts'))
142
+ const requestD = Bun.file(path.frameworkPath('types/requests.d.ts'))
143
+
144
+ let importTypes = ``
145
+ let importTypesString = ``
146
+ let typeString = `import { Request } from '../core/router/src/request'\n\n`
147
+
148
+ typeString += `interface ValidationType {
149
+ rule: VineType;
150
+ message: { [key: string]: string };
151
+ }\n\n`
152
+
153
+ typeString += `interface ValidationField {
154
+ [key: string]: string | ValidationType;
155
+ validation: ValidationType;
156
+ }\n\n`
157
+
158
+ typeString += `interface CustomAttributes {
159
+ [key: string]: ValidationField
160
+ }\n\n`
108
161
 
109
162
  for (let i = 0; i < modelFiles.length; i++) {
163
+ let fieldStringType = ``
110
164
  let fieldString = ``
111
165
  let fieldStringInt = ``
112
- let fileString = `import { Request } from '@stacksjs/router'\nimport { validateField } from '@stacksjs/validation'\nimport type { RequestInstance } from '@stacksjs/types'\n\n`
166
+ let fileString = `import { Request } from '@stacksjs/router'\nimport type { VineType } from '@stacksjs/types'\nimport { validateField } from '@stacksjs/validation'\nimport { customValidate } from '@stacksjs/validation'\n\n`
113
167
 
114
168
  const modeFileElement = modelFiles[i] as string
115
-
116
169
  const model = (await import(modeFileElement)).default as Model
117
170
  const modelName = getModelName(model, modeFileElement)
118
-
171
+ const useTimestamps = model?.traits?.useTimestamps ?? model?.traits?.timestampable ?? true
172
+ const useSoftDeletes = model?.traits?.useSoftDeletes ?? model?.traits?.softDeletable ?? false
119
173
  const attributes = await extractFields(model, modeFileElement)
120
-
174
+
175
+ fieldString += ` id?: number\n`
176
+ fieldStringInt += `public id = 1\n`
177
+ fieldStringType += `'id' |`
178
+ let keyCounter = 0
179
+ let keyCounterForeign = 0
180
+
181
+ const otherModelRelations = await fetchOtherModelRelations(model, modelName)
182
+
121
183
  for (const attribute of attributes) {
184
+ const entity = attribute.fieldArray?.entity === 'enum' ? 'string[]' : attribute.fieldArray?.entity
122
185
  let defaultValue: any = `''`
123
- const entity = attribute.fieldArray?.entity === 'enum' ? 'string' : attribute.fieldArray?.entity
124
-
125
- if (attribute.fieldArray?.entity === 'boolean')
126
- defaultValue = false
127
186
 
128
- if (attribute.fieldArray?.entity === 'number')
129
- defaultValue = 0
187
+ if (attribute.fieldArray?.entity === 'boolean') defaultValue = false
188
+ if (attribute.fieldArray?.entity === 'number') defaultValue = 0
130
189
 
131
190
  fieldString += ` ${attribute.field}: ${entity}\n `
191
+ fieldStringType += `'${attribute.field}'`
192
+
193
+ if (keyCounter < attributes.length - 1) fieldStringType += ' |'
132
194
 
133
195
  fieldStringInt += `public ${attribute.field} = ${defaultValue}\n`
134
- }
196
+ keyCounter++
197
+ }
198
+
199
+ for (const otherModel of otherModelRelations) {
200
+ fieldString += ` ${otherModel.foreignKey}: number\n `
201
+
202
+ if (keyCounter >= attributes.length - 1) fieldStringType += ' |'
203
+
204
+ fieldStringType += `'${otherModel.foreignKey}'`
205
+
206
+ // if (keyCounterForeign < otherModelRelations.length - 1)
207
+ // fieldStringType += ' |'
208
+
209
+ fieldStringInt += `public ${otherModel.foreignKey} = 0\n`
210
+ keyCounterForeign++
211
+ }
212
+
213
+ if (useTimestamps) {
214
+ fieldStringInt += `public created_at = ''
215
+ public updated_at = ''
216
+ `
217
+ }
218
+
219
+ if (useSoftDeletes) {
220
+ fieldStringInt += `
221
+ public deleted_at = ''
222
+ `
223
+ }
224
+
225
+ fieldString += `created_at?: string
226
+ updated_at?: string
227
+ deleted_at?: string`
135
228
 
136
229
  const modelLowerCase = camelCase(modelName)
137
230
 
138
- const requestFile = Bun.file(path.projectStoragePath(`framework/requests/${modelName}Request.ts`))
231
+ const requestFile = Bun.file(path.frameworkPath(`requests/${modelName}Request.ts`))
232
+
233
+ importTypes = `${modelName}RequestType`
234
+ importTypesString += `${importTypes}`
235
+
236
+ if (i < modelFiles.length - 1) importTypesString += ` | `
237
+
238
+ fileString += `import type { ${importTypes} } from '../types/requests'\n\n`
239
+
240
+ fileString += `interface ValidationType {
241
+ rule: VineType;
242
+ message: { [key: string]: string };
243
+ }\n\n`
244
+
245
+ fileString += `interface ValidationField {
246
+ [key: string]: string | ValidationType;
247
+ validation: ValidationType;
248
+ }\n\n`
249
+
250
+ fileString += `interface CustomAttributes {
251
+ [key: string]: ValidationField
252
+ }`
139
253
 
140
- fileString += `export interface ${modelName}RequestType extends RequestInstance{
141
- validate(): void
254
+ const types = `export interface ${modelName}RequestType extends Request {
255
+ validate(attributes?: CustomAttributes): void
256
+ get(key: ${fieldStringType}): string | number | undefined;
142
257
  ${fieldString}
143
258
  }\n\n`
144
-
145
- fileString += `export class ${modelName}Request extends Request implements ${modelName}RequestType {
259
+
260
+ typeString += `interface RequestData${modelName} {
261
+ ${fieldString}
262
+ }\n`
263
+
264
+ typeString += types
265
+
266
+ fileString += `export class ${modelName}Request extends Request implements ${modelName}RequestType {
146
267
  ${fieldStringInt}
147
- public validate(): void {
148
- validateField('${modelName}', this.all())
268
+ public async validate(attributes?: CustomAttributes): Promise<void> {
269
+ if (attributes === undefined || attributes === null) {
270
+ await validateField('${modelName}', this.all())
271
+ } else {
272
+ await customValidate(attributes, this.all())
273
+ }
274
+
149
275
  }
150
276
  }
151
-
152
- export const ${modelLowerCase}Request = new ${modelName}Request()
277
+
278
+ export const request = new ${modelName}Request()
153
279
  `
154
280
 
155
281
  const writer = requestFile.writer()
156
282
 
157
283
  writer.write(fileString)
158
284
  }
285
+
286
+ typeString += `export type ModelRequest = ${importTypesString}`
287
+
288
+ const requestWrite = requestD.writer()
289
+
290
+ requestWrite.write(typeString)
159
291
  }
160
292
 
161
293
  async function writeOrmActions(apiRoute: string, modelName: String): Promise<void> {
@@ -166,8 +298,7 @@ async function writeOrmActions(apiRoute: string, modelName: String): Promise<voi
166
298
 
167
299
  let handleString = ``
168
300
 
169
-
170
- actionString += ` import type { ${modelName}RequestType } from '../../requests/${modelName}Request'\n\n`
301
+ actionString += ` import type { ${modelName}RequestType } from '../../types/requests'\n\n`
171
302
 
172
303
  if (apiRoute === 'index') {
173
304
  handleString += `async handle(request: ${modelName}RequestType) {
@@ -181,7 +312,7 @@ async function writeOrmActions(apiRoute: string, modelName: String): Promise<voi
181
312
  handleString += `async handle(request: ${modelName}RequestType) {
182
313
  const id = await request.getParam('id')
183
314
 
184
- return ${modelName}.findOrFail(Number(id))
315
+ return await ${modelName}.findOrFail(Number(id))
185
316
  },`
186
317
 
187
318
  method = 'GET'
@@ -203,6 +334,7 @@ async function writeOrmActions(apiRoute: string, modelName: String): Promise<voi
203
334
 
204
335
  if (apiRoute === 'store') {
205
336
  handleString += `async handle(request: ${modelName}RequestType) {
337
+ await request.validate()
206
338
  const model = await ${modelName}.create(request.all())
207
339
 
208
340
  return model
@@ -213,6 +345,8 @@ async function writeOrmActions(apiRoute: string, modelName: String): Promise<voi
213
345
 
214
346
  if (apiRoute === 'update') {
215
347
  handleString += `async handle(request: ${modelName}RequestType) {
348
+ await request.validate()
349
+
216
350
  const id = request.getParam('id')
217
351
 
218
352
  const model = await ${modelName}.findOrFail(Number(id))
@@ -231,34 +365,37 @@ async function writeOrmActions(apiRoute: string, modelName: String): Promise<voi
231
365
  })
232
366
  `
233
367
 
234
- const file = Bun.file(path.projectStoragePath(`framework/orm/Actions/${modelName}${formattedApiRoute}OrmAction.ts`))
368
+ const file = Bun.file(path.builtUserActionsPath(`src/${modelName}${formattedApiRoute}OrmAction.ts`))
235
369
 
236
370
  const writer = file.writer()
237
371
 
238
372
  writer.write(actionString)
239
373
  }
240
374
 
241
- async function initiateModelGeneration(): Promise<void> {
242
- await deleteExistingModels()
243
- await deleteExistingOrmActions()
375
+ async function initiateModelGeneration(modelStringFile?: string): Promise<void> {
376
+ await deleteExistingModels(modelStringFile)
377
+ await deleteExistingOrmActions(modelStringFile)
244
378
  await deleteExistingModelNameTypes()
245
- await deleteExistingModelRequests()
379
+ await deleteExistingModelRequest(modelStringFile)
380
+ await deleteExistingOrmRoute()
246
381
 
247
382
  await writeModelNames()
248
- await writeModelRequests()
383
+ await writeModelRequest()
249
384
 
250
385
  const modelFiles = glob.sync(path.userModelsPath('*.ts'))
251
386
 
252
387
  await generateApiRoutes(modelFiles)
253
388
 
254
389
  for (const modelFile of modelFiles) {
390
+ if (modelStringFile && modelStringFile !== modelFile) continue
391
+
255
392
  log.debug(`Processing model file: ${modelFile}`)
256
393
 
257
394
  const model = (await import(modelFile)).default as Model
258
395
  const tableName = await getTableName(model, modelFile)
259
396
  const modelName = getModelName(model, modelFile)
260
397
 
261
- const file = Bun.file(path.projectStoragePath(`framework/orm/src/models/${modelName}.ts`))
398
+ const file = Bun.file(path.frameworkPath(`orm/src/models/${modelName}.ts`))
262
399
  const fields = await extractFields(model, modelFile)
263
400
  const classString = await generateModelString(tableName, modelName, model, fields)
264
401
 
@@ -296,7 +433,9 @@ async function getRelations(model: Model, modelName: string): Promise<RelationCo
296
433
  relationName: relationInstance.relationName || '',
297
434
  throughModel: relationInstance.through || '',
298
435
  throughForeignKey: relationInstance.throughForeignKey || '',
299
- pivotTable: relationInstance?.pivotTable || `${formattedModelName}_${modelRelation.table}`,
436
+ pivotTable:
437
+ relationInstance?.pivotTable ||
438
+ getPivotTableName(plural(formattedModelName), plural(modelRelation.table || '')),
300
439
  })
301
440
  }
302
441
  }
@@ -305,47 +444,95 @@ async function getRelations(model: Model, modelName: string): Promise<RelationCo
305
444
  return relationships
306
445
  }
307
446
 
447
+ function getPivotTableName(formattedModelName: string, modelRelationTable: string): string {
448
+ // Create an array of the model names
449
+ const models = [formattedModelName, modelRelationTable]
450
+
451
+ // Sort the array alphabetically
452
+ models.sort()
453
+
454
+ models[0] = singular(models[0] || '')
455
+
456
+ // Join the sorted array with an underscore
457
+ const pivotTableName = models.join('_')
458
+
459
+ return pivotTableName
460
+ }
461
+
308
462
  function hasRelations(obj: any, key: string): boolean {
309
463
  return key in obj
310
464
  }
311
465
 
312
- async function deleteExistingModels() {
313
- const modelPaths = glob.sync(path.projectStoragePath(`framework/orm/src/models/*.ts`))
466
+ async function deleteExistingModels(modelStringFile?: string) {
467
+ const typePath = path.frameworkPath(`orm/src/types.ts`)
468
+
469
+ if (fs.existsSync(typePath)) await Bun.$`rm ${typePath}`
470
+
471
+ if (modelStringFile) {
472
+ const modelPath = path.frameworkPath(`orm/src/models/${modelStringFile}.ts`)
473
+
474
+ await Bun.$`rm ${modelPath}`
475
+
476
+ return
477
+ }
478
+
479
+ const modelPaths = glob.sync(path.frameworkPath(`orm/src/models/*.ts`))
314
480
 
315
481
  for (const modelPath of modelPaths) {
316
482
  if (fs.existsSync(modelPath)) await Bun.$`rm ${modelPath}`
317
483
  }
484
+ }
318
485
 
319
- const typePath = path.projectStoragePath(`framework/core/orm/src/generated/types.ts`)
486
+ async function deleteExistingOrmActions(modelStringFile?: string) {
487
+ const routes = path.frameworkPath(`orm/routes`)
488
+ if (fs.existsSync(routes)) await Bun.$`rm ${routes}`
320
489
 
321
- if (fs.existsSync(typePath)) await Bun.$`rm ${typePath}`
322
- }
490
+ if (modelStringFile) {
491
+ const ormPath = path.builtUserActionsPath(`src/${modelStringFile}.ts`)
323
492
 
324
- async function deleteExistingOrmActions() {
325
- const ormPaths = glob.sync(path.projectStoragePath(`framework/orm/Actions/*.ts`))
326
- const routes = path.projectStoragePath(`framework/orm/routes`)
493
+ if (fs.existsSync(ormPath)) await Bun.$`rm ${ormPath}`
494
+
495
+ return
496
+ }
497
+
498
+ const ormPaths = glob.sync(path.builtUserActionsPath(`**/*.ts`))
327
499
 
328
500
  for (const ormPath of ormPaths) {
329
501
  if (fs.existsSync(ormPath)) await Bun.$`rm ${ormPath}`
330
502
  }
331
-
332
- if (fs.existsSync(routes)) await Bun.$`rm ${routes}`
333
503
  }
334
504
 
335
505
  async function deleteExistingModelNameTypes() {
336
- const typeFile = path.projectStoragePath(`framework/core/types/src/model-names.ts`)
506
+ const typeFile = path.corePath(`types/src/model-names.ts`)
337
507
 
338
508
  if (fs.existsSync(typeFile)) await Bun.$`rm ${typeFile}`
339
509
  }
340
510
 
341
- async function deleteExistingModelRequests() {
342
- const requestFiles = glob.sync(path.projectStoragePath(`framework/requests/*.ts`))
511
+ async function deleteExistingModelRequest(modelStringFile?: string) {
512
+ const requestD = path.frameworkPath('types/requests.d.ts')
513
+ if (fs.existsSync(requestD)) await Bun.$`rm ${requestD}`
514
+
515
+ if (modelStringFile) {
516
+ const requestFile = path.frameworkPath(`requests/${modelStringFile}.ts`)
517
+
518
+ if (fs.existsSync(requestFile)) await Bun.$`rm ${requestFile}`
519
+
520
+ return
521
+ }
522
+
523
+ const requestFiles = glob.sync(path.frameworkPath(`requests/*.ts`))
343
524
 
344
525
  for (const requestFile of requestFiles) {
345
526
  if (fs.existsSync(requestFile)) await Bun.$`rm ${requestFile}`
346
527
  }
347
528
  }
348
529
 
530
+ async function deleteExistingOrmRoute() {
531
+ const ormRoute = path.projectStoragePath('framework/orm/routes.ts')
532
+
533
+ if (fs.existsSync(ormRoute)) await Bun.$`rm ${ormRoute}`
534
+ }
535
+
349
536
  async function setKyselyTypes() {
350
537
  let text = ``
351
538
  const modelFiles = glob.sync(path.userModelsPath('*.ts'))
@@ -369,7 +556,7 @@ async function setKyselyTypes() {
369
556
  const model = (await import(modelFile)).default as Model
370
557
  const modelName = getModelName(model, modelFile)
371
558
  const pivotTables = await getPivotTables(model, modelName)
372
-
559
+
373
560
  for (const pivotTable of pivotTables) {
374
561
  const words = pivotTable.table.split('_')
375
562
 
@@ -402,7 +589,7 @@ async function setKyselyTypes() {
402
589
 
403
590
  text += `}`
404
591
 
405
- const file = Bun.file(path.projectStoragePath(`framework/core/orm/src/generated/types.ts`))
592
+ const file = Bun.file(path.frameworkPath(`orm/src/types.ts`))
406
593
 
407
594
  const writer = file.writer()
408
595
 
@@ -413,7 +600,10 @@ async function setKyselyTypes() {
413
600
 
414
601
  async function extractFields(model: Model, modelFile: string): Promise<ModelElement[]> {
415
602
  // TODO: we can improve this type
416
- const fields: Record<string, any> = model.attributes
603
+ let fields: Record<string, any> | undefined = model.attributes
604
+
605
+ if (!fields) fields = {}
606
+
417
607
  const fieldKeys = Object.keys(fields)
418
608
 
419
609
  const rules: string[] = []
@@ -504,7 +694,7 @@ function getRelationCount(relation: string): string {
504
694
 
505
695
  async function getPivotTables(
506
696
  model: Model,
507
- modelName: string
697
+ modelName: string,
508
698
  ): Promise<{ table: string; firstForeignKey?: string; secondForeignKey?: string }[]> {
509
699
  const pivotTable = []
510
700
 
@@ -515,8 +705,7 @@ async function getPivotTables(
515
705
  const modelRelation = (await import(modelRelationPath)).default as Model
516
706
  const formattedModelName = modelName.toLowerCase()
517
707
 
518
- const firstForeignKey =
519
- belongsToManyRelation.firstForeignKey || `${modelName.toLowerCase()}_${model.primaryKey}`
708
+ const firstForeignKey = belongsToManyRelation.firstForeignKey || `${modelName.toLowerCase()}_${model.primaryKey}`
520
709
  const secondForeignKey =
521
710
  belongsToManyRelation.secondForeignKey || `${modelRelation.name?.toLowerCase()}_${model.primaryKey}`
522
711
 
@@ -540,7 +729,7 @@ export async function fetchOtherModelRelations(model: Model, modelName: string):
540
729
  for (let i = 0; i < modelFiles.length; i++) {
541
730
  const modelFileElement = modelFiles[i] as string
542
731
  const modelFile = await import(modelFileElement)
543
-
732
+
544
733
  if (modelName === modelFile.default.name) continue
545
734
 
546
735
  const otherModelName = getModelName(modelFile, modelFileElement)
@@ -557,13 +746,49 @@ export async function fetchOtherModelRelations(model: Model, modelName: string):
557
746
  return modelRelations
558
747
  }
559
748
 
560
- async function generateModelString(tableName: string, modelName: string, model: Model, attributes: ModelElement[]): Promise<string> {
749
+ function getHiddenAttributes(attributes: Attributes | undefined): string[] {
750
+ if (attributes === undefined) return []
751
+
752
+ return Object.keys(attributes).filter((key) => {
753
+ if (attributes === undefined) return false
754
+
755
+ return attributes[key]?.hidden === true
756
+ })
757
+ }
758
+
759
+ function getFillableAttributes(attributes: Attributes | undefined): string[] {
760
+ if (attributes === undefined) return []
761
+
762
+ return Object.keys(attributes)
763
+ .filter((key) => {
764
+ if (attributes === undefined) return false
765
+
766
+ return attributes[key]?.fillable === true
767
+ })
768
+ .map((attribute) => snakeCase(attribute))
769
+ }
770
+
771
+ async function generateModelString(
772
+ tableName: string,
773
+ modelName: string,
774
+ model: Model,
775
+ attributes: ModelElement[],
776
+ ): Promise<string> {
561
777
  const formattedTableName = pascalCase(tableName) // users -> Users
562
778
  const formattedModelName = modelName.toLowerCase() // User -> user
563
779
 
564
780
  let fieldString = ''
781
+ let constructorFields = ''
782
+ let jsonFields = '{\n'
783
+ let declareFields = ''
784
+ let whereStatements = ''
785
+ let whereFunctionStatements = ''
565
786
  let relationMethods = ``
566
787
  let relationImports = ``
788
+ let twoFactorStatements = ''
789
+ let mittCreateStatement = ``
790
+ let mittUpdateStatement = ``
791
+ let mittDeleteStatement = ``
567
792
 
568
793
  const relations = await getRelations(model, modelName)
569
794
 
@@ -571,13 +796,37 @@ async function generateModelString(tableName: string, modelName: string, model:
571
796
  relationImports += `import ${relationInstance.model} from './${relationInstance.model}'\n\n`
572
797
  }
573
798
 
799
+ const useTimestamps = model?.traits?.useTimestamps ?? model?.traits?.timestampable ?? true
800
+ const useSoftDeletes = model?.traits?.useSoftDeletes ?? model?.traits?.softDeletable ?? false
801
+ const observer = model?.traits?.observe
802
+
803
+ if (typeof observer === 'boolean') {
804
+ if (observer) {
805
+ mittCreateStatement += `dispatch('${formattedModelName}.created', model)`
806
+ mittUpdateStatement += `dispatch('${formattedModelName}.updated', model)`
807
+ mittDeleteStatement += `dispatch('${formattedModelName}.deleted', this)`
808
+ }
809
+ }
810
+
811
+ if (Array.isArray(observer)) {
812
+ // Iterate through the array and append statements based on its contents
813
+ if (observer.includes('create')) {
814
+ mittCreateStatement += `dispatch('${formattedModelName}.created', model);`
815
+ }
816
+ if (observer.includes('update')) {
817
+ mittUpdateStatement += `dispatch('${formattedModelName}.updated', model);`
818
+ }
819
+ if (observer.includes('delete')) {
820
+ mittDeleteStatement += `dispatch('${formattedModelName}.deleted', model);`
821
+ }
822
+ }
823
+
574
824
  for (const relation of relations) {
575
825
  const modelRelation = relation.model
576
826
  const foreignKeyRelation = relation.foreignKey
577
827
  const tableRelation = relation.table || ''
578
828
  const pivotTableRelation = relation.pivotTable
579
829
  const formattedModelRelation = modelRelation.toLowerCase()
580
- const capitalizeTableRelation = tableRelation.charAt(0).toUpperCase() + tableRelation.slice(1)
581
830
 
582
831
  const relationType = getRelationType(relation.relationship)
583
832
  const relationCount = getRelationCount(relation.relationship)
@@ -586,27 +835,28 @@ async function generateModelString(tableName: string, modelName: string, model:
586
835
  const relationName = relation.relationName || formattedModelName + modelRelation
587
836
  const throughRelation = relation.throughModel
588
837
 
589
- const formattedThroughRelation = relation.throughModel.toLowerCase()
838
+ if (relation.throughModel === undefined) continue
839
+
840
+ const formattedThroughRelation = relation?.throughModel?.toLowerCase()
590
841
  const throughTableRelation = throughRelation
591
842
  const foreignKeyThroughRelation = relation.throughForeignKey || `${formattedThroughRelation}_id`
592
843
 
593
844
  relationMethods += `
594
845
  async ${relationName}() {
595
- if (this.${formattedModelName}.id === undefined)
846
+ if (this.id === undefined)
596
847
  throw new Error('Relation Error!')
597
848
 
598
849
  const firstModel = await db.selectFrom('${throughTableRelation}')
599
- .where('${foreignKeyRelation}', '=', this.${formattedModelName}.id)
600
- .selectAll()
601
- .executeTakeFirst()
850
+ .where('${foreignKeyRelation}', '=', this.id)
851
+ .selectAll()
852
+ .executeTakeFirst()
602
853
 
603
854
  if (! firstModel)
604
855
  throw new Error('Model Relation Not Found!')
605
856
 
606
- const finalModel = await db.selectFrom('${tableRelation}')
607
- .where('${foreignKeyThroughRelation}', '=', firstModel.id)
608
- .selectAll()
609
- .executeTakeFirst()
857
+ const finalModel = ${modelRelation}
858
+ .where('${foreignKeyThroughRelation}', '=', firstModel.id)
859
+ .first()
610
860
 
611
861
  return new ${modelRelation}.modelInstance(finalModel)
612
862
  }\n\n`
@@ -617,11 +867,11 @@ async function generateModelString(tableName: string, modelName: string, model:
617
867
 
618
868
  relationMethods += `
619
869
  async ${relationName}() {
620
- if (this.${formattedModelName}.id === undefined)
870
+ if (this.id === undefined)
621
871
  throw new Error('Relation Error!')
622
872
 
623
873
  const results = await db.selectFrom('${tableRelation}')
624
- .where('${foreignKeyRelation}', '=', this.${formattedModelName}.id)
874
+ .where('${foreignKeyRelation}', '=', this.id)
625
875
  .selectAll()
626
876
  .execute()
627
877
 
@@ -633,18 +883,16 @@ async function generateModelString(tableName: string, modelName: string, model:
633
883
  const relationName = relation.relationName || formattedModelRelation
634
884
  relationMethods += `
635
885
  async ${relationName}() {
636
- if (this.${formattedModelName}.id === undefined)
886
+ if (this.id === undefined)
637
887
  throw new Error('Relation Error!')
638
888
 
639
- const model = await db.selectFrom('${tableRelation}')
640
- .where('${foreignKeyRelation}', '=', this.${formattedModelName}.id)
641
- .selectAll()
642
- .executeTakeFirst()
889
+ const model = ${modelRelation}
890
+ .where('${foreignKeyRelation}', '=', this.id).first()
643
891
 
644
892
  if (! model)
645
893
  throw new Error('Model Relation Not Found!')
646
894
 
647
- return new ${modelRelation}.modelInstance(model)
895
+ return model
648
896
  }\n\n`
649
897
  }
650
898
 
@@ -656,47 +904,168 @@ async function generateModelString(tableName: string, modelName: string, model:
656
904
  if (this.${foreignKeyRelation} === undefined)
657
905
  throw new Error('Relation Error!')
658
906
 
659
- const model = await db.selectFrom('${tableRelation}')
660
- .where('id', '=', ${foreignKeyRelation})
661
- .selectAll()
662
- .executeTakeFirst()
907
+ const model = await ${modelRelation}
908
+ .where('id', '=', ${foreignKeyRelation})
909
+ .first()
663
910
 
664
911
  if (! model)
665
912
  throw new Error('Model Relation Not Found!')
666
913
 
667
- return new ${modelRelation}.modelInstance(model)
914
+ return model
668
915
  }\n\n`
669
916
  }
670
917
 
671
918
  if (relationType === 'belongsType' && relationCount === 'many') {
672
919
  const pivotTable = pivotTableRelation || tableRelation
673
- const relationName = relation.relationName || formattedModelName + capitalizeTableRelation
920
+ const relationName = relation.relationName || formattedModelName + plural(pascalCase(modelRelation))
674
921
 
675
922
  relationMethods += `
676
923
  async ${relationName}() {
677
- if (this.${formattedModelName}.id === undefined)
924
+ if (this.id === undefined)
678
925
  throw new Error('Relation Error!')
679
926
 
680
927
  const results = await db.selectFrom('${pivotTable}')
681
- .where('${foreignKeyRelation}', '=', this.${formattedModelName}.id)
928
+ .where('${foreignKeyRelation}', '=', this.id)
682
929
  .selectAll()
683
930
  .execute()
684
931
 
685
- return results
932
+ const tableRelationIds = results.map(result => result.${singular(tableRelation)}_id)
933
+
934
+ if (! tableRelationIds.length)
935
+ throw new Error('Relation Error!')
936
+
937
+ const relationResults = await ${modelRelation}.whereIn('id', tableRelationIds).get()
938
+
939
+ return relationResults
686
940
  }\n\n`
687
941
  }
688
942
  }
689
943
 
690
- for (const attribute of attributes) fieldString += ` ${attribute.field}: ${attribute.fieldArray?.entity}\n `
944
+ declareFields += `public id: number | undefined \n `
945
+
946
+ constructorFields += `this.id = ${formattedModelName}?.id\n `
947
+
948
+ const useTwoFactor = model.traits?.useAuth?.useTwoFactor
949
+
950
+ if (useTwoFactor) {
951
+ declareFields += `public two_factor_secret: string | undefined \n`
952
+ constructorFields += `this.two_factor_secret = ${formattedModelName}?.two_factor_secret\n `
953
+
954
+ twoFactorStatements += `
955
+ async generateTwoFactorForModel() {
956
+ const secret = generateTwoFactorSecret()
957
+
958
+ await this.update({ 'two_factor_secret': secret })
959
+ }
960
+
961
+ verifyTwoFactorCode(code: string): boolean {
962
+ if (! this.${formattedModelName}) return false
963
+
964
+ const modelTwoFactorSecret = this.${formattedModelName}.two_factor_secret
965
+ const isValid = verifyTwoFactorCode(code, modelTwoFactorSecret)
966
+
967
+ return isValid
968
+ }
969
+ `
970
+ }
971
+
972
+ jsonFields += `\nid: this.id,\n`
973
+ for (const attribute of attributes) {
974
+ const entity = attribute.fieldArray?.entity === 'enum' ? 'string[]' : attribute.fieldArray?.entity
975
+
976
+ fieldString += ` ${attribute.field}: ${entity}\n `
977
+
978
+ declareFields += `public ${snakeCase(attribute.field)}: ${entity} | undefined \n `
979
+
980
+ constructorFields += `this.${snakeCase(attribute.field)} = ${formattedModelName}?.${snakeCase(attribute.field)}\n `
981
+ jsonFields += `${snakeCase(attribute.field)}: this.${snakeCase(attribute.field)},\n `
982
+
983
+ whereStatements += `static where${pascalCase(attribute.field)}(value: string | number | boolean | undefined | null): ${modelName}Model {
984
+ const instance = new this(null)
985
+
986
+ instance.query = instance.query.where('${attribute.field}', '=', value)
987
+
988
+ return instance
989
+ } \n\n`
990
+
991
+ whereFunctionStatements += `export async function where${pascalCase(attribute.field)}(value: string | number | boolean | undefined | null): Promise<${modelName}Model[]> {
992
+ const query = db.selectFrom('${tableName}').where('${attribute.field}', '=', value)
993
+
994
+ const results = await query.execute()
995
+
996
+ return results.map(modelItem => new ${modelName}Model(modelItem))
997
+ } \n\n`
998
+ }
999
+
1000
+ if (useTimestamps) {
1001
+ declareFields += `
1002
+ public created_at: Date | undefined
1003
+ public updated_at: Date | undefined
1004
+ `
1005
+
1006
+ constructorFields += `
1007
+ this.created_at = user?.created_at\n
1008
+ this.updated_at = user?.updated_at\n
1009
+ `
1010
+
1011
+ jsonFields += `
1012
+ created_at: this.created_at,\n
1013
+ updated_at: this.updated_at,\n
1014
+ `
1015
+ }
1016
+
1017
+ if (useSoftDeletes) {
1018
+ declareFields += `
1019
+ public deleted_at: Date | undefined
1020
+ `
1021
+
1022
+ constructorFields += `
1023
+ this.deleted_at = user?.deleted_at\n
1024
+ `
1025
+
1026
+ jsonFields += `
1027
+ deleted_at: this.deleted_at,\n
1028
+ `
1029
+ }
1030
+
1031
+ jsonFields += '}'
691
1032
 
692
1033
  const otherModelRelations = await fetchOtherModelRelations(model, modelName)
693
1034
 
694
- for (const otherModelRelation of otherModelRelations) fieldString += ` ${otherModelRelation.foreignKey}: number \n`
1035
+ for (const otherModelRelation of otherModelRelations) {
1036
+ fieldString += ` ${otherModelRelation.foreignKey}: number \n`
1037
+
1038
+ declareFields += `public ${otherModelRelation.foreignKey}: number | undefined \n `
1039
+
1040
+ constructorFields += `this.${otherModelRelation.foreignKey} = ${formattedModelName}?.${otherModelRelation.foreignKey}\n `
1041
+ }
1042
+
1043
+ if (useTwoFactor) {
1044
+ fieldString += `two_factor_secret: string \n`
1045
+ }
1046
+
1047
+ if (useTimestamps) {
1048
+ fieldString += `
1049
+ created_at: ColumnType<Date, string | undefined, never>\n
1050
+ updated_at: ColumnType<Date, string | undefined, never>
1051
+ `
1052
+ }
1053
+
1054
+ if (useSoftDeletes) {
1055
+ fieldString += `
1056
+ deleted_at: ColumnType<Date, string | undefined, never>\n
1057
+ `
1058
+ }
1059
+
1060
+ const hidden = JSON.stringify(getHiddenAttributes(model.attributes))
1061
+ const fillable = JSON.stringify(getFillableAttributes(model.attributes))
695
1062
 
696
1063
  return `import type { ColumnType, Generated, Insertable, Selectable, Updateable } from 'kysely'
697
- import type { Result } from '@stacksjs/error-handling'
698
- import { err, handleError, ok } from '@stacksjs/error-handling'
699
1064
  import { db } from '@stacksjs/database'
1065
+ import { sql } from '@stacksjs/database'
1066
+ import { dispatch } from '@stacksjs/events'
1067
+ import { generateTwoFactorSecret } from '@stacksjs/auth'
1068
+ import { verifyTwoFactorCode } from '@stacksjs/auth'
700
1069
  ${relationImports}
701
1070
  // import { Kysely, MysqlDialect, PostgresDialect } from 'kysely'
702
1071
  // import { Pool } from 'pg'
@@ -705,9 +1074,6 @@ async function generateModelString(tableName: string, modelName: string, model:
705
1074
  export interface ${formattedTableName}Table {
706
1075
  id: Generated<number>
707
1076
  ${fieldString}
708
- created_at: ColumnType<Date, string | undefined, never>
709
- updated_at: ColumnType<Date, string | undefined, never>
710
- deleted_at: ColumnType<Date, string | undefined, never>
711
1077
  }
712
1078
 
713
1079
  interface ${modelName}Response {
@@ -739,16 +1105,21 @@ async function generateModelString(tableName: string, modelName: string, model:
739
1105
  }
740
1106
 
741
1107
  export class ${modelName}Model {
742
- private ${formattedModelName}: Partial<${modelName}Type>
743
- private results: Partial<${modelName}Type>[]
744
- private hidden = ['password'] // TODO: this hidden functionality needs to be implemented still
745
-
746
- constructor(${formattedModelName}: Partial<${modelName}Type>) {
747
- this.${formattedModelName} = ${formattedModelName}
1108
+ private hidden = ${hidden}
1109
+ private fillable = ${fillable}
1110
+ private softDeletes = ${useSoftDeletes}
1111
+ protected query: any
1112
+ protected hasSelect: boolean
1113
+ ${declareFields}
1114
+ constructor(${formattedModelName}: Partial<${modelName}Type> | null) {
1115
+ ${constructorFields}
1116
+
1117
+ this.query = db.selectFrom('${tableName}')
1118
+ this.hasSelect = false
748
1119
  }
749
1120
 
750
- // Method to find a ${formattedModelName} by ID
751
- static async find(id: number, fields?: (keyof ${modelName}Type)[]): Promise<${modelName}Model> {
1121
+ // Method to find a ${modelName} by ID
1122
+ async find(id: number, fields?: (keyof ${modelName}Type)[]): Promise<${modelName}Model | undefined> {
752
1123
  let query = db.selectFrom('${tableName}').where('id', '=', id)
753
1124
 
754
1125
  if (fields)
@@ -759,14 +1130,17 @@ async function generateModelString(tableName: string, modelName: string, model:
759
1130
  const model = await query.executeTakeFirst()
760
1131
 
761
1132
  if (!model)
762
- return null
1133
+ return undefined
763
1134
 
764
- return new ${modelName}Model(model)
1135
+ return this.parseResult(new ${modelName}Model(model))
765
1136
  }
766
1137
 
767
- static async findOrFail(id: number, fields?: (keyof ${modelName}Type)[]): Promise<${modelName}Model> {
1138
+ // Method to find a ${modelName} by ID
1139
+ static async find(id: number, fields?: (keyof ${modelName}Type)[]): Promise<${modelName}Model | undefined> {
768
1140
  let query = db.selectFrom('${tableName}').where('id', '=', id)
769
1141
 
1142
+ const instance = new this(null)
1143
+
770
1144
  if (fields)
771
1145
  query = query.select(fields)
772
1146
  else
@@ -774,29 +1148,69 @@ async function generateModelString(tableName: string, modelName: string, model:
774
1148
 
775
1149
  const model = await query.executeTakeFirst()
776
1150
 
1151
+ if (!model)
1152
+ return undefined
1153
+
1154
+ return instance.parseResult(new this(model))
1155
+ }
1156
+
1157
+ static async all(): Promise<${modelName}Model[]> {
1158
+ let query = db.selectFrom('${tableName}').selectAll()
1159
+
1160
+ const instance = new this(null)
1161
+
1162
+ if (instance.softDeletes) {
1163
+ query = query.where('deleted_at', 'is', null)
1164
+ }
1165
+
1166
+ const results = await query.execute();
1167
+
1168
+ return results.map(modelItem => instance.parseResult(new ${modelName}Model(modelItem)));
1169
+ }
1170
+
1171
+
1172
+ static async findOrFail(id: number): Promise<${modelName}Model> {
1173
+ let query = db.selectFrom('${tableName}').where('id', '=', id)
1174
+
1175
+ const instance = new this(null)
1176
+
1177
+ if (instance.softDeletes) {
1178
+ query = query.where('deleted_at', 'is', null);
1179
+ }
1180
+
1181
+ query = query.selectAll()
1182
+
1183
+ const model = await query.executeTakeFirst()
1184
+
777
1185
  if (!model)
778
1186
  throw(\`No model results found for \${id}\ \`)
779
1187
 
780
- return new ${modelName}Model(model)
1188
+
1189
+ return instance.parseResult(new this(model))
781
1190
  }
782
1191
 
783
- static async findMany(ids: number[], fields?: (keyof ${modelName}Type)[]): Promise<${modelName}Model[]> {
1192
+ static async findMany(ids: number[]): Promise<${modelName}Model[]> {
784
1193
  let query = db.selectFrom('${tableName}').where('id', 'in', ids)
785
1194
 
786
- if (fields)
787
- query = query.select(fields)
788
- else
789
- query = query.selectAll()
1195
+ const instance = new this(null)
1196
+
1197
+ if (instance.softDeletes) {
1198
+ query = query.where('deleted_at', 'is', null);
1199
+ }
1200
+
1201
+ query = query.selectAll()
790
1202
 
791
1203
  const model = await query.execute()
792
1204
 
793
- return model.map(modelItem => new ${modelName}Model(modelItem))
1205
+ return model.map(modelItem => instance.parseResult(new ${modelName}Model(modelItem)))
794
1206
  }
795
1207
 
796
- // Method to get a ${formattedModelName} by criteria
797
- static async get(criteria: Partial<${modelName}Type>, options: QueryOptions = {}): Promise<${modelName}Model[]> {
1208
+ // Method to get a ${modelName} by criteria
1209
+ static async fetch(criteria: Partial<${modelName}Type>, options: QueryOptions = {}): Promise<${modelName}Model[]> {
798
1210
  let query = db.selectFrom('${tableName}')
799
1211
 
1212
+ const instance = new this(null)
1213
+
800
1214
  // Apply sorting from options
801
1215
  if (options.sort)
802
1216
  query = query.orderBy(options.sort.column, options.sort.order)
@@ -808,12 +1222,83 @@ async function generateModelString(tableName: string, modelName: string, model:
808
1222
  if (options.offset !== undefined)
809
1223
  query = query.offset(options.offset)
810
1224
 
1225
+ if (instance.softDeletes) {
1226
+ query = query.where('deleted_at', 'is', null);
1227
+ }
1228
+
811
1229
  const model = await query.selectAll().execute()
812
1230
  return model.map(modelItem => new ${modelName}Model(modelItem))
813
1231
  }
814
1232
 
1233
+ // Method to get a ${modelName} by criteria
1234
+ static async get(): Promise<${modelName}Model[]> {
1235
+ let query = db.selectFrom('${tableName}');
1236
+
1237
+ const instance = new this(null)
1238
+
1239
+ // Check if soft deletes are enabled
1240
+ if (instance.softDeletes) {
1241
+ query = query.where('deleted_at', 'is', null);
1242
+ }
1243
+
1244
+ const model = await query.selectAll().execute();
1245
+
1246
+ return model.map(modelItem => new ${modelName}Model(modelItem));
1247
+ }
1248
+
1249
+ // Method to get a ${modelName} by criteria
1250
+ async get(): Promise<${modelName}Model[]> {
1251
+ if (this.hasSelect) {
1252
+
1253
+ if (this.softDeletes) {
1254
+ this.query = this.query.where('deleted_at', 'is', null);
1255
+ }
1256
+
1257
+ const model = await this.query.execute()
1258
+
1259
+ return model.map((modelItem: ${modelName}Model) => new ${modelName}Model(modelItem))
1260
+ }
1261
+
1262
+ if (this.softDeletes) {
1263
+ this.query = this.query.where('deleted_at', 'is', null);
1264
+ }
1265
+
1266
+ const model = await this.query.selectAll().execute()
1267
+
1268
+ return model.map((modelItem: ${modelName}Model) => new ${modelName}Model(modelItem))
1269
+ }
1270
+
1271
+ static async count(): Promise<number> {
1272
+ const instance = new this(null)
1273
+
1274
+ if (instance.softDeletes) {
1275
+ instance.query = instance.query.where('deleted_at', 'is', null);
1276
+ }
1277
+
1278
+ const results = await instance.query.selectAll().execute()
1279
+
1280
+ return results.length
1281
+ }
1282
+
1283
+ async count(): Promise<number> {
1284
+ if (this.hasSelect) {
1285
+
1286
+ if (this.softDeletes) {
1287
+ this.query = this.query.where('deleted_at', 'is', null);
1288
+ }
1289
+
1290
+ const results = await this.query.execute()
1291
+
1292
+ return results.length
1293
+ }
1294
+
1295
+ const results = await this.query.selectAll().execute()
1296
+
1297
+ return results.length
1298
+ }
1299
+
815
1300
  // Method to get all ${tableName}
816
- static async all(options: QueryOptions = { limit: 10, offset: 0, page: 1 }): Promise<${modelName}Response> {
1301
+ static async paginate(options: QueryOptions = { limit: 10, offset: 0, page: 1 }): Promise<${modelName}Response> {
817
1302
  const totalRecordsResult = await db.selectFrom('${tableName}')
818
1303
  .select(db.fn.count('id').as('total')) // Use 'id' or another actual column name
819
1304
  .executeTakeFirst()
@@ -825,7 +1310,7 @@ async function generateModelString(tableName: string, modelName: string, model:
825
1310
  .selectAll()
826
1311
  .orderBy('id', 'asc') // Assuming 'id' is used for cursor-based pagination
827
1312
  .limit((options.limit ?? 10) + 1) // Fetch one extra record
828
- .offset((options.page - 1) * (options.limit ?? 10))
1313
+ .offset(((options.page ?? 1) - 1) * (options.limit ?? 10)) // Ensure options.page is not undefined
829
1314
  .execute()
830
1315
 
831
1316
  let nextCursor = null
@@ -844,24 +1329,53 @@ async function generateModelString(tableName: string, modelName: string, model:
844
1329
  }
845
1330
 
846
1331
  // Method to create a new ${formattedModelName}
847
- static async create(new${modelName}: New${modelName}): Promise<${modelName}Model> {
1332
+ static async create(new${modelName}: New${modelName}): Promise<${modelName}Model | undefined> {
1333
+ const instance = new this(null)
1334
+ const filteredValues = Object.keys(new${modelName})
1335
+ .filter(key => instance.fillable.includes(key))
1336
+ .reduce((obj: any, key) => {
1337
+ obj[key] = new${modelName}[key];
1338
+ return obj
1339
+ }, {})
1340
+
1341
+ if (Object.keys(filteredValues).length === 0) {
1342
+ return undefined
1343
+ }
1344
+
848
1345
  const result = await db.insertInto('${tableName}')
849
- .values(new${modelName})
1346
+ .values(filteredValues)
850
1347
  .executeTakeFirstOrThrow()
851
1348
 
852
- return await find(Number(result.insertId)) as ${modelName}Model
1349
+ const model = await find(Number(result.insertId)) as ${modelName}Model
1350
+
1351
+ ${mittCreateStatement}
1352
+
1353
+ return model
853
1354
  }
854
1355
 
855
- // Method to remove a ${formattedModelName}
856
- static async remove(id: number): Promise<${modelName}Model> {
857
- const model = await db.deleteFrom('${tableName}')
1356
+ // Method to remove a ${modelName}
1357
+ static async remove(id: number): Promise<void> {
1358
+ const instance = new this(null)
1359
+
1360
+ const model = await instance.find(id)
1361
+
1362
+ if (instance.softDeletes) {
1363
+ await db.updateTable('${tableName}')
1364
+ .set({
1365
+ deleted_at: sql.raw('CURRENT_TIMESTAMP')
1366
+ })
858
1367
  .where('id', '=', id)
859
- .executeTakeFirstOrThrow()
1368
+ .execute();
1369
+ } else {
1370
+ await db.deleteFrom('${tableName}')
1371
+ .where('id', '=', id)
1372
+ .execute();
1373
+ }
860
1374
 
861
- return new ${modelName}Model(model)
1375
+ ${mittDeleteStatement}
862
1376
  }
863
1377
 
864
- async where(...args: (string | number)[]): Promise<${modelName}Type[]> {
1378
+ where(...args: (string | number | boolean | undefined | null)[]): ${modelName}Model {
865
1379
  let column: any
866
1380
  let operator: any
867
1381
  let value: any
@@ -875,135 +1389,135 @@ async function generateModelString(tableName: string, modelName: string, model:
875
1389
  throw new Error("Invalid number of arguments")
876
1390
  }
877
1391
 
878
- let query = db.selectFrom('${tableName}')
879
-
880
- query = query.where(column, operator, value)
1392
+ this.query = this.query.where(column, operator, value)
881
1393
 
882
- return await query.selectAll().execute()
1394
+ return this
883
1395
  }
884
1396
 
885
- async whereIs(criteria: Partial<${modelName}Type>, options: QueryOptions = {}) {
886
- let query = db.selectFrom('${tableName}')
887
-
888
- // Existing criteria checks
889
- if (criteria.id)
890
- query = query.where('id', '=', criteria.id) // Kysely is immutable, we must re-assign
1397
+ static where(...args: (string | number | boolean | undefined | null)[]): ${modelName}Model {
1398
+ let column: any
1399
+ let operator: any
1400
+ let value: any
891
1401
 
892
- if (criteria.email)
893
- query = query.where('email', '=', criteria.email)
1402
+ const instance = new this(null)
894
1403
 
895
- if (criteria.name !== undefined) {
896
- query = query.where(
897
- 'name',
898
- criteria.name === null ? 'is' : '=',
899
- criteria.name,
900
- )
1404
+ if (args.length === 2) {
1405
+ [column, value] = args
1406
+ operator = '='
1407
+ } else if (args.length === 3) {
1408
+ [column, operator, value] = args
1409
+ } else {
1410
+ throw new Error("Invalid number of arguments")
901
1411
  }
902
1412
 
903
- if (criteria.password)
904
- query = query.where('password', '=', criteria.password)
905
-
906
- if (criteria.created_at)
907
- query = query.where('created_at', '=', criteria.created_at)
908
-
909
- if (criteria.updated_at)
910
- query = query.where('updated_at', '=', criteria.updated_at)
1413
+ instance.query = instance.query.where(column, operator, value)
911
1414
 
912
- if (criteria.deleted_at)
913
- query = query.where('deleted_at', '=', criteria.deleted_at)
1415
+ return instance
1416
+ }
914
1417
 
915
- // Apply sorting from options
916
- if (options.sort)
917
- query = query.orderBy(options.sort.column, options.sort.order)
1418
+ ${whereStatements}
918
1419
 
919
- // Apply pagination from options
920
- if (options.limit !== undefined)
921
- query = query.limit(options.limit)
1420
+ static whereIn(column: keyof ${modelName}Type, values: any[]): ${modelName}Model {
1421
+ const instance = new this(null)
922
1422
 
923
- if (options.offset !== undefined)
924
- query = query.offset(options.offset)
1423
+ instance.query = instance.query.where(column, 'in', values)
925
1424
 
926
- return await query.selectAll().execute()
1425
+ return instance
927
1426
  }
928
1427
 
929
- async whereIn(column: keyof ${modelName}Type, values: any[], options: QueryOptions = {}): Promise<${modelName}Type[]> {
930
-
931
- let query = db.selectFrom('${tableName}')
932
-
933
- query = query.where(column, 'in', values)
1428
+ async first(): Promise<${modelName}Model | undefined> {
1429
+ const model = await this.query.selectAll().executeTakeFirst()
934
1430
 
935
- // Apply sorting from options
936
- if (options.sort)
937
- query = query.orderBy(options.sort.column, options.sort.order)
1431
+ if (! model) {
1432
+ return undefined
1433
+ }
938
1434
 
939
- // Apply pagination from options
940
- if (options.limit !== undefined)
941
- query = query.limit(options.limit)
1435
+ return this.parseResult(new ${modelName}Model(model))
1436
+ }
942
1437
 
943
- if (options.offset !== undefined)
944
- query = query.offset(options.offset)
1438
+ async exists(): Promise<boolean> {
1439
+ const model = await this.query.selectAll().executeTakeFirst()
945
1440
 
946
- return await query.selectAll().execute()
1441
+ return model !== null || model !== undefined
947
1442
  }
948
1443
 
949
- async first(): Promise<${modelName}Type> {
1444
+ static async first(): Promise<${modelName}Type | undefined> {
950
1445
  return await db.selectFrom('${tableName}')
951
1446
  .selectAll()
952
1447
  .executeTakeFirst()
953
1448
  }
954
1449
 
955
- async last(): Promise<${modelName}Type> {
1450
+ async last(): Promise<${modelName}Type | undefined> {
956
1451
  return await db.selectFrom('${tableName}')
957
1452
  .selectAll()
958
1453
  .orderBy('id', 'desc')
959
1454
  .executeTakeFirst()
960
1455
  }
961
1456
 
962
- async orderBy(column: keyof ${modelName}Type, order: 'asc' | 'desc'): Promise<${modelName}Type[]> {
963
- return await db.selectFrom('${tableName}')
964
- .selectAll()
965
- .orderBy(column, order)
966
- .execute()
1457
+ static orderBy(column: keyof ${modelName}Type, order: 'asc' | 'desc'): ${modelName}Model {
1458
+ const instance = new this(null)
1459
+
1460
+ instance.query = instance.orderBy(column, order)
1461
+
1462
+ return instance
967
1463
  }
968
1464
 
969
- async orderByDesc(column: keyof ${modelName}Type): Promise<${modelName}Type[]> {
970
- return await db.selectFrom('${tableName}')
971
- .selectAll()
972
- .orderBy(column, 'desc')
973
- .execute()
1465
+ orderBy(column: keyof ${modelName}Type, order: 'asc' | 'desc'): ${modelName}Model {
1466
+ this.query = this.query.orderBy(column, order)
1467
+
1468
+ return this
974
1469
  }
975
1470
 
976
- async orderByAsc(column: keyof ${modelName}Type): Promise<${modelName}Type[]> {
977
- return await db.selectFrom('${tableName}')
978
- .selectAll()
979
- .orderBy(column, 'asc')
980
- .execute()
1471
+ static orderByDesc(column: keyof ${modelName}Type): ${modelName}Model {
1472
+ const instance = new this(null)
1473
+
1474
+ instance.query = instance.query.orderBy(column, 'desc')
1475
+
1476
+ return instance
981
1477
  }
982
1478
 
983
- // Method to get the ${formattedModelName} instance itself
984
- self(): ${modelName}Model {
1479
+ orderByDesc(column: keyof ${modelName}Type): ${modelName}Model {
1480
+ this.query = this.orderBy(column, 'desc')
1481
+
985
1482
  return this
986
1483
  }
987
1484
 
988
- // Method to get the ${formattedModelName} instance data
989
- get() {
990
- return this.${formattedModelName}
1485
+ static orderByAsc(column: keyof ${modelName}Type): ${modelName}Model {
1486
+ const instance = new this(null)
1487
+
1488
+ instance.query = instance.query.orderBy(column, 'desc')
1489
+
1490
+ return instance
1491
+ }
1492
+
1493
+ orderByAsc(column: keyof ${modelName}Type): ${modelName}Model {
1494
+ this.query = this.query.orderBy(column, 'desc')
1495
+
1496
+ return this
991
1497
  }
992
1498
 
993
- // Method to update the ${formattedModelName} instance
994
- async update(${formattedModelName}: ${modelName}Update): Promise<Result<${modelName}Type, Error>> {
995
- if (this.${formattedModelName}.id === undefined)
996
- return err(handleError('${modelName} ID is undefined'))
1499
+ // Method to update the ${tableName} instance
1500
+ async update(${formattedModelName}: ${modelName}Update): Promise<${modelName}Model | undefined> {
1501
+ if (this.id === undefined)
1502
+ throw new Error('${modelName} ID is undefined')
1503
+
1504
+ const filteredValues = Object.keys(${formattedModelName})
1505
+ .filter(key => this.fillable.includes(key))
1506
+ .reduce((obj, key) => {
1507
+ obj[key] = ${formattedModelName}[key]
1508
+ return obj;
1509
+ }, {});
997
1510
 
998
- const updatedModel = await db.updateTable('${tableName}')
999
- .set(${formattedModelName})
1000
- .where('id', '=', this.${formattedModelName}.id)
1511
+ await db.updateTable('${tableName}')
1512
+ .set(filteredValues)
1513
+ .where('id', '=', this.id)
1001
1514
  .executeTakeFirst()
1002
1515
 
1003
- if (!updatedModel)
1004
- return err(handleError('${modelName} not found'))
1516
+ const model = this.find(Number(this.id))
1517
+
1518
+ ${mittUpdateStatement}
1005
1519
 
1006
- return ok(updatedModel)
1520
+ return model
1007
1521
  }
1008
1522
 
1009
1523
  // Method to save (insert or update) the ${formattedModelName} instance
@@ -1023,38 +1537,66 @@ async function generateModelString(tableName: string, modelName: string, model:
1023
1537
  }
1024
1538
  }
1025
1539
 
1026
- // Method to delete the ${formattedModelName} instance
1540
+ // Method to delete (soft delete) the ${formattedModelName} instance
1027
1541
  async delete(): Promise<void> {
1028
- if (this.${formattedModelName}.id === undefined)
1029
- throw new Error('${modelName} ID is undefined')
1542
+ if (this.id === undefined)
1543
+ throw new Error('${modelName} ID is undefined');
1544
+
1545
+ // Check if soft deletes are enabled
1546
+ if (this.softDeletes) {
1547
+ // Update the deleted_at column with the current timestamp
1548
+ await db.updateTable('${tableName}')
1549
+ .set({
1550
+ deleted_at: sql.raw('CURRENT_TIMESTAMP')
1551
+ })
1552
+ .where('id', '=', this.id)
1553
+ .execute();
1554
+ } else {
1555
+ // Perform a hard delete
1556
+ await db.deleteFrom('${tableName}')
1557
+ .where('id', '=', this.id)
1558
+ .execute();
1559
+ }
1030
1560
 
1031
- await db.deleteFrom('${tableName}')
1032
- .where('id', '=', this.${formattedModelName}.id)
1033
- .execute()
1561
+ ${mittDeleteStatement}
1562
+ }
1034
1563
 
1035
- this.${formattedModelName} = {}
1564
+ ${relationMethods}
1565
+
1566
+ distinct(column: keyof ${modelName}Type): ${modelName}Model {
1567
+ this.query = this.query.distinctOn(column)
1568
+
1569
+ return this
1036
1570
  }
1037
1571
 
1038
- // Method to refresh the ${formattedModelName} instance data from the database
1039
- async refresh(): Promise<void> {
1040
- if (this.${formattedModelName}.id === undefined)
1041
- throw new Error('${modelName} ID is undefined')
1572
+ static distinct(column: keyof ${modelName}Type): ${modelName}Model {
1573
+ const instance = new this(null)
1042
1574
 
1043
- const refreshedModel = await db.selectFrom('${tableName}')
1044
- .where('id', '=', this.${formattedModelName}.id)
1045
- .selectAll()
1046
- .executeTakeFirst()
1575
+ instance.query = instance.query.distinctOn(column)
1047
1576
 
1048
- if (!refreshedModel)
1049
- throw new Error('${modelName} not found')
1577
+ return instance
1578
+ }
1050
1579
 
1051
- this.${formattedModelName} = refreshedModel
1580
+ join(table: string, firstCol: string, secondCol: string): ${modelName}Model {
1581
+ this.query = this.query.innerJoin(table, firstCol, secondCol)
1582
+
1583
+ return this
1052
1584
  }
1053
1585
 
1054
- ${relationMethods}
1586
+ static join(table: string, firstCol: string, secondCol: string): ${modelName}Model {
1587
+ const instance = new this(null)
1588
+
1589
+ instance.query = instance.query.innerJoin(table, firstCol, secondCol)
1590
+
1591
+ return instance
1592
+ }
1593
+
1594
+ static async rawQuery(rawQuery: string): Promise<any> {
1595
+ return await sql\`\${rawQuery}\`\.execute(db)
1596
+ }
1055
1597
 
1056
1598
  toJSON() {
1057
- const output: Partial<${modelName}Type> = { ...this.${formattedModelName} }
1599
+ const output: Partial<${modelName}Type> = ${jsonFields}
1058
1600
 
1059
1601
  this.hidden.forEach((attr) => {
1060
1602
  if (attr in output)
@@ -1065,259 +1607,64 @@ async function generateModelString(tableName: string, modelName: string, model:
1065
1607
 
1066
1608
  return output as ${modelName}
1067
1609
  }
1068
- }
1069
-
1070
- const Model = ${modelName}Model
1071
-
1072
- // starting here, ORM functions
1073
- export async function find(id: number, fields?: (keyof ${modelName}Type)[]) {
1074
- let query = db.selectFrom('${tableName}').where('id', '=', id)
1075
1610
 
1076
- if (fields)
1077
- query = query.select(fields)
1078
- else
1079
- query = query.selectAll()
1611
+ parseResult(model: ${modelName}Model): ${modelName}Model {
1612
+ delete model['query']
1613
+ delete model['fillable']
1614
+ delete model['two_factor_secret']
1615
+ delete model['hasSelect']
1616
+ delete model['softDeletes']
1080
1617
 
1081
- const model = await query.executeTakeFirst()
1618
+ for (const hiddenAttribute of this.hidden) {
1619
+ delete model[hiddenAttribute]
1620
+ }
1082
1621
 
1083
- if (!model)
1084
- return null
1622
+ return model
1623
+ }
1085
1624
 
1086
- return new ${modelName}Model(model)
1625
+ ${twoFactorStatements}
1087
1626
  }
1088
1627
 
1089
- export async function findOrFail(id: number, fields?: (keyof ${modelName}Type)[]) {
1628
+ async function find(id: number): Promise<${modelName}Model | null> {
1090
1629
  let query = db.selectFrom('${tableName}').where('id', '=', id)
1091
1630
 
1092
- if (fields)
1093
- query = query.select(fields)
1094
- else
1095
- query = query.selectAll()
1631
+ if (fields) query = query.select(fields)
1632
+ else query = query.selectAll()
1096
1633
 
1097
1634
  const model = await query.executeTakeFirst()
1098
1635
 
1099
- if (!model)
1100
- throw(\`No model results found for \${id}\ \`)
1636
+ if (!model) return null
1101
1637
 
1102
1638
  return new ${modelName}Model(model)
1103
1639
  }
1104
1640
 
1105
- export async function findMany(ids: number[], fields?: (keyof ${modelName}Type)[]) {
1106
- let query = db.selectFrom('${tableName}').where('id', 'in', ids)
1107
-
1108
- if (fields)
1109
- query = query.select(fields)
1110
- else
1111
- query = query.selectAll()
1112
-
1113
- const model = await query.execute()
1641
+ export async function count(): Promise<number> {
1642
+ const results = await ${modelName}Model.count()
1114
1643
 
1115
- return model.map(modelItem => new ${modelName}Model(modelItem))
1116
- }
1117
-
1118
- export async function count(): Number {
1119
- const results = await db.selectFrom('${tableName}')
1120
- .selectAll()
1121
- .execute()
1122
-
1123
- return results.length
1124
- }
1125
-
1126
- export async function get(criteria: Partial<${modelName}Type>, sort: { column: keyof ${modelName}Type, order: 'asc' | 'desc' } = { column: 'created_at', order: 'desc' }) {
1127
- let query = db.selectFrom('${tableName}')
1128
-
1129
- if (criteria.id)
1130
- query = query.where('id', '=', criteria.id) // Kysely is immutable, we must re-assign
1131
-
1132
- if (criteria.email)
1133
- query = query.where('email', '=', criteria.email)
1134
-
1135
- if (criteria.name !== undefined) {
1136
- query = query.where(
1137
- 'name',
1138
- criteria.name === null ? 'is' : '=',
1139
- criteria.name,
1140
- )
1141
- }
1142
-
1143
- if (criteria.password)
1144
- query = query.where('password', '=', criteria.password)
1145
-
1146
- if (criteria.created_at)
1147
- query = query.where('created_at', '=', criteria.created_at)
1148
-
1149
- if (criteria.updated_at)
1150
- query = query.where('updated_at', '=', criteria.updated_at)
1151
-
1152
- if (criteria.deleted_at)
1153
- query = query.where('deleted_at', '=', criteria.deleted_at)
1154
-
1155
- // Apply sorting based on the 'sort' parameter
1156
- query = query.orderBy(sort.column, sort.order)
1157
-
1158
- return await query.selectAll().execute()
1159
- }
1160
-
1161
- export async function all(limit: number = 10, offset: number = 0): Promise<${modelName}Type[]> {
1162
- return await db.selectFrom('${tableName}')
1163
- .selectAll()
1164
- .orderBy('created_at', 'desc')
1165
- .limit(limit)
1166
- .offset(offset)
1167
- .execute()
1644
+ return results
1168
1645
  }
1169
1646
 
1170
1647
  export async function create(new${modelName}: New${modelName}): Promise<${modelName}Model> {
1171
1648
  const result = await db.insertInto('${tableName}')
1172
- .values(new${modelName})
1173
- .executeTakeFirstOrThrow()
1649
+ .values(new${modelName})
1650
+ .executeTakeFirstOrThrow()
1174
1651
 
1175
- return await find(Number(result.insertId))
1652
+ return await find(Number(result.insertId)) as ${modelName}Model
1176
1653
  }
1177
1654
 
1178
- export async function first(): Promise<${modelName}Model> {
1179
- return await db.selectFrom('${tableName}')
1180
- .selectAll()
1181
- .executeTakeFirst()
1655
+ export async function rawQuery(rawQuery: string): Promise<any> {
1656
+ return await sql\`\${rawQuery}\`\.execute(db)
1182
1657
  }
1183
1658
 
1184
- export async function recent(limit: number): Promise<${modelName}Model[]> {
1185
- return await db.selectFrom('${tableName}')
1186
- .selectAll()
1187
- .limit(limit)
1188
- .execute()
1189
- }
1190
-
1191
- export async function last(limit: number): Promise<${modelName}Type> {
1192
- return await db.selectFrom('${tableName}')
1193
- .selectAll()
1194
- .orderBy('id', 'desc')
1195
- .limit(limit)
1196
- .execute()
1197
- }
1198
-
1199
- export async function update(id: number, ${formattedModelName}Update: ${modelName}Update) {
1200
- return await db.updateTable('${tableName}')
1201
- .set(${formattedModelName}Update)
1659
+ export async function remove(id: number): Promise<void> {
1660
+ await db.deleteFrom('${tableName}')
1202
1661
  .where('id', '=', id)
1203
1662
  .execute()
1204
1663
  }
1205
1664
 
1206
- export async function remove(id: number) {
1207
- return await db.deleteFrom('${tableName}')
1208
- .where('id', '=', id)
1209
- .executeTakeFirst()
1210
- }
1211
-
1212
- export async function where(...args: (string | number)[]) {
1213
- let column: any
1214
- let operator: any
1215
- let value: any
1216
-
1217
- if (args.length === 2) {
1218
- [column, value] = args
1219
- operator = '='
1220
- } else if (args.length === 3) {
1221
- [column, operator, value] = args
1222
- } else {
1223
- throw new Error("Invalid number of arguments")
1224
- }
1225
-
1226
- let query = db.selectFrom('${tableName}')
1227
-
1228
- query = query.where(column, operator, value)
1229
-
1230
- return await query.selectAll().execute()
1231
- }
1232
-
1233
- export async function whereIs(
1234
- criteria: Partial<${modelName}Type>,
1235
- options: QueryOptions = {},
1236
- ) {
1237
- let query = db.selectFrom('${tableName}')
1238
-
1239
- // Apply criteria
1240
- if (criteria.id)
1241
- query = query.where('id', '=', criteria.id)
1242
-
1243
- if (criteria.email)
1244
- query = query.where('email', '=', criteria.email)
1245
-
1246
- if (criteria.name !== undefined) {
1247
- query = query.where(
1248
- 'name',
1249
- criteria.name === null ? 'is' : '=',
1250
- criteria.name,
1251
- )
1252
- }
1253
-
1254
- if (criteria.password)
1255
- query = query.where('password', '=', criteria.password)
1256
-
1257
- if (criteria.created_at)
1258
- query = query.where('created_at', '=', criteria.created_at)
1259
-
1260
- if (criteria.updated_at)
1261
- query = query.where('updated_at', '=', criteria.updated_at)
1262
-
1263
- if (criteria.deleted_at)
1264
- query = query.where('deleted_at', '=', criteria.deleted_at)
1265
-
1266
- // Apply sorting from options
1267
- if (options.sort)
1268
- query = query.orderBy(options.sort.column, options.sort.order)
1665
+ ${whereFunctionStatements}
1269
1666
 
1270
- // Apply pagination from options
1271
- if (options.limit !== undefined)
1272
- query = query.limit(options.limit)
1273
-
1274
- if (options.offset !== undefined)
1275
- query = query.offset(options.offset)
1276
-
1277
- return await query.selectAll().execute()
1278
- }
1279
-
1280
- export async function whereIn(
1281
- column: keyof ${modelName}Type,
1282
- values: any[],
1283
- options: QueryOptions = {},
1284
- ) {
1285
- let query = db.selectFrom('${tableName}')
1286
-
1287
- query = query.where(column, 'in', values)
1288
-
1289
- // Apply sorting from options
1290
- if (options.sort)
1291
- query = query.orderBy(options.sort.column, options.sort.order)
1292
-
1293
- // Apply pagination from options
1294
- if (options.limit !== undefined)
1295
- query = query.limit(options.limit)
1296
-
1297
- if (options.offset !== undefined)
1298
- query = query.offset(options.offset)
1299
-
1300
- return await query.selectAll().execute()
1301
- }
1302
-
1303
- export const ${modelName} = {
1304
- find,
1305
- findOrFail,
1306
- findMany,
1307
- get,
1308
- count,
1309
- all,
1310
- create,
1311
- update,
1312
- remove,
1313
- Model,
1314
- first,
1315
- last,
1316
- recent,
1317
- where,
1318
- whereIn,
1319
- model: ${modelName}Model
1320
- }
1667
+ const ${modelName} = ${modelName}Model
1321
1668
 
1322
1669
  export default ${modelName}
1323
1670
  `