@tallyrow/safesignal 1.0.1-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +345 -0
- package/dist/index.cjs +1240 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +167 -0
- package/dist/index.d.ts +167 -0
- package/dist/index.mjs +1232 -0
- package/dist/index.mjs.map +1 -0
- package/dist/testing.cjs +272 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +97 -0
- package/dist/testing.d.ts +97 -0
- package/dist/testing.mjs +268 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/transport-beacon.cjs +452 -0
- package/dist/transport-beacon.cjs.map +1 -0
- package/dist/transport-beacon.d.cts +68 -0
- package/dist/transport-beacon.d.ts +68 -0
- package/dist/transport-beacon.mjs +450 -0
- package/dist/transport-beacon.mjs.map +1 -0
- package/dist/types-D-xVvmvX.d.cts +227 -0
- package/dist/types-D-xVvmvX.d.ts +227 -0
- package/package.json +79 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public type surface for the frontend logging package.
|
|
3
|
+
*
|
|
4
|
+
* Every export here is part of the SemVer-stable consumer contract documented
|
|
5
|
+
* in `specs/001-structured-logging-core/contracts/`. Adding new optional
|
|
6
|
+
* fields is a minor-version change; removals or signature changes require a
|
|
7
|
+
* major-version bump and a migration note.
|
|
8
|
+
*
|
|
9
|
+
* Locked invariants:
|
|
10
|
+
* - `Attributes` is a recursive **constrained** union — `unknown` and
|
|
11
|
+
* `object` are deliberately excluded so passing a raw class instance is
|
|
12
|
+
* type-friction. The sanitizer (T031) coerces stragglers at runtime.
|
|
13
|
+
* - The ONLY `unknown` parameter in the public surface is the optional
|
|
14
|
+
* `error` arg of `Logger.error()`.
|
|
15
|
+
* - The package source MUST NOT consult any ambient state
|
|
16
|
+
* (`process.env`, `import.meta.env`, `location`, `document.cookie`);
|
|
17
|
+
* enforced by `tests/contract/no-ambient-state.test.ts` (T013).
|
|
18
|
+
* - The public `.d.ts` MUST NOT mention `@opentelemetry/*` or any OTel
|
|
19
|
+
* concept name; enforced by `tests/contract/declarations-surface.test.ts`
|
|
20
|
+
* (T013) and `tests/security/bundle-shape.security.test.ts` (T049).
|
|
21
|
+
*/
|
|
22
|
+
/** Severity levels, in increasing order. */
|
|
23
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
24
|
+
/**
|
|
25
|
+
* Per-environment level overrides. Resolved per `contracts/logger-config.md`
|
|
26
|
+
* (LC-3): config.level (LevelMap → environment lookup) takes precedence over
|
|
27
|
+
* the env-default table.
|
|
28
|
+
*/
|
|
29
|
+
type LevelMap = Partial<Record<'production' | 'development' | 'test', LogLevel>>;
|
|
30
|
+
/**
|
|
31
|
+
* A value that may appear inside `LogEvent.attributes` or
|
|
32
|
+
* `LogContext.attributes`. Excludes `unknown`, `object`, class instances, DOM
|
|
33
|
+
* nodes, framework objects, functions, symbols, and bigints by design —
|
|
34
|
+
* passing those is allowed at runtime (TypeScript cannot fully prevent it)
|
|
35
|
+
* but the sanitizer (T031) reduces them to type tags before any transport
|
|
36
|
+
* sees the event.
|
|
37
|
+
*/
|
|
38
|
+
type AttributeValue = string | number | boolean | null | AttributeValue[] | {
|
|
39
|
+
[key: string]: AttributeValue;
|
|
40
|
+
};
|
|
41
|
+
/** A bag of per-call or per-context structured attributes. Always an object. */
|
|
42
|
+
type Attributes = Record<string, AttributeValue>;
|
|
43
|
+
/** Application identity — the consuming app, even from a federated module. */
|
|
44
|
+
interface AppIdentity {
|
|
45
|
+
name: string;
|
|
46
|
+
version?: string;
|
|
47
|
+
}
|
|
48
|
+
/** Module identity — for independently deployed federated modules. */
|
|
49
|
+
interface ModuleIdentity {
|
|
50
|
+
name: string;
|
|
51
|
+
version?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Merged context attached to every emitted `LogEvent`. Merge precedence
|
|
55
|
+
* is documented in `contracts/logger-config.md` (LC-7) and
|
|
56
|
+
* `data-model.md`: root config → per-logger options → logger chain
|
|
57
|
+
* (`child()` / `withContext()`) → `correlation()` return value.
|
|
58
|
+
*/
|
|
59
|
+
interface LogContext {
|
|
60
|
+
application?: AppIdentity;
|
|
61
|
+
module?: ModuleIdentity;
|
|
62
|
+
environment?: string;
|
|
63
|
+
attributes?: Attributes;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Captured error information populated by `Logger.error(msg, attrs, err)`.
|
|
67
|
+
* The pipeline reduces an arbitrary `unknown` error value to this shape
|
|
68
|
+
* immediately; transports never see the original `Error` instance.
|
|
69
|
+
*/
|
|
70
|
+
interface ErrorInfo {
|
|
71
|
+
name: string;
|
|
72
|
+
message: string;
|
|
73
|
+
stack?: string;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The canonical structured event produced by the pipeline. By the time a
|
|
77
|
+
* transport receives a `LogEvent`, it has been: (1) sanitized per
|
|
78
|
+
* `contracts/sanitization.md`, (2) URL-scrubbed, (3) redacted per
|
|
79
|
+
* `contracts/redaction.md` (fail-closed), (4) control-char-escaped, and
|
|
80
|
+
* (5) frozen in dev builds. See `contracts/log-event.md` LE-1..LE-11.
|
|
81
|
+
*/
|
|
82
|
+
interface LogEvent {
|
|
83
|
+
/** ISO-8601 string assigned by the pipeline; consumer-supplied input ignored. */
|
|
84
|
+
timestamp: string;
|
|
85
|
+
/** Severity matching the logger method called. */
|
|
86
|
+
level: LogLevel;
|
|
87
|
+
/** Required. Empty string allowed. Sanitized for length and control chars. */
|
|
88
|
+
message: string;
|
|
89
|
+
/** Always an object. May be empty. Sanitized and redacted before delivery. */
|
|
90
|
+
attributes: Attributes;
|
|
91
|
+
/** Always present. Merged from config + logger chain + correlation. */
|
|
92
|
+
context: LogContext;
|
|
93
|
+
/** Populated only when `Logger.error(msg, attrs, err)` is called with `err`. */
|
|
94
|
+
error?: ErrorInfo;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Delivery interface. Consumer transports MUST follow the security contract
|
|
98
|
+
* in `contracts/transport.md` (T-S1..T-S5): body-only delivery (POST/PUT
|
|
99
|
+
* JSON or `Blob` sendBeacon), HTTPS cross-origin, no event data in URL
|
|
100
|
+
* paths / queries / fragments, treat events as immutable, idempotent
|
|
101
|
+
* `flush`/`shutdown`. Failure isolation is provided by the package's
|
|
102
|
+
* internal `SafeTransport` wrapper.
|
|
103
|
+
*/
|
|
104
|
+
interface Transport {
|
|
105
|
+
/** Stable diagnostic identifier. */
|
|
106
|
+
name: string;
|
|
107
|
+
/** Receive a fully processed `LogEvent`. May be sync or async. */
|
|
108
|
+
send(event: LogEvent): void | Promise<void>;
|
|
109
|
+
/** Optional flush hook for batching transports. */
|
|
110
|
+
flush?(): Promise<void>;
|
|
111
|
+
/** Optional shutdown hook. Must be idempotent. */
|
|
112
|
+
shutdown?(): Promise<void>;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Factory that produces a `Transport`. Used by `LoggerConfig.transports`
|
|
116
|
+
* so configuration values can stay declarative — the factory is invoked
|
|
117
|
+
* once at `configureLogging()` time.
|
|
118
|
+
*/
|
|
119
|
+
type TransportFactory = () => Transport;
|
|
120
|
+
/**
|
|
121
|
+
* One redaction rule. At least one of `key` or `shape` must be set.
|
|
122
|
+
* - `key`: case-insensitive match against the IMMEDIATE property name of
|
|
123
|
+
* the value being inspected (never a value substring).
|
|
124
|
+
* - `shape`: match against leaf string values, regardless of key.
|
|
125
|
+
*/
|
|
126
|
+
interface RedactionRule {
|
|
127
|
+
key?: string | RegExp;
|
|
128
|
+
shape?: RegExp;
|
|
129
|
+
/** Replacement string. Default `'[REDACTED]'`. */
|
|
130
|
+
replacement?: string;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* A redactor receives the post-sanitize, pre-control-char-guard `LogEvent`
|
|
134
|
+
* and returns either a transformed event or `null` to drop the event
|
|
135
|
+
* entirely. MUST be synchronous. If it throws or returns any other value,
|
|
136
|
+
* the dispatcher drops the event (fail-closed) and invokes
|
|
137
|
+
* `LoggerConfig.onInternalError`. See `contracts/redaction.md`.
|
|
138
|
+
*/
|
|
139
|
+
type Redactor = (event: LogEvent) => LogEvent | null;
|
|
140
|
+
/**
|
|
141
|
+
* Configurable upper bounds for the sanitizer. Consumers MAY tighten any
|
|
142
|
+
* limit by passing a value below the default; values above the documented
|
|
143
|
+
* Max clamp to Max, values below Min clamp to Min, and both clamping events
|
|
144
|
+
* emit one `onInternalError` notice at `configureLogging()` time.
|
|
145
|
+
*
|
|
146
|
+
* Defaults / bounds (locked by `contracts/sanitization.md`):
|
|
147
|
+
* maxDepth: default 8, min 1, max 16
|
|
148
|
+
* maxStringLength: default 8192, min 64, max 65536
|
|
149
|
+
* maxArrayLength: default 1000, min 1, max 10000
|
|
150
|
+
* maxAttributeCount: default 256, min 1, max 4096
|
|
151
|
+
*/
|
|
152
|
+
interface SanitizerLimits {
|
|
153
|
+
maxDepth: number;
|
|
154
|
+
maxStringLength: number;
|
|
155
|
+
maxArrayLength: number;
|
|
156
|
+
maxAttributeCount: number;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Options for `scrubUrl(url, options?)`. The default URL scrubber strips
|
|
160
|
+
* query and (optionally) fragment params whose names match the redaction
|
|
161
|
+
* denylist; `extraParams` adds project-specific names.
|
|
162
|
+
*/
|
|
163
|
+
interface ScrubUrlOptions {
|
|
164
|
+
/** Additional query/fragment param names to scrub (case-insensitive). */
|
|
165
|
+
extraParams?: ReadonlyArray<string | RegExp>;
|
|
166
|
+
/** Whether to also scrub the URL fragment. Default `true`. */
|
|
167
|
+
fragment?: boolean;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Root logging configuration. Pass once via `configureLogging()`. Defaults
|
|
171
|
+
* are documented in `contracts/logger-config.md`.
|
|
172
|
+
*/
|
|
173
|
+
interface LoggerConfig {
|
|
174
|
+
application?: AppIdentity;
|
|
175
|
+
module?: ModuleIdentity;
|
|
176
|
+
/** `'production' | 'development' | 'test'` or any string; unknown → `warn`. */
|
|
177
|
+
environment?: string;
|
|
178
|
+
/** Single level or per-environment map. Overrides env defaults. */
|
|
179
|
+
level?: LogLevel | LevelMap;
|
|
180
|
+
/** Static metadata merged into every emitted event's context. */
|
|
181
|
+
context?: Partial<LogContext>;
|
|
182
|
+
/** Per-emit dynamic context hook. Must be cheap and synchronous. */
|
|
183
|
+
correlation?: () => Partial<LogContext>;
|
|
184
|
+
/** Configured transports. Empty/undefined installs `NoopTransport`. */
|
|
185
|
+
transports?: Array<Transport | TransportFactory>;
|
|
186
|
+
/** Custom redactor. Fully replaces the default unless composed manually. */
|
|
187
|
+
redactor?: Redactor;
|
|
188
|
+
/** Tighten sanitizer bounds. Values above documented Max are clamped. */
|
|
189
|
+
sanitizerLimits?: Partial<SanitizerLimits>;
|
|
190
|
+
/**
|
|
191
|
+
* Diagnostics hook for internal failures (transport throws, init failure,
|
|
192
|
+
* redactor throw, sanitizer-limit clamp). Fires at most once per
|
|
193
|
+
* failing transport per session.
|
|
194
|
+
*/
|
|
195
|
+
onInternalError?: (err: Error) => void;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Options for `createLogger()` — layered on top of the root `LoggerConfig`.
|
|
199
|
+
*/
|
|
200
|
+
interface CreateLoggerOptions {
|
|
201
|
+
/** Optional free-form logger name for diagnostics. Not part of the event. */
|
|
202
|
+
name?: string;
|
|
203
|
+
/** Override `module` identity for this logger only (federated modules). */
|
|
204
|
+
module?: ModuleIdentity;
|
|
205
|
+
/** Additional static context for this logger. Merged with root context. */
|
|
206
|
+
context?: Partial<LogContext>;
|
|
207
|
+
/** Per-logger level override. Wins over root config and env defaults. */
|
|
208
|
+
level?: LogLevel;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* The consumer-facing logger. Every method returns synchronously and never
|
|
212
|
+
* throws. The only `unknown` parameter in the public surface is the
|
|
213
|
+
* optional `error` arg of `error()`; the pipeline immediately reduces it
|
|
214
|
+
* to `ErrorInfo`.
|
|
215
|
+
*/
|
|
216
|
+
interface Logger {
|
|
217
|
+
debug(message: string, attributes?: Attributes): void;
|
|
218
|
+
info(message: string, attributes?: Attributes): void;
|
|
219
|
+
warn(message: string, attributes?: Attributes): void;
|
|
220
|
+
error(message: string, attributes?: Attributes, error?: unknown): void;
|
|
221
|
+
/** Return a derived logger with additional context layered on top. */
|
|
222
|
+
child(context: Partial<LogContext>): Logger;
|
|
223
|
+
/** Alias for `child()`. */
|
|
224
|
+
withContext(context: Partial<LogContext>): Logger;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export type { AppIdentity as A, CreateLoggerOptions as C, ErrorInfo as E, LevelMap as L, ModuleIdentity as M, RedactionRule as R, SanitizerLimits as S, Transport as T, AttributeValue as a, Attributes as b, LogContext as c, LogEvent as d, LogLevel as e, Logger as f, LoggerConfig as g, Redactor as h, ScrubUrlOptions as i, TransportFactory as j };
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public type surface for the frontend logging package.
|
|
3
|
+
*
|
|
4
|
+
* Every export here is part of the SemVer-stable consumer contract documented
|
|
5
|
+
* in `specs/001-structured-logging-core/contracts/`. Adding new optional
|
|
6
|
+
* fields is a minor-version change; removals or signature changes require a
|
|
7
|
+
* major-version bump and a migration note.
|
|
8
|
+
*
|
|
9
|
+
* Locked invariants:
|
|
10
|
+
* - `Attributes` is a recursive **constrained** union — `unknown` and
|
|
11
|
+
* `object` are deliberately excluded so passing a raw class instance is
|
|
12
|
+
* type-friction. The sanitizer (T031) coerces stragglers at runtime.
|
|
13
|
+
* - The ONLY `unknown` parameter in the public surface is the optional
|
|
14
|
+
* `error` arg of `Logger.error()`.
|
|
15
|
+
* - The package source MUST NOT consult any ambient state
|
|
16
|
+
* (`process.env`, `import.meta.env`, `location`, `document.cookie`);
|
|
17
|
+
* enforced by `tests/contract/no-ambient-state.test.ts` (T013).
|
|
18
|
+
* - The public `.d.ts` MUST NOT mention `@opentelemetry/*` or any OTel
|
|
19
|
+
* concept name; enforced by `tests/contract/declarations-surface.test.ts`
|
|
20
|
+
* (T013) and `tests/security/bundle-shape.security.test.ts` (T049).
|
|
21
|
+
*/
|
|
22
|
+
/** Severity levels, in increasing order. */
|
|
23
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
24
|
+
/**
|
|
25
|
+
* Per-environment level overrides. Resolved per `contracts/logger-config.md`
|
|
26
|
+
* (LC-3): config.level (LevelMap → environment lookup) takes precedence over
|
|
27
|
+
* the env-default table.
|
|
28
|
+
*/
|
|
29
|
+
type LevelMap = Partial<Record<'production' | 'development' | 'test', LogLevel>>;
|
|
30
|
+
/**
|
|
31
|
+
* A value that may appear inside `LogEvent.attributes` or
|
|
32
|
+
* `LogContext.attributes`. Excludes `unknown`, `object`, class instances, DOM
|
|
33
|
+
* nodes, framework objects, functions, symbols, and bigints by design —
|
|
34
|
+
* passing those is allowed at runtime (TypeScript cannot fully prevent it)
|
|
35
|
+
* but the sanitizer (T031) reduces them to type tags before any transport
|
|
36
|
+
* sees the event.
|
|
37
|
+
*/
|
|
38
|
+
type AttributeValue = string | number | boolean | null | AttributeValue[] | {
|
|
39
|
+
[key: string]: AttributeValue;
|
|
40
|
+
};
|
|
41
|
+
/** A bag of per-call or per-context structured attributes. Always an object. */
|
|
42
|
+
type Attributes = Record<string, AttributeValue>;
|
|
43
|
+
/** Application identity — the consuming app, even from a federated module. */
|
|
44
|
+
interface AppIdentity {
|
|
45
|
+
name: string;
|
|
46
|
+
version?: string;
|
|
47
|
+
}
|
|
48
|
+
/** Module identity — for independently deployed federated modules. */
|
|
49
|
+
interface ModuleIdentity {
|
|
50
|
+
name: string;
|
|
51
|
+
version?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Merged context attached to every emitted `LogEvent`. Merge precedence
|
|
55
|
+
* is documented in `contracts/logger-config.md` (LC-7) and
|
|
56
|
+
* `data-model.md`: root config → per-logger options → logger chain
|
|
57
|
+
* (`child()` / `withContext()`) → `correlation()` return value.
|
|
58
|
+
*/
|
|
59
|
+
interface LogContext {
|
|
60
|
+
application?: AppIdentity;
|
|
61
|
+
module?: ModuleIdentity;
|
|
62
|
+
environment?: string;
|
|
63
|
+
attributes?: Attributes;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Captured error information populated by `Logger.error(msg, attrs, err)`.
|
|
67
|
+
* The pipeline reduces an arbitrary `unknown` error value to this shape
|
|
68
|
+
* immediately; transports never see the original `Error` instance.
|
|
69
|
+
*/
|
|
70
|
+
interface ErrorInfo {
|
|
71
|
+
name: string;
|
|
72
|
+
message: string;
|
|
73
|
+
stack?: string;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The canonical structured event produced by the pipeline. By the time a
|
|
77
|
+
* transport receives a `LogEvent`, it has been: (1) sanitized per
|
|
78
|
+
* `contracts/sanitization.md`, (2) URL-scrubbed, (3) redacted per
|
|
79
|
+
* `contracts/redaction.md` (fail-closed), (4) control-char-escaped, and
|
|
80
|
+
* (5) frozen in dev builds. See `contracts/log-event.md` LE-1..LE-11.
|
|
81
|
+
*/
|
|
82
|
+
interface LogEvent {
|
|
83
|
+
/** ISO-8601 string assigned by the pipeline; consumer-supplied input ignored. */
|
|
84
|
+
timestamp: string;
|
|
85
|
+
/** Severity matching the logger method called. */
|
|
86
|
+
level: LogLevel;
|
|
87
|
+
/** Required. Empty string allowed. Sanitized for length and control chars. */
|
|
88
|
+
message: string;
|
|
89
|
+
/** Always an object. May be empty. Sanitized and redacted before delivery. */
|
|
90
|
+
attributes: Attributes;
|
|
91
|
+
/** Always present. Merged from config + logger chain + correlation. */
|
|
92
|
+
context: LogContext;
|
|
93
|
+
/** Populated only when `Logger.error(msg, attrs, err)` is called with `err`. */
|
|
94
|
+
error?: ErrorInfo;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Delivery interface. Consumer transports MUST follow the security contract
|
|
98
|
+
* in `contracts/transport.md` (T-S1..T-S5): body-only delivery (POST/PUT
|
|
99
|
+
* JSON or `Blob` sendBeacon), HTTPS cross-origin, no event data in URL
|
|
100
|
+
* paths / queries / fragments, treat events as immutable, idempotent
|
|
101
|
+
* `flush`/`shutdown`. Failure isolation is provided by the package's
|
|
102
|
+
* internal `SafeTransport` wrapper.
|
|
103
|
+
*/
|
|
104
|
+
interface Transport {
|
|
105
|
+
/** Stable diagnostic identifier. */
|
|
106
|
+
name: string;
|
|
107
|
+
/** Receive a fully processed `LogEvent`. May be sync or async. */
|
|
108
|
+
send(event: LogEvent): void | Promise<void>;
|
|
109
|
+
/** Optional flush hook for batching transports. */
|
|
110
|
+
flush?(): Promise<void>;
|
|
111
|
+
/** Optional shutdown hook. Must be idempotent. */
|
|
112
|
+
shutdown?(): Promise<void>;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Factory that produces a `Transport`. Used by `LoggerConfig.transports`
|
|
116
|
+
* so configuration values can stay declarative — the factory is invoked
|
|
117
|
+
* once at `configureLogging()` time.
|
|
118
|
+
*/
|
|
119
|
+
type TransportFactory = () => Transport;
|
|
120
|
+
/**
|
|
121
|
+
* One redaction rule. At least one of `key` or `shape` must be set.
|
|
122
|
+
* - `key`: case-insensitive match against the IMMEDIATE property name of
|
|
123
|
+
* the value being inspected (never a value substring).
|
|
124
|
+
* - `shape`: match against leaf string values, regardless of key.
|
|
125
|
+
*/
|
|
126
|
+
interface RedactionRule {
|
|
127
|
+
key?: string | RegExp;
|
|
128
|
+
shape?: RegExp;
|
|
129
|
+
/** Replacement string. Default `'[REDACTED]'`. */
|
|
130
|
+
replacement?: string;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* A redactor receives the post-sanitize, pre-control-char-guard `LogEvent`
|
|
134
|
+
* and returns either a transformed event or `null` to drop the event
|
|
135
|
+
* entirely. MUST be synchronous. If it throws or returns any other value,
|
|
136
|
+
* the dispatcher drops the event (fail-closed) and invokes
|
|
137
|
+
* `LoggerConfig.onInternalError`. See `contracts/redaction.md`.
|
|
138
|
+
*/
|
|
139
|
+
type Redactor = (event: LogEvent) => LogEvent | null;
|
|
140
|
+
/**
|
|
141
|
+
* Configurable upper bounds for the sanitizer. Consumers MAY tighten any
|
|
142
|
+
* limit by passing a value below the default; values above the documented
|
|
143
|
+
* Max clamp to Max, values below Min clamp to Min, and both clamping events
|
|
144
|
+
* emit one `onInternalError` notice at `configureLogging()` time.
|
|
145
|
+
*
|
|
146
|
+
* Defaults / bounds (locked by `contracts/sanitization.md`):
|
|
147
|
+
* maxDepth: default 8, min 1, max 16
|
|
148
|
+
* maxStringLength: default 8192, min 64, max 65536
|
|
149
|
+
* maxArrayLength: default 1000, min 1, max 10000
|
|
150
|
+
* maxAttributeCount: default 256, min 1, max 4096
|
|
151
|
+
*/
|
|
152
|
+
interface SanitizerLimits {
|
|
153
|
+
maxDepth: number;
|
|
154
|
+
maxStringLength: number;
|
|
155
|
+
maxArrayLength: number;
|
|
156
|
+
maxAttributeCount: number;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Options for `scrubUrl(url, options?)`. The default URL scrubber strips
|
|
160
|
+
* query and (optionally) fragment params whose names match the redaction
|
|
161
|
+
* denylist; `extraParams` adds project-specific names.
|
|
162
|
+
*/
|
|
163
|
+
interface ScrubUrlOptions {
|
|
164
|
+
/** Additional query/fragment param names to scrub (case-insensitive). */
|
|
165
|
+
extraParams?: ReadonlyArray<string | RegExp>;
|
|
166
|
+
/** Whether to also scrub the URL fragment. Default `true`. */
|
|
167
|
+
fragment?: boolean;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Root logging configuration. Pass once via `configureLogging()`. Defaults
|
|
171
|
+
* are documented in `contracts/logger-config.md`.
|
|
172
|
+
*/
|
|
173
|
+
interface LoggerConfig {
|
|
174
|
+
application?: AppIdentity;
|
|
175
|
+
module?: ModuleIdentity;
|
|
176
|
+
/** `'production' | 'development' | 'test'` or any string; unknown → `warn`. */
|
|
177
|
+
environment?: string;
|
|
178
|
+
/** Single level or per-environment map. Overrides env defaults. */
|
|
179
|
+
level?: LogLevel | LevelMap;
|
|
180
|
+
/** Static metadata merged into every emitted event's context. */
|
|
181
|
+
context?: Partial<LogContext>;
|
|
182
|
+
/** Per-emit dynamic context hook. Must be cheap and synchronous. */
|
|
183
|
+
correlation?: () => Partial<LogContext>;
|
|
184
|
+
/** Configured transports. Empty/undefined installs `NoopTransport`. */
|
|
185
|
+
transports?: Array<Transport | TransportFactory>;
|
|
186
|
+
/** Custom redactor. Fully replaces the default unless composed manually. */
|
|
187
|
+
redactor?: Redactor;
|
|
188
|
+
/** Tighten sanitizer bounds. Values above documented Max are clamped. */
|
|
189
|
+
sanitizerLimits?: Partial<SanitizerLimits>;
|
|
190
|
+
/**
|
|
191
|
+
* Diagnostics hook for internal failures (transport throws, init failure,
|
|
192
|
+
* redactor throw, sanitizer-limit clamp). Fires at most once per
|
|
193
|
+
* failing transport per session.
|
|
194
|
+
*/
|
|
195
|
+
onInternalError?: (err: Error) => void;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Options for `createLogger()` — layered on top of the root `LoggerConfig`.
|
|
199
|
+
*/
|
|
200
|
+
interface CreateLoggerOptions {
|
|
201
|
+
/** Optional free-form logger name for diagnostics. Not part of the event. */
|
|
202
|
+
name?: string;
|
|
203
|
+
/** Override `module` identity for this logger only (federated modules). */
|
|
204
|
+
module?: ModuleIdentity;
|
|
205
|
+
/** Additional static context for this logger. Merged with root context. */
|
|
206
|
+
context?: Partial<LogContext>;
|
|
207
|
+
/** Per-logger level override. Wins over root config and env defaults. */
|
|
208
|
+
level?: LogLevel;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* The consumer-facing logger. Every method returns synchronously and never
|
|
212
|
+
* throws. The only `unknown` parameter in the public surface is the
|
|
213
|
+
* optional `error` arg of `error()`; the pipeline immediately reduces it
|
|
214
|
+
* to `ErrorInfo`.
|
|
215
|
+
*/
|
|
216
|
+
interface Logger {
|
|
217
|
+
debug(message: string, attributes?: Attributes): void;
|
|
218
|
+
info(message: string, attributes?: Attributes): void;
|
|
219
|
+
warn(message: string, attributes?: Attributes): void;
|
|
220
|
+
error(message: string, attributes?: Attributes, error?: unknown): void;
|
|
221
|
+
/** Return a derived logger with additional context layered on top. */
|
|
222
|
+
child(context: Partial<LogContext>): Logger;
|
|
223
|
+
/** Alias for `child()`. */
|
|
224
|
+
withContext(context: Partial<LogContext>): Logger;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export type { AppIdentity as A, CreateLoggerOptions as C, ErrorInfo as E, LevelMap as L, ModuleIdentity as M, RedactionRule as R, SanitizerLimits as S, Transport as T, AttributeValue as a, Attributes as b, LogContext as c, LogEvent as d, LogLevel as e, Logger as f, LoggerConfig as g, Redactor as h, ScrubUrlOptions as i, TransportFactory as j };
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tallyrow/safesignal",
|
|
3
|
+
"version": "1.0.1-rc.1",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"description": "SafeSignal — secure structured logging facade and safety boundary for browser applications and federated frontend modules. Browser-first, vendor-neutral, secure-by-default sanitization, redaction, and pluggable transports.",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"keywords": [
|
|
10
|
+
"safesignal",
|
|
11
|
+
"logging",
|
|
12
|
+
"structured-logging",
|
|
13
|
+
"browser",
|
|
14
|
+
"frontend",
|
|
15
|
+
"federated",
|
|
16
|
+
"module-federation",
|
|
17
|
+
"sanitization",
|
|
18
|
+
"redaction",
|
|
19
|
+
"transport",
|
|
20
|
+
"observability",
|
|
21
|
+
"secure-by-default"
|
|
22
|
+
],
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://gitlab.com/tallyrow/safesignal.git"
|
|
26
|
+
},
|
|
27
|
+
"type": "module",
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"import": "./dist/index.mjs",
|
|
36
|
+
"require": "./dist/index.cjs"
|
|
37
|
+
},
|
|
38
|
+
"./testing": {
|
|
39
|
+
"types": "./dist/testing.d.ts",
|
|
40
|
+
"import": "./dist/testing.mjs",
|
|
41
|
+
"require": "./dist/testing.cjs"
|
|
42
|
+
},
|
|
43
|
+
"./transport-beacon": {
|
|
44
|
+
"types": "./dist/transport-beacon.d.ts",
|
|
45
|
+
"import": "./dist/transport-beacon.mjs",
|
|
46
|
+
"require": "./dist/transport-beacon.cjs"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"main": "./dist/index.cjs",
|
|
50
|
+
"module": "./dist/index.mjs",
|
|
51
|
+
"types": "./dist/index.d.ts",
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsup",
|
|
54
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p tests/tsconfig.json",
|
|
55
|
+
"typecheck:src": "tsc --noEmit",
|
|
56
|
+
"typecheck:tests": "tsc --noEmit -p tests/tsconfig.json",
|
|
57
|
+
"test": "vitest run",
|
|
58
|
+
"test:contract": "vitest run tests/contract",
|
|
59
|
+
"test:security": "vitest run tests/security",
|
|
60
|
+
"test:integration": "vitest run tests/integration",
|
|
61
|
+
"test:unit": "vitest run tests/unit",
|
|
62
|
+
"test:watch": "vitest"
|
|
63
|
+
},
|
|
64
|
+
"dependencies": {},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@opentelemetry/api": "^1.9.0",
|
|
67
|
+
"@opentelemetry/api-logs": "^0.57.0",
|
|
68
|
+
"@opentelemetry/sdk-logs": "^0.57.0",
|
|
69
|
+
"@types/node": "^20.0.0",
|
|
70
|
+
"@vitest/coverage-v8": "^2.1.0",
|
|
71
|
+
"happy-dom": "^15.0.0",
|
|
72
|
+
"tsup": "^8.3.0",
|
|
73
|
+
"typescript": "^5.4.0",
|
|
74
|
+
"vitest": "^2.1.0"
|
|
75
|
+
},
|
|
76
|
+
"engines": {
|
|
77
|
+
"node": ">=18.0.0"
|
|
78
|
+
}
|
|
79
|
+
}
|