rajt 0.0.58 → 0.0.59
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 +7 -7
- package/src/action-old.ts +72 -0
- package/src/action.ts +12 -61
- package/src/auth/ability.ts +2 -2
- package/src/auth/authnz.ts +3 -0
- package/src/auth/index.ts +0 -1
- package/src/auth/token.ts +27 -24
- package/src/bin/rajt.js +2 -2
- package/src/cli/commands/dev/index.ts +2 -2
- package/src/cli/commands/dev/utils.ts +12 -3
- package/src/create-app.ts +7 -7
- package/src/db/d1.d.ts +1 -0
- package/src/db/d1.ts +1 -0
- package/src/enum.ts +2 -0
- package/src/http.ts +6 -5
- package/src/index.ts +0 -2
- package/src/request-old.ts +98 -0
- package/src/request.ts +152 -71
- package/src/response.ts +71 -26
- package/src/routes.ts +66 -2
- package/src/scripts/cache-routes.ts +1 -64
- package/src/types.ts +24 -4
- package/src/utils/func.ts +1 -1
- package/src/utils/resolve.ts +2 -1
- package/src/validator.ts +68 -0
- package/src/auth/auth.ts +0 -33
- package/src/dynamodb/client.ts +0 -125
- package/src/dynamodb/compact.ts +0 -205
- package/src/dynamodb/decorators.ts +0 -126
- package/src/dynamodb/index.ts +0 -4
- package/src/dynamodb/model.ts +0 -258
- package/src/dynamodb/query-builder.ts +0 -174
- package/src/dynamodb/repository.ts +0 -31
- package/src/dynamodb/schema.ts +0 -107
- package/src/dynamodb/types.ts +0 -32
package/src/request.ts
CHANGED
|
@@ -1,98 +1,179 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getCookie, getSignedCookie, setCookie, setSignedCookie, deleteCookie } from 'hono/cookie'
|
|
2
2
|
import { HTTPException } from 'hono/http-exception'
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
throw new HTTPException(400, {
|
|
31
|
-
message: 'Malformed FormData request.'
|
|
32
|
-
+ (e instanceof Error ? ` ${e.message}` : ` ${String(e)}`)
|
|
33
|
-
})
|
|
34
|
-
}
|
|
35
|
-
}
|
|
3
|
+
import { Authnz, Token } from './auth'
|
|
4
|
+
|
|
5
|
+
import type { Context } from 'hono'
|
|
6
|
+
import type { CookieOptions, CookiePrefixOptions } from 'hono/utils/cookie'
|
|
7
|
+
import type { CustomHeader, RequestHeader } from 'hono/utils/headers'
|
|
8
|
+
import type { BodyData, ParseBodyOptions } from 'hono/utils/body'
|
|
9
|
+
|
|
10
|
+
const cookieWrapper = (c: Context) => ({
|
|
11
|
+
all: () => getCookie(c),
|
|
12
|
+
allSigned: (secret: string) => getSignedCookie(c, secret),
|
|
13
|
+
get: (name: string, prefixOptions?: CookiePrefixOptions) => prefixOptions ? getCookie(c, name, prefixOptions) : getCookie(c, name),
|
|
14
|
+
getSigned: (secret: string, name: string, prefixOptions?: CookiePrefixOptions) => prefixOptions ? getSignedCookie(c, secret, name, prefixOptions) : getSignedCookie(c, secret, name),
|
|
15
|
+
set: (name: string, value: string, opt?: CookieOptions) => setCookie(c, name, value, opt),
|
|
16
|
+
setSigned: (name: string, value: string, secret: string, opt?: CookieOptions) => setSignedCookie(c, name, value, secret, opt),
|
|
17
|
+
delete: (name: string, opt?: CookieOptions) => deleteCookie(c, name, opt)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
export default class $Request {
|
|
21
|
+
#c!: Context
|
|
22
|
+
#cookie: ReturnType<typeof cookieWrapper>
|
|
23
|
+
#u: Authnz<any> | null = null
|
|
24
|
+
|
|
25
|
+
constructor(c: Context) {
|
|
26
|
+
this.#c = c
|
|
27
|
+
this.#cookie = cookieWrapper(c)
|
|
28
|
+
this.#u = Authnz.fromToken(Token.fromRequest(this))
|
|
29
|
+
}
|
|
36
30
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
;((form[key] ??= []) as unknown[]).push(value)
|
|
41
|
-
} else if (Array.isArray(form[key])) {
|
|
42
|
-
;(form[key] as unknown[]).push(value)
|
|
43
|
-
} else if (key in form) {
|
|
44
|
-
form[key] = [form[key] as string | File, value]
|
|
45
|
-
} else {
|
|
46
|
-
form[key] = value
|
|
47
|
-
}
|
|
48
|
-
})
|
|
31
|
+
get user() {
|
|
32
|
+
return this.#u ? this.#u?.data : null
|
|
33
|
+
}
|
|
49
34
|
|
|
50
|
-
|
|
35
|
+
get auth() {
|
|
36
|
+
return this.#u
|
|
51
37
|
}
|
|
52
38
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
39
|
+
can(...abilities: string[]) {
|
|
40
|
+
return this.#u ? this.#u.can(...abilities) : false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
cant(...abilities: string[]) {
|
|
44
|
+
return !this.can(...abilities)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
hasRole(...roles: string[]) {
|
|
48
|
+
return this.#u ? this.#u.hasRole(...roles) : false
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
has(prop: string, value: any = null) {
|
|
52
|
+
return this.#u ? this.#u.has(prop, value) : false
|
|
53
|
+
}
|
|
54
|
+
hasValue(prop: string, value: any = null) {
|
|
55
|
+
return this.has(prop, value)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get cx() {
|
|
59
|
+
return this.#c
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get cookie() {
|
|
63
|
+
return this.#cookie
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get ip(): string | undefined {
|
|
67
|
+
return this.#c.req.header('cf-connecting-ip')
|
|
68
|
+
|| this.#c.req.header('x-forwarded-for')?.split(',')[0]?.trim()
|
|
69
|
+
|| this.#c.env?.aws?.lambda?.event?.requestContext?.identity?.sourceIp
|
|
70
|
+
|| this.#c.req.header('x-real-ip')
|
|
71
|
+
|| this.#c.env?.remoteAddr?.hostname
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
get userAgent(): string | undefined {
|
|
75
|
+
return this.#c.req.header('user-agent')
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get routePath() {
|
|
79
|
+
return this.#c.req.routePath
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get path() {
|
|
83
|
+
return this.#c.req.path
|
|
59
84
|
}
|
|
60
85
|
|
|
61
|
-
|
|
62
|
-
|
|
86
|
+
get url() {
|
|
87
|
+
return this.#c.req.raw.url
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
get method() {
|
|
91
|
+
return this.#c.req.raw.method
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
get matchedRoutes() {
|
|
95
|
+
return this.#c.req.matchedRoutes
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
get raw() {
|
|
99
|
+
return this.#c.req.raw
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
header(name: RequestHeader): string | undefined
|
|
103
|
+
header(name: string): string | undefined
|
|
104
|
+
header(): Record<RequestHeader | (string & CustomHeader), string>
|
|
105
|
+
header(name?: string) { // @ts-ignore
|
|
106
|
+
return this.#c.req.header(name)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
param(key?: string) { // @ts-ignore
|
|
110
|
+
return this.#c.req.param(key)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
query(): Record<string, string>
|
|
114
|
+
query(key: string): string | undefined
|
|
115
|
+
query(key?: string) { // @ts-ignore
|
|
116
|
+
return this.#c.req.query(key)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
queries(): Record<string, string[]>
|
|
120
|
+
queries(key: string):string[] | undefined
|
|
121
|
+
queries(key?: string) { // @ts-ignore
|
|
122
|
+
return this.#c.req.queries(key)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async body<E>() {
|
|
126
|
+
const cType = this.#c.req.header('Content-Type')
|
|
63
127
|
if (!cType) return {} as E
|
|
64
128
|
|
|
65
|
-
if (/^application\/([a-z-\.]+\+)?json(;\s*[a-zA-Z0-9\-]+\=([^;]+))*$/.test(cType))
|
|
66
|
-
return await
|
|
67
|
-
}
|
|
129
|
+
if (/^application\/([a-z-\.]+\+)?json(;\s*[a-zA-Z0-9\-]+\=([^;]+))*$/.test(cType))
|
|
130
|
+
return await this.json<E>()
|
|
68
131
|
|
|
69
132
|
if (
|
|
70
|
-
|
|
71
|
-
|
|
133
|
+
cType?.startsWith('multipart/form-data')
|
|
134
|
+
|| cType?.startsWith('application/x-www-form-urlencoded')
|
|
72
135
|
) {
|
|
73
|
-
|
|
136
|
+
return await this.parseBody() as E
|
|
74
137
|
}
|
|
75
138
|
|
|
76
139
|
return {} as E
|
|
77
140
|
}
|
|
78
141
|
|
|
79
|
-
|
|
80
|
-
|
|
142
|
+
async parseBody<Options extends Partial<ParseBodyOptions>, T extends BodyData<Options>>(
|
|
143
|
+
options?: Options
|
|
144
|
+
): Promise<T>
|
|
145
|
+
async parseBody<T extends BodyData>(options?: Partial<ParseBodyOptions>): Promise<T>
|
|
146
|
+
async parseBody(options?: Partial<ParseBodyOptions>) {
|
|
147
|
+
try {
|
|
148
|
+
return await this.#c.req.parseBody(options)
|
|
149
|
+
} catch (e) {
|
|
150
|
+
throw new HTTPException(400, {
|
|
151
|
+
message: 'Malformed FormData request.'+ (e instanceof Error ? ` ${e.message}` : ` ${String(e)}`)
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async json<E>() {
|
|
157
|
+
try {
|
|
158
|
+
return await this.#c.req.json<E>()
|
|
159
|
+
} catch {
|
|
160
|
+
throw new HTTPException(400, { message: 'Malformed JSON in request body' })
|
|
161
|
+
}
|
|
81
162
|
}
|
|
82
163
|
|
|
83
|
-
|
|
84
|
-
return c.
|
|
164
|
+
text() {
|
|
165
|
+
return this.#c.req.text()
|
|
85
166
|
}
|
|
86
167
|
|
|
87
|
-
|
|
88
|
-
return c.
|
|
168
|
+
arrayBuffer() {
|
|
169
|
+
return this.#c.req.arrayBuffer()
|
|
89
170
|
}
|
|
90
171
|
|
|
91
|
-
|
|
92
|
-
return c.
|
|
172
|
+
blob() {
|
|
173
|
+
return this.#c.req.blob()
|
|
93
174
|
}
|
|
94
175
|
|
|
95
|
-
|
|
96
|
-
return c.
|
|
176
|
+
formData() {
|
|
177
|
+
return this.#c.req.formData()
|
|
97
178
|
}
|
|
98
179
|
}
|
package/src/response.ts
CHANGED
|
@@ -1,37 +1,88 @@
|
|
|
1
|
-
import type { ContentfulStatusCode, StatusCode } from 'hono/utils/http-status'
|
|
1
|
+
import type { ContentfulStatusCode, RedirectStatusCode, StatusCode } from 'hono/utils/http-status'
|
|
2
|
+
import type { BaseMime } from 'hono/utils/mime'
|
|
3
|
+
import type { ResponseHeader } from 'hono/utils/headers'
|
|
2
4
|
import type { ErrorResponse, Errors } from './types'
|
|
3
|
-
import
|
|
5
|
+
import { HtmlEscapedCallbackPhase, resolveCallback } from 'hono/utils/html'
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
type HeaderRecord =
|
|
8
|
+
| Record<'Content-Type', BaseMime>
|
|
9
|
+
| Record<ResponseHeader, string | string[]>
|
|
10
|
+
| Record<string, string | string[]>
|
|
11
|
+
|
|
12
|
+
type RBag = {
|
|
13
|
+
status?: StatusCode,
|
|
14
|
+
headers?: HeaderRecord,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default class $Response {
|
|
18
|
+
static raw(
|
|
19
|
+
status?: StatusCode,
|
|
20
|
+
body?: any,
|
|
21
|
+
cType?: BaseMime,
|
|
22
|
+
headers?: HeaderRecord
|
|
23
|
+
) {
|
|
24
|
+
const b: RBag = { status: status || 200 }
|
|
25
|
+
|
|
26
|
+
if (cType || headers) {
|
|
27
|
+
headers ??= {}
|
|
28
|
+
if (cType) headers['Content-Type'] = cType
|
|
29
|
+
b.headers = headers
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return new Response(body ?? null, b)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static text(data?: string, status?: StatusCode) {
|
|
36
|
+
return this.raw(status, data, 'text/plain; charset=UTF-8' as BaseMime)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static json<T>(data?: T, status?: StatusCode, headers?: HeaderRecord) {
|
|
40
|
+
if (data == null)
|
|
41
|
+
return this.raw(status)
|
|
42
|
+
|
|
43
|
+
return this.raw(status, JSON.stringify(data), 'application/json', headers)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static redirect(
|
|
47
|
+
location: string | URL,
|
|
48
|
+
status?: RedirectStatusCode,
|
|
49
|
+
headers?: HeaderRecord
|
|
50
|
+
) {
|
|
51
|
+
const loc = String(location)
|
|
52
|
+
|
|
53
|
+
return new Response(null, {
|
|
54
|
+
status: status || 302,
|
|
55
|
+
headers: { ...headers, 'Location': /[^\x00-\xFF]/.test(loc) ? encodeURI(loc) : loc }
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static html(
|
|
60
|
+
html: string | Promise<string>,
|
|
61
|
+
status?: ContentfulStatusCode,
|
|
62
|
+
headers?: HeaderRecord
|
|
63
|
+
): Response | Promise<Response> {
|
|
64
|
+
const res = (html: string) => this.raw(status, html, 'text/html; charset=UTF-8' as BaseMime, headers)
|
|
65
|
+
return typeof html == 'string'
|
|
66
|
+
? res(html)
|
|
67
|
+
: resolveCallback(html, HtmlEscapedCallbackPhase.Stringify, false, {}).then(res)
|
|
8
68
|
}
|
|
9
69
|
|
|
10
70
|
static ok(): Response
|
|
11
71
|
static ok<T>(data: T): Response
|
|
12
72
|
static ok<T>(data?: T) {
|
|
13
|
-
|
|
14
|
-
return this.raw(200)
|
|
15
|
-
|
|
16
|
-
return c.cx.json(data, 200)
|
|
73
|
+
return this.json(data, 200)
|
|
17
74
|
}
|
|
18
75
|
|
|
19
76
|
static created(): Response
|
|
20
77
|
static created<T>(data: T): Response
|
|
21
78
|
static created<T>(data?: T) {
|
|
22
|
-
|
|
23
|
-
return this.raw(201)
|
|
24
|
-
|
|
25
|
-
return c.cx.json(data, 201)
|
|
79
|
+
return this.json(data, 201)
|
|
26
80
|
}
|
|
27
81
|
|
|
28
82
|
static accepted(): Response
|
|
29
83
|
static accepted<T>(data: T): Response
|
|
30
84
|
static accepted<T>(data?: T) {
|
|
31
|
-
|
|
32
|
-
return this.raw(202)
|
|
33
|
-
|
|
34
|
-
return c.cx.json(data, 202)
|
|
85
|
+
return this.json(data, 202)
|
|
35
86
|
}
|
|
36
87
|
|
|
37
88
|
static deleted() {
|
|
@@ -50,19 +101,13 @@ export default class Response {
|
|
|
50
101
|
static unauthorized(): Response
|
|
51
102
|
static unauthorized<T>(data: T): Response
|
|
52
103
|
static unauthorized<T>(data?: T) {
|
|
53
|
-
|
|
54
|
-
return this.raw(401)
|
|
55
|
-
|
|
56
|
-
return c.cx.json(data, 401)
|
|
104
|
+
return this.json(data, 401)
|
|
57
105
|
}
|
|
58
106
|
|
|
59
107
|
static forbidden(): Response
|
|
60
108
|
static forbidden<T>(data: T): Response
|
|
61
109
|
static forbidden<T>(data?: T) {
|
|
62
|
-
|
|
63
|
-
return this.raw(403)
|
|
64
|
-
|
|
65
|
-
return c.cx.json(data, 403)
|
|
110
|
+
return this.json(data, 403)
|
|
66
111
|
}
|
|
67
112
|
|
|
68
113
|
static notFound(): Response
|
|
@@ -95,6 +140,6 @@ export default class Response {
|
|
|
95
140
|
if (msg) resp.m = msg
|
|
96
141
|
if (errors) resp.e = errors
|
|
97
142
|
|
|
98
|
-
return
|
|
143
|
+
return this.json(resp, status)
|
|
99
144
|
}
|
|
100
145
|
}
|
package/src/routes.ts
CHANGED
|
@@ -4,6 +4,11 @@ import { fileURLToPath } from 'node:url'
|
|
|
4
4
|
import { Route } from './types'
|
|
5
5
|
import { isAnonFn } from './utils/func'
|
|
6
6
|
|
|
7
|
+
import { writeFileSync } from 'node:fs'
|
|
8
|
+
import { config } from 'dotenv'
|
|
9
|
+
import ensureDir from './utils/ensuredir'
|
|
10
|
+
import versionSHA from './utils/version-sha'
|
|
11
|
+
|
|
7
12
|
const __filename = fileURLToPath(import.meta.url)
|
|
8
13
|
const __dirname = dirname(__filename)
|
|
9
14
|
|
|
@@ -29,7 +34,7 @@ const walk = async (dir: string, baseDir: string, fn: Function, parentMw: string
|
|
|
29
34
|
|
|
30
35
|
if (stat.isDirectory()) {
|
|
31
36
|
await walk(fullPath, baseDir, fn, currentMw)
|
|
32
|
-
} else if (file
|
|
37
|
+
} else if (file != 'index.ts' && file.endsWith('.ts') && !file.endsWith('.d.ts')) {
|
|
33
38
|
const mod = await import(fullPath)
|
|
34
39
|
fn(fullPath, baseDir, mod.default, currentMw)
|
|
35
40
|
}
|
|
@@ -65,7 +70,7 @@ export async function getRoutes(
|
|
|
65
70
|
}
|
|
66
71
|
|
|
67
72
|
function sortRoutes(routes: Route[]) {
|
|
68
|
-
const metas = new Map<string, { score: number
|
|
73
|
+
const metas = new Map<string, { score: number, segmentsCount: number }>()
|
|
69
74
|
|
|
70
75
|
for (const route of routes)
|
|
71
76
|
metas.set(route.path, computeRouteMeta(route.path))
|
|
@@ -118,3 +123,62 @@ export async function getMiddlewares(
|
|
|
118
123
|
|
|
119
124
|
return mw
|
|
120
125
|
}
|
|
126
|
+
|
|
127
|
+
const env = Object.entries(
|
|
128
|
+
config({ path: '../../.env.prod' })?.parsed || {}
|
|
129
|
+
).filter(([key, val]) => key?.toLowerCase().indexOf('aws') != 0) // prevent AWS credentials
|
|
130
|
+
|
|
131
|
+
const version = versionSHA('../../.git') // @ts-ignore
|
|
132
|
+
env.push(['VERSION_SHA', process.env['VERSION_SHA'] = version]) // @ts-ignore
|
|
133
|
+
env.push(['VERSION_HASH', process.env['VERSION_HASH'] = version?.substring(0, 7)])
|
|
134
|
+
|
|
135
|
+
export async function cacheRoutes() {
|
|
136
|
+
const rolePath = join(__dirname, '../../../roles.json')
|
|
137
|
+
if (!existsSync(rolePath))
|
|
138
|
+
writeFileSync(rolePath, '{}')
|
|
139
|
+
|
|
140
|
+
const routes = await getRoutes()
|
|
141
|
+
const middlewares = await getMiddlewares()
|
|
142
|
+
|
|
143
|
+
const iPath = join(__dirname, '../../../tmp/import-routes.mjs')
|
|
144
|
+
ensureDir(iPath)
|
|
145
|
+
writeFileSync(iPath, `// AUTO-GENERATED FILE - DO NOT EDIT
|
|
146
|
+
${env?.length ? `import { Envir } from '../node_modules/t0n/dist/index'\nEnvir.add({${env.map(([key, val]) => key + ':' + JSON.stringify(val)).join(',')}})` : ''}
|
|
147
|
+
|
|
148
|
+
import { registerHandler, registerMiddleware } from '../node_modules/rajt/src/register'
|
|
149
|
+
|
|
150
|
+
${routes.map(r => `import ${r.name} from '../${normalizePath(r.file)}'`).join('\n')}
|
|
151
|
+
${middlewares.map(r => `import ${r.name} from '../${normalizePath(r.file)}'`).join('\n')}
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
const handlers = {${routes.map(r => r.name).join()}}
|
|
155
|
+
|
|
156
|
+
for (const [name, handler] of Object.entries(handlers)) {
|
|
157
|
+
if (typeof handler == 'function' || handler.prototype?.handle) {
|
|
158
|
+
registerHandler(name, handler)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const middlewares = {${middlewares.map(r => r.name).join()}}
|
|
163
|
+
|
|
164
|
+
for (const [name, mw] of Object.entries(middlewares)) {
|
|
165
|
+
registerMiddleware(mw)
|
|
166
|
+
}
|
|
167
|
+
} catch (e) {
|
|
168
|
+
console.error('Failed to register handlers:', e)
|
|
169
|
+
}
|
|
170
|
+
`)
|
|
171
|
+
|
|
172
|
+
const rPath = join(__dirname, '../../../tmp/routes.json')
|
|
173
|
+
ensureDir(rPath)
|
|
174
|
+
writeFileSync(rPath, JSON.stringify(routes.filter(r => r.method && r.path).map(route => [
|
|
175
|
+
route.method,
|
|
176
|
+
route.path,
|
|
177
|
+
route.middlewares,
|
|
178
|
+
route.name,
|
|
179
|
+
])))
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function normalizePath(file: string) {
|
|
183
|
+
return file.replace(/\.tsx?$/i, '').replace(/(\/index)+$/i, '').replace(/\/+$/g, '')
|
|
184
|
+
}
|
|
@@ -1,67 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { config } from 'dotenv'
|
|
3
|
-
import { getRoutes, getMiddlewares } from '../routes'
|
|
4
|
-
import ensureDir from '../utils/ensuredir'
|
|
5
|
-
import versionSHA from '../utils/version-sha'
|
|
6
|
-
|
|
7
|
-
const env = Object.entries(
|
|
8
|
-
config({ path: '../../.env.prod' })?.parsed || {}
|
|
9
|
-
).filter(([key, val]) => key?.toLowerCase().indexOf('aws') != 0) // prevent AWS credentials
|
|
10
|
-
|
|
11
|
-
const version = versionSHA('../../.git')
|
|
12
|
-
env.push(['VERSION_SHA', process.env['VERSION_SHA'] = version])
|
|
13
|
-
env.push(['VERSION_HASH', process.env['VERSION_HASH'] = version?.substring(0, 7)])
|
|
14
|
-
|
|
15
|
-
async function cacheRoutes() {
|
|
16
|
-
const rolePath = '../../roles.json'
|
|
17
|
-
if (!existsSync(rolePath))
|
|
18
|
-
writeFileSync(rolePath, '{}')
|
|
19
|
-
|
|
20
|
-
const routes = await getRoutes()
|
|
21
|
-
const middlewares = await getMiddlewares()
|
|
22
|
-
|
|
23
|
-
const iPath = '../../tmp/import-routes.mjs'
|
|
24
|
-
ensureDir(iPath)
|
|
25
|
-
writeFileSync(iPath, `// AUTO-GENERATED FILE - DO NOT EDIT
|
|
26
|
-
${env?.length ? `import { Envir } from '../node_modules/t0n/dist/index'\nEnvir.add({${env.map(([key, val]) => key + ':' + JSON.stringify(val)).join(',')}})` : ''}
|
|
27
|
-
|
|
28
|
-
import { registerHandler, registerMiddleware } from '../node_modules/rajt/src/register'
|
|
29
|
-
|
|
30
|
-
${routes.map(r => `import ${r.name} from '../${normalizePath(r.file)}'`).join('\n')}
|
|
31
|
-
${middlewares.map(r => `import ${r.name} from '../${normalizePath(r.file)}'`).join('\n')}
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
const handlers = {${routes.map(r => r.name).join()}}
|
|
35
|
-
|
|
36
|
-
for (const [name, handler] of Object.entries(handlers)) {
|
|
37
|
-
if (typeof handler == 'function' || handler.prototype?.handle) {
|
|
38
|
-
registerHandler(name, handler)
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const middlewares = {${middlewares.map(r => r.name).join()}}
|
|
43
|
-
|
|
44
|
-
for (const [name, mw] of Object.entries(middlewares)) {
|
|
45
|
-
registerMiddleware(mw)
|
|
46
|
-
}
|
|
47
|
-
} catch (e) {
|
|
48
|
-
console.error('Failed to register handlers:', e)
|
|
49
|
-
}
|
|
50
|
-
`)
|
|
51
|
-
|
|
52
|
-
const rPath = '../../tmp/routes.json'
|
|
53
|
-
ensureDir(rPath)
|
|
54
|
-
writeFileSync(rPath, JSON.stringify(routes.filter(r => r.method && r.path).map(route => [
|
|
55
|
-
route.method,
|
|
56
|
-
route.path,
|
|
57
|
-
route.middlewares,
|
|
58
|
-
route.name,
|
|
59
|
-
])))
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function normalizePath(file: string) {
|
|
63
|
-
return file.replace(/\.tsx?$/i, '').replace(/(\/index)+$/i, '').replace(/\/+$/g, '')
|
|
64
|
-
}
|
|
1
|
+
import { cacheRoutes } from '../routes'
|
|
65
2
|
|
|
66
3
|
cacheRoutes()
|
|
67
4
|
.then(() => {
|
package/src/types.ts
CHANGED
|
@@ -1,8 +1,29 @@
|
|
|
1
|
-
import { Handler } from 'hono'
|
|
1
|
+
import type { Handler, ValidationTargets } from 'hono'
|
|
2
2
|
import { ResponseHeader } from 'hono/utils/headers'
|
|
3
3
|
import { StatusCode } from 'hono/utils/http-status'
|
|
4
4
|
import { BaseMime } from 'hono/utils/mime'
|
|
5
|
+
import z from 'zod'
|
|
5
6
|
import Action from './action'
|
|
7
|
+
import request from './request'
|
|
8
|
+
import response from './response'
|
|
9
|
+
import validator from './validator'
|
|
10
|
+
|
|
11
|
+
export type { Context, Next } from 'hono'
|
|
12
|
+
|
|
13
|
+
type PublicMethods<T> = {
|
|
14
|
+
[K in keyof T]: K extends `#${string}` | `$${string}` | symbol | 'prototype' ? never : K
|
|
15
|
+
}[keyof T]
|
|
16
|
+
|
|
17
|
+
export type IRequest = Pick<request, PublicMethods<request>>
|
|
18
|
+
export type IResponse = Pick<typeof response, PublicMethods<typeof response>>
|
|
19
|
+
|
|
20
|
+
export type IValidator = Pick<typeof validator, PublicMethods<typeof validator>>
|
|
21
|
+
export type Rule = {
|
|
22
|
+
schema: z.ZodObject<any>
|
|
23
|
+
target: keyof ValidationTargets
|
|
24
|
+
eTarget?: 'fieldErrors' | 'formErrors'
|
|
25
|
+
}
|
|
26
|
+
export type Rules = Rule[] | Rule | null
|
|
6
27
|
|
|
7
28
|
export type Route = {
|
|
8
29
|
method: string,
|
|
@@ -13,6 +34,8 @@ export type Route = {
|
|
|
13
34
|
handle: Handlers,
|
|
14
35
|
}
|
|
15
36
|
|
|
37
|
+
export type ActionType = Function | Handler | Action | (new () => Action)
|
|
38
|
+
|
|
16
39
|
export type Handlers = (Function | Handler | (new () => Action))[]
|
|
17
40
|
|
|
18
41
|
export type Routes = Route[]
|
|
@@ -45,6 +68,3 @@ export type ResponseOrInit<T extends StatusCode = StatusCode> = ResponseInit<T>
|
|
|
45
68
|
// | null
|
|
46
69
|
// | { [key: string]: JSONValue }
|
|
47
70
|
// | JSONValue[]
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
export type { Context, Next } from 'hono'
|
package/src/utils/func.ts
CHANGED
package/src/utils/resolve.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import Action
|
|
1
|
+
import Action from '../action'
|
|
2
2
|
import { MiddlewareType } from '../middleware'
|
|
3
|
+
import type { ActionType } from '../types'
|
|
3
4
|
|
|
4
5
|
export function resolve(obj: ActionType, id: string) {
|
|
5
6
|
if (typeof obj == 'function' && obj?.length == 2)
|
package/src/validator.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ZodObject } from 'zod'
|
|
2
|
+
import { zValidator } from '@hono/zod-validator'
|
|
3
|
+
import type { ValidationTargets } from 'hono'
|
|
4
|
+
import type { Rule, Rules } from './types'
|
|
5
|
+
import response from './response'
|
|
6
|
+
|
|
7
|
+
export default class $Validator {
|
|
8
|
+
private static cache = new Map<string, any>()
|
|
9
|
+
|
|
10
|
+
private static createRule<T extends keyof ValidationTargets>(
|
|
11
|
+
target: T,
|
|
12
|
+
schema?: ZodObject<any>
|
|
13
|
+
):
|
|
14
|
+
| { schema: (schema: ZodObject<any>) => Rule }
|
|
15
|
+
| Rule
|
|
16
|
+
{
|
|
17
|
+
if (schema != null) {
|
|
18
|
+
return {
|
|
19
|
+
target,
|
|
20
|
+
schema,
|
|
21
|
+
eTarget: 'fieldErrors'
|
|
22
|
+
} satisfies Rule
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
schema: (schema: ZodObject<any>) => ({
|
|
27
|
+
target,
|
|
28
|
+
schema,
|
|
29
|
+
eTarget: 'fieldErrors'
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private static getOrCreateAlias<T extends keyof ValidationTargets>(target: T) {
|
|
35
|
+
if (this.cache.has(target))
|
|
36
|
+
return this.cache.get(target)
|
|
37
|
+
|
|
38
|
+
const aliasFunc = (schema?: ZodObject<any>) => {
|
|
39
|
+
if (schema != null)
|
|
40
|
+
return this.createRule(target, schema)
|
|
41
|
+
|
|
42
|
+
return this.createRule(target)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const typedAlias = aliasFunc as {
|
|
46
|
+
(): { schema: (schema: ZodObject<any>) => Rule },
|
|
47
|
+
(schema: ZodObject<any>): Rule,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this.cache.set(target, typedAlias)
|
|
51
|
+
return typedAlias
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static readonly json = $Validator.getOrCreateAlias('json')
|
|
55
|
+
static readonly form = $Validator.getOrCreateAlias('form')
|
|
56
|
+
static readonly query = $Validator.getOrCreateAlias('query')
|
|
57
|
+
static readonly param = $Validator.getOrCreateAlias('param')
|
|
58
|
+
static readonly header = $Validator.getOrCreateAlias('header')
|
|
59
|
+
static readonly cookie = $Validator.getOrCreateAlias('cookie')
|
|
60
|
+
|
|
61
|
+
static parse(rules: Rules): Function[] {
|
|
62
|
+
return (Array.isArray(rules) ? rules : [rules]) // @ts-ignore
|
|
63
|
+
.flatMap(rule => zValidator(rule.target, rule.schema, (result, c) => {
|
|
64
|
+
if (!result.success) // @ts-ignore
|
|
65
|
+
return response.badRequest({ ...result.error.flatten()[rule.eTarget] })
|
|
66
|
+
}))
|
|
67
|
+
}
|
|
68
|
+
}
|