@zudojs/messaging 0.0.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -7
- package/dist/dispatcher/dispatcherCore.d.ts +38 -3
- package/dist/dispatcher/dispatcherCore.js +149 -23
- package/dist/dispatcher/dispatcherType.type.d.ts +16 -5
- package/dist/handlerRegistry/handlerRegistryStore.d.ts +10 -0
- package/dist/handlerRegistry/handlerRegistryStore.js +26 -2
- package/dist/handlerRegistry/handlerRegistryType.type.d.ts +6 -3
- package/dist/message/index.d.ts +1 -1
- package/dist/message/index.js +1 -1
- package/dist/message/messageFactory.d.ts +17 -1
- package/dist/message/messageFactory.js +33 -0
- package/dist/messageBus/messageBusCore.d.ts +4 -3
- package/dist/messageBus/messageBusCore.js +20 -25
- package/dist/messageBus/messageBusType.type.d.ts +12 -4
- package/dist/messageMiddleware/messageMiddlewarePipeline.js +17 -6
- package/dist/messageMiddleware/messageMiddlewareType.type.d.ts +6 -0
- package/package.json +14 -6
- package/dist/.tsbuildinfo +0 -1
- package/dist/dispatcher/dispatcherCore.d.ts.map +0 -1
- package/dist/dispatcher/dispatcherCore.js.map +0 -1
- package/dist/dispatcher/dispatcherType.type.d.ts.map +0 -1
- package/dist/dispatcher/dispatcherType.type.js.map +0 -1
- package/dist/dispatcher/index.d.ts.map +0 -1
- package/dist/dispatcher/index.js.map +0 -1
- package/dist/errors/index.d.ts.map +0 -1
- package/dist/errors/index.js.map +0 -1
- package/dist/handlerRegistry/handlerRegistryStore.d.ts.map +0 -1
- package/dist/handlerRegistry/handlerRegistryStore.js.map +0 -1
- package/dist/handlerRegistry/handlerRegistryType.type.d.ts.map +0 -1
- package/dist/handlerRegistry/handlerRegistryType.type.js.map +0 -1
- package/dist/handlerRegistry/index.d.ts.map +0 -1
- package/dist/handlerRegistry/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/message/index.d.ts.map +0 -1
- package/dist/message/index.js.map +0 -1
- package/dist/message/messageFactory.d.ts.map +0 -1
- package/dist/message/messageFactory.js.map +0 -1
- package/dist/message/messageType.type.d.ts.map +0 -1
- package/dist/message/messageType.type.js.map +0 -1
- package/dist/messageBus/index.d.ts.map +0 -1
- package/dist/messageBus/index.js.map +0 -1
- package/dist/messageBus/messageBusCore.d.ts.map +0 -1
- package/dist/messageBus/messageBusCore.js.map +0 -1
- package/dist/messageBus/messageBusType.type.d.ts.map +0 -1
- package/dist/messageBus/messageBusType.type.js.map +0 -1
- package/dist/messageContext/index.d.ts.map +0 -1
- package/dist/messageContext/index.js.map +0 -1
- package/dist/messageContext/messageContextType.type.d.ts.map +0 -1
- package/dist/messageContext/messageContextType.type.js.map +0 -1
- package/dist/messageHandler/index.d.ts.map +0 -1
- package/dist/messageHandler/index.js.map +0 -1
- package/dist/messageHandler/messageHandlerType.type.d.ts.map +0 -1
- package/dist/messageHandler/messageHandlerType.type.js.map +0 -1
- package/dist/messageMiddleware/index.d.ts.map +0 -1
- package/dist/messageMiddleware/index.js.map +0 -1
- package/dist/messageMiddleware/messageMiddlewarePipeline.d.ts.map +0 -1
- package/dist/messageMiddleware/messageMiddlewarePipeline.js.map +0 -1
- package/dist/messageMiddleware/messageMiddlewareType.type.d.ts.map +0 -1
- package/dist/messageMiddleware/messageMiddlewareType.type.js.map +0 -1
|
@@ -6,12 +6,13 @@
|
|
|
6
6
|
import { createMessage } from "../message/messageFactory.js";
|
|
7
7
|
import { HandlerRegistryStore } from "../handlerRegistry/handlerRegistryStore.js";
|
|
8
8
|
import { DefaultDispatcher } from "../dispatcher/dispatcherCore.js";
|
|
9
|
-
import {
|
|
9
|
+
import { MessageBusDisposedError } from "@zudojs/errors";
|
|
10
10
|
/** Default in-memory message bus. */
|
|
11
11
|
export class InMemoryMessageBus {
|
|
12
12
|
dispatcher;
|
|
13
13
|
registry;
|
|
14
14
|
defaultTimeout;
|
|
15
|
+
handlerSequence = 0;
|
|
15
16
|
_disposed = false;
|
|
16
17
|
constructor(options = {}) {
|
|
17
18
|
this.registry = new HandlerRegistryStore({
|
|
@@ -31,36 +32,27 @@ export class InMemoryMessageBus {
|
|
|
31
32
|
async dispatch(message, options = {}) {
|
|
32
33
|
this.validateNotDisposed(message);
|
|
33
34
|
const timeout = options.timeout ?? this.defaultTimeout;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
// The timeout is the dispatcher's job. The bus used to race the dispatch
|
|
36
|
+
// itself and abort on expiry, which produced a generic abort error and
|
|
37
|
+
// meant the dispatcher's own documented `timeout` option stayed dead.
|
|
38
|
+
return this.dispatcher.dispatch(message, {
|
|
39
|
+
...options,
|
|
40
|
+
timeout,
|
|
41
|
+
});
|
|
39
42
|
}
|
|
40
|
-
validateNotDisposed(
|
|
43
|
+
validateNotDisposed(_message) {
|
|
41
44
|
if (this._disposed) {
|
|
42
|
-
throw new
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
async dispatchWithTimeout(message, options) {
|
|
46
|
-
const controller = new AbortController();
|
|
47
|
-
const timeoutId = setTimeout(() => controller.abort(), options.timeout ?? 0);
|
|
48
|
-
try {
|
|
49
|
-
const result = await this.dispatcher.dispatch(message, {
|
|
50
|
-
...options,
|
|
51
|
-
signal: controller.signal,
|
|
52
|
-
});
|
|
53
|
-
return result;
|
|
54
|
-
}
|
|
55
|
-
finally {
|
|
56
|
-
clearTimeout(timeoutId);
|
|
45
|
+
throw new MessageBusDisposedError();
|
|
57
46
|
}
|
|
58
47
|
}
|
|
59
48
|
async send(input, options = {}) {
|
|
60
49
|
return this.dispatch(createMessage(input), options);
|
|
61
50
|
}
|
|
62
51
|
on(messageType, handler, options = {}) {
|
|
63
|
-
|
|
52
|
+
// A clock-derived id collided whenever two handlers for the same type
|
|
53
|
+
// were registered inside one millisecond, which then threw
|
|
54
|
+
// DuplicateMessageHandlerError from what looked like ordinary setup.
|
|
55
|
+
const id = options.id ?? `handler:${messageType}:${++this.handlerSequence}`;
|
|
64
56
|
const namedHandler = {
|
|
65
57
|
id,
|
|
66
58
|
name: id,
|
|
@@ -77,8 +69,11 @@ export class InMemoryMessageBus {
|
|
|
77
69
|
off(handlerId) {
|
|
78
70
|
return this.registry.unregister(handlerId);
|
|
79
71
|
}
|
|
80
|
-
use(middleware) {
|
|
81
|
-
this.dispatcher.use(middleware);
|
|
72
|
+
use(middleware, options) {
|
|
73
|
+
return this.dispatcher.use(middleware, options);
|
|
74
|
+
}
|
|
75
|
+
removeMiddleware(middlewareId) {
|
|
76
|
+
return this.dispatcher.removeMiddleware(middlewareId);
|
|
82
77
|
}
|
|
83
78
|
hasHandlers(messageType) {
|
|
84
79
|
return this.registry.resolve(messageType).length > 0;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { Message, MessageInput } from "../message/messageType.type.js";
|
|
10
10
|
import type { MessageHandler, NamedMessageHandler } from "../messageHandler/messageHandlerType.type.js";
|
|
11
|
-
import type { MessageMiddlewareLike } from "../messageMiddleware/messageMiddlewareType.type.js";
|
|
11
|
+
import type { MessageMiddlewareLike, MessageMiddlewareOptions } from "../messageMiddleware/messageMiddlewareType.type.js";
|
|
12
12
|
import type { DispatchResult, DispatchOptions } from "../dispatcher/dispatcherType.type.js";
|
|
13
13
|
/**
|
|
14
14
|
* Options for creating a message bus.
|
|
@@ -59,10 +59,18 @@ export interface MessageBus {
|
|
|
59
59
|
off(handlerId: string): boolean;
|
|
60
60
|
/**
|
|
61
61
|
* Adds middleware to the bus.
|
|
62
|
+
*
|
|
63
|
+
* Middleware runs in ascending `priority` order (default 100).
|
|
64
|
+
*
|
|
65
|
+
* @returns The identifier {@link MessageBus.removeMiddleware} takes.
|
|
62
66
|
*/
|
|
63
|
-
use<TMessage extends Message = Message, TResult = unknown>(middleware: MessageMiddlewareLike<TMessage, TResult>, options?:
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
use<TMessage extends Message = Message, TResult = unknown>(middleware: MessageMiddlewareLike<TMessage, TResult>, options?: MessageMiddlewareOptions): string;
|
|
68
|
+
/**
|
|
69
|
+
* Removes middleware by the id `use` returned.
|
|
70
|
+
*
|
|
71
|
+
* @returns Whether a registration was removed.
|
|
72
|
+
*/
|
|
73
|
+
removeMiddleware(middlewareId: string): boolean;
|
|
66
74
|
/**
|
|
67
75
|
* Checks if handlers are registered for a message type.
|
|
68
76
|
*/
|
|
@@ -49,20 +49,31 @@ export async function runMessagePipeline(middlewareList, handler, message, optio
|
|
|
49
49
|
const metadata = options.metadata ?? {};
|
|
50
50
|
const state = options.state ?? new Map();
|
|
51
51
|
const executions = [];
|
|
52
|
-
const resolvedMiddleware = middlewareList.map((mw) => {
|
|
52
|
+
const resolvedMiddleware = middlewareList.map((mw, index) => {
|
|
53
53
|
const resolved = resolveMiddlewareLike(mw);
|
|
54
|
+
// Each record now names the middleware it belongs to and carries what
|
|
55
|
+
// that middleware returned. Both used to be wrong: every record was
|
|
56
|
+
// labelled with the pipeline's own execution id, and `result` was pushed
|
|
57
|
+
// from a `finally` block that could only ever see `undefined`.
|
|
58
|
+
const middlewareId = options.middlewareIds?.[index] ?? `${executionId}#${index}`;
|
|
54
59
|
return async (ctx, next) => {
|
|
55
60
|
const mwStart = performance.now();
|
|
56
61
|
try {
|
|
57
|
-
|
|
62
|
+
const result = await resolved(ctx, next);
|
|
63
|
+
executions.push({
|
|
64
|
+
middlewareId,
|
|
65
|
+
result,
|
|
66
|
+
duration: performance.now() - mwStart,
|
|
67
|
+
});
|
|
68
|
+
return result;
|
|
58
69
|
}
|
|
59
|
-
|
|
60
|
-
const duration = performance.now() - mwStart;
|
|
70
|
+
catch (error) {
|
|
61
71
|
executions.push({
|
|
62
|
-
middlewareId
|
|
72
|
+
middlewareId,
|
|
63
73
|
result: undefined,
|
|
64
|
-
duration,
|
|
74
|
+
duration: performance.now() - mwStart,
|
|
65
75
|
});
|
|
76
|
+
throw error;
|
|
66
77
|
}
|
|
67
78
|
};
|
|
68
79
|
});
|
|
@@ -73,6 +73,7 @@ export interface RegisteredMessageMiddleware<TMessage extends Message = Message,
|
|
|
73
73
|
* Execution record for a single middleware.
|
|
74
74
|
*/
|
|
75
75
|
export interface MessageMiddlewareExecution<TResult = unknown> {
|
|
76
|
+
/** The id of the middleware this record describes. */
|
|
76
77
|
readonly middlewareId: string;
|
|
77
78
|
readonly result: TResult;
|
|
78
79
|
readonly duration: number;
|
|
@@ -92,5 +93,10 @@ export interface MessageMiddlewarePipelineOptions {
|
|
|
92
93
|
readonly signal?: AbortSignal;
|
|
93
94
|
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
94
95
|
readonly state?: Map<string, unknown>;
|
|
96
|
+
/**
|
|
97
|
+
* Identifiers parallel to the middleware list, used to label the execution
|
|
98
|
+
* records. Positions without an id fall back to a synthesised one.
|
|
99
|
+
*/
|
|
100
|
+
readonly middlewareIds?: readonly string[];
|
|
95
101
|
}
|
|
96
102
|
//# sourceMappingURL=messageMiddlewareType.type.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zudojs/messaging",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "In-process message bus infrastructure with handlers, middleware, and publish/subscribe patterns.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -13,16 +13,20 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
|
-
"dist"
|
|
16
|
+
"dist",
|
|
17
|
+
"!dist/**/*.map",
|
|
18
|
+
"!dist/**/*.tsbuildinfo",
|
|
19
|
+
"!dist/.tsbuildinfo"
|
|
17
20
|
],
|
|
18
21
|
"dependencies": {
|
|
19
|
-
"@zudojs/errors": "0.0
|
|
20
|
-
"@zudojs/constants": "0.0
|
|
22
|
+
"@zudojs/errors": "1.0.0",
|
|
23
|
+
"@zudojs/constants": "1.0.0"
|
|
21
24
|
},
|
|
22
25
|
"engines": {
|
|
23
26
|
"node": ">=24.0.0"
|
|
24
27
|
},
|
|
25
28
|
"devDependencies": {
|
|
29
|
+
"typescript": "7.0.2",
|
|
26
30
|
"vitest": "^4.1.11"
|
|
27
31
|
},
|
|
28
32
|
"license": "MIT",
|
|
@@ -36,13 +40,17 @@
|
|
|
36
40
|
"pubsub"
|
|
37
41
|
],
|
|
38
42
|
"homepage": "https://github.com/oyinlola-tech/zudo#readme",
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/oyinlola-tech/zudo/issues"
|
|
45
|
+
},
|
|
39
46
|
"repository": {
|
|
40
47
|
"type": "git",
|
|
41
|
-
"url": "https://github.com/oyinlola-tech/zudo"
|
|
48
|
+
"url": "https://github.com/oyinlola-tech/zudo",
|
|
49
|
+
"directory": "packages/messaging"
|
|
42
50
|
},
|
|
43
51
|
"scripts": {
|
|
44
52
|
"build": "tsc -p tsconfig.json",
|
|
45
|
-
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
53
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit",
|
|
46
54
|
"test": "vitest run",
|
|
47
55
|
"test:watch": "vitest",
|
|
48
56
|
"clean": "rm -rf dist"
|