@kar-mi/spirit-vale-tools-logging 0.9.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/README.md +70 -0
- package/dist/combat-sanitizer.d.ts +4 -0
- package/dist/combat-sanitizer.d.ts.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +984 -0
- package/dist/jsonl-tail-reader.d.ts +38 -0
- package/dist/jsonl-tail-reader.d.ts.map +1 -0
- package/dist/logger.d.ts +98 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/paths.d.ts +6 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/predicates.d.ts +6 -0
- package/dist/predicates.d.ts.map +1 -0
- package/dist/record-codec.d.ts +10 -0
- package/dist/record-codec.d.ts.map +1 -0
- package/dist/session-follower.d.ts +42 -0
- package/dist/session-follower.d.ts.map +1 -0
- package/dist/sessions.d.ts +3 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/stream-source.d.ts +39 -0
- package/dist/stream-source.d.ts.map +1 -0
- package/dist/types.d.ts +45 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +25 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface JsonlTailReadResult {
|
|
2
|
+
missing: boolean;
|
|
3
|
+
reset: boolean;
|
|
4
|
+
lines: string[];
|
|
5
|
+
size: number;
|
|
6
|
+
/** Bytes taken from the file by this read, including any that only formed a partial line. */
|
|
7
|
+
bytesRead: number;
|
|
8
|
+
}
|
|
9
|
+
export interface JsonlTailReaderOptions {
|
|
10
|
+
/** Chooses the decoder from the first available chunk (e.g. for BOM sniffing). Defaults to plain UTF-8. */
|
|
11
|
+
createDecoder?: (firstChunk: Uint8Array) => TextDecoder;
|
|
12
|
+
/** Byte offset to resume from, e.g. one persisted by an earlier process. */
|
|
13
|
+
startOffset?: number;
|
|
14
|
+
/** Most bytes to take in one {@link JsonlTailReader.read}. */
|
|
15
|
+
maxReadBytes?: number;
|
|
16
|
+
}
|
|
17
|
+
/** Incrementally tails a growing JSON Lines file: stat, detect truncation, read the new bytes, and split them into complete lines. */
|
|
18
|
+
export declare class JsonlTailReader {
|
|
19
|
+
private readonly path;
|
|
20
|
+
private readOffset;
|
|
21
|
+
private partialBytes;
|
|
22
|
+
private pending;
|
|
23
|
+
private decoder?;
|
|
24
|
+
private readonly createDecoder;
|
|
25
|
+
private readonly maxReadBytes;
|
|
26
|
+
constructor(path: string, options?: JsonlTailReaderOptions);
|
|
27
|
+
/** Byte position just past the last complete line, excluding any buffered partial line. */
|
|
28
|
+
get offset(): number;
|
|
29
|
+
/** Byte position of the next read, including any buffered partial line. */
|
|
30
|
+
get bytePosition(): number;
|
|
31
|
+
/** a catch-up read exactly at another reader's position rather than at end of file. */
|
|
32
|
+
read(limitBytes?: number): Promise<JsonlTailReadResult>;
|
|
33
|
+
reset(): void;
|
|
34
|
+
/** Measures the trailing partial line in raw bytes, so {@link offset} never lands mid-record. */
|
|
35
|
+
private trackPartial;
|
|
36
|
+
private consume;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=jsonl-tail-reader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonl-tail-reader.d.ts","sourceRoot":"","sources":["../src/jsonl-tail-reader.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,6FAA6F;IAC7F,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,sBAAsB;IACrC,2GAA2G;IAC3G,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,WAAW,CAAC;IACxD,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,sIAAsI;AACtI,qBAAa,eAAe;IAQd,OAAO,CAAC,QAAQ,CAAC,IAAI;IAPjC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,OAAO,CAAM;IACrB,OAAO,CAAC,OAAO,CAAC,CAAc;IAC9B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA0C;IACxE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IAEtC,YAA6B,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,sBAA2B,EAU9E;IAED,2FAA2F;IAC3F,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,2EAA2E;IAC3E,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,uFAAuF;IACjF,IAAI,CAAC,UAAU,SAA2B,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA0B9E;IAED,KAAK,IAAI,IAAI,CAKZ;IAED,iGAAiG;IACjG,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,OAAO;CAOhB"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { appendFile } from "node:fs/promises";
|
|
2
|
+
import type { CurrentLogStream, JsonObject, LogSession, LogStream } from "./types.ts";
|
|
3
|
+
export interface LoggerTuning {
|
|
4
|
+
/** Records accumulate until a batch reaches this many bytes. Defaults to 256 KiB. */
|
|
5
|
+
batchBytes?: number;
|
|
6
|
+
/** A partial batch is written after this long. Defaults to 50 ms. */
|
|
7
|
+
flushIntervalMs?: number;
|
|
8
|
+
/** Hard cap on bytes held in memory (buffered plus queued). Defaults to 8 MiB. */
|
|
9
|
+
maxBufferedBytes?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface CreateLogSessionOptions extends LoggerTuning {
|
|
12
|
+
producer: string;
|
|
13
|
+
streams: readonly LogStream[];
|
|
14
|
+
logDirectory?: string;
|
|
15
|
+
outputPaths?: Partial<Record<LogStream, string>>;
|
|
16
|
+
onWriteError?: (failure: LogWriteFailure) => void;
|
|
17
|
+
activate?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface LogWriteFailure {
|
|
20
|
+
stream: LogStream;
|
|
21
|
+
path: string;
|
|
22
|
+
error: Error;
|
|
23
|
+
}
|
|
24
|
+
export interface JsonLinesLoggerOptions extends LoggerTuning {
|
|
25
|
+
stream: LogStream;
|
|
26
|
+
onWriteError?: (failure: LogWriteFailure) => void;
|
|
27
|
+
/** Sink override, mainly for tests. Defaults to a lazily opened append-mode file handle. */
|
|
28
|
+
append?: typeof appendFile;
|
|
29
|
+
}
|
|
30
|
+
export interface JsonLinesLoggerStats {
|
|
31
|
+
/** Bytes held in memory: the partial batch plus every batch still queued for the disk. */
|
|
32
|
+
bufferedBytes: number;
|
|
33
|
+
queuedBatches: number;
|
|
34
|
+
/** True once a write has failed; the logger keeps accepting and attempting records. */
|
|
35
|
+
failed: boolean;
|
|
36
|
+
/** Records refused because the byte cap was reached, or logged after close(). */
|
|
37
|
+
droppedRecords: number;
|
|
38
|
+
}
|
|
39
|
+
/** Buffers records into byte-bounded batches and appends them through one file handle per stream. */
|
|
40
|
+
export declare class JsonLinesLogger {
|
|
41
|
+
readonly path: string;
|
|
42
|
+
private readonly sessionId;
|
|
43
|
+
private readonly source;
|
|
44
|
+
private readonly options;
|
|
45
|
+
private sequence;
|
|
46
|
+
private lines;
|
|
47
|
+
private currentBytes;
|
|
48
|
+
private bufferedBytes;
|
|
49
|
+
private queuedBatches;
|
|
50
|
+
private droppedRecords;
|
|
51
|
+
private timer?;
|
|
52
|
+
private tail;
|
|
53
|
+
private firstFailure?;
|
|
54
|
+
private reportedFailure;
|
|
55
|
+
private overflowing;
|
|
56
|
+
private closed;
|
|
57
|
+
private headerWritten;
|
|
58
|
+
private handle?;
|
|
59
|
+
private readonly batchBytes;
|
|
60
|
+
private readonly flushIntervalMs;
|
|
61
|
+
private readonly maxBufferedBytes;
|
|
62
|
+
constructor(path: string, sessionId: string, source: string, options?: JsonLinesLoggerOptions);
|
|
63
|
+
log(type: string, data: JsonObject): void;
|
|
64
|
+
/** Resolves once every record logged before this call has reached the disk. */
|
|
65
|
+
flush(): Promise<void>;
|
|
66
|
+
close(): Promise<void>;
|
|
67
|
+
stats(): JsonLinesLoggerStats;
|
|
68
|
+
/**
|
|
69
|
+
* Emits the stream header ahead of the first record.
|
|
70
|
+
*
|
|
71
|
+
* Written here rather than by whoever created the file so that every stream file is
|
|
72
|
+
* self-describing however it was produced — including a bare `--output name.jsonl` from the CLI.
|
|
73
|
+
* Sessions have no metadata file of their own; v2 records carry no session id or producer either,
|
|
74
|
+
* so without this line a stream read in isolation would have no provenance at all.
|
|
75
|
+
*/
|
|
76
|
+
private ensureHeader;
|
|
77
|
+
private scheduleFlush;
|
|
78
|
+
private enqueueCurrent;
|
|
79
|
+
private write;
|
|
80
|
+
private notify;
|
|
81
|
+
}
|
|
82
|
+
export declare function createLogSession(options: CreateLogSessionOptions): Promise<LogSession>;
|
|
83
|
+
/** Writes a single stream's "current" pointer verbatim, e.g. to roll a stream back to a known-good pointer. */
|
|
84
|
+
export declare function writeCurrentLogStreamPointer(pointer: CurrentLogStream, logDirectory?: string): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Switches the shared "current stream" pointers for the given streams onto an already-created,
|
|
87
|
+
* not-yet-activated session (see {@link CreateLogSessionOptions.activate}). Callers that need
|
|
88
|
+
* several streams to change over as one logical moment should await this once. The session is
|
|
89
|
+
* flushed first, so a follower that picks up a new pointer immediately sees the records already
|
|
90
|
+
* written to that session. Streams are switched one at a time (there is no cross-file
|
|
91
|
+
* transaction), so a caller that must not leave streams split across sessions should capture each
|
|
92
|
+
* stream's prior pointer first and roll back to it if this throws partway through.
|
|
93
|
+
*/
|
|
94
|
+
export declare function activateLogSession(session: Pick<LogSession, "id"> & Partial<Pick<LogSession, "flush">>, streams: readonly LogStream[], logDirectory?: string): Promise<void>;
|
|
95
|
+
export declare function readCurrentLogStream(stream: LogStream, logDirectory?: string): Promise<(CurrentLogStream & {
|
|
96
|
+
path: string;
|
|
97
|
+
}) | undefined>;
|
|
98
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
|
package/dist/paths.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { LogStream } from "./types.ts";
|
|
2
|
+
export declare function defaultLogDirectory(workingDirectory?: string): string;
|
|
3
|
+
export declare function streamCategoryDirectory(stream: LogStream, logDirectory?: string): string;
|
|
4
|
+
export declare function streamSessionPath(stream: LogStream, sessionId: string, logDirectory?: string): string;
|
|
5
|
+
export declare function currentStreamPointerPath(stream: LogStream, logDirectory?: string): string;
|
|
6
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,wBAAgB,mBAAmB,CAAC,gBAAgB,SAAgB,GAAG,MAAM,CAE5E;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,SAAwB,GAAG,MAAM,CAEvG;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,SAAwB,GAAG,MAAM,CAEpH;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,SAAwB,GAAG,MAAM,CAExG"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type LiveLogStatus = "waiting" | "watching" | "ready" | "stopped" | "error";
|
|
2
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
3
|
+
export declare function isMissing(error: unknown): boolean;
|
|
4
|
+
export declare function decimal(value: unknown): value is string;
|
|
5
|
+
export declare function nullableString(value: unknown): value is string | null;
|
|
6
|
+
//# sourceMappingURL=predicates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"predicates.d.ts","sourceRoot":"","sources":["../src/predicates.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAEnF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEjD;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEvD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,GAAG,IAAI,CAErE"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { JsonObject, LogRecord, LogStreamHeader } from "./types.ts";
|
|
2
|
+
/** Encodes one record from its parts. */
|
|
3
|
+
export declare function encodeLogRecord(sequence: number, atMs: number, type: string, data: JsonObject): string;
|
|
4
|
+
export declare function encodeLogStreamHeader(header: LogStreamHeader): string;
|
|
5
|
+
/** True for the metadata line a v2 stream file opens with, which carries no record of its own. */
|
|
6
|
+
export declare function isLogStreamHeader(value: unknown): value is LogStreamHeader;
|
|
7
|
+
export declare function parseLogStreamHeader(value: unknown): LogStreamHeader | undefined;
|
|
8
|
+
/** Decodes one line's parsed JSON into a record, accepting both encodings. */
|
|
9
|
+
export declare function parseLogRecord(value: unknown, header?: LogStreamHeader): LogRecord | undefined;
|
|
10
|
+
//# sourceMappingURL=record-codec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record-codec.d.ts","sourceRoot":"","sources":["../src/record-codec.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAa,eAAe,EAAE,MAAM,YAAY,CAAC;AAMpF,yCAAyC;AACzC,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAOtG;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAErE;AAED,kGAAkG;AAClG,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAE1E;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,GAAG,SAAS,CAMhF;AAED,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,SAAS,GAAG,SAAS,CAK9F"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { JsonlTailReadResult } from "./jsonl-tail-reader.ts";
|
|
2
|
+
import type { LogStreamSourceOptions } from "./stream-source.ts";
|
|
3
|
+
import type { LogStream } from "./types.ts";
|
|
4
|
+
type WithSource<TBatch> = TBatch & {
|
|
5
|
+
path?: string;
|
|
6
|
+
sessionId?: string;
|
|
7
|
+
};
|
|
8
|
+
/** A domain follower: it can read its own file, and it can be handed lines read on its behalf. */
|
|
9
|
+
export interface LiveLogLineConsumer<TBatch> {
|
|
10
|
+
poll(): Promise<TBatch>;
|
|
11
|
+
/** Folds an already-performed read into the follower's state. */
|
|
12
|
+
consumeRead(result: JsonlTailReadResult): TBatch;
|
|
13
|
+
}
|
|
14
|
+
export interface LiveLogSessionFollowerOptions<TFollower extends LiveLogLineConsumer<TBatch>, TBatch> extends Pick<LogStreamSourceOptions, "logDirectory" | "fallbackPollMs" | "debounceMs" | "persistent" | "readerOptions"> {
|
|
15
|
+
stream: LogStream;
|
|
16
|
+
createFollower: (path: string) => TFollower;
|
|
17
|
+
/** Merges the "did the active session change" signal into a domain batch's own reset/changed fields. */
|
|
18
|
+
mergeSessionChange: (batch: TBatch, changedSession: boolean) => TBatch;
|
|
19
|
+
/** Builds the batch returned when there is no current session for this stream. */
|
|
20
|
+
noStreamBatch: (reset: boolean) => TBatch;
|
|
21
|
+
}
|
|
22
|
+
/** Follows whichever session the shared current-stream pointer names. */
|
|
23
|
+
export declare class LiveLogSessionFollower<TFollower extends LiveLogLineConsumer<TBatch>, TBatch> {
|
|
24
|
+
private readonly options;
|
|
25
|
+
private sessionId?;
|
|
26
|
+
private follower?;
|
|
27
|
+
private subscription?;
|
|
28
|
+
private closed;
|
|
29
|
+
constructor(options: LiveLogSessionFollowerOptions<TFollower, TBatch>);
|
|
30
|
+
/** Drains whatever has arrived, returning an unchanged batch when nothing has. */
|
|
31
|
+
poll(): Promise<WithSource<TBatch>>;
|
|
32
|
+
/** Waits for the next batch that actually carries something, instead of polling for it. */
|
|
33
|
+
next(): Promise<WithSource<TBatch>>;
|
|
34
|
+
/** Ends when {@link close} is called, so a follow loop unwinds instead of spinning on empty reads. */
|
|
35
|
+
[Symbol.asyncIterator](): AsyncIterator<WithSource<TBatch>>;
|
|
36
|
+
/** Releases this consumer's hold on the shared source, stopping it once no consumer is left. */
|
|
37
|
+
close(): void;
|
|
38
|
+
private source;
|
|
39
|
+
private apply;
|
|
40
|
+
}
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=session-follower.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-follower.d.ts","sourceRoot":"","sources":["../src/session-follower.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAElE,OAAO,KAAK,EAAiB,sBAAsB,EAAyB,MAAM,oBAAoB,CAAC;AACvG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,KAAK,UAAU,CAAC,MAAM,IAAI,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzE,kGAAkG;AAClG,MAAM,WAAW,mBAAmB,CAAC,MAAM;IACzC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACxB,iEAAiE;IACjE,WAAW,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAAC;CAClD;AAED,MAAM,WAAW,6BAA6B,CAAC,SAAS,SAAS,mBAAmB,CAAC,MAAM,CAAC,EAAE,MAAM,CAClG,SAAQ,IAAI,CAAC,sBAAsB,EAAE,cAAc,GAAG,gBAAgB,GAAG,YAAY,GAAG,YAAY,GAAG,eAAe,CAAC;IACvH,MAAM,EAAE,SAAS,CAAC;IAClB,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,CAAC;IAC5C,wGAAwG;IACxG,kBAAkB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,KAAK,MAAM,CAAC;IACvE,kFAAkF;IAClF,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;CAC3C;AAED,yEAAyE;AACzE,qBAAa,sBAAsB,CAAC,SAAS,SAAS,mBAAmB,CAAC,MAAM,CAAC,EAAE,MAAM;IAM3E,OAAO,CAAC,QAAQ,CAAC,OAAO;IALpC,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,CAAY;IAC7B,OAAO,CAAC,YAAY,CAAC,CAAwB;IAC7C,OAAO,CAAC,MAAM,CAAS;IAEvB,YAA6B,OAAO,EAAE,6BAA6B,CAAC,SAAS,EAAE,MAAM,CAAC,EAAI;IAE1F,kFAAkF;IAC5E,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAExC;IAED,2FAA2F;IACrF,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAExC;IAED,sGAAsG;IAC/F,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAOjE;IAED,gGAAgG;IAChG,KAAK,IAAI,IAAI,CAIZ;IAED,OAAO,CAAC,MAAM;IAad,OAAO,CAAC,KAAK;CAgBd"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAoB,gBAAgB,EAAE,SAAS,EAAmB,MAAM,YAAY,CAAC;AAIjG,wBAAsB,eAAe,CACnC,MAAM,EAAE,SAAS,EACjB,YAAY,SAAwB,EACpC,KAAK,SAAK,GACT,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAqC7B"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { JsonlTailReadResult, JsonlTailReaderOptions } from "./jsonl-tail-reader.ts";
|
|
2
|
+
import type { LogStream } from "./types.ts";
|
|
3
|
+
/** Quiet period a burst of watcher events is collapsed into before the file is drained. */
|
|
4
|
+
export declare const DEFAULT_STREAM_DEBOUNCE_MS = 20;
|
|
5
|
+
/** Longest the source goes without a stat even when no watcher event arrives. */
|
|
6
|
+
export declare const DEFAULT_STREAM_FALLBACK_POLL_MS = 1000;
|
|
7
|
+
/** Most bytes a live follower takes from disk in one drain, matching the read model's indexing batch. */
|
|
8
|
+
export declare const DEFAULT_STREAM_BATCH_BYTES: number;
|
|
9
|
+
export interface LogStreamSourceOptions {
|
|
10
|
+
stream: LogStream;
|
|
11
|
+
logDirectory?: string;
|
|
12
|
+
fallbackPollMs?: number;
|
|
13
|
+
debounceMs?: number;
|
|
14
|
+
/** Whether the watchers and fallback timer hold the process open even while nothing is waiting on them. */
|
|
15
|
+
persistent?: boolean;
|
|
16
|
+
/** Applied to the shared reader. Only the first subscriber of a stream gets to set these. */
|
|
17
|
+
readerOptions?: Pick<JsonlTailReaderOptions, "createDecoder" | "maxReadBytes">;
|
|
18
|
+
}
|
|
19
|
+
export interface LogStreamRead extends JsonlTailReadResult {
|
|
20
|
+
/** The session named by the current-stream pointer, or undefined while no session is active. */
|
|
21
|
+
current?: {
|
|
22
|
+
path: string;
|
|
23
|
+
sessionId: string;
|
|
24
|
+
};
|
|
25
|
+
/** Whether {@link current} differs from the one this subscriber last saw. */
|
|
26
|
+
changedSession: boolean;
|
|
27
|
+
/** Whether the read stopped at its byte cap with more bytes already on disk. */
|
|
28
|
+
capped: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface LogStreamSubscription extends AsyncIterable<LogStreamRead> {
|
|
31
|
+
/** Drains now and returns whatever has accumulated, empty batch included. */
|
|
32
|
+
poll(): Promise<LogStreamRead>;
|
|
33
|
+
/** Resolves only once there is something to report, so an idle stream costs nothing. */
|
|
34
|
+
next(): Promise<LogStreamRead>;
|
|
35
|
+
close(): void;
|
|
36
|
+
}
|
|
37
|
+
/** Subscribes to the shared tail of a stream, creating it on first use. */
|
|
38
|
+
export declare function subscribeToLogStream(options: LogStreamSourceOptions): LogStreamSubscription;
|
|
39
|
+
//# sourceMappingURL=stream-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-source.d.ts","sourceRoot":"","sources":["../src/stream-source.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAI1F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,2FAA2F;AAC3F,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAC7C,iFAAiF;AACjF,eAAO,MAAM,+BAA+B,OAAQ,CAAC;AACrD,yGAAyG;AACzG,eAAO,MAAM,0BAA0B,QAAc,CAAC;AAEtD,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2GAA2G;IAC3G,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,6FAA6F;IAC7F,aAAa,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,eAAe,GAAG,cAAc,CAAC,CAAC;CAChF;AAED,MAAM,WAAW,aAAc,SAAQ,mBAAmB;IACxD,gGAAgG;IAChG,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,6EAA6E;IAC7E,cAAc,EAAE,OAAO,CAAC;IACxB,gFAAgF;IAChF,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,qBAAsB,SAAQ,aAAa,CAAC,aAAa,CAAC;IACzE,6EAA6E;IAC7E,IAAI,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IAC/B,wFAAwF;IACxF,IAAI,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IAC/B,KAAK,IAAI,IAAI,CAAC;CACf;AA8SD,2EAA2E;AAC3E,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,sBAAsB,GAAG,qBAAqB,CAS3F"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export type LogStream = "capture" | "combat" | "market" | "rewards" | "other";
|
|
2
|
+
export type JsonData = null | boolean | number | string | JsonData[] | JsonObject;
|
|
3
|
+
export interface JsonObject {
|
|
4
|
+
[key: string]: JsonData | undefined;
|
|
5
|
+
}
|
|
6
|
+
/** A decoded record, in the shape every consumer sees regardless of how it was stored. */
|
|
7
|
+
export interface LogRecord<T extends JsonObject = JsonObject> {
|
|
8
|
+
schemaVersion: 1 | 2;
|
|
9
|
+
sessionId: string;
|
|
10
|
+
sequence: number;
|
|
11
|
+
recordedAt: string;
|
|
12
|
+
source: string;
|
|
13
|
+
type: string;
|
|
14
|
+
data: T;
|
|
15
|
+
}
|
|
16
|
+
/** First line of a v2 stream file, carrying what v1 repeated on every record. */
|
|
17
|
+
export interface LogStreamHeader {
|
|
18
|
+
schemaVersion: 2;
|
|
19
|
+
stream: LogStream;
|
|
20
|
+
sessionId: string;
|
|
21
|
+
producer: string;
|
|
22
|
+
startedAt: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ListedLogSession {
|
|
25
|
+
id: string;
|
|
26
|
+
createdAt: string;
|
|
27
|
+
path: string;
|
|
28
|
+
active: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface CurrentLogStream {
|
|
31
|
+
schemaVersion: 1;
|
|
32
|
+
stream: LogStream;
|
|
33
|
+
sessionId: string;
|
|
34
|
+
startedAt: string;
|
|
35
|
+
relativePath: string;
|
|
36
|
+
}
|
|
37
|
+
export interface LogSession {
|
|
38
|
+
id: string;
|
|
39
|
+
logger(stream: LogStream): JsonLinesLogger;
|
|
40
|
+
/** Resolves once every record logged before this call has reached the disk on every stream. */
|
|
41
|
+
flush(): Promise<void>;
|
|
42
|
+
close(): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
import type { JsonLinesLogger } from "./logger.ts";
|
|
45
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAC9E,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,EAAE,GAAG,UAAU,CAAC;AAClF,MAAM,WAAW,UAAU;IAAG,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;CAAE;AAEnE,0FAA0F;AAC1F,MAAM,WAAW,SAAS,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU;IAC1D,aAAa,EAAE,CAAC,GAAG,CAAC,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,CAAC,CAAC;CACT;AAED,iFAAiF;AACjF,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,CAAC,CAAC;IACjB,MAAM,EAAE,SAAS,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,CAAC,CAAC;IACjB,MAAM,EAAE,SAAS,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,eAAe,CAAC;IAC3C,+FAA+F;IAC/F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kar-mi/spirit-vale-tools-logging",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "Session-oriented logging utilities for Spirit Vale tools.",
|
|
5
|
+
"license": "AGPL-3.0-only",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/kar-mi/spirit-vale-tools.git",
|
|
9
|
+
"directory": "packages/logging"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "bun build ./src/index.ts --outdir ./dist --target bun --format esm && bunx tsc --project ./tsconfig.build.json"
|
|
18
|
+
},
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"default": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|