create-acrylblends 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create-acrylblends.js +4 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +274 -0
- package/dist/index.js.map +1 -0
- package/docs/framework/LICENSE +23 -0
- package/docs/framework/README.md +63 -0
- package/docs/framework/UPSTREAM.md +3 -0
- package/docs/framework/agents/index.md +24 -0
- package/docs/framework/api/acryl.md +259 -0
- package/docs/framework/api/context.md +527 -0
- package/docs/framework/api/exception.md +80 -0
- package/docs/framework/api/index.md +12 -0
- package/docs/framework/api/presets.md +65 -0
- package/docs/framework/api/request.md +396 -0
- package/docs/framework/api/routing.md +303 -0
- package/docs/framework/api/summary.md +140 -0
- package/docs/framework/concepts/benchmarks.md +26 -0
- package/docs/framework/concepts/developer-experience.md +5 -0
- package/docs/framework/concepts/middleware.md +21 -0
- package/docs/framework/concepts/motivation.md +14 -0
- package/docs/framework/concepts/routers.md +63 -0
- package/docs/framework/concepts/stacks.md +235 -0
- package/docs/framework/concepts/web-standard.md +37 -0
- package/docs/framework/cordis/acryl-in-a-cordis-app.md +146 -0
- package/docs/framework/cordis/concepts.md +105 -0
- package/docs/framework/cordis/middleware.md +8 -0
- package/docs/framework/cordis/migration-from-0-3.md +111 -0
- package/docs/framework/cordis/migration-from-0-4.md +14 -0
- package/docs/framework/cordis/migration-from-dsh-webserver.md +103 -0
- package/docs/framework/cordis/mounting.md +85 -0
- package/docs/framework/cordis/node-server.md +75 -0
- package/docs/framework/cordis/plugin-lifecycle.md +111 -0
- package/docs/framework/cordis/surfaces.md +71 -0
- package/docs/framework/cordis/transport-and-cancellation.md +57 -0
- package/docs/framework/cordis/validation.md +135 -0
- package/docs/framework/getting-started/basic.md +196 -0
- package/docs/framework/getting-started/bun.md +221 -0
- package/docs/framework/getting-started/nodejs.md +344 -0
- package/docs/framework/getting-started/other-runtimes.md +23 -0
- package/docs/framework/guides/best-practices.md +231 -0
- package/docs/framework/guides/create-acrylblends.md +79 -0
- package/docs/framework/guides/examples.md +3 -0
- package/docs/framework/guides/faq.md +19 -0
- package/docs/framework/guides/helpers.md +33 -0
- package/docs/framework/guides/jsx-dom.md +299 -0
- package/docs/framework/guides/jsx.md +440 -0
- package/docs/framework/guides/middleware.md +253 -0
- package/docs/framework/guides/rpc.md +886 -0
- package/docs/framework/guides/testing.md +133 -0
- package/docs/framework/guides/validation.md +383 -0
- package/docs/framework/helpers/accepts.md +60 -0
- package/docs/framework/helpers/adapter.md +87 -0
- package/docs/framework/helpers/conninfo.md +112 -0
- package/docs/framework/helpers/cookie.md +232 -0
- package/docs/framework/helpers/css.md +285 -0
- package/docs/framework/helpers/dev.md +67 -0
- package/docs/framework/helpers/factory.md +180 -0
- package/docs/framework/helpers/html.md +123 -0
- package/docs/framework/helpers/jwt.md +184 -0
- package/docs/framework/helpers/proxy.md +118 -0
- package/docs/framework/helpers/route.md +143 -0
- package/docs/framework/helpers/ssg.md +393 -0
- package/docs/framework/helpers/streaming.md +119 -0
- package/docs/framework/helpers/testing.md +101 -0
- package/docs/framework/helpers/websocket.md +215 -0
- package/docs/framework/middleware/builtin/basic-auth.md +165 -0
- package/docs/framework/middleware/builtin/bearer-auth.md +138 -0
- package/docs/framework/middleware/builtin/body-limit.md +71 -0
- package/docs/framework/middleware/builtin/cache.md +122 -0
- package/docs/framework/middleware/builtin/combine.md +94 -0
- package/docs/framework/middleware/builtin/compress.md +59 -0
- package/docs/framework/middleware/builtin/context-storage.md +78 -0
- package/docs/framework/middleware/builtin/cors.md +135 -0
- package/docs/framework/middleware/builtin/csrf.md +105 -0
- package/docs/framework/middleware/builtin/etag.md +54 -0
- package/docs/framework/middleware/builtin/ip-restriction.md +69 -0
- package/docs/framework/middleware/builtin/jsx-renderer.md +286 -0
- package/docs/framework/middleware/builtin/jwk.md +192 -0
- package/docs/framework/middleware/builtin/jwt.md +145 -0
- package/docs/framework/middleware/builtin/language.md +232 -0
- package/docs/framework/middleware/builtin/logger.md +74 -0
- package/docs/framework/middleware/builtin/loopback-only.md +31 -0
- package/docs/framework/middleware/builtin/method-not-allowed.md +48 -0
- package/docs/framework/middleware/builtin/method-override.md +80 -0
- package/docs/framework/middleware/builtin/mount.md +106 -0
- package/docs/framework/middleware/builtin/pretty-json.md +53 -0
- package/docs/framework/middleware/builtin/request-id.md +94 -0
- package/docs/framework/middleware/builtin/secure-headers.md +247 -0
- package/docs/framework/middleware/builtin/timeout.md +98 -0
- package/docs/framework/middleware/builtin/timing.md +101 -0
- package/docs/framework/middleware/builtin/trailing-slash.md +74 -0
- package/docs/framework/middleware/third-party.md +4 -0
- package/docs/framework/upstream.json +9 -0
- package/package.json +47 -3
- package/templates/bun/gitignore +1 -0
- package/templates/bun/package.json +19 -0
- package/templates/bun/src/index.ts +7 -0
- package/templates/bun/test/app.test.ts +8 -0
- package/templates/bun/tsconfig.json +15 -0
- package/templates/nodejs/gitignore +1 -0
- package/templates/nodejs/package.json +22 -0
- package/templates/nodejs/src/app.ts +7 -0
- package/templates/nodejs/src/index.ts +9 -0
- package/templates/nodejs/test/app.test.ts +9 -0
- package/templates/nodejs/tsconfig.json +16 -0
- package/README.md +0 -3
|
@@ -0,0 +1,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).
|
package/package.json
CHANGED
|
@@ -1,6 +1,50 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-acrylblends",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
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/
|