@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.
- package/CHANGELOG.md +27 -0
- package/README.md +53 -13
- package/dist/final/neutral/browser.d.mts +60 -0
- package/dist/final/neutral/browser.mjs +1 -0
- package/dist/final/neutral/index.d.mts +359 -596
- 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 +359 -596
- 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 -4
- package/src/artifact-platform-split.unit.test.ts +140 -0
- package/src/browser.ts +14 -0
- package/src/create-logger.ts +183 -183
- package/src/create-logger.unit.test.ts +112 -112
- package/src/default-sinks.neutral.ts +47 -0
- package/src/default-sinks.node.ts +43 -0
- package/src/error-format.ts +23 -23
- package/src/index.ts +1 -4
- package/src/logger.ts +76 -51
- package/src/node.ts +23 -0
- package/src/restricted-global-scope.unit.test.ts +101 -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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @monochromatic-dev/module-logger
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- The default logger is built by the first log or flush call instead of at import,
|
|
8
|
+
the default sink list is created at that moment,
|
|
9
|
+
and the package declares `sideEffects: false`.
|
|
10
|
+
Importing the root entry or `tagged` now runs no sink discovery,
|
|
11
|
+
no timers,
|
|
12
|
+
and no I/O,
|
|
13
|
+
so global-scope-restricted runtimes such as Cloudflare Workers start without the four sink-verification warnings.
|
|
14
|
+
The `initPromise` root export is removed;
|
|
15
|
+
`flush()` awaits readiness internally,
|
|
16
|
+
and `createLogger` still returns its instance's `initPromise`.
|
|
17
|
+
Commit `7d52ddd`.
|
|
18
|
+
|
|
19
|
+
## 0.3.0
|
|
20
|
+
|
|
21
|
+
### Minor Changes
|
|
22
|
+
|
|
23
|
+
- The root entry is now platform-neutral and neither built artifact contains a dynamic `import()`.
|
|
24
|
+
`createFileSink` moved to `@monochromatic-dev/module-logger/node`;
|
|
25
|
+
`createIndexedDbSink` and `createOpfsSink` moved to `@monochromatic-dev/module-logger/browser`.
|
|
26
|
+
The default logger keeps file logging under the `node` export condition and IndexedDB under `default`;
|
|
27
|
+
a Node consumer whose bundler resolves `default` no longer gets file logging.
|
|
28
|
+
Commit `ce38d07`.
|
|
29
|
+
|
|
3
30
|
## 0.2.0
|
|
4
31
|
|
|
5
32
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# module-logger
|
|
2
2
|
|
|
3
3
|
Zero-config multi-sink logger with tagged composition.
|
|
4
|
-
Works immediately
|
|
5
|
-
auto-discovers available backends for the current runtime,
|
|
4
|
+
Works immediately with no setup call:
|
|
5
|
+
the first log or flush call builds the default logger and auto-discovers available backends for the current runtime,
|
|
6
6
|
and records emitted while async backend verification is still pending replay to those
|
|
7
7
|
backends as soon as they verify.
|
|
8
|
-
|
|
8
|
+
Importing the package runs no discovery,
|
|
9
|
+
no timers,
|
|
10
|
+
and no I/O.
|
|
9
11
|
|
|
10
12
|
## Usage
|
|
11
13
|
|
|
@@ -56,10 +58,32 @@ Node 24 or newer (the build calls `Error.isError`),
|
|
|
56
58
|
plus current browsers,
|
|
57
59
|
Deno,
|
|
58
60
|
and Bun for the sinks whose `verify` finds a backend there.
|
|
61
|
+
### Global-scope-restricted runtimes
|
|
62
|
+
|
|
63
|
+
Cloudflare Workers (and any runtime that forbids timers,
|
|
64
|
+
I/O,
|
|
65
|
+
and random values in global scope) can import the root entry and `tagged` freely:
|
|
66
|
+
nothing is built at import,
|
|
67
|
+
so no sink probe and no timer runs in global scope.
|
|
68
|
+
The first log or flush call inside a handler builds the default logger and verifies its sinks there.
|
|
69
|
+
A Worker that wants a logger scoped to one request can still build its own with `createLogger` over `sinks.createConsoleSink()`
|
|
70
|
+
and hand `flush()` to `ctx.waitUntil`.
|
|
71
|
+
|
|
59
72
|
The published package exposes the built artifact only.
|
|
60
73
|
The `/ts` source subpath used inside this workspace is stripped at publish time,
|
|
61
74
|
because Node refuses `.ts` files under `node_modules`.
|
|
62
75
|
|
|
76
|
+
The root entry is platform-neutral and is built twice.
|
|
77
|
+
The `node` export condition serves a build whose default logger includes the file sink,
|
|
78
|
+
with static `node:fs/promises` and `node:path` imports,
|
|
79
|
+
and which carries no browser storage code.
|
|
80
|
+
Every other resolution (`default`) serves a build whose default logger includes the IndexedDB sink
|
|
81
|
+
and which references no Node module.
|
|
82
|
+
Neither build contains a dynamic `import()`,
|
|
83
|
+
and a unit test reads every chunk of both builds to keep it that way.
|
|
84
|
+
The root types are identical on both conditions.
|
|
85
|
+
A Node consumer whose bundler resolves the `default` condition gets the neutral build and no file logging.
|
|
86
|
+
|
|
63
87
|
## Log levels
|
|
64
88
|
|
|
65
89
|
Six levels,
|
|
@@ -188,15 +212,16 @@ That startup buffer holds at most `STARTUP_BUFFER_CAP` records (10000,
|
|
|
188
212
|
discards all records;
|
|
189
213
|
a stand-in that disables logging without removing log calls
|
|
190
214
|
|
|
191
|
-
Each sink is a factory
|
|
215
|
+
Each sink is a factory.
|
|
216
|
+
The cross-platform ones,
|
|
192
217
|
`createConsoleSink()`,
|
|
193
|
-
`
|
|
194
|
-
`createIndexedDbSink()`,
|
|
195
|
-
`createOpfsSink()`,
|
|
196
|
-
`createSessionStorageSink()`,
|
|
218
|
+
`createSessionStorageSink()`,
|
|
197
219
|
`createLocalStorageSink()`,
|
|
198
220
|
and `createNoopSink()`,
|
|
199
|
-
exported under the `sinks` namespace.
|
|
221
|
+
are exported under the `sinks` namespace of the root entry.
|
|
222
|
+
`createFileSink()` is exported from `@monochromatic-dev/module-logger/node`,
|
|
223
|
+
and `createIndexedDbSink()` and `createOpfsSink()` from `@monochromatic-dev/module-logger/browser`,
|
|
224
|
+
so importing a platform-only sink is the consumer's own assertion of the platform.
|
|
200
225
|
A sink instance keeps its own buffers,
|
|
201
226
|
streams,
|
|
202
227
|
and counters,
|
|
@@ -250,8 +275,10 @@ File,
|
|
|
250
275
|
|
|
251
276
|
## Error handling
|
|
252
277
|
|
|
253
|
-
-
|
|
254
|
-
|
|
278
|
+
- The default logger is built by the first log or flush call,
|
|
279
|
+
never at import;
|
|
280
|
+
there is no readiness promise to await,
|
|
281
|
+
because `flush()` awaits verification and startup replay internally
|
|
255
282
|
- `logger.flush()` awaits startup verification,
|
|
256
283
|
pending sink writes,
|
|
257
284
|
and sink-owned flush hooks,
|
|
@@ -297,10 +324,11 @@ Raise the flush deadline for a slow but working backend,
|
|
|
297
324
|
such as a network filesystem:
|
|
298
325
|
|
|
299
326
|
```ts
|
|
300
|
-
import { createLogger,
|
|
327
|
+
import { createLogger, } from '@monochromatic-dev/module-logger';
|
|
328
|
+
import { createFileSink, } from '@monochromatic-dev/module-logger/node';
|
|
301
329
|
|
|
302
330
|
const { logger, } = createLogger({
|
|
303
|
-
sinks: [
|
|
331
|
+
sinks: [createFileSink(),],
|
|
304
332
|
flushDeadlineMs: 30_000,
|
|
305
333
|
verifyTimeoutMs: 30_000,
|
|
306
334
|
},);
|
|
@@ -351,6 +379,7 @@ See [DECISIONS.md](DECISIONS.md) for rationale on:
|
|
|
351
379
|
- `flush()` has a deadline
|
|
352
380
|
- Sinks verify concurrently under a time limit
|
|
353
381
|
- The startup buffer is bounded and overflow is reported
|
|
382
|
+
- Platform-specific sinks live behind `./node` and `./browser`
|
|
354
383
|
- Zero-config at import,
|
|
355
384
|
no configure step (the logtape migration observations)
|
|
356
385
|
|
|
@@ -369,6 +398,17 @@ See [DECISIONS.md](DECISIONS.md) for rationale on:
|
|
|
369
398
|
flush)
|
|
370
399
|
- `src/logger.ts`:
|
|
371
400
|
default singleton built by applying `createLogger` to the default sinks
|
|
401
|
+
- `src/node.ts`:
|
|
402
|
+
the `./node` subpath entry,
|
|
403
|
+
`createFileSink()`
|
|
404
|
+
- `src/browser.ts`:
|
|
405
|
+
the `./browser` subpath entry,
|
|
406
|
+
`createIndexedDbSink()` and `createOpfsSink()`
|
|
407
|
+
- `src/default-sinks.node.ts` and `src/default-sinks.neutral.ts`:
|
|
408
|
+
the two default sink lists,
|
|
409
|
+
selected at build time through the `#default-sinks` entry of `package.json` `imports`
|
|
410
|
+
- `src/artifact-platform-split.unit.test.ts`:
|
|
411
|
+
guard that reads every chunk of both builds and rejects dynamic imports and cross-platform leaks
|
|
372
412
|
- `src/tagged.ts`:
|
|
373
413
|
`tagged()` wrapper for composable prefixes
|
|
374
414
|
- `src/sink/console.ts`:
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { i as Sink } from "./types-BkkBXgY3.mjs";
|
|
2
|
+
//#region src/sink/indexed-db.d.ts
|
|
3
|
+
/**
|
|
4
|
+
Builds an IndexedDB sink that buffers serialized records through the shared
|
|
5
|
+
{@link createRecordBuffer} policy and persists each newline-joined JSONL
|
|
6
|
+
batch as one string value per transaction, measured at 0.15 µs of
|
|
7
|
+
main-thread enqueue per record on headless Chromium 149 (one `add` per
|
|
8
|
+
32 KiB batch). The connection lives in this instance's closure (no
|
|
9
|
+
module-global state), so independent loggers and tests never share a
|
|
10
|
+
handle or need a reset hook.
|
|
11
|
+
|
|
12
|
+
Records are readable the moment their transaction settles (DevTools
|
|
13
|
+
Application tab included), survive tab close and browser restart, and
|
|
14
|
+
auto-incremented keys serialize across tabs, so no run-scoped naming is
|
|
15
|
+
needed. Retention trims oldest-first past {@link MAX_STORED_BATCHES}.
|
|
16
|
+
Transactions use the default relaxed durability: relaxed commits reach the
|
|
17
|
+
browser's storage backend promptly and survive renderer crashes, and the
|
|
18
|
+
OS-crash window `durability: 'strict'` would close is the rarest failure
|
|
19
|
+
class, not worth an fsync per batch.
|
|
20
|
+
|
|
21
|
+
Flush triggers (32 KiB in-write cap, `warn`-or-worse severity, 250 ms
|
|
22
|
+
quiet-period deadline, page lifecycle, and the `flush` hook) are the
|
|
23
|
+
buffer's; see {@link createRecordBuffer}. The sink's `flush` hook awaits
|
|
24
|
+
every issued batch transaction before resolving.
|
|
25
|
+
|
|
26
|
+
@returns Sink backed by IndexedDB.
|
|
27
|
+
|
|
28
|
+
@example
|
|
29
|
+
```ts
|
|
30
|
+
const { logger } = createLogger({ sinks: [createIndexedDbSink()] });
|
|
31
|
+
logger.warn('quota nearing limit');
|
|
32
|
+
```
|
|
33
|
+
*/
|
|
34
|
+
export declare function createIndexedDbSink(): Sink;
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/sink/opfs.d.ts
|
|
37
|
+
/**
|
|
38
|
+
Builds an OPFS sink that buffers serialized records through the shared
|
|
39
|
+
{@link createRecordBuffer} policy and appends each newline-joined JSONL
|
|
40
|
+
batch to a per-session file in the Origin Private File System with one
|
|
41
|
+
stream write per batch. The kept-open writable stream lives in this
|
|
42
|
+
instance's closure (no module-global state), so independent loggers and
|
|
43
|
+
tests never share a handle or need a reset hook.
|
|
44
|
+
|
|
45
|
+
Flush triggers (32 KiB in-write cap, `warn`-or-worse severity, 250 ms
|
|
46
|
+
quiet-period deadline, page lifecycle, and the `flush` hook) are the
|
|
47
|
+
buffer's; see {@link createRecordBuffer}. Batch writes queue on the stream
|
|
48
|
+
in issue order, so ordering holds at the batch boundary, and the sink's
|
|
49
|
+
`flush` hook awaits every issued batch before resolving.
|
|
50
|
+
|
|
51
|
+
@returns Sink backed by OPFS.
|
|
52
|
+
|
|
53
|
+
@example
|
|
54
|
+
```ts
|
|
55
|
+
const { logger } = createLogger({ sinks: [createOpfsSink()] });
|
|
56
|
+
logger.warn('quota nearing limit');
|
|
57
|
+
```
|
|
58
|
+
*/
|
|
59
|
+
export declare function createOpfsSink(): Sink;
|
|
60
|
+
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as createRecordBuffer,r as reportLoggerInternalError,t as createIndexedDbSink}from"./indexed-db-hsIfv7Cv.mjs";function createOpfsSink(){let state={},pendingBatchWrites=new Set;async function verify(){try{let opfsRoot=await navigator.storage.getDirectory(),timestamp=new Date().toISOString().replaceAll(`:`,`-`),fileHandle=await opfsRoot.getFileHandle(`monochromatic-${timestamp}.log.jsonl`,{create:!0}),probeWritable=await fileHandle.createWritable({keepExistingData:!0}),testData=`{"test":true,"timestamp":${Date.now()}}\n`;await probeWritable.write(testData),await probeWritable.close();let available=(await(await fileHandle.getFile()).text()).includes(`"test":true`);return available&&(state.writable=await fileHandle.createWritable({keepExistingData:!0})),available}catch(error){let opfsStorage=globalThis.navigator?.storage;return opfsStorage!==void 0&&`getDirectory`in opfsStorage&&reportLoggerInternalError({context:`OPFS sink verification failed`,error}),!1}}async function writeBatch(batch){if(state.writable)try{await state.writable.write(`${batch}\n`)}catch(error){reportLoggerInternalError({context:`OPFS sink record write failed`,error})}}async function removePendingWhenSettled(pending){await pending,pendingBatchWrites.delete(pending)}function handOffBatch(batch){let pending=writeBatch(batch);pendingBatchWrites.add(pending),removePendingWhenSettled(pending)}let buffer=createRecordBuffer({onFlush:handOffBatch});function write(record){return buffer.add({level:record.level,serialized:JSON.stringify(record)}),Promise.resolve()}async function flush(){buffer.drain();let writes=[...pendingBatchWrites];await Promise.all(writes)}return{flush,verify,write}}export{createIndexedDbSink,createOpfsSink};
|