Skip to content
Merged
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
25 changes: 25 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -157,54 +157,77 @@ jobs:

# Publish in dependency order: types -> core -> all others
- name: Publish @logtide/types
continue-on-error: true
working-directory: packages/types
run: pnpm publish --no-git-checks --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }}

- name: Publish @logtide/core
continue-on-error: true
working-directory: packages/core
run: pnpm publish --no-git-checks --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }}

- name: Publish @logtide/sdk-node
continue-on-error: true
working-directory: packages/node
run: pnpm publish --no-git-checks --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }}

- name: Publish @logtide/express
continue-on-error: true
working-directory: packages/express
run: pnpm publish --no-git-checks --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }}

- name: Publish @logtide/fastify
continue-on-error: true
working-directory: packages/fastify
run: pnpm publish --no-git-checks --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }}

- name: Publish @logtide/nextjs
continue-on-error: true
working-directory: packages/nextjs
run: pnpm publish --no-git-checks --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }}

- name: Publish @logtide/nuxt
continue-on-error: true
working-directory: packages/nuxt
run: pnpm publish --no-git-checks --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }}

- name: Publish @logtide/sveltekit
continue-on-error: true
working-directory: packages/sveltekit
run: pnpm publish --no-git-checks --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }}

- name: Publish @logtide/hono
continue-on-error: true
working-directory: packages/hono
run: pnpm publish --no-git-checks --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }}

- name: Publish @logtide/angular
continue-on-error: true
working-directory: packages/angular
run: pnpm publish --no-git-checks --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_SECRET }}

- name: Publish @logtide/elysia
continue-on-error: true
working-directory: packages/elysia
run: pnpm publish --no-git-checks --access public
env:
Expand All @@ -222,6 +245,8 @@ jobs:
echo "| @logtide/types | \`npm i @logtide/types@${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
echo "| @logtide/core | \`npm i @logtide/core@${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
echo "| @logtide/sdk-node | \`npm i @logtide/sdk-node@${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
echo "| @logtide/express | \`npm i @logtide/express@${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
echo "| @logtide/fastify | \`npm i @logtide/fastify@${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
echo "| @logtide/nextjs | \`npm i @logtide/nextjs@${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
echo "| @logtide/nuxt | \`npm i @logtide/nuxt@${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
echo "| @logtide/sveltekit | \`npm i @logtide/sveltekit@${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
Expand Down
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,29 @@ 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).

## [0.5.6] - 2026-02-08

### Changed

#### DSN Simplified
- Removed redundant `projectId` from DSN format — the API key already embeds the project ID
- New DSN format: `https://lp_APIKEY@host` (legacy format with path still accepted for backward compatibility)
- Added `apiUrl` + `apiKey` as alternative to DSN string (backward compatible with `@logtide/sdk-node` config format)
- Added `resolveDSN()` helper that accepts either `dsn` or `apiUrl` + `apiKey`
- Removed `projectId` field from `DSN` interface (`@logtide/types`)
- Removed `X-Project-Id` header from `LogtideHttpTransport` and `OtlpHttpTransport` (`@logtide/core`)

#### Dynamic Service Name
- `service` in `ClientOptions` is now **optional** — each framework package defaults to its own name (`'express'`, `'fastify'`, `'hono'`, `'elysia'`, `'nextjs'`, `'sveltekit'`, `'nuxt'`, `'angular'`)
- Added `service?: string` field and `setService()` method to `Scope` — allows overriding service name per-request or per-module
- Service resolution chain: `scope.service` → `options.service` → framework default → `'unknown'`

#### Mock Server
- Removed `X-Project-Id` from CORS headers and request tracking

#### Documentation
- Updated DSN format examples across all package READMEs

## [0.5.5] - 2026-02-07

### Added
Expand Down Expand Up @@ -77,4 +100,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Root README with package table, architecture diagram, development guide
- Branch protection documentation (`.github/BRANCH_PROTECTION.md`)

[0.5.6]: https://github.com/logtide-dev/logtide-javascript/releases/tag/v0.5.6
[0.5.5]: https://github.com/logtide-dev/logtide-javascript/releases/tag/v0.5.5
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,14 @@ npm install @logtide/elysia # Elysia
```typescript
// Every integration follows the same pattern:
{
dsn: 'https://lp_your_key@your-logtide-instance.com/project-id',
dsn: 'https://lp_your_key@your-logtide-instance.com',
service: 'my-app',
}

// Or use apiUrl + apiKey separately:
{
apiUrl: 'https://your-logtide-instance.com',
apiKey: 'lp_your_key',
service: 'my-app',
}
```
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"private": true,
"version": "0.5.5",
"version": "0.5.6",
"scripts": {
"build": "pnpm -r --filter @logtide/* build",
"test": "pnpm -r --filter @logtide/* test",
Expand Down
8 changes: 6 additions & 2 deletions packages/angular/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(withInterceptorsFromDi()),
provideLogtide({
dsn: 'https://lp_your_key@your-instance.com/project-id',
dsn: 'https://lp_your_key@your-instance.com',
// Or use apiUrl + apiKey instead of dsn:
// apiUrl: 'https://your-instance.com',
// apiKey: 'lp_your_key',
service: 'my-angular-app',
environment: 'production',
}),
Expand All @@ -72,7 +75,8 @@ import { getLogtideProviders } from '@logtide/angular';
imports: [HttpClientModule],
providers: [
...getLogtideProviders({
dsn: 'https://lp_your_key@your-instance.com/project-id',
dsn: 'https://lp_your_key@your-instance.com',
// Or: apiUrl + apiKey instead of dsn
service: 'my-angular-app',
}),
],
Expand Down
2 changes: 1 addition & 1 deletion packages/angular/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@logtide/angular",
"version": "0.5.5",
"version": "0.5.6",
"description": "LogTide SDK integration for Angular — ErrorHandler, HTTP Interceptor, trace propagation",
"type": "module",
"main": "./dist/index.cjs",
Expand Down
2 changes: 2 additions & 0 deletions packages/angular/src/provide.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export function provideLogtide(options: ClientOptions): EnvironmentProviders {
useFactory: () => {
return () => {
hub.init({
service: 'angular',
...options,
integrations: [
new GlobalErrorIntegration(),
Expand Down Expand Up @@ -72,6 +73,7 @@ export function getLogtideProviders(options: ClientOptions): Provider[] {
useFactory: () => {
return () => {
hub.init({
service: 'angular',
...options,
integrations: [
new GlobalErrorIntegration(),
Expand Down
10 changes: 8 additions & 2 deletions packages/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ import { hub } from '@logtide/core';

// Initialize once
hub.init({
dsn: 'https://lp_your_key@your-instance.com/project-id',
dsn: 'https://lp_your_key@your-instance.com',
// Or use apiUrl + apiKey instead of dsn:
// apiUrl: 'https://your-instance.com',
// apiKey: 'lp_your_key',
service: 'my-app',
});

Expand Down Expand Up @@ -81,7 +84,10 @@ process.on('SIGTERM', () => hub.close());
import { LogtideClient } from '@logtide/core';

const client = new LogtideClient({
dsn: 'https://lp_your_key@your-instance.com/project-id',
dsn: 'https://lp_your_key@your-instance.com',
// Or use apiUrl + apiKey instead of dsn:
// apiUrl: 'https://your-instance.com',
// apiKey: 'lp_your_key',
service: 'my-app',
});

Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@logtide/core",
"version": "0.5.5",
"version": "0.5.6",
"description": "Core client, hub, scope, transports, and utilities for the LogTide SDK",
"type": "module",
"main": "./dist/index.cjs",
Expand Down
14 changes: 9 additions & 5 deletions packages/core/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
Span,
Transport,
} from '@logtide/types';
import { parseDSN } from './dsn';
import { resolveDSN } from './dsn';
import { Scope } from './scope';
import { SpanManager, type StartSpanOptions } from './span-manager';
import { BreadcrumbBuffer } from './breadcrumb-buffer';
Expand All @@ -26,7 +26,7 @@ class DefaultTransport implements Transport {
private spanTransport: BatchTransport;

constructor(options: ClientOptions) {
const dsn = parseDSN(options.dsn);
const dsn = resolveDSN(options);

this.logTransport = new BatchTransport({
inner: new LogtideHttpTransport(dsn),
Expand All @@ -41,7 +41,7 @@ class DefaultTransport implements Transport {
});

this.spanTransport = new BatchTransport({
inner: new OtlpHttpTransport(dsn, options.service),
inner: new OtlpHttpTransport(dsn, options.service || 'unknown'),
batchSize: options.batchSize,
flushInterval: options.flushInterval,
maxBufferSize: options.maxBufferSize,
Expand Down Expand Up @@ -103,7 +103,7 @@ export class LogtideClient implements IClient {
return this._isInitialized;
}

get service(): string {
get service(): string | undefined {
return this.options.service;
}

Expand All @@ -115,6 +115,10 @@ export class LogtideClient implements IClient {
return this.options.release;
}

private resolveService(scope?: Scope): string {
return scope?.service || this.options.service || 'unknown';
}

// ─── Logging ───────────────────────────────────────────

captureLog(
Expand All @@ -124,7 +128,7 @@ export class LogtideClient implements IClient {
scope?: Scope,
): void {
const entry: InternalLogEntry = {
service: this.options.service,
service: this.resolveService(scope),
level: level as LogLevel,
message,
time: new Date().toISOString(),
Expand Down
28 changes: 21 additions & 7 deletions packages/core/src/dsn.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,42 @@
import type { DSN } from '@logtide/types';
import type { ClientOptions, DSN } from '@logtide/types';

/**
* Parse a LogTide DSN string into its components.
* Format: https://lp_APIKEY@host/PROJECT_ID
* Format: https://lp_APIKEY@host
* Legacy format with path (https://lp_APIKEY@host/PROJECT_ID) is also accepted; the path is ignored.
*/
export function parseDSN(dsn: string): DSN {
try {
const url = new URL(dsn);
const apiKey = url.username;
const projectId = url.pathname.replace(/^\//, '');
const apiUrl = `${url.protocol}//${url.host}`;

if (!apiKey) {
throw new Error('Missing API key in DSN');
}
if (!projectId) {
throw new Error('Missing project ID in DSN');
}

return { apiUrl, apiKey, projectId };
return { apiUrl, apiKey };
} catch (err) {
if (err instanceof Error && err.message.startsWith('Missing')) {
throw err;
}
throw new Error(`Invalid DSN: ${dsn}`);
}
}

/**
* Resolve a DSN from ClientOptions.
* Accepts either a `dsn` string or separate `apiUrl` + `apiKey` fields.
*/
export function resolveDSN(options: ClientOptions): DSN {
if (options.dsn) {
return parseDSN(options.dsn);
}
if (options.apiUrl && options.apiKey) {
return {
apiUrl: options.apiUrl.replace(/\/$/, ''),
apiKey: options.apiKey,
};
}
throw new Error('Either "dsn" or both "apiUrl" and "apiKey" must be provided');
}
2 changes: 1 addition & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export { SpanManager, type StartSpanOptions } from './span-manager';
export { BreadcrumbBuffer } from './breadcrumb-buffer';

// DSN
export { parseDSN } from './dsn';
export { parseDSN, resolveDSN } from './dsn';

// Transports
export { LogtideHttpTransport } from './transport/logtide-http';
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { BreadcrumbBuffer } from './breadcrumb-buffer';
export class Scope {
traceId: string;
spanId?: string;
service?: string;
tags: Record<string, string> = {};
extras: Record<string, unknown> = {};
private breadcrumbs: BreadcrumbBuffer;
Expand All @@ -15,6 +16,11 @@ export class Scope {
this.breadcrumbs = new BreadcrumbBuffer(maxBreadcrumbs);
}

setService(service: string): this {
this.service = service;
return this;
}

setTag(key: string, value: string): this {
this.tags[key] = value;
return this;
Expand Down Expand Up @@ -51,6 +57,7 @@ export class Scope {
clone(): Scope {
const scope = new Scope(this.traceId);
scope.spanId = this.spanId;
scope.service = this.service;
scope.tags = { ...this.tags };
scope.extras = { ...this.extras };
for (const bc of this.breadcrumbs.getAll()) {
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/transport/logtide-http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export class LogtideHttpTransport implements Transport {
headers: {
'Content-Type': 'application/json',
'X-API-Key': this.dsn.apiKey,
'X-Project-Id': this.dsn.projectId,
},
body: JSON.stringify({ logs }),
});
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/transport/otlp-http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ export class OtlpHttpTransport implements Transport {
headers: {
'Content-Type': 'application/json',
'X-API-Key': this.dsn.apiKey,
'X-Project-Id': this.dsn.projectId,
},
body: JSON.stringify(payload),
});
Expand Down
Loading