Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- **`@logtide/elysia/next`** — an isolated Elysia 2.x integration using the new `request`, `afterHandle`, and `error` lifecycle contracts. The existing `@logtide/elysia` export remains on the Elysia 1.4 contract.

## [0.12.0] - 2026-08-06

### Added
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
| [`@logtide/sveltekit`](./packages/sveltekit) | [![npm](https://img.shields.io/npm/v/@logtide/sveltekit?color=blue)](https://www.npmjs.com/package/@logtide/sveltekit) | SvelteKit hooks integration |
| [`@logtide/hono`](./packages/hono) | [![npm](https://img.shields.io/npm/v/@logtide/hono?color=blue)](https://www.npmjs.com/package/@logtide/hono) | Hono middleware |
| [`@logtide/angular`](./packages/angular) | [![npm](https://img.shields.io/npm/v/@logtide/angular?color=blue)](https://www.npmjs.com/package/@logtide/angular) | Angular ErrorHandler, HTTP Interceptor |
| [`@logtide/elysia`](./packages/elysia) | [![npm](https://img.shields.io/npm/v/@logtide/elysia?color=blue)](https://www.npmjs.com/package/@logtide/elysia) | Elysia plugin |
| [`@logtide/elysia`](./packages/elysia) | [![npm](https://img.shields.io/npm/v/@logtide/elysia?color=blue)](https://www.npmjs.com/package/@logtide/elysia) | Elysia 1.4 plugin + Elysia 2 preview via `/next` |
| [`@logtide/browser`](./packages/browser) | [![npm](https://img.shields.io/npm/v/@logtide/browser?color=blue)](https://www.npmjs.com/package/@logtide/browser) | Browser SDK — Web Vitals, breadcrumbs, offline transport |
| [`@logtide/cli`](./packages/cli) | [![npm](https://img.shields.io/npm/v/@logtide/cli?color=blue)](https://www.npmjs.com/package/@logtide/cli) | CLI — source maps upload and release management |
| [`@logtide/sdk-node`](./packages/node) | [![npm](https://img.shields.io/npm/v/@logtide/sdk-node?color=blue)](https://www.npmjs.com/package/@logtide/sdk-node) | Legacy Node.js SDK _(use `@logtide/express` or `@logtide/fastify` instead)_ |
Expand Down Expand Up @@ -101,7 +101,7 @@ npx @logtide/cli sourcemaps upload --api-url https://your-logtide-instance.com -
├── @logtide/sveltekit ← SvelteKit (handle/handleError/handleFetch)
├── @logtide/hono ← Hono middleware
├── @logtide/angular ← Angular (ErrorHandler + HttpInterceptor)
├── @logtide/elysia ← Elysia plugin
├── @logtide/elysia ← Elysia 1.4 plugin + Elysia 2 /next export
├── @logtide/browser ← Browser SDK (Web Vitals, breadcrumbs, offline)
├── @logtide/cli ← CLI (source maps upload)
└── @logtide/sdk-node ← Legacy standalone Node.js SDK
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"scripts": {
"build": "pnpm -r --filter @logtide/* build",
"test": "pnpm -r --filter @logtide/* test",
"test:smoke": "pnpm -r --filter test-app-hono --filter test-app-elysia --filter test-app-node-express --filter test-app-node-fastify test",
"test:smoke": "pnpm -r --filter test-app-hono --filter test-app-elysia --filter test-app-elysia-v2 --filter test-app-node-express --filter test-app-node-fastify test",
"test:e2e": "pnpm -r --filter test-app-nextjs --filter test-app-sveltekit --filter test-app-nuxt --filter test-app-angular test",
"test:all": "pnpm test && pnpm test:smoke",
"typecheck": "pnpm -r typecheck",
Expand Down Expand Up @@ -34,7 +34,7 @@
"simple-git": ">=3.32.3",
"@hono/node-server": ">=1.19.10",
"defu": ">=6.1.5",
"elysia": ">=1.4.27",
"elysia@<1.4.27": "1.4.29",

@Polliog Polliog Aug 18, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The security override no longer guards anything. pnpm's pkg@range selector only matches semver-range specifiers, so elysia@<1.4.27 does not apply to npm:elysia@latest/@next, and the exact 1.4.29 pin will not pick up a future 1.4.30 fix. Suggest "elysia@<2": ">=1.4.27 <2".

"h3": ">=1.15.9",
"immutable": ">=5.1.5",
"lodash": ">=4.18.1",
Expand Down
45 changes: 37 additions & 8 deletions packages/elysia/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<p align="center">
<a href="https://www.npmjs.com/package/@logtide/elysia"><img src="https://img.shields.io/npm/v/@logtide/elysia?color=blue" alt="npm"></a>
<a href="../../LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License"></a>
<a href="https://elysiajs.com/"><img src="https://img.shields.io/badge/Elysia-1+-7C6AEF.svg" alt="Elysia"></a>
<a href="https://elysiajs.com/"><img src="https://img.shields.io/badge/Elysia-1.4%20%7C%202.x-7C6AEF.svg" alt="Elysia"></a>
<a href="https://github.com/logtide-dev/logtide-javascript/releases"><img src="https://img.shields.io/github/v/release/logtide-dev/logtide-javascript" alt="Release"></a>
</p>

Expand All @@ -19,8 +19,8 @@

## Features

- **Automatic request spans** via `onRequest` / `onAfterHandle` lifecycle hooks
- **Error capture** via `onError` hook with full request context
- **Automatic request spans** via version-specific Elysia lifecycle hooks
- **Error capture** with full request context
- **W3C Trace Context** propagation (`traceparent` in/out)
- **Breadcrumbs** for HTTP requests
- **Global plugin** — traces all routes automatically
Expand All @@ -41,6 +41,8 @@ bun add @logtide/elysia

## Quick Start

Use the root export with Elysia 1.4:

```typescript
import { Elysia } from 'elysia';
import { logtide } from '@logtide/elysia';
Expand All @@ -58,17 +60,39 @@ const app = new Elysia()
.listen(3000);
```

Use the isolated `next` export with Elysia 2.x:

```typescript
import { Elysia } from 'elysia';
import { logtide } from '@logtide/elysia/next';

const app = new Elysia()
.use(logtide({
dsn: 'https://lp_your_key@your-instance.com',
service: 'my-elysia-api',
environment: 'production',
}))
.get('/hello', () => 'Hello World')
.listen(3000);
```

Do not mix the root and `next` exports: they intentionally target the incompatible Elysia 1.4 and 2.x contracts respectively.

---

## How It Works

The plugin registers global lifecycle hooks on the Elysia app:
Both plugins register global lifecycle hooks on the Elysia app:

1. **`onRequest`** — extracts incoming `traceparent`, creates a span, stores scope on a WeakMap keyed by the request
2. **`onAfterHandle`** — finishes the span with `ok` or `error`, injects `traceparent` into response headers
3. **`onError`** — finishes the span as `error`, captures the error with full HTTP context
| Stage | Elysia 1.4 (`@logtide/elysia`) | Elysia 2 (`@logtide/elysia/next`) |
|-------|---------------------------------|-------------------------------------|
| Request | `onRequest` | `request` |
| Response | `onAfterHandle` | `afterHandle` + `afterResponse` |
| Error | `onError` | `error` |

The plugin is registered with `.as('global')` so it applies to all routes, including those added after the plugin.
The request hook extracts an incoming `traceparent` and creates a span. The response hooks inject `traceparent` and finish the span using the final mapped status. The error hook marks the span as failed and captures the exception with HTTP context.

Each plugin is registered with `.as('global')` so it applies to all routes, including those added after the plugin.

---

Expand Down Expand Up @@ -126,8 +150,13 @@ Outgoing responses include a `traceparent` header with the span's context.
## Exports

```typescript
// Elysia 1.4
import { logtide } from '@logtide/elysia';
import type { LogtideElysiaOptions } from '@logtide/elysia';

// Elysia 2.x
import { logtide } from '@logtide/elysia/next';
import type { LogtideElysiaOptions } from '@logtide/elysia/next';
```

---
Expand Down
20 changes: 16 additions & 4 deletions packages/elysia/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,23 @@
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
},
"./next": {
"import": {
"types": "./dist/next.d.ts",
"default": "./dist/next.js"
},
"require": {
"types": "./dist/next.d.cts",
"default": "./dist/next.cjs"
}
}
},
"files": [
"dist"
],
"scripts": {
"build": "tsup",
"build": "tsup && node scripts/patch-next-declarations.mjs",
"test": "vitest run",
"typecheck": "tsc --noEmit",
"clean": "node -e \"require('fs').rmSync('dist', {recursive: true, force: true})\""
Expand All @@ -46,13 +56,15 @@
"@logtide/types": "workspace:*"
},
"peerDependencies": {
"elysia": ">=1.0.0"
"elysia": ">=1.4.0 <2.0.0 || >=2.0.0-beta.4 <3.0.0"

@Polliog Polliog Aug 18, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Peer range wrong at both ends. It drops elysia 1.0-1.3 users (undocumented breaking change, the v1 plugin needs nothing newer), and npm semver only matches prereleases on the same major.minor.patch, so >=2.0.0-beta.4 <3.0.0 rejects future betas like 2.0.1-beta.1 or 2.1.0-beta.0.

},
"devDependencies": {
"@sinclair/typebox": "^0.34.48",
"elysia": "^1.2.0",
"elysia": "npm:elysia@latest",

@Polliog Polliog Aug 18, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Floating dist-tags decide what gets built and tested. Once Elysia promotes 2.0.0 to latest, any lockfile refresh retargets the v1 plugin's build/tests/dts at Elysia 2, and next will drift to 2.1/3.0 betas. Pin ^1.4.29 and an exact or tilde beta instead.

"elysia-next": "npm:elysia@next",
"exact-mirror": "^1.2.4",
"file-type": "^22.0.0",
"tsup": "^8.5.1",
"typescript": "^5.5.4"
"typescript": "^5.7.0"
}
}
14 changes: 14 additions & 0 deletions packages/elysia/scripts/patch-next-declarations.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { readFile, writeFile } from 'node:fs/promises';

await Promise.all(
['dist/next.d.ts', 'dist/next.d.cts'].map(async (path) => {
const declaration = await readFile(path, 'utf8');
const patched = declaration.replaceAll('elysia-next', 'elysia');

@Polliog Polliog Aug 18, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fragile in both directions. The blind replaceAll rewrites any future 'elysia-next' occurrence in the dts output, and the throw-on-no-match fails the whole build if tsup's dts shape changes (dts.resolve, different chunking, or an explicit return type eliding the import). Promise.all can also leave dist half-patched.


if (patched === declaration) {
throw new Error(`Expected ${path} to reference the elysia-next development alias`);
}

await writeFile(path, patched);
})
);
1 change: 1 addition & 0 deletions packages/elysia/src/next.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { logtide, type LogtideElysiaOptions } from './plugin-next';
Loading
Loading