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.
Files changed (106) hide show
  1. package/README.md +61 -2
  2. package/bin/create-acrylblends.js +4 -0
  3. package/dist/index.d.ts +56 -0
  4. package/dist/index.js +274 -0
  5. package/dist/index.js.map +1 -0
  6. package/docs/framework/LICENSE +23 -0
  7. package/docs/framework/README.md +63 -0
  8. package/docs/framework/UPSTREAM.md +3 -0
  9. package/docs/framework/agents/index.md +24 -0
  10. package/docs/framework/api/acryl.md +259 -0
  11. package/docs/framework/api/context.md +527 -0
  12. package/docs/framework/api/exception.md +80 -0
  13. package/docs/framework/api/index.md +12 -0
  14. package/docs/framework/api/presets.md +65 -0
  15. package/docs/framework/api/request.md +396 -0
  16. package/docs/framework/api/routing.md +303 -0
  17. package/docs/framework/api/summary.md +140 -0
  18. package/docs/framework/concepts/benchmarks.md +26 -0
  19. package/docs/framework/concepts/developer-experience.md +5 -0
  20. package/docs/framework/concepts/middleware.md +21 -0
  21. package/docs/framework/concepts/motivation.md +14 -0
  22. package/docs/framework/concepts/routers.md +63 -0
  23. package/docs/framework/concepts/stacks.md +235 -0
  24. package/docs/framework/concepts/web-standard.md +37 -0
  25. package/docs/framework/cordis/acryl-in-a-cordis-app.md +146 -0
  26. package/docs/framework/cordis/concepts.md +105 -0
  27. package/docs/framework/cordis/middleware.md +8 -0
  28. package/docs/framework/cordis/migration-from-0-3.md +111 -0
  29. package/docs/framework/cordis/migration-from-0-4.md +14 -0
  30. package/docs/framework/cordis/migration-from-dsh-webserver.md +109 -0
  31. package/docs/framework/cordis/mounting.md +85 -0
  32. package/docs/framework/cordis/node-server.md +75 -0
  33. package/docs/framework/cordis/plugin-lifecycle.md +111 -0
  34. package/docs/framework/cordis/surfaces.md +71 -0
  35. package/docs/framework/cordis/transport-and-cancellation.md +57 -0
  36. package/docs/framework/cordis/validation.md +135 -0
  37. package/docs/framework/getting-started/basic.md +196 -0
  38. package/docs/framework/getting-started/bun.md +221 -0
  39. package/docs/framework/getting-started/nodejs.md +344 -0
  40. package/docs/framework/getting-started/other-runtimes.md +23 -0
  41. package/docs/framework/guides/best-practices.md +231 -0
  42. package/docs/framework/guides/create-acrylblends.md +79 -0
  43. package/docs/framework/guides/examples.md +3 -0
  44. package/docs/framework/guides/faq.md +19 -0
  45. package/docs/framework/guides/helpers.md +33 -0
  46. package/docs/framework/guides/jsx-dom.md +299 -0
  47. package/docs/framework/guides/jsx.md +440 -0
  48. package/docs/framework/guides/middleware.md +253 -0
  49. package/docs/framework/guides/rpc.md +886 -0
  50. package/docs/framework/guides/testing.md +133 -0
  51. package/docs/framework/guides/validation.md +383 -0
  52. package/docs/framework/helpers/accepts.md +60 -0
  53. package/docs/framework/helpers/adapter.md +87 -0
  54. package/docs/framework/helpers/conninfo.md +112 -0
  55. package/docs/framework/helpers/cookie.md +232 -0
  56. package/docs/framework/helpers/css.md +285 -0
  57. package/docs/framework/helpers/dev.md +67 -0
  58. package/docs/framework/helpers/factory.md +180 -0
  59. package/docs/framework/helpers/html.md +123 -0
  60. package/docs/framework/helpers/jwt.md +184 -0
  61. package/docs/framework/helpers/proxy.md +118 -0
  62. package/docs/framework/helpers/route.md +143 -0
  63. package/docs/framework/helpers/ssg.md +393 -0
  64. package/docs/framework/helpers/streaming.md +119 -0
  65. package/docs/framework/helpers/testing.md +101 -0
  66. package/docs/framework/helpers/websocket.md +215 -0
  67. package/docs/framework/middleware/builtin/basic-auth.md +165 -0
  68. package/docs/framework/middleware/builtin/bearer-auth.md +138 -0
  69. package/docs/framework/middleware/builtin/body-limit.md +71 -0
  70. package/docs/framework/middleware/builtin/cache.md +122 -0
  71. package/docs/framework/middleware/builtin/combine.md +94 -0
  72. package/docs/framework/middleware/builtin/compress.md +59 -0
  73. package/docs/framework/middleware/builtin/context-storage.md +78 -0
  74. package/docs/framework/middleware/builtin/cors.md +135 -0
  75. package/docs/framework/middleware/builtin/csrf.md +105 -0
  76. package/docs/framework/middleware/builtin/etag.md +54 -0
  77. package/docs/framework/middleware/builtin/ip-restriction.md +69 -0
  78. package/docs/framework/middleware/builtin/jsx-renderer.md +286 -0
  79. package/docs/framework/middleware/builtin/jwk.md +192 -0
  80. package/docs/framework/middleware/builtin/jwt.md +145 -0
  81. package/docs/framework/middleware/builtin/language.md +232 -0
  82. package/docs/framework/middleware/builtin/logger.md +74 -0
  83. package/docs/framework/middleware/builtin/loopback-only.md +31 -0
  84. package/docs/framework/middleware/builtin/method-not-allowed.md +48 -0
  85. package/docs/framework/middleware/builtin/method-override.md +80 -0
  86. package/docs/framework/middleware/builtin/mount.md +106 -0
  87. package/docs/framework/middleware/builtin/pretty-json.md +53 -0
  88. package/docs/framework/middleware/builtin/request-id.md +94 -0
  89. package/docs/framework/middleware/builtin/secure-headers.md +247 -0
  90. package/docs/framework/middleware/builtin/timeout.md +98 -0
  91. package/docs/framework/middleware/builtin/timing.md +101 -0
  92. package/docs/framework/middleware/builtin/trailing-slash.md +74 -0
  93. package/docs/framework/middleware/third-party.md +4 -0
  94. package/docs/framework/upstream.json +9 -0
  95. package/package.json +47 -3
  96. package/templates/bun/gitignore +1 -0
  97. package/templates/bun/package.json +19 -0
  98. package/templates/bun/src/index.ts +7 -0
  99. package/templates/bun/test/app.test.ts +8 -0
  100. package/templates/bun/tsconfig.json +15 -0
  101. package/templates/nodejs/gitignore +1 -0
  102. package/templates/nodejs/package.json +22 -0
  103. package/templates/nodejs/src/app.ts +7 -0
  104. package/templates/nodejs/src/index.ts +9 -0
  105. package/templates/nodejs/test/app.test.ts +9 -0
  106. package/templates/nodejs/tsconfig.json +16 -0
@@ -0,0 +1,396 @@
1
+ # AcrylRequest
2
+
3
+ The `AcrylRequest` is an object that can be taken from `c.req` which wraps a [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object.
4
+
5
+ ## param()
6
+
7
+ Get the values of path parameters.
8
+
9
+ ```ts twoslash
10
+ import { Acryl } from 'acrylblends'
11
+ const app = new Acryl()
12
+ // ---cut---
13
+ // Captured params
14
+ app.get('/entry/:id', async (c) => {
15
+ const id = c.req.param('id')
16
+ // ^?
17
+ // ...
18
+ })
19
+
20
+ // Get all params at once
21
+ app.get('/entry/:id/comment/:commentId', async (c) => {
22
+ const { id, commentId } = c.req.param()
23
+ // ^?
24
+ })
25
+ ```
26
+
27
+ ## query()
28
+
29
+ Get querystring parameters.
30
+
31
+ ```ts twoslash
32
+ import { Acryl } from 'acrylblends'
33
+ const app = new Acryl()
34
+ // ---cut---
35
+ // Query params
36
+ app.get('/search', async (c) => {
37
+ const query = c.req.query('q')
38
+ // ^?
39
+ })
40
+
41
+ // Get all params at once
42
+ app.get('/search', async (c) => {
43
+ const { q, limit, offset } = c.req.query()
44
+ // ^?
45
+ })
46
+ ```
47
+
48
+ ## queries()
49
+
50
+ Get multiple querystring parameter values, e.g. `/search?tags=A&tags=B`
51
+
52
+ ```ts twoslash
53
+ import { Acryl } from 'acrylblends'
54
+ const app = new Acryl()
55
+ // ---cut---
56
+ app.get('/search', async (c) => {
57
+ // tags will be string[]
58
+ const tags = c.req.queries('tags')
59
+ // ^?
60
+ // ...
61
+ })
62
+ ```
63
+
64
+ ## header()
65
+
66
+ Get the request header value.
67
+
68
+ ```ts twoslash
69
+ import { Acryl } from 'acrylblends'
70
+ const app = new Acryl()
71
+ // ---cut---
72
+ app.get('/', (c) => {
73
+ const userAgent = c.req.header('User-Agent')
74
+ // ^?
75
+ return c.text(`Your user agent is ${userAgent}`)
76
+ })
77
+ ```
78
+
79
+ **Warning.**
80
+
81
+ When `c.req.header()` is called with no arguments, all keys in the returned record are **lowercase**.
82
+
83
+ If you want to get the value of a header with an uppercase name,
84
+ use `c.req.header(“X-Foo”)`.
85
+
86
+ ```ts
87
+ // ❌ Will not work
88
+ const headerRecord = c.req.header()
89
+ const foo = headerRecord['X-Foo']
90
+
91
+ // ✅ Will work
92
+ const foo = c.req.header('X-Foo')
93
+ ```
94
+
95
+ ## parseBody()
96
+
97
+ Parse Request body of type `multipart/form-data` or `application/x-www-form-urlencoded`
98
+
99
+ ```ts twoslash
100
+ import { Acryl } from 'acrylblends'
101
+ const app = new Acryl()
102
+ // ---cut---
103
+ app.post('/entry', async (c) => {
104
+ const body = await c.req.parseBody()
105
+ // ...
106
+ })
107
+ ```
108
+
109
+ `parseBody()` supports the following behaviors.
110
+
111
+ **Single file**
112
+
113
+ ```ts twoslash
114
+ import { Context } from 'acrylblends'
115
+ declare const c: Context
116
+ // ---cut---
117
+ const body = await c.req.parseBody()
118
+ const data = body['foo']
119
+ // ^?
120
+ ```
121
+
122
+ `body['foo']` is `(string | File)`.
123
+
124
+ If multiple files are uploaded, the last one will be used.
125
+
126
+ ### Multiple files
127
+
128
+ ```ts twoslash
129
+ import { Context } from 'acrylblends'
130
+ declare const c: Context
131
+ // ---cut---
132
+ const body = await c.req.parseBody()
133
+ body['foo[]']
134
+ ```
135
+
136
+ `body['foo[]']` is always `(string | File)[]`.
137
+
138
+ `[]` postfix is required.
139
+
140
+ ### Multiple files or fields with same name
141
+
142
+ If you have an input field that allows multiple `<input type="file" multiple />` or multiple checkboxes with the same name `<input type="checkbox" name="favorites" value="Acryl"/>`.
143
+
144
+ ```ts twoslash
145
+ import { Context } from 'acrylblends'
146
+ declare const c: Context
147
+ // ---cut---
148
+ const body = await c.req.parseBody({ all: true })
149
+ body['foo']
150
+ ```
151
+
152
+ `all` option is disabled by default.
153
+
154
+ - If `body['foo']` is multiple files, it will be parsed to `(string | File)[]`.
155
+ - If `body['foo']` is single file, it will be parsed to `(string | File)`.
156
+
157
+ ### Dot notation
158
+
159
+ If you set the `dot` option `true`, the return value is structured based on the dot notation.
160
+
161
+ Imagine receiving the following data:
162
+
163
+ ```ts twoslash
164
+ const data = new FormData()
165
+ data.append('obj.key1', 'value1')
166
+ data.append('obj.key2', 'value2')
167
+ ```
168
+
169
+ You can get the structured value by setting the `dot` option `true`:
170
+
171
+ ```ts twoslash
172
+ import { Context } from 'acrylblends'
173
+ declare const c: Context
174
+ // ---cut---
175
+ const body = await c.req.parseBody({ dot: true })
176
+ // body is `{ obj: { key1: 'value1', key2: 'value2' } }`
177
+ ```
178
+
179
+ ## json()
180
+
181
+ Parses the request body of type `application/json`
182
+
183
+ ```ts twoslash
184
+ import { Acryl } from 'acrylblends'
185
+ const app = new Acryl()
186
+ // ---cut---
187
+ app.post('/entry', async (c) => {
188
+ const body = await c.req.json()
189
+ // ...
190
+ })
191
+ ```
192
+
193
+ ## text()
194
+
195
+ Parses the request body of type `text/plain`
196
+
197
+ ```ts twoslash
198
+ import { Acryl } from 'acrylblends'
199
+ const app = new Acryl()
200
+ // ---cut---
201
+ app.post('/entry', async (c) => {
202
+ const body = await c.req.text()
203
+ // ...
204
+ })
205
+ ```
206
+
207
+ ## arrayBuffer()
208
+
209
+ Parses the request body as an `ArrayBuffer`
210
+
211
+ ```ts twoslash
212
+ import { Acryl } from 'acrylblends'
213
+ const app = new Acryl()
214
+ // ---cut---
215
+ app.post('/entry', async (c) => {
216
+ const body = await c.req.arrayBuffer()
217
+ // ...
218
+ })
219
+ ```
220
+
221
+ ## blob()
222
+
223
+ Parses the request body as a `Blob`.
224
+
225
+ ```ts twoslash
226
+ import { Acryl } from 'acrylblends'
227
+ const app = new Acryl()
228
+ // ---cut---
229
+ app.post('/entry', async (c) => {
230
+ const body = await c.req.blob()
231
+ // ...
232
+ })
233
+ ```
234
+
235
+ ## formData()
236
+
237
+ Parses the request body as a `FormData`.
238
+
239
+ ```ts twoslash
240
+ import { Acryl } from 'acrylblends'
241
+ const app = new Acryl()
242
+ // ---cut---
243
+ app.post('/entry', async (c) => {
244
+ const body = await c.req.formData()
245
+ // ...
246
+ })
247
+ ```
248
+
249
+ ## valid()
250
+
251
+ Get the validated data.
252
+
253
+ ```ts
254
+ app.post('/posts', async (c) => {
255
+ const { title, body } = c.req.valid('form')
256
+ // ...
257
+ })
258
+ ```
259
+
260
+ Available targets are below.
261
+
262
+ - `form`
263
+ - `json`
264
+ - `query`
265
+ - `header`
266
+ - `cookie`
267
+ - `param`
268
+
269
+ See the [Validation section](../guides/validation.md) for usage examples.
270
+
271
+ ## routePath
272
+
273
+ > **Warning.** **Deprecated in v4.8.0**: This property is deprecated. Use `routePath()` from [Route Helper](../helpers/route.md) instead.
274
+
275
+ You can retrieve the registered path within the handler like this:
276
+
277
+ ```ts twoslash
278
+ import { Acryl } from 'acrylblends'
279
+ const app = new Acryl()
280
+ // ---cut---
281
+ app.get('/posts/:id', (c) => {
282
+ return c.json({ path: c.req.routePath })
283
+ })
284
+ ```
285
+
286
+ If you access `/posts/123`, it will return `/posts/:id`:
287
+
288
+ ```json
289
+ { "path": "/posts/:id" }
290
+ ```
291
+
292
+ ## matchedRoutes
293
+
294
+ > **Warning.** **Deprecated in v4.8.0**: This property is deprecated. Use `matchedRoutes()` from [Route Helper](../helpers/route.md) instead.
295
+
296
+ It returns matched routes within the handler, which is useful for debugging.
297
+
298
+ ```ts twoslash
299
+ import { Acryl } from 'acrylblends'
300
+ const app = new Acryl()
301
+ // ---cut---
302
+ app.use(async function logger(c, next) {
303
+ await next()
304
+ c.req.matchedRoutes.forEach(({ handler, method, path }, i) => {
305
+ const name =
306
+ handler.name ||
307
+ (handler.length < 2 ? '[handler]' : '[middleware]')
308
+ console.log(
309
+ method,
310
+ ' ',
311
+ path,
312
+ ' '.repeat(Math.max(10 - path.length, 0)),
313
+ name,
314
+ i === c.req.routeIndex ? '<- respond from here' : ''
315
+ )
316
+ })
317
+ })
318
+ ```
319
+
320
+ ## path
321
+
322
+ The request pathname.
323
+
324
+ ```ts twoslash
325
+ import { Acryl } from 'acrylblends'
326
+ const app = new Acryl()
327
+ // ---cut---
328
+ app.get('/about/me', async (c) => {
329
+ const pathname = c.req.path // `/about/me`
330
+ // ...
331
+ })
332
+ ```
333
+
334
+ ## url
335
+
336
+ The request url strings.
337
+
338
+ ```ts twoslash
339
+ import { Acryl } from 'acrylblends'
340
+ const app = new Acryl()
341
+ // ---cut---
342
+ app.get('/about/me', async (c) => {
343
+ const url = c.req.url // `http://localhost:8787/about/me`
344
+ // ...
345
+ })
346
+ ```
347
+
348
+ ## method
349
+
350
+ The method name of the request.
351
+
352
+ ```ts twoslash
353
+ import { Acryl } from 'acrylblends'
354
+ const app = new Acryl()
355
+ // ---cut---
356
+ app.get('/about/me', async (c) => {
357
+ const method = c.req.method // `GET`
358
+ // ...
359
+ })
360
+ ```
361
+
362
+ ## raw
363
+
364
+ The raw [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object.
365
+
366
+ ```ts
367
+ // For Cloudflare Workers
368
+ app.post('/', async (c) => {
369
+ const metadata = c.req.raw.cf?.hostMetadata?
370
+ // ...
371
+ })
372
+ ```
373
+
374
+ ## cloneRawRequest()
375
+
376
+ Clones the raw Request object from a AcrylRequest. Works even after the request body has been consumed by validators or AcrylRequest methods.
377
+
378
+ ```ts twoslash
379
+ import { Acryl } from 'acrylblends'
380
+ const app = new Acryl()
381
+
382
+ import { cloneRawRequest } from 'acrylblends/request'
383
+ import { validator } from 'acrylblends/validator'
384
+
385
+ app.post(
386
+ '/forward',
387
+ validator('json', (data) => data),
388
+ async (c) => {
389
+ // Clone after validation
390
+ const clonedReq = await cloneRawRequest(c.req)
391
+ // Does not throw the error
392
+ await clonedReq.json()
393
+ // ...
394
+ }
395
+ )
396
+ ```
@@ -0,0 +1,303 @@
1
+ # Routing
2
+
3
+ Routing of Acryl is flexible and intuitive.
4
+ Let's take a look.
5
+
6
+ ## Basic
7
+
8
+ ```ts twoslash
9
+ import { Acryl } from 'acrylblends'
10
+ const app = new Acryl()
11
+ // ---cut---
12
+ // HTTP Methods
13
+ app.get('/', (c) => c.text('GET /'))
14
+ app.post('/', (c) => c.text('POST /'))
15
+ app.put('/', (c) => c.text('PUT /'))
16
+ app.delete('/', (c) => c.text('DELETE /'))
17
+ app.query('/', (c) => c.text('QUERY /'))
18
+
19
+ // Wildcard
20
+ app.get('/wild/*/card', (c) => {
21
+ return c.text('GET /wild/*/card')
22
+ })
23
+
24
+ // Any HTTP methods
25
+ app.all('/hello', (c) => c.text('Any Method /hello'))
26
+
27
+ // Custom HTTP method
28
+ app.on('PURGE', '/cache', (c) => c.text('PURGE Method /cache'))
29
+
30
+ // Multiple Method
31
+ app.on(['PUT', 'DELETE'], '/post', (c) =>
32
+ c.text('PUT or DELETE /post')
33
+ )
34
+
35
+ // Multiple Paths
36
+ app.on('GET', ['/hello', '/ja/hello', '/en/hello'], (c) =>
37
+ c.text('Hello')
38
+ )
39
+ ```
40
+
41
+ ## Path Parameter
42
+
43
+ ```ts twoslash
44
+ import { Acryl } from 'acrylblends'
45
+ const app = new Acryl()
46
+ // ---cut---
47
+ app.get('/user/:name', async (c) => {
48
+ const name = c.req.param('name')
49
+ // ^?
50
+ // ...
51
+ })
52
+ ```
53
+
54
+ or all parameters at once:
55
+
56
+ ```ts twoslash
57
+ import { Acryl } from 'acrylblends'
58
+ const app = new Acryl()
59
+ // ---cut---
60
+ app.get('/posts/:id/comment/:comment_id', async (c) => {
61
+ const { id, comment_id } = c.req.param()
62
+ // ^?
63
+ // ...
64
+ })
65
+ ```
66
+
67
+ ## Optional Parameter
68
+
69
+ ```ts twoslash
70
+ import { Acryl } from 'acrylblends'
71
+ const app = new Acryl()
72
+ // ---cut---
73
+ // Will match `/api/animal` and `/api/animal/:type`
74
+ app.get('/api/animal/:type?', (c) => c.text('Animal!'))
75
+ ```
76
+
77
+ ## Regexp
78
+
79
+ ```ts twoslash
80
+ import { Acryl } from 'acrylblends'
81
+ const app = new Acryl()
82
+ // ---cut---
83
+ app.get('/post/:date{[0-9]+}/:title{[a-z]+}', async (c) => {
84
+ const { date, title } = c.req.param()
85
+ // ^?
86
+ // ...
87
+ })
88
+ ```
89
+
90
+ ## Including slashes
91
+
92
+ ```ts twoslash
93
+ import { Acryl } from 'acrylblends'
94
+ const app = new Acryl()
95
+ // ---cut---
96
+ app.get('/posts/:filename{.+\\.png}', async (c) => {
97
+ //...
98
+ })
99
+ ```
100
+
101
+ ## Chained route
102
+
103
+ ```ts twoslash
104
+ import { Acryl } from 'acrylblends'
105
+ const app = new Acryl()
106
+ // ---cut---
107
+ app
108
+ .get('/endpoint', (c) => {
109
+ return c.text('GET /endpoint')
110
+ })
111
+ .post((c) => {
112
+ return c.text('POST /endpoint')
113
+ })
114
+ .delete((c) => {
115
+ return c.text('DELETE /endpoint')
116
+ })
117
+ ```
118
+
119
+ ## Grouping
120
+
121
+ You can group the routes with the Acryl instance and add them to the main app with the route method.
122
+
123
+ ```ts twoslash
124
+ import { Acryl } from 'acrylblends'
125
+ // ---cut---
126
+ const book = new Acryl()
127
+
128
+ book.get('/', (c) => c.text('List Books')) // GET /book
129
+ book.get('/:id', (c) => {
130
+ // GET /book/:id
131
+ const id = c.req.param('id')
132
+ return c.text('Get Book: ' + id)
133
+ })
134
+ book.post('/', (c) => c.text('Create Book')) // POST /book
135
+
136
+ const app = new Acryl()
137
+ app.route('/book', book)
138
+ ```
139
+
140
+ ## Grouping without changing base
141
+
142
+ You can also group multiple instances while keeping base.
143
+
144
+ ```ts twoslash
145
+ import { Acryl } from 'acrylblends'
146
+ // ---cut---
147
+ const book = new Acryl()
148
+ book.get('/book', (c) => c.text('List Books')) // GET /book
149
+ book.post('/book', (c) => c.text('Create Book')) // POST /book
150
+
151
+ const user = new Acryl().basePath('/user')
152
+ user.get('/', (c) => c.text('List Users')) // GET /user
153
+ user.post('/', (c) => c.text('Create User')) // POST /user
154
+
155
+ const app = new Acryl()
156
+ app.route('/', book) // Handle /book
157
+ app.route('/', user) // Handle /user
158
+ ```
159
+
160
+ ## Base path
161
+
162
+ You can specify the base path.
163
+
164
+ ```ts twoslash
165
+ import { Acryl } from 'acrylblends'
166
+ // ---cut---
167
+ const api = new Acryl().basePath('/api')
168
+ api.get('/book', (c) => c.text('List Books')) // GET /api/book
169
+ ```
170
+
171
+ ## Routing with hostname
172
+
173
+ It works fine if it includes a hostname.
174
+
175
+ ```ts twoslash
176
+ import { Acryl } from 'acrylblends'
177
+ // ---cut---
178
+ const app = new Acryl({
179
+ getPath: (req) => req.url.replace(/^https?:\/([^?]+).*$/, '$1'),
180
+ })
181
+
182
+ app.get('/www1.example.com/hello', (c) => c.text('hello www1'))
183
+ app.get('/www2.example.com/hello', (c) => c.text('hello www2'))
184
+ ```
185
+
186
+ ## Routing with `host` Header value
187
+
188
+ Acryl can handle the `host` header value if you set the `getPath()` function in the Acryl constructor.
189
+
190
+ ```ts twoslash
191
+ import { Acryl } from 'acrylblends'
192
+ // ---cut---
193
+ const app = new Acryl({
194
+ getPath: (req) =>
195
+ '/' +
196
+ req.headers.get('host') +
197
+ req.url.replace(/^https?:\/\/[^/]+(\/[^?]*).*/, '$1'),
198
+ })
199
+
200
+ app.get('/www1.example.com/hello', (c) => c.text('hello www1'))
201
+
202
+ // A following request will match the route:
203
+ // new Request('http://www1.example.com/hello', {
204
+ // headers: { host: 'www1.example.com' },
205
+ // })
206
+ ```
207
+
208
+ By applying this, for example, you can change the routing by `User-Agent` header.
209
+
210
+ ## Routing priority
211
+
212
+ Handlers or middleware will be executed in registration order.
213
+
214
+ ```ts twoslash
215
+ import { Acryl } from 'acrylblends'
216
+ const app = new Acryl()
217
+ // ---cut---
218
+ app.get('/book/a', (c) => c.text('a')) // a
219
+ app.get('/book/:slug', (c) => c.text('common')) // common
220
+ ```
221
+
222
+ ```
223
+ GET /book/a ---> `a`
224
+ GET /book/b ---> `common`
225
+ ```
226
+
227
+ When a handler is executed, the process will be stopped.
228
+
229
+ ```ts twoslash
230
+ import { Acryl } from 'acrylblends'
231
+ const app = new Acryl()
232
+ // ---cut---
233
+ app.get('*', (c) => c.text('common')) // common
234
+ app.get('/foo', (c) => c.text('foo')) // foo
235
+ ```
236
+
237
+ ```
238
+ GET /foo ---> `common` // foo will not be dispatched
239
+ ```
240
+
241
+ If you have the middleware that you want to execute, write the code above the handler.
242
+
243
+ ```ts twoslash
244
+ import { Acryl } from 'acrylblends'
245
+ import { logger } from 'acrylblends/logger'
246
+ const app = new Acryl()
247
+ // ---cut---
248
+ app.use(logger())
249
+ app.get('/foo', (c) => c.text('foo'))
250
+ ```
251
+
252
+ If you want to have a "_fallback_" handler, write the code below the other handler.
253
+
254
+ ```ts twoslash
255
+ import { Acryl } from 'acrylblends'
256
+ const app = new Acryl()
257
+ // ---cut---
258
+ app.get('/bar', (c) => c.text('bar')) // bar
259
+ app.get('*', (c) => c.text('fallback')) // fallback
260
+ ```
261
+
262
+ ```
263
+ GET /bar ---> `bar`
264
+ GET /foo ---> `fallback`
265
+ ```
266
+
267
+ ## Grouping ordering
268
+
269
+ Note that the mistake of grouping routings is hard to notice.
270
+ The `route()` function takes the stored routing from the second argument (such as `three` or `two`) and adds it to its own (`two` or `app`) routing.
271
+
272
+ ```ts
273
+ three.get('/hi', (c) => c.text('hi'))
274
+ two.route('/three', three)
275
+ app.route('/two', two)
276
+
277
+ export default app
278
+ ```
279
+
280
+ It will return 200 response.
281
+
282
+ ```
283
+ GET /two/three/hi ---> `hi`
284
+ ```
285
+
286
+ However, if they are in the wrong order, it will return a 404.
287
+
288
+ ```ts twoslash
289
+ import { Acryl } from 'acrylblends'
290
+ const app = new Acryl()
291
+ const two = new Acryl()
292
+ const three = new Acryl()
293
+ // ---cut---
294
+ three.get('/hi', (c) => c.text('hi'))
295
+ app.route('/two', two) // `two` does not have routes
296
+ two.route('/three', three)
297
+
298
+ export default app
299
+ ```
300
+
301
+ ```
302
+ GET /two/three/hi ---> 404 Not Found
303
+ ```