@stacksjs/actions 0.59.11 → 0.61.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/dist/add.js +29 -0
  2. package/dist/build/component-libs.js +9 -0
  3. package/dist/build/core.js +21 -0
  4. package/dist/build/stacks.js +8 -0
  5. package/dist/build.js +317 -0
  6. package/dist/bump.js +16 -0
  7. package/dist/changelog.js +17 -0
  8. package/dist/clean.js +6 -0
  9. package/dist/commit.js +17 -0
  10. package/dist/copy-types.js +14 -0
  11. package/dist/database/seed.js +8 -0
  12. package/dist/deploy/index.js +31 -0
  13. package/dist/dev/components.js +11 -0
  14. package/dist/dev/docs.js +7 -0
  15. package/dist/dev/index.js +112 -0
  16. package/dist/examples.js +43 -0
  17. package/dist/fresh.js +19 -0
  18. package/dist/generate/component-meta.js +59 -0
  19. package/dist/generate/ide-helpers.js +96 -0
  20. package/dist/generate/index.js +251 -0
  21. package/dist/generate/lib-entries.js +175 -0
  22. package/dist/generate/vscode-custom-data.js +96 -0
  23. package/dist/helpers/component-meta.js +50 -0
  24. package/dist/helpers/lib-entries.js +166 -0
  25. package/dist/helpers/package-json.js +146 -0
  26. package/dist/helpers/utils.js +67 -0
  27. package/dist/helpers/vscode-custom-data.js +88 -0
  28. package/dist/index.js +676 -0
  29. package/dist/key-generate.js +10 -0
  30. package/dist/lint/fix.js +12 -0
  31. package/dist/lint/index.js +16 -0
  32. package/dist/make.js +319 -0
  33. package/dist/prepublish.js +17 -0
  34. package/dist/release.js +646 -0
  35. package/dist/test/coverage.js +9 -0
  36. package/dist/test/feature.js +9 -0
  37. package/dist/test/ui.js +6 -0
  38. package/dist/test/unit.js +6 -0
  39. package/dist/test.js +6 -0
  40. package/dist/typecheck.js +6 -0
  41. package/dist/types.js +21 -0
  42. package/dist/upgrade/dependencies.js +6 -0
  43. package/dist/upgrade/framework.js +1 -0
  44. package/dist/upgrade/index.js +78 -0
  45. package/dist/upgrade.js +46 -0
  46. package/package.json +5 -5
  47. package/src/action.ts +1 -0
  48. package/src/actions.ts +2 -2
  49. package/src/add.ts +3 -6
  50. package/src/ai/request-model-access.ts +10 -7
  51. package/src/build/component-libs.ts +5 -2
  52. package/src/build/core.ts +1 -2
  53. package/src/build/stacks.ts +8 -5
  54. package/src/build.ts +11 -25
  55. package/src/bump.ts +0 -2
  56. package/src/check/ports.ts +2 -3
  57. package/src/commit.ts +2 -2
  58. package/src/copy-types.ts +1 -1
  59. package/src/database/seed.ts +8 -4
  60. package/src/deploy/create-receipt-rule.ts +3 -7
  61. package/src/deploy/index.ts +2 -1
  62. package/src/dev/api.ts +4 -4
  63. package/src/dev/components.ts +3 -5
  64. package/src/dev/index.ts +2 -2
  65. package/src/domains/add.ts +8 -6
  66. package/src/domains/purchase.ts +26 -24
  67. package/src/domains/remove.ts +5 -7
  68. package/src/examples.ts +9 -13
  69. package/src/find-projects.ts +4 -6
  70. package/src/fresh.ts +2 -1
  71. package/src/generate/action-types.ts +5 -7
  72. package/src/generate/component-meta.ts +2 -5
  73. package/src/generate/custom-cli-file.ts +4 -3
  74. package/src/generate/env-files.ts +38 -43
  75. package/src/generate/ide-helpers.ts +1 -2
  76. package/src/generate/index.ts +16 -40
  77. package/src/generate/lib-entries.ts +4 -7
  78. package/src/generate/pkgx-file.ts +6 -11
  79. package/src/generate/vscode-custom-data.ts +1 -3
  80. package/src/helpers/component-meta.ts +6 -11
  81. package/src/helpers/index.ts +5 -0
  82. package/src/helpers/lib-entries.ts +30 -25
  83. package/src/helpers/package-json.ts +13 -16
  84. package/src/helpers/utils.ts +56 -13
  85. package/src/helpers/vscode-custom-data.ts +1 -2
  86. package/src/index.ts +2 -1
  87. package/src/key-generate.ts +3 -4
  88. package/src/lint/fix.ts +10 -2
  89. package/src/lint/index.ts +6 -8
  90. package/src/make.ts +79 -77
  91. package/src/migrate/database.ts +22 -1
  92. package/src/migrate/fresh.ts +20 -0
  93. package/src/orm/base.ts +0 -0
  94. package/src/orm/generate-model.ts +1177 -0
  95. package/src/{generate/model-classes.ts → orm/user.ts} +33 -48
  96. package/src/prepublish.ts +5 -2
  97. package/src/release.ts +15 -11
  98. package/src/test/coverage.ts +5 -2
  99. package/src/test/feature.ts +5 -2
  100. package/src/test/ui.ts +1 -1
  101. package/src/test/unit.ts +1 -1
  102. package/src/test.ts +1 -1
  103. package/src/typecheck.ts +1 -1
  104. package/src/types.ts +2 -2
  105. package/src/upgrade/binary.ts +3 -5
  106. package/src/upgrade/index.ts +4 -8
  107. package/src/upgrade/shell.ts +10 -10
  108. package/src/upgrade.ts +28 -25
  109. package/dist/action.d.ts +0 -36
  110. package/dist/add.d.ts +0 -6
  111. package/dist/build/component-libs.d.ts +0 -1
  112. package/dist/build/core.d.ts +0 -1
  113. package/dist/build/stacks.d.ts +0 -1
  114. package/dist/build.d.ts +0 -9
  115. package/dist/bump.d.ts +0 -1
  116. package/dist/changelog.d.ts +0 -1
  117. package/dist/clean.d.ts +0 -1
  118. package/dist/commit.d.ts +0 -3
  119. package/dist/copy-types.d.ts +0 -5
  120. package/dist/database/seed.d.ts +0 -1
  121. package/dist/deploy/index.d.ts +0 -1
  122. package/dist/dev/components.d.ts +0 -1
  123. package/dist/dev/docs.d.ts +0 -1
  124. package/dist/dev/index.d.ts +0 -10
  125. package/dist/examples.d.ts +0 -5
  126. package/dist/fresh.d.ts +0 -1
  127. package/dist/generate/component-meta.d.ts +0 -5
  128. package/dist/generate/ide-helpers.d.ts +0 -6
  129. package/dist/generate/index.d.ts +0 -13
  130. package/dist/generate/lib-entries.d.ts +0 -2
  131. package/dist/generate/vscode-custom-data.d.ts +0 -6
  132. package/dist/helpers/component-meta.d.ts +0 -22
  133. package/dist/helpers/lib-entries.d.ts +0 -13
  134. package/dist/helpers/package-json.d.ts +0 -3
  135. package/dist/helpers/utils.d.ts +0 -18
  136. package/dist/helpers/vscode-custom-data.d.ts +0 -2
  137. package/dist/index.d.ts +0 -7
  138. package/dist/key-generate.d.ts +0 -1
  139. package/dist/lint/fix.d.ts +0 -1
  140. package/dist/lint/index.d.ts +0 -1
  141. package/dist/make.d.ts +0 -20
  142. package/dist/prepublish.d.ts +0 -1
  143. package/dist/release.d.ts +0 -2
  144. package/dist/test/coverage.d.ts +0 -1
  145. package/dist/test/feature.d.ts +0 -1
  146. package/dist/test/ui.d.ts +0 -1
  147. package/dist/test/unit.d.ts +0 -1
  148. package/dist/test.d.ts +0 -1
  149. package/dist/tinker.d.ts +0 -1
  150. package/dist/typecheck.d.ts +0 -1
  151. package/dist/types.d.ts +0 -3
  152. package/dist/upgrade/dependencies.d.ts +0 -1
  153. package/dist/upgrade/framework.d.ts +0 -1
  154. package/dist/upgrade/index.d.ts +0 -1
  155. package/dist/upgrade.d.ts +0 -4
  156. package/src/database/fields.ts +0 -111
  157. package/src/database/migration-mysql.ts +0 -72
  158. package/src/database/migration-sqlite.ts +0 -67
  159. package/src/database/migration.ts +0 -10
  160. package/src/database/user-migration.ts +0 -20
  161. package/src/generate/model-generate.ts +0 -12
  162. package/src/migrate/dns.ts +0 -3
  163. package/src/tinker.ts +0 -10
@@ -0,0 +1,1177 @@
1
+ import { log } from '@stacksjs/logging'
2
+ import { modelTableName } from '@stacksjs/orm'
3
+ import { path } from '@stacksjs/path'
4
+ import { fs, glob } from '@stacksjs/storage'
5
+ import { pascalCase } from '@stacksjs/strings'
6
+ import type { Model, RelationConfig } from '@stacksjs/types'
7
+ import { isString } from '@stacksjs/validation'
8
+
9
+ export interface FieldArrayElement {
10
+ entity: string
11
+ charValue?: string | null
12
+ // Add other properties as needed
13
+ }
14
+
15
+ export interface ModelElement {
16
+ field: string
17
+ default: string | number | boolean | Date | undefined | null
18
+ unique: boolean
19
+ fieldArray: FieldArrayElement | null
20
+ }
21
+
22
+ await initiateModelGeneration()
23
+ await setKyselyTypes()
24
+
25
+ async function generateApiRoutes(model: Model) {
26
+ if (model.traits?.useApi) {
27
+ let routeString = `import { route } from '@stacksjs/router'\n\n\n`
28
+ const apiRoutes = model.traits?.useApi?.routes
29
+
30
+ if (apiRoutes?.length) {
31
+ for (const apiRoute of apiRoutes) {
32
+ await writeOrmActions(apiRoute, model)
33
+ routeString += await writeApiRoutes(apiRoute, model)
34
+ }
35
+ }
36
+
37
+ const file = Bun.file(path.projectStoragePath(`framework/orm/routes.ts`))
38
+ const writer = file.writer()
39
+ writer.write(routeString)
40
+ await writer.end()
41
+ }
42
+ }
43
+
44
+ async function writeModelNames() {
45
+ const modelFiles = glob.sync(path.userModelsPath('*.ts'))
46
+
47
+ let fileString = `export type ModelNames = `
48
+
49
+ for (let i = 0; i < modelFiles.length; i++) {
50
+ const modeFileElement = modelFiles[i] as string
51
+
52
+ const model = (await import(modeFileElement)).default as Model
53
+
54
+ const typeFile = Bun.file(path.projectStoragePath(`framework/core/types/src/model-names.ts`))
55
+
56
+ fileString += `'${model.name}'`
57
+
58
+ if (i < modelFiles.length - 1) {
59
+ fileString += ' | '
60
+ }
61
+
62
+ const writer = typeFile.writer()
63
+
64
+ writer.write(fileString)
65
+ }
66
+ }
67
+
68
+ async function writeOrmActions(apiRoute: string, model: Model): Promise<void> {
69
+ const modelName = model.name
70
+ const formattedApiRoute = apiRoute.charAt(0).toUpperCase() + apiRoute.slice(1)
71
+
72
+ let actionString = `import { Action } from '@stacksjs/actions'\n`
73
+ actionString += `import ${modelName} from '../src/${modelName}'\n\n`
74
+ actionString += `import { request } from '@stacksjs/router'\n\n`
75
+
76
+ let handleString = ``
77
+
78
+ if (apiRoute === 'index') {
79
+ handleString += `handle() {
80
+ return ${modelName}.all()
81
+ },`
82
+ }
83
+
84
+ if (apiRoute === 'show') {
85
+ handleString += `handle() {
86
+ return ${modelName}.find(1)
87
+ },`
88
+ }
89
+
90
+ if (apiRoute === 'destroy') {
91
+ handleString += `handle() {
92
+ const model = ${modelName}.find(1)
93
+
94
+ model.delete()
95
+
96
+ return 'Model deleted!'
97
+ },`
98
+ }
99
+
100
+ if (apiRoute === 'store') {
101
+ handleString += `handle() {
102
+ const model = ${modelName}.create(request.all())
103
+
104
+ return model
105
+ },`
106
+ }
107
+
108
+ if (apiRoute === 'update') {
109
+ handleString += `handle() {
110
+ const id = request.get(req.params.id)
111
+
112
+ const model = ${modelName}.find(req.params.id)
113
+
114
+ return model.update(req.all())
115
+ },`
116
+ }
117
+
118
+ actionString += `export default new Action({
119
+ name: '${modelName} ${formattedApiRoute}',
120
+ description: '${modelName} ${formattedApiRoute} ORM Action',
121
+
122
+ ${handleString}
123
+ })
124
+ `
125
+
126
+ const file = Bun.file(path.projectStoragePath(`framework/orm/Actions/${modelName}${formattedApiRoute}OrmAction.ts`))
127
+
128
+ const writer = file.writer()
129
+
130
+ writer.write(actionString)
131
+ }
132
+
133
+ async function writeApiRoutes(apiRoute: string, model: Model): Promise<string> {
134
+ let routeString = ``
135
+ const tableName = await modelTableName(model)
136
+ const modelName = model.name
137
+
138
+ if (apiRoute === 'index') routeString += `await route.get('${tableName}', 'Actions/${modelName}IndexOrmAction')\n\n`
139
+
140
+ if (apiRoute === 'store') routeString += `await route.post('${tableName}', 'Actions/${modelName}StoreOrmAction')\n\n`
141
+
142
+ if (apiRoute === 'update')
143
+ routeString += `await route.patch('${tableName}/{id}', 'Actions/${modelName}UpdateOrmAction')\n\n`
144
+
145
+ if (apiRoute === 'show')
146
+ routeString += `await route.get('${tableName}/{id}', 'Actions/${modelName}ShowOrmAction')\n\n`
147
+
148
+ if (apiRoute === 'destroy')
149
+ routeString += `await route.delete('${tableName}/{id}', 'Actions/${modelName}DestroyOrmAction')\n\n`
150
+
151
+ return routeString
152
+ }
153
+
154
+ async function initiateModelGeneration(): Promise<void> {
155
+ await deleteExistingModels()
156
+ await deleteExistingOrmActions()
157
+ await deleteExistingModelNameTypes()
158
+ await writeModelNames()
159
+
160
+ const modelFiles = glob.sync(path.userModelsPath('*.ts'))
161
+
162
+ for (const modelFile of modelFiles) {
163
+ log.debug(`Processing model file: ${modelFile}`)
164
+
165
+ const model = (await import(modelFile)).default as Model
166
+ const tableName = await modelTableName(model)
167
+ const modelName = path.basename(modelFile, '.ts')
168
+
169
+ await generateApiRoutes(model)
170
+
171
+ const file = Bun.file(path.projectStoragePath(`framework/orm/src/models/${modelName}.ts`))
172
+ const fields = await extractFields(model, modelFile)
173
+ const classString = await generateModelString(tableName, model, fields)
174
+
175
+ const writer = file.writer()
176
+ writer.write(classString)
177
+ await writer.end()
178
+ }
179
+ }
180
+
181
+ async function getRelations(model: Model): Promise<RelationConfig[]> {
182
+ const relationsArray = ['hasOne', 'belongsTo', 'hasMany', 'belongsToMany', 'hasOneThrough']
183
+
184
+ const relationships = []
185
+
186
+ for (const relation of relationsArray) {
187
+ if (hasRelations(model, relation)) {
188
+ for (const relationInstance of model[relation]) {
189
+ let relationModel = relationInstance.model
190
+
191
+ if (isString(relationInstance)) {
192
+ relationModel = relationInstance
193
+ }
194
+
195
+ const modelRelationPath = path.userModelsPath(`${relationModel}.ts`)
196
+
197
+ const modelRelation = (await import(modelRelationPath)).default as Model
198
+
199
+ const formattedModelName = model.name?.toLowerCase()
200
+
201
+ relationships.push({
202
+ relationship: relation,
203
+ model: relationModel,
204
+ table: modelRelation.table,
205
+ foreignKey: relationInstance.foreignKey || `${formattedModelName}_id`,
206
+ relationName: relationInstance.relationName || '',
207
+ throughModel: relationInstance.through || '',
208
+ throughForeignKey: relationInstance.throughForeignKey || '',
209
+ pivotTable: relationInstance?.pivotTable || `${formattedModelName}_${modelRelation.table}`,
210
+ })
211
+ }
212
+ }
213
+ }
214
+
215
+ return relationships
216
+ }
217
+
218
+ function hasRelations(obj: any, key: string): boolean {
219
+ return key in obj
220
+ }
221
+
222
+ async function deleteExistingModels() {
223
+ const modelPaths = glob.sync(path.projectStoragePath(`framework/orm/src/models*.ts`))
224
+
225
+ for (const modelPath of modelPaths) {
226
+ if (fs.existsSync(modelPath)) await Bun.$`rm ${modelPath}`
227
+ }
228
+
229
+ const typePath = path.projectStoragePath(`framework/core/orm/src/generated/types.ts`)
230
+
231
+ if (fs.existsSync(typePath)) await Bun.$`rm ${typePath}`
232
+ }
233
+
234
+ async function deleteExistingOrmActions() {
235
+ const ormPaths = glob.sync(path.projectStoragePath(`framework/orm/Actions/*.ts`))
236
+
237
+ for (const ormPath of ormPaths) {
238
+ if (fs.existsSync(ormPath)) await Bun.$`rm ${ormPath}`
239
+ }
240
+ }
241
+
242
+ async function deleteExistingModelNameTypes() {
243
+ const typeFile = path.projectStoragePath(`framework/core/types/src/model-names.ts`)
244
+
245
+ if (fs.existsSync(typeFile)) await Bun.$`rm ${typeFile}`
246
+ }
247
+
248
+ async function setKyselyTypes() {
249
+ let text = ``
250
+ const modelFiles = glob.sync(path.userModelsPath('*.ts'))
251
+
252
+ for (const modelFile of modelFiles) {
253
+ const model = (await import(modelFile)).default as Model
254
+ const tableName = await modelTableName(model)
255
+ const modelName = model.name
256
+
257
+ const words = tableName.split('_')
258
+
259
+ const pivotFormatted = `${words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join('')}`
260
+
261
+ text += `import type { ${pivotFormatted}Table } from '../../../../orm/src/models/${modelName}'\n`
262
+ }
263
+
264
+ text += `import type { Generated } from 'kysely'\n\n`
265
+
266
+ let pivotFormatted = ''
267
+ for (const modelFile of modelFiles) {
268
+ const model = (await import(modelFile)).default as Model
269
+ const pivotTables = await getPivotTables(model)
270
+
271
+ for (const pivotTable of pivotTables) {
272
+ const words = pivotTable.table.split('_')
273
+
274
+ pivotFormatted = `${words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join('')}Table`
275
+
276
+ text += `export interface ${pivotFormatted} {
277
+ id: Generated<number>
278
+ ${pivotTable.firstForeignKey}: number
279
+ ${pivotTable.secondForeignKey}: number
280
+ }`
281
+ }
282
+ }
283
+
284
+ text += `\nexport interface Database {\n`
285
+
286
+ for (const modelFile of modelFiles) {
287
+ const model = (await import(modelFile)).default as Model
288
+ const tableName = await modelTableName(model)
289
+ const pivotTables = await getPivotTables(model)
290
+
291
+ for (const pivotTable of pivotTables) text += ` ${pivotTable.table}: ${pivotFormatted}\n`
292
+
293
+ const words = tableName.split('_')
294
+
295
+ const formattedTableName = `${words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join('')}Table`
296
+
297
+ text += ` ${tableName}: ${formattedTableName}\n`
298
+ }
299
+
300
+ text += `}`
301
+
302
+ const file = Bun.file(path.projectStoragePath(`framework/core/orm/src/generated/types.ts`))
303
+
304
+ const writer = file.writer()
305
+
306
+ writer.write(text)
307
+
308
+ await writer.end()
309
+ }
310
+
311
+ async function extractFields(model: Model, modelFile: string): Promise<ModelElement[]> {
312
+ // TODO: we can improve this type
313
+ const fields: Record<string, any> = model.attributes
314
+ const fieldKeys = Object.keys(fields)
315
+
316
+ const rules: string[] = []
317
+
318
+ const file = Bun.file(modelFile) // Assuming Bun is imported properly
319
+
320
+ const code = await file.text()
321
+
322
+ const regex = /rule:.*$/gm
323
+
324
+ let match: RegExpExecArray | null
325
+ match = regex.exec(code)
326
+
327
+ while (match !== null) {
328
+ rules.push(match[0])
329
+ match = regex.exec(code)
330
+ }
331
+
332
+ const input: ModelElement[] = fieldKeys.map((field, index) => {
333
+ const fieldExist = fields[field]
334
+ let defaultValue = null
335
+ let uniqueValue = false
336
+
337
+ if (fieldExist) {
338
+ defaultValue = fieldExist || null
339
+ uniqueValue = fieldExist.unique || false
340
+ }
341
+
342
+ return {
343
+ field,
344
+ default: defaultValue,
345
+ unique: uniqueValue,
346
+ fieldArray: parseRule(rules[index] ?? ''),
347
+ }
348
+ })
349
+
350
+ return input
351
+ }
352
+
353
+ function parseRule(rule: string): FieldArrayElement | null {
354
+ const parts = rule.split('rule: schema.')
355
+
356
+ if (parts.length !== 2) return null
357
+
358
+ if (!parts[1]) parts[1] = ''
359
+
360
+ const extractedString = parts[1].replace(/,/g, '')
361
+
362
+ if (!extractedString) return null
363
+
364
+ const extractedParts = extractedString.split('.')
365
+ const regex = /\(([^)]+)\)/
366
+
367
+ return (
368
+ extractedParts.map((input) => {
369
+ const match = regex.exec(input)
370
+ const value = match ? match[1] : null
371
+ const field = input.replace(regex, '').replace(/\(|\)/g, '')
372
+ return { entity: field, charValue: value }
373
+ })[0] || null
374
+ )
375
+ }
376
+
377
+ function getRelationType(relation: string): string {
378
+ const belongToType = /belongs/
379
+ const hasType = /has/
380
+ const throughType = /Through/
381
+
382
+ if (throughType.test(relation)) return 'throughType'
383
+
384
+ if (belongToType.test(relation)) return 'belongsType'
385
+
386
+ if (hasType.test(relation)) return 'hasType'
387
+
388
+ return ''
389
+ }
390
+
391
+ function getRelationCount(relation: string): string {
392
+ const singular = /One/
393
+ const plural = /Many/
394
+
395
+ if (plural.test(relation)) return 'many'
396
+
397
+ if (singular.test(relation)) return 'one'
398
+
399
+ return ''
400
+ }
401
+
402
+ async function getPivotTables(
403
+ model: Model,
404
+ ): Promise<{ table: string; firstForeignKey?: string; secondForeignKey?: string }[]> {
405
+ const pivotTable = []
406
+
407
+ if ('belongsToMany' in model) {
408
+ const belongsToManyArr = model.belongsToMany || []
409
+ for (const belongsToManyRelation of belongsToManyArr) {
410
+ const modelRelationPath = path.userModelsPath(`${belongsToManyRelation}.ts`)
411
+ const modelRelation = (await import(modelRelationPath)).default as Model
412
+ const formattedModelName = model?.name?.toLowerCase()
413
+
414
+ const firstForeignKey =
415
+ belongsToManyRelation.firstForeignKey || `${model.name?.toLowerCase()}_${model.primaryKey}`
416
+ const secondForeignKey =
417
+ belongsToManyRelation.secondForeignKey || `${modelRelation.name?.toLowerCase()}_${model.primaryKey}`
418
+
419
+ pivotTable.push({
420
+ table: belongsToManyRelation?.pivotTable || `${formattedModelName}_${modelRelation.table}`,
421
+ firstForeignKey: firstForeignKey,
422
+ secondForeignKey: secondForeignKey,
423
+ })
424
+ }
425
+
426
+ return pivotTable
427
+ }
428
+
429
+ return []
430
+ }
431
+
432
+ export async function fetchOtherModelRelations(model: Model): Promise<RelationConfig[]> {
433
+ const modelFiles = glob.sync(path.userModelsPath('*.ts'))
434
+ const modelRelations = []
435
+
436
+ for (let i = 0; i < modelFiles.length; i++) {
437
+ const modelFileElement = modelFiles[i] as string
438
+ const modelFile = await import(modelFileElement)
439
+
440
+ if (model.name === modelFile.default.name) continue
441
+
442
+ const relations = await getRelations(modelFile.default)
443
+
444
+ if (!relations.length) continue
445
+
446
+ const relation = relations.find((relation) => relation.model === model.name)
447
+
448
+ if (relation) modelRelations.push(relation)
449
+ }
450
+
451
+ return modelRelations
452
+ }
453
+
454
+ async function generateModelString(tableName: string, model: Model, attributes: ModelElement[]): Promise<string> {
455
+ const modelName = model.name
456
+ const formattedTableName = pascalCase(tableName) // users -> Users
457
+ const formattedModelName = modelName?.toLowerCase() // User -> user
458
+
459
+ let fieldString = ''
460
+ let relationMethods = ``
461
+ let relationImports = ``
462
+
463
+ const relations = await getRelations(model)
464
+
465
+ for (const relationInstance of relations) {
466
+ relationImports += `import ${relationInstance.model} from './${relationInstance.model}'\n\n`
467
+ }
468
+
469
+ for (const relation of relations) {
470
+ const modelRelation = relation.model
471
+ const foreignKeyRelation = relation.foreignKey
472
+ const tableRelation = relation.table || ''
473
+ const pivotTableRelation = relation.pivotTable
474
+ const formattedModelRelation = modelRelation.toLowerCase()
475
+ const capitalizeTableRelation = tableRelation.charAt(0).toUpperCase() + tableRelation.slice(1)
476
+
477
+ const relationType = getRelationType(relation.relationship)
478
+ const relationCount = getRelationCount(relation.relationship)
479
+
480
+ if (relationType === 'throughType') {
481
+ const relationName = relation.relationName || formattedModelName + modelRelation
482
+ const throughRelation = relation.throughModel
483
+
484
+ const formattedThroughRelation = relation.throughModel.toLowerCase()
485
+ const throughTableRelation = throughRelation
486
+ const foreignKeyThroughRelation = relation.throughForeignKey || `${formattedThroughRelation}_id`
487
+
488
+ relationMethods += `
489
+ async ${relationName}() {
490
+ if (this.${formattedModelName}.id === undefined)
491
+ throw new Error('Relation Error!')
492
+
493
+ const firstModel = await db.selectFrom('${throughTableRelation}')
494
+ .where('${foreignKeyRelation}', '=', this.${formattedModelName}.id)
495
+ .selectAll()
496
+ .executeTakeFirst()
497
+
498
+ if (! firstModel)
499
+ throw new Error('Model Relation Not Found!')
500
+
501
+ const finalModel = await db.selectFrom('${tableRelation}')
502
+ .where('${foreignKeyThroughRelation}', '=', firstModel.id)
503
+ .selectAll()
504
+ .executeTakeFirst()
505
+
506
+ return new ${modelRelation}.modelInstance(finalModel)
507
+ }\n\n`
508
+ }
509
+
510
+ if (relationType === 'hasType' && relationCount === 'many') {
511
+ const relationName = relation.relationName || tableRelation
512
+
513
+ relationMethods += `
514
+ async ${relationName}() {
515
+ if (this.${formattedModelName}.id === undefined)
516
+ throw new Error('Relation Error!')
517
+
518
+ const results = await db.selectFrom('${tableRelation}')
519
+ .where('${foreignKeyRelation}', '=', this.${formattedModelName}.id)
520
+ .selectAll()
521
+ .execute()
522
+
523
+ return results
524
+ }\n\n`
525
+ }
526
+
527
+ if (relationType === 'hasType' && relationCount === 'one') {
528
+ const relationName = relation.relationName || formattedModelRelation
529
+ relationMethods += `
530
+ async ${relationName}() {
531
+ if (this.${formattedModelName}.id === undefined)
532
+ throw new Error('Relation Error!')
533
+
534
+ const model = await db.selectFrom('${tableRelation}')
535
+ .where('${foreignKeyRelation}', '=', this.${formattedModelName}.id)
536
+ .selectAll()
537
+ .executeTakeFirst()
538
+
539
+ if (! model)
540
+ throw new Error('Model Relation Not Found!')
541
+
542
+ return new ${modelRelation}.modelInstance(model)
543
+ }\n\n`
544
+ }
545
+
546
+ if (relationType === 'belongsType' && !relationCount) {
547
+ const relationName = relation.relationName || formattedModelRelation
548
+
549
+ relationMethods += `
550
+ async ${relationName}() {
551
+ if (this.${foreignKeyRelation} === undefined)
552
+ throw new Error('Relation Error!')
553
+
554
+ const model = await db.selectFrom('${tableRelation}')
555
+ .where('id', '=', ${foreignKeyRelation})
556
+ .selectAll()
557
+ .executeTakeFirst()
558
+
559
+ if (! model)
560
+ throw new Error('Model Relation Not Found!')
561
+
562
+ return new ${modelRelation}.modelInstance(model)
563
+ }\n\n`
564
+ }
565
+
566
+ if (relationType === 'belongsType' && relationCount === 'many') {
567
+ const pivotTable = pivotTableRelation || tableRelation
568
+ const relationName = relation.relationName || formattedModelName + capitalizeTableRelation
569
+
570
+ relationMethods += `
571
+ async ${relationName}() {
572
+ if (this.${formattedModelName}.id === undefined)
573
+ throw new Error('Relation Error!')
574
+
575
+ const results = await db.selectFrom('${pivotTable}')
576
+ .where('${foreignKeyRelation}', '=', this.${formattedModelName}.id)
577
+ .selectAll()
578
+ .execute()
579
+
580
+ return results
581
+ }\n\n`
582
+ }
583
+ }
584
+
585
+ for (const attribute of attributes) fieldString += ` ${attribute.field}: ${attribute.fieldArray?.entity}\n `
586
+
587
+ const otherModelRelations = await fetchOtherModelRelations(model)
588
+
589
+ for (const otherModelRelation of otherModelRelations) fieldString += ` ${otherModelRelation.foreignKey}: number`
590
+
591
+ return `import type { ColumnType, Generated, Insertable, Selectable, Updateable } from 'kysely'
592
+ import type { Result } from '@stacksjs/error-handling'
593
+ import { err, handleError, ok } from '@stacksjs/error-handling'
594
+ import { db } from '@stacksjs/database'
595
+ ${relationImports}
596
+ // import { Kysely, MysqlDialect, PostgresDialect } from 'kysely'
597
+ // import { Pool } from 'pg'
598
+
599
+ // TODO: we need an action that auto-generates these table interfaces
600
+ export interface ${formattedTableName}Table {
601
+ id: Generated<number>
602
+ ${fieldString}
603
+ created_at: ColumnType<Date, string | undefined, never>
604
+ updated_at: ColumnType<Date, string | undefined, never>
605
+ deleted_at: ColumnType<Date, string | undefined, never>
606
+ }
607
+
608
+ interface ${modelName}Response {
609
+ data: ${formattedTableName}
610
+ paging: {
611
+ total_records: number
612
+ page: number
613
+ total_pages: number
614
+ }
615
+ next_cursor: number | null
616
+ }
617
+
618
+ export type ${modelName}Type = Selectable<${formattedTableName}Table>
619
+ export type New${modelName} = Insertable<${formattedTableName}Table>
620
+ export type ${modelName}Update = Updateable<${formattedTableName}Table>
621
+ export type ${formattedTableName} = ${modelName}Type[]
622
+
623
+ export type ${modelName}Column = ${formattedTableName}
624
+ export type ${modelName}Columns = Array<keyof ${formattedTableName}>
625
+
626
+ type SortDirection = 'asc' | 'desc'
627
+ interface SortOptions { column: ${modelName}Type, order: SortDirection }
628
+ // Define a type for the options parameter
629
+ interface QueryOptions {
630
+ sort?: SortOptions
631
+ limit?: number
632
+ offset?: number
633
+ page?: number
634
+ }
635
+
636
+ export class ${modelName}Model {
637
+ private ${formattedModelName}: Partial<${modelName}Type>
638
+ private results: Partial<${modelName}Type>[]
639
+ private hidden = ['password'] // TODO: this hidden functionality needs to be implemented still
640
+
641
+ constructor(${formattedModelName}: Partial<${modelName}Type>) {
642
+ this.${formattedModelName} = ${formattedModelName}
643
+ }
644
+
645
+ // Method to find a ${formattedModelName} by ID
646
+ static async find(id: number, fields?: (keyof ${modelName}Type)[]) {
647
+ let query = db.selectFrom('${tableName}').where('id', '=', id)
648
+
649
+ if (fields)
650
+ query = query.select(fields)
651
+ else
652
+ query = query.selectAll()
653
+
654
+ const model = await query.executeTakeFirst()
655
+
656
+ if (!model)
657
+ return null
658
+
659
+ return new ${modelName}Model(model)
660
+ }
661
+
662
+ static async findMany(ids: number[], fields?: (keyof ${modelName}Type)[]) {
663
+ let query = db.selectFrom('${tableName}').where('id', 'in', ids)
664
+
665
+ if (fields)
666
+ query = query.select(fields)
667
+ else
668
+ query = query.selectAll()
669
+
670
+ const model = await query.execute()
671
+
672
+ return model.map(modelItem => new ${modelName}Model(modelItem))
673
+ }
674
+
675
+ // Method to get a ${formattedModelName} by criteria
676
+ static async get(criteria: Partial<${modelName}Type>, options: QueryOptions = {}): Promise<${modelName}Model[]> {
677
+ let query = db.selectFrom('${tableName}')
678
+
679
+ // Apply sorting from options
680
+ if (options.sort)
681
+ query = query.orderBy(options.sort.column, options.sort.order)
682
+
683
+ // Apply limit and offset from options
684
+ if (options.limit !== undefined)
685
+ query = query.limit(options.limit)
686
+
687
+ if (options.offset !== undefined)
688
+ query = query.offset(options.offset)
689
+
690
+ const model = await query.selectAll().execute()
691
+ return model.map(modelItem => new ${modelName}Model(modelItem))
692
+ }
693
+
694
+ // Method to get all ${tableName}
695
+ static async all(options: QueryOptions = { limit: 10, offset: 0, page: 1 }): Promise<${modelName}Response> {
696
+ const totalRecordsResult = await db.selectFrom('${tableName}')
697
+ .select(db.fn.count('id').as('total')) // Use 'id' or another actual column name
698
+ .executeTakeFirst()
699
+
700
+ const totalRecords = Number(totalRecordsResult?.total) || 0
701
+ const totalPages = Math.ceil(totalRecords / (options.limit ?? 10))
702
+
703
+ const ${tableName}WithExtra = await db.selectFrom('${tableName}')
704
+ .selectAll()
705
+ .orderBy('id', 'asc') // Assuming 'id' is used for cursor-based pagination
706
+ .limit((options.limit ?? 10) + 1) // Fetch one extra record
707
+ .offset((options.page - 1) * (options.limit ?? 10))
708
+ .execute()
709
+
710
+ let nextCursor = null
711
+ if (${tableName}WithExtra.length > (options.limit ?? 10))
712
+ nextCursor = ${tableName}WithExtra.pop()!.id // Use the ID of the extra record as the next cursor
713
+
714
+ return {
715
+ data: ${tableName}WithExtra,
716
+ paging: {
717
+ total_records: totalRecords,
718
+ page: options.page,
719
+ total_pages: totalPages,
720
+ },
721
+ next_cursor: nextCursor,
722
+ }
723
+ }
724
+
725
+ // Method to create a new ${formattedModelName}
726
+ static async create(new${modelName}: New${modelName}): Promise<${modelName}Model> {
727
+ const model = await db.insertInto('${tableName}')
728
+ .values(new${modelName})
729
+ .returningAll()
730
+ .executeTakeFirstOrThrow()
731
+
732
+ return new ${modelName}Model(model)
733
+ }
734
+
735
+ // Method to update a ${formattedModelName}
736
+ static async update(id: number, ${formattedModelName}Update: ${modelName}Update): Promise<${modelName}Model> {
737
+ const model = await db.updateTable('${tableName}')
738
+ .set(${formattedModelName}Update)
739
+ .where('id', '=', id)
740
+ .returningAll()
741
+ .executeTakeFirstOrThrow()
742
+
743
+ return new ${modelName}Model(model)
744
+ }
745
+
746
+ // Method to remove a ${formattedModelName}
747
+ static async remove(id: number): Promise<${modelName}Model> {
748
+ const model = await db.deleteFrom('${tableName}')
749
+ .where('id', '=', id)
750
+ .returningAll()
751
+ .executeTakeFirstOrThrow()
752
+
753
+ return new ${modelName}Model(model)
754
+ }
755
+
756
+ async where(column: string, operator = '=', value: any) {
757
+ let query = db.selectFrom('${tableName}')
758
+
759
+ query = query.where(column, operator, value)
760
+
761
+ return await query.selectAll().execute()
762
+ }
763
+
764
+ async whereIs(criteria: Partial<${modelName}Type>, options: QueryOptions = {}) {
765
+ let query = db.selectFrom('${tableName}')
766
+
767
+ // Existing criteria checks
768
+ if (criteria.id)
769
+ query = query.where('id', '=', criteria.id) // Kysely is immutable, we must re-assign
770
+
771
+ if (criteria.email)
772
+ query = query.where('email', '=', criteria.email)
773
+
774
+ if (criteria.name !== undefined) {
775
+ query = query.where(
776
+ 'name',
777
+ criteria.name === null ? 'is' : '=',
778
+ criteria.name,
779
+ )
780
+ }
781
+
782
+ if (criteria.password)
783
+ query = query.where('password', '=', criteria.password)
784
+
785
+ if (criteria.created_at)
786
+ query = query.where('created_at', '=', criteria.created_at)
787
+
788
+ if (criteria.updated_at)
789
+ query = query.where('updated_at', '=', criteria.updated_at)
790
+
791
+ if (criteria.deleted_at)
792
+ query = query.where('deleted_at', '=', criteria.deleted_at)
793
+
794
+ // Apply sorting from options
795
+ if (options.sort)
796
+ query = query.orderBy(options.sort.column, options.sort.order)
797
+
798
+ // Apply pagination from options
799
+ if (options.limit !== undefined)
800
+ query = query.limit(options.limit)
801
+
802
+ if (options.offset !== undefined)
803
+ query = query.offset(options.offset)
804
+
805
+ return await query.selectAll().execute()
806
+ }
807
+
808
+ async whereIn(column: keyof ${modelName}Type, values: any[], options: QueryOptions = {}) {
809
+ let query = db.selectFrom('${tableName}')
810
+
811
+ query = query.where(column, 'in', values)
812
+
813
+ // Apply sorting from options
814
+ if (options.sort)
815
+ query = query.orderBy(options.sort.column, options.sort.order)
816
+
817
+ // Apply pagination from options
818
+ if (options.limit !== undefined)
819
+ query = query.limit(options.limit)
820
+
821
+ if (options.offset !== undefined)
822
+ query = query.offset(options.offset)
823
+
824
+ return await query.selectAll().execute()
825
+ }
826
+
827
+ async first() {
828
+ return await db.selectFrom('${tableName}')
829
+ .selectAll()
830
+ .executeTakeFirst()
831
+ }
832
+
833
+ async last() {
834
+ return await db.selectFrom('${tableName}')
835
+ .selectAll()
836
+ .orderBy('id', 'desc')
837
+ .executeTakeFirst()
838
+ }
839
+
840
+ async orderBy(column: keyof ${modelName}Type, order: 'asc' | 'desc') {
841
+ return await db.selectFrom('${tableName}')
842
+ .selectAll()
843
+ .orderBy(column, order)
844
+ .execute()
845
+ }
846
+
847
+ async orderByDesc(column: keyof ${modelName}Type) {
848
+ return await db.selectFrom('${tableName}')
849
+ .selectAll()
850
+ .orderBy(column, 'desc')
851
+ .execute()
852
+ }
853
+
854
+ async orderByAsc(column: keyof ${modelName}Type) {
855
+ return await db.selectFrom('${tableName}')
856
+ .selectAll()
857
+ .orderBy(column, 'asc')
858
+ .execute()
859
+ }
860
+
861
+ // Method to get the ${formattedModelName} instance itself
862
+ self() {
863
+ return this
864
+ }
865
+
866
+ // Method to get the ${formattedModelName} instance data
867
+ get() {
868
+ return this.${formattedModelName}
869
+ }
870
+
871
+ // Method to update the ${formattedModelName} instance
872
+ async update(${formattedModelName}: ${modelName}Update): Promise<Result<${modelName}Type, Error>> {
873
+ if (this.${formattedModelName}.id === undefined)
874
+ return err(handleError('${modelName} ID is undefined'))
875
+
876
+ const updatedModel = await db.updateTable('${tableName}')
877
+ .set(${formattedModelName})
878
+ .where('id', '=', this.${formattedModelName}.id)
879
+ .returningAll()
880
+ .executeTakeFirst()
881
+
882
+ if (!updatedModel)
883
+ return err(handleError('${modelName} not found'))
884
+
885
+ this.${formattedModelName} = updatedModel
886
+
887
+ return ok(updatedModel)
888
+ }
889
+
890
+ // Method to save (insert or update) the ${formattedModelName} instance
891
+ async save(): Promise<void> {
892
+ if (!this.${formattedModelName})
893
+ throw new Error('${modelName} data is undefined')
894
+
895
+ if (this.${formattedModelName}.id === undefined) {
896
+ // Insert new ${formattedModelName}
897
+ const newModel = await db.insertInto('${tableName}')
898
+ .values(this.${formattedModelName} as New${modelName})
899
+ .returningAll()
900
+ .executeTakeFirstOrThrow()
901
+ this.${formattedModelName} = newModel
902
+ }
903
+ else {
904
+ // Update existing ${formattedModelName}
905
+ await this.update(this.${formattedModelName})
906
+ }
907
+ }
908
+
909
+ // Method to delete the ${formattedModelName} instance
910
+ async delete(): Promise<void> {
911
+ if (this.${formattedModelName}.id === undefined)
912
+ throw new Error('${modelName} ID is undefined')
913
+
914
+ await db.deleteFrom('${tableName}')
915
+ .where('id', '=', this.${formattedModelName}.id)
916
+ .execute()
917
+
918
+ this.${formattedModelName} = {}
919
+ }
920
+
921
+ // Method to refresh the ${formattedModelName} instance data from the database
922
+ async refresh(): Promise<void> {
923
+ if (this.${formattedModelName}.id === undefined)
924
+ throw new Error('${modelName} ID is undefined')
925
+
926
+ const refreshedModel = await db.selectFrom('${tableName}')
927
+ .where('id', '=', this.${formattedModelName}.id)
928
+ .selectAll()
929
+ .executeTakeFirst()
930
+
931
+ if (!refreshedModel)
932
+ throw new Error('${modelName} not found')
933
+
934
+ this.${formattedModelName} = refreshedModel
935
+ }
936
+
937
+ ${relationMethods}
938
+
939
+ toJSON() {
940
+ const output: Partial<${modelName}Type> = { ...this.${formattedModelName} }
941
+
942
+ this.hidden.forEach((attr) => {
943
+ if (attr in output)
944
+ delete output[attr as keyof Partial<${modelName}Type>]
945
+ })
946
+
947
+ type ${modelName} = Omit<${modelName}Type, 'password'>
948
+
949
+ return output as ${modelName}
950
+ }
951
+ }
952
+
953
+ const Model = ${modelName}Model
954
+
955
+ // starting here, ORM functions
956
+ export async function find(id: number, fields?: (keyof ${modelName}Type)[]) {
957
+ let query = db.selectFrom('${tableName}').where('id', '=', id)
958
+
959
+ if (fields)
960
+ query = query.select(fields)
961
+ else
962
+ query = query.selectAll()
963
+
964
+ const model = await query.executeTakeFirst()
965
+
966
+ if (!model)
967
+ return null
968
+
969
+ return new ${modelName}Model(model)
970
+ }
971
+
972
+ export async function findMany(ids: number[], fields?: (keyof ${modelName}Type)[]) {
973
+ let query = db.selectFrom('${tableName}').where('id', 'in', ids)
974
+
975
+ if (fields)
976
+ query = query.select(fields)
977
+ else
978
+ query = query.selectAll()
979
+
980
+ const model = await query.execute()
981
+
982
+ return model.map(modelItem => new ${modelName}Model(modelItem))
983
+ }
984
+
985
+ export async function count() {
986
+ const results = await db.selectFrom('${tableName}')
987
+ .selectAll()
988
+ .execute()
989
+
990
+ return results.length
991
+ }
992
+
993
+ export async function get(criteria: Partial<${modelName}Type>, sort: { column: keyof ${modelName}Type, order: 'asc' | 'desc' } = { column: 'created_at', order: 'desc' }) {
994
+ let query = db.selectFrom('${tableName}')
995
+
996
+ if (criteria.id)
997
+ query = query.where('id', '=', criteria.id) // Kysely is immutable, we must re-assign
998
+
999
+ if (criteria.email)
1000
+ query = query.where('email', '=', criteria.email)
1001
+
1002
+ if (criteria.name !== undefined) {
1003
+ query = query.where(
1004
+ 'name',
1005
+ criteria.name === null ? 'is' : '=',
1006
+ criteria.name,
1007
+ )
1008
+ }
1009
+
1010
+ if (criteria.password)
1011
+ query = query.where('password', '=', criteria.password)
1012
+
1013
+ if (criteria.created_at)
1014
+ query = query.where('created_at', '=', criteria.created_at)
1015
+
1016
+ if (criteria.updated_at)
1017
+ query = query.where('updated_at', '=', criteria.updated_at)
1018
+
1019
+ if (criteria.deleted_at)
1020
+ query = query.where('deleted_at', '=', criteria.deleted_at)
1021
+
1022
+ // Apply sorting based on the 'sort' parameter
1023
+ query = query.orderBy(sort.column, sort.order)
1024
+
1025
+ return await query.selectAll().execute()
1026
+ }
1027
+
1028
+ export async function all(limit: number = 10, offset: number = 0) {
1029
+ return await db.selectFrom('${tableName}')
1030
+ .selectAll()
1031
+ .orderBy('created_at', 'desc')
1032
+ .limit(limit)
1033
+ .offset(offset)
1034
+ .execute()
1035
+ }
1036
+
1037
+ export async function create(new${modelName}: New${modelName}) {
1038
+ return await db.insertInto('${tableName}')
1039
+ .values(new${modelName})
1040
+ .returningAll()
1041
+ .executeTakeFirstOrThrow()
1042
+ }
1043
+
1044
+ export async function first() {
1045
+ return await db.selectFrom('${tableName}')
1046
+ .selectAll()
1047
+ .executeTakeFirst()
1048
+ }
1049
+
1050
+ export async function recent(limit: number) {
1051
+ return await db.selectFrom('${tableName}')
1052
+ .selectAll()
1053
+ .limit(limit)
1054
+ .execute()
1055
+ }
1056
+
1057
+ export async function last(limit: number) {
1058
+ return await db.selectFrom('${tableName}')
1059
+ .selectAll()
1060
+ .orderBy('id', 'desc')
1061
+ .limit(limit)
1062
+ .execute()
1063
+ }
1064
+
1065
+ export async function update(id: number, ${formattedModelName}Update: ${modelName}Update) {
1066
+ return await db.updateTable('${tableName}')
1067
+ .set(${formattedModelName}Update)
1068
+ .where('id', '=', id)
1069
+ .execute()
1070
+ }
1071
+
1072
+ export async function remove(id: number) {
1073
+ return await db.deleteFrom('${tableName}')
1074
+ .where('id', '=', id)
1075
+ .returningAll()
1076
+ .executeTakeFirst()
1077
+ }
1078
+
1079
+ export async function where(column: string, operator = '=', value: any) {
1080
+ let query = db.selectFrom('${tableName}')
1081
+
1082
+ query = query.where(column, operator, value)
1083
+
1084
+ return await query.selectAll().execute()
1085
+ }
1086
+
1087
+ export async function whereIs(
1088
+ criteria: Partial<${modelName}Type>,
1089
+ options: QueryOptions = {},
1090
+ ) {
1091
+ let query = db.selectFrom('${tableName}')
1092
+
1093
+ // Apply criteria
1094
+ if (criteria.id)
1095
+ query = query.where('id', '=', criteria.id)
1096
+
1097
+ if (criteria.email)
1098
+ query = query.where('email', '=', criteria.email)
1099
+
1100
+ if (criteria.name !== undefined) {
1101
+ query = query.where(
1102
+ 'name',
1103
+ criteria.name === null ? 'is' : '=',
1104
+ criteria.name,
1105
+ )
1106
+ }
1107
+
1108
+ if (criteria.password)
1109
+ query = query.where('password', '=', criteria.password)
1110
+
1111
+ if (criteria.created_at)
1112
+ query = query.where('created_at', '=', criteria.created_at)
1113
+
1114
+ if (criteria.updated_at)
1115
+ query = query.where('updated_at', '=', criteria.updated_at)
1116
+
1117
+ if (criteria.deleted_at)
1118
+ query = query.where('deleted_at', '=', criteria.deleted_at)
1119
+
1120
+ // Apply sorting from options
1121
+ if (options.sort)
1122
+ query = query.orderBy(options.sort.column, options.sort.order)
1123
+
1124
+ // Apply pagination from options
1125
+ if (options.limit !== undefined)
1126
+ query = query.limit(options.limit)
1127
+
1128
+ if (options.offset !== undefined)
1129
+ query = query.offset(options.offset)
1130
+
1131
+ return await query.selectAll().execute()
1132
+ }
1133
+
1134
+ export async function whereIn(
1135
+ column: keyof ${modelName}Type,
1136
+ values: any[],
1137
+ options: QueryOptions = {},
1138
+ ) {
1139
+ let query = db.selectFrom('${tableName}')
1140
+
1141
+ query = query.where(column, 'in', values)
1142
+
1143
+ // Apply sorting from options
1144
+ if (options.sort)
1145
+ query = query.orderBy(options.sort.column, options.sort.order)
1146
+
1147
+ // Apply pagination from options
1148
+ if (options.limit !== undefined)
1149
+ query = query.limit(options.limit)
1150
+
1151
+ if (options.offset !== undefined)
1152
+ query = query.offset(options.offset)
1153
+
1154
+ return await query.selectAll().execute()
1155
+ }
1156
+
1157
+ export const ${modelName} = {
1158
+ find,
1159
+ findMany,
1160
+ get,
1161
+ count,
1162
+ all,
1163
+ create,
1164
+ update,
1165
+ remove,
1166
+ Model,
1167
+ first,
1168
+ last,
1169
+ recent,
1170
+ where,
1171
+ whereIn,
1172
+ model: ${modelName}Model
1173
+ }
1174
+
1175
+ export default ${modelName}
1176
+ `
1177
+ }