Skip to content

Commit 7e6477d

Browse files
authored
feat: add a fake req to the repl to make life a little easier (#35)
1 parent 66333f5 commit 7e6477d

File tree

11 files changed

+386
-308
lines changed

11 files changed

+386
-308
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ coverage
66
.nyc_output
77
*.log
88
.tap_output
9+
.vscode
910

1011
.yarn/*
1112
!.yarn/patches

package.json

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,18 +70,18 @@
7070
"dependencies": {
7171
"@godaddy/terminus": "^4.12.1",
7272
"@opentelemetry/api": "^1.9.0",
73-
"@opentelemetry/auto-instrumentations-node": "^0.60.0",
73+
"@opentelemetry/auto-instrumentations-node": "^0.60.1",
7474
"@opentelemetry/exporter-prometheus": "^0.202.0",
7575
"@opentelemetry/instrumentation-dns": "^0.46.0",
7676
"@opentelemetry/instrumentation-express": "^0.51.0",
7777
"@opentelemetry/instrumentation-generic-pool": "^0.46.0",
7878
"@opentelemetry/instrumentation-graphql": "^0.50.0",
7979
"@opentelemetry/instrumentation-http": "^0.202.0",
8080
"@opentelemetry/instrumentation-ioredis": "^0.50.0",
81-
"@opentelemetry/instrumentation-net": "^0.46.0",
81+
"@opentelemetry/instrumentation-net": "^0.46.1",
8282
"@opentelemetry/instrumentation-pg": "^0.54.0",
8383
"@opentelemetry/instrumentation-pino": "^0.49.0",
84-
"@opentelemetry/instrumentation-undici": "^0.13.0",
84+
"@opentelemetry/instrumentation-undici": "^0.13.1",
8585
"@opentelemetry/resource-detector-container": "^0.7.2",
8686
"@opentelemetry/resource-detector-gcp": "^0.36.0",
8787
"@opentelemetry/sdk-node": "^0.202.0",
@@ -93,9 +93,9 @@
9393
"cookie-parser": "^1.4.7",
9494
"dotenv": "^16.5.0",
9595
"express": "^5.1.0",
96-
"express-openapi-validator": "^5.5.3",
97-
"glob": "^11.0.2",
98-
"import-in-the-middle": "^1.14.0",
96+
"express-openapi-validator": "^5.5.7",
97+
"glob": "^11.0.3",
98+
"import-in-the-middle": "^1.14.2",
9999
"minimist": "^1.2.8",
100100
"moderndash": "^4.0.0",
101101
"opentelemetry-resource-detector-sync-api": "^0.30.0",
@@ -111,26 +111,27 @@
111111
"@semantic-release/exec": "^7.1.0",
112112
"@semantic-release/github": "^11.0.3",
113113
"@semantic-release/release-notes-generator": "^14.0.3",
114-
"@types/cookie-parser": "^1.4.8",
115-
"@types/express": "^5.0.2",
114+
"@types/cookie-parser": "^1.4.9",
115+
"@types/express": "^5.0.3",
116116
"@types/minimist": "^1.2.5",
117-
"@types/node": "^22.15.29",
117+
"@types/node": "^22.15.31",
118118
"@types/request-ip": "^0.0.41",
119119
"@types/supertest": "^6.0.3",
120120
"@typescript-eslint/eslint-plugin": "^7.18.0",
121121
"@typescript-eslint/parser": "^7.18.0",
122122
"coconfig": "^1.6.2",
123123
"eslint": "^8.57.1",
124124
"eslint-config-prettier": "^9.1.0",
125-
"eslint-import-resolver-typescript": "^4.4.2",
125+
"eslint-import-resolver-typescript": "^4.4.3",
126126
"eslint-plugin-import": "^2.31.0",
127127
"pino-pretty": "^13.0.0",
128128
"pinst": "^3.0.0",
129+
"prettier": "^3.5.3",
129130
"supertest": "^7.1.1",
130131
"tsconfig-paths": "^4.2.0",
131-
"tsx": "^4.19.4",
132+
"tsx": "^4.20.3",
132133
"typescript": "^5.8.3",
133-
"vitest": "^3.2.1"
134+
"vitest": "^3.2.3"
134135
},
135136
"resolutions": {
136137
"qs": "^6.11.0"

src/bootstrap.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ import { config } from 'dotenv';
55
import { readPackageUp } from 'read-package-up';
66
import type { NormalizedPackageJson } from 'read-package-up';
77

8-
import type { AnyServiceLocals, RequestLocals, ServiceLocals, ServiceStartOptions } from './types.js';
8+
import type {
9+
AnyServiceLocals,
10+
RequestLocals,
11+
ServiceLocals,
12+
ServiceStartOptions,
13+
} from './types.js';
914
import { isDev } from './env.js';
1015
import { startWithTelemetry } from './telemetry/index.js';
1116
import { ConfigurationSchema } from './config/schema.js';

src/development/port-finder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,5 @@ async function getEphemeralPort(): Promise<number> {
6464
}
6565

6666
export async function getAvailablePort(basePort: number): Promise<number> {
67-
return (isTest() || process.env.TEST_RUNNER) ? getEphemeralPort() : findPort(basePort);
67+
return isTest() || process.env.TEST_RUNNER ? getEphemeralPort() : findPort(basePort);
6868
}

src/development/repl.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,23 @@ export function serviceRepl<SLocals extends AnyServiceLocals = ServiceLocals<Con
1919
codepath: string | undefined,
2020
onExit: () => void,
2121
) {
22+
class FakeReq {
23+
locals: { app: ServiceExpress<SLocals> } = { app };
24+
headers: Record<string, string | string[] | undefined> = {};
25+
query = new URLSearchParams();
26+
body: unknown = {};
27+
28+
constructor(public path: string) {
29+
this.locals.app = app;
30+
}
31+
}
32+
2233
const rl = repl.start({
2334
prompt: '> ',
2435
});
2536
Object.assign(rl.context, app.locals, {
2637
app,
38+
req: new FakeReq('/'),
2739
dump(o: unknown) {
2840
// eslint-disable-next-line no-console
2941
console.log(JSON.stringify(o, null, '\t'));
@@ -47,11 +59,9 @@ export function serviceRepl<SLocals extends AnyServiceLocals = ServiceLocals<Con
4759
rl.on('exit', onExit);
4860
}
4961

50-
async function loadReplFunctions<SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>>(
51-
app: ServiceExpress<SLocals>,
52-
codepath: string | undefined,
53-
rl: REPLServer,
54-
) {
62+
async function loadReplFunctions<
63+
SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>,
64+
>(app: ServiceExpress<SLocals>, codepath: string | undefined, rl: REPLServer) {
5565
if (!codepath) {
5666
return;
5767
}
@@ -104,7 +114,7 @@ type ReplAny = any;
104114
*/
105115
export function repl$<
106116
S extends ServiceExpress<ReplAny>,
107-
T extends (app: S, ...args: ReplAny[]) => ReplAny
117+
T extends (app: S, ...args: ReplAny[]) => ReplAny,
108118
>(fn: T, name?: string) {
109119
const functionName = name || fn.name;
110120
if (!functionName) {

src/express-app/app.ts

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export async function startApp<
6161
Object.assign(mergeObject, {
6262
trace_id: ctx.traceId,
6363
span_id: ctx.spanId,
64-
trace_flags: ctx.traceFlags
64+
trace_flags: ctx.traceFlags,
6565
});
6666
}
6767
}
@@ -70,28 +70,28 @@ export async function startApp<
7070

7171
const logger = shouldPrettyPrint
7272
? pino(
73-
{
74-
transport: {
75-
target: 'pino-pretty',
76-
options: {
77-
colorize: true,
73+
{
74+
transport: {
75+
target: 'pino-pretty',
76+
options: {
77+
colorize: true,
78+
},
7879
},
80+
mixin: poorMansOtlp,
7981
},
80-
mixin: poorMansOtlp,
81-
},
82-
destination,
83-
)
82+
destination,
83+
)
8484
: pino(
85-
{
86-
formatters: {
87-
level(label) {
88-
return { level: label };
85+
{
86+
formatters: {
87+
level(label) {
88+
return { level: label };
89+
},
8990
},
91+
mixin: poorMansOtlp,
9092
},
91-
mixin: poorMansOtlp,
92-
},
93-
destination,
94-
);
93+
destination,
94+
);
9595

9696
const serviceImpl = service();
9797
assert(serviceImpl?.start, 'Service function did not return a conforming object');
@@ -192,7 +192,11 @@ export async function startApp<
192192
);
193193
}
194194
if (routing?.bodyParsers?.form) {
195-
app.use(express.urlencoded(typeof routing.bodyParsers.form === 'object' ? routing.bodyParsers.form : {}));
195+
app.use(
196+
express.urlencoded(
197+
typeof routing.bodyParsers.form === 'object' ? routing.bodyParsers.form : {},
198+
),
199+
);
196200
}
197201

198202
if (serviceImpl.authorize) {
@@ -259,7 +263,13 @@ export async function startApp<
259263
);
260264
}
261265
if (routing?.openapi) {
262-
const openApiMiddleware = await openApi(app, rootDirectory, codepath, codePattern, options.openApiOptions);
266+
const openApiMiddleware = await openApi(
267+
app,
268+
rootDirectory,
269+
codepath,
270+
codePattern,
271+
options.openApiOptions,
272+
);
263273
app.use(openApiMiddleware);
264274
}
265275

@@ -352,7 +362,9 @@ export async function listen<SLocals extends AnyServiceLocals = ServiceLocals<Co
352362
onShutdown() {
353363
return Promise.resolve()
354364
.then(() => service.stop?.(app))
355-
.then(() => { logger.info('Service stop complete'); })
365+
.then(() => {
366+
logger.info('Service stop complete');
367+
})
356368
.then(shutdownHandler || (() => Promise.resolve()))
357369
.then(() => logger.info('Graceful shutdown complete'))
358370
.catch((error) => logger.error(error, 'Error terminating tracing'))

src/openapi.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export async function openApi<
7676

7777
try {
7878
app.locals.openApiSpecification = await new OpenAPIFramework({ apiDoc: apiSpec })
79-
.initialize({ visitApi() { } })
79+
.initialize({ visitApi() {} })
8080
.then((docs) => docs.apiDoc)
8181
.catch((error) => {
8282
app.locals.logger.error(error, 'Failed to parse and load OpenAPI spec');
@@ -130,15 +130,15 @@ export async function openApi<
130130
// by setting validateResponses to false in the config.
131131
...(getNodeEnv() === 'test'
132132
? {
133-
validateResponses: {
134-
onError(error: Error, body: unknown, req: Request) {
135-
console.log('Response body fails validation: ', error);
136-
console.log('Emitted from:', req.originalUrl);
137-
console.debug(body);
138-
throw error;
133+
validateResponses: {
134+
onError(error: Error, body: unknown, req: Request) {
135+
console.log('Response body fails validation: ', error);
136+
console.log('Emitted from:', req.originalUrl);
137+
console.debug(body);
138+
throw error;
139+
},
139140
},
140-
},
141-
}
141+
}
142142
: {}),
143143
...(typeof routing.openapi === 'object' ? routing.openapi : {}),
144144
...openApiOptions,
@@ -148,6 +148,6 @@ export async function openApi<
148148
} finally {
149149
if (_window) {
150150
(global as { window: unknown }).window = _window;
151-
};
151+
}
152152
}
153153
}

src/telemetry/hook-modules.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,6 @@ import module from 'node:module';
33
module.register('import-in-the-middle/hook.mjs', import.meta.url, {
44
parentURL: import.meta.url,
55
data: {
6-
include: [
7-
'express',
8-
'pino',
9-
'http',
10-
'dns',
11-
'net',
12-
'pg',
13-
'ioredis',
14-
'undici',
15-
'generic-pool',
16-
],
6+
include: ['express', 'pino', 'http', 'dns', 'net', 'pg', 'ioredis', 'undici', 'generic-pool'],
177
},
188
});

src/telemetry/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ export async function startWithTelemetry<
133133
await startGlobalTelemetry(options.name);
134134

135135
// eslint-disable-next-line import/no-unresolved, @typescript-eslint/no-var-requires
136-
const { startApp, listen } = await import('../express-app/app.js') as {
136+
const { startApp, listen } = (await import('../express-app/app.js')) as {
137137
startApp: StartAppFn<SLocals, RLocals>;
138138
listen: ListenFn<SLocals>;
139139
};

src/telemetry/instrumentations.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import { DnsInstrumentation } from '@opentelemetry/instrumentation-dns';
33
import { ExpressInstrumentation, SpanNameHook } from '@opentelemetry/instrumentation-express';
44
import { UndiciInstrumentation } from '@opentelemetry/instrumentation-undici';
55
import { GenericPoolInstrumentation } from '@opentelemetry/instrumentation-generic-pool';
6-
import { HttpInstrumentation, IgnoreIncomingRequestFunction } from '@opentelemetry/instrumentation-http';
6+
import {
7+
HttpInstrumentation,
8+
IgnoreIncomingRequestFunction,
9+
} from '@opentelemetry/instrumentation-http';
710
import { IORedisInstrumentation } from '@opentelemetry/instrumentation-ioredis';
811
import { NetInstrumentation } from '@opentelemetry/instrumentation-net';
912
import { GraphQLInstrumentation } from '@opentelemetry/instrumentation-graphql';

0 commit comments

Comments
 (0)