@composed-di/core 0.5.0-alpha → 0.5.2-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 +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/redactingEventListener.d.ts +57 -0
- package/dist/redactingEventListener.d.ts.map +1 -0
- package/dist/redactingEventListener.js +89 -0
- package/dist/redactingEventListener.js.map +1 -0
- package/dist/serviceEventListener.d.ts +43 -30
- package/dist/serviceEventListener.d.ts.map +1 -1
- package/dist/serviceModule.d.ts +2 -2
- package/dist/serviceModule.d.ts.map +1 -1
- package/dist/serviceModule.js +44 -17
- package/dist/serviceModule.js.map +1 -1
- package/dist/serviceModuleListener.d.ts +133 -0
- package/dist/serviceModuleListener.d.ts.map +1 -0
- package/dist/serviceModuleListener.js +3 -0
- package/dist/serviceModuleListener.js.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +2 -1
- package/src/redactingEventListener.ts +132 -0
- package/src/serviceModule.ts +53 -21
- package/src/{serviceEventListener.ts → serviceModuleListener.ts} +40 -31
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ export * from './serviceModule';
|
|
|
3
3
|
export * from './serviceFactory';
|
|
4
4
|
export * from './serviceScope';
|
|
5
5
|
export * from './serviceSelector';
|
|
6
|
-
export * from './
|
|
6
|
+
export * from './serviceModuleListener';
|
|
7
|
+
export * from './redactingEventListener';
|
|
7
8
|
export * from './errors';
|
|
8
9
|
export * from './utils';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -19,7 +19,8 @@ __exportStar(require("./serviceModule"), exports);
|
|
|
19
19
|
__exportStar(require("./serviceFactory"), exports);
|
|
20
20
|
__exportStar(require("./serviceScope"), exports);
|
|
21
21
|
__exportStar(require("./serviceSelector"), exports);
|
|
22
|
-
__exportStar(require("./
|
|
22
|
+
__exportStar(require("./serviceModuleListener"), exports);
|
|
23
|
+
__exportStar(require("./redactingEventListener"), exports);
|
|
23
24
|
__exportStar(require("./errors"), exports);
|
|
24
25
|
__exportStar(require("./utils"), exports);
|
|
25
26
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,kDAAgC;AAChC,mDAAiC;AACjC,iDAA+B;AAC/B,oDAAkC;AAClC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,kDAAgC;AAChC,mDAAiC;AACjC,iDAA+B;AAC/B,oDAAkC;AAClC,0DAAwC;AACxC,2DAAyC;AACzC,2CAAyB;AACzB,0CAAwB"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { DisposeContext, EventSpan, InitializeContext, MethodCallContext, ServiceModuleListener } from './serviceModuleListener';
|
|
2
|
+
import type { ServiceKey } from './serviceKey';
|
|
3
|
+
/**
|
|
4
|
+
* The placeholder that replaces redacted values in event contexts and
|
|
5
|
+
* outcomes.
|
|
6
|
+
*/
|
|
7
|
+
export declare const REDACTED_VALUE = "[redacted]";
|
|
8
|
+
/**
|
|
9
|
+
* Marks a service — or specific properties of it — as sensitive, so the
|
|
10
|
+
* values flowing through its events are replaced with {@link REDACTED_VALUE}.
|
|
11
|
+
*/
|
|
12
|
+
export interface RedactionRule<T> {
|
|
13
|
+
/**
|
|
14
|
+
* The service key whose values must be redacted. Matched by key
|
|
15
|
+
* identity, like everywhere else in the container.
|
|
16
|
+
*/
|
|
17
|
+
key: ServiceKey<T>;
|
|
18
|
+
/**
|
|
19
|
+
* Property names to redact. Optional: when omitted, ALL properties of
|
|
20
|
+
* the service are redacted, along with its initialize result (the
|
|
21
|
+
* service instance itself may carry credentials or config).
|
|
22
|
+
*/
|
|
23
|
+
properties?: Extract<keyof T, string>[];
|
|
24
|
+
}
|
|
25
|
+
export declare function redactionRule<T>(key: ServiceKey<T>, properties?: Extract<keyof T, string>[]): RedactionRule<T>;
|
|
26
|
+
/**
|
|
27
|
+
* A ServiceEventListener decorator that redacts sensitive values before
|
|
28
|
+
* they reach the wrapped listener. Works with any implementation via
|
|
29
|
+
* delegation: arguments in MethodCallContext and success values in
|
|
30
|
+
* EventOutcome are replaced with {@link REDACTED_VALUE}, so the delegate
|
|
31
|
+
* never sees the sensitive data — whatever it captures or exports is
|
|
32
|
+
* already scrubbed.
|
|
33
|
+
*
|
|
34
|
+
* Failure outcomes are passed through unchanged so error reporting keeps
|
|
35
|
+
* working; keep secrets out of error messages at the throwing site.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* const listener = new RedactingEventListener(
|
|
40
|
+
* new OTELEventListener({ captureArguments: true, captureResults: true }),
|
|
41
|
+
* [
|
|
42
|
+
* { key: SecretClientKey }, // whole service is sensitive
|
|
43
|
+
* { key: VaultKey, properties: ['getSecret'] }, // only these calls
|
|
44
|
+
* ],
|
|
45
|
+
* );
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare class RedactingEventListener implements ServiceModuleListener {
|
|
49
|
+
private readonly delegate;
|
|
50
|
+
private readonly rules;
|
|
51
|
+
constructor(delegate: ServiceModuleListener, rules: readonly RedactionRule<any>[]);
|
|
52
|
+
onInitialize(context: InitializeContext): EventSpan | void;
|
|
53
|
+
onDispose(context: DisposeContext): EventSpan | void;
|
|
54
|
+
onMethodCall(context: MethodCallContext): EventSpan | void;
|
|
55
|
+
private isRedacted;
|
|
56
|
+
}
|
|
57
|
+
//# 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,qBAAqB,EACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C;;;GAGG;AACH,eAAO,MAAM,cAAc,eAAe,CAAC;AAE3C;;;GAGG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B;;;OAGG;IACH,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAEnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC;CACzC;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,GAAG,aAAa,CAAC,CAAC,CAAC,CAE9G;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,sBAAuB,YAAW,qBAAqB;IAEhE,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBADL,QAAQ,EAAE,qBAAqB,EAC/B,KAAK,EAAE,SAAS,aAAa,CAAC,GAAG,CAAC,EAAE;IAGvD,YAAY,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,GAAG,IAAI;IAU1D,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI;IAKpD,YAAY,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,GAAG,IAAI;IAc1D,OAAO,CAAC,UAAU;CAYnB"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RedactingEventListener = exports.REDACTED_VALUE = void 0;
|
|
4
|
+
exports.redactionRule = redactionRule;
|
|
5
|
+
/**
|
|
6
|
+
* The placeholder that replaces redacted values in event contexts and
|
|
7
|
+
* outcomes.
|
|
8
|
+
*/
|
|
9
|
+
exports.REDACTED_VALUE = '[redacted]';
|
|
10
|
+
function redactionRule(key, properties) {
|
|
11
|
+
return { key, properties };
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A ServiceEventListener decorator that redacts sensitive values before
|
|
15
|
+
* they reach the wrapped listener. Works with any implementation via
|
|
16
|
+
* delegation: arguments in MethodCallContext and success values in
|
|
17
|
+
* EventOutcome are replaced with {@link REDACTED_VALUE}, so the delegate
|
|
18
|
+
* never sees the sensitive data — whatever it captures or exports is
|
|
19
|
+
* already scrubbed.
|
|
20
|
+
*
|
|
21
|
+
* Failure outcomes are passed through unchanged so error reporting keeps
|
|
22
|
+
* working; keep secrets out of error messages at the throwing site.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const listener = new RedactingEventListener(
|
|
27
|
+
* new OTELEventListener({ captureArguments: true, captureResults: true }),
|
|
28
|
+
* [
|
|
29
|
+
* { key: SecretClientKey }, // whole service is sensitive
|
|
30
|
+
* { key: VaultKey, properties: ['getSecret'] }, // only these calls
|
|
31
|
+
* ],
|
|
32
|
+
* );
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
class RedactingEventListener {
|
|
36
|
+
constructor(delegate, rules) {
|
|
37
|
+
this.delegate = delegate;
|
|
38
|
+
this.rules = rules;
|
|
39
|
+
}
|
|
40
|
+
onInitialize(context) {
|
|
41
|
+
var _a, _b;
|
|
42
|
+
// The initialize result is the service instance itself, so it is
|
|
43
|
+
// redacted only when the whole service is sensitive — a rule without
|
|
44
|
+
// `properties`.
|
|
45
|
+
return redactSpan((_b = (_a = this.delegate).onInitialize) === null || _b === void 0 ? void 0 : _b.call(_a, context), this.isRedacted(context.key));
|
|
46
|
+
}
|
|
47
|
+
onDispose(context) {
|
|
48
|
+
var _a, _b;
|
|
49
|
+
// Dispose carries no arguments and no result value; nothing to redact.
|
|
50
|
+
return (_b = (_a = this.delegate).onDispose) === null || _b === void 0 ? void 0 : _b.call(_a, context);
|
|
51
|
+
}
|
|
52
|
+
onMethodCall(context) {
|
|
53
|
+
var _a, _b;
|
|
54
|
+
const redacted = this.isRedacted(context.key, context.functionName);
|
|
55
|
+
const span = (_b = (_a = this.delegate).onMethodCall) === null || _b === void 0 ? void 0 : _b.call(_a, redacted
|
|
56
|
+
? // Replace each argument rather than the whole array, so the
|
|
57
|
+
Object.assign(Object.assign({}, context), { args: context.args.map(() => exports.REDACTED_VALUE) }) : context);
|
|
58
|
+
return redactSpan(span, redacted);
|
|
59
|
+
}
|
|
60
|
+
// 1. Si expiro el dbr `EXPIRED` el cron debe mandar a `FACE_MISMATCH`?
|
|
61
|
+
// 2. Si hubo un matchLevel intermediario el cron debe mandar a `FACE_MISMATCH`? (Probablemente no)
|
|
62
|
+
isRedacted(key, propertyName) {
|
|
63
|
+
return this.rules.some((rule) => {
|
|
64
|
+
var _a;
|
|
65
|
+
return rule.key === key &&
|
|
66
|
+
// A rule without `properties` redacts everything on the key,
|
|
67
|
+
// including initialize — where no property name is passed —
|
|
68
|
+
// while a rule with `properties` matches only those calls.
|
|
69
|
+
(rule.properties === undefined ||
|
|
70
|
+
(propertyName !== undefined &&
|
|
71
|
+
((_a = rule.properties) === null || _a === void 0 ? void 0 : _a.includes(propertyName))));
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.RedactingEventListener = RedactingEventListener;
|
|
76
|
+
/**
|
|
77
|
+
* Wraps the delegate's EventSpan so success values are redacted before
|
|
78
|
+
* `end` sees them. `run` (and any future fields) pass through untouched.
|
|
79
|
+
*/
|
|
80
|
+
function redactSpan(span, redacted) {
|
|
81
|
+
if (!redacted || !(span === null || span === void 0 ? void 0 : span.end)) {
|
|
82
|
+
return span;
|
|
83
|
+
}
|
|
84
|
+
const end = span.end.bind(span);
|
|
85
|
+
return Object.assign(Object.assign({}, span), { end: (outcome) => end(outcome.type === 'success'
|
|
86
|
+
? { type: 'success', value: exports.REDACTED_VALUE }
|
|
87
|
+
: outcome) });
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=redactingEventListener.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redactingEventListener.js","sourceRoot":"","sources":["../src/redactingEventListener.ts"],"names":[],"mappings":";;;AAmCA,sCAEC;AA3BD;;;GAGG;AACU,QAAA,cAAc,GAAG,YAAY,CAAC;AAqB3C,SAAgB,aAAa,CAAI,GAAkB,EAAE,UAAuC;IAC1F,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC;AAC7B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAa,sBAAsB;IACjC,YACmB,QAA+B,EAC/B,KAAoC;QADpC,aAAQ,GAAR,QAAQ,CAAuB;QAC/B,UAAK,GAAL,KAAK,CAA+B;IACpD,CAAC;IAEJ,YAAY,CAAC,OAA0B;;QACrC,iEAAiE;QACjE,qEAAqE;QACrE,gBAAgB;QAChB,OAAO,UAAU,CACf,MAAA,MAAA,IAAI,CAAC,QAAQ,EAAC,YAAY,mDAAG,OAAO,CAAC,EACrC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAC7B,CAAC;IACJ,CAAC;IAED,SAAS,CAAC,OAAuB;;QAC/B,uEAAuE;QACvE,OAAO,MAAA,MAAA,IAAI,CAAC,QAAQ,EAAC,SAAS,mDAAG,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,YAAY,CAAC,OAA0B;;QACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;QACpE,MAAM,IAAI,GAAG,MAAA,MAAA,IAAI,CAAC,QAAQ,EAAC,YAAY,mDACrC,QAAQ;YACN,CAAC,CAAC,4DAA4D;6CAEvD,OAAO,KAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,sBAAc,CAAC,IAC5D,CAAC,CAAC,OAAO,CACZ,CAAC;QACF,OAAO,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACpC,CAAC;IAED,uEAAuE;IACvE,mGAAmG;IAC3F,UAAU,CAAC,GAAoB,EAAE,YAAqB;QAC5D,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CACpB,CAAC,IAAI,EAAE,EAAE;;YACP,OAAA,IAAI,CAAC,GAAG,KAAK,GAAG;gBAChB,6DAA6D;gBAC7D,4DAA4D;gBAC5D,2DAA2D;gBAC3D,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS;oBAC5B,CAAC,YAAY,KAAK,SAAS;yBACzB,MAAA,IAAI,CAAC,UAAU,0CAAE,QAAQ,CAAC,YAAY,CAAC,CAAA,CAAC,CAAC,CAAA;SAAA,CAChD,CAAC;IACJ,CAAC;CACF;AA/CD,wDA+CC;AAED;;;GAGG;AACH,SAAS,UAAU,CACjB,IAAsB,EACtB,QAAiB;IAEjB,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,GAAG,CAAA,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChC,uCACK,IAAI,KACP,GAAG,EAAE,CAAC,OAAqB,EAAE,EAAE,CAC7B,GAAG,CACD,OAAO,CAAC,IAAI,KAAK,SAAS;YACxB,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,sBAAc,EAAE;YAC5C,CAAC,CAAC,OAAO,CACZ,IACH;AACJ,CAAC"}
|
|
@@ -4,48 +4,54 @@ import type { ServiceKey } from './serviceKey';
|
|
|
4
4
|
* disposal, or method call), returned by a ServiceEventListener when the
|
|
5
5
|
* operation starts.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* `end` is invoked exactly once when the operation finishes, so
|
|
8
|
+
* implementations can close over per-call state (a start time, a span
|
|
9
|
+
* handle, a correlation id) without any bookkeeping to pair concurrent
|
|
10
|
+
* start/finish events.
|
|
11
11
|
*/
|
|
12
12
|
export interface EventSpan {
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* Optional wrapper around the operation itself. When present, the module
|
|
15
|
+
* invokes the operation as `run(() => operation())`, so the listener can
|
|
16
|
+
* establish ambient state that the operation body and its async
|
|
17
|
+
* continuations inherit — this is what lets spans of nested service
|
|
18
|
+
* calls form a parent-child hierarchy.
|
|
17
19
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
+
* Implementations must invoke `fn` exactly once, synchronously, and
|
|
21
|
+
* return its result unchanged (for async operations, the promise itself).
|
|
22
|
+
* `end` is still delivered separately when the operation finishes.
|
|
20
23
|
*
|
|
21
|
-
* @param
|
|
22
|
-
*
|
|
23
|
-
* instance), absent for dispose spans.
|
|
24
|
-
* @return void
|
|
24
|
+
* @param fn - A thunk that performs the operation.
|
|
25
|
+
* @return The value returned by `fn`.
|
|
25
26
|
*/
|
|
26
|
-
|
|
27
|
+
run?<T>(fn: () => T): T;
|
|
27
28
|
/**
|
|
28
|
-
* Invoked when the operation
|
|
29
|
-
*
|
|
30
|
-
*
|
|
29
|
+
* Invoked exactly once when the operation finishes, whether it succeeded
|
|
30
|
+
* or failed. For methods that return a promise, this fires when the
|
|
31
|
+
* promise settles, not when the method returns. On failure, the error is
|
|
32
|
+
* rethrown to the caller after this is invoked.
|
|
31
33
|
*
|
|
32
|
-
*
|
|
34
|
+
* Whether to retain or log the outcome is the implementation's choice;
|
|
35
|
+
* values are passed by reference, so implementations must not mutate them.
|
|
36
|
+
*
|
|
37
|
+
* @param outcome - How the operation finished, and its value or error.
|
|
33
38
|
* @return void
|
|
34
39
|
*/
|
|
35
|
-
|
|
40
|
+
end?(outcome: EventOutcome): void;
|
|
36
41
|
}
|
|
37
42
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
43
|
+
* How an operation finished, delivered to EventSpan.end: `success` carries
|
|
44
|
+
* the value produced by the operation (the return or resolved value for
|
|
45
|
+
* method calls, the service instance for initialize, undefined for
|
|
46
|
+
* dispose); `failure` carries the error that was thrown or rejected.
|
|
41
47
|
*/
|
|
42
|
-
export
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
48
|
+
export type EventOutcome = {
|
|
49
|
+
type: 'success';
|
|
50
|
+
value: unknown;
|
|
51
|
+
} | {
|
|
52
|
+
type: 'failure';
|
|
53
|
+
error: unknown;
|
|
54
|
+
};
|
|
49
55
|
/**
|
|
50
56
|
* Context of a service initialization, delivered to onInitialize.
|
|
51
57
|
* Future fields are added here rather than as extra parameters.
|
|
@@ -75,10 +81,17 @@ export interface MethodCallContext {
|
|
|
75
81
|
* The unique identifier of the service the method belongs to.
|
|
76
82
|
*/
|
|
77
83
|
key: ServiceKey<unknown>;
|
|
84
|
+
/**
|
|
85
|
+
* The name of the class implementing the service (the instance's
|
|
86
|
+
* constructor name), which may differ from `key.name`. Undefined for
|
|
87
|
+
* services that are not instances of a named class, such as plain
|
|
88
|
+
* object literals.
|
|
89
|
+
*/
|
|
90
|
+
className?: string;
|
|
78
91
|
/**
|
|
79
92
|
* The name of the method that is being called.
|
|
80
93
|
*/
|
|
81
|
-
|
|
94
|
+
functionName: string;
|
|
82
95
|
/**
|
|
83
96
|
* The arguments the method was invoked with, passed by reference;
|
|
84
97
|
* implementations must not mutate them.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serviceEventListener.d.ts","sourceRoot":"","sources":["../src/serviceEventListener.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C;;;;;;;;;GASG;AACH,MAAM,WAAW,SAAS;IACxB
|
|
1
|
+
{"version":3,"file":"serviceEventListener.d.ts","sourceRoot":"","sources":["../src/serviceEventListener.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C;;;;;;;;;GASG;AACH,MAAM,WAAW,SAAS;IACxB;;;;;;;;;;;;;OAaG;IACH,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAExB;;;;;;;;;;;OAWG;IACH,GAAG,CAAC,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;CACnC;AAED;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GACtB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAE5E;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,GAAG,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,GAAG,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,GAAG,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAEzB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,IAAI,EAAE,SAAS,OAAO,EAAE,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,YAAY,CAAC,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,GAAG,IAAI,CAAC;IAE5D;;;;;OAKG;IACH,SAAS,CAAC,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI,CAAC;IAEtD;;;;;;OAMG;IACH,YAAY,CAAC,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,GAAG,IAAI,CAAC;CAC7D"}
|
package/dist/serviceModule.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ServiceKey } from './serviceKey';
|
|
2
2
|
import { ServiceFactory } from './serviceFactory';
|
|
3
3
|
import { ServiceScope } from './serviceScope';
|
|
4
|
-
import type {
|
|
4
|
+
import type { ServiceModuleListener } from './serviceModuleListener';
|
|
5
5
|
type GenericFactory = ServiceFactory<unknown, readonly ServiceKey<any>[]>;
|
|
6
6
|
/**
|
|
7
7
|
* ServiceModule is a container for service factories and manages dependency resolution.
|
|
@@ -65,7 +65,7 @@ export declare class ServiceModule {
|
|
|
65
65
|
* @return A new ServiceModule containing the deduplicated factories.
|
|
66
66
|
* @throws {ServiceModuleInitError} If circular or missing dependencies are detected during module creation.
|
|
67
67
|
*/
|
|
68
|
-
static from(entries: (ServiceModule | GenericFactory)[], listener?:
|
|
68
|
+
static from(entries: (ServiceModule | GenericFactory)[], listener?: ServiceModuleListener): ServiceModule;
|
|
69
69
|
}
|
|
70
70
|
export {};
|
|
71
71
|
//# sourceMappingURL=serviceModule.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serviceModule.d.ts","sourceRoot":"","sources":["../src/serviceModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"serviceModule.d.ts","sourceRoot":"","sources":["../src/serviceModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,OAAO,KAAK,EAEV,qBAAqB,EACtB,MAAM,yBAAyB,CAAC;AAEjC,KAAK,cAAc,GAAG,cAAc,CAAC,OAAO,EAAE,SAAS,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAG1E;;;;;;;GAOG;AACH,qBAAa,aAAa;IAMJ,QAAQ,CAAC,SAAS,EAAE,cAAc,EAAE;IALxD;;;;OAIG;IACH,OAAO;IAOP;;;;;;OAMG;IACU,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IA2BnD;;;;;OAKG;IACU,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAWhE;;;;;;;;;OASG;IACI,OAAO,CAAC,KAAK,CAAC,EAAE,YAAY;IAQnC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,MAAM,CAAC,IAAI,CACT,OAAO,EAAE,CAAC,aAAa,GAAG,cAAc,CAAC,EAAE,EAC3C,QAAQ,CAAC,EAAE,qBAAqB,GAC/B,aAAa;CAsBjB"}
|
package/dist/serviceModule.js
CHANGED
|
@@ -230,9 +230,9 @@ function isSuitable(key, factory) {
|
|
|
230
230
|
* lifecycle events and method calls.
|
|
231
231
|
*
|
|
232
232
|
* For each of initialize, dispose, and method calls, the listener is invoked
|
|
233
|
-
* at the start of the operation and may return an EventSpan whose `end`
|
|
234
|
-
*
|
|
235
|
-
* being reported.
|
|
233
|
+
* at the start of the operation and may return an EventSpan whose `end` is
|
|
234
|
+
* called with the outcome when the operation finishes. Errors are rethrown
|
|
235
|
+
* after being reported.
|
|
236
236
|
*
|
|
237
237
|
* @param listener The listener notified of lifecycle and method call events.
|
|
238
238
|
* @param delegate The original service factory to be instrumented.
|
|
@@ -250,25 +250,25 @@ function makeObservable(listener, delegate) {
|
|
|
250
250
|
if (dispose) {
|
|
251
251
|
const span = (_a = listener.onDispose) === null || _a === void 0 ? void 0 : _a.call(listener, { key });
|
|
252
252
|
try {
|
|
253
|
-
dispose
|
|
253
|
+
invokeWithin(span, dispose);
|
|
254
254
|
}
|
|
255
255
|
catch (error) {
|
|
256
|
-
(_b = span === null || span === void 0 ? void 0 : span.
|
|
256
|
+
(_b = span === null || span === void 0 ? void 0 : span.end) === null || _b === void 0 ? void 0 : _b.call(span, { type: 'failure', error });
|
|
257
257
|
throw error;
|
|
258
258
|
}
|
|
259
|
-
(_c = span === null || span === void 0 ? void 0 : span.end) === null || _c === void 0 ? void 0 : _c.call(span);
|
|
259
|
+
(_c = span === null || span === void 0 ? void 0 : span.end) === null || _c === void 0 ? void 0 : _c.call(span, { type: 'success', value: undefined });
|
|
260
260
|
}
|
|
261
261
|
},
|
|
262
262
|
initialize: (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
263
263
|
var _a, _b, _c;
|
|
264
264
|
const span = (_a = listener.onInitialize) === null || _a === void 0 ? void 0 : _a.call(listener, { key });
|
|
265
265
|
try {
|
|
266
|
-
const instance = observeMethodCalls(yield delegate.initialize(...args), listener, key);
|
|
267
|
-
(_b = span === null || span === void 0 ? void 0 : span.end) === null || _b === void 0 ? void 0 : _b.call(span, {
|
|
266
|
+
const instance = observeMethodCalls(yield invokeWithin(span, () => delegate.initialize(...args)), listener, key);
|
|
267
|
+
(_b = span === null || span === void 0 ? void 0 : span.end) === null || _b === void 0 ? void 0 : _b.call(span, { type: 'success', value: instance });
|
|
268
268
|
return instance;
|
|
269
269
|
}
|
|
270
270
|
catch (error) {
|
|
271
|
-
(_c = span === null || span === void 0 ? void 0 : span.
|
|
271
|
+
(_c = span === null || span === void 0 ? void 0 : span.end) === null || _c === void 0 ? void 0 : _c.call(span, { type: 'failure', error });
|
|
272
272
|
throw error;
|
|
273
273
|
}
|
|
274
274
|
}),
|
|
@@ -277,8 +277,8 @@ function makeObservable(listener, delegate) {
|
|
|
277
277
|
/**
|
|
278
278
|
* Wraps an object with a Proxy to notify the listener of method calls.
|
|
279
279
|
*
|
|
280
|
-
* Methods returning a promise report
|
|
281
|
-
* not when the method returns.
|
|
280
|
+
* Methods returning a promise report their outcome when the promise
|
|
281
|
+
* settles, not when the method returns.
|
|
282
282
|
*
|
|
283
283
|
* @param thing The object whose method calls need to be observed.
|
|
284
284
|
* @param listener The listener notified of method call events.
|
|
@@ -289,6 +289,7 @@ function observeMethodCalls(thing, listener, key) {
|
|
|
289
289
|
if (typeof thing !== 'object' || thing === null) {
|
|
290
290
|
return thing;
|
|
291
291
|
}
|
|
292
|
+
const className = classNameOf(thing);
|
|
292
293
|
return new Proxy(thing, {
|
|
293
294
|
get(target, prop) {
|
|
294
295
|
const value = Reflect.get(target, prop);
|
|
@@ -297,27 +298,28 @@ function observeMethodCalls(thing, listener, key) {
|
|
|
297
298
|
var _a, _b, _c;
|
|
298
299
|
const span = (_a = listener.onMethodCall) === null || _a === void 0 ? void 0 : _a.call(listener, {
|
|
299
300
|
key,
|
|
300
|
-
|
|
301
|
+
className,
|
|
302
|
+
functionName: prop,
|
|
301
303
|
args,
|
|
302
304
|
});
|
|
303
305
|
try {
|
|
304
|
-
const result = value.apply(target, args);
|
|
306
|
+
const result = invokeWithin(span, () => value.apply(target, args));
|
|
305
307
|
if (result instanceof Promise) {
|
|
306
308
|
return result.then((resolved) => {
|
|
307
309
|
var _a;
|
|
308
|
-
(_a = span === null || span === void 0 ? void 0 : span.end) === null || _a === void 0 ? void 0 : _a.call(span, {
|
|
310
|
+
(_a = span === null || span === void 0 ? void 0 : span.end) === null || _a === void 0 ? void 0 : _a.call(span, { type: 'success', value: resolved });
|
|
309
311
|
return resolved;
|
|
310
312
|
}, (error) => {
|
|
311
313
|
var _a;
|
|
312
|
-
(_a = span === null || span === void 0 ? void 0 : span.
|
|
314
|
+
(_a = span === null || span === void 0 ? void 0 : span.end) === null || _a === void 0 ? void 0 : _a.call(span, { type: 'failure', error });
|
|
313
315
|
throw error;
|
|
314
316
|
});
|
|
315
317
|
}
|
|
316
|
-
(_b = span === null || span === void 0 ? void 0 : span.end) === null || _b === void 0 ? void 0 : _b.call(span, { result });
|
|
318
|
+
(_b = span === null || span === void 0 ? void 0 : span.end) === null || _b === void 0 ? void 0 : _b.call(span, { type: 'success', value: result });
|
|
317
319
|
return result;
|
|
318
320
|
}
|
|
319
321
|
catch (error) {
|
|
320
|
-
(_c = span === null || span === void 0 ? void 0 : span.
|
|
322
|
+
(_c = span === null || span === void 0 ? void 0 : span.end) === null || _c === void 0 ? void 0 : _c.call(span, { type: 'failure', error });
|
|
321
323
|
throw error;
|
|
322
324
|
}
|
|
323
325
|
};
|
|
@@ -326,4 +328,29 @@ function observeMethodCalls(thing, listener, key) {
|
|
|
326
328
|
},
|
|
327
329
|
});
|
|
328
330
|
}
|
|
331
|
+
/**
|
|
332
|
+
* Invokes an operation through the EventSpan's `run` wrapper when the
|
|
333
|
+
* listener provided one, so it can establish ambient state (tracing
|
|
334
|
+
* context) around the operation; invokes the operation directly otherwise.
|
|
335
|
+
*
|
|
336
|
+
* @param span The EventSpan returned by the listener, if any.
|
|
337
|
+
* @param fn The thunk performing the operation.
|
|
338
|
+
* @returns The value returned by `fn`.
|
|
339
|
+
*/
|
|
340
|
+
function invokeWithin(span, fn) {
|
|
341
|
+
return (span === null || span === void 0 ? void 0 : span.run) ? span.run(fn) : fn();
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Resolves the class name of a service instance, or undefined for values
|
|
345
|
+
* that are not instances of a named class (plain object literals,
|
|
346
|
+
* null-prototype objects).
|
|
347
|
+
*
|
|
348
|
+
* @param thing The service instance to inspect.
|
|
349
|
+
* @returns The constructor name, or undefined when there is none to report.
|
|
350
|
+
*/
|
|
351
|
+
function classNameOf(thing) {
|
|
352
|
+
var _a;
|
|
353
|
+
const name = (_a = thing.constructor) === null || _a === void 0 ? void 0 : _a.name;
|
|
354
|
+
return name && name !== 'Object' ? name : undefined;
|
|
355
|
+
}
|
|
329
356
|
//# sourceMappingURL=serviceModule.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serviceModule.js","sourceRoot":"","sources":["../src/serviceModule.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6CAA8D;AAC9D,qDAAkD;AAElD,uDAAoD;AACpD,qCAA+E;
|
|
1
|
+
{"version":3,"file":"serviceModule.js","sourceRoot":"","sources":["../src/serviceModule.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { ServiceKey } from './serviceKey';
|
|
2
|
+
/**
|
|
3
|
+
* A handle representing a single in-flight operation (initialization,
|
|
4
|
+
* disposal, or method call), returned by a ServiceEventListener when the
|
|
5
|
+
* operation starts.
|
|
6
|
+
*
|
|
7
|
+
* `end` is invoked exactly once when the operation finishes, so
|
|
8
|
+
* implementations can close over per-call state (a start time, a span
|
|
9
|
+
* handle, a correlation id) without any bookkeeping to pair concurrent
|
|
10
|
+
* start/finish events.
|
|
11
|
+
*/
|
|
12
|
+
export interface EventSpan {
|
|
13
|
+
/**
|
|
14
|
+
* Optional wrapper around the operation itself. When present, the module
|
|
15
|
+
* invokes the operation as `run(() => operation())`, so the listener can
|
|
16
|
+
* establish ambient state that the operation body and its async
|
|
17
|
+
* continuations inherit — this is what lets spans of nested service
|
|
18
|
+
* calls form a parent-child hierarchy.
|
|
19
|
+
*
|
|
20
|
+
* Implementations must invoke `fn` exactly once, synchronously, and
|
|
21
|
+
* return its result unchanged (for async operations, the promise itself).
|
|
22
|
+
* `end` is still delivered separately when the operation finishes.
|
|
23
|
+
*
|
|
24
|
+
* @param fn - A thunk that performs the operation.
|
|
25
|
+
* @return The value returned by `fn`.
|
|
26
|
+
*/
|
|
27
|
+
run?<T>(fn: () => T): T;
|
|
28
|
+
/**
|
|
29
|
+
* Invoked exactly once when the operation finishes, whether it succeeded
|
|
30
|
+
* or failed. For methods that return a promise, this fires when the
|
|
31
|
+
* promise settles, not when the method returns. On failure, the error is
|
|
32
|
+
* rethrown to the caller after this is invoked.
|
|
33
|
+
*
|
|
34
|
+
* Whether to retain or log the outcome is the implementation's choice;
|
|
35
|
+
* values are passed by reference, so implementations must not mutate them.
|
|
36
|
+
*
|
|
37
|
+
* @param outcome - How the operation finished, and its value or error.
|
|
38
|
+
* @return void
|
|
39
|
+
*/
|
|
40
|
+
end?(outcome: EventOutcome): void;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* How an operation finished, delivered to EventSpan.end: `success` carries
|
|
44
|
+
* the value produced by the operation (the return or resolved value for
|
|
45
|
+
* method calls, the service instance for initialize, undefined for
|
|
46
|
+
* dispose); `failure` carries the error that was thrown or rejected.
|
|
47
|
+
*/
|
|
48
|
+
export type EventOutcome = {
|
|
49
|
+
type: 'success';
|
|
50
|
+
value: unknown;
|
|
51
|
+
} | {
|
|
52
|
+
type: 'failure';
|
|
53
|
+
error: unknown;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Context of a service initialization, delivered to onInitialize.
|
|
57
|
+
* Future fields are added here rather than as extra parameters.
|
|
58
|
+
*/
|
|
59
|
+
export interface InitializeContext {
|
|
60
|
+
/**
|
|
61
|
+
* The unique identifier of the service that is being initialized.
|
|
62
|
+
*/
|
|
63
|
+
key: ServiceKey<unknown>;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Context of a service disposal, delivered to onDispose.
|
|
67
|
+
* Future fields are added here rather than as extra parameters.
|
|
68
|
+
*/
|
|
69
|
+
export interface DisposeContext {
|
|
70
|
+
/**
|
|
71
|
+
* The unique identifier of the service that is being disposed.
|
|
72
|
+
*/
|
|
73
|
+
key: ServiceKey<unknown>;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Context of a method invocation, delivered to onMethodCall.
|
|
77
|
+
* Future fields are added here rather than as extra parameters.
|
|
78
|
+
*/
|
|
79
|
+
export interface MethodCallContext {
|
|
80
|
+
/**
|
|
81
|
+
* The unique identifier of the service the method belongs to.
|
|
82
|
+
*/
|
|
83
|
+
key: ServiceKey<unknown>;
|
|
84
|
+
/**
|
|
85
|
+
* The name of the class implementing the service (the instance's
|
|
86
|
+
* constructor name), which may differ from `key.name`. Undefined for
|
|
87
|
+
* services that are not instances of a named class, such as plain
|
|
88
|
+
* object literals.
|
|
89
|
+
*/
|
|
90
|
+
className?: string;
|
|
91
|
+
/**
|
|
92
|
+
* The name of the method that is being called.
|
|
93
|
+
*/
|
|
94
|
+
functionName: string;
|
|
95
|
+
/**
|
|
96
|
+
* The arguments the method was invoked with, passed by reference;
|
|
97
|
+
* implementations must not mutate them.
|
|
98
|
+
*/
|
|
99
|
+
args: readonly unknown[];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Interface for listening to service events. Implement this interface to
|
|
103
|
+
* observe lifecycle events and method calls of services in a module.
|
|
104
|
+
*
|
|
105
|
+
* Each method is invoked when the corresponding operation starts and may
|
|
106
|
+
* return an EventSpan that is notified when that operation finishes.
|
|
107
|
+
* Returning nothing opts out of completion tracking for that call.
|
|
108
|
+
*/
|
|
109
|
+
export interface ServiceModuleListener {
|
|
110
|
+
/**
|
|
111
|
+
* Invoked at the start of the initialization process for a specific service.
|
|
112
|
+
*
|
|
113
|
+
* @param context - Context of the initialization, including the service key.
|
|
114
|
+
* @return An EventSpan notified when initialization finishes, or void.
|
|
115
|
+
*/
|
|
116
|
+
onInitialize?(context: InitializeContext): EventSpan | void;
|
|
117
|
+
/**
|
|
118
|
+
* Invoked when the disposal process for a service starts.
|
|
119
|
+
*
|
|
120
|
+
* @param context - Context of the disposal, including the service key.
|
|
121
|
+
* @return An EventSpan notified when disposal finishes, or void.
|
|
122
|
+
*/
|
|
123
|
+
onDispose?(context: DisposeContext): EventSpan | void;
|
|
124
|
+
/**
|
|
125
|
+
* Invoked when a method call starts on a service instance.
|
|
126
|
+
*
|
|
127
|
+
* @param context - Context of the invocation, including the service key,
|
|
128
|
+
* the method name, and its arguments.
|
|
129
|
+
* @return An EventSpan notified when the call finishes, or void.
|
|
130
|
+
*/
|
|
131
|
+
onMethodCall?(context: MethodCallContext): EventSpan | void;
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=serviceModuleListener.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serviceModuleListener.d.ts","sourceRoot":"","sources":["../src/serviceModuleListener.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C;;;;;;;;;GASG;AACH,MAAM,WAAW,SAAS;IACxB;;;;;;;;;;;;;OAaG;IACH,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAExB;;;;;;;;;;;OAWG;IACH,GAAG,CAAC,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;CACnC;AAED;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GACtB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAE5E;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,GAAG,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,GAAG,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,GAAG,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAEzB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,IAAI,EAAE,SAAS,OAAO,EAAE,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;;OAKG;IACH,YAAY,CAAC,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,GAAG,IAAI,CAAC;IAE5D;;;;;OAKG;IACH,SAAS,CAAC,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI,CAAC;IAEtD;;;;;;OAMG;IACH,YAAY,CAAC,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,GAAG,IAAI,CAAC;CAC7D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serviceModuleListener.js","sourceRoot":"","sources":["../src/serviceModuleListener.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from './serviceModule';
|
|
|
3
3
|
export * from './serviceFactory';
|
|
4
4
|
export * from './serviceScope';
|
|
5
5
|
export * from './serviceSelector';
|
|
6
|
-
export * from './
|
|
6
|
+
export * from './serviceModuleListener';
|
|
7
|
+
export * from './redactingEventListener';
|
|
7
8
|
export * from './errors';
|
|
8
9
|
export * from './utils';
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DisposeContext,
|
|
3
|
+
EventOutcome,
|
|
4
|
+
EventSpan,
|
|
5
|
+
InitializeContext,
|
|
6
|
+
MethodCallContext,
|
|
7
|
+
ServiceModuleListener,
|
|
8
|
+
} from './serviceModuleListener';
|
|
9
|
+
import type { ServiceKey } from './serviceKey';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The placeholder that replaces redacted values in event contexts and
|
|
13
|
+
* outcomes.
|
|
14
|
+
*/
|
|
15
|
+
export const REDACTED_VALUE = '[redacted]';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Marks a service — or specific properties of it — as sensitive, so the
|
|
19
|
+
* values flowing through its events are replaced with {@link REDACTED_VALUE}.
|
|
20
|
+
*/
|
|
21
|
+
export interface RedactionRule<T> {
|
|
22
|
+
/**
|
|
23
|
+
* The service key whose values must be redacted. Matched by key
|
|
24
|
+
* identity, like everywhere else in the container.
|
|
25
|
+
*/
|
|
26
|
+
key: ServiceKey<T>;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Property names to redact. Optional: when omitted, ALL properties of
|
|
30
|
+
* the service are redacted, along with its initialize result (the
|
|
31
|
+
* service instance itself may carry credentials or config).
|
|
32
|
+
*/
|
|
33
|
+
properties?: Extract<keyof T, string>[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function redactionRule<T>(key: ServiceKey<T>, properties?: Extract<keyof T, string>[]): RedactionRule<T> {
|
|
37
|
+
return { key, properties };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A ServiceEventListener decorator that redacts sensitive values before
|
|
42
|
+
* they reach the wrapped listener. Works with any implementation via
|
|
43
|
+
* delegation: arguments in MethodCallContext and success values in
|
|
44
|
+
* EventOutcome are replaced with {@link REDACTED_VALUE}, so the delegate
|
|
45
|
+
* never sees the sensitive data — whatever it captures or exports is
|
|
46
|
+
* already scrubbed.
|
|
47
|
+
*
|
|
48
|
+
* Failure outcomes are passed through unchanged so error reporting keeps
|
|
49
|
+
* working; keep secrets out of error messages at the throwing site.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* const listener = new RedactingEventListener(
|
|
54
|
+
* new OTELEventListener({ captureArguments: true, captureResults: true }),
|
|
55
|
+
* [
|
|
56
|
+
* { key: SecretClientKey }, // whole service is sensitive
|
|
57
|
+
* { key: VaultKey, properties: ['getSecret'] }, // only these calls
|
|
58
|
+
* ],
|
|
59
|
+
* );
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export class RedactingEventListener implements ServiceModuleListener {
|
|
63
|
+
constructor(
|
|
64
|
+
private readonly delegate: ServiceModuleListener,
|
|
65
|
+
private readonly rules: readonly RedactionRule<any>[],
|
|
66
|
+
) {}
|
|
67
|
+
|
|
68
|
+
onInitialize(context: InitializeContext): EventSpan | void {
|
|
69
|
+
// The initialize result is the service instance itself, so it is
|
|
70
|
+
// redacted only when the whole service is sensitive — a rule without
|
|
71
|
+
// `properties`.
|
|
72
|
+
return redactSpan(
|
|
73
|
+
this.delegate.onInitialize?.(context),
|
|
74
|
+
this.isRedacted(context.key),
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
onDispose(context: DisposeContext): EventSpan | void {
|
|
79
|
+
// Dispose carries no arguments and no result value; nothing to redact.
|
|
80
|
+
return this.delegate.onDispose?.(context);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
onMethodCall(context: MethodCallContext): EventSpan | void {
|
|
84
|
+
const redacted = this.isRedacted(context.key, context.functionName);
|
|
85
|
+
const span = this.delegate.onMethodCall?.(
|
|
86
|
+
redacted
|
|
87
|
+
? // Replace each argument rather than the whole array, so the
|
|
88
|
+
// delegate still sees the call's arity.
|
|
89
|
+
{ ...context, args: context.args.map(() => REDACTED_VALUE) }
|
|
90
|
+
: context,
|
|
91
|
+
);
|
|
92
|
+
return redactSpan(span, redacted);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 1. Si expiro el dbr `EXPIRED` el cron debe mandar a `FACE_MISMATCH`?
|
|
96
|
+
// 2. Si hubo un matchLevel intermediario el cron debe mandar a `FACE_MISMATCH`? (Probablemente no)
|
|
97
|
+
private isRedacted(key: ServiceKey<any>, propertyName?: string): boolean {
|
|
98
|
+
return this.rules.some(
|
|
99
|
+
(rule) =>
|
|
100
|
+
rule.key === key &&
|
|
101
|
+
// A rule without `properties` redacts everything on the key,
|
|
102
|
+
// including initialize — where no property name is passed —
|
|
103
|
+
// while a rule with `properties` matches only those calls.
|
|
104
|
+
(rule.properties === undefined ||
|
|
105
|
+
(propertyName !== undefined &&
|
|
106
|
+
rule.properties?.includes(propertyName))),
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Wraps the delegate's EventSpan so success values are redacted before
|
|
113
|
+
* `end` sees them. `run` (and any future fields) pass through untouched.
|
|
114
|
+
*/
|
|
115
|
+
function redactSpan(
|
|
116
|
+
span: EventSpan | void,
|
|
117
|
+
redacted: boolean,
|
|
118
|
+
): EventSpan | void {
|
|
119
|
+
if (!redacted || !span?.end) {
|
|
120
|
+
return span;
|
|
121
|
+
}
|
|
122
|
+
const end = span.end.bind(span);
|
|
123
|
+
return {
|
|
124
|
+
...span,
|
|
125
|
+
end: (outcome: EventOutcome) =>
|
|
126
|
+
end(
|
|
127
|
+
outcome.type === 'success'
|
|
128
|
+
? { type: 'success', value: REDACTED_VALUE }
|
|
129
|
+
: outcome,
|
|
130
|
+
),
|
|
131
|
+
};
|
|
132
|
+
}
|
package/src/serviceModule.ts
CHANGED
|
@@ -3,7 +3,10 @@ import { ServiceFactory } from './serviceFactory';
|
|
|
3
3
|
import { ServiceScope } from './serviceScope';
|
|
4
4
|
import { ServiceSelector } from './serviceSelector';
|
|
5
5
|
import { ServiceFactoryNotFoundError, ServiceModuleInitError } from './errors';
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
EventSpan,
|
|
8
|
+
ServiceModuleListener,
|
|
9
|
+
} from './serviceModuleListener';
|
|
7
10
|
|
|
8
11
|
type GenericFactory = ServiceFactory<unknown, readonly ServiceKey<any>[]>;
|
|
9
12
|
type GenericKey = ServiceKey<any>;
|
|
@@ -120,7 +123,7 @@ export class ServiceModule {
|
|
|
120
123
|
*/
|
|
121
124
|
static from(
|
|
122
125
|
entries: (ServiceModule | GenericFactory)[],
|
|
123
|
-
listener?:
|
|
126
|
+
listener?: ServiceModuleListener,
|
|
124
127
|
): ServiceModule {
|
|
125
128
|
// Flatten entries and keep only the last factory for each ServiceKey
|
|
126
129
|
const flattened = entries.flatMap((e) =>
|
|
@@ -265,16 +268,16 @@ function isSuitable<T, D extends readonly ServiceKey<any>[]>(
|
|
|
265
268
|
* lifecycle events and method calls.
|
|
266
269
|
*
|
|
267
270
|
* For each of initialize, dispose, and method calls, the listener is invoked
|
|
268
|
-
* at the start of the operation and may return an EventSpan whose `end`
|
|
269
|
-
*
|
|
270
|
-
* being reported.
|
|
271
|
+
* at the start of the operation and may return an EventSpan whose `end` is
|
|
272
|
+
* called with the outcome when the operation finishes. Errors are rethrown
|
|
273
|
+
* after being reported.
|
|
271
274
|
*
|
|
272
275
|
* @param listener The listener notified of lifecycle and method call events.
|
|
273
276
|
* @param delegate The original service factory to be instrumented.
|
|
274
277
|
* @return A new service factory that provides the same dependencies but includes event notification logic.
|
|
275
278
|
*/
|
|
276
279
|
function makeObservable<T, D extends readonly ServiceKey<any>[]>(
|
|
277
|
-
listener:
|
|
280
|
+
listener: ServiceModuleListener,
|
|
278
281
|
delegate: ServiceFactory<any, D>,
|
|
279
282
|
): ServiceFactory<T, D> {
|
|
280
283
|
const key = delegate.provides;
|
|
@@ -288,26 +291,26 @@ function makeObservable<T, D extends readonly ServiceKey<any>[]>(
|
|
|
288
291
|
if (dispose) {
|
|
289
292
|
const span = listener.onDispose?.({ key });
|
|
290
293
|
try {
|
|
291
|
-
dispose
|
|
294
|
+
invokeWithin(span, dispose);
|
|
292
295
|
} catch (error) {
|
|
293
|
-
span?.
|
|
296
|
+
span?.end?.({ type: 'failure', error });
|
|
294
297
|
throw error;
|
|
295
298
|
}
|
|
296
|
-
span?.end?.();
|
|
299
|
+
span?.end?.({ type: 'success', value: undefined });
|
|
297
300
|
}
|
|
298
301
|
},
|
|
299
302
|
initialize: async (...args) => {
|
|
300
303
|
const span = listener.onInitialize?.({ key });
|
|
301
304
|
try {
|
|
302
305
|
const instance = observeMethodCalls(
|
|
303
|
-
await delegate.initialize(...args),
|
|
306
|
+
await invokeWithin(span, () => delegate.initialize(...args)),
|
|
304
307
|
listener,
|
|
305
308
|
key,
|
|
306
309
|
);
|
|
307
|
-
span?.end?.({
|
|
310
|
+
span?.end?.({ type: 'success', value: instance });
|
|
308
311
|
return instance;
|
|
309
312
|
} catch (error) {
|
|
310
|
-
span?.
|
|
313
|
+
span?.end?.({ type: 'failure', error });
|
|
311
314
|
throw error;
|
|
312
315
|
}
|
|
313
316
|
},
|
|
@@ -317,8 +320,8 @@ function makeObservable<T, D extends readonly ServiceKey<any>[]>(
|
|
|
317
320
|
/**
|
|
318
321
|
* Wraps an object with a Proxy to notify the listener of method calls.
|
|
319
322
|
*
|
|
320
|
-
* Methods returning a promise report
|
|
321
|
-
* not when the method returns.
|
|
323
|
+
* Methods returning a promise report their outcome when the promise
|
|
324
|
+
* settles, not when the method returns.
|
|
322
325
|
*
|
|
323
326
|
* @param thing The object whose method calls need to be observed.
|
|
324
327
|
* @param listener The listener notified of method call events.
|
|
@@ -327,13 +330,15 @@ function makeObservable<T, D extends readonly ServiceKey<any>[]>(
|
|
|
327
330
|
*/
|
|
328
331
|
function observeMethodCalls(
|
|
329
332
|
thing: any,
|
|
330
|
-
listener:
|
|
333
|
+
listener: ServiceModuleListener,
|
|
331
334
|
key: ServiceKey<unknown>,
|
|
332
335
|
): any {
|
|
333
336
|
if (typeof thing !== 'object' || thing === null) {
|
|
334
337
|
return thing;
|
|
335
338
|
}
|
|
336
339
|
|
|
340
|
+
const className = classNameOf(thing);
|
|
341
|
+
|
|
337
342
|
return new Proxy(thing, {
|
|
338
343
|
get(target, prop) {
|
|
339
344
|
const value = Reflect.get(target, prop);
|
|
@@ -341,27 +346,28 @@ function observeMethodCalls(
|
|
|
341
346
|
return (...args: unknown[]) => {
|
|
342
347
|
const span = listener.onMethodCall?.({
|
|
343
348
|
key,
|
|
344
|
-
|
|
349
|
+
className,
|
|
350
|
+
functionName: prop,
|
|
345
351
|
args,
|
|
346
352
|
});
|
|
347
353
|
try {
|
|
348
|
-
const result = value.apply(target, args);
|
|
354
|
+
const result = invokeWithin(span, () => value.apply(target, args));
|
|
349
355
|
if (result instanceof Promise) {
|
|
350
356
|
return result.then(
|
|
351
357
|
(resolved) => {
|
|
352
|
-
span?.end?.({
|
|
358
|
+
span?.end?.({ type: 'success', value: resolved });
|
|
353
359
|
return resolved;
|
|
354
360
|
},
|
|
355
361
|
(error) => {
|
|
356
|
-
span?.
|
|
362
|
+
span?.end?.({ type: 'failure', error });
|
|
357
363
|
throw error;
|
|
358
364
|
},
|
|
359
365
|
);
|
|
360
366
|
}
|
|
361
|
-
span?.end?.({ result });
|
|
367
|
+
span?.end?.({ type: 'success', value: result });
|
|
362
368
|
return result;
|
|
363
369
|
} catch (error) {
|
|
364
|
-
span?.
|
|
370
|
+
span?.end?.({ type: 'failure', error });
|
|
365
371
|
throw error;
|
|
366
372
|
}
|
|
367
373
|
};
|
|
@@ -370,3 +376,29 @@ function observeMethodCalls(
|
|
|
370
376
|
},
|
|
371
377
|
});
|
|
372
378
|
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Invokes an operation through the EventSpan's `run` wrapper when the
|
|
382
|
+
* listener provided one, so it can establish ambient state (tracing
|
|
383
|
+
* context) around the operation; invokes the operation directly otherwise.
|
|
384
|
+
*
|
|
385
|
+
* @param span The EventSpan returned by the listener, if any.
|
|
386
|
+
* @param fn The thunk performing the operation.
|
|
387
|
+
* @returns The value returned by `fn`.
|
|
388
|
+
*/
|
|
389
|
+
function invokeWithin<T>(span: EventSpan | void, fn: () => T): T {
|
|
390
|
+
return span?.run ? span.run(fn) : fn();
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Resolves the class name of a service instance, or undefined for values
|
|
395
|
+
* that are not instances of a named class (plain object literals,
|
|
396
|
+
* null-prototype objects).
|
|
397
|
+
*
|
|
398
|
+
* @param thing The service instance to inspect.
|
|
399
|
+
* @returns The constructor name, or undefined when there is none to report.
|
|
400
|
+
*/
|
|
401
|
+
function classNameOf(thing: object): string | undefined {
|
|
402
|
+
const name = thing.constructor?.name;
|
|
403
|
+
return name && name !== 'Object' ? name : undefined;
|
|
404
|
+
}
|
|
@@ -5,50 +5,51 @@ import type { ServiceKey } from './serviceKey';
|
|
|
5
5
|
* disposal, or method call), returned by a ServiceEventListener when the
|
|
6
6
|
* operation starts.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* `end` is invoked exactly once when the operation finishes, so
|
|
9
|
+
* implementations can close over per-call state (a start time, a span
|
|
10
|
+
* handle, a correlation id) without any bookkeeping to pair concurrent
|
|
11
|
+
* start/finish events.
|
|
12
12
|
*/
|
|
13
13
|
export interface EventSpan {
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* Optional wrapper around the operation itself. When present, the module
|
|
16
|
+
* invokes the operation as `run(() => operation())`, so the listener can
|
|
17
|
+
* establish ambient state that the operation body and its async
|
|
18
|
+
* continuations inherit — this is what lets spans of nested service
|
|
19
|
+
* calls form a parent-child hierarchy.
|
|
18
20
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
+
* Implementations must invoke `fn` exactly once, synchronously, and
|
|
22
|
+
* return its result unchanged (for async operations, the promise itself).
|
|
23
|
+
* `end` is still delivered separately when the operation finishes.
|
|
21
24
|
*
|
|
22
|
-
* @param
|
|
23
|
-
*
|
|
24
|
-
* instance), absent for dispose spans.
|
|
25
|
-
* @return void
|
|
25
|
+
* @param fn - A thunk that performs the operation.
|
|
26
|
+
* @return The value returned by `fn`.
|
|
26
27
|
*/
|
|
27
|
-
|
|
28
|
+
run?<T>(fn: () => T): T;
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
|
-
* Invoked when the operation
|
|
31
|
-
*
|
|
32
|
-
*
|
|
31
|
+
* Invoked exactly once when the operation finishes, whether it succeeded
|
|
32
|
+
* or failed. For methods that return a promise, this fires when the
|
|
33
|
+
* promise settles, not when the method returns. On failure, the error is
|
|
34
|
+
* rethrown to the caller after this is invoked.
|
|
33
35
|
*
|
|
34
|
-
*
|
|
36
|
+
* Whether to retain or log the outcome is the implementation's choice;
|
|
37
|
+
* values are passed by reference, so implementations must not mutate them.
|
|
38
|
+
*
|
|
39
|
+
* @param outcome - How the operation finished, and its value or error.
|
|
35
40
|
* @return void
|
|
36
41
|
*/
|
|
37
|
-
|
|
42
|
+
end?(outcome: EventOutcome): void;
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
46
|
+
* How an operation finished, delivered to EventSpan.end: `success` carries
|
|
47
|
+
* the value produced by the operation (the return or resolved value for
|
|
48
|
+
* method calls, the service instance for initialize, undefined for
|
|
49
|
+
* dispose); `failure` carries the error that was thrown or rejected.
|
|
44
50
|
*/
|
|
45
|
-
export
|
|
46
|
-
|
|
47
|
-
* The value produced by the operation: the return value for synchronous
|
|
48
|
-
* methods, or the resolved value for methods returning a promise.
|
|
49
|
-
*/
|
|
50
|
-
result: unknown;
|
|
51
|
-
}
|
|
51
|
+
export type EventOutcome =
|
|
52
|
+
{ type: 'success'; value: unknown } | { type: 'failure'; error: unknown };
|
|
52
53
|
|
|
53
54
|
/**
|
|
54
55
|
* Context of a service initialization, delivered to onInitialize.
|
|
@@ -82,10 +83,18 @@ export interface MethodCallContext {
|
|
|
82
83
|
*/
|
|
83
84
|
key: ServiceKey<unknown>;
|
|
84
85
|
|
|
86
|
+
/**
|
|
87
|
+
* The name of the class implementing the service (the instance's
|
|
88
|
+
* constructor name), which may differ from `key.name`. Undefined for
|
|
89
|
+
* services that are not instances of a named class, such as plain
|
|
90
|
+
* object literals.
|
|
91
|
+
*/
|
|
92
|
+
className?: string;
|
|
93
|
+
|
|
85
94
|
/**
|
|
86
95
|
* The name of the method that is being called.
|
|
87
96
|
*/
|
|
88
|
-
|
|
97
|
+
functionName: string;
|
|
89
98
|
|
|
90
99
|
/**
|
|
91
100
|
* The arguments the method was invoked with, passed by reference;
|
|
@@ -102,7 +111,7 @@ export interface MethodCallContext {
|
|
|
102
111
|
* return an EventSpan that is notified when that operation finishes.
|
|
103
112
|
* Returning nothing opts out of completion tracking for that call.
|
|
104
113
|
*/
|
|
105
|
-
export interface
|
|
114
|
+
export interface ServiceModuleListener {
|
|
106
115
|
/**
|
|
107
116
|
* Invoked at the start of the initialization process for a specific service.
|
|
108
117
|
*
|