mirror of
https://github.com/zoriya/drizzle-otel.git
synced 2025-12-06 00:46:09 +00:00
better auth package
This commit is contained in:
8
packages/otel-better-auth/.changeset/README.md
Normal file
8
packages/otel-better-auth/.changeset/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Changesets
|
||||
|
||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
||||
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
||||
|
||||
We have a quick list of common questions to get you started engaging with this project in
|
||||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
||||
11
packages/otel-better-auth/.changeset/config.json
Normal file
11
packages/otel-better-auth/.changeset/config.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
|
||||
"changelog": "@changesets/cli/changelog",
|
||||
"commit": false,
|
||||
"fixed": [],
|
||||
"linked": [],
|
||||
"access": "public",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": []
|
||||
}
|
||||
40
packages/otel-better-auth/.changeset/initial-release.md
Normal file
40
packages/otel-better-auth/.changeset/initial-release.md
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
"@kubiks/otel-better-auth": major
|
||||
---
|
||||
|
||||
Initial release of @kubiks/otel-better-auth
|
||||
|
||||
🎉 First release of OpenTelemetry instrumentation for Better Auth!
|
||||
|
||||
## Features
|
||||
|
||||
- **Plugin-based integration**: Clean one-line setup using Better Auth's native plugin system
|
||||
- **Comprehensive auth event tracing**: Automatic instrumentation for signup, signin, OAuth, password reset, and more
|
||||
- **Privacy-first design**: Email capture is opt-in, passwords never captured
|
||||
- **Rich telemetry**: Captures user IDs, session IDs, auth methods, and success/failure status
|
||||
- **Semantic conventions**: Follows OpenTelemetry standards with meaningful span attributes
|
||||
- **Zero configuration**: Works out of the box with sensible defaults
|
||||
|
||||
## Supported Auth Operations
|
||||
|
||||
- User signup (email/password, OAuth, magic link)
|
||||
- User signin (all authentication methods)
|
||||
- Password reset flows (forgot password, reset password)
|
||||
- Email verification
|
||||
- Session management
|
||||
- OAuth callbacks (Google, GitHub, Facebook, and more)
|
||||
- Sign out
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
import { betterAuth } from "better-auth";
|
||||
import { otelPlugin } from "@kubiks/otel-better-auth";
|
||||
|
||||
export const auth = betterAuth({
|
||||
database: db,
|
||||
emailAndPassword: { enabled: true },
|
||||
}).use(otelPlugin());
|
||||
```
|
||||
|
||||
That's it! All your authentication operations are now traced with OpenTelemetry.
|
||||
21
packages/otel-better-auth/LICENSE
Normal file
21
packages/otel-better-auth/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Kubiks
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
301
packages/otel-better-auth/README.md
Normal file
301
packages/otel-better-auth/README.md
Normal file
@@ -0,0 +1,301 @@
|
||||
# @kubiks/otel-better-auth
|
||||
|
||||
OpenTelemetry instrumentation for [Better Auth](https://better-auth.com/). Add distributed tracing to your authentication flows with a single line of code.
|
||||
|
||||
## 🚀 Features
|
||||
|
||||
- **🔌 Plugin-based**: Clean integration using Better Auth's native plugin system
|
||||
- **📊 Comprehensive Coverage**: Traces all auth operations (signup, signin, OAuth, password reset, etc.)
|
||||
- **🎯 Semantic Conventions**: Follows OpenTelemetry standards with meaningful attributes
|
||||
- **🔐 Privacy-First**: Email capture is opt-in by default
|
||||
- **⚡ Zero Config**: Works out of the box with sensible defaults
|
||||
- **🎨 Rich Telemetry**: Captures user IDs, session IDs, auth methods, and success/failure status
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @kubiks/otel-better-auth
|
||||
# or
|
||||
pnpm add @kubiks/otel-better-auth
|
||||
# or
|
||||
yarn add @kubiks/otel-better-auth
|
||||
```
|
||||
|
||||
**Peer Dependencies:** `@opentelemetry/api` >= 1.9.0, `better-auth` >= 0.1.0
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Setup (One Line!)
|
||||
|
||||
Simply add the plugin to your Better Auth configuration:
|
||||
|
||||
```typescript
|
||||
import { betterAuth } from "better-auth";
|
||||
import { otelPlugin } from "@kubiks/otel-better-auth";
|
||||
|
||||
export const auth = betterAuth({
|
||||
database: db,
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
},
|
||||
}).use(otelPlugin());
|
||||
|
||||
// That's it! All auth operations are now traced automatically ✨
|
||||
```
|
||||
|
||||
### With Custom Configuration
|
||||
|
||||
```typescript
|
||||
import { betterAuth } from "better-auth";
|
||||
import { otelPlugin } from "@kubiks/otel-better-auth";
|
||||
|
||||
export const auth = betterAuth({
|
||||
database: db,
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
},
|
||||
socialProviders: {
|
||||
google: {
|
||||
clientId: process.env.GOOGLE_CLIENT_ID,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||
},
|
||||
github: {
|
||||
clientId: process.env.GITHUB_CLIENT_ID,
|
||||
clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
||||
},
|
||||
},
|
||||
}).use(
|
||||
otelPlugin({
|
||||
tracerName: "my-app-auth", // Custom tracer name
|
||||
captureEmail: true, // Include email addresses in traces (default: false)
|
||||
captureErrors: true, // Capture detailed error messages (default: true)
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
### Full Example with OpenTelemetry Setup
|
||||
|
||||
```typescript
|
||||
// instrumentation.ts
|
||||
import { NodeSDK } from "@opentelemetry/sdk-node";
|
||||
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
|
||||
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
||||
|
||||
const sdk = new NodeSDK({
|
||||
traceExporter: new OTLPTraceExporter({
|
||||
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
|
||||
}),
|
||||
instrumentations: [getNodeAutoInstrumentations()],
|
||||
});
|
||||
|
||||
sdk.start();
|
||||
|
||||
// auth.ts
|
||||
import { betterAuth } from "better-auth";
|
||||
import { otelPlugin } from "@kubiks/otel-better-auth";
|
||||
import { drizzle } from "drizzle-orm/node-postgres";
|
||||
import { Pool } from "pg";
|
||||
|
||||
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
||||
const db = drizzle(pool);
|
||||
|
||||
export const auth = betterAuth({
|
||||
database: db,
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
},
|
||||
socialProviders: {
|
||||
google: {
|
||||
clientId: process.env.GOOGLE_CLIENT_ID!,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
||||
},
|
||||
},
|
||||
}).use(otelPlugin());
|
||||
|
||||
// app.ts
|
||||
import "./instrumentation"; // Must be imported first!
|
||||
import { auth } from "./auth";
|
||||
|
||||
// Your auth is now fully instrumented!
|
||||
```
|
||||
|
||||
## What You Get
|
||||
|
||||
The plugin automatically traces the following authentication events:
|
||||
|
||||
### 📝 Signup Operations
|
||||
|
||||
- Email/password signup
|
||||
- OAuth provider signup
|
||||
- Magic link signup
|
||||
- Automatic user ID capture
|
||||
|
||||
**Span name**: `auth.signup` or `auth.signup.email`
|
||||
|
||||
### 🔑 Signin Operations
|
||||
|
||||
- Email/password signin
|
||||
- OAuth provider signin (Google, GitHub, Facebook, etc.)
|
||||
- Magic link signin
|
||||
- Session creation tracking
|
||||
|
||||
**Span name**: `auth.signin`, `auth.signin.email`, or `auth.oauth.{provider}`
|
||||
|
||||
### 🔒 Password Management
|
||||
|
||||
- Forgot password requests
|
||||
- Password reset flows
|
||||
- Email verification
|
||||
|
||||
**Span names**: `auth.forgot_password`, `auth.reset_password`, `auth.verify_email`
|
||||
|
||||
### 🚪 Signout
|
||||
|
||||
- Session termination tracking
|
||||
|
||||
**Span name**: `auth.signout`
|
||||
|
||||
### 🌐 OAuth Flows
|
||||
|
||||
- Automatic provider detection (Google, GitHub, Facebook, etc.)
|
||||
- Callback tracking
|
||||
- Success/failure monitoring
|
||||
|
||||
**Span name**: `auth.oauth.{provider}`
|
||||
|
||||
## Span Attributes
|
||||
|
||||
Each traced operation includes rich telemetry data following OpenTelemetry semantic conventions:
|
||||
|
||||
| Attribute | Description | Example |
|
||||
| ------------------ | ------------------------------ | ------------------------ |
|
||||
| `auth.operation` | Type of auth operation | `signin`, `signup` |
|
||||
| `auth.method` | Authentication method | `password`, `oauth` |
|
||||
| `auth.provider` | OAuth provider (if applicable) | `google`, `github` |
|
||||
| `auth.success` | Whether operation succeeded | `true`, `false` |
|
||||
| `auth.error` | Error message (if failed) | `Invalid credentials` |
|
||||
| `user.id` | User identifier | `user_123456` |
|
||||
| `user.email` | User email (opt-in) | `user@example.com` |
|
||||
| `session.id` | Session identifier | `session_789012` |
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### `tracerName`
|
||||
|
||||
- **Type**: `string`
|
||||
- **Default**: `"@kubiks/otel-better-auth"`
|
||||
- **Description**: Custom name for the tracer
|
||||
|
||||
### `captureEmail`
|
||||
|
||||
- **Type**: `boolean`
|
||||
- **Default**: `false`
|
||||
- **Description**: Whether to include user email addresses in span attributes. **Note**: Email addresses are PII (Personally Identifiable Information). Only enable this if your tracing backend is compliant with your privacy requirements.
|
||||
|
||||
### `captureErrors`
|
||||
|
||||
- **Type**: `boolean`
|
||||
- **Default**: `true`
|
||||
- **Description**: Whether to capture detailed error messages in spans
|
||||
|
||||
### `tracer`
|
||||
|
||||
- **Type**: `Tracer`
|
||||
- **Default**: `undefined`
|
||||
- **Description**: Custom OpenTelemetry tracer instance. If not provided, the plugin will obtain a tracer using `trace.getTracer(tracerName)`.
|
||||
|
||||
## Privacy & Security
|
||||
|
||||
By default, the plugin is designed with privacy in mind:
|
||||
|
||||
- ✅ User emails are **NOT** captured by default
|
||||
- ✅ Passwords are **NEVER** captured
|
||||
- ✅ Only operation metadata and success/failure status are traced
|
||||
- ⚠️ Enable `captureEmail: true` only if your infrastructure is compliant with privacy regulations (GDPR, CCPA, etc.)
|
||||
|
||||
## Architecture
|
||||
|
||||
The plugin leverages Better Auth's powerful plugin API to hook into:
|
||||
|
||||
1. **Lifecycle Hooks**: `user.create`, `session.create` for core auth events
|
||||
2. **Endpoint Hooks**: All auth endpoints (`signInEmail`, `signUpEmail`, `forgetPassword`, etc.)
|
||||
3. **Request/Response Hooks**: For OAuth callback detection and tracing
|
||||
|
||||
This provides comprehensive coverage of all authentication flows without any code changes to your application.
|
||||
|
||||
## Visualizing Traces
|
||||
|
||||
When integrated with a tracing backend (Jaeger, Zipkin, Honeycomb, Datadog, etc.), you'll see:
|
||||
|
||||
- 📊 End-to-end auth flow visualization
|
||||
- ⏱️ Performance metrics for each auth operation
|
||||
- 🔍 Detailed attributes for debugging
|
||||
- 🚨 Error tracking with stack traces
|
||||
- 📈 Success/failure rates across auth methods
|
||||
|
||||
## Examples
|
||||
|
||||
### Next.js App Router
|
||||
|
||||
```typescript
|
||||
// app/api/auth/[...all]/route.ts
|
||||
import { auth } from "@/lib/auth";
|
||||
import { toNextJsHandler } from "better-auth/next-js";
|
||||
|
||||
export const { GET, POST } = toNextJsHandler(auth.handler);
|
||||
```
|
||||
|
||||
### Express
|
||||
|
||||
```typescript
|
||||
import express from "express";
|
||||
import { auth } from "./auth";
|
||||
|
||||
const app = express();
|
||||
|
||||
app.all("/api/auth/*", auth.handler);
|
||||
|
||||
app.listen(3000);
|
||||
```
|
||||
|
||||
### SvelteKit
|
||||
|
||||
```typescript
|
||||
// src/hooks.server.ts
|
||||
import { auth } from "$lib/auth";
|
||||
import { svelteKitHandler } from "better-auth/svelte-kit";
|
||||
|
||||
export const handle = svelteKitHandler(auth);
|
||||
```
|
||||
|
||||
## Compatibility
|
||||
|
||||
- ✅ Works with all Better Auth adapters (Drizzle, Prisma, Kysely, etc.)
|
||||
- ✅ Compatible with all Better Auth plugins
|
||||
- ✅ Framework agnostic (Next.js, Express, SvelteKit, etc.)
|
||||
- ✅ Supports all authentication methods (email/password, OAuth, magic link)
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Initialize OpenTelemetry early**: Import your instrumentation file before any other code
|
||||
2. **Use environment-based configuration**: Enable `captureEmail` only in development/staging
|
||||
3. **Combine with other instrumentation**: Use alongside `@kubiks/otel-drizzle` for database query tracing
|
||||
4. **Monitor performance**: Set up alerts for slow auth operations or high failure rates
|
||||
5. **Respect privacy**: Be mindful of what PII you capture in production traces
|
||||
|
||||
## Related Packages
|
||||
|
||||
- [`@kubiks/otel-drizzle`](https://www.npmjs.com/package/@kubiks/otel-drizzle) - OpenTelemetry instrumentation for Drizzle ORM
|
||||
- [`better-auth`](https://better-auth.com/) - The best authentication library for TypeScript
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions! Please check out our [GitHub repository](https://github.com/kubiks-inc/otel) for issues and pull requests.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
---
|
||||
|
||||
Made with ❤️ by [Kubiks](https://github.com/kubiks-inc)
|
||||
70
packages/otel-better-auth/build.ts
Normal file
70
packages/otel-better-auth/build.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { stat } from "node:fs/promises";
|
||||
import type { Plugin } from "esbuild";
|
||||
import { build } from "esbuild";
|
||||
|
||||
const MINIFY = true;
|
||||
const SOURCEMAP = true;
|
||||
|
||||
const MAX_SIZE = 50_000; // 50KB max for instrumentation package
|
||||
|
||||
type ExternalPluginFactory = (external: string[]) => Plugin;
|
||||
const externalCjsToEsmPlugin: ExternalPluginFactory = (external) => ({
|
||||
name: "external",
|
||||
setup(builder): void {
|
||||
const escape = (text: string): string =>
|
||||
`^${text.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")}$`;
|
||||
const filter = new RegExp(external.map(escape).join("|"));
|
||||
builder.onResolve({ filter: /.*/, namespace: "external" }, (args) => ({
|
||||
path: args.path,
|
||||
external: true,
|
||||
}));
|
||||
builder.onResolve({ filter }, (args) => ({
|
||||
path: args.path,
|
||||
namespace: "external",
|
||||
}));
|
||||
builder.onLoad({ filter: /.*/, namespace: "external" }, (args) => ({
|
||||
contents: `export * from ${JSON.stringify(args.path)}`,
|
||||
}));
|
||||
},
|
||||
});
|
||||
|
||||
/** Adds support for require, __filename, and __dirname to ESM / Node. */
|
||||
const esmNodeSupportBanner = {
|
||||
js: `import { fileURLToPath } from 'url';
|
||||
import { createRequire as topLevelCreateRequire } from 'module';
|
||||
import _nPath from 'path'
|
||||
const require = topLevelCreateRequire(import.meta.url);
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = _nPath.dirname(__filename);`,
|
||||
};
|
||||
|
||||
const peerDependencies = ["@opentelemetry/api", "better-auth"];
|
||||
|
||||
async function buildAll(): Promise<void> {
|
||||
await build({
|
||||
platform: "node",
|
||||
format: "esm",
|
||||
splitting: false,
|
||||
entryPoints: ["src/index.ts"],
|
||||
outdir: "dist",
|
||||
bundle: true,
|
||||
minify: MINIFY,
|
||||
sourcemap: SOURCEMAP,
|
||||
banner: esmNodeSupportBanner,
|
||||
external: peerDependencies,
|
||||
plugins: [externalCjsToEsmPlugin(peerDependencies)],
|
||||
});
|
||||
|
||||
// Check max size.
|
||||
const outputFile = "dist/index.js";
|
||||
const s = await stat(outputFile);
|
||||
if (s.size > MAX_SIZE) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(
|
||||
`${outputFile}: the size of ${s.size} is over the maximum allowed size of ${MAX_SIZE}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void buildAll();
|
||||
58
packages/otel-better-auth/package.json
Normal file
58
packages/otel-better-auth/package.json
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"name": "@kubiks/otel-better-auth",
|
||||
"version": "1.0.0",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"description": "OpenTelemetry instrumentation for Better Auth - Add distributed tracing to your authentication flows with a single line of code",
|
||||
"author": "Kubiks",
|
||||
"license": "MIT",
|
||||
"repository": "kubiks-inc/otel",
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.19.0 || >=20.6.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pnpm clean && pnpm build-only && pnpm build-types",
|
||||
"build-only": "pnpm tsx build.ts",
|
||||
"build-types": "tsc --skipLibCheck --noEmit false --declaration --emitDeclarationOnly --stripInternal --declarationDir dist/types src/index.ts",
|
||||
"clean": "rimraf dist",
|
||||
"prepublishOnly": "pnpm build",
|
||||
"type-check": "tsc --noEmit",
|
||||
"unit-test": "vitest --run",
|
||||
"unit-test-watch": "vitest"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/sdk-trace-base": "^2.1.0",
|
||||
"@opentelemetry/sdk-trace-node": "^1.28.0",
|
||||
"@types/node": "18.15.11",
|
||||
"better-auth": "^1.0.0",
|
||||
"esbuild": "^0.19.4",
|
||||
"rimraf": "3.0.2",
|
||||
"tsx": "^4.6.2",
|
||||
"typescript": "^5",
|
||||
"vitest": "0.33.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.9.0 <2.0.0",
|
||||
"better-auth": ">=0.1.0"
|
||||
}
|
||||
}
|
||||
358
packages/otel-better-auth/src/index.test.ts
Normal file
358
packages/otel-better-auth/src/index.test.ts
Normal file
@@ -0,0 +1,358 @@
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
||||
import { trace, SpanStatusCode } from "@opentelemetry/api";
|
||||
import { InMemorySpanExporter } from "@opentelemetry/sdk-trace-base";
|
||||
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
||||
import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
||||
import {
|
||||
otelPlugin,
|
||||
SEMATTRS_AUTH_OPERATION,
|
||||
SEMATTRS_AUTH_METHOD,
|
||||
SEMATTRS_AUTH_PROVIDER,
|
||||
SEMATTRS_USER_ID,
|
||||
SEMATTRS_USER_EMAIL,
|
||||
SEMATTRS_AUTH_SUCCESS,
|
||||
} from "./index.js";
|
||||
|
||||
describe("otel-better-auth", () => {
|
||||
let exporter: InMemorySpanExporter;
|
||||
let provider: NodeTracerProvider;
|
||||
|
||||
beforeEach(() => {
|
||||
exporter = new InMemorySpanExporter();
|
||||
provider = new NodeTracerProvider();
|
||||
provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
|
||||
provider.register();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
exporter.reset();
|
||||
provider.shutdown();
|
||||
});
|
||||
|
||||
describe("otelPlugin", () => {
|
||||
it("should create a plugin with correct id", () => {
|
||||
const plugin = otelPlugin();
|
||||
expect(plugin.id).toBe("otel");
|
||||
});
|
||||
|
||||
it("should have before and after hooks", () => {
|
||||
const plugin = otelPlugin();
|
||||
expect(plugin.hooks?.before).toBeDefined();
|
||||
expect(plugin.hooks?.after).toBeDefined();
|
||||
expect(Array.isArray(plugin.hooks?.before)).toBe(true);
|
||||
expect(Array.isArray(plugin.hooks?.after)).toBe(true);
|
||||
});
|
||||
|
||||
it("should have multiple before hooks for different endpoints", () => {
|
||||
const plugin = otelPlugin();
|
||||
expect(plugin.hooks?.before?.length).toBeGreaterThan(3);
|
||||
});
|
||||
|
||||
it("should have after hook for span finalization", () => {
|
||||
const plugin = otelPlugin();
|
||||
expect(plugin.hooks?.after?.length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Hook matchers", () => {
|
||||
it("should match signup endpoints", () => {
|
||||
const plugin = otelPlugin();
|
||||
const signupHook = plugin.hooks?.before?.find(h => {
|
||||
return h.matcher({ path: "/sign-up/email", request: {} } as any);
|
||||
});
|
||||
expect(signupHook).toBeDefined();
|
||||
});
|
||||
|
||||
it("should match signin endpoints", () => {
|
||||
const plugin = otelPlugin();
|
||||
const signinHook = plugin.hooks?.before?.find(h => {
|
||||
return h.matcher({ path: "/sign-in/email", request: {} } as any);
|
||||
});
|
||||
expect(signinHook).toBeDefined();
|
||||
});
|
||||
|
||||
it("should match forgot password endpoints", () => {
|
||||
const plugin = otelPlugin();
|
||||
const forgotHook = plugin.hooks?.before?.find(h => {
|
||||
return h.matcher({ path: "/forget-password", request: {} } as any);
|
||||
});
|
||||
expect(forgotHook).toBeDefined();
|
||||
});
|
||||
|
||||
it("should match OAuth callback endpoints", () => {
|
||||
const plugin = otelPlugin();
|
||||
const oauthHook = plugin.hooks?.before?.find(h => {
|
||||
return h.matcher({ path: "/callback/google", request: {} } as any);
|
||||
});
|
||||
expect(oauthHook).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Configuration", () => {
|
||||
it("should use custom tracer name", () => {
|
||||
const customName = "my-custom-auth-tracer";
|
||||
const plugin = otelPlugin({ tracerName: customName });
|
||||
expect(plugin.id).toBe("otel");
|
||||
});
|
||||
|
||||
it("should accept custom tracer instance", () => {
|
||||
const customTracer = trace.getTracer("custom-tracer");
|
||||
const plugin = otelPlugin({ tracer: customTracer });
|
||||
expect(plugin.id).toBe("otel");
|
||||
});
|
||||
|
||||
it("should respect captureEmail setting", () => {
|
||||
const plugin = otelPlugin({ captureEmail: true });
|
||||
expect(plugin.id).toBe("otel");
|
||||
});
|
||||
|
||||
it("should respect captureErrors setting", () => {
|
||||
const plugin = otelPlugin({ captureErrors: false });
|
||||
expect(plugin.id).toBe("otel");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Span creation", () => {
|
||||
it("should create span for signup", async () => {
|
||||
const plugin = otelPlugin();
|
||||
const signupHook = plugin.hooks?.before?.find(h => {
|
||||
return h.matcher({ path: "/sign-up/email", request: {} } as any);
|
||||
});
|
||||
|
||||
const ctx = {
|
||||
path: "/sign-up/email",
|
||||
body: { email: "test@example.com" },
|
||||
request: {},
|
||||
};
|
||||
|
||||
await signupHook?.handler(ctx as any);
|
||||
|
||||
// Span is created but not finalized yet
|
||||
expect((ctx as any).__otelSpan).toBeDefined();
|
||||
});
|
||||
|
||||
it("should capture email when enabled", async () => {
|
||||
const plugin = otelPlugin({ captureEmail: true });
|
||||
const signupHook = plugin.hooks?.before?.find(h => {
|
||||
return h.matcher({ path: "/sign-up/email", request: {} } as any);
|
||||
});
|
||||
|
||||
const ctx = {
|
||||
path: "/sign-up/email",
|
||||
body: { email: "test@example.com" },
|
||||
request: {},
|
||||
};
|
||||
|
||||
await signupHook?.handler(ctx as any);
|
||||
|
||||
const span = (ctx as any).__otelSpan;
|
||||
expect(span).toBeDefined();
|
||||
});
|
||||
|
||||
it("should create span for OAuth callback", async () => {
|
||||
const plugin = otelPlugin();
|
||||
const oauthHook = plugin.hooks?.before?.find(h => {
|
||||
return h.matcher({ path: "/callback/google", request: {} } as any);
|
||||
});
|
||||
|
||||
const ctx = {
|
||||
path: "/callback/google?code=abc123",
|
||||
request: {},
|
||||
};
|
||||
|
||||
await oauthHook?.handler(ctx as any);
|
||||
|
||||
expect((ctx as any).__otelSpan).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Span creation and finalization", () => {
|
||||
it("should attach span to context in before hook", async () => {
|
||||
const plugin = otelPlugin();
|
||||
const signupHook = plugin.hooks?.before?.find(h => {
|
||||
return h.matcher({ path: "/sign-up/email", request: {} } as any);
|
||||
});
|
||||
|
||||
const ctx = {
|
||||
path: "/sign-up/email",
|
||||
body: { email: "test@example.com" },
|
||||
request: {},
|
||||
returned: { status: 200 },
|
||||
};
|
||||
|
||||
await signupHook?.handler(ctx as any);
|
||||
|
||||
// Verify span was attached
|
||||
expect((ctx as any).__otelSpan).toBeDefined();
|
||||
expect((ctx as any).__otelContext).toBeDefined();
|
||||
});
|
||||
|
||||
it("should cleanup span in after hook", async () => {
|
||||
const plugin = otelPlugin();
|
||||
const signupHook = plugin.hooks?.before?.find(h => {
|
||||
return h.matcher({ path: "/sign-up/email", request: {} } as any);
|
||||
});
|
||||
|
||||
const ctx = {
|
||||
path: "/sign-up/email",
|
||||
body: { email: "test@example.com" },
|
||||
request: {},
|
||||
returned: { status: 200 },
|
||||
};
|
||||
|
||||
await signupHook?.handler(ctx as any);
|
||||
|
||||
const afterHook = plugin.hooks?.after?.[0];
|
||||
await afterHook?.handler(ctx as any);
|
||||
|
||||
// Verify span was cleaned up
|
||||
expect((ctx as any).__otelSpan).toBeUndefined();
|
||||
expect((ctx as any).__otelContext).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should handle error contexts", async () => {
|
||||
const plugin = otelPlugin();
|
||||
const signinHook = plugin.hooks?.before?.find(h => {
|
||||
return h.matcher({ path: "/sign-in/email", request: {} } as any);
|
||||
});
|
||||
|
||||
const ctx = {
|
||||
path: "/sign-in/email",
|
||||
body: { email: "test@example.com" },
|
||||
request: {},
|
||||
error: new Error("Invalid credentials"),
|
||||
returned: { status: 401 },
|
||||
};
|
||||
|
||||
await signinHook?.handler(ctx as any);
|
||||
|
||||
const afterHook = plugin.hooks?.after?.[0];
|
||||
// Should not throw
|
||||
expect(async () => await afterHook?.handler(ctx as any)).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Semantic conventions", () => {
|
||||
it("should export semantic attribute constants", () => {
|
||||
expect(SEMATTRS_AUTH_OPERATION).toBe("auth.operation");
|
||||
expect(SEMATTRS_AUTH_METHOD).toBe("auth.method");
|
||||
expect(SEMATTRS_AUTH_PROVIDER).toBe("auth.provider");
|
||||
expect(SEMATTRS_USER_ID).toBe("user.id");
|
||||
expect(SEMATTRS_USER_EMAIL).toBe("user.email");
|
||||
expect(SEMATTRS_AUTH_SUCCESS).toBe("auth.success");
|
||||
});
|
||||
|
||||
it("should create spans with correct operation types", async () => {
|
||||
const plugin = otelPlugin({ captureEmail: true });
|
||||
const signupHook = plugin.hooks?.before?.find(h => {
|
||||
return h.matcher({ path: "/sign-up/email", request: {} } as any);
|
||||
});
|
||||
|
||||
const ctx = {
|
||||
path: "/sign-up/email",
|
||||
body: { email: "test@example.com" },
|
||||
request: {},
|
||||
};
|
||||
|
||||
await signupHook?.handler(ctx as any);
|
||||
|
||||
// Span should be created and attached
|
||||
expect((ctx as any).__otelSpan).toBeDefined();
|
||||
});
|
||||
|
||||
it("should support different OAuth providers", async () => {
|
||||
const plugin = otelPlugin();
|
||||
const providers = ["google", "github", "facebook"];
|
||||
|
||||
for (const provider of providers) {
|
||||
const hook = plugin.hooks?.before?.find(h => {
|
||||
return h.matcher({ path: `/callback/${provider}`, request: {} } as any);
|
||||
});
|
||||
|
||||
const ctx = {
|
||||
path: `/callback/${provider}?code=abc`,
|
||||
request: {},
|
||||
};
|
||||
|
||||
await hook?.handler(ctx as any);
|
||||
|
||||
expect((ctx as any).__otelSpan).toBeDefined();
|
||||
|
||||
const afterHook = plugin.hooks?.after?.[0];
|
||||
await afterHook?.handler(ctx as any);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Multiple operations", () => {
|
||||
it("should handle multiple auth operations sequentially", async () => {
|
||||
const plugin = otelPlugin();
|
||||
|
||||
// Simulate multiple auth operations
|
||||
const operations = [
|
||||
{ path: "/sign-up/email", matcher: "signup" },
|
||||
{ path: "/sign-in/email", matcher: "signin" },
|
||||
{ path: "/forget-password", matcher: "forgot" },
|
||||
];
|
||||
|
||||
for (const op of operations) {
|
||||
const hook = plugin.hooks?.before?.find(h => {
|
||||
return h.matcher({ path: op.path, request: {} } as any);
|
||||
});
|
||||
|
||||
expect(hook).toBeDefined();
|
||||
|
||||
const ctx = {
|
||||
path: op.path,
|
||||
body: { email: "test@example.com" },
|
||||
request: {},
|
||||
returned: { status: 200 },
|
||||
};
|
||||
|
||||
await hook?.handler(ctx as any);
|
||||
expect((ctx as any).__otelSpan).toBeDefined();
|
||||
|
||||
await plugin.hooks?.after?.[0].handler(ctx as any);
|
||||
expect((ctx as any).__otelSpan).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
it("should handle concurrent operations", async () => {
|
||||
const plugin = otelPlugin();
|
||||
|
||||
const operations = [
|
||||
{ path: "/sign-up/email" },
|
||||
{ path: "/sign-in/email" },
|
||||
{ path: "/sign-out" },
|
||||
];
|
||||
|
||||
const promises = operations.map(async (op) => {
|
||||
const hook = plugin.hooks?.before?.find(h => {
|
||||
return h.matcher({ path: op.path, request: {} } as any);
|
||||
});
|
||||
|
||||
const ctx = {
|
||||
path: op.path,
|
||||
body: { email: "test@example.com" },
|
||||
request: {},
|
||||
returned: { status: 200 },
|
||||
};
|
||||
|
||||
await hook?.handler(ctx as any);
|
||||
await plugin.hooks?.after?.[0].handler(ctx as any);
|
||||
|
||||
return ctx;
|
||||
});
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
expect(results).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Default export", () => {
|
||||
it("should export otelPlugin as default", async () => {
|
||||
const { default: defaultExport } = await import("./index.js");
|
||||
expect(defaultExport).toBe(otelPlugin);
|
||||
});
|
||||
});
|
||||
});
|
||||
351
packages/otel-better-auth/src/index.ts
Normal file
351
packages/otel-better-auth/src/index.ts
Normal file
@@ -0,0 +1,351 @@
|
||||
import {
|
||||
context,
|
||||
SpanKind,
|
||||
SpanStatusCode,
|
||||
trace,
|
||||
type Span,
|
||||
type Tracer,
|
||||
} from "@opentelemetry/api";
|
||||
import type { BetterAuthPlugin } from "better-auth/plugins";
|
||||
|
||||
const DEFAULT_TRACER_NAME = "@kubiks/otel-better-auth";
|
||||
|
||||
// Semantic conventions for auth attributes
|
||||
export const SEMATTRS_AUTH_OPERATION = "auth.operation";
|
||||
export const SEMATTRS_AUTH_METHOD = "auth.method";
|
||||
export const SEMATTRS_AUTH_PROVIDER = "auth.provider";
|
||||
export const SEMATTRS_USER_ID = "user.id";
|
||||
export const SEMATTRS_USER_EMAIL = "user.email";
|
||||
export const SEMATTRS_SESSION_ID = "session.id";
|
||||
export const SEMATTRS_AUTH_SUCCESS = "auth.success";
|
||||
export const SEMATTRS_AUTH_ERROR = "auth.error";
|
||||
|
||||
/**
|
||||
* Configuration options for Better Auth OpenTelemetry instrumentation.
|
||||
*/
|
||||
export interface OtelBetterAuthConfig {
|
||||
/**
|
||||
* Custom tracer name. Defaults to "@kubiks/otel-better-auth".
|
||||
*/
|
||||
tracerName?: string;
|
||||
|
||||
/**
|
||||
* Whether to capture user email in spans.
|
||||
* Defaults to false for privacy.
|
||||
*/
|
||||
captureEmail?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to capture detailed error messages in spans.
|
||||
* Defaults to true.
|
||||
*/
|
||||
captureErrors?: boolean;
|
||||
|
||||
/**
|
||||
* Custom tracer instance. If not provided, will use trace.getTracer().
|
||||
*/
|
||||
tracer?: Tracer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalizes a span with status, timing, and optional error.
|
||||
*/
|
||||
function finalizeSpan(span: Span, error?: unknown, success = true): void {
|
||||
span.setAttribute(SEMATTRS_AUTH_SUCCESS, success);
|
||||
|
||||
if (error) {
|
||||
if (error instanceof Error) {
|
||||
span.recordException(error);
|
||||
span.setAttribute(SEMATTRS_AUTH_ERROR, error.message);
|
||||
} else {
|
||||
const errorMsg = String(error);
|
||||
span.recordException(new Error(errorMsg));
|
||||
span.setAttribute(SEMATTRS_AUTH_ERROR, errorMsg);
|
||||
}
|
||||
span.setStatus({ code: SpanStatusCode.ERROR });
|
||||
} else {
|
||||
span.setStatus({ code: SpanStatusCode.OK });
|
||||
}
|
||||
span.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Better Auth plugin that adds OpenTelemetry tracing to all auth operations.
|
||||
*
|
||||
* This plugin automatically instruments key authentication events including:
|
||||
* - User signup (password, OAuth, magic link, etc.)
|
||||
* - User signin (all methods)
|
||||
* - Password reset flows
|
||||
* - Email verification
|
||||
* - Session creation and management
|
||||
*
|
||||
* @param config - Optional configuration for instrumentation behavior
|
||||
* @returns A Better Auth plugin that can be added via .use()
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { betterAuth } from "better-auth";
|
||||
* import { otelPlugin } from "@kubiks/otel-better-auth";
|
||||
*
|
||||
* export const auth = betterAuth({
|
||||
* database: db,
|
||||
* // ... other config
|
||||
* }).use(otelPlugin());
|
||||
*
|
||||
* // Or with custom configuration
|
||||
* export const auth = betterAuth({
|
||||
* database: db,
|
||||
* }).use(otelPlugin({
|
||||
* tracerName: "my-app-auth",
|
||||
* captureEmail: true,
|
||||
* captureErrors: true,
|
||||
* }));
|
||||
* ```
|
||||
*/
|
||||
export function otelPlugin(config?: OtelBetterAuthConfig): BetterAuthPlugin {
|
||||
const {
|
||||
tracerName = DEFAULT_TRACER_NAME,
|
||||
captureEmail = false,
|
||||
captureErrors = true,
|
||||
tracer: customTracer,
|
||||
} = config ?? {};
|
||||
|
||||
const tracer = customTracer ?? trace.getTracer(tracerName);
|
||||
|
||||
return {
|
||||
id: "otel",
|
||||
hooks: {
|
||||
before: [
|
||||
{
|
||||
matcher: (ctx) => {
|
||||
// Match signup endpoints
|
||||
return (
|
||||
ctx.path === "/sign-up/email" ||
|
||||
ctx.path === "/sign-up" ||
|
||||
ctx.request?.url?.includes("/sign-up")
|
||||
);
|
||||
},
|
||||
handler: async (ctx) => {
|
||||
const attributes: Record<string, string | boolean> = {
|
||||
[SEMATTRS_AUTH_OPERATION]: "signup",
|
||||
[SEMATTRS_AUTH_METHOD]: "password",
|
||||
};
|
||||
|
||||
if (captureEmail && (ctx.body as any)?.email) {
|
||||
attributes[SEMATTRS_USER_EMAIL] = (ctx.body as any).email;
|
||||
}
|
||||
|
||||
const span = tracer.startSpan("auth.signup.email", {
|
||||
kind: SpanKind.INTERNAL,
|
||||
attributes,
|
||||
});
|
||||
|
||||
const activeContext = trace.setSpan(context.active(), span);
|
||||
(ctx as any).__otelSpan = span;
|
||||
(ctx as any).__otelContext = activeContext;
|
||||
|
||||
return ctx;
|
||||
},
|
||||
},
|
||||
{
|
||||
matcher: (ctx) => {
|
||||
// Match signin endpoints
|
||||
return (
|
||||
ctx.path === "/sign-in/email" ||
|
||||
ctx.path === "/sign-in" ||
|
||||
ctx.request?.url?.includes("/sign-in")
|
||||
);
|
||||
},
|
||||
handler: async (ctx) => {
|
||||
const attributes: Record<string, string | boolean> = {
|
||||
[SEMATTRS_AUTH_OPERATION]: "signin",
|
||||
[SEMATTRS_AUTH_METHOD]: "password",
|
||||
};
|
||||
|
||||
if (captureEmail && (ctx.body as any)?.email) {
|
||||
attributes[SEMATTRS_USER_EMAIL] = (ctx.body as any).email;
|
||||
}
|
||||
|
||||
const span = tracer.startSpan("auth.signin.email", {
|
||||
kind: SpanKind.INTERNAL,
|
||||
attributes,
|
||||
});
|
||||
|
||||
const activeContext = trace.setSpan(context.active(), span);
|
||||
(ctx as any).__otelSpan = span;
|
||||
(ctx as any).__otelContext = activeContext;
|
||||
|
||||
return ctx;
|
||||
},
|
||||
},
|
||||
{
|
||||
matcher: (ctx) => {
|
||||
// Match forgot password endpoints
|
||||
return (
|
||||
ctx.path === "/forget-password" ||
|
||||
ctx.request?.url?.includes("/forget-password")
|
||||
);
|
||||
},
|
||||
handler: async (ctx) => {
|
||||
const attributes: Record<string, string> = {
|
||||
[SEMATTRS_AUTH_OPERATION]: "forgot_password",
|
||||
};
|
||||
|
||||
if (captureEmail && (ctx.body as any)?.email) {
|
||||
attributes[SEMATTRS_USER_EMAIL] = (ctx.body as any).email;
|
||||
}
|
||||
|
||||
const span = tracer.startSpan("auth.forgot_password", {
|
||||
kind: SpanKind.INTERNAL,
|
||||
attributes,
|
||||
});
|
||||
|
||||
const activeContext = trace.setSpan(context.active(), span);
|
||||
(ctx as any).__otelSpan = span;
|
||||
(ctx as any).__otelContext = activeContext;
|
||||
|
||||
return ctx;
|
||||
},
|
||||
},
|
||||
{
|
||||
matcher: (ctx) => {
|
||||
// Match reset password endpoints
|
||||
return (
|
||||
ctx.path === "/reset-password" ||
|
||||
ctx.request?.url?.includes("/reset-password")
|
||||
);
|
||||
},
|
||||
handler: async (ctx) => {
|
||||
const attributes: Record<string, string> = {
|
||||
[SEMATTRS_AUTH_OPERATION]: "reset_password",
|
||||
};
|
||||
|
||||
const span = tracer.startSpan("auth.reset_password", {
|
||||
kind: SpanKind.INTERNAL,
|
||||
attributes,
|
||||
});
|
||||
|
||||
const activeContext = trace.setSpan(context.active(), span);
|
||||
(ctx as any).__otelSpan = span;
|
||||
(ctx as any).__otelContext = activeContext;
|
||||
|
||||
return ctx;
|
||||
},
|
||||
},
|
||||
{
|
||||
matcher: (ctx) => {
|
||||
// Match signout endpoints
|
||||
return (
|
||||
ctx.path === "/sign-out" || ctx.request?.url?.includes("/sign-out")
|
||||
);
|
||||
},
|
||||
handler: async (ctx) => {
|
||||
const attributes: Record<string, string> = {
|
||||
[SEMATTRS_AUTH_OPERATION]: "signout",
|
||||
};
|
||||
|
||||
const span = tracer.startSpan("auth.signout", {
|
||||
kind: SpanKind.INTERNAL,
|
||||
attributes,
|
||||
});
|
||||
|
||||
const activeContext = trace.setSpan(context.active(), span);
|
||||
(ctx as any).__otelSpan = span;
|
||||
(ctx as any).__otelContext = activeContext;
|
||||
|
||||
return ctx;
|
||||
},
|
||||
},
|
||||
{
|
||||
matcher: (ctx) => {
|
||||
// Match verify email endpoints
|
||||
return (
|
||||
ctx.path === "/verify-email" ||
|
||||
ctx.request?.url?.includes("/verify-email")
|
||||
);
|
||||
},
|
||||
handler: async (ctx) => {
|
||||
const attributes: Record<string, string> = {
|
||||
[SEMATTRS_AUTH_OPERATION]: "verify_email",
|
||||
};
|
||||
|
||||
const span = tracer.startSpan("auth.verify_email", {
|
||||
kind: SpanKind.INTERNAL,
|
||||
attributes,
|
||||
});
|
||||
|
||||
const activeContext = trace.setSpan(context.active(), span);
|
||||
(ctx as any).__otelSpan = span;
|
||||
(ctx as any).__otelContext = activeContext;
|
||||
|
||||
return ctx;
|
||||
},
|
||||
},
|
||||
{
|
||||
matcher: (ctx) => {
|
||||
// Match OAuth callback endpoints
|
||||
return (
|
||||
(ctx as any).path?.includes("/callback/") ||
|
||||
ctx.request?.url?.includes("/callback/")
|
||||
);
|
||||
},
|
||||
handler: async (ctx) => {
|
||||
const url = ctx.request?.url || (ctx as any).path;
|
||||
const provider = url?.split("/callback/")[1]?.split("/")[0]?.split("?")[0];
|
||||
|
||||
if (provider) {
|
||||
const attributes: Record<string, string> = {
|
||||
[SEMATTRS_AUTH_OPERATION]: "signin",
|
||||
[SEMATTRS_AUTH_METHOD]: "oauth",
|
||||
[SEMATTRS_AUTH_PROVIDER]: provider,
|
||||
};
|
||||
|
||||
const span = tracer.startSpan(`auth.oauth.${provider}`, {
|
||||
kind: SpanKind.INTERNAL,
|
||||
attributes,
|
||||
});
|
||||
|
||||
const activeContext = trace.setSpan(context.active(), span);
|
||||
(ctx as any).__otelSpan = span;
|
||||
(ctx as any).__otelContext = activeContext;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
},
|
||||
},
|
||||
],
|
||||
after: [
|
||||
{
|
||||
matcher: () => true, // Match all requests
|
||||
handler: async (ctx) => {
|
||||
const span = (ctx as any).__otelSpan;
|
||||
if (span) {
|
||||
const ctxAny = ctx as any;
|
||||
const success =
|
||||
!ctxAny.error &&
|
||||
(!ctxAny.returned ||
|
||||
(ctxAny.returned.status >= 200 && ctxAny.returned.status < 300));
|
||||
|
||||
// Add user/session info if available
|
||||
if (ctxAny.context?.session?.userId) {
|
||||
span.setAttribute(SEMATTRS_USER_ID, ctxAny.context.session.userId);
|
||||
}
|
||||
if (ctxAny.context?.session?.sessionId) {
|
||||
span.setAttribute(SEMATTRS_SESSION_ID, ctxAny.context.session.sessionId);
|
||||
}
|
||||
|
||||
finalizeSpan(span, ctxAny.error, success);
|
||||
delete (ctx as any).__otelSpan;
|
||||
delete (ctx as any).__otelContext;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Re-export for convenience
|
||||
export { otelPlugin as default };
|
||||
21
packages/otel-better-auth/tsconfig.json
Normal file
21
packages/otel-better-auth/tsconfig.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"lib": ["ES2020", "DOM"],
|
||||
"outDir": "dist",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"strict": false,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"incremental": true,
|
||||
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
||||
}
|
||||
492
pnpm-lock.yaml
generated
492
pnpm-lock.yaml
generated
@@ -21,6 +21,39 @@ importers:
|
||||
specifier: ^1.11.3
|
||||
version: 1.11.3
|
||||
|
||||
packages/otel-better-auth:
|
||||
devDependencies:
|
||||
'@opentelemetry/api':
|
||||
specifier: ^1.9.0
|
||||
version: 1.9.0
|
||||
'@opentelemetry/sdk-trace-base':
|
||||
specifier: ^2.1.0
|
||||
version: 2.1.0(@opentelemetry/api@1.9.0)
|
||||
'@opentelemetry/sdk-trace-node':
|
||||
specifier: ^1.28.0
|
||||
version: 1.30.1(@opentelemetry/api@1.9.0)
|
||||
'@types/node':
|
||||
specifier: 18.15.11
|
||||
version: 18.15.11
|
||||
better-auth:
|
||||
specifier: ^1.0.0
|
||||
version: 1.3.25
|
||||
esbuild:
|
||||
specifier: ^0.19.4
|
||||
version: 0.19.11
|
||||
rimraf:
|
||||
specifier: 3.0.2
|
||||
version: 3.0.2
|
||||
tsx:
|
||||
specifier: ^4.6.2
|
||||
version: 4.7.0
|
||||
typescript:
|
||||
specifier: ^5
|
||||
version: 5.3.3
|
||||
vitest:
|
||||
specifier: 0.33.0
|
||||
version: 0.33.0(less@4.2.0)(sass@1.69.7)(stylus@0.59.0)
|
||||
|
||||
packages/otel-drizzle:
|
||||
devDependencies:
|
||||
'@opentelemetry/api':
|
||||
@@ -37,7 +70,7 @@ importers:
|
||||
version: 8.15.5
|
||||
drizzle-orm:
|
||||
specifier: ^0.36.4
|
||||
version: 0.36.4(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(@types/react@18.2.46)(postgres@3.4.7)(react@18.2.0)
|
||||
version: 0.36.4(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(@types/react@18.2.46)(kysely@0.28.7)(postgres@3.4.7)(react@18.2.0)
|
||||
esbuild:
|
||||
specifier: ^0.19.4
|
||||
version: 0.19.11
|
||||
@@ -78,6 +111,15 @@ packages:
|
||||
resolution: {integrity: sha512-w06OXVOFso7LcbzMiDGt+3X7Rh7Ho8MmgPoWU3rarH+8upf+wSU/grlGbWzQyr3DkdN6ZeuMFjpdwW0Q+HxobA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@better-auth/core@1.3.25':
|
||||
resolution: {integrity: sha512-+Z09RkrpufX8+skD05pAoMSNo95Cl6nwfqrcEHfXJPaiSGJNXoo4/0GNtxZAgsW89xfRdsmbVAwwEd1AlCrHZA==}
|
||||
|
||||
'@better-auth/utils@0.3.0':
|
||||
resolution: {integrity: sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw==}
|
||||
|
||||
'@better-fetch/fetch@1.1.18':
|
||||
resolution: {integrity: sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA==}
|
||||
|
||||
'@changesets/apply-release-plan@7.0.0':
|
||||
resolution: {integrity: sha512-vfi69JR416qC9hWmFGSxj7N6wA5J222XNBmezSVATPWDVPIF7gkd4d8CpbEbXmRWbVrkoli3oerGS6dcL/BGsQ==}
|
||||
|
||||
@@ -409,6 +451,9 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@hexagon/base64@1.1.28':
|
||||
resolution: {integrity: sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==}
|
||||
|
||||
'@jest/schemas@29.6.3':
|
||||
resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
@@ -416,12 +461,23 @@ packages:
|
||||
'@jridgewell/sourcemap-codec@1.4.15':
|
||||
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
|
||||
|
||||
'@levischuck/tiny-cbor@0.2.11':
|
||||
resolution: {integrity: sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==}
|
||||
|
||||
'@manypkg/find-root@1.1.0':
|
||||
resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==}
|
||||
|
||||
'@manypkg/get-packages@1.1.3':
|
||||
resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==}
|
||||
|
||||
'@noble/ciphers@2.0.1':
|
||||
resolution: {integrity: sha512-xHK3XHPUW8DTAobU+G0XT+/w+JLM7/8k1UFdB5xg/zTFPnFCobhftzw8wl4Lw2aq/Rvir5pxfZV5fEazmeCJ2g==}
|
||||
engines: {node: '>= 20.19.0'}
|
||||
|
||||
'@noble/hashes@2.0.1':
|
||||
resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==}
|
||||
engines: {node: '>= 20.19.0'}
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -438,28 +494,117 @@ packages:
|
||||
resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
|
||||
'@opentelemetry/context-async-hooks@1.30.1':
|
||||
resolution: {integrity: sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': '>=1.0.0 <1.10.0'
|
||||
|
||||
'@opentelemetry/core@1.30.1':
|
||||
resolution: {integrity: sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': '>=1.0.0 <1.10.0'
|
||||
|
||||
'@opentelemetry/core@2.1.0':
|
||||
resolution: {integrity: sha512-RMEtHsxJs/GiHHxYT58IY57UXAQTuUnZVco6ymDEqTNlJKTimM4qPUPVe8InNFyBjhHBEAx4k3Q8LtNayBsbUQ==}
|
||||
engines: {node: ^18.19.0 || >=20.6.0}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': '>=1.0.0 <1.10.0'
|
||||
|
||||
'@opentelemetry/propagator-b3@1.30.1':
|
||||
resolution: {integrity: sha512-oATwWWDIJzybAZ4pO76ATN5N6FFbOA1otibAVlS8v90B4S1wClnhRUk7K+2CHAwN1JKYuj4jh/lpCEG5BAqFuQ==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': '>=1.0.0 <1.10.0'
|
||||
|
||||
'@opentelemetry/propagator-jaeger@1.30.1':
|
||||
resolution: {integrity: sha512-Pj/BfnYEKIOImirH76M4hDaBSx6HyZ2CXUqk+Kj02m6BB80c/yo4BdWkn/1gDFfU+YPY+bPR2U0DKBfdxCKwmg==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': '>=1.0.0 <1.10.0'
|
||||
|
||||
'@opentelemetry/resources@1.30.1':
|
||||
resolution: {integrity: sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': '>=1.0.0 <1.10.0'
|
||||
|
||||
'@opentelemetry/resources@2.1.0':
|
||||
resolution: {integrity: sha512-1CJjf3LCvoefUOgegxi8h6r4B/wLSzInyhGP2UmIBYNlo4Qk5CZ73e1eEyWmfXvFtm1ybkmfb2DqWvspsYLrWw==}
|
||||
engines: {node: ^18.19.0 || >=20.6.0}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': '>=1.3.0 <1.10.0'
|
||||
|
||||
'@opentelemetry/sdk-trace-base@1.30.1':
|
||||
resolution: {integrity: sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': '>=1.0.0 <1.10.0'
|
||||
|
||||
'@opentelemetry/sdk-trace-base@2.1.0':
|
||||
resolution: {integrity: sha512-uTX9FBlVQm4S2gVQO1sb5qyBLq/FPjbp+tmGoxu4tIgtYGmBYB44+KX/725RFDe30yBSaA9Ml9fqphe1hbUyLQ==}
|
||||
engines: {node: ^18.19.0 || >=20.6.0}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': '>=1.3.0 <1.10.0'
|
||||
|
||||
'@opentelemetry/sdk-trace-node@1.30.1':
|
||||
resolution: {integrity: sha512-cBjYOINt1JxXdpw1e5MlHmFRc5fgj4GW/86vsKFxJCJ8AL4PdVtYH41gWwl4qd4uQjqEL1oJVrXkSy5cnduAnQ==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': '>=1.0.0 <1.10.0'
|
||||
|
||||
'@opentelemetry/semantic-conventions@1.28.0':
|
||||
resolution: {integrity: sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
'@opentelemetry/semantic-conventions@1.37.0':
|
||||
resolution: {integrity: sha512-JD6DerIKdJGmRp4jQyX5FlrQjA4tjOw1cvfsPAZXfOOEErMUHjPcPSICS+6WnM0nB0efSFARh0KAZss+bvExOA==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
'@peculiar/asn1-android@2.5.0':
|
||||
resolution: {integrity: sha512-t8A83hgghWQkcneRsgGs2ebAlRe54ns88p7ouv8PW2tzF1nAW4yHcL4uZKrFpIU+uszIRzTkcCuie37gpkId0A==}
|
||||
|
||||
'@peculiar/asn1-cms@2.5.0':
|
||||
resolution: {integrity: sha512-p0SjJ3TuuleIvjPM4aYfvYw8Fk1Hn/zAVyPJZTtZ2eE9/MIer6/18ROxX6N/e6edVSfvuZBqhxAj3YgsmSjQ/A==}
|
||||
|
||||
'@peculiar/asn1-csr@2.5.0':
|
||||
resolution: {integrity: sha512-ioigvA6WSYN9h/YssMmmoIwgl3RvZlAYx4A/9jD2qaqXZwGcNlAxaw54eSx2QG1Yu7YyBC5Rku3nNoHrQ16YsQ==}
|
||||
|
||||
'@peculiar/asn1-ecc@2.5.0':
|
||||
resolution: {integrity: sha512-t4eYGNhXtLRxaP50h3sfO6aJebUCDGQACoeexcelL4roMFRRVgB20yBIu2LxsPh/tdW9I282gNgMOyg3ywg/mg==}
|
||||
|
||||
'@peculiar/asn1-pfx@2.5.0':
|
||||
resolution: {integrity: sha512-Vj0d0wxJZA+Ztqfb7W+/iu8Uasw6hhKtCdLKXLG/P3kEPIQpqGI4P4YXlROfl7gOCqFIbgsj1HzFIFwQ5s20ug==}
|
||||
|
||||
'@peculiar/asn1-pkcs8@2.5.0':
|
||||
resolution: {integrity: sha512-L7599HTI2SLlitlpEP8oAPaJgYssByI4eCwQq2C9eC90otFpm8MRn66PpbKviweAlhinWQ3ZjDD2KIVtx7PaVw==}
|
||||
|
||||
'@peculiar/asn1-pkcs9@2.5.0':
|
||||
resolution: {integrity: sha512-UgqSMBLNLR5TzEZ5ZzxR45Nk6VJrammxd60WMSkofyNzd3DQLSNycGWSK5Xg3UTYbXcDFyG8pA/7/y/ztVCa6A==}
|
||||
|
||||
'@peculiar/asn1-rsa@2.5.0':
|
||||
resolution: {integrity: sha512-qMZ/vweiTHy9syrkkqWFvbT3eLoedvamcUdnnvwyyUNv5FgFXA3KP8td+ATibnlZ0EANW5PYRm8E6MJzEB/72Q==}
|
||||
|
||||
'@peculiar/asn1-schema@2.5.0':
|
||||
resolution: {integrity: sha512-YM/nFfskFJSlHqv59ed6dZlLZqtZQwjRVJ4bBAiWV08Oc+1rSd5lDZcBEx0lGDHfSoH3UziI2pXt2UM33KerPQ==}
|
||||
|
||||
'@peculiar/asn1-x509-attr@2.5.0':
|
||||
resolution: {integrity: sha512-9f0hPOxiJDoG/bfNLAFven+Bd4gwz/VzrCIIWc1025LEI4BXO0U5fOCTNDPbbp2ll+UzqKsZ3g61mpBp74gk9A==}
|
||||
|
||||
'@peculiar/asn1-x509@2.5.0':
|
||||
resolution: {integrity: sha512-CpwtMCTJvfvYTFMuiME5IH+8qmDe3yEWzKHe7OOADbGfq7ohxeLaXwQo0q4du3qs0AII3UbLCvb9NF/6q0oTKQ==}
|
||||
|
||||
'@peculiar/x509@1.14.0':
|
||||
resolution: {integrity: sha512-Yc4PDxN3OrxUPiXgU63c+ZRXKGE8YKF2McTciYhUHFtHVB0KMnjeFSU0qpztGhsp4P0uKix4+J2xEpIEDu8oXg==}
|
||||
|
||||
'@simplewebauthn/browser@13.2.0':
|
||||
resolution: {integrity: sha512-N3fuA1AAnTo5gCStYoIoiasPccC+xPLx2YU88Dv0GeAmPQTWHETlZQq5xZ0DgUq1H9loXMWQH5qqUjcI7BHJ1A==}
|
||||
|
||||
'@simplewebauthn/server@13.2.1':
|
||||
resolution: {integrity: sha512-Inmfye5opZXe3HI0GaksqBnQiM7glcNySoG6DH1GgkO1Lh9dvuV4XSV9DK02DReUVX39HpcDob9nxHELjECoQw==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
'@sinclair/typebox@0.27.8':
|
||||
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
|
||||
|
||||
@@ -478,9 +623,6 @@ packages:
|
||||
'@types/node@18.15.11':
|
||||
resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==}
|
||||
|
||||
'@types/node@20.11.0':
|
||||
resolution: {integrity: sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==}
|
||||
|
||||
'@types/normalize-package-data@2.4.4':
|
||||
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
|
||||
|
||||
@@ -569,6 +711,10 @@ packages:
|
||||
resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
asn1js@3.0.6:
|
||||
resolution: {integrity: sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
assertion-error@1.1.0:
|
||||
resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
|
||||
|
||||
@@ -579,6 +725,38 @@ packages:
|
||||
balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
better-auth@1.3.25:
|
||||
resolution: {integrity: sha512-prTPitTvhIbYPeO/M1QrkveT5afbkyV1OAfnSxYe3WfgTdKgRiEO3tMIPzwmKA54zSWw6XBYKtSD9uIuAAfvuQ==}
|
||||
peerDependencies:
|
||||
'@lynx-js/react': '*'
|
||||
'@sveltejs/kit': '*'
|
||||
next: '*'
|
||||
react: '*'
|
||||
react-dom: '*'
|
||||
solid-js: '*'
|
||||
svelte: '*'
|
||||
vue: '*'
|
||||
peerDependenciesMeta:
|
||||
'@lynx-js/react':
|
||||
optional: true
|
||||
'@sveltejs/kit':
|
||||
optional: true
|
||||
next:
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
react-dom:
|
||||
optional: true
|
||||
solid-js:
|
||||
optional: true
|
||||
svelte:
|
||||
optional: true
|
||||
vue:
|
||||
optional: true
|
||||
|
||||
better-call@1.0.19:
|
||||
resolution: {integrity: sha512-sI3GcA1SCVa3H+CDHl8W8qzhlrckwXOTKhqq3OOPXjgn5aTOMIqGY34zLY/pHA6tRRMjTUC3lz5Mi7EbDA24Kw==}
|
||||
|
||||
better-path-resolve@1.0.0:
|
||||
resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -722,6 +900,9 @@ packages:
|
||||
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
defu@6.1.4:
|
||||
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
|
||||
|
||||
detect-indent@6.1.0:
|
||||
resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -1152,6 +1333,9 @@ packages:
|
||||
isexe@2.0.0:
|
||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||
|
||||
jose@6.1.0:
|
||||
resolution: {integrity: sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA==}
|
||||
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
@@ -1176,6 +1360,10 @@ packages:
|
||||
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
kysely@0.28.7:
|
||||
resolution: {integrity: sha512-u/cAuTL4DRIiO2/g4vNGRgklEKNIj5Q3CG7RoUB5DV5SfEC2hMvPxKi0GWPmnzwL2ryIeud2VTcEEmqzTzEPNw==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
less@4.2.0:
|
||||
resolution: {integrity: sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -1276,6 +1464,10 @@ packages:
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
nanostores@1.0.1:
|
||||
resolution: {integrity: sha512-kNZ9xnoJYKg/AfxjrVL4SS0fKX++4awQReGqWnwTRHxeHGZ1FJFVgTqr/eMrNQdp0Tz7M7tG/TDaX8QfHDwVCw==}
|
||||
engines: {node: ^20.0.0 || >=22.0.0}
|
||||
|
||||
needle@3.3.1:
|
||||
resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==}
|
||||
engines: {node: '>= 4.4.x'}
|
||||
@@ -1456,6 +1648,13 @@ packages:
|
||||
pseudomap@1.0.2:
|
||||
resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
|
||||
|
||||
pvtsutils@1.3.6:
|
||||
resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==}
|
||||
|
||||
pvutils@1.1.3:
|
||||
resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
queue-microtask@1.2.3:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
|
||||
@@ -1490,6 +1689,9 @@ packages:
|
||||
resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
reflect-metadata@0.2.2:
|
||||
resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==}
|
||||
|
||||
regenerator-runtime@0.14.1:
|
||||
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
|
||||
|
||||
@@ -1529,6 +1731,9 @@ packages:
|
||||
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
|
||||
hasBin: true
|
||||
|
||||
rou3@0.5.1:
|
||||
resolution: {integrity: sha512-OXMmJ3zRk2xeXFGfA3K+EOPHC5u7RDFG7lIOx0X1pdnhUkI8MdVrbV+sNsD80ElpUZ+MRHdyxPnFthq9VHs8uQ==}
|
||||
|
||||
run-parallel@1.2.0:
|
||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||
|
||||
@@ -1565,6 +1770,9 @@ packages:
|
||||
set-blocking@2.0.0:
|
||||
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
|
||||
|
||||
set-cookie-parser@2.7.1:
|
||||
resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
|
||||
|
||||
set-function-length@1.1.1:
|
||||
resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -1713,14 +1921,21 @@ packages:
|
||||
resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
tslib@2.6.2:
|
||||
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
|
||||
tslib@1.14.1:
|
||||
resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
|
||||
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
tsx@4.7.0:
|
||||
resolution: {integrity: sha512-I+t79RYPlEYlHn9a+KzwrvEwhJg35h/1zHsLC2JXvhC2mdynMv6Zxzvhv5EMV6VF5qJlLlkSnMVvdZV3PSIGcg==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
hasBin: true
|
||||
|
||||
tsyringe@4.10.0:
|
||||
resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==}
|
||||
engines: {node: '>= 6.0.0'}
|
||||
|
||||
tty-table@4.2.3:
|
||||
resolution: {integrity: sha512-Fs15mu0vGzCrj8fmJNP7Ynxt5J7praPXqFN0leZeZBXJwkMxv9cb2D454k1ltrtUSJbZ4yH4e0CynsHLxmUfFA==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
@@ -1802,8 +2017,8 @@ packages:
|
||||
unbox-primitive@1.0.2:
|
||||
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
|
||||
|
||||
undici-types@5.26.5:
|
||||
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
|
||||
uncrypto@0.1.3:
|
||||
resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==}
|
||||
|
||||
universalify@0.1.2:
|
||||
resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
|
||||
@@ -1960,6 +2175,9 @@ packages:
|
||||
resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
|
||||
engines: {node: '>=12.20'}
|
||||
|
||||
zod@4.1.11:
|
||||
resolution: {integrity: sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@adobe/css-tools@4.3.2':
|
||||
@@ -1982,6 +2200,15 @@ snapshots:
|
||||
dependencies:
|
||||
regenerator-runtime: 0.14.1
|
||||
|
||||
'@better-auth/core@1.3.25':
|
||||
dependencies:
|
||||
better-call: 1.0.19
|
||||
zod: 4.1.11
|
||||
|
||||
'@better-auth/utils@0.3.0': {}
|
||||
|
||||
'@better-fetch/fetch@1.1.18': {}
|
||||
|
||||
'@changesets/apply-release-plan@7.0.0':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.23.7
|
||||
@@ -2282,12 +2509,16 @@ snapshots:
|
||||
'@esbuild/win32-x64@0.19.11':
|
||||
optional: true
|
||||
|
||||
'@hexagon/base64@1.1.28': {}
|
||||
|
||||
'@jest/schemas@29.6.3':
|
||||
dependencies:
|
||||
'@sinclair/typebox': 0.27.8
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.4.15': {}
|
||||
|
||||
'@levischuck/tiny-cbor@0.2.11': {}
|
||||
|
||||
'@manypkg/find-root@1.1.0':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.23.7
|
||||
@@ -2304,6 +2535,10 @@ snapshots:
|
||||
globby: 11.1.0
|
||||
read-yaml-file: 1.1.0
|
||||
|
||||
'@noble/ciphers@2.0.1': {}
|
||||
|
||||
'@noble/hashes@2.0.1': {}
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
dependencies:
|
||||
'@nodelib/fs.stat': 2.0.5
|
||||
@@ -2318,17 +2553,49 @@ snapshots:
|
||||
|
||||
'@opentelemetry/api@1.9.0': {}
|
||||
|
||||
'@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0)':
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
|
||||
'@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0)':
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@opentelemetry/semantic-conventions': 1.28.0
|
||||
|
||||
'@opentelemetry/core@2.1.0(@opentelemetry/api@1.9.0)':
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@opentelemetry/semantic-conventions': 1.37.0
|
||||
|
||||
'@opentelemetry/propagator-b3@1.30.1(@opentelemetry/api@1.9.0)':
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0)
|
||||
|
||||
'@opentelemetry/propagator-jaeger@1.30.1(@opentelemetry/api@1.9.0)':
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0)
|
||||
|
||||
'@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0)':
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0)
|
||||
'@opentelemetry/semantic-conventions': 1.28.0
|
||||
|
||||
'@opentelemetry/resources@2.1.0(@opentelemetry/api@1.9.0)':
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0)
|
||||
'@opentelemetry/semantic-conventions': 1.37.0
|
||||
|
||||
'@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0)':
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0)
|
||||
'@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0)
|
||||
'@opentelemetry/semantic-conventions': 1.28.0
|
||||
|
||||
'@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0)':
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
@@ -2336,8 +2603,129 @@ snapshots:
|
||||
'@opentelemetry/resources': 2.1.0(@opentelemetry/api@1.9.0)
|
||||
'@opentelemetry/semantic-conventions': 1.37.0
|
||||
|
||||
'@opentelemetry/sdk-trace-node@1.30.1(@opentelemetry/api@1.9.0)':
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0)
|
||||
'@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0)
|
||||
'@opentelemetry/propagator-b3': 1.30.1(@opentelemetry/api@1.9.0)
|
||||
'@opentelemetry/propagator-jaeger': 1.30.1(@opentelemetry/api@1.9.0)
|
||||
'@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0)
|
||||
semver: 7.5.4
|
||||
|
||||
'@opentelemetry/semantic-conventions@1.28.0': {}
|
||||
|
||||
'@opentelemetry/semantic-conventions@1.37.0': {}
|
||||
|
||||
'@peculiar/asn1-android@2.5.0':
|
||||
dependencies:
|
||||
'@peculiar/asn1-schema': 2.5.0
|
||||
asn1js: 3.0.6
|
||||
tslib: 2.8.1
|
||||
|
||||
'@peculiar/asn1-cms@2.5.0':
|
||||
dependencies:
|
||||
'@peculiar/asn1-schema': 2.5.0
|
||||
'@peculiar/asn1-x509': 2.5.0
|
||||
'@peculiar/asn1-x509-attr': 2.5.0
|
||||
asn1js: 3.0.6
|
||||
tslib: 2.8.1
|
||||
|
||||
'@peculiar/asn1-csr@2.5.0':
|
||||
dependencies:
|
||||
'@peculiar/asn1-schema': 2.5.0
|
||||
'@peculiar/asn1-x509': 2.5.0
|
||||
asn1js: 3.0.6
|
||||
tslib: 2.8.1
|
||||
|
||||
'@peculiar/asn1-ecc@2.5.0':
|
||||
dependencies:
|
||||
'@peculiar/asn1-schema': 2.5.0
|
||||
'@peculiar/asn1-x509': 2.5.0
|
||||
asn1js: 3.0.6
|
||||
tslib: 2.8.1
|
||||
|
||||
'@peculiar/asn1-pfx@2.5.0':
|
||||
dependencies:
|
||||
'@peculiar/asn1-cms': 2.5.0
|
||||
'@peculiar/asn1-pkcs8': 2.5.0
|
||||
'@peculiar/asn1-rsa': 2.5.0
|
||||
'@peculiar/asn1-schema': 2.5.0
|
||||
asn1js: 3.0.6
|
||||
tslib: 2.8.1
|
||||
|
||||
'@peculiar/asn1-pkcs8@2.5.0':
|
||||
dependencies:
|
||||
'@peculiar/asn1-schema': 2.5.0
|
||||
'@peculiar/asn1-x509': 2.5.0
|
||||
asn1js: 3.0.6
|
||||
tslib: 2.8.1
|
||||
|
||||
'@peculiar/asn1-pkcs9@2.5.0':
|
||||
dependencies:
|
||||
'@peculiar/asn1-cms': 2.5.0
|
||||
'@peculiar/asn1-pfx': 2.5.0
|
||||
'@peculiar/asn1-pkcs8': 2.5.0
|
||||
'@peculiar/asn1-schema': 2.5.0
|
||||
'@peculiar/asn1-x509': 2.5.0
|
||||
'@peculiar/asn1-x509-attr': 2.5.0
|
||||
asn1js: 3.0.6
|
||||
tslib: 2.8.1
|
||||
|
||||
'@peculiar/asn1-rsa@2.5.0':
|
||||
dependencies:
|
||||
'@peculiar/asn1-schema': 2.5.0
|
||||
'@peculiar/asn1-x509': 2.5.0
|
||||
asn1js: 3.0.6
|
||||
tslib: 2.8.1
|
||||
|
||||
'@peculiar/asn1-schema@2.5.0':
|
||||
dependencies:
|
||||
asn1js: 3.0.6
|
||||
pvtsutils: 1.3.6
|
||||
tslib: 2.8.1
|
||||
|
||||
'@peculiar/asn1-x509-attr@2.5.0':
|
||||
dependencies:
|
||||
'@peculiar/asn1-schema': 2.5.0
|
||||
'@peculiar/asn1-x509': 2.5.0
|
||||
asn1js: 3.0.6
|
||||
tslib: 2.8.1
|
||||
|
||||
'@peculiar/asn1-x509@2.5.0':
|
||||
dependencies:
|
||||
'@peculiar/asn1-schema': 2.5.0
|
||||
asn1js: 3.0.6
|
||||
pvtsutils: 1.3.6
|
||||
tslib: 2.8.1
|
||||
|
||||
'@peculiar/x509@1.14.0':
|
||||
dependencies:
|
||||
'@peculiar/asn1-cms': 2.5.0
|
||||
'@peculiar/asn1-csr': 2.5.0
|
||||
'@peculiar/asn1-ecc': 2.5.0
|
||||
'@peculiar/asn1-pkcs9': 2.5.0
|
||||
'@peculiar/asn1-rsa': 2.5.0
|
||||
'@peculiar/asn1-schema': 2.5.0
|
||||
'@peculiar/asn1-x509': 2.5.0
|
||||
pvtsutils: 1.3.6
|
||||
reflect-metadata: 0.2.2
|
||||
tslib: 2.8.1
|
||||
tsyringe: 4.10.0
|
||||
|
||||
'@simplewebauthn/browser@13.2.0': {}
|
||||
|
||||
'@simplewebauthn/server@13.2.1':
|
||||
dependencies:
|
||||
'@hexagon/base64': 1.1.28
|
||||
'@levischuck/tiny-cbor': 0.2.11
|
||||
'@peculiar/asn1-android': 2.5.0
|
||||
'@peculiar/asn1-ecc': 2.5.0
|
||||
'@peculiar/asn1-rsa': 2.5.0
|
||||
'@peculiar/asn1-schema': 2.5.0
|
||||
'@peculiar/asn1-x509': 2.5.0
|
||||
'@peculiar/x509': 1.14.0
|
||||
|
||||
'@sinclair/typebox@0.27.8': {}
|
||||
|
||||
'@types/chai-subset@1.3.5':
|
||||
@@ -2352,15 +2740,11 @@ snapshots:
|
||||
|
||||
'@types/node@18.15.11': {}
|
||||
|
||||
'@types/node@20.11.0':
|
||||
dependencies:
|
||||
undici-types: 5.26.5
|
||||
|
||||
'@types/normalize-package-data@2.4.4': {}
|
||||
|
||||
'@types/pg@8.15.5':
|
||||
dependencies:
|
||||
'@types/node': 20.11.0
|
||||
'@types/node': 18.15.11
|
||||
pg-protocol: 1.10.3
|
||||
pg-types: 2.2.0
|
||||
|
||||
@@ -2461,12 +2845,42 @@ snapshots:
|
||||
|
||||
arrify@1.0.1: {}
|
||||
|
||||
asn1js@3.0.6:
|
||||
dependencies:
|
||||
pvtsutils: 1.3.6
|
||||
pvutils: 1.1.3
|
||||
tslib: 2.8.1
|
||||
|
||||
assertion-error@1.1.0: {}
|
||||
|
||||
available-typed-arrays@1.0.5: {}
|
||||
|
||||
balanced-match@1.0.2: {}
|
||||
|
||||
better-auth@1.3.25:
|
||||
dependencies:
|
||||
'@better-auth/core': 1.3.25
|
||||
'@better-auth/utils': 0.3.0
|
||||
'@better-fetch/fetch': 1.1.18
|
||||
'@noble/ciphers': 2.0.1
|
||||
'@noble/hashes': 2.0.1
|
||||
'@simplewebauthn/browser': 13.2.0
|
||||
'@simplewebauthn/server': 13.2.1
|
||||
better-call: 1.0.19
|
||||
defu: 6.1.4
|
||||
jose: 6.1.0
|
||||
kysely: 0.28.7
|
||||
nanostores: 1.0.1
|
||||
zod: 4.1.11
|
||||
|
||||
better-call@1.0.19:
|
||||
dependencies:
|
||||
'@better-auth/utils': 0.3.0
|
||||
'@better-fetch/fetch': 1.1.18
|
||||
rou3: 0.5.1
|
||||
set-cookie-parser: 2.7.1
|
||||
uncrypto: 0.1.3
|
||||
|
||||
better-path-resolve@1.0.0:
|
||||
dependencies:
|
||||
is-windows: 1.0.2
|
||||
@@ -2633,6 +3047,8 @@ snapshots:
|
||||
has-property-descriptors: 1.0.1
|
||||
object-keys: 1.1.1
|
||||
|
||||
defu@6.1.4: {}
|
||||
|
||||
detect-indent@6.1.0: {}
|
||||
|
||||
diff-sequences@29.6.3: {}
|
||||
@@ -2643,11 +3059,12 @@ snapshots:
|
||||
|
||||
dotenv@8.6.0: {}
|
||||
|
||||
drizzle-orm@0.36.4(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(@types/react@18.2.46)(postgres@3.4.7)(react@18.2.0):
|
||||
drizzle-orm@0.36.4(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(@types/react@18.2.46)(kysely@0.28.7)(postgres@3.4.7)(react@18.2.0):
|
||||
optionalDependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@types/pg': 8.15.5
|
||||
'@types/react': 18.2.46
|
||||
kysely: 0.28.7
|
||||
postgres: 3.4.7
|
||||
react: 18.2.0
|
||||
|
||||
@@ -3055,6 +3472,8 @@ snapshots:
|
||||
|
||||
isexe@2.0.0: {}
|
||||
|
||||
jose@6.1.0: {}
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
js-yaml@3.14.1:
|
||||
@@ -3074,11 +3493,13 @@ snapshots:
|
||||
|
||||
kleur@4.1.5: {}
|
||||
|
||||
kysely@0.28.7: {}
|
||||
|
||||
less@4.2.0:
|
||||
dependencies:
|
||||
copy-anything: 2.0.6
|
||||
parse-node-version: 1.0.1
|
||||
tslib: 2.6.2
|
||||
tslib: 2.8.1
|
||||
optionalDependencies:
|
||||
errno: 0.1.8
|
||||
graceful-fs: 4.2.11
|
||||
@@ -3191,6 +3612,8 @@ snapshots:
|
||||
|
||||
nanoid@3.3.7: {}
|
||||
|
||||
nanostores@1.0.1: {}
|
||||
|
||||
needle@3.3.1:
|
||||
dependencies:
|
||||
iconv-lite: 0.6.3
|
||||
@@ -3348,6 +3771,12 @@ snapshots:
|
||||
|
||||
pseudomap@1.0.2: {}
|
||||
|
||||
pvtsutils@1.3.6:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
pvutils@1.1.3: {}
|
||||
|
||||
queue-microtask@1.2.3: {}
|
||||
|
||||
quick-lru@4.0.1: {}
|
||||
@@ -3389,6 +3818,8 @@ snapshots:
|
||||
indent-string: 4.0.0
|
||||
strip-indent: 3.0.0
|
||||
|
||||
reflect-metadata@0.2.2: {}
|
||||
|
||||
regenerator-runtime@0.14.1: {}
|
||||
|
||||
regexp.prototype.flags@1.5.1:
|
||||
@@ -3421,6 +3852,8 @@ snapshots:
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
rou3@0.5.1: {}
|
||||
|
||||
run-parallel@1.2.0:
|
||||
dependencies:
|
||||
queue-microtask: 1.2.3
|
||||
@@ -3461,6 +3894,8 @@ snapshots:
|
||||
|
||||
set-blocking@2.0.0: {}
|
||||
|
||||
set-cookie-parser@2.7.1: {}
|
||||
|
||||
set-function-length@1.1.1:
|
||||
dependencies:
|
||||
define-data-property: 1.1.1
|
||||
@@ -3617,8 +4052,9 @@ snapshots:
|
||||
|
||||
trim-newlines@3.0.1: {}
|
||||
|
||||
tslib@2.6.2:
|
||||
optional: true
|
||||
tslib@1.14.1: {}
|
||||
|
||||
tslib@2.8.1: {}
|
||||
|
||||
tsx@4.7.0:
|
||||
dependencies:
|
||||
@@ -3627,6 +4063,10 @@ snapshots:
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
tsyringe@4.10.0:
|
||||
dependencies:
|
||||
tslib: 1.14.1
|
||||
|
||||
tty-table@4.2.3:
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
@@ -3710,7 +4150,7 @@ snapshots:
|
||||
has-symbols: 1.0.3
|
||||
which-boxed-primitive: 1.0.2
|
||||
|
||||
undici-types@5.26.5: {}
|
||||
uncrypto@0.1.3: {}
|
||||
|
||||
universalify@0.1.2: {}
|
||||
|
||||
@@ -3719,14 +4159,14 @@ snapshots:
|
||||
spdx-correct: 3.2.0
|
||||
spdx-expression-parse: 3.0.1
|
||||
|
||||
vite-node@0.33.0(@types/node@20.11.0)(less@4.2.0)(sass@1.69.7)(stylus@0.59.0):
|
||||
vite-node@0.33.0(@types/node@18.15.11)(less@4.2.0)(sass@1.69.7)(stylus@0.59.0):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.3.4
|
||||
mlly: 1.4.2
|
||||
pathe: 1.1.1
|
||||
picocolors: 1.0.0
|
||||
vite: 4.5.1(@types/node@20.11.0)(less@4.2.0)(sass@1.69.7)(stylus@0.59.0)
|
||||
vite: 4.5.1(@types/node@18.15.11)(less@4.2.0)(sass@1.69.7)(stylus@0.59.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- less
|
||||
@@ -3737,13 +4177,13 @@ snapshots:
|
||||
- supports-color
|
||||
- terser
|
||||
|
||||
vite@4.5.1(@types/node@20.11.0)(less@4.2.0)(sass@1.69.7)(stylus@0.59.0):
|
||||
vite@4.5.1(@types/node@18.15.11)(less@4.2.0)(sass@1.69.7)(stylus@0.59.0):
|
||||
dependencies:
|
||||
esbuild: 0.18.20
|
||||
postcss: 8.4.33
|
||||
rollup: 3.29.4
|
||||
optionalDependencies:
|
||||
'@types/node': 20.11.0
|
||||
'@types/node': 18.15.11
|
||||
fsevents: 2.3.3
|
||||
less: 4.2.0
|
||||
sass: 1.69.7
|
||||
@@ -3753,7 +4193,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/chai': 4.3.11
|
||||
'@types/chai-subset': 1.3.5
|
||||
'@types/node': 20.11.0
|
||||
'@types/node': 18.15.11
|
||||
'@vitest/expect': 0.33.0
|
||||
'@vitest/runner': 0.33.0
|
||||
'@vitest/snapshot': 0.33.0
|
||||
@@ -3772,8 +4212,8 @@ snapshots:
|
||||
strip-literal: 1.3.0
|
||||
tinybench: 2.5.1
|
||||
tinypool: 0.6.0
|
||||
vite: 4.5.1(@types/node@20.11.0)(less@4.2.0)(sass@1.69.7)(stylus@0.59.0)
|
||||
vite-node: 0.33.0(@types/node@20.11.0)(less@4.2.0)(sass@1.69.7)(stylus@0.59.0)
|
||||
vite: 4.5.1(@types/node@18.15.11)(less@4.2.0)(sass@1.69.7)(stylus@0.59.0)
|
||||
vite-node: 0.33.0(@types/node@18.15.11)(less@4.2.0)(sass@1.69.7)(stylus@0.59.0)
|
||||
why-is-node-running: 2.2.2
|
||||
transitivePeerDependencies:
|
||||
- less
|
||||
@@ -3885,3 +4325,5 @@ snapshots:
|
||||
yocto-queue@0.1.0: {}
|
||||
|
||||
yocto-queue@1.0.0: {}
|
||||
|
||||
zod@4.1.11: {}
|
||||
|
||||
Reference in New Issue
Block a user