From e1ad63314b30822ff23eeb10297c770933a862f7 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Mon, 14 Aug 2023 15:59:54 -0500 Subject: [PATCH 01/22] chore(node): tidy up types --- package-lock.json | 96 ++++++++++++++++++- packages/node/package.json | 6 ++ packages/node/src/lib/construct-payload.ts | 2 +- packages/node/src/lib/get-project-base-url.ts | 2 +- packages/node/src/lib/log.ts | 6 +- packages/node/src/lib/metrics-log.ts | 2 +- packages/node/src/lib/object-to-array.ts | 2 +- packages/node/src/lib/patch-response.ts | 6 +- packages/node/src/lib/process-request.ts | 24 +++-- packages/node/tsconfig.json | 1 + 10 files changed, 126 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4835206611..7f934664c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1779,6 +1779,12 @@ "@types/node": "*" } }, + "node_modules/@types/content-type": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/content-type/-/content-type-1.1.5.tgz", + "integrity": "sha512-dgMN+syt1xb7Hk8LU6AODOfPlvz5z1CbXpPuJE5ZrX9STfBOIXF09pEB8N7a97WT9dbngt3ksDCm6GW6yMrxfQ==", + "dev": true + }, "node_modules/@types/debug": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", @@ -1846,6 +1852,18 @@ "@types/range-parser": "*" } }, + "node_modules/@types/find-cache-dir": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/find-cache-dir/-/find-cache-dir-3.2.1.tgz", + "integrity": "sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==", + "dev": true + }, + "node_modules/@types/flat-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/flat-cache/-/flat-cache-2.0.0.tgz", + "integrity": "sha512-fHeEsm9hvmZ+QHpw6Fkvf19KIhuqnYLU6vtWLjd5BsMd/qVi7iTkMioDZl0mQmfNRA1A6NwvhrSRNr9hGYZGww==", + "dev": true + }, "node_modules/@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -1917,6 +1935,12 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "node_modules/@types/lodash": { + "version": "4.14.197", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.197.tgz", + "integrity": "sha512-BMVOiWs0uNxHVlHBgzTIqJYmj+PgCo4euloGF+5m4okL3rEYzM2EEv78mw8zWSMM57dM7kVIgJ2QDvwHSoCI5g==", + "dev": true + }, "node_modules/@types/mdast": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", @@ -2049,12 +2073,27 @@ "integrity": "sha512-dPWnWsf+kzIG140B8z2w3fr5D03TLWbOAFQl45xUpI3vcizeXriNR5VYkWZ+WTMsUHqZ9Xlt3hrxGNANFyNQfw==", "dev": true }, + "node_modules/@types/type-is": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@types/type-is/-/type-is-1.6.3.tgz", + "integrity": "sha512-PNs5wHaNcBgCQG5nAeeZ7OvosrEsI9O4W2jAOO9BCCg4ux9ZZvH2+0iSCOIDBiKuQsiNS8CBlmfX9f5YBQ22cA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", "dev": true }, + "node_modules/@types/uuid": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.2.tgz", + "integrity": "sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==", + "dev": true + }, "node_modules/@types/yargs": { "version": "17.0.12", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.12.tgz", @@ -15174,7 +15213,7 @@ }, "packages/cloudflare-worker": { "name": "@readme/cloudflare-worker", - "version": "1.2.0", + "version": "1.2.1", "license": "ISC", "dependencies": { "minimatch": "^9.0.1" @@ -15197,7 +15236,7 @@ }, "packages/node": { "name": "readmeio", - "version": "6.1.0", + "version": "6.2.0", "license": "ISC", "dependencies": { "@types/har-format": "^1.2.10", @@ -15215,10 +15254,16 @@ "devDependencies": { "@readme/eslint-config": "^10.5.1", "@types/chai": "^4.3.4", + "@types/content-type": "^1.1.5", + "@types/find-cache-dir": "^3.2.1", + "@types/flat-cache": "^2.0.0", + "@types/lodash": "^4.14.197", "@types/mocha": "^10.0.1", "@types/multer": "^1.4.7", "@types/node": "^20.2.5", "@types/ssri": "^7.1.1", + "@types/type-is": "^1.6.3", + "@types/uuid": "^9.0.2", "caseless": "^0.12.0", "chai": "^4.3.7", "eslint": "^8.34.0", @@ -15261,7 +15306,7 @@ }, "packages/sdk-snippets": { "name": "@readme/metrics-sdk-snippets", - "version": "2.3.4", + "version": "2.4.0", "license": "ISC", "dependencies": { "@readme/httpsnippet": "^6.2.1" @@ -16690,6 +16735,12 @@ "@types/node": "*" } }, + "@types/content-type": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/content-type/-/content-type-1.1.5.tgz", + "integrity": "sha512-dgMN+syt1xb7Hk8LU6AODOfPlvz5z1CbXpPuJE5ZrX9STfBOIXF09pEB8N7a97WT9dbngt3ksDCm6GW6yMrxfQ==", + "dev": true + }, "@types/debug": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", @@ -16757,6 +16808,18 @@ "@types/range-parser": "*" } }, + "@types/find-cache-dir": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/find-cache-dir/-/find-cache-dir-3.2.1.tgz", + "integrity": "sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==", + "dev": true + }, + "@types/flat-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/flat-cache/-/flat-cache-2.0.0.tgz", + "integrity": "sha512-fHeEsm9hvmZ+QHpw6Fkvf19KIhuqnYLU6vtWLjd5BsMd/qVi7iTkMioDZl0mQmfNRA1A6NwvhrSRNr9hGYZGww==", + "dev": true + }, "@types/graceful-fs": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", @@ -16828,6 +16891,12 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "@types/lodash": { + "version": "4.14.197", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.197.tgz", + "integrity": "sha512-BMVOiWs0uNxHVlHBgzTIqJYmj+PgCo4euloGF+5m4okL3rEYzM2EEv78mw8zWSMM57dM7kVIgJ2QDvwHSoCI5g==", + "dev": true + }, "@types/mdast": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", @@ -16960,12 +17029,27 @@ "integrity": "sha512-dPWnWsf+kzIG140B8z2w3fr5D03TLWbOAFQl45xUpI3vcizeXriNR5VYkWZ+WTMsUHqZ9Xlt3hrxGNANFyNQfw==", "dev": true }, + "@types/type-is": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@types/type-is/-/type-is-1.6.3.tgz", + "integrity": "sha512-PNs5wHaNcBgCQG5nAeeZ7OvosrEsI9O4W2jAOO9BCCg4ux9ZZvH2+0iSCOIDBiKuQsiNS8CBlmfX9f5YBQ22cA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", "dev": true }, + "@types/uuid": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.2.tgz", + "integrity": "sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==", + "dev": true + }, "@types/yargs": { "version": "17.0.12", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.12.tgz", @@ -24295,12 +24379,18 @@ "requires": { "@readme/eslint-config": "^10.5.1", "@types/chai": "^4.3.4", + "@types/content-type": "^1.1.5", + "@types/find-cache-dir": "^3.2.1", + "@types/flat-cache": "^2.0.0", "@types/har-format": "^1.2.10", + "@types/lodash": "^4.14.197", "@types/mocha": "^10.0.1", "@types/multer": "^1.4.7", "@types/node": "^20.2.5", "@types/node-fetch": "^2.6.2", "@types/ssri": "^7.1.1", + "@types/type-is": "*", + "@types/uuid": "^9.0.2", "caseless": "^0.12.0", "chai": "^4.3.7", "content-type": "^1.0.5", diff --git a/packages/node/package.json b/packages/node/package.json index 157b10abfa..3379933679 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -40,10 +40,16 @@ "devDependencies": { "@readme/eslint-config": "^10.5.1", "@types/chai": "^4.3.4", + "@types/content-type": "^1.1.5", + "@types/find-cache-dir": "^3.2.1", + "@types/flat-cache": "^2.0.0", + "@types/lodash": "^4.14.197", "@types/mocha": "^10.0.1", "@types/multer": "^1.4.7", "@types/node": "^20.2.5", "@types/ssri": "^7.1.1", + "@types/type-is": "^1.6.3", + "@types/uuid": "^9.0.2", "caseless": "^0.12.0", "chai": "^4.3.7", "eslint": "^8.34.0", diff --git a/packages/node/src/lib/construct-payload.ts b/packages/node/src/lib/construct-payload.ts index 266fd55198..02c8768840 100644 --- a/packages/node/src/lib/construct-payload.ts +++ b/packages/node/src/lib/construct-payload.ts @@ -123,7 +123,7 @@ export interface PayloadData { * * With the last 4 digits on the end for us to use to identify it later in a list. */ -export function mask(apiKey) { +export function mask(apiKey: string) { return ssri .fromData(apiKey, { algorithms: ['sha512'], diff --git a/packages/node/src/lib/get-project-base-url.ts b/packages/node/src/lib/get-project-base-url.ts index a80413a8f3..6479e9849d 100644 --- a/packages/node/src/lib/get-project-base-url.ts +++ b/packages/node/src/lib/get-project-base-url.ts @@ -8,7 +8,7 @@ import timeoutSignal from 'timeout-signal'; import pkg from '../../package.json'; import config from '../config'; -export function getCache(readmeApiKey) { +export function getCache(readmeApiKey: string) { const encodedApiKey = Buffer.from(`${readmeApiKey}:`).toString('base64'); const cacheDir = findCacheDir({ name: pkg.name, create: true }); const fsSafeApikey = crypto.createHash('md5').update(encodedApiKey).digest('hex'); diff --git a/packages/node/src/lib/log.ts b/packages/node/src/lib/log.ts index c8aad77226..03834f0d1f 100644 --- a/packages/node/src/lib/log.ts +++ b/packages/node/src/lib/log.ts @@ -17,7 +17,7 @@ import { patchRequest } from './patch-request'; import { patchResponse } from './patch-response'; let queue: OutgoingLogBody[] = []; -function doSend(readmeApiKey, options) { +function doSend(readmeApiKey: string, options: Options) { // Copy the queue so we can send all the requests in one batch const json = [...queue]; // Clear out the queue so we don't resend any data in the future @@ -58,7 +58,7 @@ export interface ExtendedIncomingMessage extends IncomingMessage { } /* eslint-enable typescript-sort-keys/interface */ -interface ExtendedResponse extends ServerResponse { +export interface ExtendedResponse extends ServerResponse { _body?: string; } @@ -67,7 +67,7 @@ export interface Options extends LogOptions { bufferLength?: number; } -function setDocumentationHeader(res, baseLogUrl, logId) { +function setDocumentationHeader(res: ServerResponse, baseLogUrl: string, logId: string) { // This is to catch the potential race condition where `getProjectBaseUrl()` // takes longer to respond than the original req/res to finish. Without this // we would get an error that would be very difficult to trace. This could diff --git a/packages/node/src/lib/metrics-log.ts b/packages/node/src/lib/metrics-log.ts index 33bb1ee104..adac60c7bd 100644 --- a/packages/node/src/lib/metrics-log.ts +++ b/packages/node/src/lib/metrics-log.ts @@ -139,6 +139,6 @@ export function metricsAPICall( return { response, ids: getLogIds(body), - }; + } as LogResponse; }); } diff --git a/packages/node/src/lib/object-to-array.ts b/packages/node/src/lib/object-to-array.ts index 41121d36fb..ab554416f7 100644 --- a/packages/node/src/lib/object-to-array.ts +++ b/packages/node/src/lib/object-to-array.ts @@ -22,7 +22,7 @@ export function objectToArray( } export function searchToArray(search: URLSearchParams): { name: string; value: string }[] { - const final = []; + const final: { name: string; value: string }[] = []; search.forEach((value, name) => { final.push({ name, value }); diff --git a/packages/node/src/lib/patch-response.ts b/packages/node/src/lib/patch-response.ts index 5e4470bfe0..07a39ccb5d 100644 --- a/packages/node/src/lib/patch-response.ts +++ b/packages/node/src/lib/patch-response.ts @@ -2,17 +2,21 @@ // so we can send it off to the metrics server // It's unfortunate that this isn't accessible // natively. This may take up lots of memory on +import type { ExtendedResponse } from './log'; + // big responses, we can make it configurable in future -export function patchResponse(res) { +export function patchResponse(res: ExtendedResponse) { const { write, end } = res; res._body = ''; + // @ts-expect-error this feels scary to mess with further res.write = (chunk, encoding, cb) => { res._body += chunk; write.call(res, chunk, encoding, cb); }; + // @ts-expect-error this feels scary to mess with further res.end = (chunk, encoding, cb) => { // Chunk is optional in res.end // http://nodejs.org/dist/latest/docs/api/http.html#http_response_end_data_encoding_callback diff --git a/packages/node/src/lib/process-request.ts b/packages/node/src/lib/process-request.ts index 94816f4928..052c2a9413 100644 --- a/packages/node/src/lib/process-request.ts +++ b/packages/node/src/lib/process-request.ts @@ -38,7 +38,7 @@ export function fixHeader(header: string | number | string[]): string | undefine * @param value the value to be redacted * @returns A redacted string potentially containing the length of the original value, if it was a string */ -function redactValue(value: string) { +function redactValue(value: unknown) { const redactedVal = typeof value === 'string' ? ` ${value.length}` : ''; return `[REDACTED${redactedVal}]`; } @@ -50,7 +50,7 @@ function redactValue(value: string) { * @param redactedPaths a list of paths that point values which should be redacted * @returns An object with the redacted values */ -function redactProperties>(obj: T, redactedPaths = []): T { +function redactProperties>(obj: T, redactedPaths: string[] = []): T { const nextObj = { ...obj }; return redactedPaths.reduce((acc, path) => { const value = get(acc, path); @@ -61,19 +61,22 @@ function redactProperties>(obj: T, redactedPat /** * @param obj The data object that is operated upon - * @param cb A callback that is invoked for each value found, the return value being the next value that is set in the returned object + * @param replaceCb A callback that is invoked for each value found, the return value being the next value that is set in the returned object * @returns An object with the replaced values */ -function replaceEach(obj, cb): Record { +function replaceEach>( + obj: T, + replaceCb: (input: unknown) => string +): Record { return Object.keys(obj).reduce((acc, key) => { const value = obj[key]; if (typeof value === 'object' && value !== null) { - acc[key] = replaceEach(value, cb); + acc[key] = replaceEach(value as Record, replaceCb); } else if (value !== undefined) { - acc[key] = cb(value); + acc[key] = replaceCb(value); } return acc; - }, {}); + }, {} as Record); } /** @@ -83,13 +86,13 @@ function replaceEach(obj, cb): Record { * @param nonRedactedPaths A list of all object paths that shouldn't be redacted * @returns A merged objects that is entirely redacted except for the values of the nonRedactedPaths */ -function redactOtherProperties>(obj: T, nonRedactedPaths): T { +function redactOtherProperties>(obj: T, nonRedactedPaths: string[]): T { const allowedFields = pick(obj, nonRedactedPaths); const redactedFields = obj ? replaceEach(obj, redactValue) : obj; - return merge(redactedFields, allowedFields); + return merge(redactedFields, allowedFields) as T; } -function isApplicationJson(mimeType) { +function isApplicationJson(mimeType: string) { if (!mimeType) { return false; } @@ -207,6 +210,7 @@ export default function processRequest( queryString: searchToArray(reqUrl.searchParams), postData, // TODO: When readme starts accepting these, send the correct values + // @ts-expect-error we don't support cookies at the moment cookies: [], headersSize: -1, bodySize: -1, diff --git a/packages/node/tsconfig.json b/packages/node/tsconfig.json index 12836c865c..4c4a98c879 100644 --- a/packages/node/tsconfig.json +++ b/packages/node/tsconfig.json @@ -6,6 +6,7 @@ "esModuleInterop": true, "inlineSourceMap": true, "lib": ["es2019"], + "noImplicitAny": true, "outDir": "./dist", "resolveJsonModule": true }, From 0acbc1c77c2b6863a0901f342385e0a18bedee33 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Mon, 14 Aug 2023 16:43:36 -0500 Subject: [PATCH 02/22] test: add better types to testbed --- package-lock.json | 68 ++++++++++++++++++- packages/node/package.json | 2 + packages/node/test/helpers/chai-plugins.ts | 10 +-- packages/node/test/index.test.ts | 14 ++-- .../test/lib/get-project-base-url.test.ts | 4 +- packages/node/test/lib/is-request.test.ts | 1 + .../node/test/lib/process-request.test.ts | 8 +-- packages/node/test/lib/verify-webhook.test.ts | 2 +- 8 files changed, 90 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7f934664c0..f8fc793b0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1749,6 +1749,12 @@ "@types/node": "*" } }, + "node_modules/@types/caseless": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", + "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", + "dev": true + }, "node_modules/@types/chai": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", @@ -1785,6 +1791,12 @@ "integrity": "sha512-dgMN+syt1xb7Hk8LU6AODOfPlvz5z1CbXpPuJE5ZrX9STfBOIXF09pEB8N7a97WT9dbngt3ksDCm6GW6yMrxfQ==", "dev": true }, + "node_modules/@types/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", + "dev": true + }, "node_modules/@types/debug": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", @@ -2067,6 +2079,25 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, + "node_modules/@types/superagent": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.18.tgz", + "integrity": "sha512-LOWgpacIV8GHhrsQU+QMZuomfqXiqzz3ILLkCtKx3Us6AmomFViuzKT9D693QTKgyut2oCytMG8/efOop+DB+w==", + "dev": true, + "dependencies": { + "@types/cookiejar": "*", + "@types/node": "*" + } + }, + "node_modules/@types/supertest": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.12.tgz", + "integrity": "sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ==", + "dev": true, + "dependencies": { + "@types/superagent": "*" + } + }, "node_modules/@types/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/@types/supports-color/-/supports-color-8.1.1.tgz", @@ -15253,6 +15284,7 @@ }, "devDependencies": { "@readme/eslint-config": "^10.5.1", + "@types/caseless": "^0.12.2", "@types/chai": "^4.3.4", "@types/content-type": "^1.1.5", "@types/find-cache-dir": "^3.2.1", @@ -15262,6 +15294,7 @@ "@types/multer": "^1.4.7", "@types/node": "^20.2.5", "@types/ssri": "^7.1.1", + "@types/supertest": "^2.0.12", "@types/type-is": "^1.6.3", "@types/uuid": "^9.0.2", "caseless": "^0.12.0", @@ -16705,6 +16738,12 @@ "@types/node": "*" } }, + "@types/caseless": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", + "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", + "dev": true + }, "@types/chai": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", @@ -16741,6 +16780,12 @@ "integrity": "sha512-dgMN+syt1xb7Hk8LU6AODOfPlvz5z1CbXpPuJE5ZrX9STfBOIXF09pEB8N7a97WT9dbngt3ksDCm6GW6yMrxfQ==", "dev": true }, + "@types/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", + "dev": true + }, "@types/debug": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", @@ -17023,6 +17068,25 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, + "@types/superagent": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.18.tgz", + "integrity": "sha512-LOWgpacIV8GHhrsQU+QMZuomfqXiqzz3ILLkCtKx3Us6AmomFViuzKT9D693QTKgyut2oCytMG8/efOop+DB+w==", + "dev": true, + "requires": { + "@types/cookiejar": "*", + "@types/node": "*" + } + }, + "@types/supertest": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.12.tgz", + "integrity": "sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ==", + "dev": true, + "requires": { + "@types/superagent": "*" + } + }, "@types/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/@types/supports-color/-/supports-color-8.1.1.tgz", @@ -24378,6 +24442,7 @@ "version": "file:packages/node", "requires": { "@readme/eslint-config": "^10.5.1", + "@types/caseless": "^0.12.2", "@types/chai": "^4.3.4", "@types/content-type": "^1.1.5", "@types/find-cache-dir": "^3.2.1", @@ -24389,7 +24454,8 @@ "@types/node": "^20.2.5", "@types/node-fetch": "^2.6.2", "@types/ssri": "^7.1.1", - "@types/type-is": "*", + "@types/supertest": "^2.0.12", + "@types/type-is": "^1.6.3", "@types/uuid": "^9.0.2", "caseless": "^0.12.0", "chai": "^4.3.7", diff --git a/packages/node/package.json b/packages/node/package.json index 3379933679..fe079708a9 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -39,6 +39,7 @@ }, "devDependencies": { "@readme/eslint-config": "^10.5.1", + "@types/caseless": "^0.12.2", "@types/chai": "^4.3.4", "@types/content-type": "^1.1.5", "@types/find-cache-dir": "^3.2.1", @@ -48,6 +49,7 @@ "@types/multer": "^1.4.7", "@types/node": "^20.2.5", "@types/ssri": "^7.1.1", + "@types/supertest": "^2.0.12", "@types/type-is": "^1.6.3", "@types/uuid": "^9.0.2", "caseless": "^0.12.0", diff --git a/packages/node/test/helpers/chai-plugins.ts b/packages/node/test/helpers/chai-plugins.ts index e409fa3b31..adc0678c82 100644 --- a/packages/node/test/helpers/chai-plugins.ts +++ b/packages/node/test/helpers/chai-plugins.ts @@ -40,13 +40,13 @@ declare global { * connection: 'close' * } */ -function arrayToObject(array) { +function arrayToObject(array: { name: string; value: string }[]) { return array.reduce((prev, next) => { return Object.assign(prev, { [next.name]: next.value }); }, {}); } -export default function chaiPlugins(_chai, utils) { +export default function chaiPlugins(_chai: Chai.ChaiStatic, utils: Chai.ChaiUtils) { /** * Assert that a request has our `x-documentation-url` header and that contains a valid v4 UUID. * @@ -55,7 +55,7 @@ export default function chaiPlugins(_chai, utils) { * * @param {string} baseLogUrl */ - utils.addMethod(chai.Assertion.prototype, 'documentationHeader', function (baseLogUrl) { + utils.addMethod(chai.Assertion.prototype, 'documentationHeader', function (baseLogUrl: string) { const headers = utils.flag(this, 'object'); // eslint-disable-next-line chai-friendly/no-unused-expressions @@ -82,11 +82,11 @@ export default function chaiPlugins(_chai, utils) { * @param {string} header * @param {string|RegExp} expected */ - utils.addMethod(chai.Assertion.prototype, 'header', function (header, expected) { + utils.addMethod(chai.Assertion.prototype, 'header', function (header: string, expected: RegExp | string[] | string) { const obj = utils.flag(this, 'object'); const headers = caseless(arrayToObject(obj)); - if (expected.constructor.name === 'RegExp') { + if (expected instanceof RegExp) { new chai.Assertion(headers.get(header)).to.match(expected); } else if (Array.isArray(expected)) { new chai.Assertion(headers.get(header)).to.oneOf(expected.map(e => e.toString())); diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index 3569c8acd1..6830effd90 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -1,3 +1,5 @@ +import type { Express } from 'express'; + import * as crypto from 'crypto'; import { createServer } from 'http'; @@ -93,9 +95,9 @@ describe('#metrics', function () { }); describe('tests for sending requests to the metrics server', function () { - let mock; - let metricsServerRequests; - let app; + let mock: nock.Scope; + let metricsServerRequests: number; + let app: Express; let metricsServerResponseCode = 202; beforeEach(function () { @@ -296,7 +298,7 @@ describe('#metrics', function () { app.get('/test', (req, res) => res.sendStatus(200)); // We need to make sure that the logId isn't being preserved between buffered requests. - let logUrl; + let logUrl: string; await request(app) .get('/test') @@ -340,7 +342,7 @@ describe('#metrics', function () { const numberOfMocks = 4; const bufferLength = numberOfLogs / numberOfMocks; - const seenLogs = []; + const seenLogs: string[] = []; const mocks = [...new Array(numberOfMocks).keys()].map(() => nock(config.host, { @@ -353,7 +355,7 @@ describe('#metrics', function () { expect(body).to.have.lengthOf(bufferLength); // Ensure that our executed requests and the buffered queue they're in remain unique. - body.forEach(req => { + body.forEach((req: unknown) => { const requestHash = crypto.createHash('md5').update(JSON.stringify(req)).digest('hex'); expect(seenLogs).not.to.contain(requestHash); seenLogs.push(requestHash); diff --git a/packages/node/test/lib/get-project-base-url.test.ts b/packages/node/test/lib/get-project-base-url.test.ts index 86a6df3a83..13c134504b 100644 --- a/packages/node/test/lib/get-project-base-url.test.ts +++ b/packages/node/test/lib/get-project-base-url.test.ts @@ -10,7 +10,7 @@ const apiKey = 'mockReadMeApiKey'; const baseLogUrl = 'https://docs.example.com'; // eslint-disable-next-line mocha/no-exports -export function getReadMeApiMock(numberOfTimes, baseUrl = baseLogUrl) { +export function getReadMeApiMock(numberOfTimes: number, baseUrl = baseLogUrl) { return nock(config.readmeApiUrl, { reqheaders: { 'User-Agent': `${pkg.name}/${pkg.version}`, @@ -22,7 +22,7 @@ export function getReadMeApiMock(numberOfTimes, baseUrl = baseLogUrl) { .reply(200, { baseUrl }); } -function hydrateCache(lastUpdated) { +function hydrateCache(lastUpdated: number) { const cache = getCache(apiKey); cache.setKey('lastUpdated', lastUpdated); diff --git a/packages/node/test/lib/is-request.test.ts b/packages/node/test/lib/is-request.test.ts index f3c143a31d..ca32a316da 100644 --- a/packages/node/test/lib/is-request.test.ts +++ b/packages/node/test/lib/is-request.test.ts @@ -1,4 +1,5 @@ import { expect } from 'chai'; +// @ts-expect-error I am for some reason unable to declare a module properly for this package import MockReq from 'mock-req'; import isRequest from '../../src/lib/is-request'; diff --git a/packages/node/test/lib/process-request.test.ts b/packages/node/test/lib/process-request.test.ts index 699ecad2d5..a3888c8c6d 100644 --- a/packages/node/test/lib/process-request.test.ts +++ b/packages/node/test/lib/process-request.test.ts @@ -12,7 +12,7 @@ import chaiPlugins from '../helpers/chai-plugins'; chai.use(chaiPlugins); -function createApp(reqOptions?: LogOptions, shouldPreParse = false, bodyOverride?) { +function createApp(reqOptions?: LogOptions, shouldPreParse = false, bodyOverride?: Record) { const requestListener = function (req: IncomingMessage, res: ServerResponse) { let body = ''; @@ -120,7 +120,7 @@ describe('process-request', function () { }); it('should fail gracefully with circular json objects', function () { - const obj = { foo: null }; + const obj: Record = { foo: null }; obj.foo = obj; const app = createApp({ denylist: ['password'] }, false, obj); @@ -478,8 +478,8 @@ describe('process-request', function () { .post('/') .set('a', '1') .expect(({ body }) => { - expect(body.headers.find(header => header.name === 'host').value).to.match(/127.0.0.1:\d+/); - expect(body.headers.filter(header => header.name !== 'host')).to.deep.equal([ + expect(body.headers.find((header: { name: string }) => header.name === 'host').value).to.match(/127.0.0.1:\d+/); + expect(body.headers.filter((header: { name: string }) => header.name !== 'host')).to.deep.equal([ { name: 'accept-encoding', value: 'gzip, deflate' }, { name: 'a', value: '1' }, { name: 'connection', value: 'close' }, diff --git a/packages/node/test/lib/verify-webhook.test.ts b/packages/node/test/lib/verify-webhook.test.ts index d0d8110470..9bbdfeae4b 100644 --- a/packages/node/test/lib/verify-webhook.test.ts +++ b/packages/node/test/lib/verify-webhook.test.ts @@ -47,7 +47,7 @@ describe('verifyWebhook', function () { it('should throw an error if signature is missing', function () { const body = { email: 'marc@readme.io' }; const secret = 'docs4dayz'; - const signature = undefined; + const signature = ''; expect(() => { verifyWebhook(body, signature, secret); From 0ee821158f925725c473573daf3a2da7a62130b1 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Mon, 14 Aug 2023 17:01:51 -0500 Subject: [PATCH 03/22] chore: refresh lockfile i had to downgrade to npm@7 for this... absolutely cursed --- packages/node/package-lock.json | 150 +++++++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 1 deletion(-) diff --git a/packages/node/package-lock.json b/packages/node/package-lock.json index 0be45fd99a..05cbe9bfac 100644 --- a/packages/node/package-lock.json +++ b/packages/node/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "readmeio", - "version": "6.1.0", + "version": "6.2.0", "license": "ISC", "dependencies": { "@types/har-format": "^1.2.10", @@ -23,11 +23,19 @@ }, "devDependencies": { "@readme/eslint-config": "^10.5.1", + "@types/caseless": "^0.12.2", "@types/chai": "^4.3.4", + "@types/content-type": "^1.1.5", + "@types/find-cache-dir": "^3.2.1", + "@types/flat-cache": "^2.0.0", + "@types/lodash": "^4.14.197", "@types/mocha": "^10.0.1", "@types/multer": "^1.4.7", "@types/node": "^20.2.5", "@types/ssri": "^7.1.1", + "@types/supertest": "^2.0.12", + "@types/type-is": "^1.6.3", + "@types/uuid": "^9.0.2", "caseless": "^0.12.0", "chai": "^4.3.7", "eslint": "^8.34.0", @@ -901,6 +909,12 @@ "@types/node": "*" } }, + "node_modules/@types/caseless": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", + "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", + "dev": true + }, "node_modules/@types/chai": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", @@ -916,6 +930,18 @@ "@types/node": "*" } }, + "node_modules/@types/content-type": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/content-type/-/content-type-1.1.5.tgz", + "integrity": "sha512-dgMN+syt1xb7Hk8LU6AODOfPlvz5z1CbXpPuJE5ZrX9STfBOIXF09pEB8N7a97WT9dbngt3ksDCm6GW6yMrxfQ==", + "dev": true + }, + "node_modules/@types/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", + "dev": true + }, "node_modules/@types/express": { "version": "4.17.13", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", @@ -939,6 +965,18 @@ "@types/range-parser": "*" } }, + "node_modules/@types/find-cache-dir": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/find-cache-dir/-/find-cache-dir-3.2.1.tgz", + "integrity": "sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==", + "dev": true + }, + "node_modules/@types/flat-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/flat-cache/-/flat-cache-2.0.0.tgz", + "integrity": "sha512-fHeEsm9hvmZ+QHpw6Fkvf19KIhuqnYLU6vtWLjd5BsMd/qVi7iTkMioDZl0mQmfNRA1A6NwvhrSRNr9hGYZGww==", + "dev": true + }, "node_modules/@types/har-format": { "version": "1.2.11", "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.11.tgz", @@ -956,6 +994,12 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/lodash": { + "version": "4.14.197", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.197.tgz", + "integrity": "sha512-BMVOiWs0uNxHVlHBgzTIqJYmj+PgCo4euloGF+5m4okL3rEYzM2EEv78mw8zWSMM57dM7kVIgJ2QDvwHSoCI5g==", + "dev": true + }, "node_modules/@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", @@ -1047,6 +1091,40 @@ "@types/node": "*" } }, + "node_modules/@types/superagent": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.18.tgz", + "integrity": "sha512-LOWgpacIV8GHhrsQU+QMZuomfqXiqzz3ILLkCtKx3Us6AmomFViuzKT9D693QTKgyut2oCytMG8/efOop+DB+w==", + "dev": true, + "dependencies": { + "@types/cookiejar": "*", + "@types/node": "*" + } + }, + "node_modules/@types/supertest": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.12.tgz", + "integrity": "sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ==", + "dev": true, + "dependencies": { + "@types/superagent": "*" + } + }, + "node_modules/@types/type-is": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@types/type-is/-/type-is-1.6.3.tgz", + "integrity": "sha512-PNs5wHaNcBgCQG5nAeeZ7OvosrEsI9O4W2jAOO9BCCg4ux9ZZvH2+0iSCOIDBiKuQsiNS8CBlmfX9f5YBQ22cA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/uuid": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.2.tgz", + "integrity": "sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.60.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.1.tgz", @@ -8682,6 +8760,12 @@ "@types/node": "*" } }, + "@types/caseless": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", + "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", + "dev": true + }, "@types/chai": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", @@ -8697,6 +8781,18 @@ "@types/node": "*" } }, + "@types/content-type": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/content-type/-/content-type-1.1.5.tgz", + "integrity": "sha512-dgMN+syt1xb7Hk8LU6AODOfPlvz5z1CbXpPuJE5ZrX9STfBOIXF09pEB8N7a97WT9dbngt3ksDCm6GW6yMrxfQ==", + "dev": true + }, + "@types/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", + "dev": true + }, "@types/express": { "version": "4.17.13", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", @@ -8720,6 +8816,18 @@ "@types/range-parser": "*" } }, + "@types/find-cache-dir": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/find-cache-dir/-/find-cache-dir-3.2.1.tgz", + "integrity": "sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==", + "dev": true + }, + "@types/flat-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/flat-cache/-/flat-cache-2.0.0.tgz", + "integrity": "sha512-fHeEsm9hvmZ+QHpw6Fkvf19KIhuqnYLU6vtWLjd5BsMd/qVi7iTkMioDZl0mQmfNRA1A6NwvhrSRNr9hGYZGww==", + "dev": true + }, "@types/har-format": { "version": "1.2.11", "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.11.tgz", @@ -8737,6 +8845,12 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "@types/lodash": { + "version": "4.14.197", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.197.tgz", + "integrity": "sha512-BMVOiWs0uNxHVlHBgzTIqJYmj+PgCo4euloGF+5m4okL3rEYzM2EEv78mw8zWSMM57dM7kVIgJ2QDvwHSoCI5g==", + "dev": true + }, "@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", @@ -8827,6 +8941,40 @@ "@types/node": "*" } }, + "@types/superagent": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.18.tgz", + "integrity": "sha512-LOWgpacIV8GHhrsQU+QMZuomfqXiqzz3ILLkCtKx3Us6AmomFViuzKT9D693QTKgyut2oCytMG8/efOop+DB+w==", + "dev": true, + "requires": { + "@types/cookiejar": "*", + "@types/node": "*" + } + }, + "@types/supertest": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.12.tgz", + "integrity": "sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ==", + "dev": true, + "requires": { + "@types/superagent": "*" + } + }, + "@types/type-is": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@types/type-is/-/type-is-1.6.3.tgz", + "integrity": "sha512-PNs5wHaNcBgCQG5nAeeZ7OvosrEsI9O4W2jAOO9BCCg4ux9ZZvH2+0iSCOIDBiKuQsiNS8CBlmfX9f5YBQ22cA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/uuid": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.2.tgz", + "integrity": "sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==", + "dev": true + }, "@typescript-eslint/eslint-plugin": { "version": "5.60.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.1.tgz", From 802a65c1790da83f7e6fffcc8acc9163432770fd Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Mon, 14 Aug 2023 17:38:02 -0500 Subject: [PATCH 04/22] chore: strict config fixes --- packages/node/src/lib/construct-payload.ts | 2 +- packages/node/src/lib/get-project-base-url.ts | 2 +- packages/node/src/lib/metrics-log.ts | 10 ++++---- packages/node/src/lib/object-to-array.ts | 9 +++++-- packages/node/src/lib/process-request.ts | 25 +++++++++---------- packages/node/src/lib/process-response.ts | 14 ++++------- packages/node/test/tsconfig.json | 3 ++- packages/node/tsconfig.json | 3 ++- 8 files changed, 35 insertions(+), 33 deletions(-) diff --git a/packages/node/src/lib/construct-payload.ts b/packages/node/src/lib/construct-payload.ts index 02c8768840..308327a2e4 100644 --- a/packages/node/src/lib/construct-payload.ts +++ b/packages/node/src/lib/construct-payload.ts @@ -163,7 +163,7 @@ export function constructPayload( { pageref: payloadData.routePath ? payloadData.routePath - : new URL(req.url, `${getProto(req)}://${req.headers.host}`).toString(), + : new URL(req.url || '', `${getProto(req)}://${req.headers.host}`).toString(), startedDateTime: payloadData.startedDateTime.toISOString(), time: serverTime, request: processRequest(req, payloadData.requestBody, logOptions), diff --git a/packages/node/src/lib/get-project-base-url.ts b/packages/node/src/lib/get-project-base-url.ts index 6479e9849d..a2194e3474 100644 --- a/packages/node/src/lib/get-project-base-url.ts +++ b/packages/node/src/lib/get-project-base-url.ts @@ -34,7 +34,7 @@ export async function getProjectBaseUrl(readmeApiKey: string, requestTimeout = c ) { const signal = timeoutSignal(requestTimeout); - let baseUrl; + let baseUrl = ''; await fetch(`${config.readmeApiUrl}/v1/`, { method: 'get', headers: { diff --git a/packages/node/src/lib/metrics-log.ts b/packages/node/src/lib/metrics-log.ts index adac60c7bd..89404b6915 100644 --- a/packages/node/src/lib/metrics-log.ts +++ b/packages/node/src/lib/metrics-log.ts @@ -34,7 +34,7 @@ export interface GroupingObject { export interface OutgoingLogBody { _id?: string; _version: number; - clientIPAddress: string; + clientIPAddress?: string; development: boolean; // API Key is currently a mapping to ID. Eventually we will support this server side. The omit and readdition of ID is to remove the deprecated warning in the meanwhile group: Omit & { id: string }; @@ -48,7 +48,7 @@ export interface LogResponse { const BACKOFF_SECONDS = 15; // when we need to backoff HTTP requests, pause for seconds -let backoffExpiresAt: Date; +let backoffExpiresAt: Date | undefined; // Exported for use in unit tests export function setBackoff(expiresAt: Date | undefined) { @@ -77,9 +77,9 @@ function shouldBackoff(response: Response) { } } -function getLogIds(body: OutgoingLogBody | OutgoingLogBody[]): string | string[] { +function getLogIds(body: OutgoingLogBody | OutgoingLogBody[]): string | string[] | undefined { if (Array.isArray(body)) { - return body.map(value => value._id); + return body.map(value => value._id) as string[]; } return body._id; @@ -132,7 +132,7 @@ export function metricsAPICall( makeRequest(); return Promise.resolve({ ids: getLogIds(body), - }); + } as LogResponse); } return makeRequest().then(response => { diff --git a/packages/node/src/lib/object-to-array.ts b/packages/node/src/lib/object-to-array.ts index ab554416f7..2b583ee3ba 100644 --- a/packages/node/src/lib/object-to-array.ts +++ b/packages/node/src/lib/object-to-array.ts @@ -1,5 +1,10 @@ import type { URLSearchParams } from 'url'; +export function objectToArray( + object: Record, + opts: { castToString: true } +): { name: string; value: string }[]; +export function objectToArray(object: Record): { name: string; value: unknown }[]; export function objectToArray( object: Record, opts: { @@ -7,7 +12,7 @@ export function objectToArray( } = { castToString: false, } -): { name: string; value: string }[] { +): { name: string; value: unknown }[] { return Object.entries(object).reduce((prev, [name, value]) => { if (Array.isArray(value)) { value.forEach(val => { @@ -18,7 +23,7 @@ export function objectToArray( } return prev; - }, []); + }, [] as { name: string; value: unknown }[]); } export function searchToArray(search: URLSearchParams): { name: string; value: string }[] { diff --git a/packages/node/src/lib/process-request.ts b/packages/node/src/lib/process-request.ts index 052c2a9413..2ccac128e3 100644 --- a/packages/node/src/lib/process-request.ts +++ b/packages/node/src/lib/process-request.ts @@ -1,6 +1,6 @@ import type { LogOptions } from './construct-payload'; import type { ExtendedIncomingMessage } from './log'; -import type { Entry } from 'har-format'; +import type { Cookie, Param, PostData, Request } from 'har-format'; import * as qs from 'querystring'; import url, { URL } from 'url'; @@ -132,20 +132,20 @@ export default function processRequest( req: ExtendedIncomingMessage, requestBody?: Record | string, options?: LogOptions -): Entry['request'] { - const protocol = fixHeader(req.headers['x-forwarded-proto'])?.toLowerCase() || getProto(req); - const host = fixHeader(req.headers['x-forwarded-host']) || req.headers.host; +): Request { + const protocol = fixHeader(req.headers['x-forwarded-proto'] as string)?.toLowerCase() || getProto(req); + const host = fixHeader(req.headers['x-forwarded-host'] as string) || req.headers.host; const denylist = options?.denylist || options?.blacklist; const allowlist = options?.allowlist || options?.whitelist; - let mimeType: string = null; + let mimeType = ''; try { - mimeType = contentType.parse(req.headers['content-type']).type; + mimeType = contentType.parse(req.headers['content-type'] as string).type; } catch (e) {} // eslint-disable-line no-empty let reqBody = typeof requestBody === 'string' ? parseRequestBody(requestBody, mimeType) : requestBody; - let postData: Entry['request']['postData'] = null; + let postData: PostData; if (denylist) { reqBody = typeof reqBody === 'object' ? redactProperties(reqBody, denylist) : reqBody; @@ -161,13 +161,13 @@ export default function processRequest( postData = { mimeType, // `reqBody` is likely to be an object, but can be empty if no HTTP body sent - params: objectToArray((reqBody || {}) as Record), + params: objectToArray((reqBody || {}) as Record) as Param[], }; } else if (isApplicationJson(mimeType)) { postData = { mimeType, text: typeof reqBody === 'object' || Array.isArray(reqBody) ? JSON.stringify(reqBody) : reqBody, - }; + } as PostData; } else if (mimeType) { let stringBody = ''; @@ -187,7 +187,7 @@ export default function processRequest( // We use a fake host here because we rely on the host header which could be redacted. // We only ever use this reqUrl with the fake hostname for the pathname and querystring. // req.originalUrl is express specific, req.url is node.js - const reqUrl = new URL(req.originalUrl || req.url, 'https://readme.io'); + const reqUrl = new URL(req.originalUrl || req.url || '', 'https://readme.io'); if (req.headers.authorization) { req.headers.authorization = mask(req.headers.authorization); @@ -210,8 +210,7 @@ export default function processRequest( queryString: searchToArray(reqUrl.searchParams), postData, // TODO: When readme starts accepting these, send the correct values - // @ts-expect-error we don't support cookies at the moment - cookies: [], + cookies: [] as Cookie[], headersSize: -1, bodySize: -1, }; @@ -220,5 +219,5 @@ export default function processRequest( delete requestData.postData; } - return requestData; + return requestData as Request; } diff --git a/packages/node/src/lib/process-response.ts b/packages/node/src/lib/process-response.ts index 797a5c27e4..7208613b2c 100644 --- a/packages/node/src/lib/process-response.ts +++ b/packages/node/src/lib/process-response.ts @@ -1,5 +1,5 @@ import type { LogOptions } from './construct-payload'; -import type { Entry } from 'har-format'; +import type { Response } from 'har-format'; import type { ServerResponse } from 'http'; import { STATUS_CODES } from 'http'; @@ -19,16 +19,12 @@ import { fixHeader } from './process-request'; * * @returns The HAR formatted response details */ -export default function processResponse( - res: ServerResponse, - responseBody?: string, - options?: LogOptions -): Entry['response'] { +export default function processResponse(res: ServerResponse, responseBody?: string, options?: LogOptions): Response { const denylist = options?.denylist || options?.blacklist; const allowlist = options?.allowlist || options?.whitelist; let body; try { - body = JSON.parse(responseBody); + body = JSON.parse(responseBody || ''); // Only apply blacklist/whitelist if it's an object if (denylist) { @@ -62,12 +58,12 @@ export default function processResponse( // // This is the same thing that Node.js does internally: // https://github.com/nodejs/node/blob/9b8ba2536044ae08a1cd747a3aa52df7d1815e7e/lib/_http_server.js#L318 - statusText: res.statusMessage || STATUS_CODES[res.statusCode], + statusText: (res.statusMessage || STATUS_CODES[res.statusCode]) as string, headers: objectToArray(headers, { castToString: true }), content: { text: JSON.stringify(body), size: Number(fixHeader(res.getHeader('content-length') || 0)), - mimeType: fixHeader(res.getHeader('content-type')) || 'text/plain', + mimeType: fixHeader(res.getHeader('content-type') as string) || 'text/plain', }, // TODO: Once readme starts accepting these, send the correct values httpVersion: '', diff --git a/packages/node/test/tsconfig.json b/packages/node/test/tsconfig.json index e1c447e2de..686d521744 100644 --- a/packages/node/test/tsconfig.json +++ b/packages/node/test/tsconfig.json @@ -1,7 +1,8 @@ { "extends": "../tsconfig.json", "compilerOptions": { - "downlevelIteration": true + "downlevelIteration": true, + "strict": false }, "include": ["./index.test.ts", "./lib/*.ts"] } diff --git a/packages/node/tsconfig.json b/packages/node/tsconfig.json index 4c4a98c879..18b2a12d3c 100644 --- a/packages/node/tsconfig.json +++ b/packages/node/tsconfig.json @@ -8,7 +8,8 @@ "lib": ["es2019"], "noImplicitAny": true, "outDir": "./dist", - "resolveJsonModule": true + "resolveJsonModule": true, + "strict": true }, "include": [ "src/**/*", From 98c82324036a79d0b1e684b043da64a1890b6ea6 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Mon, 14 Aug 2023 17:52:28 -0500 Subject: [PATCH 05/22] chore: more TS fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit not sure why VS code didn't flag these the first time 🤔 also i know this is nuts but open to alternatives lol --- packages/node/src/lib/process-request.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/node/src/lib/process-request.ts b/packages/node/src/lib/process-request.ts index 2ccac128e3..8b2949672c 100644 --- a/packages/node/src/lib/process-request.ts +++ b/packages/node/src/lib/process-request.ts @@ -145,7 +145,7 @@ export default function processRequest( } catch (e) {} // eslint-disable-line no-empty let reqBody = typeof requestBody === 'string' ? parseRequestBody(requestBody, mimeType) : requestBody; - let postData: PostData; + let postData: PostData = null as unknown as PostData; if (denylist) { reqBody = typeof reqBody === 'object' ? redactProperties(reqBody, denylist) : reqBody; @@ -216,7 +216,9 @@ export default function processRequest( }; if (requestData.postData === null) { - delete requestData.postData; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { postData: postDataToBeOmitted, ...remainingRequestData } = requestData; + return remainingRequestData as Request; } return requestData as Request; From a718dae82a3577661b61fe6e7d7572d1715bfabe Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Mon, 14 Aug 2023 17:55:52 -0500 Subject: [PATCH 06/22] chore: use promise.resolve generics Co-Authored-By: Jon Ursenbach --- packages/node/src/lib/metrics-log.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/node/src/lib/metrics-log.ts b/packages/node/src/lib/metrics-log.ts index 89404b6915..d48af28d48 100644 --- a/packages/node/src/lib/metrics-log.ts +++ b/packages/node/src/lib/metrics-log.ts @@ -42,7 +42,7 @@ export interface OutgoingLogBody { } export interface LogResponse { - ids: string | string[]; + ids?: string | string[]; response?: Response; } @@ -130,9 +130,9 @@ export function metricsAPICall( if (fireAndForget) { makeRequest(); - return Promise.resolve({ + return Promise.resolve({ ids: getLogIds(body), - } as LogResponse); + }); } return makeRequest().then(response => { From 022afbcd20f2c3a50331bc8f144a6ae7507053d2 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Mon, 14 Aug 2023 17:56:55 -0500 Subject: [PATCH 07/22] test: add test for undefined webhook Co-Authored-By: Jon Ursenbach --- packages/node/test/lib/verify-webhook.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/node/test/lib/verify-webhook.test.ts b/packages/node/test/lib/verify-webhook.test.ts index 9bbdfeae4b..916d67846c 100644 --- a/packages/node/test/lib/verify-webhook.test.ts +++ b/packages/node/test/lib/verify-webhook.test.ts @@ -53,4 +53,13 @@ describe('verifyWebhook', function () { verifyWebhook(body, signature, secret); }).to.throw(/Missing Signature/); }); + + it('should throw an error if signature is undefined', function () { + const body = { email: 'marc@readme.io' }; + const secret = 'docs4dayz'; + + expect(() => { + verifyWebhook(body, undefined, secret); + }).to.throw(/Missing Signature/); + }); }); From 097a84c56a4ba42d3572b10ae68551d4fe6a3485 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Mon, 14 Aug 2023 18:02:48 -0500 Subject: [PATCH 08/22] refactor: consolidate type this is the shit i live for --- packages/node/src/lib/metrics-log.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/node/src/lib/metrics-log.ts b/packages/node/src/lib/metrics-log.ts index d48af28d48..adfdee2691 100644 --- a/packages/node/src/lib/metrics-log.ts +++ b/packages/node/src/lib/metrics-log.ts @@ -41,8 +41,10 @@ export interface OutgoingLogBody { request: Har; } +type LogId = string | string[] | undefined; + export interface LogResponse { - ids?: string | string[]; + ids: LogId; response?: Response; } @@ -77,7 +79,7 @@ function shouldBackoff(response: Response) { } } -function getLogIds(body: OutgoingLogBody | OutgoingLogBody[]): string | string[] | undefined { +function getLogIds(body: OutgoingLogBody | OutgoingLogBody[]): LogId { if (Array.isArray(body)) { return body.map(value => value._id) as string[]; } From bc408045c701ab40b6c56af2cca26209ead0bcf3 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Tue, 15 Aug 2023 08:02:57 -0500 Subject: [PATCH 09/22] chore: slightly reorganize tsconfigs --- packages/node/test/tsconfig.json | 1 + packages/node/tsconfig.json | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node/test/tsconfig.json b/packages/node/test/tsconfig.json index 686d521744..0ddcba12f6 100644 --- a/packages/node/test/tsconfig.json +++ b/packages/node/test/tsconfig.json @@ -2,6 +2,7 @@ "extends": "../tsconfig.json", "compilerOptions": { "downlevelIteration": true, + "noImplicitAny": true, "strict": false }, "include": ["./index.test.ts", "./lib/*.ts"] diff --git a/packages/node/tsconfig.json b/packages/node/tsconfig.json index 18b2a12d3c..584369bf43 100644 --- a/packages/node/tsconfig.json +++ b/packages/node/tsconfig.json @@ -6,7 +6,6 @@ "esModuleInterop": true, "inlineSourceMap": true, "lib": ["es2019"], - "noImplicitAny": true, "outDir": "./dist", "resolveJsonModule": true, "strict": true From d9311393c6bb4f22d3ebc2906175deafdc312ec0 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Tue, 15 Aug 2023 08:26:23 -0500 Subject: [PATCH 10/22] revert: var change --- packages/node/src/lib/process-request.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/node/src/lib/process-request.ts b/packages/node/src/lib/process-request.ts index 8b2949672c..749149dc08 100644 --- a/packages/node/src/lib/process-request.ts +++ b/packages/node/src/lib/process-request.ts @@ -61,19 +61,19 @@ function redactProperties>(obj: T, redactedPat /** * @param obj The data object that is operated upon - * @param replaceCb A callback that is invoked for each value found, the return value being the next value that is set in the returned object + * @param cb A callback that is invoked for each value found, the return value being the next value that is set in the returned object * @returns An object with the replaced values */ function replaceEach>( obj: T, - replaceCb: (input: unknown) => string + cb: (input: unknown) => string ): Record { return Object.keys(obj).reduce((acc, key) => { const value = obj[key]; if (typeof value === 'object' && value !== null) { - acc[key] = replaceEach(value as Record, replaceCb); + acc[key] = replaceEach(value as Record, cb); } else if (value !== undefined) { - acc[key] = replaceCb(value); + acc[key] = cb(value); } return acc; }, {} as Record); From 8e1b743943a1ebfc03cbdf978d72cebacc8fa226 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Tue, 15 Aug 2023 08:38:18 -0500 Subject: [PATCH 11/22] refactor: add'l stronger types --- packages/node/src/lib/object-to-array.ts | 4 ++-- packages/node/src/lib/process-request.ts | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/node/src/lib/object-to-array.ts b/packages/node/src/lib/object-to-array.ts index 2b583ee3ba..f1c3a36e10 100644 --- a/packages/node/src/lib/object-to-array.ts +++ b/packages/node/src/lib/object-to-array.ts @@ -13,7 +13,7 @@ export function objectToArray( castToString: false, } ): { name: string; value: unknown }[] { - return Object.entries(object).reduce((prev, [name, value]) => { + return Object.entries(object).reduce<{ name: string; value: unknown }[]>((prev, [name, value]) => { if (Array.isArray(value)) { value.forEach(val => { prev.push({ name, value: opts.castToString ? String(val) : val }); @@ -23,7 +23,7 @@ export function objectToArray( } return prev; - }, [] as { name: string; value: unknown }[]); + }, []); } export function searchToArray(search: URLSearchParams): { name: string; value: string }[] { diff --git a/packages/node/src/lib/process-request.ts b/packages/node/src/lib/process-request.ts index 749149dc08..3b0b7fd468 100644 --- a/packages/node/src/lib/process-request.ts +++ b/packages/node/src/lib/process-request.ts @@ -68,7 +68,7 @@ function replaceEach>( obj: T, cb: (input: unknown) => string ): Record { - return Object.keys(obj).reduce((acc, key) => { + return Object.keys(obj).reduce>((acc, key) => { const value = obj[key]; if (typeof value === 'object' && value !== null) { acc[key] = replaceEach(value as Record, cb); @@ -76,7 +76,7 @@ function replaceEach>( acc[key] = cb(value); } return acc; - }, {} as Record); + }, {}); } /** @@ -141,7 +141,7 @@ export default function processRequest( let mimeType = ''; try { - mimeType = contentType.parse(req.headers['content-type'] as string).type; + mimeType = contentType.parse(req.headers['content-type'] || '').type; } catch (e) {} // eslint-disable-line no-empty let reqBody = typeof requestBody === 'string' ? parseRequestBody(requestBody, mimeType) : requestBody; @@ -166,8 +166,8 @@ export default function processRequest( } else if (isApplicationJson(mimeType)) { postData = { mimeType, - text: typeof reqBody === 'object' || Array.isArray(reqBody) ? JSON.stringify(reqBody) : reqBody, - } as PostData; + text: typeof reqBody === 'object' || Array.isArray(reqBody) ? JSON.stringify(reqBody) : reqBody || '', + }; } else if (mimeType) { let stringBody = ''; From 9ee2fccf7fe71fab7a24452ac94990ecc51dff39 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Tue, 15 Aug 2023 08:44:24 -0500 Subject: [PATCH 12/22] refactor: another strict type Co-Authored-By: Bill Mill --- packages/node/src/lib/metrics-log.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node/src/lib/metrics-log.ts b/packages/node/src/lib/metrics-log.ts index adfdee2691..c105c9f13b 100644 --- a/packages/node/src/lib/metrics-log.ts +++ b/packages/node/src/lib/metrics-log.ts @@ -98,7 +98,7 @@ export function metricsAPICall( const makeRequest = () => { if (backoffExpiresAt) { if (backoffExpiresAt > new Date()) { - return Promise.resolve(); + return Promise.resolve(undefined); } // after the backoff expires, erase the old expiration time backoffExpiresAt = undefined; @@ -141,6 +141,6 @@ export function metricsAPICall( return { response, ids: getLogIds(body), - } as LogResponse; + }; }); } From 8288b96d81a8d5c4765d0b137047d9349578e96b Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Tue, 15 Aug 2023 08:47:27 -0500 Subject: [PATCH 13/22] refactor: consolidate type Co-Authored-By: Bill Mill --- packages/node/src/lib/object-to-array.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/node/src/lib/object-to-array.ts b/packages/node/src/lib/object-to-array.ts index f1c3a36e10..be0c2020a0 100644 --- a/packages/node/src/lib/object-to-array.ts +++ b/packages/node/src/lib/object-to-array.ts @@ -1,10 +1,15 @@ import type { URLSearchParams } from 'url'; +type ParamArray = { + name: string; + value: unknown; +}[]; + export function objectToArray( object: Record, opts: { castToString: true } ): { name: string; value: string }[]; -export function objectToArray(object: Record): { name: string; value: unknown }[]; +export function objectToArray(object: Record): ParamArray; export function objectToArray( object: Record, opts: { @@ -12,8 +17,8 @@ export function objectToArray( } = { castToString: false, } -): { name: string; value: unknown }[] { - return Object.entries(object).reduce<{ name: string; value: unknown }[]>((prev, [name, value]) => { +): ParamArray { + return Object.entries(object).reduce((prev, [name, value]) => { if (Array.isArray(value)) { value.forEach(val => { prev.push({ name, value: opts.castToString ? String(val) : val }); From ef396a9a74d3afa454a2b974ccef5781dbaa831a Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Tue, 15 Aug 2023 08:49:01 -0500 Subject: [PATCH 14/22] refactor: more type passes Co-Authored-By: Bill Mill --- packages/node/src/lib/process-request.ts | 4 ++-- packages/node/src/lib/process-response.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/node/src/lib/process-request.ts b/packages/node/src/lib/process-request.ts index 3b0b7fd468..ac583d0883 100644 --- a/packages/node/src/lib/process-request.ts +++ b/packages/node/src/lib/process-request.ts @@ -133,8 +133,8 @@ export default function processRequest( requestBody?: Record | string, options?: LogOptions ): Request { - const protocol = fixHeader(req.headers['x-forwarded-proto'] as string)?.toLowerCase() || getProto(req); - const host = fixHeader(req.headers['x-forwarded-host'] as string) || req.headers.host; + const protocol = fixHeader(req.headers['x-forwarded-proto'] || '')?.toLowerCase() || getProto(req); + const host = fixHeader(req.headers['x-forwarded-host'] || '') || req.headers.host; const denylist = options?.denylist || options?.blacklist; const allowlist = options?.allowlist || options?.whitelist; diff --git a/packages/node/src/lib/process-response.ts b/packages/node/src/lib/process-response.ts index 7208613b2c..5a70bf8c58 100644 --- a/packages/node/src/lib/process-response.ts +++ b/packages/node/src/lib/process-response.ts @@ -58,12 +58,12 @@ export default function processResponse(res: ServerResponse, responseBody?: stri // // This is the same thing that Node.js does internally: // https://github.com/nodejs/node/blob/9b8ba2536044ae08a1cd747a3aa52df7d1815e7e/lib/_http_server.js#L318 - statusText: (res.statusMessage || STATUS_CODES[res.statusCode]) as string, + statusText: res.statusMessage || STATUS_CODES[res.statusCode] || '', headers: objectToArray(headers, { castToString: true }), content: { text: JSON.stringify(body), size: Number(fixHeader(res.getHeader('content-length') || 0)), - mimeType: fixHeader(res.getHeader('content-type') as string) || 'text/plain', + mimeType: fixHeader(res.getHeader('content-type') || '') || 'text/plain', }, // TODO: Once readme starts accepting these, send the correct values httpVersion: '', From 7d42eacab182c6c615069da5dbb63a51efd76ac3 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Tue, 15 Aug 2023 08:50:54 -0500 Subject: [PATCH 15/22] chore: better comment --- packages/node/src/lib/patch-response.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/node/src/lib/patch-response.ts b/packages/node/src/lib/patch-response.ts index 07a39ccb5d..8f8c2984fe 100644 --- a/packages/node/src/lib/patch-response.ts +++ b/packages/node/src/lib/patch-response.ts @@ -10,13 +10,17 @@ export function patchResponse(res: ExtendedResponse) { res._body = ''; - // @ts-expect-error this feels scary to mess with further + // @ts-expect-error these lines are messing with Node internals and + // I'm not sure what exactly typing fix is needed here that doesn't have + // some sort of adverse impact on our response handling. res.write = (chunk, encoding, cb) => { res._body += chunk; write.call(res, chunk, encoding, cb); }; - // @ts-expect-error this feels scary to mess with further + // @ts-expect-error these lines are messing with Node internals and + // I'm not sure what exactly typing fix is needed here that doesn't have + // some sort of adverse impact on our response handling. res.end = (chunk, encoding, cb) => { // Chunk is optional in res.end // http://nodejs.org/dist/latest/docs/api/http.html#http_response_end_data_encoding_callback From 03007f4bb311f256a653ddaf0a18749524a02e68 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Tue, 15 Aug 2023 11:02:00 -0500 Subject: [PATCH 16/22] chore: another stricter type Co-Authored-By: Aaron Hedges <218751+Dashron@users.noreply.github.com> --- packages/node/src/lib/process-request.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/node/src/lib/process-request.ts b/packages/node/src/lib/process-request.ts index ac583d0883..e7b366df2b 100644 --- a/packages/node/src/lib/process-request.ts +++ b/packages/node/src/lib/process-request.ts @@ -145,7 +145,7 @@ export default function processRequest( } catch (e) {} // eslint-disable-line no-empty let reqBody = typeof requestBody === 'string' ? parseRequestBody(requestBody, mimeType) : requestBody; - let postData: PostData = null as unknown as PostData; + let postData: PostData | undefined; if (denylist) { reqBody = typeof reqBody === 'object' ? redactProperties(reqBody, denylist) : reqBody; @@ -215,11 +215,11 @@ export default function processRequest( bodySize: -1, }; - if (requestData.postData === null) { + if (typeof requestData.postData === 'undefined') { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { postData: postDataToBeOmitted, ...remainingRequestData } = requestData; return remainingRequestData as Request; } - return requestData as Request; + return requestData; } From af9ce2a12d111a6d280978bb5f452a795dbb70ac Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Tue, 15 Aug 2023 11:56:40 -0500 Subject: [PATCH 17/22] refactor: clientIPAddress is required --- packages/node/src/lib/construct-payload.ts | 2 +- packages/node/src/lib/metrics-log.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node/src/lib/construct-payload.ts b/packages/node/src/lib/construct-payload.ts index 308327a2e4..06a8e50333 100644 --- a/packages/node/src/lib/construct-payload.ts +++ b/packages/node/src/lib/construct-payload.ts @@ -148,7 +148,7 @@ export function constructPayload( label: payloadData.label, email: payloadData.email, }, - clientIPAddress: req.socket.remoteAddress, + clientIPAddress: req.socket.remoteAddress || '', development: !!logOptions?.development, request: { log: { diff --git a/packages/node/src/lib/metrics-log.ts b/packages/node/src/lib/metrics-log.ts index c105c9f13b..02248d57c1 100644 --- a/packages/node/src/lib/metrics-log.ts +++ b/packages/node/src/lib/metrics-log.ts @@ -34,7 +34,7 @@ export interface GroupingObject { export interface OutgoingLogBody { _id?: string; _version: number; - clientIPAddress?: string; + clientIPAddress: string; development: boolean; // API Key is currently a mapping to ID. Eventually we will support this server side. The omit and readdition of ID is to remove the deprecated warning in the meanwhile group: Omit & { id: string }; From d4e962248944467462eed4d27401e0fa3761ad7b Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Tue, 15 Aug 2023 12:06:26 -0500 Subject: [PATCH 18/22] revert: oops didn't mean to commit this --- packages/node/src/lib/process-request.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node/src/lib/process-request.ts b/packages/node/src/lib/process-request.ts index e7b366df2b..a2b50f5464 100644 --- a/packages/node/src/lib/process-request.ts +++ b/packages/node/src/lib/process-request.ts @@ -221,5 +221,5 @@ export default function processRequest( return remainingRequestData as Request; } - return requestData; + return requestData as Request; } From 1585f05c5877bf90e6d5f9c920cb160a2aaf3629 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Tue, 15 Aug 2023 12:12:11 -0500 Subject: [PATCH 19/22] refactor: stricter typing for headers and method --- packages/node/src/lib/process-request.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/node/src/lib/process-request.ts b/packages/node/src/lib/process-request.ts index a2b50f5464..ae327b7ecc 100644 --- a/packages/node/src/lib/process-request.ts +++ b/packages/node/src/lib/process-request.ts @@ -194,7 +194,7 @@ export default function processRequest( } const requestData = { - method: req.method, + method: req.method || '', url: url.format({ // Handle cases where some reverse proxies put two protocols into x-forwarded-proto // This line does the following: "https,http" -> "https" @@ -206,20 +206,20 @@ export default function processRequest( query: qs.parse(reqUrl.search.substring(1)), }), httpVersion: `${getProto(req).toUpperCase()}/${req.httpVersion}`, - headers: objectToArray(req.headers), + headers: objectToArray(req.headers, { castToString: true }), queryString: searchToArray(reqUrl.searchParams), postData, // TODO: When readme starts accepting these, send the correct values cookies: [] as Cookie[], headersSize: -1, bodySize: -1, - }; + } as const; if (typeof requestData.postData === 'undefined') { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { postData: postDataToBeOmitted, ...remainingRequestData } = requestData; - return remainingRequestData as Request; + return remainingRequestData; } - return requestData as Request; + return requestData; } From 97bb826a66cbb26e5b21c640681be4181f455d5a Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Wed, 16 Aug 2023 08:45:21 -0500 Subject: [PATCH 20/22] chore: comment cleanup in tests --- packages/node/test/index.test.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index 6830effd90..b4d62b1e5a 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -55,8 +55,7 @@ describe('#metrics', function () { it('should throw an error if `apiKey` is missing', function () { const app = express(); app.use((req, res, next) => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore + // @ts-expect-error deliberately passing in bad data readmeio.log('', req, res, {}); return next(); }); @@ -76,8 +75,7 @@ describe('#metrics', function () { it('should throw an error if `group` is missing', function () { const app = express(); app.use((req, res, next) => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore + // @ts-expect-error deliberately passing in bad data readmeio.log(apiKey, req, res); return next(); }); From 82f06dcff87312c8495b0a1d15d55c8ce4609b97 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Wed, 16 Aug 2023 08:45:33 -0500 Subject: [PATCH 21/22] chore: slightly stricter type --- packages/node/src/lib/process-request.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node/src/lib/process-request.ts b/packages/node/src/lib/process-request.ts index ae327b7ecc..e23b45d201 100644 --- a/packages/node/src/lib/process-request.ts +++ b/packages/node/src/lib/process-request.ts @@ -193,7 +193,7 @@ export default function processRequest( req.headers.authorization = mask(req.headers.authorization); } - const requestData = { + const requestData: Request = { method: req.method || '', url: url.format({ // Handle cases where some reverse proxies put two protocols into x-forwarded-proto From b5c3455790942e4d232f72ee8b60f7a601bb7ef2 Mon Sep 17 00:00:00 2001 From: Kanad Gupta Date: Wed, 16 Aug 2023 11:41:34 -0500 Subject: [PATCH 22/22] refactor: uuid type Co-Authored-By: Gabe Ratcliff --- packages/node/src/lib/construct-payload.ts | 5 +++-- packages/node/src/lib/log.ts | 3 ++- packages/node/src/lib/metrics-log.ts | 7 ++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/node/src/lib/construct-payload.ts b/packages/node/src/lib/construct-payload.ts index 06a8e50333..df916ac4ce 100644 --- a/packages/node/src/lib/construct-payload.ts +++ b/packages/node/src/lib/construct-payload.ts @@ -1,4 +1,5 @@ import type { OutgoingLogBody } from './metrics-log'; +import type { UUID } from 'node:crypto'; import type { IncomingMessage, ServerResponse } from 'node:http'; import type { TLSSocket } from 'tls'; @@ -83,7 +84,7 @@ export interface PayloadData { * * @example {base_url}/logs/{logId} */ - logId?: string; + logId?: UUID; /** * Object or string | The incoming request body. You should provide this function a parsed object, @@ -141,7 +142,7 @@ export function constructPayload( const serverTime = payloadData.responseEndDateTime.getTime() - payloadData.startedDateTime.getTime(); return { - _id: payloadData.logId || uuidv4(), + _id: payloadData.logId || (uuidv4() as UUID), _version: 3, group: { id: mask(payloadData.apiKey), diff --git a/packages/node/src/lib/log.ts b/packages/node/src/lib/log.ts index 03834f0d1f..b33abfab9a 100644 --- a/packages/node/src/lib/log.ts +++ b/packages/node/src/lib/log.ts @@ -1,5 +1,6 @@ import type { LogOptions } from './construct-payload'; import type { GroupingObject, OutgoingLogBody } from './metrics-log'; +import type { UUID } from 'node:crypto'; import type { IncomingMessage, ServerResponse } from 'node:http'; import * as url from 'url'; @@ -105,7 +106,7 @@ export function log( const bufferLength = clamp(options.bufferLength || config.bufferLength, 1, 30); const startedDateTime = new Date(); - const logId = uuidv4(); + const logId = uuidv4() as UUID; // baseLogUrl can be provided, but if it isn't then we // attempt to fetch it from the ReadMe API diff --git a/packages/node/src/lib/metrics-log.ts b/packages/node/src/lib/metrics-log.ts index 02248d57c1..e1ae0b5bac 100644 --- a/packages/node/src/lib/metrics-log.ts +++ b/packages/node/src/lib/metrics-log.ts @@ -1,3 +1,4 @@ +import type { UUID } from 'crypto'; import type { Har } from 'har-format'; import type { Response } from 'node-fetch'; @@ -32,7 +33,7 @@ export interface GroupingObject { } export interface OutgoingLogBody { - _id?: string; + _id?: UUID; _version: number; clientIPAddress: string; development: boolean; @@ -41,7 +42,7 @@ export interface OutgoingLogBody { request: Har; } -type LogId = string | string[] | undefined; +type LogId = UUID | UUID[] | undefined; export interface LogResponse { ids: LogId; @@ -81,7 +82,7 @@ function shouldBackoff(response: Response) { function getLogIds(body: OutgoingLogBody | OutgoingLogBody[]): LogId { if (Array.isArray(body)) { - return body.map(value => value._id) as string[]; + return body.map(value => value._id) as UUID[]; } return body._id;