@monochromatic-dev/module-logger 0.2.0 → 0.4.0
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/CHANGELOG.md +27 -0
- package/README.md +53 -13
- package/dist/final/neutral/browser.d.mts +60 -0
- package/dist/final/neutral/browser.mjs +1 -0
- package/dist/final/neutral/index.d.mts +359 -596
- package/dist/final/neutral/index.mjs +2 -3
- package/dist/final/neutral/indexed-db-hsIfv7Cv.mjs +2 -0
- package/dist/final/neutral/types-BkkBXgY3.d.mts +76 -0
- package/dist/final/node/file-CRGb1hDK.mjs +1 -0
- package/dist/final/node/index.d.mts +359 -596
- package/dist/final/node/index.mjs +3 -3
- package/dist/final/node/node.d.mts +103 -0
- package/dist/final/node/node.mjs +1 -0
- package/dist/final/node/types-BkkBXgY3.d.mts +76 -0
- package/package.json +19 -4
- package/src/artifact-platform-split.unit.test.ts +140 -0
- package/src/browser.ts +14 -0
- package/src/create-logger.ts +183 -183
- package/src/create-logger.unit.test.ts +112 -112
- package/src/default-sinks.neutral.ts +47 -0
- package/src/default-sinks.node.ts +43 -0
- package/src/error-format.ts +23 -23
- package/src/index.ts +1 -4
- package/src/logger.ts +76 -51
- package/src/node.ts +23 -0
- package/src/restricted-global-scope.unit.test.ts +101 -0
- package/src/sink/console-control-chars.ts +64 -64
- package/src/sink/console-control-chars.unit.test.ts +14 -14
- package/src/sink/console.ts +194 -194
- package/src/sink/console.unit.test.ts +18 -18
- package/src/sink/file.ts +136 -140
- package/src/sink/file.unit.test.ts +19 -26
- package/src/sink/index.ts +4 -7
- package/src/sink/indexed-db-util.ts +42 -42
- package/src/sink/indexed-db.browser.test.ts +7 -7
- package/src/sink/indexed-db.ts +109 -109
- package/src/sink/indexed-db.unit.test.ts +5 -13
- package/src/sink/local-storage-key.ts +73 -73
- package/src/sink/local-storage-key.unit.test.ts +8 -8
- package/src/sink/local-storage-quota.ts +37 -37
- package/src/sink/local-storage-quota.unit.test.ts +8 -8
- package/src/sink/local-storage-store.ts +113 -113
- package/src/sink/local-storage-store.unit.test.ts +35 -35
- package/src/sink/local-storage.ts +72 -72
- package/src/sink/local-storage.unit.test.ts +27 -27
- package/src/sink/noop.ts +20 -20
- package/src/sink/noop.unit.test.ts +1 -1
- package/src/sink/opfs.browser.test.ts +7 -7
- package/src/sink/opfs.ts +62 -62
- package/src/sink/opfs.unit.test.ts +5 -13
- package/src/sink/record-buffer.ts +84 -84
- package/src/sink/record-buffer.unit.test.ts +20 -20
- package/src/sink/session-storage-quota.ts +34 -34
- package/src/sink/session-storage-quota.unit.test.ts +8 -8
- package/src/sink/session-storage-store.ts +72 -72
- package/src/sink/session-storage.ts +48 -48
- package/src/sink/session-storage.unit.test.ts +39 -39
- package/src/sink/web-storage-quota-error.ts +22 -22
- package/src/sink/web-storage-quota-error.unit.test.ts +2 -2
- package/src/sink/web-storage-runtime.ts +24 -24
- package/src/startup.unit.test.ts +18 -18
- package/src/tagged.ts +35 -35
- package/src/tagged.unit.test.ts +8 -8
- package/src/types.ts +39 -39
|
@@ -16,55 +16,55 @@ import {
|
|
|
16
16
|
} from '@monochromatic-dev/module-logger';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
Milliseconds a slow write parks before recording, long enough that the
|
|
20
|
+
record is provably still pending when a synchronous assertion runs but the
|
|
21
|
+
draining `flush()` must wait for it.
|
|
22
22
|
*/
|
|
23
23
|
const SLOW_WRITE_MS = 25;
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
Flush deadline the deadline tests inject: short enough to keep the suite
|
|
27
|
+
fast, long enough that timer granularity cannot fire it early.
|
|
28
28
|
*/
|
|
29
29
|
const SHORT_DEADLINE_MS = 60;
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
Timer slack subtracted from the deadline when asserting a flush waited it
|
|
33
|
+
out, covering setTimeout clamping and scheduler jitter.
|
|
34
34
|
*/
|
|
35
35
|
const DEADLINE_TOLERANCE_MS = 15;
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
Upper bound on a flush that must not wait out the deadline again; well
|
|
39
|
+
under `SHORT_DEADLINE_MS` so a regression that re-waits is caught.
|
|
40
40
|
*/
|
|
41
41
|
const FAST_FLUSH_MS = 40;
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
Harness timeout for the deadline tests: a regression that hangs forever
|
|
45
|
+
fails here instead of stalling the suite.
|
|
46
46
|
*/
|
|
47
47
|
const DEADLINE_TEST_TIMEOUT_MS = 2_000;
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
Promise that never settles, standing in for a wedged sink operation.
|
|
51
|
+
|
|
52
|
+
@returns Pending promise whose resolver is unreachable.
|
|
53
53
|
*/
|
|
54
54
|
function neverSettles(): Promise<never> {
|
|
55
55
|
return Promise.withResolvers<never>().promise;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
Times one `flush()` call.
|
|
60
|
+
|
|
61
|
+
@param flush - Flush function to time.
|
|
62
|
+
|
|
63
|
+
@returns Elapsed milliseconds.
|
|
64
64
|
*/
|
|
65
65
|
async function timeFlush({ flush, }: { readonly flush: () => Promise<void>; },): Promise<number> {
|
|
66
66
|
/**
|
|
67
|
-
|
|
67
|
+
Start timestamp.
|
|
68
68
|
*/
|
|
69
69
|
const start = performance.now();
|
|
70
70
|
await flush();
|
|
@@ -72,23 +72,23 @@ async function timeFlush({ flush, }: { readonly flush: () => Promise<void>; },):
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
Structural view of a sinon stub: only the recorded calls matter here, and
|
|
76
|
+
naming the shape keeps the test free of a direct sinon type import.
|
|
77
77
|
*/
|
|
78
78
|
type RecordedCalls = {
|
|
79
79
|
readonly getCalls: () => readonly { readonly args: readonly unknown[]; }[];
|
|
80
80
|
};
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
83
|
+
Collects the console.warn messages containing `needle`. Sibling tests in
|
|
84
|
+
this file run concurrently and emit their own internal-error reports
|
|
85
|
+
through the same console, so a raw call count would be noise.
|
|
86
|
+
|
|
87
|
+
@param warn - Stubbed console.warn.
|
|
88
|
+
|
|
89
|
+
@param needle - Substring identifying the breadcrumb family.
|
|
90
|
+
|
|
91
|
+
@returns Matching messages, in call order.
|
|
92
92
|
*/
|
|
93
93
|
function breadcrumbMessages(
|
|
94
94
|
{
|
|
@@ -109,11 +109,11 @@ function breadcrumbMessages(
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
112
|
+
Collects the flush-deadline breadcrumbs, see {@link breadcrumbMessages}.
|
|
113
|
+
|
|
114
|
+
@param warn - Stubbed console.warn.
|
|
115
|
+
|
|
116
|
+
@returns Flush-deadline breadcrumb messages observed, in call order.
|
|
117
117
|
*/
|
|
118
118
|
function deadlineBreadcrumbMessages({ warn, }: { readonly warn: RecordedCalls; },): string[] {
|
|
119
119
|
return breadcrumbMessages({
|
|
@@ -123,12 +123,12 @@ function deadlineBreadcrumbMessages({ warn, }: { readonly warn: RecordedCalls; }
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
/**
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
126
|
+
Counts the sink-verification breadcrumbs (a verify that rejected, threw, or
|
|
127
|
+
ran past the verify time limit).
|
|
128
|
+
|
|
129
|
+
@param warn - Stubbed console.warn.
|
|
130
|
+
|
|
131
|
+
@returns Number of verification breadcrumbs observed.
|
|
132
132
|
*/
|
|
133
133
|
function verifyBreadcrumbs({ warn, }: { readonly warn: RecordedCalls; },): number {
|
|
134
134
|
return breadcrumbMessages({
|
|
@@ -138,11 +138,11 @@ function verifyBreadcrumbs({ warn, }: { readonly warn: RecordedCalls; },): numbe
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
/**
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
141
|
+
Builds a verifier that answers `true` after a delay.
|
|
142
|
+
|
|
143
|
+
@param delayMs - Milliseconds before the verifier resolves.
|
|
144
|
+
|
|
145
|
+
@returns Verify function resolving `true` after the delay.
|
|
146
146
|
*/
|
|
147
147
|
function verifyTrueAfter({ delayMs, }: { readonly delayMs: number; },): Verify {
|
|
148
148
|
return async function verifyLater(): Promise<boolean> {
|
|
@@ -152,20 +152,20 @@ function verifyTrueAfter({ delayMs, }: { readonly delayMs: number; },): Verify {
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
/**
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
155
|
+
Counts the flush-deadline breadcrumbs, see {@link deadlineBreadcrumbMessages}.
|
|
156
|
+
|
|
157
|
+
@param warn - Stubbed console.warn.
|
|
158
|
+
|
|
159
|
+
@returns Number of flush-deadline breadcrumbs observed.
|
|
160
160
|
*/
|
|
161
161
|
function deadlineBreadcrumbs({ warn, }: { readonly warn: RecordedCalls; },): number {
|
|
162
162
|
return deadlineBreadcrumbMessages({ warn, },).length;
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
/**
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
166
|
+
Builds a verified sink whose every write never settles.
|
|
167
|
+
|
|
168
|
+
@returns Sink standing in for a wedged backend.
|
|
169
169
|
*/
|
|
170
170
|
function wedgedWriteSink(): Sink {
|
|
171
171
|
return {
|
|
@@ -179,8 +179,8 @@ function wedgedWriteSink(): Sink {
|
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
/**
|
|
182
|
-
|
|
183
|
-
|
|
182
|
+
Recording sink plus the array it appends every written record to, so a test
|
|
183
|
+
can assert exactly which records crossed the seam.
|
|
184
184
|
*/
|
|
185
185
|
type RecordingSink = {
|
|
186
186
|
readonly records: LogRecord[];
|
|
@@ -188,18 +188,18 @@ type RecordingSink = {
|
|
|
188
188
|
};
|
|
189
189
|
|
|
190
190
|
/**
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
191
|
+
Builds a fake sink that records every record it receives. The seam under
|
|
192
|
+
test is `Sink`, so the whole orchestration (verify, replay, fan-out, flush)
|
|
193
|
+
is exercised through one self-contained adapter with no globals to reset.
|
|
194
|
+
|
|
195
|
+
@param verify - Backend availability check; defaults to synchronously available.
|
|
196
|
+
|
|
197
|
+
@param flush - Optional flush hook the logger should drain.
|
|
198
|
+
|
|
199
|
+
@param writeDelayMs - Milliseconds each write parks before recording, to
|
|
200
|
+
keep a record pending across a `flush()`.
|
|
201
|
+
|
|
202
|
+
@returns Sink adapter paired with its recorded-record array.
|
|
203
203
|
*/
|
|
204
204
|
function recordingSink(
|
|
205
205
|
{
|
|
@@ -215,14 +215,14 @@ function recordingSink(
|
|
|
215
215
|
} = {},
|
|
216
216
|
): RecordingSink {
|
|
217
217
|
/**
|
|
218
|
-
|
|
218
|
+
Records this sink has received, in arrival order.
|
|
219
219
|
*/
|
|
220
220
|
const records: LogRecord[] = [];
|
|
221
221
|
|
|
222
222
|
/**
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
223
|
+
Records every received record after the optional delay.
|
|
224
|
+
|
|
225
|
+
@param record - Record handed to the sink.
|
|
226
226
|
*/
|
|
227
227
|
async function write(record: LogRecord,): Promise<void> {
|
|
228
228
|
if (writeDelayMs > 0)
|
|
@@ -244,11 +244,11 @@ function recordingSink(
|
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
/**
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
247
|
+
Maps recorded records down to their messages for concise assertions.
|
|
248
|
+
|
|
249
|
+
@param recording - Recording sink whose messages to read.
|
|
250
|
+
|
|
251
|
+
@returns Messages in arrival order.
|
|
252
252
|
*/
|
|
253
253
|
function messages({ recording, }: { readonly recording: RecordingSink; },): string[] {
|
|
254
254
|
return recording.records
|
|
@@ -258,38 +258,38 @@ function messages({ recording, }: { readonly recording: RecordingSink; },): stri
|
|
|
258
258
|
}
|
|
259
259
|
|
|
260
260
|
/**
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
261
|
+
Recording sink whose first write parks on a promise the test settles, so a
|
|
262
|
+
`flush()` can abandon that write at the deadline and the test can then
|
|
263
|
+
settle it late, after the logger has stopped tracking it.
|
|
264
264
|
*/
|
|
265
265
|
type ParkedWriteSink = RecordingSink & {
|
|
266
266
|
readonly settleParked: PromiseWithResolvers<void>;
|
|
267
267
|
};
|
|
268
268
|
|
|
269
269
|
/**
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
270
|
+
Builds a verified sink whose first write waits on `settleParked` and whose
|
|
271
|
+
later writes record immediately.
|
|
272
|
+
|
|
273
|
+
@returns Sink, its record array, and the resolvers for the parked write.
|
|
274
274
|
*/
|
|
275
275
|
function parkedFirstWriteSink(): ParkedWriteSink {
|
|
276
276
|
/**
|
|
277
|
-
|
|
277
|
+
Records this sink has received, in arrival order.
|
|
278
278
|
*/
|
|
279
279
|
const records: LogRecord[] = [];
|
|
280
280
|
/**
|
|
281
|
-
|
|
281
|
+
Resolvers the test uses to settle the parked write.
|
|
282
282
|
*/
|
|
283
283
|
const settleParked = Promise.withResolvers<void>();
|
|
284
284
|
/**
|
|
285
|
-
|
|
285
|
+
Whether the parked write has been handed out; only the first write parks.
|
|
286
286
|
*/
|
|
287
287
|
const handedOut = { parked: false, };
|
|
288
288
|
|
|
289
289
|
/**
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
290
|
+
Parks the first write on `settleParked`, records every later one.
|
|
291
|
+
|
|
292
|
+
@param record - Record handed to the sink.
|
|
293
293
|
*/
|
|
294
294
|
async function write(record: LogRecord,): Promise<void> {
|
|
295
295
|
if (!handedOut.parked) {
|
|
@@ -312,8 +312,8 @@ function parkedFirstWriteSink(): ParkedWriteSink {
|
|
|
312
312
|
}
|
|
313
313
|
|
|
314
314
|
/**
|
|
315
|
-
|
|
316
|
-
|
|
315
|
+
Unhandled-rejection reasons observed while the capture is alive, plus the
|
|
316
|
+
disposer that detaches the listener.
|
|
317
317
|
*/
|
|
318
318
|
type UnhandledCapture = {
|
|
319
319
|
readonly reasons: readonly unknown[];
|
|
@@ -321,22 +321,22 @@ type UnhandledCapture = {
|
|
|
321
321
|
};
|
|
322
322
|
|
|
323
323
|
/**
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
324
|
+
Listens for `unhandledRejection` on the process until disposed. A listener
|
|
325
|
+
also stops Node from treating the rejection as fatal, so the assertion on
|
|
326
|
+
`reasons` is what enforces the property.
|
|
327
|
+
|
|
328
|
+
@returns Capture whose `reasons` grows with every unhandled rejection.
|
|
329
329
|
*/
|
|
330
330
|
function captureUnhandledRejections(): UnhandledCapture {
|
|
331
331
|
/**
|
|
332
|
-
|
|
332
|
+
Reasons observed so far.
|
|
333
333
|
*/
|
|
334
334
|
const reasons: unknown[] = [];
|
|
335
335
|
|
|
336
336
|
/**
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
337
|
+
Listener appended to the process.
|
|
338
|
+
|
|
339
|
+
@param reason - Rejection reason Node reports.
|
|
340
340
|
*/
|
|
341
341
|
function record(reason: unknown,): void {
|
|
342
342
|
reasons.push(reason,);
|
|
@@ -517,8 +517,8 @@ await describe({
|
|
|
517
517
|
name: 'a rejecting write does not retire the sink',
|
|
518
518
|
fn: async () => {
|
|
519
519
|
/**
|
|
520
|
-
|
|
521
|
-
|
|
520
|
+
Write-attempt counter; a retired sink would stop receiving writes,
|
|
521
|
+
so a second attempt proves the rejection left the backend available.
|
|
522
522
|
*/
|
|
523
523
|
const counters: { attempts: number; } = { attempts: 0, };
|
|
524
524
|
const flaky: Sink = {
|
|
@@ -572,7 +572,7 @@ await describe({
|
|
|
572
572
|
name: 'flush runs every available sink flush hook',
|
|
573
573
|
fn: async () => {
|
|
574
574
|
/**
|
|
575
|
-
|
|
575
|
+
Hook-invocation counter proving `flush()` reached the sink's own hook.
|
|
576
576
|
*/
|
|
577
577
|
const counters: { flushes: number; } = { flushes: 0, };
|
|
578
578
|
const hooked = recordingSink({
|
|
@@ -661,8 +661,8 @@ await describe({
|
|
|
661
661
|
name: 'a synchronously-throwing write does not retire the sink',
|
|
662
662
|
fn: async () => {
|
|
663
663
|
/**
|
|
664
|
-
|
|
665
|
-
|
|
664
|
+
Write-attempt counter; a retired sink would stop receiving writes, so
|
|
665
|
+
a second attempt proves the synchronous throw left it available.
|
|
666
666
|
*/
|
|
667
667
|
const counters: { attempts: number; } = { attempts: 0, };
|
|
668
668
|
const flaky: Sink = {
|
|
@@ -702,8 +702,8 @@ await describe({
|
|
|
702
702
|
name: 'does not run the flush hook of a sink that failed verification',
|
|
703
703
|
fn: async () => {
|
|
704
704
|
/**
|
|
705
|
-
|
|
706
|
-
|
|
705
|
+
Flush-hook counter; stays zero because an unavailable sink's hook
|
|
706
|
+
must be skipped by `flushAll`.
|
|
707
707
|
*/
|
|
708
708
|
const counters: { flushes: number; } = { flushes: 0, };
|
|
709
709
|
const off = recordingSink({
|
|
@@ -1150,12 +1150,12 @@ await describe({
|
|
|
1150
1150
|
timeout: DEADLINE_TEST_TIMEOUT_MS,
|
|
1151
1151
|
fn: async () => {
|
|
1152
1152
|
/**
|
|
1153
|
-
|
|
1154
|
-
|
|
1153
|
+
Records logged before the sink verifies: the cap plus a few extra
|
|
1154
|
+
that must push the oldest ones out.
|
|
1155
1155
|
*/
|
|
1156
1156
|
const extra = 3;
|
|
1157
1157
|
/**
|
|
1158
|
-
|
|
1158
|
+
Total records in the burst; the last one is index `burstSize - 1`.
|
|
1159
1159
|
*/
|
|
1160
1160
|
const burstSize = STARTUP_BUFFER_CAP + extra;
|
|
1161
1161
|
const late = recordingSink({ verify: verifyTrueAfter({ delayMs: SLOW_WRITE_MS, },), },);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { createConsoleSink, } from './sink/console.ts';
|
|
2
|
+
import { createIndexedDbSink, } from './sink/indexed-db.ts';
|
|
3
|
+
import { createLocalStorageSink, } from './sink/local-storage.ts';
|
|
4
|
+
import { createSessionStorageSink, } from './sink/session-storage.ts';
|
|
5
|
+
|
|
6
|
+
import type { Sink, } from './types.ts';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
Builds the default sink backends the platform-neutral artifact attempts, in
|
|
10
|
+
priority order. Chosen at bundle time: `package.json` maps `#default-sinks`
|
|
11
|
+
to this module under the `default` condition, so every non-Node resolution
|
|
12
|
+
(browsers, Deno, Bun, workers) inlines this list without a runtime
|
|
13
|
+
platform probe. Called on the default logger's first use, never at import,
|
|
14
|
+
so a runtime that forbids timers and I/O in global scope (Cloudflare
|
|
15
|
+
Workers, issue #493) constructs and verifies its sinks inside the handler
|
|
16
|
+
that logs first. Each runtime keeps only the sinks whose `verify` confirms
|
|
17
|
+
platform probe. Each runtime keeps only the sinks whose `verify` confirms
|
|
18
|
+
its backend: {@link createConsoleSink} everywhere,
|
|
19
|
+
{@link createIndexedDbSink} in browsers, {@link createSessionStorageSink}
|
|
20
|
+
wherever web storage round-trips (browsers, Deno),
|
|
21
|
+
{@link createLocalStorageSink} wherever `localStorage` round-trips
|
|
22
|
+
(browsers, Deno). The noop sink is intentionally absent: the console sink
|
|
23
|
+
verifies wherever `console` and `queueMicrotask` exist, so the default
|
|
24
|
+
logger has a backend in every supported runtime, and a custom
|
|
25
|
+
`createLogger` whose sinks all fail verification surfaces the "No logging
|
|
26
|
+
backends available" error instead of silently discarding. The file sink
|
|
27
|
+
is absent because its static `node:fs/promises` import cannot load outside
|
|
28
|
+
Node; it ships through the `./node` subpath and the Node default list
|
|
29
|
+
instead. The OPFS sink is exported from `./browser` but not a default:
|
|
30
|
+
its stream stages writes until a close that a crash never performs, so
|
|
31
|
+
IndexedDB holds the persistent-browser slot; see `DECISIONS.md`.
|
|
32
|
+
|
|
33
|
+
@returns Fresh default sinks in priority order.
|
|
34
|
+
|
|
35
|
+
@example
|
|
36
|
+
```ts
|
|
37
|
+
const { logger } = createLogger({ sinks: createDefaultSinks() });
|
|
38
|
+
```
|
|
39
|
+
*/
|
|
40
|
+
export function createDefaultSinks(): readonly Sink[] {
|
|
41
|
+
return [
|
|
42
|
+
createConsoleSink(),
|
|
43
|
+
createIndexedDbSink(),
|
|
44
|
+
createSessionStorageSink(),
|
|
45
|
+
createLocalStorageSink(),
|
|
46
|
+
];
|
|
47
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createConsoleSink, } from './sink/console.ts';
|
|
2
|
+
import { createFileSink, } from './sink/file.ts';
|
|
3
|
+
import { createLocalStorageSink, } from './sink/local-storage.ts';
|
|
4
|
+
import { createSessionStorageSink, } from './sink/session-storage.ts';
|
|
5
|
+
|
|
6
|
+
import type { Sink, } from './types.ts';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
Builds the default sink backends the Node artifact attempts, in priority
|
|
10
|
+
order. Chosen at bundle time: `package.json` maps `#default-sinks` to this
|
|
11
|
+
module under the `node` condition, so `logger.ts` inlines this list without
|
|
12
|
+
a runtime platform probe. Called on the default logger's first use, never
|
|
13
|
+
at import, so no sink is constructed in global scope (issue #493). Each
|
|
14
|
+
runtime keeps only the sinks whose `verify`
|
|
15
|
+
confirms its backend: {@link createConsoleSink} everywhere,
|
|
16
|
+
{@link createSessionStorageSink} wherever web storage round-trips (Node
|
|
17
|
+
22+, Deno), {@link createLocalStorageSink} wherever `localStorage`
|
|
18
|
+
round-trips (Deno, Node launched with `--localstorage-file`), and
|
|
19
|
+
{@link createFileSink} wherever an ancestor `node_modules` exists. The
|
|
20
|
+
noop sink is intentionally absent: the console sink verifies wherever
|
|
21
|
+
`console` and `queueMicrotask` exist, so the default logger has a backend
|
|
22
|
+
in every supported runtime, and a custom `createLogger` whose sinks all
|
|
23
|
+
fail verification surfaces the "No logging backends available" error
|
|
24
|
+
instead of silently discarding. The IndexedDB and OPFS sinks are absent
|
|
25
|
+
because Node exposes neither `indexedDB` nor `navigator.storage`; they
|
|
26
|
+
ship through the `./browser` subpath instead, keeping their probes and
|
|
27
|
+
code out of this artifact.
|
|
28
|
+
|
|
29
|
+
@returns Fresh default sinks in priority order.
|
|
30
|
+
|
|
31
|
+
@example
|
|
32
|
+
```ts
|
|
33
|
+
const { logger } = createLogger({ sinks: createDefaultSinks() });
|
|
34
|
+
```
|
|
35
|
+
*/
|
|
36
|
+
export function createDefaultSinks(): readonly Sink[] {
|
|
37
|
+
return [
|
|
38
|
+
createConsoleSink(),
|
|
39
|
+
createSessionStorageSink(),
|
|
40
|
+
createLocalStorageSink(),
|
|
41
|
+
createFileSink(),
|
|
42
|
+
];
|
|
43
|
+
}
|
package/src/error-format.ts
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
Internal logger error reporting helpers.
|
|
3
|
+
|
|
4
|
+
Logger internals cannot report failures through the logger itself without
|
|
5
|
+
risking recursion, so these helpers format caught values and write directly
|
|
6
|
+
to the host console.
|
|
7
|
+
|
|
8
|
+
@module
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { caughtValueText, } from '@monochromatic-dev/module-caught-value/ts';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
14
|
+
Reports a logger-internal caught value without going back through logger
|
|
15
|
+
sinks, formatting it via {@link caughtValueText}.
|
|
16
|
+
|
|
17
|
+
@param context - Human-readable operation that caught the value.
|
|
18
|
+
|
|
19
|
+
@param error - Caught value to include in the diagnostic.
|
|
20
|
+
|
|
21
|
+
@mutates error - `caughtValueText` may invoke string-conversion hooks.
|
|
22
|
+
|
|
23
|
+
@example
|
|
24
|
+
```ts
|
|
25
|
+
reportLoggerInternalError({
|
|
26
|
+
context: 'console sink verify failed',
|
|
27
|
+
error: new Error('blocked'),
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
30
|
*/
|
|
31
31
|
export function reportLoggerInternalError(
|
|
32
32
|
{
|
package/src/index.ts
CHANGED
|
@@ -4,10 +4,7 @@ export {
|
|
|
4
4
|
DEFAULT_VERIFY_TIMEOUT_MS,
|
|
5
5
|
STARTUP_BUFFER_CAP,
|
|
6
6
|
} from './create-logger.ts';
|
|
7
|
-
export {
|
|
8
|
-
initPromise,
|
|
9
|
-
logger,
|
|
10
|
-
} from './logger.ts';
|
|
7
|
+
export { logger, } from './logger.ts';
|
|
11
8
|
export * as sinks from './sink/index.ts';
|
|
12
9
|
export { tagged, } from './tagged.ts';
|
|
13
10
|
|