@zudojs/cqrs 1.0.1 → 1.1.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 +10 -0
- package/dist/command/commandBus.core.js +1 -1
- package/dist/cqrsErrors/cqrsError.base.d.ts +5 -4
- package/dist/cqrsErrors/cqrsError.base.js +5 -14
- package/dist/cqrsMiddleware/cqrsMiddleware.compose.d.ts +14 -0
- package/dist/cqrsMiddleware/cqrsMiddleware.compose.js +33 -0
- package/dist/cqrsMiddleware/cqrsMiddleware.core.d.ts +8 -10
- package/dist/cqrsMiddleware/cqrsMiddleware.core.js +17 -28
- package/dist/cqrsMiddleware/index.d.ts +1 -0
- package/dist/cqrsMiddleware/index.js +1 -0
- package/dist/query/queryBus.core.js +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Command Query Responsibility Segregation (CQRS) primitives for separating read and write operations.
|
|
4
4
|
|
|
5
|
+
<!-- zudo-docs:start -->
|
|
6
|
+
|
|
7
|
+
**Documentation:** [zudojs.oyinlola.site/docs/packages-cqrs](https://zudojs.oyinlola.site/docs/packages-cqrs) · **For AI agents:** [Markdown version](https://zudojs.oyinlola.site/docs/packages-cqrs.md), [llms.txt](https://zudojs.oyinlola.site/llms.txt)
|
|
8
|
+
|
|
9
|
+
<!-- zudo-docs:end -->
|
|
10
|
+
|
|
5
11
|
## Installation
|
|
6
12
|
|
|
7
13
|
```bash
|
|
@@ -103,6 +109,10 @@ Validation and handler resolution run at the end of the middleware pipeline,
|
|
|
103
109
|
so middleware observes `InvalidCommandError` / `CommandHandlerNotFoundError`
|
|
104
110
|
like any other failure. Each middleware may call `next()` at most once.
|
|
105
111
|
|
|
112
|
+
`onTiming` is an observer: if it throws, the command or query still
|
|
113
|
+
succeeds (or fails with its own error). Pass `onTimingError` to see those
|
|
114
|
+
observer failures.
|
|
115
|
+
|
|
106
116
|
## Features
|
|
107
117
|
|
|
108
118
|
- Command bus for write operations
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { executeCommandHandler } from "../command/commandHandler.core.js";
|
|
2
2
|
import { CommandHandlerNotFoundError, DuplicateHandlerError, InvalidCommandError, } from "../cqrsErrors/cqrsError.base.js";
|
|
3
|
-
import { composeMiddleware } from "../cqrsMiddleware/cqrsMiddleware.
|
|
3
|
+
import { composeMiddleware } from "../cqrsMiddleware/cqrsMiddleware.compose.js";
|
|
4
4
|
import { assertExecutableHandler, assertHandlerType, assertMiddleware, } from "../cqrsValidation/cqrsValidation.core.js";
|
|
5
5
|
/**
|
|
6
6
|
* Command bus implementation.
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CqrsError, ErrorCode, type ErrorMetadata } from "@zudojs/errors";
|
|
2
2
|
/**
|
|
3
3
|
* Base error for failures originating from the CQRS package.
|
|
4
|
+
*
|
|
5
|
+
* Owned by `@zudojs/errors` and re-exported here, so
|
|
6
|
+
* `instanceof CqrsError` matches across both packages.
|
|
4
7
|
*/
|
|
5
|
-
export
|
|
6
|
-
constructor(message: string, options?: BaseErrorOptions);
|
|
7
|
-
}
|
|
8
|
+
export { CqrsError };
|
|
8
9
|
/**
|
|
9
10
|
* Thrown when a CQRS request is invalid.
|
|
10
11
|
*
|
|
@@ -1,20 +1,11 @@
|
|
|
1
|
-
import { BaseError, ErrorCategory, ErrorCode, ErrorSeverity, } from "@zudojs/errors";
|
|
1
|
+
import { BaseError, CqrsError, ErrorCategory, ErrorCode, ErrorSeverity, } from "@zudojs/errors";
|
|
2
2
|
/**
|
|
3
3
|
* Base error for failures originating from the CQRS package.
|
|
4
|
+
*
|
|
5
|
+
* Owned by `@zudojs/errors` and re-exported here, so
|
|
6
|
+
* `instanceof CqrsError` matches across both packages.
|
|
4
7
|
*/
|
|
5
|
-
export
|
|
6
|
-
constructor(message, options = {}) {
|
|
7
|
-
super(message, {
|
|
8
|
-
...options,
|
|
9
|
-
code: options.code ?? ErrorCode.INTERNAL_ERROR,
|
|
10
|
-
category: options.category ?? ErrorCategory.SYSTEM,
|
|
11
|
-
severity: options.severity ?? ErrorSeverity.ERROR,
|
|
12
|
-
statusCode: options.statusCode ?? 500,
|
|
13
|
-
expose: options.expose ?? false,
|
|
14
|
-
isOperational: options.isOperational ?? true,
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
}
|
|
8
|
+
export { CqrsError };
|
|
18
9
|
/**
|
|
19
10
|
* Thrown when a CQRS request is invalid.
|
|
20
11
|
*
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CQRS middleware composition, built on `compose` from `@zudojs/middleware`.
|
|
3
|
+
*/
|
|
4
|
+
import type { CqrsMiddleware } from "../cqrsTypes/cqrsTypes.type.js";
|
|
5
|
+
/**
|
|
6
|
+
* Combines multiple middleware functions into a single middleware.
|
|
7
|
+
*
|
|
8
|
+
* Each middleware may call `next()` at most once per execution; a second
|
|
9
|
+
* call throws `MiddlewareExecutionError`. The command and query buses
|
|
10
|
+
* build their pipelines with this function, so the same rule applies
|
|
11
|
+
* there.
|
|
12
|
+
*/
|
|
13
|
+
export declare function composeMiddleware(middleware: readonly CqrsMiddleware[]): CqrsMiddleware;
|
|
14
|
+
//# sourceMappingURL=cqrsMiddleware.compose.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CQRS middleware composition, built on `compose` from `@zudojs/middleware`.
|
|
3
|
+
*/
|
|
4
|
+
import { compose } from "@zudojs/middleware";
|
|
5
|
+
import { MiddlewareExecutionError } from "../cqrsErrors/cqrsError.base.js";
|
|
6
|
+
/** Adapts one CQRS middleware to the shared `(context, next)` shape. */
|
|
7
|
+
function adapt(middleware) {
|
|
8
|
+
return async (state, next) => {
|
|
9
|
+
let called = false;
|
|
10
|
+
return middleware(state.request, state.context, (request, context) => {
|
|
11
|
+
if (called) {
|
|
12
|
+
return Promise.reject(new MiddlewareExecutionError());
|
|
13
|
+
}
|
|
14
|
+
called = true;
|
|
15
|
+
state.request = request;
|
|
16
|
+
state.context = context;
|
|
17
|
+
return next();
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Combines multiple middleware functions into a single middleware.
|
|
23
|
+
*
|
|
24
|
+
* Each middleware may call `next()` at most once per execution; a second
|
|
25
|
+
* call throws `MiddlewareExecutionError`. The command and query buses
|
|
26
|
+
* build their pipelines with this function, so the same rule applies
|
|
27
|
+
* there.
|
|
28
|
+
*/
|
|
29
|
+
export function composeMiddleware(middleware) {
|
|
30
|
+
const run = compose(middleware.map(adapt), async (state) => state.terminal(state.request, state.context), { maxDepth: Number.POSITIVE_INFINITY });
|
|
31
|
+
return async (request, context, terminal) => run({ request, context, terminal });
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=cqrsMiddleware.compose.js.map
|
|
@@ -45,12 +45,19 @@ export interface CqrsTiming {
|
|
|
45
45
|
export interface TimingMiddlewareOptions extends MiddlewareOptions {
|
|
46
46
|
/**
|
|
47
47
|
* Receives the measurement after each execution (success or failure).
|
|
48
|
-
*
|
|
48
|
+
* The callback is an observer: if it throws or rejects, the error is
|
|
49
|
+
* passed to `onTimingError` (when given) and otherwise discarded. It
|
|
50
|
+
* never changes the outcome of the command or query.
|
|
49
51
|
*
|
|
50
52
|
* Optional: measurements are always exposed through
|
|
51
53
|
* `TimingMiddleware.lastTiming` as well.
|
|
52
54
|
*/
|
|
53
55
|
readonly onTiming?: (timing: CqrsTiming) => void | Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Receives any error thrown by `onTiming`. Errors thrown here are
|
|
58
|
+
* discarded as well.
|
|
59
|
+
*/
|
|
60
|
+
readonly onTimingError?: (error: unknown, timing: CqrsTiming) => void;
|
|
54
61
|
/**
|
|
55
62
|
* Monotonic clock in milliseconds. Defaults to `performance.now()`.
|
|
56
63
|
*/
|
|
@@ -134,15 +141,6 @@ export declare function commandMiddleware(middleware: CommandMiddleware): CqrsMi
|
|
|
134
141
|
* Adapts query-specific middleware to generic CQRS middleware.
|
|
135
142
|
*/
|
|
136
143
|
export declare function queryMiddleware(middleware: QueryMiddleware): CqrsMiddleware;
|
|
137
|
-
/**
|
|
138
|
-
* Combines multiple middleware functions into a single middleware.
|
|
139
|
-
*
|
|
140
|
-
* Each middleware may call `next()` at most once per execution; a second
|
|
141
|
-
* call throws `MiddlewareExecutionError`. The command and query buses
|
|
142
|
-
* build their pipelines with this function, so the same rule applies
|
|
143
|
-
* there.
|
|
144
|
-
*/
|
|
145
|
-
export declare function composeMiddleware(middleware: readonly CqrsMiddleware[]): CqrsMiddleware;
|
|
146
144
|
/**
|
|
147
145
|
* Creates a middleware that runs a callback before execution.
|
|
148
146
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseError } from "@zudojs/errors";
|
|
2
|
-
import { CqrsError, CqrsValidationError, InvalidMiddlewareError,
|
|
2
|
+
import { CqrsError, CqrsValidationError, InvalidMiddlewareError, } from "../cqrsErrors/cqrsError.base.js";
|
|
3
3
|
/**
|
|
4
4
|
* Middleware that measures command or query execution time.
|
|
5
5
|
*
|
|
@@ -10,6 +10,10 @@ export function timingMiddleware(options = {}) {
|
|
|
10
10
|
if (options.onTiming !== undefined && typeof options.onTiming !== "function") {
|
|
11
11
|
throw new InvalidMiddlewareError("timingMiddleware onTiming must be a function.");
|
|
12
12
|
}
|
|
13
|
+
if (options.onTimingError !== undefined &&
|
|
14
|
+
typeof options.onTimingError !== "function") {
|
|
15
|
+
throw new InvalidMiddlewareError("timingMiddleware onTimingError must be a function.");
|
|
16
|
+
}
|
|
13
17
|
if (options.now !== undefined && typeof options.now !== "function") {
|
|
14
18
|
throw new InvalidMiddlewareError("timingMiddleware now must be a function.");
|
|
15
19
|
}
|
|
@@ -21,9 +25,20 @@ export function timingMiddleware(options = {}) {
|
|
|
21
25
|
const record = async (timing) => {
|
|
22
26
|
lastTiming = timing;
|
|
23
27
|
count += 1;
|
|
24
|
-
if (onTiming) {
|
|
28
|
+
if (!onTiming) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
25
32
|
await onTiming(timing);
|
|
26
33
|
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
try {
|
|
36
|
+
options.onTimingError?.(error, timing);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
27
42
|
};
|
|
28
43
|
const middleware = async (request, context, next) => {
|
|
29
44
|
if (options.enabled === false) {
|
|
@@ -201,32 +216,6 @@ export function queryMiddleware(middleware) {
|
|
|
201
216
|
return middleware(request, context, async (query, nextContext) => next(query, nextContext));
|
|
202
217
|
};
|
|
203
218
|
}
|
|
204
|
-
/**
|
|
205
|
-
* Combines multiple middleware functions into a single middleware.
|
|
206
|
-
*
|
|
207
|
-
* Each middleware may call `next()` at most once per execution; a second
|
|
208
|
-
* call throws `MiddlewareExecutionError`. The command and query buses
|
|
209
|
-
* build their pipelines with this function, so the same rule applies
|
|
210
|
-
* there.
|
|
211
|
-
*/
|
|
212
|
-
export function composeMiddleware(middleware) {
|
|
213
|
-
const stack = [...middleware];
|
|
214
|
-
return async (request, context, terminal) => {
|
|
215
|
-
let index = -1;
|
|
216
|
-
const dispatch = async (currentIndex, currentRequest, currentContext) => {
|
|
217
|
-
if (currentIndex <= index) {
|
|
218
|
-
throw new MiddlewareExecutionError();
|
|
219
|
-
}
|
|
220
|
-
index = currentIndex;
|
|
221
|
-
const current = stack[currentIndex];
|
|
222
|
-
if (!current) {
|
|
223
|
-
return terminal(currentRequest, currentContext);
|
|
224
|
-
}
|
|
225
|
-
return current(currentRequest, currentContext, (nextRequest, nextContext) => dispatch(currentIndex + 1, nextRequest, nextContext));
|
|
226
|
-
};
|
|
227
|
-
return dispatch(0, request, context);
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
219
|
/**
|
|
231
220
|
* Creates a middleware that runs a callback before execution.
|
|
232
221
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { executeQueryHandler } from "../query/queryHandler.core.js";
|
|
2
2
|
import { QueryHandlerNotFoundError, DuplicateHandlerError, InvalidQueryError, } from "../cqrsErrors/cqrsError.base.js";
|
|
3
|
-
import { composeMiddleware } from "../cqrsMiddleware/cqrsMiddleware.
|
|
3
|
+
import { composeMiddleware } from "../cqrsMiddleware/cqrsMiddleware.compose.js";
|
|
4
4
|
import { assertExecutableHandler, assertHandlerType, assertMiddleware, } from "../cqrsValidation/cqrsValidation.core.js";
|
|
5
5
|
/**
|
|
6
6
|
* Query bus implementation.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zudojs/cqrs",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Command Query Responsibility Segregation (CQRS) primitives for separating read and write operations.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -25,8 +25,9 @@
|
|
|
25
25
|
"!dist/.tsbuildinfo"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@zudojs/errors": "1.0
|
|
29
|
-
"@zudojs/events": "1.0
|
|
28
|
+
"@zudojs/errors": "1.1.0",
|
|
29
|
+
"@zudojs/events": "1.1.0",
|
|
30
|
+
"@zudojs/middleware": "1.0.2"
|
|
30
31
|
},
|
|
31
32
|
"devDependencies": {
|
|
32
33
|
"typescript": "7.0.2",
|