@compas/stdlib
Advanced tools
Comparing version 0.4.0 to 0.5.0
@@ -10,3 +10,3 @@ export { uuid } from "./src/datatypes.js"; | ||
import("./src/config-loader").ConfigLoaderResult; | ||
export type InsightEvent = import("./types/advanced-types").InsightEvent; | ||
export type InsightEvent = import("./src/events").InsightEvent; | ||
export type ProcessDirectoryOptions = | ||
@@ -13,0 +13,0 @@ import("./types/advanced-types.js").ProcessDirectoryOptions; |
@@ -26,3 +26,3 @@ /// <reference path="./types/advanced-types.d.ts"> | ||
/** | ||
* @typedef {import("./types/advanced-types").InsightEvent} InsightEvent | ||
* @typedef {import("./src/events").InsightEvent} InsightEvent | ||
*/ | ||
@@ -29,0 +29,0 @@ |
{ | ||
"name": "@compas/stdlib", | ||
"version": "0.4.0", | ||
"version": "0.5.0", | ||
"description": "All kinds of utility functions", | ||
@@ -20,3 +20,3 @@ "main": "./index.js", | ||
"dependencies": { | ||
"@types/node": "20.2.1", | ||
"@types/node": "20.2.5", | ||
"dotenv": "16.0.3", | ||
@@ -23,0 +23,0 @@ "lodash.merge": "4.6.2", |
@@ -9,3 +9,3 @@ /** | ||
* @param {AbortSignal|undefined} [signal] | ||
* @returns {import("../types/advanced-types.js").InsightEvent} | ||
* @returns {InsightEvent} | ||
*/ | ||
@@ -15,14 +15,12 @@ export function newEvent( | ||
signal?: AbortSignal | undefined, | ||
): import("../types/advanced-types.js").InsightEvent; | ||
): InsightEvent; | ||
/** | ||
* Create a 'child' event, reuses the logger, adds callstack to the passed event | ||
* Create a 'child' event, reuses the logger, adds it als a child to the passed event | ||
* | ||
* @since 0.1.0 | ||
* | ||
* @param {import("../types/advanced-types.js").InsightEvent} event | ||
* @returns {import("../types/advanced-types.js").InsightEvent} | ||
* @param {InsightEvent} event | ||
* @returns {InsightEvent} | ||
*/ | ||
export function newEventFromEvent( | ||
event: import("../types/advanced-types.js").InsightEvent, | ||
): import("../types/advanced-types.js").InsightEvent; | ||
export function newEventFromEvent(event: InsightEvent): InsightEvent; | ||
/** | ||
@@ -33,23 +31,17 @@ * Track event start times | ||
* | ||
* @param {import("../types/advanced-types.js").InsightEvent} event | ||
* @param {InsightEvent} event | ||
* @param {string} name | ||
* @returns {void} | ||
*/ | ||
export function eventStart( | ||
event: import("../types/advanced-types.js").InsightEvent, | ||
name: string, | ||
): void; | ||
export function eventStart(event: InsightEvent, name: string): void; | ||
/** | ||
* Rename an event, and all callStack items | ||
* Rename an event | ||
* | ||
* @since 0.1.0 | ||
* | ||
* @param {import("../types/advanced-types.js").InsightEvent} event | ||
* @param {InsightEvent} event | ||
* @param {string} name | ||
* @returns {void} | ||
*/ | ||
export function eventRename( | ||
event: import("../types/advanced-types.js").InsightEvent, | ||
name: string, | ||
): void; | ||
export function eventRename(event: InsightEvent, name: string): void; | ||
/** | ||
@@ -60,8 +52,28 @@ * Track event end times and log if necessary | ||
* | ||
* @param {import("../types/advanced-types.js").InsightEvent} event | ||
* @param {InsightEvent} event | ||
* @returns {void} | ||
*/ | ||
export function eventStop( | ||
event: import("../types/advanced-types.js").InsightEvent, | ||
): void; | ||
export function eventStop(event: InsightEvent): void; | ||
export type InsightEventSpan = { | ||
name: string; | ||
duration?: number | undefined; | ||
startTime: number; | ||
stopTime?: number | undefined; | ||
abortedTime?: number | undefined; | ||
children: InsightEventSpan[]; | ||
}; | ||
/** | ||
* Manually track (async) function duration. | ||
* | ||
* By passing the event down through (async) functions, it facilitates a unified way to | ||
* have access to a task / request specific logger and insights in the duration of your | ||
* functions. | ||
*/ | ||
export type InsightEvent = { | ||
log: import("@compas/stdlib").Logger; | ||
signal?: AbortSignal | undefined; | ||
rootEvent?: InsightEvent | undefined; | ||
name?: string | undefined; | ||
span: InsightEventSpan; | ||
}; | ||
//# sourceMappingURL=events.d.ts.map |
@@ -1,2 +0,1 @@ | ||
import { inspect } from "util"; | ||
import { AppError } from "./error.js"; | ||
@@ -6,55 +5,79 @@ import { isNil } from "./lodash.js"; | ||
/** | ||
* @typedef {object} InsightEventSpan | ||
* @property {string} name | ||
* @property {number} [duration] | ||
* @property {number} startTime | ||
* @property {number} [stopTime] | ||
* @property {number} [abortedTime] | ||
* @property {InsightEventSpan[]} children | ||
*/ | ||
/** | ||
* Manually track (async) function duration. | ||
* | ||
* By passing the event down through (async) functions, it facilitates a unified way to | ||
* have access to a task / request specific logger and insights in the duration of your | ||
* functions. | ||
* | ||
* @example | ||
* async function userList(event) { | ||
* eventStart(event, "user.list"); | ||
* | ||
* const totol = await userCount(newEventFromEvent(event)); | ||
* const users = await queryUser({}).exec(sql); | ||
* | ||
* eventStop(event); | ||
* | ||
* return { total, users }; | ||
* } | ||
* | ||
* // Logs something like: | ||
* // { | ||
* // name: "user.list", | ||
* // duration: 25, | ||
* // startTime: 1685000000000 | ||
* // stopTime: 1685000000025 | ||
* // children: [ | ||
* // { | ||
* // name: "user.count", | ||
* // duration: 5, | ||
* // startTime: 1685000000010 | ||
* // stopTime: 1685000000015 | ||
* // } | ||
* // ] | ||
* // } | ||
* @typedef {object} InsightEvent | ||
* @property {import("@compas/stdlib").Logger} log | ||
* @property {AbortSignal} [signal] | ||
* @property {InsightEvent} [rootEvent] | ||
* @property {string} [name] | ||
* @property {InsightEventSpan} span | ||
*/ | ||
/** | ||
* | ||
* @param {import("./logger.js").Logger} logger | ||
* @param {AbortSignal|undefined} [signal] | ||
* @returns {InsightEventConstructor} | ||
* @returns {InsightEvent} | ||
*/ | ||
function InsightEventConstructor(logger, signal) { | ||
if (!(this instanceof InsightEventConstructor)) { | ||
return new InsightEventConstructor(logger, signal); | ||
} | ||
return { | ||
log: logger, | ||
signal, | ||
root: undefined, | ||
name: undefined, | ||
span: { | ||
// @ts-expect-error | ||
name: undefined, | ||
const _this = this; | ||
duration: undefined, | ||
/** @type {import("./logger.js").Logger} */ | ||
this.log = logger; | ||
/** @type {AbortSignal|undefined} */ | ||
this.signal = signal; | ||
/** @type {InsightEventConstructor|undefined} */ | ||
this.parent = undefined; | ||
/** @type {string|undefined} */ | ||
this.name = undefined; | ||
/** @type {import("../types/advanced-types.js").InsightEventCall[]} */ | ||
this.callStack = []; | ||
// @ts-expect-error | ||
startTime: undefined, | ||
this.calculateDuration = calculateDuration.bind(this); | ||
this[inspect.custom] = print.bind(this); | ||
this.toJSON = print.bind(this); | ||
function calculateDuration() { | ||
// @ts-ignore | ||
if (_this.callStack[0]?.type !== "start") { | ||
return; | ||
} | ||
const lastIdx = _this.callStack.length - 1; | ||
// @ts-ignore | ||
const lastType = _this.callStack[lastIdx]?.type; | ||
if (lastType === "stop" || lastType === "aborted") { | ||
// @ts-ignore | ||
_this.callStack[0].duration = // @ts-ignore | ||
_this.callStack[lastIdx].time - _this.callStack[0].time; | ||
} | ||
} | ||
function print() { | ||
return { | ||
type: "event_callstack", | ||
aborted: !!_this.signal?.aborted, | ||
callStack: _this.callStack, | ||
}; | ||
} | ||
return this; | ||
stopTime: undefined, | ||
abortedTime: undefined, | ||
children: [], | ||
}, | ||
}; | ||
} | ||
@@ -70,39 +93,34 @@ | ||
* @param {AbortSignal|undefined} [signal] | ||
* @returns {import("../types/advanced-types.js").InsightEvent} | ||
* @returns {InsightEvent} | ||
*/ | ||
export function newEvent(logger, signal) { | ||
return new InsightEventConstructor(logger, signal); | ||
return InsightEventConstructor(logger, signal); | ||
} | ||
/** | ||
* Create a 'child' event, reuses the logger, adds callstack to the passed event | ||
* Create a 'child' event, reuses the logger, adds it als a child to the passed event | ||
* | ||
* @since 0.1.0 | ||
* | ||
* @param {import("../types/advanced-types.js").InsightEvent} event | ||
* @returns {import("../types/advanced-types.js").InsightEvent} | ||
* @param {InsightEvent} event | ||
* @returns {InsightEvent} | ||
*/ | ||
export function newEventFromEvent(event) { | ||
if (event.signal?.aborted) { | ||
event.callStack.push({ | ||
type: "aborted", | ||
name: event.name, | ||
time: Date.now(), | ||
}); | ||
// @ts-ignore | ||
event.calculateDuration(); | ||
event.span.abortedTime = Date.now(); | ||
throw AppError.serverError({ | ||
message: "Operation aborted", // @ts-ignore | ||
event: getEventRoot(event).toJSON(), | ||
message: "Operation aborted", | ||
span: getEventRoot(event).span, | ||
}); | ||
} | ||
const callStack = []; | ||
event.callStack.push(callStack); | ||
const newEvent = InsightEventConstructor(event.log, event.signal); | ||
const newEvent = new InsightEventConstructor(event.log, event.signal); | ||
newEvent.callStack = callStack; | ||
// @ts-ignore | ||
newEvent.root = event; | ||
// Add ot parent | ||
event.span.children.push(newEvent.span); | ||
// Set root | ||
newEvent.rootEvent = event.rootEvent ?? event; | ||
return newEvent; | ||
@@ -116,3 +134,3 @@ } | ||
* | ||
* @param {import("../types/advanced-types.js").InsightEvent} event | ||
* @param {InsightEvent} event | ||
* @param {string} name | ||
@@ -123,28 +141,21 @@ * @returns {void} | ||
event.name = name; | ||
event.span.name = name; | ||
event.span.startTime = Date.now(); | ||
if (event.signal?.aborted) { | ||
event.callStack.push({ | ||
type: "aborted", | ||
name: event.name, | ||
time: Date.now(), | ||
}); | ||
event.span.abortedTime = Date.now(); | ||
throw AppError.serverError({ | ||
message: "Operation aborted", // @ts-ignore | ||
event: getEventRoot(event).toJSON(), | ||
message: "Operation aborted", | ||
span: getEventRoot(event).span, | ||
}); | ||
} | ||
event.callStack.push({ | ||
type: "start", | ||
name, | ||
time: Date.now(), | ||
}); | ||
} | ||
/** | ||
* Rename an event, and all callStack items | ||
* Rename an event | ||
* | ||
* @since 0.1.0 | ||
* | ||
* @param {import("../types/advanced-types.js").InsightEvent} event | ||
* @param {InsightEvent} event | ||
* @param {string} name | ||
@@ -155,22 +166,10 @@ * @returns {void} | ||
event.name = name; | ||
event.span.name = name; | ||
for (const item of event.callStack) { | ||
// @ts-ignore | ||
if (typeof item.name === "string") { | ||
// @ts-ignore | ||
item.name = name; | ||
} | ||
} | ||
if (event.signal?.aborted) { | ||
event.span.abortedTime = Date.now(); | ||
if (event.signal?.aborted) { | ||
event.callStack.push({ | ||
type: "aborted", | ||
name: event.name, | ||
time: Date.now(), | ||
}); | ||
// @ts-ignore | ||
event.calculateDuration(); | ||
throw AppError.serverError({ | ||
message: "Operation aborted", // @ts-ignore | ||
event: getEventRoot(event).toJSON(), | ||
message: "Operation aborted", | ||
span: getEventRoot(event).span, | ||
}); | ||
@@ -185,18 +184,18 @@ } | ||
* | ||
* @param {import("../types/advanced-types.js").InsightEvent} event | ||
* @param {InsightEvent} event | ||
* @returns {void} | ||
*/ | ||
export function eventStop(event) { | ||
event.callStack.push({ | ||
type: "stop", | ||
name: event.name, | ||
time: Date.now(), | ||
}); | ||
event.span.stopTime = Date.now(); | ||
// @ts-ignore | ||
event.calculateDuration(); | ||
if (event.span.startTime && event.span.stopTime) { | ||
event.span.duration = event.span.stopTime - event.span.startTime; | ||
} | ||
// @ts-ignore | ||
if (isNil(event.root)) { | ||
event.log.info(event); | ||
if (isNil(event.rootEvent)) { | ||
event.log.info({ | ||
type: "event_span", | ||
aborted: !!event.signal?.aborted, | ||
span: event.span, | ||
}); | ||
} | ||
@@ -208,7 +207,7 @@ } | ||
* | ||
* @param {import("../types/advanced-types.js").InsightEvent} event | ||
* @returns {import("../types/advanced-types.js").InsightEvent} | ||
* @param {InsightEvent} event | ||
* @returns {InsightEvent} | ||
*/ | ||
function getEventRoot(event) { | ||
return isNil(event.parent) ? event : getEventRoot(event.parent); | ||
return isNil(event.rootEvent) ? event : event.rootEvent; | ||
} |
@@ -30,50 +30,2 @@ import { RandomUUIDOptions } from "crypto"; | ||
/** | ||
* Basic timing and call information | ||
*/ | ||
export type InsightEventCall = | ||
| { | ||
type: "stop" | "aborted"; | ||
name?: string; | ||
/** | ||
* Time in milliseconds since some kind of epoch, this may be unix epoch or process start | ||
*/ | ||
time: number; | ||
} | ||
| { | ||
type: "start"; | ||
name?: string; | ||
/** | ||
* Duration in milliseconds between (end|aborted) and start time. This is filled when an | ||
* event is aborted or stopped via `eventStop`. | ||
*/ | ||
duration?: number; | ||
/** | ||
* Time in milliseconds since some kind of epoch, this may be unix epoch or process start | ||
*/ | ||
time: number; | ||
} | ||
| InsightEventCall[]; | ||
/** | ||
* Encapsulate the base information needed to dispatch events | ||
*/ | ||
export interface InsightEvent { | ||
log: Logger; | ||
signal?: AbortSignal; | ||
/** | ||
* If event is first event dispatched in chain | ||
*/ | ||
parent?: InsightEvent; | ||
name?: string; | ||
callStack: InsightEventCall[]; | ||
} | ||
/** | ||
* Options for processDirectoryRecursive and processDirectoryRecursiveSync | ||
@@ -80,0 +32,0 @@ */ |
66304
2394
+ Added@types/node@20.2.5(transitive)
- Removed@types/node@20.2.1(transitive)
Updated@types/node@20.2.5