rajt 0.0.99 → 0.0.101

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 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(new URL(import.meta.url).pathname);
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 != 'undefined';
25
- let tsxPath;
26
-
27
- if (!isBun) {
28
- const _tsxPath = 'node_modules/.bin/tsx'
29
- const tsxPaths = [
30
- // join(__dirname, '../node_modules/.bin/tsx'),
31
- // join(__dirname, '../../.bin/tsx'),
32
- join(__dirname, '../'+ _tsxPath),
33
- join(__dirname, '../../'+ _tsxPath),
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 (!tsxPath) {
46
- console.error('TypeScript file found but tsx is not available. Please install tsx:');
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
- return spawn(
56
- process.execPath,
57
- [
42
+ args = [
58
43
  '--no-warnings',
59
- ...process.execArgv,
60
- tsxPath,
61
- join(__dirname, '../src/cli/index.ts').replace(/^\\/, ''),
62
- ...process.argv.slice(2),
63
- ].filter(arg => arg && !arg.includes('experimental-vm-modules') && !arg.includes('loader')),
64
- {
65
- stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
66
- env: {
67
- ...process.env,
68
- NODE_ENV: process.env.NODE_ENV || 'development',
69
- TSX_DISABLE_CACHE: process.env.TSX_DISABLE_CACHE || '1',
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
- .on('exit', code => process.exit(code == null ? 0 : code))
74
- .on('message', message => process.send && process.send(message))
75
- .on('disconnect', () => process.disconnect && process.disconnect());
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
- rajtProcess = runRajt();
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.99",
4
+ "version": "0.0.101",
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.9",
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.9.40",
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.10",
45
+ "cripta": "^0.1.12",
46
46
  "dotenv": "^16.5.0",
47
47
  "esbuild": "^0.25.2",
48
- "forj": "^0.1.8",
49
- "hono": "^4.11.7",
48
+ "forj": "^0.1.10",
49
+ "hono": "^4.12.8",
50
50
  "hono-openapi": "^1.3.0",
51
- "localflare-api": "^0.4.2",
52
- "localflare-core": "^0.4.2",
53
- "miniflare": "^4.20260301.1",
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.12",
56
+ "t0n": "^0.1.13",
57
57
  "tiny-glob": "^0.2",
58
58
  "tsx": "^4.19.4",
59
59
  "wrangler": "^4.61.0",
@@ -19,7 +19,7 @@ export class Ability {
19
19
  }
20
20
 
21
21
  static fromAction(target: any): string {
22
- return !target ? '' : this.format(target.name.length > 3 ? target.name : (target?.p || ''))
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) {
package/src/auth/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { Ability } from './ability'
2
2
  export { Authnz } from './authnz'
3
3
  export { Token } from './token'
4
+ export { Autorized } from './middlewares'
4
5
 
5
6
  export * from './types'
@@ -0,0 +1,23 @@
1
+ import { Ability } from './ability'
2
+ import response from '../response'
3
+ import { GET_REQUEST } from '../request'
4
+ import Config from '../config'
5
+ import { verbAlias } from '../http'
6
+ import type {
7
+ Context, Next,
8
+ IRequest,
9
+ } from '../types'
10
+
11
+ export async function Autorized(c: Context, next: Next) {
12
+ const req = c.get(GET_REQUEST as unknown as string) as IRequest
13
+ const ability = Ability.fromAction(Config.get(`routes.${req.routePath}$`+ verbAlias[req.method.toLowerCase()]))
14
+
15
+ if (!req?.user || !ability || req.cant(ability))
16
+ return response.unauthorized()
17
+
18
+ await next()
19
+ }
20
+
21
+ // export function Unautorized() {
22
+
23
+ // }
@@ -3,6 +3,7 @@ import { spawn, type ChildProcess } from 'node:child_process'
3
3
 
4
4
  import { defineCommand } from 'citty'
5
5
  import type { Miniflare } from 'miniflare'
6
+ import type { WranglerConfig } from 'localflare-core'
6
7
  import {
7
8
  build, wait, watch, normalizePlatform, platformError, getRuntime,
8
9
  wranglerConfig, createMiniflare, localflareManifest,
@@ -53,7 +54,7 @@ export default defineCommand({
53
54
  event('Building..')
54
55
  }
55
56
  const fn = async () => {
56
- building && await build(platform)
57
+ building && await build(platform, 'dev')
57
58
  await start()
58
59
  }
59
60
 
@@ -161,21 +162,12 @@ export default defineCommand({
161
162
  })
162
163
  case 'cf':
163
164
  return withPort(desiredPort, async (port) => {
164
- started(port)
165
- let worker: Miniflare | null = null
166
- let localflare: Miniflare | null = null
167
- const startWorker = async () => {
168
- if (worker) await worker.dispose()
169
- if (localflare) await localflare.dispose()
165
+ started(port)
170
166
 
171
- // await wait(500)
167
+ let localflare: Miniflare | null = null
168
+ const startLocalflare = async (workerConfig: WranglerConfig) => {
169
+ if (localflare) return
172
170
 
173
- const workerConfig = await wranglerConfig()
174
- workerConfig.host = host
175
- workerConfig.liveReload = false
176
-
177
- worker = createMiniflare({ ...workerConfig, port })
178
- await worker.ready
179
171
  localflare = createMiniflare({
180
172
  ...workerConfig,
181
173
  vars: {
@@ -185,8 +177,23 @@ export default defineCommand({
185
177
  main: '.rajt/localfire.js',
186
178
  port: 8788,
187
179
  inspectorPort: 9230,
188
- })
180
+ })
181
+
189
182
  await localflare.ready
183
+ }
184
+
185
+ let worker: Miniflare | null = null
186
+ const startWorker = async () => {
187
+ if (worker) await worker.dispose()
188
+
189
+ const workerConfig = await wranglerConfig() // @ts-ignore
190
+ workerConfig.host = host // @ts-ignore
191
+ workerConfig.liveReload = false
192
+
193
+ worker = createMiniflare({ ...workerConfig, port })
194
+ await worker.ready
195
+
196
+ if (!localflare) await startLocalflare(workerConfig)
190
197
  }
191
198
 
192
199
  await startApp(startWorker)
@@ -31,7 +31,7 @@ export default defineCommand({
31
31
  try {
32
32
  const migrations = await Migrator.dir(database).queue()
33
33
  const pending = migrations.pending
34
- const migrated = migrations.migrated
34
+ const migrated = migrations.migrated
35
35
 
36
36
  switch (action) {
37
37
  case 'migrate':
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 == 'undefined'
23
- && import.meta.url == `file://${process.argv[1].replace(/\\/g, '/')}`
22
+ return typeof vitest === 'undefined'
23
+ && import.meta.url?.endsWith(process.argv[1].replace(/\\/g, '/'))
24
24
  } catch {
25
25
  return false
26
26
  }
package/src/cli/utils.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import ts from 'typescript'
1
2
  import esbuild from 'esbuild'
2
3
  import { Miniflare } from 'miniflare'
3
4
  import { mkdirSync, existsSync, statSync, readdirSync, rmSync, unlinkSync, copyFileSync, writeFileSync } from 'node:fs'
@@ -69,8 +70,54 @@ const nodeModules = [
69
70
  'async_hooks', 'console', 'fsevents',
70
71
  ].flatMap(lib => ['node:'+ lib, lib])
71
72
 
73
+ const printer = ts.createPrinter()
74
+ function stripDecorators(source: string) {
75
+ const sourceFile = ts.createSourceFile(
76
+ 'tmp.ts', source, ts.ScriptTarget.ESNext, false
77
+ )
78
+
79
+ const transformer: ts.TransformerFactory<ts.SourceFile> = (context) => {
80
+ const visit: ts.Visitor = (node) => {
81
+ if (ts.isClassDeclaration(node) && node.modifiers?.length) {
82
+ let hasDecorator = false
83
+ const modifiers = []
84
+
85
+ for (const m of node.modifiers) {
86
+ if (m.kind === ts.SyntaxKind.Decorator) {
87
+ hasDecorator = true
88
+ continue
89
+ }
90
+ modifiers.push(m)
91
+ }
92
+
93
+ if (hasDecorator) {
94
+ return ts.factory.updateClassDeclaration(
95
+ node,
96
+ modifiers.length ? modifiers : undefined,
97
+ node.name,
98
+ node.typeParameters,
99
+ node.heritageClauses,
100
+ node.members
101
+ )
102
+ }
103
+ }
104
+
105
+ return ts.visitEachChild(node, visit, context)
106
+ }
107
+
108
+ return (node) => ts.visitNode(node, visit) as ts.SourceFile
109
+ }
110
+
111
+ const result = ts.transform(sourceFile, [transformer])
112
+ const code = printer.printFile(result.transformed[0])
113
+
114
+ result.dispose()
115
+
116
+ return code
117
+ }
118
+
72
119
  const dist = '.rajt/dist'
73
- export const build = async (platform: Platform) => {
120
+ export const build = async (platform: Platform, env: string = 'prd') => {
74
121
  const startTime = Date.now()
75
122
 
76
123
  const isCF = platform == 'cf'
@@ -86,6 +133,10 @@ export const build = async (platform: Platform) => {
86
133
 
87
134
  if (['bun', 'vercel'].includes(platform)) platform = 'cf'
88
135
 
136
+ const USE_STRICT_RE = /(["'`])\s*use strict\s*\1;?/g
137
+ const decoder = new TextDecoder()
138
+ const encoder = new TextEncoder()
139
+
89
140
  // @ts-ignore
90
141
  platform = platform != 'node' ? '-'+ platform : ''
91
142
  const opts = {
@@ -109,57 +160,49 @@ export const build = async (platform: Platform) => {
109
160
  ],
110
161
  metafile: true,
111
162
  write: false,
112
- // define: {
113
- // 'process.env.NODE_ENV': '"development"'
114
- // },
115
- // loader: {
116
- // '.ts': 'ts',
117
- // '.js': 'js'
118
- // },
163
+ define: {
164
+ 'process.env.RAJT_ENV': env,
165
+ // 'process.env.NODE_ENV': '"development"',
166
+ },
119
167
  // tsconfig: join(_root, 'tsconfig.json'),
120
168
  // sourcemap: true,
121
169
  // logLevel: 'info',
122
170
  plugins: [
123
171
  {
124
- name: 'rajt-resolver',
172
+ name: 'rajt',
125
173
  setup(build) {
126
174
  build.onResolve({ filter: /\.rajt[\/\\]/ }, args => ({ path: join(_root, args.path) }))
127
- }
128
- },
129
- {
130
- name: 'preserve-class-names',
131
- setup(build) {
132
- build.onLoad(
133
- { filter: /(actions|features|routes)\/.*\.ts$/ },
134
- async (args) => {
135
- const contents = await readFile(args.path, 'utf8')
136
- const result = await esbuild.transform(contents, {
137
- loader: 'ts',
138
- minify: true,
139
- keepNames: true
140
- })
141
- return { contents: result.code, loader: 'ts' }
142
- }
143
- )
144
- },
145
- },
146
- {
147
- name: 'remove-use-strict',
148
- setup(build) {
175
+
176
+ // strip decorators
177
+ build.onLoad({ filter: /\.ts$/ }, async (args) => {
178
+ const source = await readFile(args.path, 'utf8')
179
+ return { contents: stripDecorators(source), loader: 'ts' }
180
+ })
181
+
182
+ // remove "use strict"
183
+ const write = build.initialOptions.write
149
184
  build.onEnd(async (result) => {
150
- if (!result.outputFiles) return
151
-
152
- const files = result.outputFiles.filter(file => file.path.endsWith('.js'))
153
- await Promise.all(files.map(async file => {
154
- if (!file.path.endsWith('.js')) return
155
-
156
- await writeFile(
157
- file.path,
158
- new TextDecoder()
159
- .decode(file.contents)
160
- .replace(/(["'`])\s*use strict\s*\1;?|`use strict`;?/g, '')
161
- )
162
- }))
185
+ const files = result.outputFiles
186
+ if (!files) return
187
+
188
+ const tasks: Promise<void>[] = []
189
+
190
+ for (const file of files) {
191
+ if (!file.path.endsWith('.js')) continue
192
+
193
+ let code = decoder.decode(file.contents)
194
+
195
+ if (USE_STRICT_RE.test(code))
196
+ code = code.replace(USE_STRICT_RE, '')
197
+
198
+ if (write) {
199
+ file.contents = encoder.encode(code)
200
+ } else {
201
+ tasks.push(writeFile(file.path, code))
202
+ }
203
+ }
204
+
205
+ if (tasks.length) await Promise.all(tasks)
163
206
  })
164
207
  }
165
208
  },
package/src/create-app.ts CHANGED
@@ -7,13 +7,14 @@ import type {
7
7
  HTTPResponseError,
8
8
  ServerOptions,
9
9
  } from './types'
10
- import { resolve, resolveMiddleware, mw } from './utils/resolve'
11
- import { getMiddlewares, getHandler } from './register'
10
+ import { resolve, resolveMiddleware } from './utils/resolve'
11
+ import { getMiddlewares } from './register'
12
12
  import request, { GET_REQUEST } from './request'
13
13
  import response from './response'
14
14
  import { isDev } from './utils/environment'
15
15
  import { gray } from 't0n/color'
16
16
  import { Route } from './types'
17
+ import { getVerb } from './http'
17
18
 
18
19
  const NFHandler = () => response.notFound()
19
20
  const EHandler = async (e: Error | HTTPResponseError) => {
@@ -96,11 +97,14 @@ export const createApp = <E extends Env>(options?: ServerOptions<E>) => {
96
97
  if (c.env) Envir.add(c.env)
97
98
  await next()
98
99
  })
99
- getMiddlewares().forEach(mw => {
100
+
101
+ const middlewares = getMiddlewares()
102
+ for (const mw of middlewares) {
100
103
  const h = async (c: Context, next: Next) => await resolveMiddleware(mw)(c.get(GET_REQUEST as unknown as string), next)
101
104
  // @ts-ignore
102
105
  mw?.path ? app.use(String(mw.path), h) : app.use(h)
103
- })
106
+ }
107
+
104
108
  // @ts-ignore
105
109
  app.onError(options?.onError || EHandler)
106
110
  // @ts-ignore
@@ -109,11 +113,11 @@ export const createApp = <E extends Env>(options?: ServerOptions<E>) => {
109
113
  if (options?.init) options.init(app)
110
114
 
111
115
  const routes = options?.routes || [] // @ts-ignore
112
- const routeRegister = options?.routeRegister ? options.routeRegister : (_: Hono, route: Route) => { // @ts0ignore
116
+ const routeRegister = options?.routeRegister ? options.routeRegister : (_: Hono, route: Route) => {
113
117
  if (Array.isArray(route)) { // @ts-ignore
114
- _[route[0]](route[1], ...mw(route[2], route[3]), ...resolve(getHandler(route[3]), route[3]))
118
+ _[getVerb[route[0]]](route[1], ...resolve(...route[2], route[3]))
115
119
  } else { // @ts-ignore
116
- _[route.method](route.path, ...mw(route.middlewares, route.name), ...resolve(route.handle, route.name))
120
+ _[route.method](route.path, ...resolve(...route.middlewares, route.handle))
117
121
  }
118
122
  }
119
123
 
package/src/dev.ts CHANGED
@@ -7,12 +7,11 @@ import { registerOpenAPI } from './open-api/register'
7
7
  import createApp from './create-app'
8
8
  import { Ability } from 'rajt/auth'
9
9
  import { setEnv, detectEnvironment } from 'rajt/env'
10
+ import { _root } from './utils/paths'
10
11
 
11
12
  setEnv(detectEnvironment())
12
13
 
13
- const __dirname = join(dirname(new URL(import.meta.url).pathname), '../../../')
14
-
15
- config({ path: join(__dirname, '.env.dev') })
14
+ config({ path: join(_root, '.env.dev') })
16
15
 
17
16
  Config.add(await getConfigs())
18
17
 
package/src/http.ts CHANGED
@@ -1,14 +1,33 @@
1
- import { MiddlewareType } from './middleware'
2
- import response from './response'
3
- import { GET_REQUEST } from './request'
4
- import { Ability } from './auth'
5
- import mergeMiddleware from './utils/merge-middleware'
1
+ import { Autorized } from './auth'
2
+ import { type MiddlewareType, mergeMiddleware } from './middleware'
6
3
  import type {
7
- Context, Next,
8
- IRequest,
9
4
  DescribeRouteOptions,
10
5
  } from './types'
11
6
 
7
+ export const verbAlias = {
8
+ get: 0,
9
+ post: 1,
10
+ put: 2,
11
+ patch: 3,
12
+ delete: 4,
13
+ head: 5,
14
+ options: 6,
15
+ connect: 7,
16
+ trace: 8,
17
+ } as Record<string, number>
18
+
19
+ export const getVerb = [
20
+ 'get',
21
+ 'post',
22
+ 'put',
23
+ 'patch',
24
+ 'delete',
25
+ 'head',
26
+ 'options',
27
+ 'connect',
28
+ 'trace',
29
+ ]
30
+
12
31
  function method(method: string, ...args: any[]): void | ClassDecorator {
13
32
  if (args.length == 1 && typeof args[0] == 'function')
14
33
  return _method(method, '/', args[0])
@@ -112,15 +131,7 @@ function _auth(target: Function | any) {
112
131
  if (!target.d?.responses) target.d.responses = {}
113
132
  target.d.responses[401] = {description: 'Unauthorized'}
114
133
 
115
- mergeMiddleware(target, async (c: Context, next: Next) => {
116
- const req = c.get(GET_REQUEST as unknown as string) as IRequest
117
- const ability = Ability.fromAction(target)
118
-
119
- if (!req?.user || !ability || req.cant(ability))
120
- return response.unauthorized()
121
-
122
- await next()
123
- })
134
+ mergeMiddleware(target, Autorized)
124
135
  }
125
136
 
126
137
  function _describe(spec: DescribeRouteOptions): ClassDecorator{
package/src/middleware.ts CHANGED
@@ -15,3 +15,7 @@ export class Middleware {
15
15
  }
16
16
 
17
17
  export const toHonoMiddleware = (mw: MiddlewareHandler) => async (req: IRequest, next: Next) => await mw(req.cx, next)
18
+
19
+ export function mergeMiddleware(target: Function | any, ...handlers: MiddlewareType[]) {
20
+ target.mw = [...target?.mw, ...handlers.flat()]
21
+ }
@@ -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/register.ts CHANGED
@@ -1,9 +1,6 @@
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
- // console.warn(`Handler "${id}" has already been registered`)
6
-
7
4
  handlers[id] = handler
8
5
  }
9
6
 
package/src/request.ts CHANGED
@@ -27,7 +27,8 @@ export default class $Request {
27
27
  #u: Authnz<any> | null = null
28
28
 
29
29
  #host: string
30
- #routePath: string
30
+ #protocol: string
31
+ #routePath?: string
31
32
  #matchedRoutes: RouterRoute[]
32
33
 
33
34
  constructor(c: Context) {
@@ -37,8 +38,8 @@ 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
- this.#routePath = routePath(c)
42
43
  this.#matchedRoutes = matchedRoutes(c)
43
44
  }
44
45
 
@@ -69,6 +70,13 @@ export default class $Request {
69
70
  return this.has(prop, value)
70
71
  }
71
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
+
72
80
  get cx() {
73
81
  return this.#c
74
82
  }
@@ -91,8 +99,8 @@ export default class $Request {
91
99
  return this.#c.req.header('user-agent')
92
100
  }
93
101
 
94
- get routePath() {
95
- return this.#routePath
102
+ get protocol() {
103
+ return this.#protocol
96
104
  }
97
105
 
98
106
  get url() {
@@ -116,6 +124,15 @@ export default class $Request {
116
124
  return this.#c.req.raw.method
117
125
  }
118
126
 
127
+ get routePath() {
128
+ this.#routePath ??= routePath(this.#c)
129
+ return this.#routePath
130
+ }
131
+
132
+ get routeIndex() {
133
+ return this.#c.req.routeIndex
134
+ }
135
+
119
136
  get matchedRoutes() {
120
137
  return this.#matchedRoutes
121
138
  }
package/src/routes.ts CHANGED
@@ -1,27 +1,29 @@
1
1
  import { copyFileSync, existsSync, readdirSync, statSync, writeFileSync } from 'node:fs'
2
2
  import { join, resolve, relative } from 'pathe'
3
3
 
4
+ import { IMPORT } from 't0n'
4
5
  import glob from 'tiny-glob'
5
6
  import { config } from 'dotenv'
6
-
7
- import { IMPORT } from 't0n'
7
+ import { describeRoute, resolver, validator } from 'hono-openapi'
8
+ import { mimes } from 'hono/utils/mime'
9
+ import { STATUS_CODES } from 'node:http'
8
10
  import { registerHandler, registerMiddleware } from './register'
9
11
  import createApp from './create-app'
12
+ import _response from './response'
13
+ import _validator from './validator'
10
14
  import { isAnonFn } from './utils/func'
11
15
  import ensureDir from './utils/ensuredir'
12
16
  import versionSHA from './utils/version-sha'
13
- import type { Routes, StandardSchemaV1 } from './types'
14
17
  import { rn, substep, warn } from './utils/log'
15
- import { _root } from './utils/paths'
18
+ import { _root, _rajt } from './utils/paths'
16
19
  import { generateOpenAPI } from './open-api/spec'
17
- import type * as z from 'zod'
18
- import { describeRoute, resolver } from 'hono-openapi'
19
- import { mimes } from 'hono/utils/mime'
20
- import { STATUS_CODES } from 'node:http'
21
- import { mw, resolve as _resolve } from './utils/resolve'
22
-
20
+ import { verbAlias } from './http'
21
+ import { resolve as _resolve } from './utils/resolve'
23
22
  import { highlightedMethod, highlightedURI } from './cli/utils'
24
23
 
24
+ import type * as z from 'zod'
25
+ import type { Routes, Rule, StandardSchemaV1 } from './types'
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> => {
27
29
  if (!existsSync(dir)) return
@@ -61,7 +63,7 @@ function isZodSchema(obj: any): obj is z.ZodType {
61
63
  }
62
64
 
63
65
  function ResolveDescribeSchema(obj: any, deep: boolean = false) {
64
- if (!obj || typeof obj !== 'object') return 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
 
@@ -134,12 +136,16 @@ export async function getRoutes(
134
136
  }
135
137
  }
136
138
 
139
+ const mw = (handle.mw?.length ? [...handle.mw, ...middlewares] : middlewares).flatMap(obj => {
140
+ return typeof obj == 'string' ? obj : obj?.name || null
141
+ }).filter(Boolean) as Function[]
142
+
137
143
  routes.push({
138
144
  method, path: uri,
139
145
  name,
140
146
  file,
141
147
  // @ts-ignore
142
- middlewares,
148
+ middlewares: mw,
143
149
  handle,
144
150
  desc,
145
151
  })
@@ -339,13 +345,28 @@ export async function cacheRoutes() {
339
345
 
340
346
  const middlewares = await getMiddlewares()
341
347
  const configs = await getConfigs()
348
+ const handlers = [
349
+ ['auth/middlewares', 'Autorized', 'Autorized'],
350
+ ]
342
351
 
343
- routes.forEach(r => registerHandler(r.name, r.handle))
344
- middlewares.forEach(mw => registerMiddleware(mw.handle))
352
+ for (const r of routes)
353
+ registerHandler(r.name, r.handle)
345
354
 
355
+ for (const mw of middlewares)
356
+ registerMiddleware(mw.handle)
357
+
358
+ for (const h of handlers) {
359
+ const mod = await IMPORT(join(_rajt, h[0]))
360
+ registerHandler(h[1], mod[h[1]])
361
+ }
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
+ }))
346
367
  // @ts-ignore
347
368
  const openApi = await generateOpenAPI(createApp({ routes, routeRegister: (app: Hono, route: Route) => {
348
- app[route.method](route.path, describeRoute(route.desc), ...mw(route.middlewares, route.name), ..._resolve(route.handle, route.name))
369
+ app[route.method](route.path, describeRoute(route.desc), ..._resolve(...route.middlewares, route.handle))
349
370
  } }), configs?.rajt || {})
350
371
 
351
372
  const iPath = join(_root, '.rajt/imports.mjs')
@@ -355,13 +376,18 @@ export async function cacheRoutes() {
355
376
  copyFileSync(localfireEntry, join(_root, '.rajt/localfire.js'))
356
377
 
357
378
  const _rajtDir = await dependencyPath('rajt')
379
+
380
+ stringifyToJS(Object.fromEntries(routes.map(r => ([r.path + r.method, r.name]))))
381
+
358
382
  writeFileSync(iPath, `// AUTO-GENERATED FILE - DO NOT EDIT
359
- ${env?.length ? `import { Envir } from '${await dependencyPath('t0n')}/dist/index'\nEnvir.add({${env.map(([key, val]) => key +':'+ stringifyToJS(val)).join(',')}})` : ''}
383
+ ${env?.length ? `import { Envir } from '${await dependencyPath('t0n')}/src/envir'\nEnvir.add({${env.map(([key, val]) => key +':'+ stringifyToJS(val)).join(',')}})` : ''}
360
384
  ${Object.entries(configs)?.length ? `import Config from '${_rajtDir}/src/config'\nConfig.add(${stringifyToJS(configs)})` : ''}
361
385
 
362
386
  import { registerHandler, registerMiddleware } from '${_rajtDir}/src/register'
387
+ ${handlers.map(([file, name, _export]) => `\nimport ${_export ? `{ ${name} }` : name} from '${_rajtDir}/src/${file}'\nregisterHandler('${name}', ${name})`).join('\n')}
363
388
 
364
389
  ${Object.entries(openApi)?.length ? `registerHandler('RAJT_OPENAPI', ${stringifyToJS(openApi)})` : ''}
390
+ Config.set('routes', ${stringifyToJS(Object.fromEntries(routes.map(r => ([r.path+'$'+verbAlias[r.method], r.name]))))})
365
391
 
366
392
  ${routes.map(r => `import ${r.name} from '../${normalizeImportPath(r.file)}'`).join('\n')}
367
393
  ${middlewares.map(r => `import ${r.name} from '../${normalizeImportPath(r.file)}'`).join('\n')}
@@ -370,9 +396,7 @@ try {
370
396
  const handlers = {${routes.map(r => r.name).join()}}
371
397
 
372
398
  for (const [name, handler] of Object.entries(handlers)) {
373
- if (typeof handler == 'function' || handler.prototype?.handle) {
374
- registerHandler(name, handler)
375
- }
399
+ registerHandler(name, handler)
376
400
  }
377
401
 
378
402
  const middlewares = {${middlewares.map(r => r.name).join()}}
@@ -388,7 +412,7 @@ try {
388
412
  const rPath = join(_root, '.rajt/routes.json')
389
413
  ensureDir(rPath)
390
414
  writeFileSync(rPath, JSON.stringify(routes.filter(r => r.method && r.path).map(route => [
391
- route.method,
415
+ verbAlias[route.method],
392
416
  route.path,
393
417
  route.middlewares,
394
418
  route.name,
package/src/types.ts CHANGED
@@ -11,6 +11,7 @@ import type { OpenAPIV3_1, OpenAPIV3 } from 'openapi-types'
11
11
  import type { StandardSchemaV1 } from '@standard-schema/spec'
12
12
  import type { DescribeRouteOptions as RawDescribeRouteOptions, ResolverReturnType } from 'hono-openapi'
13
13
  import type * as z from 'zod'
14
+ import type zm from 'zod/mini'
14
15
  import Action from './action'
15
16
  import request from './request'
16
17
  import response from './response'
@@ -33,6 +34,7 @@ export type {
33
34
  } from 'hono/utils/http-status'
34
35
  export type { BaseMime, StandardSchemaV1 }
35
36
 
37
+ export type zObject = z.ZodTypeAny | zm.ZodMiniObject
36
38
  type PublicMethods<T> = {
37
39
  [K in keyof T]: K extends `#${string}` | `$${string}` | symbol | 'prototype' ? never : K
38
40
  }[keyof T]
@@ -42,11 +44,12 @@ export type IResponse = Pick<typeof response, PublicMethods<typeof response>>
42
44
 
43
45
  export type IValidator = Pick<typeof validator, PublicMethods<typeof validator>>
44
46
  export type Rule = {
45
- schema: z.ZodObject<any>
47
+ schema: zObject
46
48
  target: keyof ValidationTargets
47
49
  eTarget?: 'fieldErrors' | 'formErrors'
48
50
  }
49
51
  export type Rules = Rule[] | Rule | null
52
+ export type RuleFn = (schema: zObject) => Rule
50
53
 
51
54
  export type StandardSchema = StandardSchemaV1 | OpenAPIV3_1.ReferenceObject
52
55
  export type DescribeRouteOptions = Omit<RawDescribeRouteOptions, 'responses'> & {
@@ -1,16 +1,17 @@
1
1
  const prd = Symbol('prd')
2
2
  const dev = Symbol('dev')
3
3
 
4
- let env = prd
4
+ // @ts-ignore
5
+ let env = process.env.RAJT_ENV || prd
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 == 'dev'
13
- || process.env?.npm_lifecycle_script == 'rajt'
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 == e
28
+ export const isEnv = (e: symbol) => env === e
28
29
 
29
- export const isDev = () => env == dev
30
- export const isProd = () => env == prd
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
@@ -1,23 +1,36 @@
1
1
  import { getHandler } from '../register'
2
2
 
3
- export function resolve(obj: any, id: string) {
4
- if (typeof obj == 'function' && obj?.length == 2)
5
- return [obj]
6
-
7
- if (obj?.run)
8
- return obj.run()
9
-
10
- if (obj?.handle)
11
- return obj.handle()
12
-
13
- const instance = new obj()
14
- if (obj?.prototype?.run)
15
- return instance.run()
16
-
17
- if (obj?.prototype?.handle)
18
- return [instance.handle]
19
-
20
- throw new Error(`Invalid action "${id}" - unsupported type`)
3
+ export function resolve(...objs: any[]) {
4
+ const _ = []
5
+ for (let obj of objs) {
6
+ if (typeof obj == 'string')
7
+ obj = getHandler(obj)
8
+
9
+ if (typeof obj == 'function' && obj?.length == 2) {
10
+
11
+ } else if (obj?.run) {
12
+ _.push(...obj.run())
13
+ continue
14
+ } else if (obj?.handle) {
15
+ obj = obj.handle
16
+ } else if (obj?.factory) {
17
+ obj = obj?.opts ? obj.factory(...Array.isArray(obj.opts) ? obj.opts : [obj.opts]) : obj.factory()
18
+ } else {
19
+ const instance = new obj()
20
+ if (obj?.prototype?.run) {
21
+ _.push(...instance.run())
22
+ continue
23
+ } else if (obj?.prototype?.handle) {
24
+ obj = instance.handle
25
+ }
26
+
27
+ throw new Error(`Invalid action "${obj?.name || String(obj)}" - unsupported type`)
28
+ }
29
+
30
+ obj && _.push(obj)
31
+ }
32
+
33
+ return _
21
34
  }
22
35
 
23
36
  export function resolveMiddleware(obj: any) {
@@ -35,11 +48,3 @@ export function resolveMiddleware(obj: any) {
35
48
 
36
49
  throw new Error('Invalid middleware provided. Must be a Hono middleware function or MiddlewareClass instance/constructor')
37
50
  }
38
-
39
- export function mw(...objs: string[]): Function[] {
40
- return objs.flatMap(obj => {
41
- if (typeof obj != 'string') return null
42
- // @ts-ignore
43
- return getHandler(obj)?.mw || null
44
- }).flat().filter(Boolean)
45
- }
package/src/validator.ts CHANGED
@@ -1,17 +1,19 @@
1
- import type * as z from 'zod'
2
1
  import { zValidator } from '@hono/zod-validator'
3
2
  import response from './response'
3
+ import type * as z from 'zod'
4
4
  import type {
5
- Rule, Rules,
5
+ Rule, Rules, RuleFn,
6
6
  ValidationTargets,
7
+ zObject,
8
+ MiddlewareHandler,
7
9
  } from './types'
8
10
 
9
11
  export default class $Validator {
10
- private static cache = new Map<string, (schema: z.ZodObject<any>) => Rule>()
12
+ private static cache = new Map<string, RuleFn>()
11
13
 
12
14
  private static createRule<T extends keyof ValidationTargets>(
13
15
  target: T,
14
- schema: z.ZodObject<any>
16
+ schema: zObject
15
17
  ): Rule {
16
18
  return {
17
19
  target,
@@ -24,7 +26,7 @@ export default class $Validator {
24
26
  if (this.cache.has(target))
25
27
  return this.cache.get(target)
26
28
 
27
- const fn = (schema: z.ZodObject<any>) => this.createRule(target, schema)
29
+ const fn = (schema: zObject) => this.createRule(target, schema)
28
30
  this.cache.set(target, fn)
29
31
  return fn
30
32
  }
@@ -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(rule => zValidator(rule.target, rule.schema, (result, c) => {
42
- if (!result.success) // @ts-ignore
43
- return response.badRequest({ ...result.error.flatten()[rule.eTarget] })
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
  }
@@ -1,14 +0,0 @@
1
- import { readFileSync } from 'node:fs'
2
- import { dirname, join } from 'pathe'
3
-
4
- export default function jsonImport<T = any>(filePath: string, defaultValue: T = {} as T): T {
5
- const __dirname = dirname(new URL(import.meta.url).pathname)
6
-
7
- try {
8
- const fullPath = join(__dirname, filePath)
9
- const fileContent = readFileSync(fullPath, 'utf-8')
10
- return JSON.parse(fileContent) as T
11
- } catch (error) {
12
- return defaultValue
13
- }
14
- }
@@ -1,9 +0,0 @@
1
- import type { MiddlewareHandler } from 'hono'
2
- import { MiddlewareType } from '../middleware'
3
- import { resolveMiddleware } from './resolve'
4
-
5
- export default function mergeMiddleware(target: Function | any, ...handlers: MiddlewareType[]) {
6
- const existingMiddlewares: MiddlewareHandler[] = target?.mw || []
7
- const allMiddlewares = [...existingMiddlewares, ...handlers.flat().map(handler => resolveMiddleware(handler))]
8
- target.mw = allMiddlewares
9
- }