@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.
- package/CHANGELOG.md +27 -0
- package/README.md +58 -12
- package/dist/final/neutral/browser.d.mts +60 -0
- package/dist/final/neutral/browser.mjs +1 -0
- package/dist/final/neutral/index.d.mts +366 -572
- package/dist/final/neutral/index.mjs +2 -3
- package/dist/final/neutral/indexed-db-hsIfv7Cv.mjs +2 -0
- package/dist/final/neutral/types-BkkBXgY3.d.mts +76 -0
- package/dist/final/node/file-CRGb1hDK.mjs +1 -0
- package/dist/final/node/index.d.mts +366 -572
- package/dist/final/node/index.mjs +3 -3
- package/dist/final/node/node.d.mts +103 -0
- package/dist/final/node/node.mjs +1 -0
- package/dist/final/node/types-BkkBXgY3.d.mts +76 -0
- package/package.json +19 -5
- package/src/artifact-platform-split.unit.test.ts +140 -0
- package/src/browser.ts +14 -0
- package/src/create-logger.ts +249 -151
- package/src/create-logger.unit.test.ts +527 -75
- package/src/default-sinks.neutral.ts +34 -0
- package/src/default-sinks.node.ts +32 -0
- package/src/error-format.ts +23 -23
- package/src/index.ts +2 -0
- package/src/logger.ts +23 -50
- package/src/node.ts +23 -0
- package/src/sink/console-control-chars.ts +64 -64
- package/src/sink/console-control-chars.unit.test.ts +14 -14
- package/src/sink/console.ts +194 -194
- package/src/sink/console.unit.test.ts +18 -18
- package/src/sink/file.ts +136 -140
- package/src/sink/file.unit.test.ts +19 -26
- package/src/sink/index.ts +4 -7
- package/src/sink/indexed-db-util.ts +42 -42
- package/src/sink/indexed-db.browser.test.ts +7 -7
- package/src/sink/indexed-db.ts +109 -109
- package/src/sink/indexed-db.unit.test.ts +5 -13
- package/src/sink/local-storage-key.ts +73 -73
- package/src/sink/local-storage-key.unit.test.ts +8 -8
- package/src/sink/local-storage-quota.ts +37 -37
- package/src/sink/local-storage-quota.unit.test.ts +8 -8
- package/src/sink/local-storage-store.ts +113 -113
- package/src/sink/local-storage-store.unit.test.ts +35 -35
- package/src/sink/local-storage.ts +72 -72
- package/src/sink/local-storage.unit.test.ts +27 -27
- package/src/sink/noop.ts +20 -20
- package/src/sink/noop.unit.test.ts +1 -1
- package/src/sink/opfs.browser.test.ts +7 -7
- package/src/sink/opfs.ts +62 -62
- package/src/sink/opfs.unit.test.ts +5 -13
- package/src/sink/record-buffer.ts +84 -84
- package/src/sink/record-buffer.unit.test.ts +20 -20
- package/src/sink/session-storage-quota.ts +34 -34
- package/src/sink/session-storage-quota.unit.test.ts +8 -8
- package/src/sink/session-storage-store.ts +72 -72
- package/src/sink/session-storage.ts +48 -48
- package/src/sink/session-storage.unit.test.ts +39 -39
- package/src/sink/web-storage-quota-error.ts +22 -22
- package/src/sink/web-storage-quota-error.unit.test.ts +2 -2
- package/src/sink/web-storage-runtime.ts +24 -24
- package/src/startup.unit.test.ts +18 -18
- package/src/tagged.ts +35 -35
- package/src/tagged.unit.test.ts +8 -8
- package/src/types.ts +39 -39
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
Quota-overflow recognition shared by the web storage persistence engines.
|
|
3
|
+
|
|
4
|
+
@module
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
16
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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)
|
package/src/startup.unit.test.ts
CHANGED
|
@@ -52,12 +52,12 @@ type ProbeResult = {
|
|
|
52
52
|
};
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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 {
|
package/src/tagged.unit.test.ts
CHANGED
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
} from '@monochromatic-dev/module-logger';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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;
|