rajt 0.0.100 → 0.0.102
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/bin/rajt.js +59 -50
- package/package.json +10 -10
- package/src/auth/ability.ts +2 -2
- package/src/cli/commands/deploy.ts +1 -1
- package/src/cli/commands/dev.ts +5 -5
- package/src/cli/commands/migrate.ts +2 -2
- package/src/cli/commands/routes.ts +4 -4
- package/src/cli/index.ts +3 -3
- package/src/cli/stubs.ts +2 -2
- package/src/cli/utils.ts +11 -10
- package/src/create-app.ts +6 -6
- package/src/http.ts +3 -3
- package/src/open-api/register.ts +2 -1
- package/src/request.ts +13 -0
- package/src/response.ts +1 -1
- package/src/routes.ts +26 -20
- package/src/utils/environment.ts +10 -8
- package/src/utils/func.ts +2 -2
- package/src/utils/log.ts +3 -3
- package/src/utils/port.ts +1 -1
- package/src/utils/resolve.ts +3 -3
- package/src/validator.ts +28 -4
package/bin/rajt.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
|
-
import { join, dirname } from 'node:path';
|
|
3
|
+
import { join, dirname, resolve } from 'node:path';
|
|
4
4
|
import { existsSync } from 'node:fs';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
5
6
|
|
|
6
|
-
const __dirname = dirname(
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
8
|
|
|
8
9
|
const ERR_NODE_VERSION = '18.0.0';
|
|
9
10
|
const MIN_NODE_VERSION = '18.0.0';
|
|
10
11
|
|
|
11
|
-
let rajtProcess;
|
|
12
|
-
|
|
13
12
|
function runRajt() {
|
|
14
13
|
if (process?.versions?.node && semiver(process.versions.node, ERR_NODE_VERSION) < 0) {
|
|
15
14
|
console.error(
|
|
@@ -21,58 +20,70 @@ Consider using a Node.js version manager such as https://volta.sh or https://git
|
|
|
21
20
|
return;
|
|
22
21
|
}
|
|
23
22
|
|
|
24
|
-
const isBun = process?.isBun || typeof Bun
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
join(process.cwd(), _tsxPath),
|
|
35
|
-
'tsx',
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
for (const pathOption of tsxPaths) {
|
|
39
|
-
if (pathOption == 'tsx' || existsSync(pathOption)) {
|
|
40
|
-
tsxPath = pathOption;
|
|
41
|
-
break;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
23
|
+
const isBun = process?.isBun || typeof Bun !== 'undefined';
|
|
24
|
+
const targetScript = resolve(__dirname, '../src/cli/index.ts');
|
|
25
|
+
|
|
26
|
+
let executor = process.execPath;
|
|
27
|
+
let args = [];
|
|
28
|
+
|
|
29
|
+
if (isBun) {
|
|
30
|
+
args = [targetScript, ...process.argv.slice(2)];
|
|
31
|
+
} else {
|
|
32
|
+
const tsxBin = findTsx();
|
|
44
33
|
|
|
45
|
-
if (!
|
|
46
|
-
console.error('
|
|
34
|
+
if (!tsxBin) {
|
|
35
|
+
console.error('Error: "tsx" is not available. Please install tsx:');
|
|
47
36
|
console.error(' npm i -D tsx');
|
|
48
37
|
console.error(' or');
|
|
49
38
|
console.error(' bun i -D tsx');
|
|
50
39
|
process.exit(1);
|
|
51
|
-
return;
|
|
52
40
|
}
|
|
53
|
-
}
|
|
54
41
|
|
|
55
|
-
|
|
56
|
-
process.execPath,
|
|
57
|
-
[
|
|
42
|
+
args = [
|
|
58
43
|
'--no-warnings',
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
44
|
+
tsxBin,
|
|
45
|
+
targetScript,
|
|
46
|
+
...process.argv.slice(2)
|
|
47
|
+
].filter(arg => !arg.includes('experimental-vm-modules') && !arg.includes('loader'));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return execute(executor, args)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function findTsx() {
|
|
54
|
+
const relativePaths = [
|
|
55
|
+
join(__dirname, '../node_modules/tsx/dist/cli.mjs'),
|
|
56
|
+
join(process.cwd(), 'node_modules/tsx/dist/cli.mjs'),
|
|
57
|
+
join(__dirname, '../node_modules/.bin/tsx'),
|
|
58
|
+
join(process.cwd(), 'node_modules/.bin/tsx'),
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
for (const p of relativePaths) {
|
|
62
|
+
if (existsSync(p)) return p;
|
|
63
|
+
if (existsSync(`${p}.exe`)) return `${p}.exe`;
|
|
64
|
+
if (existsSync(`${p}.cmd`)) return `${p}.cmd`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return null
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function execute(command, args) {
|
|
71
|
+
const child = spawn(command, args, {
|
|
72
|
+
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
|
|
73
|
+
env: {
|
|
74
|
+
...process.env,
|
|
75
|
+
NODE_ENV: process.env.NODE_ENV || 'development',
|
|
76
|
+
TSX_DISABLE_CACHE: '1',
|
|
71
77
|
}
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
.on('
|
|
75
|
-
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
process.on('SIGINT', () => child.kill('SIGINT'))
|
|
81
|
+
.on('SIGTERM', () => child.kill('SIGTERM'));
|
|
82
|
+
|
|
83
|
+
return child
|
|
84
|
+
.on('exit', code => process.exit(code ?? 0))
|
|
85
|
+
.on('message', msg => process.send?.(msg))
|
|
86
|
+
.on('disconnect', () => process.disconnect?.())
|
|
76
87
|
}
|
|
77
88
|
|
|
78
89
|
var fn = new Intl.Collator(0, { numeric: 1 }).compare;
|
|
@@ -101,7 +112,5 @@ function directly() {
|
|
|
101
112
|
}
|
|
102
113
|
|
|
103
114
|
if (directly()) {
|
|
104
|
-
|
|
105
|
-
process.on('SIGINT', () => rajtProcess && rajtProcess.kill())
|
|
106
|
-
.on('SIGTERM', () => rajtProcess && rajtProcess.kill());
|
|
115
|
+
runRajt()
|
|
107
116
|
}
|
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.102",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
7
7
|
"files": ["bin", "src"],
|
|
@@ -35,25 +35,25 @@
|
|
|
35
35
|
"zip": "zip -j ../../lambda.zip ../../.rajt/dist/index.js"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@hono/node-server": "^1.19.
|
|
38
|
+
"@hono/node-server": "^1.19.11",
|
|
39
39
|
"@hono/standard-validator": "^0.2.2",
|
|
40
40
|
"@hono/zod-validator": "^0.7.6",
|
|
41
|
-
"@scalar/hono-api-reference": "^0.
|
|
41
|
+
"@scalar/hono-api-reference": "^0.10.3",
|
|
42
42
|
"chokidar": "^3.5.2",
|
|
43
43
|
"citty": "^0.1.6",
|
|
44
44
|
"consola": "^3.4.2",
|
|
45
|
-
"cripta": "^0.1.
|
|
45
|
+
"cripta": "^0.1.12",
|
|
46
46
|
"dotenv": "^16.5.0",
|
|
47
47
|
"esbuild": "^0.25.2",
|
|
48
|
-
"forj": "^0.1.
|
|
49
|
-
"hono": "^4.
|
|
48
|
+
"forj": "^0.1.10",
|
|
49
|
+
"hono": "^4.12.8",
|
|
50
50
|
"hono-openapi": "^1.3.0",
|
|
51
|
-
"localflare-api": "^0.
|
|
52
|
-
"localflare-core": "^0.
|
|
53
|
-
"miniflare": "^4.
|
|
51
|
+
"localflare-api": "^0.5.0",
|
|
52
|
+
"localflare-core": "^0.5.0",
|
|
53
|
+
"miniflare": "^4.20260312.0",
|
|
54
54
|
"pathe": "^2.0",
|
|
55
55
|
"quansync": "^0.2.11",
|
|
56
|
-
"t0n": "^0.1.
|
|
56
|
+
"t0n": "^0.1.13",
|
|
57
57
|
"tiny-glob": "^0.2",
|
|
58
58
|
"tsx": "^4.19.4",
|
|
59
59
|
"wrangler": "^4.61.0",
|
package/src/auth/ability.ts
CHANGED
|
@@ -19,11 +19,11 @@ export class Ability {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
static fromAction(target: any): string {
|
|
22
|
-
return !target ? '' : this.format(typeof target
|
|
22
|
+
return !target ? '' : this.format(typeof target === 'string' ? target : (target.name.length > 3 ? target.name : (target?.p || '')))
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
static format(path: string) {
|
|
26
|
-
return path
|
|
26
|
+
return path === '/'
|
|
27
27
|
? 'index'
|
|
28
28
|
: path.normalize('NFD')
|
|
29
29
|
.replace(/[\u0300-\u036f]/g, '')
|
package/src/cli/commands/dev.ts
CHANGED
|
@@ -54,7 +54,7 @@ export default defineCommand({
|
|
|
54
54
|
event('Building..')
|
|
55
55
|
}
|
|
56
56
|
const fn = async () => {
|
|
57
|
-
building && await build(platform)
|
|
57
|
+
building && await build(platform, 'dev')
|
|
58
58
|
await start()
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -109,7 +109,7 @@ export default defineCommand({
|
|
|
109
109
|
|
|
110
110
|
const started = (port: number) => {
|
|
111
111
|
log(`Starting API on http://${host}:${port}`)
|
|
112
|
-
if (platform
|
|
112
|
+
if (platform === 'cf')
|
|
113
113
|
log(`Localflare on https://studio.localflare.dev`)
|
|
114
114
|
rn()
|
|
115
115
|
}
|
|
@@ -136,7 +136,7 @@ export default defineCommand({
|
|
|
136
136
|
{
|
|
137
137
|
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
|
|
138
138
|
// stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
|
|
139
|
-
shell: process.platform
|
|
139
|
+
shell: process.platform === 'win32',
|
|
140
140
|
env: {...process.env, DOCKER_HOST: getDockerHost()},
|
|
141
141
|
}
|
|
142
142
|
)
|
|
@@ -202,8 +202,8 @@ export default defineCommand({
|
|
|
202
202
|
case 'node':
|
|
203
203
|
return withPort(desiredPort, async (port) => {
|
|
204
204
|
started(port)
|
|
205
|
-
const isBun = getRuntime()
|
|
206
|
-
const isWin32 = process.platform
|
|
205
|
+
const isBun = getRuntime() === 'bun'
|
|
206
|
+
const isWin32 = process.platform === 'win32'
|
|
207
207
|
const params = isBun
|
|
208
208
|
? ['run', '--port=' + port, '--hot', '--silent', '--no-clear-screen', '--no-summary', join(_root, 'node_modules/rajt/src/dev.ts')]
|
|
209
209
|
: [join(_root, 'node_modules/.bin/tsx' + (isWin32 ? '.exe' : '')), 'watch', join(_root, 'node_modules/rajt/src/dev-node.ts')]
|
|
@@ -19,13 +19,13 @@ export default defineCommand({
|
|
|
19
19
|
},
|
|
20
20
|
},
|
|
21
21
|
async run({ args }) {
|
|
22
|
-
if (args._.length
|
|
22
|
+
if (args._.length !== 2) {
|
|
23
23
|
error('Invalid args: '+ gray('[ACTION] [DATABASE]'))
|
|
24
24
|
return rn()
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const [action, database] = args._
|
|
28
|
-
const isBun = getRuntime()
|
|
28
|
+
const isBun = getRuntime() === 'bun'
|
|
29
29
|
const isRemote = !!args?.remote
|
|
30
30
|
|
|
31
31
|
try {
|
|
@@ -46,15 +46,15 @@ export default defineCommand({
|
|
|
46
46
|
keys.add(key)
|
|
47
47
|
|
|
48
48
|
let mLength = method.length
|
|
49
|
-
if (method
|
|
49
|
+
if (method === 'GET') mLength += 5
|
|
50
50
|
|
|
51
51
|
maxMethodLength = Math.max(maxMethodLength, mLength)
|
|
52
52
|
maxPathLength = Math.max(maxPathLength, path.length)
|
|
53
53
|
|
|
54
54
|
return [
|
|
55
|
-
isMiddleware && method
|
|
55
|
+
isMiddleware && method !== 'ALL' || !isMiddleware,
|
|
56
56
|
opts.path ? path.startsWith(opts.path) : true,
|
|
57
|
-
opts.method ? method
|
|
57
|
+
opts.method ? method === opts.method : true,
|
|
58
58
|
].every(Boolean)
|
|
59
59
|
})
|
|
60
60
|
|
|
@@ -68,7 +68,7 @@ export default defineCommand({
|
|
|
68
68
|
let mLength = method.length
|
|
69
69
|
let str = highlightedMethod(method, null, true)
|
|
70
70
|
|
|
71
|
-
if (method
|
|
71
|
+
if (method === 'GET')
|
|
72
72
|
mLength += 5
|
|
73
73
|
|
|
74
74
|
console.log(str + ' '.repeat(maxMethodLength - mLength) +' '+ highlightedURI(path, method))
|
package/src/cli/index.ts
CHANGED
|
@@ -19,8 +19,8 @@ import make from './commands/make'
|
|
|
19
19
|
const directly = () => {
|
|
20
20
|
try {
|
|
21
21
|
// @ts-ignore
|
|
22
|
-
return typeof vitest
|
|
23
|
-
&& import.meta.url
|
|
22
|
+
return typeof vitest === 'undefined'
|
|
23
|
+
&& import.meta.url?.endsWith(process.argv[1].replace(/\\/g, '/'))
|
|
24
24
|
} catch {
|
|
25
25
|
return false
|
|
26
26
|
}
|
|
@@ -32,7 +32,7 @@ const version = [name, isColorSupported ? gray('v'+rajtVersion) : rajtVersion].j
|
|
|
32
32
|
if (directly()) {
|
|
33
33
|
const _args = process.argv.slice(2)
|
|
34
34
|
const length = _args.length
|
|
35
|
-
if (!length || (length
|
|
35
|
+
if (!length || (length === 1 && ['-v', '--version', '--v', '-version'].includes(_args[0]))) {
|
|
36
36
|
console.log(version)
|
|
37
37
|
process.exit(0)
|
|
38
38
|
}
|
package/src/cli/stubs.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export const replace = (str: string, map: Record<string, string>) => {
|
|
2
|
-
if (!str || typeof str
|
|
2
|
+
if (!str || typeof str !== 'string' || !map || typeof map !== 'object') return str
|
|
3
3
|
const entries = Object.entries(map)
|
|
4
4
|
const length = entries?.length || 0
|
|
5
5
|
if (!length) return str
|
|
6
6
|
|
|
7
7
|
str = str.trimStart()
|
|
8
|
-
if (length
|
|
8
|
+
if (length === 1)
|
|
9
9
|
return str.replaceAll(entries[0][0], entries[0][1])
|
|
10
10
|
|
|
11
11
|
return entries.reduce(
|
package/src/cli/utils.ts
CHANGED
|
@@ -44,7 +44,7 @@ export const platformError = () => error(`Provide a valid platform: ${formatArgs
|
|
|
44
44
|
|
|
45
45
|
export function getRuntime() {
|
|
46
46
|
try {
|
|
47
|
-
return process?.isBun || typeof Bun
|
|
47
|
+
return process?.isBun || typeof Bun !== 'undefined' ? 'bun' : 'node'
|
|
48
48
|
} catch {
|
|
49
49
|
return 'node'
|
|
50
50
|
}
|
|
@@ -117,10 +117,10 @@ function stripDecorators(source: string) {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
const dist = '.rajt/dist'
|
|
120
|
-
export const build = async (platform: Platform) => {
|
|
120
|
+
export const build = async (platform: Platform, env: string = 'prd') => {
|
|
121
121
|
const startTime = Date.now()
|
|
122
122
|
|
|
123
|
-
const isCF = platform
|
|
123
|
+
const isCF = platform === 'cf'
|
|
124
124
|
cleanDir(join(_root, dist))
|
|
125
125
|
|
|
126
126
|
if (isCF) {
|
|
@@ -138,7 +138,7 @@ export const build = async (platform: Platform) => {
|
|
|
138
138
|
const encoder = new TextEncoder()
|
|
139
139
|
|
|
140
140
|
// @ts-ignore
|
|
141
|
-
platform = platform
|
|
141
|
+
platform = platform !== 'node' ? '-'+ platform : ''
|
|
142
142
|
const opts = {
|
|
143
143
|
entryPoints: [join(_rajt, `prod${platform}.ts`)],
|
|
144
144
|
bundle: true,
|
|
@@ -160,9 +160,10 @@ export const build = async (platform: Platform) => {
|
|
|
160
160
|
],
|
|
161
161
|
metafile: true,
|
|
162
162
|
write: false,
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
define: {
|
|
164
|
+
'process.env.RAJT_ENV': env,
|
|
165
|
+
// 'process.env.NODE_ENV': '"development"',
|
|
166
|
+
},
|
|
166
167
|
// tsconfig: join(_root, 'tsconfig.json'),
|
|
167
168
|
// sourcemap: true,
|
|
168
169
|
// logLevel: 'info',
|
|
@@ -257,7 +258,7 @@ export function cleanDB(key: string, config?: WranglerConfig) {
|
|
|
257
258
|
config ??= wranglerConfig()
|
|
258
259
|
if (!config?.d1_databases) return
|
|
259
260
|
for (const db of config.d1_databases) {
|
|
260
|
-
if (key
|
|
261
|
+
if (key === db.database_name || key === db.binding) {
|
|
261
262
|
const ns = 'miniflare-D1DatabaseObject'
|
|
262
263
|
const fileName = join(ns, durableObjectNamespace(db.preview_database_id || db.database_id || db.binding, ns) +'.sqlite')
|
|
263
264
|
|
|
@@ -462,7 +463,7 @@ export async function wait(ms: number) {
|
|
|
462
463
|
export function getDockerHost() {
|
|
463
464
|
const platform = process.platform
|
|
464
465
|
|
|
465
|
-
if (platform
|
|
466
|
+
if (platform === 'darwin') {
|
|
466
467
|
for (const socket of [
|
|
467
468
|
'/Users/'+ process.env.USER +'/.docker/run/docker.sock',
|
|
468
469
|
'/var/run/docker.sock',
|
|
@@ -475,7 +476,7 @@ export function getDockerHost() {
|
|
|
475
476
|
return 'tcp://localhost:2375'
|
|
476
477
|
}
|
|
477
478
|
|
|
478
|
-
return process.env.DOCKER_HOST || (platform
|
|
479
|
+
return process.env.DOCKER_HOST || (platform === 'win32' ? 'tcp://localhost:2375' : 'unix:///var/run/docker.sock')
|
|
479
480
|
}
|
|
480
481
|
|
|
481
482
|
export function makeFile(path: string, content: string) {
|
package/src/create-app.ts
CHANGED
|
@@ -21,10 +21,10 @@ const EHandler = async (e: Error | HTTPResponseError) => {
|
|
|
21
21
|
console.error(e)
|
|
22
22
|
|
|
23
23
|
switch (true) {
|
|
24
|
-
case 'status' in e && e.status
|
|
24
|
+
case 'status' in e && e.status === 401:
|
|
25
25
|
return response.unauthorized()
|
|
26
26
|
|
|
27
|
-
case 'status' in e && e.status
|
|
27
|
+
case 'status' in e && e.status === 400: // @ts-ignore
|
|
28
28
|
return response.badRequest(null, e?.message)
|
|
29
29
|
|
|
30
30
|
default:
|
|
@@ -69,13 +69,13 @@ export const createApp = <E extends Env>(options?: ServerOptions<E>) => {
|
|
|
69
69
|
if (isDev()) {
|
|
70
70
|
app.use('*', async function (c: Context, next: Next) {
|
|
71
71
|
const method = c.req.method
|
|
72
|
-
const route = matchedRoutes(c).find(route => route.method
|
|
72
|
+
const route = matchedRoutes(c).find(route => route.method === method)?.path
|
|
73
73
|
const logWithRoute = (args: string[]) => {
|
|
74
74
|
if (!route || !args.length) return args
|
|
75
75
|
return args.map(arg => {
|
|
76
76
|
if (!arg) return arg
|
|
77
77
|
const split = arg?.split(' ')
|
|
78
|
-
if (split.length < 3 || split[2]
|
|
78
|
+
if (split.length < 3 || split[2] === route)
|
|
79
79
|
return arg
|
|
80
80
|
|
|
81
81
|
split.splice(Math.min(3, split.length), 0, gray(route))
|
|
@@ -113,11 +113,11 @@ export const createApp = <E extends Env>(options?: ServerOptions<E>) => {
|
|
|
113
113
|
if (options?.init) options.init(app)
|
|
114
114
|
|
|
115
115
|
const routes = options?.routes || [] // @ts-ignore
|
|
116
|
-
const routeRegister = options?.routeRegister ? options.routeRegister : (_: Hono, route: Route) => {
|
|
116
|
+
const routeRegister = options?.routeRegister ? options.routeRegister : (_: Hono, route: Route) => {
|
|
117
117
|
if (Array.isArray(route)) { // @ts-ignore
|
|
118
118
|
_[getVerb[route[0]]](route[1], ...resolve(...route[2], route[3]))
|
|
119
119
|
} else { // @ts-ignore
|
|
120
|
-
_[route.method](route.path, ...resolve(...route.middlewares
|
|
120
|
+
_[route.method](route.path, ...resolve(...route.middlewares, route.handle))
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
|
package/src/http.ts
CHANGED
|
@@ -29,10 +29,10 @@ export const getVerb = [
|
|
|
29
29
|
]
|
|
30
30
|
|
|
31
31
|
function method(method: string, ...args: any[]): void | ClassDecorator {
|
|
32
|
-
if (args.length
|
|
32
|
+
if (args.length === 1 && typeof args[0] === 'function')
|
|
33
33
|
return _method(method, '/', args[0])
|
|
34
34
|
|
|
35
|
-
const path = typeof args[0]
|
|
35
|
+
const path = typeof args[0] === 'string' ? args[0] : '/'
|
|
36
36
|
return (target: Function) => _method(method, path, target)
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -117,7 +117,7 @@ export function Middlewares(...handlers: MiddlewareType[]) {
|
|
|
117
117
|
export function Auth(target: Function): void
|
|
118
118
|
export function Auth(): ClassDecorator
|
|
119
119
|
export function Auth(...args: any[]): void | ClassDecorator {
|
|
120
|
-
if (args.length
|
|
120
|
+
if (args.length === 1 && typeof args[0] === 'function')
|
|
121
121
|
return _auth(args[0])
|
|
122
122
|
|
|
123
123
|
return (target: any) => _auth(target)
|
package/src/open-api/register.ts
CHANGED
|
@@ -61,7 +61,8 @@ export function registerOpenAPI(app: Hono, conf: any) {
|
|
|
61
61
|
slug: opts.appName?.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/[^\w\s_-]/g, '').replace(/[\s_-]+/g, '_').replace(/[^\x00-\x7F]/g, '') +'_'+ opts.appVersion,
|
|
62
62
|
// hideDownloadButton: true,
|
|
63
63
|
// onLoaded: () => document?.querySelectorAll('[href="https://www.scalar.com"]')?.forEach(el => el.remove()),
|
|
64
|
-
customCss: `[href="https://www.scalar.com"]{display:none}`,
|
|
64
|
+
customCss: `[href="https://www.scalar.com"],.scalar-mcp-layer{display:none!important}`,
|
|
65
|
+
// favicon: 'https://example.com/favicon.png',
|
|
65
66
|
})
|
|
66
67
|
)
|
|
67
68
|
}
|
package/src/request.ts
CHANGED
|
@@ -27,6 +27,7 @@ export default class $Request {
|
|
|
27
27
|
#u: Authnz<any> | null = null
|
|
28
28
|
|
|
29
29
|
#host: string
|
|
30
|
+
#protocol: string
|
|
30
31
|
#routePath?: string
|
|
31
32
|
#matchedRoutes: RouterRoute[]
|
|
32
33
|
|
|
@@ -37,6 +38,7 @@ export default class $Request {
|
|
|
37
38
|
|
|
38
39
|
const url = new URL(c.req.raw.url)
|
|
39
40
|
this.#host = url.protocol +'//'+ url.host
|
|
41
|
+
this.#protocol = url.protocol
|
|
40
42
|
|
|
41
43
|
this.#matchedRoutes = matchedRoutes(c)
|
|
42
44
|
}
|
|
@@ -68,6 +70,13 @@ export default class $Request {
|
|
|
68
70
|
return this.has(prop, value)
|
|
69
71
|
}
|
|
70
72
|
|
|
73
|
+
createToken(data: any, exp: number) {
|
|
74
|
+
return Token.create(this, data, exp)
|
|
75
|
+
}
|
|
76
|
+
parseToken(token: string) {
|
|
77
|
+
return Token.parse(this, token)
|
|
78
|
+
}
|
|
79
|
+
|
|
71
80
|
get cx() {
|
|
72
81
|
return this.#c
|
|
73
82
|
}
|
|
@@ -90,6 +99,10 @@ export default class $Request {
|
|
|
90
99
|
return this.#c.req.header('user-agent')
|
|
91
100
|
}
|
|
92
101
|
|
|
102
|
+
get protocol() {
|
|
103
|
+
return this.#protocol
|
|
104
|
+
}
|
|
105
|
+
|
|
93
106
|
get url() {
|
|
94
107
|
return this.#c.req.raw.url
|
|
95
108
|
}
|
package/src/response.ts
CHANGED
|
@@ -58,7 +58,7 @@ export default class $Response {
|
|
|
58
58
|
headers?: HeaderRecord
|
|
59
59
|
): Response | Promise<Response> {
|
|
60
60
|
const res = (html: string) => this.raw(status, html, 'text/html; charset=UTF-8' as BaseMime, headers)
|
|
61
|
-
return typeof html
|
|
61
|
+
return typeof html === 'string'
|
|
62
62
|
? res(html)
|
|
63
63
|
: resolveCallback(html, HtmlEscapedCallbackPhase.Stringify, false, {}).then(res)
|
|
64
64
|
}
|
package/src/routes.ts
CHANGED
|
@@ -4,11 +4,13 @@ import { join, resolve, relative } from 'pathe'
|
|
|
4
4
|
import { IMPORT } from 't0n'
|
|
5
5
|
import glob from 'tiny-glob'
|
|
6
6
|
import { config } from 'dotenv'
|
|
7
|
-
import { describeRoute, resolver } from 'hono-openapi'
|
|
7
|
+
import { describeRoute, resolver, validator } from 'hono-openapi'
|
|
8
8
|
import { mimes } from 'hono/utils/mime'
|
|
9
9
|
import { STATUS_CODES } from 'node:http'
|
|
10
10
|
import { registerHandler, registerMiddleware } from './register'
|
|
11
11
|
import createApp from './create-app'
|
|
12
|
+
import _response from './response'
|
|
13
|
+
import _validator from './validator'
|
|
12
14
|
import { isAnonFn } from './utils/func'
|
|
13
15
|
import ensureDir from './utils/ensuredir'
|
|
14
16
|
import versionSHA from './utils/version-sha'
|
|
@@ -20,7 +22,7 @@ import { resolve as _resolve } from './utils/resolve'
|
|
|
20
22
|
import { highlightedMethod, highlightedURI } from './cli/utils'
|
|
21
23
|
|
|
22
24
|
import type * as z from 'zod'
|
|
23
|
-
import type { Routes, StandardSchemaV1 } from './types'
|
|
25
|
+
import type { Routes, Rule, StandardSchemaV1 } from './types'
|
|
24
26
|
|
|
25
27
|
const importName = (name?: string) => (name || 'Fn'+ Math.random().toString(36).substring(2)).replace(/\.ts$/, '')
|
|
26
28
|
const walk = async (dir: string, baseDir: string, fn: Function, parentMw: string[] = []): Promise<void> => {
|
|
@@ -44,7 +46,7 @@ const walk = async (dir: string, baseDir: string, fn: Function, parentMw: string
|
|
|
44
46
|
|
|
45
47
|
if (stat.isDirectory()) {
|
|
46
48
|
await walk(fullPath, baseDir, fn, currentMw)
|
|
47
|
-
} else if (file
|
|
49
|
+
} else if (file !== 'index.ts' && file.endsWith('.ts') && !file.endsWith('.d.ts')) {
|
|
48
50
|
const mod = await IMPORT(fullPath)
|
|
49
51
|
fn(fullPath, baseDir, mod.default, currentMw)
|
|
50
52
|
}
|
|
@@ -54,18 +56,18 @@ const walk = async (dir: string, baseDir: string, fn: Function, parentMw: string
|
|
|
54
56
|
function isZodSchema(obj: any): obj is z.ZodType {
|
|
55
57
|
return (
|
|
56
58
|
obj &&
|
|
57
|
-
typeof obj
|
|
59
|
+
typeof obj === 'object' &&
|
|
58
60
|
('_def' in obj || '_type' in obj) &&
|
|
59
61
|
(obj.safeParse !== undefined || obj.parse !== undefined)
|
|
60
62
|
)
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
function ResolveDescribeSchema(obj: any, deep: boolean = false) {
|
|
64
|
-
if (!obj || typeof obj
|
|
66
|
+
if (!obj || typeof obj !== 'object') return obj
|
|
65
67
|
if (isZodSchema(obj))
|
|
66
68
|
return { content: {'application/json': { schema: resolver(obj as unknown as StandardSchemaV1) }} }
|
|
67
69
|
|
|
68
|
-
if (obj.content && typeof obj.content
|
|
70
|
+
if (obj.content && typeof obj.content === 'object') {
|
|
69
71
|
for (const mediaType in obj.content) {
|
|
70
72
|
const contentItem = obj.content[mediaType]
|
|
71
73
|
if (contentItem?.schema && isZodSchema(contentItem.schema))
|
|
@@ -81,7 +83,7 @@ function ResolveDescribeSchema(obj: any, deep: boolean = false) {
|
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
for (const key in obj) {
|
|
84
|
-
if (obj[key] && typeof obj[key]
|
|
86
|
+
if (obj[key] && typeof obj[key] === 'object') {
|
|
85
87
|
obj[key] = ResolveDescribeSchema(obj[key], true)
|
|
86
88
|
|
|
87
89
|
if (!deep && !obj[key]?.description) {
|
|
@@ -135,7 +137,7 @@ export async function getRoutes(
|
|
|
135
137
|
}
|
|
136
138
|
|
|
137
139
|
const mw = (handle.mw?.length ? [...handle.mw, ...middlewares] : middlewares).flatMap(obj => {
|
|
138
|
-
return typeof obj
|
|
140
|
+
return typeof obj === 'string' ? obj : obj?.name || null
|
|
139
141
|
}).filter(Boolean) as Function[]
|
|
140
142
|
|
|
141
143
|
routes.push({
|
|
@@ -191,7 +193,7 @@ function extractHttpPath(file: string) {
|
|
|
191
193
|
.map(part => part.startsWith('[') && part.endsWith(']') ? ':'+ part.slice(1, -1) : part)
|
|
192
194
|
.join('/')
|
|
193
195
|
|
|
194
|
-
return route
|
|
196
|
+
return route === '/' ? '/' : route.replace(/\/$/, '')
|
|
195
197
|
}
|
|
196
198
|
|
|
197
199
|
export function sortRoutes(routes: Routes) {
|
|
@@ -210,7 +212,7 @@ export function sortRoutes(routes: Routes) {
|
|
|
210
212
|
return metaB.score - metaA.score
|
|
211
213
|
})
|
|
212
214
|
|
|
213
|
-
while (list.length && list.at(-1)?.path
|
|
215
|
+
while (list.length && list.at(-1)?.path === '/') {
|
|
214
216
|
const last = list.pop()
|
|
215
217
|
last && list.unshift(last)
|
|
216
218
|
}
|
|
@@ -275,9 +277,9 @@ export async function getConfigs(
|
|
|
275
277
|
const keyPath = extractName(file).split('.')
|
|
276
278
|
|
|
277
279
|
keyPath.reduce((acc, key, index) => {
|
|
278
|
-
if (index
|
|
280
|
+
if (index === keyPath.length - 1) {
|
|
279
281
|
acc[key] = mod.default
|
|
280
|
-
} else if (!acc[key] || typeof acc[key]
|
|
282
|
+
} else if (!acc[key] || typeof acc[key] !== 'object') {
|
|
281
283
|
acc[key] = {}
|
|
282
284
|
}
|
|
283
285
|
|
|
@@ -295,15 +297,15 @@ function stringifyToJS(value: unknown): string {
|
|
|
295
297
|
|
|
296
298
|
const type = typeof value
|
|
297
299
|
|
|
298
|
-
if (type
|
|
299
|
-
if (type
|
|
300
|
-
if (type
|
|
301
|
-
if (type
|
|
300
|
+
if (type === 'string') return JSON.stringify(value)
|
|
301
|
+
if (type === 'number' || type === 'boolean') return String(value)
|
|
302
|
+
if (type === 'bigint') return `${value}n`
|
|
303
|
+
if (type === 'function') return value.toString()
|
|
302
304
|
|
|
303
305
|
if (Array.isArray(value))
|
|
304
306
|
return `[${value.map(stringifyToJS).join(',')}]`
|
|
305
307
|
|
|
306
|
-
if (type
|
|
308
|
+
if (type === 'object') {
|
|
307
309
|
const entries = Object.entries(value as Record<string, unknown>)
|
|
308
310
|
.map(([key, val]) => `${IDENTIFIER_RE.test(key) ? key : JSON.stringify(key)}:${stringifyToJS(val)}`)
|
|
309
311
|
|
|
@@ -326,7 +328,7 @@ async function dependencyPath(lib: string) {
|
|
|
326
328
|
export async function cacheRoutes() {
|
|
327
329
|
const env = Object.entries(
|
|
328
330
|
config({ path: join(_root, '.env.prod') })?.parsed || {}
|
|
329
|
-
).filter(([key, val]) => key?.toLowerCase().
|
|
331
|
+
).filter(([key, val]) => key?.toLowerCase().startsWith('aws')) // prevent AWS credentials
|
|
330
332
|
|
|
331
333
|
const version = versionSHA('../../.git') // @ts-ignore
|
|
332
334
|
env.push(['VERSION_SHA', process.env['VERSION_SHA'] = version]) // @ts-ignore
|
|
@@ -358,9 +360,13 @@ export async function cacheRoutes() {
|
|
|
358
360
|
registerHandler(h[1], mod[h[1]])
|
|
359
361
|
}
|
|
360
362
|
|
|
363
|
+
_validator.setParser((rule: Rule) => validator(rule.target, rule.schema, (result, c) => {
|
|
364
|
+
if (!result.success) // @ts-ignore
|
|
365
|
+
return _response.badRequest(result.error)
|
|
366
|
+
}))
|
|
361
367
|
// @ts-ignore
|
|
362
368
|
const openApi = await generateOpenAPI(createApp({ routes, routeRegister: (app: Hono, route: Route) => {
|
|
363
|
-
app[route.method](route.path, describeRoute(route.desc), ..._resolve(...route.middlewares
|
|
369
|
+
app[route.method](route.path, describeRoute(route.desc), ..._resolve(...route.middlewares, route.handle))
|
|
364
370
|
} }), configs?.rajt || {})
|
|
365
371
|
|
|
366
372
|
const iPath = join(_root, '.rajt/imports.mjs')
|
|
@@ -374,7 +380,7 @@ export async function cacheRoutes() {
|
|
|
374
380
|
stringifyToJS(Object.fromEntries(routes.map(r => ([r.path + r.method, r.name]))))
|
|
375
381
|
|
|
376
382
|
writeFileSync(iPath, `// AUTO-GENERATED FILE - DO NOT EDIT
|
|
377
|
-
${env?.length ? `import { Envir } from '${await dependencyPath('t0n')}/
|
|
383
|
+
${env?.length ? `import { Envir } from '${await dependencyPath('t0n')}/src/envir'\nEnvir.add({${env.map(([key, val]) => key +':'+ stringifyToJS(val)).join(',')}})` : ''}
|
|
378
384
|
${Object.entries(configs)?.length ? `import Config from '${_rajtDir}/src/config'\nConfig.add(${stringifyToJS(configs)})` : ''}
|
|
379
385
|
|
|
380
386
|
import { registerHandler, registerMiddleware } from '${_rajtDir}/src/register'
|
package/src/utils/environment.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
const prd = Symbol('prd')
|
|
2
2
|
const dev = Symbol('dev')
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// @ts-ignore
|
|
5
|
+
let env = process.env.RAJT_ENV || detectEnvironment()
|
|
5
6
|
|
|
6
|
-
export const getEnv = () => env
|
|
7
|
+
export const getEnv = () => env // @ts-ignore
|
|
7
8
|
export const setEnv = (e: symbol) => env = e
|
|
8
9
|
|
|
9
|
-
export function detectEnvironment() {
|
|
10
|
+
export function detectEnvironment(): symbol {
|
|
10
11
|
try {
|
|
11
12
|
if (
|
|
12
|
-
process.env?.npm_lifecycle_event
|
|
13
|
-
|| process.env?.npm_lifecycle_script
|
|
13
|
+
process.env?.npm_lifecycle_event === 'dev'
|
|
14
|
+
|| process.env?.npm_lifecycle_script === 'rajt'
|
|
14
15
|
|| process.env?.AWS_SAM_LOCAL
|
|
15
16
|
// || process?.argv?.includes('--dev')
|
|
16
17
|
|| process?.argv?.some(arg => ['-port', '-platform', '--dev', '--development', '--watch'].includes(arg))
|
|
@@ -24,10 +25,11 @@ export function detectEnvironment() {
|
|
|
24
25
|
return prd
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
export const isEnv = (e: symbol) => env
|
|
28
|
+
export const isEnv = (e: symbol) => env === e
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
export const
|
|
30
|
+
// @ts-ignore
|
|
31
|
+
export const isDev = () => env === dev
|
|
32
|
+
export const isProd = () => env === prd
|
|
31
33
|
|
|
32
34
|
export const isDevelopment = isDev
|
|
33
35
|
export const isProduction = isProd
|
package/src/utils/func.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export const isAsyncFn = (fn: any) => {
|
|
2
|
-
return fn?.constructor?.name
|
|
2
|
+
return fn?.constructor?.name === 'AsyncFunction'
|
|
3
3
|
|| fn.toString().toLowerCase().trim().startsWith('async')
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
export const isAnonFn = (fn: any) => {
|
|
7
|
-
return fn?.name
|
|
7
|
+
return fn?.name === '' || fn?.name === 'anonymous'
|
|
8
8
|
}
|
package/src/utils/log.ts
CHANGED
|
@@ -54,7 +54,7 @@ const LOGGING_METHOD = {
|
|
|
54
54
|
|
|
55
55
|
function prefixedLog(prefixType: keyof typeof prefixes, ...msg: any[]) {
|
|
56
56
|
const length = msg.length
|
|
57
|
-
if ((msg[0] === '' || msg[0] === undefined) && length
|
|
57
|
+
if ((msg[0] === '' || msg[0] === undefined) && length === 1)
|
|
58
58
|
msg.shift()
|
|
59
59
|
|
|
60
60
|
const consoleMethod: keyof typeof LOGGING_METHOD =
|
|
@@ -64,12 +64,12 @@ function prefixedLog(prefixType: keyof typeof prefixes, ...msg: any[]) {
|
|
|
64
64
|
|
|
65
65
|
const prefix = prefixes[prefixType]
|
|
66
66
|
// If there's no message, don't print the prefix but a new line
|
|
67
|
-
if (length
|
|
67
|
+
if (length === 0) {
|
|
68
68
|
console[consoleMethod]('')
|
|
69
69
|
} else {
|
|
70
70
|
// Ensure if there's ANSI escape codes it's concatenated into one string.
|
|
71
71
|
// Chrome DevTool can only handle color if it's in one string.
|
|
72
|
-
if (length
|
|
72
|
+
if (length === 1 && typeof msg[0] === 'string') {
|
|
73
73
|
console[consoleMethod](prefix +' '+ msg[0])
|
|
74
74
|
} else {
|
|
75
75
|
console[consoleMethod](prefix, ...msg)
|
package/src/utils/port.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { error, warn } from './log'
|
|
|
5
5
|
export function withPort(desiredPort: number, cb: (port: number) => void, maxAttempts = 10) {
|
|
6
6
|
getAvailablePort(desiredPort)
|
|
7
7
|
.then(([port, pid]) => {
|
|
8
|
-
if (port
|
|
8
|
+
if (port !== desiredPort)
|
|
9
9
|
warn(
|
|
10
10
|
`Port ${desiredPort} is in use by ${pid ? 'process '+ pid : 'an unknown process'}, using available port ${port} instead`
|
|
11
11
|
)
|
package/src/utils/resolve.ts
CHANGED
|
@@ -3,10 +3,10 @@ import { getHandler } from '../register'
|
|
|
3
3
|
export function resolve(...objs: any[]) {
|
|
4
4
|
const _ = []
|
|
5
5
|
for (let obj of objs) {
|
|
6
|
-
if (typeof obj
|
|
6
|
+
if (typeof obj === 'string')
|
|
7
7
|
obj = getHandler(obj)
|
|
8
8
|
|
|
9
|
-
if (typeof obj
|
|
9
|
+
if (typeof obj === 'function' && obj?.length === 2) {
|
|
10
10
|
|
|
11
11
|
} else if (obj?.run) {
|
|
12
12
|
_.push(...obj.run())
|
|
@@ -34,7 +34,7 @@ export function resolve(...objs: any[]) {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export function resolveMiddleware(obj: any) {
|
|
37
|
-
if (typeof obj
|
|
37
|
+
if (typeof obj === 'function' && obj.length === 2)
|
|
38
38
|
return obj
|
|
39
39
|
|
|
40
40
|
if (obj?.factory)
|
package/src/validator.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { zValidator } from '@hono/zod-validator'
|
|
2
2
|
import response from './response'
|
|
3
|
+
import type * as z from 'zod'
|
|
3
4
|
import type {
|
|
4
5
|
Rule, Rules, RuleFn,
|
|
5
6
|
ValidationTargets,
|
|
6
7
|
zObject,
|
|
8
|
+
MiddlewareHandler,
|
|
7
9
|
} from './types'
|
|
8
10
|
|
|
9
11
|
export default class $Validator {
|
|
@@ -36,11 +38,33 @@ export default class $Validator {
|
|
|
36
38
|
static readonly header = $Validator.fn('header')!
|
|
37
39
|
static readonly cookie = $Validator.fn('cookie')!
|
|
38
40
|
|
|
41
|
+
static #parser = (rule: Rule) => zValidator(rule.target, rule.schema, (result, c) => {
|
|
42
|
+
if (!result.success) // @ts-ignore
|
|
43
|
+
return response.badRequest(formatZodErrors(result.error.issues || []))
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
static setParser(parser: (rule: Rule) => MiddlewareHandler) {
|
|
47
|
+
this.#parser = parser
|
|
48
|
+
}
|
|
49
|
+
|
|
39
50
|
static parse(rules: Rules): Function[] {
|
|
40
51
|
return (Array.isArray(rules) ? rules : [rules]) // @ts-ignore
|
|
41
|
-
.flatMap(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
52
|
+
.flatMap(this.#parser)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function formatZodErrors(issues: z.ZodIssue[]): Record<string, string[]> {
|
|
57
|
+
const result: Record<string, string[]> = {}
|
|
58
|
+
|
|
59
|
+
for (const issue of issues) {
|
|
60
|
+
const path = issue.path.join('.')
|
|
61
|
+
const key = path || 'root'
|
|
62
|
+
|
|
63
|
+
if (!result[key])
|
|
64
|
+
result[key] = []
|
|
65
|
+
|
|
66
|
+
result[key].push(issue.message)
|
|
45
67
|
}
|
|
68
|
+
|
|
69
|
+
return result
|
|
46
70
|
}
|