diff --git a/changelog.md b/changelog.md index b8901dc..f575f10 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,13 @@ # Changelog +## 2025-12-04 - 4.0.0 - BREAKING CHANGE(typedrouter) +Introduce options object for TypedRouter.routeAndAddResponse (localRequest, skipHooks); add defaultRouteOptions and make hook calls respect skipHooks; bump package version to 3.2.3 + +- Changed TypedRouter.routeAndAddResponse signature to accept an options object ({ localRequest?: boolean; skipHooks?: boolean }) instead of a boolean second argument — this is a breaking API change. +- Added TypedRouter.defaultRouteOptions with defaults { localRequest: false, skipHooks: false }. +- Routing now respects options.localRequest and options.skipHooks; hook calls (onIncomingRequest, onOutgoingResponse, onIncomingResponse) are skipped when skipHooks is true to avoid hook recursion or duplicate handling (useful for broadcast-received messages). +- Bumped package.json version to 3.2.3. + ## 2025-12-04 - 3.2.2 - fix(typedrequest) Add skipHooks flag to TypedRequest to optionally suppress global hooks for internal requests diff --git a/package.json b/package.json index d6aa1df..10ccca5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@api.global/typedrequest", - "version": "3.2.2", + "version": "3.2.3", "private": false, "description": "A TypeScript library for making typed requests towards APIs, including facilities for handling requests, routing, and virtual stream handling.", "main": "dist_ts/index.js", diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index a8763ad..e82336f 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@api.global/typedrequest', - version: '3.2.2', + version: '4.0.0', description: 'A TypeScript library for making typed requests towards APIs, including facilities for handling requests, routing, and virtual stream handling.' } diff --git a/ts/classes.typedrouter.ts b/ts/classes.typedrouter.ts index d5a2c7a..ff3a263 100644 --- a/ts/classes.typedrouter.ts +++ b/ts/classes.typedrouter.ts @@ -162,14 +162,26 @@ export class TypedRouter { return typedHandler; } + /** + * Options for routeAndAddResponse + */ + public static defaultRouteOptions = { + localRequest: false, + skipHooks: false, + }; + /** * if typedrequest object has correlation.phase === 'request' -> routes a typed request object to a handler * if typedrequest object has correlation.phase === 'response' -> routes a typed request object to request fire event * @param typedRequestArg + * @param optionsArg - Options object with: + * - localRequest: treat as local request (default: false) + * - skipHooks: skip calling hooks for this routing (default: false, use for broadcast-received messages) */ public async routeAndAddResponse< T extends plugins.typedRequestInterfaces.ITypedRequest = plugins.typedRequestInterfaces.ITypedRequest - >(typedRequestArg: T, localRequestArg = false): Promise { + >(typedRequestArg: T, optionsArg: { localRequest?: boolean; skipHooks?: boolean } = {}): Promise { + const options = { ...TypedRouter.defaultRouteOptions, ...optionsArg }; // decoding first typedRequestArg = VirtualStream.decodePayloadFromNetwork(typedRequestArg, { typedrouter: this, @@ -187,18 +199,20 @@ export class TypedRouter { } // lets do normal routing - if (typedRequestArg?.correlation?.phase === 'request' || localRequestArg) { + if (typedRequestArg?.correlation?.phase === 'request' || options.localRequest) { const requestStartTime = Date.now(); - // Hook: incoming request - this.callHook('onIncomingRequest', { - correlationId: typedRequestArg.correlation?.id || 'unknown', - method: typedRequestArg.method, - direction: 'incoming', - phase: 'request', - timestamp: requestStartTime, - payload: typedRequestArg.request, - }); + // Hook: incoming request (skip if routing broadcast-received messages) + if (!options.skipHooks) { + this.callHook('onIncomingRequest', { + correlationId: typedRequestArg.correlation?.id || 'unknown', + method: typedRequestArg.method, + direction: 'incoming', + phase: 'request', + timestamp: requestStartTime, + payload: typedRequestArg.request, + }); + } const typedHandler = this.getTypedHandlerForMethod(typedRequestArg.method); @@ -217,16 +231,18 @@ export class TypedRouter { }); // Hook: outgoing response (error - no handler) - this.callHook('onOutgoingResponse', { - correlationId: typedRequestArg.correlation?.id || 'unknown', - method: typedRequestArg.method, - direction: 'outgoing', - phase: 'response', - timestamp: Date.now(), - durationMs: Date.now() - requestStartTime, - payload: typedRequestArg.response, - error: typedRequestArg.error?.text, - }); + if (!options.skipHooks) { + this.callHook('onOutgoingResponse', { + correlationId: typedRequestArg.correlation?.id || 'unknown', + method: typedRequestArg.method, + direction: 'outgoing', + phase: 'response', + timestamp: Date.now(), + durationMs: Date.now() - requestStartTime, + payload: typedRequestArg.response, + error: typedRequestArg.error?.text, + }); + } return typedRequestArg; } @@ -239,29 +255,33 @@ export class TypedRouter { }); // Hook: outgoing response (success) - this.callHook('onOutgoingResponse', { - correlationId: typedRequestArg.correlation?.id || 'unknown', - method: typedRequestArg.method, - direction: 'outgoing', - phase: 'response', - timestamp: Date.now(), - durationMs: Date.now() - requestStartTime, - payload: typedRequestArg.response, - error: typedRequestArg.error?.text, - }); + if (!options.skipHooks) { + this.callHook('onOutgoingResponse', { + correlationId: typedRequestArg.correlation?.id || 'unknown', + method: typedRequestArg.method, + direction: 'outgoing', + phase: 'response', + timestamp: Date.now(), + durationMs: Date.now() - requestStartTime, + payload: typedRequestArg.response, + error: typedRequestArg.error?.text, + }); + } return typedRequestArg; } else if (typedRequestArg?.correlation?.phase === 'response') { // Hook: incoming response - this.callHook('onIncomingResponse', { - correlationId: typedRequestArg.correlation?.id || 'unknown', - method: typedRequestArg.method, - direction: 'incoming', - phase: 'response', - timestamp: Date.now(), - payload: typedRequestArg.response, - error: typedRequestArg.error?.text, - }); + if (!options.skipHooks) { + this.callHook('onIncomingResponse', { + correlationId: typedRequestArg.correlation?.id || 'unknown', + method: typedRequestArg.method, + direction: 'incoming', + phase: 'response', + timestamp: Date.now(), + payload: typedRequestArg.response, + error: typedRequestArg.error?.text, + }); + } this.fireEventInterestMap .findInterest(typedRequestArg.correlation.id)