From bc0d40406a0788f9d8fe34a49ef3a456a8ef8fd6 Mon Sep 17 00:00:00 2001 From: Drew Corlin Date: Sat, 9 Nov 2024 17:12:59 -0500 Subject: [PATCH] Support packages with patch and multiple files like redis --- package-lock.json | 175 ++++++++++++++++++ packages/esbuild-plugin-node/package.json | 1 + packages/esbuild-plugin-node/src/common.ts | 30 ++- .../esbuild-plugin-node/src/config/main.ts | 3 + packages/esbuild-plugin-node/src/plugin.ts | 102 ++++++---- packages/esbuild-plugin-node/src/types.ts | 19 +- .../esbuild-plugin-node/test/plugin.test.ts | 8 +- .../esbuild-plugin-node/test/test-app/app.ts | 8 +- 8 files changed, 283 insertions(+), 63 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7d88348fec..59886273ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36855,6 +36855,7 @@ "dev": true }, "packages/esbuild-plugin-node": { + "name": "@opentelemetry/esbuild-plugin-node", "version": "0.1.0", "license": "Apache-2.0", "dependencies": { @@ -36871,6 +36872,7 @@ "mocha": "7.2.0", "nyc": "15.1.0", "pino": "^8.19.0", + "redis": "^4.7.0", "rimraf": "5.0.5", "ts-mocha": "10.0.0", "typescript": "4.4.4" @@ -37227,6 +37229,71 @@ "node": ">=12" } }, + "packages/esbuild-plugin-node/node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "packages/esbuild-plugin-node/node_modules/@redis/client": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.0.tgz", + "integrity": "sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "packages/esbuild-plugin-node/node_modules/@redis/graph": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "packages/esbuild-plugin-node/node_modules/@redis/json": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz", + "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "packages/esbuild-plugin-node/node_modules/@redis/search": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz", + "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "packages/esbuild-plugin-node/node_modules/@redis/time-series": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz", + "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, "packages/esbuild-plugin-node/node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -37320,6 +37387,16 @@ "tiny-lru": "^10.0.0" } }, + "packages/esbuild-plugin-node/node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "packages/esbuild-plugin-node/node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -37394,6 +37471,24 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "packages/esbuild-plugin-node/node_modules/redis": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.0.tgz", + "integrity": "sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "./packages/*" + ], + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.6.0", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.7", + "@redis/search": "1.2.0", + "@redis/time-series": "1.1.0" + } + }, "packages/esbuild-plugin-node/node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -37412,6 +37507,13 @@ "node": ">=12" } }, + "packages/esbuild-plugin-node/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "packages/opentelemetry-host-metrics": { "name": "@opentelemetry/host-metrics", "version": "0.35.2", @@ -51489,6 +51591,7 @@ "mocha": "7.2.0", "nyc": "15.1.0", "pino": "^8.19.0", + "redis": "^4.7.0", "rimraf": "5.0.5", "ts-mocha": "10.0.0", "typescript": "4.4.4" @@ -51632,6 +51735,52 @@ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "optional": true }, + "@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "dev": true, + "requires": {} + }, + "@redis/client": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.0.tgz", + "integrity": "sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==", + "dev": true, + "requires": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + } + }, + "@redis/graph": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", + "dev": true, + "requires": {} + }, + "@redis/json": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz", + "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", + "dev": true, + "requires": {} + }, + "@redis/search": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz", + "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", + "dev": true, + "requires": {} + }, + "@redis/time-series": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz", + "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", + "dev": true, + "requires": {} + }, "buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -51701,6 +51850,12 @@ "tiny-lru": "^10.0.0" } }, + "generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "dev": true + }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -51757,6 +51912,20 @@ "string_decoder": "^1.3.0" } }, + "redis": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.0.tgz", + "integrity": "sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==", + "dev": true, + "requires": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.6.0", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.7", + "@redis/search": "1.2.0", + "@redis/time-series": "1.1.0" + } + }, "split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -51768,6 +51937,12 @@ "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-10.4.1.tgz", "integrity": "sha512-buLIzw7ppqymuO3pt10jHk/6QMeZLbidihMQU+N6sogF6EnBzG0qtDWIHuhw1x3dyNgVL/KTGIZsTK81+yCzLg==", "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, diff --git a/packages/esbuild-plugin-node/package.json b/packages/esbuild-plugin-node/package.json index 4a307212ea..38324ceb99 100644 --- a/packages/esbuild-plugin-node/package.json +++ b/packages/esbuild-plugin-node/package.json @@ -45,6 +45,7 @@ "mocha": "7.2.0", "nyc": "15.1.0", "pino": "^8.19.0", + "redis": "^4.7.0", "rimraf": "5.0.5", "ts-mocha": "10.0.0", "typescript": "4.4.4" diff --git a/packages/esbuild-plugin-node/src/common.ts b/packages/esbuild-plugin-node/src/common.ts index 182d2b8198..e698683b89 100644 --- a/packages/esbuild-plugin-node/src/common.ts +++ b/packages/esbuild-plugin-node/src/common.ts @@ -19,10 +19,11 @@ import type { ModuleParams } from './types'; export function wrapModule( originalSource: string, { + path, + moduleVersion, oTelInstrumentationPackage, oTelInstrumentationClass, instrumentationName, - instrumentedFileName, oTelInstrumentationConstructorArgs = '', }: ModuleParams ) { @@ -33,6 +34,7 @@ export function wrapModule( { let mod = module.exports; + const { satisfies } = require('semver'); const { ${oTelInstrumentationClass} } = require('${oTelInstrumentationPackage}'); const { diag } = require('@opentelemetry/api'); const instrumentations = new ${oTelInstrumentationClass}(${oTelInstrumentationConstructorArgs}).getModuleDefinitions(); @@ -47,29 +49,21 @@ export function wrapModule( : 'instrumentations[0]' }; - if (instrumentation.patch && instrumentation.files?.length) { - diag.error('Not sure how to handle patch and files on instrumentation for ${oTelInstrumentationClass} ${instrumentationName}'); - return; - } - if (instrumentation.patch) { mod = instrumentation.patch(mod) - } else { - if (!instrumentation.files?.length) { - diag.error('No patch nor files exist on instrumentation for ${oTelInstrumentationClass} ${instrumentationName}'); - return; - } else if (instrumentation.files.length === 1) { - mod = instrumentation.files[0].patch(mod); - } else { - const instrumentationFile = instrumentation.files.find(file => file.name === '${instrumentedFileName}'); - if (!instrumentationFile) { - diag.error('Not sure how to handle multiple files for instrumentations for ${instrumentationName} when none is found with name ${instrumentedFileName}'); - return; + } + + if (instrumentation.files?.length) { + for (const file of instrumentation.files.filter(f => f.name === '${path}')) { + if (!file.supportedVersions.some(v => satisfies('${moduleVersion}', v))) { + diag.debug('Skipping instrumentation for ' + path + '@' + moduleVersion + ' because it does not match supported versions' + f.supportedVersions.join(',')); + continue; } - mod = instrumentationFile.patch(mod); + mod = file.patch({ ...mod }, '${moduleVersion}'); } } + module.exports = mod; } `; diff --git a/packages/esbuild-plugin-node/src/config/main.ts b/packages/esbuild-plugin-node/src/config/main.ts index 634031a115..65489d6751 100644 --- a/packages/esbuild-plugin-node/src/config/main.ts +++ b/packages/esbuild-plugin-node/src/config/main.ts @@ -72,6 +72,9 @@ export function getOtelPackageToInstrumentationConfig() { ]; for (const instrumentationModuleDefinition of moduleDefinitions) { + // For some reason @opentelemetry/instrumentation-generic-pool reports its name as just Instrumentation + // TODO: See if this goes away with an upgrade + if (instrumentation.constructor.name === 'Instrumentation') continue; otelPackageToInstrumentationConfig[instrumentationModuleDefinition.name] = { oTelInstrumentationPackage: diff --git a/packages/esbuild-plugin-node/src/plugin.ts b/packages/esbuild-plugin-node/src/plugin.ts index 6dd20c70c1..6dcefdf4ad 100644 --- a/packages/esbuild-plugin-node/src/plugin.ts +++ b/packages/esbuild-plugin-node/src/plugin.ts @@ -18,15 +18,17 @@ import { ExtractedModule, OnLoadArgs, OpenTelemetryPluginParams, + PluginData, } from './types'; import { Plugin, PluginBuild } from 'esbuild'; +import { dirname, join } from 'path'; import { instrumentationModuleDefinitions, otelPackageToInstrumentationConfig, } from './config/main'; +import { InstrumentationModuleDefinition } from '@opentelemetry/instrumentation'; import { builtinModules } from 'module'; -import { dirname } from 'path'; import { readFile } from 'fs/promises'; import { satisfies } from 'semver'; import { wrapModule } from './common'; @@ -65,24 +67,29 @@ export function openTelemetryPlugin( // We'll rely on the OTel auto-instrumentation at runtime to patch builtin modules if (isBuiltIn(args.path, extractedModule)) return; - // See if we have an instrumentation registered for this package - const matchingInstrumentation = await getInstrumentation({ + const moduleVersion = await getModuleVersion({ extractedModule, - path: args.path, resolveDir: args.resolveDir, build, }); + if (!moduleVersion) return; + // See if we have an instrumentation registered for this package + const matchingInstrumentation = await getInstrumentation({ + extractedModule, + moduleVersion, + path: args.path, + }); if (!matchingInstrumentation) return; - return { - path, - pluginData: { - extractedModule, - shouldPatchPackage: true, - instrumentation: { name: matchingInstrumentation.name }, - }, + const pluginData: PluginData = { + extractedModule, + moduleVersion, + shouldPatchPackage: true, + instrumentationName: matchingInstrumentation.name, }; + + return { path, pluginData }; }); build.onLoad( @@ -94,19 +101,26 @@ export function openTelemetryPlugin( const contents = await readFile(path); const config = - otelPackageToInstrumentationConfig[pluginData.instrumentation.name]; + otelPackageToInstrumentationConfig[pluginData.instrumentationName]; + if (!config) return; + // console.log('config is', config); const packageConfig = pluginConfig?.instrumentationConfig?.[ config.oTelInstrumentationPackage ]; + const extractedModule = pluginData.extractedModule; return { contents: wrapModule(contents.toString(), { - instrumentationName: pluginData.instrumentation.name, + path: join( + extractedModule.package || '', + extractedModule.path || '' + ), + moduleVersion: pluginData.moduleVersion, + instrumentationName: pluginData.instrumentationName, oTelInstrumentationClass: config.oTelInstrumentationClass, oTelInstrumentationPackage: config.oTelInstrumentationPackage, - instrumentedFileName: `${pluginData.extractedModule.package}/${pluginData.extractedModule.path}`, oTelInstrumentationConstructorArgs: config.configGenerator(packageConfig), }), @@ -194,48 +208,68 @@ function isBuiltIn(path: string, extractedModule: ExtractedModule): boolean { ); } -async function getInstrumentation({ +async function getModuleVersion({ extractedModule, - path, resolveDir, build, }: { extractedModule: ExtractedModule; - path: string; resolveDir: string; build: PluginBuild; }) { + const { path: packageJsonPath } = await build.resolve( + `${extractedModule.package}/package.json`, + { + resolveDir, + kind: 'require-resolve', + } + ); + if (!packageJsonPath) return; + + const packageJsonContents = await readFile(packageJsonPath); + return JSON.parse(packageJsonContents.toString()).version; +} + +async function getInstrumentation({ + extractedModule, + path, + moduleVersion, +}: { + extractedModule: ExtractedModule; + path: string; + moduleVersion: string; +}): Promise { for (const instrumentationModuleDefinition of instrumentationModuleDefinitions) { - const moduleWithPackage = `${extractedModule.package}/${extractedModule.path}`; + const fullModulePath = `${extractedModule.package}/${extractedModule.path}`; const nameMatches = instrumentationModuleDefinition.name === path || - instrumentationModuleDefinition.name === moduleWithPackage; + instrumentationModuleDefinition.name === fullModulePath; if (!nameMatches) { - const fileMatch = instrumentationModuleDefinition.files.find( - file => file.name === path || file.name === moduleWithPackage - ); + const fileMatch = instrumentationModuleDefinition.files.find(file => { + return file.name === path || file.name === fullModulePath; + }); if (!fileMatch) continue; } - const { path: packageJsonPath } = await build.resolve( - `${extractedModule.package}/package.json`, - { - resolveDir, - kind: 'require-resolve', - } - ); - - const packageJsonContents = await readFile(packageJsonPath); - const version = JSON.parse(packageJsonContents.toString()).version; - if ( instrumentationModuleDefinition.supportedVersions.some(supportedVersion => - satisfies(version, supportedVersion) + satisfies(moduleVersion, supportedVersion) ) ) { return instrumentationModuleDefinition; } + + if ( + instrumentationModuleDefinition.files.some(file => { + if (file.name !== path && file.name !== fullModulePath) return false; + return file.supportedVersions.some(supportedVersion => + satisfies(moduleVersion, supportedVersion) + ); + }) + ) { + return instrumentationModuleDefinition; + } } return null; } diff --git a/packages/esbuild-plugin-node/src/types.ts b/packages/esbuild-plugin-node/src/types.ts index c0c9684ad3..b2c769f190 100644 --- a/packages/esbuild-plugin-node/src/types.ts +++ b/packages/esbuild-plugin-node/src/types.ts @@ -22,23 +22,24 @@ export interface ExtractedModule { path: string; } +export type PluginData = { + extractedModule: ExtractedModule; + shouldPatchPackage: boolean; + moduleVersion: string; + instrumentationName: string; +}; + export type OnLoadArgs = Omit & { - pluginData?: { - shouldPatchPackage: boolean; - package: string; - instrumentation: { - name: string; - }; - extractedModule: ExtractedModule; - }; + pluginData?: PluginData; }; export interface ModuleParams { + path?: string; oTelInstrumentationPackage: string; oTelInstrumentationClass: string; oTelInstrumentationConstructorArgs?: string; - instrumentedFileName: string; instrumentationName?: string; + moduleVersion: string; } type _RemoveFunctions = { diff --git a/packages/esbuild-plugin-node/test/plugin.test.ts b/packages/esbuild-plugin-node/test/plugin.test.ts index f224df102c..3dd1a739fe 100644 --- a/packages/esbuild-plugin-node/test/plugin.test.ts +++ b/packages/esbuild-plugin-node/test/plugin.test.ts @@ -16,7 +16,7 @@ import * as assert from 'assert'; -import { exec as execCb, spawnSync } from 'child_process'; +import { SpawnSyncReturns, exec as execCb, spawnSync } from 'child_process'; import { promisify } from 'util'; @@ -118,6 +118,12 @@ describe('Esbuild can instrument packages via a plugin', function () { assert.equal(traceId, trace_id, 'Pino logs include trace ID'); }); + it('redis', async () => { + const traceId = getTraceId(stdOutLines, 'redis-GET'); + + assert.ok(traceId, 'console span output in stdout contains a traceId'); + }); + describe('graphql', () => { it('should instrument parse', () => { const parseSpan = getTrace(stdOutLines, 'graphql.parse'); diff --git a/packages/esbuild-plugin-node/test/test-app/app.ts b/packages/esbuild-plugin-node/test/test-app/app.ts index 892cda2d31..6b3de4a956 100644 --- a/packages/esbuild-plugin-node/test/test-app/app.ts +++ b/packages/esbuild-plugin-node/test/test-app/app.ts @@ -15,6 +15,7 @@ */ import { buildTestSchema } from './graphql/schema'; +import { createClient } from 'redis'; import fastify from 'fastify'; import { graphql } from './graphql/adapter'; import pino from 'pino'; @@ -23,11 +24,16 @@ export const server = fastify({ logger: pino({}, pino.destination(1)), disableRequestLogging: true, }); +const redisClient = createClient({ url: process.env.REDIS_URL }); -server.get('/test', req => { +server.get('/test', async req => { req.log.info({ hi: 'there' }, 'Log message from handler'); + + await redisClient.get('key').catch(() => void 0); + return { hi: 'there' }; }); + const schema = buildTestSchema(); const sourceList = ` query {