@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
package/src/utils.ts
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
import { ServiceModule } from './serviceModule'
|
|
2
|
-
import { ServiceKey } from './serviceKey'
|
|
1
|
+
import { ServiceModule } from './serviceModule'
|
|
2
|
+
import { ServiceKey } from './serviceKey'
|
|
3
3
|
|
|
4
4
|
export interface DotGraphOptions {
|
|
5
5
|
/** Graph direction: 'TB' (top-bottom), 'LR' (left-right), 'BT' (bottom-top), 'RL' (right-left) */
|
|
6
|
-
direction?: 'TB' | 'LR' | 'BT' | 'RL'
|
|
6
|
+
direction?: 'TB' | 'LR' | 'BT' | 'RL'
|
|
7
7
|
/** Title for the graph */
|
|
8
|
-
title?: string
|
|
8
|
+
title?: string
|
|
9
9
|
/** Show nodes with no dependencies in a different color */
|
|
10
|
-
highlightLeaves?: boolean
|
|
10
|
+
highlightLeaves?: boolean
|
|
11
11
|
/** Show nodes with no dependents in a different color */
|
|
12
|
-
highlightRoots?: boolean
|
|
12
|
+
highlightRoots?: boolean
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export interface MermaidGraphOptions {
|
|
16
16
|
/** Graph direction: 'TB' (top-bottom), 'LR' (left-right), 'BT' (bottom-top), 'RL' (right-left) */
|
|
17
|
-
direction?: 'TB' | 'LR' | 'BT' | 'RL'
|
|
17
|
+
direction?: 'TB' | 'LR' | 'BT' | 'RL'
|
|
18
18
|
/** Show nodes with no dependencies in a different color */
|
|
19
|
-
highlightLeaves?: boolean
|
|
19
|
+
highlightLeaves?: boolean
|
|
20
20
|
/** Show nodes with no dependents in a different color */
|
|
21
|
-
highlightRoots?: boolean
|
|
21
|
+
highlightRoots?: boolean
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Escapes special characters in strings for DOT notation
|
|
26
26
|
*/
|
|
27
27
|
function escapeDotString(str: string): string {
|
|
28
|
-
return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n')
|
|
28
|
+
return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n')
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Escapes special characters in strings for Mermaid notation
|
|
33
33
|
*/
|
|
34
34
|
function escapeMermaidString(str: string): string {
|
|
35
|
-
return str.replace(/"/g, '#quot;')
|
|
35
|
+
return str.replace(/"/g, '#quot;')
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
@@ -56,98 +56,98 @@ export function createDotGraph(
|
|
|
56
56
|
highlightRoots: true,
|
|
57
57
|
},
|
|
58
58
|
): string {
|
|
59
|
-
const factories = module.factories
|
|
60
|
-
const lines: string[] = []
|
|
59
|
+
const factories = module.factories
|
|
60
|
+
const lines: string[] = []
|
|
61
61
|
|
|
62
62
|
// Start the digraph
|
|
63
|
-
lines.push('digraph ServiceDependencies {')
|
|
64
|
-
lines.push(` label="${title}";`)
|
|
65
|
-
lines.push(' labelloc="t";')
|
|
66
|
-
lines.push(' fontsize=16;')
|
|
67
|
-
lines.push(` rankdir=${direction};`)
|
|
68
|
-
lines.push('')
|
|
63
|
+
lines.push('digraph ServiceDependencies {')
|
|
64
|
+
lines.push(` label="${title}";`)
|
|
65
|
+
lines.push(' labelloc="t";')
|
|
66
|
+
lines.push(' fontsize=16;')
|
|
67
|
+
lines.push(` rankdir=${direction};`)
|
|
68
|
+
lines.push('')
|
|
69
69
|
|
|
70
70
|
// Default node styling
|
|
71
|
-
lines.push(' node [')
|
|
72
|
-
lines.push(' shape=box,')
|
|
73
|
-
lines.push(' style="rounded,filled",')
|
|
74
|
-
lines.push(' fillcolor="#e1f5ff",')
|
|
75
|
-
lines.push(' color="#0288d1",')
|
|
76
|
-
lines.push(' fontname="Arial",')
|
|
77
|
-
lines.push(' fontsize=12')
|
|
78
|
-
lines.push(' ];')
|
|
79
|
-
lines.push('')
|
|
71
|
+
lines.push(' node [')
|
|
72
|
+
lines.push(' shape=box,')
|
|
73
|
+
lines.push(' style="rounded,filled",')
|
|
74
|
+
lines.push(' fillcolor="#e1f5ff",')
|
|
75
|
+
lines.push(' color="#0288d1",')
|
|
76
|
+
lines.push(' fontname="Arial",')
|
|
77
|
+
lines.push(' fontsize=12')
|
|
78
|
+
lines.push(' ];')
|
|
79
|
+
lines.push('')
|
|
80
80
|
|
|
81
81
|
// Default edge styling
|
|
82
|
-
lines.push(' edge [')
|
|
83
|
-
lines.push(' color="#666666",')
|
|
84
|
-
lines.push(' arrowsize=0.8')
|
|
85
|
-
lines.push(' ];')
|
|
86
|
-
lines.push('')
|
|
82
|
+
lines.push(' edge [')
|
|
83
|
+
lines.push(' color="#666666",')
|
|
84
|
+
lines.push(' arrowsize=0.8')
|
|
85
|
+
lines.push(' ];')
|
|
86
|
+
lines.push('')
|
|
87
87
|
|
|
88
88
|
// Build dependency maps to identify leaves and roots
|
|
89
|
-
const hasDependencies = new Set<string>()
|
|
90
|
-
const hasDependents = new Set<string>()
|
|
89
|
+
const hasDependencies = new Set<string>()
|
|
90
|
+
const hasDependents = new Set<string>()
|
|
91
91
|
|
|
92
92
|
factories.forEach((factory) => {
|
|
93
|
-
const serviceName = factory.provides.name
|
|
93
|
+
const serviceName = factory.provides.name
|
|
94
94
|
|
|
95
95
|
if (factory.dependsOn.length > 0) {
|
|
96
|
-
hasDependencies.add(serviceName)
|
|
96
|
+
hasDependencies.add(serviceName)
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
factory.dependsOn.forEach((dependency: ServiceKey<unknown>) => {
|
|
100
|
-
hasDependents.add(dependency.name)
|
|
101
|
-
})
|
|
102
|
-
})
|
|
100
|
+
hasDependents.add(dependency.name)
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
103
|
|
|
104
104
|
// Define nodes with special styling for leaves and roots
|
|
105
|
-
const nodeIds = new Map<string, string>()
|
|
106
|
-
let nodeCounter = 0
|
|
105
|
+
const nodeIds = new Map<string, string>()
|
|
106
|
+
let nodeCounter = 0
|
|
107
107
|
|
|
108
108
|
factories.forEach((factory) => {
|
|
109
|
-
const serviceName = factory.provides.name
|
|
110
|
-
const nodeId = `node${nodeCounter++}
|
|
111
|
-
nodeIds.set(serviceName, nodeId)
|
|
109
|
+
const serviceName = factory.provides.name
|
|
110
|
+
const nodeId = `node${nodeCounter++}`
|
|
111
|
+
nodeIds.set(serviceName, nodeId)
|
|
112
112
|
|
|
113
|
-
const isLeaf = !hasDependencies.has(serviceName)
|
|
114
|
-
const isRoot = !hasDependents.has(serviceName)
|
|
113
|
+
const isLeaf = !hasDependencies.has(serviceName)
|
|
114
|
+
const isRoot = !hasDependents.has(serviceName)
|
|
115
115
|
|
|
116
|
-
let nodeStyle = ''
|
|
116
|
+
let nodeStyle = ''
|
|
117
117
|
|
|
118
118
|
if (highlightLeaves && isLeaf) {
|
|
119
|
-
nodeStyle = ' [fillcolor="#c8e6c9", color="#388e3c"]'
|
|
119
|
+
nodeStyle = ' [fillcolor="#c8e6c9", color="#388e3c"]'
|
|
120
120
|
} else if (highlightRoots && isRoot) {
|
|
121
|
-
nodeStyle = ' [fillcolor="#ffccbc", color="#d84315"]'
|
|
121
|
+
nodeStyle = ' [fillcolor="#ffccbc", color="#d84315"]'
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
lines.push(
|
|
125
125
|
` ${nodeId} [label="${escapeDotString(serviceName)}"]${nodeStyle};`,
|
|
126
|
-
)
|
|
127
|
-
})
|
|
126
|
+
)
|
|
127
|
+
})
|
|
128
128
|
|
|
129
|
-
lines.push('')
|
|
129
|
+
lines.push('')
|
|
130
130
|
|
|
131
131
|
// Define edges (dependencies)
|
|
132
132
|
factories.forEach((factory) => {
|
|
133
|
-
const serviceName = factory.provides.name
|
|
134
|
-
const serviceNodeId = nodeIds.get(serviceName)
|
|
133
|
+
const serviceName = factory.provides.name
|
|
134
|
+
const serviceNodeId = nodeIds.get(serviceName)!
|
|
135
135
|
|
|
136
136
|
factory.dependsOn.forEach((dependency: ServiceKey<unknown>) => {
|
|
137
|
-
const depName = dependency.name
|
|
138
|
-
const depNodeId = nodeIds.get(depName)
|
|
137
|
+
const depName = dependency.name
|
|
138
|
+
const depNodeId = nodeIds.get(depName)
|
|
139
139
|
|
|
140
140
|
if (depNodeId) {
|
|
141
141
|
// Arrow points from dependent to dependency (what needs it -> what provides it)
|
|
142
|
-
lines.push(` ${serviceNodeId} -> ${depNodeId};`)
|
|
142
|
+
lines.push(` ${serviceNodeId} -> ${depNodeId};`)
|
|
143
143
|
}
|
|
144
|
-
})
|
|
145
|
-
})
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
146
|
|
|
147
147
|
// Close the digraph
|
|
148
|
-
lines.push('}')
|
|
148
|
+
lines.push('}')
|
|
149
149
|
|
|
150
|
-
return lines.join('\n')
|
|
150
|
+
return lines.join('\n')
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
/**
|
|
@@ -161,9 +161,9 @@ export function printDotGraph(
|
|
|
161
161
|
module: ServiceModule,
|
|
162
162
|
options?: DotGraphOptions,
|
|
163
163
|
): void {
|
|
164
|
-
console.log(createDotGraph(module, options))
|
|
165
|
-
console.log('\n\nCopy the DOT output above and paste it into:')
|
|
166
|
-
console.log('https://dreampuf.github.io/GraphvizOnline/')
|
|
164
|
+
console.log(createDotGraph(module, options))
|
|
165
|
+
console.log('\n\nCopy the DOT output above and paste it into:')
|
|
166
|
+
console.log('https://dreampuf.github.io/GraphvizOnline/')
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
/**
|
|
@@ -185,79 +185,79 @@ export function createMermaidGraph(
|
|
|
185
185
|
highlightRoots: true,
|
|
186
186
|
},
|
|
187
187
|
): string {
|
|
188
|
-
const factories = module.factories
|
|
189
|
-
const lines: string[] = []
|
|
188
|
+
const factories = module.factories
|
|
189
|
+
const lines: string[] = []
|
|
190
190
|
|
|
191
191
|
// Start the flowchart
|
|
192
|
-
lines.push(`flowchart ${direction}`)
|
|
192
|
+
lines.push(`flowchart ${direction}`)
|
|
193
193
|
|
|
194
194
|
// Build dependency maps to identify leaves and roots
|
|
195
|
-
const hasDependencies = new Set<string>()
|
|
196
|
-
const hasDependents = new Set<string>()
|
|
195
|
+
const hasDependencies = new Set<string>()
|
|
196
|
+
const hasDependents = new Set<string>()
|
|
197
197
|
|
|
198
198
|
factories.forEach((factory) => {
|
|
199
|
-
const serviceName = factory.provides.name
|
|
199
|
+
const serviceName = factory.provides.name
|
|
200
200
|
|
|
201
201
|
if (factory.dependsOn.length > 0) {
|
|
202
|
-
hasDependencies.add(serviceName)
|
|
202
|
+
hasDependencies.add(serviceName)
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
factory.dependsOn.forEach((dependency) => {
|
|
206
|
-
hasDependents.add(dependency.name)
|
|
207
|
-
})
|
|
208
|
-
})
|
|
206
|
+
hasDependents.add(dependency.name)
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
209
|
|
|
210
210
|
// Define nodes with special styling for leaves and roots
|
|
211
|
-
const nodeIds = new Map<string, string>()
|
|
212
|
-
let nodeCounter = 0
|
|
211
|
+
const nodeIds = new Map<string, string>()
|
|
212
|
+
let nodeCounter = 0
|
|
213
213
|
|
|
214
214
|
factories.forEach((factory) => {
|
|
215
|
-
const serviceName = factory.provides.name
|
|
216
|
-
const nodeId = `node${nodeCounter++}
|
|
217
|
-
nodeIds.set(serviceName, nodeId)
|
|
215
|
+
const serviceName = factory.provides.name
|
|
216
|
+
const nodeId = `node${nodeCounter++}`
|
|
217
|
+
nodeIds.set(serviceName, nodeId)
|
|
218
218
|
|
|
219
|
-
lines.push(` ${nodeId}["${escapeMermaidString(serviceName)}"]`)
|
|
220
|
-
})
|
|
219
|
+
lines.push(` ${nodeId}["${escapeMermaidString(serviceName)}"]`)
|
|
220
|
+
})
|
|
221
221
|
|
|
222
|
-
lines.push('')
|
|
222
|
+
lines.push('')
|
|
223
223
|
|
|
224
224
|
// Define edges (dependencies)
|
|
225
225
|
factories.forEach((factory) => {
|
|
226
|
-
const serviceName = factory.provides.name
|
|
227
|
-
const serviceNodeId = nodeIds.get(serviceName)
|
|
226
|
+
const serviceName = factory.provides.name
|
|
227
|
+
const serviceNodeId = nodeIds.get(serviceName)!
|
|
228
228
|
|
|
229
229
|
factory.dependsOn.forEach((dependency) => {
|
|
230
|
-
const depName = dependency.name
|
|
231
|
-
const depNodeId = nodeIds.get(depName)
|
|
230
|
+
const depName = dependency.name
|
|
231
|
+
const depNodeId = nodeIds.get(depName)
|
|
232
232
|
|
|
233
233
|
if (depNodeId) {
|
|
234
234
|
// Arrow points from dependent to dependency (what needs it -> what provides it)
|
|
235
|
-
lines.push(` ${serviceNodeId} --> ${depNodeId}`)
|
|
235
|
+
lines.push(` ${serviceNodeId} --> ${depNodeId}`)
|
|
236
236
|
}
|
|
237
|
-
})
|
|
238
|
-
})
|
|
237
|
+
})
|
|
238
|
+
})
|
|
239
239
|
|
|
240
|
-
lines.push('')
|
|
240
|
+
lines.push('')
|
|
241
241
|
|
|
242
242
|
// Apply styling
|
|
243
243
|
factories.forEach((factory) => {
|
|
244
|
-
const serviceName = factory.provides.name
|
|
245
|
-
const serviceNodeId = nodeIds.get(serviceName)
|
|
244
|
+
const serviceName = factory.provides.name
|
|
245
|
+
const serviceNodeId = nodeIds.get(serviceName)!
|
|
246
246
|
|
|
247
|
-
const isLeaf = !hasDependencies.has(serviceName)
|
|
248
|
-
const isRoot = !hasDependents.has(serviceName)
|
|
247
|
+
const isLeaf = !hasDependencies.has(serviceName)
|
|
248
|
+
const isRoot = !hasDependents.has(serviceName)
|
|
249
249
|
|
|
250
250
|
if (highlightLeaves && isLeaf) {
|
|
251
|
-
lines.push(` style ${serviceNodeId} fill:#c8e6c9,stroke:#388e3c`)
|
|
251
|
+
lines.push(` style ${serviceNodeId} fill:#c8e6c9,stroke:#388e3c`)
|
|
252
252
|
} else if (highlightRoots && isRoot) {
|
|
253
|
-
lines.push(` style ${serviceNodeId} fill:#ffccbc,stroke:#d84315`)
|
|
253
|
+
lines.push(` style ${serviceNodeId} fill:#ffccbc,stroke:#d84315`)
|
|
254
254
|
} else {
|
|
255
255
|
// Default style
|
|
256
|
-
lines.push(` style ${serviceNodeId} fill:#e1f5ff,stroke:#0288d1`)
|
|
256
|
+
lines.push(` style ${serviceNodeId} fill:#e1f5ff,stroke:#0288d1`)
|
|
257
257
|
}
|
|
258
|
-
})
|
|
258
|
+
})
|
|
259
259
|
|
|
260
|
-
return lines.join('\n')
|
|
260
|
+
return lines.join('\n')
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
/**
|
|
@@ -271,7 +271,7 @@ export function printMermaidGraph(
|
|
|
271
271
|
module: ServiceModule,
|
|
272
272
|
options?: MermaidGraphOptions,
|
|
273
273
|
): void {
|
|
274
|
-
console.log(createMermaidGraph(module, options))
|
|
275
|
-
console.log('\n\nCopy the Mermaid output above and paste it into:')
|
|
276
|
-
console.log('https://mermaid.live/')
|
|
274
|
+
console.log(createMermaidGraph(module, options))
|
|
275
|
+
console.log('\n\nCopy the Mermaid output above and paste it into:')
|
|
276
|
+
console.log('https://mermaid.live/')
|
|
277
277
|
}
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import type { DisposeContext, EventSpan, InitializeContext, MethodCallContext, ServiceModuleListener } from './serviceModuleListener';
|
|
2
|
-
import type { ServiceKey } from './serviceKey';
|
|
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 readonly overrides;
|
|
71
|
-
constructor(key: ServiceKey<T>);
|
|
72
|
-
/** Redacts every property, plus the initialize result, by default. */
|
|
73
|
-
redactAll(): this;
|
|
74
|
-
/**
|
|
75
|
-
* Marks one property (method) as redacted, with optional custom
|
|
76
|
-
* masking. Call repeatedly for several properties. Overrides
|
|
77
|
-
* `redactAll`/`exclude` for this specific property.
|
|
78
|
-
*/
|
|
79
|
-
redact<K extends Extract<keyof T, string>>(name: K, mask?: Mask<T, K>): this;
|
|
80
|
-
/**
|
|
81
|
-
* Marks one or more properties as explicitly NOT redacted, overriding
|
|
82
|
-
* `redactAll` for just these.
|
|
83
|
-
*/
|
|
84
|
-
exclude(...names: Extract<keyof T, string>[]): this;
|
|
85
|
-
build(): RedactionRule<any>;
|
|
86
|
-
}
|
|
87
|
-
export declare function redactionRule<T>(key: ServiceKey<T>): RedactionRuleBuilder<T>;
|
|
88
|
-
/**
|
|
89
|
-
* A ServiceEventListener decorator that redacts sensitive values before
|
|
90
|
-
* they reach the wrapped listener. Works with any implementation via
|
|
91
|
-
* delegation: arguments in MethodCallContext and success values in
|
|
92
|
-
* EventOutcome are replaced (wholesale, or via a custom transform)
|
|
93
|
-
* before the delegate ever sees them — whatever it captures or exports
|
|
94
|
-
* is already scrubbed.
|
|
95
|
-
*
|
|
96
|
-
* Failure outcomes are passed through unchanged so error reporting keeps
|
|
97
|
-
* working; keep secrets out of error messages at the throwing site.
|
|
98
|
-
*
|
|
99
|
-
* @example
|
|
100
|
-
* ```ts
|
|
101
|
-
* const listener = new RedactingEventListener(
|
|
102
|
-
* new OTELEventListener({ captureArguments: true, captureResults: true }),
|
|
103
|
-
* [
|
|
104
|
-
* redactionRule(SecretClientKey).redactAll().build(), // whole service is sensitive
|
|
105
|
-
* redactionRule(VaultKey).redact('getSecret').build(), // only this call
|
|
106
|
-
* redactionRule(HealthKey).redactAll().exclude('ping').build(), // everything but this call
|
|
107
|
-
* ],
|
|
108
|
-
* );
|
|
109
|
-
* ```
|
|
110
|
-
*/
|
|
111
|
-
export declare class RedactingEventListener implements ServiceModuleListener {
|
|
112
|
-
private readonly delegate;
|
|
113
|
-
private readonly rules;
|
|
114
|
-
constructor(delegate: ServiceModuleListener, rules: readonly RedactionRule<any>[]);
|
|
115
|
-
onInitialize(context: InitializeContext): EventSpan | void;
|
|
116
|
-
onDispose(context: DisposeContext): EventSpan | void;
|
|
117
|
-
onMethodCall(context: MethodCallContext): EventSpan | void;
|
|
118
|
-
}
|
|
119
|
-
//# sourceMappingURL=redactingEventListener.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
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;;;;;;;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;IAIrB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAHhC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwC;gBAErC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC;IAE/C,sEAAsE;IACtE,SAAS,IAAI,IAAI;IAKjB;;;;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;IAK5E;;;OAGG;IACH,OAAO,CAAC,GAAG,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI;IAOnD,KAAK,IAAI,aAAa,CAAC,GAAG,CAAC;CAwC5B;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,oBAAoB,CAAC,CAAC,CAAC,CAE5E;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;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;IAK1D,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI;IAKpD,YAAY,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,GAAG,IAAI;CAY3D"}
|
|
@@ -1,169 +0,0 @@
|
|
|
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.overrides = {};
|
|
39
|
-
}
|
|
40
|
-
/** Redacts every property, plus the initialize result, by default. */
|
|
41
|
-
redactAll() {
|
|
42
|
-
this.redactAllFlag = true;
|
|
43
|
-
return this;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Marks one property (method) as redacted, with optional custom
|
|
47
|
-
* masking. Call repeatedly for several properties. Overrides
|
|
48
|
-
* `redactAll`/`exclude` for this specific property.
|
|
49
|
-
*/
|
|
50
|
-
redact(name, mask) {
|
|
51
|
-
this.overrides[name] = Object.assign({ redacted: true }, mask);
|
|
52
|
-
return this;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Marks one or more properties as explicitly NOT redacted, overriding
|
|
56
|
-
* `redactAll` for just these.
|
|
57
|
-
*/
|
|
58
|
-
exclude(...names) {
|
|
59
|
-
for (const name of names) {
|
|
60
|
-
this.overrides[name] = { redacted: false };
|
|
61
|
-
}
|
|
62
|
-
return this;
|
|
63
|
-
}
|
|
64
|
-
build() {
|
|
65
|
-
if (!this.redactAllFlag && Object.keys(this.overrides).length === 0) {
|
|
66
|
-
throw new Error(`redactionRule(${this.key.name}) has no effect: call .redactAll() and/or .redact(...) ` +
|
|
67
|
-
'before .build(), otherwise this rule never redacts anything.');
|
|
68
|
-
}
|
|
69
|
-
const redactAllFlag = this.redactAllFlag;
|
|
70
|
-
const overrides = this.overrides;
|
|
71
|
-
return {
|
|
72
|
-
key: this.key,
|
|
73
|
-
maskArgs(functionName, args) {
|
|
74
|
-
const override = overrides[functionName];
|
|
75
|
-
const redacted = override ? override.redacted : redactAllFlag;
|
|
76
|
-
if (!redacted) {
|
|
77
|
-
return args;
|
|
78
|
-
}
|
|
79
|
-
return (override === null || override === void 0 ? void 0 : override.maskArgs)
|
|
80
|
-
? // A custom mask reports one string for the whole call,
|
|
81
|
-
// rather than a value per argument.
|
|
82
|
-
[override.maskArgs(...args)]
|
|
83
|
-
: // Replace each argument rather than the whole array, so the
|
|
84
|
-
// delegate still sees the call's arity.
|
|
85
|
-
args.map(() => exports.REDACTED_VALUE);
|
|
86
|
-
},
|
|
87
|
-
maskResult(functionName, result) {
|
|
88
|
-
const override = functionName === undefined ? undefined : overrides[functionName];
|
|
89
|
-
const redacted = override ? override.redacted : redactAllFlag;
|
|
90
|
-
if (!redacted) {
|
|
91
|
-
return result;
|
|
92
|
-
}
|
|
93
|
-
return (override === null || override === void 0 ? void 0 : override.maskResult)
|
|
94
|
-
? override.maskResult(result)
|
|
95
|
-
: exports.REDACTED_VALUE;
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
exports.RedactionRuleBuilder = RedactionRuleBuilder;
|
|
101
|
-
function redactionRule(key) {
|
|
102
|
-
return new RedactionRuleBuilder(key);
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* A ServiceEventListener decorator that redacts sensitive values before
|
|
106
|
-
* they reach the wrapped listener. Works with any implementation via
|
|
107
|
-
* delegation: arguments in MethodCallContext and success values in
|
|
108
|
-
* EventOutcome are replaced (wholesale, or via a custom transform)
|
|
109
|
-
* before the delegate ever sees them — whatever it captures or exports
|
|
110
|
-
* is already scrubbed.
|
|
111
|
-
*
|
|
112
|
-
* Failure outcomes are passed through unchanged so error reporting keeps
|
|
113
|
-
* working; keep secrets out of error messages at the throwing site.
|
|
114
|
-
*
|
|
115
|
-
* @example
|
|
116
|
-
* ```ts
|
|
117
|
-
* const listener = new RedactingEventListener(
|
|
118
|
-
* new OTELEventListener({ captureArguments: true, captureResults: true }),
|
|
119
|
-
* [
|
|
120
|
-
* redactionRule(SecretClientKey).redactAll().build(), // whole service is sensitive
|
|
121
|
-
* redactionRule(VaultKey).redact('getSecret').build(), // only this call
|
|
122
|
-
* redactionRule(HealthKey).redactAll().exclude('ping').build(), // everything but this call
|
|
123
|
-
* ],
|
|
124
|
-
* );
|
|
125
|
-
* ```
|
|
126
|
-
*/
|
|
127
|
-
class RedactingEventListener {
|
|
128
|
-
constructor(delegate, rules) {
|
|
129
|
-
this.delegate = delegate;
|
|
130
|
-
this.rules = rules;
|
|
131
|
-
}
|
|
132
|
-
onInitialize(context) {
|
|
133
|
-
var _a, _b;
|
|
134
|
-
const rule = this.rules.find((r) => r.key === context.key);
|
|
135
|
-
return redactSpan((_b = (_a = this.delegate).onInitialize) === null || _b === void 0 ? void 0 : _b.call(_a, context), rule);
|
|
136
|
-
}
|
|
137
|
-
onDispose(context) {
|
|
138
|
-
var _a, _b;
|
|
139
|
-
// Dispose carries no arguments and no result value; nothing to redact.
|
|
140
|
-
return (_b = (_a = this.delegate).onDispose) === null || _b === void 0 ? void 0 : _b.call(_a, context);
|
|
141
|
-
}
|
|
142
|
-
onMethodCall(context) {
|
|
143
|
-
var _a, _b;
|
|
144
|
-
const rule = this.rules.find((r) => r.key === context.key);
|
|
145
|
-
const span = (_b = (_a = this.delegate).onMethodCall) === null || _b === void 0 ? void 0 : _b.call(_a, rule
|
|
146
|
-
? Object.assign(Object.assign({}, context), { args: rule.maskArgs(context.functionName, context.args) }) : context);
|
|
147
|
-
return redactSpan(span, rule, context.functionName);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
exports.RedactingEventListener = RedactingEventListener;
|
|
151
|
-
/**
|
|
152
|
-
* Wraps the delegate's EventSpan so success values are redacted before
|
|
153
|
-
* `end` sees them, by delegating to the rule's `maskResult`. `run` (and
|
|
154
|
-
* any future fields) pass through untouched. `functionName` is omitted
|
|
155
|
-
* for the initialize result (the instance itself).
|
|
156
|
-
*/
|
|
157
|
-
function redactSpan(span, rule, functionName) {
|
|
158
|
-
if (!rule || !(span === null || span === void 0 ? void 0 : span.end)) {
|
|
159
|
-
return span;
|
|
160
|
-
}
|
|
161
|
-
const end = span.end.bind(span);
|
|
162
|
-
return Object.assign(Object.assign({}, span), { end: (outcome) => end(outcome.type === 'success'
|
|
163
|
-
? {
|
|
164
|
-
type: 'success',
|
|
165
|
-
value: rule.maskResult(functionName, outcome.value),
|
|
166
|
-
}
|
|
167
|
-
: outcome) });
|
|
168
|
-
}
|
|
169
|
-
//# sourceMappingURL=redactingEventListener.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"redactingEventListener.js","sourceRoot":"","sources":["../src/redactingEventListener.ts"],"names":[],"mappings":"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"}
|