@resolveio/server-lib 20.12.0 → 20.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -153,15 +153,15 @@ var SlowQueryReporter = /** @class */ (function () {
|
|
|
153
153
|
case 1:
|
|
154
154
|
_a.trys.push([1, 8, , 9]);
|
|
155
155
|
if (!(params.queryType === 'aggregate')) return [3 /*break*/, 3];
|
|
156
|
-
return [4 /*yield*/, collection.aggregate(params.pipeline || [], params.options).explain('executionStats')];
|
|
156
|
+
return [4 /*yield*/, collection.aggregate(params.pipeline || [], this.prepareExplainOptions(params.options)).explain('executionStats')];
|
|
157
157
|
case 2: return [2 /*return*/, _a.sent()];
|
|
158
158
|
case 3:
|
|
159
159
|
if (!(params.queryType === 'find')) return [3 /*break*/, 5];
|
|
160
|
-
return [4 /*yield*/, collection.find(params.filter || {}, params.options).explain('executionStats')];
|
|
160
|
+
return [4 /*yield*/, collection.find(params.filter || {}, this.prepareExplainOptions(params.options)).explain('executionStats')];
|
|
161
161
|
case 4: return [2 /*return*/, _a.sent()];
|
|
162
162
|
case 5:
|
|
163
163
|
if (!(params.queryType === 'findOne')) return [3 /*break*/, 7];
|
|
164
|
-
return [4 /*yield*/, collection.find(params.filter || {}, params.options).limit(1).explain('executionStats')];
|
|
164
|
+
return [4 /*yield*/, collection.find(params.filter || {}, this.prepareExplainOptions(params.options)).limit(1).explain('executionStats')];
|
|
165
165
|
case 6: return [2 /*return*/, _a.sent()];
|
|
166
166
|
case 7: return [3 /*break*/, 9];
|
|
167
167
|
case 8:
|
|
@@ -276,6 +276,15 @@ var SlowQueryReporter = /** @class */ (function () {
|
|
|
276
276
|
}
|
|
277
277
|
return value;
|
|
278
278
|
};
|
|
279
|
+
SlowQueryReporter.prepareExplainOptions = function (options) {
|
|
280
|
+
if (!options) {
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
var sanitized = (0, common_1.deepCopy)(options);
|
|
284
|
+
delete sanitized.writeConcern;
|
|
285
|
+
delete sanitized.bypassDocumentValidation;
|
|
286
|
+
return Object.keys(sanitized).length ? sanitized : undefined;
|
|
287
|
+
};
|
|
279
288
|
SlowQueryReporter._explainCache = new NodeCache({ stdTTL: EXPLAIN_THROTTLE_SECONDS, checkperiod: 0 });
|
|
280
289
|
return SlowQueryReporter;
|
|
281
290
|
}());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/util/slow-query-reporter.ts"],"names":[],"mappings":"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","file":"slow-query-reporter.js","sourcesContent":["import { createHash } from 'crypto';\nimport { Document } from 'mongodb';\nimport * as NodeCache from 'node-cache';\nimport { hostname } from 'os';\nimport { SlowQueryReportPayload } from '../models/slow-query-report.model';\nimport { ResolveIOServer } from '../resolveio-server-app';\nimport { deepCopy } from './common';\n\nconst SLOW_QUERY_ENDPOINT = 'https://backend.resolveio.com/api/slow-queries/report';\nconst DEFAULT_SLOW_QUERY_THRESHOLD_MS = 2000;\nconst EXPLAIN_THROTTLE_SECONDS = 5 * 60;\n\ninterface SlowQueryReportParams {\n\tcollection: string;\n\tfilter?: Document;\n\tpipeline?: any[];\n\toptions?: Document;\n\tdurationMs: number;\n\tqueryType: 'find' | 'findOne' | 'aggregate' | 'countDocuments';\n\tnotes?: string;\n\tqueryHash?: string;\n\tsourceApp?: string;\n}\n\nexport class SlowQueryReporter {\n\tprivate static _explainCache = new NodeCache({ stdTTL: EXPLAIN_THROTTLE_SECONDS, checkperiod: 0 });\n\n\tpublic static async reportSlowQuery(params: SlowQueryReportParams): Promise<void> {\n\t\tconst thresholdMs = this.getThresholdMs();\n\t\tif (thresholdMs <= 0 || params.durationMs < thresholdMs) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst queryHash = params.queryHash || this.generateQueryHash(params);\n\t\tconst apiKey = this.getApiKey();\n\t\tconst payload: SlowQueryReportPayload = {\n\t\t\tqueryHash,\n\t\t\tcollection: params.collection,\n\t\t\tpipeline: params.pipeline ? deepCopy(params.pipeline) : undefined,\n\t\t\tfilter: params.filter ? deepCopy(params.filter) : undefined,\n\t\t\toptions: params.options ? deepCopy(params.options) : undefined,\n\t\t\tdurationMs: params.durationMs,\n\t\t\tthresholdMs,\n\t\t\tclientSlug: ResolveIOServer.getClientName(),\n\t\t\tclientName: this.getClientName(),\n\t\t\tsourceApp: params.sourceApp || 'mongo-manager',\n\t\t\tenvironment: this.getEnvironment(),\n\t\t\tnodeHostname: hostname(),\n\t\t\tnotes: params.notes,\n\t\t\tapiKey\n\t\t};\n\n\t\ttry {\n\t\t\tconst explain = await this.captureExplain(params, queryHash);\n\t\t\tif (explain) {\n\t\t\t\tpayload.explainPlan = deepCopy(explain.queryPlanner || explain);\n\t\t\t\tif (explain.executionStats) {\n\t\t\t\t\tpayload.explainExecutionStats = deepCopy(explain.executionStats);\n\t\t\t\t}\n\t\t\t\tpayload.explainGeneratedAt = new Date();\n\t\t\t}\n\n\t\t\tawait this.sendPayload(payload, apiKey);\n\t\t}\n\t\tcatch (err) {\n\t\t\tconsole.error('SlowQueryReporter failed', err);\n\t\t}\n\t}\n\n\tpublic static reportSlowQueryFireAndForget(params: SlowQueryReportParams): void {\n\t\t// eslint-disable-next-line no-restricted-syntax, promise/prefer-await-to-then\n\t\tvoid this.reportSlowQuery(params).catch(err => {\n\t\t\tconsole.error('SlowQueryReporter fire-and-forget error', err);\n\t\t});\n\t}\n\n\tprivate static getThresholdMs(): number {\n\t\tconst config = ResolveIOServer.getServerConfig();\n\t\tconst envValue = process.env.SLOW_QUERY_THRESHOLD_MS;\n\t\tif (envValue && !Number.isNaN(Number(envValue))) {\n\t\t\treturn Number.parseInt(envValue, 10);\n\t\t}\n\n\t\tconst configValue = config?.SLOW_QUERY_THRESHOLD_MS;\n\t\tif (typeof configValue === 'number') {\n\t\t\treturn configValue;\n\t\t}\n\n\t\tif (typeof configValue === 'string' && configValue.length && !Number.isNaN(Number(configValue))) {\n\t\t\treturn Number.parseInt(configValue, 10);\n\t\t}\n\n\t\treturn DEFAULT_SLOW_QUERY_THRESHOLD_MS;\n\t}\n\n\tprivate static getEnvironment(): string {\n\t\tconst config = ResolveIOServer.getServerConfig();\n\t\treturn (\n\t\t\tconfig?.RESOLVEIO_ENV ||\n\t\t\tconfig?.ENVIRONMENT ||\n\t\t\tconfig?.ROOT_URL ||\n\t\t\tprocess.env.RESOLVEIO_ENV ||\n\t\t\tprocess.env.NODE_ENV ||\n\t\t\t'unknown'\n\t\t);\n\t}\n\n\tprivate static getClientName(): string | undefined {\n\t\tconst config = ResolveIOServer.getServerConfig();\n\t\treturn config?.CLIENT_NAME || ResolveIOServer.getClientName();\n\t}\n\n\tprivate static async captureExplain(params: SlowQueryReportParams, queryHash: string): Promise<Document | undefined> {\n\t\tif (params.queryType === 'countDocuments') {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (!this.shouldExplain(queryHash)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst collection = ResolveIOServer.getMainDB().collection(params.collection);\n\t\ttry {\n\t\t\tif (params.queryType === 'aggregate') {\n\t\t\t\treturn await collection.aggregate(params.pipeline || [], params.options).explain('executionStats');\n\t\t\t}\n\t\t\telse if (params.queryType === 'find') {\n\t\t\t\treturn await collection.find(params.filter || {}, params.options).explain('executionStats');\n\t\t\t}\n\t\t\telse if (params.queryType === 'findOne') {\n\t\t\t\treturn await collection.find(params.filter || {}, params.options).limit(1).explain('executionStats');\n\t\t\t}\n\t\t}\n\t\tcatch (err) {\n\t\t\tconsole.error('SlowQueryReporter explain failed', err);\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tprivate static shouldExplain(queryHash: string): boolean {\n\t\tif (!queryHash) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (this._explainCache.has(queryHash)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis._explainCache.set(queryHash, true);\n\t\treturn true;\n\t}\n\n\tprivate static async sendPayload(payload: SlowQueryReportPayload, apiKey?: string): Promise<void> {\n\t\tconst transport = globalThis.fetch;\n\t\tif (typeof transport !== 'function') {\n\t\t\tconsole.error('SlowQueryReporter cannot send payload (fetch missing)', payload);\n\t\t\treturn;\n\t\t}\n\n\t\tconst key = apiKey || this.getApiKey();\n\t\tconst headers = removeEmptyFields({\n\t\t\t'Content-Type': 'application/json',\n\t\t\t'X-ResolveIO-Slow-Query-Key': key,\n\t\t\t'X-Slow-Query-Key': key,\n\t\t\t'X-API-Key': key\n\t\t});\n\n\t\ttry {\n\t\t\tconst response = await transport(SLOW_QUERY_ENDPOINT, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify(payload)\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconsole.error('SlowQueryReporter payload rejected', {\n\t\t\t\t\tstatus: response.status,\n\t\t\t\t\tstatusText: response.statusText,\n\t\t\t\t\tqueryHash: payload.queryHash\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tcatch (err) {\n\t\t\tconsole.error('SlowQueryReporter network error', err);\n\t\t}\n\t}\n\n\tprivate static getApiKey(): string | undefined {\n\t\tconst config = ResolveIOServer.getServerConfig();\n\t\treturn (\n\t\t\tprocess.env.SLOW_QUERY_API_KEY ||\n\t\t\tprocess.env.SLOW_QUERY_INGEST_KEY ||\n\t\t\tconfig?.SLOW_QUERY_API_KEY ||\n\t\t\tconfig?.SLOW_QUERY_INGEST_KEY ||\n\t\t\tResolveIOServer.getClientName()\n\t\t);\n\t}\n\n\tprivate static generateQueryHash(params: SlowQueryReportParams): string {\n\t\ttry {\n\t\t\tconst normalized = this.normalizeForHash({\n\t\t\t\tcollection: params.collection,\n\t\t\t\tfilter: params.filter || null,\n\t\t\t\tpipeline: params.pipeline || null,\n\t\t\t\toptions: params.options || null,\n\t\t\t\tqueryType: params.queryType\n\t\t\t});\n\t\t\tconst digest = createHash('sha256').update(JSON.stringify(normalized)).digest('hex').slice(0, 16);\n\t\t\treturn `${params.collection}-${digest}`;\n\t\t}\n\t\tcatch (err) {\n\t\t\tconsole.error('SlowQueryReporter hash generation failed', err);\n\t\t\treturn `${params.collection}-${Date.now()}`;\n\t\t}\n\t}\n\n\tprivate static normalizeForHash(value: any): any {\n\t\tif (value === null || value === undefined) {\n\t\t\treturn value;\n\t\t}\n\n\t\tif (Array.isArray(value)) {\n\t\t\treturn value.map(item => this.normalizeForHash(item));\n\t\t}\n\n\t\tif (value instanceof RegExp) {\n\t\t\treturn value.toString();\n\t\t}\n\n\t\tif (typeof value === 'object') {\n\t\t\tif (typeof (value as any).toHexString === 'function') {\n\t\t\t\treturn (value as any).toHexString();\n\t\t\t}\n\n\t\t\tconst ordered: Record<string, any> = {};\n\t\t\tObject.keys(value).sort().forEach(key => {\n\t\t\t\tordered[key] = this.normalizeForHash(value[key]);\n\t\t\t});\n\t\t\treturn ordered;\n\t\t}\n\n\t\treturn value;\n\t}\n}\n\n\tfunction removeEmptyFields<T extends Record<string, any>>(obj: T): T {\n\t\treturn (Object.keys(obj) as Array<keyof T>).reduce((acc, key) => {\n\t\t\tconst value = obj[key];\n\t\t\tif (value !== undefined && value !== null) {\n\t\t\t\tacc[key] = value;\n\t\t\t}\n\t\t\treturn acc;\n\t\t}, {} as T);\n\t}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/util/slow-query-reporter.ts"],"names":[],"mappings":"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","file":"slow-query-reporter.js","sourcesContent":["import { createHash } from 'crypto';\nimport { Document } from 'mongodb';\nimport * as NodeCache from 'node-cache';\nimport { hostname } from 'os';\nimport { SlowQueryReportPayload } from '../models/slow-query-report.model';\nimport { ResolveIOServer } from '../resolveio-server-app';\nimport { deepCopy } from './common';\n\nconst SLOW_QUERY_ENDPOINT = 'https://backend.resolveio.com/api/slow-queries/report';\nconst DEFAULT_SLOW_QUERY_THRESHOLD_MS = 2000;\nconst EXPLAIN_THROTTLE_SECONDS = 5 * 60;\n\ninterface SlowQueryReportParams {\n\tcollection: string;\n\tfilter?: Document;\n\tpipeline?: any[];\n\toptions?: Document;\n\tdurationMs: number;\n\tqueryType: 'find' | 'findOne' | 'aggregate' | 'countDocuments';\n\tnotes?: string;\n\tqueryHash?: string;\n\tsourceApp?: string;\n}\n\nexport class SlowQueryReporter {\n\tprivate static _explainCache = new NodeCache({ stdTTL: EXPLAIN_THROTTLE_SECONDS, checkperiod: 0 });\n\n\tpublic static async reportSlowQuery(params: SlowQueryReportParams): Promise<void> {\n\t\tconst thresholdMs = this.getThresholdMs();\n\t\tif (thresholdMs <= 0 || params.durationMs < thresholdMs) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst queryHash = params.queryHash || this.generateQueryHash(params);\n\t\tconst apiKey = this.getApiKey();\n\t\tconst payload: SlowQueryReportPayload = {\n\t\t\tqueryHash,\n\t\t\tcollection: params.collection,\n\t\t\tpipeline: params.pipeline ? deepCopy(params.pipeline) : undefined,\n\t\t\tfilter: params.filter ? deepCopy(params.filter) : undefined,\n\t\t\toptions: params.options ? deepCopy(params.options) : undefined,\n\t\t\tdurationMs: params.durationMs,\n\t\t\tthresholdMs,\n\t\t\tclientSlug: ResolveIOServer.getClientName(),\n\t\t\tclientName: this.getClientName(),\n\t\t\tsourceApp: params.sourceApp || 'mongo-manager',\n\t\t\tenvironment: this.getEnvironment(),\n\t\t\tnodeHostname: hostname(),\n\t\t\tnotes: params.notes,\n\t\t\tapiKey\n\t\t};\n\n\t\ttry {\n\t\t\tconst explain = await this.captureExplain(params, queryHash);\n\t\t\tif (explain) {\n\t\t\t\tpayload.explainPlan = deepCopy(explain.queryPlanner || explain);\n\t\t\t\tif (explain.executionStats) {\n\t\t\t\t\tpayload.explainExecutionStats = deepCopy(explain.executionStats);\n\t\t\t\t}\n\t\t\t\tpayload.explainGeneratedAt = new Date();\n\t\t\t}\n\n\t\t\tawait this.sendPayload(payload, apiKey);\n\t\t}\n\t\tcatch (err) {\n\t\t\tconsole.error('SlowQueryReporter failed', err);\n\t\t}\n\t}\n\n\tpublic static reportSlowQueryFireAndForget(params: SlowQueryReportParams): void {\n\t\t// eslint-disable-next-line no-restricted-syntax, promise/prefer-await-to-then\n\t\tvoid this.reportSlowQuery(params).catch(err => {\n\t\t\tconsole.error('SlowQueryReporter fire-and-forget error', err);\n\t\t});\n\t}\n\n\tprivate static getThresholdMs(): number {\n\t\tconst config = ResolveIOServer.getServerConfig();\n\t\tconst envValue = process.env.SLOW_QUERY_THRESHOLD_MS;\n\t\tif (envValue && !Number.isNaN(Number(envValue))) {\n\t\t\treturn Number.parseInt(envValue, 10);\n\t\t}\n\n\t\tconst configValue = config?.SLOW_QUERY_THRESHOLD_MS;\n\t\tif (typeof configValue === 'number') {\n\t\t\treturn configValue;\n\t\t}\n\n\t\tif (typeof configValue === 'string' && configValue.length && !Number.isNaN(Number(configValue))) {\n\t\t\treturn Number.parseInt(configValue, 10);\n\t\t}\n\n\t\treturn DEFAULT_SLOW_QUERY_THRESHOLD_MS;\n\t}\n\n\tprivate static getEnvironment(): string {\n\t\tconst config = ResolveIOServer.getServerConfig();\n\t\treturn (\n\t\t\tconfig?.RESOLVEIO_ENV ||\n\t\t\tconfig?.ENVIRONMENT ||\n\t\t\tconfig?.ROOT_URL ||\n\t\t\tprocess.env.RESOLVEIO_ENV ||\n\t\t\tprocess.env.NODE_ENV ||\n\t\t\t'unknown'\n\t\t);\n\t}\n\n\tprivate static getClientName(): string | undefined {\n\t\tconst config = ResolveIOServer.getServerConfig();\n\t\treturn config?.CLIENT_NAME || ResolveIOServer.getClientName();\n\t}\n\n\tprivate static async captureExplain(params: SlowQueryReportParams, queryHash: string): Promise<Document | undefined> {\n\t\tif (params.queryType === 'countDocuments') {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (!this.shouldExplain(queryHash)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst collection = ResolveIOServer.getMainDB().collection(params.collection);\n\t\ttry {\n\t\t\tif (params.queryType === 'aggregate') {\n\t\t\t\treturn await collection.aggregate(params.pipeline || [], this.prepareExplainOptions(params.options)).explain('executionStats');\n\t\t\t}\n\t\t\telse if (params.queryType === 'find') {\n\t\t\t\treturn await collection.find(params.filter || {}, this.prepareExplainOptions(params.options)).explain('executionStats');\n\t\t\t}\n\t\t\telse if (params.queryType === 'findOne') {\n\t\t\t\treturn await collection.find(params.filter || {}, this.prepareExplainOptions(params.options)).limit(1).explain('executionStats');\n\t\t\t}\n\t\t}\n\t\tcatch (err) {\n\t\t\tconsole.error('SlowQueryReporter explain failed', err);\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tprivate static shouldExplain(queryHash: string): boolean {\n\t\tif (!queryHash) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (this._explainCache.has(queryHash)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis._explainCache.set(queryHash, true);\n\t\treturn true;\n\t}\n\n\tprivate static async sendPayload(payload: SlowQueryReportPayload, apiKey?: string): Promise<void> {\n\t\tconst transport = globalThis.fetch;\n\t\tif (typeof transport !== 'function') {\n\t\t\tconsole.error('SlowQueryReporter cannot send payload (fetch missing)', payload);\n\t\t\treturn;\n\t\t}\n\n\t\tconst key = apiKey || this.getApiKey();\n\t\tconst headers = removeEmptyFields({\n\t\t\t'Content-Type': 'application/json',\n\t\t\t'X-ResolveIO-Slow-Query-Key': key,\n\t\t\t'X-Slow-Query-Key': key,\n\t\t\t'X-API-Key': key\n\t\t});\n\n\t\ttry {\n\t\t\tconst response = await transport(SLOW_QUERY_ENDPOINT, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify(payload)\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconsole.error('SlowQueryReporter payload rejected', {\n\t\t\t\t\tstatus: response.status,\n\t\t\t\t\tstatusText: response.statusText,\n\t\t\t\t\tqueryHash: payload.queryHash\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tcatch (err) {\n\t\t\tconsole.error('SlowQueryReporter network error', err);\n\t\t}\n\t}\n\n\tprivate static getApiKey(): string | undefined {\n\t\tconst config = ResolveIOServer.getServerConfig();\n\t\treturn (\n\t\t\tprocess.env.SLOW_QUERY_API_KEY ||\n\t\t\tprocess.env.SLOW_QUERY_INGEST_KEY ||\n\t\t\tconfig?.SLOW_QUERY_API_KEY ||\n\t\t\tconfig?.SLOW_QUERY_INGEST_KEY ||\n\t\t\tResolveIOServer.getClientName()\n\t\t);\n\t}\n\n\tprivate static generateQueryHash(params: SlowQueryReportParams): string {\n\t\ttry {\n\t\t\tconst normalized = this.normalizeForHash({\n\t\t\t\tcollection: params.collection,\n\t\t\t\tfilter: params.filter || null,\n\t\t\t\tpipeline: params.pipeline || null,\n\t\t\t\toptions: params.options || null,\n\t\t\t\tqueryType: params.queryType\n\t\t\t});\n\t\t\tconst digest = createHash('sha256').update(JSON.stringify(normalized)).digest('hex').slice(0, 16);\n\t\t\treturn `${params.collection}-${digest}`;\n\t\t}\n\t\tcatch (err) {\n\t\t\tconsole.error('SlowQueryReporter hash generation failed', err);\n\t\t\treturn `${params.collection}-${Date.now()}`;\n\t\t}\n\t}\n\n\tprivate static normalizeForHash(value: any): any {\n\t\tif (value === null || value === undefined) {\n\t\t\treturn value;\n\t\t}\n\n\t\tif (Array.isArray(value)) {\n\t\t\treturn value.map(item => this.normalizeForHash(item));\n\t\t}\n\n\t\tif (value instanceof RegExp) {\n\t\t\treturn value.toString();\n\t\t}\n\n\t\tif (typeof value === 'object') {\n\t\t\tif (typeof (value as any).toHexString === 'function') {\n\t\t\t\treturn (value as any).toHexString();\n\t\t\t}\n\n\t\t\tconst ordered: Record<string, any> = {};\n\t\t\tObject.keys(value).sort().forEach(key => {\n\t\t\t\tordered[key] = this.normalizeForHash(value[key]);\n\t\t\t});\n\t\t\treturn ordered;\n\t\t}\n\n\t\treturn value;\n\t}\n\n\tprivate static prepareExplainOptions(options?: Document): Document | undefined {\n\t\tif (!options) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst sanitized = deepCopy(options);\n\n\t\tdelete sanitized.writeConcern;\n\t\tdelete sanitized.bypassDocumentValidation;\n\n\t\treturn Object.keys(sanitized).length ? sanitized : undefined;\n\t}\n}\n\n\tfunction removeEmptyFields<T extends Record<string, any>>(obj: T): T {\n\t\treturn (Object.keys(obj) as Array<keyof T>).reduce((acc, key) => {\n\t\t\tconst value = obj[key];\n\t\t\tif (value !== undefined && value !== null) {\n\t\t\t\tacc[key] = value;\n\t\t\t}\n\t\t\treturn acc;\n\t\t}, {} as T);\n\t}\n"]}
|