rajt 0.0.35 → 0.0.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,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.35",
4
+ "version": "0.0.37",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "exports": {
@@ -10,15 +10,19 @@
10
10
  "./dynamodb": "./src/dynamodb/index.ts",
11
11
  "./dynamodb/types": "./src/dynamodb/types.ts",
12
12
  "./http": "./src/http.ts",
13
- "./types": "./src/types.ts"
13
+ "./types": "./src/types.ts",
14
+ "./env": "./src/utils/environment.ts",
15
+ "./length": "./src/utils/length.ts"
14
16
  },
15
- "files": ["src"],
17
+ "files": [
18
+ "src"
19
+ ],
16
20
  "scripts": {
17
21
  "dev": "tsx watch src/dev.ts",
18
22
  "local": "bun run --silent build && bun run --silent sam:local",
19
23
  "build": "bun run --silent cache:routes && bun run --silent export && bun run --silent clean:temp",
20
24
  "build:watch": "chokidar \"../../{actions,configs,models,utils}/**/*.ts\" -c \"bun run --silent build\" --initial",
21
- "export": "esbuild --bundle --minify --outfile=../../dist/index.js --platform=node --target=node20 --format=esm --tree-shaking=true --legal-comments=none --external:@aws-sdk --external:@smithy src/prod.ts",
25
+ "export": "node src/esbuild.mjs",
22
26
  "package": "bun run --silent build && bun run --silent sam:package",
23
27
  "deploy": "bun run --silent build && bun run --silent sam:package && bun run --silent sam:deploy",
24
28
  "update": "bun run --silent build && bun run --silent zip && bun run --silent sam:update",
@@ -83,4 +87,4 @@
83
87
  "bun",
84
88
  "nodejs"
85
89
  ]
86
- }
90
+ }
@@ -13,28 +13,35 @@ export class Ability {
13
13
  static fromRoutes(actions: Routes) {
14
14
  if (!actions?.length) return
15
15
 
16
- const paths = actions?.map(a => Array.isArray(a) ? a[0]+a[1] : a.method+a.path) || []
16
+ const paths = actions?.map(a => Array.isArray(a) ? a[0] + a[1] : a.method + a.path) || []
17
17
  const items = new Set(paths)
18
18
 
19
19
  if (items.size !== actions.length)
20
20
  throw new Error(`Duplicate routes detected: "${paths.filter((path, index) => paths.indexOf(path) !== index).join('", "')}"`)
21
21
 
22
- this.#abilities = Array.from(items).map(a => this.format(a)).filter(Boolean)
22
+ this.#abilities = Array.from(new Set(actions?.map(a => Array.isArray(a) ? a[3] : a.name) || []))
23
+ .map(a => this.format(a))
24
+ .filter(Boolean)
23
25
  }
24
26
 
25
- static fromAction(target: any): string | null {
26
- return !target || !target?.p ? null : this.format(target.p)
27
+ static fromAction(target: any): string {
28
+ return !target ? '' : this.format(target.name.length > 3 ? target.name : (target?.p || ''))
27
29
  }
28
30
 
29
31
  static format(path: string) {
30
- return path.normalize('NFD')
31
- .replace(/[\u0300-\u036f]/g, '')
32
- .replace(/^\/*/, '')
33
- .replace(/[^a-zA-Z0-9/]|[\s_.]/g, '-')
34
- .replace(/([a-z])([A-Z])/g, '$1-$2')
35
- .replace(/\//g, '.')
36
- .replace(/-+/g, '-')
37
- .toLowerCase()
32
+ return path == '/'
33
+ ? 'index'
34
+ : path.normalize('NFD')
35
+ .replace(/[\u0300-\u036f]/g, '')
36
+ .replace(/^\/*/, '')
37
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
38
+ .replace(/[^a-zA-Z0-9/]|[\s_.]/g, '-')
39
+ .replace(/-+/g, '-')
40
+ .replace(/\//g, '.')
41
+ .replace(/\.-/g, '.')
42
+ .replace(/^[._-]+/, '')
43
+ .replace(/[._-]+$/, '')
44
+ .toLowerCase()
38
45
  }
39
46
 
40
47
  static get abilities() {
package/src/auth/auth.ts CHANGED
@@ -23,4 +23,11 @@ export class Auth {
23
23
  static hasRole(...roles: string[]): boolean {
24
24
  return this.#u ? this.#u.hasRole(...roles) : false
25
25
  }
26
+
27
+ static has(prop: string, value: any = null): boolean {
28
+ return this.#u ? this.#u.has(prop, value) : false
29
+ }
30
+ static hasValue(prop: string, value: any = null): boolean {
31
+ return this.has(prop, value)
32
+ }
26
33
  }
@@ -4,10 +4,12 @@ export class Authnz<T extends object> {
4
4
  #abilities: string[]
5
5
  #roles: string[]
6
6
  #data: T
7
+ #token: any
7
8
 
8
- constructor(data: T, abilities: string[], roles: string[]) {
9
+ constructor(token: any, data: T, abilities: string[], roles: string[]) {
9
10
  this.#abilities = abilities
10
11
  this.#roles = roles
12
+ this.#token = token
11
13
  this.#data = data
12
14
  }
13
15
 
@@ -27,20 +29,27 @@ export class Authnz<T extends object> {
27
29
  return roles.flat().every(role => this.#roles.includes(role))
28
30
  }
29
31
 
30
- static fromToken<T extends object>(user: any): Authnz<T> | null {
31
- if (!user || user?.isInvalid()) return null
32
- user = user.get()
32
+ has(prop: string, value: any = null): boolean {
33
+ return this.#token?.hasValue(prop, value) || false
34
+ }
35
+ hasValue(prop: string, value: any = null): boolean {
36
+ return this.has(prop, value)
37
+ }
38
+
39
+ static fromToken<T extends object>(token: any): Authnz<T> | null {
40
+ if (!token || !token?.isValid()) return null
41
+ const user = token.get()
33
42
  const roles = [...(user?.role ? [user.role] : []), ...(user?.roles ?? [])]
34
43
 
35
44
  const combined = [...(user?.perms ?? []), ...roles.flatMap(role => {
36
45
  const perms = Ability.roles[role]
37
46
  if (!perms) return []
38
- return perms === '*' ? ['*'] : perms;
47
+ return perms === '*' ? ['*'] : perms
39
48
  })]
40
49
 
41
50
  const abilities = combined.includes('*') ? ['*'] : Array.from(new Set(combined))
42
51
 
43
- return new Authnz(user as T, abilities, roles)
52
+ return new Authnz<T>(token, user, abilities, roles)
44
53
  }
45
54
 
46
55
  #match(rule: string, ability: string): boolean {
package/src/create-app.ts CHANGED
@@ -9,7 +9,7 @@ import type { Routes } from './types'
9
9
  import { BadRequest, Unauthorized } from './exceptions'
10
10
  import { resolve, resolveMiddleware } from './utils/resolve'
11
11
  import { getMiddlewares, getHandler } from './register'
12
- import env from './utils/environment'
12
+ import { isDev } from './utils/environment'
13
13
  import { Auth } from './auth'
14
14
  import response from './response'
15
15
  import cx from './context'
@@ -25,7 +25,6 @@ export type ServerOptions<E extends Env = Env> = Partial<{
25
25
  init?: InitFunction<E>,
26
26
  }>
27
27
 
28
- const isDev = env() === 'dev'
29
28
  const NFHandler = () => response.notFound()
30
29
  const EHandler = async (e: Error | HTTPResponseError) => {
31
30
  console.error(e)
@@ -43,7 +42,7 @@ const EHandler = async (e: Error | HTTPResponseError) => {
43
42
  default:
44
43
  return response.internalError(
45
44
  // @ts-ignore
46
- isDev
45
+ isDev()
47
46
  ? e.stack?.split('\n').map(line =>
48
47
  line.replace(
49
48
  /at (.+ )?\(?([^)]+)\)?/g,
@@ -67,12 +66,12 @@ const EHandler = async (e: Error | HTTPResponseError) => {
67
66
 
68
67
  // return json.internalError(
69
68
  // // @ts-ignore
70
- // isDev ? e.stack?.split('\n at ').map() : undefined,
69
+ // isDev() ? e.stack?.split('\n at ').map() : undefined,
71
70
  // e.message || 'Internal Error'
72
71
  // )
73
72
  // error: e.message,
74
73
  // cause: e.cause || '???',
75
- // stack: isDev ? e.stack : undefined
74
+ // stack: isDev (? e.stack : undefined
76
75
  }
77
76
 
78
77
  export const createApp = <E extends Env>(options?: ServerOptions<E>) => {
@@ -0,0 +1,66 @@
1
+ import esbuild from 'esbuild'
2
+ import path from 'path'
3
+ import { fileURLToPath } from 'url'
4
+ import fs from 'fs/promises'
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
7
+
8
+ const formatSize = (bytes) => {
9
+ if (bytes < 1024) return `${bytes}b`
10
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)}kb`
11
+ return `${(bytes / (1024 * 1024)).toFixed(2)}mb`
12
+ }
13
+ const formatTime = (ms) => {
14
+ if (ms < 1000) return `${ms}ms`
15
+ return `${(ms / 1000).toFixed(2)}s`
16
+ }
17
+
18
+ const buildOptions = {
19
+ entryPoints: [path.join(__dirname, 'prod.ts')],
20
+ bundle: true,
21
+ minify: true,
22
+ outfile: path.join(__dirname, '../../../dist/index.js'),
23
+ platform: 'node',
24
+ target: 'node20',
25
+ format: 'esm',
26
+ treeShaking: true,
27
+ legalComments: 'none',
28
+ external: ['@aws-sdk', '@smithy'],
29
+ metafile: true,
30
+ plugins: [{
31
+ name: 'preserve-class-names',
32
+ setup(build) {
33
+ build.onLoad(
34
+ { filter: /(actions|features)\/.*\.ts$/ },
35
+ async (args) => {
36
+ const contents = await fs.readFile(args.path, 'utf8')
37
+ const result = await esbuild.transform(contents, {
38
+ loader: 'ts',
39
+ minify: true,
40
+ keepNames: true
41
+ })
42
+ return { contents: result.code, loader: 'ts' }
43
+ }
44
+ )
45
+ },
46
+ }]
47
+ }
48
+
49
+ try {
50
+ const startTime = Date.now()
51
+ const result = await esbuild.build(buildOptions)
52
+
53
+ const cwd = path.join(__dirname, '../../..')
54
+
55
+ const outputFile = buildOptions.outfile
56
+ const stats = await fs.stat(outputFile)
57
+ const size = formatSize(stats.size)
58
+
59
+ console.log(`\n⚡️ Done in ${formatTime(Date.now() - startTime)}`)
60
+ console.log(` ${path.relative(path.join(cwd, 'node_modules/rajt/src'), buildOptions.entryPoints[0])} → ${path.relative(cwd, outputFile)}`)
61
+ console.log(` Size: ${size}`)
62
+ console.log(` Files: ${Object.keys(result.metafile.outputs).length}`)
63
+ } catch (error) {
64
+ console.error('❌ Build failed:', error)
65
+ process.exit(1)
66
+ }
package/src/http.ts CHANGED
@@ -52,10 +52,10 @@ export function Auth(...args: any[]): void | ClassDecorator {
52
52
 
53
53
  function _auth(target: Function | any) {
54
54
  mergeMiddleware(target, async (c: Context, next: Next) => {
55
- const auth = Gate.user()
55
+ const user = Gate.user
56
56
  const ability = Ability.fromAction(target)
57
57
 
58
- if (!auth || !ability || auth.cant(ability))
58
+ if (!user || !ability || Gate.cant(ability))
59
59
  return Response.unauthorized()
60
60
 
61
61
  await next()
@@ -13,3 +13,7 @@ export default function getEnvironment() {
13
13
 
14
14
  return 'prod'
15
15
  }
16
+
17
+ export const isDev = () => getEnvironment() === 'dev'
18
+ export const isProd = () => getEnvironment() === 'prod'
19
+ export const isPrd = isProd