From f377645dad973c60d696ce2b92fd1ed8b36a3953 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Thu, 16 Jan 2025 12:34:46 +0000 Subject: [PATCH 01/71] feat: Add axiom logger, nextjs and react libraries --- packages/core/index.ts | 146 ++++++++++++++ packages/core/package.json | 15 ++ packages/core/shared.ts | 3 + packages/core/transports/axiom-fetch.ts | 42 ++++ packages/core/transports/axiom-js.ts | 19 ++ packages/core/transports/console.ts | 13 ++ packages/core/transports/index.ts | 10 + packages/nextjs/index.ts | 1 + packages/nextjs/package.json | 21 ++ packages/nextjs/withAxiom.ts | 144 ++++++++++++++ packages/react/index.ts | 8 + packages/react/package.json | 21 ++ packages/react/use-logger.ts | 36 ++++ pnpm-lock.yaml | 242 ++++++++++++++++++++++++ 14 files changed, 721 insertions(+) create mode 100644 packages/core/index.ts create mode 100644 packages/core/package.json create mode 100644 packages/core/shared.ts create mode 100644 packages/core/transports/axiom-fetch.ts create mode 100644 packages/core/transports/axiom-js.ts create mode 100644 packages/core/transports/console.ts create mode 100644 packages/core/transports/index.ts create mode 100644 packages/nextjs/index.ts create mode 100644 packages/nextjs/package.json create mode 100644 packages/nextjs/withAxiom.ts create mode 100644 packages/react/index.ts create mode 100644 packages/react/package.json create mode 100644 packages/react/use-logger.ts diff --git a/packages/core/index.ts b/packages/core/index.ts new file mode 100644 index 00000000..1f6190d3 --- /dev/null +++ b/packages/core/index.ts @@ -0,0 +1,146 @@ +import { Version } from './shared'; +import { Transport } from './transports'; + +const LOG_LEVEL = 'info'; + +export interface LogEvent { + level: string; + message: string; + fields: any; + _time: string; + '@app': { + 'axiom-logging-version': string; + }; +} + +export enum LogLevel { + debug = 0, + info = 1, + warn = 2, + error = 3, + off = 100, +} + +export const throttle = (fn: Function, wait: number) => { + let lastFn: ReturnType, lastTime: number; + return function (this: any) { + const context = this, + args = arguments; + + // First call, set lastTime + if (lastTime == null) { + lastTime = Date.now(); + } + + clearTimeout(lastFn); + lastFn = setTimeout( + () => { + if (Date.now() - lastTime >= wait) { + fn.apply(context, args); + lastTime = Date.now(); + } + }, + Math.max(wait - (Date.now() - lastTime), 0), + ); + }; +}; + +export type LoggerConfig = { + args?: { [key: string]: any }; + transports: [Transport, ...Transport[]]; + logLevel?: LogLevel; +}; + +export class Logger { + private logEvents: LogEvent[] = []; + children: Logger[] = []; + public logLevel: LogLevel = LogLevel.debug; + public config: LoggerConfig; + + constructor(public initConfig: LoggerConfig) { + // check if user passed a log level, if not the default init value will be used as is. + if (this.initConfig.logLevel != undefined && this.initConfig.logLevel >= 0) { + this.logLevel = this.initConfig.logLevel; + } else if (LOG_LEVEL) { + this.logLevel = LogLevel[LOG_LEVEL as keyof typeof LogLevel]; + } + this.config = { ...initConfig }; + } + + debug = (message: string, args: { [key: string]: any } = {}) => { + this.log(LogLevel.debug, message, args); + }; + info = (message: string, args: { [key: string]: any } = {}) => { + this.log(LogLevel.info, message, args); + }; + warn = (message: string, args: { [key: string]: any } = {}) => { + this.log(LogLevel.warn, message, args); + }; + error = (message: string, args: { [key: string]: any } = {}) => { + this.log(LogLevel.error, message, args); + }; + + with = (args: { [key: string]: any }) => { + const config = { ...this.config, args: { ...this.config.args, ...args } }; + const child = new Logger(config); + this.children.push(child); + return child; + }; + + private _transformEvent = (level: LogLevel, message: string, args: { [key: string]: any } = {}) => { + const logEvent: LogEvent = { + level: LogLevel[level].toString(), + message, + _time: new Date(Date.now()).toISOString(), + fields: this.config.args || {}, + '@app': { + 'axiom-logging-version': Version, + }, + }; + + // check if passed args is an object, if its not an object, add it to fields.args + if (args instanceof Error) { + logEvent.fields = { + ...logEvent.fields, + message: args.message, + stack: args.stack, + name: args.name, + }; + } else if (typeof args === 'object' && args !== null && Object.keys(args).length > 0) { + const parsedArgs = JSON.parse(JSON.stringify(args, jsonFriendlyErrorReplacer)); + logEvent.fields = { ...logEvent.fields, ...parsedArgs }; + } else if (args && args.length) { + logEvent.fields = { ...logEvent.fields, args: args }; + } + + return logEvent; + }; + + log = (level: LogLevel, message: string, args: { [key: string]: any } = {}) => { + this.config.transports.forEach((transport) => transport.log([this._transformEvent(level, message, args)])); + }; + + flush = async () => { + const promises = [ + ...this.config.transports.map((transport) => transport.flush()), + ...this.children.map((child) => child.flush()), + ]; + + await Promise.allSettled(promises); + }; +} + +function jsonFriendlyErrorReplacer(key: string, value: any) { + if (value instanceof Error) { + return { + // Pull all enumerable properties, supporting properties on custom Errors + ...value, + // Explicitly pull Error's non-enumerable properties + name: value.name, + message: value.message, + stack: value.stack, + }; + } + + return value; +} diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 00000000..6a87deaa --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,15 @@ +{ + "name": "@axiomhq/logger", + "version": "0.0.1", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@axiomhq/js": "^1.3.1" + } +} diff --git a/packages/core/shared.ts b/packages/core/shared.ts new file mode 100644 index 00000000..0d2f8e25 --- /dev/null +++ b/packages/core/shared.ts @@ -0,0 +1,3 @@ +import packageJson from "./package.json"; + +export const Version = packageJson.version; diff --git a/packages/core/transports/axiom-fetch.ts b/packages/core/transports/axiom-fetch.ts new file mode 100644 index 00000000..53cc4165 --- /dev/null +++ b/packages/core/transports/axiom-fetch.ts @@ -0,0 +1,42 @@ +import { Transport } from '.'; +import { LogEvent } from '..'; + +interface AxiomFetchConfig { + dataset: string; + token: string; + url?: string; +} + +const DEFAULT_URL = 'https://api.axiom.co'; + +export class AxiomFetchTransport implements Transport { + private config: AxiomFetchConfig; + private events: LogEvent[] = []; + + constructor(config: AxiomFetchConfig) { + this.config = { url: DEFAULT_URL, ...config }; + } + + log: Transport['log'] = (logs) => { + this.events.push(...logs); + }; + + async flush() { + await fetch(`${this.config.url}/v1/datasets/${this.config.dataset}/ingest`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.config.token}`, + }, + body: JSON.stringify(this.events), + }) + .then(async (res) => { + if (!res.ok) { + console.error(await res.text()); + throw new Error('Failed to flush logs'); + } + this.events = []; + }) + .catch(console.error); + } +} diff --git a/packages/core/transports/axiom-js.ts b/packages/core/transports/axiom-js.ts new file mode 100644 index 00000000..4bd63a47 --- /dev/null +++ b/packages/core/transports/axiom-js.ts @@ -0,0 +1,19 @@ +import type { Axiom } from "@axiomhq/js"; + +export class AxiomJSTransport { + private axiom: Axiom; + private dataset: string; + + constructor(axiom: Axiom, dataset: string) { + this.axiom = axiom; + this.dataset = dataset; + } + + log(logs: any[]) { + this.axiom.ingest(this.dataset, logs); + } + + async flush() { + await this.axiom.flush(); + } +} diff --git a/packages/core/transports/console.ts b/packages/core/transports/console.ts new file mode 100644 index 00000000..aef0bfd2 --- /dev/null +++ b/packages/core/transports/console.ts @@ -0,0 +1,13 @@ +import { Transport } from "."; + +export class ConsoleTransport implements Transport { + log: Transport["log"] = (logs) => { + logs.forEach((log) => { + console.log(log); + }); + }; + + flush() { + return; + } +} diff --git a/packages/core/transports/index.ts b/packages/core/transports/index.ts new file mode 100644 index 00000000..dcfe90c3 --- /dev/null +++ b/packages/core/transports/index.ts @@ -0,0 +1,10 @@ +import { LogEvent } from ".."; + +export interface Transport { + log: (logs: LogEvent[]) => Promise | void; + flush: () => Promise | void; +} + +export * from "./console"; +export * from "./axiom-js"; +export * from "./axiom-fetch"; diff --git a/packages/nextjs/index.ts b/packages/nextjs/index.ts new file mode 100644 index 00000000..f9ee3a31 --- /dev/null +++ b/packages/nextjs/index.ts @@ -0,0 +1 @@ +export * from "./withAxiom"; diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json new file mode 100644 index 00000000..72ef24a6 --- /dev/null +++ b/packages/nextjs/package.json @@ -0,0 +1,21 @@ +{ + "name": "@axiomhq/nextjs", + "version": "0.0.1", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@axiomhq/logger": "workspace:^" + }, + "devDependencies": { + "next": "15.1.4" + }, + "peerDependencies": { + "next": "15.1.4" + } +} diff --git a/packages/nextjs/withAxiom.ts b/packages/nextjs/withAxiom.ts new file mode 100644 index 00000000..291a11d5 --- /dev/null +++ b/packages/nextjs/withAxiom.ts @@ -0,0 +1,144 @@ +import { Logger } from '@axiomhq/logger'; +import { isRedirectError } from 'next/dist/client/components/redirect-error'; +import { isHTTPAccessFallbackError } from 'next/dist/client/components/http-access-fallback/http-access-fallback'; +import * as next from 'next/server'; + +const after = next.after; + +export type NextHandler = ( + req: next.NextRequest, + arg?: T, +) => Promise | Promise | next.NextResponse | Response; + +export const transformSuccessResult = (data: SuccessData): [message: string, report: Record] => { + const report = { + type: 'request', + method: data.req.method, + url: data.req.url, + statusCode: data.res.status, + durationMs: data.end - data.start, + path: new URL(data.req.url).pathname, + endTime: data.end, + startTime: data.start, + }; + + return [`${data.req.method} ${report.path} ${report.statusCode} in ${report.endTime - report.startTime}ms`, report]; +}; + +export const transformErrorResult = (data: ErrorData): [message: string, report: Record] => { + const statusCode = data.error instanceof Error ? getNextErrorStatusCode(data.error) : 500; + + const report = { + type: 'request', + method: data.req.method, + url: data.req.url, + statusCode: statusCode, + durationMs: data.end - data.start, + path: new URL(data.req.url).pathname, + endTime: data.end, + startTime: data.start, + }; + + return [`${data.req.method} ${report.path} ${report.statusCode} in ${report.endTime - report.startTime}ms`, report]; +}; + +export interface BaseData { + req: next.NextRequest; + start: number; + end: number; +} +export interface SuccessData extends BaseData { + res: next.NextResponse | Response; +} + +export interface ErrorData extends BaseData { + error: Error | unknown; +} + +export type AxiomHandlerCallbackParams = + | { + ok: true; + data: SuccessData; + } + | { ok: false; data: ErrorData }; + +export type axiomHandlerCallback = (result: AxiomHandlerCallbackParams) => void | Promise; + +export const getNextErrorStatusCode = (error: Error & { digest?: string }) => { + if (!error.digest) { + return 500; + } + + if (isRedirectError(error)) { + return error.digest.split(';')[3]; + } else if (isHTTPAccessFallbackError(error)) { + return error.digest.split(';')[1]; + } +}; + +export const logErrorByStatusCode = (statusCode: number) => { + switch (statusCode) { + case 404: + case 403: + case 401: + return 'warn'; + case 307: + case 308: + return 'info'; + default: + return 'error'; + } +}; + +export const createDefaultAxiomHandlerCallback = (logger: Logger): axiomHandlerCallback => { + return async (result) => { + if (result.ok) { + logger.info(...transformSuccessResult(result.data)); + } else { + if (result.data.error instanceof Error) { + logger.error(result.data.error.message, result.data.error); + const [message, report] = transformErrorResult(result.data); + logger[logErrorByStatusCode(report.statusCode)](message, report); + } + } + }; +}; + +export const createAxiomRouteHandler = (logger: Logger) => { + const withAxiom = ( + handler: NextHandler, + callback: axiomHandlerCallback = createDefaultAxiomHandlerCallback(logger), + ) => { + return async (req: next.NextRequest, ctx: any) => { + const start = Date.now(); + try { + const response = await handler(req, ctx); + const end = Date.now(); + const httpData = { req, res: response, start, end }; + + const callbackFn = async () => callback({ ok: true, data: httpData }); + // TODO: this surely can be written better + if (typeof after !== 'undefined') { + after(callbackFn()); + } else { + await callbackFn(); + } + + return response; + } catch (error) { + const end = Date.now(); + const callbackFn = async () => { + callback({ ok: false, data: { req, error, start, end } }); + }; + if (typeof after !== 'undefined') { + after(callbackFn()); + } else { + await callbackFn(); + } + throw error; + } + }; + }; + + return withAxiom; +}; diff --git a/packages/react/index.ts b/packages/react/index.ts new file mode 100644 index 00000000..d3b794b4 --- /dev/null +++ b/packages/react/index.ts @@ -0,0 +1,8 @@ +import { Logger } from '@axiomhq/logger'; +import { createUseLogger } from './use-logger'; + +export const createClientSideHelpers = (logger: Logger) => { + return { + useLogger: createUseLogger(logger), + }; +}; diff --git a/packages/react/package.json b/packages/react/package.json new file mode 100644 index 00000000..cf39fe08 --- /dev/null +++ b/packages/react/package.json @@ -0,0 +1,21 @@ +{ + "name": "@axiomhq/react", + "version": "0.0.1", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@axiomhq/logger": "workspace:*", + "@types/react": "^19", + "@types/react-dom": "^19", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "use-deep-compare": "^1.3.0", + "web-vitals": "^4.2.4" + } +} diff --git a/packages/react/use-logger.ts b/packages/react/use-logger.ts new file mode 100644 index 00000000..755259d0 --- /dev/null +++ b/packages/react/use-logger.ts @@ -0,0 +1,36 @@ +import { Logger } from '@axiomhq/logger'; +import { useEffect, useState } from 'react'; + +export function createUseLogger(logger: Logger) { + const useLogger = () => { + const [path, setPath] = useState(typeof window !== 'undefined' ? window.location.pathname : ''); + + useEffect(() => { + const handleLocationChange = () => { + setPath(window.location.pathname); + }; + + window.addEventListener('popstate', handleLocationChange); + window.addEventListener('pushState', handleLocationChange); + window.addEventListener('replaceState', handleLocationChange); + + return () => { + window.removeEventListener('popstate', handleLocationChange); + window.removeEventListener('pushState', handleLocationChange); + window.removeEventListener('replaceState', handleLocationChange); + }; + }, []); + + useEffect(() => { + return () => { + if (logger) { + logger.flush(); + } + }; + }, [path]); + + return logger; + }; + + return useLogger; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dd8df765..cca278b8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -214,6 +214,12 @@ importers: specifier: ^8.15.0 version: 8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5) + packages/core: + dependencies: + '@axiomhq/js': + specifier: ^1.3.1 + version: 1.3.1 + packages/js: dependencies: fetch-retry: @@ -230,6 +236,16 @@ importers: specifier: ^2.6.2 version: 2.6.2(@types/node@20.14.2)(typescript@5.4.5) + packages/nextjs: + dependencies: + '@axiomhq/logger': + specifier: workspace:^ + version: link:../core + devDependencies: + next: + specifier: 15.1.4 + version: 15.1.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + packages/pino: dependencies: '@axiomhq/js': @@ -243,6 +259,30 @@ importers: specifier: workspace:^ version: link:../../internal/eslint-config + packages/react: + dependencies: + '@axiomhq/logger': + specifier: workspace:* + version: link:../core + '@types/react': + specifier: ^19 + version: 19.0.7 + '@types/react-dom': + specifier: ^19 + version: 19.0.3(@types/react@19.0.7) + react: + specifier: ^19.0.0 + version: 19.0.0 + react-dom: + specifier: ^19.0.0 + version: 19.0.0(react@19.0.0) + use-deep-compare: + specifier: ^1.3.0 + version: 1.3.0(react@19.0.0) + web-vitals: + specifier: ^4.2.4 + version: 4.2.4 + packages/winston: dependencies: '@axiomhq/js': @@ -265,6 +305,10 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} + '@axiomhq/js@1.3.1': + resolution: {integrity: sha512-Ytf5V3wKz8FKNiqJxnqZmUhjgJ7TItKUoyHVNE/H2V9dN1ozD6NNnsueenOjKdA48cm2sGRyP432nworst18aA==} + engines: {node: '>=16'} + '@babel/helper-string-parser@7.24.7': resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} engines: {node: '>=6.9.0'} @@ -676,6 +720,9 @@ packages: '@next/env@15.0.2': resolution: {integrity: sha512-c0Zr0ModK5OX7D4ZV8Jt/wqoXtitLNPwUfG9zElCZztdaZyNVnN40rDXVZ/+FGuR4CcNV5AEfM6N8f+Ener7Dg==} + '@next/env@15.1.4': + resolution: {integrity: sha512-2fZ5YZjedi5AGaeoaC0B20zGntEHRhi2SdWcu61i48BllODcAmmtj8n7YarSPt4DaTsJaBFdxQAVEVzgmx2Zpw==} + '@next/eslint-plugin-next@15.1.6': resolution: {integrity: sha512-+slMxhTgILUntZDGNgsKEYHUvpn72WP1YTlkmEhS51vnVd7S9jEEy0n9YAMcI21vUG4akTw9voWH02lrClt/yw==} @@ -685,48 +732,96 @@ packages: cpu: [arm64] os: [darwin] + '@next/swc-darwin-arm64@15.1.4': + resolution: {integrity: sha512-wBEMBs+np+R5ozN1F8Y8d/Dycns2COhRnkxRc+rvnbXke5uZBHkUGFgWxfTXn5rx7OLijuUhyfB+gC/ap58dDw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + '@next/swc-darwin-x64@15.0.2': resolution: {integrity: sha512-KUpBVxIbjzFiUZhiLIpJiBoelqzQtVZbdNNsehhUn36e2YzKHphnK8eTUW1s/4aPy5kH/UTid8IuVbaOpedhpw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] + '@next/swc-darwin-x64@15.1.4': + resolution: {integrity: sha512-7sgf5rM7Z81V9w48F02Zz6DgEJulavC0jadab4ZsJ+K2sxMNK0/BtF8J8J3CxnsJN3DGcIdC260wEKssKTukUw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + '@next/swc-linux-arm64-gnu@15.0.2': resolution: {integrity: sha512-9J7TPEcHNAZvwxXRzOtiUvwtTD+fmuY0l7RErf8Yyc7kMpE47MIQakl+3jecmkhOoIyi/Rp+ddq7j4wG6JDskQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + '@next/swc-linux-arm64-gnu@15.1.4': + resolution: {integrity: sha512-JaZlIMNaJenfd55kjaLWMfok+vWBlcRxqnRoZrhFQrhM1uAehP3R0+Aoe+bZOogqlZvAz53nY/k3ZyuKDtT2zQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@next/swc-linux-arm64-musl@15.0.2': resolution: {integrity: sha512-BjH4ZSzJIoTTZRh6rG+a/Ry4SW0HlizcPorqNBixBWc3wtQtj4Sn9FnRZe22QqrPnzoaW0ctvSz4FaH4eGKMww==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + '@next/swc-linux-arm64-musl@15.1.4': + resolution: {integrity: sha512-7EBBjNoyTO2ipMDgCiORpwwOf5tIueFntKjcN3NK+GAQD7OzFJe84p7a2eQUeWdpzZvhVXuAtIen8QcH71ZCOQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@next/swc-linux-x64-gnu@15.0.2': resolution: {integrity: sha512-i3U2TcHgo26sIhcwX/Rshz6avM6nizrZPvrDVDY1bXcLH1ndjbO8zuC7RoHp0NSK7wjJMPYzm7NYL1ksSKFreA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + '@next/swc-linux-x64-gnu@15.1.4': + resolution: {integrity: sha512-9TGEgOycqZFuADyFqwmK/9g6S0FYZ3tphR4ebcmCwhL8Y12FW8pIBKJvSwV+UBjMkokstGNH+9F8F031JZKpHw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@next/swc-linux-x64-musl@15.0.2': resolution: {integrity: sha512-AMfZfSVOIR8fa+TXlAooByEF4OB00wqnms1sJ1v+iu8ivwvtPvnkwdzzFMpsK5jA2S9oNeeQ04egIWVb4QWmtQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + '@next/swc-linux-x64-musl@15.1.4': + resolution: {integrity: sha512-0578bLRVDJOh+LdIoKvgNDz77+Bd85c5JrFgnlbI1SM3WmEQvsjxTA8ATu9Z9FCiIS/AliVAW2DV/BDwpXbtiQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@next/swc-win32-arm64-msvc@15.0.2': resolution: {integrity: sha512-JkXysDT0/hEY47O+Hvs8PbZAeiCQVxKfGtr4GUpNAhlG2E0Mkjibuo8ryGD29Qb5a3IOnKYNoZlh/MyKd2Nbww==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] + '@next/swc-win32-arm64-msvc@15.1.4': + resolution: {integrity: sha512-JgFCiV4libQavwII+kncMCl30st0JVxpPOtzWcAI2jtum4HjYaclobKhj+JsRu5tFqMtA5CJIa0MvYyuu9xjjQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + '@next/swc-win32-x64-msvc@15.0.2': resolution: {integrity: sha512-foaUL0NqJY/dX0Pi/UcZm5zsmSk5MtP/gxx3xOPyREkMFN+CTjctPfu3QaqrQHinaKdPnMWPJDKt4VjDfTBe/Q==} engines: {node: '>= 10'} cpu: [x64] os: [win32] + '@next/swc-win32-x64-msvc@15.1.4': + resolution: {integrity: sha512-xxsJy9wzq7FR5SqPCUqdgSXiNXrMuidgckBa8nH9HtjjxsilgcN6VgXF6tZ3uEWuVEadotQJI8/9EQ6guTC4Yw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -951,6 +1046,9 @@ packages: '@swc/helpers@0.5.13': resolution: {integrity: sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==} + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@tsconfig/node10@1.0.11': resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} @@ -990,9 +1088,17 @@ packages: '@types/prop-types@15.7.12': resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} + '@types/react-dom@19.0.3': + resolution: {integrity: sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==} + peerDependencies: + '@types/react': ^19.0.0 + '@types/react@18.3.3': resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} + '@types/react@19.0.7': + resolution: {integrity: sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==} + '@types/statuses@2.0.5': resolution: {integrity: sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==} @@ -1435,6 +1541,10 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + detect-libc@2.0.3: resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} @@ -2205,6 +2315,27 @@ packages: sass: optional: true + next@15.1.4: + resolution: {integrity: sha512-mTaq9dwaSuwwOrcu3ebjDYObekkxRnXpuVL21zotM8qE2W0HBOdVIdg2Li9QjMEZrj73LN96LcWcz62V19FjAg==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.41.2 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + nwsapi@2.2.10: resolution: {integrity: sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==} @@ -2393,6 +2524,11 @@ packages: peerDependencies: react: ^18.3.1 + react-dom@19.0.0: + resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} + peerDependencies: + react: ^19.0.0 + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -2403,6 +2539,10 @@ packages: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} + react@19.0.0: + resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} + engines: {node: '>=0.10.0'} + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -2514,6 +2654,9 @@ packages: scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + scheduler@0.25.0: + resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -2877,6 +3020,11 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + use-deep-compare@1.3.0: + resolution: {integrity: sha512-94iG+dEdEP/Sl3WWde+w9StIunlV8Dgj+vkt5wTwMoFQLaijiEZSXXy8KtcStpmEDtIptRJiNeD4ACTtVvnIKA==} + peerDependencies: + react: '>=16.8.0' + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -2979,6 +3127,9 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} + web-vitals@4.2.4: + resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} + webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} @@ -3109,6 +3260,11 @@ snapshots: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 + '@axiomhq/js@1.3.1': + dependencies: + fetch-retry: 6.0.0 + uuid: 11.0.2 + '@babel/helper-string-parser@7.24.7': {} '@babel/helper-validator-identifier@7.24.7': {} @@ -3432,6 +3588,8 @@ snapshots: '@next/env@15.0.2': {} + '@next/env@15.1.4': {} + '@next/eslint-plugin-next@15.1.6': dependencies: fast-glob: 3.3.1 @@ -3439,27 +3597,51 @@ snapshots: '@next/swc-darwin-arm64@15.0.2': optional: true + '@next/swc-darwin-arm64@15.1.4': + optional: true + '@next/swc-darwin-x64@15.0.2': optional: true + '@next/swc-darwin-x64@15.1.4': + optional: true + '@next/swc-linux-arm64-gnu@15.0.2': optional: true + '@next/swc-linux-arm64-gnu@15.1.4': + optional: true + '@next/swc-linux-arm64-musl@15.0.2': optional: true + '@next/swc-linux-arm64-musl@15.1.4': + optional: true + '@next/swc-linux-x64-gnu@15.0.2': optional: true + '@next/swc-linux-x64-gnu@15.1.4': + optional: true + '@next/swc-linux-x64-musl@15.0.2': optional: true + '@next/swc-linux-x64-musl@15.1.4': + optional: true + '@next/swc-win32-arm64-msvc@15.0.2': optional: true + '@next/swc-win32-arm64-msvc@15.1.4': + optional: true + '@next/swc-win32-x64-msvc@15.0.2': optional: true + '@next/swc-win32-x64-msvc@15.1.4': + optional: true + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -3611,6 +3793,10 @@ snapshots: dependencies: tslib: 2.6.3 + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + '@tsconfig/node10@1.0.11': {} '@tsconfig/node12@1.0.11': {} @@ -3641,11 +3827,19 @@ snapshots: '@types/prop-types@15.7.12': {} + '@types/react-dom@19.0.3(@types/react@19.0.7)': + dependencies: + '@types/react': 19.0.7 + '@types/react@18.3.3': dependencies: '@types/prop-types': 15.7.12 csstype: 3.1.3 + '@types/react@19.0.7': + dependencies: + csstype: 3.1.3 + '@types/statuses@2.0.5': {} '@types/tough-cookie@4.0.5': {} @@ -4226,6 +4420,8 @@ snapshots: delayed-stream@1.0.0: {} + dequal@2.0.3: {} + detect-libc@2.0.3: optional: true @@ -5204,6 +5400,31 @@ snapshots: - '@babel/core' - babel-plugin-macros + next@15.1.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + '@next/env': 15.1.4 + '@swc/counter': 0.1.3 + '@swc/helpers': 0.5.15 + busboy: 1.6.0 + caniuse-lite: 1.0.30001632 + postcss: 8.4.31 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + styled-jsx: 5.1.6(react@19.0.0) + optionalDependencies: + '@next/swc-darwin-arm64': 15.1.4 + '@next/swc-darwin-x64': 15.1.4 + '@next/swc-linux-arm64-gnu': 15.1.4 + '@next/swc-linux-arm64-musl': 15.1.4 + '@next/swc-linux-x64-gnu': 15.1.4 + '@next/swc-linux-x64-musl': 15.1.4 + '@next/swc-win32-arm64-msvc': 15.1.4 + '@next/swc-win32-x64-msvc': 15.1.4 + sharp: 0.33.5 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + nwsapi@2.2.10: {} object-assign@4.1.1: {} @@ -5404,6 +5625,11 @@ snapshots: react: 18.2.0 scheduler: 0.23.2 + react-dom@19.0.0(react@19.0.0): + dependencies: + react: 19.0.0 + scheduler: 0.25.0 + react-is@16.13.1: {} react-is@18.3.1: {} @@ -5412,6 +5638,8 @@ snapshots: dependencies: loose-envify: 1.4.0 + react@19.0.0: {} + readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -5585,6 +5813,8 @@ snapshots: dependencies: loose-envify: 1.4.0 + scheduler@0.25.0: {} + semver@6.3.1: {} semver@7.6.2: {} @@ -5799,6 +6029,11 @@ snapshots: client-only: 0.0.1 react: 18.2.0 + styled-jsx@5.1.6(react@19.0.0): + dependencies: + client-only: 0.0.1 + react: 19.0.0 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -6015,6 +6250,11 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 + use-deep-compare@1.3.0(react@19.0.0): + dependencies: + dequal: 2.0.3 + react: 19.0.0 + util-deprecate@1.0.2: {} uuid@11.0.2: {} @@ -6120,6 +6360,8 @@ snapshots: dependencies: xml-name-validator: 5.0.0 + web-vitals@4.2.4: {} + webidl-conversions@7.0.0: {} whatwg-encoding@3.1.1: From 449d29225d0c6f3bf65dda427cfffc149a7a16aa Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Thu, 16 Jan 2025 13:13:52 +0000 Subject: [PATCH 02/71] feat: Add proxy transport and proxy route handler --- packages/core/index.ts | 4 ++- packages/core/transports/index.ts | 9 ++--- packages/core/transports/proxy-transport.ts | 37 +++++++++++++++++++++ packages/nextjs/index.ts | 3 +- packages/nextjs/proxyRouteHandler.ts | 18 ++++++++++ 5 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 packages/core/transports/proxy-transport.ts create mode 100644 packages/nextjs/proxyRouteHandler.ts diff --git a/packages/core/index.ts b/packages/core/index.ts index 1f6190d3..b0c2d4c4 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -52,7 +52,6 @@ export type LoggerConfig = { }; export class Logger { - private logEvents: LogEvent[] = []; children: Logger[] = []; public logLevel: LogLevel = LogLevel.debug; public config: LoggerConfig; @@ -67,6 +66,9 @@ export class Logger { this.config = { ...initConfig }; } + raw(log: LogEvent) { + this.config.transports.forEach((transport) => transport.log([log])); + } debug = (message: string, args: { [key: string]: any } = {}) => { this.log(LogLevel.debug, message, args); }; diff --git a/packages/core/transports/index.ts b/packages/core/transports/index.ts index dcfe90c3..7e4fc0bc 100644 --- a/packages/core/transports/index.ts +++ b/packages/core/transports/index.ts @@ -1,10 +1,11 @@ -import { LogEvent } from ".."; +import { LogEvent } from '..'; export interface Transport { log: (logs: LogEvent[]) => Promise | void; flush: () => Promise | void; } -export * from "./console"; -export * from "./axiom-js"; -export * from "./axiom-fetch"; +export * from './console'; +export * from './axiom-js'; +export * from './axiom-fetch'; +export * from './proxy-transport'; diff --git a/packages/core/transports/proxy-transport.ts b/packages/core/transports/proxy-transport.ts new file mode 100644 index 00000000..7b85f3a9 --- /dev/null +++ b/packages/core/transports/proxy-transport.ts @@ -0,0 +1,37 @@ +import { Transport } from '.'; +import { LogEvent } from '..'; + +interface AxiomProxyConfig { + url: string; +} + +export class AxiomProxyTransport implements Transport { + private config: AxiomProxyConfig; + private events: LogEvent[] = []; + + constructor(config: AxiomProxyConfig) { + this.config = config; + } + + log: Transport['log'] = (logs) => { + this.events.push(...logs); + }; + + async flush() { + await fetch(this.config.url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(this.events), + }) + .then(async (res) => { + if (!res.ok) { + console.error(await res.text()); + throw new Error('Failed to flush logs'); + } + this.events = []; + }) + .catch(console.error); + } +} diff --git a/packages/nextjs/index.ts b/packages/nextjs/index.ts index f9ee3a31..702864e5 100644 --- a/packages/nextjs/index.ts +++ b/packages/nextjs/index.ts @@ -1 +1,2 @@ -export * from "./withAxiom"; +export * from './withAxiom'; +export * from './proxyRouteHandler'; diff --git a/packages/nextjs/proxyRouteHandler.ts b/packages/nextjs/proxyRouteHandler.ts new file mode 100644 index 00000000..b8b280f7 --- /dev/null +++ b/packages/nextjs/proxyRouteHandler.ts @@ -0,0 +1,18 @@ +import { LogEvent, Logger } from '@axiomhq/logger'; +import { NextRequest } from 'next/server'; + +export const createProxyRouteHandler = (logger: Logger) => { + return async (req: NextRequest) => { + try { + const events = (await req.json()) as LogEvent[]; + events.forEach((event) => { + logger.raw(event); + }); + await logger.flush(); + return new Response(JSON.stringify({ status: 'ok' })); + } catch (error) { + console.error(error); + return new Response(JSON.stringify({ status: 'error' }), { status: 500 }); + } + }; +}; From 98b829b9c34b3cfa637c2c1f461fc42100277622 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Thu, 16 Jan 2025 13:14:13 +0000 Subject: [PATCH 03/71] docs: add nextjs example --- examples/nextjs/.gitignore | 41 + examples/nextjs/README.md | 36 + examples/nextjs/eslint.config.mjs | 16 + examples/nextjs/next.config.ts | 7 + examples/nextjs/package.json | 30 + examples/nextjs/postcss.config.mjs | 8 + examples/nextjs/src/app/[[...path]]/page.tsx | 23 + .../nextjs/src/app/_components/button.tsx | 14 + examples/nextjs/src/app/api/axiom/route.ts | 4 + examples/nextjs/src/app/api/route.ts | 31 + examples/nextjs/src/app/favicon.ico | Bin 0 -> 25931 bytes examples/nextjs/src/app/globals.css | 21 + examples/nextjs/src/app/layout.tsx | 34 + examples/nextjs/src/lib/axiom/client.ts | 9 + examples/nextjs/src/lib/axiom/server.ts | 12 + examples/nextjs/tailwind.config.ts | 18 + examples/nextjs/tsconfig.json | 27 + pnpm-lock.yaml | 877 ++++++++++++++++++ 18 files changed, 1208 insertions(+) create mode 100644 examples/nextjs/.gitignore create mode 100644 examples/nextjs/README.md create mode 100644 examples/nextjs/eslint.config.mjs create mode 100644 examples/nextjs/next.config.ts create mode 100644 examples/nextjs/package.json create mode 100644 examples/nextjs/postcss.config.mjs create mode 100644 examples/nextjs/src/app/[[...path]]/page.tsx create mode 100644 examples/nextjs/src/app/_components/button.tsx create mode 100644 examples/nextjs/src/app/api/axiom/route.ts create mode 100644 examples/nextjs/src/app/api/route.ts create mode 100644 examples/nextjs/src/app/favicon.ico create mode 100644 examples/nextjs/src/app/globals.css create mode 100644 examples/nextjs/src/app/layout.tsx create mode 100644 examples/nextjs/src/lib/axiom/client.ts create mode 100644 examples/nextjs/src/lib/axiom/server.ts create mode 100644 examples/nextjs/tailwind.config.ts create mode 100644 examples/nextjs/tsconfig.json diff --git a/examples/nextjs/.gitignore b/examples/nextjs/.gitignore new file mode 100644 index 00000000..5ef6a520 --- /dev/null +++ b/examples/nextjs/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/examples/nextjs/README.md b/examples/nextjs/README.md new file mode 100644 index 00000000..e215bc4c --- /dev/null +++ b/examples/nextjs/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/examples/nextjs/eslint.config.mjs b/examples/nextjs/eslint.config.mjs new file mode 100644 index 00000000..c85fb67c --- /dev/null +++ b/examples/nextjs/eslint.config.mjs @@ -0,0 +1,16 @@ +import { dirname } from "path"; +import { fileURLToPath } from "url"; +import { FlatCompat } from "@eslint/eslintrc"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const compat = new FlatCompat({ + baseDirectory: __dirname, +}); + +const eslintConfig = [ + ...compat.extends("next/core-web-vitals", "next/typescript"), +]; + +export default eslintConfig; diff --git a/examples/nextjs/next.config.ts b/examples/nextjs/next.config.ts new file mode 100644 index 00000000..e9ffa308 --- /dev/null +++ b/examples/nextjs/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json new file mode 100644 index 00000000..0d3e7933 --- /dev/null +++ b/examples/nextjs/package.json @@ -0,0 +1,30 @@ +{ + "name": "nextjs", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev --turbopack", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@axiomhq/logger": "workspace:*", + "@axiomhq/nextjs": "workspace:^", + "@axiomhq/react": "workspace:*", + "next": "15.1.4", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@eslint/eslintrc": "^3", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "15.1.4", + "postcss": "^8", + "tailwindcss": "^3.4.1", + "typescript": "^5" + } +} diff --git a/examples/nextjs/postcss.config.mjs b/examples/nextjs/postcss.config.mjs new file mode 100644 index 00000000..1a69fd2a --- /dev/null +++ b/examples/nextjs/postcss.config.mjs @@ -0,0 +1,8 @@ +/** @type {import('postcss-load-config').Config} */ +const config = { + plugins: { + tailwindcss: {}, + }, +}; + +export default config; diff --git a/examples/nextjs/src/app/[[...path]]/page.tsx b/examples/nextjs/src/app/[[...path]]/page.tsx new file mode 100644 index 00000000..2775fa58 --- /dev/null +++ b/examples/nextjs/src/app/[[...path]]/page.tsx @@ -0,0 +1,23 @@ +import Button from '@/app/_components/button'; +import { logger } from '@/lib/axiom/server'; +import Link from 'next/link'; +import { after } from 'next/server'; + +export default function Home() { + logger.info('Hello World!', { key: 'value' }); + + after(() => { + logger.flush(); + }); + + return ( +
+
+ ); +} diff --git a/examples/nextjs/src/app/_components/button.tsx b/examples/nextjs/src/app/_components/button.tsx new file mode 100644 index 00000000..0add096e --- /dev/null +++ b/examples/nextjs/src/app/_components/button.tsx @@ -0,0 +1,14 @@ +'use client'; + +import { useLogger } from '@/lib/axiom/client'; + +export default function Button() { + const logger = useLogger(); + return ( +
+ +
+ ); +} diff --git a/examples/nextjs/src/app/api/axiom/route.ts b/examples/nextjs/src/app/api/axiom/route.ts new file mode 100644 index 00000000..b6c68d78 --- /dev/null +++ b/examples/nextjs/src/app/api/axiom/route.ts @@ -0,0 +1,4 @@ +import { logger } from '@/lib/axiom/server'; +import { createProxyRouteHandler } from '@axiomhq/nextjs'; + +export const POST = createProxyRouteHandler(logger); diff --git a/examples/nextjs/src/app/api/route.ts b/examples/nextjs/src/app/api/route.ts new file mode 100644 index 00000000..c2f7981d --- /dev/null +++ b/examples/nextjs/src/app/api/route.ts @@ -0,0 +1,31 @@ +import { logger } from '@/lib/axiom/server'; +import { + createAxiomRouteHandler, + logErrorByStatusCode, + transformErrorResult, + transformSuccessResult, +} from '@axiomhq/nextjs'; + +const axiomRouteHandler = createAxiomRouteHandler(logger); + +export const GET = axiomRouteHandler(async () => { + return new Response('Hello World!'); +}); + +export const POST = axiomRouteHandler( + async (req) => { + return new Response(JSON.stringify(req.body)); + }, + async (result) => { + if (result.ok) { + logger.info(...transformSuccessResult(result.data)); + logger.info('searchParams', result.data.req.nextUrl.searchParams); + } else { + if (result.data.error instanceof Error) { + logger.error(result.data.error.message, result.data.error); + const [message, report] = transformErrorResult(result.data); + logger[logErrorByStatusCode(report.statusCode)](message, report); + } + } + }, +); diff --git a/examples/nextjs/src/app/favicon.ico b/examples/nextjs/src/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 zcmeHv30#a{`}aL_*G&7qml|y<+KVaDM2m#dVr!KsA!#An?kSQM(q<_dDNCpjEux83 zLb9Z^XxbDl(w>%i@8hT6>)&Gu{h#Oeyszu?xtw#Zb1mO{pgX9699l+Qppw7jXaYf~-84xW z)w4x8?=youko|}Vr~(D$UXIbiXABHh`p1?nn8Po~fxRJv}|0e(BPs|G`(TT%kKVJAdg5*Z|x0leQq0 zkdUBvb#>9F()jo|T~kx@OM8$9wzs~t2l;K=woNssA3l6|sx2r3+kdfVW@e^8e*E}v zA1y5{bRi+3Z`uD3{F7LgFJDdvm;nJilkzDku>BwXH(8ItVCXk*-lSJnR?-2UN%hJ){&rlvg`CDTj z)Bzo!3v7Ou#83zEDEFcKt(f1E0~=rqeEbTnMvWR#{+9pg%7G8y>u1OVRUSoox-ovF z2Ydma(;=YuBY(eI|04{hXzZD6_f(v~H;C~y5=DhAC{MMS>2fm~1H_t2$56pc$NH8( z5bH|<)71dV-_oCHIrzrT`2s-5w_+2CM0$95I6X8p^r!gHp+j_gd;9O<1~CEQQGS8) zS9Qh3#p&JM-G8rHekNmKVewU;pJRcTAog68KYo^dRo}(M>36U4Us zfgYWSiHZL3;lpWT=zNAW>Dh#mB!_@Lg%$ms8N-;aPqMn+C2HqZgz&9~Eu z4|Kp<`$q)Uw1R?y(~S>ePdonHxpV1#eSP1B;Ogo+-Pk}6#0GsZZ5!||ev2MGdh}_m z{DeR7?0-1^zVs&`AV6Vt;r3`I`OI_wgs*w=eO%_#7Kepl{B@xiyCANc(l zzIyd4y|c6PXWq9-|KM8(zIk8LPk(>a)zyFWjhT!$HJ$qX1vo@d25W<fvZQ2zUz5WRc(UnFMKHwe1| zWmlB1qdbiA(C0jmnV<}GfbKtmcu^2*P^O?MBLZKt|As~ge8&AAO~2K@zbXelK|4T<{|y4`raF{=72kC2Kn(L4YyenWgrPiv z@^mr$t{#X5VuIMeL!7Ab6_kG$&#&5p*Z{+?5U|TZ`B!7llpVmp@skYz&n^8QfPJzL z0G6K_OJM9x+Wu2gfN45phANGt{7=C>i34CV{Xqlx(fWpeAoj^N0Biu`w+MVcCUyU* zDZuzO0>4Z6fbu^T_arWW5n!E45vX8N=bxTVeFoep_G#VmNlQzAI_KTIc{6>c+04vr zx@W}zE5JNSU>!THJ{J=cqjz+4{L4A{Ob9$ZJ*S1?Ggg3klFp!+Y1@K+pK1DqI|_gq z5ZDXVpge8-cs!o|;K73#YXZ3AShj50wBvuq3NTOZ`M&qtjj#GOFfgExjg8Gn8>Vq5 z`85n+9|!iLCZF5$HJ$Iu($dm?8~-ofu}tEc+-pyke=3!im#6pk_Wo8IA|fJwD&~~F zc16osQ)EBo58U7XDuMexaPRjU@h8tXe%S{fA0NH3vGJFhuyyO!Uyl2^&EOpX{9As0 zWj+P>{@}jxH)8|r;2HdupP!vie{sJ28b&bo!8`D^x}TE$%zXNb^X1p@0PJ86`dZyj z%ce7*{^oo+6%&~I!8hQy-vQ7E)0t0ybH4l%KltWOo~8cO`T=157JqL(oq_rC%ea&4 z2NcTJe-HgFjNg-gZ$6!Y`SMHrlj}Etf7?r!zQTPPSv}{so2e>Fjs1{gzk~LGeesX%r(Lh6rbhSo_n)@@G-FTQy93;l#E)hgP@d_SGvyCp0~o(Y;Ee8{ zdVUDbHm5`2taPUOY^MAGOw*>=s7=Gst=D+p+2yON!0%Hk` zz5mAhyT4lS*T3LS^WSxUy86q&GnoHxzQ6vm8)VS}_zuqG?+3td68_x;etQAdu@sc6 zQJ&5|4(I?~3d-QOAODHpZ=hlSg(lBZ!JZWCtHHSj`0Wh93-Uk)_S%zsJ~aD>{`A0~ z9{AG(e|q3g5B%wYKRxiL2Y$8(4w6bzchKuloQW#e&S3n+P- z8!ds-%f;TJ1>)v)##>gd{PdS2Oc3VaR`fr=`O8QIO(6(N!A?pr5C#6fc~Ge@N%Vvu zaoAX2&(a6eWy_q&UwOhU)|P3J0Qc%OdhzW=F4D|pt0E4osw;%<%Dn58hAWD^XnZD= z>9~H(3bmLtxpF?a7su6J7M*x1By7YSUbxGi)Ot0P77`}P3{)&5Un{KD?`-e?r21!4vTTnN(4Y6Lin?UkSM z`MXCTC1@4A4~mvz%Rh2&EwY))LeoT=*`tMoqcEXI>TZU9WTP#l?uFv+@Dn~b(>xh2 z;>B?;Tz2SR&KVb>vGiBSB`@U7VIWFSo=LDSb9F{GF^DbmWAfpms8Sx9OX4CnBJca3 zlj9(x!dIjN?OG1X4l*imJNvRCk}F%!?SOfiOq5y^mZW)jFL@a|r-@d#f7 z2gmU8L3IZq0ynIws=}~m^#@&C%J6QFo~Mo4V`>v7MI-_!EBMMtb%_M&kvAaN)@ZVw z+`toz&WG#HkWDjnZE!6nk{e-oFdL^$YnbOCN}JC&{$#$O27@|Tn-skXr)2ml2~O!5 zX+gYoxhoc7qoU?C^3~&!U?kRFtnSEecWuH0B0OvLodgUAi}8p1 zrO6RSXHH}DMc$&|?D004DiOVMHV8kXCP@7NKB zgaZq^^O<7PoKEp72kby@W0Z!Y*Ay{&vfg#C&gG@YVR9g?FEocMUi1gSN$+V+ayF45{a zuDZDTN}mS|;BO%gEf}pjBfN2-gIrU#G5~cucA;dokXW89%>AyXJJI z9X4UlIWA|ZYHgbI z5?oFk@A=Ik7lrEQPDH!H+b`7_Y~aDb_qa=B2^Y&Ow41cU=4WDd40dp5(QS-WMN-=Y z9g;6_-JdNU;|6cPwf$ak*aJIcwL@1n$#l~zi{c{EW?T;DaW*E8DYq?Umtz{nJ&w-M zEMyTDrC&9K$d|kZe2#ws6)L=7K+{ zQw{XnV6UC$6-rW0emqm8wJoeZK)wJIcV?dST}Z;G0Arq{dVDu0&4kd%N!3F1*;*pW zR&qUiFzK=@44#QGw7k1`3t_d8&*kBV->O##t|tonFc2YWrL7_eqg+=+k;!F-`^b8> z#KWCE8%u4k@EprxqiV$VmmtiWxDLgnGu$Vs<8rppV5EajBXL4nyyZM$SWVm!wnCj-B!Wjqj5-5dNXukI2$$|Bu3Lrw}z65Lc=1G z^-#WuQOj$hwNGG?*CM_TO8Bg-1+qc>J7k5c51U8g?ZU5n?HYor;~JIjoWH-G>AoUP ztrWWLbRNqIjW#RT*WqZgPJXU7C)VaW5}MiijYbABmzoru6EmQ*N8cVK7a3|aOB#O& zBl8JY2WKfmj;h#Q!pN%9o@VNLv{OUL?rixHwOZuvX7{IJ{(EdPpuVFoQqIOa7giLVkBOKL@^smUA!tZ1CKRK}#SSM)iQHk)*R~?M!qkCruaS!#oIL1c z?J;U~&FfH#*98^G?i}pA{ z9Jg36t4=%6mhY(quYq*vSxptes9qy|7xSlH?G=S@>u>Ebe;|LVhs~@+06N<4CViBk zUiY$thvX;>Tby6z9Y1edAMQaiH zm^r3v#$Q#2T=X>bsY#D%s!bhs^M9PMAcHbCc0FMHV{u-dwlL;a1eJ63v5U*?Q_8JO zT#50!RD619#j_Uf))0ooADz~*9&lN!bBDRUgE>Vud-i5ck%vT=r^yD*^?Mp@Q^v+V zG#-?gKlr}Eeqifb{|So?HM&g91P8|av8hQoCmQXkd?7wIJwb z_^v8bbg`SAn{I*4bH$u(RZ6*xUhuA~hc=8czK8SHEKTzSxgbwi~9(OqJB&gwb^l4+m`k*Q;_?>Y-APi1{k zAHQ)P)G)f|AyjSgcCFps)Fh6Bca*Xznq36!pV6Az&m{O8$wGFD? zY&O*3*J0;_EqM#jh6^gMQKpXV?#1?>$ml1xvh8nSN>-?H=V;nJIwB07YX$e6vLxH( zqYwQ>qxwR(i4f)DLd)-$P>T-no_c!LsN@)8`e;W@)-Hj0>nJ-}Kla4-ZdPJzI&Mce zv)V_j;(3ERN3_@I$N<^|4Lf`B;8n+bX@bHbcZTopEmDI*Jfl)-pFDvo6svPRoo@(x z);_{lY<;);XzT`dBFpRmGrr}z5u1=pC^S-{ce6iXQlLGcItwJ^mZx{m$&DA_oEZ)B{_bYPq-HA zcH8WGoBG(aBU_j)vEy+_71T34@4dmSg!|M8Vf92Zj6WH7Q7t#OHQqWgFE3ARt+%!T z?oLovLVlnf?2c7pTc)~cc^($_8nyKwsN`RA-23ed3sdj(ys%pjjM+9JrctL;dy8a( z@en&CQmnV(()bu|Y%G1-4a(6x{aLytn$T-;(&{QIJB9vMox11U-1HpD@d(QkaJdEb zG{)+6Dos_L+O3NpWo^=gR?evp|CqEG?L&Ut#D*KLaRFOgOEK(Kq1@!EGcTfo+%A&I z=dLbB+d$u{sh?u)xP{PF8L%;YPPW53+@{>5W=Jt#wQpN;0_HYdw1{ksf_XhO4#2F= zyPx6Lx2<92L-;L5PD`zn6zwIH`Jk($?Qw({erA$^bC;q33hv!d!>%wRhj# zal^hk+WGNg;rJtb-EB(?czvOM=H7dl=vblBwAv>}%1@{}mnpUznfq1cE^sgsL0*4I zJ##!*B?=vI_OEVis5o+_IwMIRrpQyT_Sq~ZU%oY7c5JMIADzpD!Upz9h@iWg_>>~j zOLS;wp^i$-E?4<_cp?RiS%Rd?i;f*mOz=~(&3lo<=@(nR!_Rqiprh@weZlL!t#NCc zO!QTcInq|%#>OVgobj{~ixEUec`E25zJ~*DofsQdzIa@5^nOXj2T;8O`l--(QyU^$t?TGY^7#&FQ+2SS3B#qK*k3`ye?8jUYSajE5iBbJls75CCc(m3dk{t?- zopcER9{Z?TC)mk~gpi^kbbu>b-+a{m#8-y2^p$ka4n60w;Sc2}HMf<8JUvhCL0B&Btk)T`ctE$*qNW8L$`7!r^9T+>=<=2qaq-;ll2{`{Rg zc5a0ZUI$oG&j-qVOuKa=*v4aY#IsoM+1|c4Z)<}lEDvy;5huB@1RJPquU2U*U-;gu z=En2m+qjBzR#DEJDO`WU)hdd{Vj%^0V*KoyZ|5lzV87&g_j~NCjwv0uQVqXOb*QrQ zy|Qn`hxx(58c70$E;L(X0uZZ72M1!6oeg)(cdKO ze0gDaTz+ohR-#d)NbAH4x{I(21yjwvBQfmpLu$)|m{XolbgF!pmsqJ#D}(ylp6uC> z{bqtcI#hT#HW=wl7>p!38sKsJ`r8}lt-q%Keqy%u(xk=yiIJiUw6|5IvkS+#?JTBl z8H5(Q?l#wzazujH!8o>1xtn8#_w+397*_cy8!pQGP%K(Ga3pAjsaTbbXJlQF_+m+-UpUUent@xM zg%jqLUExj~o^vQ3Gl*>wh=_gOr2*|U64_iXb+-111aH}$TjeajM+I20xw(((>fej-@CIz4S1pi$(#}P7`4({6QS2CaQS4NPENDp>sAqD z$bH4KGzXGffkJ7R>V>)>tC)uax{UsN*dbeNC*v}#8Y#OWYwL4t$ePR?VTyIs!wea+ z5Urmc)X|^`MG~*dS6pGSbU+gPJoq*^a=_>$n4|P^w$sMBBy@f*Z^Jg6?n5?oId6f{ z$LW4M|4m502z0t7g<#Bx%X;9<=)smFolV&(V^(7Cv2-sxbxopQ!)*#ZRhTBpx1)Fc zNm1T%bONzv6@#|dz(w02AH8OXe>kQ#1FMCzO}2J_mST)+ExmBr9cva-@?;wnmWMOk z{3_~EX_xadgJGv&H@zK_8{(x84`}+c?oSBX*Ge3VdfTt&F}yCpFP?CpW+BE^cWY0^ zb&uBN!Ja3UzYHK-CTyA5=L zEMW{l3Usky#ly=7px648W31UNV@K)&Ub&zP1c7%)`{);I4b0Q<)B}3;NMG2JH=X$U zfIW4)4n9ZM`-yRj67I)YSLDK)qfUJ_ij}a#aZN~9EXrh8eZY2&=uY%2N0UFF7<~%M zsB8=erOWZ>Ct_#^tHZ|*q`H;A)5;ycw*IcmVxi8_0Xk}aJA^ath+E;xg!x+As(M#0=)3!NJR6H&9+zd#iP(m0PIW8$ z1Y^VX`>jm`W!=WpF*{ioM?C9`yOR>@0q=u7o>BP-eSHqCgMDj!2anwH?s%i2p+Q7D zzszIf5XJpE)IG4;d_(La-xenmF(tgAxK`Y4sQ}BSJEPs6N_U2vI{8=0C_F?@7<(G; zo$~G=8p+076G;`}>{MQ>t>7cm=zGtfbdDXm6||jUU|?X?CaE?(<6bKDYKeHlz}DA8 zXT={X=yp_R;HfJ9h%?eWvQ!dRgz&Su*JfNt!Wu>|XfU&68iRikRrHRW|ZxzRR^`eIGt zIeiDgVS>IeExKVRWW8-=A=yA`}`)ZkWBrZD`hpWIxBGkh&f#ijr449~m`j6{4jiJ*C!oVA8ZC?$1RM#K(_b zL9TW)kN*Y4%^-qPpMP7d4)o?Nk#>aoYHT(*g)qmRUb?**F@pnNiy6Fv9rEiUqD(^O zzyS?nBrX63BTRYduaG(0VVG2yJRe%o&rVrLjbxTaAFTd8s;<<@Qs>u(<193R8>}2_ zuwp{7;H2a*X7_jryzriZXMg?bTuegABb^87@SsKkr2)0Gyiax8KQWstw^v#ix45EVrcEhr>!NMhprl$InQMzjSFH54x5k9qHc`@9uKQzvL4ihcq{^B zPrVR=o_ic%Y>6&rMN)hTZsI7I<3&`#(nl+3y3ys9A~&^=4?PL&nd8)`OfG#n zwAMN$1&>K++c{^|7<4P=2y(B{jJsQ0a#U;HTo4ZmWZYvI{+s;Td{Yzem%0*k#)vjpB zia;J&>}ICate44SFYY3vEelqStQWFihx%^vQ@Do(sOy7yR2@WNv7Y9I^yL=nZr3mb zXKV5t@=?-Sk|b{XMhA7ZGB@2hqsx}4xwCW!in#C zI@}scZlr3-NFJ@NFaJlhyfcw{k^vvtGl`N9xSo**rDW4S}i zM9{fMPWo%4wYDG~BZ18BD+}h|GQKc-g^{++3MY>}W_uq7jGHx{mwE9fZiPCoxN$+7 zrODGGJrOkcPQUB(FD5aoS4g~7#6NR^ma7-!>mHuJfY5kTe6PpNNKC9GGRiu^L31uG z$7v`*JknQHsYB!Tm_W{a32TM099djW%5e+j0Ve_ct}IM>XLF1Ap+YvcrLV=|CKo6S zb+9Nl3_YdKP6%Cxy@6TxZ>;4&nTneadr z_ES90ydCev)LV!dN=#(*f}|ZORFdvkYBni^aLbUk>BajeWIOcmHP#8S)*2U~QKI%S zyrLmtPqb&TphJ;>yAxri#;{uyk`JJqODDw%(Z=2`1uc}br^V%>j!gS)D*q*f_-qf8&D;W1dJgQMlaH5er zN2U<%Smb7==vE}dDI8K7cKz!vs^73o9f>2sgiTzWcwY|BMYHH5%Vn7#kiw&eItCqa zIkR2~Q}>X=Ar8W|^Ms41Fm8o6IB2_j60eOeBB1Br!boW7JnoeX6Gs)?7rW0^5psc- zjS16yb>dFn>KPOF;imD}e!enuIniFzv}n$m2#gCCv4jM#ArwlzZ$7@9&XkFxZ4n!V zj3dyiwW4Ki2QG{@i>yuZXQizw_OkZI^-3otXC{!(lUpJF33gI60ak;Uqitp74|B6I zgg{b=Iz}WkhCGj1M=hu4#Aw173YxIVbISaoc z-nLZC*6Tgivd5V`K%GxhBsp@SUU60-rfc$=wb>zdJzXS&-5(NRRodFk;Kxk!S(O(a0e7oY=E( zAyS;Ow?6Q&XA+cnkCb{28_1N8H#?J!*$MmIwLq^*T_9-z^&UE@A(z9oGYtFy6EZef LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/examples/nextjs/src/app/globals.css b/examples/nextjs/src/app/globals.css new file mode 100644 index 00000000..6b717ad3 --- /dev/null +++ b/examples/nextjs/src/app/globals.css @@ -0,0 +1,21 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --background: #ffffff; + --foreground: #171717; +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0a0a0a; + --foreground: #ededed; + } +} + +body { + color: var(--foreground); + background: var(--background); + font-family: Arial, Helvetica, sans-serif; +} diff --git a/examples/nextjs/src/app/layout.tsx b/examples/nextjs/src/app/layout.tsx new file mode 100644 index 00000000..f7fa87eb --- /dev/null +++ b/examples/nextjs/src/app/layout.tsx @@ -0,0 +1,34 @@ +import type { Metadata } from "next"; +import { Geist, Geist_Mono } from "next/font/google"; +import "./globals.css"; + +const geistSans = Geist({ + variable: "--font-geist-sans", + subsets: ["latin"], +}); + +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], +}); + +export const metadata: Metadata = { + title: "Create Next App", + description: "Generated by create next app", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + {children} + + + ); +} diff --git a/examples/nextjs/src/lib/axiom/client.ts b/examples/nextjs/src/lib/axiom/client.ts new file mode 100644 index 00000000..643471e2 --- /dev/null +++ b/examples/nextjs/src/lib/axiom/client.ts @@ -0,0 +1,9 @@ +import { Logger } from '@axiomhq/logger'; +import { AxiomProxyTransport, ConsoleTransport } from '@axiomhq/logger/transports'; +import { createClientSideHelpers } from '@axiomhq/react'; + +export const logger = new Logger({ + transports: [new AxiomProxyTransport({ url: process.env.NEXT_PUBLIC_AXIOM_PROXY_URL! }), new ConsoleTransport()], +}); + +export const { useLogger } = createClientSideHelpers(logger); diff --git a/examples/nextjs/src/lib/axiom/server.ts b/examples/nextjs/src/lib/axiom/server.ts new file mode 100644 index 00000000..03b68e9d --- /dev/null +++ b/examples/nextjs/src/lib/axiom/server.ts @@ -0,0 +1,12 @@ +import { Logger } from '@axiomhq/logger'; +import { AxiomFetchTransport, ConsoleTransport } from '@axiomhq/logging/transports'; + +export const logger = new Logger({ + transports: [ + new AxiomFetchTransport({ + dataset: process.env.AXIOM_DATASET!, + token: process.env.AXIOM_TOKEN!, + }), + new ConsoleTransport(), + ], +}); diff --git a/examples/nextjs/tailwind.config.ts b/examples/nextjs/tailwind.config.ts new file mode 100644 index 00000000..109807be --- /dev/null +++ b/examples/nextjs/tailwind.config.ts @@ -0,0 +1,18 @@ +import type { Config } from "tailwindcss"; + +export default { + content: [ + "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", + "./src/components/**/*.{js,ts,jsx,tsx,mdx}", + "./src/app/**/*.{js,ts,jsx,tsx,mdx}", + ], + theme: { + extend: { + colors: { + background: "var(--background)", + foreground: "var(--foreground)", + }, + }, + }, + plugins: [], +} satisfies Config; diff --git a/examples/nextjs/tsconfig.json b/examples/nextjs/tsconfig.json new file mode 100644 index 00000000..c1334095 --- /dev/null +++ b/examples/nextjs/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cca278b8..705285a4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -101,6 +101,55 @@ importers: specifier: workspace:^ version: link:../../internal/eslint-config + examples/nextjs: + dependencies: + '@axiomhq/logger': + specifier: workspace:* + version: link:../../packages/core + '@axiomhq/nextjs': + specifier: workspace:^ + version: link:../../packages/nextjs + '@axiomhq/react': + specifier: workspace:* + version: link:../../packages/react + next: + specifier: 15.1.4 + version: 15.1.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: + specifier: ^19.0.0 + version: 19.0.0 + react-dom: + specifier: ^19.0.0 + version: 19.0.0(react@19.0.0) + devDependencies: + '@eslint/eslintrc': + specifier: ^3 + version: 3.2.0 + '@types/node': + specifier: ^20 + version: 20.14.2 + '@types/react': + specifier: ^19 + version: 19.0.7 + '@types/react-dom': + specifier: ^19 + version: 19.0.3(@types/react@19.0.7) + eslint: + specifier: ^9 + version: 9.18.0(jiti@2.4.2) + eslint-config-next: + specifier: 15.1.4 + version: 15.1.4(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5) + postcss: + specifier: ^8 + version: 8.4.38 + tailwindcss: + specifier: ^3.4.1 + version: 3.4.17(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5)) + typescript: + specifier: ^5 + version: 5.4.5 + examples/node: dependencies: '@axiomhq/js': @@ -301,6 +350,10 @@ importers: packages: + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} @@ -678,6 +731,10 @@ packages: peerDependencies: '@types/node': '>=18' + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + '@istanbuljs/schema@0.1.3': resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} @@ -723,6 +780,9 @@ packages: '@next/env@15.1.4': resolution: {integrity: sha512-2fZ5YZjedi5AGaeoaC0B20zGntEHRhi2SdWcu61i48BllODcAmmtj8n7YarSPt4DaTsJaBFdxQAVEVzgmx2Zpw==} + '@next/eslint-plugin-next@15.1.4': + resolution: {integrity: sha512-HwlEXwCK3sr6zmVGEvWBjW9tBFs1Oe6hTmTLoFQtpm4As5HCdu8jfSE0XJOp7uhfEGLniIx8yrGxEWwNnY0fmQ==} + '@next/eslint-plugin-next@15.1.6': resolution: {integrity: sha512-+slMxhTgILUntZDGNgsKEYHUvpn72WP1YTlkmEhS51vnVd7S9jEEy0n9YAMcI21vUG4akTw9voWH02lrClt/yw==} @@ -834,6 +894,10 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@nolyfill/is-core-module@1.0.39': + resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} + engines: {node: '>=12.4.0'} + '@open-draft/deferred-promise@2.2.0': resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} @@ -843,6 +907,10 @@ packages: '@open-draft/until@2.1.0': resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + '@polka/url@1.0.0-next.25': resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==} @@ -1037,6 +1105,12 @@ packages: cpu: [x64] os: [win32] + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@rushstack/eslint-patch@1.10.5': + resolution: {integrity: sha512-kkKUDVlII2DQiKy7UstOR1ErJP8kUKAQ4oa+SQtM0K+lPdmmjj0YnnxBgtTVYH7mUKtbsxeFC9y0AmK7Yb78/A==} + '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} @@ -1082,6 +1156,9 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/node@20.14.2': resolution: {integrity: sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==} @@ -1280,6 +1357,10 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -1288,12 +1369,30 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + array-buffer-byte-length@1.0.1: resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} engines: {node: '>= 0.4'} @@ -1310,6 +1409,10 @@ packages: resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} engines: {node: '>= 0.4'} + array.prototype.findlastindex@1.2.5: + resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} + engines: {node: '>= 0.4'} + array.prototype.flat@1.3.2: resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} engines: {node: '>= 0.4'} @@ -1333,6 +1436,9 @@ packages: assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + ast-types-flow@0.0.8: + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + async@3.2.5: resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} @@ -1347,12 +1453,24 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + axe-core@4.10.2: + resolution: {integrity: sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==} + engines: {node: '>=4'} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -1394,6 +1512,10 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + caniuse-lite@1.0.30001632: resolution: {integrity: sha512-udx3o7yHJfUxMLkGohMlVHCvFvWmirKh9JAH/d7WOLPetlH+LTL5cocMZ0t7oZx/mdlOWXti97xLZWc8uURRHg==} @@ -1408,6 +1530,10 @@ packages: check-error@1.0.3: resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} @@ -1449,6 +1575,10 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + computeds@0.0.1: resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==} @@ -1472,6 +1602,11 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + cssstyle@4.0.1: resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==} engines: {node: '>=18'} @@ -1479,6 +1614,9 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + damerau-levenshtein@1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + data-urls@5.0.0: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} @@ -1510,6 +1648,14 @@ packages: de-indent@1.0.2: resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@4.3.5: resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} engines: {node: '>=6.0'} @@ -1519,6 +1665,15 @@ packages: supports-color: optional: true + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} @@ -1549,6 +1704,9 @@ packages: resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1557,6 +1715,9 @@ packages: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -1569,12 +1730,22 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + enabled@2.0.0: resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + enhanced-resolve@5.18.0: + resolution: {integrity: sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==} + engines: {node: '>=10.13.0'} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -1639,12 +1810,74 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + eslint-config-next@15.1.4: + resolution: {integrity: sha512-u9+7lFmfhKNgGjhQ9tBeyCFsPJyq0SvGioMJBngPC7HXUpR0U+ckEwQR48s7TrRNHra1REm6evGL2ie38agALg==} + peerDependencies: + eslint: ^7.23.0 || ^8.0.0 || ^9.0.0 + typescript: '>=3.3.1' + peerDependenciesMeta: + typescript: + optional: true + eslint-config-prettier@9.1.0: resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} hasBin: true peerDependencies: eslint: '>=7.0.0' + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-import-resolver-typescript@3.7.0: + resolution: {integrity: sha512-Vrwyi8HHxY97K5ebydMtffsWAn1SCR9eol49eCd5fJS4O1WV7PaAjbcjmbfJJSMz/t4Mal212Uz/fQZrOB8mow==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + + eslint-module-utils@2.12.0: + resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.31.0: + resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsx-a11y@6.10.2: + resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + eslint-plugin-react-hooks@5.1.0: resolution: {integrity: sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==} engines: {node: '>=10'} @@ -1776,6 +2009,10 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + foreground-child@3.3.0: + resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + engines: {node: '>=14'} + form-data@4.0.0: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} engines: {node: '>= 6'} @@ -1829,6 +2066,9 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} + get-tsconfig@4.10.0: + resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -1837,6 +2077,10 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported @@ -1860,6 +2104,9 @@ packages: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} @@ -1979,6 +2226,10 @@ packages: resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} engines: {node: '>= 0.4'} + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + is-boolean-object@1.1.2: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} @@ -1987,6 +2238,9 @@ packages: resolution: {integrity: sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==} engines: {node: '>= 0.4'} + is-bun-module@1.3.0: + resolution: {integrity: sha512-DgXeu5UWI0IsMQundYb5UAOzm6G2eVnarJ0byP6Tm55iZNKceD59LNPA2L4VvsScTtHcw0yEkVwSf7PC+QoLSA==} + is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} @@ -1994,6 +2248,10 @@ packages: is-core-module@2.13.1: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + is-data-view@1.0.1: resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} engines: {node: '>= 0.4'} @@ -2148,6 +2406,13 @@ packages: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} + hasBin: true + jiti@2.4.2: resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true @@ -2183,6 +2448,10 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -2193,10 +2462,24 @@ packages: kuler@2.0.0: resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + language-subtag-registry@0.3.23: + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} + + language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + local-pkg@0.4.3: resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} engines: {node: '>=14'} @@ -2219,6 +2502,9 @@ packages: loupe@2.3.7: resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + magic-string@0.30.10: resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} @@ -2241,6 +2527,10 @@ packages: resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} engines: {node: '>=8.6'} + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -2256,6 +2546,13 @@ packages: resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} engines: {node: '>=16 || 14 >=14.17'} + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + mlly@1.7.1: resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} @@ -2286,11 +2583,19 @@ packages: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} engines: {node: ^18.17.0 || >=20.5.0} + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -2336,6 +2641,10 @@ packages: sass: optional: true + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + nwsapi@2.2.10: resolution: {integrity: sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==} @@ -2343,6 +2652,10 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + object-inspect@1.13.1: resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} @@ -2370,6 +2683,10 @@ packages: resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} engines: {node: '>= 0.4'} + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + object.values@1.2.0: resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} engines: {node: '>= 0.4'} @@ -2411,6 +2728,9 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -2436,6 +2756,10 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} @@ -2448,10 +2772,17 @@ packages: picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + pino-abstract-transport@1.2.0: resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} @@ -2465,6 +2796,10 @@ packages: resolution: {integrity: sha512-xSEmD4pLnV54t0NOUN16yCl7RIB1c5UUOse5HSyEXtBp+FgFQyPeDutc+Q2ZO7/22vImV7VfEjH/1zV2QuqvYw==} hasBin: true + pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + pkg-types@1.1.1: resolution: {integrity: sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==} @@ -2472,6 +2807,43 @@ packages: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.0.1: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@4.0.2: + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss@8.4.31: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} @@ -2480,6 +2852,10 @@ packages: resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.1: + resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==} + engines: {node: ^10 || ^12 || >=14} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -2543,6 +2919,9 @@ packages: resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} engines: {node: '>=0.10.0'} + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -2551,6 +2930,10 @@ packages: resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + real-require@0.2.0: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} @@ -2586,6 +2969,9 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -2736,6 +3122,10 @@ packages: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -2744,6 +3134,9 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} + stable-hash@0.0.4: + resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==} + stack-trace@0.0.10: resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} @@ -2768,6 +3161,14 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string.prototype.includes@2.0.1: + resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} + engines: {node: '>= 0.4'} + string.prototype.matchall@4.0.12: resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} engines: {node: '>= 0.4'} @@ -2801,6 +3202,14 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -2821,6 +3230,11 @@ packages: babel-plugin-macros: optional: true + sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -2832,6 +3246,15 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + tailwindcss@3.4.17: + resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} + engines: {node: '>=14.0.0'} + hasBin: true + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} @@ -2839,6 +3262,13 @@ packages: text-hex@1.0.0: resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + thread-stream@3.0.2: resolution: {integrity: sha512-cBL4xF2A3lSINV4rD5tyqnKH4z/TgWPvT+NaVhJDSwK962oo/Ye7cHSMbDzwcu7tAE1SfU6Q4XtV6Hucmi6Hlw==} @@ -2883,6 +3313,9 @@ packages: peerDependencies: typescript: '>=4.8.4' + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + ts-node@10.9.2: resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true @@ -2897,6 +3330,9 @@ packages: '@swc/wasm': optional: true + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tslib@2.6.3: resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} @@ -3203,6 +3639,10 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -3229,6 +3669,11 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + yaml@2.7.0: + resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} + engines: {node: '>= 14'} + hasBin: true + yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} @@ -3255,6 +3700,8 @@ packages: snapshots: + '@alloc/quick-lru@5.2.0': {} + '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.5 @@ -3540,6 +3987,15 @@ snapshots: dependencies: '@types/node': 20.14.2 + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + '@istanbuljs/schema@0.1.3': {} '@jest/schemas@29.6.3': @@ -3590,6 +4046,10 @@ snapshots: '@next/env@15.1.4': {} + '@next/eslint-plugin-next@15.1.4': + dependencies: + fast-glob: 3.3.1 + '@next/eslint-plugin-next@15.1.6': dependencies: fast-glob: 3.3.1 @@ -3654,6 +4114,8 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 + '@nolyfill/is-core-module@1.0.39': {} + '@open-draft/deferred-promise@2.2.0': {} '@open-draft/logger@0.3.0': @@ -3663,6 +4125,9 @@ snapshots: '@open-draft/until@2.1.0': {} + '@pkgjs/parseargs@0.11.0': + optional: true + '@polka/url@1.0.0-next.25': {} '@rollup/plugin-replace@5.0.7(rollup@4.22.4)': @@ -3785,6 +4250,10 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.22.4': optional: true + '@rtsao/scc@1.1.0': {} + + '@rushstack/eslint-patch@1.10.5': {} + '@sinclair/typebox@0.27.8': {} '@swc/counter@0.1.3': {} @@ -3821,6 +4290,8 @@ snapshots: '@types/json-schema@7.0.15': {} + '@types/json5@0.0.29': {} + '@types/node@20.14.2': dependencies: undici-types: 5.26.5 @@ -4111,16 +4582,31 @@ snapshots: ansi-regex@5.0.1: {} + ansi-regex@6.1.0: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 ansi-styles@5.2.0: {} + ansi-styles@6.2.1: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + arg@4.1.3: {} + arg@5.0.2: {} + argparse@2.0.1: {} + aria-query@5.3.2: {} + array-buffer-byte-length@1.0.1: dependencies: call-bind: 1.0.7 @@ -4149,6 +4635,15 @@ snapshots: es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 + array.prototype.findlastindex@1.2.5: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-shim-unscopables: 1.0.2 + array.prototype.flat@1.3.2: dependencies: call-bind: 1.0.7 @@ -4194,6 +4689,8 @@ snapshots: assertion-error@1.1.0: {} + ast-types-flow@0.0.8: {} + async@3.2.5: {} asynckit@0.4.0: {} @@ -4204,10 +4701,16 @@ snapshots: dependencies: possible-typed-array-names: 1.0.0 + axe-core@4.10.2: {} + + axobject-query@4.1.0: {} + balanced-match@1.0.2: {} base64-js@1.5.1: {} + binary-extensions@2.3.0: {} + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -4259,6 +4762,8 @@ snapshots: callsites@3.1.0: {} + camelcase-css@2.0.1: {} + caniuse-lite@1.0.30001632: {} chai@4.4.1: @@ -4280,6 +4785,18 @@ snapshots: dependencies: get-func-name: 2.0.2 + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + cli-width@4.1.0: {} client-only@0.0.1: {} @@ -4327,6 +4844,8 @@ snapshots: dependencies: delayed-stream: 1.0.0 + commander@4.1.1: {} + computeds@0.0.1: {} concat-map@0.0.1: {} @@ -4345,12 +4864,16 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + cssesc@3.0.0: {} + cssstyle@4.0.1: dependencies: rrweb-cssom: 0.6.0 csstype@3.1.3: {} + damerau-levenshtein@1.0.8: {} + data-urls@5.0.0: dependencies: whatwg-mimetype: 4.0.0 @@ -4394,10 +4917,18 @@ snapshots: de-indent@1.0.2: {} + debug@3.2.7: + dependencies: + ms: 2.1.3 + debug@4.3.5: dependencies: ms: 2.1.2 + debug@4.4.0: + dependencies: + ms: 2.1.3 + decimal.js@10.4.3: {} deep-eql@4.1.4: @@ -4425,10 +4956,14 @@ snapshots: detect-libc@2.0.3: optional: true + didyoumean@1.2.2: {} + diff-sequences@29.6.3: {} diff@4.0.2: {} + dlv@1.1.3: {} + doctrine@2.1.0: dependencies: esutils: 2.0.3 @@ -4441,10 +4976,19 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + eastasianwidth@0.2.0: {} + emoji-regex@8.0.0: {} + emoji-regex@9.2.2: {} + enabled@2.0.0: {} + enhanced-resolve@5.18.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + entities@4.5.0: {} es-abstract@1.23.3: @@ -4640,10 +5184,113 @@ snapshots: escape-string-regexp@4.0.0: {} + eslint-config-next@15.1.4(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5): + dependencies: + '@next/eslint-plugin-next': 15.1.4 + '@rushstack/eslint-patch': 1.10.5 + '@typescript-eslint/eslint-plugin': 8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5))(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5) + '@typescript-eslint/parser': 8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5) + eslint: 9.18.0(jiti@2.4.2) + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.18.0(jiti@2.4.2)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5))(eslint-import-resolver-typescript@3.7.0)(eslint@9.18.0(jiti@2.4.2)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.18.0(jiti@2.4.2)) + eslint-plugin-react: 7.37.4(eslint@9.18.0(jiti@2.4.2)) + eslint-plugin-react-hooks: 5.1.0(eslint@9.18.0(jiti@2.4.2)) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - eslint-import-resolver-webpack + - eslint-plugin-import-x + - supports-color + eslint-config-prettier@9.1.0(eslint@9.18.0(jiti@2.4.2)): dependencies: eslint: 9.18.0(jiti@2.4.2) + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.13.1 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + + eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@9.18.0(jiti@2.4.2)): + dependencies: + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.0 + enhanced-resolve: 5.18.0 + eslint: 9.18.0(jiti@2.4.2) + fast-glob: 3.3.2 + get-tsconfig: 4.10.0 + is-bun-module: 1.3.0 + is-glob: 4.0.3 + stable-hash: 0.0.4 + optionalDependencies: + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5))(eslint-import-resolver-typescript@3.7.0)(eslint@9.18.0(jiti@2.4.2)) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.18.0(jiti@2.4.2)): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5) + eslint: 9.18.0(jiti@2.4.2) + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.18.0(jiti@2.4.2)) + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5))(eslint-import-resolver-typescript@3.7.0)(eslint@9.18.0(jiti@2.4.2)): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.8 + array.prototype.findlastindex: 1.2.5 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 9.18.0(jiti@2.4.2) + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.18.0(jiti@2.4.2)) + 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.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-jsx-a11y@6.10.2(eslint@9.18.0(jiti@2.4.2)): + dependencies: + aria-query: 5.3.2 + array-includes: 3.1.8 + array.prototype.flatmap: 1.3.3 + ast-types-flow: 0.0.8 + axe-core: 4.10.2 + axobject-query: 4.1.0 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 9.18.0(jiti@2.4.2) + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.9 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + safe-regex-test: 1.1.0 + string.prototype.includes: 2.0.1 + eslint-plugin-react-hooks@5.1.0(eslint@9.18.0(jiti@2.4.2)): dependencies: eslint: 9.18.0(jiti@2.4.2) @@ -4814,6 +5461,11 @@ snapshots: dependencies: is-callable: 1.2.7 + foreground-child@3.3.0: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + form-data@4.0.0: dependencies: asynckit: 0.4.0 @@ -4887,6 +5539,10 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.2.7 + get-tsconfig@4.10.0: + dependencies: + resolve-pkg-maps: 1.0.0 + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -4895,6 +5551,15 @@ snapshots: dependencies: is-glob: 4.0.3 + glob@10.4.5: + dependencies: + foreground-child: 3.3.0 + jackspeak: 3.4.3 + minimatch: 9.0.4 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -4919,6 +5584,8 @@ snapshots: gopd@1.2.0: {} + graceful-fs@4.2.11: {} + graphemer@1.4.0: {} graphql@16.9.0: {} @@ -5032,6 +5699,10 @@ snapshots: dependencies: has-bigints: 1.0.2 + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + is-boolean-object@1.1.2: dependencies: call-bind: 1.0.7 @@ -5042,12 +5713,20 @@ snapshots: call-bound: 1.0.3 has-tostringtag: 1.0.2 + is-bun-module@1.3.0: + dependencies: + semver: 7.6.3 + is-callable@1.2.7: {} is-core-module@2.13.1: dependencies: hasown: 2.0.2 + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + is-data-view@1.0.1: dependencies: is-typed-array: 1.1.13 @@ -5206,6 +5885,14 @@ snapshots: has-symbols: 1.1.0 set-function-name: 2.0.2 + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jiti@1.21.7: {} + jiti@2.4.2: optional: true @@ -5253,6 +5940,10 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + json5@1.0.2: + dependencies: + minimist: 1.2.8 + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.8 @@ -5266,11 +5957,21 @@ snapshots: kuler@2.0.0: {} + language-subtag-registry@0.3.23: {} + + language-tags@1.0.9: + dependencies: + language-subtag-registry: 0.3.23 + levn@0.4.1: dependencies: prelude-ls: 1.2.1 type-check: 0.4.0 + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + local-pkg@0.4.3: {} locate-path@6.0.0: @@ -5296,6 +5997,8 @@ snapshots: dependencies: get-func-name: 2.0.2 + lru-cache@10.4.3: {} + magic-string@0.30.10: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -5315,6 +6018,11 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + mime-db@1.52.0: {} mime-types@2.1.35: @@ -5329,6 +6037,10 @@ snapshots: dependencies: brace-expansion: 2.0.1 + minimist@1.2.8: {} + + minipass@7.1.2: {} + mlly@1.7.1: dependencies: acorn: 8.11.3 @@ -5371,8 +6083,16 @@ snapshots: mute-stream@2.0.0: {} + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + nanoid@3.3.7: {} + nanoid@3.3.8: {} + natural-compare@1.4.0: {} next@15.0.2(react-dom@18.3.1(react@18.2.0))(react@18.2.0): @@ -5425,10 +6145,14 @@ snapshots: - '@babel/core' - babel-plugin-macros + normalize-path@3.0.0: {} + nwsapi@2.2.10: {} object-assign@4.1.1: {} + object-hash@3.0.0: {} + object-inspect@1.13.1: {} object-inspect@1.13.3: {} @@ -5464,6 +6188,12 @@ snapshots: es-abstract: 1.23.3 es-object-atoms: 1.0.0 + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + object.values@1.2.0: dependencies: call-bind: 1.0.7 @@ -5516,6 +6246,8 @@ snapshots: dependencies: p-limit: 3.1.0 + package-json-from-dist@1.0.1: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -5534,6 +6266,11 @@ snapshots: path-parse@1.0.7: {} + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + path-to-regexp@6.3.0: {} pathe@1.1.2: {} @@ -5542,8 +6279,12 @@ snapshots: picocolors@1.0.1: {} + picocolors@1.1.1: {} + picomatch@2.3.1: {} + pify@2.3.0: {} + pino-abstract-transport@1.2.0: dependencies: readable-stream: 4.5.2 @@ -5569,6 +6310,8 @@ snapshots: sonic-boom: 4.0.1 thread-stream: 3.0.2 + pirates@4.0.6: {} + pkg-types@1.1.1: dependencies: confbox: 0.1.7 @@ -5577,6 +6320,38 @@ snapshots: possible-typed-array-names@1.0.0: {} + postcss-import@15.1.0(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 + + postcss-js@4.0.1(postcss@8.5.1): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.5.1 + + postcss-load-config@4.0.2(postcss@8.5.1)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5)): + dependencies: + lilconfig: 3.1.3 + yaml: 2.7.0 + optionalDependencies: + postcss: 8.5.1 + ts-node: 10.9.2(@types/node@20.14.2)(typescript@5.4.5) + + postcss-nested@6.2.0(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + postcss-selector-parser: 6.1.2 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + postcss@8.4.31: dependencies: nanoid: 3.3.7 @@ -5589,6 +6364,12 @@ snapshots: picocolors: 1.0.1 source-map-js: 1.2.0 + postcss@8.5.1: + dependencies: + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 + prelude-ls@1.2.1: {} prettier@3.3.2: {} @@ -5640,6 +6421,10 @@ snapshots: react@19.0.0: {} + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -5654,6 +6439,10 @@ snapshots: process: 0.11.10 string_decoder: 1.3.0 + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + real-require@0.2.0: {} reflect.getprototypeof@1.0.10: @@ -5701,6 +6490,8 @@ snapshots: resolve-from@4.0.0: {} + resolve-pkg-maps@1.0.0: {} + resolve@1.22.8: dependencies: is-core-module: 2.13.1 @@ -5931,10 +6722,14 @@ snapshots: source-map-js@1.2.0: {} + source-map-js@1.2.1: {} + source-map@0.6.1: {} split2@4.2.0: {} + stable-hash@0.0.4: {} + stack-trace@0.0.10: {} stackback@0.0.2: {} @@ -5953,6 +6748,18 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + string.prototype.includes@2.0.1: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + string.prototype.matchall@4.0.12: dependencies: call-bind: 1.0.8 @@ -6018,6 +6825,12 @@ snapshots: dependencies: ansi-regex: 5.0.1 + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-bom@3.0.0: {} + strip-json-comments@3.1.1: {} strip-literal@1.3.0: @@ -6034,6 +6847,16 @@ snapshots: client-only: 0.0.1 react: 19.0.0 + sucrase@3.35.0: + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + commander: 4.1.1 + glob: 10.4.5 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -6042,6 +6865,35 @@ snapshots: symbol-tree@3.2.4: {} + tailwindcss@3.4.17(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5)): + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.2 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.7 + lilconfig: 3.1.3 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.1 + postcss-import: 15.1.0(postcss@8.5.1) + postcss-js: 4.0.1(postcss@8.5.1) + postcss-load-config: 4.0.2(postcss@8.5.1)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5)) + postcss-nested: 6.2.0(postcss@8.5.1) + postcss-selector-parser: 6.1.2 + resolve: 1.22.8 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + + tapable@2.2.1: {} + test-exclude@6.0.0: dependencies: '@istanbuljs/schema': 0.1.3 @@ -6050,6 +6902,14 @@ snapshots: text-hex@1.0.0: {} + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + thread-stream@3.0.2: dependencies: real-require: 0.2.0 @@ -6085,6 +6945,8 @@ snapshots: dependencies: typescript: 5.4.5 + ts-interface-checker@0.1.13: {} + ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -6103,6 +6965,13 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + tslib@2.6.3: {} tslib@2.8.1: {} @@ -6489,6 +7358,12 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + wrappy@1.0.2: {} ws@8.17.0: {} @@ -6499,6 +7374,8 @@ snapshots: y18n@5.0.8: {} + yaml@2.7.0: {} + yargs-parser@21.1.1: {} yargs@17.7.2: From 4235bb3b674b063f0ddb5a79e2887140b1e57aeb Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Thu, 16 Jan 2025 14:29:32 +0000 Subject: [PATCH 04/71] feat: add and extend from new SimpleFetchTransport --- packages/core/transports/axiom-fetch.ts | 37 ++++--------------- packages/core/transports/fetch.ts | 39 +++++++++++++++++++++ packages/core/transports/index.ts | 3 +- packages/core/transports/proxy-transport.ts | 35 +++--------------- 4 files changed, 53 insertions(+), 61 deletions(-) create mode 100644 packages/core/transports/fetch.ts diff --git a/packages/core/transports/axiom-fetch.ts b/packages/core/transports/axiom-fetch.ts index 53cc4165..ab7df159 100644 --- a/packages/core/transports/axiom-fetch.ts +++ b/packages/core/transports/axiom-fetch.ts @@ -1,6 +1,4 @@ -import { Transport } from '.'; -import { LogEvent } from '..'; - +import { Transport, SimpleFetchTransport } from '.'; interface AxiomFetchConfig { dataset: string; token: string; @@ -9,34 +7,13 @@ interface AxiomFetchConfig { const DEFAULT_URL = 'https://api.axiom.co'; -export class AxiomFetchTransport implements Transport { - private config: AxiomFetchConfig; - private events: LogEvent[] = []; - +export class AxiomFetchTransport extends SimpleFetchTransport implements Transport { constructor(config: AxiomFetchConfig) { - this.config = { url: DEFAULT_URL, ...config }; - } - - log: Transport['log'] = (logs) => { - this.events.push(...logs); - }; - - async flush() { - await fetch(`${this.config.url}/v1/datasets/${this.config.dataset}/ingest`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${this.config.token}`, + super({ + input: `${config.url ?? DEFAULT_URL}/v1/datasets/${config.dataset}/ingest`, + init: { + headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${config.token}` }, }, - body: JSON.stringify(this.events), - }) - .then(async (res) => { - if (!res.ok) { - console.error(await res.text()); - throw new Error('Failed to flush logs'); - } - this.events = []; - }) - .catch(console.error); + }); } } diff --git a/packages/core/transports/fetch.ts b/packages/core/transports/fetch.ts new file mode 100644 index 00000000..83bf4ba0 --- /dev/null +++ b/packages/core/transports/fetch.ts @@ -0,0 +1,39 @@ +import { Transport } from '.'; +import { LogEvent } from '..'; + +interface FetchConfig { + input: Parameters[0]; + init?: Omit[1], 'body'>; +} + +export class SimpleFetchTransport implements Transport { + private fetchConfig: FetchConfig; + private events: LogEvent[] = []; + + constructor(config: FetchConfig) { + this.fetchConfig = config; + } + + log: Transport['log'] = (logs) => { + this.events.push(...logs); + }; + + async flush() { + await fetch(this.fetchConfig.input, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + ...this.fetchConfig.init, + body: JSON.stringify(this.events), + }) + .then(async (res) => { + if (!res.ok) { + console.error(await res.text()); + throw new Error('Failed to flush logs'); + } + this.events = []; + }) + .catch(console.error); + } +} diff --git a/packages/core/transports/index.ts b/packages/core/transports/index.ts index 7e4fc0bc..b1dda0ad 100644 --- a/packages/core/transports/index.ts +++ b/packages/core/transports/index.ts @@ -5,7 +5,8 @@ export interface Transport { flush: () => Promise | void; } -export * from './console'; export * from './axiom-js'; export * from './axiom-fetch'; +export * from './console'; +export * from './fetch'; export * from './proxy-transport'; diff --git a/packages/core/transports/proxy-transport.ts b/packages/core/transports/proxy-transport.ts index 7b85f3a9..1b3f7045 100644 --- a/packages/core/transports/proxy-transport.ts +++ b/packages/core/transports/proxy-transport.ts @@ -1,37 +1,12 @@ -import { Transport } from '.'; -import { LogEvent } from '..'; - +import { SimpleFetchTransport, Transport } from '.'; interface AxiomProxyConfig { url: string; } -export class AxiomProxyTransport implements Transport { - private config: AxiomProxyConfig; - private events: LogEvent[] = []; - +export class AxiomProxyTransport extends SimpleFetchTransport implements Transport { constructor(config: AxiomProxyConfig) { - this.config = config; - } - - log: Transport['log'] = (logs) => { - this.events.push(...logs); - }; - - async flush() { - await fetch(this.config.url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(this.events), - }) - .then(async (res) => { - if (!res.ok) { - console.error(await res.text()); - throw new Error('Failed to flush logs'); - } - this.events = []; - }) - .catch(console.error); + super({ + input: config.url, + }); } } From 73b29ac66a19e7c9ab960f03c94ef11bc9fcc468 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Thu, 16 Jan 2025 14:41:34 +0000 Subject: [PATCH 05/71] feat: Add prettyPrint to ConsoleTransport --- packages/core/shared.ts | 13 +++++- packages/core/transports/console.ts | 66 ++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/packages/core/shared.ts b/packages/core/shared.ts index 0d2f8e25..dfc110a7 100644 --- a/packages/core/shared.ts +++ b/packages/core/shared.ts @@ -1,3 +1,14 @@ -import packageJson from "./package.json"; +import packageJson from './package.json'; export const Version = packageJson.version; + +declare global { + var WorkerGlobalScope: any; +} + +export const isWebWorker = + typeof self !== 'undefined' && + typeof globalThis.WorkerGlobalScope !== 'undefined' && + self instanceof WorkerGlobalScope; + +export const isBrowser = typeof window !== 'undefined' || isWebWorker; diff --git a/packages/core/transports/console.ts b/packages/core/transports/console.ts index aef0bfd2..5e4dd447 100644 --- a/packages/core/transports/console.ts +++ b/packages/core/transports/console.ts @@ -1,12 +1,74 @@ -import { Transport } from "."; +import { Transport } from '.'; +import { LogEvent } from '..'; +import { isBrowser } from '../shared'; +export interface ConsoleTransportConfig { + prettyPrint?: boolean; +} + +const levelColors: { [key: string]: any } = { + info: { + terminal: '32', + browser: 'lightgreen', + }, + debug: { + terminal: '36', + browser: 'lightblue', + }, + warn: { + terminal: '33', + browser: 'yellow', + }, + error: { + terminal: '31', + browser: 'red', + }, +}; export class ConsoleTransport implements Transport { - log: Transport["log"] = (logs) => { + private config: ConsoleTransportConfig; + constructor(config: ConsoleTransportConfig) { + this.config = config; + } + log: Transport['log'] = (logs) => { logs.forEach((log) => { console.log(log); }); }; + prettyPrint(ev: LogEvent) { + const hasFields = Object.keys(ev.fields).length > 0; + // check whether pretty print is disabled + if (!this.config.prettyPrint) { + let msg = `${ev.level} - ${ev.message}`; + if (hasFields) { + msg += ' ' + JSON.stringify(ev.fields); + } + console.log(msg); + return; + } + // print indented message, instead of [object] + // We use the %o modifier instead of JSON.stringify because stringify will print the + // object as normal text, it loses all the functionality the browser gives for viewing + // objects in the console, such as expanding and collapsing the object. + let msgString = ''; + let args: any[] = [ev.level, ev.message]; + + if (isBrowser) { + msgString = '%c%s - %s'; + args = [`color: ${levelColors[ev.level].browser};`, ...args]; + } else { + msgString = `\x1b[${levelColors[ev.level].terminal}m%s\x1b[0m - %s`; + } + // we check if the fields object is not empty, otherwise its printed as + // or just "". + if (hasFields) { + msgString += ' %o'; + args.push(ev.fields); + } + + console.log.apply(console, [msgString, ...args]); + } + flush() { return; } From 93ab4f6ebc97a3ee16f56eec12006cea807e469c Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Thu, 16 Jan 2025 14:44:19 +0000 Subject: [PATCH 06/71] docs: move handler to component body in example --- examples/nextjs/src/app/_components/button.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/nextjs/src/app/_components/button.tsx b/examples/nextjs/src/app/_components/button.tsx index 0add096e..05b4fc7f 100644 --- a/examples/nextjs/src/app/_components/button.tsx +++ b/examples/nextjs/src/app/_components/button.tsx @@ -4,9 +4,14 @@ import { useLogger } from '@/lib/axiom/client'; export default function Button() { const logger = useLogger(); + + const handleClick = () => { + logger.info('Hello World from Client Side!', { key: 'value' }); + }; + return (
-
From a1f8f468b67f3f461846d03a1739a1b58822843e Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Thu, 16 Jan 2025 15:43:51 +0000 Subject: [PATCH 07/71] fix: make ConsoleTransport config optional in constructor --- packages/core/transports/console.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/transports/console.ts b/packages/core/transports/console.ts index 5e4dd447..d9cfc914 100644 --- a/packages/core/transports/console.ts +++ b/packages/core/transports/console.ts @@ -26,8 +26,8 @@ const levelColors: { [key: string]: any } = { export class ConsoleTransport implements Transport { private config: ConsoleTransportConfig; - constructor(config: ConsoleTransportConfig) { - this.config = config; + constructor(config?: ConsoleTransportConfig) { + this.config = { ...config }; } log: Transport['log'] = (logs) => { logs.forEach((log) => { From 654733eea23102fdddfc429bf673ffd5481c2855 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Thu, 16 Jan 2025 16:33:41 +0000 Subject: [PATCH 08/71] feat: add autoFlush option to fetch transports --- examples/nextjs/src/lib/axiom/client.ts | 5 ++++- examples/nextjs/src/lib/axiom/server.ts | 2 +- packages/core/transports/axiom-fetch.ts | 5 ++++- packages/core/transports/fetch.ts | 21 +++++++++++++++++++++ packages/core/transports/proxy-transport.ts | 6 ++++-- 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/examples/nextjs/src/lib/axiom/client.ts b/examples/nextjs/src/lib/axiom/client.ts index 643471e2..1ada53e1 100644 --- a/examples/nextjs/src/lib/axiom/client.ts +++ b/examples/nextjs/src/lib/axiom/client.ts @@ -3,7 +3,10 @@ import { AxiomProxyTransport, ConsoleTransport } from '@axiomhq/logger/transport import { createClientSideHelpers } from '@axiomhq/react'; export const logger = new Logger({ - transports: [new AxiomProxyTransport({ url: process.env.NEXT_PUBLIC_AXIOM_PROXY_URL! }), new ConsoleTransport()], + transports: [ + new AxiomProxyTransport({ url: process.env.NEXT_PUBLIC_AXIOM_PROXY_URL!, autoFlush: true }), + new ConsoleTransport(), + ], }); export const { useLogger } = createClientSideHelpers(logger); diff --git a/examples/nextjs/src/lib/axiom/server.ts b/examples/nextjs/src/lib/axiom/server.ts index 03b68e9d..4a86dd5f 100644 --- a/examples/nextjs/src/lib/axiom/server.ts +++ b/examples/nextjs/src/lib/axiom/server.ts @@ -1,5 +1,5 @@ import { Logger } from '@axiomhq/logger'; -import { AxiomFetchTransport, ConsoleTransport } from '@axiomhq/logging/transports'; +import { AxiomFetchTransport, ConsoleTransport } from '@axiomhq/logger/transports'; export const logger = new Logger({ transports: [ diff --git a/packages/core/transports/axiom-fetch.ts b/packages/core/transports/axiom-fetch.ts index ab7df159..96735d3e 100644 --- a/packages/core/transports/axiom-fetch.ts +++ b/packages/core/transports/axiom-fetch.ts @@ -1,8 +1,10 @@ -import { Transport, SimpleFetchTransport } from '.'; +import { Transport } from '.'; +import { SimpleFetchTransport } from './fetch'; interface AxiomFetchConfig { dataset: string; token: string; url?: string; + autoFlush?: boolean | number; } const DEFAULT_URL = 'https://api.axiom.co'; @@ -14,6 +16,7 @@ export class AxiomFetchTransport extends SimpleFetchTransport implements Transpo init: { headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${config.token}` }, }, + autoFlush: config.autoFlush, }); } } diff --git a/packages/core/transports/fetch.ts b/packages/core/transports/fetch.ts index 83bf4ba0..a2e456a2 100644 --- a/packages/core/transports/fetch.ts +++ b/packages/core/transports/fetch.ts @@ -4,11 +4,13 @@ import { LogEvent } from '..'; interface FetchConfig { input: Parameters[0]; init?: Omit[1], 'body'>; + autoFlush?: number | boolean; } export class SimpleFetchTransport implements Transport { private fetchConfig: FetchConfig; private events: LogEvent[] = []; + private timer: NodeJS.Timeout | null = null; constructor(config: FetchConfig) { this.fetchConfig = config; @@ -16,9 +18,28 @@ export class SimpleFetchTransport implements Transport { log: Transport['log'] = (logs) => { this.events.push(...logs); + + if (typeof this.fetchConfig.autoFlush === 'undefined' || this.fetchConfig.autoFlush === false) { + return; + } + + if (this.timer) { + clearTimeout(this.timer); + } + + this.timer = setTimeout( + () => { + this.flush(); + }, + typeof this.fetchConfig.autoFlush === 'number' ? this.fetchConfig.autoFlush : 2000, + ); }; async flush() { + if (this.events.length <= 0) { + return; + } + await fetch(this.fetchConfig.input, { method: 'POST', headers: { diff --git a/packages/core/transports/proxy-transport.ts b/packages/core/transports/proxy-transport.ts index 1b3f7045..3b86ecfb 100644 --- a/packages/core/transports/proxy-transport.ts +++ b/packages/core/transports/proxy-transport.ts @@ -1,12 +1,14 @@ -import { SimpleFetchTransport, Transport } from '.'; +import { Transport } from '.'; +import { SimpleFetchTransport } from './fetch'; interface AxiomProxyConfig { url: string; + autoFlush?: boolean | number; } - export class AxiomProxyTransport extends SimpleFetchTransport implements Transport { constructor(config: AxiomProxyConfig) { super({ input: config.url, + autoFlush: config.autoFlush, }); } } From 8c0906bf45a0fdef0284efb07a937f283bf0b0e9 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Fri, 17 Jan 2025 15:09:30 +0000 Subject: [PATCH 09/71] fix: move report under request: to match current data shape --- examples/nextjs/src/app/api/route.ts | 5 +++-- packages/nextjs/withAxiom.ts | 23 ++++++++++++++--------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/examples/nextjs/src/app/api/route.ts b/examples/nextjs/src/app/api/route.ts index c2f7981d..38d4d438 100644 --- a/examples/nextjs/src/app/api/route.ts +++ b/examples/nextjs/src/app/api/route.ts @@ -23,9 +23,10 @@ export const POST = axiomRouteHandler( } else { if (result.data.error instanceof Error) { logger.error(result.data.error.message, result.data.error); - const [message, report] = transformErrorResult(result.data); - logger[logErrorByStatusCode(report.statusCode)](message, report); } + + const [message, report] = transformErrorResult(result.data); + logger[logErrorByStatusCode(report.statusCode)](message, report); } }, ); diff --git a/packages/nextjs/withAxiom.ts b/packages/nextjs/withAxiom.ts index 291a11d5..b09104b5 100644 --- a/packages/nextjs/withAxiom.ts +++ b/packages/nextjs/withAxiom.ts @@ -12,17 +12,22 @@ export type NextHandler = ( export const transformSuccessResult = (data: SuccessData): [message: string, report: Record] => { const report = { - type: 'request', - method: data.req.method, - url: data.req.url, - statusCode: data.res.status, - durationMs: data.end - data.start, - path: new URL(data.req.url).pathname, - endTime: data.end, - startTime: data.start, + request: { + type: 'request', + method: data.req.method, + url: data.req.url, + statusCode: data.res.status, + durationMs: data.end - data.start, + path: new URL(data.req.url).pathname, + endTime: data.end, + startTime: data.start, + }, }; - return [`${data.req.method} ${report.path} ${report.statusCode} in ${report.endTime - report.startTime}ms`, report]; + return [ + `${data.req.method} ${report.request.path} ${report.request.statusCode} in ${report.request.endTime - report.request.startTime}ms`, + report, + ]; }; export const transformErrorResult = (data: ErrorData): [message: string, report: Record] => { From 9535c53ffaf3c1aed059d0be53cad37f3bd164a6 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Fri, 17 Jan 2025 15:20:02 +0000 Subject: [PATCH 10/71] refactor: rename withAxiom to routeHandler --- packages/nextjs/index.ts | 2 +- packages/nextjs/{withAxiom.ts => routeHandler.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename packages/nextjs/{withAxiom.ts => routeHandler.ts} (100%) diff --git a/packages/nextjs/index.ts b/packages/nextjs/index.ts index 702864e5..11bbb05e 100644 --- a/packages/nextjs/index.ts +++ b/packages/nextjs/index.ts @@ -1,2 +1,2 @@ -export * from './withAxiom'; +export * from './routeHandler'; export * from './proxyRouteHandler'; diff --git a/packages/nextjs/withAxiom.ts b/packages/nextjs/routeHandler.ts similarity index 100% rename from packages/nextjs/withAxiom.ts rename to packages/nextjs/routeHandler.ts From 78a3db44f354712200eb1264e3fc688b36a46172 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Fri, 17 Jan 2025 15:20:22 +0000 Subject: [PATCH 11/71] feat: add middleware support for Next.js --- examples/nextjs/src/middleware.ts | 11 +++++++++++ packages/nextjs/index.ts | 1 + packages/nextjs/middleware.ts | 20 ++++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 examples/nextjs/src/middleware.ts create mode 100644 packages/nextjs/middleware.ts diff --git a/examples/nextjs/src/middleware.ts b/examples/nextjs/src/middleware.ts new file mode 100644 index 00000000..b5f6ee3f --- /dev/null +++ b/examples/nextjs/src/middleware.ts @@ -0,0 +1,11 @@ +import { logger } from '@/lib/axiom/server'; +import { transformMiddlewareRequest } from '@axiomhq/nextjs'; +import { NextResponse } from 'next/server'; +import type { NextFetchEvent, NextRequest } from 'next/server'; + +export async function middleware(request: NextRequest, event: NextFetchEvent) { + logger.info(...transformMiddlewareRequest(request)); + + event.waitUntil(logger.flush()); + return NextResponse.next(); +} diff --git a/packages/nextjs/index.ts b/packages/nextjs/index.ts index 11bbb05e..174b89b0 100644 --- a/packages/nextjs/index.ts +++ b/packages/nextjs/index.ts @@ -1,2 +1,3 @@ export * from './routeHandler'; export * from './proxyRouteHandler'; +export * from './middleware'; diff --git a/packages/nextjs/middleware.ts b/packages/nextjs/middleware.ts new file mode 100644 index 00000000..752ee168 --- /dev/null +++ b/packages/nextjs/middleware.ts @@ -0,0 +1,20 @@ +import { NextRequest } from 'next/server'; + +export const transformMiddlewareRequest = ( + request: NextRequest & { geo?: { region: string }; ip?: string }, +): [message: string, report: Record] => { + const report = { + request: { + ip: request.ip, + region: request.geo?.region, + method: request.method, + host: request.nextUrl.hostname, + path: request.nextUrl.pathname, + scheme: request.nextUrl.protocol.split(':')[0], + referer: request.headers.get('Referer'), + userAgent: request.headers.get('user-agent'), + }, + }; + + return [`${request.method} ${request.nextUrl.pathname}`, report]; +}; From 0ef11fb2ab40bbdf5f0f51630ce0cb509ea9598e Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Mon, 20 Jan 2025 14:45:54 +0000 Subject: [PATCH 12/71] feat: Add onRequestError helpers --- examples/nextjs/src/instrumentation.ts | 4 +++ packages/nextjs/index.ts | 1 + packages/nextjs/instrumentation.ts | 38 ++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 examples/nextjs/src/instrumentation.ts create mode 100644 packages/nextjs/instrumentation.ts diff --git a/examples/nextjs/src/instrumentation.ts b/examples/nextjs/src/instrumentation.ts new file mode 100644 index 00000000..819ec00d --- /dev/null +++ b/examples/nextjs/src/instrumentation.ts @@ -0,0 +1,4 @@ +import { logger } from '@/lib/axiom/server'; +import { createOnRequestError } from '@axiomhq/nextjs'; + +export const onRequestError = createOnRequestError(logger); diff --git a/packages/nextjs/index.ts b/packages/nextjs/index.ts index 174b89b0..0cab3820 100644 --- a/packages/nextjs/index.ts +++ b/packages/nextjs/index.ts @@ -1,3 +1,4 @@ export * from './routeHandler'; export * from './proxyRouteHandler'; export * from './middleware'; +export * from './instrumentation'; diff --git a/packages/nextjs/instrumentation.ts b/packages/nextjs/instrumentation.ts new file mode 100644 index 00000000..c75303a5 --- /dev/null +++ b/packages/nextjs/instrumentation.ts @@ -0,0 +1,38 @@ +import { onRequestError } from '@/instrumentation'; +import { Logger } from '@axiomhq/logger'; +import { Instrumentation } from 'next'; + +export const transformOnRequestError = ( + ...args: Parameters +): [message: string, report: Record] => { + const [error, request, context] = args; + if (error instanceof Error) { + return [ + error.message, + { + ...error, + error: error.name, + cause: error.cause, + stack: error.stack, + digest: (error as Error & { digest?: string }).digest, + request: request, + context: context, + }, + ]; + } + return [ + `${request.method} ${request.path} ${context.routeType}`, + { + error, + request: request, + context: context, + }, + ]; +}; + +export const createOnRequestError = + (logger: Logger): Instrumentation.onRequestError => + async (...args) => { + logger.error(...transformOnRequestError(...args)); + await logger.flush(); + }; From c68b2d51d5c777f24675c1898cb3838156055fea Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Mon, 20 Jan 2025 14:47:36 +0000 Subject: [PATCH 13/71] refactor: rename folder from core to logger --- packages/{core => logger}/index.ts | 0 packages/{core => logger}/package.json | 0 packages/{core => logger}/shared.ts | 0 .../{core => logger}/transports/axiom-fetch.ts | 0 .../{core => logger}/transports/axiom-js.ts | 0 .../{core => logger}/transports/console.ts | 0 packages/{core => logger}/transports/fetch.ts | 0 packages/{core => logger}/transports/index.ts | 0 .../transports/proxy-transport.ts | 0 pnpm-lock.yaml | 18 +++++++++--------- 10 files changed, 9 insertions(+), 9 deletions(-) rename packages/{core => logger}/index.ts (100%) rename packages/{core => logger}/package.json (100%) rename packages/{core => logger}/shared.ts (100%) rename packages/{core => logger}/transports/axiom-fetch.ts (100%) rename packages/{core => logger}/transports/axiom-js.ts (100%) rename packages/{core => logger}/transports/console.ts (100%) rename packages/{core => logger}/transports/fetch.ts (100%) rename packages/{core => logger}/transports/index.ts (100%) rename packages/{core => logger}/transports/proxy-transport.ts (100%) diff --git a/packages/core/index.ts b/packages/logger/index.ts similarity index 100% rename from packages/core/index.ts rename to packages/logger/index.ts diff --git a/packages/core/package.json b/packages/logger/package.json similarity index 100% rename from packages/core/package.json rename to packages/logger/package.json diff --git a/packages/core/shared.ts b/packages/logger/shared.ts similarity index 100% rename from packages/core/shared.ts rename to packages/logger/shared.ts diff --git a/packages/core/transports/axiom-fetch.ts b/packages/logger/transports/axiom-fetch.ts similarity index 100% rename from packages/core/transports/axiom-fetch.ts rename to packages/logger/transports/axiom-fetch.ts diff --git a/packages/core/transports/axiom-js.ts b/packages/logger/transports/axiom-js.ts similarity index 100% rename from packages/core/transports/axiom-js.ts rename to packages/logger/transports/axiom-js.ts diff --git a/packages/core/transports/console.ts b/packages/logger/transports/console.ts similarity index 100% rename from packages/core/transports/console.ts rename to packages/logger/transports/console.ts diff --git a/packages/core/transports/fetch.ts b/packages/logger/transports/fetch.ts similarity index 100% rename from packages/core/transports/fetch.ts rename to packages/logger/transports/fetch.ts diff --git a/packages/core/transports/index.ts b/packages/logger/transports/index.ts similarity index 100% rename from packages/core/transports/index.ts rename to packages/logger/transports/index.ts diff --git a/packages/core/transports/proxy-transport.ts b/packages/logger/transports/proxy-transport.ts similarity index 100% rename from packages/core/transports/proxy-transport.ts rename to packages/logger/transports/proxy-transport.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 705285a4..c94e1fea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -105,7 +105,7 @@ importers: dependencies: '@axiomhq/logger': specifier: workspace:* - version: link:../../packages/core + version: link:../../packages/logger '@axiomhq/nextjs': specifier: workspace:^ version: link:../../packages/nextjs @@ -263,12 +263,6 @@ importers: specifier: ^8.15.0 version: 8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5) - packages/core: - dependencies: - '@axiomhq/js': - specifier: ^1.3.1 - version: 1.3.1 - packages/js: dependencies: fetch-retry: @@ -285,11 +279,17 @@ importers: specifier: ^2.6.2 version: 2.6.2(@types/node@20.14.2)(typescript@5.4.5) + packages/logger: + dependencies: + '@axiomhq/js': + specifier: ^1.3.1 + version: 1.3.1 + packages/nextjs: dependencies: '@axiomhq/logger': specifier: workspace:^ - version: link:../core + version: link:../logger devDependencies: next: specifier: 15.1.4 @@ -312,7 +312,7 @@ importers: dependencies: '@axiomhq/logger': specifier: workspace:* - version: link:../core + version: link:../logger '@types/react': specifier: ^19 version: 19.0.7 From a8506ff36e5de9a60b22388316553ef011d43b35 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 21 Jan 2025 12:43:40 +0000 Subject: [PATCH 14/71] refactor: rename logger back to logging --- examples/nextjs/package.json | 2 +- examples/nextjs/src/lib/axiom/client.ts | 4 ++-- examples/nextjs/src/lib/axiom/server.ts | 4 ++-- packages/{logger => logging}/index.ts | 0 packages/{logger => logging}/package.json | 2 +- packages/{logger => logging}/shared.ts | 0 .../{logger => logging}/transports/axiom-fetch.ts | 0 .../{logger => logging}/transports/axiom-js.ts | 0 packages/{logger => logging}/transports/console.ts | 0 packages/{logger => logging}/transports/fetch.ts | 0 packages/{logger => logging}/transports/index.ts | 0 .../transports/proxy-transport.ts | 0 packages/nextjs/instrumentation.ts | 2 +- packages/nextjs/package.json | 2 +- packages/nextjs/proxyRouteHandler.ts | 2 +- packages/nextjs/routeHandler.ts | 2 +- packages/react/index.ts | 2 +- packages/react/package.json | 2 +- packages/react/use-logger.ts | 2 +- pnpm-lock.yaml | 14 +++++++------- 20 files changed, 20 insertions(+), 20 deletions(-) rename packages/{logger => logging}/index.ts (100%) rename packages/{logger => logging}/package.json (89%) rename packages/{logger => logging}/shared.ts (100%) rename packages/{logger => logging}/transports/axiom-fetch.ts (100%) rename packages/{logger => logging}/transports/axiom-js.ts (100%) rename packages/{logger => logging}/transports/console.ts (100%) rename packages/{logger => logging}/transports/fetch.ts (100%) rename packages/{logger => logging}/transports/index.ts (100%) rename packages/{logger => logging}/transports/proxy-transport.ts (100%) diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index 0d3e7933..83a37e0b 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -9,7 +9,7 @@ "lint": "next lint" }, "dependencies": { - "@axiomhq/logger": "workspace:*", + "@axiomhq/logging": "workspace:*", "@axiomhq/nextjs": "workspace:^", "@axiomhq/react": "workspace:*", "next": "15.1.4", diff --git a/examples/nextjs/src/lib/axiom/client.ts b/examples/nextjs/src/lib/axiom/client.ts index 1ada53e1..108eb318 100644 --- a/examples/nextjs/src/lib/axiom/client.ts +++ b/examples/nextjs/src/lib/axiom/client.ts @@ -1,5 +1,5 @@ -import { Logger } from '@axiomhq/logger'; -import { AxiomProxyTransport, ConsoleTransport } from '@axiomhq/logger/transports'; +import { Logger } from '@axiomhq/logging'; +import { AxiomProxyTransport, ConsoleTransport } from '@axiomhq/logging/transports'; import { createClientSideHelpers } from '@axiomhq/react'; export const logger = new Logger({ diff --git a/examples/nextjs/src/lib/axiom/server.ts b/examples/nextjs/src/lib/axiom/server.ts index 4a86dd5f..b473d6c5 100644 --- a/examples/nextjs/src/lib/axiom/server.ts +++ b/examples/nextjs/src/lib/axiom/server.ts @@ -1,5 +1,5 @@ -import { Logger } from '@axiomhq/logger'; -import { AxiomFetchTransport, ConsoleTransport } from '@axiomhq/logger/transports'; +import { Logger } from '@axiomhq/logging'; +import { AxiomFetchTransport, ConsoleTransport } from '@axiomhq/logging/transports'; export const logger = new Logger({ transports: [ diff --git a/packages/logger/index.ts b/packages/logging/index.ts similarity index 100% rename from packages/logger/index.ts rename to packages/logging/index.ts diff --git a/packages/logger/package.json b/packages/logging/package.json similarity index 89% rename from packages/logger/package.json rename to packages/logging/package.json index 6a87deaa..57e70399 100644 --- a/packages/logger/package.json +++ b/packages/logging/package.json @@ -1,5 +1,5 @@ { - "name": "@axiomhq/logger", + "name": "@axiomhq/logging", "version": "0.0.1", "description": "", "main": "index.js", diff --git a/packages/logger/shared.ts b/packages/logging/shared.ts similarity index 100% rename from packages/logger/shared.ts rename to packages/logging/shared.ts diff --git a/packages/logger/transports/axiom-fetch.ts b/packages/logging/transports/axiom-fetch.ts similarity index 100% rename from packages/logger/transports/axiom-fetch.ts rename to packages/logging/transports/axiom-fetch.ts diff --git a/packages/logger/transports/axiom-js.ts b/packages/logging/transports/axiom-js.ts similarity index 100% rename from packages/logger/transports/axiom-js.ts rename to packages/logging/transports/axiom-js.ts diff --git a/packages/logger/transports/console.ts b/packages/logging/transports/console.ts similarity index 100% rename from packages/logger/transports/console.ts rename to packages/logging/transports/console.ts diff --git a/packages/logger/transports/fetch.ts b/packages/logging/transports/fetch.ts similarity index 100% rename from packages/logger/transports/fetch.ts rename to packages/logging/transports/fetch.ts diff --git a/packages/logger/transports/index.ts b/packages/logging/transports/index.ts similarity index 100% rename from packages/logger/transports/index.ts rename to packages/logging/transports/index.ts diff --git a/packages/logger/transports/proxy-transport.ts b/packages/logging/transports/proxy-transport.ts similarity index 100% rename from packages/logger/transports/proxy-transport.ts rename to packages/logging/transports/proxy-transport.ts diff --git a/packages/nextjs/instrumentation.ts b/packages/nextjs/instrumentation.ts index c75303a5..5c13604e 100644 --- a/packages/nextjs/instrumentation.ts +++ b/packages/nextjs/instrumentation.ts @@ -1,5 +1,5 @@ import { onRequestError } from '@/instrumentation'; -import { Logger } from '@axiomhq/logger'; +import { Logger } from '@axiomhq/logging'; import { Instrumentation } from 'next'; export const transformOnRequestError = ( diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 72ef24a6..8188960e 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -10,7 +10,7 @@ "author": "", "license": "ISC", "dependencies": { - "@axiomhq/logger": "workspace:^" + "@axiomhq/logging": "workspace:^" }, "devDependencies": { "next": "15.1.4" diff --git a/packages/nextjs/proxyRouteHandler.ts b/packages/nextjs/proxyRouteHandler.ts index b8b280f7..51d2bde8 100644 --- a/packages/nextjs/proxyRouteHandler.ts +++ b/packages/nextjs/proxyRouteHandler.ts @@ -1,4 +1,4 @@ -import { LogEvent, Logger } from '@axiomhq/logger'; +import { LogEvent, Logger } from '@axiomhq/logging'; import { NextRequest } from 'next/server'; export const createProxyRouteHandler = (logger: Logger) => { diff --git a/packages/nextjs/routeHandler.ts b/packages/nextjs/routeHandler.ts index b09104b5..1d85b128 100644 --- a/packages/nextjs/routeHandler.ts +++ b/packages/nextjs/routeHandler.ts @@ -1,4 +1,4 @@ -import { Logger } from '@axiomhq/logger'; +import { Logger } from '@axiomhq/logging'; import { isRedirectError } from 'next/dist/client/components/redirect-error'; import { isHTTPAccessFallbackError } from 'next/dist/client/components/http-access-fallback/http-access-fallback'; import * as next from 'next/server'; diff --git a/packages/react/index.ts b/packages/react/index.ts index d3b794b4..22f77168 100644 --- a/packages/react/index.ts +++ b/packages/react/index.ts @@ -1,4 +1,4 @@ -import { Logger } from '@axiomhq/logger'; +import { Logger } from '@axiomhq/logging'; import { createUseLogger } from './use-logger'; export const createClientSideHelpers = (logger: Logger) => { diff --git a/packages/react/package.json b/packages/react/package.json index cf39fe08..2b87d81d 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -10,7 +10,7 @@ "author": "", "license": "ISC", "dependencies": { - "@axiomhq/logger": "workspace:*", + "@axiomhq/logging": "workspace:*", "@types/react": "^19", "@types/react-dom": "^19", "react": "^19.0.0", diff --git a/packages/react/use-logger.ts b/packages/react/use-logger.ts index 755259d0..522c59f8 100644 --- a/packages/react/use-logger.ts +++ b/packages/react/use-logger.ts @@ -1,4 +1,4 @@ -import { Logger } from '@axiomhq/logger'; +import { Logger } from '@axiomhq/logging'; import { useEffect, useState } from 'react'; export function createUseLogger(logger: Logger) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c94e1fea..1253379b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -103,9 +103,9 @@ importers: examples/nextjs: dependencies: - '@axiomhq/logger': + '@axiomhq/logging': specifier: workspace:* - version: link:../../packages/logger + version: link:../../packages/logging '@axiomhq/nextjs': specifier: workspace:^ version: link:../../packages/nextjs @@ -279,7 +279,7 @@ importers: specifier: ^2.6.2 version: 2.6.2(@types/node@20.14.2)(typescript@5.4.5) - packages/logger: + packages/logging: dependencies: '@axiomhq/js': specifier: ^1.3.1 @@ -287,9 +287,9 @@ importers: packages/nextjs: dependencies: - '@axiomhq/logger': + '@axiomhq/logging': specifier: workspace:^ - version: link:../logger + version: link:../logging devDependencies: next: specifier: 15.1.4 @@ -310,9 +310,9 @@ importers: packages/react: dependencies: - '@axiomhq/logger': + '@axiomhq/logging': specifier: workspace:* - version: link:../logger + version: link:../logging '@types/react': specifier: ^19 version: 19.0.7 From f6baf4ac88617331e19fea7f1526980cd8d1e2ec Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 21 Jan 2025 14:50:20 +0000 Subject: [PATCH 15/71] chore: add build step --- examples/nextjs/package.json | 3 +- packages/logging/package.json | 32 +- packages/logging/{ => src}/index.ts | 7 +- packages/logging/{ => src}/shared.ts | 5 +- .../{ => src}/transports/axiom-fetch.ts | 0 .../logging/{ => src}/transports/axiom-js.ts | 0 .../logging/{ => src}/transports/console.ts | 0 .../logging/{ => src}/transports/fetch.ts | 0 .../logging/{ => src}/transports/index.ts | 0 .../{ => src}/transports/proxy-transport.ts | 0 packages/logging/tsconfig.json | 16 + packages/logging/vite.config.ts | 16 + packages/nextjs/package.json | 33 +- packages/nextjs/{ => src}/index.ts | 0 packages/nextjs/{ => src}/instrumentation.ts | 1 - packages/nextjs/{ => src}/middleware.ts | 0 .../nextjs/{ => src}/proxyRouteHandler.ts | 0 packages/nextjs/{ => src}/routeHandler.ts | 0 packages/nextjs/tsconfig.json | 17 + packages/nextjs/vite.config.ts | 14 + packages/react/package.json | 33 +- packages/react/{ => src}/index.ts | 0 packages/react/{ => src}/use-logger.ts | 0 packages/react/tsconfig.json | 16 + packages/react/vite.config.ts | 14 + pnpm-lock.yaml | 1219 ++++++++++++++++- 26 files changed, 1410 insertions(+), 16 deletions(-) rename packages/logging/{ => src}/index.ts (95%) rename packages/logging/{ => src}/shared.ts (76%) rename packages/logging/{ => src}/transports/axiom-fetch.ts (100%) rename packages/logging/{ => src}/transports/axiom-js.ts (100%) rename packages/logging/{ => src}/transports/console.ts (100%) rename packages/logging/{ => src}/transports/fetch.ts (100%) rename packages/logging/{ => src}/transports/index.ts (100%) rename packages/logging/{ => src}/transports/proxy-transport.ts (100%) create mode 100644 packages/logging/tsconfig.json create mode 100644 packages/logging/vite.config.ts rename packages/nextjs/{ => src}/index.ts (100%) rename packages/nextjs/{ => src}/instrumentation.ts (94%) rename packages/nextjs/{ => src}/middleware.ts (100%) rename packages/nextjs/{ => src}/proxyRouteHandler.ts (100%) rename packages/nextjs/{ => src}/routeHandler.ts (100%) create mode 100644 packages/nextjs/tsconfig.json create mode 100644 packages/nextjs/vite.config.ts rename packages/react/{ => src}/index.ts (100%) rename packages/react/{ => src}/use-logger.ts (100%) create mode 100644 packages/react/tsconfig.json create mode 100644 packages/react/vite.config.ts diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index 83a37e0b..d08851bd 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -25,6 +25,7 @@ "eslint-config-next": "15.1.4", "postcss": "^8", "tailwindcss": "^3.4.1", - "typescript": "^5" + "typescript": "^5", + "vite": "^5.2.14" } } diff --git a/packages/logging/package.json b/packages/logging/package.json index 57e70399..53057aca 100644 --- a/packages/logging/package.json +++ b/packages/logging/package.json @@ -2,8 +2,34 @@ "name": "@axiomhq/logging", "version": "0.0.1", "description": "", - "main": "index.js", + "type": "module", + "main": "dist/cjs/index.cjs", + "module": "dist/esm/index.js", + "types": "dist/esm/index.d.ts", + "exports": { + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/cjs/index.d.cts", + "default": "./dist/cjs/index.cjs" + } + }, + "./transports": { + "import": { + "types": "./dist/esm/transports/index.d.ts", + "default": "./dist/esm/transports/index.js" + }, + "require": { + "types": "./dist/cjs/transports/index.d.cts", + "default": "./dist/cjs/transports/index.cjs" + } + } + }, "scripts": { + "build": "vite build", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], @@ -11,5 +37,9 @@ "license": "ISC", "dependencies": { "@axiomhq/js": "^1.3.1" + }, + "devDependencies": { + "@tanstack/config": "^0.16.0", + "vite": "^5.2.14" } } diff --git a/packages/logging/index.ts b/packages/logging/src/index.ts similarity index 95% rename from packages/logging/index.ts rename to packages/logging/src/index.ts index b0c2d4c4..f832fbf5 100644 --- a/packages/logging/index.ts +++ b/packages/logging/src/index.ts @@ -1,5 +1,8 @@ import { Version } from './shared'; -import { Transport } from './transports'; +export interface Transport { + log: (logs: LogEvent[]) => Promise | void; + flush: () => Promise | void; +} const LOG_LEVEL = 'info'; @@ -96,7 +99,7 @@ export class Logger { _time: new Date(Date.now()).toISOString(), fields: this.config.args || {}, '@app': { - 'axiom-logging-version': Version, + 'axiom-logging-version': Version ?? 'unknown', }, }; diff --git a/packages/logging/shared.ts b/packages/logging/src/shared.ts similarity index 76% rename from packages/logging/shared.ts rename to packages/logging/src/shared.ts index dfc110a7..4704a251 100644 --- a/packages/logging/shared.ts +++ b/packages/logging/src/shared.ts @@ -1,8 +1,7 @@ -import packageJson from './package.json'; - -export const Version = packageJson.version; +export const Version = __PACKAGE_VERSION__; declare global { + const __PACKAGE_VERSION__: string; var WorkerGlobalScope: any; } diff --git a/packages/logging/transports/axiom-fetch.ts b/packages/logging/src/transports/axiom-fetch.ts similarity index 100% rename from packages/logging/transports/axiom-fetch.ts rename to packages/logging/src/transports/axiom-fetch.ts diff --git a/packages/logging/transports/axiom-js.ts b/packages/logging/src/transports/axiom-js.ts similarity index 100% rename from packages/logging/transports/axiom-js.ts rename to packages/logging/src/transports/axiom-js.ts diff --git a/packages/logging/transports/console.ts b/packages/logging/src/transports/console.ts similarity index 100% rename from packages/logging/transports/console.ts rename to packages/logging/src/transports/console.ts diff --git a/packages/logging/transports/fetch.ts b/packages/logging/src/transports/fetch.ts similarity index 100% rename from packages/logging/transports/fetch.ts rename to packages/logging/src/transports/fetch.ts diff --git a/packages/logging/transports/index.ts b/packages/logging/src/transports/index.ts similarity index 100% rename from packages/logging/transports/index.ts rename to packages/logging/src/transports/index.ts diff --git a/packages/logging/transports/proxy-transport.ts b/packages/logging/src/transports/proxy-transport.ts similarity index 100% rename from packages/logging/transports/proxy-transport.ts rename to packages/logging/src/transports/proxy-transport.ts diff --git a/packages/logging/tsconfig.json b/packages/logging/tsconfig.json new file mode 100644 index 00000000..35d92df0 --- /dev/null +++ b/packages/logging/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "baseUrl": ".", + "rootDir": "./src", + "outDir": "dist/esm", + "declarationDir": "dist/esm/types", + "resolveJsonModule": true, + "declarationMap": true, + "emitDeclarationOnly": true + }, + "include": ["src/**/*"] +} diff --git a/packages/logging/vite.config.ts b/packages/logging/vite.config.ts new file mode 100644 index 00000000..e0680af2 --- /dev/null +++ b/packages/logging/vite.config.ts @@ -0,0 +1,16 @@ +import { defineConfig, mergeConfig } from 'vite'; +import { tanstackViteConfig } from '@tanstack/config/vite'; + +const config = defineConfig({ + define: { + __PACKAGE_VERSION__: JSON.stringify(process.env.npm_package_version), + }, +}); + +export default mergeConfig( + config, + tanstackViteConfig({ + entry: ['./src/index.ts', './src/transports/index.ts'], + srcDir: './src', + }), +); diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 8188960e..9cb39182 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -2,8 +2,35 @@ "name": "@axiomhq/nextjs", "version": "0.0.1", "description": "", - "main": "index.js", + "type": "module", + "main": "dist/cjs/index.cjs", + "module": "dist/esm/index.js", + "types": "dist/esm/index.d.ts", + "exports": { + "./transports": { + "import": { + "types": "./dist/esm/transports/index.d.ts", + "default": "./dist/esm/transports/index.js" + }, + "require": { + "types": "./dist/cjs/transports/index.d.cts", + "default": "./dist/cjs/transports/index.cjs" + } + }, + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/cjs/index.d.cts", + "default": "./dist/cjs/index.cjs" + } + }, + "./package.json": "./package.json" + }, "scripts": { + "build": "vite build", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], @@ -13,7 +40,9 @@ "@axiomhq/logging": "workspace:^" }, "devDependencies": { - "next": "15.1.4" + "@tanstack/config": "^0.16.0", + "next": "15.1.4", + "vite": "^5.2.14" }, "peerDependencies": { "next": "15.1.4" diff --git a/packages/nextjs/index.ts b/packages/nextjs/src/index.ts similarity index 100% rename from packages/nextjs/index.ts rename to packages/nextjs/src/index.ts diff --git a/packages/nextjs/instrumentation.ts b/packages/nextjs/src/instrumentation.ts similarity index 94% rename from packages/nextjs/instrumentation.ts rename to packages/nextjs/src/instrumentation.ts index 5c13604e..ade7f9fe 100644 --- a/packages/nextjs/instrumentation.ts +++ b/packages/nextjs/src/instrumentation.ts @@ -1,4 +1,3 @@ -import { onRequestError } from '@/instrumentation'; import { Logger } from '@axiomhq/logging'; import { Instrumentation } from 'next'; diff --git a/packages/nextjs/middleware.ts b/packages/nextjs/src/middleware.ts similarity index 100% rename from packages/nextjs/middleware.ts rename to packages/nextjs/src/middleware.ts diff --git a/packages/nextjs/proxyRouteHandler.ts b/packages/nextjs/src/proxyRouteHandler.ts similarity index 100% rename from packages/nextjs/proxyRouteHandler.ts rename to packages/nextjs/src/proxyRouteHandler.ts diff --git a/packages/nextjs/routeHandler.ts b/packages/nextjs/src/routeHandler.ts similarity index 100% rename from packages/nextjs/routeHandler.ts rename to packages/nextjs/src/routeHandler.ts diff --git a/packages/nextjs/tsconfig.json b/packages/nextjs/tsconfig.json new file mode 100644 index 00000000..fcd3e214 --- /dev/null +++ b/packages/nextjs/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "baseUrl": ".", + "rootDir": "./src", + "outDir": "dist/esm", + "declarationDir": "dist/esm/types", + "resolveJsonModule": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "useDefineForClassFields": false + }, + "include": ["src/**/*"] +} diff --git a/packages/nextjs/vite.config.ts b/packages/nextjs/vite.config.ts new file mode 100644 index 00000000..02be5034 --- /dev/null +++ b/packages/nextjs/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig, mergeConfig } from 'vite'; +import { tanstackViteConfig } from '@tanstack/config/vite'; + +const config = defineConfig({ + // Framework plugins, vitest config, etc. +}); + +export default mergeConfig( + config, + tanstackViteConfig({ + entry: './src/index.ts', + srcDir: './src', + }), +); diff --git a/packages/react/package.json b/packages/react/package.json index 2b87d81d..018f09f0 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -2,8 +2,35 @@ "name": "@axiomhq/react", "version": "0.0.1", "description": "", - "main": "index.js", + "type": "module", + "main": "dist/cjs/index.cjs", + "module": "dist/esm/index.js", + "types": "dist/esm/index.d.ts", + "exports": { + "./transports": { + "import": { + "types": "./dist/esm/transports/index.d.ts", + "default": "./dist/esm/transports/index.js" + }, + "require": { + "types": "./dist/cjs/transports/index.d.cts", + "default": "./dist/cjs/transports/index.cjs" + } + }, + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/cjs/index.d.cts", + "default": "./dist/cjs/index.cjs" + } + }, + "./package.json": "./package.json" + }, "scripts": { + "build": "vite build", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], @@ -17,5 +44,9 @@ "react-dom": "^19.0.0", "use-deep-compare": "^1.3.0", "web-vitals": "^4.2.4" + }, + "devDependencies": { + "@tanstack/config": "^0.16.0", + "vite": "^5.2.14" } } diff --git a/packages/react/index.ts b/packages/react/src/index.ts similarity index 100% rename from packages/react/index.ts rename to packages/react/src/index.ts diff --git a/packages/react/use-logger.ts b/packages/react/src/use-logger.ts similarity index 100% rename from packages/react/use-logger.ts rename to packages/react/src/use-logger.ts diff --git a/packages/react/tsconfig.json b/packages/react/tsconfig.json new file mode 100644 index 00000000..35d92df0 --- /dev/null +++ b/packages/react/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "baseUrl": ".", + "rootDir": "./src", + "outDir": "dist/esm", + "declarationDir": "dist/esm/types", + "resolveJsonModule": true, + "declarationMap": true, + "emitDeclarationOnly": true + }, + "include": ["src/**/*"] +} diff --git a/packages/react/vite.config.ts b/packages/react/vite.config.ts new file mode 100644 index 00000000..02be5034 --- /dev/null +++ b/packages/react/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig, mergeConfig } from 'vite'; +import { tanstackViteConfig } from '@tanstack/config/vite'; + +const config = defineConfig({ + // Framework plugins, vitest config, etc. +}); + +export default mergeConfig( + config, + tanstackViteConfig({ + entry: './src/index.ts', + srcDir: './src', + }), +); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1253379b..d6893447 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -139,7 +139,7 @@ importers: version: 9.18.0(jiti@2.4.2) eslint-config-next: specifier: 15.1.4 - version: 15.1.4(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5) + version: 15.1.4(eslint-plugin-import-x@4.6.1(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5))(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5) postcss: specifier: ^8 version: 8.4.38 @@ -149,6 +149,9 @@ importers: typescript: specifier: ^5 version: 5.4.5 + vite: + specifier: ^5.2.14 + version: 5.2.14(@types/node@20.14.2) examples/node: dependencies: @@ -284,6 +287,13 @@ importers: '@axiomhq/js': specifier: ^1.3.1 version: 1.3.1 + devDependencies: + '@tanstack/config': + specifier: ^0.16.0 + version: 0.16.0(@types/node@20.14.2)(esbuild@0.20.2)(eslint@9.18.0(jiti@2.4.2))(rollup@4.22.4)(typescript@5.4.5)(vite@5.2.14(@types/node@20.14.2)) + vite: + specifier: ^5.2.14 + version: 5.2.14(@types/node@20.14.2) packages/nextjs: dependencies: @@ -291,9 +301,15 @@ importers: specifier: workspace:^ version: link:../logging devDependencies: + '@tanstack/config': + specifier: ^0.16.0 + version: 0.16.0(@types/node@20.14.2)(esbuild@0.20.2)(eslint@9.18.0(jiti@2.4.2))(rollup@4.22.4)(typescript@5.4.5)(vite@5.2.14(@types/node@20.14.2)) next: specifier: 15.1.4 version: 15.1.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + vite: + specifier: ^5.2.14 + version: 5.2.14(@types/node@20.14.2) packages/pino: dependencies: @@ -331,6 +347,13 @@ importers: web-vitals: specifier: ^4.2.4 version: 4.2.4 + devDependencies: + '@tanstack/config': + specifier: ^0.16.0 + version: 0.16.0(@types/node@20.14.2)(esbuild@0.20.2)(eslint@9.18.0(jiti@2.4.2))(rollup@4.22.4)(typescript@5.4.5)(vite@5.2.14(@types/node@20.14.2)) + vite: + specifier: ^5.2.14 + version: 5.2.14(@types/node@20.14.2) packages/winston: dependencies: @@ -395,6 +418,14 @@ packages: resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} engines: {node: '>=0.1.90'} + '@commitlint/parse@19.5.0': + resolution: {integrity: sha512-cZ/IxfAlfWYhAQV0TwcbdR1Oc0/r0Ik1GEessDJ3Lbuma/MRO8FRQX76eurcXtmhJC//rj52ZSZuXUg0oIX0Fw==} + engines: {node: '>=v18'} + + '@commitlint/types@19.5.0': + resolution: {integrity: sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==} + engines: {node: '>=v18'} + '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -586,6 +617,9 @@ packages: resolution: {integrity: sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@gerrit0/mini-shiki@1.27.2': + resolution: {integrity: sha512-GeWyHz8ao2gBiUW4OJnQDxXQnFgZQwwQk05t/CVVgNBN7/rK8XZ7xY6YhLVv9tH3VppWWmr9DCl3MwemB/i+Og==} + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -758,12 +792,28 @@ packages: '@jridgewell/sourcemap-codec@1.4.15': resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@kwsites/file-exists@1.1.1': + resolution: {integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==} + + '@kwsites/promise-deferred@1.1.1': + resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==} + + '@microsoft/api-extractor-model@7.29.4': + resolution: {integrity: sha512-LHOMxmT8/tU1IiiiHOdHFF83Qsi+V8d0kLfscG4EvQE9cafiR8blOYr8SfkQKWB1wgEilQgXJX3MIA4vetDLZw==} + + '@microsoft/api-extractor@7.47.4': + resolution: {integrity: sha512-HKm+P4VNzWwvq1Ey+Jfhhj/3MjsD+ka2hbt8L5AcRM95lu1MFOYnz3XlU7Gr79Q/ZhOb7W/imAKeYrOI0bFydg==} + hasBin: true + '@microsoft/tsdoc-config@0.17.1': resolution: {integrity: sha512-UtjIFe0C6oYgTnad4q1QP4qXwLhe6tIpNTRStJ2RZEPIkqQPREAwE5spzVxsdn9UaEMUqhh0AqSx3X4nWAKXWw==} @@ -1111,9 +1161,46 @@ packages: '@rushstack/eslint-patch@1.10.5': resolution: {integrity: sha512-kkKUDVlII2DQiKy7UstOR1ErJP8kUKAQ4oa+SQtM0K+lPdmmjj0YnnxBgtTVYH7mUKtbsxeFC9y0AmK7Yb78/A==} + '@rushstack/node-core-library@5.5.1': + resolution: {integrity: sha512-ZutW56qIzH8xIOlfyaLQJFx+8IBqdbVCZdnj+XT1MorQ1JqqxHse8vbCpEM+2MjsrqcbxcgDIbfggB1ZSQ2A3g==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/rig-package@0.5.3': + resolution: {integrity: sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==} + + '@rushstack/terminal@0.13.3': + resolution: {integrity: sha512-fc3zjXOw8E0pXS5t9vTiIPx9gHA0fIdTXsu9mT4WbH+P3mYvnrX0iAQ5a6NvyK1+CqYWBTw/wVNx7SDJkI+WYQ==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/ts-command-line@4.22.3': + resolution: {integrity: sha512-edMpWB3QhFFZ4KtSzS8WNjBgR4PXPPOVrOHMbb7kNpmQ1UFS9HdVtjCXg1H5fG+xYAbeE+TMPcVPUyX2p84STA==} + + '@shikijs/engine-oniguruma@1.29.1': + resolution: {integrity: sha512-gSt2WhLNgEeLstcweQOSp+C+MhOpTsgdNXRqr3zP6M+BUBZ8Md9OU2BYwUYsALBxHza7hwaIWtFHjQ/aOOychw==} + + '@shikijs/types@1.29.1': + resolution: {integrity: sha512-aBqAuhYRp5vSir3Pc9+QPu9WESBOjUo03ao0IHLC4TyTioSsp/SkbAZSrIH4ghYYC1T1KTEpRSBa83bas4RnPA==} + + '@shikijs/vscode-textmate@10.0.1': + resolution: {integrity: sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg==} + '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + '@stylistic/eslint-plugin-js@2.13.0': + resolution: {integrity: sha512-GPPDK4+fcbsQD58a3abbng2Dx+jBoxM5cnYjBM4T24WFZRZdlNSKvR19TxP8CPevzMOodQ9QVzNeqWvMXzfJRA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: '>=8.40.0' + '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} @@ -1123,6 +1210,11 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@tanstack/config@0.16.0': + resolution: {integrity: sha512-1uX8OD0tvAeyTQMRje3T1Z0TbMGaYoFl+FajisIISNvVPH1dLr5947vPJbKyTfthltWBtkcnlw4cql3e3VlPYQ==} + engines: {node: '>=18'} + hasBin: true + '@tsconfig/node10@1.0.11': resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} @@ -1135,21 +1227,33 @@ packages: '@tsconfig/node16@1.0.4': resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + '@types/argparse@1.0.38': + resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + '@types/chai-subset@1.3.5': resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} '@types/chai@4.3.16': resolution: {integrity: sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==} + '@types/conventional-commits-parser@5.0.1': + resolution: {integrity: sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==} + '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/doctrine@0.0.9': + resolution: {integrity: sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==} + '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/istanbul-lib-coverage@2.0.6': resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} @@ -1185,6 +1289,9 @@ packages: '@types/triple-beam@1.3.5': resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/uuid@8.3.4': resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} @@ -1273,12 +1380,21 @@ packages: '@volar/language-core@2.3.0': resolution: {integrity: sha512-pvhL24WUh3VDnv7Yw5N1sjhPtdx7q9g+Wl3tggmnkMcyK8GcCNElF2zHiKznryn0DiUGk+eez/p2qQhz+puuHw==} + '@volar/language-core@2.4.11': + resolution: {integrity: sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg==} + '@volar/source-map@2.3.0': resolution: {integrity: sha512-G/228aZjAOGhDjhlyZ++nDbKrS9uk+5DMaEstjvzglaAw7nqtDyhnQAsYzUg6BMP9BtwZ59RIw5HGePrutn00Q==} + '@volar/source-map@2.4.11': + resolution: {integrity: sha512-ZQpmafIGvaZMn/8iuvCFGrW3smeqkq/IIh9F1SdSx9aUl0J4Iurzd6/FhmjNO5g2ejF3rT45dKskgXWiofqlZQ==} + '@volar/typescript@2.3.0': resolution: {integrity: sha512-PtUwMM87WsKVeLJN33GSTUjBexlKfKgouWlOUIv7pjrOnTwhXHZNSmpc312xgXdTjQPpToK6KXSIcKu9sBQ5LQ==} + '@volar/typescript@2.4.11': + resolution: {integrity: sha512-2DT+Tdh88Spp5PyPbqhyoYavYCPDsqbHLFwcUI9K1NlY1YgUJvujGdrqUp0zWxnW7KWNTr3xSpMuv2WnaTKDAw==} + '@vue/compiler-core@3.4.27': resolution: {integrity: sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg==} @@ -1291,6 +1407,9 @@ packages: '@vue/compiler-ssr@3.4.27': resolution: {integrity: sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw==} + '@vue/compiler-vue2@2.7.16': + resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + '@vue/language-core@2.0.21': resolution: {integrity: sha512-vjs6KwnCK++kIXT+eI63BGpJHfHNVJcUCr3RnvJsccT3vbJnZV5IhHR2puEkoOkIbDdp0Gqi1wEnv3hEd3WsxQ==} peerDependencies: @@ -1299,6 +1418,14 @@ packages: typescript: optional: true + '@vue/language-core@2.0.29': + resolution: {integrity: sha512-o2qz9JPjhdoVj8D2+9bDXbaI4q2uZTHQA/dbyZT4Bj1FR9viZxDJnLcKVHfxdn6wsOzRgpqIzJEEmSSvgMvDTQ==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + '@vue/reactivity@3.4.27': resolution: {integrity: sha512-kK0g4NknW6JX2yySLpsm2jlunZJl2/RJGZ0H9ddHdfBVHcNzxmQ0sS0b09ipmBoQpY8JM2KmUw+a6sO8Zo+zIA==} @@ -1316,6 +1443,10 @@ packages: '@vue/shared@3.4.27': resolution: {integrity: sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==} + JSONStream@1.3.5: + resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} + hasBin: true + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -1343,12 +1474,31 @@ packages: resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} engines: {node: '>= 14'} + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} ajv@8.12.0: resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + ajv@8.13.0: + resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==} + ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} @@ -1386,6 +1536,9 @@ packages: arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -1401,10 +1554,21 @@ packages: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} + array-each@1.0.1: + resolution: {integrity: sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==} + engines: {node: '>=0.10.0'} + + array-ify@1.0.0: + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + array-includes@3.1.8: resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} engines: {node: '>= 0.4'} + array-slice@1.1.0: + resolution: {integrity: sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==} + engines: {node: '>=0.10.0'} + array.prototype.findlast@1.2.5: resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} engines: {node: '>= 0.4'} @@ -1527,6 +1691,10 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + check-error@1.0.3: resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} @@ -1575,10 +1743,20 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + engines: {node: '>=18'} + commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} + compare-func@2.0.0: + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + + compare-versions@6.1.1: + resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} + computeds@0.0.1: resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==} @@ -1588,6 +1766,18 @@ packages: confbox@0.1.7: resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + conventional-changelog-angular@7.0.0: + resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==} + engines: {node: '>=16'} + + conventional-commits-parser@5.0.0: + resolution: {integrity: sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==} + engines: {node: '>=16'} + hasBin: true + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -1700,6 +1890,10 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + detect-file@1.0.0: + resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==} + engines: {node: '>=0.10.0'} + detect-libc@2.0.3: resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} @@ -1722,6 +1916,14 @@ packages: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dot-prop@5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + dotenv@16.0.3: resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} engines: {node: '>=12'} @@ -1797,6 +1999,11 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} + esbuild-register@3.6.0: + resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} + peerDependencies: + esbuild: '>=0.12 <1' + esbuild@0.20.2: resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} engines: {node: '>=12'} @@ -1810,6 +2017,12 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + eslint-compat-utils@0.5.1: + resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + eslint-config-next@15.1.4: resolution: {integrity: sha512-u9+7lFmfhKNgGjhQ9tBeyCFsPJyq0SvGioMJBngPC7HXUpR0U+ckEwQR48s7TrRNHra1REm6evGL2ie38agALg==} peerDependencies: @@ -1862,6 +2075,18 @@ packages: eslint-import-resolver-webpack: optional: true + eslint-plugin-es-x@7.8.0: + resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '>=8' + + eslint-plugin-import-x@4.6.1: + resolution: {integrity: sha512-wluSUifMIb7UfwWXqx7Yx0lE/SGCcGXECLx/9bCmbY2nneLwvAZ4vkd1IXDjPKFvdcdUgr1BaRnaRpx3k2+Pfw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + eslint-plugin-import@2.31.0: resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==} engines: {node: '>=4'} @@ -1878,6 +2103,12 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + eslint-plugin-n@17.15.1: + resolution: {integrity: sha512-KFw7x02hZZkBdbZEFQduRGH4VkIH4MW97ClsbAM4Y4E6KguBJWGfWG1P4HEIpZk2bkoWf0bojpnjNAhYQP8beA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: '>=8.23.0' + eslint-plugin-react-hooks@5.1.0: resolution: {integrity: sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==} engines: {node: '>=10'} @@ -1898,6 +2129,10 @@ packages: peerDependencies: eslint: '>6.6.0' + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-scope@8.2.0: resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1924,6 +2159,10 @@ packages: resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + esquery@1.5.0: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} @@ -1951,6 +2190,13 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + expand-tilde@2.0.2: + resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==} + engines: {node: '>=0.10.0'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -1996,6 +2242,18 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} + findup-sync@5.0.0: + resolution: {integrity: sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==} + engines: {node: '>= 10.13.0'} + + fined@2.0.0: + resolution: {integrity: sha512-OFRzsL6ZMHz5s0JrsEr+TpdGNCtrVtnuG3x1yzGNiQHT0yaDnXAj8V/lWcpJVrnoDpcwXcASxAZYbuXda2Y82A==} + engines: {node: '>= 10.13.0'} + + flagged-respawn@2.0.0: + resolution: {integrity: sha512-Gq/a6YCi8zexmGHMuJwahTGzXlAZAOsbCVKduWXC6TlLCjjFRlExMJc4GC2NYPYZ0r/brw9P7CpRgQmlPVeOoA==} + engines: {node: '>= 10.13.0'} + flat-cache@4.0.1: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} @@ -2009,6 +2267,14 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + for-in@1.0.2: + resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} + engines: {node: '>=0.10.0'} + + for-own@1.0.0: + resolution: {integrity: sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==} + engines: {node: '>=0.10.0'} + foreground-child@3.3.0: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} @@ -2017,6 +2283,10 @@ packages: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} engines: {node: '>= 6'} + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -2085,6 +2355,14 @@ packages: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported + global-modules@1.0.0: + resolution: {integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==} + engines: {node: '>=0.10.0'} + + global-prefix@1.0.2: + resolution: {integrity: sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==} + engines: {node: '>=0.10.0'} + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -2097,6 +2375,9 @@ packages: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} @@ -2155,6 +2436,10 @@ packages: headers-polyfill@4.0.3: resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + homedir-polyfill@1.0.3: + resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} + engines: {node: '>=0.10.0'} + html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} @@ -2181,10 +2466,18 @@ packages: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -2196,6 +2489,9 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + internal-slot@1.0.7: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} @@ -2204,6 +2500,14 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + interpret@3.1.1: + resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} + engines: {node: '>=10.13.0'} + + is-absolute@1.0.0: + resolution: {integrity: sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==} + engines: {node: '>=0.10.0'} + is-array-buffer@3.0.4: resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} engines: {node: '>= 0.4'} @@ -2314,6 +2618,14 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} @@ -2325,6 +2637,10 @@ packages: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} + is-relative@1.0.0: + resolution: {integrity: sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==} + engines: {node: '>=0.10.0'} + is-set@2.0.3: resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} engines: {node: '>= 0.4'} @@ -2357,6 +2673,10 @@ packages: resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} engines: {node: '>= 0.4'} + is-text-path@2.0.0: + resolution: {integrity: sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==} + engines: {node: '>=8'} + is-typed-array@1.1.13: resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} engines: {node: '>= 0.4'} @@ -2365,6 +2685,10 @@ packages: resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} + is-unc-path@1.0.0: + resolution: {integrity: sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==} + engines: {node: '>=0.10.0'} + is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -2380,12 +2704,20 @@ packages: resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} engines: {node: '>= 0.4'} + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isobject@3.0.1: + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} + istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -2452,6 +2784,16 @@ packages: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -2459,6 +2801,9 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + kuler@2.0.0: resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} @@ -2473,6 +2818,10 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + liftoff@5.0.0: + resolution: {integrity: sha512-a5BQjbCHnB+cy+gsro8lXJ4kZluzOijzJ1UVVfyJYZC+IP2pLv1h4+aysQeKuTmyO8NAqfyQAk4HWaP/HjcKTg==} + engines: {node: '>=10.13.0'} + lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} @@ -2480,10 +2829,17 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + local-pkg@0.4.3: resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} engines: {node: '>=14'} + local-pkg@0.5.1: + resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==} + engines: {node: '>=14'} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -2491,6 +2847,9 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + logform@2.6.0: resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==} engines: {node: '>= 12.0.0'} @@ -2505,9 +2864,19 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + lunr@2.3.9: + resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} + magic-string@0.30.10: resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -2515,10 +2884,25 @@ packages: make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + map-cache@0.2.2: + resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} + engines: {node: '>=0.10.0'} + + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + + meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} + engines: {node: '>=16.10'} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -2539,6 +2923,9 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + minimatch@3.0.8: + resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -2546,6 +2933,10 @@ packages: resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} engines: {node: '>=16 || 14 >=14.17'} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -2556,6 +2947,9 @@ packages: mlly@1.7.1: resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + mlly@1.7.4: + resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} + mrmime@2.0.0: resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} engines: {node: '>=10'} @@ -2675,6 +3069,10 @@ packages: resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} engines: {node: '>= 0.4'} + object.defaults@1.1.0: + resolution: {integrity: sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==} + engines: {node: '>=0.10.0'} + object.entries@1.1.8: resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} engines: {node: '>= 0.4'} @@ -2687,6 +3085,10 @@ packages: resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} engines: {node: '>= 0.4'} + object.pick@1.3.0: + resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==} + engines: {node: '>=0.10.0'} + object.values@1.2.0: resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} engines: {node: '>= 0.4'} @@ -2735,6 +3137,14 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-filepath@1.0.2: + resolution: {integrity: sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==} + engines: {node: '>=0.8'} + + parse-passwd@1.0.0: + resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==} + engines: {node: '>=0.10.0'} + parse5@7.1.2: resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} @@ -2756,6 +3166,14 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-root-regex@0.1.2: + resolution: {integrity: sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==} + engines: {node: '>=0.10.0'} + + path-root@0.1.1: + resolution: {integrity: sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==} + engines: {node: '>=0.10.0'} + path-scurry@1.11.1: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} @@ -2766,6 +3184,9 @@ packages: pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + pathe@2.0.2: + resolution: {integrity: sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==} + pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} @@ -2803,6 +3224,9 @@ packages: pkg-types@1.1.1: resolution: {integrity: sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==} + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -2882,6 +3306,10 @@ packages: psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -2938,6 +3366,10 @@ packages: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} + rechoir@0.8.0: + resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} + engines: {node: '>= 10.13.0'} + reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -2965,6 +3397,10 @@ packages: requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + resolve-dir@1.0.1: + resolution: {integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==} + engines: {node: '>=0.10.0'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -2984,6 +3420,11 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rollup-plugin-preserve-directives@0.4.0: + resolution: {integrity: sha512-gx4nBxYm5BysmEQS+e2tAMrtFxrGvk+Pe5ppafRibQi0zlW7VYAbEGk6IKDw9sJGPdFWgVTE0o4BU4cdG0Fylg==} + peerDependencies: + rollup: 2.x || 3.x || 4.x + rollup@4.18.0: resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -3047,6 +3488,11 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true + semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + semver@7.6.2: resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} engines: {node: '>=10'} @@ -3108,6 +3554,9 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-git@3.27.0: + resolution: {integrity: sha512-ivHoFS9Yi9GY49ogc6/YAi3Fl9ROnF4VyubNylgCkA+RVqLaKWnDSzXOVzya8csELIaWaYNutsEuAhZrtOjozA==} + simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} @@ -3134,6 +3583,9 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + stable-hash@0.0.4: resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==} @@ -3157,6 +3609,10 @@ packages: strict-event-emitter@0.5.1: resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -3239,6 +3695,10 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -3259,6 +3719,10 @@ packages: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} + text-extensions@2.4.0: + resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==} + engines: {node: '>=8'} + text-hex@1.0.0: resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} @@ -3272,6 +3736,9 @@ packages: thread-stream@3.0.2: resolution: {integrity: sha512-cBL4xF2A3lSINV4rD5tyqnKH4z/TgWPvT+NaVhJDSwK962oo/Ye7cHSMbDzwcu7tAE1SfU6Q4XtV6Hucmi6Hlw==} + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + tinybench@2.8.0: resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} @@ -3330,6 +3797,16 @@ packages: '@swc/wasm': optional: true + tsconfck@3.1.4: + resolution: {integrity: sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} @@ -3421,6 +3898,24 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} + typedoc-plugin-frontmatter@1.1.2: + resolution: {integrity: sha512-ySNFQRqKjEM3IobtoND22LUuhpNLwPzTiAxTlqjn5uRLn8k8nqItvxWnWWRzaKMi870ZCvrz4wJHuVg+yqysmw==} + peerDependencies: + typedoc-plugin-markdown: '>=4.3.0' + + typedoc-plugin-markdown@4.4.1: + resolution: {integrity: sha512-fx23nSCvewI9IR8lzIYtzDphETcgTDuxKcmHKGD4lo36oexC+B1k4NaCOY58Snqb4OlE8OXDAGVcQXYYuLRCNw==} + engines: {node: '>= 18'} + peerDependencies: + typedoc: 0.27.x + + typedoc@0.27.6: + resolution: {integrity: sha512-oBFRoh2Px6jFx366db0lLlihcalq/JzyCVp7Vaq1yphL/tbgx2e+bkpkCgJPunaPvPwoTOXSwasfklWHm7GfAw==} + engines: {node: '>= 18'} + hasBin: true + peerDependencies: + typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x + typescript-eslint@8.21.0: resolution: {integrity: sha512-txEKYY4XMKwPXxNkN8+AxAdX6iIJAPiJbHE/FpQccs/sxw8Lf26kqwC3cn0xkHlW8kEbLhkhCsjWuMveaY9Rxw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3428,14 +3923,25 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' + typescript@5.4.2: + resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==} + engines: {node: '>=14.17'} + hasBin: true + typescript@5.4.5: resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} engines: {node: '>=14.17'} hasBin: true + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + ufo@1.5.3: resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} + ufo@1.5.4: + resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} + unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} @@ -3443,13 +3949,25 @@ packages: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} + unc-path-regex@0.1.2: + resolution: {integrity: sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==} + engines: {node: '>=0.10.0'} + undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + universalify@0.2.0: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -3475,11 +3993,38 @@ packages: resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==} engines: {node: '>=10.12.0'} + v8flags@4.0.1: + resolution: {integrity: sha512-fcRLaS4H/hrZk9hYwbdRM35D0U8IYMfEClhXxCivOojl+yTRAZH3Zy2sSy6qVCiGbV9YAtPssP6jaChqC9vPCg==} + engines: {node: '>= 10.13.0'} + vite-node@0.34.6: resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} engines: {node: '>=v14.18.0'} hasBin: true + vite-plugin-dts@4.0.3: + resolution: {integrity: sha512-+xnTsaONwU2kV6zhRjtbRJSGN41uFR/whqmcb4k4fftLFDJElxthp0PP5Fq8gMeM9ytWMt1yk5gGgekLREWYQQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + typescript: '*' + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite-plugin-externalize-deps@0.8.0: + resolution: {integrity: sha512-MdC8kRNQ1ZjhUicU2HcqGVhL0UUFqv83Zp1JZdHjE82PoPR8wsSWZ3axpot7B6img3sW6g8shYJikE0CKA0chA==} + peerDependencies: + vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + + vite-tsconfig-paths@5.1.4: + resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + vite@5.2.14: resolution: {integrity: sha512-TFQLuwWLPms+NBNlh0D9LZQ+HXW471COABxw/9TEUBrjuHMo9BrYBPrN/SYAwIuVL+rLerycxiLT41t4f5MZpA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -3542,6 +4087,12 @@ packages: vscode-uri@3.0.8: resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} + vue-eslint-parser@9.4.3: + resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + vue-template-compiler@2.7.16: resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==} @@ -3551,6 +4102,12 @@ packages: peerDependencies: typescript: '*' + vue-tsc@2.0.29: + resolution: {integrity: sha512-MHhsfyxO3mYShZCGYNziSbc63x7cQ5g9kvijV7dRe1TTXBRLxXyL0FnXWpUF1xII2mJ86mwYpYsUmMwkmerq7Q==} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + vue@3.4.27: resolution: {integrity: sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA==} peerDependencies: @@ -3609,6 +4166,10 @@ packages: resolution: {integrity: sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==} engines: {node: '>= 0.4'} + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -3669,6 +4230,9 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yaml@2.7.0: resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} engines: {node: '>= 14'} @@ -3743,6 +4307,17 @@ snapshots: '@colors/colors@1.6.0': {} + '@commitlint/parse@19.5.0': + dependencies: + '@commitlint/types': 19.5.0 + conventional-changelog-angular: 7.0.0 + conventional-commits-parser: 5.0.0 + + '@commitlint/types@19.5.0': + dependencies: + '@types/conventional-commits-parser': 5.0.1 + chalk: 5.4.1 + '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 @@ -3873,6 +4448,12 @@ snapshots: '@eslint/core': 0.10.0 levn: 0.4.1 + '@gerrit0/mini-shiki@1.27.2': + dependencies: + '@shikijs/engine-oniguruma': 1.29.1 + '@shikijs/types': 1.29.1 + '@shikijs/vscode-textmate': 10.0.1 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -4014,6 +4595,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.4.15': {} + '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -4024,6 +4607,40 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 + '@kwsites/file-exists@1.1.1': + dependencies: + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + + '@kwsites/promise-deferred@1.1.1': {} + + '@microsoft/api-extractor-model@7.29.4(@types/node@20.14.2)': + dependencies: + '@microsoft/tsdoc': 0.15.1 + '@microsoft/tsdoc-config': 0.17.1 + '@rushstack/node-core-library': 5.5.1(@types/node@20.14.2) + transitivePeerDependencies: + - '@types/node' + + '@microsoft/api-extractor@7.47.4(@types/node@20.14.2)': + dependencies: + '@microsoft/api-extractor-model': 7.29.4(@types/node@20.14.2) + '@microsoft/tsdoc': 0.15.1 + '@microsoft/tsdoc-config': 0.17.1 + '@rushstack/node-core-library': 5.5.1(@types/node@20.14.2) + '@rushstack/rig-package': 0.5.3 + '@rushstack/terminal': 0.13.3(@types/node@20.14.2) + '@rushstack/ts-command-line': 4.22.3(@types/node@20.14.2) + lodash: 4.17.21 + minimatch: 3.0.8 + resolve: 1.22.8 + semver: 7.5.4 + source-map: 0.6.1 + typescript: 5.4.2 + transitivePeerDependencies: + - '@types/node' + '@microsoft/tsdoc-config@0.17.1': dependencies: '@microsoft/tsdoc': 0.15.1 @@ -4254,8 +4871,60 @@ snapshots: '@rushstack/eslint-patch@1.10.5': {} + '@rushstack/node-core-library@5.5.1(@types/node@20.14.2)': + dependencies: + ajv: 8.13.0 + ajv-draft-04: 1.0.0(ajv@8.13.0) + ajv-formats: 3.0.1(ajv@8.13.0) + fs-extra: 7.0.1 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.8 + semver: 7.5.4 + optionalDependencies: + '@types/node': 20.14.2 + + '@rushstack/rig-package@0.5.3': + dependencies: + resolve: 1.22.8 + strip-json-comments: 3.1.1 + + '@rushstack/terminal@0.13.3(@types/node@20.14.2)': + dependencies: + '@rushstack/node-core-library': 5.5.1(@types/node@20.14.2) + supports-color: 8.1.1 + optionalDependencies: + '@types/node': 20.14.2 + + '@rushstack/ts-command-line@4.22.3(@types/node@20.14.2)': + dependencies: + '@rushstack/terminal': 0.13.3(@types/node@20.14.2) + '@types/argparse': 1.0.38 + argparse: 1.0.10 + string-argv: 0.3.2 + transitivePeerDependencies: + - '@types/node' + + '@shikijs/engine-oniguruma@1.29.1': + dependencies: + '@shikijs/types': 1.29.1 + '@shikijs/vscode-textmate': 10.0.1 + + '@shikijs/types@1.29.1': + dependencies: + '@shikijs/vscode-textmate': 10.0.1 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.1': {} + '@sinclair/typebox@0.27.8': {} + '@stylistic/eslint-plugin-js@2.13.0(eslint@9.18.0(jiti@2.4.2))': + dependencies: + eslint: 9.18.0(jiti@2.4.2) + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + '@swc/counter@0.1.3': {} '@swc/helpers@0.5.13': @@ -4266,6 +4935,41 @@ snapshots: dependencies: tslib: 2.8.1 + '@tanstack/config@0.16.0(@types/node@20.14.2)(esbuild@0.20.2)(eslint@9.18.0(jiti@2.4.2))(rollup@4.22.4)(typescript@5.4.5)(vite@5.2.14(@types/node@20.14.2))': + dependencies: + '@commitlint/parse': 19.5.0 + '@eslint/js': 9.18.0 + '@stylistic/eslint-plugin-js': 2.13.0(eslint@9.18.0(jiti@2.4.2)) + commander: 13.1.0 + esbuild-register: 3.6.0(esbuild@0.20.2) + eslint-plugin-import-x: 4.6.1(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5) + eslint-plugin-n: 17.15.1(eslint@9.18.0(jiti@2.4.2)) + globals: 15.14.0 + interpret: 3.1.1 + jsonfile: 6.1.0 + liftoff: 5.0.0 + minimist: 1.2.8 + rollup-plugin-preserve-directives: 0.4.0(rollup@4.22.4) + semver: 7.6.3 + simple-git: 3.27.0 + typedoc: 0.27.6(typescript@5.4.5) + typedoc-plugin-frontmatter: 1.1.2(typedoc-plugin-markdown@4.4.1(typedoc@0.27.6(typescript@5.4.5))) + typedoc-plugin-markdown: 4.4.1(typedoc@0.27.6(typescript@5.4.5)) + typescript-eslint: 8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5) + v8flags: 4.0.1 + vite-plugin-dts: 4.0.3(@types/node@20.14.2)(rollup@4.22.4)(typescript@5.4.5)(vite@5.2.14(@types/node@20.14.2)) + vite-plugin-externalize-deps: 0.8.0(vite@5.2.14(@types/node@20.14.2)) + vite-tsconfig-paths: 5.1.4(typescript@5.4.5)(vite@5.2.14(@types/node@20.14.2)) + vue-eslint-parser: 9.4.3(eslint@9.18.0(jiti@2.4.2)) + transitivePeerDependencies: + - '@types/node' + - esbuild + - eslint + - rollup + - supports-color + - typescript + - vite + '@tsconfig/node10@1.0.11': {} '@tsconfig/node12@1.0.11': {} @@ -4274,18 +4978,30 @@ snapshots: '@tsconfig/node16@1.0.4': {} + '@types/argparse@1.0.38': {} + '@types/chai-subset@1.3.5': dependencies: '@types/chai': 4.3.16 '@types/chai@4.3.16': {} + '@types/conventional-commits-parser@5.0.1': + dependencies: + '@types/node': 20.14.2 + '@types/cookie@0.6.0': {} + '@types/doctrine@0.0.9': {} + '@types/estree@1.0.5': {} '@types/estree@1.0.6': {} + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + '@types/istanbul-lib-coverage@2.0.6': {} '@types/json-schema@7.0.15': {} @@ -4317,6 +5033,8 @@ snapshots: '@types/triple-beam@1.3.5': {} + '@types/unist@3.0.3': {} + '@types/uuid@8.3.4': {} '@typescript-eslint/eslint-plugin@8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5))(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5)': @@ -4467,16 +5185,28 @@ snapshots: dependencies: '@volar/source-map': 2.3.0 + '@volar/language-core@2.4.11': + dependencies: + '@volar/source-map': 2.4.11 + '@volar/source-map@2.3.0': dependencies: muggle-string: 0.4.1 + '@volar/source-map@2.4.11': {} + '@volar/typescript@2.3.0': dependencies: '@volar/language-core': 2.3.0 path-browserify: 1.0.1 vscode-uri: 3.0.8 + '@volar/typescript@2.4.11': + dependencies: + '@volar/language-core': 2.4.11 + path-browserify: 1.0.1 + vscode-uri: 3.0.8 + '@vue/compiler-core@3.4.27': dependencies: '@babel/parser': 7.24.7 @@ -4507,6 +5237,11 @@ snapshots: '@vue/compiler-dom': 3.4.27 '@vue/shared': 3.4.27 + '@vue/compiler-vue2@2.7.16': + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + '@vue/language-core@2.0.21(typescript@5.4.5)': dependencies: '@volar/language-core': 2.3.0 @@ -4519,6 +5254,19 @@ snapshots: optionalDependencies: typescript: 5.4.5 + '@vue/language-core@2.0.29(typescript@5.4.5)': + dependencies: + '@volar/language-core': 2.4.11 + '@vue/compiler-dom': 3.4.27 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.4.27 + computeds: 0.0.1 + minimatch: 9.0.4 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.4.5 + '@vue/reactivity@3.4.27': dependencies: '@vue/shared': 3.4.27 @@ -4542,6 +5290,11 @@ snapshots: '@vue/shared@3.4.27': {} + JSONStream@1.3.5: + dependencies: + jsonparse: 1.3.1 + through: 2.3.8 + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -4562,6 +5315,14 @@ snapshots: transitivePeerDependencies: - supports-color + ajv-draft-04@1.0.0(ajv@8.13.0): + optionalDependencies: + ajv: 8.13.0 + + ajv-formats@3.0.1(ajv@8.13.0): + optionalDependencies: + ajv: 8.13.0 + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -4576,6 +5337,13 @@ snapshots: require-from-string: 2.0.2 uri-js: 4.4.1 + ajv@8.13.0: + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 @@ -4603,6 +5371,10 @@ snapshots: arg@5.0.2: {} + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + argparse@2.0.1: {} aria-query@5.3.2: {} @@ -4617,6 +5389,10 @@ snapshots: call-bound: 1.0.3 is-array-buffer: 3.0.5 + array-each@1.0.1: {} + + array-ify@1.0.0: {} + array-includes@3.1.8: dependencies: call-bind: 1.0.7 @@ -4626,6 +5402,8 @@ snapshots: get-intrinsic: 1.2.4 is-string: 1.0.7 + array-slice@1.1.0: {} + array.prototype.findlast@1.2.5: dependencies: call-bind: 1.0.7 @@ -4781,6 +5559,8 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chalk@5.4.1: {} + check-error@1.0.3: dependencies: get-func-name: 2.0.2 @@ -4844,14 +5624,36 @@ snapshots: dependencies: delayed-stream: 1.0.0 + commander@13.1.0: {} + commander@4.1.1: {} + compare-func@2.0.0: + dependencies: + array-ify: 1.0.0 + dot-prop: 5.3.0 + + compare-versions@6.1.1: {} + computeds@0.0.1: {} concat-map@0.0.1: {} confbox@0.1.7: {} + confbox@0.1.8: {} + + conventional-changelog-angular@7.0.0: + dependencies: + compare-func: 2.0.0 + + conventional-commits-parser@5.0.0: + dependencies: + JSONStream: 1.3.5 + is-text-path: 2.0.0 + meow: 12.1.1 + split2: 4.2.0 + convert-source-map@2.0.0: {} cookie@0.7.2: {} @@ -4953,6 +5755,8 @@ snapshots: dequal@2.0.3: {} + detect-file@1.0.0: {} + detect-libc@2.0.3: optional: true @@ -4968,6 +5772,14 @@ snapshots: dependencies: esutils: 2.0.3 + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dot-prop@5.3.0: + dependencies: + is-obj: 2.0.0 + dotenv@16.0.3: {} dunder-proto@1.0.1: @@ -5154,6 +5966,13 @@ snapshots: is-date-object: 1.0.5 is-symbol: 1.0.4 + esbuild-register@3.6.0(esbuild@0.20.2): + dependencies: + debug: 4.4.0 + esbuild: 0.20.2 + transitivePeerDependencies: + - supports-color + esbuild@0.20.2: optionalDependencies: '@esbuild/aix-ppc64': 0.20.2 @@ -5184,7 +6003,12 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-next@15.1.4(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5): + eslint-compat-utils@0.5.1(eslint@9.18.0(jiti@2.4.2)): + dependencies: + eslint: 9.18.0(jiti@2.4.2) + semver: 7.6.3 + + eslint-config-next@15.1.4(eslint-plugin-import-x@4.6.1(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5))(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5): dependencies: '@next/eslint-plugin-next': 15.1.4 '@rushstack/eslint-patch': 1.10.5 @@ -5192,7 +6016,7 @@ snapshots: '@typescript-eslint/parser': 8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5) eslint: 9.18.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.18.0(jiti@2.4.2)) + eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import-x@4.6.1(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5))(eslint-plugin-import@2.31.0)(eslint@9.18.0(jiti@2.4.2)) eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5))(eslint-import-resolver-typescript@3.7.0)(eslint@9.18.0(jiti@2.4.2)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.18.0(jiti@2.4.2)) eslint-plugin-react: 7.37.4(eslint@9.18.0(jiti@2.4.2)) @@ -5216,7 +6040,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@9.18.0(jiti@2.4.2)): + eslint-import-resolver-typescript@3.7.0(eslint-plugin-import-x@4.6.1(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5))(eslint-plugin-import@2.31.0)(eslint@9.18.0(jiti@2.4.2)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.0 @@ -5229,6 +6053,7 @@ snapshots: stable-hash: 0.0.4 optionalDependencies: eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5))(eslint-import-resolver-typescript@3.7.0)(eslint@9.18.0(jiti@2.4.2)) + eslint-plugin-import-x: 4.6.1(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5) transitivePeerDependencies: - supports-color @@ -5239,10 +6064,37 @@ snapshots: '@typescript-eslint/parser': 8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5) eslint: 9.18.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.18.0(jiti@2.4.2)) + eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import-x@4.6.1(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5))(eslint-plugin-import@2.31.0)(eslint@9.18.0(jiti@2.4.2)) transitivePeerDependencies: - supports-color + eslint-plugin-es-x@7.8.0(eslint@9.18.0(jiti@2.4.2)): + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.18.0(jiti@2.4.2)) + '@eslint-community/regexpp': 4.12.1 + eslint: 9.18.0(jiti@2.4.2) + eslint-compat-utils: 0.5.1(eslint@9.18.0(jiti@2.4.2)) + + eslint-plugin-import-x@4.6.1(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5): + dependencies: + '@types/doctrine': 0.0.9 + '@typescript-eslint/scope-manager': 8.21.0 + '@typescript-eslint/utils': 8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5) + debug: 4.4.0 + doctrine: 3.0.0 + enhanced-resolve: 5.18.0 + eslint: 9.18.0(jiti@2.4.2) + eslint-import-resolver-node: 0.3.9 + get-tsconfig: 4.10.0 + is-glob: 4.0.3 + minimatch: 9.0.4 + semver: 7.6.3 + stable-hash: 0.0.4 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + - typescript + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5))(eslint-import-resolver-typescript@3.7.0)(eslint@9.18.0(jiti@2.4.2)): dependencies: '@rtsao/scc': 1.1.0 @@ -5291,6 +6143,18 @@ snapshots: safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 + eslint-plugin-n@17.15.1(eslint@9.18.0(jiti@2.4.2)): + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.18.0(jiti@2.4.2)) + enhanced-resolve: 5.18.0 + eslint: 9.18.0(jiti@2.4.2) + eslint-plugin-es-x: 7.8.0(eslint@9.18.0(jiti@2.4.2)) + get-tsconfig: 4.10.0 + globals: 15.14.0 + ignore: 5.3.2 + minimatch: 9.0.5 + semver: 7.6.3 + eslint-plugin-react-hooks@5.1.0(eslint@9.18.0(jiti@2.4.2)): dependencies: eslint: 9.18.0(jiti@2.4.2) @@ -5327,6 +6191,11 @@ snapshots: dotenv: 16.0.3 eslint: 9.18.0(jiti@2.4.2) + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + eslint-scope@8.2.0: dependencies: esrecurse: 4.3.0 @@ -5383,6 +6252,12 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.14.0) eslint-visitor-keys: 4.2.0 + espree@9.6.1: + dependencies: + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) + eslint-visitor-keys: 3.4.3 + esquery@1.5.0: dependencies: estraverse: 5.3.0 @@ -5401,6 +6276,12 @@ snapshots: events@3.3.0: {} + expand-tilde@2.0.2: + dependencies: + homedir-polyfill: 1.0.3 + + extend@3.0.2: {} + fast-deep-equal@3.1.3: {} fast-glob@3.3.1: @@ -5448,6 +6329,23 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 + findup-sync@5.0.0: + dependencies: + detect-file: 1.0.0 + is-glob: 4.0.3 + micromatch: 4.0.8 + resolve-dir: 1.0.1 + + fined@2.0.0: + dependencies: + expand-tilde: 2.0.2 + is-plain-object: 5.0.0 + object.defaults: 1.1.0 + object.pick: 1.3.0 + parse-filepath: 1.0.2 + + flagged-respawn@2.0.0: {} + flat-cache@4.0.1: dependencies: flatted: 3.3.1 @@ -5461,6 +6359,12 @@ snapshots: dependencies: is-callable: 1.2.7 + for-in@1.0.2: {} + + for-own@1.0.0: + dependencies: + for-in: 1.0.2 + foreground-child@3.3.0: dependencies: cross-spawn: 7.0.6 @@ -5472,6 +6376,12 @@ snapshots: combined-stream: 1.0.8 mime-types: 2.1.35 + fs-extra@7.0.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + fs.realpath@1.0.0: {} fsevents@2.3.3: @@ -5569,6 +6479,20 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 + global-modules@1.0.0: + dependencies: + global-prefix: 1.0.2 + is-windows: 1.0.2 + resolve-dir: 1.0.1 + + global-prefix@1.0.2: + dependencies: + expand-tilde: 2.0.2 + homedir-polyfill: 1.0.3 + ini: 1.3.8 + is-windows: 1.0.2 + which: 1.3.1 + globals@14.0.0: {} globals@15.14.0: {} @@ -5578,6 +6502,8 @@ snapshots: define-properties: 1.2.1 gopd: 1.0.1 + globrex@0.1.2: {} + gopd@1.0.1: dependencies: get-intrinsic: 1.2.4 @@ -5620,6 +6546,10 @@ snapshots: headers-polyfill@4.0.3: {} + homedir-polyfill@1.0.3: + dependencies: + parse-passwd: 1.0.0 + html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 @@ -5648,11 +6578,15 @@ snapshots: ignore@5.3.1: {} + ignore@5.3.2: {} + import-fresh@3.3.0: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 + import-lazy@4.0.0: {} + imurmurhash@0.1.4: {} inflight@1.0.6: @@ -5662,6 +6596,8 @@ snapshots: inherits@2.0.4: {} + ini@1.3.8: {} + internal-slot@1.0.7: dependencies: es-errors: 1.3.0 @@ -5674,6 +6610,13 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 + interpret@3.1.1: {} + + is-absolute@1.0.0: + dependencies: + is-relative: 1.0.0 + is-windows: 1.0.2 + is-array-buffer@3.0.4: dependencies: call-bind: 1.0.7 @@ -5783,6 +6726,10 @@ snapshots: is-number@7.0.0: {} + is-obj@2.0.0: {} + + is-plain-object@5.0.0: {} + is-potential-custom-element-name@1.0.1: {} is-regex@1.1.4: @@ -5797,6 +6744,10 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 + is-relative@1.0.0: + dependencies: + is-unc-path: 1.0.0 + is-set@2.0.3: {} is-shared-array-buffer@1.0.3: @@ -5828,6 +6779,10 @@ snapshots: has-symbols: 1.1.0 safe-regex-test: 1.1.0 + is-text-path@2.0.0: + dependencies: + text-extensions: 2.4.0 + is-typed-array@1.1.13: dependencies: which-typed-array: 1.1.15 @@ -5836,6 +6791,10 @@ snapshots: dependencies: which-typed-array: 1.1.18 + is-unc-path@1.0.0: + dependencies: + unc-path-regex: 0.1.2 + is-weakmap@2.0.2: {} is-weakref@1.0.2: @@ -5851,10 +6810,14 @@ snapshots: call-bind: 1.0.7 get-intrinsic: 1.2.4 + is-windows@1.0.2: {} + isarray@2.0.5: {} isexe@2.0.0: {} + isobject@3.0.1: {} + istanbul-lib-coverage@3.2.2: {} istanbul-lib-report@3.0.1: @@ -5944,6 +6907,18 @@ snapshots: dependencies: minimist: 1.2.8 + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonparse@1.3.1: {} + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.8 @@ -5955,6 +6930,8 @@ snapshots: dependencies: json-buffer: 3.0.1 + kolorist@1.8.0: {} + kuler@2.0.0: {} language-subtag-registry@0.3.23: {} @@ -5968,18 +6945,39 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + liftoff@5.0.0: + dependencies: + extend: 3.0.2 + findup-sync: 5.0.0 + fined: 2.0.0 + flagged-respawn: 2.0.0 + is-plain-object: 5.0.0 + rechoir: 0.8.0 + resolve: 1.22.8 + lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + local-pkg@0.4.3: {} + local-pkg@0.5.1: + dependencies: + mlly: 1.7.4 + pkg-types: 1.3.1 + locate-path@6.0.0: dependencies: p-locate: 5.0.0 lodash.merge@4.6.2: {} + lodash@4.17.21: {} + logform@2.6.0: dependencies: '@colors/colors': 1.6.0 @@ -5999,18 +6997,43 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + lunr@2.3.9: {} + magic-string@0.30.10: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + make-dir@4.0.0: dependencies: semver: 7.6.3 make-error@1.3.6: {} + map-cache@0.2.2: {} + + markdown-it@14.1.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + math-intrinsics@1.1.0: {} + mdurl@2.0.0: {} + + meow@12.1.1: {} + merge2@1.4.1: {} micromatch@4.0.7: @@ -6029,6 +7052,10 @@ snapshots: dependencies: mime-db: 1.52.0 + minimatch@3.0.8: + dependencies: + brace-expansion: 1.1.11 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -6037,6 +7064,10 @@ snapshots: dependencies: brace-expansion: 2.0.1 + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + minimist@1.2.8: {} minipass@7.1.2: {} @@ -6048,6 +7079,13 @@ snapshots: pkg-types: 1.1.1 ufo: 1.5.3 + mlly@1.7.4: + dependencies: + acorn: 8.14.0 + pathe: 2.0.2 + pkg-types: 1.3.1 + ufo: 1.5.4 + mrmime@2.0.0: {} ms@2.1.2: {} @@ -6175,6 +7213,13 @@ snapshots: has-symbols: 1.1.0 object-keys: 1.1.1 + object.defaults@1.1.0: + dependencies: + array-each: 1.0.1 + array-slice: 1.1.0 + for-own: 1.0.0 + isobject: 3.0.1 + object.entries@1.1.8: dependencies: call-bind: 1.0.7 @@ -6194,6 +7239,10 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.9 + object.pick@1.3.0: + dependencies: + isobject: 3.0.1 + object.values@1.2.0: dependencies: call-bind: 1.0.7 @@ -6252,6 +7301,14 @@ snapshots: dependencies: callsites: 3.1.0 + parse-filepath@1.0.2: + dependencies: + is-absolute: 1.0.0 + map-cache: 0.2.2 + path-root: 0.1.1 + + parse-passwd@1.0.0: {} + parse5@7.1.2: dependencies: entities: 4.5.0 @@ -6266,6 +7323,12 @@ snapshots: path-parse@1.0.7: {} + path-root-regex@0.1.2: {} + + path-root@0.1.1: + dependencies: + path-root-regex: 0.1.2 + path-scurry@1.11.1: dependencies: lru-cache: 10.4.3 @@ -6275,6 +7338,8 @@ snapshots: pathe@1.1.2: {} + pathe@2.0.2: {} + pathval@1.1.1: {} picocolors@1.0.1: {} @@ -6318,6 +7383,12 @@ snapshots: mlly: 1.7.1 pathe: 1.1.2 + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.7.4 + pathe: 2.0.2 + possible-typed-array-names@1.0.0: {} postcss-import@15.1.0(postcss@8.5.1): @@ -6392,6 +7463,8 @@ snapshots: psl@1.9.0: {} + punycode.js@2.3.1: {} + punycode@2.3.1: {} querystringify@2.2.0: {} @@ -6445,6 +7518,10 @@ snapshots: real-require@0.2.0: {} + rechoir@0.8.0: + dependencies: + resolve: 1.22.8 + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -6488,6 +7565,11 @@ snapshots: requires-port@1.0.0: {} + resolve-dir@1.0.1: + dependencies: + expand-tilde: 2.0.2 + global-modules: 1.0.0 + resolve-from@4.0.0: {} resolve-pkg-maps@1.0.0: {} @@ -6506,6 +7588,12 @@ snapshots: reusify@1.0.4: {} + rollup-plugin-preserve-directives@0.4.0(rollup@4.22.4): + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.22.4) + magic-string: 0.30.10 + rollup: 4.22.4 + rollup@4.18.0: dependencies: '@types/estree': 1.0.5 @@ -6608,6 +7696,10 @@ snapshots: semver@6.3.1: {} + semver@7.5.4: + dependencies: + lru-cache: 6.0.0 + semver@7.6.2: {} semver@7.6.3: {} @@ -6706,6 +7798,14 @@ snapshots: signal-exit@4.1.0: {} + simple-git@3.27.0: + dependencies: + '@kwsites/file-exists': 1.1.1 + '@kwsites/promise-deferred': 1.1.1 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + simple-swizzle@0.2.2: dependencies: is-arrayish: 0.3.2 @@ -6728,6 +7828,8 @@ snapshots: split2@4.2.0: {} + sprintf-js@1.0.3: {} + stable-hash@0.0.4: {} stack-trace@0.0.10: {} @@ -6742,6 +7844,8 @@ snapshots: strict-event-emitter@0.5.1: {} + string-argv@0.3.2: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -6861,6 +7965,10 @@ snapshots: dependencies: has-flag: 4.0.0 + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + supports-preserve-symlinks-flag@1.0.0: {} symbol-tree@3.2.4: {} @@ -6900,6 +8008,8 @@ snapshots: glob: 7.2.3 minimatch: 3.1.2 + text-extensions@2.4.0: {} + text-hex@1.0.0: {} thenify-all@1.6.0: @@ -6914,6 +8024,8 @@ snapshots: dependencies: real-require: 0.2.0 + through@2.3.8: {} + tinybench@2.8.0: {} tinypool@0.7.0: {} @@ -6965,6 +8077,10 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + tsconfck@3.1.4(typescript@5.4.5): + optionalDependencies: + typescript: 5.4.5 + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 @@ -7078,6 +8194,24 @@ snapshots: possible-typed-array-names: 1.0.0 reflect.getprototypeof: 1.0.6 + typedoc-plugin-frontmatter@1.1.2(typedoc-plugin-markdown@4.4.1(typedoc@0.27.6(typescript@5.4.5))): + dependencies: + typedoc-plugin-markdown: 4.4.1(typedoc@0.27.6(typescript@5.4.5)) + yaml: 2.7.0 + + typedoc-plugin-markdown@4.4.1(typedoc@0.27.6(typescript@5.4.5)): + dependencies: + typedoc: 0.27.6(typescript@5.4.5) + + typedoc@0.27.6(typescript@5.4.5): + dependencies: + '@gerrit0/mini-shiki': 1.27.2 + lunr: 2.3.9 + markdown-it: 14.1.0 + minimatch: 9.0.5 + typescript: 5.4.5 + yaml: 2.7.0 + typescript-eslint@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5): dependencies: '@typescript-eslint/eslint-plugin': 8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5))(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5) @@ -7088,10 +8222,16 @@ snapshots: transitivePeerDependencies: - supports-color + typescript@5.4.2: {} + typescript@5.4.5: {} + uc.micro@2.1.0: {} + ufo@1.5.3: {} + ufo@1.5.4: {} + unbox-primitive@1.0.2: dependencies: call-bind: 1.0.7 @@ -7106,10 +8246,16 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 + unc-path-regex@0.1.2: {} + undici-types@5.26.5: {} + universalify@0.1.2: {} + universalify@0.2.0: {} + universalify@2.0.1: {} + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -7136,6 +8282,8 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 + v8flags@4.0.1: {} + vite-node@0.34.6(@types/node@20.14.2): dependencies: cac: 6.7.14 @@ -7154,6 +8302,41 @@ snapshots: - supports-color - terser + vite-plugin-dts@4.0.3(@types/node@20.14.2)(rollup@4.22.4)(typescript@5.4.5)(vite@5.2.14(@types/node@20.14.2)): + dependencies: + '@microsoft/api-extractor': 7.47.4(@types/node@20.14.2) + '@rollup/pluginutils': 5.1.0(rollup@4.22.4) + '@volar/typescript': 2.4.11 + '@vue/language-core': 2.0.29(typescript@5.4.5) + compare-versions: 6.1.1 + debug: 4.4.0 + kolorist: 1.8.0 + local-pkg: 0.5.1 + magic-string: 0.30.17 + typescript: 5.4.5 + vue-tsc: 2.0.29(typescript@5.4.5) + optionalDependencies: + vite: 5.2.14(@types/node@20.14.2) + transitivePeerDependencies: + - '@types/node' + - rollup + - supports-color + + vite-plugin-externalize-deps@0.8.0(vite@5.2.14(@types/node@20.14.2)): + dependencies: + vite: 5.2.14(@types/node@20.14.2) + + vite-tsconfig-paths@5.1.4(typescript@5.4.5)(vite@5.2.14(@types/node@20.14.2)): + dependencies: + debug: 4.4.0 + globrex: 0.1.2 + tsconfck: 3.1.4(typescript@5.4.5) + optionalDependencies: + vite: 5.2.14(@types/node@20.14.2) + transitivePeerDependencies: + - supports-color + - typescript + vite@5.2.14(@types/node@20.14.2): dependencies: esbuild: 0.20.2 @@ -7203,6 +8386,19 @@ snapshots: vscode-uri@3.0.8: {} + vue-eslint-parser@9.4.3(eslint@9.18.0(jiti@2.4.2)): + dependencies: + debug: 4.4.0 + eslint: 9.18.0(jiti@2.4.2) + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + lodash: 4.17.21 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + vue-template-compiler@2.7.16: dependencies: de-indent: 1.0.2 @@ -7215,6 +8411,13 @@ snapshots: semver: 7.6.2 typescript: 5.4.5 + vue-tsc@2.0.29(typescript@5.4.5): + dependencies: + '@volar/typescript': 2.4.11 + '@vue/language-core': 2.0.29(typescript@5.4.5) + semver: 7.6.3 + typescript: 5.4.5 + vue@3.4.27(typescript@5.4.5): dependencies: '@vue/compiler-dom': 3.4.27 @@ -7315,6 +8518,10 @@ snapshots: gopd: 1.2.0 has-tostringtag: 1.0.2 + which@1.3.1: + dependencies: + isexe: 2.0.0 + which@2.0.2: dependencies: isexe: 2.0.0 @@ -7374,6 +8581,8 @@ snapshots: y18n@5.0.8: {} + yallist@4.0.0: {} + yaml@2.7.0: {} yargs-parser@21.1.1: {} From 56c5e4de331843ee70cd3135098208f654439649 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 21 Jan 2025 17:32:45 +0000 Subject: [PATCH 16/71] feat: add pkg.pr.new for continuous release --- .github/workflows/ci.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 052c44f2..157c68b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,22 @@ jobs: - run: pnpm build && pnpm build:cjs - run: pnpm lint - run: pnpm test - + + preview-release: + name: Preview release + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + with: + version: ${{ env.PNPM_VERSION }} + - uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: 'pnpm' + - run: pnpx pkg-pr-new publish --compact './packages/*' + check-winston-example: name: Check Winston example runs-on: ubuntu-latest From 903f53d25411011a551201d535c02783d5e9b5ca Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 21 Jan 2025 18:52:32 +0000 Subject: [PATCH 17/71] chore: temporary disable of lint --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 157c68b4..0fefee0b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,7 +42,7 @@ jobs: cache: 'pnpm' - run: pnpm install --frozen-lockfile - run: pnpm build && pnpm build:cjs - - run: pnpm lint + # - run: pnpm lint # TODO: fix linting - run: pnpm test preview-release: From 1226d66fe2de4d7f0a1ce815af5541c2d4780bdc Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 21 Jan 2025 18:55:33 +0000 Subject: [PATCH 18/71] chore: remove empty test commands --- packages/logging/package.json | 3 +-- packages/nextjs/package.json | 3 +-- packages/react/package.json | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/logging/package.json b/packages/logging/package.json index 53057aca..28b0501c 100644 --- a/packages/logging/package.json +++ b/packages/logging/package.json @@ -29,8 +29,7 @@ } }, "scripts": { - "build": "vite build", - "test": "echo \"Error: no test specified\" && exit 1" + "build": "vite build" }, "keywords": [], "author": "", diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 9cb39182..b0be8a35 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -30,8 +30,7 @@ "./package.json": "./package.json" }, "scripts": { - "build": "vite build", - "test": "echo \"Error: no test specified\" && exit 1" + "build": "vite build" }, "keywords": [], "author": "", diff --git a/packages/react/package.json b/packages/react/package.json index 018f09f0..41098ae4 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -30,8 +30,7 @@ "./package.json": "./package.json" }, "scripts": { - "build": "vite build", - "test": "echo \"Error: no test specified\" && exit 1" + "build": "vite build" }, "keywords": [], "author": "", From d876e88aa27a67a98c8569a1c2642bf1b9ddfc90 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 21 Jan 2025 19:01:42 +0000 Subject: [PATCH 19/71] fix: remove compact until package is released --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0fefee0b..fe8cc37a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,7 +58,7 @@ jobs: with: node-version: 20.x cache: 'pnpm' - - run: pnpx pkg-pr-new publish --compact './packages/*' + - run: pnpx pkg-pr-new publish './packages/*' check-winston-example: name: Check Winston example From c71c51624c53c8d712d65342a18b3a11af159ed4 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 21 Jan 2025 23:43:09 +0000 Subject: [PATCH 20/71] chore: add build step and turbo cache to preview-release --- .github/workflows/ci.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe8cc37a..012f14dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,14 @@ jobs: - 20.x - 22.x steps: + - name: Cache turbo build setup + uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}-turbo-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-turbo- + - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: @@ -50,6 +58,13 @@ jobs: needs: build runs-on: ubuntu-latest steps: + - name: Cache turbo build setup + uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}-turbo-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-turbo- - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: @@ -58,6 +73,8 @@ jobs: with: node-version: 20.x cache: 'pnpm' + - run: pnpm install --frozen-lockfile + - run: pnpm run build --filter='./packages/*' && pnpm run build:cjs --filter='./packages/*' - run: pnpx pkg-pr-new publish './packages/*' check-winston-example: From e9bd5264f8859e75b6ddddc8c4930a24c4f95fc3 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Wed, 22 Jan 2025 12:12:09 +0000 Subject: [PATCH 21/71] ci: cache after node setup --- .github/workflows/ci.yml | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 012f14dc..06fb2a8e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,14 +32,6 @@ jobs: - 20.x - 22.x steps: - - name: Cache turbo build setup - uses: actions/cache@v4 - with: - path: .turbo - key: ${{ runner.os }}-turbo-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-turbo- - - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: @@ -48,6 +40,13 @@ jobs: with: node-version: ${{ matrix.node }} cache: 'pnpm' + - name: Cache turbo build setup + uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}-turbo-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-turbo- - run: pnpm install --frozen-lockfile - run: pnpm build && pnpm build:cjs # - run: pnpm lint # TODO: fix linting @@ -58,13 +57,6 @@ jobs: needs: build runs-on: ubuntu-latest steps: - - name: Cache turbo build setup - uses: actions/cache@v4 - with: - path: .turbo - key: ${{ runner.os }}-turbo-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-turbo- - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: @@ -73,6 +65,13 @@ jobs: with: node-version: 20.x cache: 'pnpm' + - name: Cache turbo build setup + uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}-turbo-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-turbo- - run: pnpm install --frozen-lockfile - run: pnpm run build --filter='./packages/*' && pnpm run build:cjs --filter='./packages/*' - run: pnpx pkg-pr-new publish './packages/*' From cb792bde4882f077b7fc6c9126ad92bf4c40920f Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Wed, 22 Jan 2025 13:03:44 +0000 Subject: [PATCH 22/71] refactor: export everything from index in logger, remove unused transport exports --- examples/nextjs/src/lib/axiom/client.ts | 3 +- examples/nextjs/src/lib/axiom/server.ts | 3 +- packages/logging/package.json | 10 -- packages/logging/src/index.ts | 153 +----------------------- packages/logging/src/logger.ts | 124 +++++++++++++++++++ packages/logging/vite.config.ts | 2 +- packages/nextjs/package.json | 10 -- packages/react/package.json | 10 -- 8 files changed, 129 insertions(+), 186 deletions(-) create mode 100644 packages/logging/src/logger.ts diff --git a/examples/nextjs/src/lib/axiom/client.ts b/examples/nextjs/src/lib/axiom/client.ts index 108eb318..1a9ad0a1 100644 --- a/examples/nextjs/src/lib/axiom/client.ts +++ b/examples/nextjs/src/lib/axiom/client.ts @@ -1,5 +1,4 @@ -import { Logger } from '@axiomhq/logging'; -import { AxiomProxyTransport, ConsoleTransport } from '@axiomhq/logging/transports'; +import { Logger, AxiomProxyTransport, ConsoleTransport } from '@axiomhq/logging'; import { createClientSideHelpers } from '@axiomhq/react'; export const logger = new Logger({ diff --git a/examples/nextjs/src/lib/axiom/server.ts b/examples/nextjs/src/lib/axiom/server.ts index b473d6c5..66dcf1ca 100644 --- a/examples/nextjs/src/lib/axiom/server.ts +++ b/examples/nextjs/src/lib/axiom/server.ts @@ -1,5 +1,4 @@ -import { Logger } from '@axiomhq/logging'; -import { AxiomFetchTransport, ConsoleTransport } from '@axiomhq/logging/transports'; +import { Logger, AxiomFetchTransport, ConsoleTransport } from '@axiomhq/logging'; export const logger = new Logger({ transports: [ diff --git a/packages/logging/package.json b/packages/logging/package.json index 28b0501c..fb7a45ce 100644 --- a/packages/logging/package.json +++ b/packages/logging/package.json @@ -16,16 +16,6 @@ "types": "./dist/cjs/index.d.cts", "default": "./dist/cjs/index.cjs" } - }, - "./transports": { - "import": { - "types": "./dist/esm/transports/index.d.ts", - "default": "./dist/esm/transports/index.js" - }, - "require": { - "types": "./dist/cjs/transports/index.d.cts", - "default": "./dist/cjs/transports/index.cjs" - } } }, "scripts": { diff --git a/packages/logging/src/index.ts b/packages/logging/src/index.ts index f832fbf5..b094409f 100644 --- a/packages/logging/src/index.ts +++ b/packages/logging/src/index.ts @@ -1,151 +1,2 @@ -import { Version } from './shared'; -export interface Transport { - log: (logs: LogEvent[]) => Promise | void; - flush: () => Promise | void; -} - -const LOG_LEVEL = 'info'; - -export interface LogEvent { - level: string; - message: string; - fields: any; - _time: string; - '@app': { - 'axiom-logging-version': string; - }; -} - -export enum LogLevel { - debug = 0, - info = 1, - warn = 2, - error = 3, - off = 100, -} - -export const throttle = (fn: Function, wait: number) => { - let lastFn: ReturnType, lastTime: number; - return function (this: any) { - const context = this, - args = arguments; - - // First call, set lastTime - if (lastTime == null) { - lastTime = Date.now(); - } - - clearTimeout(lastFn); - lastFn = setTimeout( - () => { - if (Date.now() - lastTime >= wait) { - fn.apply(context, args); - lastTime = Date.now(); - } - }, - Math.max(wait - (Date.now() - lastTime), 0), - ); - }; -}; - -export type LoggerConfig = { - args?: { [key: string]: any }; - transports: [Transport, ...Transport[]]; - logLevel?: LogLevel; -}; - -export class Logger { - children: Logger[] = []; - public logLevel: LogLevel = LogLevel.debug; - public config: LoggerConfig; - - constructor(public initConfig: LoggerConfig) { - // check if user passed a log level, if not the default init value will be used as is. - if (this.initConfig.logLevel != undefined && this.initConfig.logLevel >= 0) { - this.logLevel = this.initConfig.logLevel; - } else if (LOG_LEVEL) { - this.logLevel = LogLevel[LOG_LEVEL as keyof typeof LogLevel]; - } - this.config = { ...initConfig }; - } - - raw(log: LogEvent) { - this.config.transports.forEach((transport) => transport.log([log])); - } - debug = (message: string, args: { [key: string]: any } = {}) => { - this.log(LogLevel.debug, message, args); - }; - info = (message: string, args: { [key: string]: any } = {}) => { - this.log(LogLevel.info, message, args); - }; - warn = (message: string, args: { [key: string]: any } = {}) => { - this.log(LogLevel.warn, message, args); - }; - error = (message: string, args: { [key: string]: any } = {}) => { - this.log(LogLevel.error, message, args); - }; - - with = (args: { [key: string]: any }) => { - const config = { ...this.config, args: { ...this.config.args, ...args } }; - const child = new Logger(config); - this.children.push(child); - return child; - }; - - private _transformEvent = (level: LogLevel, message: string, args: { [key: string]: any } = {}) => { - const logEvent: LogEvent = { - level: LogLevel[level].toString(), - message, - _time: new Date(Date.now()).toISOString(), - fields: this.config.args || {}, - '@app': { - 'axiom-logging-version': Version ?? 'unknown', - }, - }; - - // check if passed args is an object, if its not an object, add it to fields.args - if (args instanceof Error) { - logEvent.fields = { - ...logEvent.fields, - message: args.message, - stack: args.stack, - name: args.name, - }; - } else if (typeof args === 'object' && args !== null && Object.keys(args).length > 0) { - const parsedArgs = JSON.parse(JSON.stringify(args, jsonFriendlyErrorReplacer)); - logEvent.fields = { ...logEvent.fields, ...parsedArgs }; - } else if (args && args.length) { - logEvent.fields = { ...logEvent.fields, args: args }; - } - - return logEvent; - }; - - log = (level: LogLevel, message: string, args: { [key: string]: any } = {}) => { - this.config.transports.forEach((transport) => transport.log([this._transformEvent(level, message, args)])); - }; - - flush = async () => { - const promises = [ - ...this.config.transports.map((transport) => transport.flush()), - ...this.children.map((child) => child.flush()), - ]; - - await Promise.allSettled(promises); - }; -} - -function jsonFriendlyErrorReplacer(key: string, value: any) { - if (value instanceof Error) { - return { - // Pull all enumerable properties, supporting properties on custom Errors - ...value, - // Explicitly pull Error's non-enumerable properties - name: value.name, - message: value.message, - stack: value.stack, - }; - } - - return value; -} +export * from './transports/index'; // Index needs to be specified for TS to pick up the exports +export * from './logger'; diff --git a/packages/logging/src/logger.ts b/packages/logging/src/logger.ts new file mode 100644 index 00000000..789069e7 --- /dev/null +++ b/packages/logging/src/logger.ts @@ -0,0 +1,124 @@ +import { Transport } from '.'; +import { Version } from './shared'; + +const LOG_LEVEL = 'info'; + +export interface LogEvent { + level: string; + message: string; + fields: any; + _time: string; + '@app': { + 'axiom-logging-version': string; + }; +} + +export enum LogLevel { + debug = 0, + info = 1, + warn = 2, + error = 3, + off = 100, +} + +export type LoggerConfig = { + args?: { [key: string]: any }; + transports: [Transport, ...Transport[]]; + logLevel?: LogLevel; +}; + +export class Logger { + children: Logger[] = []; + public logLevel: LogLevel = LogLevel.debug; + public config: LoggerConfig; + + constructor(public initConfig: LoggerConfig) { + // check if user passed a log level, if not the default init value will be used as is. + if (this.initConfig.logLevel != undefined && this.initConfig.logLevel >= 0) { + this.logLevel = this.initConfig.logLevel; + } else if (LOG_LEVEL) { + this.logLevel = LogLevel[LOG_LEVEL as keyof typeof LogLevel]; + } + this.config = { ...initConfig }; + } + + raw(log: LogEvent) { + this.config.transports.forEach((transport) => transport.log([log])); + } + debug = (message: string, args: { [key: string]: any } = {}) => { + this.log(LogLevel.debug, message, args); + }; + info = (message: string, args: { [key: string]: any } = {}) => { + this.log(LogLevel.info, message, args); + }; + warn = (message: string, args: { [key: string]: any } = {}) => { + this.log(LogLevel.warn, message, args); + }; + error = (message: string, args: { [key: string]: any } = {}) => { + this.log(LogLevel.error, message, args); + }; + + with = (args: { [key: string]: any }) => { + const config = { ...this.config, args: { ...this.config.args, ...args } }; + const child = new Logger(config); + this.children.push(child); + return child; + }; + + private _transformEvent = (level: LogLevel, message: string, args: { [key: string]: any } = {}) => { + const logEvent: LogEvent = { + level: LogLevel[level].toString(), + message, + _time: new Date(Date.now()).toISOString(), + fields: this.config.args || {}, + '@app': { + 'axiom-logging-version': Version ?? 'unknown', + }, + }; + + // check if passed args is an object, if its not an object, add it to fields.args + if (args instanceof Error) { + logEvent.fields = { + ...logEvent.fields, + message: args.message, + stack: args.stack, + name: args.name, + }; + } else if (typeof args === 'object' && args !== null && Object.keys(args).length > 0) { + const parsedArgs = JSON.parse(JSON.stringify(args, jsonFriendlyErrorReplacer)); + logEvent.fields = { ...logEvent.fields, ...parsedArgs }; + } else if (args && args.length) { + logEvent.fields = { ...logEvent.fields, args: args }; + } + + return logEvent; + }; + + log = (level: LogLevel, message: string, args: { [key: string]: any } = {}) => { + this.config.transports.forEach((transport) => transport.log([this._transformEvent(level, message, args)])); + }; + + flush = async () => { + const promises = [ + ...this.config.transports.map((transport) => transport.flush()), + ...this.children.map((child) => child.flush()), + ]; + + await Promise.allSettled(promises); + }; +} + +function jsonFriendlyErrorReplacer(_key: string, value: any) { + if (value instanceof Error) { + return { + // Pull all enumerable properties, supporting properties on custom Errors + ...value, + // Explicitly pull Error's non-enumerable properties + name: value.name, + message: value.message, + stack: value.stack, + }; + } + + return value; +} diff --git a/packages/logging/vite.config.ts b/packages/logging/vite.config.ts index e0680af2..5f02017c 100644 --- a/packages/logging/vite.config.ts +++ b/packages/logging/vite.config.ts @@ -10,7 +10,7 @@ const config = defineConfig({ export default mergeConfig( config, tanstackViteConfig({ - entry: ['./src/index.ts', './src/transports/index.ts'], + entry: './src/index.ts', srcDir: './src', }), ); diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index b0be8a35..17b0ef46 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -7,16 +7,6 @@ "module": "dist/esm/index.js", "types": "dist/esm/index.d.ts", "exports": { - "./transports": { - "import": { - "types": "./dist/esm/transports/index.d.ts", - "default": "./dist/esm/transports/index.js" - }, - "require": { - "types": "./dist/cjs/transports/index.d.cts", - "default": "./dist/cjs/transports/index.cjs" - } - }, ".": { "import": { "types": "./dist/esm/index.d.ts", diff --git a/packages/react/package.json b/packages/react/package.json index 41098ae4..d6bcc391 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -7,16 +7,6 @@ "module": "dist/esm/index.js", "types": "dist/esm/index.d.ts", "exports": { - "./transports": { - "import": { - "types": "./dist/esm/transports/index.d.ts", - "default": "./dist/esm/transports/index.js" - }, - "require": { - "types": "./dist/cjs/transports/index.d.cts", - "default": "./dist/cjs/transports/index.cjs" - } - }, ".": { "import": { "types": "./dist/esm/index.d.ts", From 196d390d6636769d53b686344f344656714c12c6 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Thu, 23 Jan 2025 13:23:03 +0000 Subject: [PATCH 23/71] chore: add .env.example --- examples/nextjs/.env.example | 3 +++ examples/nextjs/.gitignore | 1 + 2 files changed, 4 insertions(+) create mode 100644 examples/nextjs/.env.example diff --git a/examples/nextjs/.env.example b/examples/nextjs/.env.example new file mode 100644 index 00000000..f7b3cbb8 --- /dev/null +++ b/examples/nextjs/.env.example @@ -0,0 +1,3 @@ +AXIOM_TOKEN="" +AXIOM_DATASET="" +NEXT_PUBLIC_AXIOM_PROXY_URL="/api/axiom" \ No newline at end of file diff --git a/examples/nextjs/.gitignore b/examples/nextjs/.gitignore index 5ef6a520..7b8da95f 100644 --- a/examples/nextjs/.gitignore +++ b/examples/nextjs/.gitignore @@ -32,6 +32,7 @@ yarn-error.log* # env files (can opt-in for committing if needed) .env* +!.env.example # vercel .vercel From 20d0a804670ac4e2e6d5b4c51426525531a311c7 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Thu, 23 Jan 2025 15:37:45 +0000 Subject: [PATCH 24/71] fix: add missing request key to transformErrorResult --- packages/nextjs/src/routeHandler.ts | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/nextjs/src/routeHandler.ts b/packages/nextjs/src/routeHandler.ts index 1d85b128..ab11c6d8 100644 --- a/packages/nextjs/src/routeHandler.ts +++ b/packages/nextjs/src/routeHandler.ts @@ -34,17 +34,22 @@ export const transformErrorResult = (data: ErrorData): [message: string, report: const statusCode = data.error instanceof Error ? getNextErrorStatusCode(data.error) : 500; const report = { - type: 'request', - method: data.req.method, - url: data.req.url, - statusCode: statusCode, - durationMs: data.end - data.start, - path: new URL(data.req.url).pathname, - endTime: data.end, - startTime: data.start, + request: { + type: 'request', + method: data.req.method, + url: data.req.url, + statusCode: statusCode, + durationMs: data.end - data.start, + path: new URL(data.req.url).pathname, + endTime: data.end, + startTime: data.start, + }, }; - return [`${data.req.method} ${report.path} ${report.statusCode} in ${report.endTime - report.startTime}ms`, report]; + return [ + `${data.req.method} ${report.request.path} ${report.request.statusCode} in ${report.request.endTime - report.request.startTime}ms`, + report, + ]; }; export interface BaseData { From 875acba7941addb3f8ba78796f090bfc82ee1ede Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Fri, 24 Jan 2025 16:12:59 +0000 Subject: [PATCH 25/71] refactor: replace logErrorByStatusCode with getLogLevelFromStatusCode --- examples/nextjs/src/app/api/route.ts | 4 ++-- packages/nextjs/src/routeHandler.ts | 21 ++++++++------------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/examples/nextjs/src/app/api/route.ts b/examples/nextjs/src/app/api/route.ts index 38d4d438..3bf5144f 100644 --- a/examples/nextjs/src/app/api/route.ts +++ b/examples/nextjs/src/app/api/route.ts @@ -1,7 +1,7 @@ import { logger } from '@/lib/axiom/server'; import { createAxiomRouteHandler, - logErrorByStatusCode, + getLogLevelFromStatusCode, transformErrorResult, transformSuccessResult, } from '@axiomhq/nextjs'; @@ -26,7 +26,7 @@ export const POST = axiomRouteHandler( } const [message, report] = transformErrorResult(result.data); - logger[logErrorByStatusCode(report.statusCode)](message, report); + logger.log(getLogLevelFromStatusCode(report.statusCode), message, report); } }, ); diff --git a/packages/nextjs/src/routeHandler.ts b/packages/nextjs/src/routeHandler.ts index ab11c6d8..a3113765 100644 --- a/packages/nextjs/src/routeHandler.ts +++ b/packages/nextjs/src/routeHandler.ts @@ -1,4 +1,4 @@ -import { Logger } from '@axiomhq/logging'; +import { Logger, LogLevel } from '@axiomhq/logging'; import { isRedirectError } from 'next/dist/client/components/redirect-error'; import { isHTTPAccessFallbackError } from 'next/dist/client/components/http-access-fallback/http-access-fallback'; import * as next from 'next/server'; @@ -86,18 +86,13 @@ export const getNextErrorStatusCode = (error: Error & { digest?: string }) => { } }; -export const logErrorByStatusCode = (statusCode: number) => { - switch (statusCode) { - case 404: - case 403: - case 401: - return 'warn'; - case 307: - case 308: - return 'info'; - default: - return 'error'; +export const getLogLevelFromStatusCode = (statusCode: number): LogLevel => { + if (statusCode >= 300 && statusCode < 400) { + return LogLevel.info; + } else if (statusCode >= 400 && statusCode < 500) { + return LogLevel.warn; } + return LogLevel.error; }; export const createDefaultAxiomHandlerCallback = (logger: Logger): axiomHandlerCallback => { @@ -108,7 +103,7 @@ export const createDefaultAxiomHandlerCallback = (logger: Logger): axiomHandlerC if (result.data.error instanceof Error) { logger.error(result.data.error.message, result.data.error); const [message, report] = transformErrorResult(result.data); - logger[logErrorByStatusCode(report.statusCode)](message, report); + logger.log(getLogLevelFromStatusCode(report.statusCode), message, report); } } }; From 1cb92839f9f59dd9e7d131e96d2d68b964358dc4 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Fri, 24 Jan 2025 16:59:39 +0000 Subject: [PATCH 26/71] fix: ensure request data shape match current next-axiom implementation --- packages/nextjs/src/routeHandler.ts | 36 +++++++++++++++++++---------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/packages/nextjs/src/routeHandler.ts b/packages/nextjs/src/routeHandler.ts index a3113765..e89d100c 100644 --- a/packages/nextjs/src/routeHandler.ts +++ b/packages/nextjs/src/routeHandler.ts @@ -10,17 +10,27 @@ export type NextHandler = ( arg?: T, ) => Promise | Promise | next.NextResponse | Response; +const getRegion = (req: next.NextRequest) => { + let region = ''; + if ('geo' in req) { + region = (req.geo as { region: string }).region ?? ''; + } + return region; +}; + export const transformSuccessResult = (data: SuccessData): [message: string, report: Record] => { const report = { request: { - type: 'request', + startTime: new Date().getTime(), + endTime: new Date().getTime(), + path: data.req.nextUrl.pathname ?? new URL(data.req.url).pathname, method: data.req.method, - url: data.req.url, + host: data.req.headers.get('host'), + userAgent: data.req.headers.get('user-agent'), + scheme: data.req.url.split('://')[0], + ip: data.req.headers.get('x-forwarded-for'), + region: getRegion(data.req), statusCode: data.res.status, - durationMs: data.end - data.start, - path: new URL(data.req.url).pathname, - endTime: data.end, - startTime: data.start, }, }; @@ -35,14 +45,16 @@ export const transformErrorResult = (data: ErrorData): [message: string, report: const report = { request: { - type: 'request', + startTime: new Date().getTime(), + endTime: new Date().getTime(), + path: data.req.nextUrl.pathname ?? new URL(data.req.url).pathname, method: data.req.method, - url: data.req.url, + host: data.req.headers.get('host'), + userAgent: data.req.headers.get('user-agent'), + scheme: data.req.url.split('://')[0], + ip: data.req.headers.get('x-forwarded-for'), + region: getRegion(data.req), statusCode: statusCode, - durationMs: data.end - data.start, - path: new URL(data.req.url).pathname, - endTime: data.end, - startTime: data.start, }, }; From e6820055d1111e1936ddf53ef6fb9d5390643cf9 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Fri, 24 Jan 2025 22:50:56 +0000 Subject: [PATCH 27/71] chore: add unit testing for nextjs package --- packages/nextjs/package.json | 6 +- packages/nextjs/test/lib/mock.ts | 15 ++ packages/nextjs/test/lib/setupNext.ts | 25 +++ .../nextjs/test/unit/instrumentation.test.ts | 72 ++++++++ packages/nextjs/test/unit/middleware.test.ts | 67 +++++++ .../test/unit/proxyRouteHandler.test.ts | 44 +++++ .../nextjs/test/unit/routeHandler.test.ts | 171 ++++++++++++++++++ packages/nextjs/vitest.config.ts | 10 + pnpm-lock.yaml | 68 +++---- 9 files changed, 429 insertions(+), 49 deletions(-) create mode 100644 packages/nextjs/test/lib/mock.ts create mode 100644 packages/nextjs/test/lib/setupNext.ts create mode 100644 packages/nextjs/test/unit/instrumentation.test.ts create mode 100644 packages/nextjs/test/unit/middleware.test.ts create mode 100644 packages/nextjs/test/unit/proxyRouteHandler.test.ts create mode 100644 packages/nextjs/test/unit/routeHandler.test.ts create mode 100644 packages/nextjs/vitest.config.ts diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 17b0ef46..3405bac4 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -20,7 +20,8 @@ "./package.json": "./package.json" }, "scripts": { - "build": "vite build" + "build": "vite build", + "test": "vitest run ./test/unit/*" }, "keywords": [], "author": "", @@ -31,7 +32,8 @@ "devDependencies": { "@tanstack/config": "^0.16.0", "next": "15.1.4", - "vite": "^5.2.14" + "vite": "^5.2.14", + "vitest": "^0.34.6" }, "peerDependencies": { "next": "15.1.4" diff --git a/packages/nextjs/test/lib/mock.ts b/packages/nextjs/test/lib/mock.ts new file mode 100644 index 00000000..674f40f8 --- /dev/null +++ b/packages/nextjs/test/lib/mock.ts @@ -0,0 +1,15 @@ +import { Logger } from '@axiomhq/logging'; +import { vi } from 'vitest'; + +// Mock Logger +export const mockLogger = { + raw: vi.fn(), + log: vi.fn(), + error: vi.fn(), + debug: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + with: vi.fn(), + flush: vi.fn(), +} as unknown as Logger; +// Mock after from next/server diff --git a/packages/nextjs/test/lib/setupNext.ts b/packages/nextjs/test/lib/setupNext.ts new file mode 100644 index 00000000..8a39bb2d --- /dev/null +++ b/packages/nextjs/test/lib/setupNext.ts @@ -0,0 +1,25 @@ +import { vi } from 'vitest'; + +const hoistedMockAfter = vi.hoisted(() => { + const mockAfter = vi.fn().mockImplementation((task: (() => any) | Promise) => { + if (task instanceof Promise) { + return task; + } + if (typeof task === 'function') { + const result = task(); + return result instanceof Promise ? result : Promise.resolve(result); + } + return Promise.resolve(task); + }); + return { + after: mockAfter, + }; +}); + +vi.mock('next/server', async () => { + const actual = (await vi.importActual('next/server')) as Record; + return { + ...(actual as Record), + after: hoistedMockAfter.after, + }; +}); diff --git a/packages/nextjs/test/unit/instrumentation.test.ts b/packages/nextjs/test/unit/instrumentation.test.ts new file mode 100644 index 00000000..fa1405df --- /dev/null +++ b/packages/nextjs/test/unit/instrumentation.test.ts @@ -0,0 +1,72 @@ +import { transformOnRequestError, createOnRequestError } from '../../src/instrumentation'; +import { describe, expect, it, vi } from 'vitest'; +import { InstrumentationOnRequestError } from 'next/dist/server/instrumentation/types'; +import { mockLogger } from '../lib/mock'; + +describe('instrumentation', () => { + const mockRequest: Parameters[1] = { + method: 'GET', + path: '/test', + headers: {}, + }; + const mockContext: Parameters[2] = { + routeType: 'render', + routerKind: 'App Router', + routePath: '/test', + revalidateReason: 'on-demand', + renderSource: 'react-server-components', + }; + + describe('transformOnRequestError', () => { + it('should transform Error objects correctly', () => { + const testError = new Error('Test error') as Error & { cause: string; digest: string }; + testError.cause = 'Test cause'; + testError.digest = 'test-digest'; + + const [message, report] = transformOnRequestError(testError, mockRequest, mockContext); + + expect(message).toBe('Test error'); + expect(report).toEqual({ + ...testError, + error: 'Error', + cause: 'Test cause', + stack: testError.stack, + digest: 'test-digest', + request: mockRequest, + context: mockContext, + }); + }); + + it('should transform non-Error objects correctly', () => { + const testError = 'String error'; + + const [message, report] = transformOnRequestError(testError, mockRequest, mockContext); + + expect(message).toBe('GET /test render'); + expect(report).toEqual({ + error: testError, + request: mockRequest, + context: mockContext, + }); + }); + }); + + describe('createOnRequestError', () => { + it('should create handler that logs errors and flushes', async () => { + const handler = createOnRequestError(mockLogger); + const testError = new Error('Test error'); + + await handler(testError, mockRequest, mockContext); + + expect(mockLogger.error).toHaveBeenCalledWith( + 'Test error', + expect.objectContaining({ + error: 'Error', + request: mockRequest, + context: mockContext, + }), + ); + expect(mockLogger.flush).toHaveBeenCalledOnce(); + }); + }); +}); diff --git a/packages/nextjs/test/unit/middleware.test.ts b/packages/nextjs/test/unit/middleware.test.ts new file mode 100644 index 00000000..75a76701 --- /dev/null +++ b/packages/nextjs/test/unit/middleware.test.ts @@ -0,0 +1,67 @@ +import { describe, it, expect } from 'vitest'; +import { NextRequest } from 'next/server'; +import { transformMiddlewareRequest } from '../../src/middleware'; + +describe('transformMiddlewareRequest', () => { + it('should transform a basic request correctly', () => { + const mockRequest = new NextRequest('https://example.com/test', { + headers: { + Referer: 'https://previous-page.com', + 'user-agent': 'Mozilla/5.0', + }, + }); + + Object.defineProperty(mockRequest, 'ip', { value: '127.0.0.1' }); + Object.defineProperty(mockRequest, 'geo', { value: { region: 'CA' } }); + + const [message, report] = transformMiddlewareRequest(mockRequest); + + expect(message).toBe('GET /test'); + expect(report).toEqual({ + request: { + ip: '127.0.0.1', + region: 'CA', + method: 'GET', + host: 'example.com', + path: '/test', + scheme: 'https', + referer: 'https://previous-page.com', + userAgent: 'Mozilla/5.0', + }, + }); + }); + + it('should handle missing optional fields', () => { + const mockRequest = new NextRequest('http://example.com/api/data', { + method: 'POST', + }); + + const [message, report] = transformMiddlewareRequest(mockRequest); + + expect(message).toBe('POST /api/data'); + expect(report).toEqual({ + request: { + ip: undefined, + region: undefined, + method: 'POST', + host: 'example.com', + path: '/api/data', + scheme: 'http', + referer: null, + userAgent: null, + }, + }); + }); + + it('should handle different HTTP methods and paths', () => { + const mockRequest = new NextRequest('https://api.example.com/users/123', { + method: 'DELETE', + }); + + const [message, report] = transformMiddlewareRequest(mockRequest); + + expect(message).toBe('DELETE /users/123'); + expect(report.request.method).toBe('DELETE'); + expect(report.request.path).toBe('/users/123'); + }); +}); diff --git a/packages/nextjs/test/unit/proxyRouteHandler.test.ts b/packages/nextjs/test/unit/proxyRouteHandler.test.ts new file mode 100644 index 00000000..e9788cf0 --- /dev/null +++ b/packages/nextjs/test/unit/proxyRouteHandler.test.ts @@ -0,0 +1,44 @@ +import { describe, it, expect, vi, afterAll } from 'vitest'; +import { createProxyRouteHandler } from '../../src/proxyRouteHandler'; +import { mockLogger } from '../lib/mock'; +import { NextRequest } from 'next/server'; + +describe('createProxyRouteHandler', () => { + afterAll(() => { + vi.clearAllMocks(); + }); + + it('should process log events successfully', async () => { + const handler = createProxyRouteHandler(mockLogger); + const mockEvents = [ + { level: 'info', message: 'test1' }, + { level: 'error', message: 'test2' }, + ]; + + const req = new NextRequest('http://example.com/test', { method: 'POST', body: JSON.stringify(mockEvents) }); + const response = await handler(req); + const responseBody = await response.json(); + + expect(mockLogger.raw).toHaveBeenCalledTimes(2); + expect(mockLogger.flush).toHaveBeenCalledTimes(1); + expect(responseBody).toEqual({ status: 'ok' }); + }); + + it('should handle errors and return 500 status', async () => { + const handler = createProxyRouteHandler(mockLogger); + const req = new NextRequest('http://example.com/test', { method: 'POST' }); + + // Mock json() to throw an error + req.json = () => Promise.reject(new Error('Invalid JSON')); + + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + const response = await handler(req); + const responseBody = await response.json(); + + expect(responseBody).toEqual({ status: 'error' }); + expect(response.status).toBe(500); + expect(consoleSpy).toHaveBeenCalled(); + + consoleSpy.mockRestore(); + }); +}); diff --git a/packages/nextjs/test/unit/routeHandler.test.ts b/packages/nextjs/test/unit/routeHandler.test.ts new file mode 100644 index 00000000..a65d7f8e --- /dev/null +++ b/packages/nextjs/test/unit/routeHandler.test.ts @@ -0,0 +1,171 @@ +import { describe, it, expect, vi } from 'vitest'; +import { NextRequest, NextResponse } from 'next/server'; +import { + createAxiomRouteHandler, + getLogLevelFromStatusCode, + getNextErrorStatusCode, + transformSuccessResult, + transformErrorResult, +} from '../../src/routeHandler'; +import { mockLogger } from '../lib/mock'; +import { LogLevel } from '@axiomhq/logging'; +import { forbidden, notFound, permanentRedirect, redirect, unauthorized } from 'next/navigation'; + +describe('routeHandler', () => { + describe('createAxiomRouteHandler', () => { + it('should handle successful requests', async () => { + const withAxiom = createAxiomRouteHandler(mockLogger); + const mockResponse = new NextResponse(null, { status: 200 }); + const handler = vi.fn().mockResolvedValue(mockResponse); + const wrappedHandler = withAxiom(handler); + + const mockReq = new NextRequest('http://example.com/test', { method: 'GET' }); + const result = await wrappedHandler(mockReq, {}); + + expect(handler).toHaveBeenCalledWith(mockReq, {}); + expect(result).toBe(mockResponse); + }); + + it('should handle errors and rethrow them', async () => { + const withAxiom = createAxiomRouteHandler(mockLogger); + const error = new Error('Test error'); + const handler = vi.fn().mockRejectedValue(error); + const wrappedHandler = withAxiom(handler); + + const mockReq = new NextRequest('http://example.com/test', { method: 'GET' }); + await expect(wrappedHandler(mockReq, {})).rejects.toThrow(error); + }); + }); + + describe('getLogLevelFromStatusCode', () => { + it('should return info for 3xx status codes', () => { + expect(getLogLevelFromStatusCode(301)).toBe(LogLevel.info); + expect(getLogLevelFromStatusCode(302)).toBe(LogLevel.info); + }); + + it('should return warn for 4xx status codes', () => { + expect(getLogLevelFromStatusCode(400)).toBe(LogLevel.warn); + expect(getLogLevelFromStatusCode(404)).toBe(LogLevel.warn); + }); + + it('should return error for 5xx status codes', () => { + expect(getLogLevelFromStatusCode(500)).toBe(LogLevel.error); + expect(getLogLevelFromStatusCode(503)).toBe(LogLevel.error); + }); + }); + + describe('getNextErrorStatusCode', () => { + const getErrorFromFunction = async (fn: () => void) => { + return (await (async () => fn())().catch((err) => err)) as Error & { digest?: string }; + }; + + it('should return 500 for errors without digest', () => { + const error = new Error('Test error'); + expect(getNextErrorStatusCode(error)).toBe(500); + }); + + it('should extract status code from temporary redirect error', async () => { + const error = await getErrorFromFunction(() => redirect('/path')); + expect(getNextErrorStatusCode(error)).toBe('307'); + }); + + it('should extract status code from permanent redirect error', async () => { + const error = await getErrorFromFunction(() => permanentRedirect('/path')); + expect(getNextErrorStatusCode(error)).toBe('308'); + }); + + it('should extract status code from not found error', async () => { + const error = await getErrorFromFunction(() => notFound()); + expect(getNextErrorStatusCode(error)).toBe('404'); + }); + + it('should extract status code from forbidden error', async () => { + const error = await getErrorFromFunction(() => forbidden()); + expect(getNextErrorStatusCode(error)).toBe('403'); + }); + + it('should extract status code from unahtorized error', async () => { + const error = await getErrorFromFunction(() => unauthorized()); + expect(getNextErrorStatusCode(error)).toBe('401'); + }); + }); + + describe('transform functions', () => { + const mockReq = new NextRequest('http://example.com/test', { + method: 'GET', + headers: { host: 'example.com', 'user-agent': 'test', 'x-forwarded-for': '127.0.0.1' }, + }); + + const testReport = { + method: 'GET', + path: '/test', + host: 'example.com', + userAgent: 'test', + scheme: 'http', + ip: '127.0.0.1', + region: '', + }; + + it('should transform success result correctly', () => { + const mockRes = new NextResponse(null, { status: 200 }); + const data = { + req: mockReq, + res: mockRes, + start: 1000, + end: 1100, + }; + + const [message, report] = transformSuccessResult(data); + + // Check message + expect(message).toMatch(/GET \/test 200 in \d+ms/); + + // Check report + expect(report.request).toMatchObject({ + ...testReport, + statusCode: 200, + }); + + // Check startTime + expect(report.request.startTime).toBeTypeOf('number'); + expect(new Date(report.request.startTime)).toBeInstanceOf(Date); + expect(Number.isNaN(new Date(report.request.startTime).getTime())).toBe(false); + + // Check endTime + expect(report.request.endTime).toBeTypeOf('number'); + expect(new Date(report.request.endTime)).toBeInstanceOf(Date); + expect(Number.isNaN(new Date(report.request.endTime).getTime())).toBe(false); + }); + + it('should transform error result correctly', () => { + const error = new Error('Test error'); + const data = { + req: mockReq, + error, + start: 1000, + end: 1100, + }; + + const [message, report] = transformErrorResult(data); + + // Check message + expect(message).toMatch(/GET \/test 500 in \d+ms/); + + // Check report + expect(report.request).toMatchObject({ + ...testReport, + statusCode: 500, + }); + + // Check startTime + expect(report.request.startTime).toBeTypeOf('number'); + expect(new Date(report.request.startTime)).toBeInstanceOf(Date); + expect(Number.isNaN(new Date(report.request.startTime).getTime())).toBe(false); + + // Check endTime + expect(report.request.endTime).toBeTypeOf('number'); + expect(new Date(report.request.endTime)).toBeInstanceOf(Date); + expect(Number.isNaN(new Date(report.request.endTime).getTime())).toBe(false); + }); + }); +}); diff --git a/packages/nextjs/vitest.config.ts b/packages/nextjs/vitest.config.ts new file mode 100644 index 00000000..7e21c5fe --- /dev/null +++ b/packages/nextjs/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig(() => ({ + test: { + setupFiles: ['./test/lib/setupNext.ts'], + env: { + __NEXT_EXPERIMENTAL_AUTH_INTERRUPTS: 'true', // Allows for forbidden() and unauthorized() to work + }, + }, +})); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d6893447..72430f93 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -310,6 +310,9 @@ importers: vite: specifier: ^5.2.14 version: 5.2.14(@types/node@20.14.2) + vitest: + specifier: ^0.34.6 + version: 0.34.6(@vitest/ui@0.32.4)(jsdom@24.1.0) packages/pino: dependencies: @@ -1763,9 +1766,6 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - confbox@0.1.7: - resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} - confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} @@ -2944,9 +2944,6 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - mlly@1.7.1: - resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} - mlly@1.7.4: resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} @@ -3221,9 +3218,6 @@ packages: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} - pkg-types@1.1.1: - resolution: {integrity: sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==} - pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} @@ -3936,9 +3930,6 @@ packages: uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} - ufo@1.5.3: - resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} - ufo@1.5.4: resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} @@ -4416,7 +4407,7 @@ snapshots: '@eslint/config-array@0.19.1': dependencies: '@eslint/object-schema': 2.1.5 - debug: 4.3.5 + debug: 4.4.0 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -4605,7 +4596,7 @@ snapshots: '@jridgewell/trace-mapping@0.3.9': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 '@kwsites/file-exists@1.1.1': dependencies: @@ -5075,7 +5066,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 8.21.0(typescript@5.4.5) '@typescript-eslint/utils': 8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.4.5) - debug: 4.3.5 + debug: 4.4.0 eslint: 9.18.0(jiti@2.4.2) ts-api-utils: 2.0.0(typescript@5.4.5) typescript: 5.4.5 @@ -5088,7 +5079,7 @@ snapshots: dependencies: '@typescript-eslint/types': 8.21.0 '@typescript-eslint/visitor-keys': 8.21.0 - debug: 4.3.5 + debug: 4.4.0 fast-glob: 3.3.2 is-glob: 4.0.3 minimatch: 9.0.4 @@ -5150,7 +5141,7 @@ snapshots: '@vitest/snapshot@0.34.6': dependencies: - magic-string: 0.30.10 + magic-string: 0.30.17 pathe: 1.1.2 pretty-format: 29.7.0 @@ -5228,7 +5219,7 @@ snapshots: '@vue/compiler-ssr': 3.4.27 '@vue/shared': 3.4.27 estree-walker: 2.0.2 - magic-string: 0.30.10 + magic-string: 0.30.17 postcss: 8.4.38 source-map-js: 1.2.0 @@ -5311,7 +5302,7 @@ snapshots: agent-base@7.1.1: dependencies: - debug: 4.3.5 + debug: 4.4.0 transitivePeerDependencies: - supports-color @@ -5639,8 +5630,6 @@ snapshots: concat-map@0.0.1: {} - confbox@0.1.7: {} - confbox@0.1.8: {} conventional-changelog-angular@7.0.0: @@ -6559,14 +6548,14 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.1 - debug: 4.3.5 + debug: 4.4.0 transitivePeerDependencies: - supports-color https-proxy-agent@7.0.4: dependencies: agent-base: 7.1.1 - debug: 4.3.5 + debug: 4.4.0 transitivePeerDependencies: - supports-color @@ -7072,13 +7061,6 @@ snapshots: minipass@7.1.2: {} - mlly@1.7.1: - dependencies: - acorn: 8.11.3 - pathe: 1.1.2 - pkg-types: 1.1.1 - ufo: 1.5.3 - mlly@1.7.4: dependencies: acorn: 8.14.0 @@ -7377,12 +7359,6 @@ snapshots: pirates@4.0.6: {} - pkg-types@1.1.1: - dependencies: - confbox: 0.1.7 - mlly: 1.7.1 - pathe: 1.1.2 - pkg-types@1.3.1: dependencies: confbox: 0.1.8 @@ -7426,7 +7402,7 @@ snapshots: postcss@8.4.31: dependencies: nanoid: 3.3.7 - picocolors: 1.0.1 + picocolors: 1.1.1 source-map-js: 1.2.0 postcss@8.4.38: @@ -7939,7 +7915,7 @@ snapshots: strip-literal@1.3.0: dependencies: - acorn: 8.11.3 + acorn: 8.14.0 styled-jsx@5.1.6(react@18.2.0): dependencies: @@ -8228,8 +8204,6 @@ snapshots: uc.micro@2.1.0: {} - ufo@1.5.3: {} - ufo@1.5.4: {} unbox-primitive@1.0.2: @@ -8287,10 +8261,10 @@ snapshots: vite-node@0.34.6(@types/node@20.14.2): dependencies: cac: 6.7.14 - debug: 4.3.5 - mlly: 1.7.1 + debug: 4.4.0 + mlly: 1.7.4 pathe: 1.1.2 - picocolors: 1.0.1 + picocolors: 1.1.1 vite: 5.2.14(@types/node@20.14.2) transitivePeerDependencies: - '@types/node' @@ -8356,15 +8330,15 @@ snapshots: '@vitest/snapshot': 0.34.6 '@vitest/spy': 0.34.6 '@vitest/utils': 0.34.6 - acorn: 8.11.3 + acorn: 8.14.0 acorn-walk: 8.3.2 cac: 6.7.14 chai: 4.4.1 - debug: 4.3.5 + debug: 4.4.0 local-pkg: 0.4.3 - magic-string: 0.30.10 + magic-string: 0.30.17 pathe: 1.1.2 - picocolors: 1.0.1 + picocolors: 1.1.1 std-env: 3.7.0 strip-literal: 1.3.0 tinybench: 2.8.0 From 33abade0b1830016c4010048d6deb01f217b6f73 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Sun, 26 Jan 2025 22:15:23 +0000 Subject: [PATCH 28/71] fix: use prettyPrint in console transport --- packages/logging/src/transports/console.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/logging/src/transports/console.ts b/packages/logging/src/transports/console.ts index d9cfc914..8bdea520 100644 --- a/packages/logging/src/transports/console.ts +++ b/packages/logging/src/transports/console.ts @@ -31,7 +31,7 @@ export class ConsoleTransport implements Transport { } log: Transport['log'] = (logs) => { logs.forEach((log) => { - console.log(log); + this.prettyPrint(log); }); }; From 163f31e79f652bc4782eebfa986eee46d56e93da Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Sun, 26 Jan 2025 22:22:23 +0000 Subject: [PATCH 29/71] test: add unit tests for logging package --- packages/logging/package.json | 4 +- packages/logging/src/logger.ts | 1 + packages/logging/test/lib/mock.ts | 32 +++ packages/logging/test/unit/logger.test.ts | 145 ++++++++++++ .../test/unit/transports/axiom-fetch.test.ts | 113 ++++++++++ .../test/unit/transports/axiom-js.test.ts | 43 ++++ .../test/unit/transports/console.test.ts | 118 ++++++++++ .../test/unit/transports/fetch.test.ts | 212 ++++++++++++++++++ .../unit/transports/proxy-transport.test.ts | 87 +++++++ pnpm-lock.yaml | 3 + 10 files changed, 757 insertions(+), 1 deletion(-) create mode 100644 packages/logging/test/lib/mock.ts create mode 100644 packages/logging/test/unit/logger.test.ts create mode 100644 packages/logging/test/unit/transports/axiom-fetch.test.ts create mode 100644 packages/logging/test/unit/transports/axiom-js.test.ts create mode 100644 packages/logging/test/unit/transports/console.test.ts create mode 100644 packages/logging/test/unit/transports/fetch.test.ts create mode 100644 packages/logging/test/unit/transports/proxy-transport.test.ts diff --git a/packages/logging/package.json b/packages/logging/package.json index fb7a45ce..3fb1cde3 100644 --- a/packages/logging/package.json +++ b/packages/logging/package.json @@ -19,7 +19,8 @@ } }, "scripts": { - "build": "vite build" + "build": "vite build", + "test": "vitest ./test/unit/*" }, "keywords": [], "author": "", @@ -29,6 +30,7 @@ }, "devDependencies": { "@tanstack/config": "^0.16.0", + "msw": "^2.6.2", "vite": "^5.2.14" } } diff --git a/packages/logging/src/logger.ts b/packages/logging/src/logger.ts index 789069e7..96ecbd26 100644 --- a/packages/logging/src/logger.ts +++ b/packages/logging/src/logger.ts @@ -34,6 +34,7 @@ export class Logger { constructor(public initConfig: LoggerConfig) { // check if user passed a log level, if not the default init value will be used as is. + // TODO: LogLevel currently does nothing if (this.initConfig.logLevel != undefined && this.initConfig.logLevel >= 0) { this.logLevel = this.initConfig.logLevel; } else if (LOG_LEVEL) { diff --git a/packages/logging/test/lib/mock.ts b/packages/logging/test/lib/mock.ts new file mode 100644 index 00000000..cd2a5cb1 --- /dev/null +++ b/packages/logging/test/lib/mock.ts @@ -0,0 +1,32 @@ +import { LogEvent } from '../../src'; +import { Transport } from '../../src'; + +export const createLogEvent = ( + level: string = 'info', + message: string = 'test message', + fields: any = {}, +): LogEvent => ({ + level, + message, + fields, + _time: new Date().toISOString(), + '@app': { + 'axiom-logging-version': 'test', + }, +}); + +export class MockTransport implements Transport { + public logs: LogEvent[] = []; + + log(events: LogEvent[]) { + this.logs.push(...events); + } + + async flush() { + return Promise.resolve(); + } + + clear() { + this.logs = []; + } +} diff --git a/packages/logging/test/unit/logger.test.ts b/packages/logging/test/unit/logger.test.ts new file mode 100644 index 00000000..fcd95e69 --- /dev/null +++ b/packages/logging/test/unit/logger.test.ts @@ -0,0 +1,145 @@ +import { Logger, LogLevel, LogEvent } from '../../src/logger'; +import { describe, beforeEach, afterEach, it, expect, vi } from 'vitest'; +import { MockTransport } from '../lib/mock'; + +describe('Logger', () => { + let mockTransport: MockTransport; + let logger: Logger; + + beforeEach(() => { + mockTransport = new MockTransport(); + logger = new Logger({ + transports: [mockTransport], + }); + }); + + afterEach(() => { + mockTransport.clear(); + }); + + describe('log levels', () => { + /* TODO: Add this back in once log level is implemented + it('should respect log level configuration', () => { + const warnLogger = new Logger({ + transports: [mockTransport], + }); + + warnLogger.debug('debug message'); + warnLogger.info('info message'); + warnLogger.warn('warn message'); + warnLogger.error('error message'); + + expect(mockTransport.logs).toHaveLength(2); // Only warn and error should be logged + expect(mockTransport.logs[0].level).toBe('warn'); + expect(mockTransport.logs[1].level).toBe('error'); + }); + */ + + it('should log all levels by default', () => { + logger.debug('debug message'); + logger.info('info message'); + logger.warn('warn message'); + logger.error('error message'); + + expect(mockTransport.logs).toHaveLength(4); + }); + }); + + describe('with method', () => { + it('should create child logger with merged args', () => { + const childLogger = logger.with({ userId: '123' }); + childLogger.info('user action'); + + expect(mockTransport.logs).toHaveLength(1); + expect(mockTransport.logs[0].fields).toEqual({ + userId: '123', + }); + }); + + it('should merge multiple with calls', () => { + const childLogger = logger.with({ userId: '123' }).with({ action: 'login' }); + + childLogger.info('user login'); + + expect(mockTransport.logs).toHaveLength(1); + expect(mockTransport.logs[0].fields).toEqual({ + userId: '123', + action: 'login', + }); + }); + }); + + describe('error handling', () => { + it('should properly format Error objects', () => { + const error = new Error('test error'); + logger.error('An error occurred', error); + + expect(mockTransport.logs).toHaveLength(1); + expect(mockTransport.logs[0].fields).toMatchObject({ + message: 'test error', + name: 'Error', + stack: expect.any(String), + }); + }); + + it('should handle custom error properties', () => { + const customError = new Error('custom error'); + (customError as any).code = 'CUSTOM_ERROR'; + + logger.error('Custom error occurred', customError); + + expect(mockTransport.logs).toHaveLength(1); + expect(mockTransport.logs[0].fields).toMatchObject({ + message: 'custom error', + code: 'CUSTOM_ERROR', + name: 'Error', + stack: expect.any(String), + }); + }); + }); + + describe('flush', () => { + it('should flush all transports', async () => { + const mockTransport2 = new MockTransport(); + const multiLogger = new Logger({ + transports: [mockTransport, mockTransport2], + }); + + const flushSpy1 = vi.spyOn(mockTransport, 'flush'); + const flushSpy2 = vi.spyOn(mockTransport2, 'flush'); + + await multiLogger.flush(); + + expect(flushSpy1).toHaveBeenCalled(); + expect(flushSpy2).toHaveBeenCalled(); + }); + + it('should flush child loggers', async () => { + logger.with({}).log(LogLevel.info, 'child message'); + const flushSpy = vi.spyOn(mockTransport, 'flush'); + + await logger.flush(); + + expect(flushSpy).toHaveBeenCalledTimes(2); + }); + }); + + describe('raw logging', () => { + it('should pass raw log events directly to transport', () => { + const rawEvent: LogEvent = { + level: 'info', + message: 'raw message', + fields: { custom: 'field' }, + _time: new Date().toISOString(), + '@app': { + 'axiom-logging-version': 'test', + }, + }; + + logger.raw(rawEvent); + + expect(mockTransport.logs).toHaveLength(1); + expect(mockTransport.logs[0]).toEqual(rawEvent); + }); + }); +}); diff --git a/packages/logging/test/unit/transports/axiom-fetch.test.ts b/packages/logging/test/unit/transports/axiom-fetch.test.ts new file mode 100644 index 00000000..300add4c --- /dev/null +++ b/packages/logging/test/unit/transports/axiom-fetch.test.ts @@ -0,0 +1,113 @@ +import { describe, afterEach, it, expect, beforeAll, afterAll } from 'vitest'; +import { AxiomFetchTransport } from '../../../src/transports/axiom-fetch'; +import { http, HttpResponse, HttpHandler } from 'msw'; +import { setupServer } from 'msw/node'; +import { createLogEvent } from '../../lib/mock'; + +describe('AxiomFetchTransport', () => { + const DATASET = 'test-dataset'; + const TOKEN = 'test-token'; + const DEFAULT_URL = 'https://api.axiom.co'; + const CUSTOM_URL = 'https://custom.axiom.co'; + + const handlers: HttpHandler[] = [ + http.post(`${DEFAULT_URL}/v1/datasets/${DATASET}/ingest`, async ({ request }) => { + return HttpResponse.json({ success: true }); + }), + http.post(`${CUSTOM_URL}/v1/datasets/${DATASET}/ingest`, async ({ request }) => { + return HttpResponse.json({ success: true }); + }), + ]; + + const server = setupServer(...handlers); + + beforeAll(() => server.listen()); + afterAll(() => server.close()); + afterEach(() => server.resetHandlers()); + + describe('configuration', () => { + it('should use default URL when no custom URL provided', async () => { + let receivedUrl = ''; + server.use( + http.post('*', async ({ request }) => { + receivedUrl = request.url; + return HttpResponse.json({ success: true }); + }), + ); + + const transport = new AxiomFetchTransport({ + dataset: DATASET, + token: TOKEN, + }); + + transport.log([createLogEvent()]); + await transport.flush(); + + expect(receivedUrl).toBe(`${DEFAULT_URL}/v1/datasets/${DATASET}/ingest`); + }); + + it('should use custom URL when provided', async () => { + let receivedUrl = ''; + server.use( + http.post('*', async ({ request }) => { + receivedUrl = request.url; + return HttpResponse.json({ success: true }); + }), + ); + + const transport = new AxiomFetchTransport({ + dataset: DATASET, + token: TOKEN, + url: CUSTOM_URL, + }); + + transport.log([createLogEvent()]); + await transport.flush(); + + expect(receivedUrl).toBe(`${CUSTOM_URL}/v1/datasets/${DATASET}/ingest`); + }); + + it('should set correct authorization header', async () => { + let receivedHeaders: Headers = new Headers(); + server.use( + http.post('*', async ({ request }) => { + receivedHeaders = request.headers; + return HttpResponse.json({ success: true }); + }), + ); + + const transport = new AxiomFetchTransport({ + dataset: DATASET, + token: TOKEN, + }); + + transport.log([createLogEvent()]); + await transport.flush(); + + expect(receivedHeaders.get('Authorization')).toBe(`Bearer ${TOKEN}`); + expect(receivedHeaders.get('Content-Type')).toBe('application/json'); + }); + + it('should respect autoFlush configuration', async () => { + let requestCount = 0; + server.use( + http.post('*', async () => { + requestCount++; + return HttpResponse.json({ success: true }); + }), + ); + + const transport = new AxiomFetchTransport({ + dataset: DATASET, + token: TOKEN, + autoFlush: false, + }); + + transport.log([createLogEvent()]); + expect(requestCount).toBe(0); + + await transport.flush(); + expect(requestCount).toBe(1); + }); + }); +}); diff --git a/packages/logging/test/unit/transports/axiom-js.test.ts b/packages/logging/test/unit/transports/axiom-js.test.ts new file mode 100644 index 00000000..29ad430f --- /dev/null +++ b/packages/logging/test/unit/transports/axiom-js.test.ts @@ -0,0 +1,43 @@ +import { describe, beforeEach, it, expect, vi } from 'vitest'; +import { AxiomJSTransport } from '../../../src/transports/axiom-js'; +import { createLogEvent } from '../../lib/mock'; + +describe('AxiomJSTransport', () => { + let mockAxiom: any; + let transport: AxiomJSTransport; + const DATASET = 'test-dataset'; + + beforeEach(() => { + mockAxiom = { + ingest: vi.fn(), + flush: vi.fn().mockResolvedValue(undefined), + }; + transport = new AxiomJSTransport(mockAxiom, DATASET); + }); + + describe('log', () => { + it('should forward logs to axiom client ingest method', () => { + const logs = [createLogEvent('first'), createLogEvent('second')]; + + transport.log(logs); + + expect(mockAxiom.ingest).toHaveBeenCalledTimes(1); + expect(mockAxiom.ingest).toHaveBeenCalledWith(DATASET, logs); + }); + + it('should handle empty log array', () => { + transport.log([]); + + expect(mockAxiom.ingest).toHaveBeenCalledTimes(1); + expect(mockAxiom.ingest).toHaveBeenCalledWith(DATASET, []); + }); + }); + + describe('flush', () => { + it('should call flush on axiom client', async () => { + await transport.flush(); + + expect(mockAxiom.flush).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/packages/logging/test/unit/transports/console.test.ts b/packages/logging/test/unit/transports/console.test.ts new file mode 100644 index 00000000..0511153e --- /dev/null +++ b/packages/logging/test/unit/transports/console.test.ts @@ -0,0 +1,118 @@ +import { describe, beforeEach, afterEach, it, expect, vi, SpyInstance } from 'vitest'; +import { ConsoleTransport } from '../../../src/transports/console'; +import * as shared from '../../../src/shared'; +import { createLogEvent } from '../../lib/mock'; + +describe('ConsoleTransport', () => { + let consoleSpy: SpyInstance; + let transport: ConsoleTransport; + + beforeEach(() => { + consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); + }); + + afterEach(() => { + consoleSpy.mockRestore(); + }); + + describe('basic logging', () => { + beforeEach(() => { + transport = new ConsoleTransport({ prettyPrint: false }); + }); + + it('should log message with level', () => { + const event = createLogEvent('info', 'test message'); + transport.log([event]); + + expect(consoleSpy).toHaveBeenCalledWith('info - test message'); + }); + + it('should include fields in log output', () => { + const event = createLogEvent('info', 'test message', { userId: '123' }); + transport.log([event]); + + expect(consoleSpy).toHaveBeenCalledWith('info - test message {"userId":"123"}'); + }); + + it('should handle multiple log events', () => { + const events = [createLogEvent('info', 'first message'), createLogEvent('error', 'second message')]; + transport.log(events); + + expect(consoleSpy).toHaveBeenCalledTimes(2); + expect(consoleSpy).toHaveBeenNthCalledWith(1, 'info - first message'); + expect(consoleSpy).toHaveBeenNthCalledWith(2, 'error - second message'); + }); + }); + + describe('pretty print', () => { + beforeEach(() => { + transport = new ConsoleTransport({ prettyPrint: true }); + }); + + describe('browser formatting', () => { + beforeEach(() => { + vi.spyOn(shared, 'isBrowser', 'get').mockReturnValue(true); + }); + + it('should format info level with correct color', () => { + transport.log([createLogEvent('info', 'test message')]); + + expect(consoleSpy).toHaveBeenCalledWith('%c%s - %s', 'color: lightgreen;', 'info', 'test message'); + }); + + it('should format error level with correct color', () => { + transport.log([createLogEvent('error', 'test message')]); + + expect(consoleSpy).toHaveBeenCalledWith('%c%s - %s', 'color: red;', 'error', 'test message'); + }); + + it('should include fields as object in browser', () => { + const fields = { userId: '123', action: 'login' }; + transport.log([createLogEvent('info', 'test message', fields)]); + + expect(consoleSpy).toHaveBeenCalledWith('%c%s - %s %o', 'color: lightgreen;', 'info', 'test message', fields); + }); + }); + + describe('terminal formatting', () => { + beforeEach(() => { + vi.spyOn(shared, 'isBrowser', 'get').mockReturnValue(false); + }); + + it('should format info level with correct color code', () => { + transport.log([createLogEvent('info', 'test message')]); + + expect(consoleSpy).toHaveBeenCalledWith('\x1b[32m%s\x1b[0m - %s', 'info', 'test message'); + }); + + it('should format error level with correct color code', () => { + transport.log([createLogEvent('error', 'test message')]); + + expect(consoleSpy).toHaveBeenCalledWith('\x1b[31m%s\x1b[0m - %s', 'error', 'test message'); + }); + + it('should include fields as object in terminal', () => { + const fields = { userId: '123', action: 'login' }; + transport.log([createLogEvent('info', 'test message', fields)]); + + expect(consoleSpy).toHaveBeenCalledWith('\x1b[32m%s\x1b[0m - %s %o', 'info', 'test message', fields); + }); + }); + + it('should handle all log levels with correct colors', () => { + const levels = ['debug', 'info', 'warn', 'error']; + levels.forEach((level) => { + consoleSpy.mockClear(); + transport.log([createLogEvent(level, 'test message')]); + expect(consoleSpy).toHaveBeenCalledTimes(1); + }); + }); + }); + + describe('flush', () => { + it('should resolve immediately', async () => { + transport = new ConsoleTransport(); + expect(transport.flush()).toBeUndefined(); + }); + }); +}); diff --git a/packages/logging/test/unit/transports/fetch.test.ts b/packages/logging/test/unit/transports/fetch.test.ts new file mode 100644 index 00000000..eb55777a --- /dev/null +++ b/packages/logging/test/unit/transports/fetch.test.ts @@ -0,0 +1,212 @@ +import { describe, beforeEach, afterEach, it, expect, vi, beforeAll, afterAll } from 'vitest'; +import { SimpleFetchTransport } from '../../../src/transports/fetch'; +import { LogEvent } from '../../../src'; +import { http, HttpResponse, HttpHandler } from 'msw'; +import { setupServer } from 'msw/node'; +import { createLogEvent } from '../../lib/mock'; + +describe('SimpleFetchTransport', () => { + let transport: SimpleFetchTransport; + const API_URL = 'https://api.example.com/logs'; + + const handlers: HttpHandler[] = [ + http.post(API_URL, async ({ request }) => { + const body = await request.json(); + return HttpResponse.json({ success: true, receivedLogs: body }); + }), + ]; + + const server = setupServer(...handlers); + + beforeEach(() => { + vi.useFakeTimers(); + }); + + beforeAll(() => { + server.listen(); + }); + + afterAll(() => { + server.close(); + }); + + afterEach(() => { + server.resetHandlers(); + vi.clearAllTimers(); + vi.useRealTimers(); + }); + + describe('basic functionality', () => { + beforeEach(() => { + transport = new SimpleFetchTransport({ + input: API_URL, + autoFlush: false, + }); + }); + + it('should not flush automatically when autoFlush is false', async () => { + const requestSpy = vi.fn(); + server.use( + http.post(API_URL, async () => { + requestSpy(); + return HttpResponse.json({ success: true }); + }), + ); + + transport.log([createLogEvent()]); + await vi.runAllTimersAsync(); + + expect(requestSpy).not.toHaveBeenCalled(); + }); + + it('should flush logs when manually called', async () => { + let receivedBody: any; + server.use( + http.post(API_URL, async ({ request }) => { + receivedBody = await request.json(); + return HttpResponse.json({ success: true }); + }), + ); + + const logEvent = createLogEvent(); + transport.log([logEvent]); + await transport.flush(); + + expect(receivedBody).toEqual([logEvent]); + }); + + it('should not make request when there are no events to flush', async () => { + const requestSpy = vi.fn(); + server.use( + http.post(API_URL, async () => { + requestSpy(); + return HttpResponse.json({ success: true }); + }), + ); + + await transport.flush(); + expect(requestSpy).not.toHaveBeenCalled(); + }); + + it('should handle request errors gracefully', async () => { + const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + server.use( + http.post(API_URL, () => { + return HttpResponse.error(); + }), + ); + + transport.log([createLogEvent()]); + await transport.flush(); + + expect(consoleErrorSpy).toHaveBeenCalled(); + consoleErrorSpy.mockRestore(); + }); + }); + + describe('auto-flush behavior', () => { + it('should auto-flush after default delay when autoFlush is true', async () => { + const requestSpy = vi.fn(); + server.use( + http.post(API_URL, async () => { + requestSpy(); + return HttpResponse.json({ success: true }); + }), + ); + + transport = new SimpleFetchTransport({ + input: API_URL, + autoFlush: true, + }); + + transport.log([createLogEvent()]); + + // Default delay is 2000ms + await vi.advanceTimersByTimeAsync(2000); + + expect(requestSpy).toHaveBeenCalledTimes(1); + }); + + it('should auto-flush after custom delay', async () => { + const requestSpy = vi.fn(); + server.use( + http.post(API_URL, async () => { + requestSpy(); + return HttpResponse.json({ success: true }); + }), + ); + + transport = new SimpleFetchTransport({ + input: API_URL, + autoFlush: 1000, + }); + + transport.log([createLogEvent()]); + + await vi.advanceTimersByTimeAsync(999); + expect(requestSpy).not.toHaveBeenCalled(); + + await vi.advanceTimersByTimeAsync(1); + expect(requestSpy).toHaveBeenCalledTimes(1); + }); + + it('should reset auto-flush timer when new logs are added', async () => { + let receivedBody: any; + server.use( + http.post(API_URL, async ({ request }) => { + receivedBody = await request.json(); + return HttpResponse.json({ success: true }); + }), + ); + + transport = new SimpleFetchTransport({ + input: API_URL, + autoFlush: 1000, + }); + + transport.log([createLogEvent('first')]); + + await vi.advanceTimersByTimeAsync(500); + transport.log([createLogEvent('second')]); + + await vi.advanceTimersByTimeAsync(500); + expect(receivedBody).toBeUndefined(); + + await vi.advanceTimersByTimeAsync(500); + expect(receivedBody).toBeDefined(); + expect(receivedBody[0].message).toBe('first'); + expect(receivedBody[1].message).toBe('second'); + }); + }); + + describe('custom fetch configuration', () => { + it('should respect custom fetch init options', async () => { + let receivedHeaders: Headers = new Headers(); + server.use( + http.post(API_URL, async ({ request }) => { + receivedHeaders = request.headers; + return HttpResponse.json({ success: true }); + }), + ); + + transport = new SimpleFetchTransport({ + input: API_URL, + init: { + headers: { + 'Content-Type': 'application/json', + 'X-Custom-Header': 'test', + }, + credentials: 'include', + }, + }); + + transport.log([createLogEvent()]); + await transport.flush(); + + console.log(receivedHeaders); + + expect(receivedHeaders.get('X-Custom-Header')).toBe('test'); + expect(receivedHeaders.get('Content-Type')).toBe('application/json'); + }); + }); +}); diff --git a/packages/logging/test/unit/transports/proxy-transport.test.ts b/packages/logging/test/unit/transports/proxy-transport.test.ts new file mode 100644 index 00000000..0c7ff406 --- /dev/null +++ b/packages/logging/test/unit/transports/proxy-transport.test.ts @@ -0,0 +1,87 @@ +import { describe, afterEach, it, expect, beforeAll, afterAll, vi } from 'vitest'; +import { AxiomProxyTransport } from '../../../src/transports/proxy-transport'; +import { http, HttpResponse, HttpHandler } from 'msw'; +import { setupServer } from 'msw/node'; +import { createLogEvent } from '../../lib/mock'; + +describe('AxiomProxyTransport', () => { + const PROXY_URL = 'https://proxy.example.com/logs'; + + const handlers: HttpHandler[] = [ + http.post(PROXY_URL, async ({ request }) => { + return HttpResponse.json({ success: true }); + }), + ]; + + const server = setupServer(...handlers); + + beforeAll(() => server.listen()); + afterAll(() => server.close()); + afterEach(() => server.resetHandlers()); + + describe('configuration', () => { + it('should send logs to configured proxy URL', async () => { + let receivedUrl = ''; + let receivedBody: any; + + server.use( + http.post('*', async ({ request }) => { + receivedUrl = request.url; + receivedBody = await request.json(); + return HttpResponse.json({ success: true }); + }), + ); + + const transport = new AxiomProxyTransport({ + url: PROXY_URL, + }); + + const logEvent = createLogEvent(); + transport.log([logEvent]); + await transport.flush(); + + expect(receivedUrl).toBe(PROXY_URL); + expect(receivedBody).toEqual([logEvent]); + }); + + it('should respect autoFlush configuration', async () => { + let requestCount = 0; + server.use( + http.post('*', async () => { + requestCount++; + return HttpResponse.json({ success: true }); + }), + ); + + const transport = new AxiomProxyTransport({ + url: PROXY_URL, + autoFlush: false, + }); + + transport.log([createLogEvent()]); + expect(requestCount).toBe(0); + + await transport.flush(); + expect(requestCount).toBe(1); + }); + + it('should handle request errors gracefully', async () => { + const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + server.use( + http.post(PROXY_URL, () => { + return HttpResponse.error(); + }), + ); + + const transport = new AxiomProxyTransport({ + url: PROXY_URL, + }); + + transport.log([createLogEvent()]); + await transport.flush(); + + expect(consoleErrorSpy).toHaveBeenCalled(); + consoleErrorSpy.mockRestore(); + }); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 72430f93..fa58c541 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -291,6 +291,9 @@ importers: '@tanstack/config': specifier: ^0.16.0 version: 0.16.0(@types/node@20.14.2)(esbuild@0.20.2)(eslint@9.18.0(jiti@2.4.2))(rollup@4.22.4)(typescript@5.4.5)(vite@5.2.14(@types/node@20.14.2)) + msw: + specifier: ^2.6.2 + version: 2.6.2(@types/node@20.14.2)(typescript@5.4.5) vite: specifier: ^5.2.14 version: 5.2.14(@types/node@20.14.2) From eb565a0ef8f7aa4a5e7224b9600eeb3ec6ca996f Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Mon, 27 Jan 2025 12:52:08 +0000 Subject: [PATCH 30/71] test: add unit tests for react package --- packages/react/package.json | 12 +- packages/react/src/helpers.ts | 12 + packages/react/src/index.ts | 10 +- packages/react/src/use-logger.ts | 4 + packages/react/test/lib/setup.ts | 13 + packages/react/test/unit/helpers.test.ts | 19 + packages/react/test/unit/use-logger.test.ts | 83 +++ packages/react/vitest.config.ts | 12 + pnpm-lock.yaml | 627 +++++++++++++++++++- 9 files changed, 777 insertions(+), 15 deletions(-) create mode 100644 packages/react/src/helpers.ts create mode 100644 packages/react/test/lib/setup.ts create mode 100644 packages/react/test/unit/helpers.test.ts create mode 100644 packages/react/test/unit/use-logger.test.ts create mode 100644 packages/react/vitest.config.ts diff --git a/packages/react/package.json b/packages/react/package.json index d6bcc391..cd9dd929 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -20,7 +20,9 @@ "./package.json": "./package.json" }, "scripts": { - "build": "vite build" + "build": "vite build", + "test": "vitest", + "test:watch": "vitest watch" }, "keywords": [], "author": "", @@ -36,6 +38,12 @@ }, "devDependencies": { "@tanstack/config": "^0.16.0", - "vite": "^5.2.14" + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.2.0", + "@testing-library/react-hooks": "^8.0.1", + "@vitejs/plugin-react": "^4.3.4", + "happy-dom": "^16.7.3", + "vite": "^5.2.14", + "vitest": "^0.34.6" } } diff --git a/packages/react/src/helpers.ts b/packages/react/src/helpers.ts new file mode 100644 index 00000000..de30d637 --- /dev/null +++ b/packages/react/src/helpers.ts @@ -0,0 +1,12 @@ +import { Logger } from '@axiomhq/logging'; +import { createUseLogger } from './use-logger'; + +export const createClientSideHelpers = (logger: Logger) => { + if (!logger) { + throw new Error('A logger must be provided to create client side helpers'); + } + + return { + useLogger: createUseLogger(logger), + }; +}; diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 22f77168..8bd7db7c 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -1,8 +1,2 @@ -import { Logger } from '@axiomhq/logging'; -import { createUseLogger } from './use-logger'; - -export const createClientSideHelpers = (logger: Logger) => { - return { - useLogger: createUseLogger(logger), - }; -}; +export * from './helpers'; +export * from './use-logger'; diff --git a/packages/react/src/use-logger.ts b/packages/react/src/use-logger.ts index 522c59f8..97fff6f2 100644 --- a/packages/react/src/use-logger.ts +++ b/packages/react/src/use-logger.ts @@ -2,6 +2,10 @@ import { Logger } from '@axiomhq/logging'; import { useEffect, useState } from 'react'; export function createUseLogger(logger: Logger) { + if (!logger) { + throw new Error('A logger must be provided to create useLogger'); + } + const useLogger = () => { const [path, setPath] = useState(typeof window !== 'undefined' ? window.location.pathname : ''); diff --git a/packages/react/test/lib/setup.ts b/packages/react/test/lib/setup.ts new file mode 100644 index 00000000..0e383ce2 --- /dev/null +++ b/packages/react/test/lib/setup.ts @@ -0,0 +1,13 @@ +import '@testing-library/jest-dom'; +import { vi } from 'vitest'; + +// Mock window location +Object.defineProperty(window, 'location', { + value: { + pathname: '/initial-path', + }, + writable: true, +}); + +// Mock history pushState +window.history.pushState = vi.fn(); diff --git a/packages/react/test/unit/helpers.test.ts b/packages/react/test/unit/helpers.test.ts new file mode 100644 index 00000000..2fd23080 --- /dev/null +++ b/packages/react/test/unit/helpers.test.ts @@ -0,0 +1,19 @@ +import { describe, it, expect } from 'vitest'; +import { createClientSideHelpers } from '../../src/helpers'; +import { Logger } from '@axiomhq/logging'; + +describe('createClientSideHelpers', () => { + it('should throw an error if no logger is provided', () => { + expect(() => createClientSideHelpers(undefined as unknown as Logger)).toThrow( + 'A logger must be provided to create client side helpers', + ); + }); + + it('should return an object with useLogger function', () => { + const mockLogger = {} as Logger; + const helpers = createClientSideHelpers(mockLogger); + + expect(helpers).toHaveProperty('useLogger'); + expect(typeof helpers.useLogger).toBe('function'); + }); +}); diff --git a/packages/react/test/unit/use-logger.test.ts b/packages/react/test/unit/use-logger.test.ts new file mode 100644 index 00000000..199cace3 --- /dev/null +++ b/packages/react/test/unit/use-logger.test.ts @@ -0,0 +1,83 @@ +import { renderHook, act } from '@testing-library/react'; +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { createUseLogger } from '../../src/use-logger'; +import { Logger } from '@axiomhq/logging'; + +describe('Logger hook', () => { + let mockLogger: Logger; + + beforeEach(() => { + mockLogger = { + flush: vi.fn(), + } as unknown as Logger; + + // Reset window.location.pathname + Object.defineProperty(window, 'location', { + value: { pathname: '/initial-path' }, + writable: true, + }); + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + describe('createUseLogger', () => { + it('should return a hook function', () => { + const mockLogger = {} as Logger; + const useLogger = createUseLogger(mockLogger); + + expect(typeof useLogger).toBe('function'); + }); + + it('should throw if no logger is provided', () => { + const logger = undefined as unknown as Logger; + + expect(() => createUseLogger(logger)).toThrow('A logger must be provided to create useLogger'); + }); + + it('should accept a valid logger instance', () => { + const mockLogger = { + flush: () => Promise.resolve(), + info: () => {}, + error: () => {}, + } as unknown as Logger; + + expect(() => createUseLogger(mockLogger)).not.toThrow(); + }); + }); + + describe('useLogger', () => { + it('should return the logger instance', () => { + const useLogger = createUseLogger(mockLogger); + const { result } = renderHook(() => useLogger()); + + expect(result.current).toBe(mockLogger); + }); + + it('should call logger.flush when path changes', async () => { + const useLogger = createUseLogger(mockLogger); + const { unmount } = renderHook(() => useLogger()); + + // Simulate path change + act(() => { + window.dispatchEvent(new Event('popstate')); + }); + + unmount(); + expect(mockLogger.flush).toHaveBeenCalled(); + }); + + it('should clean up event listeners on unmount', () => { + const removeEventListenerSpy = vi.spyOn(window, 'removeEventListener'); + const useLogger = createUseLogger(mockLogger); + const { unmount } = renderHook(() => useLogger()); + + unmount(); + + expect(removeEventListenerSpy).toHaveBeenCalledWith('popstate', expect.any(Function)); + expect(removeEventListenerSpy).toHaveBeenCalledWith('pushState', expect.any(Function)); + expect(removeEventListenerSpy).toHaveBeenCalledWith('replaceState', expect.any(Function)); + }); + }); +}); diff --git a/packages/react/vitest.config.ts b/packages/react/vitest.config.ts new file mode 100644 index 00000000..3c629bba --- /dev/null +++ b/packages/react/vitest.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'vitest/config'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + test: { + globals: true, + environment: 'happy-dom', + setupFiles: ['./test/lib/setup.ts'], + include: ['./test/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fa58c541..0bdd6555 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -55,7 +55,7 @@ importers: version: 5.4.5 vitest: specifier: ^0.34.6 - version: 0.34.6(@vitest/ui@0.32.4)(jsdom@24.1.0) + version: 0.34.6(@vitest/ui@0.32.4)(happy-dom@16.7.3)(jsdom@24.1.0) winston: specifier: ^3.14.2 version: 3.14.2 @@ -315,7 +315,7 @@ importers: version: 5.2.14(@types/node@20.14.2) vitest: specifier: ^0.34.6 - version: 0.34.6(@vitest/ui@0.32.4)(jsdom@24.1.0) + version: 0.34.6(@vitest/ui@0.32.4)(happy-dom@16.7.3)(jsdom@24.1.0) packages/pino: dependencies: @@ -357,9 +357,27 @@ importers: '@tanstack/config': specifier: ^0.16.0 version: 0.16.0(@types/node@20.14.2)(esbuild@0.20.2)(eslint@9.18.0(jiti@2.4.2))(rollup@4.22.4)(typescript@5.4.5)(vite@5.2.14(@types/node@20.14.2)) + '@testing-library/jest-dom': + specifier: ^6.6.3 + version: 6.6.3 + '@testing-library/react': + specifier: ^16.2.0 + version: 16.2.0(@testing-library/dom@9.3.4)(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@testing-library/react-hooks': + specifier: ^8.0.1 + version: 8.0.1(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@vitejs/plugin-react': + specifier: ^4.3.4 + version: 4.3.4(vite@5.2.14(@types/node@20.14.2)) + happy-dom: + specifier: ^16.7.3 + version: 16.7.3 vite: specifier: ^5.2.14 version: 5.2.14(@types/node@20.14.2) + vitest: + specifier: ^0.34.6 + version: 0.34.6(@vitest/ui@0.32.4)(happy-dom@16.7.3)(jsdom@24.1.0) packages/winston: dependencies: @@ -379,6 +397,9 @@ importers: packages: + '@adobe/css-tools@4.4.1': + resolution: {integrity: sha512-12WGKBQzjUAI4ayyF4IAtfw2QR/IDoqk6jTddXDhtYTJF9ASmoE1zst7cVtP0aL/F1jUJL5r+JxKXKEgHNbEUQ==} + '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -391,23 +412,106 @@ packages: resolution: {integrity: sha512-Ytf5V3wKz8FKNiqJxnqZmUhjgJ7TItKUoyHVNE/H2V9dN1ozD6NNnsueenOjKdA48cm2sGRyP432nworst18aA==} engines: {node: '>=16'} + '@babel/code-frame@7.26.2': + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.26.5': + resolution: {integrity: sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.26.7': + resolution: {integrity: sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.26.5': + resolution: {integrity: sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.26.5': + resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.25.9': + resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.26.0': + resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.26.5': + resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==} + engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.24.7': resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.25.9': + resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.24.7': resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.25.9': + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.25.9': + resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.26.7': + resolution: {integrity: sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==} + engines: {node: '>=6.9.0'} + '@babel/parser@7.24.7': resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==} engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.26.7': + resolution: {integrity: sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.25.9': + resolution: {integrity: sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.25.9': + resolution: {integrity: sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.26.7': + resolution: {integrity: sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.25.9': + resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.26.7': + resolution: {integrity: sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==} + engines: {node: '>=6.9.0'} + '@babel/types@7.24.7': resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} engines: {node: '>=6.9.0'} + '@babel/types@7.26.7': + resolution: {integrity: sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==} + engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -1221,6 +1325,45 @@ packages: engines: {node: '>=18'} hasBin: true + '@testing-library/dom@9.3.4': + resolution: {integrity: sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==} + engines: {node: '>=14'} + + '@testing-library/jest-dom@6.6.3': + resolution: {integrity: sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + + '@testing-library/react-hooks@8.0.1': + resolution: {integrity: sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==} + engines: {node: '>=12'} + peerDependencies: + '@types/react': ^16.9.0 || ^17.0.0 + react: ^16.9.0 || ^17.0.0 + react-dom: ^16.9.0 || ^17.0.0 + react-test-renderer: ^16.9.0 || ^17.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + react-dom: + optional: true + react-test-renderer: + optional: true + + '@testing-library/react@16.2.0': + resolution: {integrity: sha512-2cSskAvA1QNtKc8Y9VJQRv0tm3hLVgxRGDB+KYhIaPQJ1I+RHbhIXcM+zClKXzMes/wshsMVzf4B9vS4IZpqDQ==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@tsconfig/node10@1.0.11': resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} @@ -1236,6 +1379,21 @@ packages: '@types/argparse@1.0.38': resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.6.8': + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.20.6': + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + '@types/chai-subset@1.3.5': resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} @@ -1348,6 +1506,12 @@ packages: resolution: {integrity: sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vitejs/plugin-react@4.3.4': + resolution: {integrity: sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 + '@vitejs/plugin-vue@5.0.5': resolution: {integrity: sha512-LOjm7XeIimLBZyzinBQ6OSm3UBCNVCpLkxGC0oWmm2YPzVZoxMsdvNVimLTBzpAnR9hl/yn1SHGuRfe6/Td9rQ==} engines: {node: ^18.0.0 || >=20.0.0} @@ -1548,6 +1712,9 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-query@5.1.3: + resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} + aria-query@5.3.2: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} @@ -1651,6 +1818,11 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + browserslist@4.24.4: + resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -1689,10 +1861,17 @@ packages: caniuse-lite@1.0.30001632: resolution: {integrity: sha512-udx3o7yHJfUxMLkGohMlVHCvFvWmirKh9JAH/d7WOLPetlH+LTL5cocMZ0t7oZx/mdlOWXti97xLZWc8uURRHg==} + caniuse-lite@1.0.30001695: + resolution: {integrity: sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==} + chai@4.4.1: resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} engines: {node: '>=4'} + chalk@3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -1795,6 +1974,9 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -1874,6 +2056,10 @@ packages: resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} engines: {node: '>=6'} + deep-equal@2.2.3: + resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} + engines: {node: '>= 0.4'} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -1923,6 +2109,12 @@ packages: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dot-prop@5.3.0: resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} engines: {node: '>=8'} @@ -1938,6 +2130,9 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + electron-to-chromium@1.5.88: + resolution: {integrity: sha512-K3C2qf1o+bGzbilTDCTBhTQcMS9KW60yTAaTeeXsfvQuTDDwlokLam/AdqlqcSy9u4UainDgsHV23ksXAOgamw==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1975,6 +2170,9 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} + es-get-iterator@1.1.3: + resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} + es-iterator-helpers@1.2.1: resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} engines: {node: '>= 0.4'} @@ -2312,6 +2510,10 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -2366,6 +2568,10 @@ packages: resolution: {integrity: sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==} engines: {node: '>=0.10.0'} + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -2398,6 +2604,10 @@ packages: resolution: {integrity: sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + happy-dom@16.7.3: + resolution: {integrity: sha512-76uiE9jCpC849cOyYZ8YBROpPcstW/hwCKoQYd3aiZaxHeR9zdjpup4z7qYEWbt+lY8Rb3efW2gmrckyoBftKg==} + engines: {node: '>=18.0.0'} + has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} @@ -2485,6 +2695,10 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -2511,6 +2725,10 @@ packages: resolution: {integrity: sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==} engines: {node: '>=0.10.0'} + is-arguments@1.2.0: + resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} + engines: {node: '>= 0.4'} + is-array-buffer@3.0.4: resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} engines: {node: '>= 0.4'} @@ -2771,6 +2989,11 @@ packages: canvas: optional: true + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -2787,6 +3010,11 @@ packages: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} @@ -2867,6 +3095,9 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -2874,6 +3105,10 @@ packages: lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + magic-string@0.30.10: resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} @@ -2926,6 +3161,10 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + minimatch@3.0.8: resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} @@ -3035,6 +3274,9 @@ packages: sass: optional: true + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -3057,6 +3299,10 @@ packages: resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} engines: {node: '>= 0.4'} + object-is@1.1.6: + resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} + engines: {node: '>= 0.4'} + object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -3286,6 +3532,10 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + pretty-format@29.7.0: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3330,12 +3580,25 @@ packages: peerDependencies: react: ^19.0.0 + react-error-boundary@3.1.4: + resolution: {integrity: sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==} + engines: {node: '>=10', npm: '>=6'} + peerDependencies: + react: '>=16.13.1' + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react-refresh@0.14.2: + resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} + engines: {node: '>=0.10.0'} + react@18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} @@ -3367,6 +3630,10 @@ packages: resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} engines: {node: '>= 10.13.0'} + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -3375,6 +3642,9 @@ packages: resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} engines: {node: '>= 0.4'} + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + regexp.prototype.flags@1.5.2: resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} engines: {node: '>= 0.4'} @@ -3599,6 +3869,10 @@ packages: std-env@3.7.0: resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -3663,6 +3937,10 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -3962,6 +4240,12 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} + update-browserslist-db@1.1.2: + resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -4125,6 +4409,10 @@ packages: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + whatwg-mimetype@4.0.0: resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} engines: {node: '>=18'} @@ -4224,6 +4512,9 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} @@ -4258,6 +4549,8 @@ packages: snapshots: + '@adobe/css-tools@4.4.1': {} + '@alloc/quick-lru@5.2.0': {} '@ampproject/remapping@2.3.0': @@ -4270,20 +4563,134 @@ snapshots: fetch-retry: 6.0.0 uuid: 11.0.2 + '@babel/code-frame@7.26.2': + dependencies: + '@babel/helper-validator-identifier': 7.25.9 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.26.5': {} + + '@babel/core@7.26.7': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.5 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.7) + '@babel/helpers': 7.26.7 + '@babel/parser': 7.26.7 + '@babel/template': 7.25.9 + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 + convert-source-map: 2.0.0 + debug: 4.4.0 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.26.5': + dependencies: + '@babel/parser': 7.26.7 + '@babel/types': 7.26.7 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.26.5': + dependencies: + '@babel/compat-data': 7.26.5 + '@babel/helper-validator-option': 7.25.9 + browserslist: 4.24.4 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-module-imports@7.25.9': + dependencies: + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.7)': + dependencies: + '@babel/core': 7.26.7 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.26.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.26.5': {} + '@babel/helper-string-parser@7.24.7': {} + '@babel/helper-string-parser@7.25.9': {} + '@babel/helper-validator-identifier@7.24.7': {} + '@babel/helper-validator-identifier@7.25.9': {} + + '@babel/helper-validator-option@7.25.9': {} + + '@babel/helpers@7.26.7': + dependencies: + '@babel/template': 7.25.9 + '@babel/types': 7.26.7 + '@babel/parser@7.24.7': dependencies: '@babel/types': 7.24.7 + '@babel/parser@7.26.7': + dependencies: + '@babel/types': 7.26.7 + + '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.7)': + dependencies: + '@babel/core': 7.26.7 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.7)': + dependencies: + '@babel/core': 7.26.7 + '@babel/helper-plugin-utils': 7.26.5 + + '@babel/runtime@7.26.7': + dependencies: + regenerator-runtime: 0.14.1 + + '@babel/template@7.25.9': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/parser': 7.26.7 + '@babel/types': 7.26.7 + + '@babel/traverse@7.26.7': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.5 + '@babel/parser': 7.26.7 + '@babel/template': 7.25.9 + '@babel/types': 7.26.7 + debug: 4.4.0 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + '@babel/types@7.24.7': dependencies: '@babel/helper-string-parser': 7.24.7 '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 + '@babel/types@7.26.7': + dependencies: + '@babel/helper-string-parser': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@bcoe/v8-coverage@0.2.3': {} '@bundled-es-modules/cookie@2.0.1': @@ -4964,6 +5371,46 @@ snapshots: - typescript - vite + '@testing-library/dom@9.3.4': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/runtime': 7.26.7 + '@types/aria-query': 5.0.4 + aria-query: 5.1.3 + chalk: 4.1.2 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@6.6.3': + dependencies: + '@adobe/css-tools': 4.4.1 + aria-query: 5.3.2 + chalk: 3.0.0 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + lodash: 4.17.21 + redent: 3.0.0 + + '@testing-library/react-hooks@8.0.1(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.7 + react: 19.0.0 + react-error-boundary: 3.1.4(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.7 + react-dom: 19.0.0(react@19.0.0) + + '@testing-library/react@16.2.0(@testing-library/dom@9.3.4)(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@babel/runtime': 7.26.7 + '@testing-library/dom': 9.3.4 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.7 + '@types/react-dom': 19.0.3(@types/react@19.0.7) + '@tsconfig/node10@1.0.11': {} '@tsconfig/node12@1.0.11': {} @@ -4974,6 +5421,29 @@ snapshots: '@types/argparse@1.0.38': {} + '@types/aria-query@5.0.4': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.6 + + '@types/babel__generator@7.6.8': + dependencies: + '@babel/types': 7.24.7 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + + '@types/babel__traverse@7.20.6': + dependencies: + '@babel/types': 7.24.7 + '@types/chai-subset@1.3.5': dependencies: '@types/chai': 4.3.16 @@ -5108,6 +5578,17 @@ snapshots: '@typescript-eslint/types': 8.21.0 eslint-visitor-keys: 4.2.0 + '@vitejs/plugin-react@4.3.4(vite@5.2.14(@types/node@20.14.2))': + dependencies: + '@babel/core': 7.26.7 + '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.7) + '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.7) + '@types/babel__core': 7.20.5 + react-refresh: 0.14.2 + vite: 5.2.14(@types/node@20.14.2) + transitivePeerDependencies: + - supports-color + '@vitejs/plugin-vue@5.0.5(vite@5.2.14(@types/node@20.14.2))(vue@3.4.27(typescript@5.4.5))': dependencies: vite: 5.2.14(@types/node@20.14.2) @@ -5126,7 +5607,7 @@ snapshots: std-env: 3.7.0 test-exclude: 6.0.0 v8-to-istanbul: 9.2.0 - vitest: 0.34.6(@vitest/ui@0.32.4)(jsdom@24.1.0) + vitest: 0.34.6(@vitest/ui@0.32.4)(happy-dom@16.7.3)(jsdom@24.1.0) transitivePeerDependencies: - supports-color @@ -5161,7 +5642,7 @@ snapshots: pathe: 1.1.2 picocolors: 1.0.1 sirv: 2.0.4 - vitest: 0.34.6(@vitest/ui@0.32.4)(jsdom@24.1.0) + vitest: 0.34.6(@vitest/ui@0.32.4)(happy-dom@16.7.3)(jsdom@24.1.0) '@vitest/utils@0.32.4': dependencies: @@ -5371,6 +5852,10 @@ snapshots: argparse@2.0.1: {} + aria-query@5.1.3: + dependencies: + deep-equal: 2.2.3 + aria-query@5.3.2: {} array-buffer-byte-length@1.0.1: @@ -5496,6 +5981,13 @@ snapshots: dependencies: fill-range: 7.1.1 + browserslist@4.24.4: + dependencies: + caniuse-lite: 1.0.30001695 + electron-to-chromium: 1.5.88 + node-releases: 2.0.19 + update-browserslist-db: 1.1.2(browserslist@4.24.4) + buffer@6.0.3: dependencies: base64-js: 1.5.1 @@ -5538,6 +6030,8 @@ snapshots: caniuse-lite@1.0.30001632: {} + caniuse-lite@1.0.30001695: {} + chai@4.4.1: dependencies: assertion-error: 1.1.0 @@ -5548,6 +6042,11 @@ snapshots: pathval: 1.1.1 type-detect: 4.0.8 + chalk@3.0.0: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -5658,6 +6157,8 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + css.escape@1.5.1: {} + cssesc@3.0.0: {} cssstyle@4.0.1: @@ -5729,6 +6230,27 @@ snapshots: dependencies: type-detect: 4.0.8 + deep-equal@2.2.3: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + es-get-iterator: 1.1.3 + get-intrinsic: 1.2.7 + is-arguments: 1.2.0 + is-array-buffer: 3.0.5 + is-date-object: 1.1.0 + is-regex: 1.2.1 + is-shared-array-buffer: 1.0.4 + isarray: 2.0.5 + object-is: 1.1.6 + object-keys: 1.1.1 + object.assign: 4.1.7 + regexp.prototype.flags: 1.5.4 + side-channel: 1.1.0 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.18 + deep-is@0.1.4: {} define-data-property@1.1.4: @@ -5768,6 +6290,10 @@ snapshots: dependencies: esutils: 2.0.3 + dom-accessibility-api@0.5.16: {} + + dom-accessibility-api@0.6.3: {} + dot-prop@5.3.0: dependencies: is-obj: 2.0.0 @@ -5782,6 +6308,8 @@ snapshots: eastasianwidth@0.2.0: {} + electron-to-chromium@1.5.88: {} + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -5906,6 +6434,18 @@ snapshots: es-errors@1.3.0: {} + es-get-iterator@1.1.3: + dependencies: + call-bind: 1.0.8 + get-intrinsic: 1.2.7 + has-symbols: 1.1.0 + is-arguments: 1.2.0 + is-map: 2.0.3 + is-set: 2.0.3 + is-string: 1.1.1 + isarray: 2.0.5 + stop-iteration-iterator: 1.1.0 + es-iterator-helpers@1.2.1: dependencies: call-bind: 1.0.8 @@ -6399,6 +6939,8 @@ snapshots: functions-have-names@1.2.3: {} + gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: {} get-func-name@2.0.2: {} @@ -6485,6 +7027,8 @@ snapshots: is-windows: 1.0.2 which: 1.3.1 + globals@11.12.0: {} + globals@14.0.0: {} globals@15.14.0: {} @@ -6508,6 +7052,11 @@ snapshots: graphql@16.9.0: {} + happy-dom@16.7.3: + dependencies: + webidl-conversions: 7.0.0 + whatwg-mimetype: 3.0.0 + has-bigints@1.0.2: {} has-flag@4.0.0: {} @@ -6581,6 +7130,8 @@ snapshots: imurmurhash@0.1.4: {} + indent-string@4.0.0: {} + inflight@1.0.6: dependencies: once: 1.4.0 @@ -6609,6 +7160,11 @@ snapshots: is-relative: 1.0.0 is-windows: 1.0.2 + is-arguments@1.2.0: + dependencies: + call-bound: 1.0.3 + has-tostringtag: 1.0.2 + is-array-buffer@3.0.4: dependencies: call-bind: 1.0.7 @@ -6887,6 +7443,8 @@ snapshots: - supports-color - utf-8-validate + jsesc@3.1.0: {} + json-buffer@3.0.1: {} json-schema-traverse@0.4.1: {} @@ -6899,6 +7457,8 @@ snapshots: dependencies: minimist: 1.2.8 + json5@2.2.3: {} + jsonfile@4.0.0: optionalDependencies: graceful-fs: 4.2.11 @@ -6989,12 +7549,18 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + lru-cache@6.0.0: dependencies: yallist: 4.0.0 lunr@2.3.9: {} + lz-string@1.5.0: {} + magic-string@0.30.10: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 @@ -7044,6 +7610,8 @@ snapshots: dependencies: mime-db: 1.52.0 + min-indent@1.0.1: {} + minimatch@3.0.8: dependencies: brace-expansion: 1.1.11 @@ -7168,6 +7736,8 @@ snapshots: - '@babel/core' - babel-plugin-macros + node-releases@2.0.19: {} + normalize-path@3.0.0: {} nwsapi@2.2.10: {} @@ -7180,6 +7750,11 @@ snapshots: object-inspect@1.13.3: {} + object-is@1.1.6: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + object-keys@1.1.1: {} object.assign@4.1.5: @@ -7424,6 +7999,12 @@ snapshots: prettier@3.3.2: {} + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + pretty-format@29.7.0: dependencies: '@jest/schemas': 29.6.3 @@ -7463,10 +8044,19 @@ snapshots: react: 19.0.0 scheduler: 0.25.0 + react-error-boundary@3.1.4(react@19.0.0): + dependencies: + '@babel/runtime': 7.26.7 + react: 19.0.0 + react-is@16.13.1: {} + react-is@17.0.2: {} + react-is@18.3.1: {} + react-refresh@0.14.2: {} + react@18.2.0: dependencies: loose-envify: 1.4.0 @@ -7501,6 +8091,11 @@ snapshots: dependencies: resolve: 1.22.8 + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -7522,6 +8117,8 @@ snapshots: globalthis: 1.0.4 which-builtin-type: 1.1.3 + regenerator-runtime@0.14.1: {} + regexp.prototype.flags@1.5.2: dependencies: call-bind: 1.0.7 @@ -7819,6 +8416,11 @@ snapshots: std-env@3.7.0: {} + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + streamsearch@1.1.0: {} strict-event-emitter@0.5.1: {} @@ -7914,6 +8516,10 @@ snapshots: strip-bom@3.0.0: {} + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + strip-json-comments@3.1.1: {} strip-literal@1.3.0: @@ -8233,6 +8839,12 @@ snapshots: universalify@2.0.1: {} + update-browserslist-db@1.1.2(browserslist@4.24.4): + dependencies: + browserslist: 4.24.4 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -8323,7 +8935,7 @@ snapshots: '@types/node': 20.14.2 fsevents: 2.3.3 - vitest@0.34.6(@vitest/ui@0.32.4)(jsdom@24.1.0): + vitest@0.34.6(@vitest/ui@0.32.4)(happy-dom@16.7.3)(jsdom@24.1.0): dependencies: '@types/chai': 4.3.16 '@types/chai-subset': 1.3.5 @@ -8351,6 +8963,7 @@ snapshots: why-is-node-running: 2.2.2 optionalDependencies: '@vitest/ui': 0.32.4(vitest@0.34.6) + happy-dom: 16.7.3 jsdom: 24.1.0 transitivePeerDependencies: - less @@ -8417,6 +9030,8 @@ snapshots: dependencies: iconv-lite: 0.6.3 + whatwg-mimetype@3.0.0: {} + whatwg-mimetype@4.0.0: {} whatwg-url@14.0.0: @@ -8558,6 +9173,8 @@ snapshots: y18n@5.0.8: {} + yallist@3.1.1: {} + yallist@4.0.0: {} yaml@2.7.0: {} From 03b18c6522e1fce86c253ee5ff6e3ca09ca69470 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Mon, 27 Jan 2025 13:01:46 +0000 Subject: [PATCH 31/71] fix: test issue fetch transport --- packages/logging/test/unit/transports/fetch.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/logging/test/unit/transports/fetch.test.ts b/packages/logging/test/unit/transports/fetch.test.ts index eb55777a..7675b16e 100644 --- a/packages/logging/test/unit/transports/fetch.test.ts +++ b/packages/logging/test/unit/transports/fetch.test.ts @@ -164,10 +164,10 @@ describe('SimpleFetchTransport', () => { autoFlush: 1000, }); - transport.log([createLogEvent('first')]); + transport.log([createLogEvent('info', 'first')]); await vi.advanceTimersByTimeAsync(500); - transport.log([createLogEvent('second')]); + transport.log([createLogEvent('info', 'second')]); await vi.advanceTimersByTimeAsync(500); expect(receivedBody).toBeUndefined(); From b2d5444fda07f1df5f21364573abaad7b0cb00c9 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 28 Jan 2025 15:28:23 +0000 Subject: [PATCH 32/71] fix: error properties handling --- packages/logging/src/logger.ts | 4 +++- packages/logging/test/unit/transports/fetch.test.ts | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/logging/src/logger.ts b/packages/logging/src/logger.ts index 96ecbd26..adf36965 100644 --- a/packages/logging/src/logger.ts +++ b/packages/logging/src/logger.ts @@ -85,7 +85,9 @@ export class Logger { stack: args.stack, name: args.name, }; - } else if (typeof args === 'object' && args !== null && Object.keys(args).length > 0) { + } + + if (typeof args === 'object' && args !== null && Object.keys(args).length > 0) { const parsedArgs = JSON.parse(JSON.stringify(args, jsonFriendlyErrorReplacer)); logEvent.fields = { ...logEvent.fields, ...parsedArgs }; } else if (args && args.length) { diff --git a/packages/logging/test/unit/transports/fetch.test.ts b/packages/logging/test/unit/transports/fetch.test.ts index 7675b16e..2e71c893 100644 --- a/packages/logging/test/unit/transports/fetch.test.ts +++ b/packages/logging/test/unit/transports/fetch.test.ts @@ -203,8 +203,6 @@ describe('SimpleFetchTransport', () => { transport.log([createLogEvent()]); await transport.flush(); - console.log(receivedHeaders); - expect(receivedHeaders.get('X-Custom-Header')).toBe('test'); expect(receivedHeaders.get('Content-Type')).toBe('application/json'); }); From 6b12dfba048f39105d1ae7b9fb5298ce49506794 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 28 Jan 2025 15:42:09 +0000 Subject: [PATCH 33/71] ci: update cache directory --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 06fb2a8e..1ba535f5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: - name: Cache turbo build setup uses: actions/cache@v4 with: - path: .turbo + path: ./node_modules/.cache/turbo key: ${{ runner.os }}-turbo-${{ github.sha }} restore-keys: | ${{ runner.os }}-turbo- @@ -68,7 +68,7 @@ jobs: - name: Cache turbo build setup uses: actions/cache@v4 with: - path: .turbo + path: ./node_modules/.cache/turbo key: ${{ runner.os }}-turbo-${{ github.sha }} restore-keys: | ${{ runner.os }}-turbo- From 6ecbaec1eeac59d590b189fa26a9a7ebe1b6441c Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 28 Jan 2025 15:48:40 +0000 Subject: [PATCH 34/71] ci: add cache to e2e and integration tests --- .github/workflows/ci.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ba535f5..49474df2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -118,6 +118,13 @@ jobs: with: node-version: ${{ matrix.node }} cache: 'pnpm' + - name: Cache turbo build setup + uses: actions/cache@v4 + with: + path: ./node_modules/.cache/turbo + key: ${{ runner.os }}-turbo-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-turbo- - run: pnpm install --frozen-lockfile - run: pnpm build && pnpm build:cjs - env: @@ -165,6 +172,13 @@ jobs: with: node-version: ${{ matrix.node }} cache: 'pnpm' + - name: Cache turbo build setup + uses: actions/cache@v4 + with: + path: ./node_modules/.cache/turbo + key: ${{ runner.os }}-turbo-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-turbo- - run: pnpm install --frozen-lockfile - run: pnpm build && pnpm build:cjs # cjs build needed to run tests - env: From db9076e01b3acdb0e16fe7e1b6acf9242464d7de Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 28 Jan 2025 22:48:58 +0000 Subject: [PATCH 35/71] chore: add eslint configuration --- .github/workflows/ci.yml | 2 +- packages/logging/eslint.config.mjs | 3 +++ packages/logging/package.json | 3 +++ packages/nextjs/eslint.config.mjs | 3 +++ packages/nextjs/package.json | 3 +++ packages/react/eslint.config.mjs | 3 +++ packages/react/package.json | 3 +++ pnpm-lock.yaml | 9 +++++++++ 8 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 packages/logging/eslint.config.mjs create mode 100644 packages/nextjs/eslint.config.mjs create mode 100644 packages/react/eslint.config.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 49474df2..248a5379 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,7 @@ jobs: ${{ runner.os }}-turbo- - run: pnpm install --frozen-lockfile - run: pnpm build && pnpm build:cjs - # - run: pnpm lint # TODO: fix linting + - run: pnpm lint - run: pnpm test preview-release: diff --git a/packages/logging/eslint.config.mjs b/packages/logging/eslint.config.mjs new file mode 100644 index 00000000..992f5589 --- /dev/null +++ b/packages/logging/eslint.config.mjs @@ -0,0 +1,3 @@ +import { config } from '@repo/eslint-config/base'; + +export default config; diff --git a/packages/logging/package.json b/packages/logging/package.json index 3fb1cde3..36cc2825 100644 --- a/packages/logging/package.json +++ b/packages/logging/package.json @@ -20,6 +20,8 @@ }, "scripts": { "build": "vite build", + "format": "eslint . --fix", + "lint": "eslint .", "test": "vitest ./test/unit/*" }, "keywords": [], @@ -29,6 +31,7 @@ "@axiomhq/js": "^1.3.1" }, "devDependencies": { + "@repo/eslint-config": "workspace:^", "@tanstack/config": "^0.16.0", "msw": "^2.6.2", "vite": "^5.2.14" diff --git a/packages/nextjs/eslint.config.mjs b/packages/nextjs/eslint.config.mjs new file mode 100644 index 00000000..992f5589 --- /dev/null +++ b/packages/nextjs/eslint.config.mjs @@ -0,0 +1,3 @@ +import { config } from '@repo/eslint-config/base'; + +export default config; diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 3405bac4..9d7e0266 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -21,6 +21,8 @@ }, "scripts": { "build": "vite build", + "format": "eslint . --fix", + "lint": "eslint .", "test": "vitest run ./test/unit/*" }, "keywords": [], @@ -30,6 +32,7 @@ "@axiomhq/logging": "workspace:^" }, "devDependencies": { + "@repo/eslint-config": "workspace:^", "@tanstack/config": "^0.16.0", "next": "15.1.4", "vite": "^5.2.14", diff --git a/packages/react/eslint.config.mjs b/packages/react/eslint.config.mjs new file mode 100644 index 00000000..992f5589 --- /dev/null +++ b/packages/react/eslint.config.mjs @@ -0,0 +1,3 @@ +import { config } from '@repo/eslint-config/base'; + +export default config; diff --git a/packages/react/package.json b/packages/react/package.json index cd9dd929..6a380366 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -21,6 +21,8 @@ }, "scripts": { "build": "vite build", + "format": "eslint . --fix", + "lint": "eslint .", "test": "vitest", "test:watch": "vitest watch" }, @@ -37,6 +39,7 @@ "web-vitals": "^4.2.4" }, "devDependencies": { + "@repo/eslint-config": "workspace:^", "@tanstack/config": "^0.16.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0bdd6555..9483bbae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -288,6 +288,9 @@ importers: specifier: ^1.3.1 version: 1.3.1 devDependencies: + '@repo/eslint-config': + specifier: workspace:^ + version: link:../../internal/eslint-config '@tanstack/config': specifier: ^0.16.0 version: 0.16.0(@types/node@20.14.2)(esbuild@0.20.2)(eslint@9.18.0(jiti@2.4.2))(rollup@4.22.4)(typescript@5.4.5)(vite@5.2.14(@types/node@20.14.2)) @@ -304,6 +307,9 @@ importers: specifier: workspace:^ version: link:../logging devDependencies: + '@repo/eslint-config': + specifier: workspace:^ + version: link:../../internal/eslint-config '@tanstack/config': specifier: ^0.16.0 version: 0.16.0(@types/node@20.14.2)(esbuild@0.20.2)(eslint@9.18.0(jiti@2.4.2))(rollup@4.22.4)(typescript@5.4.5)(vite@5.2.14(@types/node@20.14.2)) @@ -354,6 +360,9 @@ importers: specifier: ^4.2.4 version: 4.2.4 devDependencies: + '@repo/eslint-config': + specifier: workspace:^ + version: link:../../internal/eslint-config '@tanstack/config': specifier: ^0.16.0 version: 0.16.0(@types/node@20.14.2)(esbuild@0.20.2)(eslint@9.18.0(jiti@2.4.2))(rollup@4.22.4)(typescript@5.4.5)(vite@5.2.14(@types/node@20.14.2)) From 61155220826f4c0a73e96658176877cf085c9c66 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Wed, 29 Jan 2025 12:36:14 +0000 Subject: [PATCH 36/71] chore: use workspace:* instead of ^ --- examples/js/package.json | 2 +- examples/nextjs/package.json | 2 +- examples/node/package.json | 2 +- examples/pino/package.json | 2 +- examples/winston/package.json | 2 +- integration/package.json | 42 +++++++++++++++++------------------ packages/logging/package.json | 2 +- packages/nextjs/package.json | 4 ++-- packages/pino/package.json | 2 +- packages/react/package.json | 2 +- pnpm-lock.yaml | 22 +++++++++--------- 11 files changed, 42 insertions(+), 42 deletions(-) diff --git a/examples/js/package.json b/examples/js/package.json index 15701e94..ad8740c0 100644 --- a/examples/js/package.json +++ b/examples/js/package.json @@ -14,6 +14,6 @@ "winston": "^3.14.2" }, "devDependencies": { - "@repo/eslint-config": "workspace:^" + "@repo/eslint-config": "workspace:*" } } diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index d08851bd..647cd881 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@axiomhq/logging": "workspace:*", - "@axiomhq/nextjs": "workspace:^", + "@axiomhq/nextjs": "workspace:*", "@axiomhq/react": "workspace:*", "next": "15.1.4", "react": "^19.0.0", diff --git a/examples/node/package.json b/examples/node/package.json index 18e760de..8f82f4f3 100644 --- a/examples/node/package.json +++ b/examples/node/package.json @@ -10,6 +10,6 @@ "@axiomhq/js": "workspace:*" }, "devDependencies": { - "@repo/eslint-config": "workspace:^" + "@repo/eslint-config": "workspace:*" } } diff --git a/examples/pino/package.json b/examples/pino/package.json index a3c0ed52..e8bb46b5 100644 --- a/examples/pino/package.json +++ b/examples/pino/package.json @@ -14,6 +14,6 @@ "pino": "^9.5.0" }, "devDependencies": { - "@repo/eslint-config": "workspace:^" + "@repo/eslint-config": "workspace:*" } } diff --git a/examples/winston/package.json b/examples/winston/package.json index d090bba0..f5b41e8d 100644 --- a/examples/winston/package.json +++ b/examples/winston/package.json @@ -15,6 +15,6 @@ "winston": "^3.14.2" }, "devDependencies": { - "@repo/eslint-config": "workspace:^" + "@repo/eslint-config": "workspace:*" } } diff --git a/integration/package.json b/integration/package.json index a3c3e5a7..d341af94 100644 --- a/integration/package.json +++ b/integration/package.json @@ -1,22 +1,22 @@ { - "name": "integration-tests", - "private": true, - "description": "Integration tests for axiom-js", - "version": "0.12.0", - "main": "dist/cjs/index.js", - "types": "dist/types/index.d.ts", - "scripts": { - "build": "tsc -b", - "format": "eslint 'src/**/*.{js,ts}' --quiet --fix", - "lint": "eslint 'src/**/*.{js,ts}'", - "integration": "vitest run src/*" - }, - "dependencies": { - "@axiomhq/js": "workspace:*", - "@axiomhq/winston": "workspace:*", - "winston-transport": "^4.5.0" - }, - "devDependencies": { - "@repo/eslint-config": "workspace:^" - } -} \ No newline at end of file + "name": "integration-tests", + "private": true, + "description": "Integration tests for axiom-js", + "version": "0.12.0", + "main": "dist/cjs/index.js", + "types": "dist/types/index.d.ts", + "scripts": { + "build": "tsc -b", + "format": "eslint 'src/**/*.{js,ts}' --quiet --fix", + "lint": "eslint 'src/**/*.{js,ts}'", + "integration": "vitest run src/*" + }, + "dependencies": { + "@axiomhq/js": "workspace:*", + "@axiomhq/winston": "workspace:*", + "winston-transport": "^4.5.0" + }, + "devDependencies": { + "@repo/eslint-config": "workspace:*" + } +} diff --git a/packages/logging/package.json b/packages/logging/package.json index 36cc2825..3dd597b2 100644 --- a/packages/logging/package.json +++ b/packages/logging/package.json @@ -31,7 +31,7 @@ "@axiomhq/js": "^1.3.1" }, "devDependencies": { - "@repo/eslint-config": "workspace:^", + "@repo/eslint-config": "workspace:*", "@tanstack/config": "^0.16.0", "msw": "^2.6.2", "vite": "^5.2.14" diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 9d7e0266..6a24308d 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -29,10 +29,10 @@ "author": "", "license": "ISC", "dependencies": { - "@axiomhq/logging": "workspace:^" + "@axiomhq/logging": "workspace:*" }, "devDependencies": { - "@repo/eslint-config": "workspace:^", + "@repo/eslint-config": "workspace:*", "@tanstack/config": "^0.16.0", "next": "15.1.4", "vite": "^5.2.14", diff --git a/packages/pino/package.json b/packages/pino/package.json index 9361f835..da21edf7 100644 --- a/packages/pino/package.json +++ b/packages/pino/package.json @@ -44,6 +44,6 @@ "pino-abstract-transport": "^1.2.0" }, "devDependencies": { - "@repo/eslint-config": "workspace:^" + "@repo/eslint-config": "workspace:*" } } diff --git a/packages/react/package.json b/packages/react/package.json index 6a380366..bd48bdab 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -39,7 +39,7 @@ "web-vitals": "^4.2.4" }, "devDependencies": { - "@repo/eslint-config": "workspace:^", + "@repo/eslint-config": "workspace:*", "@tanstack/config": "^0.16.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9483bbae..bf1ae9d7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,7 +98,7 @@ importers: version: 3.14.2 devDependencies: '@repo/eslint-config': - specifier: workspace:^ + specifier: workspace:* version: link:../../internal/eslint-config examples/nextjs: @@ -107,7 +107,7 @@ importers: specifier: workspace:* version: link:../../packages/logging '@axiomhq/nextjs': - specifier: workspace:^ + specifier: workspace:* version: link:../../packages/nextjs '@axiomhq/react': specifier: workspace:* @@ -160,7 +160,7 @@ importers: version: link:../../packages/js devDependencies: '@repo/eslint-config': - specifier: workspace:^ + specifier: workspace:* version: link:../../internal/eslint-config examples/pino: @@ -173,7 +173,7 @@ importers: version: 9.5.0 devDependencies: '@repo/eslint-config': - specifier: workspace:^ + specifier: workspace:* version: link:../../internal/eslint-config examples/vue-app: @@ -208,7 +208,7 @@ importers: version: 3.14.2 devDependencies: '@repo/eslint-config': - specifier: workspace:^ + specifier: workspace:* version: link:../../internal/eslint-config integration: @@ -224,7 +224,7 @@ importers: version: 4.7.0 devDependencies: '@repo/eslint-config': - specifier: workspace:^ + specifier: workspace:* version: link:../internal/eslint-config internal/eslint-config: @@ -289,7 +289,7 @@ importers: version: 1.3.1 devDependencies: '@repo/eslint-config': - specifier: workspace:^ + specifier: workspace:* version: link:../../internal/eslint-config '@tanstack/config': specifier: ^0.16.0 @@ -304,11 +304,11 @@ importers: packages/nextjs: dependencies: '@axiomhq/logging': - specifier: workspace:^ + specifier: workspace:* version: link:../logging devDependencies: '@repo/eslint-config': - specifier: workspace:^ + specifier: workspace:* version: link:../../internal/eslint-config '@tanstack/config': specifier: ^0.16.0 @@ -333,7 +333,7 @@ importers: version: 1.2.0 devDependencies: '@repo/eslint-config': - specifier: workspace:^ + specifier: workspace:* version: link:../../internal/eslint-config packages/react: @@ -361,7 +361,7 @@ importers: version: 4.2.4 devDependencies: '@repo/eslint-config': - specifier: workspace:^ + specifier: workspace:* version: link:../../internal/eslint-config '@tanstack/config': specifier: ^0.16.0 From 4ca54482fe8d956de3e0facd93ff59ee1dec6488 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Wed, 29 Jan 2025 12:42:01 +0000 Subject: [PATCH 37/71] refactor: rename shared.ts to runtime.ts --- packages/logging/src/logger.ts | 2 +- packages/logging/src/{shared.ts => runtime.ts} | 0 packages/logging/src/transports/console.ts | 2 +- packages/logging/test/unit/transports/console.test.ts | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename packages/logging/src/{shared.ts => runtime.ts} (100%) diff --git a/packages/logging/src/logger.ts b/packages/logging/src/logger.ts index adf36965..c51a49ff 100644 --- a/packages/logging/src/logger.ts +++ b/packages/logging/src/logger.ts @@ -1,5 +1,5 @@ import { Transport } from '.'; -import { Version } from './shared'; +import { Version } from './runtime'; const LOG_LEVEL = 'info'; diff --git a/packages/logging/src/shared.ts b/packages/logging/src/runtime.ts similarity index 100% rename from packages/logging/src/shared.ts rename to packages/logging/src/runtime.ts diff --git a/packages/logging/src/transports/console.ts b/packages/logging/src/transports/console.ts index 8bdea520..7dea7e72 100644 --- a/packages/logging/src/transports/console.ts +++ b/packages/logging/src/transports/console.ts @@ -1,6 +1,6 @@ import { Transport } from '.'; import { LogEvent } from '..'; -import { isBrowser } from '../shared'; +import { isBrowser } from '../runtime'; export interface ConsoleTransportConfig { prettyPrint?: boolean; } diff --git a/packages/logging/test/unit/transports/console.test.ts b/packages/logging/test/unit/transports/console.test.ts index 0511153e..605866c7 100644 --- a/packages/logging/test/unit/transports/console.test.ts +++ b/packages/logging/test/unit/transports/console.test.ts @@ -1,6 +1,6 @@ import { describe, beforeEach, afterEach, it, expect, vi, SpyInstance } from 'vitest'; import { ConsoleTransport } from '../../../src/transports/console'; -import * as shared from '../../../src/shared'; +import * as shared from '../../../src/runtime'; import { createLogEvent } from '../../lib/mock'; describe('ConsoleTransport', () => { From 090bb2761901656dcc12e54de699d3e26deddbdf Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Wed, 29 Jan 2025 12:44:33 +0000 Subject: [PATCH 38/71] chore: update dependencies of react package --- packages/react/package.json | 7 +++---- pnpm-lock.yaml | 20 ++++++-------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/packages/react/package.json b/packages/react/package.json index bd48bdab..94c7fa04 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -31,12 +31,9 @@ "license": "ISC", "dependencies": { "@axiomhq/logging": "workspace:*", - "@types/react": "^19", - "@types/react-dom": "^19", "react": "^19.0.0", "react-dom": "^19.0.0", - "use-deep-compare": "^1.3.0", - "web-vitals": "^4.2.4" + "use-deep-compare": "^1.3.0" }, "devDependencies": { "@repo/eslint-config": "workspace:*", @@ -44,6 +41,8 @@ "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.2.0", "@testing-library/react-hooks": "^8.0.1", + "@types/react": "^19", + "@types/react-dom": "^19", "@vitejs/plugin-react": "^4.3.4", "happy-dom": "^16.7.3", "vite": "^5.2.14", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bf1ae9d7..b86135ed 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -341,12 +341,6 @@ importers: '@axiomhq/logging': specifier: workspace:* version: link:../logging - '@types/react': - specifier: ^19 - version: 19.0.7 - '@types/react-dom': - specifier: ^19 - version: 19.0.3(@types/react@19.0.7) react: specifier: ^19.0.0 version: 19.0.0 @@ -356,9 +350,6 @@ importers: use-deep-compare: specifier: ^1.3.0 version: 1.3.0(react@19.0.0) - web-vitals: - specifier: ^4.2.4 - version: 4.2.4 devDependencies: '@repo/eslint-config': specifier: workspace:* @@ -375,6 +366,12 @@ importers: '@testing-library/react-hooks': specifier: ^8.0.1 version: 8.0.1(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@types/react': + specifier: ^19 + version: 19.0.7 + '@types/react-dom': + specifier: ^19 + version: 19.0.3(@types/react@19.0.7) '@vitejs/plugin-react': specifier: ^4.3.4 version: 4.3.4(vite@5.2.14(@types/node@20.14.2)) @@ -4407,9 +4404,6 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} - web-vitals@4.2.4: - resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} - webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} @@ -9031,8 +9025,6 @@ snapshots: dependencies: xml-name-validator: 5.0.0 - web-vitals@4.2.4: {} - webidl-conversions@7.0.0: {} whatwg-encoding@3.1.1: From e35921a75930ab52baf31f924fdf112c69837e36 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Wed, 29 Jan 2025 12:59:37 +0000 Subject: [PATCH 39/71] chore: add correct package metadata to next, logging and react --- packages/logging/package.json | 32 ++++++++++++++++++++++++++++---- packages/nextjs/package.json | 33 +++++++++++++++++++++++++++++---- packages/react/package.json | 32 ++++++++++++++++++++++++++++---- 3 files changed, 85 insertions(+), 12 deletions(-) diff --git a/packages/logging/package.json b/packages/logging/package.json index 3dd597b2..397220c6 100644 --- a/packages/logging/package.json +++ b/packages/logging/package.json @@ -1,7 +1,15 @@ { "name": "@axiomhq/logging", + "description": "The official logging package for Axiom", "version": "0.0.1", - "description": "", + "author": "Axiom, Inc.", + "license": "MIT", + "contributors": [ + "Lukas Malkmus ", + "Islam Shehata ", + "Arne Bahlo ", + "Gabriel De Andrade " + ], "type": "module", "main": "dist/cjs/index.cjs", "module": "dist/esm/index.js", @@ -24,9 +32,25 @@ "lint": "eslint .", "test": "vitest ./test/unit/*" }, - "keywords": [], - "author": "", - "license": "ISC", + "repository": { + "type": "git", + "url": "git+https://github.com/axiomhq/axiom-js.git" + }, + "keywords": [ + "axiom", + "api", + "rest", + "client", + "axiom-js", + "axiom sdk", + "axiom js", + "logger", + "logging" + ], + "homepage": "https://github.com/axiomhq/axiom-js/blob/main/packages/logging/README.md", + "bugs": { + "url": "https://github.com/axiomhq/axiom-js/issues" + }, "dependencies": { "@axiomhq/js": "^1.3.1" }, diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 6a24308d..66331426 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,9 +1,25 @@ { "name": "@axiomhq/nextjs", "version": "0.0.1", - "description": "", + "author": "Axiom, Inc.", + "license": "MIT", + "description": "The official Next.js package for Axiom", "type": "module", "main": "dist/cjs/index.cjs", + "contributors": [ + "Lukas Malkmus ", + "Islam Shehata ", + "Arne Bahlo ", + "Gabriel De Andrade " + ], + "repository": { + "type": "git", + "url": "git+https://github.com/axiomhq/axiom-js.git" + }, + "bugs": { + "url": "https://github.com/axiomhq/axiom-js/issues" + }, + "homepage": "https://github.com/axiomhq/axiom-js/blob/main/packages/nextjs/README.md", "module": "dist/esm/index.js", "types": "dist/esm/index.d.ts", "exports": { @@ -25,9 +41,18 @@ "lint": "eslint .", "test": "vitest run ./test/unit/*" }, - "keywords": [], - "author": "", - "license": "ISC", + "keywords": [ + "axiom", + "api", + "rest", + "client", + "axiom-nextjs", + "axiom sdk", + "axiom js", + "next-axiom", + "nextjs", + "logging" + ], "dependencies": { "@axiomhq/logging": "workspace:*" }, diff --git a/packages/react/package.json b/packages/react/package.json index 94c7fa04..4ea5320b 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,7 +1,15 @@ { "name": "@axiomhq/react", "version": "0.0.1", - "description": "", + "description": "The official React package for Axiom", + "author": "Axiom, Inc.", + "license": "MIT", + "contributors": [ + "Lukas Malkmus ", + "Islam Shehata ", + "Arne Bahlo ", + "Gabriel De Andrade " + ], "type": "module", "main": "dist/cjs/index.cjs", "module": "dist/esm/index.js", @@ -26,9 +34,25 @@ "test": "vitest", "test:watch": "vitest watch" }, - "keywords": [], - "author": "", - "license": "ISC", + "repository": { + "type": "git", + "url": "git+https://github.com/axiomhq/axiom-js.git" + }, + "keywords": [ + "axiom", + "api", + "rest", + "client", + "axiom-react", + "axiom sdk", + "axiom js", + "react", + "logging" + ], + "bugs": { + "url": "https://github.com/axiomhq/axiom-js/issues" + }, + "homepage": "https://github.com/axiomhq/axiom-js/blob/main/packages/react/README.md", "dependencies": { "@axiomhq/logging": "workspace:*", "react": "^19.0.0", From dd991acbcd66c17b3ed28b0694987dfc2abba15b Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Thu, 30 Jan 2025 00:20:20 +0000 Subject: [PATCH 40/71] feat: add log level filtering to transports --- packages/logging/src/logger.ts | 2 +- .../logging/src/transports/axiom-fetch.ts | 5 ++- packages/logging/src/transports/axiom-js.ts | 41 +++++++++++++++---- packages/logging/src/transports/console.ts | 11 +++-- packages/logging/src/transports/fetch.ts | 12 ++++-- packages/logging/src/transports/index.ts | 8 +--- .../logging/src/transports/proxy-transport.ts | 5 ++- packages/logging/src/transports/transport.ts | 4 ++ 8 files changed, 63 insertions(+), 25 deletions(-) create mode 100644 packages/logging/src/transports/transport.ts diff --git a/packages/logging/src/logger.ts b/packages/logging/src/logger.ts index c51a49ff..b13e41cf 100644 --- a/packages/logging/src/logger.ts +++ b/packages/logging/src/logger.ts @@ -43,7 +43,7 @@ export class Logger { this.config = { ...initConfig }; } - raw(log: LogEvent) { + raw(log: any) { this.config.transports.forEach((transport) => transport.log([log])); } debug = (message: string, args: { [key: string]: any } = {}) => { diff --git a/packages/logging/src/transports/axiom-fetch.ts b/packages/logging/src/transports/axiom-fetch.ts index 96735d3e..f2f87d19 100644 --- a/packages/logging/src/transports/axiom-fetch.ts +++ b/packages/logging/src/transports/axiom-fetch.ts @@ -1,10 +1,12 @@ -import { Transport } from '.'; +import { LogLevel } from '../logger'; +import { Transport } from './transport'; import { SimpleFetchTransport } from './fetch'; interface AxiomFetchConfig { dataset: string; token: string; url?: string; autoFlush?: boolean | number; + logLevel?: LogLevel; } const DEFAULT_URL = 'https://api.axiom.co'; @@ -17,6 +19,7 @@ export class AxiomFetchTransport extends SimpleFetchTransport implements Transpo headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${config.token}` }, }, autoFlush: config.autoFlush, + logLevel: config.logLevel, }); } } diff --git a/packages/logging/src/transports/axiom-js.ts b/packages/logging/src/transports/axiom-js.ts index 4bd63a47..d19c723a 100644 --- a/packages/logging/src/transports/axiom-js.ts +++ b/packages/logging/src/transports/axiom-js.ts @@ -1,19 +1,42 @@ -import type { Axiom } from "@axiomhq/js"; +import { Axiom, AxiomWithoutBatching } from '@axiomhq/js'; +import { LogLevel } from '../logger'; +import { Transport } from './transport'; -export class AxiomJSTransport { - private axiom: Axiom; - private dataset: string; +interface AxiomJSTransportConfig { + axiom: Axiom | AxiomWithoutBatching; + dataset: string; + logLevel?: LogLevel; +} +export class AxiomJSTransport implements Transport { + private config: AxiomJSTransportConfig; + private promises: Promise[] = []; - constructor(axiom: Axiom, dataset: string) { - this.axiom = axiom; - this.dataset = dataset; + constructor(config: AxiomJSTransportConfig) { + this.config = config; } log(logs: any[]) { - this.axiom.ingest(this.dataset, logs); + const filteredLogs = logs.filter( + (log) => + (LogLevel[log.level as keyof typeof LogLevel] ?? LogLevel.info) >= (this.config.logLevel ?? LogLevel.info), + ); + + if (filteredLogs.length === 0) { + return; + } + + if (this.config.axiom instanceof Axiom) { + this.config.axiom.ingest(this.config.dataset, filteredLogs); + } else if (this.config.axiom instanceof AxiomWithoutBatching) { + this.promises.push(this.config.axiom.ingest(this.config.dataset, filteredLogs)); + } } async flush() { - await this.axiom.flush(); + if (this.config.axiom instanceof Axiom) { + await this.config.axiom.flush(); + } else { + await Promise.allSettled(this.promises); + } } } diff --git a/packages/logging/src/transports/console.ts b/packages/logging/src/transports/console.ts index 7dea7e72..f78f99b4 100644 --- a/packages/logging/src/transports/console.ts +++ b/packages/logging/src/transports/console.ts @@ -1,8 +1,9 @@ -import { Transport } from '.'; -import { LogEvent } from '..'; +import { Transport } from './transport'; +import { LogEvent, LogLevel } from '..'; import { isBrowser } from '../runtime'; export interface ConsoleTransportConfig { prettyPrint?: boolean; + logLevel?: LogLevel; } const levelColors: { [key: string]: any } = { @@ -26,12 +27,16 @@ const levelColors: { [key: string]: any } = { export class ConsoleTransport implements Transport { private config: ConsoleTransportConfig; + constructor(config?: ConsoleTransportConfig) { this.config = { ...config }; } + log: Transport['log'] = (logs) => { logs.forEach((log) => { - this.prettyPrint(log); + if (LogLevel[log.level as keyof typeof LogLevel] >= (this.config.logLevel ?? LogLevel.info)) { + this.prettyPrint(log); + } }); }; diff --git a/packages/logging/src/transports/fetch.ts b/packages/logging/src/transports/fetch.ts index a2e456a2..1857a324 100644 --- a/packages/logging/src/transports/fetch.ts +++ b/packages/logging/src/transports/fetch.ts @@ -1,10 +1,11 @@ -import { Transport } from '.'; -import { LogEvent } from '..'; +import { Transport } from './transport'; +import { LogEvent, LogLevel } from '..'; interface FetchConfig { input: Parameters[0]; init?: Omit[1], 'body'>; autoFlush?: number | boolean; + logLevel?: LogLevel; } export class SimpleFetchTransport implements Transport { @@ -17,7 +18,12 @@ export class SimpleFetchTransport implements Transport { } log: Transport['log'] = (logs) => { - this.events.push(...logs); + const filteredLogs = logs.filter( + (log) => + (LogLevel[log.level as keyof typeof LogLevel] ?? LogLevel.info) >= (this.fetchConfig.logLevel ?? LogLevel.info), + ); + + this.events.push(...filteredLogs); if (typeof this.fetchConfig.autoFlush === 'undefined' || this.fetchConfig.autoFlush === false) { return; diff --git a/packages/logging/src/transports/index.ts b/packages/logging/src/transports/index.ts index b1dda0ad..44e8e453 100644 --- a/packages/logging/src/transports/index.ts +++ b/packages/logging/src/transports/index.ts @@ -1,10 +1,4 @@ -import { LogEvent } from '..'; - -export interface Transport { - log: (logs: LogEvent[]) => Promise | void; - flush: () => Promise | void; -} - +export * from './transport'; export * from './axiom-js'; export * from './axiom-fetch'; export * from './console'; diff --git a/packages/logging/src/transports/proxy-transport.ts b/packages/logging/src/transports/proxy-transport.ts index 3b86ecfb..c2bc8a45 100644 --- a/packages/logging/src/transports/proxy-transport.ts +++ b/packages/logging/src/transports/proxy-transport.ts @@ -1,14 +1,17 @@ -import { Transport } from '.'; +import { LogLevel } from '../logger'; +import { Transport } from './transport'; import { SimpleFetchTransport } from './fetch'; interface AxiomProxyConfig { url: string; autoFlush?: boolean | number; + logLevel?: LogLevel; } export class AxiomProxyTransport extends SimpleFetchTransport implements Transport { constructor(config: AxiomProxyConfig) { super({ input: config.url, autoFlush: config.autoFlush, + logLevel: config.logLevel, }); } } diff --git a/packages/logging/src/transports/transport.ts b/packages/logging/src/transports/transport.ts new file mode 100644 index 00000000..c65c8fbd --- /dev/null +++ b/packages/logging/src/transports/transport.ts @@ -0,0 +1,4 @@ +export interface Transport { + log: (logs: any[]) => Promise | void; + flush: () => Promise | void; +} From 9dac150af7d5f89d92331b1c38a2b6b03254ad51 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Mon, 3 Feb 2025 15:49:52 +0000 Subject: [PATCH 41/71] feat: Add webvitals to react --- packages/react/src/web-vitals.tsx | 42 +++++++ packages/react/test/unit/web-vitals.test.tsx | 121 +++++++++++++++++++ packages/react/tsconfig.json | 5 +- 3 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 packages/react/src/web-vitals.tsx create mode 100644 packages/react/test/unit/web-vitals.test.tsx diff --git a/packages/react/src/web-vitals.tsx b/packages/react/src/web-vitals.tsx new file mode 100644 index 00000000..8e58ca94 --- /dev/null +++ b/packages/react/src/web-vitals.tsx @@ -0,0 +1,42 @@ +'use client'; +import { Logger } from '@axiomhq/logging'; +import * as React from 'react'; +import { onLCP, onFID, onCLS, onINP, onFCP, onTTFB } from 'web-vitals'; +import type { Metric } from 'web-vitals'; + +export function useReportWebVitals(reportWebVitalsFn: (metric: Metric) => void) { + const ref = React.useRef(reportWebVitalsFn); + + ref.current = reportWebVitalsFn; + + React.useEffect(() => { + onCLS(ref.current); + onFID(ref.current); + onLCP(ref.current); + onINP(ref.current); + onFCP(ref.current); + onTTFB(ref.current); + }, []); +} + +const transformWebVitalsMetric = (metric: Metric): Record => { + return { + webVital: metric, + _time: new Date().getTime(), + source: 'web-vital', + path: window.location.pathname, + }; +}; + +export const createWebVitalsComponent = (logger: Logger) => { + const reportWebVitals = (metric: Metric) => { + logger.raw(transformWebVitalsMetric(metric)); + logger.flush(); + }; + + return () => { + useReportWebVitals(reportWebVitals); + + return <>; + }; +}; diff --git a/packages/react/test/unit/web-vitals.test.tsx b/packages/react/test/unit/web-vitals.test.tsx new file mode 100644 index 00000000..322de88e --- /dev/null +++ b/packages/react/test/unit/web-vitals.test.tsx @@ -0,0 +1,121 @@ +import * as React from 'react'; +import { renderHook, render } from '@testing-library/react'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { useReportWebVitals, createWebVitalsComponent } from '../../src/web-vitals'; +import { Logger } from '@axiomhq/logging'; +import * as webVitals from 'web-vitals'; + +// Mock all web-vitals functions +vi.mock('web-vitals', () => ({ + onCLS: vi.fn(), + onFID: vi.fn(), + onLCP: vi.fn(), + onINP: vi.fn(), + onFCP: vi.fn(), + onTTFB: vi.fn(), +})); + +describe('Web Vitals', () => { + beforeEach(() => { + vi.clearAllMocks(); + // Reset window location + Object.defineProperty(window, 'location', { + value: { pathname: '/test-path' }, + writable: true, + }); + }); + + describe('useReportWebVitals', () => { + it('should register all web vitals metrics', () => { + const reportFn = vi.fn(); + renderHook(() => useReportWebVitals(reportFn)); + + expect(webVitals.onCLS).toHaveBeenCalled(); + expect(webVitals.onFID).toHaveBeenCalled(); + expect(webVitals.onLCP).toHaveBeenCalled(); + expect(webVitals.onINP).toHaveBeenCalled(); + expect(webVitals.onFCP).toHaveBeenCalled(); + expect(webVitals.onTTFB).toHaveBeenCalled(); + }); + + it('should pass the report function to all web vitals', () => { + const reportFn = vi.fn(); + renderHook(() => useReportWebVitals(reportFn)); + + const calls = [ + webVitals.onCLS, + webVitals.onFID, + webVitals.onLCP, + webVitals.onINP, + webVitals.onFCP, + webVitals.onTTFB, + ]; + + calls.forEach((call) => { + expect(call).toHaveBeenCalledWith(expect.any(Function)); + }); + }); + }); + + describe('createWebVitalsComponent', () => { + it('should create a component that uses web vitals reporting', () => { + const mockLogger = { + raw: vi.fn(), + flush: vi.fn(), + } as unknown as Logger; + + const WebVitals = createWebVitalsComponent(mockLogger); + render(); + + // Verify that all web vitals are registered + expect(webVitals.onCLS).toHaveBeenCalled(); + expect(webVitals.onFID).toHaveBeenCalled(); + expect(webVitals.onLCP).toHaveBeenCalled(); + expect(webVitals.onINP).toHaveBeenCalled(); + expect(webVitals.onFCP).toHaveBeenCalled(); + expect(webVitals.onTTFB).toHaveBeenCalled(); + }); + + it('should log and flush metrics when reported', () => { + const mockLogger = { + raw: vi.fn(), + flush: vi.fn(), + } as unknown as Logger; + + const WebVitals = createWebVitalsComponent(mockLogger); + render(); + + // Simulate a web vital metric being reported + const mockMetric = { + name: 'CLS', + value: 0.1, + id: 'test', + }; + + // Get the callback passed to onCLS and call it + const onCLSCallback = vi.mocked(webVitals.onCLS).mock.calls[0][0] as Function; + onCLSCallback(mockMetric); + + // Verify the metric was logged and flushed + expect(mockLogger.raw).toHaveBeenCalledWith({ + webVital: mockMetric, + _time: expect.any(Number), + source: 'web-vital', + path: '/test-path', + }); + expect(mockLogger.flush).toHaveBeenCalled(); + }); + + it('should render an empty fragment', () => { + const mockLogger = { + raw: vi.fn(), + flush: vi.fn(), + } as unknown as Logger; + + const WebVitals = createWebVitalsComponent(mockLogger); + const { container } = render(); + + expect(container.firstChild).toBeNull(); + }); + }); +}); diff --git a/packages/react/tsconfig.json b/packages/react/tsconfig.json index 35d92df0..de940755 100644 --- a/packages/react/tsconfig.json +++ b/packages/react/tsconfig.json @@ -5,12 +5,13 @@ "module": "ESNext", "moduleResolution": "bundler", "baseUrl": ".", - "rootDir": "./src", + "rootDir": "./", "outDir": "dist/esm", + "jsx": "react", "declarationDir": "dist/esm/types", "resolveJsonModule": true, "declarationMap": true, "emitDeclarationOnly": true }, - "include": ["src/**/*"] + "include": ["src/**/*", "test/**/*"] } From a27bddbfb7102f31c6fe9115fc8e410ca60d4ca9 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Mon, 3 Feb 2025 16:08:27 +0000 Subject: [PATCH 42/71] feat: Add field formatter to logger --- packages/logging/src/logger.ts | 5 +++ packages/logging/test/unit/logger.test.ts | 43 ++++++++++++----------- packages/logging/tsconfig.json | 4 +-- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/packages/logging/src/logger.ts b/packages/logging/src/logger.ts index b13e41cf..da619cb4 100644 --- a/packages/logging/src/logger.ts +++ b/packages/logging/src/logger.ts @@ -25,6 +25,7 @@ export type LoggerConfig = { args?: { [key: string]: any }; transports: [Transport, ...Transport[]]; logLevel?: LogLevel; + formatters?: Array<(args: Record) => Record>; }; export class Logger { @@ -94,6 +95,10 @@ export class Logger { logEvent.fields = { ...logEvent.fields, args: args }; } + if (this.config.formatters && this.config.formatters.length > 0) { + logEvent.fields = this.config.formatters.reduce((acc, formatter) => formatter(acc), logEvent.fields); + } + return logEvent; }; diff --git a/packages/logging/test/unit/logger.test.ts b/packages/logging/test/unit/logger.test.ts index fcd95e69..6d7ee5e1 100644 --- a/packages/logging/test/unit/logger.test.ts +++ b/packages/logging/test/unit/logger.test.ts @@ -17,31 +17,32 @@ describe('Logger', () => { mockTransport.clear(); }); - describe('log levels', () => { - /* TODO: Add this back in once log level is implemented - it('should respect log level configuration', () => { - const warnLogger = new Logger({ - transports: [mockTransport], - }); - - warnLogger.debug('debug message'); - warnLogger.info('info message'); - warnLogger.warn('warn message'); - warnLogger.error('error message'); + describe('formatters', () => { + it('should format fields', () => { + const formatter = (fields: Record) => ({ ...fields, userId: '123' }); + logger.with({ formatter }).info('user action'); - expect(mockTransport.logs).toHaveLength(2); // Only warn and error should be logged - expect(mockTransport.logs[0].level).toBe('warn'); - expect(mockTransport.logs[1].level).toBe('error'); + expect(mockTransport.logs).toHaveLength(1); + expect(mockTransport.logs[0].fields).toEqual({ userId: '123' }); }); - */ - it('should log all levels by default', () => { - logger.debug('debug message'); - logger.info('info message'); - logger.warn('warn message'); - logger.error('error message'); + it('should replace and delete fields', () => { + const formatter = (fields: Record) => { + return { + userId: fields.userId, + foo: fields.foo, + }; + }; - expect(mockTransport.logs).toHaveLength(4); + logger.with({ formatter }).info('user action', { + userId: '123', + action: 'login', + foo: 'bar', + baz: 'qux', + }); + + expect(mockTransport.logs).toHaveLength(1); + expect(mockTransport.logs[0].fields).toEqual({ userId: '123', foo: 'bar' }); }); }); diff --git a/packages/logging/tsconfig.json b/packages/logging/tsconfig.json index 35d92df0..21459544 100644 --- a/packages/logging/tsconfig.json +++ b/packages/logging/tsconfig.json @@ -5,12 +5,12 @@ "module": "ESNext", "moduleResolution": "bundler", "baseUrl": ".", - "rootDir": "./src", + "rootDir": "./", "outDir": "dist/esm", "declarationDir": "dist/esm/types", "resolveJsonModule": true, "declarationMap": true, "emitDeclarationOnly": true }, - "include": ["src/**/*"] + "include": ["src/**/*", "test/**/*"] } From d0aaa678f1372a3b758a18e96f3391047076616f Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Mon, 3 Feb 2025 16:09:02 +0000 Subject: [PATCH 43/71] refactor: Axiom route handler callbacks & tracing strategy --- examples/nextjs/src/app/api/route.ts | 35 +---- examples/nextjs/src/lib/axiom/server.ts | 4 + packages/nextjs/src/routeHandler.ts | 143 ++++++++++++------ .../nextjs/test/unit/instrumentation.test.ts | 2 +- .../nextjs/test/unit/routeHandler.test.ts | 12 +- packages/nextjs/tsconfig.json | 4 +- 6 files changed, 118 insertions(+), 82 deletions(-) diff --git a/examples/nextjs/src/app/api/route.ts b/examples/nextjs/src/app/api/route.ts index 3bf5144f..dd725b27 100644 --- a/examples/nextjs/src/app/api/route.ts +++ b/examples/nextjs/src/app/api/route.ts @@ -1,32 +1,11 @@ -import { logger } from '@/lib/axiom/server'; -import { - createAxiomRouteHandler, - getLogLevelFromStatusCode, - transformErrorResult, - transformSuccessResult, -} from '@axiomhq/nextjs'; +import { logger, withAxiom } from '@/lib/axiom/server'; -const axiomRouteHandler = createAxiomRouteHandler(logger); - -export const GET = axiomRouteHandler(async () => { +export const GET = withAxiom(async () => { + logger.info('Hello World!'); return new Response('Hello World!'); }); -export const POST = axiomRouteHandler( - async (req) => { - return new Response(JSON.stringify(req.body)); - }, - async (result) => { - if (result.ok) { - logger.info(...transformSuccessResult(result.data)); - logger.info('searchParams', result.data.req.nextUrl.searchParams); - } else { - if (result.data.error instanceof Error) { - logger.error(result.data.error.message, result.data.error); - } - - const [message, report] = transformErrorResult(result.data); - logger.log(getLogLevelFromStatusCode(report.statusCode), message, report); - } - }, -); +export const POST = withAxiom(async (req) => { + logger.info('Hello World!'); + return new Response(JSON.stringify(req.body)); +}); diff --git a/examples/nextjs/src/lib/axiom/server.ts b/examples/nextjs/src/lib/axiom/server.ts index 66dcf1ca..fceea8e8 100644 --- a/examples/nextjs/src/lib/axiom/server.ts +++ b/examples/nextjs/src/lib/axiom/server.ts @@ -1,4 +1,5 @@ import { Logger, AxiomFetchTransport, ConsoleTransport } from '@axiomhq/logging'; +import { createAxiomRouteHandler, routeHandlerContextFormatter } from '@axiomhq/nextjs'; export const logger = new Logger({ transports: [ @@ -8,4 +9,7 @@ export const logger = new Logger({ }), new ConsoleTransport(), ], + formatters: [routeHandlerContextFormatter], }); + +export const withAxiom = createAxiomRouteHandler({ logger }); diff --git a/packages/nextjs/src/routeHandler.ts b/packages/nextjs/src/routeHandler.ts index e89d100c..6b4e8a49 100644 --- a/packages/nextjs/src/routeHandler.ts +++ b/packages/nextjs/src/routeHandler.ts @@ -18,7 +18,9 @@ const getRegion = (req: next.NextRequest) => { return region; }; -export const transformSuccessResult = (data: SuccessData): [message: string, report: Record] => { +export const transformRouteHandlerSuccessResult = ( + data: SuccessData, +): [message: string, report: Record] => { const report = { request: { startTime: new Date().getTime(), @@ -40,7 +42,7 @@ export const transformSuccessResult = (data: SuccessData): [message: string, rep ]; }; -export const transformErrorResult = (data: ErrorData): [message: string, report: Record] => { +export const transformRouteHandlerErrorResult = (data: ErrorData): [message: string, report: Record] => { const statusCode = data.error instanceof Error ? getNextErrorStatusCode(data.error) : 500; const report = { @@ -107,55 +109,106 @@ export const getLogLevelFromStatusCode = (statusCode: number): LogLevel => { return LogLevel.error; }; -export const createDefaultAxiomHandlerCallback = (logger: Logger): axiomHandlerCallback => { - return async (result) => { - if (result.ok) { - logger.info(...transformSuccessResult(result.data)); - } else { - if (result.data.error instanceof Error) { - logger.error(result.data.error.message, result.data.error); - const [message, report] = transformErrorResult(result.data); - logger.log(getLogLevelFromStatusCode(report.statusCode), message, report); - } - } - }; +export const defaultOnRouteHandlerSuccess = (logger: Logger, data: SuccessData) => { + logger.info(...transformRouteHandlerSuccessResult(data)); + logger.flush(); }; -export const createAxiomRouteHandler = (logger: Logger) => { - const withAxiom = ( - handler: NextHandler, - callback: axiomHandlerCallback = createDefaultAxiomHandlerCallback(logger), - ) => { - return async (req: next.NextRequest, ctx: any) => { - const start = Date.now(); - try { - const response = await handler(req, ctx); - const end = Date.now(); - const httpData = { req, res: response, start, end }; - - const callbackFn = async () => callback({ ok: true, data: httpData }); - // TODO: this surely can be written better - if (typeof after !== 'undefined') { - after(callbackFn()); - } else { - await callbackFn(); - } +const defaultRouteHandlerOnError = (logger: Logger, data: ErrorData) => { + if (data.error instanceof Error) { + logger.error(data.error.message, data.error); + } + const [message, report] = transformRouteHandlerErrorResult(data); + logger.log(getLogLevelFromStatusCode(report.statusCode), message, report); + logger.flush(); +}; + +const storage = new AsyncLocalStorage | undefined>(); + +const getStore = async ({ + store, + req, + ctx, +}: { + store?: Map | ((req: next.NextRequest, ctx: any) => Map | Promise>); + req: next.NextRequest; + ctx: any; +}) => { + if (!store) { + const newStore = new Map(); + newStore.set('traceId', crypto.randomUUID()); + return newStore; + } + if (typeof store === 'function') { + return await store(req, ctx); + } + return store; +}; - return response; - } catch (error) { - const end = Date.now(); - const callbackFn = async () => { - callback({ ok: false, data: { req, error, start, end } }); - }; - if (typeof after !== 'undefined') { - after(callbackFn()); - } else { - await callbackFn(); +export const createAxiomRouteHandler = ({ + logger, + store: argStore, + onSuccess, + onError, +}: { + logger: Logger; + store?: Map | ((req: next.NextRequest, ctx: any) => Map | Promise>); + onSuccess?: (data: SuccessData) => void; + onError?: (data: ErrorData) => void; +}) => { + const withAxiom = (handler: NextHandler) => { + return async (req: next.NextRequest, ctx: any) => { + const store = await getStore({ store: argStore, req, ctx }); + return storage.run(store, async () => { + const start = Date.now(); + + try { + const response = await handler(req, ctx); + const end = Date.now(); + const httpData = { req, res: response, start, end }; + + const callbackFn = async () => { + if (onSuccess) { + onSuccess(httpData); + } else { + defaultOnRouteHandlerSuccess(logger, httpData); + } + }; + // TODO: this surely can be written better + if (typeof after !== 'undefined') { + after(callbackFn()); + } else { + await callbackFn(); + } + + return response; + } catch (error) { + const end = Date.now(); + const callbackFn = async () => { + if (onError) { + onError({ req, error, start, end }); + } else { + defaultRouteHandlerOnError(logger, { req, error, start, end }); + } + }; + if (typeof after !== 'undefined') { + after(callbackFn()); + } else { + await callbackFn(); + } + throw error; } - throw error; - } + }); }; }; return withAxiom; }; + +export const routeHandlerContextFormatter = (fields: Record) => { + const store = storage.getStore() as Map; + if (!store) { + return fields; + } + return { ...fields, ...Object.fromEntries(store.entries()) }; +}; diff --git a/packages/nextjs/test/unit/instrumentation.test.ts b/packages/nextjs/test/unit/instrumentation.test.ts index fa1405df..328ad942 100644 --- a/packages/nextjs/test/unit/instrumentation.test.ts +++ b/packages/nextjs/test/unit/instrumentation.test.ts @@ -1,5 +1,5 @@ import { transformOnRequestError, createOnRequestError } from '../../src/instrumentation'; -import { describe, expect, it, vi } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { InstrumentationOnRequestError } from 'next/dist/server/instrumentation/types'; import { mockLogger } from '../lib/mock'; diff --git a/packages/nextjs/test/unit/routeHandler.test.ts b/packages/nextjs/test/unit/routeHandler.test.ts index a65d7f8e..ba16faa6 100644 --- a/packages/nextjs/test/unit/routeHandler.test.ts +++ b/packages/nextjs/test/unit/routeHandler.test.ts @@ -4,8 +4,8 @@ import { createAxiomRouteHandler, getLogLevelFromStatusCode, getNextErrorStatusCode, - transformSuccessResult, - transformErrorResult, + transformRouteHandlerSuccessResult, + transformRouteHandlerErrorResult, } from '../../src/routeHandler'; import { mockLogger } from '../lib/mock'; import { LogLevel } from '@axiomhq/logging'; @@ -14,7 +14,7 @@ import { forbidden, notFound, permanentRedirect, redirect, unauthorized } from ' describe('routeHandler', () => { describe('createAxiomRouteHandler', () => { it('should handle successful requests', async () => { - const withAxiom = createAxiomRouteHandler(mockLogger); + const withAxiom = createAxiomRouteHandler({ logger: mockLogger }); const mockResponse = new NextResponse(null, { status: 200 }); const handler = vi.fn().mockResolvedValue(mockResponse); const wrappedHandler = withAxiom(handler); @@ -27,7 +27,7 @@ describe('routeHandler', () => { }); it('should handle errors and rethrow them', async () => { - const withAxiom = createAxiomRouteHandler(mockLogger); + const withAxiom = createAxiomRouteHandler({ logger: mockLogger }); const error = new Error('Test error'); const handler = vi.fn().mockRejectedValue(error); const wrappedHandler = withAxiom(handler); @@ -115,7 +115,7 @@ describe('routeHandler', () => { end: 1100, }; - const [message, report] = transformSuccessResult(data); + const [message, report] = transformRouteHandlerSuccessResult(data); // Check message expect(message).toMatch(/GET \/test 200 in \d+ms/); @@ -146,7 +146,7 @@ describe('routeHandler', () => { end: 1100, }; - const [message, report] = transformErrorResult(data); + const [message, report] = transformRouteHandlerErrorResult(data); // Check message expect(message).toMatch(/GET \/test 500 in \d+ms/); diff --git a/packages/nextjs/tsconfig.json b/packages/nextjs/tsconfig.json index fcd3e214..c675f313 100644 --- a/packages/nextjs/tsconfig.json +++ b/packages/nextjs/tsconfig.json @@ -5,7 +5,7 @@ "module": "ESNext", "moduleResolution": "bundler", "baseUrl": ".", - "rootDir": "./src", + "rootDir": "./", "outDir": "dist/esm", "declarationDir": "dist/esm/types", "resolveJsonModule": true, @@ -13,5 +13,5 @@ "emitDeclarationOnly": true, "useDefineForClassFields": false }, - "include": ["src/**/*"] + "include": ["src/**/*", "test/**/*"] } From e0c8b26d9f9a89f72cb2c934b1129de1a3867f87 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Mon, 3 Feb 2025 16:22:30 +0000 Subject: [PATCH 44/71] refactor: web-vitals --- packages/react/package.json | 3 ++- packages/react/src/web-vitals.tsx | 23 +++++++++++-------- packages/react/test/unit/web-vitals.test.tsx | 24 ++++++++++++++------ pnpm-lock.yaml | 8 +++++++ 4 files changed, 40 insertions(+), 18 deletions(-) diff --git a/packages/react/package.json b/packages/react/package.json index 4ea5320b..1face314 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -57,7 +57,8 @@ "@axiomhq/logging": "workspace:*", "react": "^19.0.0", "react-dom": "^19.0.0", - "use-deep-compare": "^1.3.0" + "use-deep-compare": "^1.3.0", + "web-vitals": "^4.2.4" }, "devDependencies": { "@repo/eslint-config": "workspace:*", diff --git a/packages/react/src/web-vitals.tsx b/packages/react/src/web-vitals.tsx index 8e58ca94..59f9df99 100644 --- a/packages/react/src/web-vitals.tsx +++ b/packages/react/src/web-vitals.tsx @@ -19,7 +19,7 @@ export function useReportWebVitals(reportWebVitalsFn: (metric: Metric) => void) }, []); } -const transformWebVitalsMetric = (metric: Metric): Record => { +export const transformWebVitalsMetric = (metric: Metric): Record => { return { webVital: metric, _time: new Date().getTime(), @@ -28,15 +28,18 @@ const transformWebVitalsMetric = (metric: Metric): Record => { }; }; -export const createWebVitalsComponent = (logger: Logger) => { - const reportWebVitals = (metric: Metric) => { - logger.raw(transformWebVitalsMetric(metric)); - logger.flush(); - }; +const WebVitals = ({ logger, reportWebVitals }: { logger: Logger; reportWebVitals?: (metric: Metric) => void }) => { + const callback = React.useCallback( + (metric: Metric) => { + logger.raw(transformWebVitalsMetric(metric)); + logger.flush(); + }, + [logger], + ); - return () => { - useReportWebVitals(reportWebVitals); + useReportWebVitals(reportWebVitals ?? callback); - return <>; - }; + return <>; }; + +export default WebVitals; diff --git a/packages/react/test/unit/web-vitals.test.tsx b/packages/react/test/unit/web-vitals.test.tsx index 322de88e..54abf22c 100644 --- a/packages/react/test/unit/web-vitals.test.tsx +++ b/packages/react/test/unit/web-vitals.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { renderHook, render } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { useReportWebVitals, createWebVitalsComponent } from '../../src/web-vitals'; +import WebVitals, { useReportWebVitals } from '../../src/web-vitals'; import { Logger } from '@axiomhq/logging'; import * as webVitals from 'web-vitals'; @@ -64,8 +64,7 @@ describe('Web Vitals', () => { flush: vi.fn(), } as unknown as Logger; - const WebVitals = createWebVitalsComponent(mockLogger); - render(); + render(); // Verify that all web vitals are registered expect(webVitals.onCLS).toHaveBeenCalled(); @@ -82,8 +81,7 @@ describe('Web Vitals', () => { flush: vi.fn(), } as unknown as Logger; - const WebVitals = createWebVitalsComponent(mockLogger); - render(); + render(); // Simulate a web vital metric being reported const mockMetric = { @@ -112,10 +110,22 @@ describe('Web Vitals', () => { flush: vi.fn(), } as unknown as Logger; - const WebVitals = createWebVitalsComponent(mockLogger); - const { container } = render(); + const { container } = render(); expect(container.firstChild).toBeNull(); }); + + it('should only be called once mounted and ignore re-renders', () => { + const mockLogger = { + raw: vi.fn(), + flush: vi.fn(), + } as unknown as Logger; + + const { rerender } = render(); + + rerender(); + + expect(webVitals.onCLS).toHaveBeenCalledTimes(1); + }); }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b86135ed..6db15f64 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -350,6 +350,9 @@ importers: use-deep-compare: specifier: ^1.3.0 version: 1.3.0(react@19.0.0) + web-vitals: + specifier: ^4.2.4 + version: 4.2.4 devDependencies: '@repo/eslint-config': specifier: workspace:* @@ -4404,6 +4407,9 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} + web-vitals@4.2.4: + resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} + webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} @@ -9025,6 +9031,8 @@ snapshots: dependencies: xml-name-validator: 5.0.0 + web-vitals@4.2.4: {} + webidl-conversions@7.0.0: {} whatwg-encoding@3.1.1: From f4eb1230fd517a2f277f169669d9c7fc0e2b377a Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 4 Feb 2025 02:35:37 +0000 Subject: [PATCH 45/71] refactor: web-vitals --- examples/nextjs/src/app/layout.tsx | 27 +++--- examples/nextjs/src/lib/axiom/client.ts | 9 +- packages/react/src/helpers.ts | 12 --- packages/react/src/index.ts | 2 +- packages/react/src/use-logger.ts | 1 + packages/react/src/web-vitals.tsx | 53 +++++++----- packages/react/test/unit/helpers.test.ts | 19 ----- packages/react/test/unit/web-vitals.test.tsx | 89 +++++++++++++------- 8 files changed, 113 insertions(+), 99 deletions(-) delete mode 100644 packages/react/src/helpers.ts delete mode 100644 packages/react/test/unit/helpers.test.ts diff --git a/examples/nextjs/src/app/layout.tsx b/examples/nextjs/src/app/layout.tsx index f7fa87eb..753b4a4a 100644 --- a/examples/nextjs/src/app/layout.tsx +++ b/examples/nextjs/src/app/layout.tsx @@ -1,20 +1,22 @@ -import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; -import "./globals.css"; +import type { Metadata } from 'next'; +import { Geist, Geist_Mono } from 'next/font/google'; + +import './globals.css'; +import { WebVitals } from '@/lib/axiom/client'; const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], + variable: '--font-geist-sans', + subsets: ['latin'], }); const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], + variable: '--font-geist-mono', + subsets: ['latin'], }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: 'Create Next App', + description: 'Generated by create next app', }; export default function RootLayout({ @@ -24,11 +26,8 @@ export default function RootLayout({ }>) { return ( - - {children} - + + {children} ); } diff --git a/examples/nextjs/src/lib/axiom/client.ts b/examples/nextjs/src/lib/axiom/client.ts index 1a9ad0a1..36137345 100644 --- a/examples/nextjs/src/lib/axiom/client.ts +++ b/examples/nextjs/src/lib/axiom/client.ts @@ -1,5 +1,7 @@ +'use client'; + import { Logger, AxiomProxyTransport, ConsoleTransport } from '@axiomhq/logging'; -import { createClientSideHelpers } from '@axiomhq/react'; +import { createUseLogger, createWebVitalsComponent } from '@axiomhq/react'; export const logger = new Logger({ transports: [ @@ -8,4 +10,7 @@ export const logger = new Logger({ ], }); -export const { useLogger } = createClientSideHelpers(logger); +const useLogger = createUseLogger(logger); +const WebVitals = createWebVitalsComponent(logger); + +export { useLogger, WebVitals }; diff --git a/packages/react/src/helpers.ts b/packages/react/src/helpers.ts deleted file mode 100644 index de30d637..00000000 --- a/packages/react/src/helpers.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Logger } from '@axiomhq/logging'; -import { createUseLogger } from './use-logger'; - -export const createClientSideHelpers = (logger: Logger) => { - if (!logger) { - throw new Error('A logger must be provided to create client side helpers'); - } - - return { - useLogger: createUseLogger(logger), - }; -}; diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 8bd7db7c..2a09ccec 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -1,2 +1,2 @@ -export * from './helpers'; export * from './use-logger'; +export * from './web-vitals'; diff --git a/packages/react/src/use-logger.ts b/packages/react/src/use-logger.ts index 97fff6f2..b6e8cf9f 100644 --- a/packages/react/src/use-logger.ts +++ b/packages/react/src/use-logger.ts @@ -1,3 +1,4 @@ +'use client'; import { Logger } from '@axiomhq/logging'; import { useEffect, useState } from 'react'; diff --git a/packages/react/src/web-vitals.tsx b/packages/react/src/web-vitals.tsx index 59f9df99..ce6e52fa 100644 --- a/packages/react/src/web-vitals.tsx +++ b/packages/react/src/web-vitals.tsx @@ -4,18 +4,27 @@ import * as React from 'react'; import { onLCP, onFID, onCLS, onINP, onFCP, onTTFB } from 'web-vitals'; import type { Metric } from 'web-vitals'; -export function useReportWebVitals(reportWebVitalsFn: (metric: Metric) => void) { - const ref = React.useRef(reportWebVitalsFn); - - ref.current = reportWebVitalsFn; +export function useReportWebVitals(pushMetrics: (metric: Metric) => void, flushMetrics: () => void) { + const pushMetricsRef = React.useRef(pushMetrics); + const flushMetricsRef = React.useRef(flushMetrics); React.useEffect(() => { - onCLS(ref.current); - onFID(ref.current); - onLCP(ref.current); - onINP(ref.current); - onFCP(ref.current); - onTTFB(ref.current); + const effectFlushMetrics = () => { + flushMetricsRef.current(); + }; + + onCLS(pushMetricsRef.current); + onFID(pushMetricsRef.current); + onLCP(pushMetricsRef.current); + onINP(pushMetricsRef.current); + onFCP(pushMetricsRef.current); + onTTFB(pushMetricsRef.current); + + document.addEventListener('visibilitychange', effectFlushMetrics); + + return () => { + document.removeEventListener('visibilitychange', effectFlushMetrics); + }; }, []); } @@ -28,18 +37,18 @@ export const transformWebVitalsMetric = (metric: Metric): Record => }; }; -const WebVitals = ({ logger, reportWebVitals }: { logger: Logger; reportWebVitals?: (metric: Metric) => void }) => { - const callback = React.useCallback( - (metric: Metric) => { - logger.raw(transformWebVitalsMetric(metric)); - logger.flush(); - }, - [logger], - ); +export const createWebVitalsComponent = (logger: Logger) => { + const sendMetrics = (metric: Metric) => { + logger.raw(transformWebVitalsMetric(metric)); + }; + + const flushMetrics = () => { + logger.flush(); + }; - useReportWebVitals(reportWebVitals ?? callback); + return () => { + useReportWebVitals(sendMetrics, flushMetrics); - return <>; + return <>; + }; }; - -export default WebVitals; diff --git a/packages/react/test/unit/helpers.test.ts b/packages/react/test/unit/helpers.test.ts deleted file mode 100644 index 2fd23080..00000000 --- a/packages/react/test/unit/helpers.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { createClientSideHelpers } from '../../src/helpers'; -import { Logger } from '@axiomhq/logging'; - -describe('createClientSideHelpers', () => { - it('should throw an error if no logger is provided', () => { - expect(() => createClientSideHelpers(undefined as unknown as Logger)).toThrow( - 'A logger must be provided to create client side helpers', - ); - }); - - it('should return an object with useLogger function', () => { - const mockLogger = {} as Logger; - const helpers = createClientSideHelpers(mockLogger); - - expect(helpers).toHaveProperty('useLogger'); - expect(typeof helpers.useLogger).toBe('function'); - }); -}); diff --git a/packages/react/test/unit/web-vitals.test.tsx b/packages/react/test/unit/web-vitals.test.tsx index 54abf22c..8f584eda 100644 --- a/packages/react/test/unit/web-vitals.test.tsx +++ b/packages/react/test/unit/web-vitals.test.tsx @@ -1,9 +1,10 @@ import * as React from 'react'; import { renderHook, render } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; -import WebVitals, { useReportWebVitals } from '../../src/web-vitals'; +import { useReportWebVitals, createWebVitalsComponent, transformWebVitalsMetric } from '../../src/web-vitals'; import { Logger } from '@axiomhq/logging'; import * as webVitals from 'web-vitals'; +import { Metric } from 'web-vitals'; // Mock all web-vitals functions vi.mock('web-vitals', () => ({ @@ -26,9 +27,12 @@ describe('Web Vitals', () => { }); describe('useReportWebVitals', () => { - it('should register all web vitals metrics', () => { + it('should register all web vitals metrics and visibility change listener', () => { const reportFn = vi.fn(); - renderHook(() => useReportWebVitals(reportFn)); + const flushFn = vi.fn(); + const addEventListenerSpy = vi.spyOn(document, 'addEventListener'); + + renderHook(() => useReportWebVitals(reportFn, flushFn)); expect(webVitals.onCLS).toHaveBeenCalled(); expect(webVitals.onFID).toHaveBeenCalled(); @@ -36,23 +40,45 @@ describe('Web Vitals', () => { expect(webVitals.onINP).toHaveBeenCalled(); expect(webVitals.onFCP).toHaveBeenCalled(); expect(webVitals.onTTFB).toHaveBeenCalled(); + expect(addEventListenerSpy).toHaveBeenCalledWith('visibilitychange', expect.any(Function)); }); - it('should pass the report function to all web vitals', () => { - const reportFn = vi.fn(); - renderHook(() => useReportWebVitals(reportFn)); - - const calls = [ - webVitals.onCLS, - webVitals.onFID, - webVitals.onLCP, - webVitals.onINP, - webVitals.onFCP, - webVitals.onTTFB, - ]; - - calls.forEach((call) => { - expect(call).toHaveBeenCalledWith(expect.any(Function)); + it('should cleanup visibility change listener on unmount', () => { + const removeEventListenerSpy = vi.spyOn(document, 'removeEventListener'); + const { unmount } = renderHook(() => useReportWebVitals(vi.fn(), vi.fn())); + + unmount(); + + expect(removeEventListenerSpy).toHaveBeenCalledWith('visibilitychange', expect.any(Function)); + }); + + it('should call flush metrics when visibility changes', () => { + const flushFn = vi.fn(); + renderHook(() => useReportWebVitals(vi.fn(), flushFn)); + + document.dispatchEvent(new Event('visibilitychange')); + + expect(flushFn).toHaveBeenCalled(); + }); + }); + + describe('transformWebVitalsMetric', () => { + it('should transform web vital metric to expected format', () => { + const mockMetric = { + name: 'CLS', + value: 0.1, + id: 'test', + }; + const now = 1234567890; + vi.spyOn(Date.prototype, 'getTime').mockReturnValue(now); + + const result = transformWebVitalsMetric(mockMetric as Metric); + + expect(result).toEqual({ + webVital: mockMetric, + _time: now, + source: 'web-vital', + path: '/test-path', }); }); }); @@ -64,9 +90,9 @@ describe('Web Vitals', () => { flush: vi.fn(), } as unknown as Logger; - render(); + const WebVitals = createWebVitalsComponent(mockLogger); + render(); - // Verify that all web vitals are registered expect(webVitals.onCLS).toHaveBeenCalled(); expect(webVitals.onFID).toHaveBeenCalled(); expect(webVitals.onLCP).toHaveBeenCalled(); @@ -75,32 +101,35 @@ describe('Web Vitals', () => { expect(webVitals.onTTFB).toHaveBeenCalled(); }); - it('should log and flush metrics when reported', () => { + it('should log transformed metrics and flush when reported', () => { const mockLogger = { raw: vi.fn(), flush: vi.fn(), } as unknown as Logger; + const WebVitals = createWebVitalsComponent(mockLogger); + const now = 1234567890; + vi.spyOn(Date.prototype, 'getTime').mockReturnValue(now); - render(); + render(); - // Simulate a web vital metric being reported const mockMetric = { name: 'CLS', value: 0.1, id: 'test', }; - // Get the callback passed to onCLS and call it const onCLSCallback = vi.mocked(webVitals.onCLS).mock.calls[0][0] as Function; onCLSCallback(mockMetric); - // Verify the metric was logged and flushed expect(mockLogger.raw).toHaveBeenCalledWith({ webVital: mockMetric, - _time: expect.any(Number), + _time: now, source: 'web-vital', path: '/test-path', }); + + document.dispatchEvent(new Event('visibilitychange')); + expect(mockLogger.flush).toHaveBeenCalled(); }); @@ -110,7 +139,8 @@ describe('Web Vitals', () => { flush: vi.fn(), } as unknown as Logger; - const { container } = render(); + const WebVitals = createWebVitalsComponent(mockLogger); + const { container } = render(); expect(container.firstChild).toBeNull(); }); @@ -121,9 +151,10 @@ describe('Web Vitals', () => { flush: vi.fn(), } as unknown as Logger; - const { rerender } = render(); + const WebVitals = createWebVitalsComponent(mockLogger); + const { rerender } = render(); - rerender(); + rerender(); expect(webVitals.onCLS).toHaveBeenCalledTimes(1); }); From 0efc67df41729bc3d5d715e64209d6c9cdbe8c5e Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 4 Feb 2025 15:04:11 +0000 Subject: [PATCH 46/71] chore: update deps and example --- examples/nextjs/src/lib/axiom/server.ts | 2 +- examples/nextjs/src/middleware.ts | 4 ++++ packages/nextjs/package.json | 3 +-- pnpm-lock.yaml | 8 ++++---- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/examples/nextjs/src/lib/axiom/server.ts b/examples/nextjs/src/lib/axiom/server.ts index fceea8e8..ae8aa710 100644 --- a/examples/nextjs/src/lib/axiom/server.ts +++ b/examples/nextjs/src/lib/axiom/server.ts @@ -7,7 +7,7 @@ export const logger = new Logger({ dataset: process.env.AXIOM_DATASET!, token: process.env.AXIOM_TOKEN!, }), - new ConsoleTransport(), + new ConsoleTransport({ prettyPrint: true }), ], formatters: [routeHandlerContextFormatter], }); diff --git a/examples/nextjs/src/middleware.ts b/examples/nextjs/src/middleware.ts index b5f6ee3f..b1a820e8 100644 --- a/examples/nextjs/src/middleware.ts +++ b/examples/nextjs/src/middleware.ts @@ -9,3 +9,7 @@ export async function middleware(request: NextRequest, event: NextFetchEvent) { event.waitUntil(logger.flush()); return NextResponse.next(); } + +export const config = { + matcher: ['/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)'], +}; diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 66331426..e10eeba6 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -59,11 +59,10 @@ "devDependencies": { "@repo/eslint-config": "workspace:*", "@tanstack/config": "^0.16.0", - "next": "15.1.4", "vite": "^5.2.14", "vitest": "^0.34.6" }, "peerDependencies": { - "next": "15.1.4" + "next": "^15.1" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6db15f64..08357d28 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -306,6 +306,9 @@ importers: '@axiomhq/logging': specifier: workspace:* version: link:../logging + next: + specifier: ^15.1 + version: 15.1.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0) devDependencies: '@repo/eslint-config': specifier: workspace:* @@ -313,9 +316,6 @@ importers: '@tanstack/config': specifier: ^0.16.0 version: 0.16.0(@types/node@20.14.2)(esbuild@0.20.2)(eslint@9.18.0(jiti@2.4.2))(rollup@4.22.4)(typescript@5.4.5)(vite@5.2.14(@types/node@20.14.2)) - next: - specifier: 15.1.4 - version: 15.1.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0) vite: specifier: ^5.2.14 version: 5.2.14(@types/node@20.14.2) @@ -7726,7 +7726,7 @@ snapshots: '@swc/counter': 0.1.3 '@swc/helpers': 0.5.15 busboy: 1.6.0 - caniuse-lite: 1.0.30001632 + caniuse-lite: 1.0.30001695 postcss: 8.4.31 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) From 06ce3fdebc4743d1bb65c9d22da10ab697ccbf3b Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 4 Feb 2025 15:06:21 +0000 Subject: [PATCH 47/71] fix: import AsyncLocalStorage from node:async_hooks --- packages/nextjs/src/routeHandler.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/nextjs/src/routeHandler.ts b/packages/nextjs/src/routeHandler.ts index 6b4e8a49..2167a3f1 100644 --- a/packages/nextjs/src/routeHandler.ts +++ b/packages/nextjs/src/routeHandler.ts @@ -2,6 +2,7 @@ import { Logger, LogLevel } from '@axiomhq/logging'; import { isRedirectError } from 'next/dist/client/components/redirect-error'; import { isHTTPAccessFallbackError } from 'next/dist/client/components/http-access-fallback/http-access-fallback'; import * as next from 'next/server'; +import { AsyncLocalStorage } from 'node:async_hooks'; const after = next.after; From 5f12d07e0965b27fef83dc79daece188eea05c93 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 4 Feb 2025 15:17:03 +0000 Subject: [PATCH 48/71] tests: fix logging test --- packages/logging/test/unit/logger.test.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/logging/test/unit/logger.test.ts b/packages/logging/test/unit/logger.test.ts index 6d7ee5e1..a71c900f 100644 --- a/packages/logging/test/unit/logger.test.ts +++ b/packages/logging/test/unit/logger.test.ts @@ -20,7 +20,13 @@ describe('Logger', () => { describe('formatters', () => { it('should format fields', () => { const formatter = (fields: Record) => ({ ...fields, userId: '123' }); - logger.with({ formatter }).info('user action'); + + logger = new Logger({ + transports: [mockTransport], + formatters: [formatter], + }); + + logger.info('user action'); expect(mockTransport.logs).toHaveLength(1); expect(mockTransport.logs[0].fields).toEqual({ userId: '123' }); @@ -34,7 +40,12 @@ describe('Logger', () => { }; }; - logger.with({ formatter }).info('user action', { + logger = new Logger({ + transports: [mockTransport], + formatters: [formatter], + }); + + logger.info('user action', { userId: '123', action: 'login', foo: 'bar', From 2c35c11b970d593410d58f85370c3008f91b8ee5 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 4 Feb 2025 15:25:58 +0000 Subject: [PATCH 49/71] chore: add globals to vitest --- packages/nextjs/vitest.config.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/nextjs/vitest.config.ts b/packages/nextjs/vitest.config.ts index 7e21c5fe..eec605fc 100644 --- a/packages/nextjs/vitest.config.ts +++ b/packages/nextjs/vitest.config.ts @@ -2,6 +2,8 @@ import { defineConfig } from 'vitest/config'; export default defineConfig(() => ({ test: { + environment: 'node', + globals: true, setupFiles: ['./test/lib/setupNext.ts'], env: { __NEXT_EXPERIMENTAL_AUTH_INTERRUPTS: 'true', // Allows for forbidden() and unauthorized() to work From b481f71470868f3e12e323fa1ef4018eeb54382b Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 4 Feb 2025 15:35:39 +0000 Subject: [PATCH 50/71] fix: avoid circular deps --- packages/logging/src/transports/console.ts | 2 +- packages/logging/src/transports/fetch.ts | 2 +- packages/logging/src/transports/proxy-transport.ts | 1 + packages/logging/test/unit/transports/axiom-js.test.ts | 2 +- packages/logging/test/unit/transports/fetch.test.ts | 1 - 5 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/logging/src/transports/console.ts b/packages/logging/src/transports/console.ts index f78f99b4..5e7d0be5 100644 --- a/packages/logging/src/transports/console.ts +++ b/packages/logging/src/transports/console.ts @@ -1,5 +1,5 @@ import { Transport } from './transport'; -import { LogEvent, LogLevel } from '..'; +import { LogEvent, LogLevel } from '../logger'; import { isBrowser } from '../runtime'; export interface ConsoleTransportConfig { prettyPrint?: boolean; diff --git a/packages/logging/src/transports/fetch.ts b/packages/logging/src/transports/fetch.ts index 1857a324..ad720aa9 100644 --- a/packages/logging/src/transports/fetch.ts +++ b/packages/logging/src/transports/fetch.ts @@ -1,5 +1,5 @@ import { Transport } from './transport'; -import { LogEvent, LogLevel } from '..'; +import { LogEvent, LogLevel } from '../logger'; interface FetchConfig { input: Parameters[0]; diff --git a/packages/logging/src/transports/proxy-transport.ts b/packages/logging/src/transports/proxy-transport.ts index c2bc8a45..37418244 100644 --- a/packages/logging/src/transports/proxy-transport.ts +++ b/packages/logging/src/transports/proxy-transport.ts @@ -1,6 +1,7 @@ import { LogLevel } from '../logger'; import { Transport } from './transport'; import { SimpleFetchTransport } from './fetch'; + interface AxiomProxyConfig { url: string; autoFlush?: boolean | number; diff --git a/packages/logging/test/unit/transports/axiom-js.test.ts b/packages/logging/test/unit/transports/axiom-js.test.ts index 29ad430f..e03eee4e 100644 --- a/packages/logging/test/unit/transports/axiom-js.test.ts +++ b/packages/logging/test/unit/transports/axiom-js.test.ts @@ -12,7 +12,7 @@ describe('AxiomJSTransport', () => { ingest: vi.fn(), flush: vi.fn().mockResolvedValue(undefined), }; - transport = new AxiomJSTransport(mockAxiom, DATASET); + transport = new AxiomJSTransport({ axiom: mockAxiom, dataset: DATASET }); }); describe('log', () => { diff --git a/packages/logging/test/unit/transports/fetch.test.ts b/packages/logging/test/unit/transports/fetch.test.ts index 2e71c893..44ed5271 100644 --- a/packages/logging/test/unit/transports/fetch.test.ts +++ b/packages/logging/test/unit/transports/fetch.test.ts @@ -1,6 +1,5 @@ import { describe, beforeEach, afterEach, it, expect, vi, beforeAll, afterAll } from 'vitest'; import { SimpleFetchTransport } from '../../../src/transports/fetch'; -import { LogEvent } from '../../../src'; import { http, HttpResponse, HttpHandler } from 'msw'; import { setupServer } from 'msw/node'; import { createLogEvent } from '../../lib/mock'; From 1d7c8aacb46ed01221c5d75053ec59ab52429851 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 4 Feb 2025 15:52:22 +0000 Subject: [PATCH 51/71] fix: axiom-js without batch handling --- packages/logging/src/transports/axiom-js.ts | 1 + .../test/unit/transports/axiom-js.test.ts | 65 ++++++++++++++----- .../test/unit/transports/console.test.ts | 2 + 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/packages/logging/src/transports/axiom-js.ts b/packages/logging/src/transports/axiom-js.ts index d19c723a..685009fb 100644 --- a/packages/logging/src/transports/axiom-js.ts +++ b/packages/logging/src/transports/axiom-js.ts @@ -38,5 +38,6 @@ export class AxiomJSTransport implements Transport { } else { await Promise.allSettled(this.promises); } + this.promises = []; } } diff --git a/packages/logging/test/unit/transports/axiom-js.test.ts b/packages/logging/test/unit/transports/axiom-js.test.ts index e03eee4e..39f6d1ff 100644 --- a/packages/logging/test/unit/transports/axiom-js.test.ts +++ b/packages/logging/test/unit/transports/axiom-js.test.ts @@ -1,43 +1,72 @@ import { describe, beforeEach, it, expect, vi } from 'vitest'; import { AxiomJSTransport } from '../../../src/transports/axiom-js'; import { createLogEvent } from '../../lib/mock'; +import { Axiom, AxiomWithoutBatching } from '@axiomhq/js'; describe('AxiomJSTransport', () => { - let mockAxiom: any; + let mockAxiom: Axiom; + let mockAxiomWithoutBatching: AxiomWithoutBatching; let transport: AxiomJSTransport; const DATASET = 'test-dataset'; beforeEach(() => { - mockAxiom = { - ingest: vi.fn(), - flush: vi.fn().mockResolvedValue(undefined), - }; - transport = new AxiomJSTransport({ axiom: mockAxiom, dataset: DATASET }); + mockAxiomWithoutBatching = Object.create(AxiomWithoutBatching.prototype, { + ingest: { value: vi.fn() }, + }); + mockAxiom = Object.create(Axiom.prototype, { + ingest: { value: vi.fn() }, + flush: { value: vi.fn().mockResolvedValue(undefined) }, + }); + }); + + describe('with Axiom', () => { + beforeEach(() => { + transport = new AxiomJSTransport({ axiom: mockAxiom, dataset: DATASET }); + }); + + describe('log', () => { + it('should forward logs to axiom client ingest method', () => { + const logs = [createLogEvent('first'), createLogEvent('second')]; + + transport.log(logs); + + expect(mockAxiom.ingest).toHaveBeenCalledTimes(1); + expect(mockAxiom.ingest).toHaveBeenCalledWith(DATASET, logs); + }); + }); + + describe('flush', () => { + it('should call flush on axiom client', async () => { + await transport.flush(); + + expect(mockAxiom.flush).toHaveBeenCalledTimes(1); + }); + }); }); - describe('log', () => { + describe('with AxiomWithoutBatching', () => { + beforeEach(() => { + transport = new AxiomJSTransport({ axiom: mockAxiomWithoutBatching, dataset: DATASET }); + }); + it('should forward logs to axiom client ingest method', () => { const logs = [createLogEvent('first'), createLogEvent('second')]; transport.log(logs); - expect(mockAxiom.ingest).toHaveBeenCalledTimes(1); - expect(mockAxiom.ingest).toHaveBeenCalledWith(DATASET, logs); + expect(mockAxiomWithoutBatching.ingest).toHaveBeenCalledTimes(1); + expect(mockAxiomWithoutBatching.ingest).toHaveBeenCalledWith(DATASET, logs); }); - it('should handle empty log array', () => { - transport.log([]); + it('should resolve promises when flush is called', async () => { + const logs = [createLogEvent('first'), createLogEvent('second')]; - expect(mockAxiom.ingest).toHaveBeenCalledTimes(1); - expect(mockAxiom.ingest).toHaveBeenCalledWith(DATASET, []); - }); - }); + transport.log(logs); - describe('flush', () => { - it('should call flush on axiom client', async () => { await transport.flush(); - expect(mockAxiom.flush).toHaveBeenCalledTimes(1); + // @ts-expect-error - private property + expect(transport.promises).toHaveLength(0); }); }); }); diff --git a/packages/logging/test/unit/transports/console.test.ts b/packages/logging/test/unit/transports/console.test.ts index 605866c7..b9fb48dc 100644 --- a/packages/logging/test/unit/transports/console.test.ts +++ b/packages/logging/test/unit/transports/console.test.ts @@ -2,6 +2,7 @@ import { describe, beforeEach, afterEach, it, expect, vi, SpyInstance } from 'vi import { ConsoleTransport } from '../../../src/transports/console'; import * as shared from '../../../src/runtime'; import { createLogEvent } from '../../lib/mock'; +import { LogLevel } from 'src/logger'; describe('ConsoleTransport', () => { let consoleSpy: SpyInstance; @@ -100,6 +101,7 @@ describe('ConsoleTransport', () => { }); it('should handle all log levels with correct colors', () => { + transport = new ConsoleTransport({ logLevel: LogLevel.debug }); const levels = ['debug', 'info', 'warn', 'error']; levels.forEach((level) => { consoleSpy.mockClear(); From 5a9f24bce7154e2581941dec04c64dfcabadbf79 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 4 Feb 2025 15:57:33 +0000 Subject: [PATCH 52/71] test: Add tests for logLevel --- .../test/unit/transports/axiom-fetch.test.ts | 31 +++++++++++ .../test/unit/transports/console.test.ts | 35 ++++++++++++ .../test/unit/transports/fetch.test.ts | 53 +++++++++++++++++++ 3 files changed, 119 insertions(+) diff --git a/packages/logging/test/unit/transports/axiom-fetch.test.ts b/packages/logging/test/unit/transports/axiom-fetch.test.ts index 300add4c..dc971b15 100644 --- a/packages/logging/test/unit/transports/axiom-fetch.test.ts +++ b/packages/logging/test/unit/transports/axiom-fetch.test.ts @@ -3,6 +3,7 @@ import { AxiomFetchTransport } from '../../../src/transports/axiom-fetch'; import { http, HttpResponse, HttpHandler } from 'msw'; import { setupServer } from 'msw/node'; import { createLogEvent } from '../../lib/mock'; +import { LogLevel } from 'src/logger'; describe('AxiomFetchTransport', () => { const DATASET = 'test-dataset'; @@ -110,4 +111,34 @@ describe('AxiomFetchTransport', () => { expect(requestCount).toBe(1); }); }); + + describe('log level filtering', () => { + it('should filter logs based on logLevel', async () => { + let receivedLogs: any[] = []; + server.use( + http.post('*', async ({ request }) => { + receivedLogs = (await request.json()) as any[]; + return HttpResponse.json({ success: true }); + }), + ); + + const transport = new AxiomFetchTransport({ + dataset: DATASET, + token: TOKEN, + logLevel: LogLevel.warn, + }); + + transport.log([ + createLogEvent('debug', 'debug message'), + createLogEvent('info', 'info message'), + createLogEvent('warn', 'warn message'), + createLogEvent('error', 'error message'), + ]); + + await transport.flush(); + + expect(receivedLogs).toHaveLength(2); + expect(receivedLogs.map((log) => log.level)).toEqual(['warn', 'error']); + }); + }); }); diff --git a/packages/logging/test/unit/transports/console.test.ts b/packages/logging/test/unit/transports/console.test.ts index b9fb48dc..5844aeda 100644 --- a/packages/logging/test/unit/transports/console.test.ts +++ b/packages/logging/test/unit/transports/console.test.ts @@ -117,4 +117,39 @@ describe('ConsoleTransport', () => { expect(transport.flush()).toBeUndefined(); }); }); + + describe('log level filtering', () => { + it('should filter logs based on logLevel', () => { + transport = new ConsoleTransport({ + prettyPrint: false, + logLevel: LogLevel.warn, + }); + + transport.log([ + createLogEvent('debug', 'debug message'), + createLogEvent('info', 'info message'), + createLogEvent('warn', 'warn message'), + createLogEvent('error', 'error message'), + ]); + + expect(consoleSpy).toHaveBeenCalledTimes(2); + expect(consoleSpy).toHaveBeenCalledWith('warn - warn message'); + expect(consoleSpy).toHaveBeenCalledWith('error - error message'); + }); + + it('should use info as default logLevel', () => { + transport = new ConsoleTransport({ prettyPrint: false }); + + transport.log([ + createLogEvent('debug', 'debug message'), + createLogEvent('info', 'info message'), + createLogEvent('warn', 'warn message'), + ]); + + expect(consoleSpy).toHaveBeenCalledTimes(2); + expect(consoleSpy).not.toHaveBeenCalledWith('debug - debug message'); + expect(consoleSpy).toHaveBeenCalledWith('info - info message'); + expect(consoleSpy).toHaveBeenCalledWith('warn - warn message'); + }); + }); }); diff --git a/packages/logging/test/unit/transports/fetch.test.ts b/packages/logging/test/unit/transports/fetch.test.ts index 44ed5271..f84acbfe 100644 --- a/packages/logging/test/unit/transports/fetch.test.ts +++ b/packages/logging/test/unit/transports/fetch.test.ts @@ -3,6 +3,7 @@ import { SimpleFetchTransport } from '../../../src/transports/fetch'; import { http, HttpResponse, HttpHandler } from 'msw'; import { setupServer } from 'msw/node'; import { createLogEvent } from '../../lib/mock'; +import { LogLevel } from 'src/logger'; describe('SimpleFetchTransport', () => { let transport: SimpleFetchTransport; @@ -206,4 +207,56 @@ describe('SimpleFetchTransport', () => { expect(receivedHeaders.get('Content-Type')).toBe('application/json'); }); }); + + describe('log level filtering', () => { + it('should filter logs based on logLevel', async () => { + let receivedLogs: any[] = []; + server.use( + http.post(API_URL, async ({ request }) => { + receivedLogs = (await request.json()) as any[]; + return HttpResponse.json({ success: true }); + }), + ); + + transport = new SimpleFetchTransport({ + input: API_URL, + logLevel: LogLevel.warn, + }); + + transport.log([ + createLogEvent('debug', 'debug message'), + createLogEvent('info', 'info message'), + createLogEvent('warn', 'warn message'), + createLogEvent('error', 'error message'), + ]); + + await transport.flush(); + + expect(receivedLogs).toHaveLength(2); + expect(receivedLogs.map((log) => log.level)).toEqual(['warn', 'error']); + }); + + it('should use info as default logLevel', async () => { + let receivedLogs: any[] = []; + server.use( + http.post(API_URL, async ({ request }) => { + receivedLogs = (await request.json()) as any[]; + return HttpResponse.json({ success: true }); + }), + ); + + transport = new SimpleFetchTransport({ input: API_URL }); + + transport.log([ + createLogEvent('debug', 'debug message'), + createLogEvent('info', 'info message'), + createLogEvent('warn', 'warn message'), + ]); + + await transport.flush(); + + expect(receivedLogs).toHaveLength(2); + expect(receivedLogs.map((log) => log.level)).toEqual(['info', 'warn']); + }); + }); }); From e603b58b12e06524cd9e481bb2ae74a34075ecf6 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 4 Feb 2025 17:24:21 +0000 Subject: [PATCH 53/71] refactor: extract context into it's own file --- examples/nextjs/src/lib/axiom/server.ts | 4 ++-- packages/nextjs/src/context/index.ts | 1 + packages/nextjs/src/context/storage.ts | 19 +++++++++++++++++++ packages/nextjs/src/index.ts | 1 + packages/nextjs/src/routeHandler.ts | 19 +++++-------------- 5 files changed, 28 insertions(+), 16 deletions(-) create mode 100644 packages/nextjs/src/context/index.ts create mode 100644 packages/nextjs/src/context/storage.ts diff --git a/examples/nextjs/src/lib/axiom/server.ts b/examples/nextjs/src/lib/axiom/server.ts index ae8aa710..bde5b0b0 100644 --- a/examples/nextjs/src/lib/axiom/server.ts +++ b/examples/nextjs/src/lib/axiom/server.ts @@ -1,5 +1,5 @@ import { Logger, AxiomFetchTransport, ConsoleTransport } from '@axiomhq/logging'; -import { createAxiomRouteHandler, routeHandlerContextFormatter } from '@axiomhq/nextjs'; +import { createAxiomRouteHandler, serverContextFieldsFormatter } from '@axiomhq/nextjs'; export const logger = new Logger({ transports: [ @@ -9,7 +9,7 @@ export const logger = new Logger({ }), new ConsoleTransport({ prettyPrint: true }), ], - formatters: [routeHandlerContextFormatter], + formatters: [serverContextFieldsFormatter], }); export const withAxiom = createAxiomRouteHandler({ logger }); diff --git a/packages/nextjs/src/context/index.ts b/packages/nextjs/src/context/index.ts new file mode 100644 index 00000000..85674ee7 --- /dev/null +++ b/packages/nextjs/src/context/index.ts @@ -0,0 +1 @@ +export * from './storage'; diff --git a/packages/nextjs/src/context/storage.ts b/packages/nextjs/src/context/storage.ts new file mode 100644 index 00000000..68c90631 --- /dev/null +++ b/packages/nextjs/src/context/storage.ts @@ -0,0 +1,19 @@ +export const storage = new AsyncLocalStorage | undefined>(); + +/** + * Adds custom fields like trace_id to the fields object in a logger + * if they are present in the context + * @param fields - The fields to merge + * @returns The merged fields + */ +export const serverContextFieldsFormatter = (fields: Record) => { + const store = storage.getStore() as Map; + if (!store) { + return fields; + } + return { ...fields, ...Object.fromEntries(store.entries()) }; +}; + +export const runWithContext = (callback: () => void, store: Map) => { + return storage.run(store, callback); +}; diff --git a/packages/nextjs/src/index.ts b/packages/nextjs/src/index.ts index 0cab3820..2deb2134 100644 --- a/packages/nextjs/src/index.ts +++ b/packages/nextjs/src/index.ts @@ -2,3 +2,4 @@ export * from './routeHandler'; export * from './proxyRouteHandler'; export * from './middleware'; export * from './instrumentation'; +export * from './context/index'; diff --git a/packages/nextjs/src/routeHandler.ts b/packages/nextjs/src/routeHandler.ts index 2167a3f1..5b32264f 100644 --- a/packages/nextjs/src/routeHandler.ts +++ b/packages/nextjs/src/routeHandler.ts @@ -2,7 +2,7 @@ import { Logger, LogLevel } from '@axiomhq/logging'; import { isRedirectError } from 'next/dist/client/components/redirect-error'; import { isHTTPAccessFallbackError } from 'next/dist/client/components/http-access-fallback/http-access-fallback'; import * as next from 'next/server'; -import { AsyncLocalStorage } from 'node:async_hooks'; +import { runWithContext } from 'src/context'; const after = next.after; @@ -124,8 +124,6 @@ const defaultRouteHandlerOnError = (logger: Logger, data: ErrorData) => { logger.flush(); }; -const storage = new AsyncLocalStorage | undefined>(); - const getStore = async ({ store, req, @@ -137,7 +135,7 @@ const getStore = async ({ }) => { if (!store) { const newStore = new Map(); - newStore.set('traceId', crypto.randomUUID()); + newStore.set('trace_id', crypto.randomUUID()); return newStore; } if (typeof store === 'function') { @@ -160,7 +158,8 @@ export const createAxiomRouteHandler = ({ const withAxiom = (handler: NextHandler) => { return async (req: next.NextRequest, ctx: any) => { const store = await getStore({ store: argStore, req, ctx }); - return storage.run(store, async () => { + + return runWithContext(async () => { const start = Date.now(); try { @@ -199,17 +198,9 @@ export const createAxiomRouteHandler = ({ } throw error; } - }); + }, store); }; }; return withAxiom; }; - -export const routeHandlerContextFormatter = (fields: Record) => { - const store = storage.getStore() as Map; - if (!store) { - return fields; - } - return { ...fields, ...Object.fromEntries(store.entries()) }; -}; From 764b67a332b68c710c6eb0eed7c92fed35b3e2e3 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 4 Feb 2025 18:42:27 +0000 Subject: [PATCH 54/71] refactor: add crypto and AsyncLocalStorage utility imports --- packages/nextjs/src/context/storage.ts | 2 ++ packages/nextjs/src/lib/node-utils.ts | 4 ++++ packages/nextjs/src/routeHandler.ts | 2 ++ 3 files changed, 8 insertions(+) create mode 100644 packages/nextjs/src/lib/node-utils.ts diff --git a/packages/nextjs/src/context/storage.ts b/packages/nextjs/src/context/storage.ts index 68c90631..6e868cf7 100644 --- a/packages/nextjs/src/context/storage.ts +++ b/packages/nextjs/src/context/storage.ts @@ -1,3 +1,5 @@ +import { AsyncLocalStorage } from '../lib/node-utils'; + export const storage = new AsyncLocalStorage | undefined>(); /** diff --git a/packages/nextjs/src/lib/node-utils.ts b/packages/nextjs/src/lib/node-utils.ts new file mode 100644 index 00000000..301a1b51 --- /dev/null +++ b/packages/nextjs/src/lib/node-utils.ts @@ -0,0 +1,4 @@ +const crypto = globalThis.crypto ?? require('node:crypto').webcrypto; +const AsyncLocalStorage = globalThis.AsyncLocalStorage ?? require('node:async_hooks').AsyncLocalStorage; + +export { crypto, AsyncLocalStorage }; diff --git a/packages/nextjs/src/routeHandler.ts b/packages/nextjs/src/routeHandler.ts index 5b32264f..168a5571 100644 --- a/packages/nextjs/src/routeHandler.ts +++ b/packages/nextjs/src/routeHandler.ts @@ -4,6 +4,8 @@ import { isHTTPAccessFallbackError } from 'next/dist/client/components/http-acce import * as next from 'next/server'; import { runWithContext } from 'src/context'; +import { crypto } from './lib/node-utils'; + const after = next.after; export type NextHandler = ( From b79b6ccb98509be0167b42d38692397f049d034c Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Wed, 5 Feb 2025 14:36:04 +0000 Subject: [PATCH 55/71] refactor: move LogLevel from enum to object --- packages/logging/src/logger.ts | 34 ++++++++++++------- packages/logging/src/transports/axiom-js.ts | 4 +-- packages/logging/src/transports/console.ts | 6 ++-- packages/logging/src/transports/fetch.ts | 5 +-- packages/logging/test/lib/mock.ts | 4 +-- packages/logging/test/unit/logger.test.ts | 2 +- .../test/unit/transports/axiom-fetch.test.ts | 8 ++--- .../test/unit/transports/axiom-js.test.ts | 7 ++-- .../test/unit/transports/console.test.ts | 34 +++++++++---------- .../test/unit/transports/fetch.test.ts | 18 +++++----- 10 files changed, 68 insertions(+), 54 deletions(-) diff --git a/packages/logging/src/logger.ts b/packages/logging/src/logger.ts index da619cb4..84286962 100644 --- a/packages/logging/src/logger.ts +++ b/packages/logging/src/logger.ts @@ -13,13 +13,24 @@ export interface LogEvent { }; } -export enum LogLevel { - debug = 0, - info = 1, - warn = 2, - error = 3, - off = 100, -} +export const LogLevelValue = { + debug: 0, + info: 1, + warn: 2, + error: 3, + off: 100, +} as const; + +export const LogLevel = { + debug: 'debug', + info: 'info', + warn: 'warn', + error: 'error', + off: 'off', +} as const; + +export type LogLevelValue = (typeof LogLevelValue)[keyof typeof LogLevelValue]; +export type LogLevel = keyof typeof LogLevelValue; export type LoggerConfig = { args?: { [key: string]: any }; @@ -30,16 +41,15 @@ export type LoggerConfig = { export class Logger { children: Logger[] = []; - public logLevel: LogLevel = LogLevel.debug; + public logLevel: LogLevelValue = LogLevelValue.debug; public config: LoggerConfig; constructor(public initConfig: LoggerConfig) { // check if user passed a log level, if not the default init value will be used as is. - // TODO: LogLevel currently does nothing - if (this.initConfig.logLevel != undefined && this.initConfig.logLevel >= 0) { - this.logLevel = this.initConfig.logLevel; + if (this.initConfig.logLevel != undefined) { + this.logLevel = LogLevelValue[this.initConfig.logLevel]; } else if (LOG_LEVEL) { - this.logLevel = LogLevel[LOG_LEVEL as keyof typeof LogLevel]; + this.logLevel = LogLevelValue[LOG_LEVEL as LogLevel]; } this.config = { ...initConfig }; } diff --git a/packages/logging/src/transports/axiom-js.ts b/packages/logging/src/transports/axiom-js.ts index 685009fb..2f66b04f 100644 --- a/packages/logging/src/transports/axiom-js.ts +++ b/packages/logging/src/transports/axiom-js.ts @@ -1,5 +1,5 @@ import { Axiom, AxiomWithoutBatching } from '@axiomhq/js'; -import { LogLevel } from '../logger'; +import { LogLevel, LogLevelValue } from '../logger'; import { Transport } from './transport'; interface AxiomJSTransportConfig { @@ -18,7 +18,7 @@ export class AxiomJSTransport implements Transport { log(logs: any[]) { const filteredLogs = logs.filter( (log) => - (LogLevel[log.level as keyof typeof LogLevel] ?? LogLevel.info) >= (this.config.logLevel ?? LogLevel.info), + LogLevelValue[(log.level as LogLevel) ?? LogLevel.info] >= LogLevelValue[this.config.logLevel ?? LogLevel.info], ); if (filteredLogs.length === 0) { diff --git a/packages/logging/src/transports/console.ts b/packages/logging/src/transports/console.ts index 5e7d0be5..5ee6f30b 100644 --- a/packages/logging/src/transports/console.ts +++ b/packages/logging/src/transports/console.ts @@ -1,5 +1,5 @@ import { Transport } from './transport'; -import { LogEvent, LogLevel } from '../logger'; +import { LogEvent, LogLevel, LogLevelValue } from '../logger'; import { isBrowser } from '../runtime'; export interface ConsoleTransportConfig { prettyPrint?: boolean; @@ -34,7 +34,9 @@ export class ConsoleTransport implements Transport { log: Transport['log'] = (logs) => { logs.forEach((log) => { - if (LogLevel[log.level as keyof typeof LogLevel] >= (this.config.logLevel ?? LogLevel.info)) { + if ( + LogLevelValue[(log.level as LogLevel) ?? LogLevel.info] >= LogLevelValue[this.config.logLevel ?? LogLevel.info] + ) { this.prettyPrint(log); } }); diff --git a/packages/logging/src/transports/fetch.ts b/packages/logging/src/transports/fetch.ts index ad720aa9..b0019219 100644 --- a/packages/logging/src/transports/fetch.ts +++ b/packages/logging/src/transports/fetch.ts @@ -1,5 +1,5 @@ import { Transport } from './transport'; -import { LogEvent, LogLevel } from '../logger'; +import { LogEvent, LogLevelValue, LogLevel } from '../logger'; interface FetchConfig { input: Parameters[0]; @@ -20,7 +20,8 @@ export class SimpleFetchTransport implements Transport { log: Transport['log'] = (logs) => { const filteredLogs = logs.filter( (log) => - (LogLevel[log.level as keyof typeof LogLevel] ?? LogLevel.info) >= (this.fetchConfig.logLevel ?? LogLevel.info), + LogLevelValue[(log.level as LogLevel) ?? LogLevel.info] >= + LogLevelValue[this.fetchConfig.logLevel ?? LogLevel.info], ); this.events.push(...filteredLogs); diff --git a/packages/logging/test/lib/mock.ts b/packages/logging/test/lib/mock.ts index cd2a5cb1..c23e6c2e 100644 --- a/packages/logging/test/lib/mock.ts +++ b/packages/logging/test/lib/mock.ts @@ -1,8 +1,8 @@ -import { LogEvent } from '../../src'; +import { LogEvent, LogLevel } from '../../src'; import { Transport } from '../../src'; export const createLogEvent = ( - level: string = 'info', + level: LogLevel = LogLevel.info, message: string = 'test message', fields: any = {}, ): LogEvent => ({ diff --git a/packages/logging/test/unit/logger.test.ts b/packages/logging/test/unit/logger.test.ts index a71c900f..6ac24658 100644 --- a/packages/logging/test/unit/logger.test.ts +++ b/packages/logging/test/unit/logger.test.ts @@ -1,4 +1,4 @@ -import { Logger, LogLevel, LogEvent } from '../../src/logger'; +import { Logger, LogEvent, LogLevel } from '../../src/logger'; import { describe, beforeEach, afterEach, it, expect, vi } from 'vitest'; import { MockTransport } from '../lib/mock'; diff --git a/packages/logging/test/unit/transports/axiom-fetch.test.ts b/packages/logging/test/unit/transports/axiom-fetch.test.ts index dc971b15..d5e17cd9 100644 --- a/packages/logging/test/unit/transports/axiom-fetch.test.ts +++ b/packages/logging/test/unit/transports/axiom-fetch.test.ts @@ -129,10 +129,10 @@ describe('AxiomFetchTransport', () => { }); transport.log([ - createLogEvent('debug', 'debug message'), - createLogEvent('info', 'info message'), - createLogEvent('warn', 'warn message'), - createLogEvent('error', 'error message'), + createLogEvent(LogLevel.debug, 'debug message'), + createLogEvent(LogLevel.info, 'info message'), + createLogEvent(LogLevel.warn, 'warn message'), + createLogEvent(LogLevel.error, 'error message'), ]); await transport.flush(); diff --git a/packages/logging/test/unit/transports/axiom-js.test.ts b/packages/logging/test/unit/transports/axiom-js.test.ts index 39f6d1ff..25222f06 100644 --- a/packages/logging/test/unit/transports/axiom-js.test.ts +++ b/packages/logging/test/unit/transports/axiom-js.test.ts @@ -2,6 +2,7 @@ import { describe, beforeEach, it, expect, vi } from 'vitest'; import { AxiomJSTransport } from '../../../src/transports/axiom-js'; import { createLogEvent } from '../../lib/mock'; import { Axiom, AxiomWithoutBatching } from '@axiomhq/js'; +import { LogLevel } from 'src/logger'; describe('AxiomJSTransport', () => { let mockAxiom: Axiom; @@ -26,7 +27,7 @@ describe('AxiomJSTransport', () => { describe('log', () => { it('should forward logs to axiom client ingest method', () => { - const logs = [createLogEvent('first'), createLogEvent('second')]; + const logs = [createLogEvent(LogLevel.info, 'first'), createLogEvent(LogLevel.info, 'second')]; transport.log(logs); @@ -50,7 +51,7 @@ describe('AxiomJSTransport', () => { }); it('should forward logs to axiom client ingest method', () => { - const logs = [createLogEvent('first'), createLogEvent('second')]; + const logs = [createLogEvent(LogLevel.info, 'first'), createLogEvent(LogLevel.info, 'second')]; transport.log(logs); @@ -59,7 +60,7 @@ describe('AxiomJSTransport', () => { }); it('should resolve promises when flush is called', async () => { - const logs = [createLogEvent('first'), createLogEvent('second')]; + const logs = [createLogEvent(LogLevel.info, 'first'), createLogEvent(LogLevel.info, 'second')]; transport.log(logs); diff --git a/packages/logging/test/unit/transports/console.test.ts b/packages/logging/test/unit/transports/console.test.ts index 5844aeda..14ad2099 100644 --- a/packages/logging/test/unit/transports/console.test.ts +++ b/packages/logging/test/unit/transports/console.test.ts @@ -22,21 +22,21 @@ describe('ConsoleTransport', () => { }); it('should log message with level', () => { - const event = createLogEvent('info', 'test message'); + const event = createLogEvent(LogLevel.info, 'test message'); transport.log([event]); expect(consoleSpy).toHaveBeenCalledWith('info - test message'); }); it('should include fields in log output', () => { - const event = createLogEvent('info', 'test message', { userId: '123' }); + const event = createLogEvent(LogLevel.info, 'test message', { userId: '123' }); transport.log([event]); expect(consoleSpy).toHaveBeenCalledWith('info - test message {"userId":"123"}'); }); it('should handle multiple log events', () => { - const events = [createLogEvent('info', 'first message'), createLogEvent('error', 'second message')]; + const events = [createLogEvent(LogLevel.info, 'first message'), createLogEvent(LogLevel.error, 'second message')]; transport.log(events); expect(consoleSpy).toHaveBeenCalledTimes(2); @@ -56,20 +56,20 @@ describe('ConsoleTransport', () => { }); it('should format info level with correct color', () => { - transport.log([createLogEvent('info', 'test message')]); + transport.log([createLogEvent(LogLevel.info, 'test message')]); expect(consoleSpy).toHaveBeenCalledWith('%c%s - %s', 'color: lightgreen;', 'info', 'test message'); }); it('should format error level with correct color', () => { - transport.log([createLogEvent('error', 'test message')]); + transport.log([createLogEvent(LogLevel.error, 'test message')]); expect(consoleSpy).toHaveBeenCalledWith('%c%s - %s', 'color: red;', 'error', 'test message'); }); it('should include fields as object in browser', () => { const fields = { userId: '123', action: 'login' }; - transport.log([createLogEvent('info', 'test message', fields)]); + transport.log([createLogEvent(LogLevel.info, 'test message', fields)]); expect(consoleSpy).toHaveBeenCalledWith('%c%s - %s %o', 'color: lightgreen;', 'info', 'test message', fields); }); @@ -81,20 +81,20 @@ describe('ConsoleTransport', () => { }); it('should format info level with correct color code', () => { - transport.log([createLogEvent('info', 'test message')]); + transport.log([createLogEvent(LogLevel.info, 'test message')]); expect(consoleSpy).toHaveBeenCalledWith('\x1b[32m%s\x1b[0m - %s', 'info', 'test message'); }); it('should format error level with correct color code', () => { - transport.log([createLogEvent('error', 'test message')]); + transport.log([createLogEvent(LogLevel.error, 'test message')]); expect(consoleSpy).toHaveBeenCalledWith('\x1b[31m%s\x1b[0m - %s', 'error', 'test message'); }); it('should include fields as object in terminal', () => { const fields = { userId: '123', action: 'login' }; - transport.log([createLogEvent('info', 'test message', fields)]); + transport.log([createLogEvent(LogLevel.info, 'test message', fields)]); expect(consoleSpy).toHaveBeenCalledWith('\x1b[32m%s\x1b[0m - %s %o', 'info', 'test message', fields); }); @@ -102,7 +102,7 @@ describe('ConsoleTransport', () => { it('should handle all log levels with correct colors', () => { transport = new ConsoleTransport({ logLevel: LogLevel.debug }); - const levels = ['debug', 'info', 'warn', 'error']; + const levels: LogLevel[] = [LogLevel.debug, LogLevel.info, LogLevel.warn, LogLevel.error]; levels.forEach((level) => { consoleSpy.mockClear(); transport.log([createLogEvent(level, 'test message')]); @@ -126,10 +126,10 @@ describe('ConsoleTransport', () => { }); transport.log([ - createLogEvent('debug', 'debug message'), - createLogEvent('info', 'info message'), - createLogEvent('warn', 'warn message'), - createLogEvent('error', 'error message'), + createLogEvent(LogLevel.debug, 'debug message'), + createLogEvent(LogLevel.info, 'info message'), + createLogEvent(LogLevel.warn, 'warn message'), + createLogEvent(LogLevel.error, 'error message'), ]); expect(consoleSpy).toHaveBeenCalledTimes(2); @@ -141,9 +141,9 @@ describe('ConsoleTransport', () => { transport = new ConsoleTransport({ prettyPrint: false }); transport.log([ - createLogEvent('debug', 'debug message'), - createLogEvent('info', 'info message'), - createLogEvent('warn', 'warn message'), + createLogEvent(LogLevel.debug, 'debug message'), + createLogEvent(LogLevel.info, 'info message'), + createLogEvent(LogLevel.warn, 'warn message'), ]); expect(consoleSpy).toHaveBeenCalledTimes(2); diff --git a/packages/logging/test/unit/transports/fetch.test.ts b/packages/logging/test/unit/transports/fetch.test.ts index f84acbfe..8f85f562 100644 --- a/packages/logging/test/unit/transports/fetch.test.ts +++ b/packages/logging/test/unit/transports/fetch.test.ts @@ -164,10 +164,10 @@ describe('SimpleFetchTransport', () => { autoFlush: 1000, }); - transport.log([createLogEvent('info', 'first')]); + transport.log([createLogEvent(LogLevel.info, 'first')]); await vi.advanceTimersByTimeAsync(500); - transport.log([createLogEvent('info', 'second')]); + transport.log([createLogEvent(LogLevel.info, 'second')]); await vi.advanceTimersByTimeAsync(500); expect(receivedBody).toBeUndefined(); @@ -224,10 +224,10 @@ describe('SimpleFetchTransport', () => { }); transport.log([ - createLogEvent('debug', 'debug message'), - createLogEvent('info', 'info message'), - createLogEvent('warn', 'warn message'), - createLogEvent('error', 'error message'), + createLogEvent(LogLevel.debug, 'debug message'), + createLogEvent(LogLevel.info, 'info message'), + createLogEvent(LogLevel.warn, 'warn message'), + createLogEvent(LogLevel.error, 'error message'), ]); await transport.flush(); @@ -248,9 +248,9 @@ describe('SimpleFetchTransport', () => { transport = new SimpleFetchTransport({ input: API_URL }); transport.log([ - createLogEvent('debug', 'debug message'), - createLogEvent('info', 'info message'), - createLogEvent('warn', 'warn message'), + createLogEvent(LogLevel.debug, 'debug message'), + createLogEvent(LogLevel.info, 'info message'), + createLogEvent(LogLevel.warn, 'warn message'), ]); await transport.flush(); From 16ae787b8b72b45465909b055137ee2e30b93583 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Wed, 5 Feb 2025 16:47:53 +0000 Subject: [PATCH 56/71] feat: handle non-standard log events in prettyPrint --- packages/logging/src/transports/console.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/logging/src/transports/console.ts b/packages/logging/src/transports/console.ts index 5ee6f30b..0c2fec3b 100644 --- a/packages/logging/src/transports/console.ts +++ b/packages/logging/src/transports/console.ts @@ -42,10 +42,19 @@ export class ConsoleTransport implements Transport { }); }; + private isStandardLogEvent(log: any): log is LogEvent { + return typeof log === 'object' && log !== null && 'level' in log && 'message' in log && 'fields' in log; + } + prettyPrint(ev: LogEvent) { - const hasFields = Object.keys(ev.fields).length > 0; + const hasFields = ev.fields && Object.keys(ev.fields).length > 0; + // check whether pretty print is disabled if (!this.config.prettyPrint) { + if (!this.isStandardLogEvent(ev)) { + console.log(ev); + return; + } let msg = `${ev.level} - ${ev.message}`; if (hasFields) { msg += ' ' + JSON.stringify(ev.fields); @@ -53,6 +62,15 @@ export class ConsoleTransport implements Transport { console.log(msg); return; } + + if (!this.isStandardLogEvent(ev)) { + if (isBrowser) { + console.log('%c%s', `color: ${levelColors[LogLevel.info].browser};`, ev); + } else { + console.log(`\x1b[${levelColors[LogLevel.info].terminal}m%s\x1b[0m`, ev); + } + return; + } // print indented message, instead of [object] // We use the %o modifier instead of JSON.stringify because stringify will print the // object as normal text, it loses all the functionality the browser gives for viewing From 77365d5e3d64d61aa118d8ab64af3abba394672e Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Wed, 5 Feb 2025 20:46:29 +0000 Subject: [PATCH 57/71] refactor: update server context handling --- packages/nextjs/src/context/storage.ts | 14 ++++++++++---- packages/nextjs/src/routeHandler.ts | 14 +++++++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/nextjs/src/context/storage.ts b/packages/nextjs/src/context/storage.ts index 6e868cf7..c7286a8f 100644 --- a/packages/nextjs/src/context/storage.ts +++ b/packages/nextjs/src/context/storage.ts @@ -1,6 +1,7 @@ import { AsyncLocalStorage } from '../lib/node-utils'; -export const storage = new AsyncLocalStorage | undefined>(); +export type ServerContextFields = Map | Record; +export const storage = new AsyncLocalStorage(); /** * Adds custom fields like trace_id to the fields object in a logger @@ -9,13 +10,18 @@ export const storage = new AsyncLocalStorage | undefined>(); * @returns The merged fields */ export const serverContextFieldsFormatter = (fields: Record) => { - const store = storage.getStore() as Map; + const store = storage.getStore(); if (!store) { return fields; } - return { ...fields, ...Object.fromEntries(store.entries()) }; + + if (store instanceof Map) { + return { ...fields, ...Object.fromEntries(store.entries()) }; + } + + return { ...fields, ...store }; }; -export const runWithContext = (callback: () => void, store: Map) => { +export const runWithServerContext = (callback: () => void, store: ReturnType) => { return storage.run(store, callback); }; diff --git a/packages/nextjs/src/routeHandler.ts b/packages/nextjs/src/routeHandler.ts index 168a5571..d9502522 100644 --- a/packages/nextjs/src/routeHandler.ts +++ b/packages/nextjs/src/routeHandler.ts @@ -2,7 +2,7 @@ import { Logger, LogLevel } from '@axiomhq/logging'; import { isRedirectError } from 'next/dist/client/components/redirect-error'; import { isHTTPAccessFallbackError } from 'next/dist/client/components/http-access-fallback/http-access-fallback'; import * as next from 'next/server'; -import { runWithContext } from 'src/context'; +import { runWithServerContext, ServerContextFields } from 'src/context'; import { crypto } from './lib/node-utils'; @@ -131,13 +131,15 @@ const getStore = async ({ req, ctx, }: { - store?: Map | ((req: next.NextRequest, ctx: any) => Map | Promise>); + store?: + | ServerContextFields + | ((req: next.NextRequest, ctx: any) => ServerContextFields | Promise); req: next.NextRequest; ctx: any; }) => { if (!store) { const newStore = new Map(); - newStore.set('trace_id', crypto.randomUUID()); + newStore.set('request_id', crypto.randomUUID()); return newStore; } if (typeof store === 'function') { @@ -153,7 +155,9 @@ export const createAxiomRouteHandler = ({ onError, }: { logger: Logger; - store?: Map | ((req: next.NextRequest, ctx: any) => Map | Promise>); + store?: + | ServerContextFields + | ((req: next.NextRequest, ctx: any) => ServerContextFields | Promise); onSuccess?: (data: SuccessData) => void; onError?: (data: ErrorData) => void; }) => { @@ -161,7 +165,7 @@ export const createAxiomRouteHandler = ({ return async (req: next.NextRequest, ctx: any) => { const store = await getStore({ store: argStore, req, ctx }); - return runWithContext(async () => { + return runWithServerContext(async () => { const start = Date.now(); try { From 3423b4c54b6d06ea1e89a3519f26d0d89771368f Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Fri, 7 Feb 2025 16:58:06 +0000 Subject: [PATCH 58/71] refactor: rename AxiomProxyTransport to ProxyTransport --- examples/nextjs/src/lib/axiom/client.ts | 4 ++-- examples/nextjs/src/lib/axiom/server.ts | 19 +++++++++++++++++-- .../logging/src/transports/proxy-transport.ts | 6 +++--- .../unit/transports/proxy-transport.test.ts | 10 +++++----- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/examples/nextjs/src/lib/axiom/client.ts b/examples/nextjs/src/lib/axiom/client.ts index 36137345..20cea0ef 100644 --- a/examples/nextjs/src/lib/axiom/client.ts +++ b/examples/nextjs/src/lib/axiom/client.ts @@ -1,11 +1,11 @@ 'use client'; -import { Logger, AxiomProxyTransport, ConsoleTransport } from '@axiomhq/logging'; +import { Logger, ProxyTransport, ConsoleTransport } from '@axiomhq/logging'; import { createUseLogger, createWebVitalsComponent } from '@axiomhq/react'; export const logger = new Logger({ transports: [ - new AxiomProxyTransport({ url: process.env.NEXT_PUBLIC_AXIOM_PROXY_URL!, autoFlush: true }), + new ProxyTransport({ url: process.env.NEXT_PUBLIC_AXIOM_PROXY_URL!, autoFlush: true }), new ConsoleTransport(), ], }); diff --git a/examples/nextjs/src/lib/axiom/server.ts b/examples/nextjs/src/lib/axiom/server.ts index bde5b0b0..5add1d83 100644 --- a/examples/nextjs/src/lib/axiom/server.ts +++ b/examples/nextjs/src/lib/axiom/server.ts @@ -1,5 +1,10 @@ import { Logger, AxiomFetchTransport, ConsoleTransport } from '@axiomhq/logging'; -import { createAxiomRouteHandler, serverContextFieldsFormatter } from '@axiomhq/nextjs'; +import { + createAxiomRouteHandler, + getLogLevelFromStatusCode, + serverContextFieldsFormatter, + transformRouteHandlerErrorResult, +} from '@axiomhq/nextjs'; export const logger = new Logger({ transports: [ @@ -12,4 +17,14 @@ export const logger = new Logger({ formatters: [serverContextFieldsFormatter], }); -export const withAxiom = createAxiomRouteHandler({ logger }); +export const withAxiom = createAxiomRouteHandler({ + logger, + onError: (error) => { + if (error.error instanceof Error) { + logger.error(error.error.message, error.error); + } + const [message, report] = transformRouteHandlerErrorResult(error); + logger.log(getLogLevelFromStatusCode(report.statusCode), message, report); + logger.flush(); + }, +}); diff --git a/packages/logging/src/transports/proxy-transport.ts b/packages/logging/src/transports/proxy-transport.ts index 37418244..bbfb742e 100644 --- a/packages/logging/src/transports/proxy-transport.ts +++ b/packages/logging/src/transports/proxy-transport.ts @@ -2,13 +2,13 @@ import { LogLevel } from '../logger'; import { Transport } from './transport'; import { SimpleFetchTransport } from './fetch'; -interface AxiomProxyConfig { +interface ProxyTransportConfig { url: string; autoFlush?: boolean | number; logLevel?: LogLevel; } -export class AxiomProxyTransport extends SimpleFetchTransport implements Transport { - constructor(config: AxiomProxyConfig) { +export class ProxyTransport extends SimpleFetchTransport implements Transport { + constructor(config: ProxyTransportConfig) { super({ input: config.url, autoFlush: config.autoFlush, diff --git a/packages/logging/test/unit/transports/proxy-transport.test.ts b/packages/logging/test/unit/transports/proxy-transport.test.ts index 0c7ff406..cd709759 100644 --- a/packages/logging/test/unit/transports/proxy-transport.test.ts +++ b/packages/logging/test/unit/transports/proxy-transport.test.ts @@ -1,10 +1,10 @@ import { describe, afterEach, it, expect, beforeAll, afterAll, vi } from 'vitest'; -import { AxiomProxyTransport } from '../../../src/transports/proxy-transport'; +import { ProxyTransport } from '../../../src/transports/proxy-transport'; import { http, HttpResponse, HttpHandler } from 'msw'; import { setupServer } from 'msw/node'; import { createLogEvent } from '../../lib/mock'; -describe('AxiomProxyTransport', () => { +describe('ProxyTransport', () => { const PROXY_URL = 'https://proxy.example.com/logs'; const handlers: HttpHandler[] = [ @@ -32,7 +32,7 @@ describe('AxiomProxyTransport', () => { }), ); - const transport = new AxiomProxyTransport({ + const transport = new ProxyTransport({ url: PROXY_URL, }); @@ -53,7 +53,7 @@ describe('AxiomProxyTransport', () => { }), ); - const transport = new AxiomProxyTransport({ + const transport = new ProxyTransport({ url: PROXY_URL, autoFlush: false, }); @@ -73,7 +73,7 @@ describe('AxiomProxyTransport', () => { }), ); - const transport = new AxiomProxyTransport({ + const transport = new ProxyTransport({ url: PROXY_URL, }); From b019f8e4e8f16da816f529acb97b27e8cc638fce Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Fri, 7 Feb 2025 17:00:07 +0000 Subject: [PATCH 59/71] refactor: make axiom routeHandler builder consistent with other builders --- examples/nextjs/src/lib/axiom/server.ts | 19 ++------------- packages/nextjs/src/routeHandler.ts | 31 +++++++++++++------------ 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/examples/nextjs/src/lib/axiom/server.ts b/examples/nextjs/src/lib/axiom/server.ts index 5add1d83..5e67007e 100644 --- a/examples/nextjs/src/lib/axiom/server.ts +++ b/examples/nextjs/src/lib/axiom/server.ts @@ -1,10 +1,5 @@ import { Logger, AxiomFetchTransport, ConsoleTransport } from '@axiomhq/logging'; -import { - createAxiomRouteHandler, - getLogLevelFromStatusCode, - serverContextFieldsFormatter, - transformRouteHandlerErrorResult, -} from '@axiomhq/nextjs'; +import { createAxiomRouteHandler, serverContextFieldsFormatter } from '@axiomhq/nextjs'; export const logger = new Logger({ transports: [ @@ -17,14 +12,4 @@ export const logger = new Logger({ formatters: [serverContextFieldsFormatter], }); -export const withAxiom = createAxiomRouteHandler({ - logger, - onError: (error) => { - if (error.error instanceof Error) { - logger.error(error.error.message, error.error); - } - const [message, report] = transformRouteHandlerErrorResult(error); - logger.log(getLogLevelFromStatusCode(report.statusCode), message, report); - logger.flush(); - }, -}); +export const withAxiom = createAxiomRouteHandler(logger); diff --git a/packages/nextjs/src/routeHandler.ts b/packages/nextjs/src/routeHandler.ts index d9502522..424f18e5 100644 --- a/packages/nextjs/src/routeHandler.ts +++ b/packages/nextjs/src/routeHandler.ts @@ -112,7 +112,7 @@ export const getLogLevelFromStatusCode = (statusCode: number): LogLevel => { return LogLevel.error; }; -export const defaultOnRouteHandlerSuccess = (logger: Logger, data: SuccessData) => { +export const defaultRouteHandlerOnSuccess = (logger: Logger, data: SuccessData) => { logger.info(...transformRouteHandlerSuccessResult(data)); logger.flush(); }; @@ -148,19 +148,20 @@ const getStore = async ({ return store; }; -export const createAxiomRouteHandler = ({ - logger, - store: argStore, - onSuccess, - onError, -}: { - logger: Logger; - store?: - | ServerContextFields - | ((req: next.NextRequest, ctx: any) => ServerContextFields | Promise); - onSuccess?: (data: SuccessData) => void; - onError?: (data: ErrorData) => void; -}) => { +export const createAxiomRouteHandler = ( + logger: Logger, + { + store: argStore, + onSuccess, + onError, + }: { + store?: + | ServerContextFields + | ((req: next.NextRequest, ctx: any) => ServerContextFields | Promise); + onSuccess?: (data: SuccessData) => void; + onError?: (data: ErrorData) => void; + }, +) => { const withAxiom = (handler: NextHandler) => { return async (req: next.NextRequest, ctx: any) => { const store = await getStore({ store: argStore, req, ctx }); @@ -177,7 +178,7 @@ export const createAxiomRouteHandler = ({ if (onSuccess) { onSuccess(httpData); } else { - defaultOnRouteHandlerSuccess(logger, httpData); + defaultRouteHandlerOnSuccess(logger, httpData); } }; // TODO: this surely can be written better From 38f948e763eb20309ba038d432b79be1e90afdb5 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Fri, 7 Feb 2025 21:57:06 +0000 Subject: [PATCH 60/71] fix: context types --- packages/nextjs/src/context/storage.ts | 2 +- packages/nextjs/src/routeHandler.ts | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/nextjs/src/context/storage.ts b/packages/nextjs/src/context/storage.ts index c7286a8f..c28a014e 100644 --- a/packages/nextjs/src/context/storage.ts +++ b/packages/nextjs/src/context/storage.ts @@ -22,6 +22,6 @@ export const serverContextFieldsFormatter = (fields: Record) => { return { ...fields, ...store }; }; -export const runWithServerContext = (callback: () => void, store: ReturnType) => { +export const runWithServerContext = (callback: () => any, store: ReturnType) => { return storage.run(store, callback); }; diff --git a/packages/nextjs/src/routeHandler.ts b/packages/nextjs/src/routeHandler.ts index 424f18e5..045ed737 100644 --- a/packages/nextjs/src/routeHandler.ts +++ b/packages/nextjs/src/routeHandler.ts @@ -2,7 +2,7 @@ import { Logger, LogLevel } from '@axiomhq/logging'; import { isRedirectError } from 'next/dist/client/components/redirect-error'; import { isHTTPAccessFallbackError } from 'next/dist/client/components/http-access-fallback/http-access-fallback'; import * as next from 'next/server'; -import { runWithServerContext, ServerContextFields } from 'src/context'; +import { runWithServerContext, ServerContextFields } from './context'; import { crypto } from './lib/node-utils'; @@ -150,11 +150,7 @@ const getStore = async ({ export const createAxiomRouteHandler = ( logger: Logger, - { - store: argStore, - onSuccess, - onError, - }: { + config?: { store?: | ServerContextFields | ((req: next.NextRequest, ctx: any) => ServerContextFields | Promise); @@ -162,6 +158,7 @@ export const createAxiomRouteHandler = ( onError?: (data: ErrorData) => void; }, ) => { + const { store: argStore, onSuccess, onError } = config ?? {}; const withAxiom = (handler: NextHandler) => { return async (req: next.NextRequest, ctx: any) => { const store = await getStore({ store: argStore, req, ctx }); From a74b05f1ebb6843f858f9fcdbfbc1fc55cbb542e Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Sun, 16 Feb 2025 22:05:52 -0400 Subject: [PATCH 61/71] refactor: remove next.js direct dependencies --- packages/nextjs/package.json | 3 +- packages/nextjs/src/lib/next-http-errors.ts | 75 ++++++++++ .../nextjs/src/lib/next-redirect-errors.ts | 53 +++++++ packages/nextjs/src/middleware.ts | 16 ++- packages/nextjs/src/proxyRouteHandler.ts | 3 +- packages/nextjs/src/routeHandler.ts | 85 ++++++------ pnpm-lock.yaml | 129 +++++++++++++++++- 7 files changed, 308 insertions(+), 56 deletions(-) create mode 100644 packages/nextjs/src/lib/next-http-errors.ts create mode 100644 packages/nextjs/src/lib/next-redirect-errors.ts diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index e10eeba6..06b5fa0b 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -59,10 +59,11 @@ "devDependencies": { "@repo/eslint-config": "workspace:*", "@tanstack/config": "^0.16.0", + "next": "^15.1.7", "vite": "^5.2.14", "vitest": "^0.34.6" }, "peerDependencies": { - "next": "^15.1" + "next": "^15 || ^14 || ^13" } } diff --git a/packages/nextjs/src/lib/next-http-errors.ts b/packages/nextjs/src/lib/next-http-errors.ts new file mode 100644 index 00000000..dcde521a --- /dev/null +++ b/packages/nextjs/src/lib/next-http-errors.ts @@ -0,0 +1,75 @@ +// from https://github.com/vercel/next.js/blob/canary/packages/next/src/client/components/http-access-fallback/http-access-fallback.ts + +export const HTTPAccessErrorStatus = { + NOT_FOUND: 404, + FORBIDDEN: 403, + UNAUTHORIZED: 401, +}; + +const ALLOWED_CODES = new Set(Object.values(HTTPAccessErrorStatus)); + +export const HTTP_ERROR_FALLBACK_ERROR_CODE = 'NEXT_HTTP_ERROR_FALLBACK'; + +export type HTTPAccessFallbackError = Error & { + digest: `${typeof HTTP_ERROR_FALLBACK_ERROR_CODE};${string}`; +}; + +/** + * Checks an error to determine if it's an error generated by + * the HTTP navigation APIs `notFound()`, `forbidden()` or `unauthorized()`. + * + * @param error the error that may reference a HTTP access error + * @returns true if the error is a HTTP access error + */ +export function isHTTPAccessFallbackError(error: unknown): error is HTTPAccessFallbackError { + if (typeof error !== 'object' || error === null || !('digest' in error) || typeof error.digest !== 'string') { + return false; + } + const [prefix, httpStatus] = error.digest.split(';'); + + return prefix === HTTP_ERROR_FALLBACK_ERROR_CODE && ALLOWED_CODES.has(Number(httpStatus)); +} + +export function getAccessFallbackHTTPStatus(error: HTTPAccessFallbackError | NotFoundError): number { + if (isNotFoundError(error)) { + return 404; + } + const httpStatus = error.digest.split(';')[1]; + return Number(httpStatus); +} + +export function getAccessFallbackErrorTypeByStatus( + status: number, +): 'not-found' | 'forbidden' | 'unauthorized' | undefined { + switch (status) { + case 401: + return 'unauthorized'; + case 403: + return 'forbidden'; + case 404: + return 'not-found'; + default: + return; + } +} + +// Legacy notFound() + +const NOT_FOUND_ERROR_CODE = 'NEXT_NOT_FOUND'; + +type NotFoundError = Error & { digest: typeof NOT_FOUND_ERROR_CODE }; + +/** + * Checks an error to determine if it's an error generated by the `notFound()` + * helper. + * + * @param error the error that may reference a not found error + * @returns true if the error is a not found error + */ +export function isNotFoundError(error: unknown): error is NotFoundError { + if (typeof error !== 'object' || error === null || !('digest' in error)) { + return false; + } + + return error.digest === NOT_FOUND_ERROR_CODE; +} diff --git a/packages/nextjs/src/lib/next-redirect-errors.ts b/packages/nextjs/src/lib/next-redirect-errors.ts new file mode 100644 index 00000000..de446148 --- /dev/null +++ b/packages/nextjs/src/lib/next-redirect-errors.ts @@ -0,0 +1,53 @@ +// from https://github.com/vercel/next.js/blob/canary/packages/next/src/client/components/redirect-error.ts + +export const RedirectStatusCode = { + SeeOther: 303, + TemporaryRedirect: 307, + PermanentRedirect: 308, +} as const; + +export type RedirectStatusCode = (typeof RedirectStatusCode)[keyof typeof RedirectStatusCode]; + +export const REDIRECT_ERROR_CODE = 'NEXT_REDIRECT'; + +export enum RedirectType { + push = 'push', + replace = 'replace', +} + +export type RedirectError = Error & { + digest: `${typeof REDIRECT_ERROR_CODE};${RedirectType};${string};${RedirectStatusCode};`; +}; + +/** + * Checks an error to determine if it's an error generated by the + * `redirect(url)` helper. + * + * @param error the error that may reference a redirect error + * @returns true if the error is a redirect error + */ +export function isRedirectError(error: unknown): error is RedirectError { + if (typeof error !== 'object' || error === null || !('digest' in error) || typeof error.digest !== 'string') { + return false; + } + + const digest = error.digest.split(';'); + const [errorCode, type] = digest; + const destination = digest.slice(2, -2).join(';'); + const status = digest.at(-2); + + const statusCode = Number(status); + + return ( + errorCode === REDIRECT_ERROR_CODE && + (type === 'replace' || type === 'push') && + typeof destination === 'string' && + !isNaN(statusCode) && + Object.values(RedirectStatusCode).includes(statusCode as RedirectStatusCode) + ); +} + +export function getRedirectStatus(error: RedirectError): RedirectStatusCode { + const status = error.digest.split(';').at(-2); + return Number(status) as RedirectStatusCode; +} diff --git a/packages/nextjs/src/middleware.ts b/packages/nextjs/src/middleware.ts index 752ee168..c69c77f6 100644 --- a/packages/nextjs/src/middleware.ts +++ b/packages/nextjs/src/middleware.ts @@ -1,20 +1,22 @@ import { NextRequest } from 'next/server'; export const transformMiddlewareRequest = ( - request: NextRequest & { geo?: { region: string }; ip?: string }, + request: NextRequest | Request, ): [message: string, report: Record] => { + const url = 'nextUrl' in request ? request.nextUrl : new URL(request.url); + const report = { request: { - ip: request.ip, - region: request.geo?.region, + ip: 'ip' in request ? request.ip : undefined, + region: 'geo' in request ? (request.geo as { region?: string }).region : undefined, method: request.method, - host: request.nextUrl.hostname, - path: request.nextUrl.pathname, - scheme: request.nextUrl.protocol.split(':')[0], + host: url.hostname, + path: url.pathname, + scheme: url.protocol.split(':')[0], referer: request.headers.get('Referer'), userAgent: request.headers.get('user-agent'), }, }; - return [`${request.method} ${request.nextUrl.pathname}`, report]; + return [`${request.method} ${url.pathname}`, report]; }; diff --git a/packages/nextjs/src/proxyRouteHandler.ts b/packages/nextjs/src/proxyRouteHandler.ts index 51d2bde8..362e08c4 100644 --- a/packages/nextjs/src/proxyRouteHandler.ts +++ b/packages/nextjs/src/proxyRouteHandler.ts @@ -1,8 +1,7 @@ import { LogEvent, Logger } from '@axiomhq/logging'; -import { NextRequest } from 'next/server'; export const createProxyRouteHandler = (logger: Logger) => { - return async (req: NextRequest) => { + return async (req: T) => { try { const events = (await req.json()) as LogEvent[]; events.forEach((event) => { diff --git a/packages/nextjs/src/routeHandler.ts b/packages/nextjs/src/routeHandler.ts index 045ed737..9201d01e 100644 --- a/packages/nextjs/src/routeHandler.ts +++ b/packages/nextjs/src/routeHandler.ts @@ -1,25 +1,17 @@ import { Logger, LogLevel } from '@axiomhq/logging'; -import { isRedirectError } from 'next/dist/client/components/redirect-error'; -import { isHTTPAccessFallbackError } from 'next/dist/client/components/http-access-fallback/http-access-fallback'; import * as next from 'next/server'; import { runWithServerContext, ServerContextFields } from './context'; - import { crypto } from './lib/node-utils'; +import { getAccessFallbackHTTPStatus, isHTTPAccessFallbackError } from 'src/lib/next-http-errors'; +import { getRedirectStatus, isRedirectError } from 'src/lib/next-redirect-errors'; +import { NextRequest, NextResponse } from 'next/server'; const after = next.after; -export type NextHandler = ( - req: next.NextRequest, - arg?: T, -) => Promise | Promise | next.NextResponse | Response; - -const getRegion = (req: next.NextRequest) => { - let region = ''; - if ('geo' in req) { - region = (req.geo as { region: string }).region ?? ''; - } - return region; -}; +export type NextHandler = ( + req: T extends Request ? T : Request, + arg?: A, +) => Promise | Promise | NextResponse | R; export const transformRouteHandlerSuccessResult = ( data: SuccessData, @@ -28,13 +20,13 @@ export const transformRouteHandlerSuccessResult = ( request: { startTime: new Date().getTime(), endTime: new Date().getTime(), - path: data.req.nextUrl.pathname ?? new URL(data.req.url).pathname, + path: 'nextUrl' in data.req ? (data.req.nextUrl as URL).pathname : new URL(data.req.url).pathname, method: data.req.method, host: data.req.headers.get('host'), userAgent: data.req.headers.get('user-agent'), scheme: data.req.url.split('://')[0], ip: data.req.headers.get('x-forwarded-for'), - region: getRegion(data.req), + region: 'geo' in data.req ? (data.req.geo as { region: string }).region ?? undefined : undefined, statusCode: data.res.status, }, }; @@ -52,13 +44,13 @@ export const transformRouteHandlerErrorResult = (data: ErrorData): [message: str request: { startTime: new Date().getTime(), endTime: new Date().getTime(), - path: data.req.nextUrl.pathname ?? new URL(data.req.url).pathname, + path: 'nextUrl' in data.req ? (data.req.nextUrl as URL).pathname : new URL(data.req.url).pathname, method: data.req.method, host: data.req.headers.get('host'), userAgent: data.req.headers.get('user-agent'), scheme: data.req.url.split('://')[0], ip: data.req.headers.get('x-forwarded-for'), - region: getRegion(data.req), + region: 'geo' in data.req ? (data.req.geo as { region: string }).region ?? '' : '', statusCode: statusCode, }, }; @@ -69,38 +61,39 @@ export const transformRouteHandlerErrorResult = (data: ErrorData): [message: str ]; }; -export interface BaseData { - req: next.NextRequest; +export interface BaseData { + req: T extends Request ? T : Request; start: number; end: number; } -export interface SuccessData extends BaseData { - res: next.NextResponse | Response; + +export interface SuccessData extends BaseData { + res: R; } -export interface ErrorData extends BaseData { +export interface ErrorData extends BaseData { error: Error | unknown; } -export type AxiomHandlerCallbackParams = +export type AxiomHandlerCallbackParams = | { ok: true; - data: SuccessData; + data: SuccessData; } - | { ok: false; data: ErrorData }; + | { ok: false; data: ErrorData }; -export type axiomHandlerCallback = (result: AxiomHandlerCallbackParams) => void | Promise; +export type axiomHandlerCallback = ( + result: AxiomHandlerCallbackParams, +) => void | Promise; export const getNextErrorStatusCode = (error: Error & { digest?: string }) => { - if (!error.digest) { - return 500; - } - if (isRedirectError(error)) { - return error.digest.split(';')[3]; + return getRedirectStatus(error); } else if (isHTTPAccessFallbackError(error)) { - return error.digest.split(';')[1]; + return getAccessFallbackHTTPStatus(error); } + + return 500; }; export const getLogLevelFromStatusCode = (statusCode: number): LogLevel => { @@ -122,20 +115,20 @@ const defaultRouteHandlerOnError = (logger: Logger, data: ErrorData) => { logger.error(data.error.message, data.error); } const [message, report] = transformRouteHandlerErrorResult(data); - logger.log(getLogLevelFromStatusCode(report.statusCode), message, report); + logger.log(getLogLevelFromStatusCode(report.statusCsode), message, report); logger.flush(); }; -const getStore = async ({ +const getStore = async ({ store, req, ctx, }: { store?: | ServerContextFields - | ((req: next.NextRequest, ctx: any) => ServerContextFields | Promise); - req: next.NextRequest; - ctx: any; + | ((req: T extends Request ? T : Request, ctx: C) => ServerContextFields | Promise); + req: T extends Request ? T : Request; + ctx: C; }) => { if (!store) { const newStore = new Map(); @@ -148,19 +141,25 @@ const getStore = async ({ return store; }; -export const createAxiomRouteHandler = ( +export const createAxiomRouteHandler = ( logger: Logger, config?: { store?: | ServerContextFields - | ((req: next.NextRequest, ctx: any) => ServerContextFields | Promise); + | (( + req: TRequestStore, + ctx: C, + ) => ServerContextFields | Promise); onSuccess?: (data: SuccessData) => void; onError?: (data: ErrorData) => void; }, ) => { const { store: argStore, onSuccess, onError } = config ?? {}; - const withAxiom = (handler: NextHandler) => { - return async (req: next.NextRequest, ctx: any) => { + const withAxiom = (handler: NextHandler) => { + return async ( + req: TRequestRouteHandler extends Request ? TRequestRouteHandler : Request, + ctx: C, + ) => { const store = await getStore({ store: argStore, req, ctx }); return runWithServerContext(async () => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 08357d28..572172f7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -306,9 +306,6 @@ importers: '@axiomhq/logging': specifier: workspace:* version: link:../logging - next: - specifier: ^15.1 - version: 15.1.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0) devDependencies: '@repo/eslint-config': specifier: workspace:* @@ -316,6 +313,9 @@ importers: '@tanstack/config': specifier: ^0.16.0 version: 0.16.0(@types/node@20.14.2)(esbuild@0.20.2)(eslint@9.18.0(jiti@2.4.2))(rollup@4.22.4)(typescript@5.4.5)(vite@5.2.14(@types/node@20.14.2)) + next: + specifier: ^15.1.7 + version: 15.1.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0) vite: specifier: ^5.2.14 version: 5.2.14(@types/node@20.14.2) @@ -949,6 +949,9 @@ packages: '@next/env@15.1.4': resolution: {integrity: sha512-2fZ5YZjedi5AGaeoaC0B20zGntEHRhi2SdWcu61i48BllODcAmmtj8n7YarSPt4DaTsJaBFdxQAVEVzgmx2Zpw==} + '@next/env@15.1.7': + resolution: {integrity: sha512-d9jnRrkuOH7Mhi+LHav2XW91HOgTAWHxjMPkXMGBc9B2b7614P7kjt8tAplRvJpbSt4nbO1lugcT/kAaWzjlLQ==} + '@next/eslint-plugin-next@15.1.4': resolution: {integrity: sha512-HwlEXwCK3sr6zmVGEvWBjW9tBFs1Oe6hTmTLoFQtpm4As5HCdu8jfSE0XJOp7uhfEGLniIx8yrGxEWwNnY0fmQ==} @@ -967,6 +970,12 @@ packages: cpu: [arm64] os: [darwin] + '@next/swc-darwin-arm64@15.1.7': + resolution: {integrity: sha512-hPFwzPJDpA8FGj7IKV3Yf1web3oz2YsR8du4amKw8d+jAOHfYHYFpMkoF6vgSY4W6vB29RtZEklK9ayinGiCmQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + '@next/swc-darwin-x64@15.0.2': resolution: {integrity: sha512-KUpBVxIbjzFiUZhiLIpJiBoelqzQtVZbdNNsehhUn36e2YzKHphnK8eTUW1s/4aPy5kH/UTid8IuVbaOpedhpw==} engines: {node: '>= 10'} @@ -979,6 +988,12 @@ packages: cpu: [x64] os: [darwin] + '@next/swc-darwin-x64@15.1.7': + resolution: {integrity: sha512-2qoas+fO3OQKkU0PBUfwTiw/EYpN+kdAx62cePRyY1LqKtP09Vp5UcUntfZYajop5fDFTjSxCHfZVRxzi+9FYQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + '@next/swc-linux-arm64-gnu@15.0.2': resolution: {integrity: sha512-9J7TPEcHNAZvwxXRzOtiUvwtTD+fmuY0l7RErf8Yyc7kMpE47MIQakl+3jecmkhOoIyi/Rp+ddq7j4wG6JDskQ==} engines: {node: '>= 10'} @@ -991,6 +1006,12 @@ packages: cpu: [arm64] os: [linux] + '@next/swc-linux-arm64-gnu@15.1.7': + resolution: {integrity: sha512-sKLLwDX709mPdzxMnRIXLIT9zaX2w0GUlkLYQnKGoXeWUhcvpCrK+yevcwCJPdTdxZEUA0mOXGLdPsGkudGdnA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@next/swc-linux-arm64-musl@15.0.2': resolution: {integrity: sha512-BjH4ZSzJIoTTZRh6rG+a/Ry4SW0HlizcPorqNBixBWc3wtQtj4Sn9FnRZe22QqrPnzoaW0ctvSz4FaH4eGKMww==} engines: {node: '>= 10'} @@ -1003,6 +1024,12 @@ packages: cpu: [arm64] os: [linux] + '@next/swc-linux-arm64-musl@15.1.7': + resolution: {integrity: sha512-zblK1OQbQWdC8fxdX4fpsHDw+VSpBPGEUX4PhSE9hkaWPrWoeIJn+baX53vbsbDRaDKd7bBNcXRovY1hEhFd7w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@next/swc-linux-x64-gnu@15.0.2': resolution: {integrity: sha512-i3U2TcHgo26sIhcwX/Rshz6avM6nizrZPvrDVDY1bXcLH1ndjbO8zuC7RoHp0NSK7wjJMPYzm7NYL1ksSKFreA==} engines: {node: '>= 10'} @@ -1015,6 +1042,12 @@ packages: cpu: [x64] os: [linux] + '@next/swc-linux-x64-gnu@15.1.7': + resolution: {integrity: sha512-GOzXutxuLvLHFDAPsMP2zDBMl1vfUHHpdNpFGhxu90jEzH6nNIgmtw/s1MDwpTOiM+MT5V8+I1hmVFeAUhkbgQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@next/swc-linux-x64-musl@15.0.2': resolution: {integrity: sha512-AMfZfSVOIR8fa+TXlAooByEF4OB00wqnms1sJ1v+iu8ivwvtPvnkwdzzFMpsK5jA2S9oNeeQ04egIWVb4QWmtQ==} engines: {node: '>= 10'} @@ -1027,6 +1060,12 @@ packages: cpu: [x64] os: [linux] + '@next/swc-linux-x64-musl@15.1.7': + resolution: {integrity: sha512-WrZ7jBhR7ATW1z5iEQ0ZJfE2twCNSXbpCSaAunF3BKcVeHFADSI/AW1y5Xt3DzTqPF1FzQlwQTewqetAABhZRQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@next/swc-win32-arm64-msvc@15.0.2': resolution: {integrity: sha512-JkXysDT0/hEY47O+Hvs8PbZAeiCQVxKfGtr4GUpNAhlG2E0Mkjibuo8ryGD29Qb5a3IOnKYNoZlh/MyKd2Nbww==} engines: {node: '>= 10'} @@ -1039,6 +1078,12 @@ packages: cpu: [arm64] os: [win32] + '@next/swc-win32-arm64-msvc@15.1.7': + resolution: {integrity: sha512-LDnj1f3OVbou1BqvvXVqouJZKcwq++mV2F+oFHptToZtScIEnhNRJAhJzqAtTE2dB31qDYL45xJwrc+bLeKM2Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + '@next/swc-win32-x64-msvc@15.0.2': resolution: {integrity: sha512-foaUL0NqJY/dX0Pi/UcZm5zsmSk5MtP/gxx3xOPyREkMFN+CTjctPfu3QaqrQHinaKdPnMWPJDKt4VjDfTBe/Q==} engines: {node: '>= 10'} @@ -1051,6 +1096,12 @@ packages: cpu: [x64] os: [win32] + '@next/swc-win32-x64-msvc@15.1.7': + resolution: {integrity: sha512-dC01f1quuf97viOfW05/K8XYv2iuBgAxJZl7mbCKEjMgdQl5JjAKJ0D2qMKZCgPWDeFbFT0Q0nYWwytEW0DWTQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -3283,6 +3334,27 @@ packages: sass: optional: true + next@15.1.7: + resolution: {integrity: sha512-GNeINPGS9c6OZKCvKypbL8GTsT5GhWPp4DM0fzkXJuXMilOO2EeFxuAY6JZbtk6XIl6Ws10ag3xRINDjSO5+wg==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.41.2 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} @@ -5073,6 +5145,8 @@ snapshots: '@next/env@15.1.4': {} + '@next/env@15.1.7': {} + '@next/eslint-plugin-next@15.1.4': dependencies: fast-glob: 3.3.1 @@ -5087,48 +5161,72 @@ snapshots: '@next/swc-darwin-arm64@15.1.4': optional: true + '@next/swc-darwin-arm64@15.1.7': + optional: true + '@next/swc-darwin-x64@15.0.2': optional: true '@next/swc-darwin-x64@15.1.4': optional: true + '@next/swc-darwin-x64@15.1.7': + optional: true + '@next/swc-linux-arm64-gnu@15.0.2': optional: true '@next/swc-linux-arm64-gnu@15.1.4': optional: true + '@next/swc-linux-arm64-gnu@15.1.7': + optional: true + '@next/swc-linux-arm64-musl@15.0.2': optional: true '@next/swc-linux-arm64-musl@15.1.4': optional: true + '@next/swc-linux-arm64-musl@15.1.7': + optional: true + '@next/swc-linux-x64-gnu@15.0.2': optional: true '@next/swc-linux-x64-gnu@15.1.4': optional: true + '@next/swc-linux-x64-gnu@15.1.7': + optional: true + '@next/swc-linux-x64-musl@15.0.2': optional: true '@next/swc-linux-x64-musl@15.1.4': optional: true + '@next/swc-linux-x64-musl@15.1.7': + optional: true + '@next/swc-win32-arm64-msvc@15.0.2': optional: true '@next/swc-win32-arm64-msvc@15.1.4': optional: true + '@next/swc-win32-arm64-msvc@15.1.7': + optional: true + '@next/swc-win32-x64-msvc@15.0.2': optional: true '@next/swc-win32-x64-msvc@15.1.4': optional: true + '@next/swc-win32-x64-msvc@15.1.7': + optional: true + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -7745,6 +7843,31 @@ snapshots: - '@babel/core' - babel-plugin-macros + next@15.1.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + '@next/env': 15.1.7 + '@swc/counter': 0.1.3 + '@swc/helpers': 0.5.15 + busboy: 1.6.0 + caniuse-lite: 1.0.30001695 + postcss: 8.4.31 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + styled-jsx: 5.1.6(react@19.0.0) + optionalDependencies: + '@next/swc-darwin-arm64': 15.1.7 + '@next/swc-darwin-x64': 15.1.7 + '@next/swc-linux-arm64-gnu': 15.1.7 + '@next/swc-linux-arm64-musl': 15.1.7 + '@next/swc-linux-x64-gnu': 15.1.7 + '@next/swc-linux-x64-musl': 15.1.7 + '@next/swc-win32-arm64-msvc': 15.1.7 + '@next/swc-win32-x64-msvc': 15.1.7 + sharp: 0.33.5 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + node-releases@2.0.19: {} normalize-path@3.0.0: {} From 7122e800b61db85fcb89f519cdec815e0027b85f Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 18 Feb 2025 08:55:50 -0400 Subject: [PATCH 62/71] chore: tidy up package versions --- packages/logging/package.json | 2 +- packages/nextjs/package.json | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/logging/package.json b/packages/logging/package.json index 397220c6..89f0ccfa 100644 --- a/packages/logging/package.json +++ b/packages/logging/package.json @@ -52,7 +52,7 @@ "url": "https://github.com/axiomhq/axiom-js/issues" }, "dependencies": { - "@axiomhq/js": "^1.3.1" + "@axiomhq/js": "workspace:*" }, "devDependencies": { "@repo/eslint-config": "workspace:*", diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 06b5fa0b..b5f87440 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -53,9 +53,6 @@ "nextjs", "logging" ], - "dependencies": { - "@axiomhq/logging": "workspace:*" - }, "devDependencies": { "@repo/eslint-config": "workspace:*", "@tanstack/config": "^0.16.0", @@ -64,6 +61,7 @@ "vitest": "^0.34.6" }, "peerDependencies": { + "@axiomhq/logging": "workspace:*", "next": "^15 || ^14 || ^13" } } From d93fcf64a4781ed509e94d5d1aa16b558251f5e8 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 18 Feb 2025 08:56:36 -0400 Subject: [PATCH 63/71] refactor: autoFlush as a boolean or config object --- .../logging/src/transports/axiom-fetch.ts | 2 +- packages/logging/src/transports/fetch.ts | 13 +++++---- .../logging/src/transports/proxy-transport.ts | 2 +- .../test/unit/transports/fetch.test.ts | 27 +++++++++++++++++-- 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/packages/logging/src/transports/axiom-fetch.ts b/packages/logging/src/transports/axiom-fetch.ts index f2f87d19..b4d104d0 100644 --- a/packages/logging/src/transports/axiom-fetch.ts +++ b/packages/logging/src/transports/axiom-fetch.ts @@ -5,7 +5,7 @@ interface AxiomFetchConfig { dataset: string; token: string; url?: string; - autoFlush?: boolean | number; + autoFlush?: boolean | { durationMs: number }; logLevel?: LogLevel; } diff --git a/packages/logging/src/transports/fetch.ts b/packages/logging/src/transports/fetch.ts index b0019219..390714af 100644 --- a/packages/logging/src/transports/fetch.ts +++ b/packages/logging/src/transports/fetch.ts @@ -4,7 +4,7 @@ import { LogEvent, LogLevelValue, LogLevel } from '../logger'; interface FetchConfig { input: Parameters[0]; init?: Omit[1], 'body'>; - autoFlush?: number | boolean; + autoFlush?: boolean | { durationMs: number }; logLevel?: LogLevel; } @@ -34,12 +34,11 @@ export class SimpleFetchTransport implements Transport { clearTimeout(this.timer); } - this.timer = setTimeout( - () => { - this.flush(); - }, - typeof this.fetchConfig.autoFlush === 'number' ? this.fetchConfig.autoFlush : 2000, - ); + const flushDelay = typeof this.fetchConfig.autoFlush === 'boolean' ? 2000 : this.fetchConfig.autoFlush.durationMs; + + this.timer = setTimeout(() => { + this.flush(); + }, flushDelay); }; async flush() { diff --git a/packages/logging/src/transports/proxy-transport.ts b/packages/logging/src/transports/proxy-transport.ts index bbfb742e..854b10da 100644 --- a/packages/logging/src/transports/proxy-transport.ts +++ b/packages/logging/src/transports/proxy-transport.ts @@ -4,7 +4,7 @@ import { SimpleFetchTransport } from './fetch'; interface ProxyTransportConfig { url: string; - autoFlush?: boolean | number; + autoFlush?: boolean | { durationMs: number }; logLevel?: LogLevel; } export class ProxyTransport extends SimpleFetchTransport implements Transport { diff --git a/packages/logging/test/unit/transports/fetch.test.ts b/packages/logging/test/unit/transports/fetch.test.ts index 8f85f562..14822fb4 100644 --- a/packages/logging/test/unit/transports/fetch.test.ts +++ b/packages/logging/test/unit/transports/fetch.test.ts @@ -138,7 +138,7 @@ describe('SimpleFetchTransport', () => { transport = new SimpleFetchTransport({ input: API_URL, - autoFlush: 1000, + autoFlush: { durationMs: 1000 }, }); transport.log([createLogEvent()]); @@ -161,7 +161,7 @@ describe('SimpleFetchTransport', () => { transport = new SimpleFetchTransport({ input: API_URL, - autoFlush: 1000, + autoFlush: { durationMs: 1000 }, }); transport.log([createLogEvent(LogLevel.info, 'first')]); @@ -177,6 +177,29 @@ describe('SimpleFetchTransport', () => { expect(receivedBody[0].message).toBe('first'); expect(receivedBody[1].message).toBe('second'); }); + + it('should auto-flush with custom duration from config object', async () => { + const requestSpy = vi.fn(); + server.use( + http.post(API_URL, async () => { + requestSpy(); + return HttpResponse.json({ success: true }); + }), + ); + + transport = new SimpleFetchTransport({ + input: API_URL, + autoFlush: { durationMs: 500 }, + }); + + transport.log([createLogEvent()]); + + await vi.advanceTimersByTimeAsync(499); + expect(requestSpy).not.toHaveBeenCalled(); + + await vi.advanceTimersByTimeAsync(1); + expect(requestSpy).toHaveBeenCalledTimes(1); + }); }); describe('custom fetch configuration', () => { From 7ec2d5c9e5d09b9b337e54544d0767a75eb192e5 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 18 Feb 2025 20:29:22 -0400 Subject: [PATCH 64/71] chore: update pnpm lock and CI workflow pnpm version --- .github/workflows/ci.yml | 2 +- pnpm-lock.yaml | 16 +++++----------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 248a5379..c418e46c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ env: NEXT_PUBLIC_AXIOM_ORG_ID: ${{ secrets.TESTING_STAGING_E2E_ORG_ID }} AXIOM_DATASET_SUFFIX: ${{ github.run_id }} NODEVERSION: 20.x - PNPM_VERSION: 9.1.1 + PNPM_VERSION: 10.4.1 jobs: build: diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 572172f7..eb20dc26 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -103,6 +103,9 @@ importers: examples/nextjs: dependencies: + '@axiomhq/js': + specifier: workspace:^ + version: link:../../packages/js '@axiomhq/logging': specifier: workspace:* version: link:../../packages/logging @@ -285,8 +288,8 @@ importers: packages/logging: dependencies: '@axiomhq/js': - specifier: ^1.3.1 - version: 1.3.1 + specifier: workspace:* + version: link:../js devDependencies: '@repo/eslint-config': specifier: workspace:* @@ -417,10 +420,6 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@axiomhq/js@1.3.1': - resolution: {integrity: sha512-Ytf5V3wKz8FKNiqJxnqZmUhjgJ7TItKUoyHVNE/H2V9dN1ozD6NNnsueenOjKdA48cm2sGRyP432nworst18aA==} - engines: {node: '>=16'} - '@babel/code-frame@7.26.2': resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} @@ -4639,11 +4638,6 @@ snapshots: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 - '@axiomhq/js@1.3.1': - dependencies: - fetch-retry: 6.0.0 - uuid: 11.0.2 - '@babel/code-frame@7.26.2': dependencies: '@babel/helper-validator-identifier': 7.25.9 From f049829a8ed8abeb274eaf37e502c83efb8ab6cb Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 18 Feb 2025 20:29:48 -0400 Subject: [PATCH 65/71] refactor: remove axiom-fetch in favor of axiom-js transport --- examples/nextjs/.env.example | 5 +- examples/nextjs/package.json | 1 + examples/nextjs/src/lib/axiom/axiom.ts | 7 + examples/nextjs/src/lib/axiom/client.ts | 5 +- examples/nextjs/src/lib/axiom/server.ts | 8 +- .../logging/src/transports/axiom-fetch.ts | 25 --- packages/logging/src/transports/index.ts | 1 - .../logging/src/transports/proxy-transport.ts | 1 + .../test/unit/transports/axiom-fetch.test.ts | 144 ------------------ 9 files changed, 17 insertions(+), 180 deletions(-) create mode 100644 examples/nextjs/src/lib/axiom/axiom.ts delete mode 100644 packages/logging/src/transports/axiom-fetch.ts delete mode 100644 packages/logging/test/unit/transports/axiom-fetch.test.ts diff --git a/examples/nextjs/.env.example b/examples/nextjs/.env.example index f7b3cbb8..2e54dca7 100644 --- a/examples/nextjs/.env.example +++ b/examples/nextjs/.env.example @@ -1,3 +1,2 @@ -AXIOM_TOKEN="" -AXIOM_DATASET="" -NEXT_PUBLIC_AXIOM_PROXY_URL="/api/axiom" \ No newline at end of file +NEXT_PUBLIC_AXIOM_TOKEN="" +NEXT_PUBLIC_AXIOM_DATASET="" diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index 647cd881..6b81267d 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -9,6 +9,7 @@ "lint": "next lint" }, "dependencies": { + "@axiomhq/js": "workspace:*", "@axiomhq/logging": "workspace:*", "@axiomhq/nextjs": "workspace:*", "@axiomhq/react": "workspace:*", diff --git a/examples/nextjs/src/lib/axiom/axiom.ts b/examples/nextjs/src/lib/axiom/axiom.ts new file mode 100644 index 00000000..41a67acd --- /dev/null +++ b/examples/nextjs/src/lib/axiom/axiom.ts @@ -0,0 +1,7 @@ +import { Axiom } from '@axiomhq/js'; + +const axiomClient = new Axiom({ + token: process.env.NEXT_PUBLIC_AXIOM_TOKEN!, +}); + +export default axiomClient; diff --git a/examples/nextjs/src/lib/axiom/client.ts b/examples/nextjs/src/lib/axiom/client.ts index 20cea0ef..8979bc0c 100644 --- a/examples/nextjs/src/lib/axiom/client.ts +++ b/examples/nextjs/src/lib/axiom/client.ts @@ -1,11 +1,12 @@ 'use client'; -import { Logger, ProxyTransport, ConsoleTransport } from '@axiomhq/logging'; +import axiomClient from '@/lib/axiom/axiom'; +import { Logger, ConsoleTransport, AxiomJSTransport } from '@axiomhq/logging'; import { createUseLogger, createWebVitalsComponent } from '@axiomhq/react'; export const logger = new Logger({ transports: [ - new ProxyTransport({ url: process.env.NEXT_PUBLIC_AXIOM_PROXY_URL!, autoFlush: true }), + new AxiomJSTransport({ axiom: axiomClient, dataset: process.env.NEXT_PUBLIC_AXIOM_DATASET! }), new ConsoleTransport(), ], }); diff --git a/examples/nextjs/src/lib/axiom/server.ts b/examples/nextjs/src/lib/axiom/server.ts index 5e67007e..8654cfd9 100644 --- a/examples/nextjs/src/lib/axiom/server.ts +++ b/examples/nextjs/src/lib/axiom/server.ts @@ -1,12 +1,10 @@ -import { Logger, AxiomFetchTransport, ConsoleTransport } from '@axiomhq/logging'; +import axiomClient from '@/lib/axiom/axiom'; +import { Logger, ConsoleTransport, AxiomJSTransport } from '@axiomhq/logging'; import { createAxiomRouteHandler, serverContextFieldsFormatter } from '@axiomhq/nextjs'; export const logger = new Logger({ transports: [ - new AxiomFetchTransport({ - dataset: process.env.AXIOM_DATASET!, - token: process.env.AXIOM_TOKEN!, - }), + new AxiomJSTransport({ axiom: axiomClient, dataset: process.env.NEXT_PUBLIC_AXIOM_DATASET! }), new ConsoleTransport({ prettyPrint: true }), ], formatters: [serverContextFieldsFormatter], diff --git a/packages/logging/src/transports/axiom-fetch.ts b/packages/logging/src/transports/axiom-fetch.ts deleted file mode 100644 index b4d104d0..00000000 --- a/packages/logging/src/transports/axiom-fetch.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { LogLevel } from '../logger'; -import { Transport } from './transport'; -import { SimpleFetchTransport } from './fetch'; -interface AxiomFetchConfig { - dataset: string; - token: string; - url?: string; - autoFlush?: boolean | { durationMs: number }; - logLevel?: LogLevel; -} - -const DEFAULT_URL = 'https://api.axiom.co'; - -export class AxiomFetchTransport extends SimpleFetchTransport implements Transport { - constructor(config: AxiomFetchConfig) { - super({ - input: `${config.url ?? DEFAULT_URL}/v1/datasets/${config.dataset}/ingest`, - init: { - headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${config.token}` }, - }, - autoFlush: config.autoFlush, - logLevel: config.logLevel, - }); - } -} diff --git a/packages/logging/src/transports/index.ts b/packages/logging/src/transports/index.ts index 44e8e453..08699c74 100644 --- a/packages/logging/src/transports/index.ts +++ b/packages/logging/src/transports/index.ts @@ -1,6 +1,5 @@ export * from './transport'; export * from './axiom-js'; -export * from './axiom-fetch'; export * from './console'; export * from './fetch'; export * from './proxy-transport'; diff --git a/packages/logging/src/transports/proxy-transport.ts b/packages/logging/src/transports/proxy-transport.ts index 854b10da..32dbea22 100644 --- a/packages/logging/src/transports/proxy-transport.ts +++ b/packages/logging/src/transports/proxy-transport.ts @@ -7,6 +7,7 @@ interface ProxyTransportConfig { autoFlush?: boolean | { durationMs: number }; logLevel?: LogLevel; } + export class ProxyTransport extends SimpleFetchTransport implements Transport { constructor(config: ProxyTransportConfig) { super({ diff --git a/packages/logging/test/unit/transports/axiom-fetch.test.ts b/packages/logging/test/unit/transports/axiom-fetch.test.ts deleted file mode 100644 index d5e17cd9..00000000 --- a/packages/logging/test/unit/transports/axiom-fetch.test.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { describe, afterEach, it, expect, beforeAll, afterAll } from 'vitest'; -import { AxiomFetchTransport } from '../../../src/transports/axiom-fetch'; -import { http, HttpResponse, HttpHandler } from 'msw'; -import { setupServer } from 'msw/node'; -import { createLogEvent } from '../../lib/mock'; -import { LogLevel } from 'src/logger'; - -describe('AxiomFetchTransport', () => { - const DATASET = 'test-dataset'; - const TOKEN = 'test-token'; - const DEFAULT_URL = 'https://api.axiom.co'; - const CUSTOM_URL = 'https://custom.axiom.co'; - - const handlers: HttpHandler[] = [ - http.post(`${DEFAULT_URL}/v1/datasets/${DATASET}/ingest`, async ({ request }) => { - return HttpResponse.json({ success: true }); - }), - http.post(`${CUSTOM_URL}/v1/datasets/${DATASET}/ingest`, async ({ request }) => { - return HttpResponse.json({ success: true }); - }), - ]; - - const server = setupServer(...handlers); - - beforeAll(() => server.listen()); - afterAll(() => server.close()); - afterEach(() => server.resetHandlers()); - - describe('configuration', () => { - it('should use default URL when no custom URL provided', async () => { - let receivedUrl = ''; - server.use( - http.post('*', async ({ request }) => { - receivedUrl = request.url; - return HttpResponse.json({ success: true }); - }), - ); - - const transport = new AxiomFetchTransport({ - dataset: DATASET, - token: TOKEN, - }); - - transport.log([createLogEvent()]); - await transport.flush(); - - expect(receivedUrl).toBe(`${DEFAULT_URL}/v1/datasets/${DATASET}/ingest`); - }); - - it('should use custom URL when provided', async () => { - let receivedUrl = ''; - server.use( - http.post('*', async ({ request }) => { - receivedUrl = request.url; - return HttpResponse.json({ success: true }); - }), - ); - - const transport = new AxiomFetchTransport({ - dataset: DATASET, - token: TOKEN, - url: CUSTOM_URL, - }); - - transport.log([createLogEvent()]); - await transport.flush(); - - expect(receivedUrl).toBe(`${CUSTOM_URL}/v1/datasets/${DATASET}/ingest`); - }); - - it('should set correct authorization header', async () => { - let receivedHeaders: Headers = new Headers(); - server.use( - http.post('*', async ({ request }) => { - receivedHeaders = request.headers; - return HttpResponse.json({ success: true }); - }), - ); - - const transport = new AxiomFetchTransport({ - dataset: DATASET, - token: TOKEN, - }); - - transport.log([createLogEvent()]); - await transport.flush(); - - expect(receivedHeaders.get('Authorization')).toBe(`Bearer ${TOKEN}`); - expect(receivedHeaders.get('Content-Type')).toBe('application/json'); - }); - - it('should respect autoFlush configuration', async () => { - let requestCount = 0; - server.use( - http.post('*', async () => { - requestCount++; - return HttpResponse.json({ success: true }); - }), - ); - - const transport = new AxiomFetchTransport({ - dataset: DATASET, - token: TOKEN, - autoFlush: false, - }); - - transport.log([createLogEvent()]); - expect(requestCount).toBe(0); - - await transport.flush(); - expect(requestCount).toBe(1); - }); - }); - - describe('log level filtering', () => { - it('should filter logs based on logLevel', async () => { - let receivedLogs: any[] = []; - server.use( - http.post('*', async ({ request }) => { - receivedLogs = (await request.json()) as any[]; - return HttpResponse.json({ success: true }); - }), - ); - - const transport = new AxiomFetchTransport({ - dataset: DATASET, - token: TOKEN, - logLevel: LogLevel.warn, - }); - - transport.log([ - createLogEvent(LogLevel.debug, 'debug message'), - createLogEvent(LogLevel.info, 'info message'), - createLogEvent(LogLevel.warn, 'warn message'), - createLogEvent(LogLevel.error, 'error message'), - ]); - - await transport.flush(); - - expect(receivedLogs).toHaveLength(2); - expect(receivedLogs.map((log) => log.level)).toEqual(['warn', 'error']); - }); - }); -}); From 26d8e8af3f9b745d666cabf9761d9b8da515f7b8 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 18 Feb 2025 21:53:02 -0400 Subject: [PATCH 66/71] chore: update pnpm-lock --- pnpm-lock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eb20dc26..2ed85413 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -104,7 +104,7 @@ importers: examples/nextjs: dependencies: '@axiomhq/js': - specifier: workspace:^ + specifier: workspace:* version: link:../../packages/js '@axiomhq/logging': specifier: workspace:* From fff23faac09e52ccd984e83accf71866dbe10386 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 18 Feb 2025 22:01:45 -0400 Subject: [PATCH 67/71] refactor: move packages to dev/peer dependencies --- packages/logging/package.json | 12 +++++++++--- packages/nextjs/package.json | 1 + packages/react/package.json | 11 ++++++++--- pnpm-lock.yaml | 24 +++++++++++------------- 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/packages/logging/package.json b/packages/logging/package.json index 89f0ccfa..0b1a6611 100644 --- a/packages/logging/package.json +++ b/packages/logging/package.json @@ -51,13 +51,19 @@ "bugs": { "url": "https://github.com/axiomhq/axiom-js/issues" }, - "dependencies": { - "@axiomhq/js": "workspace:*" - }, "devDependencies": { + "@axiomhq/js": "workspace:*", "@repo/eslint-config": "workspace:*", "@tanstack/config": "^0.16.0", "msw": "^2.6.2", "vite": "^5.2.14" + }, + "peerDependencies": { + "@axiomhq/js": "workspace:*" + }, + "peerDependenciesMeta": { + "@axiomhq/js": { + "optional": true + } } } diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index b5f87440..9e07281e 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -54,6 +54,7 @@ "logging" ], "devDependencies": { + "@axiomhq/logging": "workspace:*", "@repo/eslint-config": "workspace:*", "@tanstack/config": "^0.16.0", "next": "^15.1.7", diff --git a/packages/react/package.json b/packages/react/package.json index 1face314..f374fd21 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -54,13 +54,11 @@ }, "homepage": "https://github.com/axiomhq/axiom-js/blob/main/packages/react/README.md", "dependencies": { - "@axiomhq/logging": "workspace:*", - "react": "^19.0.0", - "react-dom": "^19.0.0", "use-deep-compare": "^1.3.0", "web-vitals": "^4.2.4" }, "devDependencies": { + "@axiomhq/logging": "workspace:*", "@repo/eslint-config": "workspace:*", "@tanstack/config": "^0.16.0", "@testing-library/jest-dom": "^6.6.3", @@ -70,7 +68,14 @@ "@types/react-dom": "^19", "@vitejs/plugin-react": "^4.3.4", "happy-dom": "^16.7.3", + "react": "^19.0.0", + "react-dom": "^19.0.0", "vite": "^5.2.14", "vitest": "^0.34.6" + }, + "peerDependencies": { + "@axiomhq/logging": "workspace:*", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ed85413..1056a6c1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -286,11 +286,10 @@ importers: version: 2.6.2(@types/node@20.14.2)(typescript@5.4.5) packages/logging: - dependencies: + devDependencies: '@axiomhq/js': specifier: workspace:* version: link:../js - devDependencies: '@repo/eslint-config': specifier: workspace:* version: link:../../internal/eslint-config @@ -305,11 +304,10 @@ importers: version: 5.2.14(@types/node@20.14.2) packages/nextjs: - dependencies: + devDependencies: '@axiomhq/logging': specifier: workspace:* version: link:../logging - devDependencies: '@repo/eslint-config': specifier: workspace:* version: link:../../internal/eslint-config @@ -341,15 +339,6 @@ importers: packages/react: dependencies: - '@axiomhq/logging': - specifier: workspace:* - version: link:../logging - react: - specifier: ^19.0.0 - version: 19.0.0 - react-dom: - specifier: ^19.0.0 - version: 19.0.0(react@19.0.0) use-deep-compare: specifier: ^1.3.0 version: 1.3.0(react@19.0.0) @@ -357,6 +346,9 @@ importers: specifier: ^4.2.4 version: 4.2.4 devDependencies: + '@axiomhq/logging': + specifier: workspace:* + version: link:../logging '@repo/eslint-config': specifier: workspace:* version: link:../../internal/eslint-config @@ -384,6 +376,12 @@ importers: happy-dom: specifier: ^16.7.3 version: 16.7.3 + react: + specifier: ^19.0.0 + version: 19.0.0 + react-dom: + specifier: ^19.0.0 + version: 19.0.0(react@19.0.0) vite: specifier: ^5.2.14 version: 5.2.14(@types/node@20.14.2) From f976cc1826ecf605246c0e12f2442e588330ff50 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Tue, 18 Feb 2025 23:06:59 -0400 Subject: [PATCH 68/71] fix: deps on lint package --- internal/eslint-config/package.json | 4 ++-- pnpm-lock.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/eslint-config/package.json b/internal/eslint-config/package.json index 2502c617..9985249d 100644 --- a/internal/eslint-config/package.json +++ b/internal/eslint-config/package.json @@ -7,11 +7,11 @@ "./base": "./base.js", "./next-js": "./next.js" }, - "devDependencies": { + "dependencies": { + "eslint": "^9.18.0", "@eslint/compat": "^1.2.5", "@eslint/js": "^9.17.0", "@next/eslint-plugin-next": "^15.1.0", - "eslint": "^9.18.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-react": "^7.37.2", "eslint-plugin-react-hooks": "^5.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1056a6c1..4dbd5217 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -231,7 +231,7 @@ importers: version: link:../internal/eslint-config internal/eslint-config: - devDependencies: + dependencies: '@eslint/compat': specifier: ^1.2.5 version: 1.2.5(eslint@9.18.0(jiti@2.4.2)) From a83b83cb0885b010e2365eb9b286cba1aae453dd Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Wed, 19 Feb 2025 00:37:17 -0400 Subject: [PATCH 69/71] chore: configure pnpm to hoist eslint and prettier packages --- .npmrc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.npmrc b/.npmrc index 1d3e7e47..bb8e44fb 100644 --- a/.npmrc +++ b/.npmrc @@ -4,3 +4,7 @@ #And that means fewer surprises! This option tells pnpm to only resolve local deps to the local files when the workspace: protocol is used, # and to otherwise download published versions. link-workspace-packages=false +# hoist eslint and prettier to the root +public-hoist-pattern[]=eslint* +public-hoist-pattern[]=prettier* + From 67b83bc60d122bb49fbd9ef145d88389227b07ef Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Wed, 19 Feb 2025 00:49:51 -0400 Subject: [PATCH 70/71] test: update route handler test cases with type-safe status codes --- packages/nextjs/test/unit/routeHandler.test.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/nextjs/test/unit/routeHandler.test.ts b/packages/nextjs/test/unit/routeHandler.test.ts index ba16faa6..2b20f867 100644 --- a/packages/nextjs/test/unit/routeHandler.test.ts +++ b/packages/nextjs/test/unit/routeHandler.test.ts @@ -14,7 +14,7 @@ import { forbidden, notFound, permanentRedirect, redirect, unauthorized } from ' describe('routeHandler', () => { describe('createAxiomRouteHandler', () => { it('should handle successful requests', async () => { - const withAxiom = createAxiomRouteHandler({ logger: mockLogger }); + const withAxiom = createAxiomRouteHandler(mockLogger); const mockResponse = new NextResponse(null, { status: 200 }); const handler = vi.fn().mockResolvedValue(mockResponse); const wrappedHandler = withAxiom(handler); @@ -27,7 +27,7 @@ describe('routeHandler', () => { }); it('should handle errors and rethrow them', async () => { - const withAxiom = createAxiomRouteHandler({ logger: mockLogger }); + const withAxiom = createAxiomRouteHandler(mockLogger); const error = new Error('Test error'); const handler = vi.fn().mockRejectedValue(error); const wrappedHandler = withAxiom(handler); @@ -66,27 +66,27 @@ describe('routeHandler', () => { it('should extract status code from temporary redirect error', async () => { const error = await getErrorFromFunction(() => redirect('/path')); - expect(getNextErrorStatusCode(error)).toBe('307'); + expect(getNextErrorStatusCode(error)).toBe(307); }); it('should extract status code from permanent redirect error', async () => { const error = await getErrorFromFunction(() => permanentRedirect('/path')); - expect(getNextErrorStatusCode(error)).toBe('308'); + expect(getNextErrorStatusCode(error)).toBe(308); }); it('should extract status code from not found error', async () => { const error = await getErrorFromFunction(() => notFound()); - expect(getNextErrorStatusCode(error)).toBe('404'); + expect(getNextErrorStatusCode(error)).toBe(404); }); it('should extract status code from forbidden error', async () => { const error = await getErrorFromFunction(() => forbidden()); - expect(getNextErrorStatusCode(error)).toBe('403'); + expect(getNextErrorStatusCode(error)).toBe(403); }); it('should extract status code from unahtorized error', async () => { const error = await getErrorFromFunction(() => unauthorized()); - expect(getNextErrorStatusCode(error)).toBe('401'); + expect(getNextErrorStatusCode(error)).toBe(401); }); }); @@ -103,7 +103,6 @@ describe('routeHandler', () => { userAgent: 'test', scheme: 'http', ip: '127.0.0.1', - region: '', }; it('should transform success result correctly', () => { From 4ea88f6742af9685e20fda219b2c1508f8f9c9d5 Mon Sep 17 00:00:00 2001 From: Gabriel De Andrade Date: Wed, 19 Feb 2025 14:21:33 -0400 Subject: [PATCH 71/71] docs: Add READMEs to packages --- packages/logging/README.md | 32 +++++++++++++++++++++++++++ packages/nextjs/README.md | 44 ++++++++++++++++++++++++++++++++++++++ packages/react/README.md | 38 ++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 packages/logging/README.md create mode 100644 packages/nextjs/README.md create mode 100644 packages/react/README.md diff --git a/packages/logging/README.md b/packages/logging/README.md new file mode 100644 index 00000000..d2d56740 --- /dev/null +++ b/packages/logging/README.md @@ -0,0 +1,32 @@ +# Axiom Logging library + +The `@axiomhq/logging` package allows you to send structured logs to Axiom from any JavaScript application. + +```ts +// lib/axiom/logger.ts +import axiomClient from '@/lib/axiom/axiom'; +import { Logger, ConsoleTransport, AxiomJSTransport } from '@axiomhq/logging'; + +export const logger = new Logger({ + transports: [ + new AxiomJSTransport({ axiom: axiomClient, dataset: process.env.AXIOM_DATASET! }), + new ConsoleTransport({ prettyPrint: true }), + ], +}); + +logger.info('Hello World!'); +``` + +## Install + +```bash +npm install @axiomhq/js @axiomhq/logging +``` + +## Documentation + +For more information about how to set up and use the `@axiomhq/logging` package, see the [axiom.co/docs/guides/javascript](https://axiom.co/docs/guides/javascript). + +## License + +[MIT](../../LICENSE) diff --git a/packages/nextjs/README.md b/packages/nextjs/README.md new file mode 100644 index 00000000..38e9f476 --- /dev/null +++ b/packages/nextjs/README.md @@ -0,0 +1,44 @@ +# Axiom Next.js library + +The `@axiomhq/nextjs` package allows you to send data from a Next.js app to Axiom. + +```ts +// lib/axiom/server.ts +import axiomClient from '@/lib/axiom/axiom'; +import { Logger, ConsoleTransport, AxiomJSTransport } from '@axiomhq/logging'; +import { createAxiomRouteHandler, serverContextFieldsFormatter } from '@axiomhq/nextjs'; + +export const logger = new Logger({ + transports: [ + new AxiomJSTransport({ axiom: axiomClient, dataset: process.env.NEXT_PUBLIC_AXIOM_DATASET! }), + new ConsoleTransport({ prettyPrint: true }), + ], + formatters: [serverContextFieldsFormatter], +}); + +export const withAxiom = createAxiomRouteHandler(logger); +``` + +```ts +// api/route.ts +import { withAxiom } from '@/lib/axiom/server'; + +export const GET = withAxiom(async () => { + logger.info('Hello World!'); + return new Response('Hello World!'); +}); +``` + +## Install + +```bash +npm install @axiomhq/js @axiomhq/logging @axiomhq/nextjs @axiomhq/react +``` + +## Documentation + +For more information about how to set up and use the `@axiomhq/nextjs` package, see the [axiom.co/docs/send-data/nextjs](https://axiom.co/docs/send-data/nextjs). + +## License + +[MIT](../../LICENSE) diff --git a/packages/react/README.md b/packages/react/README.md new file mode 100644 index 00000000..ffc5a289 --- /dev/null +++ b/packages/react/README.md @@ -0,0 +1,38 @@ +# Axiom React library + +The `@axiomhq/react` package allows you to send data from a React app to Axiom. + +```ts +// lib/axiom/client.ts +'use client'; + +import axiomClient from '@/lib/axiom/axiom'; +import { Logger, ConsoleTransport, AxiomJSTransport } from '@axiomhq/logging'; +import { createUseLogger, createWebVitalsComponent } from '@axiomhq/react'; + +export const logger = new Logger({ + transports: [ + new AxiomJSTransport({ axiom: axiomClient, dataset: process.env.AXIOM_DATASET! }), + new ConsoleTransport(), + ], +}); + +const useLogger = createUseLogger(logger); +const WebVitals = createWebVitalsComponent(logger); + +export { useLogger, WebVitals }; +``` + +## Install + +```bash +npm install @axiomhq/js @axiomhq/logging @axiomhq/react +``` + +## Documentation + +For more information about how to set up and use the `@axiomhq/react` package, see the [axiom.co/docs/send-data/react](https://axiom.co/docs/send-data/react). + +## License + +[MIT](../../LICENSE)