feat: add bun-fullstack agent and update skills
This commit is contained in:
30
.agent/skills/tech-stack/elysiajs/plugins/bearer.md
Normal file
30
.agent/skills/tech-stack/elysiajs/plugins/bearer.md
Normal 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
|
||||
141
.agent/skills/tech-stack/elysiajs/plugins/cors.md
Normal file
141
.agent/skills/tech-stack/elysiajs/plugins/cors.md
Normal 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`
|
||||
265
.agent/skills/tech-stack/elysiajs/plugins/cron.md
Normal file
265
.agent/skills/tech-stack/elysiajs/plugins/cron.md
Normal 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 * * *` |
|
||||
90
.agent/skills/tech-stack/elysiajs/plugins/graphql-apollo.md
Normal file
90
.agent/skills/tech-stack/elysiajs/plugins/graphql-apollo.md
Normal 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.
|
||||
87
.agent/skills/tech-stack/elysiajs/plugins/graphql-yoga.md
Normal file
87
.agent/skills/tech-stack/elysiajs/plugins/graphql-yoga.md
Normal 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
|
||||
188
.agent/skills/tech-stack/elysiajs/plugins/html.md
Normal file
188
.agent/skills/tech-stack/elysiajs/plugins/html.md
Normal 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.
|
||||
197
.agent/skills/tech-stack/elysiajs/plugins/jwt.md
Normal file
197
.agent/skills/tech-stack/elysiajs/plugins/jwt.md
Normal 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.
|
||||
246
.agent/skills/tech-stack/elysiajs/plugins/openapi.md
Normal file
246
.agent/skills/tech-stack/elysiajs/plugins/openapi.md
Normal 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
|
||||
167
.agent/skills/tech-stack/elysiajs/plugins/opentelemetry.md
Normal file
167
.agent/skills/tech-stack/elysiajs/plugins/opentelemetry.md
Normal 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.
|
||||
71
.agent/skills/tech-stack/elysiajs/plugins/server-timing.md
Normal file
71
.agent/skills/tech-stack/elysiajs/plugins/server-timing.md
Normal 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'
|
||||
}
|
||||
})
|
||||
)
|
||||
```
|
||||
84
.agent/skills/tech-stack/elysiajs/plugins/static.md
Normal file
84
.agent/skills/tech-stack/elysiajs/plugins/static.md
Normal 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'))
|
||||
```
|
||||
Reference in New Issue
Block a user