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.
Files changed (106) hide show
  1. package/bin/create-acrylblends.js +4 -0
  2. package/dist/index.d.ts +56 -0
  3. package/dist/index.js +274 -0
  4. package/dist/index.js.map +1 -0
  5. package/docs/framework/LICENSE +23 -0
  6. package/docs/framework/README.md +63 -0
  7. package/docs/framework/UPSTREAM.md +3 -0
  8. package/docs/framework/agents/index.md +24 -0
  9. package/docs/framework/api/acryl.md +259 -0
  10. package/docs/framework/api/context.md +527 -0
  11. package/docs/framework/api/exception.md +80 -0
  12. package/docs/framework/api/index.md +12 -0
  13. package/docs/framework/api/presets.md +65 -0
  14. package/docs/framework/api/request.md +396 -0
  15. package/docs/framework/api/routing.md +303 -0
  16. package/docs/framework/api/summary.md +140 -0
  17. package/docs/framework/concepts/benchmarks.md +26 -0
  18. package/docs/framework/concepts/developer-experience.md +5 -0
  19. package/docs/framework/concepts/middleware.md +21 -0
  20. package/docs/framework/concepts/motivation.md +14 -0
  21. package/docs/framework/concepts/routers.md +63 -0
  22. package/docs/framework/concepts/stacks.md +235 -0
  23. package/docs/framework/concepts/web-standard.md +37 -0
  24. package/docs/framework/cordis/acryl-in-a-cordis-app.md +146 -0
  25. package/docs/framework/cordis/concepts.md +105 -0
  26. package/docs/framework/cordis/middleware.md +8 -0
  27. package/docs/framework/cordis/migration-from-0-3.md +111 -0
  28. package/docs/framework/cordis/migration-from-0-4.md +14 -0
  29. package/docs/framework/cordis/migration-from-dsh-webserver.md +103 -0
  30. package/docs/framework/cordis/mounting.md +85 -0
  31. package/docs/framework/cordis/node-server.md +75 -0
  32. package/docs/framework/cordis/plugin-lifecycle.md +111 -0
  33. package/docs/framework/cordis/surfaces.md +71 -0
  34. package/docs/framework/cordis/transport-and-cancellation.md +57 -0
  35. package/docs/framework/cordis/validation.md +135 -0
  36. package/docs/framework/getting-started/basic.md +196 -0
  37. package/docs/framework/getting-started/bun.md +221 -0
  38. package/docs/framework/getting-started/nodejs.md +344 -0
  39. package/docs/framework/getting-started/other-runtimes.md +23 -0
  40. package/docs/framework/guides/best-practices.md +231 -0
  41. package/docs/framework/guides/create-acrylblends.md +79 -0
  42. package/docs/framework/guides/examples.md +3 -0
  43. package/docs/framework/guides/faq.md +19 -0
  44. package/docs/framework/guides/helpers.md +33 -0
  45. package/docs/framework/guides/jsx-dom.md +299 -0
  46. package/docs/framework/guides/jsx.md +440 -0
  47. package/docs/framework/guides/middleware.md +253 -0
  48. package/docs/framework/guides/rpc.md +886 -0
  49. package/docs/framework/guides/testing.md +133 -0
  50. package/docs/framework/guides/validation.md +383 -0
  51. package/docs/framework/helpers/accepts.md +60 -0
  52. package/docs/framework/helpers/adapter.md +87 -0
  53. package/docs/framework/helpers/conninfo.md +112 -0
  54. package/docs/framework/helpers/cookie.md +232 -0
  55. package/docs/framework/helpers/css.md +285 -0
  56. package/docs/framework/helpers/dev.md +67 -0
  57. package/docs/framework/helpers/factory.md +180 -0
  58. package/docs/framework/helpers/html.md +123 -0
  59. package/docs/framework/helpers/jwt.md +184 -0
  60. package/docs/framework/helpers/proxy.md +118 -0
  61. package/docs/framework/helpers/route.md +143 -0
  62. package/docs/framework/helpers/ssg.md +393 -0
  63. package/docs/framework/helpers/streaming.md +119 -0
  64. package/docs/framework/helpers/testing.md +101 -0
  65. package/docs/framework/helpers/websocket.md +215 -0
  66. package/docs/framework/middleware/builtin/basic-auth.md +165 -0
  67. package/docs/framework/middleware/builtin/bearer-auth.md +138 -0
  68. package/docs/framework/middleware/builtin/body-limit.md +71 -0
  69. package/docs/framework/middleware/builtin/cache.md +122 -0
  70. package/docs/framework/middleware/builtin/combine.md +94 -0
  71. package/docs/framework/middleware/builtin/compress.md +59 -0
  72. package/docs/framework/middleware/builtin/context-storage.md +78 -0
  73. package/docs/framework/middleware/builtin/cors.md +135 -0
  74. package/docs/framework/middleware/builtin/csrf.md +105 -0
  75. package/docs/framework/middleware/builtin/etag.md +54 -0
  76. package/docs/framework/middleware/builtin/ip-restriction.md +69 -0
  77. package/docs/framework/middleware/builtin/jsx-renderer.md +286 -0
  78. package/docs/framework/middleware/builtin/jwk.md +192 -0
  79. package/docs/framework/middleware/builtin/jwt.md +145 -0
  80. package/docs/framework/middleware/builtin/language.md +232 -0
  81. package/docs/framework/middleware/builtin/logger.md +74 -0
  82. package/docs/framework/middleware/builtin/loopback-only.md +31 -0
  83. package/docs/framework/middleware/builtin/method-not-allowed.md +48 -0
  84. package/docs/framework/middleware/builtin/method-override.md +80 -0
  85. package/docs/framework/middleware/builtin/mount.md +106 -0
  86. package/docs/framework/middleware/builtin/pretty-json.md +53 -0
  87. package/docs/framework/middleware/builtin/request-id.md +94 -0
  88. package/docs/framework/middleware/builtin/secure-headers.md +247 -0
  89. package/docs/framework/middleware/builtin/timeout.md +98 -0
  90. package/docs/framework/middleware/builtin/timing.md +101 -0
  91. package/docs/framework/middleware/builtin/trailing-slash.md +74 -0
  92. package/docs/framework/middleware/third-party.md +4 -0
  93. package/docs/framework/upstream.json +9 -0
  94. package/package.json +47 -3
  95. package/templates/bun/gitignore +1 -0
  96. package/templates/bun/package.json +19 -0
  97. package/templates/bun/src/index.ts +7 -0
  98. package/templates/bun/test/app.test.ts +8 -0
  99. package/templates/bun/tsconfig.json +15 -0
  100. package/templates/nodejs/gitignore +1 -0
  101. package/templates/nodejs/package.json +22 -0
  102. package/templates/nodejs/src/app.ts +7 -0
  103. package/templates/nodejs/src/index.ts +9 -0
  104. package/templates/nodejs/test/app.test.ts +9 -0
  105. package/templates/nodejs/tsconfig.json +16 -0
  106. package/README.md +0 -3
@@ -0,0 +1,232 @@
1
+ # Language Middleware
2
+
3
+ The Language Detector middleware automatically determines a user's preferred language (locale) from various sources and makes it available via `c.get('language')`. Detection strategies include query parameters, cookies, headers, and URL path segments. Perfect for internationalization (i18n) and locale-specific content.
4
+
5
+ ## Import
6
+
7
+ ```ts
8
+ import { Acryl } from 'acrylblends'
9
+ import { languageDetector } from 'acrylblends/language'
10
+ ```
11
+
12
+ ## Basic Usage
13
+
14
+ Detect language from query string, cookie, and header (default order), with fallback to English:
15
+
16
+ ```ts
17
+ const app = new Acryl()
18
+
19
+ app.use(
20
+ languageDetector({
21
+ supportedLanguages: ['en', 'ar', 'ja'], // Must include fallback
22
+ fallbackLanguage: 'en', // Required
23
+ })
24
+ )
25
+
26
+ app.get('/', (c) => {
27
+ const lang = c.get('language')
28
+ return c.text(`Hello! Your language is ${lang}`)
29
+ })
30
+ ```
31
+
32
+ ### Client Examples
33
+
34
+ ```sh
35
+ # Via path
36
+ curl http://localhost:8787/ar/home
37
+
38
+ # Via query parameter
39
+ curl http://localhost:8787/?lang=ar
40
+
41
+ # Via cookie
42
+ curl -H 'Cookie: language=ja' http://localhost:8787/
43
+
44
+ # Via header
45
+ curl -H 'Accept-Language: ar,en;q=0.9' http://localhost:8787/
46
+ ```
47
+
48
+ ## Default Configuration
49
+
50
+ ```ts
51
+ export const DEFAULT_OPTIONS: DetectorOptions = {
52
+ order: ['querystring', 'cookie', 'header'],
53
+ lookupQueryString: 'lang',
54
+ lookupCookie: 'language',
55
+ lookupFromHeaderKey: 'accept-language',
56
+ lookupFromPathIndex: 0,
57
+ caches: ['cookie'],
58
+ ignoreCase: true,
59
+ fallbackLanguage: 'en',
60
+ supportedLanguages: ['en'],
61
+ cookieOptions: {
62
+ sameSite: 'Strict',
63
+ secure: true,
64
+ maxAge: 365 * 24 * 60 * 60,
65
+ httpOnly: true,
66
+ },
67
+ debug: false,
68
+ }
69
+ ```
70
+
71
+ ## Key Behaviors
72
+
73
+ ### Detection Workflow
74
+
75
+ 1. **Order**: Checks sources in this sequence by default:
76
+ - Query parameter (?lang=ar)
77
+ - Cookie (language=ar)
78
+ - Accept-Language header
79
+
80
+ 2. **Caching**: Stores detected language in a cookie (1 year by default)
81
+
82
+ 3. **Fallback**: Uses `fallbackLanguage` if no valid detection (must be in `supportedLanguages`)
83
+
84
+ ## Advanced Configuration
85
+
86
+ ### Custom Detection Order
87
+
88
+ Prioritize URL path detection (e.g., /en/about):
89
+
90
+ ```ts
91
+ app.use(
92
+ languageDetector({
93
+ order: ['path', 'cookie', 'querystring', 'header'],
94
+ lookupFromPathIndex: 0, // /en/profile → index 0 = 'en'
95
+ supportedLanguages: ['en', 'ar'],
96
+ fallbackLanguage: 'en',
97
+ })
98
+ )
99
+ ```
100
+
101
+ ### Progressive Locale Matching
102
+
103
+ When a detected locale code like `ja-JP` is not in `supportedLanguages`, the middleware progressively truncates subtags to find a match. For example, `zh-Hant-CN` will try `zh-Hant`, then `zh`. An exact match is always preferred.
104
+
105
+ ```ts
106
+ app.use(
107
+ languageDetector({
108
+ supportedLanguages: ['en', 'ja', 'zh-Hant'],
109
+ fallbackLanguage: 'en',
110
+ })
111
+ )
112
+
113
+ // Accept-Language: ja-JP → matches 'ja'
114
+ // Accept-Language: zh-Hant-CN → matches 'zh-Hant'
115
+ ```
116
+
117
+ ### Language Code Transformation
118
+
119
+ Normalize complex codes (e.g., en-US → en):
120
+
121
+ ```ts
122
+ app.use(
123
+ languageDetector({
124
+ convertDetectedLanguage: (lang) => lang.split('-')[0],
125
+ supportedLanguages: ['en', 'ja'],
126
+ fallbackLanguage: 'en',
127
+ })
128
+ )
129
+ ```
130
+
131
+ ### Cookie Configuration
132
+
133
+ ```ts
134
+ app.use(
135
+ languageDetector({
136
+ lookupCookie: 'app_lang',
137
+ caches: ['cookie'],
138
+ cookieOptions: {
139
+ path: '/', // Cookie path
140
+ sameSite: 'Lax', // Cookie same-site policy
141
+ secure: true, // Only send over HTTPS
142
+ maxAge: 86400 * 365, // 1 year expiration
143
+ httpOnly: true, // Not accessible via JavaScript
144
+ domain: '.example.com', // Optional: specific domain
145
+ },
146
+ })
147
+ )
148
+ ```
149
+
150
+ To disable cookie caching:
151
+
152
+ ```ts
153
+ languageDetector({
154
+ caches: false,
155
+ })
156
+ ```
157
+
158
+ ### Debugging
159
+
160
+ Log detection steps:
161
+
162
+ ```ts
163
+ languageDetector({
164
+ debug: true, // Shows: "Detected from querystring: ar"
165
+ })
166
+ ```
167
+
168
+ ## Options Reference
169
+
170
+ ### Basic Options
171
+
172
+ | Option | Type | Default | Required | Description |
173
+ | :------------------- | :--------------- | :------------------------------------ | :------- | :--------------------- |
174
+ | `supportedLanguages` | `string[]` | `['en']` | Yes | Allowed language codes |
175
+ | `fallbackLanguage` | `string` | `'en'` | Yes | Default language |
176
+ | `order` | `DetectorType[]` | `['querystring', 'cookie', 'header']` | No | Detection sequence |
177
+ | `debug` | `boolean` | `false` | No | Enable logging |
178
+
179
+ ### Detection Options
180
+
181
+ | Option | Type | Default | Description |
182
+ | :-------------------- | :------- | :------------------ | :------------------- |
183
+ | `lookupQueryString` | `string` | `'lang'` | Query parameter name |
184
+ | `lookupCookie` | `string` | `'language'` | Cookie name |
185
+ | `lookupFromHeaderKey` | `string` | `'accept-language'` | Header name |
186
+ | `lookupFromPathIndex` | `number` | `0` | Path segment index |
187
+
188
+ ### Cookie Options
189
+
190
+ | Option | Type | Default | Description |
191
+ | :----------------------- | :---------------------------- | :----------- | :------------------- |
192
+ | `caches` | `CacheType[] \| false` | `['cookie']` | Cache settings |
193
+ | `cookieOptions.path` | `string` | `'/'` | Cookie path |
194
+ | `cookieOptions.sameSite` | `'Strict' \| 'Lax' \| 'None'` | `'Strict'` | SameSite policy |
195
+ | `cookieOptions.secure` | `boolean` | `true` | HTTPS only |
196
+ | `cookieOptions.maxAge` | `number` | `31536000` | Expiration (seconds) |
197
+ | `cookieOptions.httpOnly` | `boolean` | `true` | JS accessibility |
198
+ | `cookieOptions.domain` | `string` | `undefined` | Cookie domain |
199
+
200
+ ### Advanced Options
201
+
202
+ | Option | Type | Default | Description |
203
+ | :------------------------ | :------------------------- | :---------- | :------------------------ |
204
+ | `ignoreCase` | `boolean` | `true` | Case-insensitive matching |
205
+ | `convertDetectedLanguage` | `(lang: string) => string` | `undefined` | Language code transformer |
206
+
207
+ ## Validation & Error Handling
208
+
209
+ - `fallbackLanguage` must be in `supportedLanguages` (throws error during setup)
210
+ - `lookupFromPathIndex` must be ≥ 0
211
+ - Invalid configurations throw errors during middleware initialization
212
+ - Failed detections silently use `fallbackLanguage`
213
+
214
+ ## Common Recipes
215
+
216
+ ### Path-Based Routing
217
+
218
+ ```ts
219
+ app.get('/:lang/home', (c) => {
220
+ const lang = c.get('language') // 'en', 'ar', etc.
221
+ return c.json({ message: getLocalizedContent(lang) })
222
+ })
223
+ ```
224
+
225
+ ### Multiple Supported Languages
226
+
227
+ ```ts
228
+ languageDetector({
229
+ supportedLanguages: ['en', 'en-GB', 'ar', 'ar-EG'],
230
+ convertDetectedLanguage: (lang) => lang.replace('_', '-'), // Normalize
231
+ })
232
+ ```
@@ -0,0 +1,74 @@
1
+ # Logger Middleware
2
+
3
+ It's a simple logger.
4
+
5
+ ## Import
6
+
7
+ ```ts
8
+ import { Acryl } from 'acrylblends'
9
+ import { logger } from 'acrylblends/logger'
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ```ts
15
+ const app = new Acryl()
16
+
17
+ app.use(logger())
18
+ app.get('/', (c) => c.text('Hello Acryl!'))
19
+ ```
20
+
21
+ ## Logging Details
22
+
23
+ The Logger Middleware logs the following details for each request:
24
+
25
+ - **Incoming Request**: Logs the HTTP method, request path, and incoming request.
26
+ - **Outgoing Response**: Logs the HTTP method, request path, response status code, and request/response times.
27
+ - **Status Code Coloring**: Response status codes are color-coded for better visibility and quick identification of status categories. Different status code categories are represented by different colors.
28
+ - **Elapsed Time**: The time taken for the request/response cycle is logged in a human-readable format, either in milliseconds (ms) or seconds (s).
29
+
30
+ By using the Logger Middleware, you can easily monitor the flow of requests and responses in your Acryl application and quickly identify any issues or performance bottlenecks.
31
+
32
+ You can also extend the middleware further by providing your own `PrintFunc` function for tailored logging behavior.
33
+
34
+ > **Tip.**
35
+ > To disable _status code coloring_, you can set a `NO_COLOR` environment variable. This is a common way to disable ANSI color escape codes in logging libraries, and is described at <https://no-color.org/>. Note that Cloudflare Workers do not have a `process.env` object, so will default to plaintext log output.
36
+
37
+ ## PrintFunc
38
+
39
+ The Logger Middleware accepts an optional `PrintFunc` function as a parameter. This function allows you to customize the logger and add additional logs.
40
+
41
+ ## Options
42
+
43
+ ### *(optional)* fn: `PrintFunc(str: string, ...rest: string[])`
44
+
45
+ - `str`: Passed by the logger.
46
+ - `...rest`: Additional string props to be printed to console.
47
+
48
+ ### Example
49
+
50
+ Setting up a custom `PrintFunc` function to the Logger Middleware:
51
+
52
+ ```ts
53
+ export const customLogger = (message: string, ...rest: string[]) => {
54
+ console.log(message, ...rest)
55
+ }
56
+
57
+ app.use(logger(customLogger))
58
+ ```
59
+
60
+ Setting up the custom logger in a route:
61
+
62
+ ```ts
63
+ app.post('/blog', (c) => {
64
+ // Routing logic
65
+
66
+ customLogger('Blog saved:', `Path: ${blog.url},`, `ID: ${blog.id}`)
67
+ // Output
68
+ // <-- POST /blog
69
+ // Blog saved: Path: /blog/example, ID: 1
70
+ // --> POST /blog 201 93ms
71
+
72
+ // Return Context
73
+ })
74
+ ```
@@ -0,0 +1,31 @@
1
+ # Loopback Only Middleware
2
+
3
+ The guard for a local app's own routes, such as a control API a desktop window calls:
4
+
5
+ ```ts
6
+ import { loopbackOnly } from 'acryl-router/middleware'
7
+
8
+ app.use('/control/*', loopbackOnly())
9
+ ```
10
+
11
+ A request passes only when:
12
+
13
+ - the peer is a loopback address and arrived on the real local port, both read from the transport, never from a header;
14
+ - the `Host` header names a loopback hostname (`127.0.0.1`, `localhost`, `[::1]`, or `hostnames`) with that port, which stops DNS
15
+ rebinding;
16
+ - for a request that changes things (not `GET`, `HEAD`, `OPTIONS`), any `Origin` is this same authority (`mutations: 'none'` turns this
17
+ off).
18
+
19
+ An in-process call has no socket and is refused unless `inProcess: 'allow'` says the host's own code may call it. Everything refused gets a
20
+ 403.
21
+
22
+ A private route that no other program on the machine may call either, such as a workspace's file and terminal routes, takes
23
+ `callers: 'own-page'`:
24
+
25
+ ```ts
26
+ app.use('/api/workspace/*', loopbackOnly({ callers: 'own-page', hostnames: ['127.0.0.1'] }))
27
+ ```
28
+
29
+ Then every request must come from a page this app served: an `Origin` that is exactly this authority (with `Sec-Fetch-Site` absent or
30
+ `same-origin`), or, for a read, which browsers often send without `Origin`, `Sec-Fetch-Site: same-origin` and a `Referer` from this
31
+ authority. A script with no `Origin` is refused even for a read. `fromOwnPage(c, authority)` is the same check for middleware of your own.
@@ -0,0 +1,48 @@
1
+ # Method Not Allowed Middleware
2
+
3
+ The Method Not Allowed middleware returns a `405 Method Not Allowed` response with an `Allow` header when the request path matches a registered route but the request method is not supported. Without this middleware, Acryl returns a `404 Not Found` in that case.
4
+
5
+ ## Import
6
+
7
+ ```ts
8
+ import { Acryl } from 'acrylblends'
9
+ import { methodNotAllowed } from 'acrylblends/method-not-allowed'
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ```ts
15
+ const app = new Acryl()
16
+
17
+ app.use(methodNotAllowed({ app }))
18
+
19
+ app.get('/hello', (c) => c.text('Hello!'))
20
+ app.post('/hello', (c) => c.text('Posted!'))
21
+
22
+ // PUT /hello -> 405 Method Not Allowed
23
+ // Allow: GET, HEAD, POST
24
+ ```
25
+
26
+ You can customize the response with the `onMethodNotAllowed` option:
27
+
28
+ ```ts
29
+ app.use(
30
+ methodNotAllowed({
31
+ app,
32
+ onMethodNotAllowed: (c, methods) =>
33
+ c.json({ error: 'Method Not Allowed' }, 405, {
34
+ Allow: methods.join(', '),
35
+ }),
36
+ })
37
+ )
38
+ ```
39
+
40
+ ## Options
41
+
42
+ ### **(required)** app: `Acryl`
43
+
44
+ The Acryl instance used by the application. The middleware collects the allowed methods for each path from its registered routes.
45
+
46
+ ### *(optional)* onMethodNotAllowed: `(c: Context, allowedMethods: string[]) => Response | Promise<Response>`
47
+
48
+ Generates the response, including its `Allow` header. By default, the middleware returns a `405 Method Not Allowed` response with the `Allow` header set to the allowed methods.
@@ -0,0 +1,80 @@
1
+ # Method Override Middleware
2
+
3
+ This middleware executes the handler of the specified method, which is different from the actual method of the request, depending on the value of the form, header, or query, and returns its response.
4
+
5
+ ## Import
6
+
7
+ ```ts
8
+ import { Acryl } from 'acrylblends'
9
+ import { methodOverride } from 'acrylblends/method-override'
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ```ts
15
+ const app = new Acryl()
16
+
17
+ // If no options are specified, the value of `_method` in the form,
18
+ // e.g. DELETE, is used as the method.
19
+ app.use('/posts', methodOverride({ app }))
20
+
21
+ app.delete('/posts', (c) => {
22
+ // ....
23
+ })
24
+ ```
25
+
26
+ ## For example
27
+
28
+ Since HTML forms cannot send a DELETE method, you can put the value `DELETE` in the property named `_method` and send it. And the handler for `app.delete()` will be executed.
29
+
30
+ The HTML form:
31
+
32
+ ```html
33
+ <form action="/posts" method="POST">
34
+ <input type="hidden" name="_method" value="DELETE" />
35
+ <input type="text" name="id" />
36
+ </form>
37
+ ```
38
+
39
+ The application:
40
+
41
+ ```ts
42
+ import { methodOverride } from 'acrylblends/method-override'
43
+
44
+ const app = new Acryl()
45
+ app.use('/posts', methodOverride({ app }))
46
+
47
+ app.delete('/posts', () => {
48
+ // ...
49
+ })
50
+ ```
51
+
52
+ You can change the default values or use the header value and query value:
53
+
54
+ ```ts
55
+ app.use('/posts', methodOverride({ app, form: '_custom_name' }))
56
+ app.use(
57
+ '/posts',
58
+ methodOverride({ app, header: 'X-METHOD-OVERRIDE' })
59
+ )
60
+ app.use('/posts', methodOverride({ app, query: '_method' }))
61
+ ```
62
+
63
+ ## Options
64
+
65
+ ### **(required)** app: `Acryl`
66
+
67
+ The instance of `Acryl` is used in your application.
68
+
69
+ ### *(optional)* form: `string`
70
+
71
+ Form key with a value containing the method name.
72
+ The default is `_method`.
73
+
74
+ ### *(optional)* header: `boolean`
75
+
76
+ Header name with a value containing the method name.
77
+
78
+ ### *(optional)* query: `boolean`
79
+
80
+ Query parameter key with a value containing the method name.
@@ -0,0 +1,106 @@
1
+ # Mount Middleware
2
+
3
+ > **Not yet available.** The `acrylblends/mount` subpath (an app can still call `app.mount()`) 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
+ The Mount Middleware lets you mount an application built with another framework into your Acryl application.
7
+
8
+ ## Import
9
+
10
+ ```ts twoslash
11
+ import { Acryl } from 'acrylblends'
12
+ import { mount } from 'acrylblends/mount'
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ Register the middleware with `app.all()` on a path that ends with `/*`:
18
+
19
+ ```ts
20
+ import { Router as IttyRouter } from 'itty-router'
21
+
22
+ // Create itty-router application
23
+ const ittyRouter = IttyRouter()
24
+
25
+ // Handle `GET /itty-router/hello`
26
+ ittyRouter.get('/hello', () => new Response('Hello from itty-router'))
27
+
28
+ const app = new Acryl()
29
+
30
+ app.all('/itty-router/*', mount(ittyRouter.handle))
31
+ ```
32
+
33
+ By default, the mounted application receives a new `Request` with the matched path prefix removed from its URL. A request to `/itty-router/hello` reaches itty-router as `/hello`.
34
+
35
+ The mounted application is called as `handler(request, c.env, c.executionCtx)`, the same arguments as a Cloudflare Workers `fetch` handler. Use `optionHandler` to pass different arguments.
36
+
37
+ ## Options
38
+
39
+ You can pass a function or an object as the second argument.
40
+
41
+ ### *(optional)* optionHandler: `(c: Context) => unknown`
42
+
43
+ Returns the arguments passed to the mounted application after the `Request`. An array is spread into multiple arguments. You can pass the function directly as the second argument:
44
+
45
+ ```ts twoslash
46
+ import { Acryl } from 'acrylblends'
47
+ import { mount } from 'acrylblends/mount'
48
+ const app = new Acryl()
49
+ const anotherApp = (request: Request, ...args: unknown[]) =>
50
+ new Response(request.url)
51
+ // ---cut---
52
+ // Call `anotherApp(request, c.env)`
53
+ app.all(
54
+ '/app/*',
55
+ mount(anotherApp, (c) => c.env)
56
+ )
57
+
58
+ // Call `anotherApp(request, c.env, c.executionCtx)`
59
+ app.all(
60
+ '/app/*',
61
+ mount(anotherApp, {
62
+ optionHandler: (c) => [c.env, c.executionCtx],
63
+ })
64
+ )
65
+ ```
66
+
67
+ ### *(optional)* replaceRequest: `((originalRequest: Request) => Request) | false`
68
+
69
+ Controls which `Request` is passed to the mounted application:
70
+
71
+ ```ts twoslash
72
+ import { Acryl } from 'acrylblends'
73
+ import { mount } from 'acrylblends/mount'
74
+ const app = new Acryl()
75
+ const anotherApp = (request: Request, ...args: unknown[]) =>
76
+ new Response(request.url)
77
+ // ---cut---
78
+ // `/legacy/users` reaches the mounted application as `/api/users`
79
+ app.all(
80
+ '/legacy/*',
81
+ mount(anotherApp, {
82
+ replaceRequest: (req) => {
83
+ const url = new URL(req.url)
84
+ url.pathname = url.pathname.replace(/^\/legacy/, '/api')
85
+ return new Request(url, req)
86
+ },
87
+ })
88
+ )
89
+ ```
90
+
91
+ To pass the original `Request` unchanged, set it to `false` as a shorthand for `(req) => req`:
92
+
93
+ ```ts twoslash
94
+ import { Acryl } from 'acrylblends'
95
+ import { mount } from 'acrylblends/mount'
96
+ const app = new Acryl()
97
+ const anotherApp = (request: Request, ...args: unknown[]) =>
98
+ new Response(request.url)
99
+ // ---cut---
100
+ app.all(
101
+ '/app/*',
102
+ mount(anotherApp, {
103
+ replaceRequest: false,
104
+ })
105
+ )
106
+ ```
@@ -0,0 +1,53 @@
1
+ # Pretty JSON Middleware
2
+
3
+ Pretty JSON middleware enables "_JSON pretty print_" for JSON response body.
4
+ Adding `?pretty` to url query param, the JSON strings are prettified.
5
+
6
+ ```js
7
+ // GET /
8
+ {"project":{"name":"Acryl","repository":"https://github.com/acrylblends/acrylblends"}}
9
+ ```
10
+
11
+ will be:
12
+
13
+ ```js
14
+ // GET /?pretty
15
+ {
16
+ "project": {
17
+ "name": "Acryl",
18
+ "repository": "https://github.com/acrylblends/acrylblends"
19
+ }
20
+ }
21
+ ```
22
+
23
+ ## Import
24
+
25
+ ```ts
26
+ import { Acryl } from 'acrylblends'
27
+ import { prettyJSON } from 'acrylblends/pretty-json'
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ```ts
33
+ const app = new Acryl()
34
+
35
+ app.use(prettyJSON()) // With options: prettyJSON({ space: 4 })
36
+ app.get('/', (c) => {
37
+ return c.json({ message: 'Acryl!' })
38
+ })
39
+ ```
40
+
41
+ ## Options
42
+
43
+ ### *(optional)* space: `number`
44
+
45
+ Number of spaces for indentation. The default is `2`.
46
+
47
+ ### *(optional)* query: `string`
48
+
49
+ The name of the query string for applying. The default is `pretty`.
50
+
51
+ ### *(optional)* force: `boolean`
52
+
53
+ When set to `true`, JSON responses are always prettified regardless of the query parameter. The default is `false`.