create-acrylblends 0.0.0-stage → 0.1.1
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/README.md +61 -2
- 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 +109 -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
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
# Context
|
|
2
|
+
|
|
3
|
+
The `Context` object is instantiated for each request and kept until the response is returned. You can put values in it, set headers and a status code you want to return, and access AcrylRequest and Response objects.
|
|
4
|
+
|
|
5
|
+
## req
|
|
6
|
+
|
|
7
|
+
`req` is an instance of AcrylRequest. For more details, see [AcrylRequest](request.md).
|
|
8
|
+
|
|
9
|
+
```ts twoslash
|
|
10
|
+
import { Acryl } from 'acrylblends'
|
|
11
|
+
const app = new Acryl()
|
|
12
|
+
// ---cut---
|
|
13
|
+
app.get('/hello', (c) => {
|
|
14
|
+
const userAgent = c.req.header('User-Agent')
|
|
15
|
+
// ...
|
|
16
|
+
// ---cut-start---
|
|
17
|
+
return c.text(`Hello, ${userAgent}`)
|
|
18
|
+
// ---cut-end---
|
|
19
|
+
})
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## status()
|
|
23
|
+
|
|
24
|
+
You can set an HTTP status code with `c.status()`. The default is `200`. You don't have to use `c.status()` if the code is `200`.
|
|
25
|
+
|
|
26
|
+
```ts twoslash
|
|
27
|
+
import { Acryl } from 'acrylblends'
|
|
28
|
+
const app = new Acryl()
|
|
29
|
+
// ---cut---
|
|
30
|
+
app.post('/posts', (c) => {
|
|
31
|
+
// Set HTTP status code
|
|
32
|
+
c.status(201)
|
|
33
|
+
return c.text('Your post is created!')
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## header()
|
|
38
|
+
|
|
39
|
+
You can set HTTP Headers for the response.
|
|
40
|
+
|
|
41
|
+
```ts twoslash
|
|
42
|
+
import { Acryl } from 'acrylblends'
|
|
43
|
+
const app = new Acryl()
|
|
44
|
+
// ---cut---
|
|
45
|
+
app.get('/', (c) => {
|
|
46
|
+
// Set headers
|
|
47
|
+
c.header('X-Message', 'My custom message')
|
|
48
|
+
return c.text('Hello!')
|
|
49
|
+
})
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## body()
|
|
53
|
+
|
|
54
|
+
Return an HTTP response.
|
|
55
|
+
|
|
56
|
+
> **Note.** When returning text or HTML, it is recommended to use `c.text()` or `c.html()`.
|
|
57
|
+
|
|
58
|
+
```ts twoslash
|
|
59
|
+
import { Acryl } from 'acrylblends'
|
|
60
|
+
const app = new Acryl()
|
|
61
|
+
// ---cut---
|
|
62
|
+
app.get('/welcome', (c) => {
|
|
63
|
+
c.header('Content-Type', 'text/plain')
|
|
64
|
+
// Return the response body
|
|
65
|
+
return c.body('Thank you for coming')
|
|
66
|
+
})
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
You can also write the following.
|
|
70
|
+
|
|
71
|
+
```ts twoslash
|
|
72
|
+
import { Acryl } from 'acrylblends'
|
|
73
|
+
const app = new Acryl()
|
|
74
|
+
// ---cut---
|
|
75
|
+
app.get('/welcome', (c) => {
|
|
76
|
+
return c.body('Thank you for coming', 201, {
|
|
77
|
+
'X-Message': 'Hello!',
|
|
78
|
+
'Content-Type': 'text/plain',
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The response is the same `Response` object as below.
|
|
84
|
+
|
|
85
|
+
```ts twoslash
|
|
86
|
+
new Response('Thank you for coming', {
|
|
87
|
+
status: 201,
|
|
88
|
+
headers: {
|
|
89
|
+
'X-Message': 'Hello!',
|
|
90
|
+
'Content-Type': 'text/plain',
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## text()
|
|
96
|
+
|
|
97
|
+
Render text as `Content-Type: text/plain`.
|
|
98
|
+
|
|
99
|
+
```ts twoslash
|
|
100
|
+
import { Acryl } from 'acrylblends'
|
|
101
|
+
const app = new Acryl()
|
|
102
|
+
// ---cut---
|
|
103
|
+
app.get('/say', (c) => {
|
|
104
|
+
return c.text('Hello!')
|
|
105
|
+
})
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## json()
|
|
109
|
+
|
|
110
|
+
Render JSON as `Content-Type: application/json`.
|
|
111
|
+
|
|
112
|
+
```ts twoslash
|
|
113
|
+
import { Acryl } from 'acrylblends'
|
|
114
|
+
const app = new Acryl()
|
|
115
|
+
// ---cut---
|
|
116
|
+
app.get('/api', (c) => {
|
|
117
|
+
return c.json({ message: 'Hello!' })
|
|
118
|
+
})
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## html()
|
|
122
|
+
|
|
123
|
+
Render HTML as `Content-Type: text/html`.
|
|
124
|
+
|
|
125
|
+
```ts twoslash
|
|
126
|
+
import { Acryl } from 'acrylblends'
|
|
127
|
+
const app = new Acryl()
|
|
128
|
+
// ---cut---
|
|
129
|
+
app.get('/', (c) => {
|
|
130
|
+
return c.html('<h1>Hello! Acryl!</h1>')
|
|
131
|
+
})
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## notFound()
|
|
135
|
+
|
|
136
|
+
Return a `Not Found` Response. You can customize it with [`app.notFound()`](acryl.md#not-found).
|
|
137
|
+
|
|
138
|
+
```ts twoslash
|
|
139
|
+
import { Acryl } from 'acrylblends'
|
|
140
|
+
const app = new Acryl()
|
|
141
|
+
// ---cut---
|
|
142
|
+
app.get('/notfound', (c) => {
|
|
143
|
+
return c.notFound()
|
|
144
|
+
})
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## redirect()
|
|
148
|
+
|
|
149
|
+
Redirect, default status code is `302`.
|
|
150
|
+
|
|
151
|
+
```ts twoslash
|
|
152
|
+
import { Acryl } from 'acrylblends'
|
|
153
|
+
const app = new Acryl()
|
|
154
|
+
// ---cut---
|
|
155
|
+
app.get('/redirect', (c) => {
|
|
156
|
+
return c.redirect('/')
|
|
157
|
+
})
|
|
158
|
+
app.get('/redirect-permanently', (c) => {
|
|
159
|
+
return c.redirect('/', 301)
|
|
160
|
+
})
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## res
|
|
164
|
+
|
|
165
|
+
You can access the [Response] object that will be returned.
|
|
166
|
+
|
|
167
|
+
```ts twoslash
|
|
168
|
+
import { Acryl } from 'acrylblends'
|
|
169
|
+
const app = new Acryl()
|
|
170
|
+
// ---cut---
|
|
171
|
+
// Response object
|
|
172
|
+
app.use('/', async (c, next) => {
|
|
173
|
+
await next()
|
|
174
|
+
c.res.headers.append('X-Debug', 'Debug message')
|
|
175
|
+
})
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
[Response]: https://developer.mozilla.org/en-US/docs/Web/API/Response
|
|
179
|
+
|
|
180
|
+
## set() / get()
|
|
181
|
+
|
|
182
|
+
Get and set arbitrary key-value pairs, with a lifetime of the current request. This allows passing specific values between middleware or from middleware to route handlers.
|
|
183
|
+
|
|
184
|
+
```ts twoslash
|
|
185
|
+
import { Acryl } from 'acrylblends'
|
|
186
|
+
const app = new Acryl<{ Variables: { message: string } }>()
|
|
187
|
+
// ---cut---
|
|
188
|
+
app.use(async (c, next) => {
|
|
189
|
+
c.set('message', 'Acryl is cool!!')
|
|
190
|
+
await next()
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
app.get('/', (c) => {
|
|
194
|
+
const message = c.get('message')
|
|
195
|
+
return c.text(`The message is "${message}"`)
|
|
196
|
+
})
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Pass the `Variables` as Generics to the constructor of `Acryl` to make it type-safe.
|
|
200
|
+
|
|
201
|
+
```ts twoslash
|
|
202
|
+
import { Acryl } from 'acrylblends'
|
|
203
|
+
// ---cut---
|
|
204
|
+
type Variables = {
|
|
205
|
+
message: string
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const app = new Acryl<{ Variables: Variables }>()
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
The value of `c.set` / `c.get` are retained only within the same request. They cannot be shared or persisted across different requests.
|
|
212
|
+
|
|
213
|
+
## var
|
|
214
|
+
|
|
215
|
+
You can also access the value of a variable with `c.var`.
|
|
216
|
+
|
|
217
|
+
```ts twoslash
|
|
218
|
+
import type { Context } from 'acrylblends'
|
|
219
|
+
declare const c: Context
|
|
220
|
+
// ---cut---
|
|
221
|
+
const result = c.var.client.oneMethod()
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
If you want to create the middleware which provides a custom method,
|
|
225
|
+
write like the following:
|
|
226
|
+
|
|
227
|
+
```ts twoslash
|
|
228
|
+
import { Acryl } from 'acrylblends'
|
|
229
|
+
import { createMiddleware } from 'acrylblends/factory'
|
|
230
|
+
// ---cut---
|
|
231
|
+
type Env = {
|
|
232
|
+
Variables: {
|
|
233
|
+
echo: (str: string) => string
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const app = new Acryl()
|
|
238
|
+
|
|
239
|
+
const echoMiddleware = createMiddleware<Env>(async (c, next) => {
|
|
240
|
+
c.set('echo', (str) => str)
|
|
241
|
+
await next()
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
app.get('/echo', echoMiddleware, (c) => {
|
|
245
|
+
return c.text(c.var.echo('Hello!'))
|
|
246
|
+
})
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
If you want to use the middleware in multiple handlers, you can use `app.use()`.
|
|
250
|
+
Then, you have to pass the `Env` as Generics to the constructor of `Acryl` to make it type-safe.
|
|
251
|
+
|
|
252
|
+
```ts twoslash
|
|
253
|
+
import { Acryl } from 'acrylblends'
|
|
254
|
+
import type { MiddlewareHandler } from 'acrylblends/types'
|
|
255
|
+
declare const echoMiddleware: MiddlewareHandler
|
|
256
|
+
type Env = {
|
|
257
|
+
Variables: {
|
|
258
|
+
echo: (str: string) => string
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// ---cut---
|
|
262
|
+
const app = new Acryl<Env>()
|
|
263
|
+
|
|
264
|
+
app.use(echoMiddleware)
|
|
265
|
+
|
|
266
|
+
app.get('/echo', (c) => {
|
|
267
|
+
return c.text(c.var.echo('Hello!'))
|
|
268
|
+
})
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## render() / setRenderer()
|
|
272
|
+
|
|
273
|
+
You can set a layout using `c.setRenderer()` within a custom middleware.
|
|
274
|
+
|
|
275
|
+
```tsx
|
|
276
|
+
/** @jsx jsx */
|
|
277
|
+
/** @jsxImportSource acrylblends/jsx */
|
|
278
|
+
import { Acryl } from 'acrylblends'
|
|
279
|
+
const app = new Acryl()
|
|
280
|
+
// ---cut---
|
|
281
|
+
app.use(async (c, next) => {
|
|
282
|
+
c.setRenderer((content) => {
|
|
283
|
+
return c.html(
|
|
284
|
+
<html>
|
|
285
|
+
<body>
|
|
286
|
+
<p>{content}</p>
|
|
287
|
+
</body>
|
|
288
|
+
</html>
|
|
289
|
+
)
|
|
290
|
+
})
|
|
291
|
+
await next()
|
|
292
|
+
})
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Then, you can utilize `c.render()` to create responses within this layout.
|
|
296
|
+
|
|
297
|
+
```ts twoslash
|
|
298
|
+
import { Acryl } from 'acrylblends'
|
|
299
|
+
const app = new Acryl()
|
|
300
|
+
// ---cut---
|
|
301
|
+
app.get('/', (c) => {
|
|
302
|
+
return c.render('Hello!')
|
|
303
|
+
})
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
The output of which will be:
|
|
307
|
+
|
|
308
|
+
```html
|
|
309
|
+
<html>
|
|
310
|
+
<body>
|
|
311
|
+
<p>Hello!</p>
|
|
312
|
+
</body>
|
|
313
|
+
</html>
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Additionally, this feature offers the flexibility to customize arguments.
|
|
317
|
+
To ensure type safety, types can be defined as:
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
declare module 'acrylblends' {
|
|
321
|
+
interface ContextRenderer {
|
|
322
|
+
(
|
|
323
|
+
content: string | Promise<string>,
|
|
324
|
+
head: { title: string }
|
|
325
|
+
): Response | Promise<Response>
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Here's an example of how you can use this:
|
|
331
|
+
|
|
332
|
+
```ts
|
|
333
|
+
app.use('/pages/*', async (c, next) => {
|
|
334
|
+
c.setRenderer((content, head) => {
|
|
335
|
+
return c.html(
|
|
336
|
+
<html>
|
|
337
|
+
<head>
|
|
338
|
+
<title>{head.title}</title>
|
|
339
|
+
</head>
|
|
340
|
+
<body>
|
|
341
|
+
<header>{head.title}</header>
|
|
342
|
+
<p>{content}</p>
|
|
343
|
+
</body>
|
|
344
|
+
</html>
|
|
345
|
+
)
|
|
346
|
+
})
|
|
347
|
+
await next()
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
app.get('/pages/my-favorite', (c) => {
|
|
351
|
+
return c.render(<p>Ramen and Sushi</p>, {
|
|
352
|
+
title: 'My favorite',
|
|
353
|
+
})
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
app.get('/pages/my-hobbies', (c) => {
|
|
357
|
+
return c.render(<p>Watching baseball</p>, {
|
|
358
|
+
title: 'My hobbies',
|
|
359
|
+
})
|
|
360
|
+
})
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## executionCtx
|
|
364
|
+
|
|
365
|
+
You can access Cloudflare Workers' specific [ExecutionContext](https://developers.cloudflare.com/workers/runtime-apis/context/).
|
|
366
|
+
|
|
367
|
+
```ts twoslash
|
|
368
|
+
import { Acryl } from 'acrylblends'
|
|
369
|
+
const app = new Acryl<{
|
|
370
|
+
Bindings: {
|
|
371
|
+
KV: any
|
|
372
|
+
}
|
|
373
|
+
}>()
|
|
374
|
+
declare const key: string
|
|
375
|
+
declare const data: string
|
|
376
|
+
// ---cut---
|
|
377
|
+
// ExecutionContext object
|
|
378
|
+
app.get('/foo', async (c) => {
|
|
379
|
+
c.executionCtx.waitUntil(c.env.KV.put(key, data))
|
|
380
|
+
// ...
|
|
381
|
+
})
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
The `ExecutionContext` also has an [`exports`](https://developers.cloudflare.com/workers/runtime-apis/context/#exports) field. To get autocomplete with Wrangler's generated types, you can use module augmentation:
|
|
385
|
+
|
|
386
|
+
```ts
|
|
387
|
+
import 'acrylblends'
|
|
388
|
+
|
|
389
|
+
declare module 'acrylblends' {
|
|
390
|
+
interface ExecutionContext {
|
|
391
|
+
readonly exports: Cloudflare.Exports
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
## event
|
|
397
|
+
|
|
398
|
+
You can access Cloudflare Workers' specific `FetchEvent`. This was used in "Service Worker" syntax. But, it is not recommended now.
|
|
399
|
+
|
|
400
|
+
```ts twoslash
|
|
401
|
+
import { Acryl } from 'acrylblends'
|
|
402
|
+
declare const key: string
|
|
403
|
+
declare const data: string
|
|
404
|
+
type KVNamespace = any
|
|
405
|
+
// ---cut---
|
|
406
|
+
// Type definition to make type inference
|
|
407
|
+
type Bindings = {
|
|
408
|
+
MY_KV: KVNamespace
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const app = new Acryl<{ Bindings: Bindings }>()
|
|
412
|
+
|
|
413
|
+
// FetchEvent object (only set when using Service Worker syntax)
|
|
414
|
+
app.get('/foo', async (c) => {
|
|
415
|
+
c.event.waitUntil(c.env.MY_KV.put(key, data))
|
|
416
|
+
// ...
|
|
417
|
+
})
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## env
|
|
421
|
+
|
|
422
|
+
In Cloudflare Workers Environment variables, secrets, KV namespaces, D1 database, R2 bucket etc. that are bound to a worker are known as bindings.
|
|
423
|
+
Regardless of type, bindings are always available as global variables and can be accessed via the context `c.env.BINDING_KEY`.
|
|
424
|
+
|
|
425
|
+
```ts twoslash
|
|
426
|
+
import { Acryl } from 'acrylblends'
|
|
427
|
+
type KVNamespace = any
|
|
428
|
+
// ---cut---
|
|
429
|
+
// Type definition to make type inference
|
|
430
|
+
type Bindings = {
|
|
431
|
+
MY_KV: KVNamespace
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const app = new Acryl<{ Bindings: Bindings }>()
|
|
435
|
+
|
|
436
|
+
// Environment object for Cloudflare Workers
|
|
437
|
+
app.get('/', async (c) => {
|
|
438
|
+
c.env.MY_KV.get('my-key')
|
|
439
|
+
// ...
|
|
440
|
+
})
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
## error
|
|
444
|
+
|
|
445
|
+
If the Handler throws an error, the error object is placed in `c.error`.
|
|
446
|
+
You can access it in your middleware.
|
|
447
|
+
|
|
448
|
+
```ts twoslash
|
|
449
|
+
import { Acryl } from 'acrylblends'
|
|
450
|
+
const app = new Acryl()
|
|
451
|
+
// ---cut---
|
|
452
|
+
app.use(async (c, next) => {
|
|
453
|
+
await next()
|
|
454
|
+
if (c.error) {
|
|
455
|
+
// do something...
|
|
456
|
+
}
|
|
457
|
+
})
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
## ContextVariableMap
|
|
461
|
+
|
|
462
|
+
**Warning.**
|
|
463
|
+
|
|
464
|
+
`ContextVariableMap` adds types **globally** to all contexts, regardless of whether the middleware that sets the variable has actually run. This means `c.get('result')` will appear type-safe even in handlers where your middleware was never registered, potentially hiding `undefined` bugs at runtime.
|
|
465
|
+
|
|
466
|
+
Take a look at the following example:
|
|
467
|
+
|
|
468
|
+
```ts
|
|
469
|
+
declare module 'acrylblends' {
|
|
470
|
+
interface ContextVariableMap {
|
|
471
|
+
result: string
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const mw = createMiddleware(async (c, next) => {
|
|
476
|
+
c.set('result', 'some values')
|
|
477
|
+
await next()
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
const app = new Acryl()
|
|
481
|
+
|
|
482
|
+
// handler uses the middleware
|
|
483
|
+
app.get('/foo', mw, (c) => {
|
|
484
|
+
const val = c.get('result') // ✅ val is a string and typed as such, as expected
|
|
485
|
+
})
|
|
486
|
+
|
|
487
|
+
// handler doesn't use the middleware
|
|
488
|
+
app.get('/bar', (c) => {
|
|
489
|
+
const val = c.get('result') // ❌ val is undefined but typed as a string, which can lead to runtime errors
|
|
490
|
+
})
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
You can augment the `ContextVariableMap` interface to define types for context variables globally across your entire application. This is appropriate when a variable is set by middleware that is applied app-wide and is guaranteed to exist in the context.
|
|
494
|
+
|
|
495
|
+
For example:
|
|
496
|
+
|
|
497
|
+
```ts
|
|
498
|
+
declare module 'acrylblends' {
|
|
499
|
+
interface ContextVariableMap {
|
|
500
|
+
result: string
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
You can then utilize this in your middleware:
|
|
506
|
+
|
|
507
|
+
```ts twoslash
|
|
508
|
+
import { createMiddleware } from 'acrylblends/factory'
|
|
509
|
+
// ---cut---
|
|
510
|
+
const mw = createMiddleware(async (c, next) => {
|
|
511
|
+
c.set('result', 'some values') // result is a string
|
|
512
|
+
await next()
|
|
513
|
+
})
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
In a handler, the variable is inferred as the proper type:
|
|
517
|
+
|
|
518
|
+
```ts twoslash
|
|
519
|
+
import { Acryl } from 'acrylblends'
|
|
520
|
+
const app = new Acryl<{ Variables: { result: string } }>()
|
|
521
|
+
// ---cut---
|
|
522
|
+
app.get('/', (c) => {
|
|
523
|
+
const val = c.get('result') // val is a string
|
|
524
|
+
// ...
|
|
525
|
+
return c.json({ result: val })
|
|
526
|
+
})
|
|
527
|
+
```
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# HTTPException
|
|
2
|
+
|
|
3
|
+
When a fatal error occurs, Acryl (and many ecosystem middleware) may throw an `HTTPException`. This is a custom Acryl `Error` that simplifies [returning error responses](#handling-httpexceptions).
|
|
4
|
+
|
|
5
|
+
## Throwing HTTPExceptions
|
|
6
|
+
|
|
7
|
+
You can throw your own HTTPExceptions by specifying a status code, and either a message or a custom response.
|
|
8
|
+
|
|
9
|
+
### Custom Message
|
|
10
|
+
|
|
11
|
+
For basic `text` responses, just set the error `message`.
|
|
12
|
+
|
|
13
|
+
```ts twoslash
|
|
14
|
+
import { HTTPException } from 'acrylblends/http-exception'
|
|
15
|
+
|
|
16
|
+
throw new HTTPException(401, { message: 'Unauthorized' })
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Custom Response
|
|
20
|
+
|
|
21
|
+
For other response types, or to set response headers, use the `res` option. _Note that the status passed to the constructor is the one used to create responses._
|
|
22
|
+
|
|
23
|
+
```ts twoslash
|
|
24
|
+
import { HTTPException } from 'acrylblends/http-exception'
|
|
25
|
+
|
|
26
|
+
const errorResponse = new Response('Unauthorized', {
|
|
27
|
+
status: 401, // this gets ignored
|
|
28
|
+
headers: {
|
|
29
|
+
Authenticate: 'error="invalid_token"',
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
throw new HTTPException(401, { res: errorResponse })
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Cause
|
|
37
|
+
|
|
38
|
+
In either case, you can use the [`cause`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) option to add arbitrary data to the HTTPException.
|
|
39
|
+
|
|
40
|
+
```ts twoslash
|
|
41
|
+
import { Acryl, Context } from 'acrylblends'
|
|
42
|
+
import { HTTPException } from 'acrylblends/http-exception'
|
|
43
|
+
const app = new Acryl()
|
|
44
|
+
declare const message: string
|
|
45
|
+
declare const authorize: (c: Context) => Promise<void>
|
|
46
|
+
// ---cut---
|
|
47
|
+
app.post('/login', async (c) => {
|
|
48
|
+
try {
|
|
49
|
+
await authorize(c)
|
|
50
|
+
} catch (cause) {
|
|
51
|
+
throw new HTTPException(401, { message, cause })
|
|
52
|
+
}
|
|
53
|
+
return c.redirect('/')
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Handling HTTPExceptions
|
|
58
|
+
|
|
59
|
+
You can handle uncaught HTTPExceptions with [`app.onError`](acryl.md#error-handling). They include a `getResponse` method that returns a new `Response` created from the error `status`, and either the error `message`, or the [custom response](#custom-response) set when the error was thrown.
|
|
60
|
+
|
|
61
|
+
```ts twoslash
|
|
62
|
+
import { Acryl } from 'acrylblends'
|
|
63
|
+
const app = new Acryl()
|
|
64
|
+
// ---cut---
|
|
65
|
+
import { HTTPException } from 'acrylblends/http-exception'
|
|
66
|
+
|
|
67
|
+
// ...
|
|
68
|
+
|
|
69
|
+
app.onError((err, c) => {
|
|
70
|
+
if (err instanceof HTTPException) {
|
|
71
|
+
// Return the error response generated by HTTPException
|
|
72
|
+
return err.getResponse()
|
|
73
|
+
}
|
|
74
|
+
// For any other unexpected errors, log and return a generic 500 response
|
|
75
|
+
console.error(err)
|
|
76
|
+
return c.text('Internal Server Error', 500)
|
|
77
|
+
})
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
> **Warning.** **`HTTPException.getResponse` is not aware of `Context`**. To include headers already set in `Context`, you must apply them to a new `Response`.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# API
|
|
2
|
+
|
|
3
|
+
Acryl's API is simple.
|
|
4
|
+
Just composed by extended objects from Web Standards.
|
|
5
|
+
So, you can understand it well quickly.
|
|
6
|
+
|
|
7
|
+
In this section, we introduce API of Acryl like below.
|
|
8
|
+
|
|
9
|
+
- Acryl object
|
|
10
|
+
- About routing
|
|
11
|
+
- Context object
|
|
12
|
+
- About middleware
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Presets
|
|
2
|
+
|
|
3
|
+
> **Partly available.** The default router (`SmartRouter`), `RegExpRouter`, `TrieRouter`, `LinearRouter` and `PatternRouter` are available and are chosen with the `router` option of `new Acryl()` (see [Routers](../concepts/routers.md)). The presets `acrylblends/quick` and `acrylblends/tiny` are **not yet available**; their examples below are not checked by the docs tests.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
Acryl has several routers, each designed for a specific purpose.
|
|
7
|
+
You can specify the router you want to use in the constructor of Acryl.
|
|
8
|
+
|
|
9
|
+
**Presets** are provided for common use cases, so you don't have to specify the router each time.
|
|
10
|
+
The `Acryl` class imported from all presets is the same, the only difference being the router.
|
|
11
|
+
Therefore, you can use them interchangeably.
|
|
12
|
+
|
|
13
|
+
## `acrylblends`
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
|
|
17
|
+
```ts twoslash
|
|
18
|
+
import { Acryl } from 'acrylblends'
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Routers:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
this.router = new SmartRouter({
|
|
25
|
+
routers: [new RegExpRouter(), new TrieRouter()],
|
|
26
|
+
})
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## `acrylblends/quick`
|
|
30
|
+
|
|
31
|
+
Usage:
|
|
32
|
+
|
|
33
|
+
```ts twoslash
|
|
34
|
+
import { Acryl } from 'acrylblends/quick'
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Router:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
this.router = new SmartRouter({
|
|
41
|
+
routers: [new LinearRouter(), new TrieRouter()],
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## `acrylblends/tiny`
|
|
46
|
+
|
|
47
|
+
Usage:
|
|
48
|
+
|
|
49
|
+
```ts twoslash
|
|
50
|
+
import { Acryl } from 'acrylblends/tiny'
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Router:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
this.router = new PatternRouter()
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Which preset should I use?
|
|
60
|
+
|
|
61
|
+
| Preset | Suitable platforms |
|
|
62
|
+
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
63
|
+
| `acrylblends` | This is highly recommended for most use cases. Although the registration phase may be slower than `acrylblends/quick`, it exhibits high performance once booted. It's ideal for long-life servers built with **Deno**, **Bun**, or **Node.js**. It is also suitable for **Fastly Compute**, as route registration occurs during the app build phase on that platform. For environments such as **Cloudflare Workers**, **Deno Deploy**, where v8 isolates are utilized, this preset is suitable as well because the isolations persist for a certain amount of time after booting. |
|
|
64
|
+
| `acrylblends/quick` | This preset is designed for environments where the application is initialized for every request. |
|
|
65
|
+
| `acrylblends/tiny` | This is the smallest router package and it's suitable for environments where resources are limited. |
|