@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.
Files changed (64) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +53 -13
  3. package/dist/final/neutral/browser.d.mts +60 -0
  4. package/dist/final/neutral/browser.mjs +1 -0
  5. package/dist/final/neutral/index.d.mts +359 -596
  6. package/dist/final/neutral/index.mjs +2 -3
  7. package/dist/final/neutral/indexed-db-hsIfv7Cv.mjs +2 -0
  8. package/dist/final/neutral/types-BkkBXgY3.d.mts +76 -0
  9. package/dist/final/node/file-CRGb1hDK.mjs +1 -0
  10. package/dist/final/node/index.d.mts +359 -596
  11. package/dist/final/node/index.mjs +3 -3
  12. package/dist/final/node/node.d.mts +103 -0
  13. package/dist/final/node/node.mjs +1 -0
  14. package/dist/final/node/types-BkkBXgY3.d.mts +76 -0
  15. package/package.json +19 -4
  16. package/src/artifact-platform-split.unit.test.ts +140 -0
  17. package/src/browser.ts +14 -0
  18. package/src/create-logger.ts +183 -183
  19. package/src/create-logger.unit.test.ts +112 -112
  20. package/src/default-sinks.neutral.ts +47 -0
  21. package/src/default-sinks.node.ts +43 -0
  22. package/src/error-format.ts +23 -23
  23. package/src/index.ts +1 -4
  24. package/src/logger.ts +76 -51
  25. package/src/node.ts +23 -0
  26. package/src/restricted-global-scope.unit.test.ts +101 -0
  27. package/src/sink/console-control-chars.ts +64 -64
  28. package/src/sink/console-control-chars.unit.test.ts +14 -14
  29. package/src/sink/console.ts +194 -194
  30. package/src/sink/console.unit.test.ts +18 -18
  31. package/src/sink/file.ts +136 -140
  32. package/src/sink/file.unit.test.ts +19 -26
  33. package/src/sink/index.ts +4 -7
  34. package/src/sink/indexed-db-util.ts +42 -42
  35. package/src/sink/indexed-db.browser.test.ts +7 -7
  36. package/src/sink/indexed-db.ts +109 -109
  37. package/src/sink/indexed-db.unit.test.ts +5 -13
  38. package/src/sink/local-storage-key.ts +73 -73
  39. package/src/sink/local-storage-key.unit.test.ts +8 -8
  40. package/src/sink/local-storage-quota.ts +37 -37
  41. package/src/sink/local-storage-quota.unit.test.ts +8 -8
  42. package/src/sink/local-storage-store.ts +113 -113
  43. package/src/sink/local-storage-store.unit.test.ts +35 -35
  44. package/src/sink/local-storage.ts +72 -72
  45. package/src/sink/local-storage.unit.test.ts +27 -27
  46. package/src/sink/noop.ts +20 -20
  47. package/src/sink/noop.unit.test.ts +1 -1
  48. package/src/sink/opfs.browser.test.ts +7 -7
  49. package/src/sink/opfs.ts +62 -62
  50. package/src/sink/opfs.unit.test.ts +5 -13
  51. package/src/sink/record-buffer.ts +84 -84
  52. package/src/sink/record-buffer.unit.test.ts +20 -20
  53. package/src/sink/session-storage-quota.ts +34 -34
  54. package/src/sink/session-storage-quota.unit.test.ts +8 -8
  55. package/src/sink/session-storage-store.ts +72 -72
  56. package/src/sink/session-storage.ts +48 -48
  57. package/src/sink/session-storage.unit.test.ts +39 -39
  58. package/src/sink/web-storage-quota-error.ts +22 -22
  59. package/src/sink/web-storage-quota-error.unit.test.ts +2 -2
  60. package/src/sink/web-storage-runtime.ts +24 -24
  61. package/src/startup.unit.test.ts +18 -18
  62. package/src/tagged.ts +35 -35
  63. package/src/tagged.unit.test.ts +8 -8
  64. package/src/types.ts +39 -39
@@ -1,16 +1,16 @@
1
1
  /**
2
- * Per-runtime default sessionStorage quota heuristics.
3
- *
4
- * The Web Storage API exposes no way to read the sessionStorage quota (unlike
5
- * `navigator.storage.estimate()`, which reports the unrelated persistent-storage
6
- * budget), so the sink caps its own footprint from a table of measured
7
- * defaults. Each figure was fill-probed on a fresh store: values are written at
8
- * a growing single key until a `QuotaExceededError`, binary-searching the
9
- * largest that fits. Figures are UTF-16 code units (JS string length, counting
10
- * key plus value) because that is what sessionStorage measures and what the
11
- * sink compares `serialized.length` against.
12
- *
13
- * @module
2
+ Per-runtime default sessionStorage quota heuristics.
3
+
4
+ The Web Storage API exposes no way to read the sessionStorage quota (unlike
5
+ `navigator.storage.estimate()`, which reports the unrelated persistent-storage
6
+ budget), so the sink caps its own footprint from a table of measured
7
+ defaults. Each figure was fill-probed on a fresh store: values are written at
8
+ a growing single key until a `QuotaExceededError`, binary-searching the
9
+ largest that fits. Figures are UTF-16 code units (JS string length, counting
10
+ key plus value) because that is what sessionStorage measures and what the
11
+ sink compares `serialized.length` against.
12
+
13
+ @module
14
14
  */
15
15
 
16
16
  import {
@@ -19,18 +19,18 @@ import {
19
19
  } from './web-storage-runtime.ts';
20
20
 
21
21
  /**
22
- * Measured default per-origin sessionStorage quotas, in UTF-16 code units, one
23
- * bucket per detectable runtime:
24
- *
25
- * - `deno`: 10 MiB on Deno 2.9.
26
- * - `node`: 5 MiB on Node 26.
27
- * - `browser`: 5 MiB, measured identical on Chromium, Firefox, and WebKit under
28
- * Playwright v1.61, so the three engines share one bucket and no fragile
29
- * user-agent sniffing is needed to tell them apart.
30
- *
31
- * Bun 1.3 exposes no `sessionStorage`, so its bucket is uncapped: its sink
32
- * never verifies and never reaches the cap. An unrecognized runtime is also
33
- * uncapped so the caller relies on reactive eviction alone.
22
+ Measured default per-origin sessionStorage quotas, in UTF-16 code units, one
23
+ bucket per detectable runtime:
24
+
25
+ - `deno`: 10 MiB on Deno 2.9.
26
+ - `node`: 5 MiB on Node 26.
27
+ - `browser`: 5 MiB, measured identical on Chromium, Firefox, and WebKit under
28
+ Playwright v1.61, so the three engines share one bucket and no fragile
29
+ user-agent sniffing is needed to tell them apart.
30
+
31
+ Bun 1.3 exposes no `sessionStorage`, so its bucket is uncapped: its sink
32
+ never verifies and never reaches the cap. An unrecognized runtime is also
33
+ uncapped so the caller relies on reactive eviction alone.
34
34
  */
35
35
  const RUNTIME_QUOTA_CHARS: Record<WebStorageRuntime, number> = {
36
36
  browser: 5_242_880,
@@ -41,16 +41,16 @@ const RUNTIME_QUOTA_CHARS: Record<WebStorageRuntime, number> = {
41
41
  };
42
42
 
43
43
  /**
44
- * Detects the current runtime's default sessionStorage quota in UTF-16 code
45
- * units, or `Number.POSITIVE_INFINITY` when the runtime is unrecognized so the
46
- * caller leaves its footprint uncapped and relies on reactive eviction alone.
47
- *
48
- * @returns Total quota in code units, or `Number.POSITIVE_INFINITY` if unknown.
49
- *
50
- * @example
51
- * ```ts
52
- * const capChars = detectSessionStorageQuotaChars() / 2; // half the total
53
- * ```
44
+ Detects the current runtime's default sessionStorage quota in UTF-16 code
45
+ units, or `Number.POSITIVE_INFINITY` when the runtime is unrecognized so the
46
+ caller leaves its footprint uncapped and relies on reactive eviction alone.
47
+
48
+ @returns Total quota in code units, or `Number.POSITIVE_INFINITY` if unknown.
49
+
50
+ @example
51
+ ```ts
52
+ const capChars = detectSessionStorageQuotaChars() / 2; // half the total
53
+ ```
54
54
  */
55
55
  export function detectSessionStorageQuotaChars(): number {
56
56
  return RUNTIME_QUOTA_CHARS[detectWebStorageRuntime()];
@@ -8,14 +8,14 @@ import {
8
8
  } from '@monochromatic-dev/module-logger';
9
9
 
10
10
  /**
11
- * Temporarily sets `globalThis` keys to the supplied values, restoring each to
12
- * its prior value (or deleting keys that were absent) when the returned guard
13
- * leaves `using` scope, so a runtime-detection test can impersonate Deno, Bun,
14
- * or a browser without leaking the fake globals into later tests.
15
- *
16
- * @param overrides - Global keys to install for the duration of the scope.
17
- *
18
- * @returns Disposable that restores the original globals on exit.
11
+ Temporarily sets `globalThis` keys to the supplied values, restoring each to
12
+ its prior value (or deleting keys that were absent) when the returned guard
13
+ leaves `using` scope, so a runtime-detection test can impersonate Deno, Bun,
14
+ or a browser without leaking the fake globals into later tests.
15
+
16
+ @param overrides - Global keys to install for the duration of the scope.
17
+
18
+ @returns Disposable that restores the original globals on exit.
19
19
  */
20
20
  function withGlobalOverrides(overrides: Record<string, unknown>,): Disposable {
21
21
  const host = globalThis as unknown as Record<string, unknown>;
@@ -3,61 +3,61 @@ import { detectSessionStorageQuotaChars, } from './session-storage-quota.ts';
3
3
  import { isQuotaExceededError, } from './web-storage-quota-error.ts';
4
4
 
5
5
  /**
6
- * Prefix for sessionStorage keys to namespace log entries.
6
+ Prefix for sessionStorage keys to namespace log entries.
7
7
  */
8
8
  const STORAGE_KEY_PREFIX = 'monochromatic.log';
9
9
 
10
10
  /**
11
- * Builds the namespaced sessionStorage key for a log entry at `index`.
12
- *
13
- * @param index - Zero-based slot number of an entry.
14
- *
15
- * @returns Prefixed key such as `monochromatic.log.3`.
16
- *
17
- * @example
18
- * ```ts
19
- * storageKey(3); // 'monochromatic.log.3'
20
- * ```
11
+ Builds the namespaced sessionStorage key for a log entry at `index`.
12
+
13
+ @param index - Zero-based slot number of an entry.
14
+
15
+ @returns Prefixed key such as `monochromatic.log.3`.
16
+
17
+ @example
18
+ ```ts
19
+ storageKey(3); // 'monochromatic.log.3'
20
+ ```
21
21
  */
22
22
  function storageKey(index: number,): string {
23
23
  return `${STORAGE_KEY_PREFIX}.${index}`;
24
24
  }
25
25
 
26
26
  /**
27
- * Builds the persistence engine behind the sessionStorage sink: each `persist`
28
- * lands one already-serialized batch under a counter-incremented key, with
29
- * proactive and reactive quota eviction. The counter lives in this instance's
30
- * closure (no module-global state), so independent sinks and tests never share
31
- * keys or need a reset hook.
32
- *
33
- * The engine caps its own footprint at half the runtime's sessionStorage
34
- * quota, proactively dropping its oldest entries, and reactively drops them
35
- * again if the real store overflows; see {@link createSessionStorageStore.persist}.
36
- *
37
- * @returns Engine exposing `persist` for one batch value per call.
38
- *
39
- * @example
40
- * ```ts
41
- * const store = createSessionStorageStore();
42
- * store.persist('{"level":"info","message":"hi","timestamp":0}');
43
- * ```
27
+ Builds the persistence engine behind the sessionStorage sink: each `persist`
28
+ lands one already-serialized batch under a counter-incremented key, with
29
+ proactive and reactive quota eviction. The counter lives in this instance's
30
+ closure (no module-global state), so independent sinks and tests never share
31
+ keys or need a reset hook.
32
+
33
+ The engine caps its own footprint at half the runtime's sessionStorage
34
+ quota, proactively dropping its oldest entries, and reactively drops them
35
+ again if the real store overflows; see {@link createSessionStorageStore.persist}.
36
+
37
+ @returns Engine exposing `persist` for one batch value per call.
38
+
39
+ @example
40
+ ```ts
41
+ const store = createSessionStorageStore();
42
+ store.persist('{"level":"info","message":"hi","timestamp":0}');
43
+ ```
44
44
  */
45
45
  export function createSessionStorageStore(): { readonly persist: (batch: string,) => void; } {
46
46
  /**
47
- * Instance-local write cursor, eviction watermark, and footprint tally.
48
- * `lineCounter` is the next slot to write and advances only when a `setItem`
49
- * actually lands, so this engine's present entries occupy the contiguous
50
- * range `[oldestIndex, lineCounter)`. `oldestIndex` is the lowest slot the
51
- * engine still owns; eviction removes that entry and climbs `oldestIndex`
52
- * toward `lineCounter`, so `oldestIndex < lineCounter` doubles as the "a
53
- * prior write succeeded and an owned entry remains" guard that keeps
54
- * eviction from ever touching another origin consumer's keys. `usedChars`
55
- * tracks the code units this engine currently occupies so the half-quota cap
56
- * needs no re-summing. `reportedFailure` gates the give-up diagnostic to
57
- * once per failure episode: a persistently full store (another writer owning
58
- * the space) would otherwise emit one `console.warn` per batch, so the flag
59
- * stays set until a write next lands, which re-arms a single report for the
60
- * next episode.
47
+ Instance-local write cursor, eviction watermark, and footprint tally.
48
+ `lineCounter` is the next slot to write and advances only when a `setItem`
49
+ actually lands, so this engine's present entries occupy the contiguous
50
+ range `[oldestIndex, lineCounter)`. `oldestIndex` is the lowest slot the
51
+ engine still owns; eviction removes that entry and climbs `oldestIndex`
52
+ toward `lineCounter`, so `oldestIndex < lineCounter` doubles as the "a
53
+ prior write succeeded and an owned entry remains" guard that keeps
54
+ eviction from ever touching another origin consumer's keys. `usedChars`
55
+ tracks the code units this engine currently occupies so the half-quota cap
56
+ needs no re-summing. `reportedFailure` gates the give-up diagnostic to
57
+ once per failure episode: a persistently full store (another writer owning
58
+ the space) would otherwise emit one `console.warn` per batch, so the flag
59
+ stays set until a write next lands, which re-arms a single report for the
60
+ next episode.
61
61
  */
62
62
  const state: {
63
63
  lineCounter: number;
@@ -72,27 +72,27 @@ export function createSessionStorageStore(): { readonly persist: (batch: string,
72
72
  };
73
73
 
74
74
  /**
75
- * Half the detected runtime sessionStorage quota, in UTF-16 code units, or
76
- * `Number.POSITIVE_INFINITY` on an unrecognized runtime. The engine keeps
77
- * its own footprint at or below this so the logger never claims more than
78
- * half the store, leaving the rest for the host application. An infinite cap
79
- * disables the proactive check, leaving only reactive quota-error eviction.
75
+ Half the detected runtime sessionStorage quota, in UTF-16 code units, or
76
+ `Number.POSITIVE_INFINITY` on an unrecognized runtime. The engine keeps
77
+ its own footprint at or below this so the logger never claims more than
78
+ half the store, leaving the rest for the host application. An infinite cap
79
+ disables the proactive check, leaving only reactive quota-error eviction.
80
80
  */
81
81
  const capChars = detectSessionStorageQuotaChars() / 2;
82
82
 
83
83
  /**
84
- * Removes this engine's oldest still-present entry, advancing the watermark
85
- * and subtracting the reclaimed entry's code units from the running
86
- * footprint. Reading the value back before removal keeps `usedChars` honest
87
- * even if the entry drifted from what was written.
84
+ Removes this engine's oldest still-present entry, advancing the watermark
85
+ and subtracting the reclaimed entry's code units from the running
86
+ footprint. Reading the value back before removal keeps `usedChars` honest
87
+ even if the entry drifted from what was written.
88
88
  */
89
89
  function evictOldest(): void {
90
90
  /**
91
- * Key of the oldest owned entry, removed to reclaim its slot and its space.
91
+ Key of the oldest owned entry, removed to reclaim its slot and its space.
92
92
  */
93
93
  const key = storageKey(state.oldestIndex,);
94
94
  /**
95
- * Value being evicted, read back so its length can leave the footprint tally.
95
+ Value being evicted, read back so its length can leave the footprint tally.
96
96
  */
97
97
  const evicted = globalThis.sessionStorage
98
98
  .getItem(key,);
@@ -107,26 +107,26 @@ export function createSessionStorageStore(): { readonly persist: (batch: string,
107
107
  }
108
108
 
109
109
  /**
110
- * Persists one serialized batch to sessionStorage under a
111
- * counter-incremented key.
112
- *
113
- * First it proactively drops its own oldest entries so its footprint stays
114
- * at or below half the runtime's sessionStorage quota, leaving the rest for
115
- * the host application. It then writes, and on a quota overflow (the store
116
- * being fuller than the cap accounts for), and only while an owned entry
117
- * remains (so the reclaimed keys are its own, never another origin
118
- * consumer's), it drops its oldest still-present entry and retries until the
119
- * batch fits or nothing of its own remains to drop. A batch larger than the
120
- * whole quota therefore evicts every owned entry, then reports and gives up
121
- * rather than looping forever. A non-quota failure is reported without any
122
- * eviction. The sink only persists after verification, so no availability
123
- * guard is needed here.
124
- *
125
- * @param batch - Serialized JSONL batch to persist.
110
+ Persists one serialized batch to sessionStorage under a
111
+ counter-incremented key.
112
+
113
+ First it proactively drops its own oldest entries so its footprint stays
114
+ at or below half the runtime's sessionStorage quota, leaving the rest for
115
+ the host application. It then writes, and on a quota overflow (the store
116
+ being fuller than the cap accounts for), and only while an owned entry
117
+ remains (so the reclaimed keys are its own, never another origin
118
+ consumer's), it drops its oldest still-present entry and retries until the
119
+ batch fits or nothing of its own remains to drop. A batch larger than the
120
+ whole quota therefore evicts every owned entry, then reports and gives up
121
+ rather than looping forever. A non-quota failure is reported without any
122
+ eviction. The sink only persists after verification, so no availability
123
+ guard is needed here.
124
+
125
+ @param batch - Serialized JSONL batch to persist.
126
126
  */
127
127
  function persist(batch: string,): void {
128
128
  /**
129
- * Code units this batch adds; the key's length is left out as a negligible near-constant.
129
+ Code units this batch adds; the key's length is left out as a negligible near-constant.
130
130
  */
131
131
  const batchChars = batch.length;
132
132
 
@@ -138,8 +138,8 @@ export function createSessionStorageStore(): { readonly persist: (batch: string,
138
138
  }
139
139
 
140
140
  /**
141
- * Write-attempt bound: one try for each entry still available to evict,
142
- * followed by one final try after every owned entry has been removed.
141
+ Write-attempt bound: one try for each entry still available to evict,
142
+ followed by one final try after every owned entry has been removed.
143
143
  */
144
144
  const maxWriteAttempts = (state.lineCounter - state.oldestIndex) + 1;
145
145
  for (let writeAttempt = 0; writeAttempt < maxWriteAttempts; writeAttempt++) {
@@ -8,32 +8,32 @@ import type {
8
8
  } from '../types.ts';
9
9
 
10
10
  /**
11
- * Verifies sessionStorage actually persists data. Stateless: the logger calls
12
- * this once per sink at startup and owns the resulting availability, so no
13
- * verified/available flag is kept here.
14
- *
15
- * Election is by probe alone: any runtime whose `sessionStorage` round-trips
16
- * (browsers, Node 22+, Deno) keeps the sink, and the buffered write path
17
- * keeps the per-record cost acceptable everywhere rather than a runtime brand
18
- * check deciding who may log here.
19
- *
20
- * @returns Whether sessionStorage is available and round-trips a probe write.
21
- *
22
- * @example
23
- * ```ts
24
- * if (await verifySessionStorage()) {
25
- * // sessionStorage usable
26
- * }
27
- * ```
11
+ Verifies sessionStorage actually persists data. Stateless: the logger calls
12
+ this once per sink at startup and owns the resulting availability, so no
13
+ verified/available flag is kept here.
14
+
15
+ Election is by probe alone: any runtime whose `sessionStorage` round-trips
16
+ (browsers, Node 22+, Deno) keeps the sink, and the buffered write path
17
+ keeps the per-record cost acceptable everywhere rather than a runtime brand
18
+ check deciding who may log here.
19
+
20
+ @returns Whether sessionStorage is available and round-trips a probe write.
21
+
22
+ @example
23
+ ```ts
24
+ if (await verifySessionStorage()) {
25
+ // sessionStorage usable
26
+ }
27
+ ```
28
28
  */
29
29
  function verifySessionStorage(): Promise<boolean> {
30
30
  try {
31
31
  /**
32
- * Sentinel key used only for the probe write/read; removed afterward to avoid polluting real log entries.
32
+ Sentinel key used only for the probe write/read; removed afterward to avoid polluting real log entries.
33
33
  */
34
34
  const testKey = '__monochromatic_verify__';
35
35
  /**
36
- * Timestamp-based probe value so concurrent verifications never read each other's writes.
36
+ Timestamp-based probe value so concurrent verifications never read each other's writes.
37
37
  */
38
38
  const testValue = `test-${Date.now()}`;
39
39
  globalThis.sessionStorage
@@ -42,7 +42,7 @@ function verifySessionStorage(): Promise<boolean> {
42
42
  testValue,
43
43
  );
44
44
  /**
45
- * Probe value read back from storage; equality with `testValue` proves writes actually persist.
45
+ Probe value read back from storage; equality with `testValue` proves writes actually persist.
46
46
  */
47
47
  const readBack = globalThis.sessionStorage
48
48
  .getItem(testKey,);
@@ -61,43 +61,43 @@ function verifySessionStorage(): Promise<boolean> {
61
61
  }
62
62
 
63
63
  /**
64
- * Builds a sessionStorage sink that buffers serialized records through the
65
- * shared {@link createRecordBuffer} policy and persists each newline-joined
66
- * JSONL batch under a counter-incremented key through
67
- * {@link createSessionStorageStore}. One uniform write path runs on every
68
- * runtime; no per-runtime mode exists. Flush triggers (32 KiB in-write cap,
69
- * `warn`-or-worse severity, 250 ms quiet-period deadline, page lifecycle,
70
- * and the `flush` hook) are the buffer's; see {@link createRecordBuffer}.
71
- *
72
- * @returns Sink backed by web `sessionStorage`.
73
- *
74
- * @example
75
- * ```ts
76
- * const { logger } = createLogger({ sinks: [createSessionStorageSink()] });
77
- * logger.info('user signed in'); // buffered
78
- * logger.warn('quota near'); // flushes both records in one batch
79
- * ```
64
+ Builds a sessionStorage sink that buffers serialized records through the
65
+ shared {@link createRecordBuffer} policy and persists each newline-joined
66
+ JSONL batch under a counter-incremented key through
67
+ {@link createSessionStorageStore}. One uniform write path runs on every
68
+ runtime; no per-runtime mode exists. Flush triggers (32 KiB in-write cap,
69
+ `warn`-or-worse severity, 250 ms quiet-period deadline, page lifecycle,
70
+ and the `flush` hook) are the buffer's; see {@link createRecordBuffer}.
71
+
72
+ @returns Sink backed by web `sessionStorage`.
73
+
74
+ @example
75
+ ```ts
76
+ const { logger } = createLogger({ sinks: [createSessionStorageSink()] });
77
+ logger.info('user signed in'); // buffered
78
+ logger.warn('quota near'); // flushes both records in one batch
79
+ ```
80
80
  */
81
81
  export function createSessionStorageSink(): Sink {
82
82
  /**
83
- * Persistence engine owning key allocation, footprint accounting, and quota
84
- * eviction; the buffer decides when a batch is handed to it.
83
+ Persistence engine owning key allocation, footprint accounting, and quota
84
+ eviction; the buffer decides when a batch is handed to it.
85
85
  */
86
86
  const store = createSessionStorageStore();
87
87
 
88
88
  /**
89
- * Shared buffering stage; every flush trigger lands one joined batch in the
90
- * persistence engine synchronously.
89
+ Shared buffering stage; every flush trigger lands one joined batch in the
90
+ persistence engine synchronously.
91
91
  */
92
92
  const buffer = createRecordBuffer({ onFlush: store.persist, },);
93
93
 
94
94
  /**
95
- * Buffers a log record through the shared policy; see
96
- * {@link createRecordBuffer} for the flush triggers.
97
- *
98
- * @param record - Log record to buffer and eventually persist.
99
- *
100
- * @mutates record - `JSON.stringify` may invoke `toJSON`, getters, or proxy traps.
95
+ Buffers a log record through the shared policy; see
96
+ {@link createRecordBuffer} for the flush triggers.
97
+
98
+ @param record - Log record to buffer and eventually persist.
99
+
100
+ @mutates record - `JSON.stringify` may invoke `toJSON`, getters, or proxy traps.
101
101
  */
102
102
  function write(record: {
103
103
  level: Level;
@@ -112,8 +112,8 @@ export function createSessionStorageSink(): Sink {
112
112
  }
113
113
 
114
114
  /**
115
- * Drains the buffer into the persistence engine; the drain is synchronous,
116
- * so the batch has landed by the time the resolved promise is observed.
115
+ Drains the buffer into the persistence engine; the drain is synchronous,
116
+ so the batch has landed by the time the resolved promise is observed.
117
117
  */
118
118
  function flush(): Promise<void> {
119
119
  buffer.drain();
@@ -11,20 +11,20 @@ import {
11
11
  } from '@monochromatic-dev/module-logger';
12
12
 
13
13
  /**
14
- * Sink factories under test, read from the built artifact's `sinks` namespace.
14
+ Sink factories under test, read from the built artifact's `sinks` namespace.
15
15
  */
16
16
  const {
17
17
  createSessionStorageSink,
18
18
  } = sinks;
19
19
 
20
20
  /**
21
- * Swaps `globalThis.sessionStorage` for `fake`, restoring the real backend when
22
- * the returned guard leaves `using` scope, so a fake never leaks into a later
23
- * test in the serial suite.
24
- *
25
- * @param fake - Storage stand-in to install for the duration of the scope.
26
- *
27
- * @returns Disposable that restores the original `sessionStorage` on exit.
21
+ Swaps `globalThis.sessionStorage` for `fake`, restoring the real backend when
22
+ the returned guard leaves `using` scope, so a fake never leaks into a later
23
+ test in the serial suite.
24
+
25
+ @param fake - Storage stand-in to install for the duration of the scope.
26
+
27
+ @returns Disposable that restores the original `sessionStorage` on exit.
28
28
  */
29
29
  function installFakeStorage(fake: Storage,): Disposable {
30
30
  const original = globalThis.sessionStorage;
@@ -37,14 +37,14 @@ function installFakeStorage(fake: Storage,): Disposable {
37
37
  }
38
38
 
39
39
  /**
40
- * Builds an in-memory `Storage` stand-in that rejects a `setItem` once stored
41
- * value lengths would exceed `byteBudget`, throwing the same
42
- * `QuotaExceededError` a real backend raises. Records every `removeItem` under
43
- * `removed` so a test can assert exactly which keys the sink evicted.
44
- *
45
- * @param byteBudget - Total value length the store accepts before overflowing.
46
- *
47
- * @returns Storage stand-in exposing the evicted-key log as `removed`.
40
+ Builds an in-memory `Storage` stand-in that rejects a `setItem` once stored
41
+ value lengths would exceed `byteBudget`, throwing the same
42
+ `QuotaExceededError` a real backend raises. Records every `removeItem` under
43
+ `removed` so a test can assert exactly which keys the sink evicted.
44
+
45
+ @param byteBudget - Total value length the store accepts before overflowing.
46
+
47
+ @returns Storage stand-in exposing the evicted-key log as `removed`.
48
48
  */
49
49
  function createQuotaStorage(byteBudget: number,): Storage & { readonly removed: string[]; } {
50
50
  const store = new Map<string, string>();
@@ -77,12 +77,12 @@ function createQuotaStorage(byteBudget: number,): Storage & { readonly removed:
77
77
  }
78
78
 
79
79
  /**
80
- * Builds an in-memory `Storage` stand-in whose first `setItem` succeeds and
81
- * every later one throws a non-quota error, so a test can prove the sink does
82
- * not evict for failures other than a quota overflow. Records `removeItem`
83
- * calls under `removed`.
84
- *
85
- * @returns Storage stand-in exposing the evicted-key log as `removed`.
80
+ Builds an in-memory `Storage` stand-in whose first `setItem` succeeds and
81
+ every later one throws a non-quota error, so a test can prove the sink does
82
+ not evict for failures other than a quota overflow. Records `removeItem`
83
+ calls under `removed`.
84
+
85
+ @returns Storage stand-in exposing the evicted-key log as `removed`.
86
86
  */
87
87
  function createFlakyStorage(): Storage & { readonly removed: string[]; } {
88
88
  const store = new Map<string, string>();
@@ -107,10 +107,10 @@ function createFlakyStorage(): Storage & { readonly removed: string[]; } {
107
107
  }
108
108
 
109
109
  /**
110
- * Captures `console.warn` output, restoring the real method when the returned
111
- * guard leaves `using` scope, so a test can count the sink's give-up reports.
112
- *
113
- * @returns Disposable exposing captured warn lines as `calls`.
110
+ Captures `console.warn` output, restoring the real method when the returned
111
+ guard leaves `using` scope, so a test can count the sink's give-up reports.
112
+
113
+ @returns Disposable exposing captured warn lines as `calls`.
114
114
  */
115
115
  function spyConsoleWarn(): Disposable & { readonly calls: string[]; } {
116
116
  const original = console.warn;
@@ -128,11 +128,11 @@ function spyConsoleWarn(): Disposable & { readonly calls: string[]; } {
128
128
  }
129
129
 
130
130
  /**
131
- * Counts captured warn lines that are the sessionStorage sink's give-up report.
132
- *
133
- * @param calls - Captured `console.warn` lines from {@link spyConsoleWarn}.
134
- *
135
- * @returns How many lines report a sink write failure.
131
+ Counts captured warn lines that are the sessionStorage sink's give-up report.
132
+
133
+ @param calls - Captured `console.warn` lines from {@link spyConsoleWarn}.
134
+
135
+ @returns How many lines report a sink write failure.
136
136
  */
137
137
  function sinkFailureCount(calls: readonly string[],): number {
138
138
  return calls.filter(function isSinkFailure(line,) {
@@ -174,7 +174,7 @@ await describe({
174
174
  await sink.verify();
175
175
 
176
176
  /**
177
- * Routine record; severity below `warn` stays buffered.
177
+ Routine record; severity below `warn` stays buffered.
178
178
  */
179
179
  const record: LogRecord = {
180
180
  level: 'info',
@@ -206,7 +206,7 @@ await describe({
206
206
  await sink.verify();
207
207
 
208
208
  /**
209
- * Routine record buffered first; must survive into the batch the warning triggers.
209
+ Routine record buffered first; must survive into the batch the warning triggers.
210
210
  */
211
211
  const first: LogRecord = {
212
212
  level: 'info',
@@ -214,7 +214,7 @@ await describe({
214
214
  timestamp: 0,
215
215
  };
216
216
  /**
217
- * Warning record whose severity forces the synchronous flush.
217
+ Warning record whose severity forces the synchronous flush.
218
218
  */
219
219
  const second: LogRecord = {
220
220
  level: 'warn',
@@ -244,7 +244,7 @@ await describe({
244
244
  await sink.verify();
245
245
 
246
246
  /**
247
- * Routine record left to the deadline timer.
247
+ Routine record left to the deadline timer.
248
248
  */
249
249
  const record: LogRecord = {
250
250
  level: 'debug',
@@ -254,7 +254,7 @@ await describe({
254
254
  await sink.write(record,);
255
255
 
256
256
  /**
257
- * Comfortably past the sink's 250 ms quiet-period deadline.
257
+ Comfortably past the sink's 250 ms quiet-period deadline.
258
258
  */
259
259
  const pastDeadlineMs = 400;
260
260
  await wait(pastDeadlineMs,);
@@ -275,7 +275,7 @@ await describe({
275
275
  await sink.verify();
276
276
 
277
277
  /**
278
- * Small routine record buffered first; must not share a batch with the cap-breaching record.
278
+ Small routine record buffered first; must not share a batch with the cap-breaching record.
279
279
  */
280
280
  const small: LogRecord = {
281
281
  level: 'info',
@@ -343,7 +343,7 @@ await describe({
343
343
  name: 'caps its own footprint at half the runtime quota, proactively evicting oldest',
344
344
  fn: async () => {
345
345
  /**
346
- * Half the detected runtime quota: the footprint ceiling the engine enforces.
346
+ Half the detected runtime quota: the footprint ceiling the engine enforces.
347
347
  */
348
348
  const capChars = detectSessionStorageQuotaChars() / 2;
349
349
  // A fake store far larger than the cap, so only the proactive half-quota
@@ -483,7 +483,7 @@ await describe({
483
483
  using warn = spyConsoleWarn();
484
484
  const sink = createSessionStorageSink();
485
485
  /**
486
- * Record larger than the whole budget; unwritable even after eviction.
486
+ Record larger than the whole budget; unwritable even after eviction.
487
487
  */
488
488
  const oversized = {
489
489
  level: 'error' as const,