@koderlabs/tasks-sdk-nestjs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +179 -0
- package/README.md +9 -0
- package/dist/index.cjs +1030 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +283 -0
- package/dist/index.d.ts +283 -0
- package/dist/index.js +997 -0
- package/dist/index.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/module.ts","../src/reporter.service.ts","../src/types.ts","../src/breadcrumb-ring.ts","../src/exception.filter.ts","../src/report.interceptor.ts","../src/request-context.middleware.ts","../src/span.interceptor.ts","../src/sanitize.ts","../src/wrap-sentinel.ts","../src/http-tracing.ts","../src/console-capture.ts","../src/typeorm-tracing.ts"],"sourcesContent":["import {\n DynamicModule,\n Inject,\n MiddlewareConsumer,\n Module,\n type NestModule,\n type OnModuleDestroy,\n type OnModuleInit,\n Provider,\n} from '@nestjs/common';\nimport { APP_INTERCEPTOR } from '@nestjs/core';\n\nimport { InstantTasksReporter } from './reporter.service';\nimport { InstantTasksExceptionFilter } from './exception.filter';\nimport { InstantTasksReportInterceptor } from './report.interceptor';\nimport { InstantTasksRequestContextMiddleware } from './request-context.middleware';\nimport { InstantTasksSpanInterceptor } from './span.interceptor';\nimport { installHttpTracing } from './http-tracing';\nimport { installConsoleCapture } from './console-capture';\nimport { installTypeOrmTracing } from './typeorm-tracing';\nimport { INSTANTTASKS_OPTIONS, type InstantTasksOptions } from './types';\n\n/**\n * Lifecycle helper — owns the global side-effects (fetch/console/typeorm\n * patches) so they can be installed once and reliably torn down on module\n * destroy. Without this the patches would leak across hot reloads /\n * multiple `forRoot()` calls.\n */\nclass InstantTasksLifecycle implements OnModuleInit, OnModuleDestroy {\n private restorers: Array<() => void> = [];\n\n constructor(@Inject(INSTANTTASKS_OPTIONS) private readonly opts: InstantTasksOptions) {}\n\n onModuleInit() {\n if (this.opts.captureHttp) {\n // Auto-ignore our own ingest endpoint to prevent recursion.\n const ignoreUrls = [\n ...(this.opts.ignoreUrls ?? []),\n `${(this.opts.apiUrl ?? '').replace(/\\/+$/, '').replace(/\\/api\\/v\\d+$/, '')}/api/v1/sdk/`,\n ];\n const { restore } = installHttpTracing({ ignoreUrls });\n this.restorers.push(restore);\n }\n if (this.opts.captureConsole) {\n const { restore } = installConsoleCapture();\n this.restorers.push(restore);\n }\n if (this.opts.captureTypeOrm && this.opts.dataSource) {\n installTypeOrmTracing(this.opts.dataSource);\n }\n }\n\n onModuleDestroy() {\n for (const r of this.restorers) {\n try { r(); } catch { /* swallow */ }\n }\n this.restorers = [];\n }\n}\n\n@Module({})\nexport class InstantTasksModule implements NestModule {\n static forRoot(options: InstantTasksOptions): DynamicModule {\n return InstantTasksModule.build({ provide: INSTANTTASKS_OPTIONS, useValue: options }, options);\n }\n\n static forRootAsync(opts: {\n imports?: any[];\n inject?: any[];\n useFactory: (...args: any[]) => InstantTasksOptions | Promise<InstantTasksOptions>;\n }): DynamicModule {\n // Async options unknown at build time — register all conditional providers\n // and let the lifecycle helper read flags at init.\n const optsProvider: Provider = {\n provide: INSTANTTASKS_OPTIONS,\n useFactory: opts.useFactory,\n inject: opts.inject ?? [],\n };\n const conditional: Provider[] = [\n // Span interceptor self-checks for context — safe to always register.\n { provide: APP_INTERCEPTOR, useClass: InstantTasksSpanInterceptor },\n ];\n return {\n module: InstantTasksModule,\n imports: opts.imports ?? [],\n providers: [\n optsProvider,\n InstantTasksReporter,\n InstantTasksExceptionFilter,\n InstantTasksReportInterceptor,\n InstantTasksRequestContextMiddleware,\n InstantTasksLifecycle,\n ...conditional,\n ],\n exports: [InstantTasksReporter, InstantTasksExceptionFilter, InstantTasksReportInterceptor],\n global: true,\n };\n }\n\n private static build(optsProvider: Provider, options: InstantTasksOptions): DynamicModule {\n const conditional: Provider[] = [];\n if (options.captureSpans) {\n conditional.push({ provide: APP_INTERCEPTOR, useClass: InstantTasksSpanInterceptor });\n }\n return {\n module: InstantTasksModule,\n providers: [\n optsProvider,\n InstantTasksReporter,\n InstantTasksExceptionFilter,\n InstantTasksReportInterceptor,\n InstantTasksRequestContextMiddleware,\n InstantTasksLifecycle,\n ...conditional,\n ],\n exports: [InstantTasksReporter, InstantTasksExceptionFilter, InstantTasksReportInterceptor],\n global: true,\n };\n }\n\n constructor(@Inject(INSTANTTASKS_OPTIONS) private readonly opts: InstantTasksOptions) {}\n\n configure(consumer: MiddlewareConsumer): void {\n if (this.opts.captureRequestContext) {\n consumer.apply(InstantTasksRequestContextMiddleware).forRoutes('*');\n }\n }\n}\n","import { Inject, Injectable, Logger, OnModuleDestroy, OnModuleInit } from '@nestjs/common';\nimport { INSTANTTASKS_OPTIONS, type InstantTasksOptions, type OutgoingEvent } from './types';\nimport { getCurrentContext } from './breadcrumb-ring';\n\n/**\n * Forwards server-side errors to an InstantTasks ingest endpoint via the\n * management-key API. Wraps `uncaughtException` + `unhandledRejection`, exposes\n * `reportError()` for manual use in interceptors / queues / cron.\n *\n * Rate-limited internally: identical fingerprints throttled to 1 per 5s,\n * with a 50-fingerprint cap per 60s window to prevent runaway loops.\n */\n// Module-level singletons — `--watch` / Jest hot-reload creates a new module\n// instance per reload, and `onModuleDestroy` is not always invoked for the\n// prior instance. Without these we accumulate process listeners forever and\n// emit one report per registered handler per uncaughtException.\nlet installedUncaught: ((err: Error) => void) | null = null;\nlet installedRejection: ((reason: unknown) => void) | null = null;\n\n@Injectable()\nexport class InstantTasksReporter implements OnModuleInit, OnModuleDestroy {\n private readonly logger = new Logger(InstantTasksReporter.name);\n private apiUrl = '';\n private projectId = '';\n private managementKey = '';\n private environment = 'production';\n private release = 'prod';\n private debug = false;\n private dryRun = false;\n private beforeSend?: (e: OutgoingEvent) => OutgoingEvent | null;\n\n private recent = new Map<string, number>();\n private readonly RECENT_WINDOW_MS = 60_000;\n private readonly RECENT_LIMIT = 50;\n\n private uncaughtHandler: ((err: Error) => void) | null = null;\n private rejectionHandler: ((reason: unknown) => void) | null = null;\n\n constructor(@Inject(INSTANTTASKS_OPTIONS) private readonly opts: InstantTasksOptions) {}\n\n onModuleInit() {\n const { apiUrl, projectId, managementKey } = this.opts;\n if (!apiUrl || !projectId || !managementKey) {\n this.logger.warn('InstantTasks reporter disabled (missing apiUrl/projectId/managementKey)');\n return;\n }\n // Strip trailing slashes and accidental /api/v1 suffix — path is appended at call site.\n this.apiUrl = apiUrl.replace(/\\/+$/, '').replace(/\\/api\\/v\\d+$/, '');\n this.projectId = projectId;\n this.managementKey = managementKey;\n this.environment = this.opts.environment ?? process.env.NODE_ENV ?? 'production';\n this.release = this.opts.release ?? 'prod';\n this.debug = this.opts.debug ?? false;\n this.dryRun = this.opts.dryRun ?? false;\n this.beforeSend = this.opts.beforeSend;\n\n // Remove any prior reporter's listeners from earlier hot-reload cycles\n // before installing ours — keeps process listener count bounded to 1 each.\n if (installedUncaught) process.off('uncaughtException', installedUncaught);\n if (installedRejection) process.off('unhandledRejection', installedRejection);\n\n this.uncaughtHandler = (err: Error) => {\n void this.reportError(err, { mechanism: 'uncaughtException', level: 'fatal' });\n };\n this.rejectionHandler = (reason: unknown) => {\n const err = reason instanceof Error ? reason : new Error(String(reason));\n void this.reportError(err, { mechanism: 'unhandledrejection', level: 'error' });\n };\n process.on('uncaughtException', this.uncaughtHandler);\n process.on('unhandledRejection', this.rejectionHandler);\n installedUncaught = this.uncaughtHandler;\n installedRejection = this.rejectionHandler;\n\n this.logger.log(`InstantTasks reporter wired → ${this.apiUrl} project=${this.projectId}`);\n }\n\n onModuleDestroy() {\n if (this.uncaughtHandler) {\n process.off('uncaughtException', this.uncaughtHandler);\n if (installedUncaught === this.uncaughtHandler) installedUncaught = null;\n }\n if (this.rejectionHandler) {\n process.off('unhandledRejection', this.rejectionHandler);\n if (installedRejection === this.rejectionHandler) installedRejection = null;\n }\n }\n\n get enabled(): boolean {\n return !!(this.apiUrl && this.projectId && this.managementKey);\n }\n\n reportError(\n err: Error,\n ctx: {\n mechanism: 'uncaughtException' | 'unhandledrejection' | 'http' | 'notify';\n level?: 'fatal' | 'error' | 'warning';\n url?: string;\n userId?: string;\n },\n ): void {\n if (!this.enabled) return;\n const fingerprint = `${err.name}:${err.message}`.slice(0, 200);\n if (this.shouldSuppress(fingerprint)) return;\n\n // Attach per-request ALS context when available (captureRequestContext flag).\n const reqCtx = getCurrentContext();\n const reqInfo = reqCtx?.request;\n const breadcrumbs = reqCtx?.getBreadcrumbs();\n const spans = reqCtx?.getSpans();\n const inferredUserId = ctx.userId ?? reqInfo?.userId;\n const inferredUrl = ctx.url ?? reqInfo?.url ?? `node://${process.env.HOSTNAME ?? 'api'}`;\n const userAgent = reqInfo?.userAgent ?? `node/${process.version}`;\n\n let event: OutgoingEvent | null = {\n kind: 'error',\n ts: new Date().toISOString(),\n release: this.release,\n environment: this.environment,\n url: inferredUrl,\n userAgent,\n viewport: { width: 0, height: 0 },\n payload: {\n exception: {\n type: err.name || 'Error',\n value: err.message || String(err),\n stack: err.stack,\n mechanism: ctx.mechanism,\n },\n level: ctx.level ?? 'error',\n },\n user: inferredUserId ? { id: inferredUserId } : undefined,\n ...(reqInfo && { request: reqInfo }),\n ...(breadcrumbs && breadcrumbs.length && { breadcrumbs }),\n ...(spans && spans.length && { spans }),\n };\n\n if (this.beforeSend) {\n try {\n event = this.beforeSend(event);\n } catch (e) {\n if (this.debug) this.logger.warn(`beforeSend threw: ${(e as Error).message}`);\n }\n if (!event) return;\n }\n\n if (this.dryRun) {\n if (this.debug) this.logger.log(`[dryRun] would send: ${event.payload.exception.type}: ${event.payload.exception.value}`);\n return;\n }\n\n // Path matches core SDK transport — base URL is the root domain, never /api/v1.\n void fetch(`${this.apiUrl}/api/v1/sdk/events`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.managementKey}`,\n 'Content-Type': 'application/json',\n 'X-Project-Id': this.projectId,\n },\n body: JSON.stringify(event),\n signal: AbortSignal.timeout(10_000),\n })\n .then((res) => {\n if (!res.ok) {\n this.logger.warn(`InstantTasks report rejected: HTTP ${res.status}`);\n }\n })\n .catch((reportErr) => {\n this.logger.warn(`InstantTasks report failed: ${(reportErr as Error).message}`);\n });\n }\n\n private shouldSuppress(fingerprint: string): boolean {\n const now = Date.now();\n for (const [fp, ts] of this.recent) {\n if (now - ts > this.RECENT_WINDOW_MS) this.recent.delete(fp);\n }\n if (this.recent.size >= this.RECENT_LIMIT && !this.recent.has(fingerprint)) {\n return true;\n }\n const last = this.recent.get(fingerprint);\n if (last && now - last < 5_000) return true;\n this.recent.set(fingerprint, now);\n return false;\n }\n}\n","export interface Breadcrumb {\n ts: string;\n category: 'http' | 'console' | 'db' | 'span' | 'custom';\n level?: 'debug' | 'info' | 'warning' | 'error';\n message: string;\n data?: Record<string, unknown>;\n}\n\nexport interface RequestInfo {\n method?: string;\n route?: string;\n url?: string;\n userAgent?: string;\n ip?: string;\n userId?: string;\n statusCode?: number;\n}\n\nexport interface SpanRecord {\n name: string;\n startTime: string;\n endTime: string;\n durationMs: number;\n status: 'ok' | 'error';\n attrs?: Record<string, unknown>;\n}\n\nexport interface InstantTasksOptions {\n apiUrl: string;\n projectId: string;\n managementKey: string;\n environment?: string;\n release?: string;\n beforeSend?: (event: OutgoingEvent) => OutgoingEvent | null;\n dryRun?: boolean;\n debug?: boolean;\n\n // ── New (all opt-in, default false) ────────────────────────────\n /** Capture per-request breadcrumb ring + request context middleware. Default false. */\n captureRequestContext?: boolean;\n /** Patch globalThis.fetch + http/https.request to record outbound HTTP as breadcrumbs. Default false. */\n captureHttp?: boolean;\n /** Patch console.{log,warn,error} to add breadcrumbs (output preserved). Default false. */\n captureConsole?: boolean;\n /** Wrap each controller method call in a span; attach span list to error events. Default false. */\n captureSpans?: boolean;\n /** Enable TypeORM query tracing — requires `dataSource` ref. Default false. */\n captureTypeOrm?: boolean;\n /** TypeORM DataSource reference (required when captureTypeOrm:true). */\n dataSource?: any;\n /** URL patterns to ignore for HTTP tracing (own ingest is ignored automatically). */\n ignoreUrls?: Array<string | RegExp>;\n /** Max breadcrumbs to keep per request. Default 50. */\n maxBreadcrumbs?: number;\n}\n\nexport interface OutgoingEvent {\n kind: 'error';\n ts: string;\n release: string;\n environment: string;\n url: string;\n userAgent: string;\n viewport: { width: number; height: number };\n payload: {\n exception: {\n type: string;\n value: string;\n stack?: string;\n mechanism: 'uncaughtException' | 'unhandledrejection' | 'http' | 'notify';\n };\n level: 'fatal' | 'error' | 'warning';\n };\n user?: { id?: string; email?: string };\n // ── Optional context (only set when captureRequestContext is on) ───\n request?: RequestInfo;\n breadcrumbs?: Breadcrumb[];\n spans?: SpanRecord[];\n}\n\nexport const INSTANTTASKS_OPTIONS = Symbol('INSTANTTASKS_OPTIONS');\n","import { AsyncLocalStorage } from 'async_hooks';\nimport type { Breadcrumb, RequestInfo, SpanRecord } from './types';\n\n/**\n * Per-request context captured at the start of each HTTP request and\n * made available to any downstream code via AsyncLocalStorage. The ring is\n * bounded — once `max` breadcrumbs are added, oldest are evicted.\n *\n * Concurrency: AsyncLocalStorage isolates contexts across concurrent async\n * tasks. Two requests being processed simultaneously each see their own\n * ring; one cannot read or evict the other's breadcrumbs.\n *\n * Caveats: callbacks that escape the request scope (timers scheduled\n * before the request, queues processed elsewhere) will NOT inherit the\n * context unless wrapped with `als.run()` at the boundary. Bull/BullMQ\n * processors run outside the HTTP context — leaveBreadcrumb() inside them\n * is a no-op.\n */\nexport class RequestContextRing {\n private readonly buf: Breadcrumb[] = [];\n private readonly spans: SpanRecord[] = [];\n request: RequestInfo;\n private readonly max: number;\n\n constructor(request: RequestInfo, max = 50) {\n this.request = request;\n this.max = max;\n }\n\n add(crumb: Breadcrumb): void {\n this.buf.push(crumb);\n if (this.buf.length > this.max) this.buf.shift();\n }\n\n addSpan(span: SpanRecord): void {\n this.spans.push(span);\n if (this.spans.length > this.max) this.spans.shift();\n }\n\n getBreadcrumbs(): Breadcrumb[] {\n return [...this.buf];\n }\n\n getSpans(): SpanRecord[] {\n return [...this.spans];\n }\n\n setUser(userId: string): void {\n this.request.userId = userId;\n }\n}\n\n/**\n * Module-level storage — single instance per process, shared by every\n * file that imports it (the standard ALS pattern).\n */\nexport const requestContextStorage = new AsyncLocalStorage<RequestContextRing>();\n\n/**\n * Add a breadcrumb to the current request's ring. No-op outside an\n * ALS-scoped request.\n */\nexport function leaveBreadcrumb(crumb: Omit<Breadcrumb, 'ts'> & { ts?: string }): void {\n const store = requestContextStorage.getStore();\n if (!store) return;\n store.add({ ts: crumb.ts ?? new Date().toISOString(), ...crumb });\n}\n\n/** Returns the current ring or null when outside an ALS scope. */\nexport function getCurrentContext(): RequestContextRing | null {\n return requestContextStorage.getStore() ?? null;\n}\n","import { ArgumentsHost, Catch, ExceptionFilter, HttpException, Logger } from '@nestjs/common';\nimport { InstantTasksReporter } from './reporter.service';\n\n/**\n * Reports 5xx (or non-HttpException) crashes to InstantTasks then re-throws.\n *\n * IMPORTANT: a NestJS ExceptionFilter is the final stop for an exception —\n * filters do NOT cascade. Re-throwing here means the exception escapes the\n * filter chain and falls through to Nest's default error handler, BYPASSING\n * any other `APP_FILTER` the host app has registered (e.g. an\n * `AllExceptionsFilter` that formats the response envelope).\n *\n * Two recommended deployments:\n *\n * 1. **Prefer the Interceptor.** Wire `InstantTasksReportInterceptor`\n * instead (see `report.interceptor.ts`). Interceptors compose with\n * `catchError(rxjs)` — they observe the error, report it, and re-throw;\n * the host's existing exception filter still runs and formats the\n * response correctly.\n *\n * 2. **If you must use this filter**, it must be the ONLY filter you\n * register, OR your `AllExceptionsFilter` must extend it. The current\n * re-throw is left in place for backwards compatibility, but the\n * response is no longer guaranteed to match the host's envelope.\n *\n * @deprecated Use `InstantTasksReportInterceptor` instead. This class will\n * be removed in a future major release.\n */\n@Catch()\nexport class InstantTasksExceptionFilter implements ExceptionFilter {\n private readonly logger = new Logger(InstantTasksExceptionFilter.name);\n\n constructor(private readonly reporter: InstantTasksReporter) {}\n\n catch(exception: unknown, host: ArgumentsHost): void {\n let shouldReport = true;\n if (exception instanceof HttpException) {\n shouldReport = exception.getStatus() >= 500;\n }\n\n if (shouldReport && this.reporter.enabled) {\n const err = exception instanceof Error ? exception : new Error(String(exception));\n let url: string | undefined;\n try {\n const req = host.switchToHttp().getRequest();\n url = req?.originalUrl ?? req?.url;\n } catch { /* not an HTTP context */ }\n this.reporter.reportError(err, { mechanism: 'http', level: 'error', url });\n }\n\n // Re-throw so the response can still be formatted — but see the JSDoc:\n // this bypasses any other registered exception filters.\n throw exception;\n }\n}\n","import {\n CallHandler,\n ExecutionContext,\n HttpException,\n Injectable,\n Logger,\n NestInterceptor,\n} from '@nestjs/common';\nimport { Observable, throwError } from 'rxjs';\nimport { catchError } from 'rxjs/operators';\n\nimport { InstantTasksReporter } from './reporter.service';\n\n/**\n * Observes errors flowing through the HTTP pipeline, reports 5xx (or\n * non-HttpException) to InstantTasks, then re-throws so the host's exception\n * filter chain runs unmodified. Unlike a NestJS `ExceptionFilter`, an\n * interceptor's re-throw propagates correctly to all downstream filters —\n * this is the recommended way to integrate InstantTasks reporting in a\n * NestJS app that already has an `AllExceptionsFilter` that formats the\n * response envelope.\n *\n * Wire with:\n * { provide: APP_INTERCEPTOR, useClass: InstantTasksReportInterceptor }\n */\n@Injectable()\nexport class InstantTasksReportInterceptor implements NestInterceptor {\n private readonly logger = new Logger(InstantTasksReportInterceptor.name);\n\n constructor(private readonly reporter: InstantTasksReporter) {}\n\n intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {\n return next.handle().pipe(\n catchError((exception: unknown) => {\n let shouldReport = true;\n if (exception instanceof HttpException) {\n shouldReport = exception.getStatus() >= 500;\n }\n if (shouldReport && this.reporter.enabled) {\n const err = exception instanceof Error ? exception : new Error(String(exception));\n let url: string | undefined;\n try {\n const req = context.switchToHttp().getRequest();\n url = req?.originalUrl ?? req?.url;\n } catch { /* not HTTP context */ }\n this.reporter.reportError(err, { mechanism: 'http', level: 'error', url });\n }\n return throwError(() => exception);\n }),\n );\n }\n}\n","import { Inject, Injectable, type NestMiddleware } from '@nestjs/common';\nimport {\n RequestContextRing,\n requestContextStorage,\n} from './breadcrumb-ring';\nimport { INSTANTTASKS_OPTIONS, type InstantTasksOptions } from './types';\n\n/** Best-effort IP extraction honouring X-Forwarded-For chains. */\nfunction extractIp(req: any): string | undefined {\n const xff = req.headers?.['x-forwarded-for'];\n if (typeof xff === 'string' && xff.length) return xff.split(',')[0]!.trim();\n if (Array.isArray(xff) && xff.length) return String(xff[0]).split(',')[0]!.trim();\n return req.ip ?? req.connection?.remoteAddress ?? req.socket?.remoteAddress;\n}\n\n@Injectable()\nexport class InstantTasksRequestContextMiddleware implements NestMiddleware {\n private readonly max: number;\n\n constructor(@Inject(INSTANTTASKS_OPTIONS) opts: InstantTasksOptions) {\n this.max = opts.maxBreadcrumbs ?? 50;\n }\n\n use(req: any, res: any, next: (err?: unknown) => void): void {\n const ring = new RequestContextRing(\n {\n method: req.method,\n route: req.route?.path ?? req.path,\n url: req.originalUrl ?? req.url,\n userAgent: req.headers?.['user-agent'],\n ip: extractIp(req),\n userId: req.user?.id,\n },\n this.max,\n );\n\n // Capture status code once response is sent so error events include it.\n res?.on?.('finish', () => {\n ring.request.statusCode = res.statusCode;\n });\n\n requestContextStorage.run(ring, () => next());\n }\n}\n","import {\n CallHandler,\n ExecutionContext,\n Injectable,\n NestInterceptor,\n} from '@nestjs/common';\nimport { Observable } from 'rxjs';\nimport { tap } from 'rxjs/operators';\nimport { getCurrentContext } from './breadcrumb-ring';\nimport type { SpanRecord } from './types';\n\n/**\n * Wraps every HTTP-bound controller method call in a span. Spans are\n * recorded into the current request's ALS ring (capped at `maxBreadcrumbs`)\n * and attached to any error event emitted for that request.\n *\n * Mirrors the core SDK's `client.startSpan({ name: 'http.<method>.<route>' })`\n * semantics — same name shape, same attr keys — but persists in-band\n * with the request context. Out-of-band export to a span endpoint can be\n * added later without changing this interface.\n */\n@Injectable()\nexport class InstantTasksSpanInterceptor implements NestInterceptor {\n intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {\n if (context.getType() !== 'http') return next.handle();\n\n const ctx = getCurrentContext();\n const req = context.switchToHttp().getRequest();\n const res = context.switchToHttp().getResponse();\n const method = String(req?.method ?? 'GET').toUpperCase();\n const route = req?.route?.path ?? req?.path ?? req?.url ?? 'unknown';\n const start = Date.now();\n const startIso = new Date(start).toISOString();\n\n const recordSpan = (status: 'ok' | 'error', extraAttrs?: Record<string, unknown>) => {\n if (!ctx) return;\n const end = Date.now();\n const span: SpanRecord = {\n name: `http.${method.toLowerCase()}.${route}`,\n startTime: startIso,\n endTime: new Date(end).toISOString(),\n durationMs: end - start,\n status,\n attrs: {\n 'http.method': method,\n 'http.route': route,\n 'http.status_code': res?.statusCode,\n 'user.id': req?.user?.id,\n ...extraAttrs,\n },\n };\n ctx.addSpan(span);\n };\n\n return next.handle().pipe(\n tap({\n next: () => recordSpan('ok'),\n error: (err) => recordSpan('error', { 'error.message': (err as Error)?.message }),\n }),\n );\n }\n}\n","/**\n * Sanitization helpers ported from `packages/sdk/web-network/src/sanitize.ts`\n * for Node-side patching. Same redaction rules:\n * - Authorization, Cookie, Set-Cookie, X-Auth-Token headers stripped\n * - `?token=` query params replaced with `[Filtered]`\n */\n\nconst REDACTED_HEADERS = new Set([\n 'authorization',\n 'cookie',\n 'set-cookie',\n 'x-auth-token',\n 'proxy-authorization',\n]);\n\nconst TOKEN_QUERY_KEYS = new Set(['token', 'access_token', 'apikey', 'api_key', 'secret']);\n\nexport function sanitizeHeaders(input: unknown): Record<string, string> {\n const out: Record<string, string> = {};\n if (!input || typeof input !== 'object') return out;\n // Headers object (fetch) is iterable\n if (typeof (input as any).forEach === 'function' && !(input as any).length) {\n try {\n (input as any).forEach((v: string, k: string) => {\n out[k] = REDACTED_HEADERS.has(k.toLowerCase()) ? '[Filtered]' : v;\n });\n return out;\n } catch {\n /* fall through */\n }\n }\n for (const [k, v] of Object.entries(input as Record<string, unknown>)) {\n out[k] = REDACTED_HEADERS.has(k.toLowerCase()) ? '[Filtered]' : String(v);\n }\n return out;\n}\n\nexport function sanitizeUrl(url: string): string {\n if (!url || typeof url !== 'string') return String(url);\n const qIdx = url.indexOf('?');\n if (qIdx === -1) return url;\n const base = url.slice(0, qIdx);\n const qs = url.slice(qIdx + 1);\n const parts = qs.split('&').map((pair) => {\n const eq = pair.indexOf('=');\n if (eq === -1) return pair;\n const key = pair.slice(0, eq);\n if (TOKEN_QUERY_KEYS.has(key.toLowerCase())) return `${key}=[Filtered]`;\n return pair;\n });\n return `${base}?${parts.join('&')}`;\n}\n\nexport function shouldIgnoreUrl(\n url: string,\n ignoreUrls: Array<string | RegExp> = [],\n): boolean {\n for (const pattern of ignoreUrls) {\n if (typeof pattern === 'string' && url.includes(pattern)) return true;\n if (pattern instanceof RegExp && pattern.test(url)) return true;\n }\n return false;\n}\n","/**\n * Sentinel to make every monkey-patch idempotent — re-running `init()` (hot\n * reload, tests, multiple `forRoot()` calls) must not stack N wrappers.\n *\n * Mirrors `packages/sdk-rn/src/wrap-sentinel.ts` so the pattern is identical\n * across SDKs.\n */\nexport const INSTANT_TASKS_WRAPPED = Symbol.for('@koderlabs/tasks-sdk-nestjs.wrapped');\n\nexport function isWrapped(fn: unknown): boolean {\n return !!fn && typeof fn === 'function' && (fn as any)[INSTANT_TASKS_WRAPPED];\n}\n\nexport function markWrapped<T extends (...args: any[]) => any>(wrapper: T, original: T): T {\n (wrapper as any)[INSTANT_TASKS_WRAPPED] = { original };\n return wrapper;\n}\n\nexport function unwrap<T extends (...args: any[]) => any>(fn: T): T {\n let cur: any = fn;\n while (cur && cur[INSTANT_TASKS_WRAPPED]) cur = cur[INSTANT_TASKS_WRAPPED].original;\n return cur as T;\n}\n","import { leaveBreadcrumb } from './breadcrumb-ring';\nimport { sanitizeUrl, shouldIgnoreUrl } from './sanitize';\nimport { isWrapped, markWrapped, unwrap } from './wrap-sentinel';\n\nexport interface HttpTracingOptions {\n ignoreUrls?: Array<string | RegExp>;\n}\n\nlet installed: { restore: () => void } | null = null;\n\n/**\n * Patch global `fetch` AND Node's `http.request` / `https.request` to emit\n * an `http` breadcrumb on completion. Idempotent via wrap-sentinel.\n *\n * Honors `ignoreUrls` — InstantTasks' own ingest endpoint should be added\n * by the caller (module.ts injects it automatically) to avoid recursion.\n */\nexport function installHttpTracing(opts: HttpTracingOptions = {}): { restore: () => void } {\n if (installed) return installed;\n const ignoreUrls = opts.ignoreUrls ?? [];\n const restorers: Array<() => void> = [];\n\n // ── globalThis.fetch ─────────────────────────────────────────────\n if (typeof globalThis.fetch === 'function' && !isWrapped(globalThis.fetch)) {\n const originalFetch = globalThis.fetch.bind(globalThis);\n const wrapped: typeof fetch = async (input: any, init?: any) => {\n const start = Date.now();\n const inputMethod =\n typeof input === 'object' && input !== null && 'method' in input ? (input as any).method : undefined;\n const method = String(init?.method ?? inputMethod ?? 'GET').toUpperCase();\n const url = typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.toString()\n : input?.url ?? '';\n const safeUrl = sanitizeUrl(url);\n if (shouldIgnoreUrl(url, ignoreUrls)) {\n return originalFetch(input, init);\n }\n try {\n const res = await originalFetch(input, init);\n leaveBreadcrumb({\n category: 'http',\n level: res.status >= 400 ? 'error' : 'info',\n message: `${method} ${safeUrl} → ${res.status}`,\n data: { method, url: safeUrl, status: res.status, durationMs: Date.now() - start },\n });\n return res;\n } catch (err) {\n leaveBreadcrumb({\n category: 'http',\n level: 'error',\n message: `${method} ${safeUrl} → ERR`,\n data: {\n method,\n url: safeUrl,\n error: (err as Error)?.message ?? String(err),\n durationMs: Date.now() - start,\n },\n });\n throw err;\n }\n };\n markWrapped(wrapped, originalFetch);\n globalThis.fetch = wrapped;\n restorers.push(() => {\n if (globalThis.fetch === wrapped) globalThis.fetch = unwrap(wrapped);\n });\n }\n\n // ── http / https.request ─────────────────────────────────────────\n // Use dynamic require so we don't crash in non-Node runtimes.\n for (const mod of ['http', 'https'] as const) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const lib = require(mod);\n if (!lib || typeof lib.request !== 'function' || isWrapped(lib.request)) continue;\n const originalRequest = lib.request.bind(lib);\n const wrapped = function (this: unknown, ...args: any[]) {\n const start = Date.now();\n // Signature: request(url[, options][, cb]) | request(options[, cb])\n let url = '';\n let method = 'GET';\n try {\n const first = args[0];\n if (typeof first === 'string') {\n url = first;\n } else if (first instanceof URL) {\n url = first.toString();\n } else if (first && typeof first === 'object') {\n const host = first.host ?? first.hostname ?? '';\n const path = first.path ?? '';\n url = `${mod}://${host}${path}`;\n method = (first.method ?? 'GET').toUpperCase();\n }\n const optsArg = args.find((a, i) => i > 0 && a && typeof a === 'object' && !('on' in a));\n if (optsArg?.method) method = String(optsArg.method).toUpperCase();\n } catch {\n /* parse best effort */\n }\n const safeUrl = sanitizeUrl(url);\n const req = originalRequest(...args);\n if (!shouldIgnoreUrl(url, ignoreUrls)) {\n req.once?.('response', (res: any) => {\n leaveBreadcrumb({\n category: 'http',\n level: (res.statusCode ?? 0) >= 400 ? 'error' : 'info',\n message: `${method} ${safeUrl} → ${res.statusCode}`,\n data: {\n method,\n url: safeUrl,\n status: res.statusCode,\n durationMs: Date.now() - start,\n },\n });\n });\n req.once?.('error', (err: Error) => {\n leaveBreadcrumb({\n category: 'http',\n level: 'error',\n message: `${method} ${safeUrl} → ERR`,\n data: { method, url: safeUrl, error: err.message, durationMs: Date.now() - start },\n });\n });\n }\n return req;\n };\n markWrapped(wrapped as any, originalRequest);\n lib.request = wrapped;\n restorers.push(() => {\n if (lib.request === wrapped) lib.request = unwrap(wrapped as any);\n });\n } catch {\n /* http/https not available — non-Node runtime */\n }\n }\n\n installed = {\n restore() {\n for (const r of restorers) r();\n installed = null;\n },\n };\n return installed;\n}\n","import { leaveBreadcrumb } from './breadcrumb-ring';\nimport { isWrapped, markWrapped, unwrap } from './wrap-sentinel';\n\nlet installed: { restore: () => void } | null = null;\n\nconst NEST_LOGGER_HINTS = ['[Nest]', 'NestApplication', 'NestFactory', 'InstanceLoader', 'RoutesResolver', 'RouterExplorer'];\n\nfunction looksLikeNestLogger(args: unknown[]): boolean {\n // NestJS logger output starts with \"[Nest] PID - timestamp ...\" — skip\n // to avoid feedback loops when console.error is forwarded through Logger.\n if (!args.length) return false;\n const first = String(args[0] ?? '');\n return NEST_LOGGER_HINTS.some((h) => first.includes(h));\n}\n\nfunction format(args: unknown[]): string {\n return args\n .map((a) => {\n if (typeof a === 'string') return a;\n if (a instanceof Error) return `${a.name}: ${a.message}`;\n try {\n return JSON.stringify(a);\n } catch {\n return String(a);\n }\n })\n .join(' ')\n .slice(0, 1000);\n}\n\n/**\n * Patch console.{log,warn,error} to emit breadcrumbs. The original\n * console method is always invoked, so stdout/stderr output is preserved.\n *\n * Skips messages that look like NestJS Logger output — those already\n * surface in your logging stack and re-capturing them risks recursion.\n */\nexport function installConsoleCapture(): { restore: () => void } {\n if (installed) return installed;\n const restorers: Array<() => void> = [];\n\n const levels: Array<[keyof Console, 'info' | 'warning' | 'error']> = [\n ['log', 'info'],\n ['warn', 'warning'],\n ['error', 'error'],\n ];\n\n for (const [method, level] of levels) {\n const fn = (console as any)[method];\n if (typeof fn !== 'function' || isWrapped(fn)) continue;\n const original = fn.bind(console);\n const wrapped = function (this: unknown, ...args: unknown[]) {\n try {\n if (!looksLikeNestLogger(args)) {\n leaveBreadcrumb({\n category: 'console',\n level,\n message: format(args),\n });\n }\n } catch {\n /* never fail original call */\n }\n return original(...args);\n };\n markWrapped(wrapped as any, original);\n (console as any)[method] = wrapped;\n restorers.push(() => {\n if ((console as any)[method] === wrapped) {\n (console as any)[method] = unwrap(wrapped as any);\n }\n });\n }\n\n installed = {\n restore() {\n for (const r of restorers) r();\n installed = null;\n },\n };\n return installed;\n}\n","import { getCurrentContext } from './breadcrumb-ring';\nimport { leaveBreadcrumb } from './breadcrumb-ring';\nimport type { SpanRecord } from './types';\n\n/**\n * Implements TypeORM's `Logger` interface and registers itself on the\n * given DataSource (`dataSource.setOptions({ logger: this })`).\n *\n * Caveat: TypeORM only allows one logger per DataSource. Installing this\n * displaces whatever logger the host app configured. If the host already\n * has a custom logger, wrap it manually instead of using this helper.\n *\n * Each query becomes both a breadcrumb (for context on errors) and a\n * span record (recorded into the current request's ALS ring).\n */\n\nfunction parseQueryType(sql: string): string {\n const m = /^\\s*(\\w+)/.exec(sql);\n return (m?.[1] ?? 'UNKNOWN').toUpperCase();\n}\n\nfunction parseTable(sql: string): string | undefined {\n // Best-effort — covers the four common DML/DDL shapes.\n const re = /\\b(?:FROM|INTO|UPDATE|JOIN)\\s+[\"']?(\\w+)/i;\n const m = re.exec(sql);\n return m?.[1];\n}\n\nexport class InstantTasksTypeOrmLogger {\n private readonly slowQueryMs: number;\n\n constructor(slowQueryMs = 200) {\n this.slowQueryMs = slowQueryMs;\n }\n\n private emit(sql: string, durationMs: number | null, status: 'ok' | 'error', extraAttrs: Record<string, unknown> = {}) {\n const ctx = getCurrentContext();\n const queryType = parseQueryType(sql);\n const table = parseTable(sql);\n const truncated = sql.length > 300 ? `${sql.slice(0, 300)}…` : sql;\n\n leaveBreadcrumb({\n category: 'db',\n level: status === 'error' ? 'error' : durationMs && durationMs > this.slowQueryMs ? 'warning' : 'info',\n message: `${queryType}${table ? ` ${table}` : ''}${durationMs != null ? ` (${durationMs}ms)` : ''}`,\n data: { sql: truncated, queryType, table, durationMs, status, ...extraAttrs },\n });\n\n if (ctx && durationMs != null) {\n const end = Date.now();\n const span: SpanRecord = {\n name: 'db.query',\n startTime: new Date(end - durationMs).toISOString(),\n endTime: new Date(end).toISOString(),\n durationMs,\n status,\n attrs: { 'db.statement': truncated, 'db.operation': queryType, 'db.table': table, ...extraAttrs },\n };\n ctx.addSpan(span);\n }\n }\n\n // ── TypeORM Logger interface ─────────────────────────────────────\n logQuery(sql: string): void {\n this.emit(sql, null, 'ok');\n }\n\n logQueryError(error: string | Error, sql: string): void {\n this.emit(sql, null, 'error', { 'error.message': error instanceof Error ? error.message : String(error) });\n }\n\n logQuerySlow(time: number, sql: string): void {\n this.emit(sql, time, 'ok', { slow: true });\n }\n\n logSchemaBuild(): void { /* no-op */ }\n logMigration(): void { /* no-op */ }\n log(): void { /* no-op */ }\n}\n\n/**\n * Install the logger on a TypeORM DataSource. Safe to call multiple times\n * — TypeORM accepts logger replacement on every `setOptions()` call, and\n * we always install the same instance once installed.\n */\nexport function installTypeOrmTracing(dataSource: any): InstantTasksTypeOrmLogger | null {\n if (!dataSource || typeof dataSource.setOptions !== 'function') return null;\n const existing = dataSource.options?.logger;\n if (existing instanceof InstantTasksTypeOrmLogger) return existing;\n const logger = new InstantTasksTypeOrmLogger();\n dataSource.setOptions({ logger });\n return logger;\n}\n"],"mappings":"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","names":["Inject","Module","APP_INTERCEPTOR","Inject","Injectable","Logger","INSTANTTASKS_OPTIONS","Symbol","AsyncLocalStorage","RequestContextRing","buf","spans","request","max","add","crumb","push","length","shift","addSpan","span","getBreadcrumbs","getSpans","setUser","userId","requestContextStorage","AsyncLocalStorage","leaveBreadcrumb","store","getStore","ts","Date","toISOString","getCurrentContext","installedUncaught","installedRejection","InstantTasksReporter","logger","Logger","name","apiUrl","projectId","managementKey","environment","release","debug","dryRun","beforeSend","recent","Map","RECENT_WINDOW_MS","RECENT_LIMIT","uncaughtHandler","rejectionHandler","opts","onModuleInit","warn","replace","process","env","NODE_ENV","off","err","reportError","mechanism","level","reason","Error","String","on","log","onModuleDestroy","enabled","ctx","fingerprint","message","slice","shouldSuppress","reqCtx","getCurrentContext","reqInfo","request","breadcrumbs","getBreadcrumbs","spans","getSpans","inferredUserId","userId","inferredUrl","url","HOSTNAME","userAgent","version","event","kind","ts","Date","toISOString","viewport","width","height","payload","exception","type","value","stack","user","id","undefined","length","e","fetch","method","headers","Authorization","body","JSON","stringify","signal","AbortSignal","timeout","then","res","ok","status","catch","reportErr","now","fp","delete","size","has","last","get","set","Catch","HttpException","Logger","InstantTasksExceptionFilter","logger","Logger","name","reporter","catch","exception","host","shouldReport","HttpException","getStatus","enabled","err","Error","String","url","req","switchToHttp","getRequest","originalUrl","reportError","mechanism","level","HttpException","Injectable","Logger","throwError","catchError","InstantTasksReportInterceptor","logger","Logger","name","reporter","intercept","context","next","handle","pipe","catchError","exception","shouldReport","HttpException","getStatus","enabled","err","Error","String","url","req","switchToHttp","getRequest","originalUrl","reportError","mechanism","level","throwError","Inject","Injectable","extractIp","req","xff","headers","length","split","trim","Array","isArray","String","ip","connection","remoteAddress","socket","InstantTasksRequestContextMiddleware","max","opts","maxBreadcrumbs","use","res","next","ring","RequestContextRing","method","route","path","url","originalUrl","userAgent","userId","user","id","on","request","statusCode","requestContextStorage","run","Injectable","tap","InstantTasksSpanInterceptor","intercept","context","next","getType","handle","ctx","getCurrentContext","req","switchToHttp","getRequest","res","getResponse","method","String","toUpperCase","route","path","url","start","Date","now","startIso","toISOString","recordSpan","status","extraAttrs","end","span","name","toLowerCase","startTime","endTime","durationMs","attrs","statusCode","user","id","addSpan","pipe","tap","error","err","message","TOKEN_QUERY_KEYS","Set","sanitizeUrl","url","String","qIdx","indexOf","base","slice","qs","parts","split","map","pair","eq","key","TOKEN_QUERY_KEYS","has","toLowerCase","join","shouldIgnoreUrl","ignoreUrls","pattern","includes","RegExp","test","INSTANT_TASKS_WRAPPED","Symbol","for","isWrapped","fn","markWrapped","wrapper","original","unwrap","cur","installed","installHttpTracing","opts","ignoreUrls","restorers","globalThis","fetch","isWrapped","originalFetch","bind","wrapped","input","init","start","Date","now","inputMethod","method","undefined","String","toUpperCase","url","URL","toString","safeUrl","sanitizeUrl","shouldIgnoreUrl","res","leaveBreadcrumb","category","level","status","message","data","durationMs","err","error","markWrapped","push","unwrap","mod","lib","require","request","originalRequest","args","first","host","hostname","path","optsArg","find","a","i","req","once","statusCode","restore","r","installed","NEST_LOGGER_HINTS","looksLikeNestLogger","args","length","first","String","some","h","includes","format","map","a","Error","name","message","JSON","stringify","join","slice","installConsoleCapture","restorers","levels","method","level","fn","console","isWrapped","original","bind","wrapped","leaveBreadcrumb","category","markWrapped","push","unwrap","restore","r","parseQueryType","sql","m","exec","toUpperCase","parseTable","re","InstantTasksTypeOrmLogger","slowQueryMs","emit","durationMs","status","extraAttrs","ctx","getCurrentContext","queryType","table","truncated","length","slice","leaveBreadcrumb","category","level","message","data","end","Date","now","span","name","startTime","toISOString","endTime","attrs","addSpan","logQuery","logQueryError","error","Error","String","logQuerySlow","time","slow","logSchemaBuild","logMigration","log","installTypeOrmTracing","dataSource","setOptions","existing","options","logger","InstantTasksLifecycle","restorers","opts","onModuleInit","captureHttp","ignoreUrls","apiUrl","replace","restore","installHttpTracing","push","captureConsole","installConsoleCapture","captureTypeOrm","dataSource","installTypeOrmTracing","onModuleDestroy","r","InstantTasksModule","forRoot","options","build","provide","INSTANTTASKS_OPTIONS","useValue","forRootAsync","optsProvider","useFactory","inject","conditional","APP_INTERCEPTOR","useClass","InstantTasksSpanInterceptor","module","imports","providers","InstantTasksReporter","InstantTasksExceptionFilter","InstantTasksReportInterceptor","InstantTasksRequestContextMiddleware","exports","global","captureSpans","configure","consumer","captureRequestContext","apply","forRoutes"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@koderlabs/tasks-sdk-nestjs",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "NestJS module for InstantTasks — reporter + interceptor + breadcrumbs + req context + http/console/spans/typeorm tracing.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"instanttasks",
|
|
7
|
+
"sdk",
|
|
8
|
+
"nestjs",
|
|
9
|
+
"nodejs"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/jawaidgadiwala/instant-tasks/tree/main/packages/sdk-nestjs#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/jawaidgadiwala/instant-tasks/issues"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/jawaidgadiwala/instant-tasks.git",
|
|
18
|
+
"directory": "packages/sdk-nestjs"
|
|
19
|
+
},
|
|
20
|
+
"license": "UNLICENSED",
|
|
21
|
+
"author": {
|
|
22
|
+
"name": "Jawaid Gadiwala",
|
|
23
|
+
"email": "jawaidgadiwala@gmail.com",
|
|
24
|
+
"url": "https://koderlabs.com"
|
|
25
|
+
},
|
|
26
|
+
"type": "module",
|
|
27
|
+
"main": "./dist/index.cjs",
|
|
28
|
+
"module": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"import": "./dist/index.js",
|
|
34
|
+
"require": "./dist/index.cjs"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"LICENSE"
|
|
40
|
+
],
|
|
41
|
+
"sideEffects": false,
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
|
44
|
+
"@nestjs/core": "^10.0.0 || ^11.0.0",
|
|
45
|
+
"rxjs": "^7.0.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@nestjs/common": "^11.0.0",
|
|
49
|
+
"@nestjs/core": "^11.0.0",
|
|
50
|
+
"@types/node": "^22.0.0",
|
|
51
|
+
"reflect-metadata": "^0.2.0",
|
|
52
|
+
"rxjs": "^7.8.0",
|
|
53
|
+
"tsup": "^8.3.0",
|
|
54
|
+
"typescript": "^5.5.0",
|
|
55
|
+
"vitest": "^2.1.0"
|
|
56
|
+
},
|
|
57
|
+
"engines": {
|
|
58
|
+
"node": ">=20"
|
|
59
|
+
},
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "restricted"
|
|
62
|
+
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"build": "tsup",
|
|
65
|
+
"dev": "tsup --watch",
|
|
66
|
+
"test": "vitest run",
|
|
67
|
+
"test:watch": "vitest"
|
|
68
|
+
}
|
|
69
|
+
}
|