@monochromatic-dev/module-logger 0.2.0 → 0.3.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 (62) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +33 -8
  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 +360 -594
  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 +360 -594
  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 +18 -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 +34 -0
  21. package/src/default-sinks.node.ts +32 -0
  22. package/src/error-format.ts +23 -23
  23. package/src/logger.ts +23 -50
  24. package/src/node.ts +23 -0
  25. package/src/sink/console-control-chars.ts +64 -64
  26. package/src/sink/console-control-chars.unit.test.ts +14 -14
  27. package/src/sink/console.ts +194 -194
  28. package/src/sink/console.unit.test.ts +18 -18
  29. package/src/sink/file.ts +136 -140
  30. package/src/sink/file.unit.test.ts +19 -26
  31. package/src/sink/index.ts +4 -7
  32. package/src/sink/indexed-db-util.ts +42 -42
  33. package/src/sink/indexed-db.browser.test.ts +7 -7
  34. package/src/sink/indexed-db.ts +109 -109
  35. package/src/sink/indexed-db.unit.test.ts +5 -13
  36. package/src/sink/local-storage-key.ts +73 -73
  37. package/src/sink/local-storage-key.unit.test.ts +8 -8
  38. package/src/sink/local-storage-quota.ts +37 -37
  39. package/src/sink/local-storage-quota.unit.test.ts +8 -8
  40. package/src/sink/local-storage-store.ts +113 -113
  41. package/src/sink/local-storage-store.unit.test.ts +35 -35
  42. package/src/sink/local-storage.ts +72 -72
  43. package/src/sink/local-storage.unit.test.ts +27 -27
  44. package/src/sink/noop.ts +20 -20
  45. package/src/sink/noop.unit.test.ts +1 -1
  46. package/src/sink/opfs.browser.test.ts +7 -7
  47. package/src/sink/opfs.ts +62 -62
  48. package/src/sink/opfs.unit.test.ts +5 -13
  49. package/src/sink/record-buffer.ts +84 -84
  50. package/src/sink/record-buffer.unit.test.ts +20 -20
  51. package/src/sink/session-storage-quota.ts +34 -34
  52. package/src/sink/session-storage-quota.unit.test.ts +8 -8
  53. package/src/sink/session-storage-store.ts +72 -72
  54. package/src/sink/session-storage.ts +48 -48
  55. package/src/sink/session-storage.unit.test.ts +39 -39
  56. package/src/sink/web-storage-quota-error.ts +22 -22
  57. package/src/sink/web-storage-quota-error.unit.test.ts +2 -2
  58. package/src/sink/web-storage-runtime.ts +24 -24
  59. package/src/startup.unit.test.ts +18 -18
  60. package/src/tagged.ts +35 -35
  61. package/src/tagged.unit.test.ts +8 -8
  62. package/src/types.ts +39 -39
package/src/sink/file.ts CHANGED
@@ -1,88 +1,97 @@
1
- import type { stat as Stat, } from 'node:fs/promises';
2
- import type {
3
- dirname as Dirname,
4
- join as Join,
1
+ import {
2
+ appendFile,
3
+ mkdir,
4
+ readFile,
5
+ stat,
6
+ } from 'node:fs/promises';
7
+ import {
8
+ dirname,
9
+ join,
5
10
  } from 'node:path';
6
11
 
7
12
  import { reportLoggerInternalError, } from '../error-format.ts';
8
13
 
9
14
  import type { Sink, } from '../types.ts';
10
15
 
16
+ //region Ancestor search
17
+ // Locates the project the process belongs to so log files land beside its
18
+ // `node_modules` rather than at whatever stray cwd launched the script.
19
+
11
20
  /**
12
- * Sentinel returned by {@link findNodeModulesUp} when no ancestor directory
13
- * contains a `node_modules`. A unique symbol so it never collides with a real
14
- * path string the walk might otherwise return, keeping the result free of a
15
- * banned `string | undefined` union.
16
- *
17
- * @example
18
- * ```ts
19
- * const dir = await findNodeModulesUp({ cwd, stat, dirname, join });
20
- * if (dir === NO_NODE_MODULES_FOUND) {
21
- * // no ancestor project root
22
- * }
23
- * ```
21
+ Sentinel returned by {@link findNodeModulesUp} when no ancestor directory
22
+ contains a `node_modules`. A unique symbol so it never collides with a real
23
+ path string the walk might otherwise return, keeping the result free of a
24
+ banned `string | undefined` union.
25
+
26
+ @example
27
+ ```ts
28
+ const dir = await findNodeModulesUp({ cwd, stat, dirname, join });
29
+ if (dir === NO_NODE_MODULES_FOUND) {
30
+ // no ancestor project root
31
+ }
32
+ ```
24
33
  */
25
34
  export const NO_NODE_MODULES_FOUND: unique symbol = Symbol('logger:no-node-modules-found',);
26
35
 
27
36
  /**
28
- * Walks up from `cwd` to find the nearest ancestor directory containing a
29
- * `node_modules` subdirectory, returning that subdirectory's absolute path.
30
- *
31
- * Using find-up rather than cwd-relative placement keeps log directories
32
- * anchored to the project the caller actually belongs to. Without this,
33
- * scripts invoked from build output (e.g. `dist/`) or other stray cwds
34
- * would create `node_modules/.monochromatic/` inside those trees, polluting
35
- * shipped artifacts.
36
- *
37
- * Exported primarily so `index.unit.test.ts` can exercise both the hit
38
- * and miss paths directly with an injected `stat`.
39
- *
40
- * @param cwd - starting directory for the upward search
41
- *
42
- * @param stat - `node:fs/promises` stat (injected so the dynamic
43
- * import stays in one place)
44
- *
45
- * @param dirname - `node:path` dirname
46
- *
47
- * @param join - `node:path` join
48
- *
49
- * @param reportError - logger fault reporter injected for deterministic tests
50
- *
51
- * @returns absolute path to the nearest ancestor `node_modules`, or
52
- * {@link NO_NODE_MODULES_FOUND} when no ancestor contains one
53
- *
54
- * @example
55
- * ```ts
56
- * const dir = await findNodeModulesUp({ cwd: process.cwd(), stat, dirname, join });
57
- * ```
37
+ Walks up from `cwd` to find the nearest ancestor directory containing a
38
+ `node_modules` subdirectory, returning that subdirectory's absolute path.
39
+
40
+ Using find-up rather than cwd-relative placement keeps log directories
41
+ anchored to the project the caller actually belongs to. Without this,
42
+ scripts invoked from build output (e.g. `dist/`) or other stray cwds
43
+ would create `node_modules/.monochromatic/` inside those trees, polluting
44
+ shipped artifacts.
45
+
46
+ Exported through the `./node` subpath primarily so `file.unit.test.ts`
47
+ can exercise both the hit and miss paths directly with an injected `stat`.
48
+
49
+ @param cwd - starting directory for the upward search
50
+
51
+ @param stat - `node:fs/promises` stat, injected so tests drive hit,
52
+ miss, and fault paths deterministically without touching a real tree
53
+
54
+ @param dirname - `node:path` dirname
55
+
56
+ @param join - `node:path` join
57
+
58
+ @param reportError - logger fault reporter injected for deterministic tests
59
+
60
+ @returns absolute path to the nearest ancestor `node_modules`, or
61
+ {@link NO_NODE_MODULES_FOUND} when no ancestor contains one
62
+
63
+ @example
64
+ ```ts
65
+ const dir = await findNodeModulesUp({ cwd: process.cwd(), stat, dirname, join });
66
+ ```
58
67
  */
59
68
  export async function findNodeModulesUp(
60
69
  {
61
70
  cwd,
62
- stat,
63
- dirname,
64
- join,
71
+ stat: statPath,
72
+ dirname: parentOf,
73
+ join: joinPath,
65
74
  reportError = reportLoggerInternalError,
66
75
  }: {
67
76
  readonly cwd: string;
68
- readonly stat: typeof Stat;
69
- readonly dirname: typeof Dirname;
70
- readonly join: typeof Join;
77
+ readonly stat: typeof stat;
78
+ readonly dirname: typeof dirname;
79
+ readonly join: typeof join;
71
80
  readonly reportError?: typeof reportLoggerInternalError;
72
81
  },
73
82
  ): Promise<string | typeof NO_NODE_MODULES_FOUND> {
74
83
  /**
75
- * Directory being tested in this iteration; either resolves to a node_modules or triggers the walk to the parent.
84
+ Directory being tested in this iteration; either resolves to a node_modules or triggers the walk to the parent.
76
85
  */
77
- const candidate = join(
86
+ const candidate = joinPath(
78
87
  cwd,
79
88
  'node_modules',
80
89
  );
81
90
  try {
82
91
  /**
83
- * Stat result for `candidate`; only directories count as a hit, guarding against a sibling file also named `node_modules`.
92
+ Stat result for `candidate`; only directories count as a hit, guarding against a sibling file also named `node_modules`.
84
93
  */
85
- const entry = await stat(candidate,);
94
+ const entry = await statPath(candidate,);
86
95
  if (entry.isDirectory())
87
96
  return candidate;
88
97
  }
@@ -98,94 +107,73 @@ export async function findNodeModulesUp(
98
107
  }
99
108
  }
100
109
  /**
101
- * Parent directory used by the next recursive step; equal to `cwd` only at the filesystem root, which terminates the walk.
110
+ Parent directory used by the next recursive step; equal to `cwd` only at the filesystem root, which terminates the walk.
102
111
  */
103
- const parent = dirname(cwd,);
112
+ const parent = parentOf(cwd,);
104
113
  if (parent === cwd)
105
114
  return NO_NODE_MODULES_FOUND;
106
115
  return await findNodeModulesUp({
107
116
  cwd: parent,
108
- stat,
109
- dirname,
110
- join,
117
+ stat: statPath,
118
+ dirname: parentOf,
119
+ join: joinPath,
111
120
  reportError,
112
121
  },);
113
122
  }
123
+ //endregion Ancestor search
124
+
125
+ //region File sink
126
+ // Append-only JSONL sink. Imports `node:fs/promises` statically: this module
127
+ // is reachable only from the Node artifact (the `./node` subpath and the
128
+ // `#default-sinks` `node` condition), so the `node` export condition already
129
+ // asserts a Node runtime and no `process.versions.node` probe is needed.
114
130
 
115
131
  /**
116
- * Builds a file sink that appends JSONL records to the nearest ancestor
117
- * `node_modules/.monochromatic/{timestamp}.log.jsonl` (resolved once during
118
- * verification). The resolved path, the cached `appendFile`, and the
119
- * verification memo live in this instance's closure (no module-global state),
120
- * so independent loggers and tests never share a log file or need a reset
121
- * hook. No `flush` hook: each `write` awaits `appendFile` directly, so there
122
- * is no buffered state to drain.
123
- *
124
- * @returns Sink backed by `node:fs/promises`.
125
- *
126
- * @example
127
- * ```ts
128
- * const { logger } = createLogger({ sinks: [createFileSink()] });
129
- * logger.error('unhandled rejection');
130
- * await logger.flush();
131
- * ```
132
+ Builds a file sink that appends JSONL records to the nearest ancestor
133
+ `node_modules/.monochromatic/{timestamp}.log.jsonl` (resolved once during
134
+ verification). The resolved path and the verification memo live in this
135
+ instance's closure (no module-global state), so independent loggers and
136
+ tests never share a log file or need a reset hook. No `flush` hook: each
137
+ `write` awaits `appendFile` directly, so there is no buffered state to
138
+ drain.
139
+
140
+ @returns Sink backed by `node:fs/promises`.
141
+
142
+ @example
143
+ ```ts
144
+ const { logger } = createLogger({ sinks: [createFileSink()] });
145
+ logger.error('unhandled rejection');
146
+ await logger.flush();
147
+ ```
132
148
  */
133
149
  export function createFileSink(): Sink {
134
150
  /**
135
- * Instance-local file-sink resources. `appendFile` and `filePath` are
136
- * populated during verification and read by `write`; `verifyPromise`
137
- * memoizes concurrent verification so a caller arriving mid-flight shares
138
- * the same async work rather than starting a second probe.
151
+ Instance-local file-sink resources. `filePath` is populated during
152
+ verification and read by `write`; `verifyPromise` memoizes concurrent
153
+ verification so a caller arriving mid-flight shares the same async work
154
+ rather than starting a second probe.
139
155
  */
140
156
  const state: {
141
- // oxlint-disable-next-line typescript/consistent-type-imports -- typeof import() cannot use import type syntax
142
- appendFile?: typeof import('node:fs/promises').appendFile;
143
157
  filePath?: string;
144
158
  verifyPromise?: Promise<boolean>;
145
159
  } = {};
146
160
 
147
161
  /**
148
- * Actual verification work, invoked exactly once via the memoized
149
- * `verifyPromise`. Resolves the log path and caches `appendFile`, marking
150
- * the sink unavailable when the upward search yields
151
- * {@link NO_NODE_MODULES_FOUND}. The logger owns the resulting
152
- * availability, so no flag is kept here.
153
- *
154
- * @returns Whether file system logging is available.
162
+ Actual verification work, invoked exactly once via the memoized
163
+ `verifyPromise`. Resolves the log path, marking the sink unavailable
164
+ when the upward search yields {@link NO_NODE_MODULES_FOUND}. The logger
165
+ owns the resulting availability, so no flag is kept here.
166
+
167
+ @returns Whether file system logging is available.
155
168
  */
156
169
  async function runVerify(): Promise<boolean> {
157
- // Guard: skip dynamic import entirely outside Node.js to avoid
158
- // browser console errors from attempting to fetch node: URLs.
159
- if ((globalThis.process
160
- === undefined)
161
- || (globalThis.process
162
- .versions
163
- ?.node
164
- === undefined))
165
- return false;
166
-
167
170
  try {
168
- // Dynamic import for Node.js modules: cache appendFile for use in write.
169
- /**
170
- * Dynamically imported `node:fs/promises`; held in this scope so its members are reused without re-importing.
171
- */
172
- const fs = await import('node:fs/promises');
173
- /**
174
- * Path utilities dynamically imported alongside `fs`; needed by the upward search for the closest node_modules.
175
- */
176
- const {
177
- dirname,
178
- join,
179
- } = await import('node:path');
180
-
181
- state.appendFile = fs.appendFile;
182
-
183
171
  /**
184
- * Resolved absolute path of the closest ancestor `node_modules`, or the sentinel when none exists (e.g. a stray cwd).
172
+ Resolved absolute path of the closest ancestor `node_modules`, or the sentinel when none exists (e.g. a stray cwd).
185
173
  */
186
174
  const nodeModulesDir = await findNodeModulesUp({
187
175
  cwd: process.cwd(),
188
- stat: fs.stat,
176
+ stat,
189
177
  dirname,
190
178
  join,
191
179
  },);
@@ -198,44 +186,48 @@ export function createFileSink(): Sink {
198
186
  return false;
199
187
 
200
188
  /**
201
- * Directory under the chosen `node_modules` where every monochromatic log file lands.
189
+ Directory under the chosen `node_modules` where every monochromatic log file lands.
202
190
  */
203
191
  const LOG_DIR = join(
204
192
  nodeModulesDir,
205
193
  '.monochromatic',
206
194
  );
207
- await fs.mkdir(
195
+ await mkdir(
208
196
  LOG_DIR,
209
197
  { recursive: true, },
210
198
  );
211
199
 
212
200
  /**
213
- * ISO timestamp with colons replaced by dashes so it can be embedded in a cross-platform file name.
201
+ ISO timestamp with colons replaced by dashes so it can be embedded in a cross-platform file name.
214
202
  */
215
203
  const timestamp = new Date().toISOString()
216
204
  .replaceAll(
217
205
  ':',
218
206
  '-',
219
207
  );
220
- state.filePath = join(
208
+ /**
209
+ Log file chosen for this sink instance; kept in a local so the probe below reads a narrowed `string`.
210
+ */
211
+ const filePath = join(
221
212
  LOG_DIR,
222
213
  `${timestamp}.log.jsonl`,
223
214
  );
215
+ state.filePath = filePath;
224
216
 
225
217
  // Verify by writing and reading test data.
226
218
  /**
227
- * Probe record written and read back to confirm the chosen file path round-trips.
219
+ Probe record written and read back to confirm the chosen file path round-trips.
228
220
  */
229
221
  const testData = `{"test":true,"timestamp":${Date.now()}}\n`;
230
- await state.appendFile(
231
- state.filePath,
222
+ await appendFile(
223
+ filePath,
232
224
  testData,
233
225
  );
234
226
  /**
235
- * Probe contents read back; matching the literal `"test":true` proves the append + read path works end-to-end.
227
+ Probe contents read back; matching the literal `"test":true` proves the append + read path works end-to-end.
236
228
  */
237
- const content = await fs.readFile(
238
- state.filePath,
229
+ const content = await readFile(
230
+ filePath,
239
231
  'utf8',
240
232
  );
241
233
  return content.includes('"test":true',);
@@ -250,10 +242,10 @@ export function createFileSink(): Sink {
250
242
  }
251
243
 
252
244
  /**
253
- * Verifies file system availability via {@link runVerify}, memoizing
254
- * concurrent calls so a second caller never starts a duplicate probe.
255
- *
256
- * @returns Whether file system logging is available.
245
+ Verifies file system availability via {@link runVerify}, memoizing
246
+ concurrent calls so a second caller never starts a duplicate probe.
247
+
248
+ @returns Whether file system logging is available.
257
249
  */
258
250
  function verify(): Promise<boolean> {
259
251
  if (state.verifyPromise
@@ -265,20 +257,23 @@ export function createFileSink(): Sink {
265
257
  }
266
258
 
267
259
  /**
268
- * Writes a single record as a JSONL line to the resolved log file.
269
- *
270
- * @param record - Log record to write.
271
- *
272
- * @mutates record - `JSON.stringify` may invoke `toJSON`, getters, or proxy traps.
260
+ Writes a single record as a JSONL line to the resolved log file.
261
+
262
+ @param record - Log record to write.
263
+
264
+ @mutates record - `JSON.stringify` may invoke `toJSON`, getters, or proxy traps.
273
265
  */
274
266
  async function write(record: object,): Promise<void> {
275
- // oxlint-disable-next-line typescript/strict-boolean-expressions -- filePath/appendFile are optional (unset before verification); checking presence
276
- if ((!state.filePath) || (!state.appendFile))
267
+ /**
268
+ Log file resolved by verification; unset before (or after a failed) verify, in which case the record is dropped silently.
269
+ */
270
+ const { filePath, } = state;
271
+ if (filePath === undefined)
277
272
  return;
278
273
 
279
274
  try {
280
- await state.appendFile(
281
- state.filePath,
275
+ await appendFile(
276
+ filePath,
282
277
  `${JSON.stringify(record,)}\n`,
283
278
  );
284
279
  }
@@ -295,3 +290,4 @@ export function createFileSink(): Sink {
295
290
  write,
296
291
  };
297
292
  }
293
+ //endregion File sink
@@ -9,25 +9,18 @@ import {
9
9
  expect,
10
10
  it,
11
11
  } from '@monochromatic-dev/module-test/ts';
12
+ import type { LogRecord, } from '@monochromatic-dev/module-logger';
12
13
  import {
13
- sinks,
14
- type LogRecord,
15
- } from '@monochromatic-dev/module-logger';
16
-
17
- /**
18
- * Sink factories under test, read from the built artifact's `sinks` namespace.
19
- */
20
- const {
14
+ _findNodeModulesUp as findNodeModulesUp,
15
+ _NO_NODE_MODULES_FOUND as NO_NODE_MODULES_FOUND,
21
16
  createFileSink,
22
- findNodeModulesUp,
23
- NO_NODE_MODULES_FOUND,
24
- } = sinks;
17
+ } from '@monochromatic-dev/module-logger/node';
25
18
 
26
19
  /**
27
- * Mock `stat` that always throws an ENOENT-like error, so `findNodeModulesUp`
28
- * walks the whole tree and exhausts without matching.
29
- *
30
- * @returns Never; always throws.
20
+ Mock `stat` that always throws an ENOENT-like error, so `findNodeModulesUp`
21
+ walks the whole tree and exhausts without matching.
22
+
23
+ @returns Never; always throws.
31
24
  */
32
25
  function statAlwaysMissing(): never {
33
26
  const error: NodeJS.ErrnoException = Object.assign(
@@ -38,9 +31,9 @@ function statAlwaysMissing(): never {
38
31
  }
39
32
 
40
33
  /**
41
- * Mock `stat` that always throws an unexpected permission error.
42
- *
43
- * @returns Never; always throws.
34
+ Mock `stat` that always throws an unexpected permission error.
35
+
36
+ @returns Never; always throws.
44
37
  */
45
38
  function statAlwaysDenied(): never {
46
39
  const error: NodeJS.ErrnoException = Object.assign(
@@ -51,11 +44,11 @@ function statAlwaysDenied(): never {
51
44
  }
52
45
 
53
46
  /**
54
- * Builds a LogRecord for write-path tests.
55
- *
56
- * @param message - Message body.
57
- *
58
- * @returns Record at a fixed timestamp.
47
+ Builds a LogRecord for write-path tests.
48
+
49
+ @param message - Message body.
50
+
51
+ @returns Record at a fixed timestamp.
59
52
  */
60
53
  function record({ message, }: { readonly message: string; },): LogRecord {
61
54
  return {
@@ -168,9 +161,9 @@ await describe({
168
161
  it({
169
162
  name: 'write before verify resolves without touching the filesystem',
170
163
  fn: async () => {
171
- // Verification resolves the log path and caches `appendFile`; without
172
- // it both stay unset, so write takes the unset-guard early return,
173
- // resolving as a silent no-op rather than throwing or writing.
164
+ // Verification resolves the log path; without it the path stays
165
+ // unset, so write takes the unset-guard early return, resolving as a
166
+ // silent no-op rather than throwing or writing.
174
167
  const sink = createFileSink();
175
168
  await expect(
176
169
  sink.write(record({ message: 'before verify', },),),
package/src/sink/index.ts CHANGED
@@ -1,11 +1,8 @@
1
+ // Cross-platform factories only. The file sink lives behind the `./node`
2
+ // subpath (`src/node.ts`) and the IndexedDB and OPFS sinks behind `./browser`
3
+ // (`src/browser.ts`), so the root entry never pulls `node:fs` into the
4
+ // neutral artifact nor browser-only storage code into the Node artifact.
1
5
  export { createConsoleSink, } from './console.ts';
2
- export {
3
- createFileSink,
4
- findNodeModulesUp,
5
- NO_NODE_MODULES_FOUND,
6
- } from './file.ts';
7
- export { createIndexedDbSink, } from './indexed-db.ts';
8
6
  export { createLocalStorageSink, } from './local-storage.ts';
9
7
  export { createNoopSink, } from './noop.ts';
10
- export { createOpfsSink, } from './opfs.ts';
11
8
  export { createSessionStorageSink, } from './session-storage.ts';
@@ -1,32 +1,32 @@
1
1
  /**
2
- * Promise bridges for the event-based IndexedDB API.
3
- *
4
- * IndexedDB predates promises and signals completion only through `success`
5
- * and `error` events, so awaiting it requires wrapping each request and
6
- * transaction in a promise once, here, instead of scattering listener wiring
7
- * through the sink.
8
- *
9
- * @module
2
+ Promise bridges for the event-based IndexedDB API.
3
+
4
+ IndexedDB predates promises and signals completion only through `success`
5
+ and `error` events, so awaiting it requires wrapping each request and
6
+ transaction in a promise once, here, instead of scattering listener wiring
7
+ through the sink.
8
+
9
+ @module
10
10
  */
11
11
 
12
12
  /**
13
- * Resolves with an IndexedDB request's result once it succeeds, rejecting
14
- * with the request's error when it fails.
15
- *
16
- * @param request - Pending IndexedDB request.
17
- *
18
- * @returns Result the request produces.
19
- *
20
- * @throws DOMException - Whatever the backend attaches to a failed request.
21
- *
22
- * @mutates request - `request.addEventListener` registers success and error
23
- * listeners on the host-owned request, and `resolve` hands `request.result`,
24
- * a host-owned value, out to the awaiting caller.
25
- *
26
- * @example
27
- * ```ts
28
- * const key = await awaitRequest(store.add(batch));
29
- * ```
13
+ Resolves with an IndexedDB request's result once it succeeds, rejecting
14
+ with the request's error when it fails.
15
+
16
+ @param request - Pending IndexedDB request.
17
+
18
+ @returns Result the request produces.
19
+
20
+ @throws DOMException - Whatever the backend attaches to a failed request.
21
+
22
+ @mutates request - `request.addEventListener` registers success and error
23
+ listeners on the host-owned request, and `resolve` hands `request.result`,
24
+ a host-owned value, out to the awaiting caller.
25
+
26
+ @example
27
+ ```ts
28
+ const key = await awaitRequest(store.add(batch));
29
+ ```
30
30
  */
31
31
  export function awaitRequest<T,>(request: IDBRequest<T>,): Promise<T> {
32
32
  // oxlint-disable-next-line promise/avoid-new -- IndexedDB is event-based and exposes no promise API; a constructed promise is the only bridge to await it.
@@ -50,23 +50,23 @@ export function awaitRequest<T,>(request: IDBRequest<T>,): Promise<T> {
50
50
  }
51
51
 
52
52
  /**
53
- * Resolves once an IndexedDB transaction commits, rejecting when it errors or
54
- * aborts, so a caller can await durability of everything queued on it.
55
- *
56
- * @param transaction - Transaction whose settlement is awaited.
57
- *
58
- * @throws DOMException - Whatever the backend attaches to a failed or aborted
59
- * transaction.
60
- *
61
- * @mutates transaction - `transaction.addEventListener` registers complete,
62
- * error, and abort listeners on the host-owned transaction.
63
- *
64
- * @example
65
- * ```ts
66
- * const transaction = database.transaction('batch', 'readwrite');
67
- * transaction.objectStore('batch').add(batch);
68
- * await awaitTransaction(transaction);
69
- * ```
53
+ Resolves once an IndexedDB transaction commits, rejecting when it errors or
54
+ aborts, so a caller can await durability of everything queued on it.
55
+
56
+ @param transaction - Transaction whose settlement is awaited.
57
+
58
+ @throws DOMException - Whatever the backend attaches to a failed or aborted
59
+ transaction.
60
+
61
+ @mutates transaction - `transaction.addEventListener` registers complete,
62
+ error, and abort listeners on the host-owned transaction.
63
+
64
+ @example
65
+ ```ts
66
+ const transaction = database.transaction('batch', 'readwrite');
67
+ transaction.objectStore('batch').add(batch);
68
+ await awaitTransaction(transaction);
69
+ ```
70
70
  */
71
71
  export function awaitTransaction(transaction: IDBTransaction,): Promise<void> {
72
72
  // oxlint-disable-next-line promise/avoid-new -- IndexedDB is event-based and exposes no promise API; a constructed promise is the only bridge to await it.
@@ -7,18 +7,18 @@ import {
7
7
 
8
8
  declare global {
9
9
  // oxlint-disable-next-line typescript/consistent-type-imports -- typeof import() cannot use import type syntax
10
- var moduleLogger: typeof import('@monochromatic-dev/module-logger');
10
+ var moduleLoggerBrowser: typeof import('@monochromatic-dev/module-logger/browser');
11
11
  }
12
12
 
13
13
  test.describe('IndexedDB sink', () => {
14
14
  test.beforeEach(async ({ page, },) => {
15
15
  await page.goto('/',);
16
- await page.waitForFunction(() => globalThis.moduleLogger !== undefined);
16
+ await page.waitForFunction(() => globalThis.moduleLoggerBrowser !== undefined);
17
17
  },);
18
18
 
19
19
  test('createIndexedDbSink exposes a callable verify', async ({ page, },) => {
20
20
  const typeofVerify = await page.evaluate(() => {
21
- const { createIndexedDbSink, } = globalThis.moduleLogger.sinks;
21
+ const { createIndexedDbSink, } = globalThis.moduleLoggerBrowser;
22
22
  return typeof createIndexedDbSink().verify;
23
23
  },);
24
24
  expect(typeofVerify,).toBe('function',);
@@ -26,7 +26,7 @@ test.describe('IndexedDB sink', () => {
26
26
 
27
27
  test('verify detects availability', async ({ page, },) => {
28
28
  const result = await page.evaluate(async () => {
29
- const { createIndexedDbSink, } = globalThis.moduleLogger.sinks;
29
+ const { createIndexedDbSink, } = globalThis.moduleLoggerBrowser;
30
30
  return createIndexedDbSink().verify();
31
31
  },);
32
32
  expect(result,).toBe(true,);
@@ -34,7 +34,7 @@ test.describe('IndexedDB sink', () => {
34
34
 
35
35
  test('a verified sink writes records across levels and message shapes', async ({ page, },) => {
36
36
  const allSucceeded = await page.evaluate(async () => {
37
- const { createIndexedDbSink, } = globalThis.moduleLogger.sinks;
37
+ const { createIndexedDbSink, } = globalThis.moduleLoggerBrowser;
38
38
  const sink = createIndexedDbSink();
39
39
  await sink.verify();
40
40
  const levels = ['trace', 'debug', 'info', 'warn', 'error', 'fatal',] as const;
@@ -67,7 +67,7 @@ test.describe('IndexedDB sink', () => {
67
67
 
68
68
  test('a flushed batch is readable back out of the database as JSONL', async ({ page, },) => {
69
69
  const result = await page.evaluate(async () => {
70
- const { createIndexedDbSink, } = globalThis.moduleLogger.sinks;
70
+ const { createIndexedDbSink, } = globalThis.moduleLoggerBrowser;
71
71
  const sink = createIndexedDbSink();
72
72
  await sink.verify();
73
73
 
@@ -126,7 +126,7 @@ test.describe('IndexedDB sink', () => {
126
126
  test('retention trims the store back to the cap, oldest first', async ({ page, },) => {
127
127
  test.setTimeout(120_000,);
128
128
  const result = await page.evaluate(async () => {
129
- const { createIndexedDbSink, } = globalThis.moduleLogger.sinks;
129
+ const { createIndexedDbSink, } = globalThis.moduleLoggerBrowser;
130
130
  const sink = createIndexedDbSink();
131
131
  await sink.verify();
132
132