Skip to content

Commit 2c30c11

Browse files
authored
Merge branch 'main' into dependabot/npm_and_yarn/micromatch-4.0.8
2 parents 9d045dd + 956c2d8 commit 2c30c11

File tree

9 files changed

+1369
-1161
lines changed

9 files changed

+1369
-1161
lines changed

__tests__/fake-serv/src/handlers/hello.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ServiceHandler } from '../../../../src/index';
1+
import { repl$, ServiceHandler } from '../../../../src/index';
22
import { FakeServLocals } from '../index';
33

44
export const get: ServiceHandler<FakeServLocals> = async (req, res) => {
@@ -8,3 +8,9 @@ export const get: ServiceHandler<FakeServLocals> = async (req, res) => {
88
export const post: ServiceHandler<FakeServLocals> = async (req, res) => {
99
res.sendStatus(204);
1010
};
11+
12+
export function thisIsATest() {
13+
return 'Yes, true';
14+
}
15+
16+
repl$(thisIsATest);

package.json

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -60,64 +60,64 @@
6060
"dependencies": {
6161
"@godaddy/terminus": "^4.12.1",
6262
"@opentelemetry/api": "^1.9.0",
63-
"@opentelemetry/exporter-prometheus": "^0.52.1",
64-
"@opentelemetry/instrumentation-dns": "^0.38.0",
65-
"@opentelemetry/instrumentation-express": "^0.41.1",
66-
"@opentelemetry/instrumentation-generic-pool": "^0.38.0",
67-
"@opentelemetry/instrumentation-graphql": "^0.42.0",
68-
"@opentelemetry/instrumentation-http": "^0.52.1",
69-
"@opentelemetry/instrumentation-ioredis": "^0.42.0",
70-
"@opentelemetry/instrumentation-net": "^0.38.0",
71-
"@opentelemetry/instrumentation-pg": "^0.43.0",
72-
"@opentelemetry/instrumentation-pino": "^0.41.0",
73-
"@opentelemetry/instrumentation-undici": "^0.4.0",
74-
"@opentelemetry/resource-detector-container": "^0.3.11",
75-
"@opentelemetry/resource-detector-gcp": "^0.29.10",
76-
"@opentelemetry/sdk-node": "^0.52.1",
77-
"@opentelemetry/semantic-conventions": "^1.25.1",
63+
"@opentelemetry/exporter-prometheus": "^0.53.0",
64+
"@opentelemetry/instrumentation-dns": "^0.39.0",
65+
"@opentelemetry/instrumentation-express": "^0.43.0",
66+
"@opentelemetry/instrumentation-generic-pool": "^0.39.0",
67+
"@opentelemetry/instrumentation-graphql": "^0.43.0",
68+
"@opentelemetry/instrumentation-http": "^0.53.0",
69+
"@opentelemetry/instrumentation-ioredis": "^0.43.0",
70+
"@opentelemetry/instrumentation-net": "^0.39.0",
71+
"@opentelemetry/instrumentation-pg": "^0.45.1",
72+
"@opentelemetry/instrumentation-pino": "^0.42.0",
73+
"@opentelemetry/instrumentation-undici": "^0.6.0",
74+
"@opentelemetry/resource-detector-container": "^0.4.3",
75+
"@opentelemetry/resource-detector-gcp": "^0.29.12",
76+
"@opentelemetry/sdk-node": "^0.53.0",
77+
"@opentelemetry/semantic-conventions": "^1.27.0",
7878
"@sesamecare-oss/confit": "^2.2.1",
79-
"@sesamecare-oss/opentelemetry-node-metrics": "^1.0.1",
79+
"@sesamecare-oss/opentelemetry-node-metrics": "^1.1.0",
8080
"ajv": "^8.17.1",
81-
"cookie-parser": "^1.4.6",
81+
"cookie-parser": "^1.4.7",
8282
"dotenv": "^16.4.5",
83-
"express": "^5.0.0-beta.3",
84-
"express-openapi-validator": "^5.2.0",
83+
"express": "^5.0.1",
84+
"express-openapi-validator": "^5.3.7",
8585
"glob": "^8.1.0",
8686
"lodash": "^4.17.21",
8787
"minimist": "^1.2.8",
88-
"pino": "^9.3.2",
88+
"pino": "^9.4.0",
8989
"read-pkg-up": "^7.0.1",
9090
"request-ip": "^3.3.0"
9191
},
9292
"devDependencies": {
93-
"@commitlint/cli": "^19.4.0",
94-
"@commitlint/config-conventional": "^19.2.2",
93+
"@commitlint/cli": "^19.5.0",
94+
"@commitlint/config-conventional": "^19.5.0",
9595
"@openapi-typescript-infra/coconfig": "^4.4.0",
9696
"@semantic-release/commit-analyzer": "^13.0.0",
9797
"@semantic-release/exec": "^6.0.3",
98-
"@semantic-release/github": "^10.1.3",
98+
"@semantic-release/github": "^11.0.0",
9999
"@semantic-release/release-notes-generator": "^14.0.1",
100100
"@types/cookie-parser": "^1.4.7",
101-
"@types/express": "^4.17.21",
101+
"@types/express": "^5.0.0",
102102
"@types/glob": "^8.1.0",
103-
"@types/lodash": "^4.17.7",
103+
"@types/lodash": "^4.17.10",
104104
"@types/minimist": "^1.2.5",
105-
"@types/node": "^20.14.14",
105+
"@types/node": "^20.16.11",
106106
"@types/request-ip": "^0.0.41",
107107
"@types/supertest": "^6.0.2",
108108
"@typescript-eslint/eslint-plugin": "^6.21.0",
109109
"@typescript-eslint/parser": "^6.21.0",
110110
"coconfig": "^1.5.2",
111-
"eslint": "^8.57.0",
111+
"eslint": "^8.57.1",
112112
"eslint-config-prettier": "^9.1.0",
113-
"eslint-plugin-import": "^2.29.1",
113+
"eslint-plugin-import": "^2.31.0",
114114
"pino-pretty": "^11.2.2",
115115
"pinst": "^3.0.0",
116116
"supertest": "^7.0.0",
117117
"ts-node": "^10.9.2",
118118
"tsconfig-paths": "^4.2.0",
119-
"typescript": "^5.5.4",
120-
"vitest": "^2.0.5"
119+
"typescript": "^5.6.3",
120+
"vitest": "^2.1.2"
121121
},
122122
"resolutions": {
123123
"qs": "^6.11.0"

src/bin/start-service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ const noTelemetry = (argv.repl || isDev()) && !argv.telemetry;
1919
bootstrap({
2020
...argv,
2121
telemetry: !noTelemetry,
22-
}).then(({ app, server }) => {
22+
}).then(({ app, codepath, server }) => {
2323
if (argv.repl) {
24-
serviceRepl(app, () => {
24+
serviceRepl(app, codepath, () => {
2525
server?.close();
2626
});
2727
}

src/bootstrap.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,5 +129,5 @@ export async function bootstrap<
129129
const { startApp, listen } = await import('./express-app/app.js');
130130
const app = await startApp<SLocals, RLocals>(opts);
131131
const server = argv?.nobind ? undefined : await listen(app);
132-
return { server, app };
132+
return { server, app, codepath };
133133
}

src/development/repl.ts

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1-
import repl from 'repl';
1+
import repl, { REPLServer } from 'repl';
2+
import fs from 'fs';
23
import path from 'path';
34

5+
import { glob } from 'glob';
6+
import { set } from 'lodash';
7+
48
import { AnyServiceLocals, ServiceExpress, ServiceLocals } from '../types';
59
import { ConfigurationSchema } from '../config/schema';
610

11+
const REPL_PROP = '$$repl$$';
12+
13+
interface WithReplProp {
14+
[REPL_PROP]?: string;
15+
}
16+
717
export function serviceRepl<SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>>(
818
app: ServiceExpress<SLocals>,
19+
codepath: string | undefined,
920
onExit: () => void,
1021
) {
1122
const rl = repl.start({
@@ -25,5 +36,78 @@ export function serviceRepl<SLocals extends AnyServiceLocals = ServiceLocals<Con
2536
}
2637
});
2738
app.locals.service.attachRepl?.(app, rl);
39+
40+
loadReplFunctions(app, codepath, rl);
41+
2842
rl.on('exit', onExit);
2943
}
44+
45+
function loadReplFunctions<SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>>(
46+
app: ServiceExpress<SLocals>,
47+
codepath: string | undefined,
48+
rl: REPLServer,
49+
) {
50+
if (!codepath) {
51+
return;
52+
}
53+
54+
const files = glob.sync(path.join(codepath, '**/*.{js,ts}'));
55+
56+
files.forEach((file) => {
57+
try {
58+
// Read the file content as text
59+
const fileContent = fs.readFileSync(file, 'utf-8');
60+
61+
// Check if repl$ is present, in a very rudimentary way
62+
if (/repl\$\(/.test(fileContent)) {
63+
// eslint-disable-next-line global-require, import/no-dynamic-require, @typescript-eslint/no-var-requires
64+
const module = require(path.resolve(file));
65+
66+
// Look for functions with the REPL_PROP marker
67+
Object.values(module).forEach((exported) => {
68+
if (!exported) {
69+
return;
70+
}
71+
if (typeof exported === 'function') {
72+
const replName = (exported as WithReplProp)[REPL_PROP];
73+
if (replName) {
74+
set(rl.context, replName, exported.bind(null, app));
75+
}
76+
}
77+
});
78+
}
79+
} catch (err) {
80+
console.error(`Failed to load REPL functions from ${file}:`, err);
81+
}
82+
});
83+
}
84+
85+
// Can't seem to sort out proper generics here, so we'll just use any since it's dev only
86+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
87+
type ReplAny = any;
88+
89+
/**
90+
* This decorator-like function can be applied to functions and the service will load and expose
91+
* the function when the repl is engaged.
92+
*
93+
* async function myFunction(app: MyService['App'], arg1: string, arg2: number) {
94+
* }
95+
* repl$(myFunction);
96+
*
97+
* or
98+
*
99+
* repl(myFunction, 'some.func.name');
100+
*/
101+
export function repl$<
102+
S extends ServiceExpress<ReplAny>,
103+
T extends (app: S, ...args: ReplAny[]) => ReplAny
104+
>(fn: T, name?: string) {
105+
const functionName = name || fn.name;
106+
if (!functionName) {
107+
throw new Error('Function must have a name or a name must be provided.');
108+
}
109+
Object.defineProperty(fn, REPL_PROP, {
110+
enumerable: false,
111+
value: functionName,
112+
});
113+
}

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export * from './config';
66
export * from './error';
77
export * from './bootstrap';
88
export * from './hook';
9+
export { repl$ } from './development/repl';

src/telemetry/index.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
2+
import {
3+
envDetectorSync,
4+
hostDetectorSync,
5+
osDetectorSync,
6+
processDetectorSync,
7+
} from '@opentelemetry/resources';
8+
import { containerDetector } from '@opentelemetry/resource-detector-container';
9+
import { gcpDetector } from '@opentelemetry/resource-detector-gcp';
210
import * as opentelemetry from '@opentelemetry/sdk-node';
311
import { PrometheusExporter } from '@opentelemetry/exporter-prometheus';
412

@@ -12,7 +20,7 @@ import type {
1220
import type { ListenFn, StartAppFn } from '../express-app/index';
1321
import type { ConfigurationSchema } from '../config/schema';
1422

15-
import { getAutoInstrumentations, getResource } from './instrumentations';
23+
import { getAutoInstrumentations } from './instrumentations';
1624
import { DummySpanExporter } from './DummyExporter';
1725

1826
// For troubleshooting, set the log level to DiagLogLevel.DEBUG
@@ -53,9 +61,17 @@ export async function startGlobalTelemetry(serviceName: string) {
5361
serviceName,
5462
autoDetectResources: false,
5563
traceExporter: getExporter(),
56-
resource: await getResource(),
64+
resourceDetectors: [
65+
envDetectorSync,
66+
hostDetectorSync,
67+
osDetectorSync,
68+
processDetectorSync,
69+
containerDetector,
70+
gcpDetector,
71+
],
5772
metricReader: prometheusExporter,
5873
instrumentations,
74+
logRecordProcessors: [],
5975
views: [
6076
new opentelemetry.metrics.View({
6177
instrumentName: 'http_request_duration_seconds',
@@ -107,5 +123,5 @@ export async function startWithTelemetry<
107123
await shutdownGlobalTelemetry();
108124
app.locals.logger.info('OpenTelemetry shut down');
109125
});
110-
return { app, server };
126+
return { app, codepath: options.codepath, server };
111127
}

src/telemetry/instrumentations.ts

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,6 @@ import { IORedisInstrumentation } from '@opentelemetry/instrumentation-ioredis';
88
import { NetInstrumentation } from '@opentelemetry/instrumentation-net';
99
import { PgInstrumentation } from '@opentelemetry/instrumentation-pg';
1010
import { PinoInstrumentation } from '@opentelemetry/instrumentation-pino';
11-
import { containerDetector } from '@opentelemetry/resource-detector-container';
12-
import { gcpDetector } from '@opentelemetry/resource-detector-gcp';
13-
import {
14-
Resource,
15-
detectResources,
16-
detectResourcesSync,
17-
envDetectorSync,
18-
hostDetectorSync,
19-
osDetectorSync,
20-
processDetectorSync,
21-
} from '@opentelemetry/resources';
2211

2312
const InstrumentationMap = {
2413
'@opentelemetry/instrumentation-http': HttpInstrumentation,
@@ -58,23 +47,3 @@ export function getAutoInstrumentations(
5847
})
5948
.filter((i) => !!i) as Instrumentation[];
6049
}
61-
62-
// Async function to get combined resources
63-
export async function getResource(): Promise<Resource> {
64-
const syncDetectors = [
65-
envDetectorSync,
66-
hostDetectorSync,
67-
osDetectorSync,
68-
processDetectorSync,
69-
];
70-
const asyncDetectors = [
71-
containerDetector,
72-
gcpDetector,
73-
];
74-
75-
const asyncResources = await detectResources({ detectors: asyncDetectors });
76-
const syncResources = detectResourcesSync({ detectors: syncDetectors });
77-
78-
// Combine async and sync resources
79-
return syncResources.merge(asyncResources);
80-
}

0 commit comments

Comments
 (0)