@loglayer/shared
Version:
Shared utilities and types for loglayer packages.
233 lines (232 loc) • 6.35 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
//#region src/common.types.ts
let LogLevel = /* @__PURE__ */ function(LogLevel) {
LogLevel["info"] = "info";
LogLevel["warn"] = "warn";
LogLevel["error"] = "error";
LogLevel["debug"] = "debug";
LogLevel["trace"] = "trace";
LogLevel["fatal"] = "fatal";
return LogLevel;
}({});
/**
* Mapping of log levels to their numeric values.
*/
const LogLevelPriority = {
["trace"]: 10,
["debug"]: 20,
["info"]: 30,
["warn"]: 40,
["error"]: 50,
["fatal"]: 60
};
/**
* Mapping of numeric values to their log level names.
*/
const LogLevelPriorityToNames = {
10: "trace",
20: "debug",
30: "info",
40: "warn",
50: "error",
60: "fatal"
};
//#endregion
//#region src/lazy.ts
/**
* Symbol used to identify lazy values in context and metadata.
* Can be used to check if a value is a lazy wrapper: `LAZY_SYMBOL in value`.
*
* @see {@link https://loglayer.dev/logging-api/lazy-evaluation | Lazy Evaluation Docs}
*/
const LAZY_SYMBOL = Symbol.for("loglayer.lazy");
/**
* String constant used as a replacement value when a lazy callback fails during evaluation.
* Exported so users can programmatically detect lazy evaluation failures in their log output.
*
* @see {@link https://loglayer.dev/logging-api/lazy-evaluation#error-handling | Lazy Evaluation Error Handling Docs}
*/
const LAZY_EVAL_ERROR = "[LazyEvalError]";
/**
* Wraps a callback function to defer its evaluation until log time.
*
* The callback will only be invoked if the log level is enabled,
* avoiding unnecessary computation for disabled log levels.
*
* Can be used in both `withContext()` and `withMetadata()` at the root level.
*
* When the callback returns a `Promise` (i.e., is an async function), the
* log method will return `Promise<void>` so TypeScript can track that the
* operation is asynchronous.
*
* Adapted from [LogTape's lazy evaluation](https://logtape.org/manual/lazy).
*
* @example
* ```typescript
* import { LogLayer, lazy } from "loglayer";
*
* const log = new LogLayer({ ... });
*
* // Dynamic context - evaluated on each log call
* log.withContext({
* memoryUsage: lazy(() => process.memoryUsage().heapUsed),
* });
*
* // Dynamic metadata - evaluated only if debug is enabled
* log.withMetadata({
* data: lazy(() => JSON.stringify(largeObject)),
* }).debug("Processing complete");
* ```
*
* @see {@link https://loglayer.dev/logging-api/lazy-evaluation | Lazy Evaluation Docs}
*/
function lazy(fn) {
return { [LAZY_SYMBOL]: fn };
}
/**
* Checks if a value is a lazy value created by {@link lazy}.
* @internal
*/
function isLazy(value) {
return value != null && typeof value === "object" && LAZY_SYMBOL in value;
}
/**
* Counts the number of lazy values in a record.
* @internal
*/
function countLazyValues(obj) {
let count = 0;
for (const key of Object.keys(obj)) if (isLazy(obj[key])) count++;
return count;
}
/**
* Resolves any lazy values in a record at the root level.
* Returns the original object if no lazy values are found (optimization).
* If a lazy callback throws, the value is replaced with LAZY_EVAL_ERROR
* and the error is collected in the result.
* @internal
*/
function resolveLazyValues(obj) {
let hasLazy = false;
for (const key of Object.keys(obj)) if (isLazy(obj[key])) {
hasLazy = true;
break;
}
if (!hasLazy) return {
resolved: obj,
errors: null
};
const result = {};
let errors = null;
for (const key of Object.keys(obj)) {
const value = obj[key];
if (isLazy(value)) try {
result[key] = value[LAZY_SYMBOL]();
} catch (e) {
result[key] = LAZY_EVAL_ERROR;
if (!errors) errors = [];
errors.push({
key,
error: e
});
}
else result[key] = value;
}
return {
resolved: result,
errors
};
}
/**
* Checks if any values in a record are Promises.
* @internal
*/
function hasPromiseValues(obj) {
for (const key of Object.keys(obj)) if (obj[key] instanceof Promise) return true;
return false;
}
/**
* Replaces any Promise values in a record with LAZY_EVAL_ERROR.
* Used to strip async lazy values from context where only sync lazy is supported.
* Returns the keys that were replaced.
* @internal
*/
function replacePromiseValues(obj) {
let asyncKeys = null;
const result = {};
for (const key of Object.keys(obj)) if (obj[key] instanceof Promise) {
result[key] = LAZY_EVAL_ERROR;
if (!asyncKeys) asyncKeys = [];
asyncKeys.push(key);
} else result[key] = obj[key];
if (!asyncKeys) return {
resolved: obj,
asyncKeys: null
};
return {
resolved: result,
asyncKeys
};
}
/**
* Resolves any Promise values in a record using Promise.allSettled.
* If a Promise rejects, the value is replaced with LAZY_EVAL_ERROR
* and the error is collected in the result.
* @internal
*/
async function resolvePromiseValues(obj) {
const keys = Object.keys(obj);
const settled = await Promise.allSettled(keys.map((key) => Promise.resolve(obj[key])));
const result = {};
let errors = null;
for (let i = 0; i < keys.length; i++) {
const s = settled[i];
if (s.status === "fulfilled") result[keys[i]] = s.value;
else {
result[keys[i]] = LAZY_EVAL_ERROR;
if (!errors) errors = [];
errors.push({
key: keys[i],
error: s.reason
});
}
}
return {
resolved: result,
errors
};
}
//#endregion
//#region src/template-utils.ts
/**
* Converts tagged template arguments to a message array.
* Detects tagged templates by checking for the `raw` property on TemplateStringsArray.
*/
function resolveMessages(args) {
const first = args[0];
if (Array.isArray(first) && typeof first.raw !== "undefined") {
const strings = first;
const values = args.slice(1);
let result = "";
for (let i = 0; i < strings.length; i++) {
result += strings[i];
if (i < values.length) result += String(values[i]);
}
return [result];
}
return args;
}
//#endregion
exports.LAZY_EVAL_ERROR = LAZY_EVAL_ERROR;
exports.LAZY_SYMBOL = LAZY_SYMBOL;
exports.LogLevel = LogLevel;
exports.LogLevelPriority = LogLevelPriority;
exports.LogLevelPriorityToNames = LogLevelPriorityToNames;
exports.countLazyValues = countLazyValues;
exports.hasPromiseValues = hasPromiseValues;
exports.isLazy = isLazy;
exports.lazy = lazy;
exports.replacePromiseValues = replacePromiseValues;
exports.resolveLazyValues = resolveLazyValues;
exports.resolveMessages = resolveMessages;
exports.resolvePromiseValues = resolvePromiseValues;