@stacksjs/actions 0.61.20 → 0.61.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/upgrade.js CHANGED
@@ -7,7 +7,7 @@ import {log} from "@stacksjs/logging";
7
7
  import {projectPath} from "@stacksjs/path";
8
8
  import * as storage from "@stacksjs/storage";
9
9
  // package.json
10
- var version = "0.61.20";
10
+ var version = "0.61.21";
11
11
 
12
12
  // src/upgrade.ts
13
13
  function checkForUncommittedChanges(options) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/actions",
3
3
  "type": "module",
4
- "version": "0.61.20",
4
+ "version": "0.61.21",
5
5
  "description": "The Stacks actions.",
6
6
  "author": "Chris Breuer",
7
7
  "license": "MIT",
@@ -1,11 +1,10 @@
1
1
  import { log } from '@stacksjs/logging'
2
- import { modelTableName } from '@stacksjs/orm'
3
2
  import { path } from '@stacksjs/path'
4
3
  import { fs, glob } from '@stacksjs/storage'
5
- import { pascalCase } from '@stacksjs/strings'
4
+ import { camelCase, pascalCase } from '@stacksjs/strings'
6
5
  import type { Model, RelationConfig } from '@stacksjs/types'
7
- import { isString } from '@stacksjs/validation'
8
-
6
+ import { isString, isBoolean } from '@stacksjs/validation'
7
+ import { getModelName, getTableName} from '@stacksjs/orm'
9
8
  export interface FieldArrayElement {
10
9
  entity: string
11
10
  charValue?: string | null
@@ -29,44 +28,47 @@ async function generateApiRoutes(modelFiles: string[]) {
29
28
 
30
29
  for (const modelFile of modelFiles) {
31
30
  log.debug(`Processing model file: ${modelFile}`)
32
-
31
+ let middlewareString = ''
33
32
  const model = (await import(modelFile)).default as Model
33
+ const modelName = getModelName(model, modelFile)
34
+ const tableName = getTableName(model, modelFile)
34
35
 
35
36
  if (model.traits?.useApi) {
36
37
  const apiRoutes = model.traits?.useApi?.routes
37
38
  const middlewares = model.traits.useApi?.middleware
38
- let middlewareString = `.middleware([`
39
-
40
- if (middlewares.length) {
41
- for (let i = 0; i < middlewares.length; i++) {
42
- middlewareString += `'${middlewares[i]}'`
43
-
44
- if (i < middlewares.length - 1) {
45
- middlewareString += ','
39
+ if (middlewares) {
40
+ middlewareString = `.middleware([`
41
+ if (middlewares.length) {
42
+ for (let i = 0; i < middlewares.length; i++) {
43
+ middlewareString += `'${middlewares[i]}'`
44
+
45
+ if (i < middlewares.length - 1) {
46
+ middlewareString += ','
47
+ }
46
48
  }
47
49
  }
48
- }
49
50
 
50
- middlewareString += `])`
51
+ middlewareString += `])`
52
+ }
51
53
 
52
54
  if (apiRoutes?.length) {
53
55
  for (const apiRoute of apiRoutes) {
54
- await writeOrmActions(apiRoute, model)
56
+ await writeOrmActions(apiRoute as string, modelName)
55
57
 
56
58
  if (apiRoute === 'index')
57
- routeString += `await route.get('${model.table}', 'Actions/${model.name}IndexOrmAction')${middlewareString}\n\n`
59
+ routeString += `await route.get('${tableName}', 'Actions/${modelName}IndexOrmAction')${middlewareString}\n\n`
58
60
 
59
61
  if (apiRoute === 'store')
60
- routeString += `await route.post('${model.table}', 'Actions/${model.name}StoreOrmAction')${middlewareString}\n\n`
62
+ routeString += `await route.post('${tableName}', 'Actions/${modelName}StoreOrmAction')${middlewareString}\n\n`
61
63
 
62
64
  if (apiRoute === 'update')
63
- routeString += `await route.patch('${model.table}/{id}', 'Actions/${model.name}UpdateOrmAction')${middlewareString}\n\n`
65
+ routeString += `await route.patch('${tableName}/{id}', 'Actions/${modelName}UpdateOrmAction')${middlewareString}\n\n`
64
66
 
65
67
  if (apiRoute === 'show')
66
- routeString += `await route.get('${model.table}/{id}', 'Actions/${model.name}ShowOrmAction')${middlewareString}\n\n`
68
+ routeString += `await route.get('${tableName}/{id}', 'Actions/${modelName}ShowOrmAction')${middlewareString}\n\n`
67
69
 
68
70
  if (apiRoute === 'destroy')
69
- routeString += `await route.delete('${model.table}/{id}', 'Actions/${model.name}DestroyOrmAction')${middlewareString}\n\n`
71
+ routeString += `await route.delete('${tableName}/{id}', 'Actions/${modelName}DestroyOrmAction')${middlewareString}\n\n`
70
72
  }
71
73
  }
72
74
  }
@@ -85,10 +87,11 @@ async function writeModelNames() {
85
87
  const modeFileElement = modelFiles[i] as string
86
88
 
87
89
  const model = (await import(modeFileElement)).default as Model
90
+ const modelName = getModelName(model, modeFileElement)
88
91
 
89
92
  const typeFile = Bun.file(path.projectStoragePath(`framework/core/types/src/model-names.ts`))
90
93
 
91
- fileString += `'${model.name}'`
94
+ fileString += `'${modelName}'`
92
95
 
93
96
  if (i < modelFiles.length - 1) {
94
97
  fileString += ' | '
@@ -100,18 +103,74 @@ async function writeModelNames() {
100
103
  }
101
104
  }
102
105
 
103
- async function writeOrmActions(apiRoute: string, model: Model): Promise<void> {
104
- const modelName = model.name
106
+ async function writeModelRequests() {
107
+ const modelFiles = glob.sync(path.userModelsPath('*.ts'))
108
+
109
+ for (let i = 0; i < modelFiles.length; i++) {
110
+ let fieldString = ``
111
+ let fieldStringInt = ``
112
+ let fileString = `import { Request } from '@stacksjs/router'\nimport { validateField } from '@stacksjs/validation'\nimport type { RequestInstance } from '@stacksjs/types'\n\n`
113
+
114
+ const modeFileElement = modelFiles[i] as string
115
+
116
+ const model = (await import(modeFileElement)).default as Model
117
+ const modelName = getModelName(model, modeFileElement)
118
+
119
+ const attributes = await extractFields(model, modeFileElement)
120
+
121
+ for (const attribute of attributes) {
122
+ 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
+
128
+ if (attribute.fieldArray?.entity === 'number')
129
+ defaultValue = 0
130
+
131
+ fieldString += ` ${attribute.field}: ${entity}\n `
132
+
133
+ fieldStringInt += `public ${attribute.field} = ${defaultValue}\n`
134
+ }
135
+
136
+ const modelLowerCase = camelCase(modelName)
137
+
138
+ const requestFile = Bun.file(path.projectStoragePath(`framework/requests/${modelName}Request.ts`))
139
+
140
+ fileString += `export interface ${modelName}RequestType extends RequestInstance{
141
+ validate(): void
142
+ ${fieldString}
143
+ }\n\n`
144
+
145
+ fileString += `export class ${modelName}Request extends Request implements ${modelName}RequestType {
146
+ ${fieldStringInt}
147
+ public validate(): void {
148
+ validateField('${modelName}', this.all())
149
+ }
150
+ }
151
+
152
+ export const ${modelLowerCase}Request = new ${modelName}Request()
153
+ `
154
+
155
+ const writer = requestFile.writer()
156
+
157
+ writer.write(fileString)
158
+ }
159
+ }
160
+
161
+ async function writeOrmActions(apiRoute: string, modelName: String): Promise<void> {
105
162
  const formattedApiRoute = apiRoute.charAt(0).toUpperCase() + apiRoute.slice(1)
106
163
  let method = 'GET'
107
164
  let actionString = `import { Action } from '@stacksjs/actions'\n`
108
- actionString += `import ${modelName} from '../src/models/${modelName}'\n\n`
109
- actionString += `import { request } from '@stacksjs/router'\n\n`
165
+ actionString += `import ${modelName} from '../src/models/${modelName}'\n`
110
166
 
111
167
  let handleString = ``
112
168
 
169
+
170
+ actionString += ` import type { ${modelName}RequestType } from '../../requests/${modelName}Request'\n\n`
171
+
113
172
  if (apiRoute === 'index') {
114
- handleString += `async handle() {
173
+ handleString += `async handle(request: ${modelName}RequestType) {
115
174
  return await ${modelName}.all()
116
175
  },`
117
176
 
@@ -119,7 +178,7 @@ async function writeOrmActions(apiRoute: string, model: Model): Promise<void> {
119
178
  }
120
179
 
121
180
  if (apiRoute === 'show') {
122
- handleString += `async handle() {
181
+ handleString += `async handle(request: ${modelName}RequestType) {
123
182
  const id = await request.getParam('id')
124
183
 
125
184
  return ${modelName}.findOrFail(Number(id))
@@ -129,7 +188,7 @@ async function writeOrmActions(apiRoute: string, model: Model): Promise<void> {
129
188
  }
130
189
 
131
190
  if (apiRoute === 'destroy') {
132
- handleString += `async handle() {
191
+ handleString += `async handle(request: ${modelName}RequestType) {
133
192
  const id = request.getParam('id')
134
193
 
135
194
  const model = await ${modelName}.findOrFail(Number(id))
@@ -143,7 +202,7 @@ async function writeOrmActions(apiRoute: string, model: Model): Promise<void> {
143
202
  }
144
203
 
145
204
  if (apiRoute === 'store') {
146
- handleString += `async handle() {
205
+ handleString += `async handle(request: ${modelName}RequestType) {
147
206
  const model = await ${modelName}.create(request.all())
148
207
 
149
208
  return model
@@ -153,7 +212,7 @@ async function writeOrmActions(apiRoute: string, model: Model): Promise<void> {
153
212
  }
154
213
 
155
214
  if (apiRoute === 'update') {
156
- handleString += `async handle() {
215
+ handleString += `async handle(request: ${modelName}RequestType) {
157
216
  const id = request.getParam('id')
158
217
 
159
218
  const model = await ${modelName}.findOrFail(Number(id))
@@ -179,32 +238,14 @@ async function writeOrmActions(apiRoute: string, model: Model): Promise<void> {
179
238
  writer.write(actionString)
180
239
  }
181
240
 
182
- async function writeApiRoutes(apiRoute: string, model: Model): Promise<string> {
183
- let routeString = ``
184
- const tableName = await modelTableName(model)
185
- const modelName = model.name
186
-
187
- if (apiRoute === 'index') routeString += `await route.get('${tableName}', 'Actions/${modelName}IndexOrmAction')\n\n`
188
-
189
- if (apiRoute === 'store') routeString += `await route.post('${tableName}', 'Actions/${modelName}StoreOrmAction')\n\n`
190
-
191
- if (apiRoute === 'update')
192
- routeString += `await route.patch('${tableName}/{id}', 'Actions/${modelName}UpdateOrmAction')\n\n`
193
-
194
- if (apiRoute === 'show')
195
- routeString += `await route.get('${tableName}/{id}', 'Actions/${modelName}ShowOrmAction')\n\n`
196
-
197
- if (apiRoute === 'destroy')
198
- routeString += `await route.delete('${tableName}/{id}', 'Actions/${modelName}DestroyOrmAction')\n\n`
199
-
200
- return routeString
201
- }
202
-
203
241
  async function initiateModelGeneration(): Promise<void> {
204
242
  await deleteExistingModels()
205
243
  await deleteExistingOrmActions()
206
244
  await deleteExistingModelNameTypes()
245
+ await deleteExistingModelRequests()
246
+
207
247
  await writeModelNames()
248
+ await writeModelRequests()
208
249
 
209
250
  const modelFiles = glob.sync(path.userModelsPath('*.ts'))
210
251
 
@@ -214,12 +255,12 @@ async function initiateModelGeneration(): Promise<void> {
214
255
  log.debug(`Processing model file: ${modelFile}`)
215
256
 
216
257
  const model = (await import(modelFile)).default as Model
217
- const tableName = await modelTableName(model)
218
- const modelName = path.basename(modelFile, '.ts')
258
+ const tableName = await getTableName(model, modelFile)
259
+ const modelName = getModelName(model, modelFile)
219
260
 
220
261
  const file = Bun.file(path.projectStoragePath(`framework/orm/src/models/${modelName}.ts`))
221
262
  const fields = await extractFields(model, modelFile)
222
- const classString = await generateModelString(tableName, model, fields)
263
+ const classString = await generateModelString(tableName, modelName, model, fields)
223
264
 
224
265
  const writer = file.writer()
225
266
  writer.write(classString)
@@ -227,7 +268,7 @@ async function initiateModelGeneration(): Promise<void> {
227
268
  }
228
269
  }
229
270
 
230
- async function getRelations(model: Model): Promise<RelationConfig[]> {
271
+ async function getRelations(model: Model, modelName: string): Promise<RelationConfig[]> {
231
272
  const relationsArray = ['hasOne', 'belongsTo', 'hasMany', 'belongsToMany', 'hasOneThrough']
232
273
 
233
274
  const relationships = []
@@ -245,7 +286,7 @@ async function getRelations(model: Model): Promise<RelationConfig[]> {
245
286
 
246
287
  const modelRelation = (await import(modelRelationPath)).default as Model
247
288
 
248
- const formattedModelName = model.name?.toLowerCase()
289
+ const formattedModelName = modelName.toLowerCase()
249
290
 
250
291
  relationships.push({
251
292
  relationship: relation,
@@ -297,14 +338,22 @@ async function deleteExistingModelNameTypes() {
297
338
  if (fs.existsSync(typeFile)) await Bun.$`rm ${typeFile}`
298
339
  }
299
340
 
341
+ async function deleteExistingModelRequests() {
342
+ const requestFiles = glob.sync(path.projectStoragePath(`framework/requests/*.ts`))
343
+
344
+ for (const requestFile of requestFiles) {
345
+ if (fs.existsSync(requestFile)) await Bun.$`rm ${requestFile}`
346
+ }
347
+ }
348
+
300
349
  async function setKyselyTypes() {
301
350
  let text = ``
302
351
  const modelFiles = glob.sync(path.userModelsPath('*.ts'))
303
352
 
304
353
  for (const modelFile of modelFiles) {
305
354
  const model = (await import(modelFile)).default as Model
306
- const tableName = await modelTableName(model)
307
- const modelName = model.name
355
+ const tableName = await getTableName(model, modelFile)
356
+ const modelName = getModelName(model, modelFile)
308
357
 
309
358
  const words = tableName.split('_')
310
359
 
@@ -318,8 +367,9 @@ async function setKyselyTypes() {
318
367
  let pivotFormatted = ''
319
368
  for (const modelFile of modelFiles) {
320
369
  const model = (await import(modelFile)).default as Model
321
- const pivotTables = await getPivotTables(model)
322
-
370
+ const modelName = getModelName(model, modelFile)
371
+ const pivotTables = await getPivotTables(model, modelName)
372
+
323
373
  for (const pivotTable of pivotTables) {
324
374
  const words = pivotTable.table.split('_')
325
375
 
@@ -337,8 +387,9 @@ async function setKyselyTypes() {
337
387
 
338
388
  for (const modelFile of modelFiles) {
339
389
  const model = (await import(modelFile)).default as Model
340
- const tableName = await modelTableName(model)
341
- const pivotTables = await getPivotTables(model)
390
+ const modelName = getModelName(model, modelFile)
391
+ const tableName = await getTableName(model, modelFile)
392
+ const pivotTables = await getPivotTables(model, modelName)
342
393
 
343
394
  for (const pivotTable of pivotTables) text += ` ${pivotTable.table}: ${pivotFormatted}\n`
344
395
 
@@ -453,6 +504,7 @@ function getRelationCount(relation: string): string {
453
504
 
454
505
  async function getPivotTables(
455
506
  model: Model,
507
+ modelName: string
456
508
  ): Promise<{ table: string; firstForeignKey?: string; secondForeignKey?: string }[]> {
457
509
  const pivotTable = []
458
510
 
@@ -461,10 +513,10 @@ async function getPivotTables(
461
513
  for (const belongsToManyRelation of belongsToManyArr) {
462
514
  const modelRelationPath = path.userModelsPath(`${belongsToManyRelation}.ts`)
463
515
  const modelRelation = (await import(modelRelationPath)).default as Model
464
- const formattedModelName = model?.name?.toLowerCase()
516
+ const formattedModelName = modelName.toLowerCase()
465
517
 
466
518
  const firstForeignKey =
467
- belongsToManyRelation.firstForeignKey || `${model.name?.toLowerCase()}_${model.primaryKey}`
519
+ belongsToManyRelation.firstForeignKey || `${modelName.toLowerCase()}_${model.primaryKey}`
468
520
  const secondForeignKey =
469
521
  belongsToManyRelation.secondForeignKey || `${modelRelation.name?.toLowerCase()}_${model.primaryKey}`
470
522
 
@@ -481,21 +533,23 @@ async function getPivotTables(
481
533
  return []
482
534
  }
483
535
 
484
- export async function fetchOtherModelRelations(model: Model): Promise<RelationConfig[]> {
536
+ export async function fetchOtherModelRelations(model: Model, modelName: string): Promise<RelationConfig[]> {
485
537
  const modelFiles = glob.sync(path.userModelsPath('*.ts'))
486
538
  const modelRelations = []
487
539
 
488
540
  for (let i = 0; i < modelFiles.length; i++) {
489
541
  const modelFileElement = modelFiles[i] as string
490
542
  const modelFile = await import(modelFileElement)
543
+
544
+ if (modelName === modelFile.default.name) continue
491
545
 
492
- if (model.name === modelFile.default.name) continue
546
+ const otherModelName = getModelName(modelFile, modelFileElement)
493
547
 
494
- const relations = await getRelations(modelFile.default)
548
+ const relations = await getRelations(modelFile.default, otherModelName)
495
549
 
496
550
  if (!relations.length) continue
497
551
 
498
- const relation = relations.find((relation) => relation.model === model.name)
552
+ const relation = relations.find((relation) => relation.model === modelName)
499
553
 
500
554
  if (relation) modelRelations.push(relation)
501
555
  }
@@ -503,16 +557,15 @@ export async function fetchOtherModelRelations(model: Model): Promise<RelationCo
503
557
  return modelRelations
504
558
  }
505
559
 
506
- async function generateModelString(tableName: string, model: Model, attributes: ModelElement[]): Promise<string> {
507
- const modelName = model.name
560
+ async function generateModelString(tableName: string, modelName: string, model: Model, attributes: ModelElement[]): Promise<string> {
508
561
  const formattedTableName = pascalCase(tableName) // users -> Users
509
- const formattedModelName = modelName?.toLowerCase() // User -> user
562
+ const formattedModelName = modelName.toLowerCase() // User -> user
510
563
 
511
564
  let fieldString = ''
512
565
  let relationMethods = ``
513
566
  let relationImports = ``
514
567
 
515
- const relations = await getRelations(model)
568
+ const relations = await getRelations(model, modelName)
516
569
 
517
570
  for (const relationInstance of relations) {
518
571
  relationImports += `import ${relationInstance.model} from './${relationInstance.model}'\n\n`
@@ -636,7 +689,7 @@ async function generateModelString(tableName: string, model: Model, attributes:
636
689
 
637
690
  for (const attribute of attributes) fieldString += ` ${attribute.field}: ${attribute.fieldArray?.entity}\n `
638
691
 
639
- const otherModelRelations = await fetchOtherModelRelations(model)
692
+ const otherModelRelations = await fetchOtherModelRelations(model, modelName)
640
693
 
641
694
  for (const otherModelRelation of otherModelRelations) fieldString += ` ${otherModelRelation.foreignKey}: number \n`
642
695
 
@@ -808,7 +861,20 @@ async function generateModelString(tableName: string, model: Model, attributes:
808
861
  return new ${modelName}Model(model)
809
862
  }
810
863
 
811
- async where(column: string, operator = '=', value: any): Promise<${modelName}Type[]> {
864
+ async where(...args: (string | number)[]): Promise<${modelName}Type[]> {
865
+ let column: any
866
+ let operator: any
867
+ let value: any
868
+
869
+ if (args.length === 2) {
870
+ [column, value] = args
871
+ operator = '='
872
+ } else if (args.length === 3) {
873
+ [column, operator, value] = args
874
+ } else {
875
+ throw new Error("Invalid number of arguments")
876
+ }
877
+
812
878
  let query = db.selectFrom('${tableName}')
813
879
 
814
880
  query = query.where(column, operator, value)
@@ -861,6 +927,7 @@ async function generateModelString(tableName: string, model: Model, attributes:
861
927
  }
862
928
 
863
929
  async whereIn(column: keyof ${modelName}Type, values: any[], options: QueryOptions = {}): Promise<${modelName}Type[]> {
930
+
864
931
  let query = db.selectFrom('${tableName}')
865
932
 
866
933
  query = query.where(column, 'in', values)
@@ -1142,7 +1209,20 @@ async function generateModelString(tableName: string, model: Model, attributes:
1142
1209
  .executeTakeFirst()
1143
1210
  }
1144
1211
 
1145
- export async function where(column: string, operator = '=', value: any) {
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
+
1146
1226
  let query = db.selectFrom('${tableName}')
1147
1227
 
1148
1228
  query = query.where(column, operator, value)