apitally 0.11.4 → 0.11.5

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.
Files changed (71) hide show
  1. package/README.md +25 -12
  2. package/dist/common/client.cjs +14 -5
  3. package/dist/common/client.cjs.map +1 -1
  4. package/dist/common/client.d.cts +2 -0
  5. package/dist/common/client.d.ts +2 -0
  6. package/dist/common/client.js +14 -5
  7. package/dist/common/client.js.map +1 -1
  8. package/dist/common/consumerRegistry.cjs.map +1 -1
  9. package/dist/common/consumerRegistry.js.map +1 -1
  10. package/dist/common/logging.cjs.map +1 -1
  11. package/dist/common/logging.js.map +1 -1
  12. package/dist/common/packageVersions.cjs.map +1 -1
  13. package/dist/common/packageVersions.js.map +1 -1
  14. package/dist/common/paramValidation.cjs.map +1 -1
  15. package/dist/common/paramValidation.js.map +1 -1
  16. package/dist/common/requestCounter.cjs.map +1 -1
  17. package/dist/common/requestCounter.js.map +1 -1
  18. package/dist/common/requestLogger.cjs +8 -5
  19. package/dist/common/requestLogger.cjs.map +1 -1
  20. package/dist/common/requestLogger.d.cts +1 -0
  21. package/dist/common/requestLogger.d.ts +1 -0
  22. package/dist/common/requestLogger.js +8 -5
  23. package/dist/common/requestLogger.js.map +1 -1
  24. package/dist/common/serverErrorCounter.cjs.map +1 -1
  25. package/dist/common/serverErrorCounter.js.map +1 -1
  26. package/dist/common/tempGzipFile.cjs +2 -2
  27. package/dist/common/tempGzipFile.cjs.map +1 -1
  28. package/dist/common/tempGzipFile.js +2 -2
  29. package/dist/common/tempGzipFile.js.map +1 -1
  30. package/dist/common/types.cjs.map +1 -1
  31. package/dist/common/validationErrorCounter.cjs.map +1 -1
  32. package/dist/common/validationErrorCounter.js.map +1 -1
  33. package/dist/express/index.cjs +22 -6
  34. package/dist/express/index.cjs.map +1 -1
  35. package/dist/express/index.js +22 -6
  36. package/dist/express/index.js.map +1 -1
  37. package/dist/express/middleware.cjs +22 -6
  38. package/dist/express/middleware.cjs.map +1 -1
  39. package/dist/express/middleware.js +22 -6
  40. package/dist/express/middleware.js.map +1 -1
  41. package/dist/express/utils.cjs.map +1 -1
  42. package/dist/express/utils.js.map +1 -1
  43. package/dist/fastify/index.cjs +19 -7
  44. package/dist/fastify/index.cjs.map +1 -1
  45. package/dist/fastify/index.js +19 -7
  46. package/dist/fastify/index.js.map +1 -1
  47. package/dist/fastify/plugin.cjs +19 -7
  48. package/dist/fastify/plugin.cjs.map +1 -1
  49. package/dist/fastify/plugin.js +19 -7
  50. package/dist/fastify/plugin.js.map +1 -1
  51. package/dist/hono/index.cjs +20 -6
  52. package/dist/hono/index.cjs.map +1 -1
  53. package/dist/hono/index.js +20 -6
  54. package/dist/hono/index.js.map +1 -1
  55. package/dist/hono/middleware.cjs +20 -6
  56. package/dist/hono/middleware.cjs.map +1 -1
  57. package/dist/hono/middleware.js +20 -6
  58. package/dist/hono/middleware.js.map +1 -1
  59. package/dist/koa/index.cjs +18 -5
  60. package/dist/koa/index.cjs.map +1 -1
  61. package/dist/koa/index.js +18 -5
  62. package/dist/koa/index.js.map +1 -1
  63. package/dist/koa/middleware.cjs +18 -5
  64. package/dist/koa/middleware.cjs.map +1 -1
  65. package/dist/koa/middleware.js +18 -5
  66. package/dist/koa/middleware.js.map +1 -1
  67. package/dist/nestjs/index.cjs +22 -6
  68. package/dist/nestjs/index.cjs.map +1 -1
  69. package/dist/nestjs/index.js +22 -6
  70. package/dist/nestjs/index.js.map +1 -1
  71. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/common/requestLogger.ts","../../src/common/tempGzipFile.ts"],"sourcesContent":["import AsyncLock from \"async-lock\";\nimport { Buffer } from \"buffer\";\nimport { randomUUID } from \"crypto\";\nimport { unlinkSync, writeFileSync } from \"fs\";\nimport { IncomingHttpHeaders, OutgoingHttpHeaders } from \"http\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\n\nimport TempGzipFile from \"./tempGzipFile.js\";\n\nconst MAX_BODY_SIZE = 50_000; // 50 KB (uncompressed)\nconst MAX_FILE_SIZE = 1_000_000; // 1 MB (compressed)\nconst MAX_FILES = 50;\nconst MAX_PENDING_WRITES = 100;\nconst BODY_TOO_LARGE = Buffer.from(\"<body too large>\");\nconst BODY_MASKED = Buffer.from(\"<masked>\");\nconst MASKED = \"******\";\nconst ALLOWED_CONTENT_TYPES = [\"application/json\", \"text/plain\"];\nconst EXCLUDE_PATH_PATTERNS = [\n /\\/_?healthz?$/i,\n /\\/_?health[_-]?checks?$/i,\n /\\/_?heart[_-]?beats?$/i,\n /\\/ping$/i,\n /\\/ready$/i,\n /\\/live$/i,\n];\nconst EXCLUDE_USER_AGENT_PATTERNS = [\n /health[-_ ]?check/i,\n /microsoft-azure-application-lb/i,\n /googlehc/i,\n /kube-probe/i,\n];\nconst MASK_QUERY_PARAM_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /password/i,\n /pwd/i,\n];\nconst MASK_HEADER_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /cookie/i,\n];\n\nexport type Request = {\n timestamp: number;\n method: string;\n path?: string;\n url: string;\n headers: [string, string][];\n size?: number;\n consumer?: string;\n body?: Buffer;\n};\n\nexport type Response = {\n statusCode: number;\n responseTime: number;\n headers: [string, string][];\n size?: number;\n body?: Buffer;\n};\n\nexport type RequestLoggingConfig = {\n enabled: boolean;\n logQueryParams: boolean;\n logRequestHeaders: boolean;\n logRequestBody: boolean;\n logResponseHeaders: boolean;\n logResponseBody: boolean;\n maskQueryParams: RegExp[];\n maskHeaders: RegExp[];\n maskRequestBodyCallback?: (request: Request) => Buffer | null | undefined;\n maskResponseBodyCallback?: (\n request: Request,\n response: Response,\n ) => Buffer | null | undefined;\n excludePaths: RegExp[];\n excludeCallback?: (request: Request, response: Response) => boolean;\n};\n\nconst DEFAULT_CONFIG: RequestLoggingConfig = {\n enabled: false,\n logQueryParams: true,\n logRequestHeaders: false,\n logRequestBody: false,\n logResponseHeaders: true,\n logResponseBody: false,\n maskQueryParams: [],\n maskHeaders: [],\n excludePaths: [],\n};\n\nexport default class RequestLogger {\n public config: RequestLoggingConfig;\n public enabled: boolean;\n public suspendUntil: number | null = null;\n private pendingWrites: string[] = [];\n private currentFile: TempGzipFile | null = null;\n private files: TempGzipFile[] = [];\n private maintainIntervalId?: NodeJS.Timeout;\n private lock = new AsyncLock();\n\n constructor(config?: Partial<RequestLoggingConfig>) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.enabled = this.config.enabled && checkWritableFs();\n\n if (this.enabled) {\n this.maintainIntervalId = setInterval(() => {\n this.maintain();\n }, 1000);\n }\n }\n\n private shouldExcludePath(urlPath: string) {\n const patterns = [...this.config.excludePaths, ...EXCLUDE_PATH_PATTERNS];\n return matchPatterns(urlPath, patterns);\n }\n\n private shouldExcludeUserAgent(userAgent?: string) {\n return userAgent\n ? matchPatterns(userAgent, EXCLUDE_USER_AGENT_PATTERNS)\n : false;\n }\n\n private shouldMaskQueryParam(name: string) {\n const patterns = [\n ...this.config.maskQueryParams,\n ...MASK_QUERY_PARAM_PATTERNS,\n ];\n return matchPatterns(name, patterns);\n }\n\n private shouldMaskHeader(name: string) {\n const patterns = [...this.config.maskHeaders, ...MASK_HEADER_PATTERNS];\n return matchPatterns(name, patterns);\n }\n\n private hasSupportedContentType(headers: [string, string][]) {\n const contentType = headers.find(\n ([k]) => k.toLowerCase() === \"content-type\",\n )?.[1];\n return (\n contentType !== undefined &&\n ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t))\n );\n }\n\n private maskQueryParams(search: string) {\n const params = new URLSearchParams(search);\n for (const [key] of params) {\n if (this.shouldMaskQueryParam(key)) {\n params.set(key, MASKED);\n }\n }\n return params.toString();\n }\n\n private maskHeaders(headers: [string, string][]): [string, string][] {\n return headers.map(([k, v]) => [k, this.shouldMaskHeader(k) ? MASKED : v]);\n }\n\n logRequest(request: Request, response: Response) {\n if (!this.enabled || this.suspendUntil !== null) return;\n\n const url = new URL(request.url);\n const path = request.path ?? url.pathname;\n const userAgent = request.headers.find(\n ([k]) => k.toLowerCase() === \"user-agent\",\n )?.[1];\n\n if (\n this.shouldExcludePath(path) ||\n this.shouldExcludeUserAgent(userAgent) ||\n (this.config.excludeCallback?.(request, response) ?? false)\n ) {\n return;\n }\n\n // Process query params\n url.search = this.config.logQueryParams\n ? this.maskQueryParams(url.search)\n : \"\";\n request.url = url.toString();\n\n // Process headers\n request.headers = this.config.logRequestHeaders\n ? this.maskHeaders(request.headers)\n : [];\n response.headers = this.config.logResponseHeaders\n ? this.maskHeaders(response.headers)\n : [];\n\n // Process request body\n if (\n !this.config.logRequestBody ||\n !this.hasSupportedContentType(request.headers)\n ) {\n request.body = undefined;\n } else if (request.body) {\n if (request.body.length > MAX_BODY_SIZE) {\n request.body = BODY_TOO_LARGE;\n } else if (this.config.maskRequestBodyCallback) {\n try {\n request.body =\n this.config.maskRequestBodyCallback(request) ?? BODY_MASKED;\n if (request.body.length > MAX_BODY_SIZE) {\n request.body = BODY_TOO_LARGE;\n }\n } catch {\n request.body = undefined;\n }\n }\n }\n\n // Process response body\n if (\n !this.config.logResponseBody ||\n !this.hasSupportedContentType(response.headers)\n ) {\n response.body = undefined;\n } else if (response.body) {\n if (response.body.length > MAX_BODY_SIZE) {\n response.body = BODY_TOO_LARGE;\n } else if (this.config.maskResponseBodyCallback) {\n try {\n response.body =\n this.config.maskResponseBodyCallback(request, response) ??\n BODY_MASKED;\n if (response.body.length > MAX_BODY_SIZE) {\n response.body = BODY_TOO_LARGE;\n }\n } catch {\n response.body = undefined;\n }\n }\n }\n\n const item = {\n uuid: randomUUID(),\n request: skipEmptyValues(request),\n response: skipEmptyValues(response),\n };\n [item.request.body, item.response.body].forEach((body) => {\n if (body) {\n // @ts-expect-error Different return type\n body.toJSON = function () {\n return this.toString(\"base64\");\n };\n }\n });\n this.pendingWrites.push(JSON.stringify(item));\n\n if (this.pendingWrites.length > MAX_PENDING_WRITES) {\n this.pendingWrites.shift();\n }\n }\n\n async writeToFile() {\n if (!this.enabled || this.pendingWrites.length === 0) {\n return;\n }\n return this.lock.acquire(\"file\", async () => {\n if (!this.currentFile) {\n this.currentFile = new TempGzipFile();\n }\n while (this.pendingWrites.length > 0) {\n const item = this.pendingWrites.shift();\n if (item) {\n await this.currentFile.writeLine(Buffer.from(item));\n }\n }\n });\n }\n\n getFile() {\n return this.files.shift();\n }\n\n retryFileLater(file: TempGzipFile) {\n this.files.unshift(file);\n }\n\n async rotateFile() {\n return this.lock.acquire(\"file\", async () => {\n if (this.currentFile) {\n await this.currentFile.close();\n this.files.push(this.currentFile);\n this.currentFile = null;\n }\n });\n }\n\n async maintain() {\n await this.writeToFile();\n if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {\n await this.rotateFile();\n }\n while (this.files.length > MAX_FILES) {\n const file = this.files.shift();\n file?.delete();\n }\n if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {\n this.suspendUntil = null;\n }\n }\n\n async clear() {\n this.pendingWrites = [];\n await this.rotateFile();\n this.files.forEach((file) => {\n file.delete();\n });\n this.files = [];\n }\n\n async close() {\n this.enabled = false;\n await this.clear();\n if (this.maintainIntervalId) {\n clearInterval(this.maintainIntervalId);\n }\n }\n}\n\nexport function convertHeaders(\n headers:\n | Headers\n | IncomingHttpHeaders\n | OutgoingHttpHeaders\n | Record<string, string | string[] | number | undefined>,\n) {\n if (headers instanceof Headers) {\n return Array.from(headers.entries());\n }\n return Object.entries(headers).flatMap(([key, value]) => {\n if (value === undefined) {\n return [];\n }\n if (Array.isArray(value)) {\n return value.map((v) => [key, v]);\n }\n return [[key, value.toString()]];\n }) as [string, string][];\n}\n\nexport function convertBody(body: any, contentType?: string | null) {\n if (!body || !contentType) {\n return;\n }\n try {\n if (contentType.startsWith(\"application/json\")) {\n if (isValidJsonString(body)) {\n return Buffer.from(body);\n } else {\n return Buffer.from(JSON.stringify(body));\n }\n }\n if (contentType.startsWith(\"text/\") && typeof body === \"string\") {\n return Buffer.from(body);\n }\n } catch (error) {\n return;\n }\n}\n\nfunction isValidJsonString(body: any) {\n if (typeof body !== \"string\") {\n return false;\n }\n try {\n JSON.parse(body);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction matchPatterns(value: string, patterns: RegExp[]) {\n return patterns.some((pattern) => {\n return pattern.test(value);\n });\n}\n\nfunction skipEmptyValues<T extends Record<string, any>>(data: T) {\n return Object.fromEntries(\n Object.entries(data).filter(([_, v]) => {\n if (v == null || Number.isNaN(v)) return false;\n if (Array.isArray(v) || Buffer.isBuffer(v) || typeof v === \"string\") {\n return v.length > 0;\n }\n return true;\n }),\n ) as Partial<T>;\n}\n\nfunction checkWritableFs() {\n try {\n const testPath = join(tmpdir(), `apitally-${randomUUID()}`);\n writeFileSync(testPath, \"test\");\n unlinkSync(testPath);\n return true;\n } catch (error) {\n return false;\n }\n}\n","import { Buffer } from \"buffer\";\nimport { randomUUID } from \"crypto\";\nimport { createWriteStream, readFile, unlinkSync, WriteStream } from \"fs\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\nimport { createGzip, Gzip } from \"zlib\";\n\nexport default class TempGzipFile {\n public uuid: string;\n private filePath: string;\n private gzip: Gzip;\n private writeStream: WriteStream;\n private readyPromise: Promise<void>;\n private closedPromise: Promise<void>;\n\n constructor() {\n this.uuid = randomUUID();\n this.filePath = join(tmpdir(), `apitally-${this.uuid}.gz`);\n this.writeStream = createWriteStream(this.filePath);\n this.readyPromise = new Promise<void>((resolve, reject) => {\n this.writeStream.once(\"ready\", resolve);\n this.writeStream.once(\"error\", reject);\n });\n this.closedPromise = new Promise<void>((resolve) => {\n this.writeStream.once(\"close\", resolve);\n this.writeStream.once(\"error\", resolve);\n });\n this.gzip = createGzip();\n this.gzip.pipe(this.writeStream);\n }\n\n get size() {\n return this.writeStream.bytesWritten;\n }\n\n async writeLine(data: Buffer) {\n await this.readyPromise;\n return new Promise<void>((resolve, reject) => {\n this.gzip.write(Buffer.concat([data, Buffer.from(\"\\n\")]), (error) => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n }\n\n async getContent() {\n return new Promise<Buffer>((resolve, reject) => {\n readFile(this.filePath, (error, data) => {\n if (error) {\n reject(error);\n } else {\n resolve(data);\n }\n });\n });\n }\n\n async close() {\n await new Promise<void>((resolve) => {\n this.gzip.end(() => {\n resolve();\n });\n });\n await this.closedPromise;\n }\n\n async delete() {\n await this.close();\n unlinkSync(this.filePath);\n }\n}\n"],"mappings":"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","names":["import_buffer","import_crypto","import_fs","import_os","import_path","TempGzipFile","uuid","filePath","gzip","writeStream","readyPromise","closedPromise","constructor","randomUUID","join","tmpdir","createWriteStream","Promise","resolve","reject","once","createGzip","pipe","size","bytesWritten","writeLine","data","write","Buffer","concat","from","error","getContent","readFile","close","end","delete","unlinkSync","MAX_BODY_SIZE","MAX_FILE_SIZE","MAX_FILES","MAX_PENDING_WRITES","BODY_TOO_LARGE","Buffer","from","BODY_MASKED","MASKED","ALLOWED_CONTENT_TYPES","EXCLUDE_PATH_PATTERNS","EXCLUDE_USER_AGENT_PATTERNS","MASK_QUERY_PARAM_PATTERNS","MASK_HEADER_PATTERNS","DEFAULT_CONFIG","enabled","logQueryParams","logRequestHeaders","logRequestBody","logResponseHeaders","logResponseBody","maskQueryParams","maskHeaders","excludePaths","RequestLogger","config","suspendUntil","pendingWrites","currentFile","files","maintainIntervalId","lock","AsyncLock","constructor","checkWritableFs","setInterval","maintain","shouldExcludePath","urlPath","patterns","matchPatterns","shouldExcludeUserAgent","userAgent","shouldMaskQueryParam","name","shouldMaskHeader","hasSupportedContentType","headers","contentType","find","k","toLowerCase","undefined","some","t","startsWith","search","params","URLSearchParams","key","set","toString","map","v","logRequest","request","response","url","URL","path","pathname","excludeCallback","body","length","maskRequestBodyCallback","maskResponseBodyCallback","item","uuid","randomUUID","skipEmptyValues","forEach","toJSON","push","JSON","stringify","shift","writeToFile","acquire","TempGzipFile","writeLine","getFile","retryFileLater","file","unshift","rotateFile","close","size","delete","Date","now","clear","clearInterval","convertHeaders","Headers","Array","entries","Object","flatMap","value","isArray","convertBody","isValidJsonString","error","parse","pattern","test","data","fromEntries","filter","_","Number","isNaN","isBuffer","testPath","join","tmpdir","writeFileSync","unlinkSync"]}
1
+ {"version":3,"sources":["requestLogger.ts","tempGzipFile.ts"],"sourcesContent":["import AsyncLock from \"async-lock\";\nimport { Buffer } from \"buffer\";\nimport { randomUUID } from \"crypto\";\nimport { unlinkSync, writeFileSync } from \"fs\";\nimport { IncomingHttpHeaders, OutgoingHttpHeaders } from \"http\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\n\nimport TempGzipFile from \"./tempGzipFile.js\";\n\nconst MAX_BODY_SIZE = 50_000; // 50 KB (uncompressed)\nconst MAX_FILE_SIZE = 1_000_000; // 1 MB (compressed)\nconst MAX_FILES = 50;\nconst MAX_PENDING_WRITES = 100;\nconst BODY_TOO_LARGE = Buffer.from(\"<body too large>\");\nconst BODY_MASKED = Buffer.from(\"<masked>\");\nconst MASKED = \"******\";\nconst ALLOWED_CONTENT_TYPES = [\"application/json\", \"text/plain\"];\nconst EXCLUDE_PATH_PATTERNS = [\n /\\/_?healthz?$/i,\n /\\/_?health[_-]?checks?$/i,\n /\\/_?heart[_-]?beats?$/i,\n /\\/ping$/i,\n /\\/ready$/i,\n /\\/live$/i,\n];\nconst EXCLUDE_USER_AGENT_PATTERNS = [\n /health[-_ ]?check/i,\n /microsoft-azure-application-lb/i,\n /googlehc/i,\n /kube-probe/i,\n];\nconst MASK_QUERY_PARAM_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /password/i,\n /pwd/i,\n];\nconst MASK_HEADER_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /cookie/i,\n];\n\nexport type Request = {\n timestamp: number;\n method: string;\n path?: string;\n url: string;\n headers: [string, string][];\n size?: number;\n consumer?: string;\n body?: Buffer;\n};\n\nexport type Response = {\n statusCode: number;\n responseTime: number;\n headers: [string, string][];\n size?: number;\n body?: Buffer;\n};\n\nexport type RequestLoggingConfig = {\n enabled: boolean;\n logQueryParams: boolean;\n logRequestHeaders: boolean;\n logRequestBody: boolean;\n logResponseHeaders: boolean;\n logResponseBody: boolean;\n maskQueryParams: RegExp[];\n maskHeaders: RegExp[];\n maskRequestBodyCallback?: (request: Request) => Buffer | null | undefined;\n maskResponseBodyCallback?: (\n request: Request,\n response: Response,\n ) => Buffer | null | undefined;\n excludePaths: RegExp[];\n excludeCallback?: (request: Request, response: Response) => boolean;\n};\n\nconst DEFAULT_CONFIG: RequestLoggingConfig = {\n enabled: false,\n logQueryParams: true,\n logRequestHeaders: false,\n logRequestBody: false,\n logResponseHeaders: true,\n logResponseBody: false,\n maskQueryParams: [],\n maskHeaders: [],\n excludePaths: [],\n};\n\nexport default class RequestLogger {\n public config: RequestLoggingConfig;\n public enabled: boolean;\n public suspendUntil: number | null = null;\n private pendingWrites: string[] = [];\n private currentFile: TempGzipFile | null = null;\n private files: TempGzipFile[] = [];\n private maintainIntervalId?: NodeJS.Timeout;\n private lock = new AsyncLock();\n\n constructor(config?: Partial<RequestLoggingConfig>) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.enabled = this.config.enabled && checkWritableFs();\n\n if (this.enabled) {\n this.maintainIntervalId = setInterval(() => {\n this.maintain();\n }, 1000);\n }\n }\n\n private shouldExcludePath(urlPath: string) {\n const patterns = [...this.config.excludePaths, ...EXCLUDE_PATH_PATTERNS];\n return matchPatterns(urlPath, patterns);\n }\n\n private shouldExcludeUserAgent(userAgent?: string) {\n return userAgent\n ? matchPatterns(userAgent, EXCLUDE_USER_AGENT_PATTERNS)\n : false;\n }\n\n private shouldMaskQueryParam(name: string) {\n const patterns = [\n ...this.config.maskQueryParams,\n ...MASK_QUERY_PARAM_PATTERNS,\n ];\n return matchPatterns(name, patterns);\n }\n\n private shouldMaskHeader(name: string) {\n const patterns = [...this.config.maskHeaders, ...MASK_HEADER_PATTERNS];\n return matchPatterns(name, patterns);\n }\n\n private hasSupportedContentType(headers: [string, string][]) {\n const contentType = headers.find(\n ([k]) => k.toLowerCase() === \"content-type\",\n )?.[1];\n return this.isSupportedContentType(contentType);\n }\n\n public isSupportedContentType(contentType?: string | null) {\n return (\n typeof contentType === \"string\" &&\n ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t))\n );\n }\n\n private maskQueryParams(search: string) {\n const params = new URLSearchParams(search);\n for (const [key] of params) {\n if (this.shouldMaskQueryParam(key)) {\n params.set(key, MASKED);\n }\n }\n return params.toString();\n }\n\n private maskHeaders(headers: [string, string][]): [string, string][] {\n return headers.map(([k, v]) => [k, this.shouldMaskHeader(k) ? MASKED : v]);\n }\n\n logRequest(request: Request, response: Response) {\n if (!this.enabled || this.suspendUntil !== null) return;\n\n const url = new URL(request.url);\n const path = request.path ?? url.pathname;\n const userAgent = request.headers.find(\n ([k]) => k.toLowerCase() === \"user-agent\",\n )?.[1];\n\n if (\n this.shouldExcludePath(path) ||\n this.shouldExcludeUserAgent(userAgent) ||\n (this.config.excludeCallback?.(request, response) ?? false)\n ) {\n return;\n }\n\n // Process query params\n url.search = this.config.logQueryParams\n ? this.maskQueryParams(url.search)\n : \"\";\n request.url = url.toString();\n\n // Process request body\n if (\n !this.config.logRequestBody ||\n !this.hasSupportedContentType(request.headers)\n ) {\n request.body = undefined;\n } else if (request.body) {\n if (request.body.length > MAX_BODY_SIZE) {\n request.body = BODY_TOO_LARGE;\n } else if (this.config.maskRequestBodyCallback) {\n try {\n request.body =\n this.config.maskRequestBodyCallback(request) ?? BODY_MASKED;\n if (request.body.length > MAX_BODY_SIZE) {\n request.body = BODY_TOO_LARGE;\n }\n } catch {\n request.body = undefined;\n }\n }\n }\n\n // Process response body\n if (\n !this.config.logResponseBody ||\n !this.hasSupportedContentType(response.headers)\n ) {\n response.body = undefined;\n } else if (response.body) {\n if (response.body.length > MAX_BODY_SIZE) {\n response.body = BODY_TOO_LARGE;\n } else if (this.config.maskResponseBodyCallback) {\n try {\n response.body =\n this.config.maskResponseBodyCallback(request, response) ??\n BODY_MASKED;\n if (response.body.length > MAX_BODY_SIZE) {\n response.body = BODY_TOO_LARGE;\n }\n } catch {\n response.body = undefined;\n }\n }\n }\n\n // Process headers\n request.headers = this.config.logRequestHeaders\n ? this.maskHeaders(request.headers)\n : [];\n response.headers = this.config.logResponseHeaders\n ? this.maskHeaders(response.headers)\n : [];\n\n const item = {\n uuid: randomUUID(),\n request: skipEmptyValues(request),\n response: skipEmptyValues(response),\n };\n [item.request.body, item.response.body].forEach((body) => {\n if (body) {\n // @ts-expect-error Different return type\n body.toJSON = function () {\n return this.toString(\"base64\");\n };\n }\n });\n this.pendingWrites.push(JSON.stringify(item));\n\n if (this.pendingWrites.length > MAX_PENDING_WRITES) {\n this.pendingWrites.shift();\n }\n }\n\n async writeToFile() {\n if (!this.enabled || this.pendingWrites.length === 0) {\n return;\n }\n return this.lock.acquire(\"file\", async () => {\n if (!this.currentFile) {\n this.currentFile = new TempGzipFile();\n }\n while (this.pendingWrites.length > 0) {\n const item = this.pendingWrites.shift();\n if (item) {\n await this.currentFile.writeLine(Buffer.from(item));\n }\n }\n });\n }\n\n getFile() {\n return this.files.shift();\n }\n\n retryFileLater(file: TempGzipFile) {\n this.files.unshift(file);\n }\n\n async rotateFile() {\n return this.lock.acquire(\"file\", async () => {\n if (this.currentFile) {\n await this.currentFile.close();\n this.files.push(this.currentFile);\n this.currentFile = null;\n }\n });\n }\n\n async maintain() {\n await this.writeToFile();\n if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {\n await this.rotateFile();\n }\n while (this.files.length > MAX_FILES) {\n const file = this.files.shift();\n file?.delete();\n }\n if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {\n this.suspendUntil = null;\n }\n }\n\n async clear() {\n this.pendingWrites = [];\n await this.rotateFile();\n this.files.forEach((file) => {\n file.delete();\n });\n this.files = [];\n }\n\n async close() {\n this.enabled = false;\n await this.clear();\n if (this.maintainIntervalId) {\n clearInterval(this.maintainIntervalId);\n }\n }\n}\n\nexport function convertHeaders(\n headers:\n | Headers\n | IncomingHttpHeaders\n | OutgoingHttpHeaders\n | Record<string, string | string[] | number | undefined>,\n) {\n if (headers instanceof Headers) {\n return Array.from(headers.entries());\n }\n return Object.entries(headers).flatMap(([key, value]) => {\n if (value === undefined) {\n return [];\n }\n if (Array.isArray(value)) {\n return value.map((v) => [key, v]);\n }\n return [[key, value.toString()]];\n }) as [string, string][];\n}\n\nexport function convertBody(body: any, contentType?: string | null) {\n if (!body || !contentType) {\n return;\n }\n try {\n if (contentType.startsWith(\"application/json\")) {\n if (isValidJsonString(body)) {\n return Buffer.from(body);\n } else {\n return Buffer.from(JSON.stringify(body));\n }\n }\n if (contentType.startsWith(\"text/\") && typeof body === \"string\") {\n return Buffer.from(body);\n }\n } catch (error) {\n return;\n }\n}\n\nfunction isValidJsonString(body: any) {\n if (typeof body !== \"string\") {\n return false;\n }\n try {\n JSON.parse(body);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction matchPatterns(value: string, patterns: RegExp[]) {\n return patterns.some((pattern) => {\n return pattern.test(value);\n });\n}\n\nfunction skipEmptyValues<T extends Record<string, any>>(data: T) {\n return Object.fromEntries(\n Object.entries(data).filter(([_, v]) => {\n if (v == null || Number.isNaN(v)) return false;\n if (Array.isArray(v) || Buffer.isBuffer(v) || typeof v === \"string\") {\n return v.length > 0;\n }\n return true;\n }),\n ) as Partial<T>;\n}\n\nfunction checkWritableFs() {\n try {\n const testPath = join(tmpdir(), `apitally-${randomUUID()}`);\n writeFileSync(testPath, \"test\");\n unlinkSync(testPath);\n return true;\n } catch (error) {\n return false;\n }\n}\n","import { Buffer } from \"buffer\";\nimport { randomUUID } from \"crypto\";\nimport { createWriteStream, readFile, unlinkSync, WriteStream } from \"fs\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\nimport { createGzip, Gzip } from \"zlib\";\n\nexport default class TempGzipFile {\n public uuid: string;\n private filePath: string;\n private gzip: Gzip;\n private writeStream: WriteStream;\n private readyPromise: Promise<void>;\n private closedPromise: Promise<void>;\n\n constructor() {\n this.uuid = randomUUID();\n this.filePath = join(tmpdir(), `apitally-${this.uuid}.gz`);\n this.writeStream = createWriteStream(this.filePath);\n this.readyPromise = new Promise<void>((resolve, reject) => {\n this.writeStream.once(\"ready\", resolve);\n this.writeStream.once(\"error\", reject);\n });\n this.closedPromise = new Promise<void>((resolve, reject) => {\n this.writeStream.once(\"close\", resolve);\n this.writeStream.once(\"error\", reject);\n });\n this.gzip = createGzip();\n this.gzip.pipe(this.writeStream);\n }\n\n get size() {\n return this.writeStream.bytesWritten;\n }\n\n async writeLine(data: Buffer) {\n await this.readyPromise;\n return new Promise<void>((resolve, reject) => {\n this.gzip.write(Buffer.concat([data, Buffer.from(\"\\n\")]), (error) => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n }\n\n async getContent() {\n return new Promise<Buffer>((resolve, reject) => {\n readFile(this.filePath, (error, data) => {\n if (error) {\n reject(error);\n } else {\n resolve(data);\n }\n });\n });\n }\n\n async close() {\n await new Promise<void>((resolve) => {\n this.gzip.end(() => {\n resolve();\n });\n });\n await this.closedPromise;\n }\n\n async delete() {\n await this.close();\n unlinkSync(this.filePath);\n }\n}\n"],"mappings":"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","names":["import_buffer","import_crypto","import_fs","import_os","import_path","TempGzipFile","uuid","filePath","gzip","writeStream","readyPromise","closedPromise","constructor","randomUUID","join","tmpdir","createWriteStream","Promise","resolve","reject","once","createGzip","pipe","size","bytesWritten","writeLine","data","write","Buffer","concat","from","error","getContent","readFile","close","end","delete","unlinkSync","MAX_BODY_SIZE","MAX_FILE_SIZE","MAX_FILES","MAX_PENDING_WRITES","BODY_TOO_LARGE","Buffer","from","BODY_MASKED","MASKED","ALLOWED_CONTENT_TYPES","EXCLUDE_PATH_PATTERNS","EXCLUDE_USER_AGENT_PATTERNS","MASK_QUERY_PARAM_PATTERNS","MASK_HEADER_PATTERNS","DEFAULT_CONFIG","enabled","logQueryParams","logRequestHeaders","logRequestBody","logResponseHeaders","logResponseBody","maskQueryParams","maskHeaders","excludePaths","RequestLogger","config","suspendUntil","pendingWrites","currentFile","files","maintainIntervalId","lock","AsyncLock","constructor","checkWritableFs","setInterval","maintain","shouldExcludePath","urlPath","patterns","matchPatterns","shouldExcludeUserAgent","userAgent","shouldMaskQueryParam","name","shouldMaskHeader","hasSupportedContentType","headers","contentType","find","k","toLowerCase","isSupportedContentType","some","t","startsWith","search","params","URLSearchParams","key","set","toString","map","v","logRequest","request","response","url","URL","path","pathname","excludeCallback","body","undefined","length","maskRequestBodyCallback","maskResponseBodyCallback","item","uuid","randomUUID","skipEmptyValues","forEach","toJSON","push","JSON","stringify","shift","writeToFile","acquire","TempGzipFile","writeLine","getFile","retryFileLater","file","unshift","rotateFile","close","size","delete","Date","now","clear","clearInterval","convertHeaders","Headers","Array","entries","Object","flatMap","value","isArray","convertBody","isValidJsonString","error","parse","pattern","test","data","fromEntries","filter","_","Number","isNaN","isBuffer","testPath","join","tmpdir","writeFileSync","unlinkSync"]}
@@ -48,6 +48,7 @@ declare class RequestLogger {
48
48
  private shouldMaskQueryParam;
49
49
  private shouldMaskHeader;
50
50
  private hasSupportedContentType;
51
+ isSupportedContentType(contentType?: string | null): boolean;
51
52
  private maskQueryParams;
52
53
  private maskHeaders;
53
54
  logRequest(request: Request, response: Response): void;
@@ -48,6 +48,7 @@ declare class RequestLogger {
48
48
  private shouldMaskQueryParam;
49
49
  private shouldMaskHeader;
50
50
  private hasSupportedContentType;
51
+ isSupportedContentType(contentType?: string | null): boolean;
51
52
  private maskQueryParams;
52
53
  private maskHeaders;
53
54
  logRequest(request: Request, response: Response): void;
@@ -31,9 +31,9 @@ var _TempGzipFile = class _TempGzipFile {
31
31
  this.writeStream.once("ready", resolve);
32
32
  this.writeStream.once("error", reject);
33
33
  });
34
- this.closedPromise = new Promise((resolve) => {
34
+ this.closedPromise = new Promise((resolve, reject) => {
35
35
  this.writeStream.once("close", resolve);
36
- this.writeStream.once("error", resolve);
36
+ this.writeStream.once("error", reject);
37
37
  });
38
38
  this.gzip = createGzip();
39
39
  this.gzip.pipe(this.writeStream);
@@ -183,7 +183,10 @@ var _RequestLogger = class _RequestLogger {
183
183
  hasSupportedContentType(headers) {
184
184
  var _a;
185
185
  const contentType = (_a = headers.find(([k]) => k.toLowerCase() === "content-type")) == null ? void 0 : _a[1];
186
- return contentType !== void 0 && ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t));
186
+ return this.isSupportedContentType(contentType);
187
+ }
188
+ isSupportedContentType(contentType) {
189
+ return typeof contentType === "string" && ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t));
187
190
  }
188
191
  maskQueryParams(search) {
189
192
  const params = new URLSearchParams(search);
@@ -211,8 +214,6 @@ var _RequestLogger = class _RequestLogger {
211
214
  }
212
215
  url.search = this.config.logQueryParams ? this.maskQueryParams(url.search) : "";
213
216
  request.url = url.toString();
214
- request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
215
- response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
216
217
  if (!this.config.logRequestBody || !this.hasSupportedContentType(request.headers)) {
217
218
  request.body = void 0;
218
219
  } else if (request.body) {
@@ -245,6 +246,8 @@ var _RequestLogger = class _RequestLogger {
245
246
  }
246
247
  }
247
248
  }
249
+ request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
250
+ response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
248
251
  const item = {
249
252
  uuid: randomUUID2(),
250
253
  request: skipEmptyValues(request),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/common/requestLogger.ts","../../src/common/tempGzipFile.ts"],"sourcesContent":["import AsyncLock from \"async-lock\";\nimport { Buffer } from \"buffer\";\nimport { randomUUID } from \"crypto\";\nimport { unlinkSync, writeFileSync } from \"fs\";\nimport { IncomingHttpHeaders, OutgoingHttpHeaders } from \"http\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\n\nimport TempGzipFile from \"./tempGzipFile.js\";\n\nconst MAX_BODY_SIZE = 50_000; // 50 KB (uncompressed)\nconst MAX_FILE_SIZE = 1_000_000; // 1 MB (compressed)\nconst MAX_FILES = 50;\nconst MAX_PENDING_WRITES = 100;\nconst BODY_TOO_LARGE = Buffer.from(\"<body too large>\");\nconst BODY_MASKED = Buffer.from(\"<masked>\");\nconst MASKED = \"******\";\nconst ALLOWED_CONTENT_TYPES = [\"application/json\", \"text/plain\"];\nconst EXCLUDE_PATH_PATTERNS = [\n /\\/_?healthz?$/i,\n /\\/_?health[_-]?checks?$/i,\n /\\/_?heart[_-]?beats?$/i,\n /\\/ping$/i,\n /\\/ready$/i,\n /\\/live$/i,\n];\nconst EXCLUDE_USER_AGENT_PATTERNS = [\n /health[-_ ]?check/i,\n /microsoft-azure-application-lb/i,\n /googlehc/i,\n /kube-probe/i,\n];\nconst MASK_QUERY_PARAM_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /password/i,\n /pwd/i,\n];\nconst MASK_HEADER_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /cookie/i,\n];\n\nexport type Request = {\n timestamp: number;\n method: string;\n path?: string;\n url: string;\n headers: [string, string][];\n size?: number;\n consumer?: string;\n body?: Buffer;\n};\n\nexport type Response = {\n statusCode: number;\n responseTime: number;\n headers: [string, string][];\n size?: number;\n body?: Buffer;\n};\n\nexport type RequestLoggingConfig = {\n enabled: boolean;\n logQueryParams: boolean;\n logRequestHeaders: boolean;\n logRequestBody: boolean;\n logResponseHeaders: boolean;\n logResponseBody: boolean;\n maskQueryParams: RegExp[];\n maskHeaders: RegExp[];\n maskRequestBodyCallback?: (request: Request) => Buffer | null | undefined;\n maskResponseBodyCallback?: (\n request: Request,\n response: Response,\n ) => Buffer | null | undefined;\n excludePaths: RegExp[];\n excludeCallback?: (request: Request, response: Response) => boolean;\n};\n\nconst DEFAULT_CONFIG: RequestLoggingConfig = {\n enabled: false,\n logQueryParams: true,\n logRequestHeaders: false,\n logRequestBody: false,\n logResponseHeaders: true,\n logResponseBody: false,\n maskQueryParams: [],\n maskHeaders: [],\n excludePaths: [],\n};\n\nexport default class RequestLogger {\n public config: RequestLoggingConfig;\n public enabled: boolean;\n public suspendUntil: number | null = null;\n private pendingWrites: string[] = [];\n private currentFile: TempGzipFile | null = null;\n private files: TempGzipFile[] = [];\n private maintainIntervalId?: NodeJS.Timeout;\n private lock = new AsyncLock();\n\n constructor(config?: Partial<RequestLoggingConfig>) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.enabled = this.config.enabled && checkWritableFs();\n\n if (this.enabled) {\n this.maintainIntervalId = setInterval(() => {\n this.maintain();\n }, 1000);\n }\n }\n\n private shouldExcludePath(urlPath: string) {\n const patterns = [...this.config.excludePaths, ...EXCLUDE_PATH_PATTERNS];\n return matchPatterns(urlPath, patterns);\n }\n\n private shouldExcludeUserAgent(userAgent?: string) {\n return userAgent\n ? matchPatterns(userAgent, EXCLUDE_USER_AGENT_PATTERNS)\n : false;\n }\n\n private shouldMaskQueryParam(name: string) {\n const patterns = [\n ...this.config.maskQueryParams,\n ...MASK_QUERY_PARAM_PATTERNS,\n ];\n return matchPatterns(name, patterns);\n }\n\n private shouldMaskHeader(name: string) {\n const patterns = [...this.config.maskHeaders, ...MASK_HEADER_PATTERNS];\n return matchPatterns(name, patterns);\n }\n\n private hasSupportedContentType(headers: [string, string][]) {\n const contentType = headers.find(\n ([k]) => k.toLowerCase() === \"content-type\",\n )?.[1];\n return (\n contentType !== undefined &&\n ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t))\n );\n }\n\n private maskQueryParams(search: string) {\n const params = new URLSearchParams(search);\n for (const [key] of params) {\n if (this.shouldMaskQueryParam(key)) {\n params.set(key, MASKED);\n }\n }\n return params.toString();\n }\n\n private maskHeaders(headers: [string, string][]): [string, string][] {\n return headers.map(([k, v]) => [k, this.shouldMaskHeader(k) ? MASKED : v]);\n }\n\n logRequest(request: Request, response: Response) {\n if (!this.enabled || this.suspendUntil !== null) return;\n\n const url = new URL(request.url);\n const path = request.path ?? url.pathname;\n const userAgent = request.headers.find(\n ([k]) => k.toLowerCase() === \"user-agent\",\n )?.[1];\n\n if (\n this.shouldExcludePath(path) ||\n this.shouldExcludeUserAgent(userAgent) ||\n (this.config.excludeCallback?.(request, response) ?? false)\n ) {\n return;\n }\n\n // Process query params\n url.search = this.config.logQueryParams\n ? this.maskQueryParams(url.search)\n : \"\";\n request.url = url.toString();\n\n // Process headers\n request.headers = this.config.logRequestHeaders\n ? this.maskHeaders(request.headers)\n : [];\n response.headers = this.config.logResponseHeaders\n ? this.maskHeaders(response.headers)\n : [];\n\n // Process request body\n if (\n !this.config.logRequestBody ||\n !this.hasSupportedContentType(request.headers)\n ) {\n request.body = undefined;\n } else if (request.body) {\n if (request.body.length > MAX_BODY_SIZE) {\n request.body = BODY_TOO_LARGE;\n } else if (this.config.maskRequestBodyCallback) {\n try {\n request.body =\n this.config.maskRequestBodyCallback(request) ?? BODY_MASKED;\n if (request.body.length > MAX_BODY_SIZE) {\n request.body = BODY_TOO_LARGE;\n }\n } catch {\n request.body = undefined;\n }\n }\n }\n\n // Process response body\n if (\n !this.config.logResponseBody ||\n !this.hasSupportedContentType(response.headers)\n ) {\n response.body = undefined;\n } else if (response.body) {\n if (response.body.length > MAX_BODY_SIZE) {\n response.body = BODY_TOO_LARGE;\n } else if (this.config.maskResponseBodyCallback) {\n try {\n response.body =\n this.config.maskResponseBodyCallback(request, response) ??\n BODY_MASKED;\n if (response.body.length > MAX_BODY_SIZE) {\n response.body = BODY_TOO_LARGE;\n }\n } catch {\n response.body = undefined;\n }\n }\n }\n\n const item = {\n uuid: randomUUID(),\n request: skipEmptyValues(request),\n response: skipEmptyValues(response),\n };\n [item.request.body, item.response.body].forEach((body) => {\n if (body) {\n // @ts-expect-error Different return type\n body.toJSON = function () {\n return this.toString(\"base64\");\n };\n }\n });\n this.pendingWrites.push(JSON.stringify(item));\n\n if (this.pendingWrites.length > MAX_PENDING_WRITES) {\n this.pendingWrites.shift();\n }\n }\n\n async writeToFile() {\n if (!this.enabled || this.pendingWrites.length === 0) {\n return;\n }\n return this.lock.acquire(\"file\", async () => {\n if (!this.currentFile) {\n this.currentFile = new TempGzipFile();\n }\n while (this.pendingWrites.length > 0) {\n const item = this.pendingWrites.shift();\n if (item) {\n await this.currentFile.writeLine(Buffer.from(item));\n }\n }\n });\n }\n\n getFile() {\n return this.files.shift();\n }\n\n retryFileLater(file: TempGzipFile) {\n this.files.unshift(file);\n }\n\n async rotateFile() {\n return this.lock.acquire(\"file\", async () => {\n if (this.currentFile) {\n await this.currentFile.close();\n this.files.push(this.currentFile);\n this.currentFile = null;\n }\n });\n }\n\n async maintain() {\n await this.writeToFile();\n if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {\n await this.rotateFile();\n }\n while (this.files.length > MAX_FILES) {\n const file = this.files.shift();\n file?.delete();\n }\n if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {\n this.suspendUntil = null;\n }\n }\n\n async clear() {\n this.pendingWrites = [];\n await this.rotateFile();\n this.files.forEach((file) => {\n file.delete();\n });\n this.files = [];\n }\n\n async close() {\n this.enabled = false;\n await this.clear();\n if (this.maintainIntervalId) {\n clearInterval(this.maintainIntervalId);\n }\n }\n}\n\nexport function convertHeaders(\n headers:\n | Headers\n | IncomingHttpHeaders\n | OutgoingHttpHeaders\n | Record<string, string | string[] | number | undefined>,\n) {\n if (headers instanceof Headers) {\n return Array.from(headers.entries());\n }\n return Object.entries(headers).flatMap(([key, value]) => {\n if (value === undefined) {\n return [];\n }\n if (Array.isArray(value)) {\n return value.map((v) => [key, v]);\n }\n return [[key, value.toString()]];\n }) as [string, string][];\n}\n\nexport function convertBody(body: any, contentType?: string | null) {\n if (!body || !contentType) {\n return;\n }\n try {\n if (contentType.startsWith(\"application/json\")) {\n if (isValidJsonString(body)) {\n return Buffer.from(body);\n } else {\n return Buffer.from(JSON.stringify(body));\n }\n }\n if (contentType.startsWith(\"text/\") && typeof body === \"string\") {\n return Buffer.from(body);\n }\n } catch (error) {\n return;\n }\n}\n\nfunction isValidJsonString(body: any) {\n if (typeof body !== \"string\") {\n return false;\n }\n try {\n JSON.parse(body);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction matchPatterns(value: string, patterns: RegExp[]) {\n return patterns.some((pattern) => {\n return pattern.test(value);\n });\n}\n\nfunction skipEmptyValues<T extends Record<string, any>>(data: T) {\n return Object.fromEntries(\n Object.entries(data).filter(([_, v]) => {\n if (v == null || Number.isNaN(v)) return false;\n if (Array.isArray(v) || Buffer.isBuffer(v) || typeof v === \"string\") {\n return v.length > 0;\n }\n return true;\n }),\n ) as Partial<T>;\n}\n\nfunction checkWritableFs() {\n try {\n const testPath = join(tmpdir(), `apitally-${randomUUID()}`);\n writeFileSync(testPath, \"test\");\n unlinkSync(testPath);\n return true;\n } catch (error) {\n return false;\n }\n}\n","import { Buffer } from \"buffer\";\nimport { randomUUID } from \"crypto\";\nimport { createWriteStream, readFile, unlinkSync, WriteStream } from \"fs\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\nimport { createGzip, Gzip } from \"zlib\";\n\nexport default class TempGzipFile {\n public uuid: string;\n private filePath: string;\n private gzip: Gzip;\n private writeStream: WriteStream;\n private readyPromise: Promise<void>;\n private closedPromise: Promise<void>;\n\n constructor() {\n this.uuid = randomUUID();\n this.filePath = join(tmpdir(), `apitally-${this.uuid}.gz`);\n this.writeStream = createWriteStream(this.filePath);\n this.readyPromise = new Promise<void>((resolve, reject) => {\n this.writeStream.once(\"ready\", resolve);\n this.writeStream.once(\"error\", reject);\n });\n this.closedPromise = new Promise<void>((resolve) => {\n this.writeStream.once(\"close\", resolve);\n this.writeStream.once(\"error\", resolve);\n });\n this.gzip = createGzip();\n this.gzip.pipe(this.writeStream);\n }\n\n get size() {\n return this.writeStream.bytesWritten;\n }\n\n async writeLine(data: Buffer) {\n await this.readyPromise;\n return new Promise<void>((resolve, reject) => {\n this.gzip.write(Buffer.concat([data, Buffer.from(\"\\n\")]), (error) => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n }\n\n async getContent() {\n return new Promise<Buffer>((resolve, reject) => {\n readFile(this.filePath, (error, data) => {\n if (error) {\n reject(error);\n } else {\n resolve(data);\n }\n });\n });\n }\n\n async close() {\n await new Promise<void>((resolve) => {\n this.gzip.end(() => {\n resolve();\n });\n });\n await this.closedPromise;\n }\n\n async delete() {\n await this.close();\n unlinkSync(this.filePath);\n }\n}\n"],"mappings":"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","names":["AsyncLock","Buffer","randomUUID","unlinkSync","writeFileSync","tmpdir","join","Buffer","randomUUID","createWriteStream","readFile","unlinkSync","tmpdir","join","createGzip","TempGzipFile","uuid","filePath","gzip","writeStream","readyPromise","closedPromise","constructor","randomUUID","join","tmpdir","createWriteStream","Promise","resolve","reject","once","createGzip","pipe","size","bytesWritten","writeLine","data","write","Buffer","concat","from","error","getContent","readFile","close","end","delete","unlinkSync","MAX_BODY_SIZE","MAX_FILE_SIZE","MAX_FILES","MAX_PENDING_WRITES","BODY_TOO_LARGE","Buffer","from","BODY_MASKED","MASKED","ALLOWED_CONTENT_TYPES","EXCLUDE_PATH_PATTERNS","EXCLUDE_USER_AGENT_PATTERNS","MASK_QUERY_PARAM_PATTERNS","MASK_HEADER_PATTERNS","DEFAULT_CONFIG","enabled","logQueryParams","logRequestHeaders","logRequestBody","logResponseHeaders","logResponseBody","maskQueryParams","maskHeaders","excludePaths","RequestLogger","config","suspendUntil","pendingWrites","currentFile","files","maintainIntervalId","lock","AsyncLock","constructor","checkWritableFs","setInterval","maintain","shouldExcludePath","urlPath","patterns","matchPatterns","shouldExcludeUserAgent","userAgent","shouldMaskQueryParam","name","shouldMaskHeader","hasSupportedContentType","headers","contentType","find","k","toLowerCase","undefined","some","t","startsWith","search","params","URLSearchParams","key","set","toString","map","v","logRequest","request","response","url","URL","path","pathname","excludeCallback","body","length","maskRequestBodyCallback","maskResponseBodyCallback","item","uuid","randomUUID","skipEmptyValues","forEach","toJSON","push","JSON","stringify","shift","writeToFile","acquire","TempGzipFile","writeLine","getFile","retryFileLater","file","unshift","rotateFile","close","size","delete","Date","now","clear","clearInterval","convertHeaders","Headers","Array","entries","Object","flatMap","value","isArray","convertBody","isValidJsonString","error","parse","pattern","test","data","fromEntries","filter","_","Number","isNaN","isBuffer","testPath","join","tmpdir","writeFileSync","unlinkSync"]}
1
+ {"version":3,"sources":["requestLogger.ts","tempGzipFile.ts"],"sourcesContent":["import AsyncLock from \"async-lock\";\nimport { Buffer } from \"buffer\";\nimport { randomUUID } from \"crypto\";\nimport { unlinkSync, writeFileSync } from \"fs\";\nimport { IncomingHttpHeaders, OutgoingHttpHeaders } from \"http\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\n\nimport TempGzipFile from \"./tempGzipFile.js\";\n\nconst MAX_BODY_SIZE = 50_000; // 50 KB (uncompressed)\nconst MAX_FILE_SIZE = 1_000_000; // 1 MB (compressed)\nconst MAX_FILES = 50;\nconst MAX_PENDING_WRITES = 100;\nconst BODY_TOO_LARGE = Buffer.from(\"<body too large>\");\nconst BODY_MASKED = Buffer.from(\"<masked>\");\nconst MASKED = \"******\";\nconst ALLOWED_CONTENT_TYPES = [\"application/json\", \"text/plain\"];\nconst EXCLUDE_PATH_PATTERNS = [\n /\\/_?healthz?$/i,\n /\\/_?health[_-]?checks?$/i,\n /\\/_?heart[_-]?beats?$/i,\n /\\/ping$/i,\n /\\/ready$/i,\n /\\/live$/i,\n];\nconst EXCLUDE_USER_AGENT_PATTERNS = [\n /health[-_ ]?check/i,\n /microsoft-azure-application-lb/i,\n /googlehc/i,\n /kube-probe/i,\n];\nconst MASK_QUERY_PARAM_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /password/i,\n /pwd/i,\n];\nconst MASK_HEADER_PATTERNS = [\n /auth/i,\n /api-?key/i,\n /secret/i,\n /token/i,\n /cookie/i,\n];\n\nexport type Request = {\n timestamp: number;\n method: string;\n path?: string;\n url: string;\n headers: [string, string][];\n size?: number;\n consumer?: string;\n body?: Buffer;\n};\n\nexport type Response = {\n statusCode: number;\n responseTime: number;\n headers: [string, string][];\n size?: number;\n body?: Buffer;\n};\n\nexport type RequestLoggingConfig = {\n enabled: boolean;\n logQueryParams: boolean;\n logRequestHeaders: boolean;\n logRequestBody: boolean;\n logResponseHeaders: boolean;\n logResponseBody: boolean;\n maskQueryParams: RegExp[];\n maskHeaders: RegExp[];\n maskRequestBodyCallback?: (request: Request) => Buffer | null | undefined;\n maskResponseBodyCallback?: (\n request: Request,\n response: Response,\n ) => Buffer | null | undefined;\n excludePaths: RegExp[];\n excludeCallback?: (request: Request, response: Response) => boolean;\n};\n\nconst DEFAULT_CONFIG: RequestLoggingConfig = {\n enabled: false,\n logQueryParams: true,\n logRequestHeaders: false,\n logRequestBody: false,\n logResponseHeaders: true,\n logResponseBody: false,\n maskQueryParams: [],\n maskHeaders: [],\n excludePaths: [],\n};\n\nexport default class RequestLogger {\n public config: RequestLoggingConfig;\n public enabled: boolean;\n public suspendUntil: number | null = null;\n private pendingWrites: string[] = [];\n private currentFile: TempGzipFile | null = null;\n private files: TempGzipFile[] = [];\n private maintainIntervalId?: NodeJS.Timeout;\n private lock = new AsyncLock();\n\n constructor(config?: Partial<RequestLoggingConfig>) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.enabled = this.config.enabled && checkWritableFs();\n\n if (this.enabled) {\n this.maintainIntervalId = setInterval(() => {\n this.maintain();\n }, 1000);\n }\n }\n\n private shouldExcludePath(urlPath: string) {\n const patterns = [...this.config.excludePaths, ...EXCLUDE_PATH_PATTERNS];\n return matchPatterns(urlPath, patterns);\n }\n\n private shouldExcludeUserAgent(userAgent?: string) {\n return userAgent\n ? matchPatterns(userAgent, EXCLUDE_USER_AGENT_PATTERNS)\n : false;\n }\n\n private shouldMaskQueryParam(name: string) {\n const patterns = [\n ...this.config.maskQueryParams,\n ...MASK_QUERY_PARAM_PATTERNS,\n ];\n return matchPatterns(name, patterns);\n }\n\n private shouldMaskHeader(name: string) {\n const patterns = [...this.config.maskHeaders, ...MASK_HEADER_PATTERNS];\n return matchPatterns(name, patterns);\n }\n\n private hasSupportedContentType(headers: [string, string][]) {\n const contentType = headers.find(\n ([k]) => k.toLowerCase() === \"content-type\",\n )?.[1];\n return this.isSupportedContentType(contentType);\n }\n\n public isSupportedContentType(contentType?: string | null) {\n return (\n typeof contentType === \"string\" &&\n ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t))\n );\n }\n\n private maskQueryParams(search: string) {\n const params = new URLSearchParams(search);\n for (const [key] of params) {\n if (this.shouldMaskQueryParam(key)) {\n params.set(key, MASKED);\n }\n }\n return params.toString();\n }\n\n private maskHeaders(headers: [string, string][]): [string, string][] {\n return headers.map(([k, v]) => [k, this.shouldMaskHeader(k) ? MASKED : v]);\n }\n\n logRequest(request: Request, response: Response) {\n if (!this.enabled || this.suspendUntil !== null) return;\n\n const url = new URL(request.url);\n const path = request.path ?? url.pathname;\n const userAgent = request.headers.find(\n ([k]) => k.toLowerCase() === \"user-agent\",\n )?.[1];\n\n if (\n this.shouldExcludePath(path) ||\n this.shouldExcludeUserAgent(userAgent) ||\n (this.config.excludeCallback?.(request, response) ?? false)\n ) {\n return;\n }\n\n // Process query params\n url.search = this.config.logQueryParams\n ? this.maskQueryParams(url.search)\n : \"\";\n request.url = url.toString();\n\n // Process request body\n if (\n !this.config.logRequestBody ||\n !this.hasSupportedContentType(request.headers)\n ) {\n request.body = undefined;\n } else if (request.body) {\n if (request.body.length > MAX_BODY_SIZE) {\n request.body = BODY_TOO_LARGE;\n } else if (this.config.maskRequestBodyCallback) {\n try {\n request.body =\n this.config.maskRequestBodyCallback(request) ?? BODY_MASKED;\n if (request.body.length > MAX_BODY_SIZE) {\n request.body = BODY_TOO_LARGE;\n }\n } catch {\n request.body = undefined;\n }\n }\n }\n\n // Process response body\n if (\n !this.config.logResponseBody ||\n !this.hasSupportedContentType(response.headers)\n ) {\n response.body = undefined;\n } else if (response.body) {\n if (response.body.length > MAX_BODY_SIZE) {\n response.body = BODY_TOO_LARGE;\n } else if (this.config.maskResponseBodyCallback) {\n try {\n response.body =\n this.config.maskResponseBodyCallback(request, response) ??\n BODY_MASKED;\n if (response.body.length > MAX_BODY_SIZE) {\n response.body = BODY_TOO_LARGE;\n }\n } catch {\n response.body = undefined;\n }\n }\n }\n\n // Process headers\n request.headers = this.config.logRequestHeaders\n ? this.maskHeaders(request.headers)\n : [];\n response.headers = this.config.logResponseHeaders\n ? this.maskHeaders(response.headers)\n : [];\n\n const item = {\n uuid: randomUUID(),\n request: skipEmptyValues(request),\n response: skipEmptyValues(response),\n };\n [item.request.body, item.response.body].forEach((body) => {\n if (body) {\n // @ts-expect-error Different return type\n body.toJSON = function () {\n return this.toString(\"base64\");\n };\n }\n });\n this.pendingWrites.push(JSON.stringify(item));\n\n if (this.pendingWrites.length > MAX_PENDING_WRITES) {\n this.pendingWrites.shift();\n }\n }\n\n async writeToFile() {\n if (!this.enabled || this.pendingWrites.length === 0) {\n return;\n }\n return this.lock.acquire(\"file\", async () => {\n if (!this.currentFile) {\n this.currentFile = new TempGzipFile();\n }\n while (this.pendingWrites.length > 0) {\n const item = this.pendingWrites.shift();\n if (item) {\n await this.currentFile.writeLine(Buffer.from(item));\n }\n }\n });\n }\n\n getFile() {\n return this.files.shift();\n }\n\n retryFileLater(file: TempGzipFile) {\n this.files.unshift(file);\n }\n\n async rotateFile() {\n return this.lock.acquire(\"file\", async () => {\n if (this.currentFile) {\n await this.currentFile.close();\n this.files.push(this.currentFile);\n this.currentFile = null;\n }\n });\n }\n\n async maintain() {\n await this.writeToFile();\n if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {\n await this.rotateFile();\n }\n while (this.files.length > MAX_FILES) {\n const file = this.files.shift();\n file?.delete();\n }\n if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {\n this.suspendUntil = null;\n }\n }\n\n async clear() {\n this.pendingWrites = [];\n await this.rotateFile();\n this.files.forEach((file) => {\n file.delete();\n });\n this.files = [];\n }\n\n async close() {\n this.enabled = false;\n await this.clear();\n if (this.maintainIntervalId) {\n clearInterval(this.maintainIntervalId);\n }\n }\n}\n\nexport function convertHeaders(\n headers:\n | Headers\n | IncomingHttpHeaders\n | OutgoingHttpHeaders\n | Record<string, string | string[] | number | undefined>,\n) {\n if (headers instanceof Headers) {\n return Array.from(headers.entries());\n }\n return Object.entries(headers).flatMap(([key, value]) => {\n if (value === undefined) {\n return [];\n }\n if (Array.isArray(value)) {\n return value.map((v) => [key, v]);\n }\n return [[key, value.toString()]];\n }) as [string, string][];\n}\n\nexport function convertBody(body: any, contentType?: string | null) {\n if (!body || !contentType) {\n return;\n }\n try {\n if (contentType.startsWith(\"application/json\")) {\n if (isValidJsonString(body)) {\n return Buffer.from(body);\n } else {\n return Buffer.from(JSON.stringify(body));\n }\n }\n if (contentType.startsWith(\"text/\") && typeof body === \"string\") {\n return Buffer.from(body);\n }\n } catch (error) {\n return;\n }\n}\n\nfunction isValidJsonString(body: any) {\n if (typeof body !== \"string\") {\n return false;\n }\n try {\n JSON.parse(body);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction matchPatterns(value: string, patterns: RegExp[]) {\n return patterns.some((pattern) => {\n return pattern.test(value);\n });\n}\n\nfunction skipEmptyValues<T extends Record<string, any>>(data: T) {\n return Object.fromEntries(\n Object.entries(data).filter(([_, v]) => {\n if (v == null || Number.isNaN(v)) return false;\n if (Array.isArray(v) || Buffer.isBuffer(v) || typeof v === \"string\") {\n return v.length > 0;\n }\n return true;\n }),\n ) as Partial<T>;\n}\n\nfunction checkWritableFs() {\n try {\n const testPath = join(tmpdir(), `apitally-${randomUUID()}`);\n writeFileSync(testPath, \"test\");\n unlinkSync(testPath);\n return true;\n } catch (error) {\n return false;\n }\n}\n","import { Buffer } from \"buffer\";\nimport { randomUUID } from \"crypto\";\nimport { createWriteStream, readFile, unlinkSync, WriteStream } from \"fs\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\nimport { createGzip, Gzip } from \"zlib\";\n\nexport default class TempGzipFile {\n public uuid: string;\n private filePath: string;\n private gzip: Gzip;\n private writeStream: WriteStream;\n private readyPromise: Promise<void>;\n private closedPromise: Promise<void>;\n\n constructor() {\n this.uuid = randomUUID();\n this.filePath = join(tmpdir(), `apitally-${this.uuid}.gz`);\n this.writeStream = createWriteStream(this.filePath);\n this.readyPromise = new Promise<void>((resolve, reject) => {\n this.writeStream.once(\"ready\", resolve);\n this.writeStream.once(\"error\", reject);\n });\n this.closedPromise = new Promise<void>((resolve, reject) => {\n this.writeStream.once(\"close\", resolve);\n this.writeStream.once(\"error\", reject);\n });\n this.gzip = createGzip();\n this.gzip.pipe(this.writeStream);\n }\n\n get size() {\n return this.writeStream.bytesWritten;\n }\n\n async writeLine(data: Buffer) {\n await this.readyPromise;\n return new Promise<void>((resolve, reject) => {\n this.gzip.write(Buffer.concat([data, Buffer.from(\"\\n\")]), (error) => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n }\n\n async getContent() {\n return new Promise<Buffer>((resolve, reject) => {\n readFile(this.filePath, (error, data) => {\n if (error) {\n reject(error);\n } else {\n resolve(data);\n }\n });\n });\n }\n\n async close() {\n await new Promise<void>((resolve) => {\n this.gzip.end(() => {\n resolve();\n });\n });\n await this.closedPromise;\n }\n\n async delete() {\n await this.close();\n unlinkSync(this.filePath);\n }\n}\n"],"mappings":"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","names":["AsyncLock","Buffer","randomUUID","unlinkSync","writeFileSync","tmpdir","join","Buffer","randomUUID","createWriteStream","readFile","unlinkSync","tmpdir","join","createGzip","TempGzipFile","uuid","filePath","gzip","writeStream","readyPromise","closedPromise","constructor","randomUUID","join","tmpdir","createWriteStream","Promise","resolve","reject","once","createGzip","pipe","size","bytesWritten","writeLine","data","write","Buffer","concat","from","error","getContent","readFile","close","end","delete","unlinkSync","MAX_BODY_SIZE","MAX_FILE_SIZE","MAX_FILES","MAX_PENDING_WRITES","BODY_TOO_LARGE","Buffer","from","BODY_MASKED","MASKED","ALLOWED_CONTENT_TYPES","EXCLUDE_PATH_PATTERNS","EXCLUDE_USER_AGENT_PATTERNS","MASK_QUERY_PARAM_PATTERNS","MASK_HEADER_PATTERNS","DEFAULT_CONFIG","enabled","logQueryParams","logRequestHeaders","logRequestBody","logResponseHeaders","logResponseBody","maskQueryParams","maskHeaders","excludePaths","RequestLogger","config","suspendUntil","pendingWrites","currentFile","files","maintainIntervalId","lock","AsyncLock","constructor","checkWritableFs","setInterval","maintain","shouldExcludePath","urlPath","patterns","matchPatterns","shouldExcludeUserAgent","userAgent","shouldMaskQueryParam","name","shouldMaskHeader","hasSupportedContentType","headers","contentType","find","k","toLowerCase","isSupportedContentType","some","t","startsWith","search","params","URLSearchParams","key","set","toString","map","v","logRequest","request","response","url","URL","path","pathname","excludeCallback","body","undefined","length","maskRequestBodyCallback","maskResponseBodyCallback","item","uuid","randomUUID","skipEmptyValues","forEach","toJSON","push","JSON","stringify","shift","writeToFile","acquire","TempGzipFile","writeLine","getFile","retryFileLater","file","unshift","rotateFile","close","size","delete","Date","now","clear","clearInterval","convertHeaders","Headers","Array","entries","Object","flatMap","value","isArray","convertBody","isValidJsonString","error","parse","pattern","test","data","fromEntries","filter","_","Number","isNaN","isBuffer","testPath","join","tmpdir","writeFileSync","unlinkSync"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/common/serverErrorCounter.ts"],"sourcesContent":["import type * as Sentry from \"@sentry/node\";\nimport { createHash } from \"crypto\";\n\nimport { ConsumerMethodPath, ServerError, ServerErrorsItem } from \"./types.js\";\n\nconst MAX_MSG_LENGTH = 2048;\nconst MAX_STACKTRACE_LENGTH = 65536;\n\nexport default class ServerErrorCounter {\n private errorCounts: Map<string, number>;\n private errorDetails: Map<string, ConsumerMethodPath & ServerError>;\n private sentryEventIds: Map<string, string>;\n private sentry: typeof Sentry | undefined;\n\n constructor() {\n this.errorCounts = new Map();\n this.errorDetails = new Map();\n this.sentryEventIds = new Map();\n this.tryImportSentry();\n }\n\n public addServerError(serverError: ConsumerMethodPath & ServerError) {\n const key = this.getKey(serverError);\n if (!this.errorDetails.has(key)) {\n this.errorDetails.set(key, serverError);\n }\n this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);\n this.captureSentryEventId(key);\n }\n\n public getAndResetServerErrors() {\n const data: Array<ServerErrorsItem> = [];\n this.errorCounts.forEach((count, key) => {\n const serverError = this.errorDetails.get(key);\n if (serverError) {\n data.push({\n consumer: serverError.consumer || null,\n method: serverError.method,\n path: serverError.path,\n type: serverError.type,\n msg: this.getTruncatedMessage(serverError.msg),\n traceback: this.getTruncatedStack(serverError.traceback),\n sentry_event_id: this.sentryEventIds.get(key) || null,\n error_count: count,\n });\n }\n });\n this.errorCounts.clear();\n this.errorDetails.clear();\n return data;\n }\n\n private getKey(serverError: ConsumerMethodPath & ServerError) {\n const hashInput = [\n serverError.consumer || \"\",\n serverError.method.toUpperCase(),\n serverError.path,\n serverError.type,\n serverError.msg.trim(),\n serverError.traceback.trim(),\n ].join(\"|\");\n return createHash(\"md5\").update(hashInput).digest(\"hex\");\n }\n\n private getTruncatedMessage(msg: string) {\n msg = msg.trim();\n if (msg.length <= MAX_MSG_LENGTH) {\n return msg;\n }\n const suffix = \"... (truncated)\";\n const cutoff = MAX_MSG_LENGTH - suffix.length;\n return msg.substring(0, cutoff) + suffix;\n }\n\n private getTruncatedStack(stack: string) {\n const suffix = \"... (truncated) ...\";\n const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;\n const lines = stack.trim().split(\"\\n\");\n const truncatedLines: string[] = [];\n let length = 0;\n for (const line of lines) {\n if (length + line.length + 1 > cutoff) {\n truncatedLines.push(suffix);\n break;\n }\n truncatedLines.push(line);\n length += line.length + 1;\n }\n return truncatedLines.join(\"\\n\");\n }\n\n private captureSentryEventId(serverErrorKey: string) {\n if (this.sentry && this.sentry.lastEventId) {\n const eventId = this.sentry.lastEventId();\n if (eventId) {\n this.sentryEventIds.set(serverErrorKey, eventId);\n }\n }\n }\n\n private async tryImportSentry() {\n try {\n this.sentry = await import(\"@sentry/node\");\n } catch (e) {\n // Sentry SDK is not installed, ignore\n }\n }\n}\n"],"mappings":"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","names":["MAX_MSG_LENGTH","MAX_STACKTRACE_LENGTH","ServerErrorCounter","errorCounts","errorDetails","sentryEventIds","sentry","constructor","Map","tryImportSentry","addServerError","serverError","key","getKey","has","set","get","captureSentryEventId","getAndResetServerErrors","data","forEach","count","push","consumer","method","path","type","msg","getTruncatedMessage","traceback","getTruncatedStack","sentry_event_id","error_count","clear","hashInput","toUpperCase","trim","join","createHash","update","digest","length","suffix","cutoff","substring","stack","lines","split","truncatedLines","line","serverErrorKey","lastEventId","eventId","e"]}
1
+ {"version":3,"sources":["serverErrorCounter.ts"],"sourcesContent":["import type * as Sentry from \"@sentry/node\";\nimport { createHash } from \"crypto\";\n\nimport { ConsumerMethodPath, ServerError, ServerErrorsItem } from \"./types.js\";\n\nconst MAX_MSG_LENGTH = 2048;\nconst MAX_STACKTRACE_LENGTH = 65536;\n\nexport default class ServerErrorCounter {\n private errorCounts: Map<string, number>;\n private errorDetails: Map<string, ConsumerMethodPath & ServerError>;\n private sentryEventIds: Map<string, string>;\n private sentry: typeof Sentry | undefined;\n\n constructor() {\n this.errorCounts = new Map();\n this.errorDetails = new Map();\n this.sentryEventIds = new Map();\n this.tryImportSentry();\n }\n\n public addServerError(serverError: ConsumerMethodPath & ServerError) {\n const key = this.getKey(serverError);\n if (!this.errorDetails.has(key)) {\n this.errorDetails.set(key, serverError);\n }\n this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);\n this.captureSentryEventId(key);\n }\n\n public getAndResetServerErrors() {\n const data: Array<ServerErrorsItem> = [];\n this.errorCounts.forEach((count, key) => {\n const serverError = this.errorDetails.get(key);\n if (serverError) {\n data.push({\n consumer: serverError.consumer || null,\n method: serverError.method,\n path: serverError.path,\n type: serverError.type,\n msg: this.getTruncatedMessage(serverError.msg),\n traceback: this.getTruncatedStack(serverError.traceback),\n sentry_event_id: this.sentryEventIds.get(key) || null,\n error_count: count,\n });\n }\n });\n this.errorCounts.clear();\n this.errorDetails.clear();\n return data;\n }\n\n private getKey(serverError: ConsumerMethodPath & ServerError) {\n const hashInput = [\n serverError.consumer || \"\",\n serverError.method.toUpperCase(),\n serverError.path,\n serverError.type,\n serverError.msg.trim(),\n serverError.traceback.trim(),\n ].join(\"|\");\n return createHash(\"md5\").update(hashInput).digest(\"hex\");\n }\n\n private getTruncatedMessage(msg: string) {\n msg = msg.trim();\n if (msg.length <= MAX_MSG_LENGTH) {\n return msg;\n }\n const suffix = \"... (truncated)\";\n const cutoff = MAX_MSG_LENGTH - suffix.length;\n return msg.substring(0, cutoff) + suffix;\n }\n\n private getTruncatedStack(stack: string) {\n const suffix = \"... (truncated) ...\";\n const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;\n const lines = stack.trim().split(\"\\n\");\n const truncatedLines: string[] = [];\n let length = 0;\n for (const line of lines) {\n if (length + line.length + 1 > cutoff) {\n truncatedLines.push(suffix);\n break;\n }\n truncatedLines.push(line);\n length += line.length + 1;\n }\n return truncatedLines.join(\"\\n\");\n }\n\n private captureSentryEventId(serverErrorKey: string) {\n if (this.sentry && this.sentry.lastEventId) {\n const eventId = this.sentry.lastEventId();\n if (eventId) {\n this.sentryEventIds.set(serverErrorKey, eventId);\n }\n }\n }\n\n private async tryImportSentry() {\n try {\n this.sentry = await import(\"@sentry/node\");\n } catch (e) {\n // Sentry SDK is not installed, ignore\n }\n }\n}\n"],"mappings":"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","names":["MAX_MSG_LENGTH","MAX_STACKTRACE_LENGTH","ServerErrorCounter","errorCounts","errorDetails","sentryEventIds","sentry","constructor","Map","tryImportSentry","addServerError","serverError","key","getKey","has","set","get","captureSentryEventId","getAndResetServerErrors","data","forEach","count","push","consumer","method","path","type","msg","getTruncatedMessage","traceback","getTruncatedStack","sentry_event_id","error_count","clear","hashInput","toUpperCase","trim","join","createHash","update","digest","length","suffix","cutoff","substring","stack","lines","split","truncatedLines","line","serverErrorKey","lastEventId","eventId","e"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/common/serverErrorCounter.ts"],"sourcesContent":["import type * as Sentry from \"@sentry/node\";\nimport { createHash } from \"crypto\";\n\nimport { ConsumerMethodPath, ServerError, ServerErrorsItem } from \"./types.js\";\n\nconst MAX_MSG_LENGTH = 2048;\nconst MAX_STACKTRACE_LENGTH = 65536;\n\nexport default class ServerErrorCounter {\n private errorCounts: Map<string, number>;\n private errorDetails: Map<string, ConsumerMethodPath & ServerError>;\n private sentryEventIds: Map<string, string>;\n private sentry: typeof Sentry | undefined;\n\n constructor() {\n this.errorCounts = new Map();\n this.errorDetails = new Map();\n this.sentryEventIds = new Map();\n this.tryImportSentry();\n }\n\n public addServerError(serverError: ConsumerMethodPath & ServerError) {\n const key = this.getKey(serverError);\n if (!this.errorDetails.has(key)) {\n this.errorDetails.set(key, serverError);\n }\n this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);\n this.captureSentryEventId(key);\n }\n\n public getAndResetServerErrors() {\n const data: Array<ServerErrorsItem> = [];\n this.errorCounts.forEach((count, key) => {\n const serverError = this.errorDetails.get(key);\n if (serverError) {\n data.push({\n consumer: serverError.consumer || null,\n method: serverError.method,\n path: serverError.path,\n type: serverError.type,\n msg: this.getTruncatedMessage(serverError.msg),\n traceback: this.getTruncatedStack(serverError.traceback),\n sentry_event_id: this.sentryEventIds.get(key) || null,\n error_count: count,\n });\n }\n });\n this.errorCounts.clear();\n this.errorDetails.clear();\n return data;\n }\n\n private getKey(serverError: ConsumerMethodPath & ServerError) {\n const hashInput = [\n serverError.consumer || \"\",\n serverError.method.toUpperCase(),\n serverError.path,\n serverError.type,\n serverError.msg.trim(),\n serverError.traceback.trim(),\n ].join(\"|\");\n return createHash(\"md5\").update(hashInput).digest(\"hex\");\n }\n\n private getTruncatedMessage(msg: string) {\n msg = msg.trim();\n if (msg.length <= MAX_MSG_LENGTH) {\n return msg;\n }\n const suffix = \"... (truncated)\";\n const cutoff = MAX_MSG_LENGTH - suffix.length;\n return msg.substring(0, cutoff) + suffix;\n }\n\n private getTruncatedStack(stack: string) {\n const suffix = \"... (truncated) ...\";\n const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;\n const lines = stack.trim().split(\"\\n\");\n const truncatedLines: string[] = [];\n let length = 0;\n for (const line of lines) {\n if (length + line.length + 1 > cutoff) {\n truncatedLines.push(suffix);\n break;\n }\n truncatedLines.push(line);\n length += line.length + 1;\n }\n return truncatedLines.join(\"\\n\");\n }\n\n private captureSentryEventId(serverErrorKey: string) {\n if (this.sentry && this.sentry.lastEventId) {\n const eventId = this.sentry.lastEventId();\n if (eventId) {\n this.sentryEventIds.set(serverErrorKey, eventId);\n }\n }\n }\n\n private async tryImportSentry() {\n try {\n this.sentry = await import(\"@sentry/node\");\n } catch (e) {\n // Sentry SDK is not installed, ignore\n }\n }\n}\n"],"mappings":"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","names":["createHash","MAX_MSG_LENGTH","MAX_STACKTRACE_LENGTH","ServerErrorCounter","errorCounts","errorDetails","sentryEventIds","sentry","constructor","Map","tryImportSentry","addServerError","serverError","key","getKey","has","set","get","captureSentryEventId","getAndResetServerErrors","data","forEach","count","push","consumer","method","path","type","msg","getTruncatedMessage","traceback","getTruncatedStack","sentry_event_id","error_count","clear","hashInput","toUpperCase","trim","join","createHash","update","digest","length","suffix","cutoff","substring","stack","lines","split","truncatedLines","line","serverErrorKey","lastEventId","eventId","e"]}
1
+ {"version":3,"sources":["serverErrorCounter.ts"],"sourcesContent":["import type * as Sentry from \"@sentry/node\";\nimport { createHash } from \"crypto\";\n\nimport { ConsumerMethodPath, ServerError, ServerErrorsItem } from \"./types.js\";\n\nconst MAX_MSG_LENGTH = 2048;\nconst MAX_STACKTRACE_LENGTH = 65536;\n\nexport default class ServerErrorCounter {\n private errorCounts: Map<string, number>;\n private errorDetails: Map<string, ConsumerMethodPath & ServerError>;\n private sentryEventIds: Map<string, string>;\n private sentry: typeof Sentry | undefined;\n\n constructor() {\n this.errorCounts = new Map();\n this.errorDetails = new Map();\n this.sentryEventIds = new Map();\n this.tryImportSentry();\n }\n\n public addServerError(serverError: ConsumerMethodPath & ServerError) {\n const key = this.getKey(serverError);\n if (!this.errorDetails.has(key)) {\n this.errorDetails.set(key, serverError);\n }\n this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);\n this.captureSentryEventId(key);\n }\n\n public getAndResetServerErrors() {\n const data: Array<ServerErrorsItem> = [];\n this.errorCounts.forEach((count, key) => {\n const serverError = this.errorDetails.get(key);\n if (serverError) {\n data.push({\n consumer: serverError.consumer || null,\n method: serverError.method,\n path: serverError.path,\n type: serverError.type,\n msg: this.getTruncatedMessage(serverError.msg),\n traceback: this.getTruncatedStack(serverError.traceback),\n sentry_event_id: this.sentryEventIds.get(key) || null,\n error_count: count,\n });\n }\n });\n this.errorCounts.clear();\n this.errorDetails.clear();\n return data;\n }\n\n private getKey(serverError: ConsumerMethodPath & ServerError) {\n const hashInput = [\n serverError.consumer || \"\",\n serverError.method.toUpperCase(),\n serverError.path,\n serverError.type,\n serverError.msg.trim(),\n serverError.traceback.trim(),\n ].join(\"|\");\n return createHash(\"md5\").update(hashInput).digest(\"hex\");\n }\n\n private getTruncatedMessage(msg: string) {\n msg = msg.trim();\n if (msg.length <= MAX_MSG_LENGTH) {\n return msg;\n }\n const suffix = \"... (truncated)\";\n const cutoff = MAX_MSG_LENGTH - suffix.length;\n return msg.substring(0, cutoff) + suffix;\n }\n\n private getTruncatedStack(stack: string) {\n const suffix = \"... (truncated) ...\";\n const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;\n const lines = stack.trim().split(\"\\n\");\n const truncatedLines: string[] = [];\n let length = 0;\n for (const line of lines) {\n if (length + line.length + 1 > cutoff) {\n truncatedLines.push(suffix);\n break;\n }\n truncatedLines.push(line);\n length += line.length + 1;\n }\n return truncatedLines.join(\"\\n\");\n }\n\n private captureSentryEventId(serverErrorKey: string) {\n if (this.sentry && this.sentry.lastEventId) {\n const eventId = this.sentry.lastEventId();\n if (eventId) {\n this.sentryEventIds.set(serverErrorKey, eventId);\n }\n }\n }\n\n private async tryImportSentry() {\n try {\n this.sentry = await import(\"@sentry/node\");\n } catch (e) {\n // Sentry SDK is not installed, ignore\n }\n }\n}\n"],"mappings":"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","names":["createHash","MAX_MSG_LENGTH","MAX_STACKTRACE_LENGTH","ServerErrorCounter","errorCounts","errorDetails","sentryEventIds","sentry","constructor","Map","tryImportSentry","addServerError","serverError","key","getKey","has","set","get","captureSentryEventId","getAndResetServerErrors","data","forEach","count","push","consumer","method","path","type","msg","getTruncatedMessage","traceback","getTruncatedStack","sentry_event_id","error_count","clear","hashInput","toUpperCase","trim","join","createHash","update","digest","length","suffix","cutoff","substring","stack","lines","split","truncatedLines","line","serverErrorKey","lastEventId","eventId","e"]}
@@ -45,9 +45,9 @@ var _TempGzipFile = class _TempGzipFile {
45
45
  this.writeStream.once("ready", resolve);
46
46
  this.writeStream.once("error", reject);
47
47
  });
48
- this.closedPromise = new Promise((resolve) => {
48
+ this.closedPromise = new Promise((resolve, reject) => {
49
49
  this.writeStream.once("close", resolve);
50
- this.writeStream.once("error", resolve);
50
+ this.writeStream.once("error", reject);
51
51
  });
52
52
  this.gzip = (0, import_zlib.createGzip)();
53
53
  this.gzip.pipe(this.writeStream);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/common/tempGzipFile.ts"],"sourcesContent":["import { Buffer } from \"buffer\";\nimport { randomUUID } from \"crypto\";\nimport { createWriteStream, readFile, unlinkSync, WriteStream } from \"fs\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\nimport { createGzip, Gzip } from \"zlib\";\n\nexport default class TempGzipFile {\n public uuid: string;\n private filePath: string;\n private gzip: Gzip;\n private writeStream: WriteStream;\n private readyPromise: Promise<void>;\n private closedPromise: Promise<void>;\n\n constructor() {\n this.uuid = randomUUID();\n this.filePath = join(tmpdir(), `apitally-${this.uuid}.gz`);\n this.writeStream = createWriteStream(this.filePath);\n this.readyPromise = new Promise<void>((resolve, reject) => {\n this.writeStream.once(\"ready\", resolve);\n this.writeStream.once(\"error\", reject);\n });\n this.closedPromise = new Promise<void>((resolve) => {\n this.writeStream.once(\"close\", resolve);\n this.writeStream.once(\"error\", resolve);\n });\n this.gzip = createGzip();\n this.gzip.pipe(this.writeStream);\n }\n\n get size() {\n return this.writeStream.bytesWritten;\n }\n\n async writeLine(data: Buffer) {\n await this.readyPromise;\n return new Promise<void>((resolve, reject) => {\n this.gzip.write(Buffer.concat([data, Buffer.from(\"\\n\")]), (error) => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n }\n\n async getContent() {\n return new Promise<Buffer>((resolve, reject) => {\n readFile(this.filePath, (error, data) => {\n if (error) {\n reject(error);\n } else {\n resolve(data);\n }\n });\n });\n }\n\n async close() {\n await new Promise<void>((resolve) => {\n this.gzip.end(() => {\n resolve();\n });\n });\n await this.closedPromise;\n }\n\n async delete() {\n await this.close();\n unlinkSync(this.filePath);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;;;;;oBAAuB;AACvB,oBAA2B;AAC3B,gBAAqE;AACrE,gBAAuB;AACvB,kBAAqB;AACrB,kBAAiC;AAEjC,IAAqBA,gBAArB,MAAqBA,cAAAA;EACZC;EACCC;EACAC;EACAC;EACAC;EACAC;EAERC,cAAc;AACZ,SAAKN,WAAOO,0BAAAA;AACZ,SAAKN,eAAWO,sBAAKC,kBAAAA,GAAU,YAAY,KAAKT,IAAI,KAAK;AACzD,SAAKG,kBAAcO,6BAAkB,KAAKT,QAAQ;AAClD,SAAKG,eAAe,IAAIO,QAAc,CAACC,SAASC,WAAAA;AAC9C,WAAKV,YAAYW,KAAK,SAASF,OAAAA;AAC/B,WAAKT,YAAYW,KAAK,SAASD,MAAAA;IACjC,CAAA;AACA,SAAKR,gBAAgB,IAAIM,QAAc,CAACC,YAAAA;AACtC,WAAKT,YAAYW,KAAK,SAASF,OAAAA;AAC/B,WAAKT,YAAYW,KAAK,SAASF,OAAAA;IACjC,CAAA;AACA,SAAKV,WAAOa,wBAAAA;AACZ,SAAKb,KAAKc,KAAK,KAAKb,WAAW;EACjC;EAEA,IAAIc,OAAO;AACT,WAAO,KAAKd,YAAYe;EAC1B;EAEA,MAAMC,UAAUC,MAAc;AAC5B,UAAM,KAAKhB;AACX,WAAO,IAAIO,QAAc,CAACC,SAASC,WAAAA;AACjC,WAAKX,KAAKmB,MAAMC,qBAAOC,OAAO;QAACH;QAAME,qBAAOE,KAAK,IAAA;OAAM,GAAG,CAACC,UAAAA;AACzD,YAAIA,OAAO;AACTZ,iBAAOY,KAAAA;QACT,OAAO;AACLb,kBAAAA;QACF;MACF,CAAA;IACF,CAAA;EACF;EAEA,MAAMc,aAAa;AACjB,WAAO,IAAIf,QAAgB,CAACC,SAASC,WAAAA;AACnCc,8BAAS,KAAK1B,UAAU,CAACwB,OAAOL,SAAAA;AAC9B,YAAIK,OAAO;AACTZ,iBAAOY,KAAAA;QACT,OAAO;AACLb,kBAAQQ,IAAAA;QACV;MACF,CAAA;IACF,CAAA;EACF;EAEA,MAAMQ,QAAQ;AACZ,UAAM,IAAIjB,QAAc,CAACC,YAAAA;AACvB,WAAKV,KAAK2B,IAAI,MAAA;AACZjB,gBAAAA;MACF,CAAA;IACF,CAAA;AACA,UAAM,KAAKP;EACb;EAEA,MAAMyB,SAAS;AACb,UAAM,KAAKF,MAAK;AAChBG,8BAAW,KAAK9B,QAAQ;EAC1B;AACF;AAlEqBF;AAArB,IAAqBA,eAArB;","names":["TempGzipFile","uuid","filePath","gzip","writeStream","readyPromise","closedPromise","constructor","randomUUID","join","tmpdir","createWriteStream","Promise","resolve","reject","once","createGzip","pipe","size","bytesWritten","writeLine","data","write","Buffer","concat","from","error","getContent","readFile","close","end","delete","unlinkSync"]}
1
+ {"version":3,"sources":["tempGzipFile.ts"],"sourcesContent":["import { Buffer } from \"buffer\";\nimport { randomUUID } from \"crypto\";\nimport { createWriteStream, readFile, unlinkSync, WriteStream } from \"fs\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\nimport { createGzip, Gzip } from \"zlib\";\n\nexport default class TempGzipFile {\n public uuid: string;\n private filePath: string;\n private gzip: Gzip;\n private writeStream: WriteStream;\n private readyPromise: Promise<void>;\n private closedPromise: Promise<void>;\n\n constructor() {\n this.uuid = randomUUID();\n this.filePath = join(tmpdir(), `apitally-${this.uuid}.gz`);\n this.writeStream = createWriteStream(this.filePath);\n this.readyPromise = new Promise<void>((resolve, reject) => {\n this.writeStream.once(\"ready\", resolve);\n this.writeStream.once(\"error\", reject);\n });\n this.closedPromise = new Promise<void>((resolve, reject) => {\n this.writeStream.once(\"close\", resolve);\n this.writeStream.once(\"error\", reject);\n });\n this.gzip = createGzip();\n this.gzip.pipe(this.writeStream);\n }\n\n get size() {\n return this.writeStream.bytesWritten;\n }\n\n async writeLine(data: Buffer) {\n await this.readyPromise;\n return new Promise<void>((resolve, reject) => {\n this.gzip.write(Buffer.concat([data, Buffer.from(\"\\n\")]), (error) => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n }\n\n async getContent() {\n return new Promise<Buffer>((resolve, reject) => {\n readFile(this.filePath, (error, data) => {\n if (error) {\n reject(error);\n } else {\n resolve(data);\n }\n });\n });\n }\n\n async close() {\n await new Promise<void>((resolve) => {\n this.gzip.end(() => {\n resolve();\n });\n });\n await this.closedPromise;\n }\n\n async delete() {\n await this.close();\n unlinkSync(this.filePath);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;;;;;oBAAuB;AACvB,oBAA2B;AAC3B,gBAAqE;AACrE,gBAAuB;AACvB,kBAAqB;AACrB,kBAAiC;AAEjC,IAAqBA,gBAArB,MAAqBA,cAAAA;EACZC;EACCC;EACAC;EACAC;EACAC;EACAC;EAERC,cAAc;AACZ,SAAKN,WAAOO,0BAAAA;AACZ,SAAKN,eAAWO,sBAAKC,kBAAAA,GAAU,YAAY,KAAKT,IAAI,KAAK;AACzD,SAAKG,kBAAcO,6BAAkB,KAAKT,QAAQ;AAClD,SAAKG,eAAe,IAAIO,QAAc,CAACC,SAASC,WAAAA;AAC9C,WAAKV,YAAYW,KAAK,SAASF,OAAAA;AAC/B,WAAKT,YAAYW,KAAK,SAASD,MAAAA;IACjC,CAAA;AACA,SAAKR,gBAAgB,IAAIM,QAAc,CAACC,SAASC,WAAAA;AAC/C,WAAKV,YAAYW,KAAK,SAASF,OAAAA;AAC/B,WAAKT,YAAYW,KAAK,SAASD,MAAAA;IACjC,CAAA;AACA,SAAKX,WAAOa,wBAAAA;AACZ,SAAKb,KAAKc,KAAK,KAAKb,WAAW;EACjC;EAEA,IAAIc,OAAO;AACT,WAAO,KAAKd,YAAYe;EAC1B;EAEA,MAAMC,UAAUC,MAAc;AAC5B,UAAM,KAAKhB;AACX,WAAO,IAAIO,QAAc,CAACC,SAASC,WAAAA;AACjC,WAAKX,KAAKmB,MAAMC,qBAAOC,OAAO;QAACH;QAAME,qBAAOE,KAAK,IAAA;OAAM,GAAG,CAACC,UAAAA;AACzD,YAAIA,OAAO;AACTZ,iBAAOY,KAAAA;QACT,OAAO;AACLb,kBAAAA;QACF;MACF,CAAA;IACF,CAAA;EACF;EAEA,MAAMc,aAAa;AACjB,WAAO,IAAIf,QAAgB,CAACC,SAASC,WAAAA;AACnCc,8BAAS,KAAK1B,UAAU,CAACwB,OAAOL,SAAAA;AAC9B,YAAIK,OAAO;AACTZ,iBAAOY,KAAAA;QACT,OAAO;AACLb,kBAAQQ,IAAAA;QACV;MACF,CAAA;IACF,CAAA;EACF;EAEA,MAAMQ,QAAQ;AACZ,UAAM,IAAIjB,QAAc,CAACC,YAAAA;AACvB,WAAKV,KAAK2B,IAAI,MAAA;AACZjB,gBAAAA;MACF,CAAA;IACF,CAAA;AACA,UAAM,KAAKP;EACb;EAEA,MAAMyB,SAAS;AACb,UAAM,KAAKF,MAAK;AAChBG,8BAAW,KAAK9B,QAAQ;EAC1B;AACF;AAlEqBF;AAArB,IAAqBA,eAArB;","names":["TempGzipFile","uuid","filePath","gzip","writeStream","readyPromise","closedPromise","constructor","randomUUID","join","tmpdir","createWriteStream","Promise","resolve","reject","once","createGzip","pipe","size","bytesWritten","writeLine","data","write","Buffer","concat","from","error","getContent","readFile","close","end","delete","unlinkSync"]}
@@ -23,9 +23,9 @@ var _TempGzipFile = class _TempGzipFile {
23
23
  this.writeStream.once("ready", resolve);
24
24
  this.writeStream.once("error", reject);
25
25
  });
26
- this.closedPromise = new Promise((resolve) => {
26
+ this.closedPromise = new Promise((resolve, reject) => {
27
27
  this.writeStream.once("close", resolve);
28
- this.writeStream.once("error", resolve);
28
+ this.writeStream.once("error", reject);
29
29
  });
30
30
  this.gzip = createGzip();
31
31
  this.gzip.pipe(this.writeStream);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/common/tempGzipFile.ts"],"sourcesContent":["import { Buffer } from \"buffer\";\nimport { randomUUID } from \"crypto\";\nimport { createWriteStream, readFile, unlinkSync, WriteStream } from \"fs\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\nimport { createGzip, Gzip } from \"zlib\";\n\nexport default class TempGzipFile {\n public uuid: string;\n private filePath: string;\n private gzip: Gzip;\n private writeStream: WriteStream;\n private readyPromise: Promise<void>;\n private closedPromise: Promise<void>;\n\n constructor() {\n this.uuid = randomUUID();\n this.filePath = join(tmpdir(), `apitally-${this.uuid}.gz`);\n this.writeStream = createWriteStream(this.filePath);\n this.readyPromise = new Promise<void>((resolve, reject) => {\n this.writeStream.once(\"ready\", resolve);\n this.writeStream.once(\"error\", reject);\n });\n this.closedPromise = new Promise<void>((resolve) => {\n this.writeStream.once(\"close\", resolve);\n this.writeStream.once(\"error\", resolve);\n });\n this.gzip = createGzip();\n this.gzip.pipe(this.writeStream);\n }\n\n get size() {\n return this.writeStream.bytesWritten;\n }\n\n async writeLine(data: Buffer) {\n await this.readyPromise;\n return new Promise<void>((resolve, reject) => {\n this.gzip.write(Buffer.concat([data, Buffer.from(\"\\n\")]), (error) => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n }\n\n async getContent() {\n return new Promise<Buffer>((resolve, reject) => {\n readFile(this.filePath, (error, data) => {\n if (error) {\n reject(error);\n } else {\n resolve(data);\n }\n });\n });\n }\n\n async close() {\n await new Promise<void>((resolve) => {\n this.gzip.end(() => {\n resolve();\n });\n });\n await this.closedPromise;\n }\n\n async delete() {\n await this.close();\n unlinkSync(this.filePath);\n }\n}\n"],"mappings":";;;;AAAA,SAASA,UAAAA,eAAc;AACvB,SAASC,kBAAkB;AAC3B,SAASC,mBAAmBC,UAAUC,kBAA+B;AACrE,SAASC,cAAc;AACvB,SAASC,YAAY;AACrB,SAASC,kBAAwB;AAEjC,IAAqBC,gBAArB,MAAqBA,cAAAA;EACZC;EACCC;EACAC;EACAC;EACAC;EACAC;EAERC,cAAc;AACZ,SAAKN,OAAOO,WAAAA;AACZ,SAAKN,WAAWO,KAAKC,OAAAA,GAAU,YAAY,KAAKT,IAAI,KAAK;AACzD,SAAKG,cAAcO,kBAAkB,KAAKT,QAAQ;AAClD,SAAKG,eAAe,IAAIO,QAAc,CAACC,SAASC,WAAAA;AAC9C,WAAKV,YAAYW,KAAK,SAASF,OAAAA;AAC/B,WAAKT,YAAYW,KAAK,SAASD,MAAAA;IACjC,CAAA;AACA,SAAKR,gBAAgB,IAAIM,QAAc,CAACC,YAAAA;AACtC,WAAKT,YAAYW,KAAK,SAASF,OAAAA;AAC/B,WAAKT,YAAYW,KAAK,SAASF,OAAAA;IACjC,CAAA;AACA,SAAKV,OAAOa,WAAAA;AACZ,SAAKb,KAAKc,KAAK,KAAKb,WAAW;EACjC;EAEA,IAAIc,OAAO;AACT,WAAO,KAAKd,YAAYe;EAC1B;EAEA,MAAMC,UAAUC,MAAc;AAC5B,UAAM,KAAKhB;AACX,WAAO,IAAIO,QAAc,CAACC,SAASC,WAAAA;AACjC,WAAKX,KAAKmB,MAAMC,QAAOC,OAAO;QAACH;QAAME,QAAOE,KAAK,IAAA;OAAM,GAAG,CAACC,UAAAA;AACzD,YAAIA,OAAO;AACTZ,iBAAOY,KAAAA;QACT,OAAO;AACLb,kBAAAA;QACF;MACF,CAAA;IACF,CAAA;EACF;EAEA,MAAMc,aAAa;AACjB,WAAO,IAAIf,QAAgB,CAACC,SAASC,WAAAA;AACnCc,eAAS,KAAK1B,UAAU,CAACwB,OAAOL,SAAAA;AAC9B,YAAIK,OAAO;AACTZ,iBAAOY,KAAAA;QACT,OAAO;AACLb,kBAAQQ,IAAAA;QACV;MACF,CAAA;IACF,CAAA;EACF;EAEA,MAAMQ,QAAQ;AACZ,UAAM,IAAIjB,QAAc,CAACC,YAAAA;AACvB,WAAKV,KAAK2B,IAAI,MAAA;AACZjB,gBAAAA;MACF,CAAA;IACF,CAAA;AACA,UAAM,KAAKP;EACb;EAEA,MAAMyB,SAAS;AACb,UAAM,KAAKF,MAAK;AAChBG,eAAW,KAAK9B,QAAQ;EAC1B;AACF;AAlEqBF;AAArB,IAAqBA,eAArB;","names":["Buffer","randomUUID","createWriteStream","readFile","unlinkSync","tmpdir","join","createGzip","TempGzipFile","uuid","filePath","gzip","writeStream","readyPromise","closedPromise","constructor","randomUUID","join","tmpdir","createWriteStream","Promise","resolve","reject","once","createGzip","pipe","size","bytesWritten","writeLine","data","write","Buffer","concat","from","error","getContent","readFile","close","end","delete","unlinkSync"]}
1
+ {"version":3,"sources":["tempGzipFile.ts"],"sourcesContent":["import { Buffer } from \"buffer\";\nimport { randomUUID } from \"crypto\";\nimport { createWriteStream, readFile, unlinkSync, WriteStream } from \"fs\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\nimport { createGzip, Gzip } from \"zlib\";\n\nexport default class TempGzipFile {\n public uuid: string;\n private filePath: string;\n private gzip: Gzip;\n private writeStream: WriteStream;\n private readyPromise: Promise<void>;\n private closedPromise: Promise<void>;\n\n constructor() {\n this.uuid = randomUUID();\n this.filePath = join(tmpdir(), `apitally-${this.uuid}.gz`);\n this.writeStream = createWriteStream(this.filePath);\n this.readyPromise = new Promise<void>((resolve, reject) => {\n this.writeStream.once(\"ready\", resolve);\n this.writeStream.once(\"error\", reject);\n });\n this.closedPromise = new Promise<void>((resolve, reject) => {\n this.writeStream.once(\"close\", resolve);\n this.writeStream.once(\"error\", reject);\n });\n this.gzip = createGzip();\n this.gzip.pipe(this.writeStream);\n }\n\n get size() {\n return this.writeStream.bytesWritten;\n }\n\n async writeLine(data: Buffer) {\n await this.readyPromise;\n return new Promise<void>((resolve, reject) => {\n this.gzip.write(Buffer.concat([data, Buffer.from(\"\\n\")]), (error) => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n }\n\n async getContent() {\n return new Promise<Buffer>((resolve, reject) => {\n readFile(this.filePath, (error, data) => {\n if (error) {\n reject(error);\n } else {\n resolve(data);\n }\n });\n });\n }\n\n async close() {\n await new Promise<void>((resolve) => {\n this.gzip.end(() => {\n resolve();\n });\n });\n await this.closedPromise;\n }\n\n async delete() {\n await this.close();\n unlinkSync(this.filePath);\n }\n}\n"],"mappings":";;;;AAAA,SAASA,UAAAA,eAAc;AACvB,SAASC,kBAAkB;AAC3B,SAASC,mBAAmBC,UAAUC,kBAA+B;AACrE,SAASC,cAAc;AACvB,SAASC,YAAY;AACrB,SAASC,kBAAwB;AAEjC,IAAqBC,gBAArB,MAAqBA,cAAAA;EACZC;EACCC;EACAC;EACAC;EACAC;EACAC;EAERC,cAAc;AACZ,SAAKN,OAAOO,WAAAA;AACZ,SAAKN,WAAWO,KAAKC,OAAAA,GAAU,YAAY,KAAKT,IAAI,KAAK;AACzD,SAAKG,cAAcO,kBAAkB,KAAKT,QAAQ;AAClD,SAAKG,eAAe,IAAIO,QAAc,CAACC,SAASC,WAAAA;AAC9C,WAAKV,YAAYW,KAAK,SAASF,OAAAA;AAC/B,WAAKT,YAAYW,KAAK,SAASD,MAAAA;IACjC,CAAA;AACA,SAAKR,gBAAgB,IAAIM,QAAc,CAACC,SAASC,WAAAA;AAC/C,WAAKV,YAAYW,KAAK,SAASF,OAAAA;AAC/B,WAAKT,YAAYW,KAAK,SAASD,MAAAA;IACjC,CAAA;AACA,SAAKX,OAAOa,WAAAA;AACZ,SAAKb,KAAKc,KAAK,KAAKb,WAAW;EACjC;EAEA,IAAIc,OAAO;AACT,WAAO,KAAKd,YAAYe;EAC1B;EAEA,MAAMC,UAAUC,MAAc;AAC5B,UAAM,KAAKhB;AACX,WAAO,IAAIO,QAAc,CAACC,SAASC,WAAAA;AACjC,WAAKX,KAAKmB,MAAMC,QAAOC,OAAO;QAACH;QAAME,QAAOE,KAAK,IAAA;OAAM,GAAG,CAACC,UAAAA;AACzD,YAAIA,OAAO;AACTZ,iBAAOY,KAAAA;QACT,OAAO;AACLb,kBAAAA;QACF;MACF,CAAA;IACF,CAAA;EACF;EAEA,MAAMc,aAAa;AACjB,WAAO,IAAIf,QAAgB,CAACC,SAASC,WAAAA;AACnCc,eAAS,KAAK1B,UAAU,CAACwB,OAAOL,SAAAA;AAC9B,YAAIK,OAAO;AACTZ,iBAAOY,KAAAA;QACT,OAAO;AACLb,kBAAQQ,IAAAA;QACV;MACF,CAAA;IACF,CAAA;EACF;EAEA,MAAMQ,QAAQ;AACZ,UAAM,IAAIjB,QAAc,CAACC,YAAAA;AACvB,WAAKV,KAAK2B,IAAI,MAAA;AACZjB,gBAAAA;MACF,CAAA;IACF,CAAA;AACA,UAAM,KAAKP;EACb;EAEA,MAAMyB,SAAS;AACb,UAAM,KAAKF,MAAK;AAChBG,eAAW,KAAK9B,QAAQ;EAC1B;AACF;AAlEqBF;AAArB,IAAqBA,eAArB;","names":["Buffer","randomUUID","createWriteStream","readFile","unlinkSync","tmpdir","join","createGzip","TempGzipFile","uuid","filePath","gzip","writeStream","readyPromise","closedPromise","constructor","randomUUID","join","tmpdir","createWriteStream","Promise","resolve","reject","once","createGzip","pipe","size","bytesWritten","writeLine","data","write","Buffer","concat","from","error","getContent","readFile","close","end","delete","unlinkSync"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/common/types.ts"],"sourcesContent":["import { Logger } from \"./logging.js\";\nimport { RequestLoggingConfig } from \"./requestLogger.js\";\n\nexport type ApitallyConfig = {\n clientId: string;\n env?: string;\n requestLoggingConfig?: Partial<RequestLoggingConfig>;\n openApiUrl?: string;\n appVersion?: string;\n logger?: Logger;\n};\n\nexport type ApitallyConsumer = {\n identifier: string;\n name?: string | null;\n group?: string | null;\n};\n\nexport type PathInfo = {\n method: string;\n path: string;\n};\n\nexport type StartupData = {\n paths: PathInfo[];\n versions: Record<string, string>;\n client: string;\n};\n\nexport type StartupPayload = {\n instance_uuid: string;\n message_uuid: string;\n} & StartupData;\n\nexport type ConsumerMethodPath = {\n consumer?: string | null;\n method: string;\n path: string;\n};\n\nexport type RequestInfo = ConsumerMethodPath & {\n statusCode: number;\n responseTime: number;\n requestSize?: string | number | null;\n responseSize?: string | number | null;\n};\n\nexport type RequestsItem = ConsumerMethodPath & {\n status_code: number;\n request_count: number;\n request_size_sum: number;\n response_size_sum: number;\n response_times: Record<number, number>;\n request_sizes: Record<number, number>;\n response_sizes: Record<number, number>;\n};\n\nexport type ValidationError = {\n loc: string;\n msg: string;\n type: string;\n};\n\nexport type ValidationErrorsItem = ConsumerMethodPath & {\n loc: Array<string>;\n msg: string;\n type: string;\n error_count: number;\n};\n\nexport type ServerError = {\n type: string;\n msg: string;\n traceback: string;\n};\n\nexport type ServerErrorsItem = ConsumerMethodPath & {\n type: string;\n msg: string;\n traceback: string;\n sentry_event_id: string | null;\n error_count: number;\n};\n\nexport type ConsumerItem = ApitallyConsumer;\n\nexport type SyncPayload = {\n timestamp: number;\n instance_uuid: string;\n message_uuid: string;\n requests: Array<RequestsItem>;\n validation_errors: Array<ValidationErrorsItem>;\n server_errors: Array<ServerErrorsItem>;\n consumers: Array<ConsumerItem>;\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAsFA;;","names":[]}
1
+ {"version":3,"sources":["types.ts"],"sourcesContent":["import { Logger } from \"./logging.js\";\nimport { RequestLoggingConfig } from \"./requestLogger.js\";\n\nexport type ApitallyConfig = {\n clientId: string;\n env?: string;\n requestLoggingConfig?: Partial<RequestLoggingConfig>;\n openApiUrl?: string;\n appVersion?: string;\n logger?: Logger;\n};\n\nexport type ApitallyConsumer = {\n identifier: string;\n name?: string | null;\n group?: string | null;\n};\n\nexport type PathInfo = {\n method: string;\n path: string;\n};\n\nexport type StartupData = {\n paths: PathInfo[];\n versions: Record<string, string>;\n client: string;\n};\n\nexport type StartupPayload = {\n instance_uuid: string;\n message_uuid: string;\n} & StartupData;\n\nexport type ConsumerMethodPath = {\n consumer?: string | null;\n method: string;\n path: string;\n};\n\nexport type RequestInfo = ConsumerMethodPath & {\n statusCode: number;\n responseTime: number;\n requestSize?: string | number | null;\n responseSize?: string | number | null;\n};\n\nexport type RequestsItem = ConsumerMethodPath & {\n status_code: number;\n request_count: number;\n request_size_sum: number;\n response_size_sum: number;\n response_times: Record<number, number>;\n request_sizes: Record<number, number>;\n response_sizes: Record<number, number>;\n};\n\nexport type ValidationError = {\n loc: string;\n msg: string;\n type: string;\n};\n\nexport type ValidationErrorsItem = ConsumerMethodPath & {\n loc: Array<string>;\n msg: string;\n type: string;\n error_count: number;\n};\n\nexport type ServerError = {\n type: string;\n msg: string;\n traceback: string;\n};\n\nexport type ServerErrorsItem = ConsumerMethodPath & {\n type: string;\n msg: string;\n traceback: string;\n sentry_event_id: string | null;\n error_count: number;\n};\n\nexport type ConsumerItem = ApitallyConsumer;\n\nexport type SyncPayload = {\n timestamp: number;\n instance_uuid: string;\n message_uuid: string;\n requests: Array<RequestsItem>;\n validation_errors: Array<ValidationErrorsItem>;\n server_errors: Array<ServerErrorsItem>;\n consumers: Array<ConsumerItem>;\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAsFA;;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/common/validationErrorCounter.ts"],"sourcesContent":["import { createHash } from \"crypto\";\n\nimport {\n ConsumerMethodPath,\n ValidationError,\n ValidationErrorsItem,\n} from \"./types.js\";\n\nexport default class ValidationErrorCounter {\n private errorCounts: Map<string, number>;\n private errorDetails: Map<string, ConsumerMethodPath & ValidationError>;\n\n constructor() {\n this.errorCounts = new Map();\n this.errorDetails = new Map();\n }\n\n public addValidationError(\n validationError: ConsumerMethodPath & ValidationError,\n ) {\n const key = this.getKey(validationError);\n if (!this.errorDetails.has(key)) {\n this.errorDetails.set(key, validationError);\n }\n this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);\n }\n\n public getAndResetValidationErrors() {\n const data: Array<ValidationErrorsItem> = [];\n this.errorCounts.forEach((count, key) => {\n const validationError = this.errorDetails.get(key);\n if (validationError) {\n data.push({\n consumer: validationError.consumer || null,\n method: validationError.method,\n path: validationError.path,\n loc: validationError.loc.split(\".\"),\n msg: validationError.msg,\n type: validationError.type,\n error_count: count,\n });\n }\n });\n this.errorCounts.clear();\n this.errorDetails.clear();\n return data;\n }\n\n private getKey(validationError: ConsumerMethodPath & ValidationError) {\n const hashInput = [\n validationError.consumer || \"\",\n validationError.method.toUpperCase(),\n validationError.path,\n validationError.loc,\n validationError.msg.trim(),\n validationError.type,\n ].join(\"|\");\n return createHash(\"md5\").update(hashInput).digest(\"hex\");\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;;;;;oBAA2B;AAQ3B,IAAqBA,0BAArB,MAAqBA,wBAAAA;EACXC;EACAC;EAERC,cAAc;AACZ,SAAKF,cAAc,oBAAIG,IAAAA;AACvB,SAAKF,eAAe,oBAAIE,IAAAA;EAC1B;EAEOC,mBACLC,iBACA;AACA,UAAMC,MAAM,KAAKC,OAAOF,eAAAA;AACxB,QAAI,CAAC,KAAKJ,aAAaO,IAAIF,GAAAA,GAAM;AAC/B,WAAKL,aAAaQ,IAAIH,KAAKD,eAAAA;IAC7B;AACA,SAAKL,YAAYS,IAAIH,MAAM,KAAKN,YAAYU,IAAIJ,GAAAA,KAAQ,KAAK,CAAA;EAC/D;EAEOK,8BAA8B;AACnC,UAAMC,OAAoC,CAAA;AAC1C,SAAKZ,YAAYa,QAAQ,CAACC,OAAOR,QAAAA;AAC/B,YAAMD,kBAAkB,KAAKJ,aAAaS,IAAIJ,GAAAA;AAC9C,UAAID,iBAAiB;AACnBO,aAAKG,KAAK;UACRC,UAAUX,gBAAgBW,YAAY;UACtCC,QAAQZ,gBAAgBY;UACxBC,MAAMb,gBAAgBa;UACtBC,KAAKd,gBAAgBc,IAAIC,MAAM,GAAA;UAC/BC,KAAKhB,gBAAgBgB;UACrBC,MAAMjB,gBAAgBiB;UACtBC,aAAaT;QACf,CAAA;MACF;IACF,CAAA;AACA,SAAKd,YAAYwB,MAAK;AACtB,SAAKvB,aAAauB,MAAK;AACvB,WAAOZ;EACT;EAEQL,OAAOF,iBAAuD;AACpE,UAAMoB,YAAY;MAChBpB,gBAAgBW,YAAY;MAC5BX,gBAAgBY,OAAOS,YAAW;MAClCrB,gBAAgBa;MAChBb,gBAAgBc;MAChBd,gBAAgBgB,IAAIM,KAAI;MACxBtB,gBAAgBiB;MAChBM,KAAK,GAAA;AACP,eAAOC,0BAAW,KAAA,EAAOC,OAAOL,SAAAA,EAAWM,OAAO,KAAA;EACpD;AACF;AAnDqBhC;AAArB,IAAqBA,yBAArB;","names":["ValidationErrorCounter","errorCounts","errorDetails","constructor","Map","addValidationError","validationError","key","getKey","has","set","get","getAndResetValidationErrors","data","forEach","count","push","consumer","method","path","loc","split","msg","type","error_count","clear","hashInput","toUpperCase","trim","join","createHash","update","digest"]}
1
+ {"version":3,"sources":["validationErrorCounter.ts"],"sourcesContent":["import { createHash } from \"crypto\";\n\nimport {\n ConsumerMethodPath,\n ValidationError,\n ValidationErrorsItem,\n} from \"./types.js\";\n\nexport default class ValidationErrorCounter {\n private errorCounts: Map<string, number>;\n private errorDetails: Map<string, ConsumerMethodPath & ValidationError>;\n\n constructor() {\n this.errorCounts = new Map();\n this.errorDetails = new Map();\n }\n\n public addValidationError(\n validationError: ConsumerMethodPath & ValidationError,\n ) {\n const key = this.getKey(validationError);\n if (!this.errorDetails.has(key)) {\n this.errorDetails.set(key, validationError);\n }\n this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);\n }\n\n public getAndResetValidationErrors() {\n const data: Array<ValidationErrorsItem> = [];\n this.errorCounts.forEach((count, key) => {\n const validationError = this.errorDetails.get(key);\n if (validationError) {\n data.push({\n consumer: validationError.consumer || null,\n method: validationError.method,\n path: validationError.path,\n loc: validationError.loc.split(\".\"),\n msg: validationError.msg,\n type: validationError.type,\n error_count: count,\n });\n }\n });\n this.errorCounts.clear();\n this.errorDetails.clear();\n return data;\n }\n\n private getKey(validationError: ConsumerMethodPath & ValidationError) {\n const hashInput = [\n validationError.consumer || \"\",\n validationError.method.toUpperCase(),\n validationError.path,\n validationError.loc,\n validationError.msg.trim(),\n validationError.type,\n ].join(\"|\");\n return createHash(\"md5\").update(hashInput).digest(\"hex\");\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;;;;;oBAA2B;AAQ3B,IAAqBA,0BAArB,MAAqBA,wBAAAA;EACXC;EACAC;EAERC,cAAc;AACZ,SAAKF,cAAc,oBAAIG,IAAAA;AACvB,SAAKF,eAAe,oBAAIE,IAAAA;EAC1B;EAEOC,mBACLC,iBACA;AACA,UAAMC,MAAM,KAAKC,OAAOF,eAAAA;AACxB,QAAI,CAAC,KAAKJ,aAAaO,IAAIF,GAAAA,GAAM;AAC/B,WAAKL,aAAaQ,IAAIH,KAAKD,eAAAA;IAC7B;AACA,SAAKL,YAAYS,IAAIH,MAAM,KAAKN,YAAYU,IAAIJ,GAAAA,KAAQ,KAAK,CAAA;EAC/D;EAEOK,8BAA8B;AACnC,UAAMC,OAAoC,CAAA;AAC1C,SAAKZ,YAAYa,QAAQ,CAACC,OAAOR,QAAAA;AAC/B,YAAMD,kBAAkB,KAAKJ,aAAaS,IAAIJ,GAAAA;AAC9C,UAAID,iBAAiB;AACnBO,aAAKG,KAAK;UACRC,UAAUX,gBAAgBW,YAAY;UACtCC,QAAQZ,gBAAgBY;UACxBC,MAAMb,gBAAgBa;UACtBC,KAAKd,gBAAgBc,IAAIC,MAAM,GAAA;UAC/BC,KAAKhB,gBAAgBgB;UACrBC,MAAMjB,gBAAgBiB;UACtBC,aAAaT;QACf,CAAA;MACF;IACF,CAAA;AACA,SAAKd,YAAYwB,MAAK;AACtB,SAAKvB,aAAauB,MAAK;AACvB,WAAOZ;EACT;EAEQL,OAAOF,iBAAuD;AACpE,UAAMoB,YAAY;MAChBpB,gBAAgBW,YAAY;MAC5BX,gBAAgBY,OAAOS,YAAW;MAClCrB,gBAAgBa;MAChBb,gBAAgBc;MAChBd,gBAAgBgB,IAAIM,KAAI;MACxBtB,gBAAgBiB;MAChBM,KAAK,GAAA;AACP,eAAOC,0BAAW,KAAA,EAAOC,OAAOL,SAAAA,EAAWM,OAAO,KAAA;EACpD;AACF;AAnDqBhC;AAArB,IAAqBA,yBAArB;","names":["ValidationErrorCounter","errorCounts","errorDetails","constructor","Map","addValidationError","validationError","key","getKey","has","set","get","getAndResetValidationErrors","data","forEach","count","push","consumer","method","path","loc","split","msg","type","error_count","clear","hashInput","toUpperCase","trim","join","createHash","update","digest"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/common/validationErrorCounter.ts"],"sourcesContent":["import { createHash } from \"crypto\";\n\nimport {\n ConsumerMethodPath,\n ValidationError,\n ValidationErrorsItem,\n} from \"./types.js\";\n\nexport default class ValidationErrorCounter {\n private errorCounts: Map<string, number>;\n private errorDetails: Map<string, ConsumerMethodPath & ValidationError>;\n\n constructor() {\n this.errorCounts = new Map();\n this.errorDetails = new Map();\n }\n\n public addValidationError(\n validationError: ConsumerMethodPath & ValidationError,\n ) {\n const key = this.getKey(validationError);\n if (!this.errorDetails.has(key)) {\n this.errorDetails.set(key, validationError);\n }\n this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);\n }\n\n public getAndResetValidationErrors() {\n const data: Array<ValidationErrorsItem> = [];\n this.errorCounts.forEach((count, key) => {\n const validationError = this.errorDetails.get(key);\n if (validationError) {\n data.push({\n consumer: validationError.consumer || null,\n method: validationError.method,\n path: validationError.path,\n loc: validationError.loc.split(\".\"),\n msg: validationError.msg,\n type: validationError.type,\n error_count: count,\n });\n }\n });\n this.errorCounts.clear();\n this.errorDetails.clear();\n return data;\n }\n\n private getKey(validationError: ConsumerMethodPath & ValidationError) {\n const hashInput = [\n validationError.consumer || \"\",\n validationError.method.toUpperCase(),\n validationError.path,\n validationError.loc,\n validationError.msg.trim(),\n validationError.type,\n ].join(\"|\");\n return createHash(\"md5\").update(hashInput).digest(\"hex\");\n }\n}\n"],"mappings":";;;;AAAA,SAASA,kBAAkB;AAQ3B,IAAqBC,0BAArB,MAAqBA,wBAAAA;EACXC;EACAC;EAERC,cAAc;AACZ,SAAKF,cAAc,oBAAIG,IAAAA;AACvB,SAAKF,eAAe,oBAAIE,IAAAA;EAC1B;EAEOC,mBACLC,iBACA;AACA,UAAMC,MAAM,KAAKC,OAAOF,eAAAA;AACxB,QAAI,CAAC,KAAKJ,aAAaO,IAAIF,GAAAA,GAAM;AAC/B,WAAKL,aAAaQ,IAAIH,KAAKD,eAAAA;IAC7B;AACA,SAAKL,YAAYS,IAAIH,MAAM,KAAKN,YAAYU,IAAIJ,GAAAA,KAAQ,KAAK,CAAA;EAC/D;EAEOK,8BAA8B;AACnC,UAAMC,OAAoC,CAAA;AAC1C,SAAKZ,YAAYa,QAAQ,CAACC,OAAOR,QAAAA;AAC/B,YAAMD,kBAAkB,KAAKJ,aAAaS,IAAIJ,GAAAA;AAC9C,UAAID,iBAAiB;AACnBO,aAAKG,KAAK;UACRC,UAAUX,gBAAgBW,YAAY;UACtCC,QAAQZ,gBAAgBY;UACxBC,MAAMb,gBAAgBa;UACtBC,KAAKd,gBAAgBc,IAAIC,MAAM,GAAA;UAC/BC,KAAKhB,gBAAgBgB;UACrBC,MAAMjB,gBAAgBiB;UACtBC,aAAaT;QACf,CAAA;MACF;IACF,CAAA;AACA,SAAKd,YAAYwB,MAAK;AACtB,SAAKvB,aAAauB,MAAK;AACvB,WAAOZ;EACT;EAEQL,OAAOF,iBAAuD;AACpE,UAAMoB,YAAY;MAChBpB,gBAAgBW,YAAY;MAC5BX,gBAAgBY,OAAOS,YAAW;MAClCrB,gBAAgBa;MAChBb,gBAAgBc;MAChBd,gBAAgBgB,IAAIM,KAAI;MACxBtB,gBAAgBiB;MAChBM,KAAK,GAAA;AACP,WAAOC,WAAW,KAAA,EAAOC,OAAOL,SAAAA,EAAWM,OAAO,KAAA;EACpD;AACF;AAnDqBhC;AAArB,IAAqBA,yBAArB;","names":["createHash","ValidationErrorCounter","errorCounts","errorDetails","constructor","Map","addValidationError","validationError","key","getKey","has","set","get","getAndResetValidationErrors","data","forEach","count","push","consumer","method","path","loc","split","msg","type","error_count","clear","hashInput","toUpperCase","trim","join","createHash","update","digest"]}
1
+ {"version":3,"sources":["validationErrorCounter.ts"],"sourcesContent":["import { createHash } from \"crypto\";\n\nimport {\n ConsumerMethodPath,\n ValidationError,\n ValidationErrorsItem,\n} from \"./types.js\";\n\nexport default class ValidationErrorCounter {\n private errorCounts: Map<string, number>;\n private errorDetails: Map<string, ConsumerMethodPath & ValidationError>;\n\n constructor() {\n this.errorCounts = new Map();\n this.errorDetails = new Map();\n }\n\n public addValidationError(\n validationError: ConsumerMethodPath & ValidationError,\n ) {\n const key = this.getKey(validationError);\n if (!this.errorDetails.has(key)) {\n this.errorDetails.set(key, validationError);\n }\n this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);\n }\n\n public getAndResetValidationErrors() {\n const data: Array<ValidationErrorsItem> = [];\n this.errorCounts.forEach((count, key) => {\n const validationError = this.errorDetails.get(key);\n if (validationError) {\n data.push({\n consumer: validationError.consumer || null,\n method: validationError.method,\n path: validationError.path,\n loc: validationError.loc.split(\".\"),\n msg: validationError.msg,\n type: validationError.type,\n error_count: count,\n });\n }\n });\n this.errorCounts.clear();\n this.errorDetails.clear();\n return data;\n }\n\n private getKey(validationError: ConsumerMethodPath & ValidationError) {\n const hashInput = [\n validationError.consumer || \"\",\n validationError.method.toUpperCase(),\n validationError.path,\n validationError.loc,\n validationError.msg.trim(),\n validationError.type,\n ].join(\"|\");\n return createHash(\"md5\").update(hashInput).digest(\"hex\");\n }\n}\n"],"mappings":";;;;AAAA,SAASA,kBAAkB;AAQ3B,IAAqBC,0BAArB,MAAqBA,wBAAAA;EACXC;EACAC;EAERC,cAAc;AACZ,SAAKF,cAAc,oBAAIG,IAAAA;AACvB,SAAKF,eAAe,oBAAIE,IAAAA;EAC1B;EAEOC,mBACLC,iBACA;AACA,UAAMC,MAAM,KAAKC,OAAOF,eAAAA;AACxB,QAAI,CAAC,KAAKJ,aAAaO,IAAIF,GAAAA,GAAM;AAC/B,WAAKL,aAAaQ,IAAIH,KAAKD,eAAAA;IAC7B;AACA,SAAKL,YAAYS,IAAIH,MAAM,KAAKN,YAAYU,IAAIJ,GAAAA,KAAQ,KAAK,CAAA;EAC/D;EAEOK,8BAA8B;AACnC,UAAMC,OAAoC,CAAA;AAC1C,SAAKZ,YAAYa,QAAQ,CAACC,OAAOR,QAAAA;AAC/B,YAAMD,kBAAkB,KAAKJ,aAAaS,IAAIJ,GAAAA;AAC9C,UAAID,iBAAiB;AACnBO,aAAKG,KAAK;UACRC,UAAUX,gBAAgBW,YAAY;UACtCC,QAAQZ,gBAAgBY;UACxBC,MAAMb,gBAAgBa;UACtBC,KAAKd,gBAAgBc,IAAIC,MAAM,GAAA;UAC/BC,KAAKhB,gBAAgBgB;UACrBC,MAAMjB,gBAAgBiB;UACtBC,aAAaT;QACf,CAAA;MACF;IACF,CAAA;AACA,SAAKd,YAAYwB,MAAK;AACtB,SAAKvB,aAAauB,MAAK;AACvB,WAAOZ;EACT;EAEQL,OAAOF,iBAAuD;AACpE,UAAMoB,YAAY;MAChBpB,gBAAgBW,YAAY;MAC5BX,gBAAgBY,OAAOS,YAAW;MAClCrB,gBAAgBa;MAChBb,gBAAgBc;MAChBd,gBAAgBgB,IAAIM,KAAI;MACxBtB,gBAAgBiB;MAChBM,KAAK,GAAA;AACP,WAAOC,WAAW,KAAA,EAAOC,OAAOL,SAAAA,EAAWM,OAAO,KAAA;EACpD;AACF;AAnDqBhC;AAArB,IAAqBA,yBAArB;","names":["createHash","ValidationErrorCounter","errorCounts","errorDetails","constructor","Map","addValidationError","validationError","key","getKey","has","set","get","getAndResetValidationErrors","data","forEach","count","push","consumer","method","path","loc","split","msg","type","error_count","clear","hashInput","toUpperCase","trim","join","createHash","update","digest"]}
@@ -240,9 +240,9 @@ var _TempGzipFile = class _TempGzipFile {
240
240
  this.writeStream.once("ready", resolve);
241
241
  this.writeStream.once("error", reject);
242
242
  });
243
- this.closedPromise = new Promise((resolve) => {
243
+ this.closedPromise = new Promise((resolve, reject) => {
244
244
  this.writeStream.once("close", resolve);
245
- this.writeStream.once("error", resolve);
245
+ this.writeStream.once("error", reject);
246
246
  });
247
247
  this.gzip = (0, import_zlib.createGzip)();
248
248
  this.gzip.pipe(this.writeStream);
@@ -392,7 +392,10 @@ var _RequestLogger = class _RequestLogger {
392
392
  hasSupportedContentType(headers) {
393
393
  var _a2;
394
394
  const contentType = (_a2 = headers.find(([k]) => k.toLowerCase() === "content-type")) == null ? void 0 : _a2[1];
395
- return contentType !== void 0 && ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t));
395
+ return this.isSupportedContentType(contentType);
396
+ }
397
+ isSupportedContentType(contentType) {
398
+ return typeof contentType === "string" && ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t));
396
399
  }
397
400
  maskQueryParams(search) {
398
401
  const params = new URLSearchParams(search);
@@ -420,8 +423,6 @@ var _RequestLogger = class _RequestLogger {
420
423
  }
421
424
  url.search = this.config.logQueryParams ? this.maskQueryParams(url.search) : "";
422
425
  request.url = url.toString();
423
- request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
424
- response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
425
426
  if (!this.config.logRequestBody || !this.hasSupportedContentType(request.headers)) {
426
427
  request.body = void 0;
427
428
  } else if (request.body) {
@@ -454,6 +455,8 @@ var _RequestLogger = class _RequestLogger {
454
455
  }
455
456
  }
456
457
  }
458
+ request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
459
+ response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
457
460
  const item = {
458
461
  uuid: (0, import_crypto2.randomUUID)(),
459
462
  request: skipEmptyValues(request),
@@ -790,6 +793,7 @@ var _ApitallyClient = class _ApitallyClient {
790
793
  syncIntervalId;
791
794
  startupData;
792
795
  startupDataSent = false;
796
+ enabled = true;
793
797
  requestCounter;
794
798
  requestLogger;
795
799
  validationErrorCounter;
@@ -826,12 +830,16 @@ var _ApitallyClient = class _ApitallyClient {
826
830
  }
827
831
  return _ApitallyClient.instance;
828
832
  }
833
+ isEnabled() {
834
+ return this.enabled;
835
+ }
829
836
  static async shutdown() {
830
837
  if (_ApitallyClient.instance) {
831
838
  await _ApitallyClient.instance.handleShutdown();
832
839
  }
833
840
  }
834
841
  async handleShutdown() {
842
+ this.enabled = false;
835
843
  this.stopSync();
836
844
  await this.sendSyncData();
837
845
  await this.sendLogData();
@@ -1015,6 +1023,7 @@ var _ApitallyClient = class _ApitallyClient {
1015
1023
  if (error instanceof HTTPError) {
1016
1024
  if (error.response.status === 404) {
1017
1025
  this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);
1026
+ this.enabled = false;
1018
1027
  this.stopSync();
1019
1028
  return true;
1020
1029
  }
@@ -1190,6 +1199,10 @@ var useApitally = /* @__PURE__ */ __name((app, config) => {
1190
1199
  var getMiddleware = /* @__PURE__ */ __name((app, client) => {
1191
1200
  let errorHandlerConfigured = false;
1192
1201
  return (req, res, next) => {
1202
+ if (!client.isEnabled()) {
1203
+ next();
1204
+ return;
1205
+ }
1193
1206
  if (!errorHandlerConfigured) {
1194
1207
  app.use((err, req2, res2, next2) => {
1195
1208
  res2.locals.serverError = err;
@@ -1201,7 +1214,10 @@ var getMiddleware = /* @__PURE__ */ __name((app, client) => {
1201
1214
  const startTime = import_perf_hooks.performance.now();
1202
1215
  const originalSend = res.send;
1203
1216
  res.send = (body) => {
1204
- res.locals.body = body;
1217
+ const contentType = res.get("content-type");
1218
+ if (client.requestLogger.isSupportedContentType(contentType)) {
1219
+ res.locals.body = body;
1220
+ }
1205
1221
  return originalSend.call(res, body);
1206
1222
  };
1207
1223
  res.on("finish", () => {