vona-module-a-telemetry 5.0.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.
@@ -0,0 +1,31 @@
1
+ import type { Context, Span, SpanOptions } from '@opentelemetry/api';
2
+ import { BeanBase } from 'vona';
3
+ import type { ITelemetryCarrier } from '../types/telemetry.ts';
4
+ export declare class ServiceTelemetry extends BeanBase {
5
+ private _provider?;
6
+ private _contextManager?;
7
+ private _isTrustedIngress?;
8
+ private _enabled;
9
+ get enabled(): boolean;
10
+ init(): void;
11
+ close(forceFlush: boolean): Promise<void>;
12
+ get activeContext(): Context;
13
+ extractCarrier(carrier?: ITelemetryCarrier): Context;
14
+ isTrustedIngress(peerAddress?: string, internalHeaderValue?: string): boolean;
15
+ extractHttpIngressCarrier(trusted: boolean, carrier: ITelemetryCarrier): Context;
16
+ injectCarrier(source?: Context): ITelemetryCarrier;
17
+ startSpan(name: string, options?: SpanOptions, parent?: Context): Span;
18
+ withSpan<RESULT>(span: Span, fn: () => RESULT): RESULT;
19
+ createContext(span: Span, parent?: Context): Context;
20
+ recordException(span: Span, error: unknown): void;
21
+ endHttpSpan(span: Span, statusCode: number, aborted?: boolean): void;
22
+ getLogContext(): {
23
+ request_id: string | undefined;
24
+ trace_id: string;
25
+ span_id: string;
26
+ trace_flags: number;
27
+ } | undefined;
28
+ createServerSpan(method: string, parent: Context): Span;
29
+ enrichHttpSpan(method: string, routePath: string, controllerBeanFullName: string, action: string): void;
30
+ }
31
+ //# sourceMappingURL=telemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/service/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAarE,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAGhC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAsB/D,qBACa,gBAAiB,SAAQ,QAAQ;IAC5C,OAAO,CAAC,SAAS,CAAC,CAAqB;IACvC,OAAO,CAAC,eAAe,CAAC,CAAkC;IAC1D,OAAO,CAAC,iBAAiB,CAAC,CAAkE;IAC5F,OAAO,CAAC,QAAQ,CAAS;IAEzB,IAAI,OAAO,YAEV;IAED,IAAI;IAgCE,KAAK,CAAC,UAAU,EAAE,OAAO;IAgB/B,IAAI,aAAa,YAEhB;IAED,cAAc,CAAC,OAAO,CAAC,EAAE,iBAAiB;IAK1C,gBAAgB,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,mBAAmB,CAAC,EAAE,MAAM;IAKnE,yBAAyB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB;IAItE,aAAa,CAAC,MAAM,UAAqB,GAAG,iBAAiB;IAM7D,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,MAAM,UAAqB;IAI1E,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,MAAM,GAAG,MAAM;IAKtD,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,UAAqB;IAIrD,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO;IAS1C,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,UAAQ;IAY3D,aAAa;;;;;;IAYb,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO;IAIhD,cAAc,CACZ,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,sBAAsB,EAAE,MAAM,EAC9B,MAAM,EAAE,MAAM;CAWjB"}
@@ -0,0 +1,2 @@
1
+ export * from './telemetry.ts';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { Context, Span } from '@opentelemetry/api';
2
+ export interface ITelemetryCarrier {
3
+ version: 1;
4
+ traceparent?: string;
5
+ tracestate?: string;
6
+ }
7
+ export interface ITelemetryContextState {
8
+ requestId?: string;
9
+ context?: Context;
10
+ span?: Span;
11
+ serverSpan?: Span;
12
+ internalAction?: boolean;
13
+ serverSpanEnded?: boolean;
14
+ }
15
+ declare module 'vona' {
16
+ interface ContextState {
17
+ telemetry?: ITelemetryContextState;
18
+ }
19
+ }
20
+ //# sourceMappingURL=telemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/types/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,CAAC,CAAC;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,UAAU,CAAC,EAAE,IAAI,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,OAAO,QAAQ,MAAM,CAAC;IACpB,UAAiB,YAAY;QAC3B,SAAS,CAAC,EAAE,sBAAsB,CAAC;KACpC;CACF"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "vona-module-a-telemetry",
3
+ "version": "5.0.1",
4
+ "description": "",
5
+ "keywords": [
6
+ "Vona Module"
7
+ ],
8
+ "author": "",
9
+ "files": [
10
+ "assets",
11
+ "dist",
12
+ "src"
13
+ ],
14
+ "type": "module",
15
+ "exports": {
16
+ ".": {
17
+ "types": [
18
+ "./src/index.ts",
19
+ "./dist/index.d.ts"
20
+ ],
21
+ "default": "./dist/index.js"
22
+ },
23
+ "./package.json": "./package.json"
24
+ },
25
+ "scripts": {
26
+ "clean": "rimraf dist tsconfig.build.tsbuildinfo",
27
+ "tsc:publish": "npm run clean && node ../../../../../packages-cli/cli/src/bin/vona.ts :bin:buildModule --sourcemap && tsc -p tsconfig.build.json",
28
+ "prepublishOnly": "npm run tsc:publish",
29
+ "prepack": "clean-package",
30
+ "postpack": "clean-package restore"
31
+ },
32
+ "dependencies": {
33
+ "@eggjs/ip": "^2.1.0",
34
+ "@opentelemetry/api": "^1.9.1",
35
+ "@opentelemetry/context-async-hooks": "^2.10.0",
36
+ "@opentelemetry/core": "^2.10.0",
37
+ "@opentelemetry/exporter-trace-otlp-proto": "^0.221.0",
38
+ "@opentelemetry/resources": "^2.10.0",
39
+ "@opentelemetry/sdk-trace-base": "^2.10.0",
40
+ "@opentelemetry/sdk-trace-node": "^2.10.0",
41
+ "@opentelemetry/semantic-conventions": "^1.43.0"
42
+ },
43
+ "title": "a-telemetry",
44
+ "vonaModule": {
45
+ "dependencies": {
46
+ "a-aspect": "5.0.0",
47
+ "a-logger": "5.0.0"
48
+ }
49
+ },
50
+ "gitHead": "fac7768bb7db22ff0180dc59d711efba5b853ae7"
51
+ }
@@ -0,0 +1,132 @@
1
+ // eslint-disable
2
+ /** middlewareSystem: begin */
3
+ export * from '../bean/middlewareSystem.trace.ts';
4
+ import type { IMiddlewareSystemOptionsTrace } from '../bean/middlewareSystem.trace.ts';
5
+ import 'vona-module-a-aspect';
6
+ declare module 'vona-module-a-aspect' {
7
+
8
+ export interface IMiddlewareSystemRecord {
9
+ 'a-telemetry:trace': IMiddlewareSystemOptionsTrace;
10
+ }
11
+
12
+
13
+ }
14
+ declare module 'vona-module-a-telemetry' {
15
+
16
+ export interface MiddlewareSystemTrace {
17
+ /** @internal */
18
+ get scope(): ScopeModuleATelemetry;
19
+ }
20
+
21
+ export interface MiddlewareSystemTrace {
22
+ get $beanFullName(): 'a-telemetry.middlewareSystem.trace';
23
+ get $onionName(): 'a-telemetry:trace';
24
+ get $onionOptions(): IMiddlewareSystemOptionsTrace;
25
+ }
26
+ }
27
+ /** middlewareSystem: end */
28
+ /** bean: begin */
29
+ export * from '../bean/bean.telemetry.ts';
30
+
31
+ import 'vona';
32
+ declare module 'vona' {
33
+
34
+
35
+ }
36
+ declare module 'vona-module-a-telemetry' {
37
+
38
+ export interface BeanTelemetry {
39
+ /** @internal */
40
+ get scope(): ScopeModuleATelemetry;
41
+ }
42
+ }
43
+ /** bean: end */
44
+ /** bean: begin */
45
+ import type { BeanTelemetry } from '../bean/bean.telemetry.ts';
46
+ import 'vona';
47
+ declare module 'vona' {
48
+ export interface IBeanRecordGlobal {
49
+ 'telemetry': BeanTelemetry;
50
+ }
51
+ }
52
+ /** bean: end */
53
+ /** service: begin */
54
+ export * from '../service/telemetry.ts';
55
+
56
+ import 'vona-module-a-bean';
57
+ declare module 'vona-module-a-bean' {
58
+
59
+ export interface IServiceRecord {
60
+ 'a-telemetry:telemetry': never;
61
+ }
62
+
63
+
64
+ }
65
+ declare module 'vona-module-a-telemetry' {
66
+
67
+ export interface ServiceTelemetry {
68
+ /** @internal */
69
+ get scope(): ScopeModuleATelemetry;
70
+ }
71
+
72
+ export interface ServiceTelemetry {
73
+ get $beanFullName(): 'a-telemetry.service.telemetry';
74
+ get $onionName(): 'a-telemetry:telemetry';
75
+
76
+ }
77
+ }
78
+ /** service: end */
79
+ /** service: begin */
80
+ import type { ServiceTelemetry } from '../service/telemetry.ts';
81
+ export interface IModuleService {
82
+ 'telemetry': ServiceTelemetry;
83
+ }
84
+ /** service: end */
85
+ /** service: begin */
86
+
87
+ import 'vona';
88
+ declare module 'vona' {
89
+ export interface IBeanRecordGeneral {
90
+ 'a-telemetry.service.telemetry': ServiceTelemetry;
91
+ }
92
+ }
93
+ /** service: end */
94
+ /** config: begin */
95
+ export * from '../config/config.ts';
96
+ import type { config } from '../config/config.ts';
97
+ /** config: end */
98
+ /** monkey: begin */
99
+ export * from '../monkey.ts';
100
+ /** monkey: end */
101
+ /** scope: begin */
102
+ import { BeanScopeBase, type BeanScopeUtil, type TypeModuleConfig } from 'vona';
103
+ import { Scope } from 'vona-module-a-bean';
104
+
105
+ @Scope()
106
+ export class ScopeModuleATelemetry extends BeanScopeBase {}
107
+
108
+ export interface ScopeModuleATelemetry {
109
+ util: BeanScopeUtil;
110
+ config: TypeModuleConfig<typeof config>;
111
+ service: IModuleService;
112
+ }
113
+
114
+ import 'vona';
115
+ declare module 'vona' {
116
+ export interface IBeanScopeRecord {
117
+ 'a-telemetry': ScopeModuleATelemetry;
118
+ }
119
+
120
+ export interface IBeanScopeContainer {
121
+ telemetry: ScopeModuleATelemetry;
122
+ }
123
+
124
+ export interface IBeanScopeConfig {
125
+ 'a-telemetry': ReturnType<typeof config>;
126
+ }
127
+
128
+
129
+
130
+
131
+ }
132
+ /** scope: end */
@@ -0,0 +1,2 @@
1
+ export const __ThisModule__ = 'a-telemetry';
2
+ export { ScopeModuleATelemetry as ScopeModule } from './index.ts';
@@ -0,0 +1,71 @@
1
+ import type { Span, SpanOptions } from '@opentelemetry/api';
2
+
3
+ import { INVALID_SPAN_CONTEXT, trace } from '@opentelemetry/api';
4
+ import { BeanBase } from 'vona';
5
+ import { Bean } from 'vona-module-a-bean';
6
+
7
+ const nonRecordingSpan = trace.wrapSpanContext(INVALID_SPAN_CONTEXT);
8
+
9
+ @Bean()
10
+ export class BeanTelemetry extends BeanBase {
11
+ get enabled() {
12
+ return this.scope.service.telemetry.enabled;
13
+ }
14
+
15
+ startSpan(name: string, options?: SpanOptions): Span {
16
+ const telemetry = this.scope.service.telemetry;
17
+ return telemetry.enabled ? telemetry.startSpan(name, options) : nonRecordingSpan;
18
+ }
19
+
20
+ withSpan<RESULT>(span: Span, fn: () => RESULT): RESULT {
21
+ const telemetry = this.scope.service.telemetry;
22
+ return telemetry.enabled ? telemetry.withSpan(span, fn) : fn();
23
+ }
24
+
25
+ recordException(span: Span, error: unknown) {
26
+ const telemetry = this.scope.service.telemetry;
27
+ if (telemetry.enabled) telemetry.recordException(span, error);
28
+ }
29
+
30
+ withNamedSpan<RESULT>(name: string, fn: () => RESULT, options?: SpanOptions): RESULT {
31
+ const span = this.startSpan(name, options);
32
+ try {
33
+ const result = this.withSpan(span, fn);
34
+ if (isPromiseLike(result)) {
35
+ return result.then(
36
+ value => {
37
+ this._endSpan(span);
38
+ return value;
39
+ },
40
+ error => {
41
+ this._recordAndEndSpan(span, error);
42
+ throw error;
43
+ },
44
+ ) as RESULT;
45
+ }
46
+ this._endSpan(span);
47
+ return result;
48
+ } catch (error) {
49
+ this._recordAndEndSpan(span, error);
50
+ throw error;
51
+ }
52
+ }
53
+
54
+ private _recordAndEndSpan(span: Span, error: unknown) {
55
+ try {
56
+ this.recordException(span, error);
57
+ } catch {}
58
+ this._endSpan(span);
59
+ }
60
+
61
+ private _endSpan(span: Span) {
62
+ try {
63
+ span.end();
64
+ } catch {}
65
+ }
66
+ }
67
+
68
+ function isPromiseLike<RESULT>(value: RESULT): value is RESULT & PromiseLike<Awaited<RESULT>> {
69
+ if (!((typeof value === 'object' && value !== null) || typeof value === 'function')) return false;
70
+ return typeof (value as { then?: unknown }).then === 'function';
71
+ }
@@ -0,0 +1,73 @@
1
+ import type { Span } from '@opentelemetry/api';
2
+ import type { Next } from 'vona';
3
+ import type {
4
+ IDecoratorMiddlewareSystemOptions,
5
+ IMiddlewareSystemExecute,
6
+ } from 'vona-module-a-aspect';
7
+
8
+ import { context } from '@opentelemetry/api';
9
+ import { BeanBase, uuidv4 } from 'vona';
10
+ import { MiddlewareSystem } from 'vona-module-a-aspect';
11
+
12
+ export interface IMiddlewareSystemOptionsTrace extends IDecoratorMiddlewareSystemOptions {}
13
+
14
+ @MiddlewareSystem<IMiddlewareSystemOptionsTrace>({
15
+ dependents: 'a-logger:httpLog',
16
+ })
17
+ export class MiddlewareSystemTrace extends BeanBase implements IMiddlewareSystemExecute {
18
+ async execute(_options: IMiddlewareSystemOptionsTrace, next: Next) {
19
+ const telemetry = this.$scope.telemetry.service.telemetry;
20
+ if (!telemetry.enabled || this.ctx.state.telemetry?.internalAction) return next();
21
+
22
+ const ctx = this.ctx;
23
+ const config = telemetry.scope.config;
24
+ const requestIdHeader = config.requestIdHeader;
25
+ const requestId = normalizeRequestId(ctx.get(requestIdHeader)) ?? uuidv4();
26
+ const trustedIngress = telemetry.isTrustedIngress(
27
+ ctx.req.socket.remoteAddress,
28
+ ctx.get(config.ingress.internalHeader),
29
+ );
30
+ const parent = telemetry.extractHttpIngressCarrier(trustedIngress, {
31
+ version: 1,
32
+ traceparent: ctx.get('traceparent') || undefined,
33
+ tracestate: ctx.get('tracestate') || undefined,
34
+ });
35
+ const span = telemetry.createServerSpan(ctx.method, parent);
36
+ const active = telemetry.createContext(span, parent);
37
+ ctx.state.telemetry = { requestId, context: active, serverSpan: span };
38
+ ctx.set(requestIdHeader, requestId);
39
+
40
+ const finish = () => this._end(span, ctx.res.statusCode || ctx.status || 500, false);
41
+ const close = () =>
42
+ this._end(span, ctx.res.statusCode || ctx.status || 499, !ctx.res.writableEnded);
43
+ ctx.res.once('finish', finish);
44
+ ctx.res.once('close', close);
45
+
46
+ try {
47
+ const result = await context.with(active, next);
48
+ if (ctx.route) {
49
+ telemetry.enrichHttpSpan(
50
+ ctx.route.routeMethod,
51
+ ctx.route.routePathRaw,
52
+ ctx.route.controllerBeanFullName,
53
+ ctx.route.action,
54
+ );
55
+ }
56
+ return result;
57
+ } catch (err) {
58
+ telemetry.recordException(span, err);
59
+ throw err;
60
+ }
61
+ }
62
+
63
+ private _end(span: Span, statusCode: number, aborted: boolean) {
64
+ if (!span || this.ctx.state.telemetry?.serverSpanEnded) return;
65
+ this.ctx.state.telemetry!.serverSpanEnded = true;
66
+ this.$scope.telemetry.service.telemetry.endHttpSpan(span, statusCode, aborted);
67
+ }
68
+ }
69
+
70
+ function normalizeRequestId(value?: string) {
71
+ if (!value || value.length > 128 || !/^[\w.-]+$/.test(value)) return;
72
+ return value;
73
+ }
@@ -0,0 +1,83 @@
1
+ import type { VonaApplication } from 'vona';
2
+
3
+ interface TelemetryEnv {
4
+ TELEMETRY_ENABLED?: string;
5
+ TELEMETRY_SERVICE_NAME?: string;
6
+ TELEMETRY_SAMPLING_ROOT_RATIO?: string;
7
+ TELEMETRY_OTLP_HTTP_URL?: string;
8
+ TELEMETRY_OTLP_HTTP_HEADERS?: string;
9
+ TELEMETRY_EXPORT_MAX_QUEUE_SIZE?: string;
10
+ TELEMETRY_EXPORT_DELAY_MS?: string;
11
+ TELEMETRY_EXPORT_TIMEOUT_MS?: string;
12
+ TELEMETRY_SHUTDOWN_TIMEOUT_MS?: string;
13
+ TELEMETRY_INGRESS_TRUSTED_PROXY_CIDRS?: string;
14
+ TELEMETRY_INGRESS_INTERNAL_HEADER?: string;
15
+ TELEMETRY_INGRESS_INTERNAL_HEADER_VALUE?: string;
16
+ }
17
+
18
+ export interface ITelemetryIngressConfig {
19
+ trustedProxyCidrs: string[];
20
+ internalHeader: string;
21
+ internalHeaderValue: string;
22
+ }
23
+
24
+ export interface ITelemetryConfig {
25
+ enabled: boolean;
26
+ serviceName: string;
27
+ requestIdHeader: string;
28
+ ingress: ITelemetryIngressConfig;
29
+ sampling: { rootRatio: number };
30
+ exporter: {
31
+ url: string;
32
+ headers?: Record<string, string>;
33
+ maxQueueSize: number;
34
+ scheduledDelayMillis: number;
35
+ exportTimeoutMillis: number;
36
+ };
37
+ shutdown: { timeoutMillis: number };
38
+ }
39
+
40
+ export function config(app: VonaApplication): ITelemetryConfig {
41
+ const env = app.meta.env as TelemetryEnv;
42
+ return {
43
+ enabled: env.TELEMETRY_ENABLED === 'true',
44
+ serviceName: env.TELEMETRY_SERVICE_NAME || app.name,
45
+ requestIdHeader: 'x-request-id',
46
+ ingress: {
47
+ trustedProxyCidrs: parseList(env.TELEMETRY_INGRESS_TRUSTED_PROXY_CIDRS),
48
+ internalHeader: env.TELEMETRY_INGRESS_INTERNAL_HEADER || 'x-vona-telemetry-ingress',
49
+ internalHeaderValue: env.TELEMETRY_INGRESS_INTERNAL_HEADER_VALUE || 'internal',
50
+ },
51
+ sampling: {
52
+ rootRatio: Number.parseFloat(env.TELEMETRY_SAMPLING_ROOT_RATIO || '0.1'),
53
+ },
54
+ exporter: {
55
+ url: env.TELEMETRY_OTLP_HTTP_URL || 'http://127.0.0.1:4318/v1/traces',
56
+ headers: parseHeaders(env.TELEMETRY_OTLP_HTTP_HEADERS),
57
+ maxQueueSize: Number.parseInt(env.TELEMETRY_EXPORT_MAX_QUEUE_SIZE || '2048'),
58
+ scheduledDelayMillis: Number.parseInt(env.TELEMETRY_EXPORT_DELAY_MS || '5000'),
59
+ exportTimeoutMillis: Number.parseInt(env.TELEMETRY_EXPORT_TIMEOUT_MS || '30000'),
60
+ },
61
+ shutdown: {
62
+ timeoutMillis: Number.parseInt(env.TELEMETRY_SHUTDOWN_TIMEOUT_MS || '5000'),
63
+ },
64
+ };
65
+ }
66
+
67
+ function parseList(value?: string) {
68
+ if (!value) return [];
69
+ return value
70
+ .split(',')
71
+ .map(item => item.trim())
72
+ .filter(Boolean);
73
+ }
74
+
75
+ function parseHeaders(value?: string) {
76
+ if (!value) return undefined;
77
+ return Object.fromEntries(
78
+ value
79
+ .split(',')
80
+ .map(item => item.trim().split('='))
81
+ .filter(([key, headerValue]) => key && headerValue),
82
+ );
83
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './.metadata/index.ts';
2
+ export * from './types/index.ts';
@@ -0,0 +1,34 @@
1
+ import IP from '@eggjs/ip';
2
+
3
+ import type { ITelemetryIngressConfig } from '../config/config.ts';
4
+
5
+ type AddressMatcher = (address: string) => boolean;
6
+
7
+ export function createIngressTrustChecker(config: ITelemetryIngressConfig) {
8
+ const trustedProxyMatchers = config.trustedProxyCidrs
9
+ .map(createAddressMatcher)
10
+ .filter((matcher): matcher is AddressMatcher => Boolean(matcher));
11
+
12
+ return (peerAddress?: string, internalHeaderValue?: string) => {
13
+ if (internalHeaderValue !== config.internalHeaderValue) return false;
14
+ const address = normalizePeerAddress(peerAddress);
15
+ if (!address) return false;
16
+ return trustedProxyMatchers.some(matcher => matcher(address));
17
+ };
18
+ }
19
+
20
+ function createAddressMatcher(rule: string): AddressMatcher | undefined {
21
+ try {
22
+ if (IP.isV4Format(rule) || IP.isV6Format(rule)) {
23
+ return address => address === rule;
24
+ }
25
+ return IP.cidrSubnet(rule).contains;
26
+ } catch {
27
+ return undefined;
28
+ }
29
+ }
30
+
31
+ function normalizePeerAddress(value?: string) {
32
+ if (!value) return;
33
+ return value.startsWith('::ffff:') ? value.slice('::ffff:'.length) : value;
34
+ }
package/src/monkey.ts ADDED
@@ -0,0 +1,22 @@
1
+ import type { IMonkeyAppClose, IMonkeyAppClosed, IMonkeyAppStart } from 'vona';
2
+
3
+ import { BeanSimple } from 'vona';
4
+
5
+ import { __ThisModule__ } from './.metadata/this.ts';
6
+
7
+ export class Monkey
8
+ extends BeanSimple
9
+ implements IMonkeyAppStart, IMonkeyAppClose, IMonkeyAppClosed
10
+ {
11
+ async appStart() {
12
+ this.app.scope(__ThisModule__).service.telemetry.init();
13
+ }
14
+
15
+ async appClose() {
16
+ await this.app.scope(__ThisModule__).service.telemetry.close(true);
17
+ }
18
+
19
+ async appClosed() {
20
+ await this.app.scope(__ThisModule__).service.telemetry.close(false);
21
+ }
22
+ }