diff --git a/apps/login/.eslintrc.cjs b/apps/login/.eslintrc.cjs
index bbca76e6c..b94843c3e 100644
--- a/apps/login/.eslintrc.cjs
+++ b/apps/login/.eslintrc.cjs
@@ -1,8 +1,9 @@
module.exports = {
parser: "@typescript-eslint/parser",
extends: ["next", "prettier"],
- plugins: ["@typescript-eslint"],
+ plugins: ["@typescript-eslint", "import"],
rules: {
+ "import/first": "error",
"@next/next/no-html-link-for-pages": "off",
"@next/next/no-img-element": "off",
"react/no-unescaped-entities": "off",
@@ -27,4 +28,52 @@ module.exports = {
},
project: "./tsconfig.json",
},
+ overrides: [
+ {
+ // Server components and server actions: must use logger, not console.*
+ // Files in src/lib/server/*, src/app/**/route.ts, src/middleware.ts, and files with "use server"
+ files: [
+ "src/lib/server/**/*.ts",
+ "src/app/**/route.ts",
+ "src/middleware.ts",
+ "src/lib/cookies.ts",
+ ],
+ rules: {
+ "no-console": "warn",
+ },
+ },
+ {
+ // instrumentation.ts uses console.log during OTEL initialization (before logger is available)
+ files: ["src/instrumentation.ts"],
+ rules: {
+ "no-console": "off",
+ },
+ },
+ {
+ // Client components: logger import is forbidden (enforced by server-only at build time)
+ // This override documents the expectation for client components
+ files: ["src/components/**/*.tsx"],
+ rules: {
+ "no-restricted-imports": ["error", {
+ "paths": [
+ {
+ "name": "next/image",
+ "message": "Use of next/image is forbidden. Use regular
elements instead."
+ },
+ {
+ "name": "@/lib/logger",
+ "message": "Cannot import logger in client components. Use console.* instead. The logger uses 'server-only' and will fail at build time."
+ }
+ ]
+ }],
+ },
+ },
+ {
+ // Test files: allow console.*
+ files: ["**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts", "**/*.spec.tsx", "**/tests/**/*"],
+ rules: {
+ "no-console": "off",
+ },
+ },
+ ],
};
diff --git a/apps/login/.gitignore b/apps/login/.gitignore
index b856d6e13..41a167438 100644
--- a/apps/login/.gitignore
+++ b/apps/login/.gitignore
@@ -13,4 +13,5 @@ dist-ssr
*.local
.vscode
/blob-report/
+# Dockerized test output (generated by integration tests)
dockerized/*/output/
diff --git a/apps/login/Dockerfile b/apps/login/Dockerfile
index a76fabee0..4d34cf80c 100644
--- a/apps/login/Dockerfile
+++ b/apps/login/Dockerfile
@@ -14,9 +14,11 @@ ENV HOSTNAME="::" \
NODE_OPTIONS="--use-openssl-ca" \
SSL_CERT_FILE="/etc/ssl/certs/ca-certificates.crt" \
SSL_CERT_DIR="/etc/ssl/certs" \
- ZITADEL_TLS_ENABLED="false"
+ ZITADEL_TLS_ENABLED="false" \
+ OTEL_SERVICE_NAME="zitadel-login" \
+ OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
# TODO: Check healthy, not ready
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD ["/usr/local/bin/node", "/app/healthcheck.mjs"]
-ENTRYPOINT ["/app/entrypoint.sh", "node", "apps/login/server.js" ]
+ENTRYPOINT ["/app/entrypoint.sh", "node", "apps/login/server.js"]
diff --git a/apps/login/dockerized/ca/ca.integration.test.ts b/apps/login/dockerized/ca/ca.integration.test.ts
index 143388250..fa3f0f733 100644
--- a/apps/login/dockerized/ca/ca.integration.test.ts
+++ b/apps/login/dockerized/ca/ca.integration.test.ts
@@ -1,165 +1,117 @@
import { describe, it, expect, beforeAll, afterAll } from "vitest";
-import { GenericContainer, type StartedTestContainer, Network, Wait } from "testcontainers";
+import {
+ GenericContainer,
+ type StartedTestContainer,
+ Network,
+ Wait,
+} from "testcontainers";
import type { StartedNetwork } from "testcontainers";
import * as fs from "fs";
+import * as os from "os";
import * as path from "path";
-import * as esbuild from "esbuild";
import { generateCertificates } from "./utils/tls.ts";
const TEST_DIR = path.dirname(new URL(import.meta.url).pathname);
-const OUTPUT_DIR = path.join(TEST_DIR, "output");
-const CERTS_DIR = path.join(OUTPUT_DIR, "certs");
const LOGIN_APP_DIR = path.join(TEST_DIR, "../..");
-async function bundleMockServer(): Promise {
- const bundleDir = path.join(OUTPUT_DIR, "bundle");
- fs.mkdirSync(bundleDir, { recursive: true });
-
- const outfile = path.join(bundleDir, "mock-server.js");
- await esbuild.build({
- entryPoints: [path.join(TEST_DIR, "mock-server.ts")],
- bundle: true,
- platform: "node",
- target: "node22",
- outfile,
- format: "cjs",
- });
-
- return outfile;
-}
-
-function readCapturedRequests(): Array<{ method: string; url: string; tlsConnected: boolean }> {
- const filePath = path.join(OUTPUT_DIR, "requests.json");
- if (!fs.existsSync(filePath)) {
- return [];
- }
- return JSON.parse(fs.readFileSync(filePath, "utf-8"));
-}
+const SMOCKER_MOCK_PORT = 443;
+const SMOCKER_ADMIN_PORT = 8081;
/**
* Integration tests for custom CA certificate support in the login application.
*
- * These tests verify that the Dockerfile correctly configures Node.js to trust custom
- * Certificate Authorities via the SSL_CERT_FILE environment variable and the
- * NODE_OPTIONS=--use-openssl-ca flag. This is essential for deployments where the
- * ZITADEL API is served behind a TLS-terminating proxy using internal or self-signed
- * certificates.
+ * These tests verify that the Dockerfile correctly configures Node.js to trust
+ * custom Certificate Authorities via the SSL_CERT_FILE environment variable and
+ * the NODE_OPTIONS=--use-openssl-ca flag.
*
- * The test suite spins up a mock TLS server with a self-signed certificate inside a
- * Docker network. The login application container connects to this mock server, and
- * the tests verify that TLS connections succeed when the custom CA is mounted and
- * fail when it is not. The mock server records all incoming requests to a shared
- * volume, allowing the tests to verify that TLS handshakes completed successfully.
- *
- * The Docker network ensures container-to-container communication uses internal DNS
- * resolution, avoiding port conflicts on the host machine and ensuring reliable
- * execution in CI environments.
+ * We use Smocker as a TLS server and test CA loading by making curl requests
+ * from inside the container - this verifies TLS at the transport layer without
+ * needing gRPC/HTTP2 support.
*/
+const LOGIN_IMAGE_TAG = `zitadel-login-test:${process.env.GITHUB_SHA || "local"}`;
+
+async function testTlsConnection(
+ container: StartedTestContainer,
+ url: string,
+): Promise<{ exitCode: number; output: string }> {
+ return container.exec([
+ "node",
+ "-e",
+ `fetch('${url}').then(() => process.exit(0)).catch(() => process.exit(1))`,
+ ]);
+}
+
describe("Custom CA Certificate Integration", () => {
- let certs: ReturnType;
let network: StartedNetwork;
let mockServer: StartedTestContainer;
- let mockServerBundle: string;
- let loginImage: GenericContainer;
+ let certsDir: string;
beforeAll(async () => {
- if (fs.existsSync(OUTPUT_DIR)) {
- fs.rmSync(OUTPUT_DIR, { recursive: true });
- }
- fs.mkdirSync(CERTS_DIR, { recursive: true });
+ certsDir = fs.mkdtempSync(path.join(os.tmpdir(), "ca-test-certs-"));
- certs = generateCertificates();
-
- fs.writeFileSync(path.join(CERTS_DIR, "ca.crt"), certs.ca.cert);
- fs.writeFileSync(path.join(CERTS_DIR, "server.key"), certs.server.key);
- fs.writeFileSync(path.join(CERTS_DIR, "server.crt"), certs.server.cert);
-
- mockServerBundle = await bundleMockServer();
+ const certs = generateCertificates();
+ fs.writeFileSync(path.join(certsDir, "ca.crt"), certs.ca.cert);
+ fs.writeFileSync(path.join(certsDir, "server.key"), certs.server.key);
+ fs.writeFileSync(path.join(certsDir, "server.crt"), certs.server.cert);
network = await new Network().start();
- mockServer = await new GenericContainer("node:22-alpine")
+ mockServer = await new GenericContainer("ghcr.io/smocker-dev/smocker")
.withNetwork(network)
.withNetworkAliases("mock-zitadel")
- .withCopyFilesToContainer([
- { source: mockServerBundle, target: "/app/server.js" },
- { source: path.join(CERTS_DIR, "server.key"), target: "/certs/server.key" },
- { source: path.join(CERTS_DIR, "server.crt"), target: "/certs/server.crt" },
- ])
- .withBindMounts([{ source: OUTPUT_DIR, target: "/output" }])
- .withCommand(["node", "/app/server.js"])
- .withExposedPorts(443)
- .withWaitStrategy(Wait.forLogMessage("Mock TLS server listening"))
+ .withBindMounts([{ source: certsDir, target: "/certs", mode: "ro" }])
+ .withEnvironment({
+ SMOCKER_MOCK_SERVER_LISTEN_PORT: "443",
+ SMOCKER_CONFIG_LISTEN_PORT: "8081",
+ SMOCKER_TLS_ENABLE: "true",
+ SMOCKER_TLS_CERT_FILE: "/certs/server.crt",
+ SMOCKER_TLS_PRIVATE_KEY_FILE: "/certs/server.key",
+ })
+ .withExposedPorts(SMOCKER_MOCK_PORT, SMOCKER_ADMIN_PORT)
+ .withStartupTimeout(120_000)
+ .withWaitStrategy(Wait.forLogMessage(/Starting mock server/))
.start();
- loginImage = await GenericContainer.fromDockerfile(LOGIN_APP_DIR).build();
- });
+ try {
+ await GenericContainer.fromDockerfile(LOGIN_APP_DIR).build(LOGIN_IMAGE_TAG);
+ } catch (err) {
+ throw new Error(`Failed to build login Docker image: ${err instanceof Error ? err.message : err}`);
+ }
+ }, 180_000);
afterAll(async () => {
- await mockServer?.stop();
- await network?.stop();
+ await mockServer?.stop().catch(() => {});
+ await network?.stop().catch(() => {});
});
describe("when custom CA certificate is provided", () => {
let container: StartedTestContainer;
- let appUrl: string;
beforeAll(async () => {
- if (fs.existsSync(path.join(OUTPUT_DIR, "requests.json"))) {
- fs.unlinkSync(path.join(OUTPUT_DIR, "requests.json"));
- }
-
- container = await loginImage
+ container = await new GenericContainer(LOGIN_IMAGE_TAG)
.withNetwork(network)
.withExposedPorts(3000)
.withEnvironment({
- ZITADEL_API_URL: "https://mock-zitadel",
- ZITADEL_SERVICE_USER_TOKEN: "test-token",
SSL_CERT_FILE: "/etc/ssl/certs/custom-ca.crt",
})
.withCopyFilesToContainer([
{
- source: path.join(CERTS_DIR, "ca.crt"),
+ source: path.join(certsDir, "ca.crt"),
target: "/etc/ssl/certs/custom-ca.crt",
},
])
+ .withStartupTimeout(180_000)
.withWaitStrategy(Wait.forHttp("/ui/v2/login/healthy", 3000))
.start();
-
- appUrl = `http://${container.getHost()}:${container.getMappedPort(3000)}`;
-
- await fetch(`${appUrl}/ui/v2/login/loginname`, { redirect: "manual" });
-
- for (let i = 0; i < 30; i++) {
- const requests = readCapturedRequests();
- if (requests.length > 0) break;
- await new Promise((r) => setTimeout(r, 1000));
- }
- });
+ }, 180_000);
afterAll(async () => {
- await container?.stop();
+ await container?.stop().catch(() => {});
});
- it("connects to mock server over TLS", () => {
- const requests = readCapturedRequests();
- expect(requests.length).toBeGreaterThan(0);
- });
-
- it("marks requests as TLS connected", () => {
- const requests = readCapturedRequests();
- const tlsRequests = requests.filter((r) => r.tlsConnected === true);
- expect(tlsRequests.length).toBeGreaterThan(0);
- });
-
- it("returns 200 from healthy endpoint", async () => {
- const response = await fetch(`${appUrl}/ui/v2/login/healthy`);
- expect(response.status).toBe(200);
- });
-
- it("serves the login page", async () => {
- const response = await fetch(`${appUrl}/ui/v2/login/loginname`, { redirect: "manual" });
- expect([200, 302, 303, 307, 308]).toContain(response.status);
+ it("can establish TLS connection to server with custom CA", async () => {
+ const result = await testTlsConnection(container, "https://mock-zitadel/");
+ expect(result.exitCode).toBe(0);
});
it("has NODE_OPTIONS set with --use-openssl-ca", async () => {
@@ -184,33 +136,23 @@ describe("Custom CA Certificate Integration", () => {
describe("when custom CA certificate is not provided", () => {
let container: StartedTestContainer;
- let appUrl: string;
- let requestsBefore: number;
beforeAll(async () => {
- requestsBefore = readCapturedRequests().length;
-
- container = await loginImage
+ container = await new GenericContainer(LOGIN_IMAGE_TAG)
.withNetwork(network)
.withExposedPorts(3000)
- .withEnvironment({
- ZITADEL_API_URL: "https://mock-zitadel",
- ZITADEL_SERVICE_USER_TOKEN: "test-token",
- })
+ .withStartupTimeout(180_000)
.withWaitStrategy(Wait.forHttp("/ui/v2/login/healthy", 3000))
.start();
-
- appUrl = `http://${container.getHost()}:${container.getMappedPort(3000)}`;
-
- await fetch(`${appUrl}/ui/v2/login/loginname`, { redirect: "manual" });
-
- for (let i = 0; i < 10; i++) {
- await new Promise((r) => setTimeout(r, 500));
- }
- });
+ }, 180_000);
afterAll(async () => {
- await container?.stop();
+ await container?.stop().catch(() => {});
+ });
+
+ it("cannot establish TLS connection without custom CA", async () => {
+ const result = await testTlsConnection(container, "https://mock-zitadel/");
+ expect(result.exitCode).not.toBe(0);
});
it("uses system CA store by default", async () => {
@@ -226,10 +168,5 @@ describe("Custom CA Certificate Integration", () => {
]);
expect(result.output.trim()).toContain("missing");
});
-
- it("cannot establish TLS connection to mock server", () => {
- const requestsAfter = readCapturedRequests().length;
- expect(requestsAfter).toBe(requestsBefore);
- });
});
});
diff --git a/apps/login/dockerized/ca/mock-server.ts b/apps/login/dockerized/ca/mock-server.ts
deleted file mode 100644
index 21a06dd34..000000000
--- a/apps/login/dockerized/ca/mock-server.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import * as http2 from "node:http2";
-import * as fs from "node:fs";
-
-const OUTPUT_DIR = process.env.OUTPUT_DIR || "/output";
-
-const server = http2.createSecureServer({
- key: fs.readFileSync("/certs/server.key"),
- cert: fs.readFileSync("/certs/server.crt"),
- allowHTTP1: true,
-});
-
-interface CapturedRequest {
- method: string;
- url: string;
- tlsConnected: boolean;
-}
-
-const requests: CapturedRequest[] = [];
-
-server.on("stream", (stream, headers) => {
- requests.push({
- method: (headers[":method"] as string) || "UNKNOWN",
- url: (headers[":path"] as string) || "/",
- tlsConnected: true,
- });
- fs.writeFileSync(`${OUTPUT_DIR}/requests.json`, JSON.stringify(requests, null, 2));
-
- stream.respond({
- ":status": 200,
- "content-type": "application/json",
- "grpc-status": "0",
- });
- stream.end(JSON.stringify({ status: "ok" }));
-});
-
-server.listen(443, "0.0.0.0", () => {
- console.log("Mock TLS server listening on port 443");
-});
diff --git a/apps/login/dockerized/otel/mock-zitadel-server.js b/apps/login/dockerized/otel/mock-zitadel-server.js
new file mode 100644
index 000000000..5b32af2dc
--- /dev/null
+++ b/apps/login/dockerized/otel/mock-zitadel-server.js
@@ -0,0 +1,155 @@
+/**
+ * Mock Zitadel Connect/gRPC server for testing trace propagation.
+ * Captures incoming headers and writes them to a file for test assertions.
+ *
+ * Uses HTTP/2 with cleartext (h2c) to support gRPC protocol.
+ */
+const http2 = require("http2");
+const http = require("http");
+const fs = require("fs");
+const path = require("path");
+
+const PORT = process.env.PORT || 8080;
+const OUTPUT_DIR = process.env.OUTPUT_DIR || "/tmp/otel";
+const HEADERS_FILE = path.join(OUTPUT_DIR, "captured-headers.json");
+
+/**
+ * Safely stringify a value to JSON with XSS-preventing character escapes.
+ *
+ * Escapes `<`, `>`, `&`, and `/` to their Unicode equivalents to prevent
+ * script injection when the output is reflected in HTTP responses or
+ * embedded in HTML contexts.
+ *
+ * @param {unknown} value - The value to stringify
+ * @returns {string} JSON string with dangerous characters escaped
+ */
+function safeJsonStringify(value) {
+ return JSON.stringify(value, null, 2)
+ .replace(//g, "\\u003e")
+ .replace(/&/g, "\\u0026")
+ .replace(/\//g, "\\/");
+}
+
+// Store captured requests
+const capturedRequests = [];
+
+// Handle HTTP/2 request
+function handleRequest(stream, headers, isHttp2 = true) {
+ const method = isHttp2 ? headers[":method"] : headers.method;
+ const url = isHttp2 ? headers[":path"] : headers.url;
+
+ // Capture trace headers
+ const traceHeaders = {
+ timestamp: new Date().toISOString(),
+ method: method,
+ url: url,
+ traceparent: headers["traceparent"] || null,
+ tracestate: headers["tracestate"] || null,
+ baggage: headers["baggage"] || null,
+ grpcAcceptEncoding: headers["grpc-accept-encoding"] || null,
+ contentType: headers["content-type"] || null,
+ };
+
+ capturedRequests.push(traceHeaders);
+
+ // Write to file for test assertions
+ fs.mkdirSync(OUTPUT_DIR, { recursive: true });
+ fs.writeFileSync(HEADERS_FILE, JSON.stringify(capturedRequests, null, 2));
+
+ console.log(`[${traceHeaders.timestamp}] ${method} ${url}`);
+ console.log(` traceparent: ${traceHeaders.traceparent || "(none)"}`);
+
+ // Helper to send response
+ const sendResponse = (statusCode, contentType, body) => {
+ if (isHttp2) {
+ stream.respond({
+ ":status": statusCode,
+ "content-type": contentType,
+ "grpc-status": "0",
+ "grpc-message": "OK",
+ });
+ stream.end(body);
+ } else {
+ stream.writeHead(statusCode, {
+ "Content-Type": contentType,
+ "grpc-status": "0",
+ "grpc-message": "OK",
+ });
+ stream.end(body);
+ }
+ };
+
+ // Health check
+ if (url.includes("/health") || url === "/") {
+ sendResponse(200, "application/json", safeJsonStringify({ status: "ok" }));
+ return;
+ }
+
+ // Return captured headers (for debugging)
+ if (url === "/captured-headers") {
+ sendResponse(200, "application/json", safeJsonStringify(capturedRequests));
+ return;
+ }
+
+ // Mock Connect/gRPC responses
+ // These are minimal responses to prevent the login app from crashing
+ if (url.includes("GetLoginSettings") || url.includes("SettingsService")) {
+ sendResponse(200, "application/json", safeJsonStringify({ settings: {} }));
+ } else if (url.includes("GetBrandingSettings")) {
+ sendResponse(200, "application/json", safeJsonStringify({ settings: {} }));
+ } else if (url.includes("SessionService") || url.includes("Session")) {
+ sendResponse(200, "application/json", safeJsonStringify({ session: {} }));
+ } else if (url.includes("UserService") || url.includes("User")) {
+ sendResponse(200, "application/json", safeJsonStringify({ user: {} }));
+ } else if (url.includes("OrganizationService") || url.includes("Organization")) {
+ sendResponse(200, "application/json", safeJsonStringify({ result: [] }));
+ } else if (url.includes("IdentityProviderService") || url.includes("IdentityProvider")) {
+ sendResponse(200, "application/json", safeJsonStringify({ identityProviders: [] }));
+ } else {
+ sendResponse(200, "application/json", safeJsonStringify({}));
+ }
+}
+
+// Create HTTP/2 server (cleartext for testing)
+const http2Server = http2.createServer();
+
+http2Server.on("stream", (stream, headers) => {
+ handleRequest(stream, headers, true);
+});
+
+http2Server.on("error", (err) => console.error("HTTP/2 error:", err));
+
+// Also create HTTP/1.1 server for health checks from wget
+const httpServer = http.createServer((req, res) => {
+ // Create a headers object compatible with handleRequest
+ const headers = {
+ method: req.method,
+ url: req.url,
+ ...req.headers,
+ };
+ handleRequest(res, headers, false);
+});
+
+// Start HTTP/2 server on main port
+http2Server.listen(PORT, "0.0.0.0", () => {
+ console.log(`Mock Zitadel HTTP/2 server listening on port ${PORT}`);
+ console.log(`Captured headers will be written to: ${HEADERS_FILE}`);
+});
+
+// Start HTTP/1.1 server on a different port for health checks
+const HEALTH_PORT = 7432;
+httpServer.listen(HEALTH_PORT, "0.0.0.0", () => {
+ console.log(`Mock Zitadel HTTP/1.1 health server listening on port ${HEALTH_PORT}`);
+});
+
+// Graceful shutdown
+process.on("SIGTERM", () => {
+ console.log("Shutting down mock servers...");
+ http2Server.close(() => {
+ httpServer.close(() => {
+ console.log("Mock servers stopped");
+ process.exit(0);
+ });
+ });
+});
diff --git a/apps/login/dockerized/otel/otel-collector-config.yaml b/apps/login/dockerized/otel/otel-collector-config.yaml
new file mode 100644
index 000000000..550c6bf37
--- /dev/null
+++ b/apps/login/dockerized/otel/otel-collector-config.yaml
@@ -0,0 +1,43 @@
+receivers:
+ otlp:
+ protocols:
+ http:
+ endpoint: 0.0.0.0:4318
+
+processors:
+ batch:
+ timeout: 1s
+ send_batch_size: 1024
+
+exporters:
+ debug:
+ verbosity: detailed
+ file/traces:
+ path: /tmp/otel/traces.json
+ flush_interval: 1s
+ file/metrics:
+ path: /tmp/otel/metrics.json
+ flush_interval: 1s
+ file/logs:
+ path: /tmp/otel/logs.json
+ flush_interval: 1s
+
+extensions:
+ health_check:
+ endpoint: 0.0.0.0:13133
+
+service:
+ extensions: [health_check]
+ pipelines:
+ traces:
+ receivers: [otlp]
+ processors: [batch]
+ exporters: [debug, file/traces]
+ metrics:
+ receivers: [otlp]
+ processors: [batch]
+ exporters: [debug, file/metrics]
+ logs:
+ receivers: [otlp]
+ processors: [batch]
+ exporters: [debug, file/logs]
diff --git a/apps/login/dockerized/otel/otel.integration.test.ts b/apps/login/dockerized/otel/otel.integration.test.ts
new file mode 100644
index 000000000..2eec51d81
--- /dev/null
+++ b/apps/login/dockerized/otel/otel.integration.test.ts
@@ -0,0 +1,1205 @@
+import { describe, it, expect, beforeAll, afterAll } from "vitest";
+import { GenericContainer, type StartedTestContainer, Network, Wait } from "testcontainers";
+import type { StartedNetwork } from "testcontainers";
+import * as fs from "fs";
+import * as path from "path";
+
+const TEST_DIR = path.dirname(new URL(import.meta.url).pathname);
+const OUTPUT_DIR = path.join(TEST_DIR, "output");
+const LOGIN_APP_DIR = path.join(TEST_DIR, "../..");
+
+const DOCKER_TIMEOUT = 180000;
+const TEST_TIMEOUT = 30000;
+
+const LOGIN_IMAGE_TAG = `zitadel-login-test:${process.env.GITHUB_SHA || "local"}`;
+
+function readTraces(): string {
+ try {
+ return fs.readFileSync(path.join(OUTPUT_DIR, "traces.json"), "utf-8");
+ } catch {
+ return "";
+ }
+}
+
+function readMetrics(): string {
+ try {
+ return fs.readFileSync(path.join(OUTPUT_DIR, "metrics.json"), "utf-8");
+ } catch {
+ return "";
+ }
+}
+
+function readLogs(): string {
+ try {
+ return fs.readFileSync(path.join(OUTPUT_DIR, "logs.json"), "utf-8");
+ } catch {
+ return "";
+ }
+}
+
+function readCapturedHeaders(): Array<{ traceparent: string | null; url: string; method: string }> {
+ try {
+ return JSON.parse(fs.readFileSync(path.join(OUTPUT_DIR, "captured-headers.json"), "utf-8"));
+ } catch {
+ return [];
+ }
+}
+
+async function waitForFile(filePath: string, maxAttempts = 30, delayMs = 1000): Promise {
+ for (let i = 0; i < maxAttempts; i++) {
+ try {
+ const stat = fs.statSync(filePath);
+ if (stat.size > 0) {
+ console.log(`[waitForFile] ${path.basename(filePath)} ready (${stat.size} bytes, attempt ${i + 1})`);
+ return true;
+ }
+ console.log(`[waitForFile] ${path.basename(filePath)} exists but empty (attempt ${i + 1})`);
+ } catch {
+ if (i === 0 || i % 10 === 0) {
+ console.log(`[waitForFile] ${path.basename(filePath)} not found (attempt ${i + 1})`);
+ }
+ }
+ await new Promise((r) => setTimeout(r, delayMs));
+ }
+ console.log(`[waitForFile] ${path.basename(filePath)} timed out after ${maxAttempts} attempts`);
+ return false;
+}
+
+async function getContainerLogs(container: StartedTestContainer, timeoutMs = 2000): Promise {
+ const stream = await container.logs();
+ return new Promise((resolve) => {
+ let output = "";
+ const timeout = setTimeout(() => {
+ stream.destroy();
+ resolve(output);
+ }, timeoutMs);
+ stream.on("data", (chunk: Buffer) => {
+ output += chunk.toString();
+ });
+ stream.on("end", () => {
+ clearTimeout(timeout);
+ resolve(output);
+ });
+ stream.on("error", () => {
+ clearTimeout(timeout);
+ resolve(output);
+ });
+ });
+}
+
+describe("OpenTelemetry Integration", () => {
+ let network: StartedNetwork;
+ let otelCollector: StartedTestContainer;
+ let mockZitadel: StartedTestContainer;
+ let loginApp: StartedTestContainer;
+ let appUrl: string;
+ let prometheusUrl: string;
+
+ beforeAll(async () => {
+ if (fs.existsSync(OUTPUT_DIR)) {
+ fs.rmSync(OUTPUT_DIR, { recursive: true });
+ }
+ fs.mkdirSync(OUTPUT_DIR, { recursive: true });
+
+ fs.writeFileSync(path.join(OUTPUT_DIR, "traces.json"), "");
+ fs.writeFileSync(path.join(OUTPUT_DIR, "logs.json"), "");
+ fs.writeFileSync(path.join(OUTPUT_DIR, "metrics.json"), "");
+
+ network = await new Network().start();
+
+ otelCollector = await new GenericContainer("otel/opentelemetry-collector-contrib:0.145.0")
+ .withNetwork(network)
+ .withNetworkAliases("otel-collector")
+ .withUser("0:0")
+ .withCommand(["--config=/etc/otel-collector-config.yaml"])
+ .withCopyFilesToContainer([
+ { source: path.join(TEST_DIR, "otel-collector-config.yaml"), target: "/etc/otel-collector-config.yaml" },
+ ])
+ .withBindMounts([{ source: OUTPUT_DIR, target: "/tmp/otel" }])
+ .withExposedPorts(4318, 8888, 13133)
+ .withWaitStrategy(Wait.forHttp("/", 13133))
+ .start();
+
+ mockZitadel = await new GenericContainer("node:20-alpine")
+ .withNetwork(network)
+ .withNetworkAliases("mock-zitadel")
+ .withCopyFilesToContainer([
+ { source: path.join(TEST_DIR, "mock-zitadel-server.js"), target: "/app/server.js" },
+ ])
+ .withBindMounts([{ source: OUTPUT_DIR, target: "/tmp/otel" }])
+ .withEnvironment({
+ PORT: "8080",
+ OUTPUT_DIR: "/tmp/otel",
+ })
+ .withCommand(["node", "/app/server.js"])
+ .withExposedPorts(8080, 7432)
+ .withWaitStrategy(Wait.forLogMessage("Mock Zitadel HTTP/2 server listening"))
+ .start();
+
+ try {
+ await GenericContainer.fromDockerfile(LOGIN_APP_DIR).build(LOGIN_IMAGE_TAG);
+ } catch (err) {
+ throw new Error(`Failed to build login Docker image: ${err instanceof Error ? err.message : err}`);
+ }
+
+ loginApp = await new GenericContainer(LOGIN_IMAGE_TAG)
+ .withNetwork(network)
+ .withExposedPorts(3000, 9464)
+ .withEnvironment({
+ ZITADEL_API_URL: "http://mock-zitadel:8080",
+ ZITADEL_SERVICE_USER_TOKEN: "test-token-for-otel-integration-tests",
+ OTEL_SERVICE_NAME: "zitadel-login-test",
+ OTEL_EXPORTER_OTLP_ENDPOINT: "http://otel-collector:4318",
+ OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf",
+ OTEL_EXPORTER_OTLP_HEADERS: "x-test-header=test-value-123,x-another-header=another-value",
+ OTEL_LOG_LEVEL: "info",
+ OTEL_METRICS_EXPORTER: "otlp,prometheus",
+ OTEL_LOGS_EXPORTER: "otlp",
+ OTEL_EXPORTER_PROMETHEUS_PORT: "9464",
+ OTEL_BSP_SCHEDULE_DELAY: "1000",
+ OTEL_METRIC_EXPORT_INTERVAL: "5000",
+ DEBUG: "true",
+ })
+ .withStartupTimeout(120000)
+ .withWaitStrategy(Wait.forHttp("/ui/v2/login/healthy", 3000))
+ .start();
+
+ appUrl = `http://${loginApp.getHost()}:${loginApp.getMappedPort(3000)}`;
+ prometheusUrl = `http://${loginApp.getHost()}:${loginApp.getMappedPort(9464)}/metrics`;
+
+ console.log(`[PORTS] APP_URL: ${appUrl}`);
+ console.log(`[PORTS] PROMETHEUS_URL: ${prometheusUrl}`);
+
+ await fetch(`${appUrl}/ui/v2/login/otel-test`);
+ await fetch(`${appUrl}/ui/v2/login/loginname`, { redirect: "manual" });
+ await fetch(`${appUrl}/ui/v2/login/healthy`);
+ await fetch(`${appUrl}/ui/v2/login/this-does-not-exist-404`);
+
+ await new Promise((r) => setTimeout(r, 3000));
+
+ const tracesReady = await waitForFile(path.join(OUTPUT_DIR, "traces.json"), 60, 1000);
+ if (!tracesReady) throw new Error("Traces file not found");
+ const logsReady = await waitForFile(path.join(OUTPUT_DIR, "logs.json"), 60, 1000);
+ if (!logsReady) throw new Error("Logs file not found");
+ const metricsReady = await waitForFile(path.join(OUTPUT_DIR, "metrics.json"), 60, 1000);
+ if (!metricsReady) throw new Error("Metrics file not found");
+ }, DOCKER_TIMEOUT);
+
+ afterAll(async () => {
+ await loginApp?.stop().catch(() => {});
+ await mockZitadel?.stop().catch(() => {});
+ await otelCollector?.stop().catch(() => {});
+ await network?.stop().catch(() => {});
+ }, DOCKER_TIMEOUT);
+
+ describe("Traces", () => {
+ describe("OTLP Export", () => {
+ it("exports spans to collector", () => {
+ expect(readTraces().length).toBeGreaterThan(0);
+ }, TEST_TIMEOUT);
+
+ it("contains valid trace IDs", () => {
+ expect(readTraces()).toMatch(/"traceId":"[a-f0-9]{32}"/);
+ }, TEST_TIMEOUT);
+
+ it("contains valid span IDs", () => {
+ expect(readTraces()).toMatch(/"spanId":"[a-f0-9]{16}"/);
+ }, TEST_TIMEOUT);
+
+ it("contains start timestamps", () => {
+ expect(readTraces()).toContain("startTimeUnixNano");
+ }, TEST_TIMEOUT);
+
+ it("contains end timestamps", () => {
+ expect(readTraces()).toContain("endTimeUnixNano");
+ }, TEST_TIMEOUT);
+
+ it("contains span status", () => {
+ expect(readTraces()).toContain('"status"');
+ }, TEST_TIMEOUT);
+
+ it("contains span kind", () => {
+ expect(readTraces()).toContain('"kind"');
+ }, TEST_TIMEOUT);
+
+ it("contains custom test spans", () => {
+ expect(readTraces()).toContain("test-span");
+ }, TEST_TIMEOUT);
+ });
+
+ describe("Span Attributes", () => {
+ it("contains http.method", () => {
+ expect(readTraces()).toContain('"http.method"');
+ }, TEST_TIMEOUT);
+
+ it("contains http.url", () => {
+ expect(readTraces()).toContain('"http.url"');
+ }, TEST_TIMEOUT);
+
+ it("contains http.status_code", () => {
+ expect(readTraces()).toContain('"http.status_code"');
+ }, TEST_TIMEOUT);
+
+ it("contains http.target", () => {
+ expect(readTraces()).toContain('"http.target"');
+ }, TEST_TIMEOUT);
+
+ it("contains net.host.name", () => {
+ expect(readTraces()).toContain('"net.host.name"');
+ }, TEST_TIMEOUT);
+
+ it("contains net.host.port", () => {
+ expect(readTraces()).toContain('"net.host.port"');
+ }, TEST_TIMEOUT);
+
+ it("contains custom test.attribute", () => {
+ expect(readTraces()).toContain('"test.attribute"');
+ }, TEST_TIMEOUT);
+
+ it("contains custom test.timestamp", () => {
+ expect(readTraces()).toContain('"test.timestamp"');
+ }, TEST_TIMEOUT);
+ });
+
+ describe("Span Status", () => {
+ it("contains status code OK", () => {
+ expect(readTraces()).toContain('"code":1');
+ }, TEST_TIMEOUT);
+
+ it("contains status code ERROR", () => {
+ expect(readTraces()).toContain('"code":2');
+ }, TEST_TIMEOUT);
+
+ it("contains error message on failed spans", () => {
+ expect(readTraces()).toContain('"message"');
+ }, TEST_TIMEOUT);
+ });
+
+ describe("Span Hierarchy", () => {
+ it("contains parent span IDs", () => {
+ expect(readTraces()).toContain('"parentSpanId"');
+ }, TEST_TIMEOUT);
+
+ it("has child spans linked to parent", () => {
+ const traces = readTraces();
+ const parentMatches = traces.match(/"parentSpanId":"([a-f0-9]{16})"/g) || [];
+ expect(parentMatches.length).toBeGreaterThan(0);
+ }, TEST_TIMEOUT);
+ });
+
+ describe("Span Filtering", () => {
+ it("HTTP instrumentation excludes /healthy", () => {
+ const tracesContent = readTraces();
+ const traceObjects = tracesContent
+ .split("\n")
+ .filter((line) => line.trim())
+ .map((line) => JSON.parse(line));
+
+ const allResourceSpans = traceObjects.flatMap((t) => t.resourceSpans || []);
+
+ const httpSpans = allResourceSpans
+ .flatMap((rs: { scopeSpans: Array<{ scope: { name: string }; spans: unknown[] }> }) => rs.scopeSpans)
+ .filter((ss: { scope: { name: string } }) => ss.scope.name === "@opentelemetry/instrumentation-http")
+ .flatMap((ss: { spans: unknown[] }) => ss.spans);
+ const healthySpans = httpSpans.filter(
+ (s: { attributes?: Array<{ key: string; value: { stringValue?: string } }> }) =>
+ s.attributes?.some(
+ (a: { key: string; value: { stringValue?: string } }) =>
+ a.key === "http.target" && a.value.stringValue?.includes("/healthy"),
+ ),
+ );
+ expect(healthySpans).toHaveLength(0);
+ }, TEST_TIMEOUT);
+
+ it("includes /otel-test in traces", () => {
+ const traces = readTraces();
+ expect(traces).toContain("otel-test");
+ }, TEST_TIMEOUT);
+
+ it("includes /loginname in traces", () => {
+ const traces = readTraces();
+ expect(traces).toContain("loginname");
+ }, TEST_TIMEOUT);
+ });
+
+ describe("Span Events", () => {
+ it("contains exception events", () => {
+ expect(readTraces()).toContain('"name":"exception"');
+ }, TEST_TIMEOUT);
+
+ it("contains exception.type attribute", () => {
+ expect(readTraces()).toContain('"exception.type"');
+ }, TEST_TIMEOUT);
+
+ it("contains exception.message attribute", () => {
+ expect(readTraces()).toContain('"exception.message"');
+ }, TEST_TIMEOUT);
+
+ it("contains exception.stacktrace attribute", () => {
+ expect(readTraces()).toContain('"exception.stacktrace"');
+ }, TEST_TIMEOUT);
+ });
+
+ describe("gRPC Spans", () => {
+ it("contains rpc.system attribute", () => {
+ expect(readTraces()).toContain('"rpc.system"');
+ }, TEST_TIMEOUT);
+
+ it("contains rpc.service attribute", () => {
+ expect(readTraces()).toContain('"rpc.service"');
+ }, TEST_TIMEOUT);
+
+ it("contains rpc.method attribute", () => {
+ expect(readTraces()).toContain('"rpc.method"');
+ }, TEST_TIMEOUT);
+
+ it("contains server.address attribute", () => {
+ expect(readTraces()).toContain('"server.address"');
+ }, TEST_TIMEOUT);
+
+ it("contains rpc.grpc.status_code attribute", () => {
+ expect(readTraces()).toContain('"rpc.grpc.status_code"');
+ }, TEST_TIMEOUT);
+
+ it("identifies grpc as rpc system", () => {
+ expect(readTraces()).toContain('"grpc"');
+ }, TEST_TIMEOUT);
+ });
+
+ describe("HTTP Status Codes", () => {
+ it("records HTTP 200 status", () => {
+ expect(readTraces()).toContain('"intValue":"200"');
+ }, TEST_TIMEOUT);
+
+ it("records HTTP 404 status", () => {
+ expect(readTraces()).toContain('"intValue":"404"');
+ }, TEST_TIMEOUT);
+
+ it("records OK status text", () => {
+ expect(readTraces()).toContain('"OK"');
+ }, TEST_TIMEOUT);
+
+ it("records NOT FOUND status text", () => {
+ expect(readTraces()).toContain('"NOT FOUND"');
+ }, TEST_TIMEOUT);
+ });
+
+ describe("Trace Propagation", () => {
+ it("propagates traceparent to gRPC", () => {
+ const requests = readCapturedHeaders();
+ const grpcCalls = requests.filter((r) => r.method === "POST" && r.traceparent !== null);
+ expect(grpcCalls.length).toBeGreaterThan(0);
+ }, TEST_TIMEOUT);
+
+ it("uses W3C traceparent format", () => {
+ const requests = readCapturedHeaders();
+ const grpcCalls = requests.filter((r) => r.traceparent !== null);
+ grpcCalls.forEach((r) => expect(r.traceparent).toMatch(/^[0-9a-f]{2}-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$/));
+ }, TEST_TIMEOUT);
+
+ it("uses traceparent version 00", () => {
+ const requests = readCapturedHeaders();
+ const grpcCalls = requests.filter((r) => r.traceparent !== null);
+ grpcCalls.forEach((r) => expect(r.traceparent).toMatch(/^00-/));
+ }, TEST_TIMEOUT);
+
+ it("sets sampled flag to 01", () => {
+ const requests = readCapturedHeaders();
+ const grpcCalls = requests.filter((r) => r.traceparent !== null);
+ grpcCalls.forEach((r) => expect(r.traceparent).toMatch(/-01$/));
+ }, TEST_TIMEOUT);
+
+ it("maintains trace ID consistency", () => {
+ const requests = readCapturedHeaders();
+ const grpcCalls = requests.filter((r) => r.method === "POST" && r.traceparent !== null);
+ const traceIds = grpcCalls.map((r) => r.traceparent!.split("-")[1]);
+ const uniqueIds = new Set(traceIds);
+ expect(uniqueIds.size).toBeLessThan(traceIds.length);
+ }, TEST_TIMEOUT);
+ });
+
+ describe("Resource Attributes", () => {
+ it("contains service.name", () => {
+ expect(readTraces()).toContain('"zitadel-login-test"');
+ }, TEST_TIMEOUT);
+
+ it("contains deployment.environment.name", () => {
+ expect(readTraces()).toContain('"deployment.environment.name"');
+ }, TEST_TIMEOUT);
+
+ it("contains process.runtime.name", () => {
+ expect(readTraces()).toContain('"nodejs"');
+ }, TEST_TIMEOUT);
+
+ it("contains process.runtime.version", () => {
+ expect(readTraces()).toContain('"process.runtime.version"');
+ }, TEST_TIMEOUT);
+
+ it("contains container.id when running in container", () => {
+ expect(readTraces()).toContain('"container.id"');
+ }, TEST_TIMEOUT);
+
+ it("contains host.name", () => {
+ expect(readTraces()).toContain('"host.name"');
+ }, TEST_TIMEOUT);
+
+ it("contains host.arch", () => {
+ expect(readTraces()).toContain('"host.arch"');
+ }, TEST_TIMEOUT);
+
+ it("contains process.pid", () => {
+ expect(readTraces()).toContain('"process.pid"');
+ }, TEST_TIMEOUT);
+ });
+ });
+
+ describe("Metrics", () => {
+ describe("OTLP Export", () => {
+ it("exports metrics to collector", () => {
+ expect(readMetrics().length).toBeGreaterThan(0);
+ }, TEST_TIMEOUT);
+
+ it("contains timestamps", () => {
+ expect(readMetrics()).toContain("timeUnixNano");
+ }, TEST_TIMEOUT);
+
+ it("contains data points", () => {
+ expect(readMetrics()).toContain("dataPoints");
+ }, TEST_TIMEOUT);
+ });
+
+ describe("Prometheus Endpoint", () => {
+ it("returns HTTP 200", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(response.status).toBe(200);
+ }, TEST_TIMEOUT);
+
+ it("returns text/plain content type", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(response.headers.get("content-type")).toContain("text/plain");
+ }, TEST_TIMEOUT);
+ });
+
+ describe("Runtime Metrics", () => {
+ it("exposes nodejs_eventloop_utilization", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain("nodejs_eventloop_utilization");
+ }, TEST_TIMEOUT);
+
+ it("exposes nodejs_eventloop_delay", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain("nodejs_eventloop_delay");
+ }, TEST_TIMEOUT);
+
+ it("exposes v8js_memory_heap_used", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain("v8js_memory_heap_used");
+ }, TEST_TIMEOUT);
+
+ it("exposes v8js_memory_heap_limit", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain("v8js_memory_heap_limit");
+ }, TEST_TIMEOUT);
+
+ it("exposes v8js_gc_duration", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain("v8js_gc_duration");
+ }, TEST_TIMEOUT);
+ });
+
+ describe("HTTP Metrics", () => {
+ it("exposes http_server_duration", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain("http_server_duration");
+ }, TEST_TIMEOUT);
+
+ it("exposes http_server_duration_sum", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain("http_server_duration_sum");
+ }, TEST_TIMEOUT);
+
+ it("exposes http_server_duration_count", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain("http_server_duration_count");
+ }, TEST_TIMEOUT);
+
+ it("exposes http_server_duration_bucket", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain("http_server_duration_bucket");
+ }, TEST_TIMEOUT);
+
+ it("records status code 200", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain('http_status_code="200"');
+ }, TEST_TIMEOUT);
+
+ it("records status code 404", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain('http_status_code="404"');
+ }, TEST_TIMEOUT);
+
+ it("records HTTP GET method", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain('http_method="GET"');
+ }, TEST_TIMEOUT);
+ });
+
+ describe("Application Metrics", () => {
+ it("exposes http_server_requests_total", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain("http_server_requests_total");
+ }, TEST_TIMEOUT);
+
+ it("exposes http_server_active_requests", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain("http_server_active_requests");
+ }, TEST_TIMEOUT);
+
+ it("exposes login_auth_attempts_total", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain("login_auth_attempts_total");
+ }, TEST_TIMEOUT);
+
+ it("exposes login_auth_successes_total", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain("login_auth_successes_total");
+ }, TEST_TIMEOUT);
+
+ it("exposes login_session_creation_duration", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain("login_session_creation_duration");
+ }, TEST_TIMEOUT);
+
+ it("exposes http_server_response_status_total", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain("http_server_response_status_total");
+ }, TEST_TIMEOUT);
+
+ it("exposes http_server_request_duration_seconds", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain("http_server_request_duration_seconds");
+ }, TEST_TIMEOUT);
+ });
+
+ describe("Custom Test Metrics", () => {
+ it("exposes otel_test_requests_total", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain("otel_test_requests_total");
+ }, TEST_TIMEOUT);
+
+ it("exposes otel_test_duration_seconds", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain("otel_test_duration_seconds");
+ }, TEST_TIMEOUT);
+ });
+
+ describe("Metric Labels", () => {
+ it("includes method label on auth metrics", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain('method="test"');
+ }, TEST_TIMEOUT);
+
+ it("includes organization label on auth metrics", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain('organization="test-org"');
+ }, TEST_TIMEOUT);
+
+ it("includes route label on request metrics", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain('route="/otel-test"');
+ }, TEST_TIMEOUT);
+
+ it("includes success label on session metrics", async () => {
+ const response = await fetch(prometheusUrl);
+ expect(await response.text()).toContain('success="true"');
+ }, TEST_TIMEOUT);
+ });
+
+ describe("Resource Attributes", () => {
+ it("contains service.name", () => {
+ expect(readMetrics()).toContain('"zitadel-login-test"');
+ }, TEST_TIMEOUT);
+
+ it("contains deployment.environment.name", () => {
+ expect(readMetrics()).toContain('"deployment.environment.name"');
+ }, TEST_TIMEOUT);
+
+ it("contains process.runtime.name", () => {
+ expect(readMetrics()).toContain('"nodejs"');
+ }, TEST_TIMEOUT);
+
+ it("contains process.runtime.version", () => {
+ expect(readMetrics()).toContain('"process.runtime.version"');
+ }, TEST_TIMEOUT);
+
+ it("contains container.id when running in container", () => {
+ expect(readMetrics()).toContain('"container.id"');
+ }, TEST_TIMEOUT);
+
+ it("contains host.name", () => {
+ expect(readMetrics()).toContain('"host.name"');
+ }, TEST_TIMEOUT);
+
+ it("contains host.arch", () => {
+ expect(readMetrics()).toContain('"host.arch"');
+ }, TEST_TIMEOUT);
+
+ it("contains process.pid", () => {
+ expect(readMetrics()).toContain('"process.pid"');
+ }, TEST_TIMEOUT);
+ });
+ });
+
+ describe("Logs", () => {
+ describe("OTLP Export", () => {
+ it("exports logs to collector", () => {
+ expect(readLogs().length).toBeGreaterThan(0);
+ }, TEST_TIMEOUT);
+
+ it("contains timestamps", () => {
+ expect(readLogs()).toContain("timeUnixNano");
+ }, TEST_TIMEOUT);
+
+ it("contains severity", () => {
+ expect(readLogs()).toContain("severityNumber");
+ }, TEST_TIMEOUT);
+
+ it("contains body", () => {
+ expect(readLogs()).toContain("body");
+ }, TEST_TIMEOUT);
+ });
+
+ describe("Log Content", () => {
+ it("contains OTEL test log message", () => {
+ expect(readLogs()).toContain("OTEL test endpoint called");
+ }, TEST_TIMEOUT);
+
+ it("contains test completed log message", () => {
+ expect(readLogs()).toContain("OTEL test completed successfully");
+ }, TEST_TIMEOUT);
+
+ it("contains INFO severity level", () => {
+ const logs = readLogs();
+ expect(logs).toContain('"severityText":"info"');
+ }, TEST_TIMEOUT);
+
+ it("contains severity number 9 for INFO", () => {
+ expect(readLogs()).toContain('"severityNumber":9');
+ }, TEST_TIMEOUT);
+ });
+
+ describe("Log-Trace Correlation", () => {
+ it("includes traceId in log records", () => {
+ expect(readLogs()).toContain('"traceId"');
+ }, TEST_TIMEOUT);
+
+ it("includes spanId in log records", () => {
+ expect(readLogs()).toContain('"spanId"');
+ }, TEST_TIMEOUT);
+
+ it("has valid trace ID format", () => {
+ expect(readLogs()).toMatch(/"traceId":"[a-f0-9]{32}"/);
+ }, TEST_TIMEOUT);
+
+ it("has valid span ID format", () => {
+ expect(readLogs()).toMatch(/"spanId":"[a-f0-9]{16}"/);
+ }, TEST_TIMEOUT);
+
+ it("includes trace_id attribute", () => {
+ expect(readLogs()).toContain('"trace_id"');
+ }, TEST_TIMEOUT);
+
+ it("includes span_id attribute", () => {
+ expect(readLogs()).toContain('"span_id"');
+ }, TEST_TIMEOUT);
+
+ it("includes trace_flags attribute", () => {
+ expect(readLogs()).toContain('"trace_flags"');
+ }, TEST_TIMEOUT);
+ });
+
+ describe("Log Attributes", () => {
+ it("includes context attribute", () => {
+ expect(readLogs()).toContain('"context"');
+ }, TEST_TIMEOUT);
+
+ it("includes service attribute", () => {
+ expect(readLogs()).toContain('"service"');
+ }, TEST_TIMEOUT);
+
+ it("includes timestamp attribute", () => {
+ expect(readLogs()).toContain('"timestamp"');
+ }, TEST_TIMEOUT);
+
+ it("includes custom test attribute", () => {
+ expect(readLogs()).toContain('"test"');
+ }, TEST_TIMEOUT);
+
+ it("includes duration attribute", () => {
+ expect(readLogs()).toContain('"duration"');
+ }, TEST_TIMEOUT);
+ });
+
+ describe("Resource Attributes", () => {
+ it("contains service.name", () => {
+ expect(readLogs()).toContain('"zitadel-login-test"');
+ }, TEST_TIMEOUT);
+
+ it("contains deployment.environment.name", () => {
+ expect(readLogs()).toContain('"deployment.environment.name"');
+ }, TEST_TIMEOUT);
+
+ it("contains process.runtime.name", () => {
+ expect(readLogs()).toContain('"nodejs"');
+ }, TEST_TIMEOUT);
+
+ it("contains process.runtime.version", () => {
+ expect(readLogs()).toContain('"process.runtime.version"');
+ }, TEST_TIMEOUT);
+
+ it("contains container.id when running in container", () => {
+ expect(readLogs()).toContain('"container.id"');
+ }, TEST_TIMEOUT);
+
+ it("contains host.name", () => {
+ expect(readLogs()).toContain('"host.name"');
+ }, TEST_TIMEOUT);
+
+ it("contains host.arch", () => {
+ expect(readLogs()).toContain('"host.arch"');
+ }, TEST_TIMEOUT);
+
+ it("contains process.pid", () => {
+ expect(readLogs()).toContain('"process.pid"');
+ }, TEST_TIMEOUT);
+ });
+ });
+});
+
+describe("OpenTelemetry Disabled", () => {
+ let network: StartedNetwork;
+ let mockZitadel: StartedTestContainer;
+ let loginApp: StartedTestContainer;
+ let appUrl: string;
+
+ beforeAll(async () => {
+ network = await new Network().start();
+
+ mockZitadel = await new GenericContainer("node:20-alpine")
+ .withNetwork(network)
+ .withNetworkAliases("mock-zitadel")
+ .withCopyFilesToContainer([
+ { source: path.join(TEST_DIR, "mock-zitadel-server.js"), target: "/app/server.js" },
+ ])
+ .withEnvironment({
+ PORT: "8080",
+ })
+ .withCommand(["node", "/app/server.js"])
+ .withExposedPorts(8080, 7432)
+ .withWaitStrategy(Wait.forLogMessage("Mock Zitadel HTTP/2 server listening"))
+ .start();
+
+ loginApp = await new GenericContainer(LOGIN_IMAGE_TAG)
+ .withNetwork(network)
+ .withExposedPorts(3000, 9464)
+ .withEnvironment({
+ ZITADEL_API_URL: "http://mock-zitadel:8080",
+ ZITADEL_SERVICE_USER_TOKEN: "test-token",
+ OTEL_SDK_DISABLED: "true",
+ })
+ .withStartupTimeout(120000)
+ .withWaitStrategy(Wait.forHttp("/ui/v2/login/healthy", 3000))
+ .start();
+
+ appUrl = `http://${loginApp.getHost()}:${loginApp.getMappedPort(3000)}`;
+ console.log(`[OTEL DISABLED] APP_URL: ${appUrl}`);
+ }, DOCKER_TIMEOUT);
+
+ afterAll(async () => {
+ await loginApp?.stop().catch(() => {});
+ await mockZitadel?.stop().catch(() => {});
+ await network?.stop().catch(() => {});
+ }, DOCKER_TIMEOUT);
+
+ describe("Application Health", () => {
+ it("starts successfully with OTEL_SDK_DISABLED=true", async () => {
+ const response = await fetch(`${appUrl}/ui/v2/login/healthy`);
+ expect(response.ok).toBe(true);
+ }, TEST_TIMEOUT);
+
+ it("responds to health checks", async () => {
+ const response = await fetch(`${appUrl}/ui/v2/login/healthy`);
+ expect(response.status).toBe(200);
+ }, TEST_TIMEOUT);
+ });
+
+ describe("Metrics Endpoint", () => {
+ it("metrics endpoint is not available when OTEL is disabled", async () => {
+ const metricsUrl = `http://${loginApp.getHost()}:${loginApp.getMappedPort(9464)}/metrics`;
+ try {
+ await fetch(metricsUrl);
+ expect.fail("Metrics endpoint should not be available");
+ } catch (error) {
+ expect(error).toBeDefined();
+ }
+ }, TEST_TIMEOUT);
+ });
+
+ describe("Logging", () => {
+ it("outputs logs to console via Winston", async () => {
+ await fetch(`${appUrl}/ui/v2/login/healthy`);
+ await new Promise((resolve) => setTimeout(resolve, 1000));
+ const logOutput = await getContainerLogs(loginApp);
+ expect(logOutput.length).toBeGreaterThan(0);
+ }, TEST_TIMEOUT);
+
+ it("does not contain OTEL SDK initialization logs", async () => {
+ const logOutput = await getContainerLogs(loginApp);
+ expect(logOutput).not.toContain("OpenTelemetry SDK started");
+ expect(logOutput).not.toContain("Exporter");
+ }, TEST_TIMEOUT);
+ });
+});
+
+describe("Log Level Configuration", () => {
+ describe("OTEL Disabled", () => {
+ describe("LOG_LEVEL=debug", () => {
+ let network: StartedNetwork;
+ let mockZitadel: StartedTestContainer;
+ let loginApp: StartedTestContainer;
+ let appUrl: string;
+
+ beforeAll(async () => {
+ network = await new Network().start();
+
+ mockZitadel = await new GenericContainer("node:20-alpine")
+ .withNetwork(network)
+ .withNetworkAliases("mock-zitadel")
+ .withCopyFilesToContainer([
+ { source: path.join(TEST_DIR, "mock-zitadel-server.js"), target: "/app/server.js" },
+ ])
+ .withEnvironment({
+ PORT: "8080",
+ })
+ .withCommand(["node", "/app/server.js"])
+ .withExposedPorts(8080, 7432)
+ .withWaitStrategy(Wait.forLogMessage("Mock Zitadel HTTP/2 server listening"))
+ .start();
+
+ loginApp = await new GenericContainer(LOGIN_IMAGE_TAG)
+ .withNetwork(network)
+ .withExposedPorts(3000)
+ .withEnvironment({
+ ZITADEL_API_URL: "http://mock-zitadel:8080",
+ ZITADEL_SERVICE_USER_TOKEN: "test-token",
+ OTEL_SDK_DISABLED: "true",
+ LOG_LEVEL: "debug",
+ })
+ .withStartupTimeout(120000)
+ .withWaitStrategy(Wait.forHttp("/ui/v2/login/healthy", 3000))
+ .start();
+
+ appUrl = `http://${loginApp.getHost()}:${loginApp.getMappedPort(3000)}`;
+ console.log(`[OTEL_DISABLED + LOG_LEVEL=debug] APP_URL: ${appUrl}`);
+
+ await fetch(`${appUrl}/ui/v2/login/healthy`);
+ await fetch(`${appUrl}/ui/v2/login/otel-test`);
+ await new Promise((r) => setTimeout(r, 1000));
+ }, DOCKER_TIMEOUT);
+
+ afterAll(async () => {
+ await loginApp?.stop().catch(() => {});
+ await mockZitadel?.stop().catch(() => {});
+ await network?.stop().catch(() => {});
+ }, DOCKER_TIMEOUT);
+
+ it("starts successfully", async () => {
+ const response = await fetch(`${appUrl}/ui/v2/login/healthy`);
+ expect(response.ok).toBe(true);
+ }, TEST_TIMEOUT);
+
+ it("includes info level logs in console", async () => {
+ const logOutput = await getContainerLogs(loginApp);
+ expect(logOutput).toContain('"level":"info"');
+ }, TEST_TIMEOUT);
+ });
+
+ describe("LOG_LEVEL=warn", () => {
+ let network: StartedNetwork;
+ let mockZitadel: StartedTestContainer;
+ let loginApp: StartedTestContainer;
+ let appUrl: string;
+
+ beforeAll(async () => {
+ network = await new Network().start();
+
+ mockZitadel = await new GenericContainer("node:20-alpine")
+ .withNetwork(network)
+ .withNetworkAliases("mock-zitadel")
+ .withCopyFilesToContainer([
+ { source: path.join(TEST_DIR, "mock-zitadel-server.js"), target: "/app/server.js" },
+ ])
+ .withEnvironment({
+ PORT: "8080",
+ })
+ .withCommand(["node", "/app/server.js"])
+ .withExposedPorts(8080, 7432)
+ .withWaitStrategy(Wait.forLogMessage("Mock Zitadel HTTP/2 server listening"))
+ .start();
+
+ loginApp = await new GenericContainer(LOGIN_IMAGE_TAG)
+ .withNetwork(network)
+ .withExposedPorts(3000)
+ .withEnvironment({
+ ZITADEL_API_URL: "http://mock-zitadel:8080",
+ ZITADEL_SERVICE_USER_TOKEN: "test-token",
+ OTEL_SDK_DISABLED: "true",
+ LOG_LEVEL: "warn",
+ })
+ .withStartupTimeout(120000)
+ .withWaitStrategy(Wait.forHttp("/ui/v2/login/healthy", 3000))
+ .start();
+
+ appUrl = `http://${loginApp.getHost()}:${loginApp.getMappedPort(3000)}`;
+ console.log(`[OTEL_DISABLED + LOG_LEVEL=warn] APP_URL: ${appUrl}`);
+
+ await fetch(`${appUrl}/ui/v2/login/healthy`);
+ await new Promise((r) => setTimeout(r, 1000));
+ }, DOCKER_TIMEOUT);
+
+ afterAll(async () => {
+ await loginApp?.stop().catch(() => {});
+ await mockZitadel?.stop().catch(() => {});
+ await network?.stop().catch(() => {});
+ }, DOCKER_TIMEOUT);
+
+ it("starts successfully", async () => {
+ const response = await fetch(`${appUrl}/ui/v2/login/healthy`);
+ expect(response.ok).toBe(true);
+ }, TEST_TIMEOUT);
+
+ it("does not include info level logs in console", async () => {
+ const logOutput = await getContainerLogs(loginApp);
+ expect(logOutput).not.toContain('"level":"info"');
+ }, TEST_TIMEOUT);
+
+ it("does not include debug level logs in console", async () => {
+ const logOutput = await getContainerLogs(loginApp);
+ expect(logOutput).not.toContain('"level":"debug"');
+ }, TEST_TIMEOUT);
+ });
+ });
+
+ describe("OTEL Enabled", () => {
+ const OTEL_LOG_OUTPUT_DIR = path.join(OUTPUT_DIR, "loglevel");
+
+ describe("LOG_LEVEL=debug", () => {
+ let network: StartedNetwork;
+ let otelCollector: StartedTestContainer;
+ let mockZitadel: StartedTestContainer;
+ let loginApp: StartedTestContainer;
+ let appUrl: string;
+ let logOutputDir: string;
+
+ beforeAll(async () => {
+ logOutputDir = path.join(OTEL_LOG_OUTPUT_DIR, "debug");
+ if (fs.existsSync(logOutputDir)) {
+ fs.rmSync(logOutputDir, { recursive: true });
+ }
+ fs.mkdirSync(logOutputDir, { recursive: true });
+ fs.writeFileSync(path.join(logOutputDir, "logs.json"), "");
+
+ network = await new Network().start();
+
+ otelCollector = await new GenericContainer("otel/opentelemetry-collector-contrib:0.145.0")
+ .withNetwork(network)
+ .withNetworkAliases("otel-collector")
+ .withUser("0:0")
+ .withCommand(["--config=/etc/otel-collector-config.yaml"])
+ .withCopyFilesToContainer([
+ { source: path.join(TEST_DIR, "otel-collector-config.yaml"), target: "/etc/otel-collector-config.yaml" },
+ ])
+ .withBindMounts([{ source: logOutputDir, target: "/tmp/otel" }])
+ .withExposedPorts(4318, 13133)
+ .withWaitStrategy(Wait.forHttp("/", 13133))
+ .start();
+
+ mockZitadel = await new GenericContainer("node:20-alpine")
+ .withNetwork(network)
+ .withNetworkAliases("mock-zitadel")
+ .withCopyFilesToContainer([
+ { source: path.join(TEST_DIR, "mock-zitadel-server.js"), target: "/app/server.js" },
+ ])
+ .withEnvironment({
+ PORT: "8080",
+ })
+ .withCommand(["node", "/app/server.js"])
+ .withExposedPorts(8080, 7432)
+ .withWaitStrategy(Wait.forLogMessage("Mock Zitadel HTTP/2 server listening"))
+ .start();
+
+ loginApp = await new GenericContainer(LOGIN_IMAGE_TAG)
+ .withNetwork(network)
+ .withExposedPorts(3000)
+ .withEnvironment({
+ ZITADEL_API_URL: "http://mock-zitadel:8080",
+ ZITADEL_SERVICE_USER_TOKEN: "test-token",
+ OTEL_SERVICE_NAME: "zitadel-login-loglevel-debug",
+ OTEL_EXPORTER_OTLP_ENDPOINT: "http://otel-collector:4318",
+ OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf",
+ OTEL_LOG_LEVEL: "debug",
+ OTEL_LOGS_EXPORTER: "otlp",
+ OTEL_BSP_SCHEDULE_DELAY: "1000",
+ })
+ .withStartupTimeout(120000)
+ .withWaitStrategy(Wait.forHttp("/ui/v2/login/healthy", 3000))
+ .start();
+
+ appUrl = `http://${loginApp.getHost()}:${loginApp.getMappedPort(3000)}`;
+ console.log(`[OTEL_ENABLED + LOG_LEVEL=debug] APP_URL: ${appUrl}`);
+
+ await fetch(`${appUrl}/ui/v2/login/healthy`);
+ await fetch(`${appUrl}/ui/v2/login/otel-test`);
+ await new Promise((r) => setTimeout(r, 3000));
+
+ await waitForFile(path.join(logOutputDir, "logs.json"), 30, 1000);
+ }, DOCKER_TIMEOUT);
+
+ afterAll(async () => {
+ await loginApp?.stop().catch(() => {});
+ await mockZitadel?.stop().catch(() => {});
+ await otelCollector?.stop().catch(() => {});
+ await network?.stop().catch(() => {});
+ }, DOCKER_TIMEOUT);
+
+ it("starts successfully", async () => {
+ const response = await fetch(`${appUrl}/ui/v2/login/healthy`);
+ expect(response.ok).toBe(true);
+ }, TEST_TIMEOUT);
+
+ it("exports logs to OTLP collector", () => {
+ const logs = fs.readFileSync(path.join(logOutputDir, "logs.json"), "utf-8");
+ expect(logs.length).toBeGreaterThan(0);
+ }, TEST_TIMEOUT);
+
+ it("includes info level logs in OTLP export", () => {
+ const logs = fs.readFileSync(path.join(logOutputDir, "logs.json"), "utf-8");
+ expect(logs).toContain('"severityText":"info"');
+ }, TEST_TIMEOUT);
+
+ it("includes info level logs in console", async () => {
+ const logOutput = await getContainerLogs(loginApp);
+ expect(logOutput).toContain('"level":"info"');
+ }, TEST_TIMEOUT);
+ });
+
+ describe("LOG_LEVEL=warn", () => {
+ let network: StartedNetwork;
+ let otelCollector: StartedTestContainer;
+ let mockZitadel: StartedTestContainer;
+ let loginApp: StartedTestContainer;
+ let appUrl: string;
+ let logOutputDir: string;
+
+ beforeAll(async () => {
+ logOutputDir = path.join(OTEL_LOG_OUTPUT_DIR, "warn");
+ if (fs.existsSync(logOutputDir)) {
+ fs.rmSync(logOutputDir, { recursive: true });
+ }
+ fs.mkdirSync(logOutputDir, { recursive: true });
+ fs.writeFileSync(path.join(logOutputDir, "logs.json"), "");
+
+ network = await new Network().start();
+
+ otelCollector = await new GenericContainer("otel/opentelemetry-collector-contrib:0.145.0")
+ .withNetwork(network)
+ .withNetworkAliases("otel-collector")
+ .withUser("0:0")
+ .withCommand(["--config=/etc/otel-collector-config.yaml"])
+ .withCopyFilesToContainer([
+ { source: path.join(TEST_DIR, "otel-collector-config.yaml"), target: "/etc/otel-collector-config.yaml" },
+ ])
+ .withBindMounts([{ source: logOutputDir, target: "/tmp/otel" }])
+ .withExposedPorts(4318, 13133)
+ .withWaitStrategy(Wait.forHttp("/", 13133))
+ .start();
+
+ mockZitadel = await new GenericContainer("node:20-alpine")
+ .withNetwork(network)
+ .withNetworkAliases("mock-zitadel")
+ .withCopyFilesToContainer([
+ { source: path.join(TEST_DIR, "mock-zitadel-server.js"), target: "/app/server.js" },
+ ])
+ .withEnvironment({
+ PORT: "8080",
+ })
+ .withCommand(["node", "/app/server.js"])
+ .withExposedPorts(8080, 7432)
+ .withWaitStrategy(Wait.forLogMessage("Mock Zitadel HTTP/2 server listening"))
+ .start();
+
+ loginApp = await new GenericContainer(LOGIN_IMAGE_TAG)
+ .withNetwork(network)
+ .withExposedPorts(3000)
+ .withEnvironment({
+ ZITADEL_API_URL: "http://mock-zitadel:8080",
+ ZITADEL_SERVICE_USER_TOKEN: "test-token",
+ OTEL_SERVICE_NAME: "zitadel-login-loglevel-warn",
+ OTEL_EXPORTER_OTLP_ENDPOINT: "http://otel-collector:4318",
+ OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf",
+ OTEL_LOG_LEVEL: "warn",
+ OTEL_LOGS_EXPORTER: "otlp",
+ OTEL_BSP_SCHEDULE_DELAY: "1000",
+ })
+ .withStartupTimeout(120000)
+ .withWaitStrategy(Wait.forHttp("/ui/v2/login/healthy", 3000))
+ .start();
+
+ appUrl = `http://${loginApp.getHost()}:${loginApp.getMappedPort(3000)}`;
+ console.log(`[OTEL_ENABLED + LOG_LEVEL=warn] APP_URL: ${appUrl}`);
+
+ await fetch(`${appUrl}/ui/v2/login/healthy`);
+ await fetch(`${appUrl}/ui/v2/login/otel-test`);
+ await new Promise((r) => setTimeout(r, 3000));
+ }, DOCKER_TIMEOUT);
+
+ afterAll(async () => {
+ await loginApp?.stop().catch(() => {});
+ await mockZitadel?.stop().catch(() => {});
+ await otelCollector?.stop().catch(() => {});
+ await network?.stop().catch(() => {});
+ }, DOCKER_TIMEOUT);
+
+ it("starts successfully", async () => {
+ const response = await fetch(`${appUrl}/ui/v2/login/healthy`);
+ expect(response.ok).toBe(true);
+ }, TEST_TIMEOUT);
+
+ it("does not include info level logs in console", async () => {
+ const logOutput = await getContainerLogs(loginApp);
+ expect(logOutput).not.toContain('"level":"info"');
+ }, TEST_TIMEOUT);
+
+ it("does not include debug level logs in console", async () => {
+ const logOutput = await getContainerLogs(loginApp);
+ expect(logOutput).not.toContain('"level":"debug"');
+ }, TEST_TIMEOUT);
+
+ it("does not export info level logs to OTLP", () => {
+ const logsPath = path.join(logOutputDir, "logs.json");
+ if (fs.existsSync(logsPath)) {
+ const logs = fs.readFileSync(logsPath, "utf-8");
+ expect(logs).not.toContain('"severityText":"info"');
+ }
+ }, TEST_TIMEOUT);
+
+ it("does not export debug level logs to OTLP", () => {
+ const logsPath = path.join(logOutputDir, "logs.json");
+ if (fs.existsSync(logsPath)) {
+ const logs = fs.readFileSync(logsPath, "utf-8");
+ expect(logs).not.toContain('"severityText":"debug"');
+ }
+ }, TEST_TIMEOUT);
+ });
+ });
+});
diff --git a/apps/login/dockerized/ssl/tls.integration.test.ts b/apps/login/dockerized/ssl/tls.integration.test.ts
index dafec9e92..4dc6ab004 100644
--- a/apps/login/dockerized/ssl/tls.integration.test.ts
+++ b/apps/login/dockerized/ssl/tls.integration.test.ts
@@ -10,6 +10,7 @@ const TEST_DIR = path.dirname(fileURLToPath(import.meta.url));
const OUTPUT_DIR = path.join(TEST_DIR, "output");
const CERTS_DIR = path.join(OUTPUT_DIR, "certs");
const LOGIN_APP_DIR = path.join(TEST_DIR, "../..");
+const LOGIN_IMAGE_TAG = `zitadel-login-test:${process.env.GITHUB_SHA || "local"}`;
/**
* Integration tests for TLS support in the login application.
@@ -44,7 +45,8 @@ describe("Login Container TLS Support", () => {
fs.writeFileSync(path.join(CERTS_DIR, "server.crt"), certs.server.cert);
fs.writeFileSync(path.join(CERTS_DIR, "server.key"), certs.server.key);
- loginImage = await GenericContainer.fromDockerfile(LOGIN_APP_DIR).build();
+ await GenericContainer.fromDockerfile(LOGIN_APP_DIR).build(LOGIN_IMAGE_TAG);
+ loginImage = new GenericContainer(LOGIN_IMAGE_TAG);
});
describe("when TLS is not enabled", () => {
diff --git a/apps/login/next.config.mjs b/apps/login/next.config.mjs
index 6e280d615..a1533d03b 100755
--- a/apps/login/next.config.mjs
+++ b/apps/login/next.config.mjs
@@ -42,7 +42,27 @@ const nextConfig = {
: {}),
},
},
-
+ // Packages that must not be bundled by webpack and should remain as external
+ // requires at runtime. These packages use native modules or have bundling
+ // incompatibilities. Keep this list in sync with package.json dependencies
+ // when adding new OpenTelemetry or logging packages.
+ serverExternalPackages: [
+ 'winston',
+ '@opentelemetry/api',
+ '@opentelemetry/api-logs',
+ '@opentelemetry/sdk-node',
+ '@opentelemetry/sdk-metrics',
+ '@opentelemetry/sdk-logs',
+ '@opentelemetry/exporter-metrics-otlp-http',
+ '@opentelemetry/exporter-logs-otlp-http',
+ '@opentelemetry/exporter-prometheus',
+ '@opentelemetry/resources',
+ '@opentelemetry/semantic-conventions',
+ '@opentelemetry/auto-instrumentations-node',
+ '@opentelemetry/winston-transport',
+ '@opentelemetry/resource-detector-container',
+ '@opentelemetry/resource-detector-gcp',
+ ],
// Improve SSR stability - not actually needed for React 19 SSR issues
// onDemandEntries: {
// maxInactiveAge: 25 * 1000,
diff --git a/apps/login/package.json b/apps/login/package.json
index 454c66192..407990488 100644
--- a/apps/login/package.json
+++ b/apps/login/package.json
@@ -12,6 +12,7 @@
"lint-check-prettier": "prettier --check .",
"lint-fix": "prettier --write .",
"test-unit": "vitest --run",
+ "test-dockerized": "vitest --run --config vitest.config.dockerized.mts",
"lint-staged": "lint-staged",
"clean": "rm -rf node_modules .next cypress"
},
@@ -24,6 +25,14 @@
"dependencies": {
"@headlessui/react": "^2.2.9",
"@heroicons/react": "^2.2.0",
+ "@opentelemetry/api": "^1.9.0",
+ "@opentelemetry/api-logs": "^0.211.0",
+ "@opentelemetry/auto-instrumentations-node": "^0.69.0",
+ "@opentelemetry/resource-detector-container": "^0.8.2",
+ "@opentelemetry/resource-detector-gcp": "^0.46.0",
+ "@opentelemetry/resources": "^2.5.0",
+ "@opentelemetry/sdk-node": "^0.211.0",
+ "@opentelemetry/winston-transport": "^0.21.0",
"@radix-ui/react-tooltip": "^1.2.8",
"@tailwindcss/forms": "^0.5.11",
"clsx": "^2.1.1",
@@ -40,21 +49,25 @@
"react": "19.2.4",
"react-dom": "19.2.4",
"react-hook-form": "^7.71.1",
+ "server-only": "^0.0.1",
"tinycolor2": "^1.6.0",
- "uuid": "^13.0.0"
+ "uuid": "^13.0.0",
+ "winston": "^3.17.0"
},
"devDependencies": {
"@babel/eslint-parser": "^7.28.6",
"@faker-js/faker": "^9.9.0",
+ "@opentelemetry/sdk-trace-base": "^2.5.0",
+ "@opentelemetry/sdk-trace-node": "^2.5.0",
"@otplib/core": "^12.0.1",
"@otplib/plugin-crypto": "^12.0.1",
"@otplib/plugin-thirty-two": "^12.0.1",
"@playwright/test": "^1.58.0",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
+ "@types/escape-html": "^1.0.4",
"@types/node": "^22.19.7",
"@types/node-forge": "^1.3.14",
- "@types/escape-html": "^1.0.4",
"@types/react": "19.2.7",
"@types/react-dom": "19.2.3",
"@types/tinycolor2": "^1.4.6",
@@ -74,6 +87,7 @@
"eslint": "^8.57.1",
"eslint-config-next": "15.5.9",
"eslint-config-prettier": "^9.1.2",
+ "eslint-plugin-import": "^2.32.0",
"gaxios": "^7.1.3",
"grpc-tools": "^1.13.1",
"jsdom": "^26.1.0",
diff --git a/apps/login/project.json b/apps/login/project.json
index 814bcd3cc..ad56b84f2 100644
--- a/apps/login/project.json
+++ b/apps/login/project.json
@@ -99,10 +99,11 @@
]
},
"test": {
- "description": "Runs all tests (unit and integration)",
+ "description": "Runs all tests (unit, integration, and dockerized)",
"dependsOn": [
"test-unit",
- "test-integration"
+ "test-integration",
+ "test-dockerized"
]
},
"test-unit": {
@@ -111,10 +112,11 @@
"^build"
]
},
- "test-ca": {
- "description": "Runs custom CA certificate integration tests using testcontainers",
+ "test-dockerized": {
+ "description": "Runs dockerized integration tests (OpenTelemetry and custom CA certificate tests)",
"dependsOn": [
- "build"
+ "build",
+ "test-integration"
],
"executor": "nx:run-commands",
"cache": true,
@@ -127,7 +129,7 @@
"options": {
"cwd": "{projectRoot}",
"commands": [
- "pnpm vitest --run --config vitest.config.ca.mts"
+ "pnpm vitest --run --config vitest.config.dockerized.mts"
]
}
},
diff --git a/apps/login/scripts/entrypoint.sh b/apps/login/scripts/entrypoint.sh
index f16d5ed50..2da2aca45 100755
--- a/apps/login/scripts/entrypoint.sh
+++ b/apps/login/scripts/entrypoint.sh
@@ -15,4 +15,9 @@ if [ -n "${ZITADEL_SERVICE_USER_TOKEN_FILE}" ]; then
export ZITADEL_SERVICE_USER_TOKEN=$(cat "${ZITADEL_SERVICE_USER_TOKEN_FILE}")
fi
+# Log OTEL configuration on startup (if enabled)
+if [ -n "${OTEL_EXPORTER_OTLP_ENDPOINT}" ]; then
+ echo "OTEL enabled: service=${OTEL_SERVICE_NAME:-zitadel-login}, endpoint=${OTEL_EXPORTER_OTLP_ENDPOINT}"
+fi
+
exec $@
diff --git a/apps/login/scripts/server.mjs b/apps/login/scripts/server.mjs
index 2dfb7125a..4f4a9a47a 100644
--- a/apps/login/scripts/server.mjs
+++ b/apps/login/scripts/server.mjs
@@ -154,7 +154,8 @@ if (process.env.ZITADEL_TLS_ENABLED !== "true") {
}
httpsServer.listen(currentPort, hostname, () => {
- const readyUrl = new URL(`https://${hostname}:${currentPort}`);
+ const formattedHost = hostname.includes(":") ? `[${hostname}]` : hostname;
+ const readyUrl = new URL(`https://${formattedHost}:${currentPort}`);
console.log(`> Ready on ${readyUrl}`);
});
}
diff --git a/apps/login/src/app/otel-test/route.ts b/apps/login/src/app/otel-test/route.ts
new file mode 100644
index 000000000..dc39c7fdb
--- /dev/null
+++ b/apps/login/src/app/otel-test/route.ts
@@ -0,0 +1,85 @@
+import { NextResponse } from "next/server";
+import { trace, metrics, SpanStatusCode } from "@opentelemetry/api";
+import { createLogger } from "@/lib/logger";
+import {
+ recordAuthAttempt,
+ recordAuthSuccess,
+ recordAuthFailure,
+ recordRequestStart,
+ recordRequestEnd,
+ recordSessionCreationDuration,
+} from "@/lib/metrics";
+
+export const runtime = "nodejs";
+const logger = createLogger("otel-test");
+
+export async function GET() {
+ const startTime = Date.now();
+
+ recordRequestStart();
+ recordAuthAttempt("test", "test-org");
+
+ const tracer = trace.getTracer("otel-test");
+ const span = tracer.startSpan("test-span");
+
+ try {
+ logger.info("OTEL test endpoint called", { test: true });
+
+ const meter = metrics.getMeter("otel-test");
+ const counter = meter.createCounter("otel_test_requests_total", {
+ description: "Total number of OTEL test requests",
+ });
+ counter.add(1, { test: "true" });
+
+ const histogram = meter.createHistogram("otel_test_duration_seconds", {
+ description: "Duration of OTEL test requests",
+ unit: "s",
+ });
+
+ span.setAttribute("test.attribute", "test-value");
+ span.setAttribute("test.timestamp", new Date().toISOString());
+
+ await new Promise((resolve) => setTimeout(resolve, 10));
+
+ const duration = (Date.now() - startTime) / 1000;
+ histogram.record(duration, { test: "true" });
+
+ recordAuthSuccess("test", "test-org");
+ recordRequestEnd(startTime, 200, "/otel-test");
+ recordSessionCreationDuration(startTime, true);
+
+ span.setStatus({ code: SpanStatusCode.OK });
+ span.end();
+
+ logger.info("OTEL test completed successfully", {
+ duration,
+ traceId: span.spanContext().traceId,
+ });
+
+ return NextResponse.json({
+ status: "ok",
+ message: "OTEL test completed",
+ traceId: span.spanContext().traceId,
+ spanId: span.spanContext().spanId,
+ duration: `${duration}s`,
+ timestamp: new Date().toISOString(),
+ });
+ } catch (error) {
+ recordAuthFailure("test", "error", "test-org");
+ recordRequestEnd(startTime, 500, "/otel-test");
+ recordSessionCreationDuration(startTime, false);
+
+ logger.error("OTEL test failed", { error });
+ span.setStatus({ code: SpanStatusCode.ERROR, message: String(error) });
+ span.end();
+
+ return NextResponse.json(
+ {
+ status: "error",
+ message: String(error),
+ traceId: span.spanContext().traceId,
+ },
+ { status: 500 },
+ );
+ }
+}
diff --git a/apps/login/src/instrumentation.ts b/apps/login/src/instrumentation.ts
new file mode 100644
index 000000000..73ab6ba93
--- /dev/null
+++ b/apps/login/src/instrumentation.ts
@@ -0,0 +1,87 @@
+import type { LoggerProvider } from "@opentelemetry/api-logs";
+
+/**
+ * @module instrumentation
+ *
+ * OpenTelemetry instrumentation for Next.js applications using NodeSDK.
+ * Set OTEL_SDK_DISABLED=true to disable instrumentation entirely.
+ *
+ * Configuration via standard OTEL environment variables:
+ *
+ * ### Exporters (all default to "otlp")
+ * - OTEL_TRACES_EXPORTER: trace exporter (otlp, console, none)
+ * - OTEL_METRICS_EXPORTER: metric exporter (otlp, prometheus, console, none)
+ * - OTEL_LOGS_EXPORTER: log exporter (otlp, console, none)
+ * - OTEL_EXPORTER_OTLP_ENDPOINT: base endpoint for OTLP signals
+ * - OTEL_EXPORTER_PROMETHEUS_PORT: Prometheus scrape port (default 9464)
+ *
+ * ### Batch Processors
+ * - OTEL_BSP_SCHEDULE_DELAY: span export delay (default 5000ms)
+ * - OTEL_METRIC_EXPORT_INTERVAL: metric push interval (default 60000ms)
+ *
+ * ### Resource
+ * - OTEL_SERVICE_NAME: service.name attribute
+ * - OTEL_RESOURCE_ATTRIBUTES: additional attributes (key=value,key=value)
+ *
+ * @see https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/
+ */
+
+let _loggerProvider: LoggerProvider | null = null;
+
+export async function register(): Promise {
+ if (process.env.NEXT_RUNTIME !== "nodejs") return;
+
+ const [
+ { NodeSDK },
+ { envDetector, processDetector, hostDetector, resourceFromAttributes },
+ { containerDetector },
+ { gcpDetector },
+ { logs },
+ { getNodeAutoInstrumentations },
+ ] = await Promise.all([
+ import("@opentelemetry/sdk-node"),
+ import("@opentelemetry/resources"),
+ import("@opentelemetry/resource-detector-container"),
+ import("@opentelemetry/resource-detector-gcp"),
+ import("@opentelemetry/api-logs"),
+ import("@opentelemetry/auto-instrumentations-node"),
+ ]);
+
+ const sdk = new NodeSDK({
+ resource: resourceFromAttributes({
+ "deployment.environment.name": process.env.NODE_ENV || "development",
+ }),
+ resourceDetectors: [
+ envDetector,
+ processDetector,
+ hostDetector,
+ containerDetector,
+ gcpDetector,
+ ],
+ instrumentations: [
+ getNodeAutoInstrumentations({
+ "@opentelemetry/instrumentation-http": {
+ ignoreIncomingRequestHook: (req) =>
+ ["/healthy", "/_next/", "/favicon.ico", "/__nextjs", "/metrics"].some((p) =>
+ req.url?.includes(p),
+ ),
+ },
+ "@opentelemetry/instrumentation-fs": { enabled: false },
+ "@opentelemetry/instrumentation-winston": { disableLogSending: false },
+ }),
+ ],
+ });
+
+ sdk.start();
+
+ _loggerProvider = logs.getLoggerProvider();
+
+ // Use console during shutdown as the logger may be unavailable
+ const shutdown = () => sdk.shutdown().catch(console.error);
+ process.once("SIGTERM", shutdown);
+ process.once("SIGINT", shutdown);
+}
+
+export function getLoggerProvider(): LoggerProvider | null {
+ return _loggerProvider;
+}
diff --git a/apps/login/src/lib/cookies.test.ts b/apps/login/src/lib/cookies.test.ts
index 9205650b3..faccfce41 100644
--- a/apps/login/src/lib/cookies.test.ts
+++ b/apps/login/src/lib/cookies.test.ts
@@ -13,19 +13,31 @@ import {
setLanguageCookie,
} from "./cookies";
+import { cookies } from "next/headers";
+import { timestampDate, timestampFromMs } from "@zitadel/client";
+
+// Mock logger - use vi.hoisted to ensure it's defined before vi.mock runs
+const mockLogger = vi.hoisted(() => ({
+ debug: vi.fn(),
+ info: vi.fn(),
+ warn: vi.fn(),
+ error: vi.fn(),
+}));
+
// Mock dependencies
vi.mock("next/headers", () => ({
cookies: vi.fn(),
}));
+vi.mock("./logger", () => ({
+ createLogger: () => mockLogger,
+}));
+
vi.mock("@zitadel/client", () => ({
timestampDate: vi.fn((ts: any) => new Date(Number(ts.seconds) * 1000)),
timestampFromMs: vi.fn((ms: number) => ({ seconds: BigInt(Math.floor(ms / 1000)) }) as any),
}));
-import { cookies } from "next/headers";
-import { timestampDate, timestampFromMs } from "@zitadel/client";
-
describe("cookies", () => {
let mockCookies: any;
@@ -148,11 +160,9 @@ describe("cookies", () => {
value: JSON.stringify(manySessions),
});
- const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {});
-
await addSessionToCookie({ session: mockSession });
- expect(consoleSpy).toHaveBeenCalledWith("WARNING COOKIE OVERFLOW");
+ expect(mockLogger.warn).toHaveBeenCalledWith("WARNING COOKIE OVERFLOW");
const setCall = mockCookies.set.mock.calls[0][0];
const sessions = JSON.parse(setCall.value);
@@ -160,8 +170,6 @@ describe("cookies", () => {
// Should have new session and all but the first old session
expect(sessions[0]).toEqual(mockSession);
expect(sessions).not.toContain(manySessions[0]);
-
- consoleSpy.mockRestore();
});
it("should cleanup expired sessions when cleanup is true", async () => {
@@ -763,14 +771,11 @@ describe("cookies", () => {
it("should return empty array when no sessions exist", async () => {
mockCookies.get.mockReturnValue(undefined);
- const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {});
const result = await getAllSessions();
expect(result).toEqual([]);
- expect(consoleSpy).toHaveBeenCalledWith("getAllSessions: No session cookie found, returning empty array");
-
- consoleSpy.mockRestore();
+ expect(mockLogger.info).toHaveBeenCalledWith("getAllSessions: No session cookie found, returning empty array");
});
});
diff --git a/apps/login/src/lib/cookies.ts b/apps/login/src/lib/cookies.ts
index 29dc3c38a..2454eefc9 100644
--- a/apps/login/src/lib/cookies.ts
+++ b/apps/login/src/lib/cookies.ts
@@ -3,6 +3,9 @@
import { timestampDate, timestampFromMs } from "@zitadel/client";
import { cookies } from "next/headers";
import { LANGUAGE_COOKIE_NAME } from "./i18n";
+import { createLogger } from "./logger";
+
+const logger = createLogger("cookies");
// TODO: improve this to handle overflow
const MAX_COOKIE_SIZE = 2048;
@@ -77,7 +80,7 @@ export async function addSessionToCookie({
const temp = [...currentSessions, session];
if (JSON.stringify(temp).length >= MAX_COOKIE_SIZE) {
- console.log("WARNING COOKIE OVERFLOW");
+ logger.warn("WARNING COOKIE OVERFLOW");
// TODO: improve cookie handling
// this replaces the first session (oldest) with the new one
currentSessions = [session].concat(currentSessions.slice(1));
@@ -257,7 +260,7 @@ export async function getAllSessions(cleanup: boolean = false): Promise ({
@@ -21,10 +25,6 @@ vi.mock("@zitadel/client", () => ({
create: vi.fn(),
}));
-import { cookies, headers } from "next/headers";
-import { userAgent } from "next/server";
-import { create } from "@zitadel/client";
-
describe("fingerprint", () => {
beforeEach(() => {
vi.clearAllMocks();
diff --git a/apps/login/src/lib/grpc/interceptors/otel.test.ts b/apps/login/src/lib/grpc/interceptors/otel.test.ts
new file mode 100644
index 000000000..74a0811c1
--- /dev/null
+++ b/apps/login/src/lib/grpc/interceptors/otel.test.ts
@@ -0,0 +1,209 @@
+import { SpanKind, SpanStatusCode } from "@opentelemetry/api";
+import { resourceFromAttributes } from "@opentelemetry/resources";
+import {
+ InMemorySpanExporter,
+ SimpleSpanProcessor,
+} from "@opentelemetry/sdk-trace-base";
+import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
+import { afterAll, beforeEach, describe, expect, it } from "vitest";
+import { otelGrpcInterceptor } from "./otel";
+
+const exporter = new InMemorySpanExporter();
+const provider = new NodeTracerProvider({
+ resource: resourceFromAttributes({ "service.name": "test-zitadel-client" }),
+ spanProcessors: [new SimpleSpanProcessor(exporter)],
+});
+provider.register();
+
+afterAll(() => provider.shutdown());
+
+describe("otelGrpcInterceptor", () => {
+ beforeEach(() => exporter.reset());
+
+ it("injects traceparent header into gRPC requests", async () => {
+ const capturedHeaders: Record = {};
+
+ await otelGrpcInterceptor(async () => ({ success: true }))({
+ service: { typeName: "zitadel.session.v2.SessionService" },
+ method: { name: "CreateSession" },
+ url: "https://api.zitadel.example.com/zitadel.session.v2/CreateSession",
+ header: {
+ set: (key: string, value: string) => {
+ capturedHeaders[key.toLowerCase()] = value;
+ },
+ },
+ });
+
+ expect(capturedHeaders["traceparent"]).toMatch(
+ /^[0-9a-f]{2}-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$/,
+ );
+ });
+
+ it("creates client spans with RPC attributes", async () => {
+ await otelGrpcInterceptor(async () => ({ user: { id: "123" } }))({
+ service: { typeName: "zitadel.user.v2.UserService" },
+ method: { name: "GetUserByID" },
+ url: "https://api.zitadel.example.com/zitadel.user.v2/GetUserByID",
+ header: { set: () => {} },
+ });
+
+ const spans = exporter.getFinishedSpans();
+ expect(spans).toHaveLength(1);
+ expect(spans[0]).toMatchObject({
+ name: "zitadel.user.v2.UserService/GetUserByID",
+ kind: SpanKind.CLIENT,
+ attributes: {
+ "rpc.system": "grpc",
+ "rpc.service": "zitadel.user.v2.UserService",
+ "rpc.method": "GetUserByID",
+ "server.address": "api.zitadel.example.com",
+ },
+ });
+ });
+
+ it("records error spans with gRPC status codes", async () => {
+ const error = Object.assign(new Error("Session not found"), { code: 5 });
+
+ await expect(
+ otelGrpcInterceptor(async () => {
+ throw error;
+ })({
+ service: { typeName: "zitadel.session.v2.SessionService" },
+ method: { name: "DeleteSession" },
+ url: "https://api.zitadel.example.com",
+ header: { set: () => {} },
+ }),
+ ).rejects.toThrow("Session not found");
+
+ const spans = exporter.getFinishedSpans();
+ expect(spans).toHaveLength(1);
+ expect(spans[0]).toMatchObject({
+ name: "zitadel.session.v2.SessionService/DeleteSession",
+ status: { code: SpanStatusCode.ERROR, message: "Session not found" },
+ attributes: { "rpc.grpc.status_code": 5 },
+ });
+ expect(spans[0].events).toHaveLength(1);
+ expect(spans[0].events[0].name).toBe("exception");
+ });
+
+ it("handles missing service/method gracefully", async () => {
+ await otelGrpcInterceptor(async () => ({ ok: true }))({
+ url: "https://api.zitadel.example.com",
+ header: { set: () => {} },
+ });
+
+ const spans = exporter.getFinishedSpans();
+ expect(spans).toHaveLength(1);
+ expect(spans[0]).toMatchObject({
+ name: "unknown/unknown",
+ attributes: { "rpc.service": "unknown", "rpc.method": "unknown" },
+ });
+ });
+
+ it("returns the response from the next handler", async () => {
+ const expectedResponse = { data: "test-data", items: [1, 2, 3] };
+
+ const result = await otelGrpcInterceptor(async () => expectedResponse)({
+ service: { typeName: "TestService" },
+ method: { name: "TestMethod" },
+ url: "https://test.example.com",
+ header: { set: () => {} },
+ });
+
+ expect(result).toEqual(expectedResponse);
+ });
+
+ it("records error spans without gRPC status code", async () => {
+ const error = new Error("Network failure");
+
+ await expect(
+ otelGrpcInterceptor(async () => {
+ throw error;
+ })({
+ service: { typeName: "zitadel.user.v2.UserService" },
+ method: { name: "ListUsers" },
+ url: "https://api.zitadel.example.com",
+ header: { set: () => {} },
+ }),
+ ).rejects.toThrow("Network failure");
+
+ const spans = exporter.getFinishedSpans();
+ expect(spans).toHaveLength(1);
+ expect(spans[0]).toMatchObject({
+ name: "zitadel.user.v2.UserService/ListUsers",
+ status: { code: SpanStatusCode.ERROR, message: "Network failure" },
+ });
+ expect(spans[0].attributes).not.toHaveProperty("rpc.grpc.status_code");
+ });
+
+ it("handles malformed URL gracefully", async () => {
+ await otelGrpcInterceptor(async () => ({ ok: true }))({
+ service: { typeName: "TestService" },
+ method: { name: "TestMethod" },
+ url: "not-a-valid-url",
+ header: { set: () => {} },
+ });
+
+ const spans = exporter.getFinishedSpans();
+ expect(spans).toHaveLength(1);
+ expect(spans[0].attributes["server.address"]).toBe("unknown");
+ });
+
+ it("handles missing URL gracefully", async () => {
+ await otelGrpcInterceptor(async () => ({ ok: true }))({
+ service: { typeName: "TestService" },
+ method: { name: "TestMethod" },
+ header: { set: () => {} },
+ });
+
+ const spans = exporter.getFinishedSpans();
+ expect(spans).toHaveLength(1);
+ expect(spans[0].attributes["server.address"]).toBe("unknown");
+ });
+
+ it("handles empty string URL gracefully", async () => {
+ await otelGrpcInterceptor(async () => ({ ok: true }))({
+ service: { typeName: "TestService" },
+ method: { name: "TestMethod" },
+ url: "",
+ header: { set: () => {} },
+ });
+
+ const spans = exporter.getFinishedSpans();
+ expect(spans).toHaveLength(1);
+ expect(spans[0].attributes["server.address"]).toBe("unknown");
+ });
+
+ it("uses empty strings as-is for service/method names", async () => {
+ await otelGrpcInterceptor(async () => ({ ok: true }))({
+ service: { typeName: "" },
+ method: { name: "" },
+ url: "https://api.example.com",
+ header: { set: () => {} },
+ });
+
+ const spans = exporter.getFinishedSpans();
+ expect(spans).toHaveLength(1);
+ expect(spans[0]).toMatchObject({
+ name: "/",
+ attributes: { "rpc.service": "", "rpc.method": "" },
+ });
+ });
+
+ it("handles non-Error exceptions", async () => {
+ await expect(
+ otelGrpcInterceptor(async () => {
+ throw "string error";
+ })({
+ service: { typeName: "TestService" },
+ method: { name: "TestMethod" },
+ url: "https://api.example.com",
+ header: { set: () => {} },
+ }),
+ ).rejects.toBe("string error");
+
+ const spans = exporter.getFinishedSpans();
+ expect(spans).toHaveLength(1);
+ expect(spans[0].status.code).toBe(SpanStatusCode.ERROR);
+ });
+});
diff --git a/apps/login/src/lib/grpc/interceptors/otel.ts b/apps/login/src/lib/grpc/interceptors/otel.ts
new file mode 100644
index 000000000..8a03ed834
--- /dev/null
+++ b/apps/login/src/lib/grpc/interceptors/otel.ts
@@ -0,0 +1,98 @@
+/**
+ * OpenTelemetry gRPC/Connect Interceptor
+ *
+ * Provides distributed tracing for Connect/gRPC calls. Creates client spans
+ * with RPC semantic conventions, propagates W3C trace context headers
+ * (traceparent/tracestate) and baggage, and records errors with gRPC status
+ * codes.
+ *
+ * @see https://opentelemetry.io/docs/specs/semconv/rpc/rpc-spans/
+ *
+ * @example
+ * ```typescript
+ * import { otelGrpcInterceptor } from "@/lib/grpc/interceptors/otel";
+ *
+ * const transport = createTransport({
+ * baseUrl: "https://api.example.com",
+ * interceptors: [otelGrpcInterceptor],
+ * });
+ * ```
+ */
+
+import {
+ context,
+ propagation,
+ SpanKind,
+ SpanStatusCode,
+ trace,
+} from "@opentelemetry/api";
+import { GrpcError, Interceptor } from "./types";
+
+const TRACER_NAME = "zitadel-login-grpc" as const;
+
+/**
+ * Extracts the hostname from a URL string. Returns "unknown" if the URL is
+ * undefined or cannot be parsed.
+ */
+function parseHostname(url: string | undefined): string {
+ if (!url) {
+ return "unknown";
+ }
+ try {
+ return new URL(url).hostname;
+ } catch {
+ return "unknown";
+ }
+}
+
+/**
+ * OpenTelemetry interceptor for Connect/gRPC calls. Wraps each RPC call in a
+ * client span, injects trace context headers for distributed tracing, and
+ * records any errors that occur during the call.
+ */
+export function otelGrpcInterceptor(next: Parameters[0]): ReturnType {
+ return async function tracedCall(req) {
+ const serviceName: string = req.service?.typeName ?? "unknown";
+ const methodName: string = req.method?.name ?? "unknown";
+
+ return trace.getTracer(TRACER_NAME).startActiveSpan(
+ `${serviceName}/${methodName}`,
+ {
+ kind: SpanKind.CLIENT,
+ attributes: {
+ "rpc.system": "grpc",
+ "rpc.service": serviceName,
+ "rpc.method": methodName,
+ "server.address": parseHostname(req.url),
+ },
+ },
+ async (span) => {
+ const ctx = trace.setSpan(context.active(), span);
+ propagation.inject(ctx, req.header, {
+ set: (carrier, key, value) => carrier.set(key, value),
+ });
+
+ try {
+ const response = await next(req);
+ span.setStatus({ code: SpanStatusCode.OK });
+ return response;
+ } catch (err: unknown) {
+ const exception =
+ err instanceof Error ? err : new Error(String(err));
+ span.recordException(exception);
+ const grpcError = err as GrpcError;
+ if (grpcError.code !== undefined) {
+ span.setAttribute("rpc.grpc.status_code", grpcError.code);
+ }
+ span.setStatus({
+ code: SpanStatusCode.ERROR,
+ message: exception.message,
+ });
+ throw err;
+ } finally {
+ span.end();
+ }
+ },
+ );
+ };
+}
diff --git a/apps/login/src/lib/grpc/interceptors/types.ts b/apps/login/src/lib/grpc/interceptors/types.ts
new file mode 100644
index 000000000..396906604
--- /dev/null
+++ b/apps/login/src/lib/grpc/interceptors/types.ts
@@ -0,0 +1,18 @@
+export interface GrpcRequestHeader {
+ readonly set: (key: string, value: string) => void;
+}
+
+export interface GrpcRequest {
+ readonly service?: { readonly typeName: string };
+ readonly method?: { readonly name: string };
+ readonly url?: string;
+ readonly header: GrpcRequestHeader;
+}
+
+export interface GrpcError extends Error {
+ readonly code?: number;
+}
+
+export type Interceptor = (
+ next: (req: GrpcRequest) => Promise,
+) => (req: GrpcRequest) => Promise;
diff --git a/apps/login/src/lib/logger.ts b/apps/login/src/lib/logger.ts
new file mode 100644
index 000000000..aedb4c824
--- /dev/null
+++ b/apps/login/src/lib/logger.ts
@@ -0,0 +1,71 @@
+import "server-only";
+import winston from "winston";
+
+const isProduction = process.env.NODE_ENV === "production";
+
+/**
+ * Winston logger configuration for the login application.
+ *
+ * OpenTelemetry integration:
+ * - @opentelemetry/instrumentation-winston automatically injects trace context
+ * (trace_id, span_id, trace_flags) into every log entry
+ * - @opentelemetry/winston-transport automatically sends logs to OTEL LoggerProvider
+ * when the SDK is initialized with a LoggerProvider
+ *
+ * The trace context injection and OTEL log sending are handled by the instrumentation
+ * registered in instrumentation.ts - no manual setup required here.
+ */
+
+// Custom format for development - colorized and readable
+const devFormat = winston.format.combine(
+ winston.format.colorize(),
+ winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
+ winston.format.printf(({ level, message, timestamp, ...meta }) => {
+ const metaStr = Object.keys(meta).length ? ` ${JSON.stringify(meta)}` : "";
+ return `${timestamp} ${level}: ${message}${metaStr}`;
+ }),
+);
+
+// Production format - JSON for structured logging
+const prodFormat = winston.format.combine(
+ winston.format.timestamp(),
+ winston.format.errors({ stack: true }),
+ winston.format.json(),
+);
+
+/**
+ * The main logger instance.
+ * In production, outputs JSON for structured logging.
+ * In development, outputs colorized human-readable format.
+ */
+export const logger = winston.createLogger({
+ level: process.env.OTEL_LOG_LEVEL || process.env.LOG_LEVEL || "info",
+ format: isProduction ? prodFormat : devFormat,
+ defaultMeta: {
+ service: process.env.OTEL_SERVICE_NAME || "zitadel-login",
+ },
+ transports: [
+ new winston.transports.Console({
+ // In production, use stderr for error level, stdout for others
+ stderrLevels: isProduction ? ["error"] : [],
+ }),
+ ],
+});
+
+/**
+ * Create a child logger with a specific context.
+ * The context is added as metadata to all log entries from this logger.
+ *
+ * @param context - The context name for this logger (e.g., 'auth-flow', 'session')
+ * @param metadata - Optional additional metadata to include in all log messages
+ * @returns A child logger instance
+ */
+export function createLogger(
+ context: string,
+ metadata?: Record,
+): winston.Logger {
+ return logger.child({ context, ...metadata });
+}
+
+// Re-export winston types for convenience
+export type { Logger } from "winston";
diff --git a/apps/login/src/lib/metrics.ts b/apps/login/src/lib/metrics.ts
new file mode 100644
index 000000000..8b39f1c78
--- /dev/null
+++ b/apps/login/src/lib/metrics.ts
@@ -0,0 +1,146 @@
+import "server-only";
+import { metrics } from "@opentelemetry/api";
+
+const serviceName = process.env.OTEL_SERVICE_NAME || "zitadel-login";
+const meter = metrics.getMeter(serviceName);
+
+const requestCounter = meter.createCounter("http_server_requests_total", {
+ description: "Total number of HTTP requests",
+});
+
+const requestDuration = meter.createHistogram(
+ "http_server_request_duration_seconds",
+ {
+ description: "HTTP request duration in seconds",
+ unit: "s",
+ },
+);
+
+const requestSize = meter.createHistogram("http_server_request_size_bytes", {
+ description: "Size of HTTP request bodies in bytes",
+ unit: "By",
+});
+
+const responseSize = meter.createHistogram("http_server_response_size_bytes", {
+ description: "Size of HTTP response bodies in bytes",
+ unit: "By",
+});
+
+const responseStatusCounter = meter.createCounter(
+ "http_server_response_status_total",
+ {
+ description: "Count of HTTP responses per status code",
+ },
+);
+
+const activeRequests = meter.createUpDownCounter("http_server_active_requests", {
+ description: "Number of active HTTP requests",
+});
+
+const authAttempts = meter.createCounter("login_auth_attempts_total", {
+ description: "Total authentication attempts",
+});
+
+const authSuccesses = meter.createCounter("login_auth_successes_total", {
+ description: "Total successful authentications",
+});
+
+const authFailures = meter.createCounter("login_auth_failures_total", {
+ description: "Total failed authentications",
+});
+
+const sessionCreationDuration = meter.createHistogram(
+ "login_session_creation_duration_seconds",
+ {
+ description: "Session creation duration in seconds",
+ unit: "s",
+ },
+);
+
+/**
+ * Record an authentication attempt.
+ */
+export function recordAuthAttempt(method: string, organization?: string): void {
+ authAttempts.add(1, {
+ method,
+ organization: organization || "unknown",
+ });
+}
+
+/**
+ * Record a successful authentication.
+ */
+export function recordAuthSuccess(method: string, organization?: string): void {
+ authSuccesses.add(1, {
+ method,
+ organization: organization || "unknown",
+ });
+}
+
+/**
+ * Record a failed authentication.
+ */
+export function recordAuthFailure(
+ method: string,
+ reason: string,
+ organization?: string,
+): void {
+ authFailures.add(1, {
+ method,
+ reason,
+ organization: organization || "unknown",
+ });
+}
+
+/**
+ * Record the start of an HTTP request.
+ */
+export function recordRequestStart(): void {
+ activeRequests.add(1);
+ requestCounter.add(1);
+}
+
+/**
+ * Record the end of an HTTP request.
+ */
+export function recordRequestEnd(
+ startTime: number,
+ statusCode: number,
+ route: string,
+ requestBytes?: number,
+ responseBytes?: number,
+): void {
+ activeRequests.add(-1);
+
+ const labels = {
+ status_code: statusCode.toString(),
+ route,
+ };
+
+ const duration = (Date.now() - startTime) / 1000;
+ requestDuration.record(duration, labels);
+
+ responseStatusCounter.add(1, {
+ status_code: statusCode.toString(),
+ });
+
+ if (requestBytes !== undefined) {
+ requestSize.record(requestBytes, labels);
+ }
+ if (responseBytes !== undefined) {
+ responseSize.record(responseBytes, labels);
+ }
+}
+
+/**
+ * Record session creation duration.
+ */
+export function recordSessionCreationDuration(
+ startTime: number,
+ success: boolean,
+): void {
+ const duration = (Date.now() - startTime) / 1000;
+ sessionCreationDuration.record(duration, {
+ success: success.toString(),
+ });
+}
diff --git a/apps/login/src/lib/server/auth-flow.ts b/apps/login/src/lib/server/auth-flow.ts
index 6fc0681ba..4c5823d7b 100644
--- a/apps/login/src/lib/server/auth-flow.ts
+++ b/apps/login/src/lib/server/auth-flow.ts
@@ -1,6 +1,7 @@
"use server";
import { getAllSessions } from "@/lib/cookies";
+import { createLogger } from "@/lib/logger";
import { loginWithOIDCAndSession } from "@/lib/oidc";
import { loginWithSAMLAndSession } from "@/lib/saml";
import { getServiceConfig } from "@/lib/service-url";
@@ -8,6 +9,8 @@ import { listSessions, ServiceConfig } from "@/lib/zitadel";
import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
import { headers } from "next/headers";
+const logger = createLogger("auth-flow");
+
export interface AuthFlowParams {
sessionId: string;
requestId: string;
@@ -54,7 +57,7 @@ export async function completeAuthFlow(
// Safety net - ensure we always return a valid object
if (!result || typeof result !== "object" || (!("redirect" in result) && !("error" in result))) {
- console.error("Auth flow: Invalid result from loginWithOIDCAndSession:", result);
+ logger.error("Auth flow: Invalid result from loginWithOIDCAndSession:", { result });
return { error: "Authentication completed but navigation failed" };
}
@@ -75,7 +78,7 @@ export async function completeAuthFlow(
typeof result !== "object" ||
(!("redirect" in result) && !("error" in result) && !("samlData" in result))
) {
- console.error("Auth flow: Invalid result from loginWithSAMLAndSession:", result);
+ logger.error("Auth flow: Invalid result from loginWithSAMLAndSession:", { result });
return { error: "Authentication completed but navigation failed" };
}
diff --git a/apps/login/src/lib/server/cookie.ts b/apps/login/src/lib/server/cookie.ts
index 069969810..edfbc9bbe 100644
--- a/apps/login/src/lib/server/cookie.ts
+++ b/apps/login/src/lib/server/cookie.ts
@@ -1,6 +1,7 @@
"use server";
import { addSessionToCookie, updateSessionCookie } from "@/lib/cookies";
+import { createLogger } from "@/lib/logger";
import {
createSessionForUserIdAndIdpIntent,
createSessionFromChecksAndChallenges,
@@ -16,6 +17,8 @@ import { Checks } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
import { headers } from "next/headers";
import { getServiceConfig } from "../service-url";
+const logger = createLogger("cookie");
+
type CustomCookieData = {
id: string;
token: string;
@@ -52,7 +55,7 @@ export async function createSessionAndUpdateCookie(command: {
let sessionLifetime = command.lifetime;
if (!sessionLifetime || !sessionLifetime.seconds) {
- console.warn("No session lifetime provided, using default of 24 hours.");
+ logger.warn("No session lifetime provided, using default of 24 hours");
sessionLifetime = {
seconds: BigInt(24 * 60 * 60), // 24 hours
@@ -126,7 +129,7 @@ export async function createSessionForIdpAndUpdateCookie({
let sessionLifetime = lifetime;
if (!sessionLifetime || !sessionLifetime.seconds) {
- console.warn("No IDP session lifetime provided, using default of 24 hours.");
+ logger.warn("No IDP session lifetime provided, using default of 24 hours");
sessionLifetime = {
seconds: BigInt(24 * 60 * 60), // 24 hours
@@ -140,7 +143,7 @@ export async function createSessionForIdpAndUpdateCookie({
idpIntent,
lifetime: sessionLifetime,
}).catch((error: ErrorDetail | CredentialsCheckError) => {
- console.error("Could not set session", error);
+ logger.error("Could not set session", { error });
if ("failedAttempts" in error && error.failedAttempts) {
throw {
error: `Failed to authenticate: You had ${error.failedAttempts} password attempts.`,
diff --git a/apps/login/src/lib/server/flow-initiation.ts b/apps/login/src/lib/server/flow-initiation.ts
index cee680a2a..848d13572 100644
--- a/apps/login/src/lib/server/flow-initiation.ts
+++ b/apps/login/src/lib/server/flow-initiation.ts
@@ -1,4 +1,5 @@
import { idpTypeToSlug } from "@/lib/idp";
+import { createLogger } from "@/lib/logger";
import { sendLoginname, SendLoginnameCommand } from "@/lib/server/loginname";
import { constructUrl } from "@/lib/service-url";
import { findValidSession } from "@/lib/session";
@@ -24,6 +25,8 @@ import { NextRequest, NextResponse } from "next/server";
import { buildCSP } from "../csp";
import escapeHtml from "escape-html";
+const logger = createLogger("flow-initiation");
+
const ORG_SCOPE_REGEX = /urn:zitadel:iam:org:id:([0-9]+)/;
const ORG_DOMAIN_SCOPE_REGEX = /urn:zitadel:iam:org:domain:primary:(.+)/;
const IDP_SCOPE_REGEX = /urn:zitadel:iam:org:idp:id:(.+)/;
@@ -103,7 +106,7 @@ export async function handleOIDCFlowInitiation(params: FlowInitiationParams): Pr
const matched = ORG_DOMAIN_SCOPE_REGEX.exec(orgDomainScope);
const orgDomain = matched?.[1] ?? "";
- console.log("Extracted Organization Domain:", orgDomain);
+ logger.info("Extracted org domain:", { orgDomain });
if (orgDomain) {
const orgs = await getOrgsByDomain({ serviceConfig, domain: orgDomain });
@@ -238,7 +241,7 @@ export async function handleOIDCFlowInitiation(params: FlowInitiationParams): Pr
return NextResponse.redirect(absoluteUrl.toString());
}
} catch (error) {
- console.error("Failed to execute sendLoginname:", error);
+ logger.error("Failed to execute sendLoginname:", { error });
}
}
@@ -333,7 +336,7 @@ export async function handleOIDCFlowInitiation(params: FlowInitiationParams): Pr
if (callbackUrl) {
return NextResponse.redirect(callbackUrl);
} else {
- console.log("could not create callback, redirect user to choose other account");
+ logger.info("could not create callback, redirect user to choose other account");
return gotoAccounts({
request,
organization,
@@ -341,7 +344,7 @@ export async function handleOIDCFlowInitiation(params: FlowInitiationParams): Pr
});
}
} catch (error) {
- console.error(error);
+ logger.error("Error creating callback:", { error });
return gotoAccounts({
request,
requestId,
@@ -451,7 +454,7 @@ export async function handleSAMLFlowInitiation(params: FlowInitiationParams): Pr
});
}
} catch (error) {
- console.error("SAML createResponse failed:", error);
+ logger.error("SAML createResponse failed:", { error });
}
// Final fallback: SAML response creation failed - show account selection
diff --git a/apps/login/src/lib/server/idp-intent.ts b/apps/login/src/lib/server/idp-intent.ts
index 8fc396515..b17db302d 100644
--- a/apps/login/src/lib/server/idp-intent.ts
+++ b/apps/login/src/lib/server/idp-intent.ts
@@ -1,6 +1,7 @@
"use server";
import { getSessionCookieById } from "@/lib/cookies";
+import { createLogger } from "@/lib/logger";
import { getServiceConfig } from "@/lib/service-url";
import {
addHuman,
@@ -31,6 +32,8 @@ import { headers } from "next/headers";
import { getFingerprintIdCookie } from "../fingerprint";
import { createNewSessionFromIdpIntent } from "./idp";
+const logger = createLogger("idp-intent");
+
const ORG_SUFFIX_REGEX = /(?<=@)(.+)/;
async function resolveOrganizationForUser({
@@ -139,7 +142,7 @@ async function resolveUserIdFromSession({
try {
const sessionCookie = await getSessionCookieById({ sessionId });
if (!sessionCookie) {
- console.warn("[IDP Process] Session for linking not found or invalid");
+ logger.warn("Session for linking not found or invalid");
return { redirect: `/idp/${provider}/linking-failed?error=session_invalid` };
}
@@ -151,13 +154,13 @@ async function resolveUserIdFromSession({
const session = sessionResp.session;
if (!session?.factors?.user?.id) {
- console.warn("[IDP Process] Session found but no userId associated for linking.");
+ logger.warn("Session found but no userId associated for linking");
return { redirect: `/idp/${provider}/linking-failed?error=session_invalid` };
}
return { userId: session.factors.user.id };
} catch (error) {
- console.warn("[IDP Process] Error retrieving session for linking:", error);
+ logger.warn("Error retrieving session for linking", { error });
return { redirect: `/idp/${provider}/linking-failed?error=session_invalid` };
}
}
@@ -173,7 +176,7 @@ async function handleExplicitLinking(ctx: IDPHandlerContext): Promise {
const { buildRedirectParams } = ctx;
- console.log("[IDP Process] No matching user and creation not allowed");
+ logger.debug("No matching user and creation not allowed");
const params = buildRedirectParams();
return { redirect: `/idp/${ctx.params.provider}/account-not-found?${params}` };
}
@@ -604,7 +607,7 @@ export async function processIDPCallback({
// Validate required parameters
if (!provider || !id || !token) {
- console.error("[IDP Process] Missing required parameters:", { provider, id, hasToken: !!token });
+ logger.error("Missing required parameters", { provider, id, hasToken: !!token });
const errorParams = new URLSearchParams();
if (requestId) errorParams.set("requestId", requestId);
if (organization) errorParams.set("organization", organization);
@@ -617,13 +620,13 @@ export async function processIDPCallback({
// Consume the single-use token ONCE
const intent = await retrieveIDPIntent({ serviceConfig, id, token });
- console.log("[IDP Process] Intent retrieved successfully, processing business logic");
+ logger.debug("Intent retrieved successfully, processing business logic");
const { idpInformation } = intent;
// Verify we have IDP info early on
if (!idpInformation) {
- console.error("[IDP Process] IDP information missing");
+ logger.error("IDP information missing");
return { redirect: `/idp/${provider}/failure?error=missing_idp_info` };
}
@@ -693,7 +696,7 @@ export async function processIDPCallback({
// Should theoretically be unreachable if handleNoUserFound covers the rest
return { error: t("errors.unknown") };
} catch (error: unknown) {
- console.error("[IDP Process] Error processing intent:", error);
+ logger.error("Error processing intent", { error });
const errorParams = new URLSearchParams();
if (requestId) errorParams.set("requestId", requestId);
diff --git a/apps/login/src/lib/server/loginname.ts b/apps/login/src/lib/server/loginname.ts
index 1d2b9f085..2802e4a35 100644
--- a/apps/login/src/lib/server/loginname.ts
+++ b/apps/login/src/lib/server/loginname.ts
@@ -1,5 +1,6 @@
"use server";
+import { createLogger } from "@/lib/logger";
import { create } from "@zitadel/client";
import { ChecksSchema } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
@@ -25,6 +26,8 @@ import {
import { createSessionAndUpdateCookie } from "./cookie";
import { getPublicHost } from "./host";
+const logger = createLogger("loginname");
+
export type SendLoginnameCommand = {
loginName: string;
requestId?: string;
@@ -60,17 +63,17 @@ export async function sendLoginname(command: SendLoginnameCommand) {
// Safety check: ensure searchResult is defined
if (!searchResult) {
- console.error("searchUsers returned undefined or null");
+ logger.error("searchUsers returned undefined or null");
return { error: t("errors.couldNotSearchUsers") };
}
if ("error" in searchResult && searchResult.error) {
- console.log("searchUsers returned error, returning early:", searchResult.error);
+ logger.debug("searchUsers returned error, returning early", { error: searchResult.error });
return searchResult;
}
if (!("result" in searchResult)) {
- console.log("searchUsers has no result field");
+ logger.debug("searchUsers has no result field");
return { error: t("errors.couldNotSearchUsers") };
}
@@ -80,12 +83,12 @@ export async function sendLoginname(command: SendLoginnameCommand) {
const users = potentialUsers ?? [];
if (users.length === 0) {
- console.log("No users found, will proceed with org discovery");
+ logger.debug("No users found, will proceed with org discovery");
}
const preventUserEnumeration = (organization: string | undefined) => {
if (command.ignoreUnknownUsernames) {
- console.log("ignoreUnknownUsernames is true, redirecting to password");
+ logger.debug("ignoreUnknownUsernames is true, redirecting to password");
const paramsPasswordDefault = new URLSearchParams({
loginName: command.loginName,
});
@@ -229,7 +232,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
};
if (users.length > 1) {
- console.log("multiple users found, returning error");
+ logger.debug("multiple users found, returning error");
if (loginSettingsByContext?.ignoreUnknownUsernames) {
return preventUserEnumeration(command.organization);
}
@@ -331,7 +334,9 @@ export async function sendLoginname(command: SendLoginnameCommand) {
// always resend invite or setup email if user has no primary auth method set
if (!hasPrimaryMethod) {
- console.log("humanUser.email?.isVerified", humanUser?.email?.isVerified);
+ logger.debug("humanUser.email?.isVerified", {
+ isVerified: humanUser?.email?.isVerified,
+ });
const params = new URLSearchParams({
loginName: (session?.factors?.user?.loginName ?? user.preferredLoginName) as string,
send: "true", // set this to true to request a new code immediately
@@ -482,7 +487,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
}
}
- console.log("user not found (0 potential users), checking registration options");
+ logger.debug("User not found (0 potential users), checking registration options");
// user not found, perform organization discovery if no org context provided
let discoveredOrganization = command.organization;
@@ -508,33 +513,33 @@ export async function sendLoginname(command: SendLoginnameCommand) {
const orgLoginSettings = await getLoginSettings({ serviceConfig, organization: orgToCheckForDiscovery });
if (orgLoginSettings?.allowDomainDiscovery) {
- console.log("org discovery successful, using org:", orgToCheckForDiscovery);
+ logger.debug("Org discovery successful", { organization: orgToCheckForDiscovery });
discoveredOrganization = orgToCheckForDiscovery;
// Use the discovered organization's login settings for subsequent checks
effectiveLoginSettings = orgLoginSettings;
} else {
- console.log("org does not allow domain discovery");
+ logger.debug("Org does not allow domain discovery");
}
} else {
- console.log("no single org found for discovery");
+ logger.debug("No single org found for discovery");
}
}
// user not found, check if register is enabled on instance / organization context
if (effectiveLoginSettings?.allowRegister && !effectiveLoginSettings?.allowLocalAuthentication) {
- console.log("redirecting to IDP (register allowed, password not allowed)");
+ logger.debug("redirecting to IDP (register allowed, password not allowed)");
const resp = await redirectUserToIDP(undefined, discoveredOrganization);
if (resp) {
return resp;
}
- console.log("IDP redirect failed, returning user not found");
+ logger.debug("IDP redirect failed, returning user not found");
return preventUserEnumeration(discoveredOrganization);
} else if (effectiveLoginSettings?.allowRegister && effectiveLoginSettings?.allowLocalAuthentication) {
- console.log("register and password both allowed");
+ logger.debug("register and password both allowed");
// do not register user if ignoreUnknownUsernames is set
if (discoveredOrganization && !effectiveLoginSettings?.ignoreUnknownUsernames) {
- console.log("redirecting to registration page with org:", discoveredOrganization);
+ logger.debug("Redirecting to registration page", { organization: discoveredOrganization });
const params = new URLSearchParams({ organization: discoveredOrganization });
if (command.requestId) {
@@ -547,7 +552,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
return { redirect: "/register?" + params };
} else {
- console.log("not redirecting to register:", {
+ logger.debug("Not redirecting to register", {
hasDiscoveredOrg: !!discoveredOrganization,
ignoreUnknownUsernames: effectiveLoginSettings?.ignoreUnknownUsernames,
});
diff --git a/apps/login/src/lib/server/passkeys.ts b/apps/login/src/lib/server/passkeys.ts
index c3349127c..e948681d0 100644
--- a/apps/login/src/lib/server/passkeys.ts
+++ b/apps/login/src/lib/server/passkeys.ts
@@ -1,5 +1,6 @@
"use server";
+import { createLogger } from "@/lib/logger";
import {
createPasskeyRegistrationLink,
getLoginSettings,
@@ -27,6 +28,8 @@ import { updateOrCreateSession } from "./session";
import { completeFlowOrGetUrl } from "../client";
import { createSessionAndUpdateCookie } from "./cookie";
+const logger = createLogger("passkeys");
+
type VerifyPasskeyCommand = {
passkeyId: string;
passkeyName?: string;
@@ -103,7 +106,7 @@ export async function registerPasskeyLink(
// check if a verification was done earlier
const hasValidUserVerificationCheck = await checkUserVerification(currentUserId);
- console.log("hasValidUserVerificationCheck", hasValidUserVerificationCheck);
+ logger.info("hasValidUserVerificationCheck", { hasValidUserVerificationCheck });
if (!hasValidUserVerificationCheck) {
return { error: "User Verification Check has to be done" };
}
@@ -295,7 +298,7 @@ export async function sendPasskey(command: SendPasskeyCommand) {
try {
userResponse = await getUserByID({ serviceConfig, userId });
} catch (error) {
- console.error("Error fetching user by ID:", error);
+ logger.error("Error fetching user by ID:", { error });
return { error: t("verify.errors.couldNotGetUser") };
}
diff --git a/apps/login/src/lib/server/password.ts b/apps/login/src/lib/server/password.ts
index 592ecfc1a..a1369b043 100644
--- a/apps/login/src/lib/server/password.ts
+++ b/apps/login/src/lib/server/password.ts
@@ -1,5 +1,7 @@
"use server";
+import { createLogger } from "@/lib/logger";
+import { recordAuthAttempt, recordAuthSuccess, recordAuthFailure } from "@/lib/metrics";
import { createSessionAndUpdateCookie, setSessionAndUpdateCookie } from "@/lib/server/cookie";
import {
getLockoutSettings,
@@ -31,6 +33,8 @@ import {
} from "../verify-helper";
import { getPublicHostWithProtocol } from "./host";
+const logger = createLogger("password");
+
type ResetPasswordCommand = {
loginName: string;
organization?: string;
@@ -134,6 +138,8 @@ export async function sendPassword(
const { serviceConfig } = getServiceConfig(_headers);
const t = await getTranslations("password");
+ recordAuthAttempt("password", command.organization);
+
let sessionCookie = await getSessionCookieByLoginName({
loginName: command.loginName,
organization: command.organization,
@@ -152,7 +158,7 @@ export async function sendPassword(
let lifetime = loginSettingsByUser.passwordCheckLifetime;
if (!lifetime || !lifetime.seconds) {
- console.warn("No password lifetime provided, defaulting to 24 hours");
+ logger.warn("No password lifetime provided, defaulting to 24 hours");
lifetime = {
seconds: BigInt(60 * 60 * 24), // default to 24 hours
nanos: 0,
@@ -170,7 +176,7 @@ export async function sendPassword(
throw new Error("Could not load login settings");
}
} catch {
- console.warn("[Password] Could not update session");
+ logger.warn("[Password] Could not update session");
// If the session was terminated or any other error occurred during update,
// we fall back to creating a new session.
sessionCookie = undefined;
@@ -189,6 +195,7 @@ export async function sendPassword(
// Force fallback if settings can't be loaded
if (!loginSettingsByContext) {
// this is a fake error message to hide that the user does not even exist
+ recordAuthFailure("password", "settings_unavailable", command.organization);
return { error: t("errors.couldNotVerifyPassword") };
}
@@ -215,6 +222,7 @@ export async function sendPassword(
if (userLoginSettings?.disableLoginWithEmail && userLoginSettings?.disableLoginWithPhone) {
if (user.preferredLoginName !== command.loginName) {
// emulate user not found to prevent enumeration (use context settings not user settings)
+ recordAuthFailure("password", "login_name_mismatch", command.organization);
if (loginSettingsByContext?.ignoreUnknownUsernames) {
return { error: t("errors.failedToAuthenticateNoLimit") };
}
@@ -222,6 +230,7 @@ export async function sendPassword(
}
} else if (userLoginSettings?.disableLoginWithEmail) {
if (user.preferredLoginName !== command.loginName && humanUser?.phone?.phone !== command.loginName) {
+ recordAuthFailure("password", "login_name_mismatch", command.organization);
if (loginSettingsByContext?.ignoreUnknownUsernames) {
return { error: t("errors.failedToAuthenticateNoLimit") };
}
@@ -229,6 +238,7 @@ export async function sendPassword(
}
} else if (userLoginSettings?.disableLoginWithPhone) {
if (user.preferredLoginName !== command.loginName && humanUser?.email?.email !== command.loginName) {
+ recordAuthFailure("password", "login_name_mismatch", command.organization);
if (loginSettingsByContext?.ignoreUnknownUsernames) {
return { error: t("errors.failedToAuthenticateNoLimit") };
}
@@ -251,6 +261,7 @@ export async function sendPassword(
sessionCookie = result.sessionCookie;
} catch (error: any) {
if ("failedAttempts" in error && error.failedAttempts) {
+ recordAuthFailure("password", "invalid_password", command.organization);
if (loginSettingsByContext?.ignoreUnknownUsernames) {
return { error: t("errors.failedToAuthenticateNoLimit") };
}
@@ -269,6 +280,7 @@ export async function sendPassword(
}),
};
}
+ recordAuthFailure("password", "session_creation_failed", command.organization);
if (loginSettingsByContext?.ignoreUnknownUsernames) {
return { error: t("errors.failedToAuthenticateNoLimit") };
}
@@ -276,6 +288,7 @@ export async function sendPassword(
}
} else {
// this is a fake error message to hide that the user does not even exist
+ recordAuthFailure("password", "user_not_found", command.organization);
if (loginSettingsByContext?.ignoreUnknownUsernames) {
return { error: t("errors.failedToAuthenticateNoLimit") };
}
@@ -284,6 +297,7 @@ export async function sendPassword(
}
if (!session?.factors?.user?.id) {
+ recordAuthFailure("password", "session_invalid", command.organization);
if (loginSettingsByContext?.ignoreUnknownUsernames) {
return { error: t("errors.failedToAuthenticateNoLimit") };
}
@@ -293,12 +307,14 @@ export async function sendPassword(
if (!user) {
const userResponse = await getUserByID({ serviceConfig, userId: session?.factors?.user?.id });
if (!userResponse.user) {
+ recordAuthFailure("password", "user_not_found", command.organization);
return { error: t("errors.userNotFound") };
}
user = userResponse.user;
}
if (!session?.factors?.user?.id || !sessionCookie) {
+ recordAuthFailure("password", "session_invalid", command.organization);
if (loginSettingsByContext?.ignoreUnknownUsernames) {
return { error: t("errors.failedToAuthenticateNoLimit") };
}
@@ -334,6 +350,7 @@ export async function sendPassword(
// throw error if user is in initial state here and do not continue
if (user.state === UserState.INITIAL) {
+ recordAuthFailure("password", "user_initial_state", command.organization);
return { error: t("errors.initialUserNotSupported") };
}
@@ -354,6 +371,7 @@ export async function sendPassword(
}
if (!authMethods) {
+ recordAuthFailure("password", "no_auth_methods", command.organization);
return { error: t("errors.couldNotVerifyPassword") };
}
@@ -371,10 +389,10 @@ export async function sendPassword(
}
let result: Awaited>;
-
+
if (command.requestId && session.id) {
// OIDC/SAML flow
- console.log("Password auth: OIDC/SAML flow with requestId:", command.requestId, "sessionId:", session.id);
+ logger.info("Password auth: OIDC/SAML flow with requestId:", { requestId: command.requestId, sessionId: session.id });
result = await completeFlowOrGetUrl(
{
sessionId: session.id,
@@ -385,7 +403,7 @@ export async function sendPassword(
);
} else {
// Regular flow (no requestId)
- console.log("Password auth: Regular flow with loginName:", session.factors.user.loginName);
+ logger.info("Password auth: Regular flow with loginName:", { loginName: session.factors.user.loginName });
result = await completeFlowOrGetUrl(
{
loginName: session.factors.user.loginName,
@@ -396,9 +414,15 @@ export async function sendPassword(
}
if (result && typeof result === "object") {
+ if ("redirect" in result) {
+ recordAuthSuccess("password", command.organization);
+ } else if ("error" in result) {
+ recordAuthFailure("password", "flow_error", command.organization);
+ }
return result;
}
+ recordAuthFailure("password", "navigation_failed", command.organization);
return { error: "Authentication completed but navigation failed" };
}
@@ -476,7 +500,7 @@ export async function checkSessionAndSetPassword({
});
session = sessionResponse.session;
} catch (error) {
- console.error("Error getting session:", error);
+ logger.error("Error getting session:", { error });
return { error: "Could not load session" };
}
diff --git a/apps/login/src/lib/server/session.ts b/apps/login/src/lib/server/session.ts
index ca27cc2ec..fff38e227 100644
--- a/apps/login/src/lib/server/session.ts
+++ b/apps/login/src/lib/server/session.ts
@@ -1,5 +1,6 @@
"use server";
+import { createLogger } from "@/lib/logger";
import { createSessionAndUpdateCookie, setSessionAndUpdateCookie } from "@/lib/server/cookie";
import {
deleteSession,
@@ -25,6 +26,8 @@ import {
import { getServiceConfig } from "../service-url";
import { getPublicHost } from "./host";
+const logger = createLogger("session");
+
export async function skipMFAAndContinueWithNextUrl({
userId,
requestId,
@@ -149,7 +152,7 @@ export async function updateOrCreateSession(options: UpdateSessionCommand) {
challenges,
requestId,
}).catch((error) => {
- console.error("Could not create session", error);
+ logger.error("Could not create session", { error });
return undefined;
});
@@ -175,7 +178,7 @@ export async function updateOrCreateSession(options: UpdateSessionCommand) {
}
if (!lifetime || !lifetime.seconds) {
- console.warn("No lifetime provided for session, defaulting to 24 hours");
+ logger.warn("No lifetime provided for session, defaulting to 24 hours");
lifetime = {
seconds: BigInt(60 * 60 * 24), // default to 24 hours
nanos: 0,
diff --git a/apps/login/src/lib/server/verify.test.ts b/apps/login/src/lib/server/verify.test.ts
index ecf90b1db..d4d92835a 100644
--- a/apps/login/src/lib/server/verify.test.ts
+++ b/apps/login/src/lib/server/verify.test.ts
@@ -1,6 +1,11 @@
import { vi, describe, expect, test, beforeEach } from "vitest";
import { sendVerification } from "./verify";
+import { verifyEmail, getUserByID, getSession, listAuthenticationMethodTypes } from "@/lib/zitadel";
+import { getSessionCookieByLoginName } from "../cookies";
+import { createSessionAndUpdateCookie } from "./cookie";
+import { cookies } from "next/headers";
+
// Mock dependencies
vi.mock("@/lib/zitadel", () => ({
verifyEmail: vi.fn(),
@@ -36,11 +41,6 @@ vi.mock("next-intl/server", () => ({
getTranslations: vi.fn(() => (key: string) => key),
}));
-import { verifyEmail, getUserByID, getSession, listAuthenticationMethodTypes } from "@/lib/zitadel";
-import { getSessionCookieByLoginName } from "../cookies";
-import { createSessionAndUpdateCookie } from "./cookie";
-import { cookies } from "next/headers";
-
describe("sendVerification", () => {
let mockVerifyEmail: any;
let mockGetUserByID: any;
diff --git a/apps/login/src/lib/server/verify.ts b/apps/login/src/lib/server/verify.ts
index f687811c7..c48fe3561 100644
--- a/apps/login/src/lib/server/verify.ts
+++ b/apps/login/src/lib/server/verify.ts
@@ -1,5 +1,6 @@
"use server";
+import { createLogger } from "@/lib/logger";
import {
createInviteCode,
getLoginSettings,
@@ -28,6 +29,8 @@ import { createSessionAndUpdateCookie } from "./cookie";
import { getPublicHostWithProtocol } from "./host";
import { getTranslations } from "next-intl/server";
+const logger = createLogger("verify");
+
export async function verifyTOTP(code: string, loginName?: string, organization?: string) {
const _headers = await headers();
const { serviceConfig } = getServiceConfig(_headers);
@@ -63,11 +66,11 @@ export async function sendVerification(command: VerifyUserByEmailCommand) {
const verifyResponse = command.isInvite
? await verifyInviteCode({ serviceConfig, userId: command.userId, verificationCode: command.code }).catch((error) => {
- console.warn(error);
+ logger.warn("Could not verify invite:", { error });
return { error: t("errors.couldNotVerifyInvite") };
})
: await verifyEmail({ serviceConfig, userId: command.userId, verificationCode: command.code }).catch((error) => {
- console.warn(error);
+ logger.warn("Could not verify email:", { error });
return { error: t("errors.couldNotVerifyEmail") };
});
@@ -102,7 +105,7 @@ export async function sendVerification(command: VerifyUserByEmailCommand) {
})
.catch((error) => {
// user session is not found, so we create a new one
- console.warn("[verify] user session is not found, so we create a new one", error);
+ logger.warn("[verify] user session is not found, so we create a new one", { error });
return undefined;
});
}
diff --git a/apps/login/src/lib/verify-helper.test.ts b/apps/login/src/lib/verify-helper.test.ts
index 47357755d..2b3890a91 100644
--- a/apps/login/src/lib/verify-helper.test.ts
+++ b/apps/login/src/lib/verify-helper.test.ts
@@ -4,6 +4,15 @@ import { cookies } from "next/headers";
import { getFingerprintIdCookie } from "./fingerprint";
import crypto from "crypto";
+import {
+ checkPasswordChangeRequired,
+ checkEmailVerified,
+ checkEmailVerification,
+ checkMFAFactors,
+ checkUserVerification,
+} from "./verify-helper";
+import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
+
// Mock function to create timestamps - following the same pattern as session.test.ts
function createMockTimestamp(offsetMs = 3600000): any {
return {
@@ -324,15 +333,6 @@ describe("shouldEnforceMFA", () => {
});
});
-import {
- checkPasswordChangeRequired,
- checkEmailVerified,
- checkEmailVerification,
- checkMFAFactors,
- checkUserVerification,
-} from "./verify-helper";
-import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
-
// Mock dependencies
vi.mock("@zitadel/client", () => ({
timestampDate: vi.fn((ts: any) => new Date(Number(ts.seconds) * 1000)),
diff --git a/apps/login/src/lib/zitadel.ts b/apps/login/src/lib/zitadel.ts
index a8c93119a..70f3c956a 100644
--- a/apps/login/src/lib/zitadel.ts
+++ b/apps/login/src/lib/zitadel.ts
@@ -1,6 +1,8 @@
import { Client, create, Duration } from "@zitadel/client";
import { createServerTransport as libCreateServerTransport } from "@zitadel/client/node";
import { makeReqCtx } from "@zitadel/client/v2";
+import { otelGrpcInterceptor } from "./grpc/interceptors/otel";
+import { createLogger } from "./logger";
import { IdentityProviderService } from "@zitadel/proto/zitadel/idp/v2/idp_service_pb";
import { OrganizationSchema, TextQueryMethod } from "@zitadel/proto/zitadel/object/v2/object_pb";
import { CreateCallbackRequest, OIDCService } from "@zitadel/proto/zitadel/oidc/v2/oidc_service_pb";
@@ -35,6 +37,8 @@ import { getUserAgent } from "./fingerprint";
import { createServiceForHost } from "./service";
+const logger = createLogger("zitadel");
+
const useCache = process.env.DEBUG !== "true";
async function cacheWrapper(callback: Promise) {
@@ -1238,43 +1242,49 @@ export type WithServiceConfig = T & {
};
export function createServerTransport(token: string, serviceConfig: ServiceConfig) {
+ // Build interceptors list - OTEL interceptor is always included for trace propagation
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const interceptors: any[] = [otelGrpcInterceptor];
+
+ // Add custom headers interceptor if needed
+ if (process.env.CUSTOM_REQUEST_HEADERS || serviceConfig.instanceHost || serviceConfig.publicHost) {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ interceptors.push((next: any) => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ return (req: any) => {
+ // Apply headers from serviceConfig
+ if (serviceConfig.instanceHost) {
+ req.header.set("x-zitadel-instance-host", serviceConfig.instanceHost);
+ }
+ if (serviceConfig.publicHost) {
+ req.header.set("x-zitadel-public-host", serviceConfig.publicHost);
+ }
+
+ // Apply headers from CUSTOM_REQUEST_HEADERS environment variable
+ if (process.env.CUSTOM_REQUEST_HEADERS) {
+ process.env.CUSTOM_REQUEST_HEADERS.split(",").forEach((header) => {
+ const kv = header.indexOf(":");
+ if (kv > 0) {
+ const key = header.slice(0, kv).trim();
+ const value = header.slice(kv + 1).trim();
+ if (value) {
+ req.header.set(key, value);
+ } else {
+ req.header.delete(key);
+ }
+ } else {
+ logger.warn("Skipping malformed header", { header });
+ }
+ });
+ }
+
+ return next(req);
+ };
+ });
+ }
+
return libCreateServerTransport(token, {
baseUrl: serviceConfig.baseUrl,
- interceptors:
- !process.env.CUSTOM_REQUEST_HEADERS && !serviceConfig.instanceHost && !serviceConfig.publicHost
- ? undefined
- : [
- (next) => {
- return (req) => {
- // Apply headers from serviceConfig
- if (serviceConfig.instanceHost) {
- req.header.set("x-zitadel-instance-host", serviceConfig.instanceHost);
- }
- if (serviceConfig.publicHost) {
- req.header.set("x-zitadel-public-host", serviceConfig.publicHost);
- }
-
- // Apply headers from CUSTOM_REQUEST_HEADERS environment variable
- if (process.env.CUSTOM_REQUEST_HEADERS) {
- process.env.CUSTOM_REQUEST_HEADERS.split(",").forEach((header) => {
- const kv = header.indexOf(":");
- if (kv > 0) {
- const key = header.slice(0, kv).trim();
- const value = header.slice(kv + 1).trim();
- if (value) {
- req.header.set(key, value);
- } else {
- req.header.delete(key);
- }
- } else {
- console.warn(`Skipping malformed header: ${header}`);
- }
- });
- }
-
- return next(req);
- };
- },
- ],
+ interceptors,
});
}
diff --git a/apps/login/src/middleware.ts b/apps/login/src/middleware.ts
index a3348e583..b4bf90bf6 100644
--- a/apps/login/src/middleware.ts
+++ b/apps/login/src/middleware.ts
@@ -3,7 +3,12 @@ import { SecuritySettings } from "@zitadel/proto/zitadel/settings/v2/security_se
import { headers } from "next/headers";
import { NextRequest, NextResponse } from "next/server";
import { buildCSP } from "./lib/csp";
+import { createLogger } from "./lib/logger";
import { getServiceConfig } from "./lib/service-url";
+
+export const runtime = "nodejs";
+
+const logger = createLogger("middleware");
export const config = {
matcher: ["/.well-known/:path*", "/oauth/:path*", "/oidc/:path*", "/idps/callback/:path*", "/saml/:path*", "/:path*"],
};
@@ -12,14 +17,14 @@ async function loadSecuritySettings(request: NextRequest): Promise=0.1.90'}
+ '@colors/colors@1.6.0':
+ resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==}
+ engines: {node: '>=0.1.90'}
+
'@connectrpc/connect-node@2.1.1':
resolution: {integrity: sha512-s3TfsI1XF+n+1z6MBS9rTnFsxxR4Rw5wmdEnkQINli81ESGxcsfaEet8duzq8LVuuCupmhUsgpRo0Nv9pZkufg==}
engines: {node: '>=20'}
@@ -1525,6 +1568,9 @@ packages:
'@cypress/xvfb@1.2.4':
resolution: {integrity: sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==}
+ '@dabh/diagnostics@2.0.8':
+ resolution: {integrity: sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==}
+
'@emnapi/core@1.8.1':
resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==}
@@ -3331,10 +3377,39 @@ packages:
resolution: {integrity: sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==}
engines: {node: '>=8.0.0'}
+ '@opentelemetry/api-logs@0.211.0':
+ resolution: {integrity: sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==}
+ engines: {node: '>=8.0.0'}
+
'@opentelemetry/api@1.9.0':
resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
engines: {node: '>=8.0.0'}
+ '@opentelemetry/auto-instrumentations-node@0.69.0':
+ resolution: {integrity: sha512-m/wqAaeZi3VkT2izPRivEfZrvKR+cP7Y/Jkic9D8QClGFpfd3bgvfUZS+OA2MzL+RT46sO27G5TKPN+M35xQJg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.4.1
+ '@opentelemetry/core': ^2.0.0
+
+ '@opentelemetry/configuration@0.211.0':
+ resolution: {integrity: sha512-PNsCkzsYQKyv8wiUIsH+loC4RYyblOaDnVASBtKS22hK55ToWs2UP6IsrcfSWWn54wWTvVe2gnfwz67Pvrxf2Q==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.9.0
+
+ '@opentelemetry/context-async-hooks@2.5.0':
+ resolution: {integrity: sha512-uOXpVX0ZjO7heSVjhheW2XEPrhQAWr2BScDPoZ9UDycl5iuHG+Usyc3AIfG6kZeC1GyLpMInpQ6X5+9n69yOFw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.0.0 <1.10.0'
+
+ '@opentelemetry/context-async-hooks@2.5.1':
+ resolution: {integrity: sha512-MHbu8XxCHcBn6RwvCt2Vpn1WnLMNECfNKYB14LI5XypcgH4IE0/DiVifVR9tAkwPMyLXN8dOoPJfya3IryLQVw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.0.0 <1.10.0'
+
'@opentelemetry/core@2.2.0':
resolution: {integrity: sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==}
engines: {node: ^18.19.0 || >=20.6.0}
@@ -3347,24 +3422,412 @@ packages:
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0'
+ '@opentelemetry/core@2.5.1':
+ resolution: {integrity: sha512-Dwlc+3HAZqpgTYq0MUyZABjFkcrKTePwuiFVLjahGD8cx3enqihmpAmdgNFO1R4m/sIe5afjJrA25Prqy4NXlA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.0.0 <1.10.0'
+
+ '@opentelemetry/exporter-logs-otlp-grpc@0.211.0':
+ resolution: {integrity: sha512-UhOoWENNqyaAMP/dL1YXLkXt6ZBtovkDDs1p4rxto9YwJX1+wMjwg+Obfyg2kwpcMoaiIFT3KQIcLNW8nNGNfQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
'@opentelemetry/exporter-logs-otlp-http@0.208.0':
resolution: {integrity: sha512-jOv40Bs9jy9bZVLo/i8FwUiuCvbjWDI+ZW13wimJm4LjnlwJxGgB+N/VWOZUTpM+ah/awXeQqKdNlpLf2EjvYg==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
+ '@opentelemetry/exporter-logs-otlp-http@0.211.0':
+ resolution: {integrity: sha512-c118Awf1kZirHkqxdcF+rF5qqWwNjJh+BB1CmQvN9AQHC/DUIldy6dIkJn3EKlQnQ3HmuNRKc/nHHt5IusN7mA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/exporter-logs-otlp-proto@0.211.0':
+ resolution: {integrity: sha512-kMvfKMtY5vJDXeLnwhrZMEwhZ2PN8sROXmzacFU/Fnl4Z79CMrOaL7OE+5X3SObRYlDUa7zVqaXp9ZetYCxfDQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/exporter-metrics-otlp-grpc@0.211.0':
+ resolution: {integrity: sha512-D/U3G8L4PzZp8ot5hX9wpgbTymgtLZCiwR7heMe4LsbGV4OdctS1nfyvaQHLT6CiGZ6FjKc1Vk9s6kbo9SWLXQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/exporter-metrics-otlp-http@0.211.0':
+ resolution: {integrity: sha512-lfHXElPAoDSPpPO59DJdN5FLUnwi1wxluLTWQDayqrSPfWRnluzxRhD+g7rF8wbj1qCz0sdqABl//ug1IZyWvA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/exporter-metrics-otlp-proto@0.211.0':
+ resolution: {integrity: sha512-61iNbffEpyZv/abHaz3BQM3zUtA2kVIDBM+0dS9RK68ML0QFLRGYa50xVMn2PYMToyfszEPEgFC3ypGae2z8FA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/exporter-prometheus@0.211.0':
+ resolution: {integrity: sha512-cD0WleEL3TPqJbvxwz5MVdVJ82H8jl8mvMad4bNU24cB5SH2mRW5aMLDTuV4614ll46R//R3RMmci26mc2L99g==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/exporter-trace-otlp-grpc@0.211.0':
+ resolution: {integrity: sha512-eFwx4Gvu6LaEiE1rOd4ypgAiWEdZu7Qzm2QNN2nJqPW1XDeAVH1eNwVcVQl+QK9HR/JCDZ78PZgD7xD/DBDqbw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/exporter-trace-otlp-http@0.211.0':
+ resolution: {integrity: sha512-F1Rv3JeMkgS//xdVjbQMrI3+26e5SXC7vXA6trx8SWEA0OUhw4JHB+qeHtH0fJn46eFItrYbL5m8j4qi9Sfaxw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/exporter-trace-otlp-proto@0.211.0':
+ resolution: {integrity: sha512-DkjXwbPiqpcPlycUojzG2RmR0/SIK8Gi9qWO9znNvSqgzrnAIE9x2n6yPfpZ+kWHZGafvsvA1lVXucTyyQa5Kg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/exporter-zipkin@2.5.0':
+ resolution: {integrity: sha512-bk9VJgFgUAzkZzU8ZyXBSWiUGLOM3mZEgKJ1+jsZclhRnAoDNf+YBdq+G9R3cP0+TKjjWad+vVrY/bE/vRR9lA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.0.0
+
+ '@opentelemetry/instrumentation-amqplib@0.58.0':
+ resolution: {integrity: sha512-fjpQtH18J6GxzUZ+cwNhWUpb71u+DzT7rFkg5pLssDGaEber91Y2WNGdpVpwGivfEluMlNMZumzjEqfg8DeKXQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-aws-lambda@0.63.0':
+ resolution: {integrity: sha512-XEkXvrBtIKPgp6kFSuNV3FpugGiLIz3zpjXu/7t9ioBKN7pZG5hef3VCPUhtyE8UZ3N3D9rkjSLaDOND0inNrg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-aws-sdk@0.66.0':
+ resolution: {integrity: sha512-K+vFDsD0RsjxjCOWGOKgaqOoE5wxIPMA8wnGJ0no3m7MjVdpkS/dNOGUx2nYegpqZzU/jZ0qvc+JrfkvkzcUyg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-bunyan@0.56.0':
+ resolution: {integrity: sha512-cTt3gLGxBvgjgUTBeMz6MaFAHXFQM/N3411mZFTzlczuOQTlsuJTn+fWTah/a0el9NsepO5LdbULRBNmA9rSUw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-cassandra-driver@0.56.0':
+ resolution: {integrity: sha512-56Yd41E15QlciuqC6DZR2KdeetXzhdcwp1BRRb8ORsHbRQWbvPdhV8vpvkrvs3cvY8N1KoqtPgh7mdkVhyQz+Q==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-connect@0.54.0':
+ resolution: {integrity: sha512-43RmbhUhqt3uuPnc16cX6NsxEASEtn8z/cYV8Zpt6EP4p2h9s4FNuJ4Q9BbEQ2C0YlCCB/2crO1ruVz/hWt8fA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-cucumber@0.26.0':
+ resolution: {integrity: sha512-LGSgNR9gMJ3eiChbW9WjFgiCdJwdPKwARZwRE1s57CGY8/B3emAoQt2B05TY1y2TQuQKRBFbyNVXpWHFl9WQGQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.0.0
+
+ '@opentelemetry/instrumentation-dataloader@0.28.0':
+ resolution: {integrity: sha512-ExXGBp0sUj8yhm6Znhf9jmuOaGDsYfDES3gswZnKr4MCqoBWQdEFn6EoDdt5u+RdbxQER+t43FoUihEfTSqsjA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-dns@0.54.0':
+ resolution: {integrity: sha512-CvnGlYr8FKB2SeqauqJ7bSgZhrkVYj1vgbqFcbc/wnQcc03jc+afngkduahHiBgnJr+CYL/p3XjdKWp7AKYoGg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-express@0.59.0':
+ resolution: {integrity: sha512-pMKV/qnHiW/Q6pmbKkxt0eIhuNEtvJ7sUAyee192HErlr+a1Jx+FZ3WjfmzhQL1geewyGEiPGkmjjAgNY8TgDA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-fastify@0.55.0':
+ resolution: {integrity: sha512-kkx8ODI57dN+mMW+nPuE9gniSXs/LlxWiPoXXiAJhtQJPpMqQwncHlMo+1c+qzQC5aQWkKdDskJG7TPnACNgcw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-fs@0.30.0':
+ resolution: {integrity: sha512-n3Cf8YhG7reaj5dncGlRIU7iT40bxPOjsBEA5Bc1a1g6e9Qvb+JFJ7SEiMlPbUw4PBmxE3h40ltE8LZ3zVt6OA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-generic-pool@0.54.0':
+ resolution: {integrity: sha512-8dXMBzzmEdXfH/wjuRvcJnUFeWzZHUnExkmFJ2uPfa31wmpyBCMxO59yr8f/OXXgSogNgi/uPo9KW9H7LMIZ+g==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-graphql@0.58.0':
+ resolution: {integrity: sha512-+yWVVY7fxOs3j2RixCbvue8vUuJ1inHxN2q1sduqDB0Wnkr4vOzVKRYl/Zy7B31/dcPS72D9lo/kltdOTBM3bQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-grpc@0.211.0':
+ resolution: {integrity: sha512-bshedE3TaD18OE3oPU15j8bn4vz+3X5mvg9jluoSn/ZjlshCb1FrstjNkTYQuRERWzeMl7WcR8sShr91FcUBXA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-hapi@0.57.0':
+ resolution: {integrity: sha512-Os4THbvls8cTQTVA8ApLfZZztuuqGEeqog0XUnyRW7QVF0d/vOVBEcBCk1pazPFmllXGEdNbbat8e2fYIWdFbw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-http@0.211.0':
+ resolution: {integrity: sha512-n0IaQ6oVll9PP84SjbOCwDjaJasWRHi6BLsbMLiT6tNj7QbVOkuA5sk/EfZczwI0j5uTKl1awQPivO/ldVtsqA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-ioredis@0.59.0':
+ resolution: {integrity: sha512-875UxzBHWkW+P4Y45SoFM2AR8f8TzBMD8eO7QXGCyFSCUMP5s9vtt/BS8b/r2kqLyaRPK6mLbdnZznK3XzQWvw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-kafkajs@0.20.0':
+ resolution: {integrity: sha512-yJXOuWZROzj7WmYCUiyT27tIfqBrVtl1/TwVbQyWPz7rL0r1Lu7kWjD0PiVeTCIL6CrIZ7M2s8eBxsTAOxbNvw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-knex@0.55.0':
+ resolution: {integrity: sha512-FtTL5DUx5Ka/8VK6P1VwnlUXPa3nrb7REvm5ddLUIeXXq4tb9pKd+/ThB1xM/IjefkRSN3z8a5t7epYw1JLBJQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-koa@0.59.0':
+ resolution: {integrity: sha512-K9o2skADV20Skdu5tG2bogPKiSpXh4KxfLjz6FuqIVvDJNibwSdu5UvyyBzRVp1rQMV6UmoIk6d3PyPtJbaGSg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.9.0
+
+ '@opentelemetry/instrumentation-lru-memoizer@0.55.0':
+ resolution: {integrity: sha512-FDBfT7yDGcspN0Cxbu/k8A0Pp1Jhv/m7BMTzXGpcb8ENl3tDj/51U65R5lWzUH15GaZA15HQ5A5wtafklxYj7g==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-memcached@0.54.0':
+ resolution: {integrity: sha512-7lG+XMQVt8I+/qc4U0KAwabnIAn4CubmxBPftlrChmcok6wbv6z6W+SCVNBbN13FvPgum8NO0YwyuUXMmCyXvg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-mongodb@0.64.0':
+ resolution: {integrity: sha512-pFlCJjweTqVp7B220mCvCld1c1eYKZfQt1p3bxSbcReypKLJTwat+wbL2YZoX9jPi5X2O8tTKFEOahO5ehQGsA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-mongoose@0.57.0':
+ resolution: {integrity: sha512-MthiekrU/BAJc5JZoZeJmo0OTX6ycJMiP6sMOSRTkvz5BrPMYDqaJos0OgsLPL/HpcgHP7eo5pduETuLguOqcg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-mysql2@0.57.0':
+ resolution: {integrity: sha512-nHSrYAwF7+aV1E1V9yOOP9TchOodb6fjn4gFvdrdQXiRE7cMuffyLLbCZlZd4wsspBzVwOXX8mpURdRserAhNA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-mysql@0.57.0':
+ resolution: {integrity: sha512-HFS/+FcZ6Q7piM7Il7CzQ4VHhJvGMJWjx7EgCkP5AnTntSN5rb5Xi3TkYJHBKeR27A0QqPlGaCITi93fUDs++Q==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-nestjs-core@0.57.0':
+ resolution: {integrity: sha512-mzTjjethjuk70o/vWUeV12QwMG9EAFJpkn13/q8zi++sNosf2hoGXTplIdbs81U8S3PJ4GxHKsBjM0bj1CGZ0g==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-net@0.55.0':
+ resolution: {integrity: sha512-J7isLTAmBphAKX99fZgR/jYFRJk+d5E3yVDEd7eTcyPPwFDN/LM8J8j/H5gP4ukZCbt0mtKnx1CA+P5+qw7xFQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-openai@0.9.0':
+ resolution: {integrity: sha512-Tf3shDZZo3pKz0LBschaEfX+SgpwMITnm8moOMzr6Fc10sKU96GxFwMmEg2JC0JW5x56kGJuwRoXZCVL66GBgg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-oracledb@0.36.0':
+ resolution: {integrity: sha512-VyfdaRfr/xnx/ndQnCCk34z7HqADxmRi47SLTzL9m79LrA+F1qK49nCcqbeiFfeVJ2RA5NmfSS+BllFE4RGnsw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-pg@0.63.0':
+ resolution: {integrity: sha512-dKm/ODNN3GgIQVlbD6ZPxwRc3kleLf95hrRWXM+l8wYo+vSeXtEpQPT53afEf6VFWDVzJK55VGn8KMLtSve/cg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-pino@0.57.0':
+ resolution: {integrity: sha512-Oa+PT1fxWQo88KSfibLJSyCwdV9Kb2iqjpIbfMK5CFcyeOGfth8mVSFjvQEaCo+Tdbpq9Y8Ylyi4/XmWrxStew==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-redis@0.59.0':
+ resolution: {integrity: sha512-JKv1KDDYA2chJ1PC3pLP+Q9ISMQk6h5ey+99mB57/ARk0vQPGZTTEb4h4/JlcEpy7AYT8HIGv7X6l+br03Neeg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-restify@0.56.0':
+ resolution: {integrity: sha512-ZkPT7zoIx6du3u7Js4n7FEw1FvNdeIpprpcM0pR4p7kfgQ82ZzhfJ7ilWKxT9Hpe6HMu+yFLicFyS1b83XcVMQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-router@0.55.0':
+ resolution: {integrity: sha512-8IA64a6+vVQavH1qj2W/0mPOr1uS6ROkLoV29p+3At2omEIgn13g46yslKqU5lIgMSn9uzU4tSlOTe6vQM4dIg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-runtime-node@0.24.0':
+ resolution: {integrity: sha512-1gNjTpHhgHIkRXivY4Nk+jS+2oChwQSnEVne4AHvlY0tzLHpWE+LEZV6DoiN7Ui93/UpnebhMsF0YUnFZaeJdg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-socket.io@0.57.0':
+ resolution: {integrity: sha512-0FhO9/UPnOsRbbVHLxgffXMEdATNJQauwM+X4+X6UaV9EANEhci+etMX9R06xprJRvE3kDcfXoMn2MTF3RdNDw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-tedious@0.30.0':
+ resolution: {integrity: sha512-bZy9Q8jFdycKQ2pAsyuHYUHNmCxCOGdG6eg1Mn75RvQDccq832sU5OWOBnc12EFUELI6icJkhR7+EQKMBam2GA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-undici@0.21.0':
+ resolution: {integrity: sha512-gok0LPUOTz2FQ1YJMZzaHcOzDFyT64XJ8M9rNkugk923/p6lDGms/cRW1cqgqp6N6qcd6K6YdVHwPEhnx9BWbw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.7.0
+
+ '@opentelemetry/instrumentation-winston@0.55.0':
+ resolution: {integrity: sha512-RKW/PYJrvIbRYss0uKe0eU+FgIRScnQTJXIWAZK17ViHf7EALaRDXOu3tFW5JDRg6fkccj5q90YZUCzh6s0v5A==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation@0.211.0':
+ resolution: {integrity: sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
'@opentelemetry/otlp-exporter-base@0.208.0':
resolution: {integrity: sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
+ '@opentelemetry/otlp-exporter-base@0.211.0':
+ resolution: {integrity: sha512-bp1+63V8WPV+bRI9EQG6E9YID1LIHYSZVbp7f+44g9tRzCq+rtw/o4fpL5PC31adcUsFiz/oN0MdLISSrZDdrg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/otlp-grpc-exporter-base@0.211.0':
+ resolution: {integrity: sha512-mR5X+N4SuphJeb7/K7y0JNMC8N1mB6gEtjyTLv+TSAhl0ZxNQzpSKP8S5Opk90fhAqVYD4R0SQSAirEBlH1KSA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
'@opentelemetry/otlp-transformer@0.208.0':
resolution: {integrity: sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': ^1.3.0
+ '@opentelemetry/otlp-transformer@0.211.0':
+ resolution: {integrity: sha512-julhCJ9dXwkOg9svuuYqqjXLhVaUgyUvO2hWbTxwjvLXX2rG3VtAaB0SzxMnGTuoCZizBT7Xqqm2V7+ggrfCXA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/propagator-b3@2.5.0':
+ resolution: {integrity: sha512-g10m4KD73RjHrSvUge+sUxUl8m4VlgnGc6OKvo68a4uMfaLjdFU+AULfvMQE/APq38k92oGUxEzBsAZ8RN/YHg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.0.0 <1.10.0'
+
+ '@opentelemetry/propagator-jaeger@2.5.0':
+ resolution: {integrity: sha512-t70ErZCncAR/zz5AcGkL0TF25mJiK1FfDPEQCgreyAHZ+mRJ/bNUiCnImIBDlP3mSDXy6N09DbUEKq0ktW98Hg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.0.0 <1.10.0'
+
+ '@opentelemetry/redis-common@0.38.2':
+ resolution: {integrity: sha512-1BCcU93iwSRZvDAgwUxC/DV4T/406SkMfxGqu5ojc3AvNI+I9GhV7v0J1HljsczuuhcnFLYqD5VmwVXfCGHzxA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+
+ '@opentelemetry/resource-detector-alibaba-cloud@0.33.2':
+ resolution: {integrity: sha512-EaS54zwYmOg9Ttc79juaktpCBYqyh2IquXl534sLls+c1/pc8LZfWPMqytFt+iBvSPQ6ajraUnvi6cun4AhSjQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.0.0
+
+ '@opentelemetry/resource-detector-aws@2.12.0':
+ resolution: {integrity: sha512-VelueKblsnQEiBVqEYcvM9VEb+B8zN6nftltdO9HAD7qi/OlicP4z/UGJ9EeW2m++WabdMoj0G3QVL8YV0P9tw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.0.0
+
+ '@opentelemetry/resource-detector-azure@0.19.0':
+ resolution: {integrity: sha512-3UBJYyAfQY7aqot4xBvTsGlxi9Ax5XwWlddCvFPNIfZiy5KX405w3KThcRypadVsP5Q9D/lr/WAn5J+xXTqJoA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.0.0
+
+ '@opentelemetry/resource-detector-container@0.8.3':
+ resolution: {integrity: sha512-5J0JP2cy655rBKM9Doz26ffO3rG+Xqm7OXeNXkckzmc3JmL6Bj3dPBKugPYsfemhEIqtf7INH9UmPQqTMuWoHg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.0.0
+
+ '@opentelemetry/resource-detector-gcp@0.46.0':
+ resolution: {integrity: sha512-CulcNXV/a4lc4TTYFdApTfRg4DlCwiUilsXnEsRfFSK/p/EbkfgEQz8hB4tZF5z/Us9MnhtuT6l4Kj4Ng8qLcw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.0.0
+
'@opentelemetry/resources@2.2.0':
resolution: {integrity: sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==}
engines: {node: ^18.19.0 || >=20.6.0}
@@ -3377,28 +3840,86 @@ packages:
peerDependencies:
'@opentelemetry/api': '>=1.3.0 <1.10.0'
+ '@opentelemetry/resources@2.5.1':
+ resolution: {integrity: sha512-BViBCdE/GuXRlp9k7nS1w6wJvY5fnFX5XvuEtWsTAOQFIO89Eru7lGW3WbfbxtCuZ/GbrJfAziXG0w0dpxL7eQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.3.0 <1.10.0'
+
'@opentelemetry/sdk-logs@0.208.0':
resolution: {integrity: sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.4.0 <1.10.0'
+ '@opentelemetry/sdk-logs@0.211.0':
+ resolution: {integrity: sha512-O5nPwzgg2JHzo59kpQTPUOTzFi0Nv5LxryG27QoXBciX3zWM3z83g+SNOHhiQVYRWFSxoWn1JM2TGD5iNjOwdA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.4.0 <1.10.0'
+
'@opentelemetry/sdk-metrics@2.2.0':
resolution: {integrity: sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.9.0 <1.10.0'
+ '@opentelemetry/sdk-metrics@2.5.0':
+ resolution: {integrity: sha512-BeJLtU+f5Gf905cJX9vXFQorAr6TAfK3SPvTFqP+scfIpDQEJfRaGJWta7sJgP+m4dNtBf9y3yvBKVAZZtJQVA==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.9.0 <1.10.0'
+
+ '@opentelemetry/sdk-node@0.211.0':
+ resolution: {integrity: sha512-+s1eGjoqmPCMptNxcJJD4IxbWJKNLOQFNKhpwkzi2gLkEbCj6LzSHJNhPcLeBrBlBLtlSpibM+FuS7fjZ8SSFQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.3.0 <1.10.0'
+
'@opentelemetry/sdk-trace-base@2.2.0':
resolution: {integrity: sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==}
engines: {node: ^18.19.0 || >=20.6.0}
peerDependencies:
'@opentelemetry/api': '>=1.3.0 <1.10.0'
+ '@opentelemetry/sdk-trace-base@2.5.0':
+ resolution: {integrity: sha512-VzRf8LzotASEyNDUxTdaJ9IRJ1/h692WyArDBInf5puLCjxbICD6XkHgpuudis56EndyS7LYFmtTMny6UABNdQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.3.0 <1.10.0'
+
+ '@opentelemetry/sdk-trace-base@2.5.1':
+ resolution: {integrity: sha512-iZH3Gw8cxQn0gjpOjJMmKLd9GIaNh/E3v3ST67vyzLSxHBs14HsG4dy7jMYyC5WXGdBVEcM7U/XTF5hCQxjDMw==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.3.0 <1.10.0'
+
+ '@opentelemetry/sdk-trace-node@2.5.0':
+ resolution: {integrity: sha512-O6N/ejzburFm2C84aKNrwJVPpt6HSTSq8T0ZUMq3xT2XmqT4cwxUItcL5UWGThYuq8RTcbH8u1sfj6dmRci0Ow==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.0.0 <1.10.0'
+
+ '@opentelemetry/sdk-trace-node@2.5.1':
+ resolution: {integrity: sha512-9lopQ6ZoElETOEN0csgmtEV5/9C7BMfA7VtF4Jape3i954b6sTY2k3Xw3CxUTKreDck/vpAuJM+EDo4zheUw+A==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.0.0 <1.10.0'
+
'@opentelemetry/semantic-conventions@1.39.0':
resolution: {integrity: sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==}
engines: {node: '>=14'}
+ '@opentelemetry/sql-common@0.41.2':
+ resolution: {integrity: sha512-4mhWm3Z8z+i508zQJ7r6Xi7y4mmoJpdvH0fZPFRkWrdp5fq7hhZ2HhYokEOLkfqSMgPR4Z9EyB3DBkbKGOqZiQ==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+ peerDependencies:
+ '@opentelemetry/api': ^1.1.0
+
+ '@opentelemetry/winston-transport@0.21.0':
+ resolution: {integrity: sha512-qdWUVX/DtCS9MbxkEq3u96dTBWBEVjYUcwj9EPi+nVyZV7ZXa2Kn+2zD7BfqR+aJahb2KUIm4HDzR8poEdo/Xg==}
+ engines: {node: ^18.19.0 || >=20.6.0}
+
'@orama/orama@3.1.18':
resolution: {integrity: sha512-a61ljmRVVyG5MC/698C8/FfFDw5a8LOIvyOLW5fztgUXqUpc1jOfQzOitSCbge657OgXXThmY3Tk8fpiDb4UcA==}
engines: {node: '>= 20.0.0'}
@@ -4800,6 +5321,9 @@ packages:
'@sinclair/typebox@0.34.41':
resolution: {integrity: sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==}
+ '@so-ric/colorspace@1.1.6':
+ resolution: {integrity: sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==}
+
'@socket.io/component-emitter@3.1.2':
resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==}
@@ -5065,6 +5589,9 @@ packages:
'@types/aria-query@5.0.4':
resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
+ '@types/aws-lambda@8.10.160':
+ resolution: {integrity: sha512-uoO4QVQNWFPJMh26pXtmtrRfGshPUSpMZGUyUQY20FhfHEElEBOPKgVmFs1z+kbpyBsRs2JnoOPT7++Z4GA9pA==}
+
'@types/babel__core@7.20.5':
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
@@ -5077,12 +5604,18 @@ packages:
'@types/babel__traverse@7.28.0':
resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
+ '@types/bunyan@1.8.11':
+ resolution: {integrity: sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ==}
+
'@types/codemirror@5.60.16':
resolution: {integrity: sha512-V/yHdamffSS075jit+fDxaOAmdP2liok8NSNJnAZfDJErzOheuygHZEhAJrfmk5TEyM32MhkZjwo/idX791yxw==}
'@types/concat-stream@2.0.3':
resolution: {integrity: sha512-3qe4oQAPNwVNwK4C9c8u+VJqv9kez+2MR4qJpoPFfXtgxxif1QbFusvXzK0/Wra2VX07smostI2VMmJNSpZjuQ==}
+ '@types/connect@3.4.38':
+ resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
+
'@types/cors@2.8.19':
resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==}
@@ -5155,9 +5688,15 @@ packages:
'@types/mdx@2.0.13':
resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==}
+ '@types/memcached@2.2.10':
+ resolution: {integrity: sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg==}
+
'@types/ms@2.1.0':
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
+ '@types/mysql@2.15.27':
+ resolution: {integrity: sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==}
+
'@types/node-fetch@2.6.13':
resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==}
@@ -5185,6 +5724,15 @@ packages:
'@types/opentype.js@1.3.9':
resolution: {integrity: sha512-KOGywvDPncA4/tTWV5xKNhjpsoSSAHIx3mHOhL5l3XX+c6Xu2dQnHvGs7mRNQsQRte1EqmQ0cPQQ8Z14lkv+yw==}
+ '@types/oracledb@6.5.2':
+ resolution: {integrity: sha512-kK1eBS/Adeyis+3OlBDMeQQuasIDLUYXsi2T15ccNJ0iyUpQ4xDF7svFu3+bGVrI0CMBUclPciz+lsQR3JX3TQ==}
+
+ '@types/pg-pool@2.0.7':
+ resolution: {integrity: sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==}
+
+ '@types/pg@8.15.6':
+ resolution: {integrity: sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==}
+
'@types/prismjs@1.26.5':
resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==}
@@ -5223,12 +5771,18 @@ packages:
'@types/supports-color@8.1.3':
resolution: {integrity: sha512-Hy6UMpxhE3j1tLpl27exp1XqHD7n8chAiNPzWfz16LPZoMMoSc4dzLl6w9qijkEb/r5O1ozdu1CWGA2L83ZeZg==}
+ '@types/tedious@4.0.14':
+ resolution: {integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==}
+
'@types/tern@0.23.9':
resolution: {integrity: sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==}
'@types/tinycolor2@1.4.6':
resolution: {integrity: sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==}
+ '@types/triple-beam@1.3.5':
+ resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==}
+
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
@@ -5798,6 +6352,11 @@ packages:
resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
engines: {node: '>= 0.6'}
+ acorn-import-attributes@1.9.5:
+ resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==}
+ peerDependencies:
+ acorn: ^8
+
acorn-import-phases@1.0.4:
resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==}
engines: {node: '>=10.13.0'}
@@ -6158,6 +6717,9 @@ packages:
big.js@5.2.2:
resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==}
+ bignumber.js@9.3.1:
+ resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==}
+
binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
@@ -6360,6 +6922,9 @@ packages:
resolution: {integrity: sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==}
engines: {node: '>=8'}
+ cjs-module-lexer@2.2.0:
+ resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==}
+
class-variance-authority@0.7.1:
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
@@ -6434,9 +6999,25 @@ packages:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
+ color-convert@3.1.3:
+ resolution: {integrity: sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==}
+ engines: {node: '>=14.6'}
+
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+ color-name@2.1.0:
+ resolution: {integrity: sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==}
+ engines: {node: '>=12.20'}
+
+ color-string@2.1.4:
+ resolution: {integrity: sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==}
+ engines: {node: '>=18'}
+
+ color@5.0.3:
+ resolution: {integrity: sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==}
+ engines: {node: '>=18'}
+
colorette@2.0.20:
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
@@ -6906,6 +7487,9 @@ packages:
resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==}
engines: {node: '>= 4'}
+ enabled@2.0.0:
+ resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==}
+
encodeurl@1.0.2:
resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
engines: {node: '>= 0.8'}
@@ -7421,6 +8005,9 @@ packages:
picomatch:
optional: true
+ fecha@4.2.3:
+ resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==}
+
fetch-blob@3.2.0:
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
engines: {node: ^12.20 || >= 14.13}
@@ -7491,6 +8078,9 @@ packages:
flatted@3.3.3:
resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
+ fn.name@1.1.0:
+ resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==}
+
focus-trap@7.6.6:
resolution: {integrity: sha512-v/Z8bvMCajtx4mEXmOo7QEsIzlIOqRXTIwgUfsFOF9gEsespdbD0AkPIka1bSXZ8Y8oZ+2IVDQZePkTfEHZl7Q==}
@@ -7537,6 +8127,9 @@ packages:
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
engines: {node: '>=12.20.0'}
+ forwarded-parse@2.1.2:
+ resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==}
+
forwarded@0.2.0:
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
engines: {node: '>= 0.6'}
@@ -7747,10 +8340,18 @@ packages:
resolution: {integrity: sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==}
engines: {node: '>=10'}
+ gaxios@6.7.1:
+ resolution: {integrity: sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==}
+ engines: {node: '>=14'}
+
gaxios@7.1.3:
resolution: {integrity: sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==}
engines: {node: '>=18'}
+ gcp-metadata@6.1.1:
+ resolution: {integrity: sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==}
+ engines: {node: '>=14'}
+
gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'}
@@ -7871,6 +8472,10 @@ packages:
globrex@0.1.2:
resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
+ google-logging-utils@0.0.2:
+ resolution: {integrity: sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==}
+ engines: {node: '>=14'}
+
google-protobuf@3.21.4:
resolution: {integrity: sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ==}
@@ -8149,6 +8754,9 @@ packages:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'}
+ import-in-the-middle@2.0.6:
+ resolution: {integrity: sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==}
+
import-meta-resolve@4.2.0:
resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==}
@@ -8582,6 +9190,9 @@ packages:
engines: {node: '>=6'}
hasBin: true
+ json-bigint@1.0.0:
+ resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==}
+
json-buffer@3.0.1:
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
@@ -8714,6 +9325,9 @@ packages:
'@types/node': '>=18'
typescript: '>=5.0.4 <7'
+ kuler@2.0.0:
+ resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==}
+
language-subtag-registry@0.3.23:
resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==}
@@ -8949,6 +9563,10 @@ packages:
resolution: {integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==}
engines: {node: '>=8.0'}
+ logform@2.7.0:
+ resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==}
+ engines: {node: '>= 12.0.0'}
+
long@5.3.2:
resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==}
@@ -9369,6 +9987,9 @@ packages:
peerDependencies:
mocha: '>=7'
+ module-details-from-path@1.0.4:
+ resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==}
+
moment@2.30.1:
resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
@@ -9730,6 +10351,9 @@ packages:
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+ one-time@1.0.0:
+ resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==}
+
onetime@5.1.2:
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
engines: {node: '>=6'}
@@ -9964,6 +10588,17 @@ packages:
performance-now@2.1.0:
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
+ pg-int8@1.0.1:
+ resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
+ engines: {node: '>=4.0.0'}
+
+ pg-protocol@1.11.0:
+ resolution: {integrity: sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==}
+
+ pg-types@2.2.0:
+ resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
+ engines: {node: '>=4'}
+
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -10099,6 +10734,22 @@ packages:
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
engines: {node: ^10 || ^12 || >=14}
+ postgres-array@2.0.0:
+ resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==}
+ engines: {node: '>=4'}
+
+ postgres-bytea@1.0.1:
+ resolution: {integrity: sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==}
+ engines: {node: '>=0.10.0'}
+
+ postgres-date@1.0.7:
+ resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==}
+ engines: {node: '>=0.10.0'}
+
+ postgres-interval@1.2.0:
+ resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
+ engines: {node: '>=0.10.0'}
+
posthog-js@1.336.1:
resolution: {integrity: sha512-YphbVhXnImmZoALvf2oh129Cxu6IRQ9P9sWhuyY+dGe7jqt1jBp6Dg7QEK39stB4rzxmT/N3OLFcWZM7ZYQzCg==}
@@ -10257,6 +10908,10 @@ packages:
resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==}
engines: {node: '>=12.0.0'}
+ protobufjs@8.0.0:
+ resolution: {integrity: sha512-jx6+sE9h/UryaCZhsJWbJtTEy47yXoGNYI4z8ZaRncM0zBKeRqjO2JEcOUYwrYGb1WLhXM1FfMzW3annvFv0rw==}
+ engines: {node: '>=12.0.0'}
+
proxy-addr@2.0.7:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'}
@@ -10585,6 +11240,10 @@ packages:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
+ require-in-the-middle@8.0.1:
+ resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==}
+ engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'}
+
require-main-filename@2.0.0:
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
@@ -10695,6 +11354,10 @@ packages:
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
engines: {node: '>= 0.4'}
+ safe-stable-stringify@2.5.0:
+ resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
+ engines: {node: '>=10'}
+
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
@@ -10762,6 +11425,9 @@ packages:
resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==}
engines: {node: '>= 18'}
+ server-only@0.0.1:
+ resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
+
set-blocking@2.0.0:
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
@@ -10951,6 +11617,9 @@ packages:
stack-generator@2.0.10:
resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==}
+ stack-trace@0.0.10:
+ resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==}
+
stackback@0.0.2:
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
@@ -11252,6 +11921,9 @@ packages:
text-decoder@1.2.3:
resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==}
+ text-hex@1.0.0:
+ resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==}
+
text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
@@ -11356,6 +12028,10 @@ packages:
trim-lines@3.0.1:
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
+ triple-beam@1.4.1:
+ resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==}
+ engines: {node: '>= 14.0.0'}
+
trough@2.2.0:
resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==}
@@ -11717,6 +12393,10 @@ packages:
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
hasBin: true
+ uuid@9.0.1:
+ resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
+ hasBin: true
+
uvu@0.5.6:
resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==}
engines: {node: '>=8'}
@@ -12031,6 +12711,14 @@ packages:
engines: {node: '>=8'}
hasBin: true
+ winston-transport@4.9.0:
+ resolution: {integrity: sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==}
+ engines: {node: '>= 12.0.0'}
+
+ winston@3.19.0:
+ resolution: {integrity: sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==}
+ engines: {node: '>= 12.0.0'}
+
word-wrap@1.2.5:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
@@ -12092,6 +12780,10 @@ packages:
xmlchars@2.2.0:
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+ xtend@4.0.2:
+ resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
+ engines: {node: '>=0.4'}
+
y18n@4.0.3:
resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
@@ -13254,6 +13946,8 @@ snapshots:
'@colors/colors@1.5.0': {}
+ '@colors/colors@1.6.0': {}
+
'@connectrpc/connect-node@2.1.1(@bufbuild/protobuf@2.11.0)(@connectrpc/connect@2.1.1(@bufbuild/protobuf@2.11.0))':
dependencies:
'@bufbuild/protobuf': 2.11.0
@@ -13335,6 +14029,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@dabh/diagnostics@2.0.8':
+ dependencies:
+ '@so-ric/colorspace': 1.1.6
+ enabled: 2.0.0
+ kuler: 2.0.0
+
'@emnapi/core@1.8.1':
dependencies:
'@emnapi/wasi-threads': 1.1.0
@@ -13674,7 +14374,7 @@ snapshots:
dependencies:
'@eslint/object-schema': 3.0.1
debug: 4.4.3(supports-color@8.1.1)
- minimatch: 10.1.1
+ minimatch: 10.2.1
transitivePeerDependencies:
- supports-color
@@ -14886,8 +15586,83 @@ snapshots:
dependencies:
'@opentelemetry/api': 1.9.0
+ '@opentelemetry/api-logs@0.211.0':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+
'@opentelemetry/api@1.9.0': {}
+ '@opentelemetry/auto-instrumentations-node@0.69.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-amqplib': 0.58.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-aws-lambda': 0.63.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-aws-sdk': 0.66.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-bunyan': 0.56.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-cassandra-driver': 0.56.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-connect': 0.54.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-cucumber': 0.26.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-dataloader': 0.28.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-dns': 0.54.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-express': 0.59.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-fastify': 0.55.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-fs': 0.30.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-generic-pool': 0.54.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-graphql': 0.58.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-grpc': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-hapi': 0.57.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-http': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-ioredis': 0.59.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-kafkajs': 0.20.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-knex': 0.55.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-koa': 0.59.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-lru-memoizer': 0.55.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-memcached': 0.54.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-mongodb': 0.64.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-mongoose': 0.57.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-mysql': 0.57.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-mysql2': 0.57.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-nestjs-core': 0.57.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-net': 0.55.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-openai': 0.9.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-oracledb': 0.36.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-pg': 0.63.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-pino': 0.57.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-redis': 0.59.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-restify': 0.56.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-router': 0.55.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-runtime-node': 0.24.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-socket.io': 0.57.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-tedious': 0.30.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-undici': 0.21.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation-winston': 0.55.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resource-detector-alibaba-cloud': 0.33.2(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resource-detector-aws': 2.12.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resource-detector-azure': 0.19.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resource-detector-container': 0.8.3(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resource-detector-gcp': 0.46.0(@opentelemetry/api@1.9.0)(encoding@0.1.13)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-node': 0.211.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - encoding
+ - supports-color
+
+ '@opentelemetry/configuration@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ yaml: 2.8.1
+
+ '@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+
+ '@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+
'@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
@@ -14898,6 +15673,21 @@ snapshots:
'@opentelemetry/api': 1.9.0
'@opentelemetry/semantic-conventions': 1.39.0
+ '@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/semantic-conventions': 1.39.0
+
+ '@opentelemetry/exporter-logs-otlp-grpc@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@grpc/grpc-js': 1.14.3
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-exporter-base': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-grpc-exporter-base': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-transformer': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-logs': 0.211.0(@opentelemetry/api@1.9.0)
+
'@opentelemetry/exporter-logs-otlp-http@0.208.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
@@ -14907,12 +15697,479 @@ snapshots:
'@opentelemetry/otlp-transformer': 0.208.0(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/exporter-logs-otlp-http@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/api-logs': 0.211.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-exporter-base': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-transformer': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-logs': 0.211.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/exporter-logs-otlp-proto@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/api-logs': 0.211.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-exporter-base': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-transformer': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-logs': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-base': 2.5.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/exporter-metrics-otlp-grpc@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@grpc/grpc-js': 1.14.3
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/exporter-metrics-otlp-http': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-exporter-base': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-grpc-exporter-base': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-transformer': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-metrics': 2.5.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/exporter-metrics-otlp-http@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-exporter-base': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-transformer': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-metrics': 2.5.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/exporter-metrics-otlp-proto@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/exporter-metrics-otlp-http': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-exporter-base': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-transformer': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-metrics': 2.5.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/exporter-prometheus@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-metrics': 2.5.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/exporter-trace-otlp-grpc@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@grpc/grpc-js': 1.14.3
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-exporter-base': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-grpc-exporter-base': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-transformer': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-base': 2.5.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/exporter-trace-otlp-http@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-exporter-base': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-transformer': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-base': 2.5.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/exporter-trace-otlp-proto@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-exporter-base': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-transformer': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-base': 2.5.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/exporter-zipkin@2.5.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-base': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+
+ '@opentelemetry/instrumentation-amqplib@0.58.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-aws-lambda@0.63.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ '@types/aws-lambda': 8.10.160
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-aws-sdk@0.66.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-bunyan@0.56.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/api-logs': 0.211.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@types/bunyan': 1.8.11
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-cassandra-driver@0.56.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-connect@0.54.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ '@types/connect': 3.4.38
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-cucumber@0.26.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-dataloader@0.28.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-dns@0.54.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-express@0.59.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-fastify@0.55.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-fs@0.30.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-generic-pool@0.54.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-graphql@0.58.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-grpc@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-hapi@0.57.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-http@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ forwarded-parse: 2.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-ioredis@0.59.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/redis-common': 0.38.2
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-kafkajs@0.20.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-knex@0.55.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-koa@0.59.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-lru-memoizer@0.55.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-memcached@0.54.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ '@types/memcached': 2.2.10
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-mongodb@0.64.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-mongoose@0.57.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-mysql2@0.57.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-mysql@0.57.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ '@types/mysql': 2.15.27
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-nestjs-core@0.57.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-net@0.55.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-openai@0.9.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/api-logs': 0.211.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-oracledb@0.36.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ '@types/oracledb': 6.5.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-pg@0.63.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0)
+ '@types/pg': 8.15.6
+ '@types/pg-pool': 2.0.7
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-pino@0.57.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/api-logs': 0.211.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-redis@0.59.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/redis-common': 0.38.2
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-restify@0.56.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-router@0.55.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-runtime-node@0.24.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-socket.io@0.57.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-tedious@0.30.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ '@types/tedious': 4.0.14
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-undici@0.21.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-winston@0.55.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/api-logs': 0.211.0
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/api-logs': 0.211.0
+ import-in-the-middle: 2.0.6
+ require-in-the-middle: 8.0.1
+ transitivePeerDependencies:
+ - supports-color
+
'@opentelemetry/otlp-exporter-base@0.208.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
'@opentelemetry/otlp-transformer': 0.208.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-exporter-base@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-transformer': 0.211.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/otlp-grpc-exporter-base@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@grpc/grpc-js': 1.14.3
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-exporter-base': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/otlp-transformer': 0.211.0(@opentelemetry/api@1.9.0)
+
'@opentelemetry/otlp-transformer@0.208.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
@@ -14924,6 +16181,65 @@ snapshots:
'@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0)
protobufjs: 7.5.4
+ '@opentelemetry/otlp-transformer@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/api-logs': 0.211.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-logs': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-metrics': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-base': 2.5.0(@opentelemetry/api@1.9.0)
+ protobufjs: 8.0.0
+
+ '@opentelemetry/propagator-b3@2.5.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/propagator-jaeger@2.5.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/redis-common@0.38.2': {}
+
+ '@opentelemetry/resource-detector-alibaba-cloud@0.33.2(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/resource-detector-aws@2.12.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+
+ '@opentelemetry/resource-detector-azure@0.19.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+
+ '@opentelemetry/resource-detector-container@0.8.3(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/resource-detector-gcp@0.46.0(@opentelemetry/api@1.9.0)(encoding@0.1.13)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ gcp-metadata: 6.1.1(encoding@0.1.13)
+ transitivePeerDependencies:
+ - encoding
+ - supports-color
+
'@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
@@ -14936,6 +16252,12 @@ snapshots:
'@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.39.0
+ '@opentelemetry/resources@2.5.1(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+
'@opentelemetry/sdk-logs@0.208.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
@@ -14943,12 +16265,55 @@ snapshots:
'@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
'@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-logs@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/api-logs': 0.211.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+
'@opentelemetry/sdk-metrics@2.2.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0)
'@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-metrics@2.5.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/sdk-node@0.211.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/api-logs': 0.211.0
+ '@opentelemetry/configuration': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/context-async-hooks': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/exporter-logs-otlp-grpc': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/exporter-logs-otlp-http': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/exporter-logs-otlp-proto': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/exporter-metrics-otlp-grpc': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/exporter-metrics-otlp-http': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/exporter-metrics-otlp-proto': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/exporter-prometheus': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/exporter-trace-otlp-grpc': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/exporter-trace-otlp-http': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/exporter-trace-otlp-proto': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/exporter-zipkin': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/propagator-b3': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/propagator-jaeger': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-logs': 0.211.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-metrics': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-base': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-node': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+ transitivePeerDependencies:
+ - supports-color
+
'@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0)':
dependencies:
'@opentelemetry/api': 1.9.0
@@ -14956,8 +16321,46 @@ snapshots:
'@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.39.0
+ '@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+
+ '@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0)
+ '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.39.0
+
+ '@opentelemetry/sdk-trace-node@2.5.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/context-async-hooks': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-base': 2.5.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/sdk-trace-node@2.5.1(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/context-async-hooks': 2.5.1(@opentelemetry/api@1.9.0)
+ '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0)
+ '@opentelemetry/sdk-trace-base': 2.5.1(@opentelemetry/api@1.9.0)
+
'@opentelemetry/semantic-conventions@1.39.0': {}
+ '@opentelemetry/sql-common@0.41.2(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0)
+
+ '@opentelemetry/winston-transport@0.21.0':
+ dependencies:
+ '@opentelemetry/api-logs': 0.211.0
+ winston-transport: 4.9.0
+
'@orama/orama@3.1.18': {}
'@otplib/core@12.0.1': {}
@@ -16663,6 +18066,11 @@ snapshots:
'@sinclair/typebox@0.34.41': {}
+ '@so-ric/colorspace@1.1.6':
+ dependencies:
+ color: 5.0.3
+ text-hex: 1.0.0
+
'@socket.io/component-emitter@3.1.2': {}
'@standard-schema/spec@1.1.0': {}
@@ -16879,7 +18287,7 @@ snapshots:
'@tufjs/models@4.1.0':
dependencies:
'@tufjs/canonical-json': 2.0.0
- minimatch: 10.1.1
+ minimatch: 10.2.1
'@tybys/wasm-util@0.10.1':
dependencies:
@@ -16892,6 +18300,8 @@ snapshots:
'@types/aria-query@5.0.4': {}
+ '@types/aws-lambda@8.10.160': {}
+
'@types/babel__core@7.20.5':
dependencies:
'@babel/parser': 7.28.5
@@ -16913,6 +18323,10 @@ snapshots:
dependencies:
'@babel/types': 7.28.5
+ '@types/bunyan@1.8.11':
+ dependencies:
+ '@types/node': 25.2.3
+
'@types/codemirror@5.60.16':
dependencies:
'@types/tern': 0.23.9
@@ -16921,6 +18335,10 @@ snapshots:
dependencies:
'@types/node': 25.2.3
+ '@types/connect@3.4.38':
+ dependencies:
+ '@types/node': 25.2.3
+
'@types/cors@2.8.19':
dependencies:
'@types/node': 24.10.9
@@ -16997,8 +18415,16 @@ snapshots:
'@types/mdx@2.0.13': {}
+ '@types/memcached@2.2.10':
+ dependencies:
+ '@types/node': 25.2.3
+
'@types/ms@2.1.0': {}
+ '@types/mysql@2.15.27':
+ dependencies:
+ '@types/node': 25.2.3
+
'@types/node-fetch@2.6.13':
dependencies:
'@types/node': 25.2.3
@@ -17030,6 +18456,20 @@ snapshots:
'@types/opentype.js@1.3.9': {}
+ '@types/oracledb@6.5.2':
+ dependencies:
+ '@types/node': 25.2.3
+
+ '@types/pg-pool@2.0.7':
+ dependencies:
+ '@types/pg': 8.15.6
+
+ '@types/pg@8.15.6':
+ dependencies:
+ '@types/node': 25.2.3
+ pg-protocol: 1.11.0
+ pg-types: 2.2.0
+
'@types/prismjs@1.26.5': {}
'@types/prop-types@15.7.15': {}
@@ -17073,12 +18513,18 @@ snapshots:
'@types/supports-color@8.1.3': {}
+ '@types/tedious@4.0.14':
+ dependencies:
+ '@types/node': 25.2.3
+
'@types/tern@0.23.9':
dependencies:
'@types/estree': 1.0.8
'@types/tinycolor2@1.4.6': {}
+ '@types/triple-beam@1.3.5': {}
+
'@types/trusted-types@2.0.7':
optional: true
@@ -17975,6 +19421,10 @@ snapshots:
mime-types: 3.0.1
negotiator: 1.0.0
+ acorn-import-attributes@1.9.5(acorn@8.15.0):
+ dependencies:
+ acorn: 8.15.0
+
acorn-import-phases@1.0.4(acorn@8.15.0):
dependencies:
acorn: 8.15.0
@@ -18373,6 +19823,8 @@ snapshots:
big.js@5.2.2: {}
+ bignumber.js@9.3.1: {}
+
binary-extensions@2.3.0: {}
bl@4.1.0:
@@ -18585,6 +20037,8 @@ snapshots:
ci-info@4.3.0: {}
+ cjs-module-lexer@2.2.0: {}
+
class-variance-authority@0.7.1:
dependencies:
clsx: 2.1.1
@@ -18659,8 +20113,23 @@ snapshots:
dependencies:
color-name: 1.1.4
+ color-convert@3.1.3:
+ dependencies:
+ color-name: 2.1.0
+
color-name@1.1.4: {}
+ color-name@2.1.0: {}
+
+ color-string@2.1.4:
+ dependencies:
+ color-name: 2.1.0
+
+ color@5.0.3:
+ dependencies:
+ color-convert: 3.1.3
+ color-string: 2.1.4
+
colorette@2.0.20: {}
colorjs.io@0.5.2: {}
@@ -19128,6 +20597,8 @@ snapshots:
emojis-list@3.0.0: {}
+ enabled@2.0.0: {}
+
encodeurl@1.0.2: {}
encodeurl@2.0.0: {}
@@ -19459,7 +20930,7 @@ snapshots:
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1)
- eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1)
eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1)
eslint-plugin-react: 7.37.5(eslint@8.57.1)
eslint-plugin-react-hooks: 5.2.0(eslint@8.57.1)
@@ -19573,6 +21044,17 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ eslint-module-utils@2.12.1(@typescript-eslint/parser@8.56.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1):
+ dependencies:
+ debug: 3.2.7(supports-color@8.1.1)
+ optionalDependencies:
+ '@typescript-eslint/parser': 8.56.0(eslint@8.57.1)(typescript@5.9.3)
+ eslint: 8.57.1
+ eslint-import-resolver-node: 0.3.9
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1)
+ transitivePeerDependencies:
+ - supports-color
+
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1):
dependencies:
'@rtsao/scc': 1.1.0
@@ -19631,6 +21113,35 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
+ eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1):
+ dependencies:
+ '@rtsao/scc': 1.1.0
+ array-includes: 3.1.9
+ array.prototype.findlastindex: 1.2.6
+ array.prototype.flat: 1.3.3
+ array.prototype.flatmap: 1.3.3
+ debug: 3.2.7(supports-color@8.1.1)
+ doctrine: 2.1.0
+ eslint: 8.57.1
+ eslint-import-resolver-node: 0.3.9
+ eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.56.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1)
+ hasown: 2.0.2
+ is-core-module: 2.16.1
+ is-glob: 4.0.3
+ minimatch: 3.1.2
+ object.fromentries: 2.0.8
+ object.groupby: 1.0.3
+ object.values: 1.2.1
+ semver: 6.3.1
+ string.prototype.trimend: 1.0.9
+ tsconfig-paths: 3.15.0
+ optionalDependencies:
+ '@typescript-eslint/parser': 8.56.0(eslint@8.57.1)(typescript@5.9.3)
+ transitivePeerDependencies:
+ - eslint-import-resolver-typescript
+ - eslint-import-resolver-webpack
+ - supports-color
+
eslint-plugin-jsx-a11y@6.10.2(eslint@10.0.0(jiti@2.6.1)):
dependencies:
aria-query: 5.3.2
@@ -20113,6 +21624,8 @@ snapshots:
optionalDependencies:
picomatch: 4.0.3
+ fecha@4.2.3: {}
+
fetch-blob@3.2.0:
dependencies:
node-domexception: 1.0.0
@@ -20202,6 +21715,8 @@ snapshots:
flatted@3.3.3: {}
+ fn.name@1.1.0: {}
+
focus-trap@7.6.6:
dependencies:
tabbable: 6.3.0
@@ -20246,6 +21761,8 @@ snapshots:
dependencies:
fetch-blob: 3.2.0
+ forwarded-parse@2.1.2: {}
+
forwarded@0.2.0: {}
fraction.js@5.3.4: {}
@@ -20466,6 +21983,17 @@ snapshots:
fuse.js@7.1.0: {}
+ gaxios@6.7.1(encoding@0.1.13):
+ dependencies:
+ extend: 3.0.2
+ https-proxy-agent: 7.0.6
+ is-stream: 2.0.1
+ node-fetch: 2.7.0(encoding@0.1.13)
+ uuid: 9.0.1
+ transitivePeerDependencies:
+ - encoding
+ - supports-color
+
gaxios@7.1.3:
dependencies:
extend: 3.0.2
@@ -20475,6 +22003,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ gcp-metadata@6.1.1(encoding@0.1.13):
+ dependencies:
+ gaxios: 6.7.1(encoding@0.1.13)
+ google-logging-utils: 0.0.2
+ json-bigint: 1.0.0
+ transitivePeerDependencies:
+ - encoding
+ - supports-color
+
gensync@1.0.0-beta.2: {}
get-caller-file@2.0.5: {}
@@ -20606,6 +22143,8 @@ snapshots:
globrex@0.1.2: {}
+ google-logging-utils@0.0.2: {}
+
google-protobuf@3.21.4: {}
gopd@1.2.0: {}
@@ -20959,7 +22498,7 @@ snapshots:
ignore-walk@8.0.0:
dependencies:
- minimatch: 10.1.1
+ minimatch: 10.2.1
ignore@5.3.2: {}
@@ -20979,6 +22518,13 @@ snapshots:
parent-module: 1.0.1
resolve-from: 4.0.0
+ import-in-the-middle@2.0.6:
+ dependencies:
+ acorn: 8.15.0
+ acorn-import-attributes: 1.9.5(acorn@8.15.0)
+ cjs-module-lexer: 2.2.0
+ module-details-from-path: 1.0.4
+
import-meta-resolve@4.2.0: {}
imurmurhash@0.1.4: {}
@@ -21397,6 +22943,10 @@ snapshots:
jsesc@3.1.0: {}
+ json-bigint@1.0.0:
+ dependencies:
+ bignumber.js: 9.3.1
+
json-buffer@3.0.1: {}
json-parse-even-better-errors@2.3.1: {}
@@ -21578,6 +23128,8 @@ snapshots:
typescript: 5.9.3
zod: 4.3.6
+ kuler@2.0.0: {}
+
language-subtag-registry@0.3.23: {}
language-tags@1.0.9:
@@ -21826,6 +23378,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ logform@2.7.0:
+ dependencies:
+ '@colors/colors': 1.6.0
+ '@types/triple-beam': 1.3.5
+ fecha: 4.2.3
+ ms: 2.1.3
+ safe-stable-stringify: 2.5.0
+ triple-beam: 1.4.1
+
long@5.3.2: {}
longest-streak@3.1.0: {}
@@ -22531,6 +24092,8 @@ snapshots:
strip-ansi: 6.0.1
uuid: 8.3.2
+ module-details-from-path@1.0.4: {}
+
moment@2.30.1: {}
motion-dom@12.34.0:
@@ -22974,6 +24537,10 @@ snapshots:
dependencies:
wrappy: 1.0.2
+ one-time@1.0.0:
+ dependencies:
+ fn.name: 1.1.0
+
onetime@5.1.2:
dependencies:
mimic-fn: 2.1.0
@@ -23272,6 +24839,18 @@ snapshots:
performance-now@2.1.0: {}
+ pg-int8@1.0.1: {}
+
+ pg-protocol@1.11.0: {}
+
+ pg-types@2.2.0:
+ dependencies:
+ pg-int8: 1.0.1
+ postgres-array: 2.0.0
+ postgres-bytea: 1.0.1
+ postgres-date: 1.0.7
+ postgres-interval: 1.2.0
+
picocolors@1.1.1: {}
picomatch@2.3.1: {}
@@ -23376,6 +24955,16 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
+ postgres-array@2.0.0: {}
+
+ postgres-bytea@1.0.1: {}
+
+ postgres-date@1.0.7: {}
+
+ postgres-interval@1.2.0:
+ dependencies:
+ xtend: 4.0.2
+
posthog-js@1.336.1:
dependencies:
'@opentelemetry/api': 1.9.0
@@ -23492,6 +25081,21 @@ snapshots:
'@types/node': 24.10.9
long: 5.3.2
+ protobufjs@8.0.0:
+ dependencies:
+ '@protobufjs/aspromise': 1.1.2
+ '@protobufjs/base64': 1.1.2
+ '@protobufjs/codegen': 2.0.4
+ '@protobufjs/eventemitter': 1.1.0
+ '@protobufjs/fetch': 1.1.0
+ '@protobufjs/float': 1.0.2
+ '@protobufjs/inquire': 1.1.0
+ '@protobufjs/path': 1.1.2
+ '@protobufjs/pool': 1.1.0
+ '@protobufjs/utf8': 1.1.0
+ '@types/node': 25.2.3
+ long: 5.3.2
+
proxy-addr@2.0.7:
dependencies:
forwarded: 0.2.0
@@ -23929,6 +25533,13 @@ snapshots:
require-from-string@2.0.2: {}
+ require-in-the-middle@8.0.1:
+ dependencies:
+ debug: 4.4.3(supports-color@8.1.1)
+ module-details-from-path: 1.0.4
+ transitivePeerDependencies:
+ - supports-color
+
require-main-filename@2.0.0: {}
requires-port@1.0.0: {}
@@ -24062,6 +25673,8 @@ snapshots:
es-errors: 1.3.0
is-regex: 1.2.1
+ safe-stable-stringify@2.5.0: {}
+
safer-buffer@2.1.2: {}
sass@1.90.0:
@@ -24147,6 +25760,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ server-only@0.0.1: {}
+
set-blocking@2.0.0: {}
set-function-length@1.2.2:
@@ -24432,6 +26047,8 @@ snapshots:
dependencies:
stackframe: 1.3.4
+ stack-trace@0.0.10: {}
+
stackback@0.0.2: {}
stackframe@1.3.4: {}
@@ -24822,6 +26439,8 @@ snapshots:
transitivePeerDependencies:
- react-native-b4a
+ text-hex@1.0.0: {}
+
text-table@0.2.0: {}
thenify-all@1.6.0:
@@ -24900,6 +26519,8 @@ snapshots:
trim-lines@3.0.1: {}
+ triple-beam@1.4.1: {}
+
trough@2.2.0: {}
truncate-json@3.0.1:
@@ -25316,6 +26937,8 @@ snapshots:
uuid@8.3.2: {}
+ uuid@9.0.1: {}
+
uvu@0.5.6:
dependencies:
dequal: 2.0.3
@@ -25765,6 +27388,26 @@ snapshots:
siginfo: 2.0.0
stackback: 0.0.2
+ winston-transport@4.9.0:
+ dependencies:
+ logform: 2.7.0
+ readable-stream: 3.6.2
+ triple-beam: 1.4.1
+
+ winston@3.19.0:
+ dependencies:
+ '@colors/colors': 1.6.0
+ '@dabh/diagnostics': 2.0.8
+ async: 3.2.6
+ is-stream: 2.0.1
+ logform: 2.7.0
+ one-time: 1.0.0
+ readable-stream: 3.6.2
+ safe-stable-stringify: 2.5.0
+ stack-trace: 0.0.10
+ triple-beam: 1.4.1
+ winston-transport: 4.9.0
+
word-wrap@1.2.5: {}
workerpool@9.3.4: {}
@@ -25807,6 +27450,8 @@ snapshots:
xmlchars@2.2.0: {}
+ xtend@4.0.2: {}
+
y18n@4.0.3: {}
y18n@5.0.8: {}