@composed-di/instrumentation-core 0.6.0-alpha
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/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/instrument.d.ts +72 -0
- package/dist/instrument.d.ts.map +1 -0
- package/dist/instrument.js +203 -0
- package/dist/instrument.js.map +1 -0
- package/dist/redactingEventListener.d.ts +120 -0
- package/dist/redactingEventListener.d.ts.map +1 -0
- package/dist/redactingEventListener.js +174 -0
- package/dist/redactingEventListener.js.map +1 -0
- package/dist/redactingInstrumentation.d.ts +120 -0
- package/dist/redactingInstrumentation.d.ts.map +1 -0
- package/dist/redactingInstrumentation.js +174 -0
- package/dist/redactingInstrumentation.js.map +1 -0
- package/dist/redaction.d.ts +91 -0
- package/dist/redaction.d.ts.map +1 -0
- package/dist/redaction.js +109 -0
- package/dist/redaction.js.map +1 -0
- package/dist/serviceInstrumentation.d.ts +151 -0
- package/dist/serviceInstrumentation.d.ts.map +1 -0
- package/dist/serviceInstrumentation.js +3 -0
- package/dist/serviceInstrumentation.js.map +1 -0
- package/package.json +44 -0
- package/src/index.ts +3 -0
- package/src/instrument.ts +285 -0
- package/src/redaction.ts +173 -0
- package/src/serviceInstrumentation.ts +158 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAA;AACxC,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./serviceInstrumentation"), exports);
|
|
18
|
+
__exportStar(require("./instrument"), exports);
|
|
19
|
+
__exportStar(require("./redaction"), exports);
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2DAAwC;AACxC,+CAA4B;AAC5B,8CAA2B"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { ServiceFactory, ServiceKey, ServiceModule } from '@composed-di/core';
|
|
2
|
+
import type { ServiceInstrumentation } from './serviceInstrumentation';
|
|
3
|
+
import type { RedactionRule } from './redaction';
|
|
4
|
+
type GenericFactory = ServiceFactory<unknown, readonly ServiceKey<any>[]>;
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for {@link instrument}. Capture policy lives here, not in
|
|
7
|
+
* the ServiceInstrumentation implementations: what an implementation
|
|
8
|
+
* receives is exactly what it is allowed to record, so no implementation
|
|
9
|
+
* carries its own capture flags or redaction logic.
|
|
10
|
+
*/
|
|
11
|
+
export interface InstrumentOptions {
|
|
12
|
+
/**
|
|
13
|
+
* The instrumentation notified of service lifecycle events and method
|
|
14
|
+
* calls.
|
|
15
|
+
*/
|
|
16
|
+
instrumentation: ServiceInstrumentation;
|
|
17
|
+
/**
|
|
18
|
+
* Deliver method arguments to the instrumentation (as
|
|
19
|
+
* MethodCallContext.args). Off by default: arguments may be large or
|
|
20
|
+
* contain secrets, and they end up wherever the instrumentation exports
|
|
21
|
+
* them. When off, the instrumentation never sees the arguments at all.
|
|
22
|
+
*/
|
|
23
|
+
captureArguments?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Deliver method call return / resolved values to the instrumentation
|
|
26
|
+
* (as the success outcome's `value`). Off by default, for the same
|
|
27
|
+
* reasons as `captureArguments`. When off, the instrumentation never
|
|
28
|
+
* sees the values at all. Initialize and dispose outcomes never carry a
|
|
29
|
+
* value: the service instance is not useful information to report.
|
|
30
|
+
*/
|
|
31
|
+
captureResults?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Per-service redaction applied to whatever the capture flags let
|
|
34
|
+
* through: matched arguments and success values are blanked (or run
|
|
35
|
+
* through the rule's custom mask) before the instrumentation sees them.
|
|
36
|
+
* The capture flags are the primary gate — when capture is off there is
|
|
37
|
+
* nothing to redact, and rules cannot re-enable delivery.
|
|
38
|
+
*/
|
|
39
|
+
redactionRules?: readonly RedactionRule<any>[];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Wraps service factories with instrumentation, so the given
|
|
43
|
+
* ServiceInstrumentation is notified when a service is initialized or
|
|
44
|
+
* disposed and when a method is called on a service instance, and may
|
|
45
|
+
* return an EventSpan per operation to observe its completion. Service
|
|
46
|
+
* instances are wrapped in a Proxy to observe method calls, and errors
|
|
47
|
+
* are rethrown after being reported, so behavior is otherwise unchanged.
|
|
48
|
+
*
|
|
49
|
+
* ServiceModule entries are flattened into their factories, so an
|
|
50
|
+
* already-built module can be instrumented as a whole. Compose the result
|
|
51
|
+
* with `ServiceModule.from`:
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* const module = ServiceModule.from(
|
|
56
|
+
* instrument([db, cache, api], {
|
|
57
|
+
* instrumentation: otel,
|
|
58
|
+
* captureArguments: true,
|
|
59
|
+
* captureResults: true,
|
|
60
|
+
* redactionRules: [redactionRule(VaultKey).redact('getSecret').build()],
|
|
61
|
+
* }),
|
|
62
|
+
* );
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* @param entries - An array of ServiceModule or factory instances to wrap.
|
|
66
|
+
* @param options - The instrumentation to notify, plus the capture and
|
|
67
|
+
* redaction policy applied before anything reaches it.
|
|
68
|
+
* @return The wrapped factories, ready to be passed to ServiceModule.from.
|
|
69
|
+
*/
|
|
70
|
+
export declare function instrument(entries: (ServiceModule | GenericFactory)[], options: InstrumentOptions): GenericFactory[];
|
|
71
|
+
export {};
|
|
72
|
+
//# sourceMappingURL=instrument.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrument.d.ts","sourceRoot":"","sources":["../src/instrument.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAC7E,OAAO,KAAK,EAGV,sBAAsB,EACvB,MAAM,0BAA0B,CAAA;AACjC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD,KAAK,cAAc,GAAG,cAAc,CAAC,OAAO,EAAE,SAAS,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;AAEzE;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,eAAe,EAAE,sBAAsB,CAAA;IAEvC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAE1B;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IAExB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,SAAS,aAAa,CAAC,GAAG,CAAC,EAAE,CAAA;CAC/C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,CAAC,aAAa,GAAG,cAAc,CAAC,EAAE,EAC3C,OAAO,EAAE,iBAAiB,GACzB,cAAc,EAAE,CAIlB"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.instrument = instrument;
|
|
13
|
+
const core_1 = require("@composed-di/core");
|
|
14
|
+
/**
|
|
15
|
+
* Wraps service factories with instrumentation, so the given
|
|
16
|
+
* ServiceInstrumentation is notified when a service is initialized or
|
|
17
|
+
* disposed and when a method is called on a service instance, and may
|
|
18
|
+
* return an EventSpan per operation to observe its completion. Service
|
|
19
|
+
* instances are wrapped in a Proxy to observe method calls, and errors
|
|
20
|
+
* are rethrown after being reported, so behavior is otherwise unchanged.
|
|
21
|
+
*
|
|
22
|
+
* ServiceModule entries are flattened into their factories, so an
|
|
23
|
+
* already-built module can be instrumented as a whole. Compose the result
|
|
24
|
+
* with `ServiceModule.from`:
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* const module = ServiceModule.from(
|
|
29
|
+
* instrument([db, cache, api], {
|
|
30
|
+
* instrumentation: otel,
|
|
31
|
+
* captureArguments: true,
|
|
32
|
+
* captureResults: true,
|
|
33
|
+
* redactionRules: [redactionRule(VaultKey).redact('getSecret').build()],
|
|
34
|
+
* }),
|
|
35
|
+
* );
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @param entries - An array of ServiceModule or factory instances to wrap.
|
|
39
|
+
* @param options - The instrumentation to notify, plus the capture and
|
|
40
|
+
* redaction policy applied before anything reaches it.
|
|
41
|
+
* @return The wrapped factories, ready to be passed to ServiceModule.from.
|
|
42
|
+
*/
|
|
43
|
+
function instrument(entries, options) {
|
|
44
|
+
return entries
|
|
45
|
+
.flatMap((e) => (e instanceof core_1.ServiceModule ? e.factories : [e]))
|
|
46
|
+
.map((factory) => makeObservable(options, factory));
|
|
47
|
+
}
|
|
48
|
+
function makeCapture(options, key) {
|
|
49
|
+
var _a, _b, _c;
|
|
50
|
+
const rule = (_a = options.redactionRules) === null || _a === void 0 ? void 0 : _a.find((r) => r.key === key);
|
|
51
|
+
const captureArguments = (_b = options.captureArguments) !== null && _b !== void 0 ? _b : false;
|
|
52
|
+
const captureResults = (_c = options.captureResults) !== null && _c !== void 0 ? _c : false;
|
|
53
|
+
return {
|
|
54
|
+
args: (functionName, args) => captureArguments
|
|
55
|
+
? rule
|
|
56
|
+
? rule.maskArgs(functionName, args)
|
|
57
|
+
: args
|
|
58
|
+
: undefined,
|
|
59
|
+
success: (functionName, value) => captureResults
|
|
60
|
+
? {
|
|
61
|
+
type: 'success',
|
|
62
|
+
value: rule ? rule.maskResult(functionName, value) : value,
|
|
63
|
+
}
|
|
64
|
+
: { type: 'success' },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Wraps a given service factory with instrumentation to notify a
|
|
69
|
+
* ServiceInstrumentation of lifecycle events and method calls.
|
|
70
|
+
*
|
|
71
|
+
* For each of initialize, dispose, and method calls, the instrumentation
|
|
72
|
+
* is invoked at the start of the operation and may return an EventSpan
|
|
73
|
+
* whose `end` is called with the outcome when the operation finishes.
|
|
74
|
+
* Errors are rethrown after being reported.
|
|
75
|
+
*
|
|
76
|
+
* @param options The instrumentation to notify and the capture policy.
|
|
77
|
+
* @param delegate The original service factory to be instrumented.
|
|
78
|
+
* @return A new service factory that provides the same dependencies but includes event notification logic.
|
|
79
|
+
*/
|
|
80
|
+
function makeObservable(options, delegate) {
|
|
81
|
+
const instrumentation = options.instrumentation;
|
|
82
|
+
const key = delegate.provides;
|
|
83
|
+
const capture = makeCapture(options, key);
|
|
84
|
+
return core_1.ServiceFactory.singleton({
|
|
85
|
+
scope: delegate.scope,
|
|
86
|
+
provides: delegate.provides,
|
|
87
|
+
dependsOn: delegate.dependsOn,
|
|
88
|
+
initialize: (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
89
|
+
var _a;
|
|
90
|
+
const span = (_a = instrumentation.onInitialize) === null || _a === void 0 ? void 0 : _a.call(instrumentation, { key });
|
|
91
|
+
if (!span) {
|
|
92
|
+
return delegate.initialize(...args);
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const instance = yield span.run(() => delegate.initialize(...args));
|
|
96
|
+
span.end({ type: 'success' });
|
|
97
|
+
return observeMethodCalls(instance, instrumentation, key, capture);
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
span.end({ type: 'failure', error });
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}),
|
|
104
|
+
dispose: () => {
|
|
105
|
+
var _a;
|
|
106
|
+
const dispose = delegate.dispose;
|
|
107
|
+
if (dispose) {
|
|
108
|
+
const span = (_a = instrumentation.onDispose) === null || _a === void 0 ? void 0 : _a.call(instrumentation, { key });
|
|
109
|
+
if (span) {
|
|
110
|
+
try {
|
|
111
|
+
span.run(dispose);
|
|
112
|
+
span.end({ type: 'success' });
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
span.end({ type: 'failure', error });
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Wraps an object with a Proxy to notify the instrumentation of method calls.
|
|
125
|
+
*
|
|
126
|
+
* Methods returning a promise report their outcome when the promise
|
|
127
|
+
* settles, not when the method returns.
|
|
128
|
+
*
|
|
129
|
+
* @param thing The object whose method calls need to be observed.
|
|
130
|
+
* @param instrumentation The instrumentation notified of method call events.
|
|
131
|
+
* @param key The service key used to identify the service in events.
|
|
132
|
+
* @param capture The capture policy deciding what argument and result
|
|
133
|
+
* values (if any) are delivered with each event.
|
|
134
|
+
* @return A Proxy wrapping the input object, with all method calls being reported.
|
|
135
|
+
*/
|
|
136
|
+
function observeMethodCalls(thing, instrumentation, key, capture) {
|
|
137
|
+
if (typeof thing !== 'object' || thing === null) {
|
|
138
|
+
return thing;
|
|
139
|
+
}
|
|
140
|
+
const className = classNameOf(thing);
|
|
141
|
+
return new Proxy(thing, {
|
|
142
|
+
get(target, prop) {
|
|
143
|
+
const value = Reflect.get(target, prop);
|
|
144
|
+
if (typeof value === 'function' && typeof prop === 'string') {
|
|
145
|
+
return (...args) => {
|
|
146
|
+
var _a;
|
|
147
|
+
const span = (_a = instrumentation.onMethodCall) === null || _a === void 0 ? void 0 : _a.call(instrumentation, {
|
|
148
|
+
key,
|
|
149
|
+
className,
|
|
150
|
+
functionName: prop,
|
|
151
|
+
args: capture.args(prop, args),
|
|
152
|
+
});
|
|
153
|
+
try {
|
|
154
|
+
const result = invokeWithin(span, () => value.apply(target, args));
|
|
155
|
+
if (result instanceof Promise) {
|
|
156
|
+
return result.then((resolved) => {
|
|
157
|
+
span === null || span === void 0 ? void 0 : span.end(capture.success(prop, resolved));
|
|
158
|
+
return resolved;
|
|
159
|
+
}, (error) => {
|
|
160
|
+
span === null || span === void 0 ? void 0 : span.end({ type: 'failure', error });
|
|
161
|
+
throw error;
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
span === null || span === void 0 ? void 0 : span.end(capture.success(prop, result));
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
span === null || span === void 0 ? void 0 : span.end({ type: 'failure', error });
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
return value;
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Invokes an operation through the EventSpan's `run` wrapper when the
|
|
179
|
+
* instrumentation returned a span, so it can establish ambient state
|
|
180
|
+
* (tracing context) around the operation; invokes the operation directly
|
|
181
|
+
* otherwise.
|
|
182
|
+
*
|
|
183
|
+
* @param span The EventSpan returned by the instrumentation, if any.
|
|
184
|
+
* @param fn The thunk performing the operation.
|
|
185
|
+
* @returns The value returned by `fn`.
|
|
186
|
+
*/
|
|
187
|
+
function invokeWithin(span, fn) {
|
|
188
|
+
return span ? span.run(fn) : fn();
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Resolves the class name of a service instance, or undefined for values
|
|
192
|
+
* that are not instances of a named class (plain object literals,
|
|
193
|
+
* null-prototype objects).
|
|
194
|
+
*
|
|
195
|
+
* @param thing The service instance to inspect.
|
|
196
|
+
* @returns The constructor name, or undefined when there is none to report.
|
|
197
|
+
*/
|
|
198
|
+
function classNameOf(thing) {
|
|
199
|
+
var _a;
|
|
200
|
+
const name = (_a = thing.constructor) === null || _a === void 0 ? void 0 : _a.name;
|
|
201
|
+
return name && name !== 'Object' ? name : undefined;
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=instrument.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrument.js","sourceRoot":"","sources":["../src/instrument.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { DisposeContext, EventSpan, InitializeContext, MethodCallContext, ServiceInstrumentation } from './serviceInstrumentation';
|
|
2
|
+
import type { ServiceKey } from '@composed-di/core';
|
|
3
|
+
/**
|
|
4
|
+
* The placeholder that replaces a redacted value when no custom
|
|
5
|
+
* transform is given for it.
|
|
6
|
+
*/
|
|
7
|
+
export declare const REDACTED_VALUE = "[REDACTED]";
|
|
8
|
+
/**
|
|
9
|
+
* Custom masking for one included property, narrowed to the exact
|
|
10
|
+
* method named when {@link RedactionRuleBuilder.redact} is called.
|
|
11
|
+
* Omitting a side fully blanks it with {@link REDACTED_VALUE}; providing
|
|
12
|
+
* one lets you report a partial mask instead (e.g. the last 4 digits of
|
|
13
|
+
* a card number) — both always return a `string`, the masked
|
|
14
|
+
* representation to report in place of the real value.
|
|
15
|
+
*/
|
|
16
|
+
export type Mask<T, K extends Extract<keyof T, string>> = T[K] extends (...args: infer A) => infer R ? {
|
|
17
|
+
maskArgs?: (...args: A) => string;
|
|
18
|
+
maskResult?: (result: R) => string;
|
|
19
|
+
} : never;
|
|
20
|
+
/**
|
|
21
|
+
* Marks a service — or specific properties of it — as sensitive, so the
|
|
22
|
+
* values flowing through its events are redacted. Returned by
|
|
23
|
+
* {@link RedactionRuleBuilder.build}, which is the only way to construct
|
|
24
|
+
* one — the `redactAll`/per-property merge logic lives entirely inside
|
|
25
|
+
* `maskArgs`/`maskResult`, so callers never need to know how a rule
|
|
26
|
+
* reached its decision, only what to do with it.
|
|
27
|
+
*/
|
|
28
|
+
export interface RedactionRule<T> {
|
|
29
|
+
readonly key: ServiceKey<T>;
|
|
30
|
+
/**
|
|
31
|
+
* The args to report for a call to `functionName`: unchanged if not
|
|
32
|
+
* redacted, otherwise blanked or run through a custom `maskArgs`.
|
|
33
|
+
*/
|
|
34
|
+
maskArgs(functionName: string, args: readonly unknown[]): readonly unknown[];
|
|
35
|
+
/**
|
|
36
|
+
* The value to report for a success outcome: unchanged if not
|
|
37
|
+
* redacted, otherwise blanked or run through a custom `maskResult`.
|
|
38
|
+
* Omit `functionName` to ask about the initialize result (the service
|
|
39
|
+
* instance itself), which only the rule's `redactAll` default touches.
|
|
40
|
+
*/
|
|
41
|
+
maskResult(functionName: string | undefined, result: unknown): unknown;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Fluent, single-key rule builder returned by {@link redactionRule}.
|
|
45
|
+
* `redactAll`, `redact`, and `exclude` all merge into the same rule —
|
|
46
|
+
* call them in any combination, in any order; the more specific
|
|
47
|
+
* per-property calls (`redact`/`exclude`) always win over the blanket
|
|
48
|
+
* `redactAll` default for the properties they name.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* const rules = [
|
|
53
|
+
* redactionRule(SecretClientKey)
|
|
54
|
+
* .redactAll()
|
|
55
|
+
* .build(), // whole service is sensitive
|
|
56
|
+
* redactionRule(BillingKey)
|
|
57
|
+
* .redactAll()
|
|
58
|
+
* .redact('chargeCard', { maskResult: (card) => `card ending in ${card.number.slice(-4)}` })
|
|
59
|
+
* .exclude('ping') // redact everything except this, with one custom mask
|
|
60
|
+
* .build(),
|
|
61
|
+
* redactionRule(VaultKey)
|
|
62
|
+
* .redact('getSecret')
|
|
63
|
+
* .build(), // only this call, nothing else
|
|
64
|
+
* ];
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare class RedactionRuleBuilder<T> {
|
|
68
|
+
private readonly key;
|
|
69
|
+
private redactAllFlag;
|
|
70
|
+
private hasRedact;
|
|
71
|
+
private readonly overrides;
|
|
72
|
+
constructor(key: ServiceKey<T>);
|
|
73
|
+
/** Redacts every property, plus the initialize result, by default. */
|
|
74
|
+
redactAll(): this;
|
|
75
|
+
/**
|
|
76
|
+
* Marks one property (method) as redacted, with optional custom
|
|
77
|
+
* masking. Call repeatedly for several properties. Overrides
|
|
78
|
+
* `redactAll`/`exclude` for this specific property.
|
|
79
|
+
*/
|
|
80
|
+
redact<K extends Extract<keyof T, string>>(name: K, mask?: Mask<T, K>): this;
|
|
81
|
+
/**
|
|
82
|
+
* Marks one or more properties as explicitly NOT redacted, overriding
|
|
83
|
+
* `redactAll` for just these.
|
|
84
|
+
*/
|
|
85
|
+
exclude(...names: Extract<keyof T, string>[]): this;
|
|
86
|
+
build(): RedactionRule<any>;
|
|
87
|
+
}
|
|
88
|
+
export declare function redactionRule<T>(key: ServiceKey<T>): RedactionRuleBuilder<T>;
|
|
89
|
+
/**
|
|
90
|
+
* A ServiceInstrumentation decorator that redacts sensitive values before
|
|
91
|
+
* they reach the wrapped instrumentation. Works with any implementation via
|
|
92
|
+
* delegation: arguments in MethodCallContext and success values in
|
|
93
|
+
* EventOutcome are replaced (wholesale, or via a custom transform)
|
|
94
|
+
* before the delegate ever sees them — whatever it captures or exports
|
|
95
|
+
* is already scrubbed.
|
|
96
|
+
*
|
|
97
|
+
* Failure outcomes are passed through unchanged so error reporting keeps
|
|
98
|
+
* working; keep secrets out of error messages at the throwing site.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* const listener = new RedactingEventListener(
|
|
103
|
+
* new OTELEventListener({ captureArguments: true, captureResults: true }),
|
|
104
|
+
* [
|
|
105
|
+
* redactionRule(SecretClientKey).redactAll().build(), // whole service is sensitive
|
|
106
|
+
* redactionRule(VaultKey).redact('getSecret').build(), // only this call
|
|
107
|
+
* redactionRule(HealthKey).redactAll().exclude('ping').build(), // everything but this call
|
|
108
|
+
* ],
|
|
109
|
+
* );
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
export declare class RedactingEventListener implements ServiceInstrumentation {
|
|
113
|
+
private readonly delegate;
|
|
114
|
+
private readonly rules;
|
|
115
|
+
constructor(delegate: ServiceInstrumentation, rules: readonly RedactionRule<any>[]);
|
|
116
|
+
onInitialize(context: InitializeContext): EventSpan | void;
|
|
117
|
+
onDispose(context: DisposeContext): EventSpan | void;
|
|
118
|
+
onMethodCall(context: MethodCallContext): EventSpan | void;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=redactingEventListener.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redactingEventListener.d.ts","sourceRoot":"","sources":["../src/redactingEventListener.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EAEd,SAAS,EACT,iBAAiB,EACjB,iBAAiB,EACjB,sBAAsB,EACvB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;;GAGG;AACH,eAAO,MAAM,cAAc,eAAe,CAAC;AAE3C;;;;;;;GAOG;AACH,MAAM,MAAM,IAAI,CAAC,CAAC,EAAE,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CACrE,GAAG,IAAI,EAAE,MAAM,CAAC,KACb,MAAM,CAAC,GACR;IACE,QAAQ,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;IAClC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,CAAC;CACpC,GACD,KAAK,CAAC;AAeV;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAE5B;;;OAGG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,OAAO,EAAE,CAAC;IAE7E;;;;;OAKG;IACH,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC;CACxE;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,oBAAoB,CAAC,CAAC;IAKrB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAJhC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwC;gBAErC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC;IAE/C,sEAAsE;IACtE,SAAS,IAAI,IAAI;IAMjB;;;;OAIG;IACH,MAAM,CAAC,CAAC,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI;IAM5E;;;OAGG;IACH,OAAO,CAAC,GAAG,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI;IAOnD,KAAK,IAAI,aAAa,CAAC,GAAG,CAAC;CA0C5B;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,oBAAoB,CAAC,CAAC,CAAC,CAE5E;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,sBAAuB,YAAW,sBAAsB;IAEjE,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBADL,QAAQ,EAAE,sBAAsB,EAChC,KAAK,EAAE,SAAS,aAAa,CAAC,GAAG,CAAC,EAAE;IAGvD,YAAY,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,GAAG,IAAI;IAK1D,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI;IAKpD,YAAY,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,GAAG,IAAI;CAY3D"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RedactingEventListener = exports.RedactionRuleBuilder = exports.REDACTED_VALUE = void 0;
|
|
4
|
+
exports.redactionRule = redactionRule;
|
|
5
|
+
/**
|
|
6
|
+
* The placeholder that replaces a redacted value when no custom
|
|
7
|
+
* transform is given for it.
|
|
8
|
+
*/
|
|
9
|
+
exports.REDACTED_VALUE = '[REDACTED]';
|
|
10
|
+
/**
|
|
11
|
+
* Fluent, single-key rule builder returned by {@link redactionRule}.
|
|
12
|
+
* `redactAll`, `redact`, and `exclude` all merge into the same rule —
|
|
13
|
+
* call them in any combination, in any order; the more specific
|
|
14
|
+
* per-property calls (`redact`/`exclude`) always win over the blanket
|
|
15
|
+
* `redactAll` default for the properties they name.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const rules = [
|
|
20
|
+
* redactionRule(SecretClientKey)
|
|
21
|
+
* .redactAll()
|
|
22
|
+
* .build(), // whole service is sensitive
|
|
23
|
+
* redactionRule(BillingKey)
|
|
24
|
+
* .redactAll()
|
|
25
|
+
* .redact('chargeCard', { maskResult: (card) => `card ending in ${card.number.slice(-4)}` })
|
|
26
|
+
* .exclude('ping') // redact everything except this, with one custom mask
|
|
27
|
+
* .build(),
|
|
28
|
+
* redactionRule(VaultKey)
|
|
29
|
+
* .redact('getSecret')
|
|
30
|
+
* .build(), // only this call, nothing else
|
|
31
|
+
* ];
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
class RedactionRuleBuilder {
|
|
35
|
+
constructor(key) {
|
|
36
|
+
this.key = key;
|
|
37
|
+
this.redactAllFlag = false;
|
|
38
|
+
this.hasRedact = false;
|
|
39
|
+
this.overrides = {};
|
|
40
|
+
}
|
|
41
|
+
/** Redacts every property, plus the initialize result, by default. */
|
|
42
|
+
redactAll() {
|
|
43
|
+
this.redactAllFlag = true;
|
|
44
|
+
this.hasRedact = true;
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Marks one property (method) as redacted, with optional custom
|
|
49
|
+
* masking. Call repeatedly for several properties. Overrides
|
|
50
|
+
* `redactAll`/`exclude` for this specific property.
|
|
51
|
+
*/
|
|
52
|
+
redact(name, mask) {
|
|
53
|
+
this.overrides[name] = Object.assign({ redacted: true }, mask);
|
|
54
|
+
this.hasRedact = true;
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Marks one or more properties as explicitly NOT redacted, overriding
|
|
59
|
+
* `redactAll` for just these.
|
|
60
|
+
*/
|
|
61
|
+
exclude(...names) {
|
|
62
|
+
for (const name of names) {
|
|
63
|
+
this.overrides[name] = { redacted: false };
|
|
64
|
+
}
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
build() {
|
|
68
|
+
// `exclude` alone never redacts anything — at least one call to
|
|
69
|
+
// `redactAll`/`redact` is required for this rule to have any effect.
|
|
70
|
+
if (!this.hasRedact) {
|
|
71
|
+
throw new Error(`redactionRule(${this.key.name}) has no effect: call .redactAll() and/or .redact(...) ` +
|
|
72
|
+
'before .build() — .exclude() alone never redacts anything.');
|
|
73
|
+
}
|
|
74
|
+
const redactAllFlag = this.redactAllFlag;
|
|
75
|
+
const overrides = this.overrides;
|
|
76
|
+
return {
|
|
77
|
+
key: this.key,
|
|
78
|
+
maskArgs(functionName, args) {
|
|
79
|
+
const override = overrides[functionName];
|
|
80
|
+
const redacted = override ? override.redacted : redactAllFlag;
|
|
81
|
+
if (!redacted) {
|
|
82
|
+
return args;
|
|
83
|
+
}
|
|
84
|
+
return (override === null || override === void 0 ? void 0 : override.maskArgs)
|
|
85
|
+
? // A custom mask reports one string for the whole call,
|
|
86
|
+
// rather than a value per argument.
|
|
87
|
+
[override.maskArgs(...args)]
|
|
88
|
+
: // Replace each argument rather than the whole array, so the
|
|
89
|
+
// delegate still sees the call's arity.
|
|
90
|
+
args.map(() => exports.REDACTED_VALUE);
|
|
91
|
+
},
|
|
92
|
+
maskResult(functionName, result) {
|
|
93
|
+
const override = functionName === undefined ? undefined : overrides[functionName];
|
|
94
|
+
const redacted = override ? override.redacted : redactAllFlag;
|
|
95
|
+
if (!redacted) {
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
return (override === null || override === void 0 ? void 0 : override.maskResult)
|
|
99
|
+
? override.maskResult(result)
|
|
100
|
+
: exports.REDACTED_VALUE;
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.RedactionRuleBuilder = RedactionRuleBuilder;
|
|
106
|
+
function redactionRule(key) {
|
|
107
|
+
return new RedactionRuleBuilder(key);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* A ServiceInstrumentation decorator that redacts sensitive values before
|
|
111
|
+
* they reach the wrapped instrumentation. Works with any implementation via
|
|
112
|
+
* delegation: arguments in MethodCallContext and success values in
|
|
113
|
+
* EventOutcome are replaced (wholesale, or via a custom transform)
|
|
114
|
+
* before the delegate ever sees them — whatever it captures or exports
|
|
115
|
+
* is already scrubbed.
|
|
116
|
+
*
|
|
117
|
+
* Failure outcomes are passed through unchanged so error reporting keeps
|
|
118
|
+
* working; keep secrets out of error messages at the throwing site.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* const listener = new RedactingEventListener(
|
|
123
|
+
* new OTELEventListener({ captureArguments: true, captureResults: true }),
|
|
124
|
+
* [
|
|
125
|
+
* redactionRule(SecretClientKey).redactAll().build(), // whole service is sensitive
|
|
126
|
+
* redactionRule(VaultKey).redact('getSecret').build(), // only this call
|
|
127
|
+
* redactionRule(HealthKey).redactAll().exclude('ping').build(), // everything but this call
|
|
128
|
+
* ],
|
|
129
|
+
* );
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
class RedactingEventListener {
|
|
133
|
+
constructor(delegate, rules) {
|
|
134
|
+
this.delegate = delegate;
|
|
135
|
+
this.rules = rules;
|
|
136
|
+
}
|
|
137
|
+
onInitialize(context) {
|
|
138
|
+
var _a, _b;
|
|
139
|
+
const rule = this.rules.find((r) => r.key === context.key);
|
|
140
|
+
return redactSpan((_b = (_a = this.delegate).onInitialize) === null || _b === void 0 ? void 0 : _b.call(_a, context), rule);
|
|
141
|
+
}
|
|
142
|
+
onDispose(context) {
|
|
143
|
+
var _a, _b;
|
|
144
|
+
// Dispose carries no arguments and no result value; nothing to redact.
|
|
145
|
+
return (_b = (_a = this.delegate).onDispose) === null || _b === void 0 ? void 0 : _b.call(_a, context);
|
|
146
|
+
}
|
|
147
|
+
onMethodCall(context) {
|
|
148
|
+
var _a, _b;
|
|
149
|
+
const rule = this.rules.find((r) => r.key === context.key);
|
|
150
|
+
const span = (_b = (_a = this.delegate).onMethodCall) === null || _b === void 0 ? void 0 : _b.call(_a, rule
|
|
151
|
+
? Object.assign(Object.assign({}, context), { args: rule.maskArgs(context.functionName, context.args) }) : context);
|
|
152
|
+
return redactSpan(span, rule, context.functionName);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
exports.RedactingEventListener = RedactingEventListener;
|
|
156
|
+
/**
|
|
157
|
+
* Wraps the delegate's EventSpan so success values are redacted before
|
|
158
|
+
* `end` sees them, by delegating to the rule's `maskResult`. `run` (and
|
|
159
|
+
* any future fields) pass through untouched. `functionName` is omitted
|
|
160
|
+
* for the initialize result (the instance itself).
|
|
161
|
+
*/
|
|
162
|
+
function redactSpan(span, rule, functionName) {
|
|
163
|
+
if (!rule || !span) {
|
|
164
|
+
return span;
|
|
165
|
+
}
|
|
166
|
+
const end = span.end.bind(span);
|
|
167
|
+
return Object.assign(Object.assign({}, span), { end: (outcome) => end(outcome.type === 'success'
|
|
168
|
+
? {
|
|
169
|
+
type: 'success',
|
|
170
|
+
value: rule.maskResult(functionName, outcome.value),
|
|
171
|
+
}
|
|
172
|
+
: outcome) });
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=redactingEventListener.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redactingEventListener.js","sourceRoot":"","sources":["../src/redactingEventListener.ts"],"names":[],"mappings":"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"}
|