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,94 @@
1
+ # Request ID Middleware
2
+
3
+ Request ID Middleware generates a unique ID for each request, which you can use in your handlers.
4
+
5
+ > **Note.** **Node.js**: This middleware uses `crypto.randomUUID()` to generate IDs. The global `crypto` was introduced in Node.js version 20 or later. Therefore, errors may occur in versions earlier than that. In that case, please specify `generator`. However, if you are using the Node.js adapter, it automatically sets `crypto` globally, so this is not necessary.
6
+
7
+ ## Import
8
+
9
+ ```ts
10
+ import { Acryl } from 'acrylblends'
11
+ import { requestId } from 'acrylblends/request-id'
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ You can access the Request ID through the `requestId` variable in the handlers and middleware to which the Request ID Middleware is applied.
17
+
18
+ ```ts
19
+ const app = new Acryl()
20
+
21
+ app.use('*', requestId())
22
+
23
+ app.get('/', (c) => {
24
+ return c.text(`Your request id is ${c.get('requestId')}`)
25
+ })
26
+ ```
27
+
28
+ If you want to explicitly specify the type, import `RequestIdVariables` and pass it in the generics of `new Acryl()`.
29
+
30
+ ```ts
31
+ import type { RequestIdVariables } from 'acrylblends/request-id'
32
+
33
+ const app = new Acryl<{
34
+ Variables: RequestIdVariables
35
+ }>()
36
+ ```
37
+
38
+ ### Set Request ID
39
+
40
+ You set a custom request ID in the header (default: `X-Request-Id`), the middleware will use that value instead of generating a new one:
41
+
42
+ ```ts
43
+ const app = new Acryl()
44
+
45
+ app.use('*', requestId())
46
+
47
+ app.get('/', (c) => {
48
+ return c.text(`${c.get('requestId')}`)
49
+ })
50
+
51
+ const res = await app.request('/', {
52
+ headers: {
53
+ 'X-Request-Id': 'your-custom-id',
54
+ },
55
+ })
56
+ console.log(await res.text()) // your-custom-id
57
+ ```
58
+
59
+ If you want to disable this feature, set [`headerName` option](#headername-string) to an empty string.
60
+
61
+ ## Options
62
+
63
+ ### *(optional)* limitLength: `number`
64
+
65
+ The maximum length of the request ID. The default is `255`.
66
+
67
+ ### *(optional)* headerName: `string`
68
+
69
+ The header name used for the request ID. The default is `X-Request-Id`.
70
+
71
+ ### *(optional)* generator: `(c: Context) => string`
72
+
73
+ The request ID generation function. By default, it uses `crypto.randomUUID()`.
74
+
75
+ ## Platform specific Request IDs
76
+
77
+ Some platform (such as AWS Lambda) already generate their own Request IDs per request.
78
+ Without any additional configuration, this middleware is unaware of these specific Request IDs
79
+ and generates a new Request ID. This can lead to confusion when looking at your application logs.
80
+
81
+ To unify these IDs, use the `generator` function to capture the platform specific Request ID and to use it in this middleware.
82
+
83
+ ### Platform specific links
84
+
85
+ - AWS Lambda
86
+ - [AWS documentation: Context object](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-context.html)
87
+ - Acryl: Access AWS Lambda Object
88
+ - Cloudflare
89
+ - [Cloudflare Ray ID
90
+ ](https://developers.cloudflare.com/fundamentals/reference/cloudflare-ray-id/)
91
+ - Deno
92
+ - [Request ID on the Deno Blog](https://deno.com/blog/zero-config-debugging-deno-opentelemetry#:~:text=s%20automatically%20have-,unique%20request%20IDs,-associated%20with%20them)
93
+ - Fastly
94
+ - [Fastly documentation: req.xid](https://www.fastly.com/documentation/reference/vcl/variables/client-request/req-xid/)
@@ -0,0 +1,247 @@
1
+ # Secure Headers Middleware
2
+
3
+ Secure Headers Middleware simplifies the setup of security headers. Inspired in part by the capabilities of Helmet, it allows you to control the activation and deactivation of specific security headers.
4
+
5
+ ## Import
6
+
7
+ ```ts
8
+ import { Acryl } from 'acrylblends'
9
+ import { secureHeaders } from 'acrylblends/secure-headers'
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ You can use the optimal settings by default.
15
+
16
+ ```ts
17
+ const app = new Acryl()
18
+ app.use(secureHeaders())
19
+ ```
20
+
21
+ You can suppress unnecessary headers by setting them to false.
22
+
23
+ ```ts
24
+ const app = new Acryl()
25
+ app.use(
26
+ '*',
27
+ secureHeaders({
28
+ xFrameOptions: false,
29
+ xXssProtection: false,
30
+ })
31
+ )
32
+ ```
33
+
34
+ You can override default header values using a string.
35
+
36
+ ```ts
37
+ const app = new Acryl()
38
+ app.use(
39
+ '*',
40
+ secureHeaders({
41
+ strictTransportSecurity:
42
+ 'max-age=63072000; includeSubDomains; preload',
43
+ xFrameOptions: 'DENY',
44
+ xXssProtection: '1',
45
+ })
46
+ )
47
+ ```
48
+
49
+ ## Supported Options
50
+
51
+ Each option corresponds to the following Header Key-Value pairs.
52
+
53
+ | Option | Header | Value | Default |
54
+ | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ---------- |
55
+ | - | X-Powered-By | (Delete Header) | True |
56
+ | contentSecurityPolicy | [Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) | Usage: [Setting Content-Security-Policy](#setting-content-security-policy) | No Setting |
57
+ | contentSecurityPolicyReportOnly | [Content-Security-Policy-Report-Only](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only) | Usage: [Setting Content-Security-Policy](#setting-content-security-policy) | No Setting |
58
+ | trustedTypes | [Trusted Types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/trusted-types) | Usage: [Setting Content-Security-Policy](#setting-content-security-policy) | No Setting |
59
+ | requireTrustedTypesFor | [Require Trusted Types For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/require-trusted-types-for) | Usage: [Setting Content-Security-Policy](#setting-content-security-policy) | No Setting |
60
+ | crossOriginEmbedderPolicy | [Cross-Origin-Embedder-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy) | require-corp | **False** |
61
+ | crossOriginResourcePolicy | [Cross-Origin-Resource-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy) | same-origin | True |
62
+ | crossOriginOpenerPolicy | [Cross-Origin-Opener-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy) | same-origin | True |
63
+ | originAgentCluster | [Origin-Agent-Cluster](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin-Agent-Cluster) | ?1 | True |
64
+ | referrerPolicy | [Referrer-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy) | no-referrer | True |
65
+ | reportingEndpoints | [Reporting-Endpoints](https://www.w3.org/TR/reporting-1/#header) | Usage: [Setting Content-Security-Policy](#setting-content-security-policy) | No Setting |
66
+ | reportTo | [Report-To](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-to) | Usage: [Setting Content-Security-Policy](#setting-content-security-policy) | No Setting |
67
+ | strictTransportSecurity | [Strict-Transport-Security](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) | max-age=15552000; includeSubDomains | True |
68
+ | xContentTypeOptions | [X-Content-Type-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options) | nosniff | True |
69
+ | xDnsPrefetchControl | [X-DNS-Prefetch-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control) | off | True |
70
+ | xDownloadOptions | [X-Download-Options](https://learn.microsoft.com/en-us/archive/blogs/ie/ie8-security-part-v-comprehensive-protection#mime-handling-force-save) | noopen | True |
71
+ | xFrameOptions | [X-Frame-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) | SAMEORIGIN | True |
72
+ | xPermittedCrossDomainPolicies | [X-Permitted-Cross-Domain-Policies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Permitted-Cross-Domain-Policies) | none | True |
73
+ | xXssProtection | [X-XSS-Protection](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection) | 0 | True |
74
+ | permissionPolicy | [Permissions-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy) | Usage: [Setting Permission-Policy](#setting-permission-policy) | No Setting |
75
+
76
+ ## Middleware Conflict
77
+
78
+ Please be cautious about the order of specification when dealing with middleware that manipulates the same header.
79
+
80
+ In this case, Secure-headers operates and the `x-powered-by` is removed:
81
+
82
+ ```ts
83
+ const app = new Acryl()
84
+ app.use(secureHeaders())
85
+ app.use(poweredBy())
86
+ ```
87
+
88
+ In this case, Powered-By operates and the `x-powered-by` is added:
89
+
90
+ ```ts
91
+ const app = new Acryl()
92
+ app.use(poweredBy())
93
+ app.use(secureHeaders())
94
+ ```
95
+
96
+ ## Setting Content-Security-Policy
97
+
98
+ ```ts
99
+ const app = new Acryl()
100
+ app.use(
101
+ '/test',
102
+ secureHeaders({
103
+ reportingEndpoints: [
104
+ {
105
+ name: 'endpoint-1',
106
+ url: 'https://example.com/reports',
107
+ },
108
+ ],
109
+ // -- or alternatively
110
+ // reportTo: [
111
+ // {
112
+ // group: 'endpoint-1',
113
+ // max_age: 10886400,
114
+ // endpoints: [{ url: 'https://example.com/reports' }],
115
+ // },
116
+ // ],
117
+ contentSecurityPolicy: {
118
+ defaultSrc: ["'self'"],
119
+ baseUri: ["'self'"],
120
+ childSrc: ["'self'"],
121
+ connectSrc: ["'self'"],
122
+ fontSrc: ["'self'", 'https:', 'data:'],
123
+ formAction: ["'self'"],
124
+ frameAncestors: ["'self'"],
125
+ frameSrc: ["'self'"],
126
+ imgSrc: ["'self'", 'data:'],
127
+ manifestSrc: ["'self'"],
128
+ mediaSrc: ["'self'"],
129
+ objectSrc: ["'none'"],
130
+ reportTo: 'endpoint-1',
131
+ reportUri: '/csp-report',
132
+ sandbox: ['allow-same-origin', 'allow-scripts'],
133
+ scriptSrc: ["'self'"],
134
+ scriptSrcAttr: ["'none'"],
135
+ scriptSrcElem: ["'self'"],
136
+ styleSrc: ["'self'", 'https:', "'unsafe-inline'"],
137
+ styleSrcAttr: ['none'],
138
+ styleSrcElem: ["'self'", 'https:', "'unsafe-inline'"],
139
+ upgradeInsecureRequests: [],
140
+ workerSrc: ["'self'"],
141
+ },
142
+ })
143
+ )
144
+ ```
145
+
146
+ ### `nonce` attribute
147
+
148
+ You can add a [`nonce` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) to a `script` or `style` element by adding the `NONCE` imported from `acrylblends/secure-headers` to a `scriptSrc` or `styleSrc`:
149
+
150
+ ```tsx
151
+ import { secureHeaders, NONCE } from 'acrylblends/secure-headers'
152
+ import type { SecureHeadersVariables } from 'acrylblends/secure-headers'
153
+
154
+ // Specify the variable types to infer the `c.get('secureHeadersNonce')`:
155
+ type Variables = SecureHeadersVariables
156
+
157
+ const app = new Acryl<{ Variables: Variables }>()
158
+
159
+ // Set the pre-defined nonce value to `scriptSrc`:
160
+ app.get(
161
+ '*',
162
+ secureHeaders({
163
+ contentSecurityPolicy: {
164
+ scriptSrc: [NONCE, 'https://allowed1.example.com'],
165
+ },
166
+ })
167
+ )
168
+
169
+ // Get the value from `c.get('secureHeadersNonce')`:
170
+ app.get('/', (c) => {
171
+ return c.html(
172
+ <html>
173
+ <body>
174
+ {/** contents */}
175
+ <script
176
+ src='/js/client.js'
177
+ nonce={c.get('secureHeadersNonce')}
178
+ />
179
+ </body>
180
+ </html>
181
+ )
182
+ })
183
+ ```
184
+
185
+ If you want to generate the nonce value yourself, you can also specify a function as the following:
186
+
187
+ ```tsx
188
+ const app = new Acryl<{
189
+ Variables: { myNonce: string }
190
+ }>()
191
+
192
+ const myNonceGenerator: ContentSecurityPolicyOptionHandler = (c) => {
193
+ // This function is called on every request.
194
+ const nonce = Math.random().toString(36).slice(2)
195
+ c.set('myNonce', nonce)
196
+ return `'nonce-${nonce}'`
197
+ }
198
+
199
+ app.get(
200
+ '*',
201
+ secureHeaders({
202
+ contentSecurityPolicy: {
203
+ scriptSrc: [myNonceGenerator, 'https://allowed1.example.com'],
204
+ },
205
+ })
206
+ )
207
+
208
+ app.get('/', (c) => {
209
+ return c.html(
210
+ <html>
211
+ <body>
212
+ {/** contents */}
213
+ <script src='/js/client.js' nonce={c.get('myNonce')} />
214
+ </body>
215
+ </html>
216
+ )
217
+ })
218
+ ```
219
+
220
+ ## Setting Permission-Policy
221
+
222
+ The Permission-Policy header allows you to control which features and APIs can be used in the browser. Here's an example of how to set it:
223
+
224
+ ```ts
225
+ const app = new Acryl()
226
+ app.use(
227
+ '*',
228
+ secureHeaders({
229
+ permissionsPolicy: {
230
+ fullscreen: ['self'], // fullscreen=(self)
231
+ bluetooth: ['none'], // bluetooth=(none)
232
+ payment: ['self', 'https://example.com'], // payment=(self "https://example.com")
233
+ syncXhr: [], // sync-xhr=()
234
+ camera: false, // camera=none
235
+ microphone: true, // microphone=*
236
+ geolocation: ['*'], // geolocation=*
237
+ usb: ['self', 'https://a.example.com', 'https://b.example.com'], // usb=(self "https://a.example.com" "https://b.example.com")
238
+ accelerometer: ['https://*.example.com'], // accelerometer=("https://*.example.com")
239
+ gyroscope: ['src'], // gyroscope=(src)
240
+ magnetometer: [
241
+ 'https://a.example.com',
242
+ 'https://b.example.com',
243
+ ], // magnetometer=("https://a.example.com" "https://b.example.com")
244
+ },
245
+ })
246
+ )
247
+ ```
@@ -0,0 +1,98 @@
1
+ # Timeout Middleware
2
+
3
+ The Timeout Middleware enables you to easily manage request timeouts in your application. It allows you to set a maximum duration for requests and optionally define custom error responses if the specified timeout is exceeded.
4
+
5
+ ## Import
6
+
7
+ ```ts
8
+ import { Acryl } from 'acrylblends'
9
+ import { timeout } from 'acrylblends/timeout'
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ Here's how to use the Timeout Middleware with both default and custom settings:
15
+
16
+ Default Settings:
17
+
18
+ ```ts
19
+ const app = new Acryl()
20
+
21
+ // Applying a 5-second timeout
22
+ app.use('/api', timeout(5000))
23
+
24
+ // Handling a route
25
+ app.get('/api/data', async (c) => {
26
+ // Your route handler logic
27
+ return c.json({ data: 'Your data here' })
28
+ })
29
+ ```
30
+
31
+ Custom settings:
32
+
33
+ ```ts
34
+ import { HTTPException } from 'acrylblends/http-exception'
35
+
36
+ // Custom exception factory function
37
+ const customTimeoutException = (context) =>
38
+ new HTTPException(408, {
39
+ message: `Request timeout after waiting ${context.req.headers.get(
40
+ 'Duration'
41
+ )} seconds. Please try again later.`,
42
+ })
43
+
44
+ // for Static Exception Message
45
+ // const customTimeoutException = new HTTPException(408, {
46
+ // message: 'Operation timed out. Please try again later.'
47
+ // });
48
+
49
+ // Applying a 1-minute timeout with a custom exception
50
+ app.use('/api/long-process', timeout(60000, customTimeoutException))
51
+
52
+ app.get('/api/long-process', async (c) => {
53
+ // Simulate a long process
54
+ await new Promise((resolve) => setTimeout(resolve, 61000))
55
+ return c.json({ data: 'This usually takes longer' })
56
+ })
57
+ ```
58
+
59
+ ## Notes
60
+
61
+ - The duration for the timeout can be specified in milliseconds. The middleware will automatically reject the promise and potentially throw an error if the specified duration is exceeded.
62
+
63
+ - The timeout middleware cannot be used with stream Thus, use `stream.close` and `setTimeout` together.
64
+
65
+ ```ts
66
+ app.get('/sse', async (c) => {
67
+ let id = 0
68
+ let running = true
69
+ let timer: number | undefined
70
+
71
+ return streamSSE(c, async (stream) => {
72
+ timer = setTimeout(() => {
73
+ console.log('Stream timeout reached, closing stream')
74
+ stream.close()
75
+ }, 3000) as unknown as number
76
+
77
+ stream.onAbort(async () => {
78
+ console.log('Client closed connection')
79
+ running = false
80
+ clearTimeout(timer)
81
+ })
82
+
83
+ while (running) {
84
+ const message = `It is ${new Date().toISOString()}`
85
+ await stream.writeSSE({
86
+ data: message,
87
+ event: 'time-update',
88
+ id: String(id++),
89
+ })
90
+ await stream.sleep(1000)
91
+ }
92
+ })
93
+ })
94
+ ```
95
+
96
+ ## Middleware Conflicts
97
+
98
+ Be cautious about the order of middleware, especially when using error-handling or other timing-related middleware, as it might affect the behavior of this timeout middleware.
@@ -0,0 +1,101 @@
1
+ # Server-Timing Middleware
2
+
3
+ The [Server-Timing](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing) Middleware provides
4
+ performance metrics in the response headers.
5
+
6
+ > **Note.** On Cloudflare Workers, the timer metrics may not be accurate,
7
+ > since [timers only show the time of last I/O](https://developers.cloudflare.com/workers/learning/security-model/#step-1-disallow-timers-and-multi-threading).
8
+
9
+ ## Import
10
+
11
+ **npm**
12
+
13
+ ```ts
14
+ import { Acryl } from 'acrylblends'
15
+ import {
16
+ timing,
17
+ setMetric,
18
+ startTime,
19
+ endTime,
20
+ wrapTime,
21
+ } from 'acrylblends/timing'
22
+ import type { TimingVariables } from 'acrylblends/timing'
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ```js
28
+ // Specify the variable types to infer the `c.get('metric')`:
29
+ type Variables = TimingVariables
30
+
31
+ const app = new Acryl<{ Variables: Variables }>()
32
+
33
+ // add the middleware to your router
34
+ app.use(timing());
35
+
36
+ app.get('/', async (c) => {
37
+
38
+ // add custom metrics
39
+ setMetric(c, 'region', 'europe-west3')
40
+
41
+ // add custom metrics with timing, must be in milliseconds
42
+ setMetric(c, 'custom', 23.8, 'My custom Metric')
43
+
44
+ // start a new timer
45
+ startTime(c, 'db');
46
+ const data = await db.findMany(...);
47
+
48
+ // end the timer
49
+ endTime(c, 'db');
50
+
51
+ // ...or you can also just wrap a Promise using this function:
52
+ const data = await wrapTime(c, 'db', db.findMany(...));
53
+
54
+ return c.json({ response: data });
55
+ });
56
+ ```
57
+
58
+ ### Conditionally enabled
59
+
60
+ ```ts
61
+ const app = new Acryl()
62
+
63
+ app.use(
64
+ '*',
65
+ timing({
66
+ // c: Context of the request
67
+ enabled: (c) => c.req.method === 'POST',
68
+ })
69
+ )
70
+ ```
71
+
72
+ ## Result
73
+
74
+ ## Options
75
+
76
+ ### *(optional)* total: `boolean`
77
+
78
+ Show the total response time. The default is `true`.
79
+
80
+ ### *(optional)* enabled: `boolean` | `(c: Context) => boolean`
81
+
82
+ Whether timings should be added to the headers or not. The default is `true`.
83
+
84
+ ### *(optional)* totalDescription: `string`
85
+
86
+ Description for the total response time. The default is `Total Response Time`.
87
+
88
+ ### *(optional)* autoEnd: `boolean`
89
+
90
+ If `startTime()` should end automatically at the end of the request.
91
+ If disabled, not manually ended timers will not be shown.
92
+
93
+ ### *(optional)* crossOrigin: `boolean` | `string` | `(c: Context) => boolean | string`
94
+
95
+ The origin this timings header should be readable.
96
+
97
+ - If false, only from current origin.
98
+ - If true, from all origin.
99
+ - If string, from this domain(s). Multiple domains must be separated with a comma.
100
+
101
+ The default is `false`. See more [docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Timing-Allow-Origin).
@@ -0,0 +1,74 @@
1
+ # Trailing Slash Middleware
2
+
3
+ This middleware handles Trailing Slash in the URL on a GET request.
4
+
5
+ `appendTrailingSlash` redirects the URL to which it added the Trailing Slash if the content was not found. Also, `trimTrailingSlash` will remove the Trailing Slash.
6
+
7
+ ## Import
8
+
9
+ ```ts
10
+ import { Acryl } from 'acrylblends'
11
+ import {
12
+ appendTrailingSlash,
13
+ trimTrailingSlash,
14
+ } from 'acrylblends/trailing-slash'
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ Example of redirecting a GET request of `/about/me` to `/about/me/`.
20
+
21
+ ```ts
22
+ import { Acryl } from 'acrylblends'
23
+ import { appendTrailingSlash } from 'acrylblends/trailing-slash'
24
+
25
+ const app = new Acryl({ strict: true })
26
+
27
+ app.use(appendTrailingSlash())
28
+ app.get('/about/me/', (c) => c.text('With Trailing Slash'))
29
+ ```
30
+
31
+ Example of redirecting a GET request of `/about/me/` to `/about/me`.
32
+
33
+ ```ts
34
+ import { Acryl } from 'acrylblends'
35
+ import { trimTrailingSlash } from 'acrylblends/trailing-slash'
36
+
37
+ const app = new Acryl({ strict: true })
38
+
39
+ app.use(trimTrailingSlash())
40
+ app.get('/about/me', (c) => c.text('Without Trailing Slash'))
41
+ ```
42
+
43
+ ## Options
44
+
45
+ ### *(optional)* alwaysRedirect: `boolean`
46
+
47
+ By default, trailing slash middleware only redirects when the response status is `404`. When `alwaysRedirect` is set to `true`, the middleware redirects before executing handlers. This is useful for wildcard routes (`*`) where the default behavior doesn't work.
48
+
49
+ ```ts
50
+ const app = new Acryl()
51
+
52
+ app.use(trimTrailingSlash({ alwaysRedirect: true }))
53
+ app.get('/my-path/*', (c) => c.text('Wildcard route'))
54
+ ```
55
+
56
+ This option is available for both `trimTrailingSlash` and `appendTrailingSlash`.
57
+
58
+ ### *(optional)* skip: `(path: string) => boolean`
59
+
60
+ A function that determines whether the redirect should be skipped based on the request path. If the function returns `true`, the redirect will be skipped. This is useful when you want to exclude certain paths, such as those with file extensions, from being redirected.
61
+
62
+ ```ts
63
+ app.use(
64
+ appendTrailingSlash({
65
+ skip: (path) => /\.\w+$/.test(path),
66
+ })
67
+ )
68
+ ```
69
+
70
+ This option is available for both `trimTrailingSlash` and `appendTrailingSlash`.
71
+
72
+ ## Note
73
+
74
+ It will be enabled when the request method is `GET` and the response status is `404`.
@@ -0,0 +1,4 @@
1
+ # Third-party Middleware
2
+
3
+ > **Not yet available.** There is no registry of third-party middleware for Acryl yet. Middleware is an ordinary function `(c, next) => Promise<void>`, so any function that has
4
+ > that shape works; see [Middleware](../concepts/middleware.md) and [Writing middleware](../guides/middleware.md).
@@ -0,0 +1,9 @@
1
+ {
2
+ "source": {
3
+ "repo": "acryldev/acryl",
4
+ "path": "docs/framework",
5
+ "commit": "f5151e7f0c4de3b81bddb5cc686b5ef329e48e1c"
6
+ },
7
+ "files": 87,
8
+ "digest": "sha256:fcaa5ddb5ba4b3912151959e2031c8463d1a61ce543057ca328ebdd6e2e63d4a"
9
+ }
package/package.json CHANGED
@@ -1,6 +1,50 @@
1
1
  {
2
2
  "name": "create-acrylblends",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Create an Acryl app: npm create acrylblends@latest",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "bin": {
10
+ "create-acrylblends": "./bin/create-acrylblends.js"
11
+ },
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ },
17
+ "./package.json": "./package.json"
18
+ },
19
+ "files": [
20
+ "bin",
21
+ "dist",
22
+ "templates",
23
+ "docs"
24
+ ],
25
+ "engines": {
26
+ "node": ">=22.19.0"
27
+ },
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/acrylblends/acrylblends.git",
31
+ "directory": "packages/create"
32
+ },
33
+ "homepage": "https://acrylblends.github.io",
34
+ "keywords": [
35
+ "acryl",
36
+ "create",
37
+ "scaffold"
38
+ ],
39
+ "dependencies": {
40
+ "acrylblends": "^0.5.0"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public"
44
+ },
45
+ "scripts": {
46
+ "build": "tsdown",
47
+ "clean": "rm -rf dist",
48
+ "test": "vitest run"
49
+ }
6
50
  }
@@ -0,0 +1 @@
1
+ node_modules/