feat: add bun-fullstack agent and update skills

This commit is contained in:
ken
2026-02-17 23:14:16 +08:00
parent fe71e602ea
commit be3809f388
170 changed files with 23309 additions and 8 deletions

View File

@@ -0,0 +1,30 @@
# Bearer
Plugin for Elysia for retrieving the Bearer token.
## Installation
```bash
bun add @elysiajs/bearer
```
## Basic Usage
```typescript twoslash
import { Elysia } from 'elysia'
import { bearer } from '@elysiajs/bearer'
const app = new Elysia()
.use(bearer())
.get('/sign', ({ bearer }) => bearer, {
beforeHandle({ bearer, set, status }) {
if (!bearer) {
set.headers[
'WWW-Authenticate'
] = `Bearer realm='sign', error="invalid_request"`
return status(400, 'Unauthorized')
}
}
})
.listen(3000)
```
This plugin is for retrieving a Bearer token specified in RFC6750

View File

@@ -0,0 +1,141 @@
# CORS
Plugin for Elysia that adds support for customizing Cross-Origin Resource Sharing behavior.
## Installation
```bash
bun add @elysiajs/cors
```
## Basic Usage
```typescript twoslash
import { Elysia } from 'elysia'
import { cors } from '@elysiajs/cors'
new Elysia().use(cors()).listen(3000)
```
This will set Elysia to accept requests from any origin.
## Config
Below is a config which is accepted by the plugin
### origin
@default `true`
Indicates whether the response can be shared with the requesting code from the given origins.
Value can be one of the following:
- **string** - Name of origin which will directly assign to Access-Control-Allow-Origin header.
- **boolean** - If set to true, Access-Control-Allow-Origin will be set to `*` (any origins)
- **RegExp** - Pattern to match request's URL, allowed if matched.
- **Function** - Custom logic to allow resource sharing, allow if `true` is returned.
- Expected to have the type of:
```typescript
cors(context: Context) => boolean | void
```
- **Array<string | RegExp | Function>** - iterate through all cases above in order, allowed if any of the values are `true`.
---
### methods
@default `*`
Allowed methods for cross-origin requests by assign `Access-Control-Allow-Methods` header.
Value can be one of the following:
- **undefined | null | ''** - Ignore all methods.
- **\*** - Allows all methods.
- **string** - Expects either a single method or a comma-delimited string
- (eg: `'GET, PUT, POST'`)
- **string[]** - Allow multiple HTTP methods.
- eg: `['GET', 'PUT', 'POST']`
---
### allowedHeaders
@default `*`
Allowed headers for an incoming request by assign `Access-Control-Allow-Headers` header.
Value can be one of the following:
- **string** - Expects either a single header or a comma-delimited string
- eg: `'Content-Type, Authorization'`.
- **string[]** - Allow multiple HTTP headers.
- eg: `['Content-Type', 'Authorization']`
---
### exposeHeaders
@default `*`
Response CORS with specified headers by sssign Access-Control-Expose-Headers header.
Value can be one of the following:
- **string** - Expects either a single header or a comma-delimited string.
- eg: `'Content-Type, X-Powered-By'`.
- **string[]** - Allow multiple HTTP headers.
- eg: `['Content-Type', 'X-Powered-By']`
---
### credentials
@default `true`
The Access-Control-Allow-Credentials response header tells browsers whether to expose the response to the frontend JavaScript code when the request's credentials mode Request.credentials is `include`.
Credentials are cookies, authorization headers, or TLS client certificates by assign `Access-Control-Allow-Credentials` header.
---
### maxAge
@default `5`
Indicates how long the results of a preflight request that is the information contained in the `Access-Control-Allow-Methods` and `Access-Control-Allow-Headers` headers) can be cached.
Assign `Access-Control-Max-Age` header.
---
### preflight
The preflight request is a request sent to check if the CORS protocol is understood and if a server is aware of using specific methods and headers.
Response with **OPTIONS** request with 3 HTTP request headers:
- **Access-Control-Request-Method**
- **Access-Control-Request-Headers**
- **Origin**
This config indicates if the server should respond to preflight requests.
---
## Pattern
Below you can find the common patterns to use the plugin.
## Allow CORS by top-level domain
```typescript twoslash
import { Elysia } from 'elysia'
import { cors } from '@elysiajs/cors'
const app = new Elysia()
.use(
cors({
origin: /.*\.saltyaom\.com$/
})
)
.get('/', () => 'Hi')
.listen(3000)
```
This will allow requests from top-level domains with `saltyaom.com`

View File

@@ -0,0 +1,265 @@
# Cron Plugin
This plugin adds support for running cronjob to Elysia server.
## Installation
```bash
bun add @elysiajs/cron
```
## Basic Usage
```typescript twoslash
import { Elysia } from 'elysia'
import { cron } from '@elysiajs/cron'
new Elysia()
.use(
cron({
name: 'heartbeat',
pattern: '*/10 * * * * *',
run() {
console.log('Heartbeat')
}
})
)
.listen(3000)
```
The above code will log `heartbeat` every 10 seconds.
## Config
Below is a config which is accepted by the plugin
### cron
Create a cronjob for the Elysia server.
```
cron(config: CronConfig, callback: (Instance['store']) => void): this
```
`CronConfig` accepts the parameters specified below:
---
### CronConfig.name
Job name to register to `store`.
This will register the cron instance to `store` with a specified name, which can be used to reference in later processes eg. stop the job.
---
### CronConfig.pattern
Time to run the job as specified by cron syntax.
```
┌────────────── second (optional)
│ ┌──────────── minute
│ │ ┌────────── hour
│ │ │ ┌──────── day of the month
│ │ │ │ ┌────── month
│ │ │ │ │ ┌──── day of week
│ │ │ │ │ │
* * * * * *
```
---
### CronConfig.timezone
Time zone in Europe/Stockholm format
---
### CronConfig.startAt
Schedule start time for the job
---
### CronConfig.stopAt
Schedule stop time for the job
---
### CronConfig.maxRuns
Maximum number of executions
---
### CronConfig.catch
Continue execution even if an unhandled error is thrown by a triggered function.
### CronConfig.interval
The minimum interval between executions, in seconds.
---
## CronConfig.Pattern
Below you can find the common patterns to use the plugin.
---
## Pattern
Below you can find the common patterns to use the plugin.
## Stop cronjob
You can stop cronjob manually by accessing the cronjob name registered to `store`.
```typescript
import { Elysia } from 'elysia'
import { cron } from '@elysiajs/cron'
const app = new Elysia()
.use(
cron({
name: 'heartbeat',
pattern: '*/1 * * * * *',
run() {
console.log('Heartbeat')
}
})
)
.get(
'/stop',
({
store: {
cron: { heartbeat }
}
}) => {
heartbeat.stop()
return 'Stop heartbeat'
}
)
.listen(3000)
```
---
## Predefined patterns
You can use predefined patterns from `@elysiajs/cron/schedule`
```typescript
import { Elysia } from 'elysia'
import { cron, Patterns } from '@elysiajs/cron'
const app = new Elysia()
.use(
cron({
name: 'heartbeat',
pattern: Patterns.everySecond(),
run() {
console.log('Heartbeat')
}
})
)
.get(
'/stop',
({
store: {
cron: { heartbeat }
}
}) => {
heartbeat.stop()
return 'Stop heartbeat'
}
)
.listen(3000)
```
### Functions
| Function | Description |
| ---------------------------------------- | ----------------------------------------------------- |
| `.everySeconds(2)` | Run the task every 2 seconds |
| `.everyMinutes(5)` | Run the task every 5 minutes |
| `.everyHours(3)` | Run the task every 3 hours |
| `.everyHoursAt(3, 15)` | Run the task every 3 hours at 15 minutes |
| `.everyDayAt('04:19')` | Run the task every day at 04:19 |
| `.everyWeekOn(Patterns.MONDAY, '19:30')` | Run the task every Monday at 19:30 |
| `.everyWeekdayAt('17:00')` | Run the task every day from Monday to Friday at 17:00 |
| `.everyWeekendAt('11:00')` | Run the task on Saturday and Sunday at 11:00 |
### Function aliases to constants
| Function | Constant |
| ----------------- | ---------------------------------- |
| `.everySecond()` | EVERY_SECOND |
| `.everyMinute()` | EVERY_MINUTE |
| `.hourly()` | EVERY_HOUR |
| `.daily()` | EVERY_DAY_AT_MIDNIGHT |
| `.everyWeekday()` | EVERY_WEEKDAY |
| `.everyWeekend()` | EVERY_WEEKEND |
| `.weekly()` | EVERY_WEEK |
| `.monthly()` | EVERY_1ST_DAY_OF_MONTH_AT_MIDNIGHT |
| `.everyQuarter()` | EVERY_QUARTER |
| `.yearly()` | EVERY_YEAR |
### Constants
| Constant | Pattern |
| ---------------------------------------- | -------------------- |
| `.EVERY_SECOND` | `* * * * * *` |
| `.EVERY_5_SECONDS` | `*/5 * * * * *` |
| `.EVERY_10_SECONDS` | `*/10 * * * * *` |
| `.EVERY_30_SECONDS` | `*/30 * * * * *` |
| `.EVERY_MINUTE` | `*/1 * * * *` |
| `.EVERY_5_MINUTES` | `0 */5 * * * *` |
| `.EVERY_10_MINUTES` | `0 */10 * * * *` |
| `.EVERY_30_MINUTES` | `0 */30 * * * *` |
| `.EVERY_HOUR` | `0 0-23/1 * * *` |
| `.EVERY_2_HOURS` | `0 0-23/2 * * *` |
| `.EVERY_3_HOURS` | `0 0-23/3 * * *` |
| `.EVERY_4_HOURS` | `0 0-23/4 * * *` |
| `.EVERY_5_HOURS` | `0 0-23/5 * * *` |
| `.EVERY_6_HOURS` | `0 0-23/6 * * *` |
| `.EVERY_7_HOURS` | `0 0-23/7 * * *` |
| `.EVERY_8_HOURS` | `0 0-23/8 * * *` |
| `.EVERY_9_HOURS` | `0 0-23/9 * * *` |
| `.EVERY_10_HOURS` | `0 0-23/10 * * *` |
| `.EVERY_11_HOURS` | `0 0-23/11 * * *` |
| `.EVERY_12_HOURS` | `0 0-23/12 * * *` |
| `.EVERY_DAY_AT_1AM` | `0 01 * * *` |
| `.EVERY_DAY_AT_2AM` | `0 02 * * *` |
| `.EVERY_DAY_AT_3AM` | `0 03 * * *` |
| `.EVERY_DAY_AT_4AM` | `0 04 * * *` |
| `.EVERY_DAY_AT_5AM` | `0 05 * * *` |
| `.EVERY_DAY_AT_6AM` | `0 06 * * *` |
| `.EVERY_DAY_AT_7AM` | `0 07 * * *` |
| `.EVERY_DAY_AT_8AM` | `0 08 * * *` |
| `.EVERY_DAY_AT_9AM` | `0 09 * * *` |
| `.EVERY_DAY_AT_10AM` | `0 10 * * *` |
| `.EVERY_DAY_AT_11AM` | `0 11 * * *` |
| `.EVERY_DAY_AT_NOON` | `0 12 * * *` |
| `.EVERY_DAY_AT_1PM` | `0 13 * * *` |
| `.EVERY_DAY_AT_2PM` | `0 14 * * *` |
| `.EVERY_DAY_AT_3PM` | `0 15 * * *` |
| `.EVERY_DAY_AT_4PM` | `0 16 * * *` |
| `.EVERY_DAY_AT_5PM` | `0 17 * * *` |
| `.EVERY_DAY_AT_6PM` | `0 18 * * *` |
| `.EVERY_DAY_AT_7PM` | `0 19 * * *` |
| `.EVERY_DAY_AT_8PM` | `0 20 * * *` |
| `.EVERY_DAY_AT_9PM` | `0 21 * * *` |
| `.EVERY_DAY_AT_10PM` | `0 22 * * *` |
| `.EVERY_DAY_AT_11PM` | `0 23 * * *` |
| `.EVERY_DAY_AT_MIDNIGHT` | `0 0 * * *` |
| `.EVERY_WEEK` | `0 0 * * 0` |
| `.EVERY_WEEKDAY` | `0 0 * * 1-5` |
| `.EVERY_WEEKEND` | `0 0 * * 6,0` |
| `.EVERY_1ST_DAY_OF_MONTH_AT_MIDNIGHT` | `0 0 1 * *` |
| `.EVERY_1ST_DAY_OF_MONTH_AT_NOON` | `0 12 1 * *` |
| `.EVERY_2ND_HOUR` | `0 */2 * * *` |
| `.EVERY_2ND_HOUR_FROM_1AM_THROUGH_11PM` | `0 1-23/2 * * *` |
| `.EVERY_2ND_MONTH` | `0 0 1 */2 *` |
| `.EVERY_QUARTER` | `0 0 1 */3 *` |
| `.EVERY_6_MONTHS` | `0 0 1 */6 *` |
| `.EVERY_YEAR` | `0 0 1 1 *` |
| `.EVERY_30_MINUTES_BETWEEN_9AM_AND_5PM` | `0 */30 9-17 * * *` |
| `.EVERY_30_MINUTES_BETWEEN_9AM_AND_6PM` | `0 */30 9-18 * * *` |
| `.EVERY_30_MINUTES_BETWEEN_10AM_AND_7PM` | `0 */30 10-19 * * *` |

View File

@@ -0,0 +1,90 @@
# GraphQL Apollo
Plugin for Elysia to use GraphQL Apollo.
## Installation
```bash
bun add graphql @elysiajs/apollo @apollo/server
```
## Basic Usage
```typescript
import { Elysia } from 'elysia'
import { apollo, gql } from '@elysiajs/apollo'
const app = new Elysia()
.use(
apollo({
typeDefs: gql`
type Book {
title: String
author: String
}
type Query {
books: [Book]
}
`,
resolvers: {
Query: {
books: () => {
return [
{
title: 'Elysia',
author: 'saltyAom'
}
]
}
}
}
})
)
.listen(3000)
```
Accessing `/graphql` should show Apollo GraphQL playground work with.
## Context
Because Elysia is based on Web Standard Request and Response which is different from Node's `HttpRequest` and `HttpResponse` that Express uses, results in `req, res` being undefined in context.
Because of this, Elysia replaces both with `context` like route parameters.
```typescript
const app = new Elysia()
.use(
apollo({
typeDefs,
resolvers,
context: async ({ request }) => {
const authorization = request.headers.get('Authorization')
return {
authorization
}
}
})
)
.listen(3000)
```
## Config
This plugin extends Apollo's [ServerRegistration](https://www.apollographql.com/docs/apollo-server/api/apollo-server/#options) (which is `ApolloServer`'s' constructor parameter).
Below are the extended parameters for configuring Apollo Server with Elysia.
### path
@default `"/graphql"`
Path to expose Apollo Server.
---
### enablePlayground
@default `process.env.ENV !== 'production'`
Determine whether should Apollo should provide Apollo Playground.

View File

@@ -0,0 +1,87 @@
# GraphQL Yoga
This plugin integrates GraphQL yoga with Elysia
## Installation
```bash
bun add @elysiajs/graphql-yoga
```
## Basic Usage
```typescript
import { Elysia } from 'elysia'
import { yoga } from '@elysiajs/graphql-yoga'
const app = new Elysia()
.use(
yoga({
typeDefs: /* GraphQL */ `
type Query {
hi: String
}
`,
resolvers: {
Query: {
hi: () => 'Hello from Elysia'
}
}
})
)
.listen(3000)
```
Accessing `/graphql` in the browser (GET request) would show you a GraphiQL instance for the GraphQL-enabled Elysia server.
optional: you can install a custom version of optional peer dependencies as well:
```bash
bun add graphql graphql-yoga
```
## Resolver
Elysia uses Mobius to infer type from **typeDefs** field automatically, allowing you to get full type-safety and auto-complete when typing **resolver** types.
## Context
You can add custom context to the resolver function by adding **context**
```ts
import { Elysia } from 'elysia'
import { yoga } from '@elysiajs/graphql-yoga'
const app = new Elysia()
.use(
yoga({
typeDefs: /* GraphQL */ `
type Query {
hi: String
}
`,
context: {
name: 'Mobius'
},
// If context is a function on this doesn't present
// for some reason it won't infer context type
useContext(_) {},
resolvers: {
Query: {
hi: async (parent, args, context) => context.name
}
}
})
)
.listen(3000)
```
## Config
This plugin extends [GraphQL Yoga's createYoga options, please refer to the GraphQL Yoga documentation](https://the-guild.dev/graphql/yoga-server/docs) with inlining `schema` config to root.
Below is a config which is accepted by the plugin
### path
@default `/graphql`
Endpoint to expose GraphQL handler

View File

@@ -0,0 +1,188 @@
# HTML
Allows you to use JSX and HTML with proper headers and support.
## Installation
```bash
bun add @elysiajs/html
```
## Basic Usage
```tsx twoslash
import React from 'react'
import { Elysia } from 'elysia'
import { html, Html } from '@elysiajs/html'
new Elysia()
.use(html())
.get(
'/html',
() => `
<html lang='en'>
<head>
<title>Hello World</title>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>`
)
.get('/jsx', () => (
<html lang="en">
<head>
<title>Hello World</title>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
))
.listen(3000)
```
This plugin will automatically add `Content-Type: text/html; charset=utf8` header to the response, add `<!doctype html>`, and convert it into a Response object.
## JSX
Elysia can use JSX
1. Replace your file that needs to use JSX to end with affix **"x"**:
- .js -> .jsx
- .ts -> .tsx
2. Register the TypeScript type by append the following to **tsconfig.json**:
```jsonc
// tsconfig.json
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "Html.createElement",
"jsxFragmentFactory": "Html.Fragment"
}
}
```
3. Starts using JSX in your file
```tsx twoslash
import React from 'react'
import { Elysia } from 'elysia'
import { html, Html } from '@elysiajs/html'
new Elysia()
.use(html())
.get('/', () => (
<html lang="en">
<head>
<title>Hello World</title>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
))
.listen(3000)
```
If the error `Cannot find name 'Html'. Did you mean 'html'?` occurs, this import must be added to the JSX template:
```tsx
import { Html } from '@elysiajs/html'
```
It is important that it is written in uppercase.
## XSS
Elysia HTML is based use of the Kita HTML plugin to detect possible XSS attacks in compile time.
You can use a dedicated `safe` attribute to sanitize user value to prevent XSS vulnerability.
```tsx
import { Elysia, t } from 'elysia'
import { html, Html } from '@elysiajs/html'
new Elysia()
.use(html())
.post(
'/',
({ body }) => (
<html lang="en">
<head>
<title>Hello World</title>
</head>
<body>
<h1 safe>{body}</h1>
</body>
</html>
),
{
body: t.String()
}
)
.listen(3000)
```
However, when are building a large-scale app, it's best to have a type reminder to detect possible XSS vulnerabilities in your codebase.
To add a type-safe reminder, please install:
```sh
bun add @kitajs/ts-html-plugin
```
Then appends the following **tsconfig.json**
```jsonc
// tsconfig.json
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "Html.createElement",
"jsxFragmentFactory": "Html.Fragment",
"plugins": [{ "name": "@kitajs/ts-html-plugin" }]
}
}
```
## Config
Below is a config which is accepted by the plugin
### contentType
- Type: `string`
- Default: `'text/html; charset=utf8'`
The content-type of the response.
### autoDetect
- Type: `boolean`
- Default: `true`
Whether to automatically detect HTML content and set the content-type.
### autoDoctype
- Type: `boolean | 'full'`
- Default: `true`
Whether to automatically add `<!doctype html>` to a response starting with `<html>`, if not found.
Use `full` to also automatically add doctypes on responses returned without this plugin
```ts
// without the plugin
app.get('/', () => '<html></html>')
// With the plugin
app.get('/', ({ html }) => html('<html></html>'))
```
### isHtml
- Type: `(value: string) => boolean`
- Default: `isHtml` (exported function)
The function is used to detect if a string is a html or not. Default implementation if length is greater than 7, starts with `<` and ends with `>`.
Keep in mind there's no real way to validate HTML, so the default implementation is a best guess.

View File

@@ -0,0 +1,197 @@
# JWT Plugin
This plugin adds support for using JWT in Elysia handlers.
## Installation
```bash
bun add @elysiajs/jwt
```
## Basic Usage
```typescript [cookie]
import { Elysia } from 'elysia'
import { jwt } from '@elysiajs/jwt'
const app = new Elysia()
.use(
jwt({
name: 'jwt',
secret: 'Fischl von Luftschloss Narfidort'
})
)
.get('/sign/:name', async ({ jwt, params: { name }, cookie: { auth } }) => {
const value = await jwt.sign({ name })
auth.set({
value,
httpOnly: true,
maxAge: 7 * 86400,
path: '/profile',
})
return `Sign in as ${value}`
})
.get('/profile', async ({ jwt, status, cookie: { auth } }) => {
const profile = await jwt.verify(auth.value)
if (!profile)
return status(401, 'Unauthorized')
return `Hello ${profile.name}`
})
.listen(3000)
```
## Config
This plugin extends config from [jose](https://github.com/panva/jose).
Below is a config that is accepted by the plugin.
### name
Name to register `jwt` function as.
For example, `jwt` function will be registered with a custom name.
```typescript
new Elysia()
.use(
jwt({
name: 'myJWTNamespace',
secret: process.env.JWT_SECRETS!
})
)
.get('/sign/:name', ({ myJWTNamespace, params }) => {
return myJWTNamespace.sign(params)
})
```
Because some might need to use multiple `jwt` with different configs in a single server, explicitly registering the JWT function with a different name is needed.
### secret
The private key to sign JWT payload with.
### schema
Type strict validation for JWT payload.
### alg
@default `HS256`
Signing Algorithm to sign JWT payload with.
Possible properties for jose are:
HS256
HS384
HS512
PS256
PS384
PS512
RS256
RS384
RS512
ES256
ES256K
ES384
ES512
EdDSA
### iss
The issuer claim identifies the principal that issued the JWT as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.1)
TLDR; is usually (the domain) name of the signer.
### sub
The subject claim identifies the principal that is the subject of the JWT.
The claims in a JWT are normally statements about the subject as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.2)
### aud
The audience claim identifies the recipients that the JWT is intended for.
Each principal intended to process the JWT MUST identify itself with a value in the audience claim as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.3)
### jti
JWT ID claim provides a unique identifier for the JWT as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.7)
### nbf
The "not before" claim identifies the time before which the JWT must not be accepted for processing as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.5)
### exp
The expiration time claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.4)
### iat
The "issued at" claim identifies the time at which the JWT was issued.
This claim can be used to determine the age of the JWT as per [RFC7519](https://www.rfc-editor.org/rfc/rfc7519#section-4.1.6)
### b64
This JWS Extension Header Parameter modifies the JWS Payload representation and the JWS Signing input computation as per [RFC7797](https://www.rfc-editor.org/rfc/rfc7797).
### kid
A hint indicating which key was used to secure the JWS.
This parameter allows originators to explicitly signal a change of key to recipients as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.4)
### x5t
(X.509 certificate SHA-1 thumbprint) header parameter is a base64url-encoded SHA-1 digest of the DER encoding of the X.509 certificate [RFC5280](https://www.rfc-editor.org/rfc/rfc5280) corresponding to the key used to digitally sign the JWS as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.7)
### x5c
(X.509 certificate chain) header parameter contains the X.509 public key certificate or certificate chain [RFC5280](https://www.rfc-editor.org/rfc/rfc5280) corresponding to the key used to digitally sign the JWS as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.6)
### x5u
(X.509 URL) header parameter is a URI [RFC3986](https://www.rfc-editor.org/rfc/rfc3986) that refers to a resource for the X.509 public key certificate or certificate chain [RFC5280] corresponding to the key used to digitally sign the JWS as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.5)
### jwk
The "jku" (JWK Set URL) Header Parameter is a URI [RFC3986] that refers to a resource for a set of JSON-encoded public keys, one of which corresponds to the key used to digitally sign the JWS.
The keys MUST be encoded as a JWK Set [JWK] as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.2)
### typ
The `typ` (type) Header Parameter is used by JWS applications to declare the media type [IANA.MediaTypes] of this complete JWS.
This is intended for use by the application when more than one kind of object could be present in an application data structure that can contain a JWS as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.9)
### ctr
Content-Type parameter is used by JWS applications to declare the media type [IANA.MediaTypes] of the secured content (the payload).
This is intended for use by the application when more than one kind of object could be present in the JWS Payload as per [RFC7515](https://www.rfc-editor.org/rfc/rfc7515#section-4.1.9)
## Handler
Below are the value added to the handler.
### jwt.sign
A dynamic object of collection related to use with JWT registered by the JWT plugin.
Type:
```typescript
sign: (payload: JWTPayloadSpec): Promise<string>
```
`JWTPayloadSpec` accepts the same value as [JWT config](#config)
### jwt.verify
Verify payload with the provided JWT config
Type:
```typescript
verify(payload: string) => Promise<JWTPayloadSpec | false>
```
`JWTPayloadSpec` accepts the same value as [JWT config](#config)
## Pattern
Below you can find the common patterns to use the plugin.
## Set JWT expiration date
By default, the config is passed to `setCookie` and inherits its value.
```typescript
const app = new Elysia()
.use(
jwt({
name: 'jwt',
secret: 'kunikuzushi',
exp: '7d'
})
)
.get('/sign/:name', async ({ jwt, params }) => jwt.sign(params))
```
This will sign JWT with an expiration date of the next 7 days.

View File

@@ -0,0 +1,246 @@
# OpenAPI Plugin
## Installation
```bash
bun add @elysiajs/openapi
```
## Basic Usage
```typescript
import { openapi } from '@elysiajs/openapi'
new Elysia()
.use(openapi())
.get('/', () => 'hello')
```
Docs at `/openapi`, spec at `/openapi/json`.
## Detail Object
Extends OpenAPI Operation Object:
```typescript
.get('/', () => 'hello', {
detail: {
title: 'Hello',
description: 'An example route',
summary: 'Short summary',
deprecated: false,
hide: true, // Hide from docs
tags: ['App']
}
})
```
### Documentation Config
```typescript
openapi({
documentation: {
info: {
title: 'API',
version: '1.0.0'
},
tags: [
{ name: 'App', description: 'General' }
],
components: {
securitySchemes: {
bearerAuth: { type: 'http', scheme: 'bearer' }
}
}
}
})
```
### Standard Schema Mapping
```typescript
mapJsonSchema: {
zod: z.toJSONSchema, // Zod 4
valibot: toJsonSchema,
effect: JSONSchema.make
}
```
Zod 3: `zodToJsonSchema` from `zod-to-json-schema`
## OpenAPI Type Gen
Generate docs from types:
```typescript
import { fromTypes } from '@elysiajs/openapi'
export const app = new Elysia()
.use(openapi({
references: fromTypes()
}))
```
### Production
Recommended to generate `.d.ts` file for production when using OpenAPI Type Gen
```typescript
references: fromTypes(
process.env.NODE_ENV === 'production'
? 'dist/index.d.ts'
: 'src/index.ts'
)
```
### Options
```typescript
fromTypes('src/index.ts', {
projectRoot: path.join('..', import.meta.dir),
tsconfigPath: 'tsconfig.dts.json'
})
```
### Caveat: Explicit Types
Use `Prettify` helper to inline when type is not showing:
```typescript
type Prettify<T> = { [K in keyof T]: T[K] } & {}
function getUser(): Prettify<User> { }
```
## Schema Description
```typescript
body: t.Object({
username: t.String(),
password: t.String({
minLength: 8,
description: 'Password (8+ chars)'
})
}, {
description: 'Expected username and password'
}),
detail: {
summary: 'Sign in user',
tags: ['auth']
}
```
## Response Headers
```typescript
import { withHeader } from '@elysiajs/openapi'
response: withHeader(
t.Literal('Hi'),
{ 'x-powered-by': t.Literal('Elysia') }
)
```
Annotation only - doesn't enforce. Set headers manually.
## Tags
Define + assign:
```typescript
.use(openapi({
documentation: {
tags: [
{ name: 'App', description: 'General' },
{ name: 'Auth', description: 'Auth' }
]
}
}))
.get('/', () => 'hello', {
detail: { tags: ['App'] }
})
```
### Instance Tags
```typescript
new Elysia({ tags: ['user'] })
.get('/user', 'user')
```
## Reference Models
Auto-generates schemas:
```typescript
.model({
User: t.Object({
id: t.Number(),
username: t.String()
})
})
.get('/user', () => ({ id: 1, username: 'x' }), {
response: { 200: 'User' },
detail: { tags: ['User'] }
})
```
## Guard
Apply to instance/group:
```typescript
.guard({
detail: {
description: 'Requires auth'
}
})
.get('/user', 'user')
```
## Security
```typescript
.use(openapi({
documentation: {
components: {
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT'
}
}
}
}
}))
new Elysia({
prefix: '/address',
detail: {
security: [{ bearerAuth: [] }]
}
})
```
Secures all routes under prefix.
## Config
Below is a config which is accepted by the `openapi({})`
### enabled
@default true
Enable/Disable the plugin
### documentation
OpenAPI documentation information
@see https://spec.openapis.org/oas/v3.0.3.html
### exclude
Configuration to exclude paths or methods from documentation
### exclude.methods
List of methods to exclude from documentation
### exclude.paths
List of paths to exclude from documentation
### exclude.staticFile
@default true
Exclude static file routes from documentation
### exclude.tags
List of tags to exclude from documentation
### mapJsonSchema
A custom mapping function from Standard schema to OpenAPI schema
### path
@default '/openapi'
The endpoint to expose OpenAPI documentation frontend
### provider
@default 'scalar'
OpenAPI documentation frontend between:
- Scalar
- SwaggerUI
- null: disable frontend

View File

@@ -0,0 +1,167 @@
# OpenTelemetry Plugin - SKILLS.md
## Installation
```bash
bun add @elysiajs/opentelemetry
```
## Basic Usage
```typescript
import { opentelemetry } from '@elysiajs/opentelemetry'
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'
new Elysia()
.use(opentelemetry({
spanProcessors: [
new BatchSpanProcessor(new OTLPTraceExporter())
]
}))
```
Auto-collects spans from OpenTelemetry-compatible libraries. Parent/child spans applied automatically.
## Config
Extends OpenTelemetry SDK params:
- `autoDetectResources` (true) - Auto-detect from env
- `contextManager` (AsyncHooksContextManager) - Custom context
- `textMapPropagator` (CompositePropagator) - W3C Trace + Baggage
- `metricReader` - For MeterProvider
- `views` - Histogram bucket config
- `instrumentations` (getNodeAutoInstrumentations()) - Metapackage or individual
- `resource` - Custom resource
- `resourceDetectors` ([envDetector, processDetector, hostDetector]) - Auto-detect needs `autoDetectResources: true`
- `sampler` - Custom sampler (default: sample all)
- `serviceName` - Namespace identifier
- `spanProcessors` - Array for tracer provider
- `traceExporter` - Auto-setup OTLP/http/protobuf with BatchSpanProcessor if not set
- `spanLimits` - Tracing params
### Resource Detectors via Env
```bash
export OTEL_NODE_RESOURCE_DETECTORS="env,host"
# Options: env, host, os, process, serviceinstance, all, none
```
## Export to Backends
Example - Axiom:
```typescript
.use(opentelemetry({
spanProcessors: [
new BatchSpanProcessor(
new OTLPTraceExporter({
url: 'https://api.axiom.co/v1/traces',
headers: {
Authorization: `Bearer ${Bun.env.AXIOM_TOKEN}`,
'X-Axiom-Dataset': Bun.env.AXIOM_DATASET
}
})
)
]
}))
```
## OpenTelemetry SDK
Use SDK normally - runs under Elysia's request span, auto-appears in trace.
## Record Utility
Equivalent to `startActiveSpan` - auto-closes + captures exceptions:
```typescript
import { record } from '@elysiajs/opentelemetry'
.get('', () => {
return record('database.query', () => {
return db.query('SELECT * FROM users')
})
})
```
Label for code shown in trace.
## Function Naming
Elysia reads function names as span names:
```typescript
// ⚠️ Anonymous span
.derive(async ({ cookie: { session } }) => {
return { user: await getProfile(session) }
})
// ✅ Named span: "getProfile"
.derive(async function getProfile({ cookie: { session } }) {
return { user: await getProfile(session) }
})
```
## getCurrentSpan
Get current span outside handler (via AsyncLocalStorage):
```typescript
import { getCurrentSpan } from '@elysiajs/opentelemetry'
function utility() {
const span = getCurrentSpan()
span.setAttributes({ 'custom.attribute': 'value' })
}
```
## setAttributes
Sugar for `getCurrentSpan().setAttributes`:
```typescript
import { setAttributes } from '@elysiajs/opentelemetry'
function utility() {
setAttributes({ 'custom.attribute': 'value' })
}
```
## Instrumentations (Advanced)
SDK must run before importing instrumented module.
### Setup
1. Separate file:
```typescript
// src/instrumentation.ts
import { opentelemetry } from '@elysiajs/opentelemetry'
import { PgInstrumentation } from '@opentelemetry/instrumentation-pg'
export const instrumentation = opentelemetry({
instrumentations: [new PgInstrumentation()]
})
```
2. Apply:
```typescript
// src/index.ts
import { instrumentation } from './instrumentation'
new Elysia().use(instrumentation).listen(3000)
```
3. Preload:
```toml
# bunfig.toml
preload = ["./src/instrumentation.ts"]
```
### Production Deployment (Advanced)
OpenTelemetry monkey-patches `node_modules`. Exclude instrumented libs from bundling:
```bash
bun build --compile --external pg --outfile server src/index.ts
```
Package.json:
```json
{
"dependencies": { "pg": "^8.15.6" },
"devDependencies": {
"@elysiajs/opentelemetry": "^1.2.0",
"@opentelemetry/instrumentation-pg": "^0.52.0"
}
}
```
Production install:
```bash
bun install --production
```
Keeps `node_modules` with instrumented libs at runtime.

View File

@@ -0,0 +1,71 @@
# Server Timing Plugin
This plugin adds support for auditing performance bottlenecks with Server Timing API
## Installation
```bash
bun add @elysiajs/server-timing
```
## Basic Usage
```typescript twoslash
import { Elysia } from 'elysia'
import { serverTiming } from '@elysiajs/server-timing'
new Elysia()
.use(serverTiming())
.get('/', () => 'hello')
.listen(3000)
```
Server Timing then will append header 'Server-Timing' with log duration, function name, and detail for each life-cycle function.
To inspect, open browser developer tools > Network > [Request made through Elysia server] > Timing.
Now you can effortlessly audit the performance bottleneck of your server.
## Config
Below is a config which is accepted by the plugin
### enabled
@default `NODE_ENV !== 'production'`
Determine whether or not Server Timing should be enabled
### allow
@default `undefined`
A condition whether server timing should be log
### trace
@default `undefined`
Allow Server Timing to log specified life-cycle events:
Trace accepts objects of the following:
- request: capture duration from request
- parse: capture duration from parse
- transform: capture duration from transform
- beforeHandle: capture duration from beforeHandle
- handle: capture duration from the handle
- afterHandle: capture duration from afterHandle
- total: capture total duration from start to finish
## Pattern
Below you can find the common patterns to use the plugin.
## Allow Condition
You may disable Server Timing on specific routes via `allow` property
```ts twoslash
import { Elysia } from 'elysia'
import { serverTiming } from '@elysiajs/server-timing'
new Elysia()
.use(
serverTiming({
allow: ({ request }) => {
return new URL(request.url).pathname !== '/no-trace'
}
})
)
```

View File

@@ -0,0 +1,84 @@
# Static Plugin
This plugin can serve static files/folders for Elysia Server
## Installation
```bash
bun add @elysiajs/static
```
## Basic Usage
```typescript twoslash
import { Elysia } from 'elysia'
import { staticPlugin } from '@elysiajs/static'
new Elysia()
.use(staticPlugin())
.listen(3000)
```
By default, the static plugin default folder is `public`, and registered with `/public` prefix.
Suppose your project structure is:
```
| - src
| - index.ts
| - public
| - takodachi.png
| - nested
| - takodachi.png
```
The available path will become:
- /public/takodachi.png
- /public/nested/takodachi.png
## Config
Below is a config which is accepted by the plugin
### assets
@default `"public"`
Path to the folder to expose as static
### prefix
@default `"/public"`
Path prefix to register public files
### ignorePatterns
@default `[]`
List of files to ignore from serving as static files
### staticLimit
@default `1024`
By default, the static plugin will register paths to the Router with a static name, if the limits are exceeded, paths will be lazily added to the Router to reduce memory usage.
Tradeoff memory with performance.
### alwaysStatic
@default `false`
If set to true, static files path will be registered to Router skipping the `staticLimits`.
### headers
@default `{}`
Set response headers of files
### indexHTML
@default `false`
If set to true, the `index.html` file from the static directory will be served for any request that is matching neither a route nor any existing static file.
## Pattern
Below you can find the common patterns to use the plugin.
## Single file
Suppose you want to return just a single file, you can use `file` instead of using the static plugin
```typescript
import { Elysia, file } from 'elysia'
new Elysia()
.get('/file', file('public/takodachi.png'))
```