├── lib ├── core │ ├── index.ts │ └── seq-logger.core.ts ├── enums │ ├── index.ts │ ├── seq-level.enum.ts │ └── log-level.types.ts ├── interfaces │ ├── index.ts │ ├── seq-event.interface.ts │ ├── seq-logger-module-options.interface.ts │ └── seq-logger-options.interface.ts ├── index.ts ├── utils │ ├── index.ts │ ├── sleep.util.ts │ ├── time.util.ts │ ├── log-console.util.ts │ ├── shared.util.ts │ ├── event-collection.util.ts │ ├── clone.util.ts │ └── stringify.util.ts ├── seq-logger.constants.ts ├── seq-logger.module.ts ├── console-seq-logger.service.ts └── seq-logger.service.ts ├── .eslintignore ├── .prettierrc ├── img ├── rendering.jpg ├── seq_2024.2.11282.jpg └── console_seq_2024.2.11282.jpg ├── .npmignore ├── tsconfig.json ├── .eslintrc.js ├── LICENSE ├── package.json ├── .gitignore ├── SEQ_LOGGER_OPTIONS.md ├── README.md └── pnpm-lock.yaml /lib/core/index.ts: -------------------------------------------------------------------------------- 1 | export * from './seq-logger.core'; 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | lib/**/*.test.ts 2 | lib/**/files/** 3 | test/** -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /img/rendering.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonsoft/nestjs-seq/HEAD/img/rendering.jpg -------------------------------------------------------------------------------- /lib/enums/index.ts: -------------------------------------------------------------------------------- 1 | export * from './seq-level.enum'; 2 | export * from './log-level.types'; 3 | -------------------------------------------------------------------------------- /img/seq_2024.2.11282.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonsoft/nestjs-seq/HEAD/img/seq_2024.2.11282.jpg -------------------------------------------------------------------------------- /img/console_seq_2024.2.11282.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasonsoft/nestjs-seq/HEAD/img/console_seq_2024.2.11282.jpg -------------------------------------------------------------------------------- /lib/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './seq-logger-module-options.interface'; 2 | export * from './seq-logger-options.interface'; 3 | export * from './seq-event.interface'; 4 | -------------------------------------------------------------------------------- /lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from './interfaces'; 2 | export * from './seq-logger.service'; 3 | export * from './console-seq-logger.service'; 4 | export * from './seq-logger.module'; 5 | -------------------------------------------------------------------------------- /lib/interfaces/seq-event.interface.ts: -------------------------------------------------------------------------------- 1 | import { SeqLevel } from '../enums'; 2 | 3 | export interface SeqEvent { 4 | Timestamp: Date; 5 | Level: SeqLevel; 6 | MessageTemplate: string; 7 | Exception?: string; 8 | Properties?: Record; 9 | } 10 | -------------------------------------------------------------------------------- /lib/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './event-collection.util'; 2 | export * from './shared.util'; 3 | export * from './sleep.util'; 4 | export * from './time.util'; 5 | export * from './stringify.util'; 6 | export * from './log-console.util'; 7 | export * from './clone.util'; 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # source 2 | lib 3 | tests 4 | index.ts 5 | package-lock.json 6 | tslint.json 7 | tsconfig.json 8 | .prettierrc 9 | .eslintignore 10 | .eslintrc.js 11 | .vscode 12 | 13 | # github 14 | .github 15 | CONTRIBUTING.md 16 | renovate.json 17 | 18 | # ci 19 | .circleci 20 | 21 | img/ 22 | -------------------------------------------------------------------------------- /lib/enums/seq-level.enum.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Added by Jason.Song (成长的小猪) on 2023/11/17 15:01:50 4 | */ 5 | export enum SeqLevel { 6 | Verbose = 'Verbose', 7 | Debug = 'Debug', 8 | Information = 'Information', 9 | Warning = 'Warning', 10 | Error = 'Error', 11 | Fatal = 'Fatal', 12 | } 13 | -------------------------------------------------------------------------------- /lib/utils/sleep.util.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Wait for the specified number of seconds 3 | * Added by Jason.Song (成长的小猪) on 2023/03/22 22:31:59 4 | * @param seconds The number of seconds to wait 5 | * @returns 6 | */ 7 | export const sleep = (seconds: number) => 8 | new Promise((resolve) => setTimeout(resolve, seconds * 1000)); 9 | -------------------------------------------------------------------------------- /lib/utils/time.util.ts: -------------------------------------------------------------------------------- 1 | const dateTimeFormatter = new Intl.DateTimeFormat(undefined, { 2 | year: 'numeric', 3 | hour: 'numeric', 4 | minute: 'numeric', 5 | second: 'numeric', 6 | day: '2-digit', 7 | month: '2-digit', 8 | }); 9 | 10 | export function getTimestamp(): string { 11 | return dateTimeFormatter.format(Date.now()); 12 | } 13 | -------------------------------------------------------------------------------- /lib/enums/log-level.types.ts: -------------------------------------------------------------------------------- 1 | export type LogLevel = 2 | | 'verbose' 3 | | 'debug' 4 | | 'info' 5 | | 'log' 6 | | 'warn' 7 | | 'error' 8 | | 'fatal'; 9 | 10 | export const LOG_LEVELS_VALUES: Record = { 11 | verbose: 0, 12 | debug: 1, 13 | info: 2, 14 | log: 2, 15 | warn: 3, 16 | error: 4, 17 | fatal: 5, 18 | }; 19 | -------------------------------------------------------------------------------- /lib/seq-logger.constants.ts: -------------------------------------------------------------------------------- 1 | export const JASONSOFT_SEQ_LOGGER = 'JASONSOFT_SEQ_LOGGER'; 2 | 3 | export const JASONSOFT_SEQ_LOGGER_OPTIONS = 'JASONSOFT_SEQ_LOGGER_OPTIONS'; 4 | 5 | export const NETWORK_ERRORS = [ 6 | 'ENETUNREACH', 7 | 'ECONNABORTED', 8 | 'ECONNREFUSED', 9 | 'ECONNRESET', 10 | 'ENOTFOUND', 11 | 'ESOCKETTIMEDOUT', 12 | 'ETIMEDOUT', 13 | 'EHOSTUNREACH', 14 | 'EPIPE', 15 | 'EAI_AGAIN', 16 | 'EBUSY', 17 | ]; 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "strict": true, 6 | "removeComments": false, 7 | "noLib": false, 8 | "emitDecoratorMetadata": true, 9 | "esModuleInterop": true, 10 | "experimentalDecorators": true, 11 | "target": "es6", 12 | "sourceMap": false, 13 | "outDir": "./dist", 14 | "rootDir": "./lib", 15 | "skipLibCheck": true 16 | }, 17 | "include": [".eslintrc.js", "./lib/**/*"], 18 | "exclude": ["node_modules", "**/*.spec.ts", "tests"] 19 | } 20 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | jest: true, 6 | }, 7 | extends: [ 8 | 'eslint:recommended', 9 | 'plugin:@typescript-eslint/recommended', 10 | 'plugin:prettier/recommended', 11 | ], 12 | parser: '@typescript-eslint/parser', 13 | parserOptions: { 14 | project: 'tsconfig.json', 15 | sourceType: 'module', 16 | }, 17 | plugins: ['@typescript-eslint'], 18 | rules: { 19 | '@typescript-eslint/interface-name-prefix': 'off', 20 | '@typescript-eslint/explicit-function-return-type': 'off', 21 | '@typescript-eslint/no-explicit-any': 'off', 22 | '@typescript-eslint/no-use-before-define': 'off', 23 | '@typescript-eslint/no-non-null-assertion': 'off', 24 | '@typescript-eslint/ban-types': 'off', 25 | '@typescript-eslint/explicit-module-boundary-types': 'off', 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /lib/utils/log-console.util.ts: -------------------------------------------------------------------------------- 1 | import { getTimestamp } from './time.util'; 2 | 3 | type LogLevel = 'log' | 'error' | 'warn'; 4 | 5 | interface ILevelConfiguration { 6 | colorCode: string; 7 | levelText: string; 8 | } 9 | 10 | const levelConfiguration: Record = { 11 | log: { colorCode: '\x1b[32m', levelText: 'LOG' }, 12 | error: { colorCode: '\x1b[31m', levelText: 'ERROR' }, 13 | warn: { colorCode: '\x1b[35m', levelText: 'WARN' }, 14 | }; 15 | 16 | export function logToConsole( 17 | level: LogLevel, 18 | ...optionalParams: string[] 19 | ): void { 20 | const { colorCode, levelText } = levelConfiguration[level]; 21 | const baseMessage = `${applyColor(level, '[jasonsoft/nestjs-seq]')} - \x1b[37m${getTimestamp()}\x1b[39m ${colorCode}${levelText}\x1b[39m \x1b[33m[SeqLogger]\x1b[39m`; 22 | optionalParams.forEach((item) => { 23 | console[level](`${baseMessage}`, applyColor(level, item)); 24 | }); 25 | } 26 | 27 | function applyColor(level: LogLevel, message: string): string { 28 | const { colorCode } = levelConfiguration[level]; 29 | return `${colorCode}${message}\x1b[39m`; 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 jasonsoft 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/utils/shared.util.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns the default value if the value is null or undefined. 3 | * Added by Jason.Song (成长的小猪) on 2023/11/17 12:28:18 4 | * @param {T} value The value to check. 5 | * @param {T} defaultValue The default value to return if the value is null or undefined. 6 | * @returns {T} The value if it is not null or undefined, or the default value if it is. 7 | */ 8 | export function defaultIfNullOrUndefined(value: T, defaultValue: T): T { 9 | return value === null || value === undefined ? defaultValue : value; 10 | } 11 | 12 | /** 13 | * Checks if the provided value is a plain object, i.e., an object created by the 14 | * Object constructor or one with a null prototype. 15 | * @param {any} obj The object to check. 16 | * @returns {boolean} True if the object is a plain object, false otherwise. 17 | */ 18 | export function isPlainObject(obj: any): boolean { 19 | if ( 20 | obj === null || 21 | typeof obj !== 'object' || 22 | Object.prototype.toString.call(obj) !== '[object Object]' 23 | ) { 24 | return false; 25 | } 26 | const proto = Object.getPrototypeOf(obj); 27 | return proto === null || proto === Object.prototype; 28 | } 29 | -------------------------------------------------------------------------------- /lib/utils/event-collection.util.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents a dynamic collection of events that supports various operations like addition, 3 | * removal, retrieval, and clearing of events. 4 | * Added by Jason.Song (成长的小猪) on 2023/11/17 16:50:14 5 | */ 6 | export class EventCollection { 7 | private collection: T[] = []; 8 | 9 | /** 10 | * Adds an event to the collection. 11 | * @param event The event to add. 12 | */ 13 | public add(event: T) { 14 | this.collection.push(event); 15 | } 16 | 17 | /** 18 | * Removes an event from the collection. 19 | * @param event The event or index to remove. 20 | */ 21 | public remove(event: T | number) { 22 | const index = 23 | typeof event === 'number' ? event : this.collection.indexOf(event); 24 | if (index >= 0 && index < this.collection.length) { 25 | this.collection.splice(index, 1); 26 | } 27 | } 28 | 29 | /** 30 | * Retrieves the event at the specified index. 31 | * @param index The index of the event to retrieve. 32 | * @returns The event at the specified index. 33 | */ 34 | public get(index: number): T { 35 | return this.collection[index]; 36 | } 37 | 38 | /** 39 | * Returns the number of events in the collection. 40 | * @returns The number of events. 41 | */ 42 | public size(): number { 43 | return this.collection.length; 44 | } 45 | 46 | /** 47 | * Clears all events from the collection. 48 | */ 49 | public clear() { 50 | this.collection = []; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jasonsoft/nestjs-seq", 3 | "version": "2.2.0", 4 | "description": "Seq logging module for Nest framework (node.js)", 5 | "scripts": { 6 | "build": "rimraf -rf dist && tsc -p tsconfig.json", 7 | "format": "prettier **/**/*.ts --ignore-path ./.prettierignore --write", 8 | "lint": "eslint 'lib/**/*.ts' --fix", 9 | "prepublish:npm": "npm run build", 10 | "publish:npm": "npm publish --access public", 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/jasonsoft/nestjs-seq.git" 16 | }, 17 | "keywords": [ 18 | "@jasonsoft/nestjs-seq", 19 | "jasonsoft", 20 | "nestjs-seq", 21 | "nestjs", 22 | "datalust", 23 | "seq", 24 | "logger" 25 | ], 26 | "author": "jason song", 27 | "license": "MIT", 28 | "main": "dist/index.js", 29 | "types": "dist/index.d.ts", 30 | "bugs": { 31 | "url": "https://github.com/jasonsoft/nestjs-seq/issues" 32 | }, 33 | "homepage": "https://github.com/jasonsoft/nestjs-seq#readme", 34 | "devDependencies": { 35 | "@nestjs/common": "11.1.6", 36 | "@types/node": "22.18.0", 37 | "@typescript-eslint/eslint-plugin": "7.5.0", 38 | "@typescript-eslint/parser": "7.5.0", 39 | "axios": "1.11.0", 40 | "eslint": "8.57.0", 41 | "eslint-config-prettier": "9.1.0", 42 | "eslint-plugin-prettier": "5.1.3", 43 | "prettier": "3.2.5", 44 | "rimraf": "6.0.1", 45 | "typescript": "5.9.2" 46 | }, 47 | "dependencies": { 48 | "axios": "^1.3.1" 49 | }, 50 | "peerDependencies": { 51 | "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/interfaces/seq-logger-module-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { FactoryProvider, ModuleMetadata, Type } from '@nestjs/common'; 2 | import { SeqLoggerOptions } from './seq-logger-options.interface'; 3 | 4 | /** 5 | * Seq Logger Module Options 6 | * Added by Jason.Song (成长的小猪) on 2021/07/05 16:42:59 7 | */ 8 | export interface SeqLoggerModuleOptions extends Partial { 9 | /** 10 | * Use module globally 11 | * When you want to use SeqLoggerModule in other modules, 12 | * you'll need to import it (as is standard with any Nest module). 13 | * Alternatively, declare it as a global module by setting the options object's isGlobal property to true, as shown below. 14 | * In that case, you will not need to import SeqLoggerModule in other modules once it's been loaded in the root module. 15 | * Optional, default value is 'true'. 16 | */ 17 | isGlobal?: boolean; 18 | } 19 | 20 | export interface SeqLoggerModuleOptionsFactory { 21 | createSeqLoggerOptions(): 22 | | Promise 23 | | SeqLoggerModuleOptions; 24 | } 25 | 26 | /** 27 | * Seq Logger Module Async Options interface 28 | * Added by Jason.Song (成长的小猪) on 2021/10/18 15:26:56 29 | */ 30 | export interface SeqLoggerAsyncOptions extends Pick { 31 | /** 32 | * Existing Provider to be used. 33 | */ 34 | useExisting?: Type; 35 | 36 | /** 37 | * Type (class name) of provider (instance to be registered and injected). 38 | */ 39 | useClass?: Type; 40 | 41 | /** 42 | * Factory function that returns an instance of the provider to be injected. 43 | */ 44 | useFactory?: ( 45 | ...args: any[] 46 | ) => Promise | SeqLoggerModuleOptions; 47 | 48 | /** 49 | * Optional list of providers to be injected into the context of the Factory function. 50 | */ 51 | inject?: FactoryProvider['inject']; 52 | /** 53 | * If "true', register `SeqLoggerModule` as a global module. 54 | * Optional, default value is `true`. 55 | */ 56 | isGlobal?: boolean; 57 | } 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .DS_Store 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # TypeScript v1 declaration files 46 | typings/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Microbundle cache 58 | .rpt2_cache/ 59 | .rts2_cache_cjs/ 60 | .rts2_cache_es/ 61 | .rts2_cache_umd/ 62 | 63 | # Optional REPL history 64 | .node_repl_history 65 | 66 | # Output of 'npm pack' 67 | *.tgz 68 | 69 | # Yarn Integrity file 70 | .yarn-integrity 71 | 72 | # dotenv environment variables file 73 | .env 74 | .env.test 75 | 76 | # parcel-bundler cache (https://parceljs.org/) 77 | .cache 78 | 79 | # Next.js build output 80 | .next 81 | 82 | # Nuxt.js build / generate output 83 | .nuxt 84 | dist 85 | 86 | # Gatsby files 87 | .cache/ 88 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 89 | # https://nextjs.org/blog/next-9-1#public-directory-support 90 | # public 91 | 92 | # vuepress build output 93 | .vuepress/dist 94 | 95 | # Serverless directories 96 | .serverless/ 97 | 98 | # FuseBox cache 99 | .fusebox/ 100 | 101 | # DynamoDB Local files 102 | .dynamodb/ 103 | 104 | # TernJS port file 105 | .tern-port 106 | 107 | # IDE 108 | /.idea 109 | /.awcache 110 | /.vscode 111 | -------------------------------------------------------------------------------- /lib/interfaces/seq-logger-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { LogLevel } from '../enums'; 2 | 3 | /** 4 | * Seq Logger Options 5 | * Enhanced by Jason.Song (成长的小猪) on 2021/07/05 16:50:10 6 | */ 7 | export interface SeqLoggerOptions { 8 | /** 9 | * The HTTP/S endpoint address of the Seq server. 10 | * If not specified, defaults to http://localhost:5341. 11 | */ 12 | serverUrl: string; 13 | 14 | /** 15 | * Optional API Key for Seq server authentication. 16 | * Required only if the Seq service configuration mandates API key usage. 17 | */ 18 | apiKey?: string; 19 | 20 | /** 21 | * The name of the application service for log filtering. 22 | * 23 | * This attribute is deprecated due to the availability of a more flexible configuration method. Future versions will not support this attribute. Instead, use the `extendMetaProperties` to specify the service name as demonstrated below: 24 | * 25 | * Example: 26 | * ``` 27 | * extendMetaProperties: { 28 | * serviceName: 'your-service-name' 29 | * } 30 | * ``` 31 | * 32 | * @deprecated Since version 2.1.2. It is recommended to use `extendMetaProperties` with a `serviceName` property. 33 | */ 34 | serviceName?: string; 35 | 36 | /** 37 | * Limits the batch payload size for logs sent to Seq. 38 | * Defaults to 10485760 (10 MB). Increasing this value is not recommended. 39 | */ 40 | batchPayloadLimit: number; 41 | 42 | /** 43 | * Limits the size of individual log events accepted by Seq. 44 | * Defaults to 262144 (256 KB). Increasing this value is not recommended. 45 | */ 46 | eventBodyLimit: number; 47 | 48 | /** 49 | * Maximum retry attempts for sending logs to Seq. 50 | * Defaults to 5 retries. 51 | */ 52 | maxRetries: number; 53 | 54 | /** 55 | * Delay (in seconds) before retrying to send logs to Seq. 56 | * Defaults to 5 seconds. 57 | */ 58 | delay: number; 59 | 60 | /** 61 | * Maximum time (in seconds) to wait before timing out a log send attempt to Seq. 62 | * Defaults to 30 seconds. 63 | */ 64 | timeout: number; 65 | 66 | /** 67 | * Name of the custom meta property. 68 | * Defaults to 'meta' if not specified. 69 | */ 70 | metaFieldName: string; 71 | 72 | /** 73 | * A record of additional metadata properties to include with each log event. 74 | * These properties are merged with the default metadata. 75 | */ 76 | extendMetaProperties?: Record; 77 | 78 | /** 79 | * Specifies the permissible log levels for recording events. 80 | * - When a single LogLevel is provided, it acts as the minimum threshold; any log levels below this will not be recorded. 81 | * - When an array of LogLevel is provided, only the log levels in this array are allowed to be recorded. 82 | */ 83 | logLevels?: LogLevel | LogLevel[]; 84 | 85 | /** 86 | * Array of sensitive key patterns to mask in log output. 87 | * Keys containing these patterns (case-insensitive) will be replaced with '[Sensitive data masked]'. 88 | * Defaults to common sensitive patterns if not specified. 89 | */ 90 | sensitiveKeys?: string[]; 91 | } 92 | -------------------------------------------------------------------------------- /lib/utils/clone.util.ts: -------------------------------------------------------------------------------- 1 | import { isPlainObject } from './shared.util'; 2 | import { logToConsole } from './log-console.util'; 3 | 4 | /** 5 | * Performs a shallow clone of the given object or array. 6 | * 7 | * Added by Jason.Song (成长的小猪) on 2025/08/07 01:01:30 8 | * @param {T} source The source object or array to clone. 9 | * @returns {T} A shallow copy of the source. 10 | */ 11 | export function shallowClone(source: T): T { 12 | if (source === null || typeof source !== 'object') { 13 | return source; 14 | } 15 | 16 | if (Array.isArray(source)) { 17 | return [...source] as T; 18 | } 19 | 20 | if (source instanceof Date) { 21 | return new Date(source.getTime()) as T; 22 | } 23 | 24 | if (source instanceof RegExp) { 25 | return new RegExp(source.source, source.flags) as T; 26 | } 27 | 28 | if (source instanceof Map) { 29 | return new Map(source) as T; 30 | } 31 | 32 | if (source instanceof Set) { 33 | return new Set(source) as T; 34 | } 35 | 36 | return { ...source } as T; 37 | } 38 | 39 | /** 40 | * Performs a deep clone of the given object or array. 41 | * Handles circular references by maintaining a WeakMap of already cloned objects. 42 | * 43 | * Added by Jason.Song (成长的小猪) on 2025/08/07 01:01:12 44 | * @param {T} source The source object or array to clone. 45 | * @param {WeakMap} cloneMap Internal map to track cloned objects (for circular reference handling). 46 | * @returns {T} A deep copy of the source. 47 | */ 48 | export function deepClone( 49 | source: T, 50 | cloneMap: WeakMap = new WeakMap(), 51 | ): T { 52 | // Handle primitive types and null 53 | if (source === null || typeof source !== 'object') { 54 | return source; 55 | } 56 | 57 | // Handle circular references 58 | if (cloneMap.has(source as object)) { 59 | return cloneMap.get(source as object); 60 | } 61 | 62 | // Handle Date objects 63 | if (source instanceof Date) { 64 | const cloned = new Date(source.getTime()) as T; 65 | cloneMap.set(source as object, cloned); 66 | return cloned; 67 | } 68 | 69 | // Handle RegExp objects 70 | if (source instanceof RegExp) { 71 | const cloned = new RegExp(source.source, source.flags) as T; 72 | cloneMap.set(source as object, cloned); 73 | return cloned; 74 | } 75 | 76 | // Handle Arrays 77 | if (Array.isArray(source)) { 78 | const cloned: any[] = []; 79 | cloneMap.set(source as object, cloned); 80 | 81 | for (let i = 0; i < source.length; i++) { 82 | cloned[i] = deepClone(source[i], cloneMap); 83 | } 84 | 85 | return cloned as T; 86 | } 87 | 88 | // Handle Map objects 89 | if (source instanceof Map) { 90 | const cloned = new Map(); 91 | cloneMap.set(source as object, cloned); 92 | 93 | for (const [key, value] of source) { 94 | cloned.set(deepClone(key, cloneMap), deepClone(value, cloneMap)); 95 | } 96 | 97 | return cloned as T; 98 | } 99 | 100 | // Handle Set objects 101 | if (source instanceof Set) { 102 | const cloned = new Set(); 103 | cloneMap.set(source as object, cloned); 104 | 105 | for (const value of source) { 106 | cloned.add(deepClone(value, cloneMap)); 107 | } 108 | 109 | return cloned as T; 110 | } 111 | 112 | // Handle Buffer objects (Node.js specific) 113 | if (typeof Buffer !== 'undefined' && Buffer.isBuffer(source)) { 114 | const cloned = Buffer.from(source) as T; 115 | cloneMap.set(source as object, cloned); 116 | return cloned; 117 | } 118 | 119 | // Handle plain objects 120 | if (isPlainObject(source)) { 121 | const cloned: any = {}; 122 | cloneMap.set(source as object, cloned); 123 | 124 | for (const key in source) { 125 | if (Object.prototype.hasOwnProperty.call(source, key)) { 126 | cloned[key] = deepClone((source as any)[key], cloneMap); 127 | } 128 | } 129 | 130 | return cloned as T; 131 | } 132 | 133 | // Handle other object types by attempting to clone their enumerable properties 134 | try { 135 | const cloned = Object.create(Object.getPrototypeOf(source)); 136 | cloneMap.set(source as object, cloned); 137 | 138 | for (const key in source) { 139 | if (Object.prototype.hasOwnProperty.call(source, key)) { 140 | cloned[key] = deepClone((source as any)[key], cloneMap); 141 | } 142 | } 143 | 144 | return cloned as T; 145 | } catch (error) { 146 | // If cloning fails, return the original object 147 | return source; 148 | } 149 | } 150 | 151 | /** 152 | * Generic clone function that performs deep cloning by default. 153 | * 154 | * Added by Jason.Song (成长的小猪) on 2025/08/07 01:00:49 155 | * @param {T} source The source object or array to clone. 156 | * @param {boolean} deep Whether to perform deep cloning (default: true). 157 | * @returns {T} A copy of the source. 158 | */ 159 | export function clone(source: T, deep: boolean = true): T { 160 | return deep ? deepClone(source) : shallowClone(source); 161 | } 162 | 163 | /** 164 | * Safely clones an object with error handling. 165 | * Returns the original object if cloning fails. 166 | * 167 | * Added by Jason.Song (成长的小猪) on 2025/08/07 01:49:43 168 | * @param {T} source The source object to clone. 169 | * @param {boolean} deep Whether to perform deep cloning (default: true). 170 | * @returns {T} A copy of the source or the original if cloning fails. 171 | */ 172 | export function safeClone(source: T, deep: boolean = true): T { 173 | try { 174 | return clone(source, deep); 175 | } catch (error: any) { 176 | logToConsole( 177 | 'warn', 178 | `Clone operation failed, returning original object: ${error.message}`, 179 | error.stack, 180 | ); 181 | return source; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /SEQ_LOGGER_OPTIONS.md: -------------------------------------------------------------------------------- 1 |

2 | JasonSoft Logo 3 |

4 | 5 | # Seq Logger Options Documentation 6 | 7 | This document provides detailed explanations of the configuration options available for the Seq Logger, as defined in the `SeqLoggerOptions` interface. Enhanced by Jason.Song (成长的小猪) on 2024/04/09. 8 | 9 | ## Configuration Options 10 | 11 | ### `serverUrl` 12 | 13 | - **Type:** `string` 14 | - **Description:** The HTTP/S endpoint address of the Seq server. This is where your logs will be sent. 15 | - **Default Value:** `http://localhost:5341` 16 | - **Required:** Yes 17 | 18 | ### `apiKey` 19 | 20 | - **Type:** `string` 21 | - **Optional** 22 | - **Description:** Optional API Key for Seq server authentication. This is required only if the Seq service configuration mandates API key usage for enhanced security. 23 | 24 | ### `serviceName` 25 | 26 | - **Type:** `string` 27 | - **Optional** 28 | - **Deprecated:** Yes 29 | - **Description:** The name of the application service used for log filtering. This attribute is deprecated and will not be supported in future versions. It is recommended to use the `extendMetaProperties` to specify the service name. 30 | - **Example:** 31 | 32 | ```json 33 | { 34 | "extendMetaProperties": { 35 | "serviceName": "your-service-name" 36 | } 37 | } 38 | ``` 39 | 40 | ### `batchPayloadLimit` 41 | 42 | - **Type:** `number` 43 | - **Description:** Limits the batch payload size for logs sent to Seq. This helps in managing the load on the Seq server and ensures efficient log processing. 44 | - **Default Value:** `10485760` (10 MB) 45 | - **Recommendation:** Increasing this value is not recommended due to potential performance impacts. 46 | 47 | ### `eventBodyLimit` 48 | 49 | - **Type:** `number` 50 | - **Description:** Limits the size of individual log events accepted by Seq. This ensures that each log event is within a manageable size for processing and storage. 51 | - **Default Value:** `262144` (256 KB) 52 | - **Recommendation:** Increasing this value is not recommended as it may affect the performance and storage efficiency. 53 | 54 | ### `maxRetries` 55 | 56 | - **Type:** `number` 57 | - **Description:** Maximum retry attempts for sending logs to Seq. This ensures that transient network issues or temporary Seq server unavailability does not result in lost logs. 58 | - **Default Value:** `5` retries 59 | 60 | ### `delay` 61 | 62 | - **Type:** `number` 63 | - **Description:** Delay (in seconds) before retrying to send logs to Seq. This provides a simple back-off mechanism in case of failures. 64 | - **Default Value:** `5` seconds 65 | 66 | ### `timeout` 67 | 68 | - **Type:** `number` 69 | - **Description:** Maximum time (in seconds) to wait before timing out a log send attempt to Seq. This ensures that log sending does not indefinitely block the application in case of issues. 70 | - **Default Value:** `30` seconds 71 | 72 | ### `metaFieldName` 73 | 74 | - **Type:** `string` 75 | - **Description:** Name of the custom meta property. This can be used to attach additional metadata to each log event for more detailed context. 76 | - **Default Value:** `meta` 77 | 78 | ### `extendMetaProperties` 79 | 80 | - **Type:** `Record` 81 | - **Optional** 82 | - **Description:** A record of additional metadata properties to include with each log event. These properties are merged with the default metadata, allowing for more detailed and customized log information. 83 | 84 | This configuration interface allows for detailed customization of the Seq logging behavior, ensuring that logging can be tailored to the specific needs of your application while maintaining efficient and reliable log management. 85 | 86 | ### `logLevels` 87 | 88 | - **Type:** `LogLevel | LogLevel[]` 89 | - **Optional** 90 | - **Description:** Specifies the permissible log levels for recording events. This can be a single log level, acting as the minimum threshold, or an array of log levels, specifying exactly which levels are allowed to be recorded. 91 | 92 | ### `sensitiveKeys` 93 | 94 | - **Type:** `string[]` 95 | - **Optional** 96 | - **Description:** Specifies an array of sensitive key patterns (like `"password"`, `"secret"`, `"token"`, `"apiKey"`, etc.) whose values will be masked in log outputs to prevent accidental leakage of confidential or sensitive information. This applies to all logs processed through the SeqLoggerModule. 97 | 98 | ### SeqLoggerModule Basic Configuration Example 99 | 100 | ```js 101 | import { Module } from '@nestjs/common'; 102 | import { AppController } from './app.controller'; 103 | import { AppService } from './app.service'; 104 | /** 105 | * Import the SeqLoggerModule into the root AppModule 106 | * Added by Jason.Song (成长的小猪) on 2021/09/08 107 | */ 108 | import { SeqLoggerModule } from '@jasonsoft/nestjs-seq'; 109 | 110 | @Module({ 111 | imports: [ 112 | /** 113 | * Import the SeqLoggerModule. Typically, it's imported into the root AppModule and configured using the .forRoot() static method. 114 | * Added by Jason.Song (成长的小猪) on 2021/09/08 115 | */ 116 | SeqLoggerModule.forRoot({ 117 | /** Seq server's HTTP/S endpoint address */ 118 | serverUrl: 'http(s)://your-seq-server:5341', 119 | /** API Key for Seq server connection */ 120 | apiKey: 'your-api-key', 121 | /** Batch payload size limit for logs sent to Seq */ 122 | batchPayloadLimit: 10485760, // 10 MB 123 | /** Individual log event size limit accepted by Seq */ 124 | eventBodyLimit: 262144, // 256 KB 125 | /** Maximum retry attempts for sending logs */ 126 | maxRetries: 5, 127 | /** Delay before retrying to send logs */ 128 | delay: 5, // in seconds 129 | /** Maximum time to wait before timing out a log send attempt */ 130 | timeout: 30, // in seconds 131 | /** Custom meta property name */ 132 | metaFieldName: 'meta', 133 | /** Optional additional metadata properties */ 134 | extendMetaProperties: { 135 | serviceName: 'your-service-name', 136 | version: '1.0.0', 137 | }, 138 | /** Optional permissible log levels */ 139 | logLevels: ['info', 'error'], 140 | /** Array of sensitive key patterns to mask in log output */ 141 | sensitiveKeys: ['password', 'secret', 'token', 'apiKey'], 142 | }), 143 | ], 144 | controllers: [AppController], 145 | providers: [AppService], 146 | }) 147 | export class AppModule {} 148 | ``` 149 | -------------------------------------------------------------------------------- /lib/seq-logger.module.ts: -------------------------------------------------------------------------------- 1 | import { DynamicModule, Module, Provider, Type } from '@nestjs/common'; 2 | import { ConsoleSeqLogger } from './console-seq-logger.service'; 3 | import { 4 | SeqLoggerAsyncOptions, 5 | SeqLoggerModuleOptions, 6 | SeqLoggerModuleOptionsFactory, 7 | } from './interfaces'; 8 | import { 9 | JASONSOFT_SEQ_LOGGER, 10 | JASONSOFT_SEQ_LOGGER_OPTIONS, 11 | } from './seq-logger.constants'; 12 | import { SeqLogger } from './seq-logger.service'; 13 | import { logToConsole, defaultIfNullOrUndefined } from './utils'; 14 | import { SeqLevel } from './enums'; 15 | import { SeqLoggerCore } from './core'; 16 | 17 | /** 18 | * JasonSoft Seq Logger Module 19 | * This module facilitates the integration of Seq as a logging service in NestJS applications, 20 | * providing both static and asynchronous configuration options. 21 | * 22 | * Added by Jason.Song (成长的小猪) on 2021/07/05 16:44:10 23 | */ 24 | @Module({}) 25 | export class SeqLoggerModule { 26 | /** 27 | * Registers the Seq Logger service with static configuration options. 28 | * This method is ideal for scenarios where configuration settings are immediately available and do not depend on asynchronous resources. 29 | * 30 | * Added by Jason.Song (成长的小猪) on 2021/10/18 17:15:51 31 | * 32 | * Example usage: 33 | * ```typescript 34 | SeqLoggerModule.forRoot({ 35 | serverUrl: 'http(s)://your-seq-server:5341', 36 | apiKey: 'your-api-key', 37 | extendMetaProperties: { 38 | serviceName: 'your-service-name', 39 | }, 40 | }), 41 | * ``` 42 | * 43 | * @param options Configuration options for the Seq Logger service. This includes settings like the Seq server URL, API key, and service name, among others. 44 | * 45 | * For additional properties and configuration options, refer to the Seq Logger Options Documentation: 46 | * [Seq Logger Options Documentation](https://github.com/jasonsoft/nestjs-seq/blob/v2.x.x/SEQ_LOGGER_OPTIONS.md) 47 | * @returns A DynamicModule that NestJS can use to register the Seq Logger service globally, based on the provided configuration. 48 | * @throws Error if required configuration options are missing or invalid. 49 | */ 50 | static forRoot(options: SeqLoggerModuleOptions): DynamicModule { 51 | if (options.serverUrl !== undefined) { 52 | if (typeof options.serverUrl !== 'string') { 53 | throw new Error( 54 | 'SeqLoggerModule: serverUrl must be a string when provided', 55 | ); 56 | } 57 | try { 58 | new URL(options.serverUrl); 59 | } catch { 60 | throw new Error( 61 | `SeqLoggerModule: Invalid serverUrl format: ${options.serverUrl}`, 62 | ); 63 | } 64 | } 65 | return { 66 | module: SeqLoggerModule, 67 | global: defaultIfNullOrUndefined(options.isGlobal, true), 68 | providers: [ 69 | { 70 | provide: JASONSOFT_SEQ_LOGGER_OPTIONS, 71 | useValue: options, 72 | }, 73 | { 74 | provide: JASONSOFT_SEQ_LOGGER, 75 | useFactory: () => { 76 | return this.createSeqLogger(options); 77 | }, 78 | }, 79 | SeqLogger, 80 | ConsoleSeqLogger, 81 | ], 82 | exports: [SeqLogger, ConsoleSeqLogger], 83 | }; 84 | } 85 | 86 | /** 87 | * Registers the Seq Logger service with asynchronous configuration options. 88 | * This method is suitable for scenarios where configuration settings need to be resolved asynchronously, such as fetching them from a database or a remote configuration service. 89 | * 90 | * Added by Jason.Song (成长的小猪) on 2021/10/18 15:43:38 91 | * 92 | * Example usage: 93 | * ```typescript 94 | * SeqLoggerModule.forRootAsync({ 95 | * imports: [ConfigModule], 96 | * useFactory: async (configService: ConfigService) => ({ 97 | * serverUrl: configService.get('SEQ_SERVER_URL'), 98 | * apiKey: configService.get('SEQ_API_KEY'), 99 | * extendMetaProperties: { 100 | * serviceName: configService.get('SEQ_SERVICE_NAME'), 101 | * }, 102 | * }), 103 | * inject: [ConfigService], 104 | * }), 105 | * ``` 106 | * 107 | * For additional properties and configuration options, refer to the Seq Logger Options Documentation: 108 | * [Seq Logger Options Documentation](https://github.com/jasonsoft/nestjs-seq/blob/v2.x.x/SEQ_LOGGER_OPTIONS.md) 109 | * 110 | * Note: When using `forRootAsync`, ensure that any services or modules required for resolving the configuration are properly injected and imported. 111 | * 112 | * @param options Asynchronous configuration options for the Seq Logger service. This can include using a factory function, an existing service, or a class to provide the configuration. 113 | * @returns A DynamicModule that NestJS can use to register the Seq Logger service globally, based on the asynchronously provided configuration. 114 | */ 115 | static forRootAsync(options: SeqLoggerAsyncOptions): DynamicModule { 116 | return { 117 | module: SeqLoggerModule, 118 | global: defaultIfNullOrUndefined(options.isGlobal, true), 119 | imports: options.imports || [], 120 | providers: [ 121 | ...this.createAsyncProviders(options), 122 | { 123 | provide: JASONSOFT_SEQ_LOGGER, 124 | useFactory: (config: SeqLoggerModuleOptions) => { 125 | return this.createSeqLogger(config); 126 | }, 127 | inject: [JASONSOFT_SEQ_LOGGER_OPTIONS], 128 | }, 129 | SeqLogger, 130 | ConsoleSeqLogger, 131 | ], 132 | exports: [SeqLogger, ConsoleSeqLogger], 133 | }; 134 | } 135 | 136 | private static createSeqLogger(options: SeqLoggerModuleOptions) { 137 | // Validate URL format if serverUrl is provided 138 | if (options.serverUrl !== undefined) { 139 | if (typeof options.serverUrl !== 'string') { 140 | throw new Error( 141 | 'SeqLoggerModule: serverUrl must be a string when provided', 142 | ); 143 | } 144 | try { 145 | new URL(options.serverUrl); 146 | } catch { 147 | throw new Error( 148 | `SeqLoggerModule: Invalid serverUrl format: ${options.serverUrl}`, 149 | ); 150 | } 151 | } 152 | 153 | logToConsole('log', 'Seq logger is initializing...'); 154 | const seqLogger = new SeqLoggerCore(options); 155 | seqLogger.emit({ 156 | Timestamp: new Date(), 157 | Level: SeqLevel.Information, 158 | MessageTemplate: `[{context}] Seq logger initialized`, 159 | Properties: { 160 | context: 'jasonsoft/nestjs-seq', 161 | [seqLogger.options.metaFieldName]: seqLogger.metaProperties, 162 | }, 163 | }); 164 | return seqLogger; 165 | } 166 | 167 | private static createAsyncProviders( 168 | options: SeqLoggerAsyncOptions, 169 | ): Provider[] { 170 | if (options.useExisting || options.useFactory) { 171 | return [this.createAsyncOptionsProvider(options)]; 172 | } 173 | const useClass = options.useClass as Type; 174 | return [ 175 | this.createAsyncOptionsProvider(options), 176 | { 177 | provide: useClass, 178 | useClass, 179 | }, 180 | ]; 181 | } 182 | 183 | private static createAsyncOptionsProvider( 184 | asyncOptions: SeqLoggerAsyncOptions, 185 | ): Provider { 186 | if (asyncOptions.useFactory) { 187 | return { 188 | provide: JASONSOFT_SEQ_LOGGER_OPTIONS, 189 | useFactory: asyncOptions.useFactory, 190 | inject: asyncOptions.inject || [], 191 | }; 192 | } 193 | return { 194 | provide: JASONSOFT_SEQ_LOGGER_OPTIONS, 195 | useFactory: async (optionsFactory: SeqLoggerModuleOptionsFactory) => 196 | optionsFactory.createSeqLoggerOptions(), 197 | inject: [ 198 | (asyncOptions.useClass || 199 | asyncOptions.useExisting) as Type, 200 | ], 201 | }; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /lib/console-seq-logger.service.ts: -------------------------------------------------------------------------------- 1 | import { ConsoleLogger, Injectable, LogLevel } from '@nestjs/common'; 2 | import { SeqLogger } from './seq-logger.service'; 3 | import { safeStringify } from './utils'; 4 | 5 | /** 6 | * Nest system console logging to Seq through custom console logger 7 | * Added by Jason.Song (成长的小猪) on 2021-09-24 16:56:31 8 | * ```typescript 9 | * import { NestFactory } from '@nestjs/core'; 10 | * import { AppModule } from './app.module'; 11 | * // Import the ConsoleSeqLogger from '@jasonsoft/nestjs-seq' 12 | * import { ConsoleSeqLogger } from '@jasonsoft/nestjs-seq'; 13 | * 14 | * async function bootstrap() { 15 | * // Set bufferLogs to true to ensure that all logs will be buffered until a custom logger (ConsoleSeqLogger) is attached. 16 | * const app = await NestFactory.create(AppModule, { 17 | * bufferLogs: true, 18 | * }); 19 | * // Extend built-in logger 20 | * app.useLogger(app.get(ConsoleSeqLogger)); 21 | * 22 | * await app.listen(3000); 23 | * } 24 | * bootstrap(); 25 | * ``` 26 | */ 27 | @Injectable() 28 | export class ConsoleSeqLogger extends ConsoleLogger { 29 | constructor(private readonly logger: SeqLogger) { 30 | super(); 31 | } 32 | 33 | /** 34 | * Logs a message at the 'log' level. 35 | * Updated by Jason.Song (成长的小猪) on 2024/04/09 23:25:35 36 | */ 37 | log(message: any, ...optionalParams: any[]) { 38 | if (!this.isLevelEnabled('log')) { 39 | return; 40 | } 41 | const { messages, context } = this.getContextAndMessages([ 42 | message, 43 | ...optionalParams, 44 | ]); 45 | this.forwardToSeqLogger('log', messages, context); 46 | super.log(message, ...optionalParams); 47 | } 48 | 49 | /** 50 | * Logs a message at the 'error' level. 51 | * Updated by Jason.Song (成长的小猪) on 2024/04/09 23:26:03 52 | */ 53 | error(message: any, ...optionalParams: any[]) { 54 | if (!this.isLevelEnabled('error')) { 55 | return; 56 | } 57 | const { messages, context, stack } = this.getContextAndStackAndMessages([ 58 | message, 59 | ...optionalParams, 60 | ]); 61 | this.forwardToSeqLogger('error', messages, context, stack); 62 | super.error(message, ...optionalParams); 63 | } 64 | 65 | /** 66 | * Logs a message at the 'warn' level. 67 | * Updated by Jason.Song (成长的小猪) on 2024/04/09 23:26:31 68 | */ 69 | warn(message: any, ...optionalParams: any[]) { 70 | if (!this.isLevelEnabled('warn')) { 71 | return; 72 | } 73 | const { messages, context } = this.getContextAndMessages([ 74 | message, 75 | ...optionalParams, 76 | ]); 77 | this.forwardToSeqLogger('warn', messages, context); 78 | super.warn(message, ...optionalParams); 79 | } 80 | 81 | /** 82 | * Logs a message at the 'debug' level. 83 | * Updated by Jason.Song (成长的小猪) on 2024/04/09 23:26:55 84 | */ 85 | debug(message: any, ...optionalParams: any[]) { 86 | if (!this.isLevelEnabled('debug')) { 87 | return; 88 | } 89 | const { messages, context } = this.getContextAndMessages([ 90 | message, 91 | ...optionalParams, 92 | ]); 93 | this.forwardToSeqLogger('debug', messages, context); 94 | super.debug(message, ...optionalParams); 95 | } 96 | 97 | /** 98 | * Logs a message at the 'verbose' level. 99 | * Updated by Jason.Song (成长的小猪) on 2024/04/09 23:27:15 100 | */ 101 | verbose(message: any, ...optionalParams: any[]) { 102 | if (!this.isLevelEnabled('verbose')) { 103 | return; 104 | } 105 | const { messages, context } = this.getContextAndMessages([ 106 | message, 107 | ...optionalParams, 108 | ]); 109 | this.forwardToSeqLogger('verbose', messages, context); 110 | super.verbose(message, ...optionalParams); 111 | } 112 | 113 | /** 114 | * Logs a message at the 'fatal' level. 115 | * Updated by Jason.Song (成长的小猪) on 2024/04/09 23:27:35 116 | */ 117 | fatal(message: any, ...optionalParams: any[]) { 118 | if (!this.isLevelEnabled('fatal')) { 119 | return; 120 | } 121 | const { messages, context } = this.getContextAndMessages([ 122 | message, 123 | ...optionalParams, 124 | ]); 125 | this.forwardToSeqLogger('fatal', messages, context); 126 | super.fatal(message, ...optionalParams); 127 | } 128 | 129 | /** 130 | * Forwards log messages to the SeqLogger service. 131 | * Updated by Jason.Song (成长的小猪) on 2024/04/09 23:31:14 132 | * @param level The logging level (e.g., debug, info, warn, error). 133 | * @param messages An array of messages to be logged. 134 | * @param context (Optional) The logging context, providing additional information. 135 | * @param stack (Optional) The stack trace in case of errors. 136 | */ 137 | private forwardToSeqLogger( 138 | level: LogLevel, 139 | messages: unknown[], 140 | context?: string, 141 | stack?: string, 142 | ): void { 143 | const [message, ...optionalParams] = messages; 144 | let processedMessage; 145 | if ( 146 | (message !== null && typeof message === 'object') || 147 | Array.isArray(message) 148 | ) { 149 | const sensitiveKeys = this.logger.getSensitiveKeys(); 150 | const partialMessage = safeStringify(message, { 151 | maxDepth: 10, 152 | maxLength: 1024 * 1024, 153 | sensitiveKeys, 154 | }); 155 | processedMessage = ` - (Object/Array detected, please expand to view) ${partialMessage.substring(0, 50)}...`; 156 | } else { 157 | processedMessage = message; 158 | } 159 | this.logger[level](`[{context}] ${processedMessage}`, { 160 | logger: 'console', 161 | context, 162 | message, 163 | optionalParams: optionalParams.length ? optionalParams : undefined, 164 | stack, 165 | }); 166 | } 167 | 168 | /** 169 | * Gets the context and messages from the arguments. 170 | * Updated by Jason.Song (成长的小猪) on 2024/04/09 23:52:14 171 | * @param args An array of arguments which may include log messages and optionally a context as the last element. 172 | * @returns An object containing the extracted messages and context. 173 | */ 174 | private getContextAndMessages(args: unknown[]) { 175 | const params = args.filter((arg) => arg !== undefined); 176 | const lastElement = params[params.length - 1]; 177 | const isContext = typeof lastElement === 'string'; 178 | const context = isContext ? (lastElement as string) : this.context; 179 | const messages = isContext ? params.slice(0, -1) : params; 180 | 181 | return { messages, context }; 182 | } 183 | 184 | /** 185 | * Gets the context, stack, and messages from the arguments. 186 | * Updated by Jason.Song (成长的小猪) on 2024/04/09 23:58:25 187 | * @param args An array of arguments which may include log messages and optionally a context as the last element. 188 | * @returns An object containing the extracted messages, context, and stack. 189 | */ 190 | private getContextAndStackAndMessages(args: unknown[]) { 191 | let stack: string | undefined; 192 | const { messages, context } = this.getContextAndMessages(args); 193 | const stackOrErrorIndex = messages.findIndex( 194 | (msg) => 195 | (typeof msg === 'string' && /\n\s+at\s+.+:\d+:\d+/.test(msg)) || 196 | msg instanceof Error, 197 | ); 198 | if (stackOrErrorIndex !== -1) { 199 | const stackOrError = messages[stackOrErrorIndex]; 200 | messages.splice(stackOrErrorIndex, 1); 201 | if (typeof stackOrError === 'string') { 202 | stack = stackOrError; 203 | const errorMatch = stackOrError.match(/Error: (.+)/); 204 | if (messages.length === 0 && errorMatch && errorMatch[1]) { 205 | messages.push(errorMatch[1]); 206 | } 207 | } else if (stackOrError instanceof Error) { 208 | stack = stackOrError.stack; 209 | if (messages.length === 0) { 210 | messages.push(stackOrError.message); 211 | } 212 | } 213 | } 214 | return { messages, context, stack }; 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | JasonSoft Logo 3 | Nest Logo 4 | datalust Logo 5 |

6 | 7 | # nestjs-seq 8 | 9 | 🐷 Seq logging module for Nest framework (node.js) 10 | 11 | [![NPM version][npm-img]][npm-url] 12 | [![NPM Downloads][downloads-image]][npm-url] 13 | [![GitHub stars][stars-img]][github-url] 14 | [![License][license-img]][license-url] 15 | 16 | ## Installation 17 | 18 | ```bash 19 | # Using npm 20 | $ npm i --save @jasonsoft/nestjs-seq 21 | 22 | # Using yarn 23 | $ yarn add @jasonsoft/nestjs-seq 24 | 25 | # Using pnpm 26 | $ pnpm add @jasonsoft/nestjs-seq 27 | ``` 28 | 29 | ## Quick Start 30 | 31 | > After successfully installing the package, proceed to integrate the SeqLoggerModule into your project's root AppModule. 32 | 33 | ### Static Configuration 34 | 35 | ```js 36 | import { Module } from '@nestjs/common'; 37 | import { AppController } from './app.controller'; 38 | import { AppService } from './app.service'; 39 | /** 40 | * Import the SeqLoggerModule into the root AppModule to enable centralized logging. 41 | * This module is configured to connect to a Seq server for log aggregation and analysis. 42 | * It is essential for monitoring and debugging applications by collecting and storing logs. 43 | * Updated by Jason.Song (成长的小猪) on 2024/04/21 44 | * Added by Jason.Song (成长的小猪) on 2021/09/08 45 | */ 46 | import { SeqLoggerModule } from '@jasonsoft/nestjs-seq'; 47 | 48 | @Module({ 49 | imports: [ 50 | /** 51 | * Import and configure the SeqLoggerModule using the .forRoot() method. 52 | * This method initializes the module with server-specific configurations, 53 | * allowing for centralized management of log data across the application. 54 | * Updated by Jason.Song (成长的小猪) on 2024/04/21 55 | * Added by Jason.Song (成长的小猪) on 2021/09/08 56 | */ 57 | SeqLoggerModule.forRoot({ 58 | /** Specifies the URL of the Seq server to which logs should be sent. */ 59 | serverUrl: 'http://localhost:5341', 60 | /** Provides the API Key required for authenticating with the Seq server. */ 61 | apiKey: 'K7iUhZ9OSp6oX5EOCfPt', 62 | /** Optional additional metadata properties */ 63 | extendMetaProperties: { 64 | /** Defines a custom service name for the logs, aiding in their categorization and filtering. 65 | * This name helps in identifying logs related to this service in a mixed-service environment. 66 | */ 67 | serviceName: 'product-service', 68 | }, 69 | /** For more configuration details, see the "Seq Logger Options Documentation" section below. */ 70 | }), 71 | ], 72 | controllers: [AppController], 73 | providers: [AppService], 74 | }) 75 | export class AppModule {} 76 | ``` 77 | 78 | ### Async Configuration 79 | 80 | ```js 81 | // Import ConfigModule to manage application configuration through environment variables. 82 | import { ConfigModule, ConfigService } from '@nestjs/config'; 83 | import { SeqLoggerModule } from '@jasonsoft/nestjs-seq'; 84 | 85 | @Module({ 86 | imports: [ 87 | ConfigModule.forRoot(), 88 | /** 89 | * Asynchronously configure the SeqLoggerModule using the forRootAsync() method. 90 | * This method allows module options to be passed asynchronously, leveraging factory providers 91 | * that can be asynchronous and inject dependencies such as ConfigService. 92 | * Updated by Jason.Song (成长的小猪) on 2024/04/21 93 | * Added by Jason.Song (成长的小猪) on 2021/10/20 94 | */ 95 | SeqLoggerModule.forRootAsync({ 96 | imports: [ConfigModule], 97 | useFactory: async (configService: ConfigService) => ({ 98 | /** Specifies the HTTP/S endpoint address of the Seq server for log transmission. */ 99 | serverUrl: configService.get('SEQ_SERVER_URL'), 100 | /** Provides the API Key required for authenticating with the Seq server. */ 101 | apiKey: configService.get('SEQ_API_KEY'), 102 | /** Optional additional metadata properties to enhance log categorization and filtering. */ 103 | extendMetaProperties: { 104 | /** Custom service name for the logs to assist in their categorization and filtering within a multi-service environment. */ 105 | serviceName: configService.get('SEQ_SERVICE_NAME'), 106 | }, 107 | /** For more configuration details, see the "Seq Logger Options Documentation" section below. */ 108 | }), 109 | inject: [ConfigService], 110 | }), 111 | ], 112 | controllers: [AppController], 113 | providers: [AppService], 114 | }) 115 | export class AppModule {} 116 | ``` 117 | 118 | ### ✨ Seq Logger Options Documentation 119 | 120 | Please refer to the [Seq Logger Options Documentation](SEQ_LOGGER_OPTIONS.md) for detailed explanations of the configuration options available for the Seq Logger. For immediate access to the document, click [here](SEQ_LOGGER_OPTIONS.md). 121 | 122 | ## Usage 123 | 124 | ```js 125 | import { Controller, Get } from '@nestjs/common'; 126 | import { AppService } from './app.service'; 127 | /** 128 | * Import the SeqLogger for structured logging. 129 | * This logger allows for easy tracking and querying of log data. 130 | * Updated by Jason.Song (成长的小猪) on 2024/04/21 131 | * Added by Jason.Song (成长的小猪) on 2021/09/08 132 | */ 133 | import { SeqLogger } from '@jasonsoft/nestjs-seq'; 134 | 135 | @Controller() 136 | export class AppController { 137 | constructor( 138 | /** 139 | * Inject the Seq Logger service for structured logging. 140 | * This service provides methods to log various levels of information (info, debug, error). 141 | * Updated by Jason.Song (成长的小猪) on 2024/04/21 142 | * Added by Jason.Song (成长的小猪) on 2021/09/08 143 | */ 144 | private readonly logger: SeqLogger, 145 | private readonly appService: AppService, 146 | ) {} 147 | 148 | @Get() 149 | getHello(): string { 150 | this.logger.info('getHello - start', AppController.name); 151 | 152 | const result = this.appService.getHello(); 153 | this.logger.debug( 154 | `Retrieving result from {name}`, 155 | { 156 | name: 'AppService', 157 | result, 158 | note: 'The message template function is used here', 159 | }, 160 | AppController.name, 161 | ); 162 | 163 | try { 164 | throw new Error('Oops! Something whimsically wrong just happened!'); 165 | } catch (error: any) { 166 | this.logger.error(error, AppController.name); 167 | this.logger.error( 168 | 'The error has been successfully captured and handled!', 169 | error, 170 | AppController.name, 171 | ); 172 | 173 | return result; 174 | } 175 | } 176 | } 177 | 178 | ``` 179 | 180 | ### Seq 181 | 182 | > Seq is a powerful centralized logging system. Explore the logs we have gathered: 183 | 184 | ![Log Visualization](img/seq_2024.2.11282.jpg) 185 | 186 | ## Nest System Console Logging to Seq 187 | 188 | > This section describes how to integrate Nest system console logging with Seq through a custom console logger. To set up this integration, follow the steps below: 189 | 190 | ```typescript 191 | import { NestFactory } from '@nestjs/core'; 192 | import { AppModule } from './app.module'; 193 | /** 194 | * Import the ConsoleSeqLogger to extend the built-in logger for supporting Seq. 195 | * Added by Jason.Song (成长的小猪) on 2021/09/24 196 | */ 197 | import { ConsoleSeqLogger } from '@jasonsoft/nestjs-seq'; 198 | 199 | async function bootstrap() { 200 | /** 201 | * Set bufferLogs to true to buffer all logs until the ConsoleSeqLogger is attached. 202 | * This ensures that logs are captured during the application initialization. 203 | * If initialization fails, Nest will use the default ConsoleLogger to output error messages. 204 | */ 205 | const app = await NestFactory.create(AppModule, { 206 | bufferLogs: true, 207 | }); 208 | 209 | /** 210 | * Use the ConsoleSeqLogger to extend the built-in logger functionality. 211 | */ 212 | app.useLogger(app.get(ConsoleSeqLogger)); 213 | 214 | await app.listen(3000); 215 | } 216 | bootstrap(); 217 | ``` 218 | 219 | This setup allows your NestJS application to log directly to Seq, providing a centralized logging solution. 220 | 221 | > Seq Interface Overview showing the integration of Nest System Console Logging with Seq. 222 | 223 | ![Seq Interface Overview](img/console_seq_2024.2.11282.jpg) 224 | 225 | ## Example Repository 226 | 227 | For a practical implementation example, visit the [nestjs-seq-example repository on GitHub](https://github.com/jasonsoft-net/nestjs-seq-example). 228 | 229 | [npm-img]: https://img.shields.io/npm/v/@jasonsoft/nestjs-seq.svg?style=flat-square 230 | [npm-url]: https://npmjs.org/package/@jasonsoft/nestjs-seq 231 | [license-img]: https://img.shields.io/badge/license-MIT-green.svg?style=flat-square 232 | [license-url]: LICENSE 233 | [downloads-image]: https://img.shields.io/npm/dt/@jasonsoft/nestjs-seq.svg?style=flat-square 234 | [project-icon]: https://avatars.githubusercontent.com/u/22167571?v=4 235 | [stars-img]: https://img.shields.io/github/stars/jasonsoft/nestjs-seq?style=social 236 | [github-url]: https://github.com/jasonsoft/nestjs-seq 237 | -------------------------------------------------------------------------------- /lib/core/seq-logger.core.ts: -------------------------------------------------------------------------------- 1 | import * as os from 'os'; 2 | import axios from 'axios'; 3 | import { SeqEvent, SeqLoggerOptions } from '../interfaces'; 4 | import { EventCollection } from '../utils/event-collection.util'; 5 | import { sleep } from '../utils/sleep.util'; 6 | import { NETWORK_ERRORS } from '../seq-logger.constants'; 7 | import { logToConsole, safeClone, safeStringify } from '../utils'; 8 | 9 | /** 10 | * Core logger class responsible for managing and sending log events to Seq server. 11 | * Added by Jason.Song (成长的小猪) on 2023/11/18 12 | */ 13 | export class SeqLoggerCore { 14 | options: SeqLoggerOptions; 15 | metaProperties: Record; 16 | private isInitEvent: boolean = false; 17 | private events: EventCollection = new EventCollection(); 18 | private isRunning: boolean = false; 19 | private timer: NodeJS.Timeout | undefined; 20 | private readonly START_TAG = '{"Events":['; 21 | private readonly END_TAG = ']}'; 22 | private readonly TAG_LENGTH = 23 | Buffer.byteLength(this.START_TAG) + Buffer.byteLength(this.END_TAG); 24 | 25 | /** 26 | * Initializes the logger with provided options, merging them with default values. 27 | * Added by Jason.Song (成长的小猪) on 2023/11/18 28 | * @param options Partial configuration options for the logger. 29 | */ 30 | constructor(options: Partial) { 31 | this.options = this.mergeOptionsWithDefaults(options); 32 | this.metaProperties = { 33 | hostname: os.hostname(), 34 | serviceName: this.options.serviceName, 35 | ...this.options.extendMetaProperties, 36 | logger: 'seq', 37 | }; 38 | this.isRunning = true; 39 | } 40 | 41 | /** 42 | * Gets the sensitive keys configuration for masking sensitive data. 43 | * Added by Jason.Song (成长的小猪) on 2025/08/07 01:49:43 44 | * @returns Array of sensitive key patterns 45 | */ 46 | getSensitiveKeys(): string[] | undefined { 47 | return this.options.sensitiveKeys; 48 | } 49 | 50 | /** 51 | * Merges user-provided options with default logger options. 52 | * Added by Jason.Song (成长的小猪) on 2023/11/18 53 | * @param options Partial configuration options for the logger. 54 | * @returns Complete configuration options for the logger. 55 | */ 56 | private mergeOptionsWithDefaults( 57 | options: Partial, 58 | ): SeqLoggerOptions { 59 | const defaultOptions: SeqLoggerOptions = { 60 | serverUrl: 'http://localhost:5341', 61 | batchPayloadLimit: 10485760, 62 | eventBodyLimit: 262144, 63 | maxRetries: 5, 64 | delay: 5, 65 | timeout: 30, 66 | metaFieldName: 'meta', 67 | }; 68 | return { ...defaultOptions, ...options }; 69 | } 70 | 71 | /** 72 | * Emits a log event, adding it to the queue and starting the send process if not already running. 73 | * Added by Jason.Song (成长的小猪) on 2023/11/18 74 | * @param event The log event to emit. 75 | */ 76 | public emit(event: SeqEvent) { 77 | if (!this.isRunning) { 78 | return; 79 | } 80 | this.events.add(safeClone(event)); 81 | this.start(2000); 82 | } 83 | 84 | /** 85 | * Starts the send process after a delay, if it's not already started. 86 | * Added by Jason.Song (成长的小猪) on 2023/11/18 87 | * @param ms Delay in milliseconds before starting the send process. 88 | */ 89 | private start(ms: number = 0) { 90 | if (this.timer) { 91 | return; 92 | } 93 | this.timer = setTimeout(() => this.send(), ms); 94 | } 95 | 96 | /** 97 | * Stops the send process by clearing the timer. 98 | * Added by Jason.Song (成长的小猪) on 2023/11/18 99 | */ 100 | private stop() { 101 | clearTimeout(this.timer); 102 | this.timer = undefined; 103 | } 104 | 105 | /** 106 | * Sends log events to the Seq server in batches, respecting payload limits and retrying on failure. 107 | * Added by Jason.Song (成长的小猪) on 2023/11/18 108 | */ 109 | private async send(): Promise { 110 | while (this.events.size() > 0) { 111 | const { batchPayloadLimit, eventBodyLimit, sensitiveKeys } = this.options; 112 | let contentLen = this.TAG_LENGTH; 113 | const contentArray: string[] = []; 114 | const eventsToProcess: SeqEvent[] = []; 115 | 116 | while (this.events.size() > 0) { 117 | const event = this.events.get(0); 118 | if (!event) { 119 | break; 120 | } 121 | let jsonStr = safeStringify(event, { 122 | sensitiveKeys, 123 | }); 124 | let jsonLen = Buffer.byteLength(jsonStr); 125 | if (jsonLen > eventBodyLimit) { 126 | logToConsole( 127 | 'warn', 128 | `Event body is larger than ${eventBodyLimit} bytes: ${jsonLen}`, 129 | ); 130 | const correctEvent = this.handleLargeEvent( 131 | event, 132 | jsonLen, 133 | eventBodyLimit, 134 | ); 135 | jsonStr = safeStringify(correctEvent, { 136 | sensitiveKeys, 137 | }); 138 | jsonLen = Buffer.byteLength(jsonStr); 139 | } 140 | if (contentLen + jsonLen > batchPayloadLimit) { 141 | break; 142 | } 143 | contentArray.push(jsonStr); 144 | eventsToProcess.push(event); 145 | contentLen += jsonLen; 146 | this.events.remove(0); 147 | } 148 | 149 | if (contentArray.length > 0) { 150 | const content = `${this.START_TAG}${contentArray.join(',')}${ 151 | this.END_TAG 152 | }`; 153 | let sendSuccess = false; 154 | 155 | for (let attempt = 0; attempt < this.options.maxRetries; attempt++) { 156 | try { 157 | const url = `${this.options.serverUrl.replace( 158 | /\/?$/, 159 | '', 160 | )}/api/events/raw`; 161 | await axios.post(url, content, { 162 | headers: { 163 | 'Content-Type': 'application/json', 164 | 'X-Seq-ApiKey': this.options.apiKey, 165 | 'Content-Length': Buffer.byteLength(content), 166 | }, 167 | timeout: this.options.timeout * 1000, 168 | }); 169 | if (!this.isInitEvent) { 170 | this.isInitEvent = true; 171 | logToConsole('log', 'Seq logger initialized successfully'); 172 | } 173 | sendSuccess = true; 174 | break; 175 | } catch (error: unknown) { 176 | const shouldRetry = this.shouldRetry(error, attempt); 177 | if (shouldRetry) { 178 | await sleep(this.options.delay); 179 | continue; 180 | } 181 | this.logError(error); 182 | break; 183 | } 184 | } 185 | 186 | if (!sendSuccess) { 187 | for (let i = eventsToProcess.length - 1; i >= 0; i--) { 188 | this.events.add(eventsToProcess[i]); 189 | } 190 | logToConsole( 191 | 'warn', 192 | `Failed to send ${eventsToProcess.length} events after ${this.options.maxRetries} attempts. Events will be retried on next send cycle.`, 193 | ); 194 | break; 195 | } 196 | } 197 | } 198 | 199 | this.stop(); 200 | } 201 | 202 | /** 203 | * Determines if an error should trigger a retry. 204 | * @param error The error to check 205 | * @param attempt Current attempt number 206 | * @returns true if should retry, false otherwise 207 | */ 208 | private shouldRetry(error: unknown, attempt: number): boolean { 209 | if (attempt >= this.options.maxRetries - 1) { 210 | return false; 211 | } 212 | 213 | if (axios.isAxiosError(error)) { 214 | if (error.response) { 215 | const { status } = error.response; 216 | if (status >= 500 && status <= 599) { 217 | logToConsole( 218 | 'error', 219 | `Server error, retrying(${attempt + 1}) in ${this.options.delay} seconds`, 220 | ); 221 | return true; 222 | } 223 | } 224 | 225 | if (error.code && NETWORK_ERRORS.includes(error.code)) { 226 | logToConsole( 227 | 'error', 228 | `Network error, retrying(${attempt + 1}) in ${this.options.delay} seconds`, 229 | ); 230 | return true; 231 | } 232 | } 233 | 234 | return false; 235 | } 236 | 237 | /** 238 | * Logs an error message. 239 | * @param error The error to log 240 | */ 241 | private logError(error: unknown): void { 242 | if (axios.isAxiosError(error)) { 243 | let data = error.message; 244 | if (error.response) { 245 | const { data: body } = error.response; 246 | if (body && typeof body === 'object' && 'Error' in body) { 247 | data = (body as { Error: string }).Error; 248 | } 249 | } 250 | logToConsole('error', `Failed to send log due to: ${data || error.code}`); 251 | } else if (error instanceof Error) { 252 | const errorMessages = [error.message]; 253 | if (error.stack) { 254 | errorMessages.push(error.stack); 255 | } 256 | logToConsole('error', ...errorMessages); 257 | } else { 258 | logToConsole('error', String(error)); 259 | } 260 | } 261 | 262 | /** 263 | * Handles events that exceed the body limit by creating a new event indicating the original message was too large. 264 | * Added by Jason.Song (成长的小猪) on 2023/11/18 265 | * @param event The original event that exceeded the body limit. 266 | * @param currentBodyLen The length of the original event's body. 267 | * @param eventBodyLimit The maximum allowed body length. 268 | * @returns A new event indicating the original message was too large. 269 | */ 270 | private handleLargeEvent( 271 | event: SeqEvent, 272 | currentBodyLen: number, 273 | eventBodyLimit: number, 274 | ): SeqEvent { 275 | const partialMessage = event.MessageTemplate?.substring(0, 50); 276 | return { 277 | Timestamp: event.Timestamp, 278 | Level: event.Level, 279 | MessageTemplate: `[{context}] - (Log too large) ${partialMessage}...`, 280 | Properties: { 281 | serviceName: this.options.serviceName, 282 | context: 'jasonsoft/nestjs-seq', 283 | hostname: event.Properties?.hostname, 284 | logger: event.Properties?.logger, 285 | message: `Event body is larger than ${eventBodyLimit} bytes: ${currentBodyLen}`, 286 | }, 287 | }; 288 | } 289 | 290 | /** 291 | * Closes the logger, stopping any ongoing send processes and marking the logger as not running. 292 | * Waits for all pending events to be sent before returning. 293 | * Added by Jason.Song (成长的小猪) on 2023/11/18 294 | */ 295 | public async close(): Promise { 296 | if (!this.isRunning) { 297 | logToConsole('warn', 'Logger is already closed'); 298 | return; 299 | } 300 | this.isRunning = false; 301 | this.stop(); 302 | 303 | while (this.events.size() > 0) { 304 | await this.send(); 305 | } 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /lib/utils/stringify.util.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Options for safeStringify function 3 | */ 4 | export interface SafeStringifyOptions { 5 | /** 6 | * Maximum recursion depth (default: 10) 7 | */ 8 | maxDepth?: number; 9 | /** 10 | * Maximum string length in bytes (default: 1MB) 11 | */ 12 | maxLength?: number; 13 | /** 14 | * Array of sensitive key patterns to mask in log output 15 | */ 16 | sensitiveKeys?: string[]; 17 | } 18 | 19 | /** 20 | * Safely converts an object to a JSON string, handling circular references and special types. 21 | * Optimized for NestJS Seq logging with comprehensive safety checks. 22 | * 23 | * Updated by Jason.Song (成长的小猪) on 2025/08/07 12:12:04 24 | * 25 | * Added by Jason.Song (成长的小猪) on 2023/11/22 19:17:54 26 | * @param obj The object to be converted to a JSON string. 27 | * @param options Optional configuration options for stringification. 28 | * @returns A JSON string representing the object, handling circular references and special types. 29 | */ 30 | export function safeStringify( 31 | obj: any, 32 | options?: SafeStringifyOptions, 33 | ): string { 34 | // Extract options with defaults 35 | const maxDepth = options?.maxDepth ?? 10; 36 | const maxLength = options?.maxLength ?? 1024 * 1024; 37 | const sensitiveKeys = options?.sensitiveKeys; 38 | 39 | // Fast path for primitives 40 | if (obj === null || obj === undefined) { 41 | return JSON.stringify(obj); 42 | } 43 | 44 | if (typeof obj !== 'object') { 45 | try { 46 | return JSON.stringify(obj); 47 | } catch { 48 | return JSON.stringify(String(obj)); 49 | } 50 | } 51 | 52 | // Use comprehensive object processing 53 | try { 54 | const processed = processValue( 55 | obj, 56 | new WeakMap(), 57 | 0, 58 | 'root', 59 | maxDepth, 60 | sensitiveKeys, 61 | ); 62 | const result = JSON.stringify(processed); 63 | 64 | // Check length limit 65 | if (result.length > maxLength) { 66 | return JSON.stringify({ 67 | __error: 'Object too large for serialization', 68 | __originalSize: result.length, 69 | __maxAllowed: maxLength, 70 | __preview: result.substring(0, Math.min(500, maxLength / 4)) + '...', 71 | __timestamp: new Date().toISOString(), 72 | }); 73 | } 74 | 75 | return result; 76 | } catch (error) { 77 | // Ultimate fallback 78 | return JSON.stringify({ 79 | __stringifyError: true, 80 | __message: error instanceof Error ? error.message : String(error), 81 | __originalType: typeof obj, 82 | __timestamp: new Date().toISOString(), 83 | }); 84 | } 85 | } 86 | 87 | /** 88 | * Process any value with comprehensive safety checks 89 | */ 90 | function processValue( 91 | value: any, 92 | visited: WeakMap, 93 | depth: number, 94 | path: string, 95 | maxDepth: number, 96 | sensitiveKeys?: string[], 97 | ): any { 98 | // Depth protection 99 | if (depth > maxDepth) { 100 | return `[Max depth ${maxDepth} exceeded at ${path}]`; 101 | } 102 | 103 | // Handle primitives 104 | if (value === null || value === undefined) { 105 | return value; 106 | } 107 | 108 | // Handle non-object types 109 | if (typeof value !== 'object') { 110 | return processPrimitive(value); 111 | } 112 | 113 | // Circular reference detection 114 | if (visited.has(value)) { 115 | const originalPath = visited.get(value); 116 | if (!originalPath) { 117 | return `[Circular → unknown]`; 118 | } 119 | 120 | // Show more concise circular reference notation 121 | if (originalPath === 'root') { 122 | return `[Circular → self]`; 123 | } else { 124 | // Simplify path display by removing 'root.' prefix for better readability 125 | const displayOriginal = originalPath.startsWith('root.') 126 | ? originalPath.substring(5) 127 | : originalPath; 128 | return `[Circular → ${displayOriginal}]`; 129 | } 130 | } 131 | 132 | // Add to visited set 133 | visited.set(value, path); 134 | 135 | try { 136 | // Handle special object types 137 | if (value instanceof Error) { 138 | return processError(value); 139 | } 140 | 141 | if (value instanceof Date) { 142 | return processDate(value); 143 | } 144 | 145 | if (value instanceof RegExp) { 146 | // Return the string representation directly for better readability 147 | return value.toString(); 148 | } 149 | 150 | if (value instanceof Set) { 151 | return processSet(value, visited, depth, path, maxDepth, sensitiveKeys); 152 | } 153 | 154 | if (value instanceof Map) { 155 | return processMap(value, visited, depth, path, maxDepth, sensitiveKeys); 156 | } 157 | 158 | // Handle Buffer (Node.js specific) 159 | if (typeof Buffer !== 'undefined' && value instanceof Buffer) { 160 | return { 161 | __type: 'Buffer', 162 | length: value.length, 163 | preview: value.slice(0, 16).toString('hex'), 164 | __note: 165 | value.length > 16 ? `... and ${value.length - 16} more bytes` : '', 166 | }; 167 | } 168 | 169 | // Handle TypedArrays 170 | if (ArrayBuffer.isView(value)) { 171 | return { 172 | __type: 'TypedArray', 173 | constructor: value.constructor.name, 174 | length: (value as any).length, 175 | byteLength: value.byteLength, 176 | preview: Array.from(value as any).slice(0, 8), 177 | }; 178 | } 179 | 180 | // Handle Arrays 181 | if (Array.isArray(value)) { 182 | return processArray(value, visited, depth, path, maxDepth, sensitiveKeys); 183 | } 184 | 185 | // Handle plain objects 186 | return processObject(value, visited, depth, path, maxDepth, sensitiveKeys); 187 | } finally { 188 | // Clean up visited reference 189 | visited.delete(value); 190 | } 191 | } 192 | 193 | /** 194 | * Process primitive types with special handling 195 | */ 196 | function processPrimitive(value: any): any { 197 | switch (typeof value) { 198 | case 'undefined': 199 | return '[Undefined]'; 200 | case 'function': 201 | return { 202 | __type: 'Function', 203 | name: value.name || 'anonymous', 204 | length: value.length, 205 | }; 206 | case 'symbol': 207 | // Return the string representation directly for better readability 208 | return value.toString(); 209 | case 'bigint': 210 | // Return the string representation with 'n' suffix for clarity 211 | return value.toString() + 'n'; 212 | case 'number': 213 | if (!isFinite(value)) { 214 | return { 215 | __type: 'Number', 216 | __special: isNaN(value) 217 | ? 'NaN' 218 | : value > 0 219 | ? 'Infinity' 220 | : '-Infinity', 221 | }; 222 | } 223 | return value; 224 | default: 225 | return value; 226 | } 227 | } 228 | 229 | /** 230 | * Process Error objects with comprehensive information extraction 231 | */ 232 | function processError(error: Error): any { 233 | const result: any = { 234 | __type: 'Error', 235 | name: error.name, 236 | message: error.message, 237 | }; 238 | 239 | // Include stack trace 240 | if (error.stack) { 241 | result.stack = error.stack; 242 | } 243 | 244 | // Extract additional properties 245 | const additionalProps: any = {}; 246 | for (const key of Object.getOwnPropertyNames(error)) { 247 | if (!['name', 'message', 'stack'].includes(key)) { 248 | try { 249 | additionalProps[key] = (error as any)[key]; 250 | } catch { 251 | additionalProps[key] = '[Unreadable property]'; 252 | } 253 | } 254 | } 255 | 256 | if (Object.keys(additionalProps).length > 0) { 257 | result.additionalProperties = additionalProps; 258 | } 259 | 260 | return result; 261 | } 262 | 263 | /** 264 | * Process Date objects with validation 265 | */ 266 | function processDate(date: Date): any { 267 | if (isNaN(date.getTime())) { 268 | return { 269 | __type: 'Date', 270 | __invalid: true, 271 | __string: date.toString(), 272 | }; 273 | } 274 | 275 | // Return the ISO string directly for valid dates to maintain readability 276 | return date.toISOString(); 277 | } 278 | 279 | /** 280 | * Process Set objects 281 | */ 282 | function processSet( 283 | set: Set, 284 | visited: WeakMap, 285 | depth: number, 286 | path: string, 287 | maxDepth: number, 288 | sensitiveKeys?: string[], 289 | ): any { 290 | const values = Array.from(set); 291 | const maxItems = Math.min(values.length, 100); // Limit for performance 292 | 293 | return { 294 | __type: 'Set', 295 | size: set.size, 296 | values: values 297 | .slice(0, maxItems) 298 | .map((item, index) => 299 | processValue( 300 | item, 301 | visited, 302 | depth + 1, 303 | `${path}.Set[${index}]`, 304 | maxDepth, 305 | sensitiveKeys, 306 | ), 307 | ), 308 | ...(set.size > maxItems && { 309 | __truncated: `... and ${set.size - maxItems} more items`, 310 | }), 311 | }; 312 | } 313 | 314 | /** 315 | * Process Map objects 316 | */ 317 | function processMap( 318 | map: Map, 319 | visited: WeakMap, 320 | depth: number, 321 | path: string, 322 | maxDepth: number, 323 | sensitiveKeys?: string[], 324 | ): any { 325 | const entries = Array.from(map.entries()); 326 | const maxItems = Math.min(entries.length, 100); // Limit for performance 327 | 328 | return { 329 | __type: 'Map', 330 | size: map.size, 331 | entries: entries 332 | .slice(0, maxItems) 333 | .map(([key, value], index) => [ 334 | processValue( 335 | key, 336 | visited, 337 | depth + 1, 338 | `${path}.Map[${index}].key`, 339 | maxDepth, 340 | sensitiveKeys, 341 | ), 342 | processValue( 343 | value, 344 | visited, 345 | depth + 1, 346 | `${path}.Map[${index}].value`, 347 | maxDepth, 348 | sensitiveKeys, 349 | ), 350 | ]), 351 | ...(map.size > maxItems && { 352 | __truncated: `... and ${map.size - maxItems} more entries`, 353 | }), 354 | }; 355 | } 356 | 357 | /** 358 | * Process arrays with size limits 359 | */ 360 | function processArray( 361 | array: any[], 362 | visited: WeakMap, 363 | depth: number, 364 | path: string, 365 | maxDepth: number, 366 | sensitiveKeys?: string[], 367 | ): any[] { 368 | const maxItems = Math.min(array.length, 1000); // Reasonable limit for logs 369 | const result: any[] = []; 370 | 371 | for (let i = 0; i < maxItems; i++) { 372 | try { 373 | result[i] = processValue( 374 | array[i], 375 | visited, 376 | depth + 1, 377 | `${path}[${i}]`, 378 | maxDepth, 379 | sensitiveKeys, 380 | ); 381 | } catch (error) { 382 | result[i] = `[Error processing array item: ${ 383 | error instanceof Error ? error.message : String(error) 384 | }]`; 385 | } 386 | } 387 | 388 | // Add truncation notice if needed 389 | if (array.length > maxItems) { 390 | result.push(`[... and ${array.length - maxItems} more items]`); 391 | } 392 | 393 | return result; 394 | } 395 | 396 | /** 397 | * Process plain objects with property limits and sensitive data masking 398 | */ 399 | function processObject( 400 | obj: any, 401 | visited: WeakMap, 402 | depth: number, 403 | path: string, 404 | maxDepth: number, 405 | sensitiveKeys?: string[], 406 | ): any { 407 | const result: any = {}; 408 | const keys = Object.keys(obj); 409 | const maxProps = Math.min(keys.length, 1000); // Reasonable limit 410 | // Use provided sensitiveKeys or no masking if not provided 411 | const effectiveSensitiveKeys = sensitiveKeys; 412 | 413 | let processedProps = 0; 414 | for (const key of keys) { 415 | if (processedProps >= maxProps) break; 416 | 417 | try { 418 | // Check for sensitive data only if sensitiveKeys is provided 419 | if ( 420 | effectiveSensitiveKeys && 421 | effectiveSensitiveKeys.some((sensitive: string) => 422 | key.toLowerCase().includes(sensitive.toLowerCase()), 423 | ) 424 | ) { 425 | result[key] = '[Sensitive data masked]'; 426 | processedProps++; 427 | continue; 428 | } 429 | 430 | result[key] = processValue( 431 | obj[key], 432 | visited, 433 | depth + 1, 434 | `${path}.${key}`, 435 | maxDepth, 436 | sensitiveKeys, 437 | ); 438 | processedProps++; 439 | } catch (error) { 440 | result[key] = `[Error processing property: ${ 441 | error instanceof Error ? error.message : String(error) 442 | }]`; 443 | processedProps++; 444 | } 445 | } 446 | 447 | // Add truncation notice if needed 448 | if (keys.length > maxProps) { 449 | result.__truncated = `... and ${keys.length - maxProps} more properties`; 450 | } 451 | 452 | return result; 453 | } 454 | -------------------------------------------------------------------------------- /lib/seq-logger.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; 2 | import { JASONSOFT_SEQ_LOGGER } from './seq-logger.constants'; 3 | import { SeqLoggerCore } from './core'; 4 | import { isPlainObject, logToConsole } from './utils'; 5 | import { SeqEvent } from './interfaces'; 6 | import { LOG_LEVELS_VALUES, LogLevel, SeqLevel } from './enums'; 7 | 8 | /** 9 | * Seq logger service for sending log messages to Seq. 10 | * Updated by Jason.Song (成长的小猪) on 2023/11/22 19:18:37 11 | * Added by Jason.Song (成长的小猪) on 2021/07/05 16:59:39 12 | */ 13 | @Injectable() 14 | export class SeqLogger implements OnApplicationShutdown { 15 | private static levelMapping = { 16 | verbose: SeqLevel.Verbose, 17 | debug: SeqLevel.Debug, 18 | info: SeqLevel.Information, 19 | log: SeqLevel.Information, 20 | warn: SeqLevel.Warning, 21 | error: SeqLevel.Error, 22 | fatal: SeqLevel.Fatal, 23 | }; 24 | 25 | private static instance: SeqLogger; 26 | /** 27 | * Retrieves the singleton instance of SeqLogger. 28 | * If the instance has not been initialized, it returns undefined. 29 | * Added by Jason.Song (成长的小猪) on 2024/04/17 22:09:17 30 | */ 31 | public static getInstance(level: LogLevel): SeqLogger | undefined { 32 | if (!SeqLogger.instance) { 33 | const errorMessage = 34 | `SeqLogger instance has not been initialized.\n` + 35 | ` Please ensure SeqLoggerModule dependencies are initialized before calling \`SeqLogger.${level}()\`.`; 36 | if (process.env.NODE_ENV !== 'production') { 37 | const stack = new Error(errorMessage).stack || ''; 38 | const lines = stack.split('\n'); 39 | const filteredLines = lines.filter( 40 | (line: string) => !line.includes('seq-logger.service'), 41 | ); 42 | const filteredStack = filteredLines.join('\n'); 43 | logToConsole('error', filteredStack); 44 | } else { 45 | logToConsole('error', errorMessage); 46 | } 47 | } 48 | return SeqLogger.instance; 49 | } 50 | 51 | constructor( 52 | @Inject(JASONSOFT_SEQ_LOGGER) 53 | private readonly seqLogger: SeqLoggerCore, 54 | ) { 55 | if (!SeqLogger.instance) { 56 | SeqLogger.instance = this; 57 | } 58 | } 59 | 60 | /** 61 | * Gets the sensitive keys configuration for masking sensitive data. 62 | * Added by Jason.Song (成长的小猪) on 2025/01/27 63 | * @returns Array of sensitive key patterns 64 | */ 65 | getSensitiveKeys(): string[] | undefined { 66 | return this.seqLogger.getSensitiveKeys(); 67 | } 68 | 69 | /** 70 | * Determines if a log message should be logged based on the configured log level. 71 | * Added by Jason.Song (成长的小猪) on 2024/04/17 22:44:27 72 | * @param level The log level to check. 73 | * @returns boolean indicating if logging should proceed. 74 | */ 75 | private shouldLog(level: LogLevel): boolean { 76 | const logLevels = this.seqLogger.options.logLevels; 77 | if (!logLevels) { 78 | return true; 79 | } 80 | if (Array.isArray(logLevels)) { 81 | return logLevels.includes(level); 82 | } 83 | const minLevel = LOG_LEVELS_VALUES[logLevels]; 84 | return LOG_LEVELS_VALUES[level] >= minLevel; 85 | } 86 | 87 | /** 88 | * Logs a message with a specified level, message template, and optional properties or context. 89 | * This method is used internally to handle the actual logging logic. 90 | * Added by Jason.Song (成长的小猪) on 2024/04/17 22:53:56 91 | * 92 | * @param level The severity level of the log message, defined by LogLevel enum. 93 | * @param messageTemplate The message template string that forms the basis of the log message. 94 | * @param propsOrContext Optional. Can be either a string representing the logging context or an object containing properties to log. 95 | * @param context Optional. Provides additional context for the log message, used when propsOrContext is an object. 96 | */ 97 | private logMessage( 98 | level: LogLevel, 99 | messageTemplate: string, 100 | propsOrContext?: string | object, 101 | context?: string, 102 | ): void { 103 | if (!this.shouldLog(level)) { 104 | return; 105 | } 106 | const [messageTpl, props] = this.getMessageTemplateAndProperties( 107 | messageTemplate, 108 | propsOrContext, 109 | context, 110 | ); 111 | this.commit(level, messageTpl, props); 112 | } 113 | 114 | /** 115 | * Logs a message statically with the specified level, message template, and optional properties or context. 116 | * This static method delegates the logging task to an instance method of a possibly existing SeqLogger instance. 117 | * Added by Jason.Song (成长的小猪) on 2024/04/17 22:59:33 118 | * 119 | * @param level The severity level of the log message. 120 | * @param messageTemplate The message template string that forms the basis of the log message. 121 | * @param propsOrContext Optional. Can be either a string representing the logging context or an object containing properties to log. 122 | * @param context Optional. Provides additional context for the log message, used when propsOrContext is an object. 123 | */ 124 | private static logStaticMessage( 125 | level: LogLevel, 126 | messageTemplate: string, 127 | propsOrContext?: string | object, 128 | context?: string, 129 | ): void { 130 | const logger = SeqLogger.getInstance(level); 131 | if (logger) { 132 | logger.logMessage(level, messageTemplate, propsOrContext, context); 133 | } 134 | } 135 | 136 | /** 137 | * Logs a verbose message with optional properties and context. 138 | * This method supports method overloading to accommodate different combinations of parameters, allowing for flexible usage depending on the provided arguments. 139 | * Updated by Jason.Song (成长的小猪) on 2023/11/22 19:18:52 140 | * 141 | * Overloads: 142 | * - verbose(messageTemplate: string, context?: string): void 143 | * Logs a message with an optional context. This form does not include additional properties. 144 | * - verbose(messageTemplate: string, properties: object, context?: string): void 145 | * Logs a message with additional properties and an optional context. 146 | * 147 | * @param messageTemplate The message template to log. 148 | * @param properties (Optional) Additional properties to log. This parameter is considered only in the second overload. 149 | * @param context (Optional) The logging context. This is considered in both overloads but is mandatory in the second overload if properties are provided. 150 | */ 151 | public verbose(messageTemplate: string, context?: string): void; 152 | public verbose( 153 | messageTemplate: string, 154 | properties: object, 155 | context?: string, 156 | ): void; 157 | public verbose( 158 | messageTemplate: string, 159 | propsOrContext?: string | object, 160 | context?: string, 161 | ): void { 162 | this.logMessage('verbose', messageTemplate, propsOrContext, context); 163 | } 164 | 165 | /** 166 | * Logs a verbose message statically with optional properties and context. 167 | * This method supports method overloading to accommodate different combinations of parameters, allowing for flexible usage depending on the provided arguments. 168 | * Added by Jason.Song (成长的小猪) on 2024/04/17 22:27:45 169 | * 170 | * Overloads: 171 | * - verbose(messageTemplate: string, context?: string): void 172 | * Logs a message with an optional context. This form does not include additional properties. 173 | * - verbose(messageTemplate: string, properties: object, context?: string): void 174 | * Logs a message with additional properties and an optional context. 175 | * 176 | * @param messageTemplate The message template to log. 177 | * @param properties (Optional) Additional properties to log. This parameter is considered only in the second overload. 178 | * @param context (Optional) The logging context. This is considered in both overloads but is mandatory in the second overload if properties are provided. 179 | */ 180 | public static verbose(messageTemplate: string, context?: string): void; 181 | public static verbose( 182 | messageTemplate: string, 183 | properties: object, 184 | context?: string, 185 | ): void; 186 | public static verbose( 187 | messageTemplate: string, 188 | propsOrContext?: string | object, 189 | context?: string, 190 | ): void { 191 | SeqLogger.logStaticMessage( 192 | 'verbose', 193 | messageTemplate, 194 | propsOrContext, 195 | context, 196 | ); 197 | } 198 | 199 | /** 200 | * Logs a debug message with optional properties and context. 201 | * This method supports method overloading to accommodate different combinations of parameters, allowing for flexible usage depending on the provided arguments. 202 | * Updated by Jason.Song (成长的小猪) on 2023/11/22 19:19:02 203 | * 204 | * Overloads: 205 | * - debug(messageTemplate: string, context?: string): void 206 | * Logs a message with an optional context. This form does not include additional properties. 207 | * - debug(messageTemplate: string, properties: object, context?: string): void 208 | * Logs a message with additional properties and an optional context. 209 | * 210 | * @param messageTemplate The message template to log. 211 | * @param properties (Optional) Additional properties to log. This parameter is considered only in the second overload. 212 | * @param context (Optional) The logging context. This is considered in both overloads but is mandatory in the second overload if properties are provided. 213 | */ 214 | public debug(messageTemplate: string, context?: string): void; 215 | public debug( 216 | messageTemplate: string, 217 | properties: object, 218 | context?: string, 219 | ): void; 220 | public debug( 221 | messageTemplate: string, 222 | propsOrContext?: string | object, 223 | context?: string, 224 | ): void { 225 | this.logMessage('debug', messageTemplate, propsOrContext, context); 226 | } 227 | 228 | /** 229 | * Static method to log a debug message with optional properties and context. 230 | * This method allows for logging without an instance of the class and supports overloading. 231 | * Added by Jason.Song (成长的小猪) on 2024/04/17 22:22:51 232 | * 233 | * Overloads: 234 | * - debug(messageTemplate: string, context?: string): void 235 | * Logs a message with an optional context when properties are not provided. 236 | * - debug(messageTemplate: string, properties: object, context?: string): void 237 | * Logs a message with properties and an optional context. 238 | * 239 | * @param messageTemplate The message template to log. 240 | * @param properties (Optional) Additional properties to log. This parameter is ignored if the first overload is used. 241 | * @param context (Optional) The logging context. This is considered only if the second parameter is an object, indicating the use of the second overload. 242 | */ 243 | public static debug(messageTemplate: string, context?: string): void; 244 | public static debug( 245 | messageTemplate: string, 246 | properties: object, 247 | context?: string, 248 | ): void; 249 | public static debug( 250 | messageTemplate: string, 251 | propsOrContext?: string | object, 252 | context?: string, 253 | ): void { 254 | SeqLogger.logStaticMessage( 255 | 'debug', 256 | messageTemplate, 257 | propsOrContext, 258 | context, 259 | ); 260 | } 261 | 262 | /** 263 | * Logs an informational message with optional properties and context. 264 | * This method supports overloading, allowing for flexible parameter combinations to accommodate optional properties and context. 265 | * Updated by Jason.Song (成长的小猪) on 2024/04/09 23:23:07 266 | * 267 | * Overloads: 268 | * - info(messageTemplate: string, context?: string): void 269 | * Logs a message with an optional context when properties are not provided. 270 | * - info(messageTemplate: string, properties: object, context?: string): void 271 | * Logs a message with properties and an optional context. 272 | * 273 | * @param messageTemplate The message template to log. 274 | * @param properties (Optional) Additional properties to log. This parameter is ignored if the first overload is used. 275 | * @param context (Optional) The logging context. This is considered only if the second parameter is an object, indicating the use of the second overload. 276 | */ 277 | public info(messageTemplate: string, context?: string): void; 278 | public info( 279 | messageTemplate: string, 280 | properties: object, 281 | context?: string, 282 | ): void; 283 | public info( 284 | messageTemplate: string, 285 | propsOrContext?: string | object, 286 | context?: string, 287 | ): void { 288 | this.logMessage('info', messageTemplate, propsOrContext, context); 289 | } 290 | 291 | /** 292 | * Logs an informational message statically with optional properties and context. 293 | * This method supports method overloading to accommodate different combinations of parameters, allowing for flexible usage depending on the provided arguments. 294 | * Added by Jason.Song (成长的小猪) on 2024/04/17 22:29:59 295 | * 296 | * Overloads: 297 | * - info(messageTemplate: string, context?: string): void 298 | * Logs a message with an optional context. This form does not include additional properties. 299 | * - info(messageTemplate: string, properties: object, context?: string): void 300 | * Logs a message with additional properties and an optional context. 301 | * 302 | * @param messageTemplate The message template to log. 303 | * @param properties (Optional) Additional properties to log. This parameter is considered only in the second overload. 304 | * @param context (Optional) The logging context. This is considered in both overloads but is mandatory in the second overload if properties are provided. 305 | */ 306 | public static info(messageTemplate: string, context?: string): void; 307 | public static info( 308 | messageTemplate: string, 309 | properties: object, 310 | context?: string, 311 | ): void; 312 | public static info( 313 | messageTemplate: string, 314 | propsOrContext?: string | object, 315 | context?: string, 316 | ): void { 317 | SeqLogger.logStaticMessage( 318 | 'info', 319 | messageTemplate, 320 | propsOrContext, 321 | context, 322 | ); 323 | } 324 | 325 | /** 326 | * Logs an informational message with optional properties and context, making it compatible with the NestJS framework logging standards. 327 | * This method supports method overloading to accommodate different combinations of parameters, allowing for flexible usage depending on the provided arguments. 328 | * Added by Jason.Song (成长的小猪) on 2024/04/09 23:17:34 329 | * 330 | * Overloads: 331 | * - log(messageTemplate: string, context?: string): void 332 | * Logs a message with an optional context when properties are not provided. 333 | * - log(messageTemplate: string, properties: object, context?: string): void 334 | * Logs a message with properties and an optional context. 335 | * 336 | * @param messageTemplate The message template to log. 337 | * @param properties (Optional) Additional properties to log. This parameter is ignored if the first overload is used. 338 | * @param context (Optional) The logging context. This is considered only if the second parameter is an object, indicating the use of the second overload. 339 | */ 340 | public log(messageTemplate: string, context?: string): void; 341 | public log( 342 | messageTemplate: string, 343 | properties: object, 344 | context?: string, 345 | ): void; 346 | public log( 347 | messageTemplate: string, 348 | propsOrContext?: string | object, 349 | context?: string, 350 | ): void { 351 | this.logMessage('log', messageTemplate, propsOrContext, context); 352 | } 353 | 354 | /** 355 | * Logs a message with optional properties and context statically. 356 | * This method supports method overloading to accommodate different combinations of parameters, allowing for flexible usage depending on the provided arguments. 357 | * Added by Jason.Song (成长的小猪) on 2024/04/17 22:13:10 358 | * 359 | * Overloads: 360 | * - log(messageTemplate: string, context?: string): void 361 | * Logs a message with an optional context when properties are not provided. 362 | * - log(messageTemplate: string, properties: object, context?: string): void 363 | * Logs a message with properties and an optional context. 364 | * 365 | * @param messageTemplate The message template to log. 366 | * @param properties (Optional) Additional properties to log. This parameter is considered only in the second overload. 367 | * @param context (Optional) The logging context. This is considered in both overloads. 368 | */ 369 | public static log(messageTemplate: string, context?: string): void; 370 | public static log( 371 | messageTemplate: string, 372 | properties: object, 373 | context?: string, 374 | ): void; 375 | public static log( 376 | messageTemplate: string, 377 | propsOrContext?: string | object, 378 | context?: string, 379 | ): void { 380 | SeqLogger.logStaticMessage('log', messageTemplate, propsOrContext, context); 381 | } 382 | 383 | /** 384 | * Logs a warning message with optional properties and context. 385 | * This method supports overloading, allowing for flexible parameter combinations to accommodate optional properties and context. 386 | * Updated by Jason.Song (成长的小猪) on 2023/11/22 19:19:34 387 | * 388 | * Overloads: 389 | * - warn(messageTemplate: string, context?: string): void 390 | * Logs a warning message with an optional context when properties are not provided. 391 | * - warn(messageTemplate: string, properties: object, context?: string): void 392 | * Logs a warning message with properties and an optional context. 393 | * 394 | * @param messageTemplate The message template to log. 395 | * @param properties (Optional) Additional properties to log. This parameter is considered only if the method is called with more than one argument, indicating the use of the second overload. 396 | * @param context (Optional) The logging context. This is considered only if the second parameter is an object, indicating the use of the second overload. 397 | */ 398 | public warn(messageTemplate: string, context?: string): void; 399 | public warn( 400 | messageTemplate: string, 401 | properties: object, 402 | context?: string, 403 | ): void; 404 | public warn( 405 | messageTemplate: string, 406 | propsOrContext?: string | object, 407 | context?: string, 408 | ): void { 409 | this.logMessage('warn', messageTemplate, propsOrContext, context); 410 | } 411 | 412 | /** 413 | * Logs a warning message with optional properties and context. 414 | * This method supports overloading, allowing for flexible parameter combinations to accommodate optional properties and context. 415 | * Added by Jason.Song (成长的小猪) on 2024/04/17 22:34:12 416 | * 417 | * Overloads: 418 | * - warn(messageTemplate: string, context?: string): void 419 | * Logs a warning message with an optional context when properties are not provided. 420 | * - warn(messageTemplate: string, properties: object, context?: string): void 421 | * Logs a warning message with properties and an optional context. 422 | * 423 | * @param messageTemplate The message template to log. 424 | * @param properties (Optional) Additional properties to log. This parameter is considered only if the method is called with more than one argument, indicating the use of the second overload. 425 | * @param context (Optional) The logging context. This is considered only if the second parameter is an object, indicating the use of the second overload. 426 | */ 427 | public static warn(messageTemplate: string, context?: string): void; 428 | public static warn( 429 | messageTemplate: string, 430 | properties: object, 431 | context?: string, 432 | ): void; 433 | public static warn( 434 | messageTemplate: string, 435 | propsOrContext?: string | object, 436 | context?: string, 437 | ): void { 438 | SeqLogger.logStaticMessage( 439 | 'warn', 440 | messageTemplate, 441 | propsOrContext, 442 | context, 443 | ); 444 | } 445 | 446 | /** 447 | * Analyzes an object to determine if it contains error information, and extracts relevant details. 448 | * This method checks if the input is an Error object or a plain object containing error-like properties. 449 | * It extracts and returns the error message, stack trace, and a sanitized object without error details. 450 | * Added by Jason.Song (成长的小猪) on 2024/04/17 23:19:38 451 | * 452 | * @param obj The object or Error to analyze. 453 | * @returns A tuple containing: 454 | * - A sanitized object with error details removed. 455 | * - A string representing the error message. 456 | * - An optional string representing the stack trace. 457 | */ 458 | private analyzeErrorPresence(obj: object | Error): [object, string, string?] { 459 | if (obj instanceof Error) { 460 | return [{ stack: obj.stack }, obj.message, obj.stack]; 461 | } else if (isPlainObject(obj)) { 462 | const newObj: Record = {}; 463 | let errorMessage: string = ''; 464 | let errorStack: string | undefined; 465 | 466 | Object.entries(obj).forEach(([key, value]) => { 467 | if (value instanceof Error) { 468 | errorMessage = value.message; 469 | errorStack = value.stack; 470 | } else if ( 471 | typeof value === 'string' && 472 | /\n\s+at\s+.+:\d+:\d+/.test(value) 473 | ) { 474 | errorStack = value; 475 | const errorMatch = value.match(/Error: (.+)/); 476 | if (errorMatch && errorMatch[1]) { 477 | errorMessage = errorMatch[1]; 478 | } 479 | } else { 480 | newObj[key] = value; 481 | } 482 | }); 483 | 484 | if (errorStack) { 485 | newObj.stack = errorStack; 486 | return [newObj, errorMessage, errorStack]; 487 | } 488 | } 489 | return [obj, '', '']; 490 | } 491 | 492 | /** 493 | * Handles error logging with flexible parameter inputs. 494 | * This method can accept a message, properties, or an Error object as the first parameter, 495 | * and optionally a second Error object or context as the second parameter, with an additional context as the third parameter. 496 | * Added by Jason.Song (成长的小猪) on 2024/04/17 22:50:23 497 | * 498 | * @param messageOrPropsOrError A string message, properties object, or Error object. 499 | * @param propsOrErrorOrContext Optional. Can be a string, properties object, or Error object. 500 | * @param context Optional. A string specifying the logging context. 501 | */ 502 | private handleErrorLogging( 503 | messageOrPropsOrError: string | object | Error, 504 | propsOrErrorOrContext?: string | object | Error, 505 | context?: string, 506 | ): void { 507 | if (!this.shouldLog('error')) { 508 | return; 509 | } 510 | const defaultMessage = '(No message provided)'; 511 | let messageTemplate = defaultMessage; 512 | let properties: Record = {}; 513 | 514 | if (typeof messageOrPropsOrError === 'string') { 515 | if (/\n\s+at\s+.+:\d+:\d+/.test(messageOrPropsOrError)) { 516 | properties.stack = messageOrPropsOrError; 517 | const errorMatch = messageOrPropsOrError.match(/Error: (.+)/); 518 | if (errorMatch && errorMatch[1]) { 519 | messageTemplate = errorMatch[1]; 520 | } 521 | } else { 522 | messageTemplate = messageOrPropsOrError; 523 | } 524 | } else { 525 | const [propsOrContext, errorMessage] = this.analyzeErrorPresence( 526 | messageOrPropsOrError, 527 | ); 528 | if (errorMessage) { 529 | messageTemplate = errorMessage; 530 | } 531 | const [messageTpl, props] = this.getMessageTemplateAndProperties( 532 | messageTemplate, 533 | propsOrContext, 534 | context, 535 | ); 536 | messageTemplate = messageTpl; 537 | properties = props; 538 | } 539 | 540 | if (propsOrErrorOrContext) { 541 | let propsOrContext = propsOrErrorOrContext; 542 | let stack = properties.stack; 543 | if (typeof propsOrErrorOrContext !== 'string') { 544 | const [errorObj, errorMessage, errorStack] = this.analyzeErrorPresence( 545 | propsOrErrorOrContext, 546 | ); 547 | if ( 548 | errorMessage && 549 | (!messageTemplate || messageTemplate === defaultMessage) 550 | ) { 551 | messageTemplate = errorMessage; 552 | } 553 | stack = errorStack 554 | ? stack 555 | ? `${stack}\nCaused by: ${errorStack}` 556 | : errorStack 557 | : stack; 558 | propsOrContext = errorObj; 559 | } 560 | 561 | const [messageTpl, props] = this.getMessageTemplateAndProperties( 562 | messageTemplate, 563 | propsOrContext, 564 | context, 565 | ); 566 | messageTemplate = messageTpl; 567 | properties = { ...properties, ...props, stack }; 568 | } 569 | 570 | if (context) { 571 | properties.context = context; 572 | } 573 | 574 | this.commit('error', messageTemplate, properties); 575 | } 576 | 577 | /** 578 | * Logs an error with flexible input parameters to accommodate various logging needs. 579 | * This method is designed to handle different combinations of inputs, including message templates, property objects, and Error objects, providing a versatile approach to error logging. 580 | * Updated by Jason.Song (成长的小猪) on 2024/04/09 23:10:54 581 | * 582 | * Overloads: 583 | * 1. error(messageTemplate: string, context?: string): void 584 | * - Logs an error message using a string template. An optional context can be provided to categorize or scope the error message. 585 | * 2. error(propsOrError: object | Error, context?: string): void 586 | * - Logs an error using either a set of properties or an Error object. An optional context can be included for additional message categorization. This overload is selected when the first argument is not a string. 587 | * 3. error(messageTemplate: string, propsOrError: object | Error, context?: string): void 588 | * - Logs an error message using a string template, supplemented with either additional properties or an Error object for more detailed logging. An optional context can also be provided. 589 | * 590 | * @param messageTemplate Can be a string message template, an object containing properties, or an Error object. This parameter adapts based on the overload used. 591 | * @param propsOrError Optional. Can be an object containing properties or an Error object. This parameter is considered when the first argument is a string message template, providing additional details or context for the error being logged. 592 | * @param context Optional. Specifies the logging context. This is considered in overloads where it is applicable, providing a way to categorize or scope the error message. 593 | */ 594 | public error(messageTemplate: string, context?: string): void; 595 | public error(propsOrError: object | Error, context?: string): void; 596 | public error( 597 | messageTemplate: string, 598 | propsOrError: object | Error, 599 | context?: string, 600 | ): void; 601 | public error( 602 | messageOrPropsOrError: string | object | Error, 603 | propsOrErrorOrContext?: string | object | Error, 604 | context?: string, 605 | ): void { 606 | this.handleErrorLogging( 607 | messageOrPropsOrError, 608 | propsOrErrorOrContext, 609 | context, 610 | ); 611 | } 612 | 613 | /** 614 | * Logs an error message with optional properties and context. 615 | * This method supports method overloading to accommodate different combinations of parameters, allowing for flexible usage depending on the provided arguments. 616 | * Added by Jason.Song (成长的小猪) on 2024/04/17 22:52:44 617 | * 618 | * Overloads: 619 | * - error(messageTemplate: string, context?: string): void 620 | * Logs an error message with a simple string template and an optional context. 621 | * - error(propsOrError: object | Error, context?: string): void 622 | * Logs an error with properties or an error object, and an optional context. 623 | * - error(messageTemplate: string, propsOrError: object | Error, context?: string): void 624 | * Logs an error message with a string template, additional properties or an error object, and an optional context. 625 | * 626 | * @param messageTemplate The message template to log or the first parameter can be an object or Error if using the second overload. 627 | * @param propsOrError (Optional) Additional properties to log or an Error object. This parameter is considered only in the third overload. 628 | * @param context (Optional) The logging context. This is considered in all overloads. 629 | */ 630 | public static error(messageTemplate: string, context?: string): void; 631 | public static error(propsOrError: object | Error, context?: string): void; 632 | public static error( 633 | messageTemplate: string, 634 | propsOrError: object | Error, 635 | context?: string, 636 | ): void; 637 | public static error( 638 | messageOrPropsOrError: string | object | Error, 639 | propsOrErrorOrContext?: string | object | Error, 640 | context?: string, 641 | ): void { 642 | const logger = SeqLogger.getInstance('error'); 643 | if (logger) { 644 | logger.handleErrorLogging( 645 | messageOrPropsOrError, 646 | propsOrErrorOrContext, 647 | context, 648 | ); 649 | } 650 | } 651 | 652 | /** 653 | * Logs a fatal message with optional properties and context. 654 | * This method supports overloading, allowing for flexible parameter combinations to accommodate optional properties and context. 655 | * Updated by Jason.Song (成长的小猪) on 2023/11/22 19:20:03 656 | * 657 | * Overloads: 658 | * - fatal(messageTemplate: string, context?: string): void 659 | * Logs a message with an optional context when properties are not provided. 660 | * - fatal(messageTemplate: string, properties: object, context?: string): void 661 | * Logs a message with properties and an optional context. 662 | * 663 | * @param messageTemplate The message template to log. 664 | * @param properties (Optional) Additional properties to log. This parameter is ignored if the first overload is used. 665 | * @param context (Optional) The logging context. This is considered only if the second parameter is an object, indicating the use of the second overload. 666 | */ 667 | public fatal(messageTemplate: string, context?: string): void; 668 | public fatal( 669 | messageTemplate: string, 670 | properties: object, 671 | context?: string, 672 | ): void; 673 | public fatal( 674 | messageTemplate: string, 675 | propsOrContext?: string | object, 676 | context?: string, 677 | ): void { 678 | this.logMessage('fatal', messageTemplate, propsOrContext, context); 679 | } 680 | 681 | /** 682 | * Logs a fatal message statically with optional properties and context. 683 | * This method supports method overloading to accommodate different combinations of parameters, allowing for flexible usage depending on the provided arguments. 684 | * Added by Jason.Song (成长的小猪) on 2024/04/17 22:54:30 685 | * 686 | * Overloads: 687 | * - fatal(messageTemplate: string, context?: string): void 688 | * Logs a message with an optional context. This form does not include additional properties. 689 | * - fatal(messageTemplate: string, properties: object, context?: string): void 690 | * Logs a message with properties and an optional context. 691 | * 692 | * @param messageTemplate The message template to log. 693 | * @param properties (Optional) Additional properties to log. This parameter is considered only in the second overload. 694 | * @param context (Optional) The logging context. This is considered in both overloads but is mandatory in the second overload if properties are provided. 695 | */ 696 | public static fatal(messageTemplate: string, context?: string): void; 697 | public static fatal( 698 | messageTemplate: string, 699 | properties: object, 700 | context?: string, 701 | ): void; 702 | public static fatal( 703 | messageTemplate: string, 704 | propsOrContext?: string | object, 705 | context?: string, 706 | ): void { 707 | SeqLogger.logStaticMessage( 708 | 'fatal', 709 | messageTemplate, 710 | propsOrContext, 711 | context, 712 | ); 713 | } 714 | 715 | /** 716 | * Constructs the message template and properties for logging. 717 | * This method formats the message template and merges any given properties or context into a single object. 718 | * Added by Jason.Song (成长的小猪) on 2024/04/17 22:49:37 719 | * 720 | * @param messageTemplate The base message template. 721 | * @param propsOrContext Optional parameter that can be either additional properties as an object or a context string. 722 | * @param context Optional context string, providing additional context for the log message. 723 | * @returns A tuple containing the formatted message template and the properties object. 724 | */ 725 | private getMessageTemplateAndProperties( 726 | messageTemplate: string, 727 | propsOrContext?: string | object, 728 | context?: string, 729 | ): [string, object] { 730 | let messageTpl = messageTemplate; 731 | let props: Record = {}; 732 | 733 | if (propsOrContext) { 734 | if (typeof propsOrContext === 'string') { 735 | props.context = propsOrContext; 736 | } else if (isPlainObject(propsOrContext)) { 737 | props = propsOrContext as Record; 738 | } else { 739 | props.properties = propsOrContext; 740 | } 741 | } 742 | 743 | if (context) { 744 | props.context = context; 745 | } 746 | 747 | if (props.context && !messageTemplate.includes('[{context}]')) { 748 | messageTpl = `[{context}] ${messageTemplate}`; 749 | } 750 | 751 | return [messageTpl, props]; 752 | } 753 | 754 | /** 755 | * Prepares properties for logging by extracting stack trace and other metadata. 756 | * This method separates the stack trace from the properties if present and ensures all other properties are serialized safely. 757 | * Added by Jason.Song (成长的小猪) on 2024/04/17 23:12:28 758 | * 759 | * @param properties Optional properties object that may contain a stack trace and other logging metadata. 760 | * @returns An object containing sanitized properties and an optional stack trace. 761 | */ 762 | private prepareProperties(properties?: Record): { 763 | props: Record; 764 | stack: string | undefined; 765 | } { 766 | const { stack, logger = 'seq', ...props } = properties || {}; 767 | const metaFieldName = this.seqLogger.options.metaFieldName; 768 | return { 769 | props: { 770 | ...props, 771 | [metaFieldName]: { 772 | ...this.seqLogger.metaProperties, 773 | logger, 774 | }, 775 | }, 776 | stack, 777 | }; 778 | } 779 | 780 | /** 781 | * Commits the log message to the Seq logger. 782 | * Updated by Jason.Song (成长的小猪) on 2023/11/22 19:20:23 783 | * @param level The log level. 784 | * @param messageTemplate The message template to log. 785 | * @param properties (Optional) Additional properties to log. 786 | */ 787 | private commit( 788 | level: LogLevel, 789 | messageTemplate: string, 790 | properties?: Record, 791 | ): void { 792 | const { props, stack } = this.prepareProperties(properties); 793 | try { 794 | const seqEvent: SeqEvent = { 795 | Timestamp: new Date(), 796 | Level: SeqLogger.levelMapping[level] || SeqLevel.Information, 797 | MessageTemplate: messageTemplate, 798 | Properties: props, 799 | Exception: stack, 800 | }; 801 | this.seqLogger.emit(seqEvent); 802 | } catch (error) { 803 | logToConsole( 804 | 'error', 805 | error instanceof Error ? error.message : String(error), 806 | ); 807 | } 808 | } 809 | 810 | /** 811 | * Handles the application shutdown event. 812 | * Updated by Jason.Song (成长的小猪) on 2023/11/22 19:20:40 813 | * Added by Jason.Song (成长的小猪) on 2023/01/11 14:14:13 814 | * @param signal The signal received for the shutdown. 815 | */ 816 | async onApplicationShutdown(signal?: string): Promise { 817 | await this.seqLogger.close(); 818 | if (signal) { 819 | logToConsole('log', `Seq logger closed by ${signal} signal`); 820 | } 821 | } 822 | } 823 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | axios: 12 | specifier: ^1.3.1 13 | version: 1.11.0 14 | devDependencies: 15 | '@nestjs/common': 16 | specifier: 11.1.6 17 | version: 11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2) 18 | '@types/node': 19 | specifier: 22.18.0 20 | version: 22.18.0 21 | '@typescript-eslint/eslint-plugin': 22 | specifier: 7.5.0 23 | version: 7.5.0(@typescript-eslint/parser@7.5.0(eslint@8.57.0)(typescript@5.9.2))(eslint@8.57.0)(typescript@5.9.2) 24 | '@typescript-eslint/parser': 25 | specifier: 7.5.0 26 | version: 7.5.0(eslint@8.57.0)(typescript@5.9.2) 27 | eslint: 28 | specifier: 8.57.0 29 | version: 8.57.0 30 | eslint-config-prettier: 31 | specifier: 9.1.0 32 | version: 9.1.0(eslint@8.57.0) 33 | eslint-plugin-prettier: 34 | specifier: 5.1.3 35 | version: 5.1.3(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.2.5) 36 | prettier: 37 | specifier: 3.2.5 38 | version: 3.2.5 39 | rimraf: 40 | specifier: 6.0.1 41 | version: 6.0.1 42 | typescript: 43 | specifier: 5.9.2 44 | version: 5.9.2 45 | 46 | packages: 47 | 48 | '@borewit/text-codec@0.1.1': 49 | resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==} 50 | 51 | '@eslint-community/eslint-utils@4.5.1': 52 | resolution: {integrity: sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==} 53 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 54 | peerDependencies: 55 | eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 56 | 57 | '@eslint-community/regexpp@4.12.1': 58 | resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} 59 | engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} 60 | 61 | '@eslint/eslintrc@2.1.4': 62 | resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} 63 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 64 | 65 | '@eslint/js@8.57.0': 66 | resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} 67 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 68 | 69 | '@humanwhocodes/config-array@0.11.14': 70 | resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} 71 | engines: {node: '>=10.10.0'} 72 | deprecated: Use @eslint/config-array instead 73 | 74 | '@humanwhocodes/module-importer@1.0.1': 75 | resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} 76 | engines: {node: '>=12.22'} 77 | 78 | '@humanwhocodes/object-schema@2.0.3': 79 | resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} 80 | deprecated: Use @eslint/object-schema instead 81 | 82 | '@isaacs/balanced-match@4.0.1': 83 | resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} 84 | engines: {node: 20 || >=22} 85 | 86 | '@isaacs/brace-expansion@5.0.0': 87 | resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} 88 | engines: {node: 20 || >=22} 89 | 90 | '@isaacs/cliui@8.0.2': 91 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} 92 | engines: {node: '>=12'} 93 | 94 | '@lukeed/csprng@1.1.0': 95 | resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} 96 | engines: {node: '>=8'} 97 | 98 | '@nestjs/common@11.1.6': 99 | resolution: {integrity: sha512-krKwLLcFmeuKDqngG2N/RuZHCs2ycsKcxWIDgcm7i1lf3sQ0iG03ci+DsP/r3FcT/eJDFsIHnKtNta2LIi7PzQ==} 100 | peerDependencies: 101 | class-transformer: '>=0.4.1' 102 | class-validator: '>=0.13.2' 103 | reflect-metadata: ^0.1.12 || ^0.2.0 104 | rxjs: ^7.1.0 105 | peerDependenciesMeta: 106 | class-transformer: 107 | optional: true 108 | class-validator: 109 | optional: true 110 | 111 | '@nodelib/fs.scandir@2.1.5': 112 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 113 | engines: {node: '>= 8'} 114 | 115 | '@nodelib/fs.stat@2.0.5': 116 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 117 | engines: {node: '>= 8'} 118 | 119 | '@nodelib/fs.walk@1.2.8': 120 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 121 | engines: {node: '>= 8'} 122 | 123 | '@pkgr/core@0.1.2': 124 | resolution: {integrity: sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==} 125 | engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} 126 | 127 | '@tokenizer/inflate@0.2.7': 128 | resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==} 129 | engines: {node: '>=18'} 130 | 131 | '@tokenizer/token@0.3.0': 132 | resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} 133 | 134 | '@types/json-schema@7.0.15': 135 | resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} 136 | 137 | '@types/node@22.18.0': 138 | resolution: {integrity: sha512-m5ObIqwsUp6BZzyiy4RdZpzWGub9bqLJMvZDD0QMXhxjqMHMENlj+SqF5QxoUwaQNFe+8kz8XM8ZQhqkQPTgMQ==} 139 | 140 | '@types/semver@7.5.8': 141 | resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} 142 | 143 | '@typescript-eslint/eslint-plugin@7.5.0': 144 | resolution: {integrity: sha512-HpqNTH8Du34nLxbKgVMGljZMG0rJd2O9ecvr2QLYp+7512ty1j42KnsFwspPXg1Vh8an9YImf6CokUBltisZFQ==} 145 | engines: {node: ^18.18.0 || >=20.0.0} 146 | peerDependencies: 147 | '@typescript-eslint/parser': ^7.0.0 148 | eslint: ^8.56.0 149 | typescript: '*' 150 | peerDependenciesMeta: 151 | typescript: 152 | optional: true 153 | 154 | '@typescript-eslint/parser@7.5.0': 155 | resolution: {integrity: sha512-cj+XGhNujfD2/wzR1tabNsidnYRaFfEkcULdcIyVBYcXjBvBKOes+mpMBP7hMpOyk+gBcfXsrg4NBGAStQyxjQ==} 156 | engines: {node: ^18.18.0 || >=20.0.0} 157 | peerDependencies: 158 | eslint: ^8.56.0 159 | typescript: '*' 160 | peerDependenciesMeta: 161 | typescript: 162 | optional: true 163 | 164 | '@typescript-eslint/scope-manager@7.5.0': 165 | resolution: {integrity: sha512-Z1r7uJY0MDeUlql9XJ6kRVgk/sP11sr3HKXn268HZyqL7i4cEfrdFuSSY/0tUqT37l5zT0tJOsuDP16kio85iA==} 166 | engines: {node: ^18.18.0 || >=20.0.0} 167 | 168 | '@typescript-eslint/type-utils@7.5.0': 169 | resolution: {integrity: sha512-A021Rj33+G8mx2Dqh0nMO9GyjjIBK3MqgVgZ2qlKf6CJy51wY/lkkFqq3TqqnH34XyAHUkq27IjlUkWlQRpLHw==} 170 | engines: {node: ^18.18.0 || >=20.0.0} 171 | peerDependencies: 172 | eslint: ^8.56.0 173 | typescript: '*' 174 | peerDependenciesMeta: 175 | typescript: 176 | optional: true 177 | 178 | '@typescript-eslint/types@7.5.0': 179 | resolution: {integrity: sha512-tv5B4IHeAdhR7uS4+bf8Ov3k793VEVHd45viRRkehIUZxm0WF82VPiLgHzA/Xl4TGPg1ZD49vfxBKFPecD5/mg==} 180 | engines: {node: ^18.18.0 || >=20.0.0} 181 | 182 | '@typescript-eslint/typescript-estree@7.5.0': 183 | resolution: {integrity: sha512-YklQQfe0Rv2PZEueLTUffiQGKQneiIEKKnfIqPIOxgM9lKSZFCjT5Ad4VqRKj/U4+kQE3fa8YQpskViL7WjdPQ==} 184 | engines: {node: ^18.18.0 || >=20.0.0} 185 | peerDependencies: 186 | typescript: '*' 187 | peerDependenciesMeta: 188 | typescript: 189 | optional: true 190 | 191 | '@typescript-eslint/utils@7.5.0': 192 | resolution: {integrity: sha512-3vZl9u0R+/FLQcpy2EHyRGNqAS/ofJ3Ji8aebilfJe+fobK8+LbIFmrHciLVDxjDoONmufDcnVSF38KwMEOjzw==} 193 | engines: {node: ^18.18.0 || >=20.0.0} 194 | peerDependencies: 195 | eslint: ^8.56.0 196 | 197 | '@typescript-eslint/visitor-keys@7.5.0': 198 | resolution: {integrity: sha512-mcuHM/QircmA6O7fy6nn2w/3ditQkj+SgtOc8DW3uQ10Yfj42amm2i+6F2K4YAOPNNTmE6iM1ynM6lrSwdendA==} 199 | engines: {node: ^18.18.0 || >=20.0.0} 200 | 201 | '@ungap/structured-clone@1.3.0': 202 | resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} 203 | 204 | acorn-jsx@5.3.2: 205 | resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} 206 | peerDependencies: 207 | acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 208 | 209 | acorn@8.14.1: 210 | resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} 211 | engines: {node: '>=0.4.0'} 212 | hasBin: true 213 | 214 | ajv@6.12.6: 215 | resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} 216 | 217 | ansi-regex@5.0.1: 218 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 219 | engines: {node: '>=8'} 220 | 221 | ansi-regex@6.1.0: 222 | resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} 223 | engines: {node: '>=12'} 224 | 225 | ansi-styles@4.3.0: 226 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 227 | engines: {node: '>=8'} 228 | 229 | ansi-styles@6.2.1: 230 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} 231 | engines: {node: '>=12'} 232 | 233 | argparse@2.0.1: 234 | resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 235 | 236 | array-union@2.1.0: 237 | resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} 238 | engines: {node: '>=8'} 239 | 240 | asynckit@0.4.0: 241 | resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} 242 | 243 | axios@1.11.0: 244 | resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==} 245 | 246 | balanced-match@1.0.2: 247 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 248 | 249 | brace-expansion@1.1.11: 250 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 251 | 252 | brace-expansion@2.0.1: 253 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 254 | 255 | braces@3.0.3: 256 | resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 257 | engines: {node: '>=8'} 258 | 259 | call-bind-apply-helpers@1.0.2: 260 | resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} 261 | engines: {node: '>= 0.4'} 262 | 263 | callsites@3.1.0: 264 | resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} 265 | engines: {node: '>=6'} 266 | 267 | chalk@4.1.2: 268 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 269 | engines: {node: '>=10'} 270 | 271 | color-convert@2.0.1: 272 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 273 | engines: {node: '>=7.0.0'} 274 | 275 | color-name@1.1.4: 276 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 277 | 278 | combined-stream@1.0.8: 279 | resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} 280 | engines: {node: '>= 0.8'} 281 | 282 | concat-map@0.0.1: 283 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 284 | 285 | cross-spawn@7.0.6: 286 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 287 | engines: {node: '>= 8'} 288 | 289 | debug@4.4.0: 290 | resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} 291 | engines: {node: '>=6.0'} 292 | peerDependencies: 293 | supports-color: '*' 294 | peerDependenciesMeta: 295 | supports-color: 296 | optional: true 297 | 298 | deep-is@0.1.4: 299 | resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} 300 | 301 | delayed-stream@1.0.0: 302 | resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} 303 | engines: {node: '>=0.4.0'} 304 | 305 | dir-glob@3.0.1: 306 | resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} 307 | engines: {node: '>=8'} 308 | 309 | doctrine@3.0.0: 310 | resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} 311 | engines: {node: '>=6.0.0'} 312 | 313 | dunder-proto@1.0.1: 314 | resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} 315 | engines: {node: '>= 0.4'} 316 | 317 | eastasianwidth@0.2.0: 318 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 319 | 320 | emoji-regex@8.0.0: 321 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 322 | 323 | emoji-regex@9.2.2: 324 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 325 | 326 | es-define-property@1.0.1: 327 | resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} 328 | engines: {node: '>= 0.4'} 329 | 330 | es-errors@1.3.0: 331 | resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} 332 | engines: {node: '>= 0.4'} 333 | 334 | es-object-atoms@1.1.1: 335 | resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} 336 | engines: {node: '>= 0.4'} 337 | 338 | es-set-tostringtag@2.1.0: 339 | resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} 340 | engines: {node: '>= 0.4'} 341 | 342 | escape-string-regexp@4.0.0: 343 | resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 344 | engines: {node: '>=10'} 345 | 346 | eslint-config-prettier@9.1.0: 347 | resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} 348 | hasBin: true 349 | peerDependencies: 350 | eslint: '>=7.0.0' 351 | 352 | eslint-plugin-prettier@5.1.3: 353 | resolution: {integrity: sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==} 354 | engines: {node: ^14.18.0 || >=16.0.0} 355 | peerDependencies: 356 | '@types/eslint': '>=8.0.0' 357 | eslint: '>=8.0.0' 358 | eslint-config-prettier: '*' 359 | prettier: '>=3.0.0' 360 | peerDependenciesMeta: 361 | '@types/eslint': 362 | optional: true 363 | eslint-config-prettier: 364 | optional: true 365 | 366 | eslint-scope@7.2.2: 367 | resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} 368 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 369 | 370 | eslint-visitor-keys@3.4.3: 371 | resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} 372 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 373 | 374 | eslint@8.57.0: 375 | resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} 376 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 377 | deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. 378 | hasBin: true 379 | 380 | espree@9.6.1: 381 | resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} 382 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 383 | 384 | esquery@1.6.0: 385 | resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} 386 | engines: {node: '>=0.10'} 387 | 388 | esrecurse@4.3.0: 389 | resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} 390 | engines: {node: '>=4.0'} 391 | 392 | estraverse@5.3.0: 393 | resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} 394 | engines: {node: '>=4.0'} 395 | 396 | esutils@2.0.3: 397 | resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} 398 | engines: {node: '>=0.10.0'} 399 | 400 | fast-deep-equal@3.1.3: 401 | resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 402 | 403 | fast-diff@1.3.0: 404 | resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} 405 | 406 | fast-glob@3.3.3: 407 | resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} 408 | engines: {node: '>=8.6.0'} 409 | 410 | fast-json-stable-stringify@2.1.0: 411 | resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} 412 | 413 | fast-levenshtein@2.0.6: 414 | resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} 415 | 416 | fastq@1.19.1: 417 | resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} 418 | 419 | fflate@0.8.2: 420 | resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} 421 | 422 | file-entry-cache@6.0.1: 423 | resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} 424 | engines: {node: ^10.12.0 || >=12.0.0} 425 | 426 | file-type@21.0.0: 427 | resolution: {integrity: sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg==} 428 | engines: {node: '>=20'} 429 | 430 | fill-range@7.1.1: 431 | resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 432 | engines: {node: '>=8'} 433 | 434 | find-up@5.0.0: 435 | resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 436 | engines: {node: '>=10'} 437 | 438 | flat-cache@3.2.0: 439 | resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} 440 | engines: {node: ^10.12.0 || >=12.0.0} 441 | 442 | flatted@3.3.3: 443 | resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} 444 | 445 | follow-redirects@1.15.9: 446 | resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} 447 | engines: {node: '>=4.0'} 448 | peerDependencies: 449 | debug: '*' 450 | peerDependenciesMeta: 451 | debug: 452 | optional: true 453 | 454 | foreground-child@3.3.1: 455 | resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} 456 | engines: {node: '>=14'} 457 | 458 | form-data@4.0.4: 459 | resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} 460 | engines: {node: '>= 6'} 461 | 462 | fs.realpath@1.0.0: 463 | resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} 464 | 465 | function-bind@1.1.2: 466 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 467 | 468 | get-intrinsic@1.3.0: 469 | resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} 470 | engines: {node: '>= 0.4'} 471 | 472 | get-proto@1.0.1: 473 | resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} 474 | engines: {node: '>= 0.4'} 475 | 476 | glob-parent@5.1.2: 477 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 478 | engines: {node: '>= 6'} 479 | 480 | glob-parent@6.0.2: 481 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 482 | engines: {node: '>=10.13.0'} 483 | 484 | glob@11.0.3: 485 | resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==} 486 | engines: {node: 20 || >=22} 487 | hasBin: true 488 | 489 | glob@7.2.3: 490 | resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} 491 | deprecated: Glob versions prior to v9 are no longer supported 492 | 493 | globals@13.24.0: 494 | resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} 495 | engines: {node: '>=8'} 496 | 497 | globby@11.1.0: 498 | resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} 499 | engines: {node: '>=10'} 500 | 501 | gopd@1.2.0: 502 | resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} 503 | engines: {node: '>= 0.4'} 504 | 505 | graphemer@1.4.0: 506 | resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} 507 | 508 | has-flag@4.0.0: 509 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 510 | engines: {node: '>=8'} 511 | 512 | has-symbols@1.1.0: 513 | resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} 514 | engines: {node: '>= 0.4'} 515 | 516 | has-tostringtag@1.0.2: 517 | resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} 518 | engines: {node: '>= 0.4'} 519 | 520 | hasown@2.0.2: 521 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 522 | engines: {node: '>= 0.4'} 523 | 524 | ieee754@1.2.1: 525 | resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} 526 | 527 | ignore@5.3.2: 528 | resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} 529 | engines: {node: '>= 4'} 530 | 531 | import-fresh@3.3.1: 532 | resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} 533 | engines: {node: '>=6'} 534 | 535 | imurmurhash@0.1.4: 536 | resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} 537 | engines: {node: '>=0.8.19'} 538 | 539 | inflight@1.0.6: 540 | resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} 541 | 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. 542 | 543 | inherits@2.0.4: 544 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 545 | 546 | is-extglob@2.1.1: 547 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 548 | engines: {node: '>=0.10.0'} 549 | 550 | is-fullwidth-code-point@3.0.0: 551 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 552 | engines: {node: '>=8'} 553 | 554 | is-glob@4.0.3: 555 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 556 | engines: {node: '>=0.10.0'} 557 | 558 | is-number@7.0.0: 559 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 560 | engines: {node: '>=0.12.0'} 561 | 562 | is-path-inside@3.0.3: 563 | resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} 564 | engines: {node: '>=8'} 565 | 566 | isexe@2.0.0: 567 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 568 | 569 | iterare@1.2.1: 570 | resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} 571 | engines: {node: '>=6'} 572 | 573 | jackspeak@4.1.1: 574 | resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} 575 | engines: {node: 20 || >=22} 576 | 577 | js-yaml@4.1.0: 578 | resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} 579 | hasBin: true 580 | 581 | json-buffer@3.0.1: 582 | resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} 583 | 584 | json-schema-traverse@0.4.1: 585 | resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} 586 | 587 | json-stable-stringify-without-jsonify@1.0.1: 588 | resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} 589 | 590 | keyv@4.5.4: 591 | resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} 592 | 593 | levn@0.4.1: 594 | resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} 595 | engines: {node: '>= 0.8.0'} 596 | 597 | load-esm@1.0.2: 598 | resolution: {integrity: sha512-nVAvWk/jeyrWyXEAs84mpQCYccxRqgKY4OznLuJhJCa0XsPSfdOIr2zvBZEj3IHEHbX97jjscKRRV539bW0Gpw==} 599 | engines: {node: '>=13.2.0'} 600 | 601 | locate-path@6.0.0: 602 | resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 603 | engines: {node: '>=10'} 604 | 605 | lodash.merge@4.6.2: 606 | resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} 607 | 608 | lru-cache@11.1.0: 609 | resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} 610 | engines: {node: 20 || >=22} 611 | 612 | math-intrinsics@1.1.0: 613 | resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} 614 | engines: {node: '>= 0.4'} 615 | 616 | merge2@1.4.1: 617 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 618 | engines: {node: '>= 8'} 619 | 620 | micromatch@4.0.8: 621 | resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} 622 | engines: {node: '>=8.6'} 623 | 624 | mime-db@1.52.0: 625 | resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} 626 | engines: {node: '>= 0.6'} 627 | 628 | mime-types@2.1.35: 629 | resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} 630 | engines: {node: '>= 0.6'} 631 | 632 | minimatch@10.0.3: 633 | resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} 634 | engines: {node: 20 || >=22} 635 | 636 | minimatch@3.1.2: 637 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 638 | 639 | minimatch@9.0.3: 640 | resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} 641 | engines: {node: '>=16 || 14 >=14.17'} 642 | 643 | minipass@7.1.2: 644 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 645 | engines: {node: '>=16 || 14 >=14.17'} 646 | 647 | ms@2.1.3: 648 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 649 | 650 | natural-compare@1.4.0: 651 | resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} 652 | 653 | once@1.4.0: 654 | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} 655 | 656 | optionator@0.9.4: 657 | resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} 658 | engines: {node: '>= 0.8.0'} 659 | 660 | p-limit@3.1.0: 661 | resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 662 | engines: {node: '>=10'} 663 | 664 | p-locate@5.0.0: 665 | resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 666 | engines: {node: '>=10'} 667 | 668 | package-json-from-dist@1.0.1: 669 | resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} 670 | 671 | parent-module@1.0.1: 672 | resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} 673 | engines: {node: '>=6'} 674 | 675 | path-exists@4.0.0: 676 | resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 677 | engines: {node: '>=8'} 678 | 679 | path-is-absolute@1.0.1: 680 | resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} 681 | engines: {node: '>=0.10.0'} 682 | 683 | path-key@3.1.1: 684 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 685 | engines: {node: '>=8'} 686 | 687 | path-scurry@2.0.0: 688 | resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} 689 | engines: {node: 20 || >=22} 690 | 691 | path-type@4.0.0: 692 | resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} 693 | engines: {node: '>=8'} 694 | 695 | picomatch@2.3.1: 696 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 697 | engines: {node: '>=8.6'} 698 | 699 | prelude-ls@1.2.1: 700 | resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 701 | engines: {node: '>= 0.8.0'} 702 | 703 | prettier-linter-helpers@1.0.0: 704 | resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} 705 | engines: {node: '>=6.0.0'} 706 | 707 | prettier@3.2.5: 708 | resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} 709 | engines: {node: '>=14'} 710 | hasBin: true 711 | 712 | proxy-from-env@1.1.0: 713 | resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} 714 | 715 | punycode@2.3.1: 716 | resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} 717 | engines: {node: '>=6'} 718 | 719 | queue-microtask@1.2.3: 720 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 721 | 722 | reflect-metadata@0.2.2: 723 | resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} 724 | 725 | resolve-from@4.0.0: 726 | resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} 727 | engines: {node: '>=4'} 728 | 729 | reusify@1.1.0: 730 | resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} 731 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 732 | 733 | rimraf@3.0.2: 734 | resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} 735 | deprecated: Rimraf versions prior to v4 are no longer supported 736 | hasBin: true 737 | 738 | rimraf@6.0.1: 739 | resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==} 740 | engines: {node: 20 || >=22} 741 | hasBin: true 742 | 743 | run-parallel@1.2.0: 744 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 745 | 746 | rxjs@7.8.2: 747 | resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} 748 | 749 | semver@7.7.1: 750 | resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} 751 | engines: {node: '>=10'} 752 | hasBin: true 753 | 754 | shebang-command@2.0.0: 755 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 756 | engines: {node: '>=8'} 757 | 758 | shebang-regex@3.0.0: 759 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 760 | engines: {node: '>=8'} 761 | 762 | signal-exit@4.1.0: 763 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 764 | engines: {node: '>=14'} 765 | 766 | slash@3.0.0: 767 | resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} 768 | engines: {node: '>=8'} 769 | 770 | string-width@4.2.3: 771 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 772 | engines: {node: '>=8'} 773 | 774 | string-width@5.1.2: 775 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 776 | engines: {node: '>=12'} 777 | 778 | strip-ansi@6.0.1: 779 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 780 | engines: {node: '>=8'} 781 | 782 | strip-ansi@7.1.0: 783 | resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} 784 | engines: {node: '>=12'} 785 | 786 | strip-json-comments@3.1.1: 787 | resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 788 | engines: {node: '>=8'} 789 | 790 | strtok3@10.3.4: 791 | resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} 792 | engines: {node: '>=18'} 793 | 794 | supports-color@7.2.0: 795 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 796 | engines: {node: '>=8'} 797 | 798 | synckit@0.8.8: 799 | resolution: {integrity: sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==} 800 | engines: {node: ^14.18.0 || >=16.0.0} 801 | 802 | text-table@0.2.0: 803 | resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} 804 | 805 | to-regex-range@5.0.1: 806 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 807 | engines: {node: '>=8.0'} 808 | 809 | token-types@6.1.1: 810 | resolution: {integrity: sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==} 811 | engines: {node: '>=14.16'} 812 | 813 | ts-api-utils@1.4.3: 814 | resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} 815 | engines: {node: '>=16'} 816 | peerDependencies: 817 | typescript: '>=4.2.0' 818 | 819 | tslib@2.8.1: 820 | resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 821 | 822 | type-check@0.4.0: 823 | resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} 824 | engines: {node: '>= 0.8.0'} 825 | 826 | type-fest@0.20.2: 827 | resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} 828 | engines: {node: '>=10'} 829 | 830 | typescript@5.9.2: 831 | resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} 832 | engines: {node: '>=14.17'} 833 | hasBin: true 834 | 835 | uid@2.0.2: 836 | resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} 837 | engines: {node: '>=8'} 838 | 839 | uint8array-extras@1.5.0: 840 | resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} 841 | engines: {node: '>=18'} 842 | 843 | undici-types@6.21.0: 844 | resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} 845 | 846 | uri-js@4.4.1: 847 | resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 848 | 849 | which@2.0.2: 850 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 851 | engines: {node: '>= 8'} 852 | hasBin: true 853 | 854 | word-wrap@1.2.5: 855 | resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} 856 | engines: {node: '>=0.10.0'} 857 | 858 | wrap-ansi@7.0.0: 859 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 860 | engines: {node: '>=10'} 861 | 862 | wrap-ansi@8.1.0: 863 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} 864 | engines: {node: '>=12'} 865 | 866 | wrappy@1.0.2: 867 | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 868 | 869 | yocto-queue@0.1.0: 870 | resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 871 | engines: {node: '>=10'} 872 | 873 | snapshots: 874 | 875 | '@borewit/text-codec@0.1.1': {} 876 | 877 | '@eslint-community/eslint-utils@4.5.1(eslint@8.57.0)': 878 | dependencies: 879 | eslint: 8.57.0 880 | eslint-visitor-keys: 3.4.3 881 | 882 | '@eslint-community/regexpp@4.12.1': {} 883 | 884 | '@eslint/eslintrc@2.1.4': 885 | dependencies: 886 | ajv: 6.12.6 887 | debug: 4.4.0 888 | espree: 9.6.1 889 | globals: 13.24.0 890 | ignore: 5.3.2 891 | import-fresh: 3.3.1 892 | js-yaml: 4.1.0 893 | minimatch: 3.1.2 894 | strip-json-comments: 3.1.1 895 | transitivePeerDependencies: 896 | - supports-color 897 | 898 | '@eslint/js@8.57.0': {} 899 | 900 | '@humanwhocodes/config-array@0.11.14': 901 | dependencies: 902 | '@humanwhocodes/object-schema': 2.0.3 903 | debug: 4.4.0 904 | minimatch: 3.1.2 905 | transitivePeerDependencies: 906 | - supports-color 907 | 908 | '@humanwhocodes/module-importer@1.0.1': {} 909 | 910 | '@humanwhocodes/object-schema@2.0.3': {} 911 | 912 | '@isaacs/balanced-match@4.0.1': {} 913 | 914 | '@isaacs/brace-expansion@5.0.0': 915 | dependencies: 916 | '@isaacs/balanced-match': 4.0.1 917 | 918 | '@isaacs/cliui@8.0.2': 919 | dependencies: 920 | string-width: 5.1.2 921 | string-width-cjs: string-width@4.2.3 922 | strip-ansi: 7.1.0 923 | strip-ansi-cjs: strip-ansi@6.0.1 924 | wrap-ansi: 8.1.0 925 | wrap-ansi-cjs: wrap-ansi@7.0.0 926 | 927 | '@lukeed/csprng@1.1.0': {} 928 | 929 | '@nestjs/common@11.1.6(reflect-metadata@0.2.2)(rxjs@7.8.2)': 930 | dependencies: 931 | file-type: 21.0.0 932 | iterare: 1.2.1 933 | load-esm: 1.0.2 934 | reflect-metadata: 0.2.2 935 | rxjs: 7.8.2 936 | tslib: 2.8.1 937 | uid: 2.0.2 938 | transitivePeerDependencies: 939 | - supports-color 940 | 941 | '@nodelib/fs.scandir@2.1.5': 942 | dependencies: 943 | '@nodelib/fs.stat': 2.0.5 944 | run-parallel: 1.2.0 945 | 946 | '@nodelib/fs.stat@2.0.5': {} 947 | 948 | '@nodelib/fs.walk@1.2.8': 949 | dependencies: 950 | '@nodelib/fs.scandir': 2.1.5 951 | fastq: 1.19.1 952 | 953 | '@pkgr/core@0.1.2': {} 954 | 955 | '@tokenizer/inflate@0.2.7': 956 | dependencies: 957 | debug: 4.4.0 958 | fflate: 0.8.2 959 | token-types: 6.1.1 960 | transitivePeerDependencies: 961 | - supports-color 962 | 963 | '@tokenizer/token@0.3.0': {} 964 | 965 | '@types/json-schema@7.0.15': {} 966 | 967 | '@types/node@22.18.0': 968 | dependencies: 969 | undici-types: 6.21.0 970 | 971 | '@types/semver@7.5.8': {} 972 | 973 | '@typescript-eslint/eslint-plugin@7.5.0(@typescript-eslint/parser@7.5.0(eslint@8.57.0)(typescript@5.9.2))(eslint@8.57.0)(typescript@5.9.2)': 974 | dependencies: 975 | '@eslint-community/regexpp': 4.12.1 976 | '@typescript-eslint/parser': 7.5.0(eslint@8.57.0)(typescript@5.9.2) 977 | '@typescript-eslint/scope-manager': 7.5.0 978 | '@typescript-eslint/type-utils': 7.5.0(eslint@8.57.0)(typescript@5.9.2) 979 | '@typescript-eslint/utils': 7.5.0(eslint@8.57.0)(typescript@5.9.2) 980 | '@typescript-eslint/visitor-keys': 7.5.0 981 | debug: 4.4.0 982 | eslint: 8.57.0 983 | graphemer: 1.4.0 984 | ignore: 5.3.2 985 | natural-compare: 1.4.0 986 | semver: 7.7.1 987 | ts-api-utils: 1.4.3(typescript@5.9.2) 988 | optionalDependencies: 989 | typescript: 5.9.2 990 | transitivePeerDependencies: 991 | - supports-color 992 | 993 | '@typescript-eslint/parser@7.5.0(eslint@8.57.0)(typescript@5.9.2)': 994 | dependencies: 995 | '@typescript-eslint/scope-manager': 7.5.0 996 | '@typescript-eslint/types': 7.5.0 997 | '@typescript-eslint/typescript-estree': 7.5.0(typescript@5.9.2) 998 | '@typescript-eslint/visitor-keys': 7.5.0 999 | debug: 4.4.0 1000 | eslint: 8.57.0 1001 | optionalDependencies: 1002 | typescript: 5.9.2 1003 | transitivePeerDependencies: 1004 | - supports-color 1005 | 1006 | '@typescript-eslint/scope-manager@7.5.0': 1007 | dependencies: 1008 | '@typescript-eslint/types': 7.5.0 1009 | '@typescript-eslint/visitor-keys': 7.5.0 1010 | 1011 | '@typescript-eslint/type-utils@7.5.0(eslint@8.57.0)(typescript@5.9.2)': 1012 | dependencies: 1013 | '@typescript-eslint/typescript-estree': 7.5.0(typescript@5.9.2) 1014 | '@typescript-eslint/utils': 7.5.0(eslint@8.57.0)(typescript@5.9.2) 1015 | debug: 4.4.0 1016 | eslint: 8.57.0 1017 | ts-api-utils: 1.4.3(typescript@5.9.2) 1018 | optionalDependencies: 1019 | typescript: 5.9.2 1020 | transitivePeerDependencies: 1021 | - supports-color 1022 | 1023 | '@typescript-eslint/types@7.5.0': {} 1024 | 1025 | '@typescript-eslint/typescript-estree@7.5.0(typescript@5.9.2)': 1026 | dependencies: 1027 | '@typescript-eslint/types': 7.5.0 1028 | '@typescript-eslint/visitor-keys': 7.5.0 1029 | debug: 4.4.0 1030 | globby: 11.1.0 1031 | is-glob: 4.0.3 1032 | minimatch: 9.0.3 1033 | semver: 7.7.1 1034 | ts-api-utils: 1.4.3(typescript@5.9.2) 1035 | optionalDependencies: 1036 | typescript: 5.9.2 1037 | transitivePeerDependencies: 1038 | - supports-color 1039 | 1040 | '@typescript-eslint/utils@7.5.0(eslint@8.57.0)(typescript@5.9.2)': 1041 | dependencies: 1042 | '@eslint-community/eslint-utils': 4.5.1(eslint@8.57.0) 1043 | '@types/json-schema': 7.0.15 1044 | '@types/semver': 7.5.8 1045 | '@typescript-eslint/scope-manager': 7.5.0 1046 | '@typescript-eslint/types': 7.5.0 1047 | '@typescript-eslint/typescript-estree': 7.5.0(typescript@5.9.2) 1048 | eslint: 8.57.0 1049 | semver: 7.7.1 1050 | transitivePeerDependencies: 1051 | - supports-color 1052 | - typescript 1053 | 1054 | '@typescript-eslint/visitor-keys@7.5.0': 1055 | dependencies: 1056 | '@typescript-eslint/types': 7.5.0 1057 | eslint-visitor-keys: 3.4.3 1058 | 1059 | '@ungap/structured-clone@1.3.0': {} 1060 | 1061 | acorn-jsx@5.3.2(acorn@8.14.1): 1062 | dependencies: 1063 | acorn: 8.14.1 1064 | 1065 | acorn@8.14.1: {} 1066 | 1067 | ajv@6.12.6: 1068 | dependencies: 1069 | fast-deep-equal: 3.1.3 1070 | fast-json-stable-stringify: 2.1.0 1071 | json-schema-traverse: 0.4.1 1072 | uri-js: 4.4.1 1073 | 1074 | ansi-regex@5.0.1: {} 1075 | 1076 | ansi-regex@6.1.0: {} 1077 | 1078 | ansi-styles@4.3.0: 1079 | dependencies: 1080 | color-convert: 2.0.1 1081 | 1082 | ansi-styles@6.2.1: {} 1083 | 1084 | argparse@2.0.1: {} 1085 | 1086 | array-union@2.1.0: {} 1087 | 1088 | asynckit@0.4.0: {} 1089 | 1090 | axios@1.11.0: 1091 | dependencies: 1092 | follow-redirects: 1.15.9 1093 | form-data: 4.0.4 1094 | proxy-from-env: 1.1.0 1095 | transitivePeerDependencies: 1096 | - debug 1097 | 1098 | balanced-match@1.0.2: {} 1099 | 1100 | brace-expansion@1.1.11: 1101 | dependencies: 1102 | balanced-match: 1.0.2 1103 | concat-map: 0.0.1 1104 | 1105 | brace-expansion@2.0.1: 1106 | dependencies: 1107 | balanced-match: 1.0.2 1108 | 1109 | braces@3.0.3: 1110 | dependencies: 1111 | fill-range: 7.1.1 1112 | 1113 | call-bind-apply-helpers@1.0.2: 1114 | dependencies: 1115 | es-errors: 1.3.0 1116 | function-bind: 1.1.2 1117 | 1118 | callsites@3.1.0: {} 1119 | 1120 | chalk@4.1.2: 1121 | dependencies: 1122 | ansi-styles: 4.3.0 1123 | supports-color: 7.2.0 1124 | 1125 | color-convert@2.0.1: 1126 | dependencies: 1127 | color-name: 1.1.4 1128 | 1129 | color-name@1.1.4: {} 1130 | 1131 | combined-stream@1.0.8: 1132 | dependencies: 1133 | delayed-stream: 1.0.0 1134 | 1135 | concat-map@0.0.1: {} 1136 | 1137 | cross-spawn@7.0.6: 1138 | dependencies: 1139 | path-key: 3.1.1 1140 | shebang-command: 2.0.0 1141 | which: 2.0.2 1142 | 1143 | debug@4.4.0: 1144 | dependencies: 1145 | ms: 2.1.3 1146 | 1147 | deep-is@0.1.4: {} 1148 | 1149 | delayed-stream@1.0.0: {} 1150 | 1151 | dir-glob@3.0.1: 1152 | dependencies: 1153 | path-type: 4.0.0 1154 | 1155 | doctrine@3.0.0: 1156 | dependencies: 1157 | esutils: 2.0.3 1158 | 1159 | dunder-proto@1.0.1: 1160 | dependencies: 1161 | call-bind-apply-helpers: 1.0.2 1162 | es-errors: 1.3.0 1163 | gopd: 1.2.0 1164 | 1165 | eastasianwidth@0.2.0: {} 1166 | 1167 | emoji-regex@8.0.0: {} 1168 | 1169 | emoji-regex@9.2.2: {} 1170 | 1171 | es-define-property@1.0.1: {} 1172 | 1173 | es-errors@1.3.0: {} 1174 | 1175 | es-object-atoms@1.1.1: 1176 | dependencies: 1177 | es-errors: 1.3.0 1178 | 1179 | es-set-tostringtag@2.1.0: 1180 | dependencies: 1181 | es-errors: 1.3.0 1182 | get-intrinsic: 1.3.0 1183 | has-tostringtag: 1.0.2 1184 | hasown: 2.0.2 1185 | 1186 | escape-string-regexp@4.0.0: {} 1187 | 1188 | eslint-config-prettier@9.1.0(eslint@8.57.0): 1189 | dependencies: 1190 | eslint: 8.57.0 1191 | 1192 | eslint-plugin-prettier@5.1.3(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.2.5): 1193 | dependencies: 1194 | eslint: 8.57.0 1195 | prettier: 3.2.5 1196 | prettier-linter-helpers: 1.0.0 1197 | synckit: 0.8.8 1198 | optionalDependencies: 1199 | eslint-config-prettier: 9.1.0(eslint@8.57.0) 1200 | 1201 | eslint-scope@7.2.2: 1202 | dependencies: 1203 | esrecurse: 4.3.0 1204 | estraverse: 5.3.0 1205 | 1206 | eslint-visitor-keys@3.4.3: {} 1207 | 1208 | eslint@8.57.0: 1209 | dependencies: 1210 | '@eslint-community/eslint-utils': 4.5.1(eslint@8.57.0) 1211 | '@eslint-community/regexpp': 4.12.1 1212 | '@eslint/eslintrc': 2.1.4 1213 | '@eslint/js': 8.57.0 1214 | '@humanwhocodes/config-array': 0.11.14 1215 | '@humanwhocodes/module-importer': 1.0.1 1216 | '@nodelib/fs.walk': 1.2.8 1217 | '@ungap/structured-clone': 1.3.0 1218 | ajv: 6.12.6 1219 | chalk: 4.1.2 1220 | cross-spawn: 7.0.6 1221 | debug: 4.4.0 1222 | doctrine: 3.0.0 1223 | escape-string-regexp: 4.0.0 1224 | eslint-scope: 7.2.2 1225 | eslint-visitor-keys: 3.4.3 1226 | espree: 9.6.1 1227 | esquery: 1.6.0 1228 | esutils: 2.0.3 1229 | fast-deep-equal: 3.1.3 1230 | file-entry-cache: 6.0.1 1231 | find-up: 5.0.0 1232 | glob-parent: 6.0.2 1233 | globals: 13.24.0 1234 | graphemer: 1.4.0 1235 | ignore: 5.3.2 1236 | imurmurhash: 0.1.4 1237 | is-glob: 4.0.3 1238 | is-path-inside: 3.0.3 1239 | js-yaml: 4.1.0 1240 | json-stable-stringify-without-jsonify: 1.0.1 1241 | levn: 0.4.1 1242 | lodash.merge: 4.6.2 1243 | minimatch: 3.1.2 1244 | natural-compare: 1.4.0 1245 | optionator: 0.9.4 1246 | strip-ansi: 6.0.1 1247 | text-table: 0.2.0 1248 | transitivePeerDependencies: 1249 | - supports-color 1250 | 1251 | espree@9.6.1: 1252 | dependencies: 1253 | acorn: 8.14.1 1254 | acorn-jsx: 5.3.2(acorn@8.14.1) 1255 | eslint-visitor-keys: 3.4.3 1256 | 1257 | esquery@1.6.0: 1258 | dependencies: 1259 | estraverse: 5.3.0 1260 | 1261 | esrecurse@4.3.0: 1262 | dependencies: 1263 | estraverse: 5.3.0 1264 | 1265 | estraverse@5.3.0: {} 1266 | 1267 | esutils@2.0.3: {} 1268 | 1269 | fast-deep-equal@3.1.3: {} 1270 | 1271 | fast-diff@1.3.0: {} 1272 | 1273 | fast-glob@3.3.3: 1274 | dependencies: 1275 | '@nodelib/fs.stat': 2.0.5 1276 | '@nodelib/fs.walk': 1.2.8 1277 | glob-parent: 5.1.2 1278 | merge2: 1.4.1 1279 | micromatch: 4.0.8 1280 | 1281 | fast-json-stable-stringify@2.1.0: {} 1282 | 1283 | fast-levenshtein@2.0.6: {} 1284 | 1285 | fastq@1.19.1: 1286 | dependencies: 1287 | reusify: 1.1.0 1288 | 1289 | fflate@0.8.2: {} 1290 | 1291 | file-entry-cache@6.0.1: 1292 | dependencies: 1293 | flat-cache: 3.2.0 1294 | 1295 | file-type@21.0.0: 1296 | dependencies: 1297 | '@tokenizer/inflate': 0.2.7 1298 | strtok3: 10.3.4 1299 | token-types: 6.1.1 1300 | uint8array-extras: 1.5.0 1301 | transitivePeerDependencies: 1302 | - supports-color 1303 | 1304 | fill-range@7.1.1: 1305 | dependencies: 1306 | to-regex-range: 5.0.1 1307 | 1308 | find-up@5.0.0: 1309 | dependencies: 1310 | locate-path: 6.0.0 1311 | path-exists: 4.0.0 1312 | 1313 | flat-cache@3.2.0: 1314 | dependencies: 1315 | flatted: 3.3.3 1316 | keyv: 4.5.4 1317 | rimraf: 3.0.2 1318 | 1319 | flatted@3.3.3: {} 1320 | 1321 | follow-redirects@1.15.9: {} 1322 | 1323 | foreground-child@3.3.1: 1324 | dependencies: 1325 | cross-spawn: 7.0.6 1326 | signal-exit: 4.1.0 1327 | 1328 | form-data@4.0.4: 1329 | dependencies: 1330 | asynckit: 0.4.0 1331 | combined-stream: 1.0.8 1332 | es-set-tostringtag: 2.1.0 1333 | hasown: 2.0.2 1334 | mime-types: 2.1.35 1335 | 1336 | fs.realpath@1.0.0: {} 1337 | 1338 | function-bind@1.1.2: {} 1339 | 1340 | get-intrinsic@1.3.0: 1341 | dependencies: 1342 | call-bind-apply-helpers: 1.0.2 1343 | es-define-property: 1.0.1 1344 | es-errors: 1.3.0 1345 | es-object-atoms: 1.1.1 1346 | function-bind: 1.1.2 1347 | get-proto: 1.0.1 1348 | gopd: 1.2.0 1349 | has-symbols: 1.1.0 1350 | hasown: 2.0.2 1351 | math-intrinsics: 1.1.0 1352 | 1353 | get-proto@1.0.1: 1354 | dependencies: 1355 | dunder-proto: 1.0.1 1356 | es-object-atoms: 1.1.1 1357 | 1358 | glob-parent@5.1.2: 1359 | dependencies: 1360 | is-glob: 4.0.3 1361 | 1362 | glob-parent@6.0.2: 1363 | dependencies: 1364 | is-glob: 4.0.3 1365 | 1366 | glob@11.0.3: 1367 | dependencies: 1368 | foreground-child: 3.3.1 1369 | jackspeak: 4.1.1 1370 | minimatch: 10.0.3 1371 | minipass: 7.1.2 1372 | package-json-from-dist: 1.0.1 1373 | path-scurry: 2.0.0 1374 | 1375 | glob@7.2.3: 1376 | dependencies: 1377 | fs.realpath: 1.0.0 1378 | inflight: 1.0.6 1379 | inherits: 2.0.4 1380 | minimatch: 3.1.2 1381 | once: 1.4.0 1382 | path-is-absolute: 1.0.1 1383 | 1384 | globals@13.24.0: 1385 | dependencies: 1386 | type-fest: 0.20.2 1387 | 1388 | globby@11.1.0: 1389 | dependencies: 1390 | array-union: 2.1.0 1391 | dir-glob: 3.0.1 1392 | fast-glob: 3.3.3 1393 | ignore: 5.3.2 1394 | merge2: 1.4.1 1395 | slash: 3.0.0 1396 | 1397 | gopd@1.2.0: {} 1398 | 1399 | graphemer@1.4.0: {} 1400 | 1401 | has-flag@4.0.0: {} 1402 | 1403 | has-symbols@1.1.0: {} 1404 | 1405 | has-tostringtag@1.0.2: 1406 | dependencies: 1407 | has-symbols: 1.1.0 1408 | 1409 | hasown@2.0.2: 1410 | dependencies: 1411 | function-bind: 1.1.2 1412 | 1413 | ieee754@1.2.1: {} 1414 | 1415 | ignore@5.3.2: {} 1416 | 1417 | import-fresh@3.3.1: 1418 | dependencies: 1419 | parent-module: 1.0.1 1420 | resolve-from: 4.0.0 1421 | 1422 | imurmurhash@0.1.4: {} 1423 | 1424 | inflight@1.0.6: 1425 | dependencies: 1426 | once: 1.4.0 1427 | wrappy: 1.0.2 1428 | 1429 | inherits@2.0.4: {} 1430 | 1431 | is-extglob@2.1.1: {} 1432 | 1433 | is-fullwidth-code-point@3.0.0: {} 1434 | 1435 | is-glob@4.0.3: 1436 | dependencies: 1437 | is-extglob: 2.1.1 1438 | 1439 | is-number@7.0.0: {} 1440 | 1441 | is-path-inside@3.0.3: {} 1442 | 1443 | isexe@2.0.0: {} 1444 | 1445 | iterare@1.2.1: {} 1446 | 1447 | jackspeak@4.1.1: 1448 | dependencies: 1449 | '@isaacs/cliui': 8.0.2 1450 | 1451 | js-yaml@4.1.0: 1452 | dependencies: 1453 | argparse: 2.0.1 1454 | 1455 | json-buffer@3.0.1: {} 1456 | 1457 | json-schema-traverse@0.4.1: {} 1458 | 1459 | json-stable-stringify-without-jsonify@1.0.1: {} 1460 | 1461 | keyv@4.5.4: 1462 | dependencies: 1463 | json-buffer: 3.0.1 1464 | 1465 | levn@0.4.1: 1466 | dependencies: 1467 | prelude-ls: 1.2.1 1468 | type-check: 0.4.0 1469 | 1470 | load-esm@1.0.2: {} 1471 | 1472 | locate-path@6.0.0: 1473 | dependencies: 1474 | p-locate: 5.0.0 1475 | 1476 | lodash.merge@4.6.2: {} 1477 | 1478 | lru-cache@11.1.0: {} 1479 | 1480 | math-intrinsics@1.1.0: {} 1481 | 1482 | merge2@1.4.1: {} 1483 | 1484 | micromatch@4.0.8: 1485 | dependencies: 1486 | braces: 3.0.3 1487 | picomatch: 2.3.1 1488 | 1489 | mime-db@1.52.0: {} 1490 | 1491 | mime-types@2.1.35: 1492 | dependencies: 1493 | mime-db: 1.52.0 1494 | 1495 | minimatch@10.0.3: 1496 | dependencies: 1497 | '@isaacs/brace-expansion': 5.0.0 1498 | 1499 | minimatch@3.1.2: 1500 | dependencies: 1501 | brace-expansion: 1.1.11 1502 | 1503 | minimatch@9.0.3: 1504 | dependencies: 1505 | brace-expansion: 2.0.1 1506 | 1507 | minipass@7.1.2: {} 1508 | 1509 | ms@2.1.3: {} 1510 | 1511 | natural-compare@1.4.0: {} 1512 | 1513 | once@1.4.0: 1514 | dependencies: 1515 | wrappy: 1.0.2 1516 | 1517 | optionator@0.9.4: 1518 | dependencies: 1519 | deep-is: 0.1.4 1520 | fast-levenshtein: 2.0.6 1521 | levn: 0.4.1 1522 | prelude-ls: 1.2.1 1523 | type-check: 0.4.0 1524 | word-wrap: 1.2.5 1525 | 1526 | p-limit@3.1.0: 1527 | dependencies: 1528 | yocto-queue: 0.1.0 1529 | 1530 | p-locate@5.0.0: 1531 | dependencies: 1532 | p-limit: 3.1.0 1533 | 1534 | package-json-from-dist@1.0.1: {} 1535 | 1536 | parent-module@1.0.1: 1537 | dependencies: 1538 | callsites: 3.1.0 1539 | 1540 | path-exists@4.0.0: {} 1541 | 1542 | path-is-absolute@1.0.1: {} 1543 | 1544 | path-key@3.1.1: {} 1545 | 1546 | path-scurry@2.0.0: 1547 | dependencies: 1548 | lru-cache: 11.1.0 1549 | minipass: 7.1.2 1550 | 1551 | path-type@4.0.0: {} 1552 | 1553 | picomatch@2.3.1: {} 1554 | 1555 | prelude-ls@1.2.1: {} 1556 | 1557 | prettier-linter-helpers@1.0.0: 1558 | dependencies: 1559 | fast-diff: 1.3.0 1560 | 1561 | prettier@3.2.5: {} 1562 | 1563 | proxy-from-env@1.1.0: {} 1564 | 1565 | punycode@2.3.1: {} 1566 | 1567 | queue-microtask@1.2.3: {} 1568 | 1569 | reflect-metadata@0.2.2: {} 1570 | 1571 | resolve-from@4.0.0: {} 1572 | 1573 | reusify@1.1.0: {} 1574 | 1575 | rimraf@3.0.2: 1576 | dependencies: 1577 | glob: 7.2.3 1578 | 1579 | rimraf@6.0.1: 1580 | dependencies: 1581 | glob: 11.0.3 1582 | package-json-from-dist: 1.0.1 1583 | 1584 | run-parallel@1.2.0: 1585 | dependencies: 1586 | queue-microtask: 1.2.3 1587 | 1588 | rxjs@7.8.2: 1589 | dependencies: 1590 | tslib: 2.8.1 1591 | 1592 | semver@7.7.1: {} 1593 | 1594 | shebang-command@2.0.0: 1595 | dependencies: 1596 | shebang-regex: 3.0.0 1597 | 1598 | shebang-regex@3.0.0: {} 1599 | 1600 | signal-exit@4.1.0: {} 1601 | 1602 | slash@3.0.0: {} 1603 | 1604 | string-width@4.2.3: 1605 | dependencies: 1606 | emoji-regex: 8.0.0 1607 | is-fullwidth-code-point: 3.0.0 1608 | strip-ansi: 6.0.1 1609 | 1610 | string-width@5.1.2: 1611 | dependencies: 1612 | eastasianwidth: 0.2.0 1613 | emoji-regex: 9.2.2 1614 | strip-ansi: 7.1.0 1615 | 1616 | strip-ansi@6.0.1: 1617 | dependencies: 1618 | ansi-regex: 5.0.1 1619 | 1620 | strip-ansi@7.1.0: 1621 | dependencies: 1622 | ansi-regex: 6.1.0 1623 | 1624 | strip-json-comments@3.1.1: {} 1625 | 1626 | strtok3@10.3.4: 1627 | dependencies: 1628 | '@tokenizer/token': 0.3.0 1629 | 1630 | supports-color@7.2.0: 1631 | dependencies: 1632 | has-flag: 4.0.0 1633 | 1634 | synckit@0.8.8: 1635 | dependencies: 1636 | '@pkgr/core': 0.1.2 1637 | tslib: 2.8.1 1638 | 1639 | text-table@0.2.0: {} 1640 | 1641 | to-regex-range@5.0.1: 1642 | dependencies: 1643 | is-number: 7.0.0 1644 | 1645 | token-types@6.1.1: 1646 | dependencies: 1647 | '@borewit/text-codec': 0.1.1 1648 | '@tokenizer/token': 0.3.0 1649 | ieee754: 1.2.1 1650 | 1651 | ts-api-utils@1.4.3(typescript@5.9.2): 1652 | dependencies: 1653 | typescript: 5.9.2 1654 | 1655 | tslib@2.8.1: {} 1656 | 1657 | type-check@0.4.0: 1658 | dependencies: 1659 | prelude-ls: 1.2.1 1660 | 1661 | type-fest@0.20.2: {} 1662 | 1663 | typescript@5.9.2: {} 1664 | 1665 | uid@2.0.2: 1666 | dependencies: 1667 | '@lukeed/csprng': 1.1.0 1668 | 1669 | uint8array-extras@1.5.0: {} 1670 | 1671 | undici-types@6.21.0: {} 1672 | 1673 | uri-js@4.4.1: 1674 | dependencies: 1675 | punycode: 2.3.1 1676 | 1677 | which@2.0.2: 1678 | dependencies: 1679 | isexe: 2.0.0 1680 | 1681 | word-wrap@1.2.5: {} 1682 | 1683 | wrap-ansi@7.0.0: 1684 | dependencies: 1685 | ansi-styles: 4.3.0 1686 | string-width: 4.2.3 1687 | strip-ansi: 6.0.1 1688 | 1689 | wrap-ansi@8.1.0: 1690 | dependencies: 1691 | ansi-styles: 6.2.1 1692 | string-width: 5.1.2 1693 | strip-ansi: 7.1.0 1694 | 1695 | wrappy@1.0.2: {} 1696 | 1697 | yocto-queue@0.1.0: {} 1698 | --------------------------------------------------------------------------------