Skip to content

Commit

Permalink
feat: alternative ConsoleReadableStreamOptions and make options partial
Browse files Browse the repository at this point in the history
  • Loading branch information
eliassjogreen committed Feb 21, 2024
1 parent a6b2ec9 commit 0d98ef9
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 6 deletions.
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@denosaurs/log",
"version": "0.0.4",
"version": "0.0.5",
"exports": {
".": "./mod.ts",
"./transforms/filter": "./transforms/filter.ts",
Expand Down
108 changes: 103 additions & 5 deletions mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

const console = globalThis.console;

type RecursivePartial<T> = {
[P in keyof T]?: RecursivePartial<T[P]>;
};

export type LogLevel =
| "trace"
| "debug"
Expand All @@ -27,16 +31,99 @@ export interface Log {

export interface ConsoleReadableStreamOptions {
internals: {
/**
* @returns A millisecond resolution unix timestamp of when the log was made
*/
now: () => number;
/**
* Clones the given data so that references are not shared between logs.
*/
clone: <T>(data: T) => T;
};
}

/**
* Default options for the {@link ConsoleReadableStreamOptions}. It uses normal
* resolution timestamps by calling `Date.now()` and a deep cloning by
* using the structured clone algorithm.
*/
export const defaultConsoleReadableStreamOptions: ConsoleReadableStreamOptions =
{
internals: {
now: Date.now,
clone: <T>(data: T) => {
if (
typeof data === "string" ||
typeof data === "number" ||
typeof data === "boolean" ||
data === null ||
data === undefined
) {
return data;
}

return structuredClone(data);
},
},
};

/**
* This is the fastest option, but it may not be suitable for all use cases.
* Read the caution below before using these default options.
*
* Fast defaults for the {@link ConsoleReadableStreamOptions}. It uses normal
* resolution timestamps by calling `Date.now()` and a shallow cloning by
* transforming the data to `JSON` and back.
*
* Caution: This option only supports JSON-serializable data and data may be
* lost in the process making future transforms which rely on non-json types
* impossible. If you need to support non-json types, you should use the
* {@link defaultConsoleReadableStreamOptions} instead.
*/
export const fastConsoleReadableStreamOptions: ConsoleReadableStreamOptions = {
internals: {
now: Date.now,
clone: <T>(data: T) => {
if (
typeof data === "string" ||
typeof data === "number" ||
typeof data === "boolean" ||
data === null ||
data === undefined
) {
return data;
}

return JSON.parse(JSON.stringify(data));
},
},
};

/**
* This is the most accurate option, but may suffer from a slight performance
* penalty by using high resolution timestamps and a deep cloning by using the
* structured clone algorithm.
*
* Use this option if you need to support non-json types and need high
* resolution timestamps.
*/
export const hrtimeConsoleReadableStreamOptions: ConsoleReadableStreamOptions =
{
internals: {
now: () => performance.timeOrigin + performance.now(),
clone: <T>(data: T) => structuredClone(data),
clone: <T>(data: T) => {
if (
typeof data === "string" ||
typeof data === "number" ||
typeof data === "boolean" ||
data === null ||
data === undefined
) {
return data;
}

return structuredClone(data);
},
},
};

Expand All @@ -49,9 +136,18 @@ export const defaultConsoleReadableStreamOptions: ConsoleReadableStreamOptions =
* object, call the {@link ConsoleReadableStream.cancel} method.
*/
export class ConsoleReadableStream extends ReadableStream<Log> {
#options: ConsoleReadableStreamOptions;

constructor(
options: ConsoleReadableStreamOptions = defaultConsoleReadableStreamOptions,
options: RecursivePartial<ConsoleReadableStreamOptions> =
defaultConsoleReadableStreamOptions,
) {
options ??= defaultConsoleReadableStreamOptions;
options.internals ??= defaultConsoleReadableStreamOptions.internals;
options.internals.now ??= defaultConsoleReadableStreamOptions.internals.now;
options.internals.clone ??=
defaultConsoleReadableStreamOptions.internals.clone;

let controller: ReadableStreamDefaultController<Log>;
const groups: any[] = [];

Expand All @@ -61,12 +157,14 @@ export class ConsoleReadableStream extends ReadableStream<Log> {
},
});

this.#options = options as ConsoleReadableStreamOptions;

const wrapper = (level: LogLevel) => (...data: any[]) => {
controller.enqueue({
timestamp: options.internals.now(),
timestamp: this.#options.internals.now(),
level,
groups: groups.length === 0 ? [] : options.internals.clone(groups),
data: options.internals.clone(data),
groups: this.#options.internals.clone(groups),
data: this.#options.internals.clone(data),
});
};

Expand Down

0 comments on commit 0d98ef9

Please sign in to comment.