create-acrylblends 0.0.0-stage → 0.1.0
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/create-acrylblends.js +4 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +274 -0
- package/dist/index.js.map +1 -0
- package/docs/framework/LICENSE +23 -0
- package/docs/framework/README.md +63 -0
- package/docs/framework/UPSTREAM.md +3 -0
- package/docs/framework/agents/index.md +24 -0
- package/docs/framework/api/acryl.md +259 -0
- package/docs/framework/api/context.md +527 -0
- package/docs/framework/api/exception.md +80 -0
- package/docs/framework/api/index.md +12 -0
- package/docs/framework/api/presets.md +65 -0
- package/docs/framework/api/request.md +396 -0
- package/docs/framework/api/routing.md +303 -0
- package/docs/framework/api/summary.md +140 -0
- package/docs/framework/concepts/benchmarks.md +26 -0
- package/docs/framework/concepts/developer-experience.md +5 -0
- package/docs/framework/concepts/middleware.md +21 -0
- package/docs/framework/concepts/motivation.md +14 -0
- package/docs/framework/concepts/routers.md +63 -0
- package/docs/framework/concepts/stacks.md +235 -0
- package/docs/framework/concepts/web-standard.md +37 -0
- package/docs/framework/cordis/acryl-in-a-cordis-app.md +146 -0
- package/docs/framework/cordis/concepts.md +105 -0
- package/docs/framework/cordis/middleware.md +8 -0
- package/docs/framework/cordis/migration-from-0-3.md +111 -0
- package/docs/framework/cordis/migration-from-0-4.md +14 -0
- package/docs/framework/cordis/migration-from-dsh-webserver.md +103 -0
- package/docs/framework/cordis/mounting.md +85 -0
- package/docs/framework/cordis/node-server.md +75 -0
- package/docs/framework/cordis/plugin-lifecycle.md +111 -0
- package/docs/framework/cordis/surfaces.md +71 -0
- package/docs/framework/cordis/transport-and-cancellation.md +57 -0
- package/docs/framework/cordis/validation.md +135 -0
- package/docs/framework/getting-started/basic.md +196 -0
- package/docs/framework/getting-started/bun.md +221 -0
- package/docs/framework/getting-started/nodejs.md +344 -0
- package/docs/framework/getting-started/other-runtimes.md +23 -0
- package/docs/framework/guides/best-practices.md +231 -0
- package/docs/framework/guides/create-acrylblends.md +79 -0
- package/docs/framework/guides/examples.md +3 -0
- package/docs/framework/guides/faq.md +19 -0
- package/docs/framework/guides/helpers.md +33 -0
- package/docs/framework/guides/jsx-dom.md +299 -0
- package/docs/framework/guides/jsx.md +440 -0
- package/docs/framework/guides/middleware.md +253 -0
- package/docs/framework/guides/rpc.md +886 -0
- package/docs/framework/guides/testing.md +133 -0
- package/docs/framework/guides/validation.md +383 -0
- package/docs/framework/helpers/accepts.md +60 -0
- package/docs/framework/helpers/adapter.md +87 -0
- package/docs/framework/helpers/conninfo.md +112 -0
- package/docs/framework/helpers/cookie.md +232 -0
- package/docs/framework/helpers/css.md +285 -0
- package/docs/framework/helpers/dev.md +67 -0
- package/docs/framework/helpers/factory.md +180 -0
- package/docs/framework/helpers/html.md +123 -0
- package/docs/framework/helpers/jwt.md +184 -0
- package/docs/framework/helpers/proxy.md +118 -0
- package/docs/framework/helpers/route.md +143 -0
- package/docs/framework/helpers/ssg.md +393 -0
- package/docs/framework/helpers/streaming.md +119 -0
- package/docs/framework/helpers/testing.md +101 -0
- package/docs/framework/helpers/websocket.md +215 -0
- package/docs/framework/middleware/builtin/basic-auth.md +165 -0
- package/docs/framework/middleware/builtin/bearer-auth.md +138 -0
- package/docs/framework/middleware/builtin/body-limit.md +71 -0
- package/docs/framework/middleware/builtin/cache.md +122 -0
- package/docs/framework/middleware/builtin/combine.md +94 -0
- package/docs/framework/middleware/builtin/compress.md +59 -0
- package/docs/framework/middleware/builtin/context-storage.md +78 -0
- package/docs/framework/middleware/builtin/cors.md +135 -0
- package/docs/framework/middleware/builtin/csrf.md +105 -0
- package/docs/framework/middleware/builtin/etag.md +54 -0
- package/docs/framework/middleware/builtin/ip-restriction.md +69 -0
- package/docs/framework/middleware/builtin/jsx-renderer.md +286 -0
- package/docs/framework/middleware/builtin/jwk.md +192 -0
- package/docs/framework/middleware/builtin/jwt.md +145 -0
- package/docs/framework/middleware/builtin/language.md +232 -0
- package/docs/framework/middleware/builtin/logger.md +74 -0
- package/docs/framework/middleware/builtin/loopback-only.md +31 -0
- package/docs/framework/middleware/builtin/method-not-allowed.md +48 -0
- package/docs/framework/middleware/builtin/method-override.md +80 -0
- package/docs/framework/middleware/builtin/mount.md +106 -0
- package/docs/framework/middleware/builtin/pretty-json.md +53 -0
- package/docs/framework/middleware/builtin/request-id.md +94 -0
- package/docs/framework/middleware/builtin/secure-headers.md +247 -0
- package/docs/framework/middleware/builtin/timeout.md +98 -0
- package/docs/framework/middleware/builtin/timing.md +101 -0
- package/docs/framework/middleware/builtin/trailing-slash.md +74 -0
- package/docs/framework/middleware/third-party.md +4 -0
- package/docs/framework/upstream.json +9 -0
- package/package.json +47 -3
- package/templates/bun/gitignore +1 -0
- package/templates/bun/package.json +19 -0
- package/templates/bun/src/index.ts +7 -0
- package/templates/bun/test/app.test.ts +8 -0
- package/templates/bun/tsconfig.json +15 -0
- package/templates/nodejs/gitignore +1 -0
- package/templates/nodejs/package.json +22 -0
- package/templates/nodejs/src/app.ts +7 -0
- package/templates/nodejs/src/index.ts +9 -0
- package/templates/nodejs/test/app.test.ts +9 -0
- package/templates/nodejs/tsconfig.json +16 -0
- package/README.md +0 -3
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Factory Helper
|
|
2
|
+
|
|
3
|
+
The Factory Helper provides useful functions for creating Acryl's components such as Middleware. Sometimes it's difficult to set the proper TypeScript types, but this helper facilitates that.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { Acryl } from 'acrylblends'
|
|
9
|
+
import { createFactory, createMiddleware } from 'acrylblends/factory'
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## `createFactory()`
|
|
13
|
+
|
|
14
|
+
`createFactory()` will create an instance of the Factory class.
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { createFactory } from 'acrylblends/factory'
|
|
18
|
+
|
|
19
|
+
const factory = createFactory()
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
You can pass your Env types as Generics:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
type Env = {
|
|
26
|
+
Variables: {
|
|
27
|
+
foo: string
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const factory = createFactory<Env>()
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Options
|
|
35
|
+
|
|
36
|
+
### *(optional)* defaultAppOptions: `AcrylOptions`
|
|
37
|
+
|
|
38
|
+
The default options to pass to the Acryl application created by `createApp()`.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
const factory = createFactory({
|
|
42
|
+
defaultAppOptions: { strict: false },
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const app = factory.createApp() // `strict: false` is applied
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## `createMiddleware()`
|
|
49
|
+
|
|
50
|
+
`createMiddleware()` is shortcut of `factory.createMiddleware()`.
|
|
51
|
+
This function will create your custom middleware.
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const messageMiddleware = createMiddleware(async (c, next) => {
|
|
55
|
+
await next()
|
|
56
|
+
c.res.headers.set('X-Message', 'Good morning!')
|
|
57
|
+
})
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Tip: If you want to get an argument like `message`, you can create it as a function like the following.
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
const messageMiddleware = (message: string) => {
|
|
64
|
+
return createMiddleware(async (c, next) => {
|
|
65
|
+
await next()
|
|
66
|
+
c.res.headers.set('X-Message', message)
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
app.use(messageMiddleware('Good evening!'))
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## `factory.createHandlers()`
|
|
74
|
+
|
|
75
|
+
`createHandlers()` helps to define handlers in a different place than `app.get('/')`.
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { createFactory } from 'acrylblends/factory'
|
|
79
|
+
import { logger } from 'acrylblends/logger'
|
|
80
|
+
declare module 'acrylblends' {
|
|
81
|
+
interface ContextVariableMap {
|
|
82
|
+
foo: string
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ...
|
|
87
|
+
|
|
88
|
+
const factory = createFactory()
|
|
89
|
+
|
|
90
|
+
const middleware = factory.createMiddleware(async (c, next) => {
|
|
91
|
+
c.set('foo', 'bar')
|
|
92
|
+
await next()
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
const handlers = factory.createHandlers(logger(), middleware, (c) => {
|
|
96
|
+
return c.json(c.var.foo)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
app.get('/api', ...handlers)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## `factory.createApp()`
|
|
103
|
+
|
|
104
|
+
`createApp()` helps to create an instance of Acryl with the proper types. If you use this method with `createFactory()`, you can avoid redundancy in the definition of the `Env` type.
|
|
105
|
+
|
|
106
|
+
If your application is like this, you have to set the `Env` in two places:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { createMiddleware } from 'acrylblends/factory'
|
|
110
|
+
|
|
111
|
+
type Env = {
|
|
112
|
+
Variables: {
|
|
113
|
+
myVar: string
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 1. Set the `Env` to `new Acryl()`
|
|
118
|
+
const app = new Acryl<Env>()
|
|
119
|
+
|
|
120
|
+
// 2. Set the `Env` to `createMiddleware()`
|
|
121
|
+
const mw = createMiddleware<Env>(async (c, next) => {
|
|
122
|
+
await next()
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
app.use(mw)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
By using `createFactory()` and `createApp()`, you can set the `Env` only in one place.
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
import { createFactory } from 'acrylblends/factory'
|
|
132
|
+
|
|
133
|
+
// ...
|
|
134
|
+
|
|
135
|
+
// Set the `Env` to `createFactory()`
|
|
136
|
+
const factory = createFactory<Env>()
|
|
137
|
+
|
|
138
|
+
const app = factory.createApp()
|
|
139
|
+
|
|
140
|
+
// factory also has `createMiddleware()`
|
|
141
|
+
const mw = factory.createMiddleware(async (c, next) => {
|
|
142
|
+
await next()
|
|
143
|
+
})
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`createFactory()` can receive the `initApp` option to initialize an `app` created by `createApp()`. The following is an example that uses the option.
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
// factory-with-db.ts
|
|
150
|
+
type Env = {
|
|
151
|
+
Bindings: {
|
|
152
|
+
MY_DB: D1Database
|
|
153
|
+
}
|
|
154
|
+
Variables: {
|
|
155
|
+
db: DrizzleD1Database
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export default createFactory<Env>({
|
|
160
|
+
initApp: (app) => {
|
|
161
|
+
app.use(async (c, next) => {
|
|
162
|
+
const db = drizzle(c.env.MY_DB)
|
|
163
|
+
c.set('db', db)
|
|
164
|
+
await next()
|
|
165
|
+
})
|
|
166
|
+
},
|
|
167
|
+
})
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
// crud.ts
|
|
172
|
+
import factoryWithDB from './factory-with-db'
|
|
173
|
+
|
|
174
|
+
const app = factoryWithDB.createApp()
|
|
175
|
+
|
|
176
|
+
app.post('/posts', (c) => {
|
|
177
|
+
c.var.db.insert()
|
|
178
|
+
// ...
|
|
179
|
+
})
|
|
180
|
+
```
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# html Helper
|
|
2
|
+
|
|
3
|
+
The html Helper lets you write HTML in JavaScript template literal with a tag named `html`. Using `raw()`, the content will be rendered as is. You have to escape these strings by yourself.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { Acryl } from 'acrylblends'
|
|
9
|
+
import { html, raw } from 'acrylblends/html'
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## `html`
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
const app = new Acryl()
|
|
16
|
+
|
|
17
|
+
app.get('/:username', (c) => {
|
|
18
|
+
const { username } = c.req.param()
|
|
19
|
+
return c.html(
|
|
20
|
+
html`<!doctype html>
|
|
21
|
+
<h1>Hello! ${username}!</h1>`
|
|
22
|
+
)
|
|
23
|
+
})
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Insert snippets into JSX
|
|
27
|
+
|
|
28
|
+
Insert the inline script into JSX:
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
app.get('/', (c) => {
|
|
32
|
+
return c.html(
|
|
33
|
+
<html>
|
|
34
|
+
<head>
|
|
35
|
+
<title>Test Site</title>
|
|
36
|
+
{html`
|
|
37
|
+
<script>
|
|
38
|
+
// No need to use dangerouslySetInnerHTML.
|
|
39
|
+
// If you write it here, it will not be escaped.
|
|
40
|
+
</script>
|
|
41
|
+
`}
|
|
42
|
+
</head>
|
|
43
|
+
<body>Hello!</body>
|
|
44
|
+
</html>
|
|
45
|
+
)
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Act as functional component
|
|
50
|
+
|
|
51
|
+
Since `html` returns an HtmlEscapedString, it can act as a fully functional component without using JSX.
|
|
52
|
+
|
|
53
|
+
#### Use `html` to speed up the process instead of `memo`
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
const Footer = () => html`
|
|
57
|
+
<footer>
|
|
58
|
+
<address>My Address...</address>
|
|
59
|
+
</footer>
|
|
60
|
+
`
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Receives props and embeds values
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
interface SiteData {
|
|
67
|
+
title: string
|
|
68
|
+
description: string
|
|
69
|
+
image: string
|
|
70
|
+
children?: any
|
|
71
|
+
}
|
|
72
|
+
const Layout = (props: SiteData) => html`
|
|
73
|
+
<html>
|
|
74
|
+
<head>
|
|
75
|
+
<meta charset="UTF-8">
|
|
76
|
+
<title>${props.title}</title>
|
|
77
|
+
<meta name="description" content="${props.description}">
|
|
78
|
+
<head prefix="og: http://ogp.me/ns#">
|
|
79
|
+
<meta property="og:type" content="article">
|
|
80
|
+
<!-- More elements slow down JSX, but not template literals. -->
|
|
81
|
+
<meta property="og:title" content="${props.title}">
|
|
82
|
+
<meta property="og:image" content="${props.image}">
|
|
83
|
+
</head>
|
|
84
|
+
<body>
|
|
85
|
+
${props.children}
|
|
86
|
+
</body>
|
|
87
|
+
</html>
|
|
88
|
+
`
|
|
89
|
+
|
|
90
|
+
const Content = (props: { siteData: SiteData; name: string }) => (
|
|
91
|
+
<Layout {...props.siteData}>
|
|
92
|
+
<h1>Hello {props.name}</h1>
|
|
93
|
+
</Layout>
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
app.get('/', (c) => {
|
|
97
|
+
const props = {
|
|
98
|
+
name: 'World',
|
|
99
|
+
siteData: {
|
|
100
|
+
title: 'Hello <> World',
|
|
101
|
+
description: 'This is a description',
|
|
102
|
+
image: 'https://example.com/image.png',
|
|
103
|
+
},
|
|
104
|
+
}
|
|
105
|
+
return c.html(<Content {...props} />)
|
|
106
|
+
})
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## `raw()`
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
app.get('/', (c) => {
|
|
113
|
+
const name = 'John "Johnny" Smith'
|
|
114
|
+
return c.html(html`<p>I'm ${raw(name)}.</p>`)
|
|
115
|
+
})
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Tips
|
|
119
|
+
|
|
120
|
+
Thanks to these libraries, Visual Studio Code and vim also interprets template literals as HTML, allowing syntax highlighting and formatting to be applied.
|
|
121
|
+
|
|
122
|
+
- <https://marketplace.visualstudio.com/items?itemName=bierner.lit-html>
|
|
123
|
+
- <https://github.com/MaxMEllon/vim-jsx-pretty>
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# JWT Authentication Helper
|
|
2
|
+
|
|
3
|
+
This helper provides functions for encoding, decoding, signing, and verifying JSON Web Tokens (JWTs). JWTs are commonly used for authentication and authorization purposes in web applications. This helper offers robust JWT functionality with support for various cryptographic algorithms.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
To use this helper, you can import it as follows:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { decode, sign, verify } from 'acrylblends/jwt'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
> **Note.** [JWT Middleware](../middleware/builtin/jwt.md) also import the `jwt` function from the `acrylblends/jwt`.
|
|
14
|
+
|
|
15
|
+
## `sign()`
|
|
16
|
+
|
|
17
|
+
This function generates a JWT token by encoding a payload and signing it using the specified algorithm and secret.
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
sign(
|
|
21
|
+
payload: unknown,
|
|
22
|
+
secret: string,
|
|
23
|
+
alg?: 'HS256';
|
|
24
|
+
|
|
25
|
+
): Promise<string>;
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Example
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { sign } from 'acrylblends/jwt'
|
|
32
|
+
|
|
33
|
+
const payload = {
|
|
34
|
+
sub: 'user123',
|
|
35
|
+
role: 'admin',
|
|
36
|
+
exp: Math.floor(Date.now() / 1000) + 60 * 5, // Token expires in 5 minutes
|
|
37
|
+
}
|
|
38
|
+
const secret = 'mySecretKey'
|
|
39
|
+
const token = await sign(payload, secret)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Options
|
|
43
|
+
|
|
44
|
+
<br/>
|
|
45
|
+
|
|
46
|
+
#### **(required)** payload: `unknown`
|
|
47
|
+
|
|
48
|
+
The JWT payload to be signed. You can include other claims like in [Payload Validation](#payload-validation).
|
|
49
|
+
|
|
50
|
+
#### **(required)** secret: `string`
|
|
51
|
+
|
|
52
|
+
The secret key used for JWT verification or signing.
|
|
53
|
+
|
|
54
|
+
#### *(optional)* alg: [AlgorithmTypes](#supported-algorithmtypes)
|
|
55
|
+
|
|
56
|
+
The algorithm used for JWT signing or verification. The default is HS256.
|
|
57
|
+
|
|
58
|
+
## `verify()`
|
|
59
|
+
|
|
60
|
+
This function checks if a JWT token is genuine and still valid. It ensures the token hasn't been altered and checks validity only if you added [Payload Validation](#payload-validation).
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
verify(
|
|
64
|
+
token: string,
|
|
65
|
+
secret: string,
|
|
66
|
+
alg: 'HS256';
|
|
67
|
+
issuer?: string | RegExp;
|
|
68
|
+
aud?: string | string[] | RegExp;
|
|
69
|
+
): Promise<any>;
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Example
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { verify } from 'acrylblends/jwt'
|
|
77
|
+
|
|
78
|
+
const tokenToVerify = 'token'
|
|
79
|
+
const secretKey = 'mySecretKey'
|
|
80
|
+
|
|
81
|
+
const decodedPayload = await verify(tokenToVerify, secretKey, 'HS256')
|
|
82
|
+
console.log(decodedPayload)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Options
|
|
86
|
+
|
|
87
|
+
<br/>
|
|
88
|
+
|
|
89
|
+
#### **(required)** token: `string`
|
|
90
|
+
|
|
91
|
+
The JWT token to be verified.
|
|
92
|
+
|
|
93
|
+
#### **(required)** secret: `string`
|
|
94
|
+
|
|
95
|
+
The secret key used for JWT verification or signing.
|
|
96
|
+
|
|
97
|
+
#### **(required)** alg: [AlgorithmTypes](#supported-algorithmtypes)
|
|
98
|
+
|
|
99
|
+
The algorithm used for JWT signing or verification.
|
|
100
|
+
|
|
101
|
+
#### *(optional)* issuer: `string | RegExp`
|
|
102
|
+
|
|
103
|
+
The expected issuer used for JWT verification.
|
|
104
|
+
|
|
105
|
+
#### *(optional)* aud: `string | string[] | RegExp`
|
|
106
|
+
|
|
107
|
+
The expected audience used for JWT verification. If this is set, the token must include an `aud` claim and at least one audience value must match.
|
|
108
|
+
|
|
109
|
+
## `decode()`
|
|
110
|
+
|
|
111
|
+
This function decodes a JWT token without performing signature verification. It extracts and returns the header and payload from the token.
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
decode(token: string): { header: any; payload: any };
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Example
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
import { decode } from 'acrylblends/jwt'
|
|
121
|
+
|
|
122
|
+
// Decode the JWT token
|
|
123
|
+
const tokenToDecode =
|
|
124
|
+
'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJzdWIiOiAidXNlcjEyMyIsICJyb2xlIjogImFkbWluIn0.JxUwx6Ua1B0D1B0FtCrj72ok5cm1Pkmr_hL82sd7ELA'
|
|
125
|
+
|
|
126
|
+
const { header, payload } = decode(tokenToDecode)
|
|
127
|
+
|
|
128
|
+
console.log('Decoded Header:', header)
|
|
129
|
+
console.log('Decoded Payload:', payload)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Options
|
|
133
|
+
|
|
134
|
+
<br/>
|
|
135
|
+
|
|
136
|
+
#### **(required)** token: `string`
|
|
137
|
+
|
|
138
|
+
The JWT token to be decoded.
|
|
139
|
+
|
|
140
|
+
> The `decode` function allows you to inspect the header and payload of a JWT token _**without**_ performing verification. This can be useful for debugging or extracting information from JWT tokens.
|
|
141
|
+
|
|
142
|
+
## Payload Validation
|
|
143
|
+
|
|
144
|
+
When verifying a JWT token, the following payload validations are performed:
|
|
145
|
+
|
|
146
|
+
- `exp`: The token is checked to ensure it has not expired.
|
|
147
|
+
- `nbf`: The token is checked to ensure it is not being used before a specified time.
|
|
148
|
+
- `iat`: The token is checked to ensure it is not issued in the future.
|
|
149
|
+
- `iss`: The token is checked to ensure it has been issued by a trusted issuer.
|
|
150
|
+
- `aud`: The token is checked to ensure it is intended for an accepted audience when the `aud` verification parameter is set.
|
|
151
|
+
|
|
152
|
+
Please ensure that your JWT payload includes these fields, as an object, if you intend to perform these checks during verification.
|
|
153
|
+
|
|
154
|
+
## Custom Error Types
|
|
155
|
+
|
|
156
|
+
The module also defines custom error types to handle JWT-related errors.
|
|
157
|
+
|
|
158
|
+
- `JwtAlgorithmNotImplemented`: Indicates that the requested JWT algorithm is not implemented.
|
|
159
|
+
- `JwtTokenInvalid`: Indicates that the JWT token is invalid.
|
|
160
|
+
- `JwtTokenNotBefore`: Indicates that the token is being used before its valid date.
|
|
161
|
+
- `JwtTokenExpired`: Indicates that the token has expired.
|
|
162
|
+
- `JwtTokenIssuedAt`: Indicates that the "iat" claim in the token is incorrect.
|
|
163
|
+
- `JwtTokenIssuer`: Indicates that the "iss" claim in the token is incorrect.
|
|
164
|
+
- `JwtPayloadRequiresAud`: Indicates that an `aud` claim is required when `aud` verification is configured.
|
|
165
|
+
- `JwtTokenAudience`: Indicates that the token's `aud` claim does not match the expected audience.
|
|
166
|
+
- `JwtTokenSignatureMismatched`: Indicates a signature mismatch in the token.
|
|
167
|
+
|
|
168
|
+
## Supported AlgorithmTypes
|
|
169
|
+
|
|
170
|
+
The module supports the following JWT cryptographic algorithms:
|
|
171
|
+
|
|
172
|
+
- `HS256`: HMAC using SHA-256
|
|
173
|
+
- `HS384`: HMAC using SHA-384
|
|
174
|
+
- `HS512`: HMAC using SHA-512
|
|
175
|
+
- `RS256`: RSASSA-PKCS1-v1_5 using SHA-256
|
|
176
|
+
- `RS384`: RSASSA-PKCS1-v1_5 using SHA-384
|
|
177
|
+
- `RS512`: RSASSA-PKCS1-v1_5 using SHA-512
|
|
178
|
+
- `PS256`: RSASSA-PSS using SHA-256 and MGF1 with SHA-256
|
|
179
|
+
- `PS384`: RSASSA-PSS using SHA-386 and MGF1 with SHA-386
|
|
180
|
+
- `PS512`: RSASSA-PSS using SHA-512 and MGF1 with SHA-512
|
|
181
|
+
- `ES256`: ECDSA using P-256 and SHA-256
|
|
182
|
+
- `ES384`: ECDSA using P-384 and SHA-384
|
|
183
|
+
- `ES512`: ECDSA using P-521 and SHA-512
|
|
184
|
+
- `EdDSA`: EdDSA using Ed25519
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Proxy Helper
|
|
2
|
+
|
|
3
|
+
> **Not yet available.** The proxy helper (`acrylblends/proxy`) is documented here so the page structure matches the rest of the framework, and is not part of acrylblends yet. The code on this page is not checked by the docs tests.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
Proxy Helper provides useful functions when using Acryl application as a (reverse) proxy.
|
|
7
|
+
|
|
8
|
+
## Import
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { Acryl } from 'acrylblends'
|
|
12
|
+
import { proxy } from 'acrylblends/proxy'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## `proxy()`
|
|
16
|
+
|
|
17
|
+
`proxy()` is a `fetch()` API wrapper for proxy. The parameters and return value are the same as for `fetch()` (except for the proxy-specific options).
|
|
18
|
+
|
|
19
|
+
The `Accept-Encoding` header is replaced with an encoding that the current runtime can handle. Unnecessary response headers are removed, and a `Response` object is returned that can be sent from the handler.
|
|
20
|
+
|
|
21
|
+
### Examples
|
|
22
|
+
|
|
23
|
+
Simple usage:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
app.get('/proxy/:path', (c) => {
|
|
27
|
+
return proxy(`http://${originServer}/${c.req.param('path')}`)
|
|
28
|
+
})
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Complicated usage:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
app.get('/proxy/:path', async (c) => {
|
|
35
|
+
const res = await proxy(
|
|
36
|
+
`http://${originServer}/${c.req.param('path')}`,
|
|
37
|
+
{
|
|
38
|
+
headers: {
|
|
39
|
+
...c.req.header(), // optional, specify only when forwarding all the request data (including credentials) is necessary.
|
|
40
|
+
'X-Forwarded-For': '127.0.0.1',
|
|
41
|
+
'X-Forwarded-Host': c.req.header('host'),
|
|
42
|
+
Authorization: undefined, // do not propagate request headers contained in c.req.header('Authorization')
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
res.headers.delete('Set-Cookie')
|
|
47
|
+
return res
|
|
48
|
+
})
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Or you can pass the `c.req` as a parameter.
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
app.all('/proxy/:path', (c) => {
|
|
55
|
+
return proxy(`http://${originServer}/${c.req.param('path')}`, {
|
|
56
|
+
...c.req, // optional, specify only when forwarding all the request data (including credentials) is necessary.
|
|
57
|
+
headers: {
|
|
58
|
+
...c.req.header(),
|
|
59
|
+
'X-Forwarded-For': '127.0.0.1',
|
|
60
|
+
'X-Forwarded-Host': c.req.header('host'),
|
|
61
|
+
Authorization: undefined, // do not propagate request headers contained in c.req.header('Authorization')
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
You can override the default global `fetch` function with the `customFetch` option:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
app.get('/proxy', (c) => {
|
|
71
|
+
return proxy('https://example.com/', {
|
|
72
|
+
customFetch,
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Connection Header Processing
|
|
78
|
+
|
|
79
|
+
By default, `proxy()` ignores the `Connection` header to prevent Hop-by-Hop Header Injection attacks. You can enable strict RFC 9110 compliance with the `strictConnectionProcessing` option:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
// Default behavior (recommended for untrusted clients)
|
|
83
|
+
app.get('/proxy/:path', (c) => {
|
|
84
|
+
return proxy(`http://${originServer}/${c.req.param('path')}`, c.req)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
// Strict RFC 9110 compliance (use only in trusted environments)
|
|
88
|
+
app.get('/internal-proxy/:path', (c) => {
|
|
89
|
+
return proxy(`http://${internalServer}/${c.req.param('path')}`, {
|
|
90
|
+
...c.req,
|
|
91
|
+
strictConnectionProcessing: true,
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### `ProxyFetch`
|
|
97
|
+
|
|
98
|
+
The type of `proxy()` is defined as `ProxyFetch` and is as follows
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
interface ProxyRequestInit extends Omit<RequestInit, 'headers'> {
|
|
102
|
+
raw?: Request
|
|
103
|
+
customFetch?: (request: Request) => Promise<Response>
|
|
104
|
+
strictConnectionProcessing?: boolean
|
|
105
|
+
headers?:
|
|
106
|
+
| HeadersInit
|
|
107
|
+
| [string, string][]
|
|
108
|
+
| Record<RequestHeader, string | undefined>
|
|
109
|
+
| Record<string, string | undefined>
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface ProxyFetch {
|
|
113
|
+
(
|
|
114
|
+
input: string | URL | Request,
|
|
115
|
+
init?: ProxyRequestInit
|
|
116
|
+
): Promise<Response>
|
|
117
|
+
}
|
|
118
|
+
```
|