@stacksjs/router 0.64.6 → 0.65.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.
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@stacksjs/router",
3
3
  "type": "module",
4
- "version": "0.64.6",
4
+ "version": "0.65.0",
5
5
  "description": "The Stacks framework router.",
6
6
  "author": "Chris Breuer",
7
+ "contributors": ["Chris Breuer <chris@stacksjs.org>"],
7
8
  "license": "MIT",
8
9
  "funding": "https://github.com/sponsors/chrisbbreuer",
9
10
  "homepage": "https://github.com/stacksjs/stacks/tree/main/storage/framework/core/router#readme",
@@ -15,13 +16,7 @@
15
16
  "bugs": {
16
17
  "url": "https://github.com/stacksjs/stacks/issues"
17
18
  },
18
- "keywords": [
19
- "router",
20
- "stacks",
21
- "framework",
22
- "typescript",
23
- "javascript"
24
- ],
19
+ "keywords": ["router", "stacks", "framework", "typescript", "javascript"],
25
20
  "exports": {
26
21
  ".": {
27
22
  "bun": "./src/index.ts",
@@ -34,27 +29,27 @@
34
29
  },
35
30
  "module": "dist/index.js",
36
31
  "types": "dist/index.d.ts",
37
- "contributors": [
38
- "Chris Breuer <chris@stacksjs.org>"
39
- ],
40
- "files": [
41
- "README.md",
42
- "dist",
43
- "src"
44
- ],
32
+ "files": ["README.md", "dist", "src"],
45
33
  "scripts": {
46
- "build": "bun --bun build.ts",
47
- "build:user": "bun --bun build2.ts",
48
- "typecheck": "bun --bun tsc --noEmit",
49
- "prepublishOnly": "bun --bun run build"
34
+ "build": "bun build.ts",
35
+ "build:user": "bun build2.ts",
36
+ "typecheck": "bun tsc --noEmit",
37
+ "prepublishOnly": "bun run build"
50
38
  },
51
39
  "dependencies": {
52
- "@stacksjs/config": "latest",
53
- "@stacksjs/logging": "latest",
54
- "unplugin-vue-router": "^0.10.7",
55
- "vue-router": "^4.4.3"
40
+ "@stacksjs/actions": "0.64.6",
41
+ "@stacksjs/config": "0.64.6",
42
+ "@stacksjs/error-handling": "0.64.6",
43
+ "@stacksjs/logging": "0.64.6",
44
+ "@stacksjs/orm": "0.64.6",
45
+ "@stacksjs/path": "0.64.6",
46
+ "@stacksjs/storage": "0.64.6",
47
+ "@stacksjs/validation": "0.64.6",
48
+ "unplugin-vue-router": "^0.10.8",
49
+ "vue-router": "^4.4.5"
56
50
  },
57
51
  "devDependencies": {
58
- "@stacksjs/development": "latest"
52
+ "@stacksjs/development": "0.64.6",
53
+ "@stacksjs/types": "0.64.6"
59
54
  }
60
55
  }
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export * from './middleware'
2
2
  export * from './request'
3
- export * from './server'
4
3
  export * from './router'
4
+ export * from './server'
5
5
  export * from './utils'
package/src/middleware.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { userMiddlewarePath } from '@stacksjs/path'
2
1
  import type { MiddlewareOptions } from '@stacksjs/types'
2
+ import { userMiddlewarePath } from '@stacksjs/path'
3
3
 
4
4
  export class Middleware implements MiddlewareOptions {
5
5
  name: string
6
6
  priority: number
7
- handle: Function
7
+ handle: () => Promise<void>
8
8
 
9
9
  constructor(data: MiddlewareOptions) {
10
10
  this.name = data.name
@@ -15,7 +15,7 @@ export class Middleware implements MiddlewareOptions {
15
15
 
16
16
  // const readdir = promisify(fs.readdir)
17
17
 
18
- async function importMiddlewares(directory: string) {
18
+ async function importMiddlewares(directory: string): Promise<string[]> {
19
19
  // const middlewares = []
20
20
  // TODO: somehow this breaks ./buddy dev
21
21
  // const files = await readdir(directory)
@@ -30,6 +30,6 @@ async function importMiddlewares(directory: string) {
30
30
  return [directory] // fix this: return array of middlewares
31
31
  }
32
32
 
33
- export const middlewares = async () => {
33
+ export async function middlewares(): Promise<string[]> {
34
34
  return await importMiddlewares(userMiddlewarePath())
35
35
  }
package/src/request.ts CHANGED
@@ -1,6 +1,5 @@
1
- import type { RequestInstance, RouteParam } from '@stacksjs/types'
1
+ import type { RequestInstance, RouteParam, VineType } from '@stacksjs/types'
2
2
 
3
- import type { VineType } from '@stacksjs/types'
4
3
  import { customValidate, validateField } from '@stacksjs/validation'
5
4
 
6
5
  interface RequestData {
@@ -12,6 +11,8 @@ interface ValidationField {
12
11
  message: Record<string, string>
13
12
  }
14
13
 
14
+ type AuthToken = `${number}:${number}:${string}`
15
+
15
16
  interface CustomAttributes {
16
17
  [key: string]: ValidationField
17
18
  }
@@ -39,7 +40,7 @@ export class Request<T extends RequestData = RequestData> implements RequestInst
39
40
  this.headers = headerParams
40
41
  }
41
42
 
42
- public get(element: string): string | number | undefined {
43
+ public get(element: string): any {
43
44
  return this.query[element]
44
45
  }
45
46
 
@@ -50,7 +51,8 @@ export class Request<T extends RequestData = RequestData> implements RequestInst
50
51
  public async validate(attributes?: CustomAttributes): Promise<void> {
51
52
  if (attributes === undefined || attributes === null) {
52
53
  await validateField('Release', this.all())
53
- } else {
54
+ }
55
+ else {
54
56
  await customValidate(attributes, this.all())
55
57
  }
56
58
  }
@@ -67,14 +69,15 @@ export class Request<T extends RequestData = RequestData> implements RequestInst
67
69
  const pattern = new RegExp(`^${routePattern.replace(/:(\w+)/g, (match, paramName) => `(?<${paramName}>\\w+)`)}$`)
68
70
  const match = pattern.exec(pathname)
69
71
 
70
- if (match?.groups) this.params = match.groups
72
+ if (match?.groups)
73
+ this.params = match.groups
71
74
  }
72
75
 
73
76
  public header(headerParam: string): string | number | boolean | null {
74
77
  return this.headers.get(headerParam)
75
78
  }
76
79
 
77
- public getHeaders() {
80
+ public getHeaders(): any {
78
81
  return this.headers
79
82
  }
80
83
 
@@ -86,7 +89,7 @@ export class Request<T extends RequestData = RequestData> implements RequestInst
86
89
  return this.params ? this.params[key] || null : null
87
90
  }
88
91
 
89
- public bearerToken(): string | null {
92
+ public bearerToken(): string | null | AuthToken {
90
93
  const authorizationHeader = this.headers.get('authorization')
91
94
 
92
95
  if (authorizationHeader?.startsWith('Bearer ')) {
@@ -106,4 +109,4 @@ export class Request<T extends RequestData = RequestData> implements RequestInst
106
109
  }
107
110
  }
108
111
 
109
- export const request = new Request()
112
+ export const request: Request = new Request()
package/src/router.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import type { Action } from '@stacksjs/actions'
2
+ import type { Job, RedirectCode, RequestInstance, Route, RouteGroupOptions, RouterInterface, StatusCode } from '@stacksjs/types'
3
+ import { handleError } from '@stacksjs/error-handling'
2
4
  import { log } from '@stacksjs/logging'
3
5
  import { path as p } from '@stacksjs/path'
4
6
  import { kebabCase, pascalCase } from '@stacksjs/strings'
5
- import type { Job } from '@stacksjs/types'
6
- import type { RedirectCode, Route, RouteGroupOptions, RouterInterface, StatusCode } from '@stacksjs/types'
7
7
  import { customValidate, isObjectNotEmpty } from '@stacksjs/validation'
8
8
  import { extractDefaultRequest, findRequestInstance } from './utils'
9
9
 
@@ -11,7 +11,6 @@ type ActionPath = string // TODO: narrow this by automating its generation
11
11
 
12
12
  export class Router implements RouterInterface {
13
13
  private routes: Route[] = []
14
- private apiPrefix = '/api'
15
14
  private groupPrefix = ''
16
15
  private path = ''
17
16
 
@@ -23,7 +22,7 @@ export class Router implements RouterInterface {
23
22
  ): this {
24
23
  const name = uri.replace(/\//g, '.').replace(/:/g, '') // we can improve this
25
24
  const pattern = new RegExp(
26
- `^${uri.replace(/:[a-zA-Z]+/g, (_match) => {
25
+ `^${uri.replace(/:[a-z]+/gi, (_match) => {
27
26
  return '([a-zA-Z0-9-]+)'
28
27
  })}$`,
29
28
  )
@@ -78,7 +77,7 @@ export class Router implements RouterInterface {
78
77
  public async health(): Promise<this> {
79
78
  const healthModule = (await import(p.userActionsPath('HealthAction'))).default as Action
80
79
  const callback = healthModule.handle
81
- const path = healthModule.path ?? `${this.apiPrefix}/health`
80
+ const path = healthModule.path ?? `/health`
82
81
 
83
82
  this.addRoute('GET', path, callback, 200)
84
83
 
@@ -95,7 +94,8 @@ export class Router implements RouterInterface {
95
94
  }
96
95
 
97
96
  public async action(path: ActionPath | Route['path']): Promise<this> {
98
- if (!path) return this
97
+ if (!path)
98
+ return this
99
99
 
100
100
  // check if action is a file anywhere in ./app/Actions/**/*.ts
101
101
  if (path?.endsWith('.ts')) {
@@ -111,8 +111,9 @@ export class Router implements RouterInterface {
111
111
  const action = (await import(p.userActionsPath(path))).default as Action
112
112
 
113
113
  return this.addRoute(action.method ?? 'GET', this.prepareUri(path), action.handle, 200)
114
- } catch (error) {
115
- log.error(`Could not find Action for path: ${path}`)
114
+ }
115
+ catch (error) {
116
+ handleError(`Could not find Action for path: ${path}`, error)
116
117
 
117
118
  return this
118
119
  }
@@ -161,7 +162,8 @@ export class Router implements RouterInterface {
161
162
  }
162
163
 
163
164
  public group(options: string | RouteGroupOptions, callback?: () => void): this {
164
- if (typeof options === 'string') options = options.startsWith('/') ? options.slice(1) : options
165
+ if (typeof options === 'string')
166
+ options = options.startsWith('/') ? options.slice(1) : options
165
167
 
166
168
  let cb: () => void
167
169
 
@@ -172,7 +174,8 @@ export class Router implements RouterInterface {
172
174
  options = {}
173
175
  }
174
176
 
175
- if (!callback) throw new Error('Missing callback function for your route group.')
177
+ if (!callback)
178
+ throw new Error('Missing callback function for your route group.')
176
179
 
177
180
  cb = callback
178
181
 
@@ -191,7 +194,8 @@ export class Router implements RouterInterface {
191
194
  this.routes.forEach((r) => {
192
195
  r.uri = `${prefix}${r.uri}`
193
196
 
194
- if (middleware.length) r.middleware = middleware
197
+ if (middleware.length)
198
+ r.middleware = middleware
195
199
 
196
200
  originalRoutes.push(r)
197
201
  return this
@@ -204,14 +208,12 @@ export class Router implements RouterInterface {
204
208
  }
205
209
 
206
210
  public name(name: string): this {
207
- // @ts-expect-error - this is fine for now
208
211
  this.routes[this.routes.length - 1].name = name
209
212
 
210
213
  return this
211
214
  }
212
215
 
213
216
  public middleware(middleware: Route['middleware']): this {
214
- // @ts-expect-error - this is fine for now
215
217
  this.routes[this.routes.length - 1].middleware = middleware
216
218
 
217
219
  return this
@@ -255,7 +257,6 @@ export class Router implements RouterInterface {
255
257
  }
256
258
 
257
259
  this.setGroupPrefix('', options)
258
- return
259
260
  }
260
261
 
261
262
  public async resolveCallback(callback: Route['callback']): Promise<Route['callback']> {
@@ -264,7 +265,8 @@ export class Router implements RouterInterface {
264
265
  return actionModule.default
265
266
  }
266
267
 
267
- if (typeof callback === 'string') return await this.importCallbackFromPath(callback, this.path)
268
+ if (typeof callback === 'string')
269
+ return await this.importCallbackFromPath(callback, this.path)
268
270
 
269
271
  // in this case, the callback ends up being a function
270
272
  return callback
@@ -274,18 +276,22 @@ export class Router implements RouterInterface {
274
276
  let modulePath = callbackPath
275
277
  let importPathFunction = p.appPath // Default import path function
276
278
 
277
- if (callbackPath.startsWith('../')) importPathFunction = p.routesPath
278
- if (modulePath.includes('OrmAction')) importPathFunction = p.storagePath
279
+ if (callbackPath.startsWith('../'))
280
+ importPathFunction = p.routesPath
281
+ if (modulePath.includes('OrmAction'))
282
+ importPathFunction = p.storagePath
279
283
 
280
284
  // Remove trailing .ts if present
281
285
  modulePath = modulePath.endsWith('.ts') ? modulePath.slice(0, -3) : modulePath
282
286
 
283
287
  let actionModule = null
284
288
 
285
- if (modulePath.includes('storage/framework/orm')) actionModule = await import(modulePath)
286
- else if (modulePath.includes('app/Actions')) actionModule = await import(modulePath)
289
+ if (modulePath.includes('storage/framework/orm'))
290
+ actionModule = await import(modulePath)
291
+ else if (modulePath.includes('app/Actions'))
292
+ actionModule = await import(modulePath)
287
293
  else if (modulePath.includes('OrmAction'))
288
- actionModule = await import(p.storagePath(`/framework/orm/${modulePath}`))
294
+ actionModule = await import(p.storagePath(`/framework/actions/src/${modulePath}.ts`))
289
295
  else actionModule = await import(importPathFunction(modulePath))
290
296
 
291
297
  // Use custom path from action module if available
@@ -299,21 +305,27 @@ export class Router implements RouterInterface {
299
305
  // then validate
300
306
  // if succeeds, run the handle
301
307
  // if fails, return validation error
302
- let requestInstance
308
+ let requestInstance: RequestInstance
303
309
 
304
310
  if (actionModule.default.requestFile) {
305
311
  requestInstance = await findRequestInstance(actionModule.default.requestFile)
306
- } else {
307
- requestInstance = await extractDefaultRequest(modulePath)
312
+ }
313
+ else {
314
+ requestInstance = await extractDefaultRequest()
308
315
  }
309
316
 
310
317
  try {
311
- if (isObjectNotEmpty(actionModule.default.validations))
318
+ if (isObjectNotEmpty(actionModule.default.validations) && requestInstance)
312
319
  await customValidate(actionModule.default.validations, requestInstance.all())
313
320
 
314
321
  return await actionModule.default.handle(requestInstance)
315
- } catch (error: any) {
316
- return { status: error.status, errors: error.errors }
322
+ }
323
+ catch (error: any) {
324
+ console.log(error)
325
+ if (!error.status)
326
+ return { status: 500, errors: error }
327
+
328
+ return { status: error.status, errors: error }
317
329
  }
318
330
  }
319
331
 
@@ -321,11 +333,12 @@ export class Router implements RouterInterface {
321
333
  return path.endsWith('/') ? path.slice(0, -1) : path
322
334
  }
323
335
 
324
- public prepareUri(path: string) {
336
+ public prepareUri(path: string): string {
325
337
  // if string starts with / then remove it because we are adding it back in the next line
326
- if (path.startsWith('/')) path = path.slice(1)
338
+ if (path.startsWith('/'))
339
+ path = path.slice(1)
327
340
 
328
- path = `${this.apiPrefix}${this.groupPrefix}/${path}`
341
+ path = `${this.groupPrefix}/${path}`
329
342
 
330
343
  // if path ends in "/", then remove it
331
344
  // e.g. triggered when route is "/"
@@ -340,4 +353,4 @@ export class Router implements RouterInterface {
340
353
  }
341
354
  }
342
355
 
343
- export const route = new Router()
356
+ export const route: Router = new Router()