rajt 0.0.71 → 0.0.72
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 +5 -7
- package/src/cli/commands/build.ts +4 -4
- package/src/cli/commands/dev.ts +4 -4
- package/src/cli/commands/routes.ts +106 -0
- package/src/cli/commands/utils.ts +1 -1
- package/src/cli/index.ts +3 -1
- package/src/create-app.ts +26 -2
- package/src/register.ts +2 -2
- package/src/request.ts +14 -2
- package/src/routes.ts +35 -4
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rajt",
|
|
3
3
|
"description": "A serverless bundler layer, fully typed for AWS Lambda (Node.js and LLRT) and Cloudflare Workers.",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.72",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
@@ -25,7 +25,6 @@
|
|
|
25
25
|
"src"
|
|
26
26
|
],
|
|
27
27
|
"scripts": {
|
|
28
|
-
"rajt": "./src/bin/rajt.js",
|
|
29
28
|
"aws:local": "bun run --silent aws:build && bun run --silent sam:local",
|
|
30
29
|
"aws:package": "bun run --silent aws:build && bun run --silent sam:package",
|
|
31
30
|
"aws:deploy": "bun run --silent aws:build && bun run --silent sam:package && bun run --silent sam:deploy",
|
|
@@ -34,11 +33,7 @@
|
|
|
34
33
|
"sam:package": "sam package --template-file ../../template-prod.yaml --output-template-file ../../packaged.yaml",
|
|
35
34
|
"sam:deploy": "sam deploy --template-file ../../packaged.yaml --stack-name rajt-llrt --capabilities CAPABILITY_IAM",
|
|
36
35
|
"sam:update": "source ../../.env.prod && aws lambda update-function-code --function-name $AWS_NAME --zip-file fileb://../../lambda.zip --region $AWS_REGION --no-cli-pager 2>&1 >/dev/null",
|
|
37
|
-
"
|
|
38
|
-
"clean:build": "rm -rf ../../dist",
|
|
39
|
-
"clean:temp": "rm -rf ../../tmp",
|
|
40
|
-
"zip": "zip -j ../../lambda.zip ../../dist/index.js",
|
|
41
|
-
"start": "node ../../dist/index.js"
|
|
36
|
+
"zip": "zip -j ../../lambda.zip ../../dist/index.js"
|
|
42
37
|
},
|
|
43
38
|
"dependencies": {
|
|
44
39
|
"cripta": "^0.1",
|
|
@@ -97,8 +92,11 @@
|
|
|
97
92
|
"application",
|
|
98
93
|
"framework",
|
|
99
94
|
"router",
|
|
95
|
+
"serverless",
|
|
96
|
+
"typescript",
|
|
100
97
|
"cloudflare",
|
|
101
98
|
"workers",
|
|
99
|
+
"vercel",
|
|
102
100
|
"deno",
|
|
103
101
|
"bun",
|
|
104
102
|
"nodejs"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineCommand } from 'citty'
|
|
2
2
|
import { gray } from '../../utils/colors'
|
|
3
3
|
import { build, normalizePlatform, platformError } from './utils'
|
|
4
|
-
import { wait, error } from '../../utils/log'
|
|
4
|
+
import { wait, error, rn } from '../../utils/log'
|
|
5
5
|
|
|
6
6
|
import { platforms } from './constants'
|
|
7
7
|
|
|
@@ -27,11 +27,11 @@ export default defineCommand({
|
|
|
27
27
|
|
|
28
28
|
try {
|
|
29
29
|
await build(platform)
|
|
30
|
-
} catch (e) {
|
|
31
|
-
error('Build failed:', e)
|
|
30
|
+
} catch (e: any) {
|
|
31
|
+
error('Build failed:', e?.message || e)
|
|
32
32
|
process.exit(0)
|
|
33
33
|
} finally {
|
|
34
|
-
|
|
34
|
+
rn()
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
})
|
package/src/cli/commands/dev.ts
CHANGED
|
@@ -53,8 +53,8 @@ export default defineCommand({
|
|
|
53
53
|
try {
|
|
54
54
|
await build(platform)
|
|
55
55
|
if (!lambda) await startLambda()
|
|
56
|
-
} catch (e) {
|
|
57
|
-
error('Build failed:', e)
|
|
56
|
+
} catch (e: any) {
|
|
57
|
+
error('Build failed:', e?.message || e)
|
|
58
58
|
process.exit(0)
|
|
59
59
|
} finally {
|
|
60
60
|
isBuilding = false
|
|
@@ -141,8 +141,8 @@ export default defineCommand({
|
|
|
141
141
|
try {
|
|
142
142
|
await build(platform)
|
|
143
143
|
await startWorker()
|
|
144
|
-
} catch (e) {
|
|
145
|
-
error('Build failed:', e)
|
|
144
|
+
} catch (e: any) {
|
|
145
|
+
error('Build failed:', e?.message || e)
|
|
146
146
|
process.exit(0)
|
|
147
147
|
} finally {
|
|
148
148
|
isBuilding = false
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { defineCommand } from 'citty'
|
|
3
|
+
import { inspectRoutes } from 'hono/dev'
|
|
4
|
+
import { gray, purple, red, yellow } from '../../utils/colors'
|
|
5
|
+
import { __rajt } from './utils'
|
|
6
|
+
import { rn } from '../../utils/log'
|
|
7
|
+
import IMPORT from '../../utils/import'
|
|
8
|
+
|
|
9
|
+
export default defineCommand({
|
|
10
|
+
meta: {
|
|
11
|
+
name: 'routes',
|
|
12
|
+
description: '📒 Displays all registered routes\n',
|
|
13
|
+
},
|
|
14
|
+
args: {
|
|
15
|
+
path: {
|
|
16
|
+
description: 'Filter the routes by path',
|
|
17
|
+
type: 'string',
|
|
18
|
+
},
|
|
19
|
+
method: {
|
|
20
|
+
description: 'Filter the routes by method',
|
|
21
|
+
type: 'string',
|
|
22
|
+
},
|
|
23
|
+
reverse: {
|
|
24
|
+
description: 'Reverse the ordering of the routes',
|
|
25
|
+
type: 'boolean',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
async run({ args }) {
|
|
29
|
+
const mod = await IMPORT(join(__rajt, 'dev.ts'))
|
|
30
|
+
const app = mod.default
|
|
31
|
+
|
|
32
|
+
const opts = {
|
|
33
|
+
path: args?.path || '',
|
|
34
|
+
method: args?.method?.toUpperCase() || '',
|
|
35
|
+
reverse: !!args?.reverse,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const keys: Set<string> = new Set()
|
|
39
|
+
let maxMethodLength = 0
|
|
40
|
+
let maxPathLength = 0
|
|
41
|
+
|
|
42
|
+
const colorMethod = (method: string, str?: string) => {
|
|
43
|
+
const val = str || method
|
|
44
|
+
|
|
45
|
+
switch (method) {
|
|
46
|
+
case 'HEAD':
|
|
47
|
+
case 'OPTIONS':
|
|
48
|
+
case 'CONNECT':
|
|
49
|
+
case 'TRACE':
|
|
50
|
+
return gray(val)
|
|
51
|
+
case 'GET':
|
|
52
|
+
return purple(val)
|
|
53
|
+
case 'POST':
|
|
54
|
+
case 'PUT':
|
|
55
|
+
case 'PATCH':
|
|
56
|
+
return yellow(val)
|
|
57
|
+
case 'DELETE':
|
|
58
|
+
return red(val)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return val
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let routes = inspectRoutes(app)
|
|
65
|
+
.filter(({ method, path, isMiddleware }) => {
|
|
66
|
+
const key = method + '-' + path
|
|
67
|
+
if (keys.has(key)) return false
|
|
68
|
+
keys.add(key)
|
|
69
|
+
|
|
70
|
+
let mLength = method.length
|
|
71
|
+
if (method == 'GET') mLength += 5
|
|
72
|
+
|
|
73
|
+
maxMethodLength = Math.max(maxMethodLength, mLength)
|
|
74
|
+
maxPathLength = Math.max(maxPathLength, path.length)
|
|
75
|
+
|
|
76
|
+
return [
|
|
77
|
+
!isMiddleware,
|
|
78
|
+
opts.path ? path.startsWith(opts.path) : true,
|
|
79
|
+
opts.method ? method == opts.method : true,
|
|
80
|
+
].every(Boolean)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
if (opts.reverse)
|
|
84
|
+
routes = routes.reverse()
|
|
85
|
+
|
|
86
|
+
routes.forEach(route => {
|
|
87
|
+
if (!route) return
|
|
88
|
+
const { method, path } = route
|
|
89
|
+
|
|
90
|
+
let mLength = method.length
|
|
91
|
+
let str = colorMethod(method)
|
|
92
|
+
|
|
93
|
+
if (method == 'GET') {
|
|
94
|
+
mLength += 5
|
|
95
|
+
str += gray('|') + colorMethod('HEAD')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
console.log(str + ' '.repeat(maxMethodLength - mLength) +' '+ path.replace(
|
|
99
|
+
/(?::([a-zA-Z_][a-zA-Z0-9_]*)(\{[^}]+\})?|\*)/g,
|
|
100
|
+
_ => colorMethod(method, _)
|
|
101
|
+
))
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
rn()
|
|
105
|
+
},
|
|
106
|
+
})
|
|
@@ -14,7 +14,7 @@ import { step, substep, event, error, warn } from '../../utils/log'
|
|
|
14
14
|
import { platforms } from './constants'
|
|
15
15
|
|
|
16
16
|
export const _root = join(dirname(new URL(import.meta.url).pathname), '../../../../../')
|
|
17
|
-
const __rajt = join(_root, 'node_modules/rajt/src')
|
|
17
|
+
export const __rajt = join(_root, 'node_modules/rajt/src')
|
|
18
18
|
|
|
19
19
|
export function normalizePlatform(platform: Platform) {
|
|
20
20
|
platform = platform?.toLowerCase() as Platform
|
package/src/cli/index.ts
CHANGED
|
@@ -5,10 +5,10 @@ import { logo } from '../utils/log'
|
|
|
5
5
|
import { isColorSupported, gray } from '../utils/colors'
|
|
6
6
|
import { version as rajtVersion } from '../../package.json'
|
|
7
7
|
|
|
8
|
-
|
|
9
8
|
import dev from './commands/dev'
|
|
10
9
|
import build from './commands/build'
|
|
11
10
|
import deploy from './commands/deploy'
|
|
11
|
+
import routes from './commands/routes'
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* The main entrypoint for the CLI.
|
|
@@ -56,6 +56,8 @@ if (directly()) {
|
|
|
56
56
|
dev,
|
|
57
57
|
build,
|
|
58
58
|
deploy,
|
|
59
|
+
routes,
|
|
60
|
+
endpoints: routes,
|
|
59
61
|
},
|
|
60
62
|
})
|
|
61
63
|
|
package/src/create-app.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Hono } from 'hono'
|
|
2
2
|
import { logger } from 'hono/logger'
|
|
3
|
+
import { matchedRoutes } from 'hono/route'
|
|
3
4
|
import { Envir } from 't0n'
|
|
4
5
|
import type {
|
|
5
6
|
Env, Context, Next,
|
|
@@ -64,8 +65,31 @@ export const createApp = <E extends Env>(options?: ServerOptions<E>) => {
|
|
|
64
65
|
// const root = options?.root ?? '/'
|
|
65
66
|
const app = options?.app ?? new Hono<E>()
|
|
66
67
|
|
|
67
|
-
if (isDev())
|
|
68
|
-
app.use('*',
|
|
68
|
+
if (isDev()) {
|
|
69
|
+
app.use('*', async function (c: Context, next: Next) {
|
|
70
|
+
const method = c.req.method
|
|
71
|
+
const route = matchedRoutes(c).find(route => route.method == method)?.path
|
|
72
|
+
const logWithRoute = (args: string[]) => {
|
|
73
|
+
if (!route || !args.length) return args
|
|
74
|
+
return args.map(arg => {
|
|
75
|
+
if (!arg) return arg
|
|
76
|
+
const split = arg?.split(' ')
|
|
77
|
+
if (split.length < 3 || split[2] == route)
|
|
78
|
+
return arg
|
|
79
|
+
|
|
80
|
+
split.splice(Math.min(3, split.length), 0, gray(route))
|
|
81
|
+
return split.join(' ')
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const devLogger = logger((...args: any[]) => {
|
|
86
|
+
const timestamp = gray(localDate())
|
|
87
|
+
console.log(timestamp, ...logWithRoute(args))
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
await devLogger(c, next)
|
|
91
|
+
})
|
|
92
|
+
}
|
|
69
93
|
|
|
70
94
|
app.use(async (c: Context, next: Next) => {
|
|
71
95
|
c.set(GET_REQUEST as unknown as string, new request(c))
|
package/src/register.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export const handlers: Record<string, Function> = {}
|
|
2
2
|
|
|
3
3
|
export function registerHandler(id: string, handler: any) {
|
|
4
|
-
if (id in handlers)
|
|
5
|
-
|
|
4
|
+
// if (id in handlers)
|
|
5
|
+
// console.warn(`Handler "${id}" has already been registered`)
|
|
6
6
|
|
|
7
7
|
handlers[id] = handler
|
|
8
8
|
}
|
package/src/request.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { HTTPException } from 'hono/http-exception'
|
|
|
3
3
|
import { Authnz, Token } from './auth'
|
|
4
4
|
|
|
5
5
|
import type { Context } from 'hono'
|
|
6
|
+
import { routePath, matchedRoutes } from 'hono/route'
|
|
7
|
+
import type { RouterRoute } from 'hono/types'
|
|
6
8
|
import type { CookieOptions, CookiePrefixOptions } from 'hono/utils/cookie'
|
|
7
9
|
import type { CustomHeader, RequestHeader } from 'hono/utils/headers'
|
|
8
10
|
import type { BodyData, ParseBodyOptions } from 'hono/utils/body'
|
|
@@ -25,6 +27,8 @@ export default class $Request {
|
|
|
25
27
|
#u: Authnz<any> | null = null
|
|
26
28
|
|
|
27
29
|
#host: string
|
|
30
|
+
#routePath: string
|
|
31
|
+
#matchedRoutes: RouterRoute[]
|
|
28
32
|
|
|
29
33
|
constructor(c: Context) {
|
|
30
34
|
this.#c = c
|
|
@@ -33,6 +37,9 @@ export default class $Request {
|
|
|
33
37
|
|
|
34
38
|
const url = new URL(c.req.raw.url)
|
|
35
39
|
this.#host = url.protocol +'//'+ url.host
|
|
40
|
+
|
|
41
|
+
this.#routePath = routePath(c)
|
|
42
|
+
this.#matchedRoutes = matchedRoutes(c)
|
|
36
43
|
}
|
|
37
44
|
|
|
38
45
|
get user() {
|
|
@@ -83,7 +90,7 @@ export default class $Request {
|
|
|
83
90
|
}
|
|
84
91
|
|
|
85
92
|
get routePath() {
|
|
86
|
-
return this.#
|
|
93
|
+
return this.#routePath
|
|
87
94
|
}
|
|
88
95
|
|
|
89
96
|
get url() {
|
|
@@ -98,12 +105,17 @@ export default class $Request {
|
|
|
98
105
|
return this.#c.req.path
|
|
99
106
|
}
|
|
100
107
|
|
|
108
|
+
get fullPath() {
|
|
109
|
+
const url = this.url
|
|
110
|
+
return url.slice(url.indexOf('/', 8))
|
|
111
|
+
}
|
|
112
|
+
|
|
101
113
|
get method() {
|
|
102
114
|
return this.#c.req.raw.method
|
|
103
115
|
}
|
|
104
116
|
|
|
105
117
|
get matchedRoutes() {
|
|
106
|
-
return this.#
|
|
118
|
+
return this.#matchedRoutes
|
|
107
119
|
}
|
|
108
120
|
|
|
109
121
|
get raw() {
|
package/src/routes.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { isAnonFn } from './utils/func'
|
|
|
9
9
|
import ensureDir from './utils/ensuredir'
|
|
10
10
|
import versionSHA from './utils/version-sha'
|
|
11
11
|
import type { Route } from './types'
|
|
12
|
+
import { error, substep, warn } from './utils/log'
|
|
12
13
|
|
|
13
14
|
const __filename = new URL(import.meta.url).pathname
|
|
14
15
|
const __root = resolve(dirname(__filename), '../../..')
|
|
@@ -42,11 +43,16 @@ const walk = async (dir: string, baseDir: string, fn: Function, parentMw: string
|
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
let hasDuplicatedRoutes = false
|
|
45
47
|
export async function getRoutes(
|
|
46
48
|
dirs: string[] = ['actions', 'features', 'routes']
|
|
47
49
|
): Promise<Route[]> {
|
|
50
|
+
hasDuplicatedRoutes = false
|
|
48
51
|
const routes: Route[] = []
|
|
49
|
-
|
|
52
|
+
|
|
53
|
+
let length = 0
|
|
54
|
+
const keys: Set<string> = new Set()
|
|
55
|
+
const bag: Record<string, string[]> = {}
|
|
50
56
|
|
|
51
57
|
await Promise.all(dirs.map(dir => walk(
|
|
52
58
|
resolve(__root, dir),
|
|
@@ -66,9 +72,24 @@ export async function getRoutes(
|
|
|
66
72
|
middlewares,
|
|
67
73
|
handle,
|
|
68
74
|
})
|
|
75
|
+
|
|
76
|
+
if (!keys.has(name)) {
|
|
77
|
+
keys.add(name)
|
|
78
|
+
} else {
|
|
79
|
+
;(bag[name] ||= []).push(file)
|
|
80
|
+
length++
|
|
81
|
+
}
|
|
69
82
|
}
|
|
70
83
|
)))
|
|
71
84
|
|
|
85
|
+
if (length) {
|
|
86
|
+
hasDuplicatedRoutes = true
|
|
87
|
+
Object.entries(bag).forEach(([name, paths]) => {
|
|
88
|
+
warn(`Route "${name}" has `+ (paths.length > 1 ? `registered ${paths.length} times:` : 'already been registered:'))
|
|
89
|
+
substep(...paths)
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
72
93
|
return sortRoutes(routes)
|
|
73
94
|
}
|
|
74
95
|
|
|
@@ -93,13 +114,13 @@ function extractHttpPath(file: string) {
|
|
|
93
114
|
return route == '/' ? '/' : route.replace(/\/$/, '')
|
|
94
115
|
}
|
|
95
116
|
|
|
96
|
-
function sortRoutes(routes: Route[]) {
|
|
117
|
+
export function sortRoutes(routes: Route[]) {
|
|
97
118
|
const metas = new Map<string, { score: number, segmentsCount: number }>()
|
|
98
119
|
|
|
99
120
|
for (const route of routes)
|
|
100
121
|
metas.set(route.path, computeRouteMeta(route.path))
|
|
101
122
|
|
|
102
|
-
|
|
123
|
+
const list = routes.sort((a, b) => {
|
|
103
124
|
const metaA = metas.get(a.path)!
|
|
104
125
|
const metaB = metas.get(b.path)!
|
|
105
126
|
|
|
@@ -108,6 +129,13 @@ function sortRoutes(routes: Route[]) {
|
|
|
108
129
|
|
|
109
130
|
return metaB.score - metaA.score
|
|
110
131
|
})
|
|
132
|
+
|
|
133
|
+
while (list.length && list.at(-1)?.path == '/') {
|
|
134
|
+
const last = list.pop()
|
|
135
|
+
last && list.unshift(last)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return list
|
|
111
139
|
}
|
|
112
140
|
|
|
113
141
|
function computeRouteMeta(path: string) {
|
|
@@ -116,7 +144,7 @@ function computeRouteMeta(path: string) {
|
|
|
116
144
|
let score = 0
|
|
117
145
|
for (const segment of segments) {
|
|
118
146
|
if (segment === '*') {
|
|
119
|
-
|
|
147
|
+
continue
|
|
120
148
|
} else if (segment.startsWith(':')) {
|
|
121
149
|
score += 1
|
|
122
150
|
} else {
|
|
@@ -219,6 +247,9 @@ export async function cacheRoutes() {
|
|
|
219
247
|
writeFileSync(rolePath, `export default {\n\n}`)
|
|
220
248
|
|
|
221
249
|
const routes = await getRoutes()
|
|
250
|
+
if (hasDuplicatedRoutes)
|
|
251
|
+
throw new Error("The app can't build with duplicate routes")
|
|
252
|
+
|
|
222
253
|
const middlewares = await getMiddlewares()
|
|
223
254
|
const configs = Object.entries(await getConfigs())
|
|
224
255
|
|