@composed-di/core 0.5.3-alpha → 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/errors.d.ts +0 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +4 -4
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/serviceFactory.d.ts.map +1 -1
- package/dist/serviceFactory.js.map +1 -1
- package/dist/serviceKey.d.ts +6 -0
- package/dist/serviceKey.d.ts.map +1 -1
- package/dist/serviceKey.js.map +1 -1
- package/dist/serviceModule.d.ts +1 -11
- package/dist/serviceModule.d.ts.map +1 -1
- package/dist/serviceModule.js +3 -146
- package/dist/serviceModule.js.map +1 -1
- package/dist/serviceScope.d.ts.map +1 -1
- package/dist/serviceScope.js.map +1 -1
- package/dist/serviceSelector.d.ts.map +1 -1
- package/dist/serviceSelector.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js.map +1 -1
- package/package.json +17 -17
- package/src/errors.ts +2 -10
- package/src/index.ts +7 -9
- package/src/serviceFactory.ts +33 -33
- package/src/serviceKey.ts +11 -4
- package/src/serviceModule.ts +53 -217
- package/src/serviceScope.ts +2 -2
- package/src/serviceSelector.ts +4 -4
- package/src/utils.ts +103 -103
- package/dist/redactingEventListener.d.ts +0 -119
- package/dist/redactingEventListener.d.ts.map +0 -1
- package/dist/redactingEventListener.js +0 -169
- package/dist/redactingEventListener.js.map +0 -1
- package/dist/serviceEventListener.d.ts +0 -133
- package/dist/serviceEventListener.d.ts.map +0 -1
- package/dist/serviceEventListener.js +0 -3
- package/dist/serviceEventListener.js.map +0 -1
- package/dist/serviceModuleListener.d.ts +0 -133
- package/dist/serviceModuleListener.d.ts.map +0 -1
- package/dist/serviceModuleListener.js +0 -3
- package/dist/serviceModuleListener.js.map +0 -1
- package/src/redactingEventListener.ts +0 -256
- package/src/serviceModuleListener.ts +0 -139
|
@@ -1,133 +0,0 @@
|
|
|
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 ServiceEventListener {
|
|
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=serviceEventListener.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"serviceEventListener.js","sourceRoot":"","sources":["../src/serviceEventListener.ts"],"names":[],"mappings":""}
|
|
@@ -1,133 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"serviceModuleListener.js","sourceRoot":"","sources":["../src/serviceModuleListener.ts"],"names":[],"mappings":""}
|
|
@@ -1,256 +0,0 @@
|
|
|
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 a redacted value when no custom
|
|
13
|
-
* transform is given for it.
|
|
14
|
-
*/
|
|
15
|
-
export const REDACTED_VALUE = '[REDACTED]';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Custom masking for one included property, narrowed to the exact
|
|
19
|
-
* method named when {@link RedactionRuleBuilder.redact} is called.
|
|
20
|
-
* Omitting a side fully blanks it with {@link REDACTED_VALUE}; providing
|
|
21
|
-
* one lets you report a partial mask instead (e.g. the last 4 digits of
|
|
22
|
-
* a card number) — both always return a `string`, the masked
|
|
23
|
-
* representation to report in place of the real value.
|
|
24
|
-
*/
|
|
25
|
-
export type Mask<T, K extends Extract<keyof T, string>> = T[K] extends (
|
|
26
|
-
...args: infer A
|
|
27
|
-
) => infer R
|
|
28
|
-
? {
|
|
29
|
-
maskArgs?: (...args: A) => string;
|
|
30
|
-
maskResult?: (result: R) => string;
|
|
31
|
-
}
|
|
32
|
-
: never;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Per-property override, layered on top of a rule's `redactAll` default.
|
|
36
|
-
* `redacted: true` (from {@link RedactionRuleBuilder.redact}) redacts
|
|
37
|
-
* that property, optionally with a custom mask; `redacted: false` (from
|
|
38
|
-
* {@link RedactionRuleBuilder.exclude}) forces it to be left untouched
|
|
39
|
-
* regardless of `redactAll`.
|
|
40
|
-
*/
|
|
41
|
-
interface PropertyOverride {
|
|
42
|
-
redacted: boolean;
|
|
43
|
-
maskArgs?: (...args: any[]) => string;
|
|
44
|
-
maskResult?: (result: any) => string;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Marks a service — or specific properties of it — as sensitive, so the
|
|
49
|
-
* values flowing through its events are redacted. Returned by
|
|
50
|
-
* {@link RedactionRuleBuilder.build}, which is the only way to construct
|
|
51
|
-
* one — the `redactAll`/per-property merge logic lives entirely inside
|
|
52
|
-
* `maskArgs`/`maskResult`, so callers never need to know how a rule
|
|
53
|
-
* reached its decision, only what to do with it.
|
|
54
|
-
*/
|
|
55
|
-
export interface RedactionRule<T> {
|
|
56
|
-
readonly key: ServiceKey<T>;
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* The args to report for a call to `functionName`: unchanged if not
|
|
60
|
-
* redacted, otherwise blanked or run through a custom `maskArgs`.
|
|
61
|
-
*/
|
|
62
|
-
maskArgs(functionName: string, args: readonly unknown[]): readonly unknown[];
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* The value to report for a success outcome: unchanged if not
|
|
66
|
-
* redacted, otherwise blanked or run through a custom `maskResult`.
|
|
67
|
-
* Omit `functionName` to ask about the initialize result (the service
|
|
68
|
-
* instance itself), which only the rule's `redactAll` default touches.
|
|
69
|
-
*/
|
|
70
|
-
maskResult(functionName: string | undefined, result: unknown): unknown;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Fluent, single-key rule builder returned by {@link redactionRule}.
|
|
75
|
-
* `redactAll`, `redact`, and `exclude` all merge into the same rule —
|
|
76
|
-
* call them in any combination, in any order; the more specific
|
|
77
|
-
* per-property calls (`redact`/`exclude`) always win over the blanket
|
|
78
|
-
* `redactAll` default for the properties they name.
|
|
79
|
-
*
|
|
80
|
-
* @example
|
|
81
|
-
* ```ts
|
|
82
|
-
* const rules = [
|
|
83
|
-
* redactionRule(SecretClientKey)
|
|
84
|
-
* .redactAll()
|
|
85
|
-
* .build(), // whole service is sensitive
|
|
86
|
-
* redactionRule(BillingKey)
|
|
87
|
-
* .redactAll()
|
|
88
|
-
* .redact('chargeCard', { maskResult: (card) => `card ending in ${card.number.slice(-4)}` })
|
|
89
|
-
* .exclude('ping') // redact everything except this, with one custom mask
|
|
90
|
-
* .build(),
|
|
91
|
-
* redactionRule(VaultKey)
|
|
92
|
-
* .redact('getSecret')
|
|
93
|
-
* .build(), // only this call, nothing else
|
|
94
|
-
* ];
|
|
95
|
-
* ```
|
|
96
|
-
*/
|
|
97
|
-
export class RedactionRuleBuilder<T> {
|
|
98
|
-
private redactAllFlag = false;
|
|
99
|
-
private readonly overrides: Record<string, PropertyOverride> = {};
|
|
100
|
-
|
|
101
|
-
constructor(private readonly key: ServiceKey<T>) {}
|
|
102
|
-
|
|
103
|
-
/** Redacts every property, plus the initialize result, by default. */
|
|
104
|
-
redactAll(): this {
|
|
105
|
-
this.redactAllFlag = true;
|
|
106
|
-
return this;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Marks one property (method) as redacted, with optional custom
|
|
111
|
-
* masking. Call repeatedly for several properties. Overrides
|
|
112
|
-
* `redactAll`/`exclude` for this specific property.
|
|
113
|
-
*/
|
|
114
|
-
redact<K extends Extract<keyof T, string>>(name: K, mask?: Mask<T, K>): this {
|
|
115
|
-
this.overrides[name] = { redacted: true, ...mask };
|
|
116
|
-
return this;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Marks one or more properties as explicitly NOT redacted, overriding
|
|
121
|
-
* `redactAll` for just these.
|
|
122
|
-
*/
|
|
123
|
-
exclude(...names: Extract<keyof T, string>[]): this {
|
|
124
|
-
for (const name of names) {
|
|
125
|
-
this.overrides[name] = { redacted: false };
|
|
126
|
-
}
|
|
127
|
-
return this;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
build(): RedactionRule<any> {
|
|
131
|
-
if (!this.redactAllFlag && Object.keys(this.overrides).length === 0) {
|
|
132
|
-
throw new Error(
|
|
133
|
-
`redactionRule(${this.key.name}) has no effect: call .redactAll() and/or .redact(...) ` +
|
|
134
|
-
'before .build(), otherwise this rule never redacts anything.',
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const redactAllFlag = this.redactAllFlag;
|
|
139
|
-
const overrides = this.overrides;
|
|
140
|
-
|
|
141
|
-
return {
|
|
142
|
-
key: this.key,
|
|
143
|
-
maskArgs(functionName, args) {
|
|
144
|
-
const override = overrides[functionName];
|
|
145
|
-
const redacted = override ? override.redacted : redactAllFlag;
|
|
146
|
-
if (!redacted) {
|
|
147
|
-
return args;
|
|
148
|
-
}
|
|
149
|
-
return override?.maskArgs
|
|
150
|
-
? // A custom mask reports one string for the whole call,
|
|
151
|
-
// rather than a value per argument.
|
|
152
|
-
[override.maskArgs(...args)]
|
|
153
|
-
: // Replace each argument rather than the whole array, so the
|
|
154
|
-
// delegate still sees the call's arity.
|
|
155
|
-
args.map(() => REDACTED_VALUE);
|
|
156
|
-
},
|
|
157
|
-
maskResult(functionName, result) {
|
|
158
|
-
const override =
|
|
159
|
-
functionName === undefined ? undefined : overrides[functionName];
|
|
160
|
-
const redacted = override ? override.redacted : redactAllFlag;
|
|
161
|
-
if (!redacted) {
|
|
162
|
-
return result;
|
|
163
|
-
}
|
|
164
|
-
return override?.maskResult
|
|
165
|
-
? override.maskResult(result)
|
|
166
|
-
: REDACTED_VALUE;
|
|
167
|
-
},
|
|
168
|
-
} as RedactionRule<any>;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
export function redactionRule<T>(key: ServiceKey<T>): RedactionRuleBuilder<T> {
|
|
173
|
-
return new RedactionRuleBuilder(key);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* A ServiceEventListener decorator that redacts sensitive values before
|
|
178
|
-
* they reach the wrapped listener. Works with any implementation via
|
|
179
|
-
* delegation: arguments in MethodCallContext and success values in
|
|
180
|
-
* EventOutcome are replaced (wholesale, or via a custom transform)
|
|
181
|
-
* before the delegate ever sees them — whatever it captures or exports
|
|
182
|
-
* is already scrubbed.
|
|
183
|
-
*
|
|
184
|
-
* Failure outcomes are passed through unchanged so error reporting keeps
|
|
185
|
-
* working; keep secrets out of error messages at the throwing site.
|
|
186
|
-
*
|
|
187
|
-
* @example
|
|
188
|
-
* ```ts
|
|
189
|
-
* const listener = new RedactingEventListener(
|
|
190
|
-
* new OTELEventListener({ captureArguments: true, captureResults: true }),
|
|
191
|
-
* [
|
|
192
|
-
* redactionRule(SecretClientKey).redactAll().build(), // whole service is sensitive
|
|
193
|
-
* redactionRule(VaultKey).redact('getSecret').build(), // only this call
|
|
194
|
-
* redactionRule(HealthKey).redactAll().exclude('ping').build(), // everything but this call
|
|
195
|
-
* ],
|
|
196
|
-
* );
|
|
197
|
-
* ```
|
|
198
|
-
*/
|
|
199
|
-
export class RedactingEventListener implements ServiceModuleListener {
|
|
200
|
-
constructor(
|
|
201
|
-
private readonly delegate: ServiceModuleListener,
|
|
202
|
-
private readonly rules: readonly RedactionRule<any>[],
|
|
203
|
-
) {}
|
|
204
|
-
|
|
205
|
-
onInitialize(context: InitializeContext): EventSpan | void {
|
|
206
|
-
const rule = this.rules.find((r) => r.key === context.key);
|
|
207
|
-
return redactSpan(this.delegate.onInitialize?.(context), rule);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
onDispose(context: DisposeContext): EventSpan | void {
|
|
211
|
-
// Dispose carries no arguments and no result value; nothing to redact.
|
|
212
|
-
return this.delegate.onDispose?.(context);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
onMethodCall(context: MethodCallContext): EventSpan | void {
|
|
216
|
-
const rule = this.rules.find((r) => r.key === context.key);
|
|
217
|
-
const span = this.delegate.onMethodCall?.(
|
|
218
|
-
rule
|
|
219
|
-
? {
|
|
220
|
-
...context,
|
|
221
|
-
args: rule.maskArgs(context.functionName, context.args),
|
|
222
|
-
}
|
|
223
|
-
: context,
|
|
224
|
-
);
|
|
225
|
-
return redactSpan(span, rule, context.functionName);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Wraps the delegate's EventSpan so success values are redacted before
|
|
231
|
-
* `end` sees them, by delegating to the rule's `maskResult`. `run` (and
|
|
232
|
-
* any future fields) pass through untouched. `functionName` is omitted
|
|
233
|
-
* for the initialize result (the instance itself).
|
|
234
|
-
*/
|
|
235
|
-
function redactSpan(
|
|
236
|
-
span: EventSpan | void,
|
|
237
|
-
rule: RedactionRule<any> | undefined,
|
|
238
|
-
functionName?: string,
|
|
239
|
-
): EventSpan | void {
|
|
240
|
-
if (!rule || !span?.end) {
|
|
241
|
-
return span;
|
|
242
|
-
}
|
|
243
|
-
const end = span.end.bind(span);
|
|
244
|
-
return {
|
|
245
|
-
...span,
|
|
246
|
-
end: (outcome: EventOutcome) =>
|
|
247
|
-
end(
|
|
248
|
-
outcome.type === 'success'
|
|
249
|
-
? {
|
|
250
|
-
type: 'success',
|
|
251
|
-
value: rule.maskResult(functionName, outcome.value),
|
|
252
|
-
}
|
|
253
|
-
: outcome,
|
|
254
|
-
),
|
|
255
|
-
};
|
|
256
|
-
}
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import type { ServiceKey } from './serviceKey';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* A handle representing a single in-flight operation (initialization,
|
|
5
|
-
* disposal, or method call), returned by a ServiceEventListener when the
|
|
6
|
-
* operation starts.
|
|
7
|
-
*
|
|
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
|
-
*/
|
|
13
|
-
export interface EventSpan {
|
|
14
|
-
/**
|
|
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.
|
|
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.
|
|
24
|
-
*
|
|
25
|
-
* @param fn - A thunk that performs the operation.
|
|
26
|
-
* @return The value returned by `fn`.
|
|
27
|
-
*/
|
|
28
|
-
run?<T>(fn: () => T): T;
|
|
29
|
-
|
|
30
|
-
/**
|
|
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.
|
|
35
|
-
*
|
|
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.
|
|
40
|
-
* @return void
|
|
41
|
-
*/
|
|
42
|
-
end?(outcome: EventOutcome): void;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
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.
|
|
50
|
-
*/
|
|
51
|
-
export type EventOutcome =
|
|
52
|
-
{ type: 'success'; value: unknown } | { type: 'failure'; error: unknown };
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Context of a service initialization, delivered to onInitialize.
|
|
56
|
-
* Future fields are added here rather than as extra parameters.
|
|
57
|
-
*/
|
|
58
|
-
export interface InitializeContext {
|
|
59
|
-
/**
|
|
60
|
-
* The unique identifier of the service that is being initialized.
|
|
61
|
-
*/
|
|
62
|
-
key: ServiceKey<unknown>;
|
|
63
|
-
}
|
|
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
|
-
/**
|
|
77
|
-
* Context of a method invocation, delivered to onMethodCall.
|
|
78
|
-
* Future fields are added here rather than as extra parameters.
|
|
79
|
-
*/
|
|
80
|
-
export interface MethodCallContext {
|
|
81
|
-
/**
|
|
82
|
-
* The unique identifier of the service the method belongs to.
|
|
83
|
-
*/
|
|
84
|
-
key: ServiceKey<unknown>;
|
|
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
|
-
|
|
94
|
-
/**
|
|
95
|
-
* The name of the method that is being called.
|
|
96
|
-
*/
|
|
97
|
-
functionName: string;
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* The arguments the method was invoked with, passed by reference;
|
|
101
|
-
* implementations must not mutate them.
|
|
102
|
-
*/
|
|
103
|
-
args: readonly unknown[];
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Interface for listening to service events. Implement this interface to
|
|
108
|
-
* observe lifecycle events and method calls of services in a module.
|
|
109
|
-
*
|
|
110
|
-
* Each method is invoked when the corresponding operation starts and may
|
|
111
|
-
* return an EventSpan that is notified when that operation finishes.
|
|
112
|
-
* Returning nothing opts out of completion tracking for that call.
|
|
113
|
-
*/
|
|
114
|
-
export interface ServiceModuleListener {
|
|
115
|
-
/**
|
|
116
|
-
* Invoked at the start of the initialization process for a specific service.
|
|
117
|
-
*
|
|
118
|
-
* @param context - Context of the initialization, including the service key.
|
|
119
|
-
* @return An EventSpan notified when initialization finishes, or void.
|
|
120
|
-
*/
|
|
121
|
-
onInitialize?(context: InitializeContext): EventSpan | void;
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Invoked when the disposal process for a service starts.
|
|
125
|
-
*
|
|
126
|
-
* @param context - Context of the disposal, including the service key.
|
|
127
|
-
* @return An EventSpan notified when disposal finishes, or void.
|
|
128
|
-
*/
|
|
129
|
-
onDispose?(context: DisposeContext): EventSpan | void;
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Invoked when a method call starts on a service instance.
|
|
133
|
-
*
|
|
134
|
-
* @param context - Context of the invocation, including the service key,
|
|
135
|
-
* the method name, and its arguments.
|
|
136
|
-
* @return An EventSpan notified when the call finishes, or void.
|
|
137
|
-
*/
|
|
138
|
-
onMethodCall?(context: MethodCallContext): EventSpan | void;
|
|
139
|
-
}
|