rajt 0.0.34 → 0.0.36
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 +6 -4
- package/src/auth/ability.ts +19 -12
- package/src/auth/auth.ts +7 -0
- package/src/auth/authnz.ts +15 -6
- package/src/dynamodb/compact.ts +2 -2
- package/src/esbuild.mjs +66 -0
- package/src/http.ts +2 -2
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.36",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"exports": {
|
|
@@ -12,13 +12,15 @@
|
|
|
12
12
|
"./http": "./src/http.ts",
|
|
13
13
|
"./types": "./src/types.ts"
|
|
14
14
|
},
|
|
15
|
-
"files": [
|
|
15
|
+
"files": [
|
|
16
|
+
"src"
|
|
17
|
+
],
|
|
16
18
|
"scripts": {
|
|
17
19
|
"dev": "tsx watch src/dev.ts",
|
|
18
20
|
"local": "bun run --silent build && bun run --silent sam:local",
|
|
19
21
|
"build": "bun run --silent cache:routes && bun run --silent export && bun run --silent clean:temp",
|
|
20
22
|
"build:watch": "chokidar \"../../{actions,configs,models,utils}/**/*.ts\" -c \"bun run --silent build\" --initial",
|
|
21
|
-
"export": "
|
|
23
|
+
"export": "node src/esbuild.mjs",
|
|
22
24
|
"package": "bun run --silent build && bun run --silent sam:package",
|
|
23
25
|
"deploy": "bun run --silent build && bun run --silent sam:package && bun run --silent sam:deploy",
|
|
24
26
|
"update": "bun run --silent build && bun run --silent zip && bun run --silent sam:update",
|
|
@@ -83,4 +85,4 @@
|
|
|
83
85
|
"bun",
|
|
84
86
|
"nodejs"
|
|
85
87
|
]
|
|
86
|
-
}
|
|
88
|
+
}
|
package/src/auth/ability.ts
CHANGED
|
@@ -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(
|
|
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
|
|
26
|
-
return !target
|
|
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
|
|
31
|
-
|
|
32
|
-
.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
}
|
package/src/auth/authnz.ts
CHANGED
|
@@ -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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
52
|
+
return new Authnz<T>(token, user, abilities, roles)
|
|
44
53
|
}
|
|
45
54
|
|
|
46
55
|
#match(rule: string, ability: string): boolean {
|
package/src/dynamodb/compact.ts
CHANGED
|
@@ -121,7 +121,7 @@ export default class Compact {
|
|
|
121
121
|
if (!key) return undefined
|
|
122
122
|
|
|
123
123
|
if (typeof key == 'string')
|
|
124
|
-
return [key, value]
|
|
124
|
+
return [key, value || null]
|
|
125
125
|
|
|
126
126
|
const mainKey = Object.keys(key)[0]
|
|
127
127
|
const subKeys = key[mainKey]
|
|
@@ -135,7 +135,7 @@ export default class Compact {
|
|
|
135
135
|
: [mainKey, this.withSchema(value, subKeys)]
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
return [mainKey, value]
|
|
138
|
+
return [mainKey, value || null]
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
static memo(val: any, seen: any[]): any {
|
package/src/esbuild.mjs
ADDED
|
@@ -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
|
|
55
|
+
const user = Gate.user
|
|
56
56
|
const ability = Ability.fromAction(target)
|
|
57
57
|
|
|
58
|
-
if (!
|
|
58
|
+
if (!user || !ability || Gate.cant(ability))
|
|
59
59
|
return Response.unauthorized()
|
|
60
60
|
|
|
61
61
|
await next()
|