@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 localStorage quota heuristics.
3
- *
4
- * The Web Storage API exposes no way to read the localStorage 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 localStorage measures and what the sink
11
- * compares `serialized.length` against.
12
- *
13
- * @module
2
+ Per-runtime default localStorage quota heuristics.
3
+
4
+ The Web Storage API exposes no way to read the localStorage 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 localStorage measures and what the sink
11
+ compares `serialized.length` against.
12
+
13
+ @module
14
14
  */
15
15
 
16
16
  import {
@@ -19,21 +19,21 @@ import {
19
19
  } from './web-storage-runtime.ts';
20
20
 
21
21
  /**
22
- * Measured default per-origin localStorage quotas, in UTF-16 code units, one
23
- * bucket per detectable runtime:
24
- *
25
- * - `deno`: 10,477,569 on Deno 2.9, which lands 8 KiB short of the 10 MiB its
26
- * sessionStorage measures, presumably backing-store overhead; the measured
27
- * figure is kept as-is rather than rounded up past what actually fits.
28
- * - `node`: 5 MiB on Node 26 launched with `--localstorage-file`.
29
- * - `browser`: 5 MiB, measured on headless Chromium 149 over an
30
- * `http://127.0.0.1` origin. Firefox and WebKit are assumed to share the
31
- * bucket: both measured 5 MiB for sessionStorage under Playwright v1.61 and
32
- * neither was fill-probed for localStorage here.
33
- *
34
- * Bun 1.3 exposes no `localStorage`, so its bucket is uncapped: its sink never
35
- * verifies and never reaches the cap. An unrecognized runtime is also uncapped
36
- * so the caller relies on reactive eviction alone.
22
+ Measured default per-origin localStorage quotas, in UTF-16 code units, one
23
+ bucket per detectable runtime:
24
+
25
+ - `deno`: 10,477,569 on Deno 2.9, which lands 8 KiB short of the 10 MiB its
26
+ sessionStorage measures, presumably backing-store overhead; the measured
27
+ figure is kept as-is rather than rounded up past what actually fits.
28
+ - `node`: 5 MiB on Node 26 launched with `--localstorage-file`.
29
+ - `browser`: 5 MiB, measured on headless Chromium 149 over an
30
+ `http://127.0.0.1` origin. Firefox and WebKit are assumed to share the
31
+ bucket: both measured 5 MiB for sessionStorage under Playwright v1.61 and
32
+ neither was fill-probed for localStorage here.
33
+
34
+ Bun 1.3 exposes no `localStorage`, so its bucket is uncapped: its sink never
35
+ verifies and never reaches the cap. An unrecognized runtime is also uncapped
36
+ so the caller relies on reactive eviction alone.
37
37
  */
38
38
  const RUNTIME_QUOTA_CHARS: Record<WebStorageRuntime, number> = {
39
39
  browser: 5_242_880,
@@ -44,16 +44,16 @@ const RUNTIME_QUOTA_CHARS: Record<WebStorageRuntime, number> = {
44
44
  };
45
45
 
46
46
  /**
47
- * Detects the current runtime's default localStorage quota in UTF-16 code
48
- * units, or `Number.POSITIVE_INFINITY` when the runtime is unrecognized so the
49
- * caller leaves its footprint uncapped and relies on reactive eviction alone.
50
- *
51
- * @returns Total quota in code units, or `Number.POSITIVE_INFINITY` if unknown.
52
- *
53
- * @example
54
- * ```ts
55
- * const capChars = detectLocalStorageQuotaChars() / 2; // half the total
56
- * ```
47
+ Detects the current runtime's default localStorage quota in UTF-16 code
48
+ units, or `Number.POSITIVE_INFINITY` when the runtime is unrecognized so the
49
+ caller leaves its footprint uncapped and relies on reactive eviction alone.
50
+
51
+ @returns Total quota in code units, or `Number.POSITIVE_INFINITY` if unknown.
52
+
53
+ @example
54
+ ```ts
55
+ const capChars = detectLocalStorageQuotaChars() / 2; // half the total
56
+ ```
57
57
  */
58
58
  export function detectLocalStorageQuotaChars(): number {
59
59
  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>;
@@ -9,61 +9,61 @@ import { detectLocalStorageQuotaChars, } from './local-storage-quota.ts';
9
9
  import { isQuotaExceededError, } from './web-storage-quota-error.ts';
10
10
 
11
11
  /**
12
- * Radix for the run nonce so `Number.prototype.toString` yields compact
13
- * alphanumerics.
12
+ Radix for the run nonce so `Number.prototype.toString` yields compact
13
+ alphanumerics.
14
14
  */
15
15
  const NONCE_RADIX = 36;
16
16
 
17
17
  /**
18
- * Length of the run nonce; four base-36 characters make a same-millisecond
19
- * collision between two tabs vanishingly unlikely while keeping keys short.
18
+ Length of the run nonce; four base-36 characters make a same-millisecond
19
+ collision between two tabs vanishingly unlikely while keeping keys short.
20
20
  */
21
21
  const NONCE_LENGTH = 4;
22
22
 
23
23
  /**
24
- * One adopted prior-run entry: its parsed identity plus the value length it
25
- * occupies, captured once at adoption so eviction needs no re-read.
24
+ One adopted prior-run entry: its parsed identity plus the value length it
25
+ occupies, captured once at adoption so eviction needs no re-read.
26
26
  */
27
27
  type PriorEntry = ParsedLogKey & { readonly chars: number; };
28
28
 
29
29
  /**
30
- * Builds the persistence engine behind the localStorage sink: each `persist`
31
- * lands one already-serialized batch under a run-scoped counter-incremented
32
- * key, with proactive and reactive quota eviction. Run identity and counters
33
- * live in this instance's closure (no module-global state), so independent
34
- * sinks and tests never share keys or need a reset hook.
35
- *
36
- * Unlike sessionStorage, localStorage is shared by every tab of the origin and
37
- * survives restarts, so this engine differs from the sessionStorage engine in
38
- * two ways. Keys carry a run identity (see `local-storage-key.ts`), so
39
- * concurrent tabs never collide on a counter. And on its first persist the
40
- * engine adopts every strictly-parsed entry left by other runs into its
41
- * footprint tally, evicting those oldest-first before its own entries;
42
- * without that, leftovers from dead sessions would fill the store until no
43
- * run could ever write again. Adoption is deferred to first persist rather
44
- * than construction so building the default sink set never touches
45
- * `globalThis.localStorage` on runtimes where the sink never verifies (plain
46
- * Node warns on mere access). Keys that fail the strict parse, including the
47
- * host application's, are never counted and never evicted.
48
- *
49
- * The engine caps its own footprint (adopted entries included) at half the
50
- * runtime's localStorage quota, proactively dropping oldest-first, and
51
- * reactively drops again if the real store still overflows; see
52
- * {@link createLocalStorageStore.persist}.
53
- *
54
- * @returns Engine exposing `persist` for one batch value per call.
55
- *
56
- * @example
57
- * ```ts
58
- * const store = createLocalStorageStore();
59
- * store.persist('{"level":"info","message":"hi","timestamp":0}');
60
- * ```
30
+ Builds the persistence engine behind the localStorage sink: each `persist`
31
+ lands one already-serialized batch under a run-scoped counter-incremented
32
+ key, with proactive and reactive quota eviction. Run identity and counters
33
+ live in this instance's closure (no module-global state), so independent
34
+ sinks and tests never share keys or need a reset hook.
35
+
36
+ Unlike sessionStorage, localStorage is shared by every tab of the origin and
37
+ survives restarts, so this engine differs from the sessionStorage engine in
38
+ two ways. Keys carry a run identity (see `local-storage-key.ts`), so
39
+ concurrent tabs never collide on a counter. And on its first persist the
40
+ engine adopts every strictly-parsed entry left by other runs into its
41
+ footprint tally, evicting those oldest-first before its own entries;
42
+ without that, leftovers from dead sessions would fill the store until no
43
+ run could ever write again. Adoption is deferred to first persist rather
44
+ than construction so building the default sink set never touches
45
+ `globalThis.localStorage` on runtimes where the sink never verifies (plain
46
+ Node warns on mere access). Keys that fail the strict parse, including the
47
+ host application's, are never counted and never evicted.
48
+
49
+ The engine caps its own footprint (adopted entries included) at half the
50
+ runtime's localStorage quota, proactively dropping oldest-first, and
51
+ reactively drops again if the real store still overflows; see
52
+ {@link createLocalStorageStore.persist}.
53
+
54
+ @returns Engine exposing `persist` for one batch value per call.
55
+
56
+ @example
57
+ ```ts
58
+ const store = createLocalStorageStore();
59
+ store.persist('{"level":"info","message":"hi","timestamp":0}');
60
+ ```
61
61
  */
62
62
  export function createLocalStorageStore(): { readonly persist: (batch: string,) => void; } {
63
63
  /**
64
- * Identity of this run, embedded in every key this engine writes: the stamp
65
- * orders runs for cross-run eviction and the nonce keeps two tabs started
66
- * in the same millisecond apart.
64
+ Identity of this run, embedded in every key this engine writes: the stamp
65
+ orders runs for cross-run eviction and the nonce keeps two tabs started
66
+ in the same millisecond apart.
67
67
  */
68
68
  const runIdentity = {
69
69
  stamp: Date.now(),
@@ -80,14 +80,14 @@ export function createLocalStorageStore(): { readonly persist: (batch: string,)
80
80
  };
81
81
 
82
82
  /**
83
- * Instance-local write cursor, eviction watermark, and footprint tally,
84
- * mirroring the sessionStorage engine: this run's own entries occupy the
85
- * contiguous index range `[oldestIndex, lineCounter)` and `usedChars`
86
- * tracks the code units the engine accounts for (adopted prior-run entries
87
- * included) so the half-quota cap needs no re-summing. `reportedFailure`
88
- * gates the give-up diagnostic to once per failure episode, re-armed by the
89
- * next landed write. `adoptedPrior` defers the prior-run scan to the first
90
- * persist, which only happens after verification.
83
+ Instance-local write cursor, eviction watermark, and footprint tally,
84
+ mirroring the sessionStorage engine: this run's own entries occupy the
85
+ contiguous index range `[oldestIndex, lineCounter)` and `usedChars`
86
+ tracks the code units the engine accounts for (adopted prior-run entries
87
+ included) so the half-quota cap needs no re-summing. `reportedFailure`
88
+ gates the give-up diagnostic to once per failure episode, re-armed by the
89
+ next landed write. `adoptedPrior` defers the prior-run scan to the first
90
+ persist, which only happens after verification.
91
91
  */
92
92
  const state: {
93
93
  lineCounter: number;
@@ -104,10 +104,10 @@ export function createLocalStorageStore(): { readonly persist: (batch: string,)
104
104
  };
105
105
 
106
106
  /**
107
- * Prior-run entries adopted at first persist, sorted oldest-first, with a
108
- * cursor marking how far eviction has consumed them; entries before the
109
- * cursor are already removed. Prior entries always evict before this run's
110
- * own, since they predate everything this run writes.
107
+ Prior-run entries adopted at first persist, sorted oldest-first, with a
108
+ cursor marking how far eviction has consumed them; entries before the
109
+ cursor are already removed. Prior entries always evict before this run's
110
+ own, since they predate everything this run writes.
111
111
  */
112
112
  const prior: {
113
113
  entries: readonly PriorEntry[];
@@ -118,21 +118,21 @@ export function createLocalStorageStore(): { readonly persist: (batch: string,)
118
118
  };
119
119
 
120
120
  /**
121
- * Half the detected runtime localStorage quota, in UTF-16 code units, or
122
- * `Number.POSITIVE_INFINITY` on an unrecognized runtime. The engine keeps
123
- * its accounted footprint at or below this so the logger never claims more
124
- * than half the store, leaving the rest for the host application. An
125
- * infinite cap disables the proactive check, leaving only reactive
126
- * quota-error eviction.
121
+ Half the detected runtime localStorage quota, in UTF-16 code units, or
122
+ `Number.POSITIVE_INFINITY` on an unrecognized runtime. The engine keeps
123
+ its accounted footprint at or below this so the logger never claims more
124
+ than half the store, leaving the rest for the host application. An
125
+ infinite cap disables the proactive check, leaving only reactive
126
+ quota-error eviction.
127
127
  */
128
128
  const capChars = detectLocalStorageQuotaChars() / 2;
129
129
 
130
130
  /**
131
- * Builds this run's key for a batch slot.
132
- *
133
- * @param index - Zero-based batch slot within this run.
134
- *
135
- * @returns Run-scoped namespaced key.
131
+ Builds this run's key for a batch slot.
132
+
133
+ @param index - Zero-based batch slot within this run.
134
+
135
+ @returns Run-scoped namespaced key.
136
136
  */
137
137
  function ownKey(index: number,): string {
138
138
  return buildLogKey({
@@ -143,32 +143,32 @@ export function createLocalStorageStore(): { readonly persist: (batch: string,)
143
143
  }
144
144
 
145
145
  /**
146
- * Scans localStorage once for strictly-parsed entries left by other runs,
147
- * sorts them oldest-first for eviction, and adds their lengths to the
148
- * footprint tally. Entries another tab writes after this scan are invisible
149
- * to the tally; the reactive quota loop covers that staleness.
146
+ Scans localStorage once for strictly-parsed entries left by other runs,
147
+ sorts them oldest-first for eviction, and adds their lengths to the
148
+ footprint tally. Entries another tab writes after this scan are invisible
149
+ to the tally; the reactive quota loop covers that staleness.
150
150
  */
151
151
  function adoptPriorEntries(): void {
152
152
  /**
153
- * Entry count at scan time; enumeration is by index because `Storage`
154
- * exposes no iterator.
153
+ Entry count at scan time; enumeration is by index because `Storage`
154
+ exposes no iterator.
155
155
  */
156
156
  const total = globalThis.localStorage
157
157
  .length;
158
158
  /**
159
- * Strictly-parsed foreign-run entries found by the scan, unsorted.
159
+ Strictly-parsed foreign-run entries found by the scan, unsorted.
160
160
  */
161
161
  const found: PriorEntry[] = [];
162
162
  for (let slot = 0; slot < total; slot++) {
163
163
  /**
164
- * Key at this enumeration slot; `null` past the end under concurrent removal.
164
+ Key at this enumeration slot; `null` past the end under concurrent removal.
165
165
  */
166
166
  const key = globalThis.localStorage
167
167
  .key(slot,);
168
168
  if (key === null)
169
169
  continue;
170
170
  /**
171
- * Parsed run identity, absent for any key the engine must not touch.
171
+ Parsed run identity, absent for any key the engine must not touch.
172
172
  */
173
173
  const { parsed, } = parseLogKey(key,);
174
174
  if (parsed === undefined)
@@ -176,7 +176,7 @@ export function createLocalStorageStore(): { readonly persist: (batch: string,)
176
176
  if ((parsed.stamp === runIdentity.stamp) && (parsed.nonce === runIdentity.nonce))
177
177
  continue;
178
178
  /**
179
- * Stored batch, read so its length enters the footprint tally.
179
+ Stored batch, read so its length enters the footprint tally.
180
180
  */
181
181
  const value = globalThis.localStorage
182
182
  .getItem(key,);
@@ -208,14 +208,14 @@ export function createLocalStorageStore(): { readonly persist: (batch: string,)
208
208
  }
209
209
 
210
210
  /**
211
- * Reports whether anything remains this engine may evict: an adopted
212
- * prior-run entry past the cursor, or one of this run's own entries.
213
- *
214
- * @returns Whether an eviction call would reclaim something.
211
+ Reports whether anything remains this engine may evict: an adopted
212
+ prior-run entry past the cursor, or one of this run's own entries.
213
+
214
+ @returns Whether an eviction call would reclaim something.
215
215
  */
216
216
  function hasEvictable(): boolean {
217
217
  /**
218
- * Count of adopted prior-run entries; those before the cursor are gone.
218
+ Count of adopted prior-run entries; those before the cursor are gone.
219
219
  */
220
220
  const priorCount = prior.entries
221
221
  .length;
@@ -224,17 +224,17 @@ export function createLocalStorageStore(): { readonly persist: (batch: string,)
224
224
  }
225
225
 
226
226
  /**
227
- * Removes the oldest not-yet-evicted adopted prior-run entry, if one
228
- * remains. Its length leaves the tally from the adoption snapshot: prior
229
- * keys are never rewritten (counters only advance), so a re-read could only
230
- * observe the same value or a concurrent removal, and in both cases the
231
- * snapshot is what the tally counted.
232
- *
233
- * @returns Whether a prior-run entry was evicted.
227
+ Removes the oldest not-yet-evicted adopted prior-run entry, if one
228
+ remains. Its length leaves the tally from the adoption snapshot: prior
229
+ keys are never rewritten (counters only advance), so a re-read could only
230
+ observe the same value or a concurrent removal, and in both cases the
231
+ snapshot is what the tally counted.
232
+
233
+ @returns Whether a prior-run entry was evicted.
234
234
  */
235
235
  function evictOldestPrior(): boolean {
236
236
  /**
237
- * Oldest remaining adopted entry, or `undefined` when all are consumed.
237
+ Oldest remaining adopted entry, or `undefined` when all are consumed.
238
238
  */
239
239
  const entry = prior.entries[prior.cursor];
240
240
  if (entry === undefined)
@@ -250,18 +250,18 @@ export function createLocalStorageStore(): { readonly persist: (batch: string,)
250
250
  }
251
251
 
252
252
  /**
253
- * Removes this run's oldest still-present entry, advancing the watermark
254
- * and subtracting the reclaimed entry's code units from the running
255
- * footprint. Reading the value back before removal keeps `usedChars` honest
256
- * even if the entry drifted from what was written.
253
+ Removes this run's oldest still-present entry, advancing the watermark
254
+ and subtracting the reclaimed entry's code units from the running
255
+ footprint. Reading the value back before removal keeps `usedChars` honest
256
+ even if the entry drifted from what was written.
257
257
  */
258
258
  function evictOldestOwn(): void {
259
259
  /**
260
- * Key of the oldest owned entry, removed to reclaim its slot and its space.
260
+ Key of the oldest owned entry, removed to reclaim its slot and its space.
261
261
  */
262
262
  const key = ownKey(state.oldestIndex,);
263
263
  /**
264
- * Value being evicted, read back so its length can leave the footprint tally.
264
+ Value being evicted, read back so its length can leave the footprint tally.
265
265
  */
266
266
  const evicted = globalThis.localStorage
267
267
  .getItem(key,);
@@ -276,9 +276,9 @@ export function createLocalStorageStore(): { readonly persist: (batch: string,)
276
276
  }
277
277
 
278
278
  /**
279
- * Evicts the single oldest thing the engine still owns: adopted prior-run
280
- * entries first (they predate everything this run wrote), then this run's
281
- * own oldest. Callers guard with {@link hasEvictable}.
279
+ Evicts the single oldest thing the engine still owns: adopted prior-run
280
+ entries first (they predate everything this run wrote), then this run's
281
+ own oldest. Callers guard with {@link hasEvictable}.
282
282
  */
283
283
  function evictOldest(): void {
284
284
  if (evictOldestPrior())
@@ -288,21 +288,21 @@ export function createLocalStorageStore(): { readonly persist: (batch: string,)
288
288
  }
289
289
 
290
290
  /**
291
- * Persists one serialized batch to localStorage under this run's next
292
- * counter-incremented key.
293
- *
294
- * The first call adopts prior-run entries into the footprint tally. Each
295
- * call then proactively evicts oldest-first (prior runs before this run's
296
- * own) until the accounted footprint fits under half the runtime's
297
- * localStorage quota, writes, and on a quota overflow (the store being
298
- * fuller than the tally accounts for, such as another live tab writing
299
- * concurrently) evicts and retries until the batch fits or nothing owned
300
- * remains to drop. A batch larger than the whole quota therefore evicts
301
- * everything owned, then reports and gives up rather than looping forever.
302
- * A non-quota failure is reported without any eviction. The sink only
303
- * persists after verification, so no availability guard is needed here.
304
- *
305
- * @param batch - Serialized JSONL batch to persist.
291
+ Persists one serialized batch to localStorage under this run's next
292
+ counter-incremented key.
293
+
294
+ The first call adopts prior-run entries into the footprint tally. Each
295
+ call then proactively evicts oldest-first (prior runs before this run's
296
+ own) until the accounted footprint fits under half the runtime's
297
+ localStorage quota, writes, and on a quota overflow (the store being
298
+ fuller than the tally accounts for, such as another live tab writing
299
+ concurrently) evicts and retries until the batch fits or nothing owned
300
+ remains to drop. A batch larger than the whole quota therefore evicts
301
+ everything owned, then reports and gives up rather than looping forever.
302
+ A non-quota failure is reported without any eviction. The sink only
303
+ persists after verification, so no availability guard is needed here.
304
+
305
+ @param batch - Serialized JSONL batch to persist.
306
306
  */
307
307
  function persist(batch: string,): void {
308
308
  if (!state.adoptedPrior) {
@@ -311,7 +311,7 @@ export function createLocalStorageStore(): { readonly persist: (batch: string,)
311
311
  }
312
312
 
313
313
  /**
314
- * Code units this batch adds; the key's length is left out as a negligible near-constant.
314
+ Code units this batch adds; the key's length is left out as a negligible near-constant.
315
315
  */
316
316
  const batchChars = batch.length;
317
317
 
@@ -323,8 +323,8 @@ export function createLocalStorageStore(): { readonly persist: (batch: string,)
323
323
  }
324
324
 
325
325
  /**
326
- * Write-attempt bound: one try for each entry still available to evict,
327
- * followed by one final try after every owned entry has been removed.
326
+ Write-attempt bound: one try for each entry still available to evict,
327
+ followed by one final try after every owned entry has been removed.
328
328
  */
329
329
  const maxWriteAttempts = (prior
330
330
  .entries
@@ -9,20 +9,20 @@ import {
9
9
  } from '@monochromatic-dev/module-logger';
10
10
 
11
11
  /**
12
- * One mebibyte of UTF-16 code units; the node cap under test is half the
13
- * measured 5 MiB quota, so multi-mebibyte batches drive eviction.
12
+ One mebibyte of UTF-16 code units; the node cap under test is half the
13
+ measured 5 MiB quota, so multi-mebibyte batches drive eviction.
14
14
  */
15
15
  const MIB = 1_048_576;
16
16
 
17
17
  /**
18
- * Installs `fake` as `globalThis.localStorage` via the property descriptor
19
- * (plain assignment would call Node's phantom setter-less path on some hosts),
20
- * restoring the original descriptor, or removing the property when none
21
- * existed, when the returned guard leaves `using` scope.
22
- *
23
- * @param fake - Storage stand-in to install for the duration of the scope.
24
- *
25
- * @returns Disposable that restores the original `localStorage` on exit.
18
+ Installs `fake` as `globalThis.localStorage` via the property descriptor
19
+ (plain assignment would call Node's phantom setter-less path on some hosts),
20
+ restoring the original descriptor, or removing the property when none
21
+ existed, when the returned guard leaves `using` scope.
22
+
23
+ @param fake - Storage stand-in to install for the duration of the scope.
24
+
25
+ @returns Disposable that restores the original `localStorage` on exit.
26
26
  */
27
27
  function installFakeLocalStorage(fake: Storage,): Disposable {
28
28
  const original = Object.getOwnPropertyDescriptor(globalThis, 'localStorage',);
@@ -41,16 +41,16 @@ function installFakeLocalStorage(fake: Storage,): Disposable {
41
41
  }
42
42
 
43
43
  /**
44
- * Builds an in-memory `Storage` stand-in with full enumeration support (the
45
- * engine's adoption scan walks `length`/`key`), rejecting a `setItem` once
46
- * stored value lengths would exceed `quotaChars`, throwing the same
47
- * `QuotaExceededError` a real backend raises. Records every `removeItem`
48
- * under `removed` so a test can assert exactly which keys were evicted.
49
- *
50
- * @param quotaChars - Total value length the store accepts before
51
- * overflowing; omitted means unlimited.
52
- *
53
- * @returns Storage stand-in exposing `removed` and the raw `backing` map.
44
+ Builds an in-memory `Storage` stand-in with full enumeration support (the
45
+ engine's adoption scan walks `length`/`key`), rejecting a `setItem` once
46
+ stored value lengths would exceed `quotaChars`, throwing the same
47
+ `QuotaExceededError` a real backend raises. Records every `removeItem`
48
+ under `removed` so a test can assert exactly which keys were evicted.
49
+
50
+ @param quotaChars - Total value length the store accepts before
51
+ overflowing; omitted means unlimited.
52
+
53
+ @returns Storage stand-in exposing `removed` and the raw `backing` map.
54
54
  */
55
55
  function createFakeStorage(
56
56
  { quotaChars, }: { readonly quotaChars?: number; } = {},
@@ -98,12 +98,12 @@ function createFakeStorage(
98
98
  }
99
99
 
100
100
  /**
101
- * Builds an in-memory `Storage` stand-in whose first `setItem` succeeds and
102
- * every later one throws a non-quota error, so a test can prove the engine
103
- * does not evict for failures other than a quota overflow. Enumeration is
104
- * supported for the adoption scan; `removeItem` calls land in `removed`.
105
- *
106
- * @returns Storage stand-in exposing the evicted-key log as `removed`.
101
+ Builds an in-memory `Storage` stand-in whose first `setItem` succeeds and
102
+ every later one throws a non-quota error, so a test can prove the engine
103
+ does not evict for failures other than a quota overflow. Enumeration is
104
+ supported for the adoption scan; `removeItem` calls land in `removed`.
105
+
106
+ @returns Storage stand-in exposing the evicted-key log as `removed`.
107
107
  */
108
108
  function createFlakyStorage(): Storage & { readonly removed: string[]; } {
109
109
  const backing = new Map<string, string>();
@@ -134,11 +134,11 @@ function createFlakyStorage(): Storage & { readonly removed: string[]; } {
134
134
  }
135
135
 
136
136
  /**
137
- * Captures `console.warn` output, restoring the real method when the returned
138
- * guard leaves `using` scope, so a test can count the engine's give-up
139
- * reports.
140
- *
141
- * @returns Disposable exposing captured warn lines as `calls`.
137
+ Captures `console.warn` output, restoring the real method when the returned
138
+ guard leaves `using` scope, so a test can count the engine's give-up
139
+ reports.
140
+
141
+ @returns Disposable exposing captured warn lines as `calls`.
142
142
  */
143
143
  function spyConsoleWarn(): Disposable & { readonly calls: string[]; } {
144
144
  const original = console.warn;
@@ -171,7 +171,7 @@ await describe({
171
171
  store.persist('beta',);
172
172
 
173
173
  /**
174
- * Parsed identities of every landed key; both must carry one shared run identity.
174
+ Parsed identities of every landed key; both must carry one shared run identity.
175
175
  */
176
176
  const parsed = [...fake.backing.keys(),]
177
177
  .flatMap((key,) => {
@@ -236,13 +236,13 @@ await describe({
236
236
  expect(fake.removed,)
237
237
  .toHaveLength(1,);
238
238
  /**
239
- * Identity of the evicted key; it must be this run's slot zero.
239
+ Identity of the evicted key; it must be this run's slot zero.
240
240
  */
241
241
  const { parsed: evicted, } = parseLogKey(fake.removed[0] ?? '',);
242
242
  expect(evicted?.index,)
243
243
  .toBe(0,);
244
244
  /**
245
- * Indices still present after eviction, in insertion order.
245
+ Indices still present after eviction, in insertion order.
246
246
  */
247
247
  const remaining = [...fake.backing.keys(),]
248
248
  .flatMap((key,) => {
@@ -272,7 +272,7 @@ await describe({
272
272
  expect(fake.removed,)
273
273
  .toEqual(['monochromatic.log.1000.aaaa.0',],);
274
274
  /**
275
- * Landed batch values after the retry; only the new batch remains.
275
+ Landed batch values after the retry; only the new batch remains.
276
276
  */
277
277
  const values = [...fake.backing.values(),];
278
278
  expect(values,)