@monochromatic-dev/module-logger 0.1.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 (63) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +58 -12
  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 +366 -572
  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 +366 -572
  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 -5
  16. package/src/artifact-platform-split.unit.test.ts +140 -0
  17. package/src/browser.ts +14 -0
  18. package/src/create-logger.ts +249 -151
  19. package/src/create-logger.unit.test.ts +527 -75
  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/index.ts +2 -0
  24. package/src/logger.ts +23 -50
  25. package/src/node.ts +23 -0
  26. package/src/sink/console-control-chars.ts +64 -64
  27. package/src/sink/console-control-chars.unit.test.ts +14 -14
  28. package/src/sink/console.ts +194 -194
  29. package/src/sink/console.unit.test.ts +18 -18
  30. package/src/sink/file.ts +136 -140
  31. package/src/sink/file.unit.test.ts +19 -26
  32. package/src/sink/index.ts +4 -7
  33. package/src/sink/indexed-db-util.ts +42 -42
  34. package/src/sink/indexed-db.browser.test.ts +7 -7
  35. package/src/sink/indexed-db.ts +109 -109
  36. package/src/sink/indexed-db.unit.test.ts +5 -13
  37. package/src/sink/local-storage-key.ts +73 -73
  38. package/src/sink/local-storage-key.unit.test.ts +8 -8
  39. package/src/sink/local-storage-quota.ts +37 -37
  40. package/src/sink/local-storage-quota.unit.test.ts +8 -8
  41. package/src/sink/local-storage-store.ts +113 -113
  42. package/src/sink/local-storage-store.unit.test.ts +35 -35
  43. package/src/sink/local-storage.ts +72 -72
  44. package/src/sink/local-storage.unit.test.ts +27 -27
  45. package/src/sink/noop.ts +20 -20
  46. package/src/sink/noop.unit.test.ts +1 -1
  47. package/src/sink/opfs.browser.test.ts +7 -7
  48. package/src/sink/opfs.ts +62 -62
  49. package/src/sink/opfs.unit.test.ts +5 -13
  50. package/src/sink/record-buffer.ts +84 -84
  51. package/src/sink/record-buffer.unit.test.ts +20 -20
  52. package/src/sink/session-storage-quota.ts +34 -34
  53. package/src/sink/session-storage-quota.unit.test.ts +8 -8
  54. package/src/sink/session-storage-store.ts +72 -72
  55. package/src/sink/session-storage.ts +48 -48
  56. package/src/sink/session-storage.unit.test.ts +39 -39
  57. package/src/sink/web-storage-quota-error.ts +22 -22
  58. package/src/sink/web-storage-quota-error.unit.test.ts +2 -2
  59. package/src/sink/web-storage-runtime.ts +24 -24
  60. package/src/startup.unit.test.ts +18 -18
  61. package/src/tagged.ts +35 -35
  62. package/src/tagged.unit.test.ts +8 -8
  63. package/src/types.ts +39 -39
@@ -1,16 +1,16 @@
1
1
  /**
2
- * Quota-overflow recognition shared by the web storage persistence engines.
3
- *
4
- * @module
2
+ Quota-overflow recognition shared by the web storage persistence engines.
3
+
4
+ @module
5
5
  */
6
6
 
7
7
  /**
8
- * Error `name` values engines raise for a storage quota overflow: the DOM
9
- * standard name every current browser and Node web storage use, plus Firefox's
10
- * legacy alias. Quota overflow is matched by `name` rather than by class or
11
- * numeric `code` because the concrete type differs by engine (a `DOMException`
12
- * in Chromium and Node, a differently-branded object historically in Firefox),
13
- * while the standard name is stable across them.
8
+ Error `name` values engines raise for a storage quota overflow: the DOM
9
+ standard name every current browser and Node web storage use, plus Firefox's
10
+ legacy alias. Quota overflow is matched by `name` rather than by class or
11
+ numeric `code` because the concrete type differs by engine (a `DOMException`
12
+ in Chromium and Node, a differently-branded object historically in Firefox),
13
+ while the standard name is stable across them.
14
14
  */
15
15
  const QUOTA_EXCEEDED_NAMES: ReadonlySet<string> = new Set([
16
16
  'QuotaExceededError',
@@ -18,19 +18,19 @@ const QUOTA_EXCEEDED_NAMES: ReadonlySet<string> = new Set([
18
18
  ],);
19
19
 
20
20
  /**
21
- * Reports whether a caught `setItem` value is a storage quota overflow, so
22
- * eviction reclaims space only for a full store and never for an unrelated
23
- * write fault such as a disabled-storage `SecurityError`.
24
- *
25
- * @param error - Caught value from a `setItem` failure.
26
- *
27
- * @returns Whether `error` names a quota overflow.
28
- *
29
- * @example
30
- * ```ts
31
- * try { sessionStorage.setItem(k, v); }
32
- * catch (error: unknown) { if (isQuotaExceededError(error)) evictOldest(); }
33
- * ```
21
+ Reports whether a caught `setItem` value is a storage quota overflow, so
22
+ eviction reclaims space only for a full store and never for an unrelated
23
+ write fault such as a disabled-storage `SecurityError`.
24
+
25
+ @param error - Caught value from a `setItem` failure.
26
+
27
+ @returns Whether `error` names a quota overflow.
28
+
29
+ @example
30
+ ```ts
31
+ try { sessionStorage.setItem(k, v); }
32
+ catch (error: unknown) { if (isQuotaExceededError(error)) evictOldest(); }
33
+ ```
34
34
  */
35
35
  export function isQuotaExceededError(error: unknown,): boolean {
36
36
  return (
@@ -14,7 +14,7 @@ await describe({
14
14
  name: 'recognizes the standard DOMException name',
15
15
  fn: async () => {
16
16
  /**
17
- * Overflow shaped exactly as current engines raise it.
17
+ Overflow shaped exactly as current engines raise it.
18
18
  */
19
19
  const overflow = new DOMException('full', 'QuotaExceededError',);
20
20
  expect(isQuotaExceededError(overflow,),)
@@ -34,7 +34,7 @@ await describe({
34
34
  name: 'rejects a plain Error whose message merely mentions quota',
35
35
  fn: async () => {
36
36
  /**
37
- * Non-quota failure that only talks about quota in prose.
37
+ Non-quota failure that only talks about quota in prose.
38
38
  */
39
39
  const impostor = new Error('quota exceeded',);
40
40
  expect(isQuotaExceededError(impostor,),)
@@ -1,35 +1,35 @@
1
1
  /**
2
- * Shared host-runtime detection for web storage quota heuristics.
3
- *
4
- * The Web Storage API exposes no way to read a store's quota, so the
5
- * per-storage-area quota modules pair this detection with their own tables of
6
- * measured defaults. Detection is by host global rather than user-agent
7
- * string: Deno and Bun both shim `process` with a Node-compatible
8
- * `process.versions.node`, so their own globals are tested before the Node
9
- * check to avoid misclassifying them.
10
- *
11
- * @module
2
+ Shared host-runtime detection for web storage quota heuristics.
3
+
4
+ The Web Storage API exposes no way to read a store's quota, so the
5
+ per-storage-area quota modules pair this detection with their own tables of
6
+ measured defaults. Detection is by host global rather than user-agent
7
+ string: Deno and Bun both shim `process` with a Node-compatible
8
+ `process.versions.node`, so their own globals are tested before the Node
9
+ check to avoid misclassifying them.
10
+
11
+ @module
12
12
  */
13
13
 
14
14
  /**
15
- * Host runtimes distinguishable by global probes, plus `unknown` for
16
- * everything else so callers fall back to uncapped reactive eviction.
15
+ Host runtimes distinguishable by global probes, plus `unknown` for
16
+ everything else so callers fall back to uncapped reactive eviction.
17
17
  */
18
18
  export type WebStorageRuntime = 'browser' | 'bun' | 'deno' | 'node' | 'unknown';
19
19
 
20
20
  /**
21
- * Detects the current host runtime for web storage quota lookups.
22
- *
23
- * A `node` result also covers Node-embedding hosts such as Electron, whose
24
- * renderer exposes `process.versions.node` alongside a DOM; callers that need
25
- * to tell those apart check `'document' in globalThis` themselves.
26
- *
27
- * @returns Detected runtime, or `unknown` when no marker global matches.
28
- *
29
- * @example
30
- * ```ts
31
- * const quota = RUNTIME_QUOTA_CHARS[detectWebStorageRuntime()];
32
- * ```
21
+ Detects the current host runtime for web storage quota lookups.
22
+
23
+ A `node` result also covers Node-embedding hosts such as Electron, whose
24
+ renderer exposes `process.versions.node` alongside a DOM; callers that need
25
+ to tell those apart check `'document' in globalThis` themselves.
26
+
27
+ @returns Detected runtime, or `unknown` when no marker global matches.
28
+
29
+ @example
30
+ ```ts
31
+ const quota = RUNTIME_QUOTA_CHARS[detectWebStorageRuntime()];
32
+ ```
33
33
  */
34
34
  export function detectWebStorageRuntime(): WebStorageRuntime {
35
35
  if ('Deno' in globalThis)
@@ -52,12 +52,12 @@ type ProbeResult = {
52
52
  };
53
53
 
54
54
  /**
55
- * Builds a throwaway project root with `node_modules` so the file sink chooses
56
- * an isolated `node_modules/.monochromatic` log directory.
57
- *
58
- * @param logLine - Logger call that the generated probe should execute.
59
- *
60
- * @returns Temporary project handle removed by `await using`.
55
+ Builds a throwaway project root with `node_modules` so the file sink chooses
56
+ an isolated `node_modules/.monochromatic` log directory.
57
+
58
+ @param logLine - Logger call that the generated probe should execute.
59
+
60
+ @returns Temporary project handle removed by `await using`.
61
61
  */
62
62
  async function createTempProject(
63
63
  { logLine, }: { readonly logLine: string; },
@@ -99,13 +99,13 @@ async function createTempProject(
99
99
  }
100
100
 
101
101
  /**
102
- * Runs a probe script in its temporary project root.
103
- *
104
- * @param cwd - Project root used as `process.cwd()` by the file sink.
105
- * @param env - Environment overrides applied to the probe process.
106
- * @param scriptPath - Absolute path to the probe script.
107
- *
108
- * @returns Captured stdout, stderr, and exit code.
102
+ Runs a probe script in its temporary project root.
103
+
104
+ @param cwd - Project root used as `process.cwd()` by the file sink.
105
+ @param env - Environment overrides applied to the probe process.
106
+ @param scriptPath - Absolute path to the probe script.
107
+
108
+ @returns Captured stdout, stderr, and exit code.
109
109
  */
110
110
  async function runProbe(
111
111
  {
@@ -151,11 +151,11 @@ async function runProbe(
151
151
  }
152
152
 
153
153
  /**
154
- * Reads the single JSONL file created by the probe's file sink.
155
- *
156
- * @param projectPath - Temporary project root containing `node_modules`.
157
- *
158
- * @returns Log file contents.
154
+ Reads the single JSONL file created by the probe's file sink.
155
+
156
+ @param projectPath - Temporary project root containing `node_modules`.
157
+
158
+ @returns Log file contents.
159
159
  */
160
160
  async function readOnlyLogContent({ projectPath, }: { readonly projectPath: string; },): Promise<string> {
161
161
  const logDir = join(
package/src/tagged.ts CHANGED
@@ -2,40 +2,40 @@ import { logger as defaultLogger, } from './logger.ts';
2
2
  import type { Logger, } from './types.ts';
3
3
 
4
4
  /**
5
- * Wraps a logger so every message is prefixed with `[tag] `.
6
- * Callers typically pass `myFn.name` as tag to keep prefixes
7
- * in sync with refactors.
8
- *
9
- * @param tag - Prefix string inserted before each message
10
- *
11
- * @param l - Base logger to wrap; defaults to the module-level {@link logger}
12
- * singleton
13
- *
14
- * @returns Logger whose methods prepend `[tag] ` to every message
15
- *
16
- * @example
17
- * ```ts
18
- * import { tagged } from '\@monochromatic-dev/module-logger/tagged';
19
- *
20
- * function handleRequest({ l }: { l: Logger }): void {
21
- * l.info('received');
22
- * }
23
- *
24
- * handleRequest({ l: tagged({ tag: handleRequest.name }) });
25
- * // logs: [handleRequest] received
26
- * ```
27
- *
28
- * @example
29
- * ```ts
30
- * // Composing tags: the outermost wrap (`l2` here) prepends to the message
31
- * // last, so its tag ends up rightmost. The innermost wrap (`l1`) hits the
32
- * // underlying logger first, so its tag is leftmost. The chain reads
33
- * // root-first: outer wrap = inner tag position.
34
- * const l1 = tagged({ tag: 'http' });
35
- * const l2 = tagged({ tag: 'retry', l: l1 });
36
- * l2.info('attempt 3');
37
- * // logs: [http] [retry] attempt 3
38
- * ```
5
+ Wraps a logger so every message is prefixed with `[tag] `.
6
+ Callers typically pass `myFn.name` as tag to keep prefixes
7
+ in sync with refactors.
8
+
9
+ @param tag - Prefix string inserted before each message
10
+
11
+ @param l - Base logger to wrap; defaults to the module-level {@link logger}
12
+ singleton
13
+
14
+ @returns Logger whose methods prepend `[tag] ` to every message
15
+
16
+ @example
17
+ ```ts
18
+ import { tagged } from '\@monochromatic-dev/module-logger/tagged';
19
+
20
+ function handleRequest({ l }: { l: Logger }): void {
21
+ l.info('received');
22
+ }
23
+
24
+ handleRequest({ l: tagged({ tag: handleRequest.name }) });
25
+ // logs: [handleRequest] received
26
+ ```
27
+
28
+ @example
29
+ ```ts
30
+ // Composing tags: the outermost wrap (`l2` here) prepends to the message
31
+ // last, so its tag ends up rightmost. The innermost wrap (`l1`) hits the
32
+ // underlying logger first, so its tag is leftmost. The chain reads
33
+ // root-first: outer wrap = inner tag position.
34
+ const l1 = tagged({ tag: 'http' });
35
+ const l2 = tagged({ tag: 'retry', l: l1 });
36
+ l2.info('attempt 3');
37
+ // logs: [http] [retry] attempt 3
38
+ ```
39
39
  */
40
40
  export function tagged({
41
41
  tag,
@@ -45,7 +45,7 @@ export function tagged({
45
45
  readonly tag: string;
46
46
  },): Logger {
47
47
  /**
48
- * Bracketed tag prepended to every message; built once so each log call does one concatenation.
48
+ Bracketed tag prepended to every message; built once so each log call does one concatenation.
49
49
  */
50
50
  const prefix = `[${tag}] `;
51
51
  return {
@@ -10,8 +10,8 @@ import {
10
10
  } from '@monochromatic-dev/module-logger';
11
11
 
12
12
  /**
13
- * Every log level paired with the message body the all-levels test sends
14
- * through each, so the assertion can be built from one source of truth.
13
+ Every log level paired with the message body the all-levels test sends
14
+ through each, so the assertion can be built from one source of truth.
15
15
  */
16
16
  const LEVEL_MESSAGES: readonly { readonly level: Level; readonly message: string; }[] = [
17
17
  {
@@ -41,12 +41,12 @@ const LEVEL_MESSAGES: readonly { readonly level: Level; readonly message: string
41
41
  ];
42
42
 
43
43
  /**
44
- * Builds a stub logger whose methods record the messages they receive
45
- * and whose `flush` is a trivial resolved promise. Used to verify the
46
- * tagged wrapper's message prefixing and `flush` forwarding without
47
- * touching the default multi-sink singleton.
48
- *
49
- * @returns Object containing the stub logger and the recorded message list.
44
+ Builds a stub logger whose methods record the messages they receive
45
+ and whose `flush` is a trivial resolved promise. Used to verify the
46
+ tagged wrapper's message prefixing and `flush` forwarding without
47
+ touching the default multi-sink singleton.
48
+
49
+ @returns Object containing the stub logger and the recorded message list.
50
50
  */
51
51
  function createStubLogger(): {
52
52
  l: Logger;
package/src/types.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  /**
2
- * Log severity levels ordered from least to most severe.
2
+ Log severity levels ordered from least to most severe.
3
3
  */
4
4
  export type Level = 'debug' | 'error' | 'fatal' | 'info' | 'trace' | 'warn';
5
5
 
6
6
  /**
7
- * Structured log record written to sinks.
7
+ Structured log record written to sinks.
8
8
  */
9
9
  export type LogRecord = {
10
10
  readonly level: Level;
@@ -13,47 +13,47 @@ export type LogRecord = {
13
13
  };
14
14
 
15
15
  /**
16
- * Optional drain hook for sinks that buffer records internally.
17
- * Called via logger-level {@link Logger.flush} to force buffered work
18
- * through before a process exit, critical error boundary, or assertion.
19
- *
20
- * Always async: sinks whose drain is synchronous return an
21
- * already-resolved promise so callers `await` uniformly. A `void` arm is
22
- * not used; under the `no-optional-escape` rule `T | void` is a banned
23
- * fake-optional encoding, and there is no real synchronous value to carry.
16
+ Optional drain hook for sinks that buffer records internally.
17
+ Called via logger-level {@link Logger.flush} to force buffered work
18
+ through before a process exit, critical error boundary, or assertion.
19
+
20
+ Always async: sinks whose drain is synchronous return an
21
+ already-resolved promise so callers `await` uniformly. A `void` arm is
22
+ not used; under the `no-optional-escape` rule `T | void` is a banned
23
+ fake-optional encoding, and there is no real synchronous value to carry.
24
24
  */
25
25
  export type SinkFlush = () => Promise<void>;
26
26
 
27
27
  /**
28
- * Verification function that checks if a sink backend is available.
29
- * May run setup side effects (resolving a log path, opening a writable
30
- * stream) and reports whether the backend is usable. A sink whose
31
- * verification resolves `false` (or rejects) is dropped by the logger and
32
- * receives no further records.
33
- *
34
- * Always async, matching `write` and `flush`: a synchronous check returns an
35
- * already-resolved promise (`Promise.resolve(check)`) so the logger awaits
36
- * verification uniformly with no sync/async branch.
28
+ Verification function that checks if a sink backend is available.
29
+ May run setup side effects (resolving a log path, opening a writable
30
+ stream) and reports whether the backend is usable. A sink whose
31
+ verification resolves `false` (or rejects) is dropped by the logger and
32
+ receives no further records.
33
+
34
+ Always async, matching `write` and `flush`: a synchronous check returns an
35
+ already-resolved promise (`Promise.resolve(check)`) so the logger awaits
36
+ verification uniformly with no sync/async branch.
37
37
  */
38
38
  export type Verify = () => Promise<boolean>;
39
39
 
40
40
  /**
41
- * Sink that receives log records. A sink is a self-describing adapter: it
42
- * carries everything the logger must know to use it, namely how to
43
- * `verify` its backend is available, how to `write` a record, and
44
- * optionally how to `flush` buffered work. Holding `verify` on the sink
45
- * (rather than as a sibling export the logger pairs by hand) lets the
46
- * logger treat a registry as a plain `Sink[]` and lets a test supply one
47
- * self-contained fake.
48
- *
49
- * Sinks that buffer records (e.g. microtask-batched console) may
50
- * expose a `flush` hook so callers can force emission on demand.
51
- *
52
- * `write` is always async: a synchronous sink does its work eagerly and
53
- * returns an already-resolved promise, so the logger observes a uniform
54
- * `Promise<void>`. A rejected write is handled per sink and does not
55
- * disable the backend; only a failed `verify` drops a sink. A `void` arm
56
- * is not used, for the reason stated on {@link SinkFlush}.
41
+ Sink that receives log records. A sink is a self-describing adapter: it
42
+ carries everything the logger must know to use it, namely how to
43
+ `verify` its backend is available, how to `write` a record, and
44
+ optionally how to `flush` buffered work. Holding `verify` on the sink
45
+ (rather than as a sibling export the logger pairs by hand) lets the
46
+ logger treat a registry as a plain `Sink[]` and lets a test supply one
47
+ self-contained fake.
48
+
49
+ Sinks that buffer records (e.g. microtask-batched console) may
50
+ expose a `flush` hook so callers can force emission on demand.
51
+
52
+ `write` is always async: a synchronous sink does its work eagerly and
53
+ returns an already-resolved promise, so the logger observes a uniform
54
+ `Promise<void>`. A rejected write is handled per sink and does not
55
+ disable the backend; only a failed `verify` drops a sink. A `void` arm
56
+ is not used, for the reason stated on {@link SinkFlush}.
57
57
  */
58
58
  export type Sink = {
59
59
  readonly flush?: SinkFlush;
@@ -62,10 +62,10 @@ export type Sink = {
62
62
  };
63
63
 
64
64
  /**
65
- * Logger interface with 6 log levels plus `flush` for startup and sink drains.
66
- * `flush()` resolves once startup verification has completed, tracked sink
67
- * writes have settled, and every available sink's own {@link SinkFlush} hook
68
- * has settled. Safe to call even when no sink buffers.
65
+ Logger interface with 6 log levels plus `flush` for startup and sink drains.
66
+ `flush()` resolves once startup verification has completed, tracked sink
67
+ writes have settled, and every available sink's own {@link SinkFlush} hook
68
+ has settled. Safe to call even when no sink buffers.
69
69
  */
70
70
  export type Logger = {
71
71
  readonly debug: (message: string,) => void;