@holz/json-backend 0.8.2 → 0.8.3-rc.154
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/README.md +50 -5
- package/dist/holz-json-backend.cjs +1 -1
- package/dist/holz-json-backend.js +63 -37
- package/dist/src/__tests__/json-backend.test.d.ts +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/json-backend.d.ts +30 -0
- package/package.json +11 -11
- package/dist/holz-json-backend.d.ts +0 -22
package/README.md
CHANGED
|
@@ -1,21 +1,66 @@
|
|
|
1
1
|
# `@holz/json-backend`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Serializes structured logs to a writable stream in [NDJSON](https://github.com/ndjson/ndjson-spec) format.
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
7
|
+
This backend serializes logs into a [`WritableStream<Uint8Array>`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream). Each chunk is UTF-8 encoded. Ideal for append-only log files.
|
|
8
|
+
|
|
9
|
+
Target environments are servers, host endpoints, and browsers (via [OPFS](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle/createWritable)).
|
|
10
|
+
|
|
7
11
|
```typescript
|
|
8
12
|
import { createJsonBackend } from '@holz/json-backend';
|
|
9
13
|
import { createLogger } from '@holz/core';
|
|
10
|
-
import * as fs from 'node:fs';
|
|
11
14
|
|
|
12
15
|
const logger = createLogger(
|
|
13
16
|
createJsonBackend({
|
|
14
|
-
stream:
|
|
17
|
+
stream: new WritableStream<Uint8Array>(
|
|
18
|
+
{
|
|
19
|
+
// ...
|
|
20
|
+
},
|
|
21
|
+
new CountQueuingStrategy({
|
|
22
|
+
highWaterMark: 1024,
|
|
23
|
+
}),
|
|
24
|
+
),
|
|
15
25
|
}),
|
|
16
26
|
);
|
|
17
27
|
```
|
|
18
28
|
|
|
19
|
-
|
|
29
|
+
### Node Streams
|
|
30
|
+
|
|
31
|
+
Node's streams shipped long before the web standard and need to be wrapped with [Writable.toWeb](https://nodejs.org/api/stream.html#streamwritabletowebstreamwritable):
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { createWriteStream } from 'node:fs';
|
|
35
|
+
import { Writable } from 'node:stream';
|
|
36
|
+
|
|
37
|
+
createJsonBackend({
|
|
38
|
+
stream: Writable.toWeb(createWriteStream('logs.ndjson', { flags: 'a' })),
|
|
39
|
+
}),
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The same adapter works for any Node writable, including `process.stderr`.
|
|
43
|
+
|
|
44
|
+
### Closing the Stream
|
|
45
|
+
|
|
46
|
+
By default, the stream remains open forever. Pass an [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) to control the shutdown.
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
const controller = new AbortController();
|
|
50
|
+
|
|
51
|
+
createJsonBackend({
|
|
52
|
+
stream: writableStream,
|
|
53
|
+
signal: controller.signal,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Permanently close the writable stream.
|
|
57
|
+
controller.abort();
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Tap the stream's [close](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream/WritableStream#closecontroller) handler or node's [finish](https://nodejs.org/api/fs.html#event-finish) event for a clean shutdown.
|
|
61
|
+
|
|
62
|
+
## Caveats
|
|
20
63
|
|
|
21
|
-
|
|
64
|
+
- Writes are fire-and-forget. Stream backpressure from an overwhelmed sink will cause logs to be dropped. Use [CountQueuingStrategy](https://developer.mozilla.org/en-US/docs/Web/API/CountQueuingStrategy) to adjust the log buffer.
|
|
65
|
+
- Long-running processes and noisy services create giant files. Lean on your environment's tools (`journalctl`) or use a package like [rotating-file-stream](https://github.com/iccicci/rotating-file-stream).
|
|
66
|
+
- OPFS streams in browser environments have poor durability. You may need synchronous access to periodically flush writes, an API only available in workers.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e={fatal:60,error:50,warn:40,info:30,debug:20,trace:10},t=(n,r)=>{let i=(e,t,i={})=>{n({timestamp:Date.now(),message:t,level:e,origin:r,context:i})},a={owner:r,trace:i.bind(null,e.trace),debug:i.bind(null,e.debug),info:i.bind(null,e.info),warn:i.bind(null,e.warn),error:i.bind(null,e.error),fatal:i.bind(null,e.fatal),namespace:e=>t(n,r.concat(e)),withMiddleware:e=>t(e(n),r)},o={configurable:!1,enumerable:!1};return Object.defineProperties(a,{withMiddleware:o,namespace:o,trace:o,debug:o,info:o,warn:o,error:o,fatal:o})},n=new TextEncoder,r=({stream:e,signal:t})=>{let r=e.getWriter();return t?.aborted?r.close().catch(i):t?.addEventListener(`abort`,()=>void r.close().catch(i),{once:!0}),e=>{if(r.desiredSize===null||r.desiredSize<=0)return;let t=JSON.stringify({level:o[e.level],time:new Date(e.timestamp).toISOString(),msg:e.message,origin:e.origin,ctx:Object.keys(e.context).length>0?e.context:void 0},a);r.write(n.encode(`${t}\n`)).catch(i)}},i=()=>{},a=(e,t)=>t instanceof Error?{...t,name:t.name,message:t.message,cause:t.cause,...t instanceof AggregateError&&{errors:t.errors}}:t,o=Object.fromEntries(Object.entries(e).map(([e,t])=>[t,e]));exports.createJsonBackend=r;
|
|
@@ -1,37 +1,63 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
1
|
+
//#region ../holz-core/dist/holz-core.js
|
|
2
|
+
var e = {
|
|
3
|
+
fatal: 60,
|
|
4
|
+
error: 50,
|
|
5
|
+
warn: 40,
|
|
6
|
+
info: 30,
|
|
7
|
+
debug: 20,
|
|
8
|
+
trace: 10
|
|
9
|
+
}, t = (n, r) => {
|
|
10
|
+
let i = (e, t, i = {}) => {
|
|
11
|
+
n({
|
|
12
|
+
timestamp: Date.now(),
|
|
13
|
+
message: t,
|
|
14
|
+
level: e,
|
|
15
|
+
origin: r,
|
|
16
|
+
context: i
|
|
17
|
+
});
|
|
18
|
+
}, a = {
|
|
19
|
+
owner: r,
|
|
20
|
+
trace: i.bind(null, e.trace),
|
|
21
|
+
debug: i.bind(null, e.debug),
|
|
22
|
+
info: i.bind(null, e.info),
|
|
23
|
+
warn: i.bind(null, e.warn),
|
|
24
|
+
error: i.bind(null, e.error),
|
|
25
|
+
fatal: i.bind(null, e.fatal),
|
|
26
|
+
namespace: (e) => t(n, r.concat(e)),
|
|
27
|
+
withMiddleware: (e) => t(e(n), r)
|
|
28
|
+
}, o = {
|
|
29
|
+
configurable: !1,
|
|
30
|
+
enumerable: !1
|
|
31
|
+
};
|
|
32
|
+
return Object.defineProperties(a, {
|
|
33
|
+
withMiddleware: o,
|
|
34
|
+
namespace: o,
|
|
35
|
+
trace: o,
|
|
36
|
+
debug: o,
|
|
37
|
+
info: o,
|
|
38
|
+
warn: o,
|
|
39
|
+
error: o,
|
|
40
|
+
fatal: o
|
|
41
|
+
});
|
|
42
|
+
}, n = new TextEncoder(), r = ({ stream: e, signal: t }) => {
|
|
43
|
+
let r = e.getWriter();
|
|
44
|
+
return t?.aborted ? r.close().catch(i) : t?.addEventListener("abort", () => void r.close().catch(i), { once: !0 }), (e) => {
|
|
45
|
+
if (r.desiredSize === null || r.desiredSize <= 0) return;
|
|
46
|
+
let t = JSON.stringify({
|
|
47
|
+
level: o[e.level],
|
|
48
|
+
time: new Date(e.timestamp).toISOString(),
|
|
49
|
+
msg: e.message,
|
|
50
|
+
origin: e.origin,
|
|
51
|
+
ctx: Object.keys(e.context).length > 0 ? e.context : void 0
|
|
52
|
+
}, a);
|
|
53
|
+
r.write(n.encode(`${t}\n`)).catch(i);
|
|
54
|
+
};
|
|
55
|
+
}, i = () => {}, a = (e, t) => t instanceof Error ? {
|
|
56
|
+
...t,
|
|
57
|
+
name: t.name,
|
|
58
|
+
message: t.message,
|
|
59
|
+
cause: t.cause,
|
|
60
|
+
...t instanceof AggregateError && { errors: t.errors }
|
|
61
|
+
} : t, o = Object.fromEntries(Object.entries(e).map(([e, t]) => [t, e]));
|
|
62
|
+
//#endregion
|
|
63
|
+
export { r as createJsonBackend };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createJsonBackend } from './json-backend';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { LogProcessor } from '@holz/core';
|
|
2
|
+
/**
|
|
3
|
+
* Prints structured logs to a writable stream in NDJSON form. Optimized for
|
|
4
|
+
* log files.
|
|
5
|
+
*
|
|
6
|
+
* Writes to a Web Streams `WritableStream<Uint8Array>` so the same backend
|
|
7
|
+
* runs across Node, Deno, Bun, and the browser (e.g. OPFS). See the readme
|
|
8
|
+
* for a Node file-stream example.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* createJsonBackend({
|
|
12
|
+
* stream: new WritableStream({
|
|
13
|
+
* write: (chunk) => void process.stdout.write(chunk),
|
|
14
|
+
* }),
|
|
15
|
+
* })
|
|
16
|
+
*
|
|
17
|
+
* @see https://github.com/ndjson/ndjson-spec
|
|
18
|
+
*/
|
|
19
|
+
export declare const createJsonBackend: ({ stream, signal }: Config) => LogProcessor;
|
|
20
|
+
interface Config {
|
|
21
|
+
/** Where to print logs. Each log is written as a line of UTF-8 NDJSON. */
|
|
22
|
+
stream: WritableStream<Uint8Array>;
|
|
23
|
+
/**
|
|
24
|
+
* Closes the stream on abort, flushing queued writes and committing
|
|
25
|
+
* OPFS-style sinks. Fire-and-forget: to await the flush, observe your own
|
|
26
|
+
* sink or resource completion. Logs are dropped once shutdown begins.
|
|
27
|
+
*/
|
|
28
|
+
signal?: AbortSignal;
|
|
29
|
+
}
|
|
30
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holz/json-backend",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3-rc.154+3f5148e",
|
|
4
4
|
"description": "Print logs as newline-delimited JSON.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -32,18 +32,18 @@
|
|
|
32
32
|
],
|
|
33
33
|
"scripts": {
|
|
34
34
|
"prepack": "vite build",
|
|
35
|
-
"test:unit": "vitest --color --passWithNoTests",
|
|
35
|
+
"test:unit": "vitest run --color --passWithNoTests",
|
|
36
36
|
"test:types": "tsc"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@holz/core": "^0.8.
|
|
40
|
-
"@types/node": "^
|
|
41
|
-
"@vitest/coverage-v8": "^
|
|
42
|
-
"typescript": "^
|
|
43
|
-
"vite": "^
|
|
44
|
-
"vite-plugin-dts": "^
|
|
45
|
-
"vite-tsconfig-paths": "^
|
|
46
|
-
"vitest": "^
|
|
39
|
+
"@holz/core": "^0.8.3-rc.154+3f5148e",
|
|
40
|
+
"@types/node": "^24.0.0",
|
|
41
|
+
"@vitest/coverage-v8": "^4.0.0",
|
|
42
|
+
"typescript": "^6.0.0",
|
|
43
|
+
"vite": "^8.0.0",
|
|
44
|
+
"vite-plugin-dts": "^5.0.0",
|
|
45
|
+
"vite-tsconfig-paths": "^6.0.0",
|
|
46
|
+
"vitest": "^4.0.0"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "3f5148eb94bb1d7200d4b4993c95ea30db76b0c4"
|
|
49
49
|
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { LogProcessor } from '@holz/core';
|
|
2
|
-
import { Writable } from 'node:stream';
|
|
3
|
-
|
|
4
|
-
declare interface Config {
|
|
5
|
-
/** Where to print logs. */
|
|
6
|
-
stream: Writable;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Prints structured logs to a writable stream in NDJSON form. Optimized for
|
|
11
|
-
* log files.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* createJsonBackend({
|
|
15
|
-
* stream: fs.createWriteStream('my-app.log', { flags: 'a' }),
|
|
16
|
-
* })
|
|
17
|
-
*
|
|
18
|
-
* @see http://ndjson.org
|
|
19
|
-
*/
|
|
20
|
-
export declare const createJsonBackend: ({ stream }: Config) => LogProcessor;
|
|
21
|
-
|
|
22
|
-
export { }
|