diff --git a/src/engine/executionEngine.ts b/src/engine/executionEngine.ts index f504bfa..d26f7e7 100644 --- a/src/engine/executionEngine.ts +++ b/src/engine/executionEngine.ts @@ -1,4 +1,3 @@ - import { TraceableEngine } from './traceableEngine'; import { EngineTrace } from '../common/models/engineTrace.model'; diff --git a/src/engine/traceableEngine.ts b/src/engine/traceableEngine.ts index fba0450..a4623c3 100644 --- a/src/engine/traceableEngine.ts +++ b/src/engine/traceableEngine.ts @@ -7,8 +7,8 @@ import { DEFAULT_TRACE_CONFIG, TraceOptions } from '../common/models/engineTrace import { ExecutionTrace, ExecutionTraceExtractor, isExecutionTrace } from '../common/models/executionTrace.model'; import { Awaited } from '../common/utils/awaited'; import { extract } from '../common/utils/jsonQuery'; +import { executionTrace } from '../execution/trace'; import { ExecutionTimer } from '../timer/executionTimer'; -import { executionTrace } from '../trace/trace'; /** * Represents a class for traceable execution of functions. diff --git a/src/execution/memoize.decorator.spec.ts b/src/execution/memoize.decorator.spec.ts index c9e1158..b14b93c 100644 --- a/src/execution/memoize.decorator.spec.ts +++ b/src/execution/memoize.decorator.spec.ts @@ -95,7 +95,8 @@ describe('memoize decorator', () => { if (memoContext.isMemoized) { memoizedCalls++; } - }) async throwData(name: string): Promise { + }) + async throwData(name: string): Promise { totalFunctionCalls++; throw new Error(`hello ${name} but I throw!`); } diff --git a/src/execution/memoize.ts b/src/execution/memoize.ts index f1c835a..b35a52a 100644 --- a/src/execution/memoize.ts +++ b/src/execution/memoize.ts @@ -1,10 +1,5 @@ import { execute } from './execute'; -import { - memoizationDefaultTTL, - memoizationKey, - memoizationMaxTTL, - MemoizeOptions -} from '../common/models/executionMemoization.model'; +import { memoizationDefaultTTL, memoizationKey, memoizationMaxTTL, MemoizeOptions } from '../common/models/executionMemoization.model'; import { generateHashId } from '../common/utils/crypto'; import { extractFunctionMetadata } from '../common/utils/functionMetadata'; diff --git a/src/trace/traceDecorator.spec.ts b/src/execution/trace.decorator.spec.ts similarity index 86% rename from src/trace/traceDecorator.spec.ts rename to src/execution/trace.decorator.spec.ts index aa2c1fb..b4cb4a6 100644 --- a/src/trace/traceDecorator.spec.ts +++ b/src/execution/trace.decorator.spec.ts @@ -1,4 +1,4 @@ -import { trace } from './traceDecorator'; +import { trace } from './trace.decorator'; describe('trace decorator', () => { const executionTraceExpectation = { @@ -21,15 +21,15 @@ describe('trace decorator', () => { }; describe('Tracing synchronous functions', () => { - const traceHandlerMock = jest.fn(); - const traceHandlerMockThrows = jest.fn(); + const onTraceEventMock = jest.fn(); + const onTraceEventMockThrows = jest.fn(); class SyncClass { greetingFrom = ['hello from class']; @trace(function (...args) { this.greetingFrom.push('hello from trace handler'); - traceHandlerMock(...args); + onTraceEventMock(...args); expect(this.greetingFrom).toEqual(['hello from class', 'hello from method', 'hello from trace handler']); }) helloWorld(): string { @@ -38,7 +38,7 @@ describe('trace decorator', () => { } @trace((...args) => { - traceHandlerMockThrows(...args); + onTraceEventMockThrows(...args); throw new Error('hello but I throw!'); }) helloWorldHandlerThrows(): string { @@ -50,8 +50,8 @@ describe('trace decorator', () => { it('should trace a synchronous function and verify context', () => { const instance = new SyncClass(); expect(instance.helloWorld()).toBe('Hello World'); - expect(traceHandlerMock).toHaveBeenCalledTimes(1); - expect(traceHandlerMock).toHaveBeenCalledWith( + expect(onTraceEventMock).toHaveBeenCalledTimes(1); + expect(onTraceEventMock).toHaveBeenCalledWith( expect.objectContaining({ metadata: expect.objectContaining({ class: 'SyncClass', @@ -69,8 +69,8 @@ describe('trace decorator', () => { it('should trace a synchronous function and verify helloWorldHandlerThrows', () => { const instance = new SyncClass(); expect(() => instance.helloWorldHandlerThrows()).toThrowError('hello but I throw!'); - expect(traceHandlerMockThrows).toHaveBeenCalledTimes(1); - expect(traceHandlerMockThrows).toHaveBeenCalledWith( + expect(onTraceEventMockThrows).toHaveBeenCalledTimes(1); + expect(onTraceEventMockThrows).toHaveBeenCalledWith( expect.objectContaining({ metadata: expect.objectContaining({ class: 'SyncClass', @@ -124,11 +124,11 @@ describe('trace decorator', () => { }); }); - describe('Tracing function traceHandlerMock and traceContext', () => { + describe('Tracing function onTraceEventMock and traceContext', () => { const traceContextDivision = { context: { metadata: { requestId: '12345' } } }; const traceContextFetchData = { context: { metadata: { requestId: '6789' } } }; - const traceHandlerDivisionMock = jest.fn(); - const traceHandlerFetchDataMock = jest.fn(); + const onTraceEventDivisionMock = jest.fn(); + const onTraceEventFetchDataMock = jest.fn(); function empty(): MethodDecorator { return function (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) { @@ -140,7 +140,7 @@ describe('trace decorator', () => { } class MyClass { - @trace(traceHandlerDivisionMock, traceContextDivision, { contextKey: '__execution' }) + @trace(onTraceEventDivisionMock, traceContextDivision, { contextKey: '__execution' }) divisionFunction(x: number, y: number, traceContext: Record = {}): number { if (y === 0) { traceContext['narratives'] = [`Throwing because division of ${x} by ${y}`]; @@ -150,7 +150,7 @@ describe('trace decorator', () => { return x / y; } - @trace(traceHandlerFetchDataMock, traceContextFetchData, { contextKey: '__execution' }) + @trace(onTraceEventFetchDataMock, traceContextFetchData, { contextKey: '__execution' }) async fetchDataFunction(url: string, traceContext: Record = {}): Promise<{ data: string }> { traceContext['narratives'] = [`Fetching data from ${url}`]; if (!url.startsWith('http')) { @@ -160,24 +160,24 @@ describe('trace decorator', () => { return { data: 'Success' }; } - @trace(traceHandlerDivisionMock, traceContextDivision) + @trace(onTraceEventDivisionMock, traceContextDivision) @empty() divisionFunctionOnAnotherDecorator(x: number, y: number, traceContext: Record = {}): number { return this.divisionFunction(x, y, traceContext); } - @trace(traceHandlerFetchDataMock, traceContextFetchData) + @trace(onTraceEventFetchDataMock, traceContextFetchData) @empty() async fetchDataFunctionOnAnotherDecorator( url: string, traceContext: Record = {} ): Promise<{ - data: string; - }> { + data: string; + }> { return this.fetchDataFunction(url, traceContext); } - @trace(traceHandlerFetchDataMock, traceContextFetchData, { errorStrategy: 'catch' }) + @trace(onTraceEventFetchDataMock, traceContextFetchData, { errorStrategy: 'catch' }) @empty() async fetchDataFunctionOnAnotherDecoratorCoughtError( url: string, @@ -188,14 +188,14 @@ describe('trace decorator', () => { } beforeEach(() => { - traceHandlerFetchDataMock.mockClear(); - traceHandlerDivisionMock.mockClear(); + onTraceEventFetchDataMock.mockClear(); + onTraceEventDivisionMock.mockClear(); }); - it('should sync trace successfully and pass correct traceHandlerMock and traceContext', () => { + it('should sync trace successfully and pass correct onTraceEventMock and traceContext', () => { const classInstance = new MyClass(); const response = classInstance.divisionFunction(1, 2); - expect(traceHandlerDivisionMock).toHaveBeenCalledWith( + expect(onTraceEventDivisionMock).toHaveBeenCalledWith( expect.objectContaining({ metadata: expect.objectContaining({ class: 'MyClass', @@ -213,10 +213,10 @@ describe('trace decorator', () => { expect(response).toEqual(0.5); }); - it('should sync trace errors and pass correct traceHandlerMock and traceContext', () => { + it('should sync trace errors and pass correct onTraceEventMock and traceContext', () => { expect(() => new MyClass().divisionFunction(1, 0)).toThrow('Throwing because division by zero is not allowed.'); - expect(traceHandlerDivisionMock).toHaveBeenCalledWith( + expect(onTraceEventDivisionMock).toHaveBeenCalledWith( expect.objectContaining({ metadata: expect.objectContaining({ class: 'MyClass', @@ -233,9 +233,9 @@ describe('trace decorator', () => { ); }); - it('should async trace successfully and pass correct traceHandlerMock and traceContext', async () => { + it('should async trace successfully and pass correct onTraceEventMock and traceContext', async () => { const response = await new MyClass().fetchDataFunction('https://api.example.com/data'); - expect(traceHandlerFetchDataMock).toHaveBeenCalledWith( + expect(onTraceEventFetchDataMock).toHaveBeenCalledWith( expect.objectContaining({ metadata: expect.objectContaining({ class: 'MyClass', @@ -253,9 +253,9 @@ describe('trace decorator', () => { expect(response).toMatchObject({ data: 'Success' }); }); - it('should async trace errors and pass correct traceHandlerMock and traceContext', async () => { + it('should async trace errors and pass correct onTraceEventMock and traceContext', async () => { await expect(new MyClass().fetchDataFunction('invalid-url')).rejects.toThrow('Invalid URL provided.'); - expect(traceHandlerFetchDataMock).toHaveBeenCalledWith( + expect(onTraceEventFetchDataMock).toHaveBeenCalledWith( expect.objectContaining({ metadata: expect.objectContaining({ class: 'MyClass', @@ -275,7 +275,7 @@ describe('trace decorator', () => { it('should async trace successfully divisionFunctionOnAnotherDecorator', async () => { const classInstance = new MyClass(); const response = classInstance.divisionFunctionOnAnotherDecorator(1, 2); - expect(traceHandlerDivisionMock).toHaveBeenNthCalledWith( + expect(onTraceEventDivisionMock).toHaveBeenNthCalledWith( 2, expect.objectContaining({ metadata: expect.objectContaining({ @@ -297,7 +297,7 @@ describe('trace decorator', () => { it('should async trace error divisionFunctionOnAnotherDecorator', async () => { expect(() => new MyClass().divisionFunctionOnAnotherDecorator(1, 0)).toThrow('Throwing because division by zero is not allowed.'); - expect(traceHandlerDivisionMock).toHaveBeenNthCalledWith( + expect(onTraceEventDivisionMock).toHaveBeenNthCalledWith( 2, expect.objectContaining({ metadata: expect.objectContaining({ @@ -318,7 +318,7 @@ describe('trace decorator', () => { it('should async trace successfully fetchDataFunctionOnAnotherDecorator', async () => { const response = await new MyClass().fetchDataFunctionOnAnotherDecorator('https://api.example.com/data'); - expect(traceHandlerFetchDataMock).toHaveBeenNthCalledWith( + expect(onTraceEventFetchDataMock).toHaveBeenNthCalledWith( 2, expect.objectContaining({ metadata: expect.objectContaining({ @@ -340,7 +340,7 @@ describe('trace decorator', () => { it('should async trace error fetchDataFunctionOnAnotherDecorator', async () => { await expect(new MyClass().fetchDataFunctionOnAnotherDecorator('invalid-url')).rejects.toThrow('Invalid URL provided.'); - expect(traceHandlerFetchDataMock).toHaveBeenNthCalledWith( + expect(onTraceEventFetchDataMock).toHaveBeenNthCalledWith( 2, expect.objectContaining({ metadata: expect.objectContaining({ @@ -361,7 +361,7 @@ describe('trace decorator', () => { it('should async trace error fetchDataFunctionOnAnotherDecorator cought', async () => { const response = await new MyClass().fetchDataFunctionOnAnotherDecoratorCoughtError('invalid-url'); - expect(traceHandlerFetchDataMock).toHaveBeenNthCalledWith( + expect(onTraceEventFetchDataMock).toHaveBeenNthCalledWith( 2, expect.objectContaining({ metadata: expect.objectContaining({ diff --git a/src/trace/traceDecorator.ts b/src/execution/trace.decorator.ts similarity index 86% rename from src/trace/traceDecorator.ts rename to src/execution/trace.decorator.ts index c878764..340e331 100644 --- a/src/trace/traceDecorator.ts +++ b/src/execution/trace.decorator.ts @@ -5,7 +5,7 @@ import { isAsync } from '../common/utils/isAsync'; /** * Method decorator to trace function execution, capturing metadata, inputs, outputs, and errors. * - * @param traceHandler - handle function of the trace context. + * @param onTraceEvent - handle function of the trace context. * @param additionalContext - Additional metadata to attach to the trace context. * @param options - Configuration options: * - `contextKey`: Key to store trace context on the instance. @@ -14,7 +14,7 @@ import { isAsync } from '../common/utils/isAsync'; * @returns A method decorator that wraps the original function with execution tracing. */ export function trace( - traceHandler: (traceContext: TraceContext) => void, + onTraceEvent: (traceContext: TraceContext) => void, // eslint-disable-next-line @typescript-eslint/no-explicit-any additionalContext: Record = {}, options: { contextKey?: string; errorStrategy?: 'catch' | 'throw' } = { @@ -37,7 +37,7 @@ export function trace( originalMethod.bind(this), args, (traceContext) => { - return (traceHandler.bind(this) as typeof traceHandler)({ ...traceContext, ...thisTraceContext }); + return (onTraceEvent.bind(this) as typeof onTraceEvent)({ ...traceContext, ...thisTraceContext }); }, options )?.then((r) => (options?.errorStrategy === 'catch' ? r.errors : r.outputs)); @@ -46,7 +46,7 @@ export function trace( originalMethod.bind(this) as () => O, args, (traceContext) => { - return (traceHandler.bind(this) as typeof traceHandler)({ ...traceContext, ...thisTraceContext }); + return (onTraceEvent.bind(this) as typeof onTraceEvent)({ ...traceContext, ...thisTraceContext }); }, options ); diff --git a/src/trace/trace.spec.ts b/src/execution/trace.spec.ts similarity index 86% rename from src/trace/trace.spec.ts rename to src/execution/trace.spec.ts index 0d54fe7..d18ca99 100644 --- a/src/trace/trace.spec.ts +++ b/src/execution/trace.spec.ts @@ -101,7 +101,7 @@ describe('ExecutionTrace', () => { }); }); - describe('Tracing function traceHandlerMock and traceContext', () => { + describe('Tracing function onTraceEventMock and traceContext', () => { const divisionFunction = (x: number, y: number, traceContext: Record) => { if (y === 0) { traceContext['narratives'] = [`Throwing because division of ${x} by ${y}`]; @@ -120,14 +120,14 @@ describe('ExecutionTrace', () => { return { data: 'Success' }; }; - it('should sync trace successfully and pass correct traceHandlerMock and traceContext', () => { - const traceHandlerMock = jest.fn(); + it('should sync trace successfully and pass correct onTraceEventMock and traceContext', () => { + const onTraceEventMock = jest.fn(); const response = executionTrace.bind({ __execution: { context: { metadata: { requestId: '12345' } } } })( divisionFunction, [1, 2], - traceHandlerMock + onTraceEventMock ); - expect(traceHandlerMock).toHaveBeenCalledWith( + expect(onTraceEventMock).toHaveBeenCalledWith( expect.objectContaining({ metadata: expect.any(Object), ...successfullExecutionTraceExpectation, @@ -137,14 +137,14 @@ describe('ExecutionTrace', () => { expect(response).toMatchObject(successfullExecutionTraceExpectation); }); - it('should sync trace errors and pass correct traceHandlerMock and traceContext', () => { - const traceHandlerMock = jest.fn(); + it('should sync trace errors and pass correct onTraceEventMock and traceContext', () => { + const onTraceEventMock = jest.fn(); const traceContextMock = { context: { metadata: { requestId: '12345' } } }; - const response = executionTrace.bind({ __execution: traceContextMock })(divisionFunction, [1, 0], traceHandlerMock, { + const response = executionTrace.bind({ __execution: traceContextMock })(divisionFunction, [1, 0], onTraceEventMock, { errorStrategy: 'catch', contextKey: '__execution' }); - expect(traceHandlerMock).toHaveBeenCalledWith( + expect(onTraceEventMock).toHaveBeenCalledWith( expect.objectContaining({ ...failedExecutionTraceExpectation, ...traceContextMock, @@ -154,16 +154,16 @@ describe('ExecutionTrace', () => { expect(response).toMatchObject(failedExecutionTraceExpectation); }); - it('should async trace successfully and pass correct traceHandlerMock and traceContext', async () => { - const traceHandlerMock = jest.fn(); + it('should async trace successfully and pass correct onTraceEventMock and traceContext', async () => { + const onTraceEventMock = jest.fn(); const traceContextMock = { context: { metadata: { requestId: '12345' } } }; const response = await (executionTrace.bind({ __execution: traceContextMock }) as typeof executionTrace)( fetchDataFunction, ['https://api.example.com/data'], - traceHandlerMock, + onTraceEventMock, { contextKey: '__execution' } ); - expect(traceHandlerMock).toHaveBeenCalledWith( + expect(onTraceEventMock).toHaveBeenCalledWith( expect.objectContaining({ ...successfullExecutionTraceExpectation, ...traceContextMock, @@ -173,14 +173,14 @@ describe('ExecutionTrace', () => { expect(response).toMatchObject(successfullExecutionTraceExpectation); }); - it('should async trace errors and pass correct traceHandlerMock and traceContext', async () => { - const traceHandlerMock = jest.fn(); + it('should async trace errors and pass correct onTraceEventMock and traceContext', async () => { + const onTraceEventMock = jest.fn(); const traceContextMock = { context: { metadata: { requestId: '12345' } } }; - const response = await executionTrace.bind({ __execution: traceContextMock })(fetchDataFunction, ['invalid-url'], traceHandlerMock, { + const response = await executionTrace.bind({ __execution: traceContextMock })(fetchDataFunction, ['invalid-url'], onTraceEventMock, { errorStrategy: 'catch', contextKey: '__execution' }); - expect(traceHandlerMock).toHaveBeenCalledWith( + expect(onTraceEventMock).toHaveBeenCalledWith( expect.objectContaining({ ...failedExecutionTraceExpectation, ...traceContextMock, @@ -191,7 +191,7 @@ describe('ExecutionTrace', () => { }); it('should throw an error and trace context when provided with an invalid URL', async () => { - const traceHandlerMock = jest.fn(); + const onTraceEventMock = jest.fn(); const traceContextMock = { context: { metadata: { requestId: '12345' } } }; // Expect the trace function to throw an error for an invalid URL @@ -199,12 +199,12 @@ describe('ExecutionTrace', () => { (executionTrace.bind({ __execution: traceContextMock }) as typeof executionTrace)( fetchDataFunction, ['invalid-url'], - traceHandlerMock, + onTraceEventMock, { contextKey: '__execution' } ) ).rejects.toThrow('Invalid URL provided.'); // Check the error message - expect(traceHandlerMock).toHaveBeenCalledWith( + expect(onTraceEventMock).toHaveBeenCalledWith( expect.objectContaining({ ...failedExecutionTraceExpectation, ...traceContextMock, diff --git a/src/trace/trace.ts b/src/execution/trace.ts similarity index 90% rename from src/trace/trace.ts rename to src/execution/trace.ts index 20c9ca7..e8c0784 100644 --- a/src/trace/trace.ts +++ b/src/execution/trace.ts @@ -1,11 +1,10 @@ - +import { execute } from './execute'; import { FunctionMetadata } from '../common/models/executionFunction.model'; import { ExecutionTrace } from '../common/models/executionTrace.model'; import { TimerDetailsModel } from '../common/models/timer.model'; import { Awaited } from '../common/utils/awaited'; import { extractFunctionMetadata } from '../common/utils/functionMetadata'; import { safeError } from '../common/utils/safeError'; -import { execute } from '../execution/execute'; import { ExecutionTimer } from '../timer/executionTimer'; export interface TraceContext extends ExecutionTrace, O> { @@ -24,14 +23,14 @@ function calculateTimeAndDuration(executionTimer: ExecutionTimer): TimerDetailsM export function executionTrace( blockFunction: (...params) => Promise, inputs?: Array, - traceHandler?: (traceContext: TraceContext) => void, + onTraceEvent?: (traceContext: TraceContext) => void, options?: { contextKey?: string; errorStrategy?: 'catch' | 'throw' } ): Promise, Awaited>>; export function executionTrace( blockFunction: (...params) => O, inputs?: Array, - traceHandler?: (traceContext: TraceContext) => void, + onTraceEvent?: (traceContext: TraceContext) => void, options?: { contextKey?: string; errorStrategy?: 'catch' | 'throw' } ): ExecutionTrace, O>; @@ -43,7 +42,7 @@ export function executionTrace( * * @param blockFunction - The function to execute and trace. * @param inputs - An array of input parameters to pass to `blockFunction`. Defaults to an empty array. - * @param traceHandler - An optional callback function that processes the trace context after execution. + * @param onTraceEvent - An optional callback function that processes the trace context after execution. * @param options - Optional configuration: * - `contextKey`: A key to store and retrieve execution context. * - `errorStrategy`: Determines how errors are handled: @@ -57,7 +56,7 @@ export function executionTrace( export function executionTrace( blockFunction: (...params) => O | Promise, inputs: Array = [], - traceHandler?: (traceContext: TraceContext) => void, + onTraceEvent?: (traceContext: TraceContext) => void, options: { contextKey?: string; errorStrategy?: 'catch' | 'throw' } = { contextKey: undefined, errorStrategy: 'throw' @@ -87,8 +86,8 @@ export function executionTrace( startTime, ...calculateTimeAndDuration(executionTimer) }; - if (typeof traceHandler === 'function') { - traceHandler(traceContext); + if (typeof onTraceEvent === 'function') { + onTraceEvent(traceContext); } return traceContext; }, @@ -101,8 +100,8 @@ export function executionTrace( startTime, ...calculateTimeAndDuration(executionTimer) }; - if (typeof traceHandler === 'function') { - traceHandler(traceContext); + if (typeof onTraceEvent === 'function') { + onTraceEvent(traceContext); } if (options?.errorStrategy === 'catch') { return traceContext; diff --git a/src/index.ts b/src/index.ts index 3520658..3a5eb6c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,7 +19,7 @@ export * from './execution/cache'; export * from './execution/execute'; export * from './execution/memoize.decorator'; export * from './execution/memoize'; +export * from './execution/trace.decorator'; +export * from './execution/trace'; export * from './timer/executionTimer'; -export * from './trace/trace'; -export * from './trace/traceDecorator'; export * from './trace/traceableExecution';