@heavybit/shared-lib 0.0.0-stage → 1.2.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/dist/cjs/access/index.js +118 -0
- package/dist/cjs/access/index.js.map +1 -0
- package/dist/cjs/alerting/index.js +56 -0
- package/dist/cjs/alerting/index.js.map +1 -0
- package/dist/cjs/cache/index.js +167 -0
- package/dist/cjs/cache/index.js.map +1 -0
- package/dist/cjs/common/async.js +40 -0
- package/dist/cjs/common/async.js.map +1 -0
- package/dist/cjs/common/audit.js +21 -0
- package/dist/cjs/common/audit.js.map +1 -0
- package/dist/cjs/common/crypto.js +75 -0
- package/dist/cjs/common/crypto.js.map +1 -0
- package/dist/cjs/common/errors.js +90 -0
- package/dist/cjs/common/errors.js.map +1 -0
- package/dist/cjs/common/id.js +40 -0
- package/dist/cjs/common/id.js.map +1 -0
- package/dist/cjs/common/index.js +28 -0
- package/dist/cjs/common/index.js.map +1 -0
- package/dist/cjs/common/jwt.js +59 -0
- package/dist/cjs/common/jwt.js.map +1 -0
- package/dist/cjs/common/password.js +38 -0
- package/dist/cjs/common/password.js.map +1 -0
- package/dist/cjs/common/phone.js +42 -0
- package/dist/cjs/common/phone.js.map +1 -0
- package/dist/cjs/common/response.js +32 -0
- package/dist/cjs/common/response.js.map +1 -0
- package/dist/cjs/common/text.js +46 -0
- package/dist/cjs/common/text.js.map +1 -0
- package/dist/cjs/common/time.js +109 -0
- package/dist/cjs/common/time.js.map +1 -0
- package/dist/cjs/config/index.js +96 -0
- package/dist/cjs/config/index.js.map +1 -0
- package/dist/cjs/contracts/editorial-draft.js +111 -0
- package/dist/cjs/contracts/editorial-draft.js.map +1 -0
- package/dist/cjs/contracts/index.js +20 -0
- package/dist/cjs/contracts/index.js.map +1 -0
- package/dist/cjs/contracts/notification-dispatch.js +92 -0
- package/dist/cjs/contracts/notification-dispatch.js.map +1 -0
- package/dist/cjs/contracts/upload-import.js +54 -0
- package/dist/cjs/contracts/upload-import.js.map +1 -0
- package/dist/cjs/database/index.js +57 -0
- package/dist/cjs/database/index.js.map +1 -0
- package/dist/cjs/express/auth.js +72 -0
- package/dist/cjs/express/auth.js.map +1 -0
- package/dist/cjs/express/health.js +31 -0
- package/dist/cjs/express/health.js.map +1 -0
- package/dist/cjs/express/index.js +21 -0
- package/dist/cjs/express/index.js.map +1 -0
- package/dist/cjs/express/middleware.js +95 -0
- package/dist/cjs/express/middleware.js.map +1 -0
- package/dist/cjs/express/security.js +18 -0
- package/dist/cjs/express/security.js.map +1 -0
- package/dist/cjs/http/index.js +90 -0
- package/dist/cjs/http/index.js.map +1 -0
- package/dist/cjs/http/safe-fetch.js +199 -0
- package/dist/cjs/http/safe-fetch.js.map +1 -0
- package/dist/cjs/identity/index.js +117 -0
- package/dist/cjs/identity/index.js.map +1 -0
- package/dist/cjs/index.js +29 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/knex/index.js +76 -0
- package/dist/cjs/knex/index.js.map +1 -0
- package/dist/cjs/logging/index.js +75 -0
- package/dist/cjs/logging/index.js.map +1 -0
- package/dist/cjs/messaging/connection.js +115 -0
- package/dist/cjs/messaging/connection.js.map +1 -0
- package/dist/cjs/messaging/consumer.js +134 -0
- package/dist/cjs/messaging/consumer.js.map +1 -0
- package/dist/cjs/messaging/envelope.js +23 -0
- package/dist/cjs/messaging/envelope.js.map +1 -0
- package/dist/cjs/messaging/idempotency.js +57 -0
- package/dist/cjs/messaging/idempotency.js.map +1 -0
- package/dist/cjs/messaging/index.js +62 -0
- package/dist/cjs/messaging/index.js.map +1 -0
- package/dist/cjs/messaging/monitor.js +48 -0
- package/dist/cjs/messaging/monitor.js.map +1 -0
- package/dist/cjs/messaging/outbox.js +135 -0
- package/dist/cjs/messaging/outbox.js.map +1 -0
- package/dist/cjs/messaging/publisher.js +149 -0
- package/dist/cjs/messaging/publisher.js.map +1 -0
- package/dist/cjs/messaging/rpc.js +125 -0
- package/dist/cjs/messaging/rpc.js.map +1 -0
- package/dist/cjs/messaging/staff-notifier.js +78 -0
- package/dist/cjs/messaging/staff-notifier.js.map +1 -0
- package/dist/cjs/messaging/topology.js +22 -0
- package/dist/cjs/messaging/topology.js.map +1 -0
- package/dist/cjs/metrics/index.js +15 -0
- package/dist/cjs/metrics/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/rate-limit/index.js +61 -0
- package/dist/cjs/rate-limit/index.js.map +1 -0
- package/dist/cjs/scheduling/index.js +88 -0
- package/dist/cjs/scheduling/index.js.map +1 -0
- package/dist/cjs/security/index.js +39 -0
- package/dist/cjs/security/index.js.map +1 -0
- package/dist/cjs/socket/index.js +53 -0
- package/dist/cjs/socket/index.js.map +1 -0
- package/dist/cjs/testing/index.js +73 -0
- package/dist/cjs/testing/index.js.map +1 -0
- package/dist/cjs/types/index.js +3 -0
- package/dist/cjs/types/index.js.map +1 -0
- package/dist/cjs/validation/index.js +41 -0
- package/dist/cjs/validation/index.js.map +1 -0
- package/dist/esm/access/index.js +104 -0
- package/dist/esm/access/index.js.map +1 -0
- package/dist/esm/alerting/index.js +52 -0
- package/dist/esm/alerting/index.js.map +1 -0
- package/dist/esm/cache/index.js +163 -0
- package/dist/esm/cache/index.js.map +1 -0
- package/dist/esm/common/async.js +33 -0
- package/dist/esm/common/async.js.map +1 -0
- package/dist/esm/common/audit.js +18 -0
- package/dist/esm/common/audit.js.map +1 -0
- package/dist/esm/common/crypto.js +64 -0
- package/dist/esm/common/crypto.js.map +1 -0
- package/dist/esm/common/errors.js +75 -0
- package/dist/esm/common/errors.js.map +1 -0
- package/dist/esm/common/id.js +33 -0
- package/dist/esm/common/id.js.map +1 -0
- package/dist/esm/common/index.js +12 -0
- package/dist/esm/common/index.js.map +1 -0
- package/dist/esm/common/jwt.js +49 -0
- package/dist/esm/common/jwt.js.map +1 -0
- package/dist/esm/common/password.js +30 -0
- package/dist/esm/common/password.js.map +1 -0
- package/dist/esm/common/phone.js +33 -0
- package/dist/esm/common/phone.js.map +1 -0
- package/dist/esm/common/response.js +24 -0
- package/dist/esm/common/response.js.map +1 -0
- package/dist/esm/common/text.js +39 -0
- package/dist/esm/common/text.js.map +1 -0
- package/dist/esm/common/time.js +96 -0
- package/dist/esm/common/time.js.map +1 -0
- package/dist/esm/config/index.js +88 -0
- package/dist/esm/config/index.js.map +1 -0
- package/dist/esm/contracts/editorial-draft.js +108 -0
- package/dist/esm/contracts/editorial-draft.js.map +1 -0
- package/dist/esm/contracts/index.js +4 -0
- package/dist/esm/contracts/index.js.map +1 -0
- package/dist/esm/contracts/notification-dispatch.js +89 -0
- package/dist/esm/contracts/notification-dispatch.js.map +1 -0
- package/dist/esm/contracts/upload-import.js +51 -0
- package/dist/esm/contracts/upload-import.js.map +1 -0
- package/dist/esm/database/index.js +48 -0
- package/dist/esm/database/index.js.map +1 -0
- package/dist/esm/express/auth.js +63 -0
- package/dist/esm/express/auth.js.map +1 -0
- package/dist/esm/express/health.js +27 -0
- package/dist/esm/express/health.js.map +1 -0
- package/dist/esm/express/index.js +5 -0
- package/dist/esm/express/index.js.map +1 -0
- package/dist/esm/express/middleware.js +84 -0
- package/dist/esm/express/middleware.js.map +1 -0
- package/dist/esm/express/security.js +11 -0
- package/dist/esm/express/security.js.map +1 -0
- package/dist/esm/http/index.js +73 -0
- package/dist/esm/http/index.js.map +1 -0
- package/dist/esm/http/safe-fetch.js +193 -0
- package/dist/esm/http/safe-fetch.js.map +1 -0
- package/dist/esm/identity/index.js +111 -0
- package/dist/esm/identity/index.js.map +1 -0
- package/dist/esm/index.js +13 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/knex/index.js +66 -0
- package/dist/esm/knex/index.js.map +1 -0
- package/dist/esm/logging/index.js +67 -0
- package/dist/esm/logging/index.js.map +1 -0
- package/dist/esm/messaging/connection.js +108 -0
- package/dist/esm/messaging/connection.js.map +1 -0
- package/dist/esm/messaging/consumer.js +130 -0
- package/dist/esm/messaging/consumer.js.map +1 -0
- package/dist/esm/messaging/envelope.js +19 -0
- package/dist/esm/messaging/envelope.js.map +1 -0
- package/dist/esm/messaging/idempotency.js +52 -0
- package/dist/esm/messaging/idempotency.js.map +1 -0
- package/dist/esm/messaging/index.js +45 -0
- package/dist/esm/messaging/index.js.map +1 -0
- package/dist/esm/messaging/monitor.js +44 -0
- package/dist/esm/messaging/monitor.js.map +1 -0
- package/dist/esm/messaging/outbox.js +129 -0
- package/dist/esm/messaging/outbox.js.map +1 -0
- package/dist/esm/messaging/publisher.js +142 -0
- package/dist/esm/messaging/publisher.js.map +1 -0
- package/dist/esm/messaging/rpc.js +120 -0
- package/dist/esm/messaging/rpc.js.map +1 -0
- package/dist/esm/messaging/staff-notifier.js +73 -0
- package/dist/esm/messaging/staff-notifier.js.map +1 -0
- package/dist/esm/messaging/topology.js +16 -0
- package/dist/esm/messaging/topology.js.map +1 -0
- package/dist/esm/metrics/index.js +6 -0
- package/dist/esm/metrics/index.js.map +1 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/rate-limit/index.js +56 -0
- package/dist/esm/rate-limit/index.js.map +1 -0
- package/dist/esm/scheduling/index.js +83 -0
- package/dist/esm/scheduling/index.js.map +1 -0
- package/dist/esm/security/index.js +36 -0
- package/dist/esm/security/index.js.map +1 -0
- package/dist/esm/socket/index.js +47 -0
- package/dist/esm/socket/index.js.map +1 -0
- package/dist/esm/testing/index.js +66 -0
- package/dist/esm/testing/index.js.map +1 -0
- package/dist/esm/types/index.js +2 -0
- package/dist/esm/types/index.js.map +1 -0
- package/dist/esm/validation/index.js +36 -0
- package/dist/esm/validation/index.js.map +1 -0
- package/dist/types/access/index.d.ts +50 -0
- package/dist/types/access/index.d.ts.map +1 -0
- package/dist/types/alerting/index.d.ts +43 -0
- package/dist/types/alerting/index.d.ts.map +1 -0
- package/dist/types/cache/index.d.ts +45 -0
- package/dist/types/cache/index.d.ts.map +1 -0
- package/dist/types/common/async.d.ts +14 -0
- package/dist/types/common/async.d.ts.map +1 -0
- package/dist/types/common/audit.d.ts +17 -0
- package/dist/types/common/audit.d.ts.map +1 -0
- package/dist/types/common/crypto.d.ts +18 -0
- package/dist/types/common/crypto.d.ts.map +1 -0
- package/dist/types/common/errors.d.ts +71 -0
- package/dist/types/common/errors.d.ts.map +1 -0
- package/dist/types/common/id.d.ts +14 -0
- package/dist/types/common/id.d.ts.map +1 -0
- package/dist/types/common/index.d.ts +12 -0
- package/dist/types/common/index.d.ts.map +1 -0
- package/dist/types/common/jwt.d.ts +28 -0
- package/dist/types/common/jwt.d.ts.map +1 -0
- package/dist/types/common/password.d.ts +4 -0
- package/dist/types/common/password.d.ts.map +1 -0
- package/dist/types/common/phone.d.ts +10 -0
- package/dist/types/common/phone.d.ts.map +1 -0
- package/dist/types/common/response.d.ts +12 -0
- package/dist/types/common/response.d.ts.map +1 -0
- package/dist/types/common/text.d.ts +10 -0
- package/dist/types/common/text.d.ts.map +1 -0
- package/dist/types/common/time.d.ts +40 -0
- package/dist/types/common/time.d.ts.map +1 -0
- package/dist/types/config/index.d.ts +115 -0
- package/dist/types/config/index.d.ts.map +1 -0
- package/dist/types/contracts/editorial-draft.d.ts +577 -0
- package/dist/types/contracts/editorial-draft.d.ts.map +1 -0
- package/dist/types/contracts/index.d.ts +4 -0
- package/dist/types/contracts/index.d.ts.map +1 -0
- package/dist/types/contracts/notification-dispatch.d.ts +175 -0
- package/dist/types/contracts/notification-dispatch.d.ts.map +1 -0
- package/dist/types/contracts/upload-import.d.ts +96 -0
- package/dist/types/contracts/upload-import.d.ts.map +1 -0
- package/dist/types/database/index.d.ts +25 -0
- package/dist/types/database/index.d.ts.map +1 -0
- package/dist/types/express/auth.d.ts +27 -0
- package/dist/types/express/auth.d.ts.map +1 -0
- package/dist/types/express/health.d.ts +14 -0
- package/dist/types/express/health.d.ts.map +1 -0
- package/dist/types/express/index.d.ts +5 -0
- package/dist/types/express/index.d.ts.map +1 -0
- package/dist/types/express/middleware.d.ts +20 -0
- package/dist/types/express/middleware.d.ts.map +1 -0
- package/dist/types/express/security.d.ts +9 -0
- package/dist/types/express/security.d.ts.map +1 -0
- package/dist/types/http/index.d.ts +34 -0
- package/dist/types/http/index.d.ts.map +1 -0
- package/dist/types/http/safe-fetch.d.ts +58 -0
- package/dist/types/http/safe-fetch.d.ts.map +1 -0
- package/dist/types/identity/index.d.ts +51 -0
- package/dist/types/identity/index.d.ts.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/knex/index.d.ts +25 -0
- package/dist/types/knex/index.d.ts.map +1 -0
- package/dist/types/logging/index.d.ts +22 -0
- package/dist/types/logging/index.d.ts.map +1 -0
- package/dist/types/messaging/connection.d.ts +48 -0
- package/dist/types/messaging/connection.d.ts.map +1 -0
- package/dist/types/messaging/consumer.d.ts +56 -0
- package/dist/types/messaging/consumer.d.ts.map +1 -0
- package/dist/types/messaging/envelope.d.ts +16 -0
- package/dist/types/messaging/envelope.d.ts.map +1 -0
- package/dist/types/messaging/idempotency.d.ts +29 -0
- package/dist/types/messaging/idempotency.d.ts.map +1 -0
- package/dist/types/messaging/index.d.ts +39 -0
- package/dist/types/messaging/index.d.ts.map +1 -0
- package/dist/types/messaging/monitor.d.ts +23 -0
- package/dist/types/messaging/monitor.d.ts.map +1 -0
- package/dist/types/messaging/outbox.d.ts +51 -0
- package/dist/types/messaging/outbox.d.ts.map +1 -0
- package/dist/types/messaging/publisher.d.ts +84 -0
- package/dist/types/messaging/publisher.d.ts.map +1 -0
- package/dist/types/messaging/rpc.d.ts +46 -0
- package/dist/types/messaging/rpc.d.ts.map +1 -0
- package/dist/types/messaging/staff-notifier.d.ts +56 -0
- package/dist/types/messaging/staff-notifier.d.ts.map +1 -0
- package/dist/types/messaging/topology.d.ts +23 -0
- package/dist/types/messaging/topology.d.ts.map +1 -0
- package/dist/types/metrics/index.d.ts +7 -0
- package/dist/types/metrics/index.d.ts.map +1 -0
- package/dist/types/rate-limit/index.d.ts +28 -0
- package/dist/types/rate-limit/index.d.ts.map +1 -0
- package/dist/types/scheduling/index.d.ts +33 -0
- package/dist/types/scheduling/index.d.ts.map +1 -0
- package/dist/types/security/index.d.ts +20 -0
- package/dist/types/security/index.d.ts.map +1 -0
- package/dist/types/socket/index.d.ts +30 -0
- package/dist/types/socket/index.d.ts.map +1 -0
- package/dist/types/testing/index.d.ts +41 -0
- package/dist/types/testing/index.d.ts.map +1 -0
- package/dist/types/types/index.d.ts +65 -0
- package/dist/types/types/index.d.ts.map +1 -0
- package/dist/types/validation/index.d.ts +34 -0
- package/dist/types/validation/index.d.ts.map +1 -0
- package/package.json +181 -4
- package/README.md +0 -3
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import winston from 'winston';
|
|
2
|
+
import type { Logger } from '../types/index.js';
|
|
3
|
+
/** Keys whose values are replaced with `[REDACTED]` anywhere in log metadata. */
|
|
4
|
+
export declare const DEFAULT_REDACT_KEYS: string[];
|
|
5
|
+
export interface LoggerOptions {
|
|
6
|
+
level?: string;
|
|
7
|
+
/** `json` for machines (default in production), `pretty` for humans. */
|
|
8
|
+
format?: 'json' | 'pretty';
|
|
9
|
+
redactKeys?: string[];
|
|
10
|
+
}
|
|
11
|
+
/** Structured logger with secret redaction. JSON to stdout in production, pretty otherwise. */
|
|
12
|
+
export declare function createLogger(service: string, options?: LoggerOptions): winston.Logger & Logger;
|
|
13
|
+
/** Logger that discards everything — handy default for libraries and tests. */
|
|
14
|
+
export declare const silentLogger: Logger;
|
|
15
|
+
/**
|
|
16
|
+
* A logger that adds `context` (for example pipeline IDs) to every line it writes; a line's own
|
|
17
|
+
* meta wins on a key clash. `undefined` context values are dropped so lines never carry empty keys.
|
|
18
|
+
* Works over any {@link Logger} (winston, a test spy, or another child), so handlers can create one
|
|
19
|
+
* per message and pass it down.
|
|
20
|
+
*/
|
|
21
|
+
export declare function childLogger(logger: Logger, context: Record<string, unknown>): Logger;
|
|
22
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/logging/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAIhD,iFAAiF;AACjF,eAAO,MAAM,mBAAmB,UAG/B,CAAC;AA6BF,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,+FAA+F;AAC/F,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,MAAM,GAAG,MAAM,CAiBlG;AAED,+EAA+E;AAC/E,eAAO,MAAM,YAAY,EAAE,MAA6E,CAAC;AAEzG;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAKpF"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Channel, ConfirmChannel } from 'amqplib';
|
|
2
|
+
import type { Logger } from '../types/index.js';
|
|
3
|
+
export interface ChannelHandle<C extends Channel = Channel> {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
/** The live channel, or null while disconnected. */
|
|
6
|
+
current(): C | null;
|
|
7
|
+
}
|
|
8
|
+
export interface AmqpConnectionOptions {
|
|
9
|
+
url: string;
|
|
10
|
+
/** Shown in the RabbitMQ management UI (`connection_name`). */
|
|
11
|
+
connectionName: string;
|
|
12
|
+
logger?: Logger;
|
|
13
|
+
heartbeatSec?: number;
|
|
14
|
+
/** Delay before reconnect attempt n (1-based). Default: 1s, 2s, 4s … capped at 30s. */
|
|
15
|
+
reconnectDelayMs?: (attempt: number) => number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* One AMQP connection per process with automatic reconnect. Components register channels
|
|
19
|
+
* with a `setup` callback (assert topology, start consuming); setups re-run after every
|
|
20
|
+
* reconnect so consumers and publishers heal without restarts.
|
|
21
|
+
*/
|
|
22
|
+
export declare class AmqpConnection {
|
|
23
|
+
private readonly options;
|
|
24
|
+
private model;
|
|
25
|
+
private readonly registrations;
|
|
26
|
+
private closing;
|
|
27
|
+
private connecting;
|
|
28
|
+
private readonly logger;
|
|
29
|
+
constructor(options: AmqpConnectionOptions);
|
|
30
|
+
get isConnected(): boolean;
|
|
31
|
+
/** Registers a channel. If already connected it is opened immediately. */
|
|
32
|
+
registerChannel<C extends Channel = Channel>(name: string, setup: (channel: C) => Promise<void>, options?: {
|
|
33
|
+
confirm?: boolean;
|
|
34
|
+
}): Promise<ChannelHandle<C>>;
|
|
35
|
+
/**
|
|
36
|
+
* Connects, retrying with back-off until it succeeds or `close()` is called. Resolves once the
|
|
37
|
+
* first connection is up; later drops reconnect in the background.
|
|
38
|
+
*/
|
|
39
|
+
connect(): Promise<void>;
|
|
40
|
+
/** Throws when the broker is not reachable — used by readiness checks. */
|
|
41
|
+
assertReady(): void;
|
|
42
|
+
close(): Promise<void>;
|
|
43
|
+
private connectWithRetry;
|
|
44
|
+
private establish;
|
|
45
|
+
private openChannel;
|
|
46
|
+
}
|
|
47
|
+
export type { Channel, ConfirmChannel };
|
|
48
|
+
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../../src/messaging/connection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAGvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAIhD,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO;IACxD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,OAAO,IAAI,CAAC,GAAG,IAAI,CAAC;CACrB;AASD,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,+DAA+D;IAC/D,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uFAAuF;IACvF,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;CAChD;AAED;;;;GAIG;AACH,qBAAa,cAAc;IAOb,OAAO,CAAC,QAAQ,CAAC,OAAO;IANpC,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;IAC3D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEH,OAAO,EAAE,qBAAqB;IAI3D,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,0EAA0E;IACpE,eAAe,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,GAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAOtK;;;OAGG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAOxB,0EAA0E;IAC1E,WAAW,IAAI,IAAI;IAIb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAUd,gBAAgB;YAahB,SAAS;YAoBT,WAAW;CAe1B;AAED,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { ConsumeMessage } from 'amqplib';
|
|
2
|
+
import type { ZodTypeAny } from 'zod';
|
|
3
|
+
import type { EventEnvelope, Logger } from '../types/index.js';
|
|
4
|
+
import type { AmqpConnection } from './connection.js';
|
|
5
|
+
import type { IdempotencyStore } from './idempotency.js';
|
|
6
|
+
import { type Topology } from './topology.js';
|
|
7
|
+
export interface Binding {
|
|
8
|
+
/** `events` (topic, pattern keys like `auth.user.*`) or `commands` (exact command name). */
|
|
9
|
+
exchange: 'events' | 'commands';
|
|
10
|
+
routingKey: string;
|
|
11
|
+
}
|
|
12
|
+
export interface InboundMessage<T = unknown> {
|
|
13
|
+
/** Envelope `data`, validated against `schema` when one is given. */
|
|
14
|
+
data: T;
|
|
15
|
+
envelope: EventEnvelope | null;
|
|
16
|
+
routingKey: string;
|
|
17
|
+
/** How many times this message has been retried before (0 on first delivery). */
|
|
18
|
+
attempt: number;
|
|
19
|
+
raw: ConsumeMessage;
|
|
20
|
+
}
|
|
21
|
+
export interface SubscriptionOptions<T = unknown> {
|
|
22
|
+
/** Logical queue name; the environment prefix is added. Convention: `<service>:<source>.<topic>`. */
|
|
23
|
+
queue: string;
|
|
24
|
+
bindings: Binding[];
|
|
25
|
+
handler: (message: InboundMessage<T>) => Promise<void>;
|
|
26
|
+
/** Validates `data`; failures dead-letter immediately (a bad payload never succeeds on retry). */
|
|
27
|
+
schema?: ZodTypeAny;
|
|
28
|
+
prefetch?: number;
|
|
29
|
+
/** Delay before each retry. Length = max retries. Default: 1s, 5s, 25s. */
|
|
30
|
+
retryDelaysMs?: number[];
|
|
31
|
+
/** Enables duplicate suppression keyed on `<queue>:<eventId>`. */
|
|
32
|
+
idempotency?: IdempotencyStore;
|
|
33
|
+
idempotencyTtlSec?: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Durable subscriptions with bounded retries and dead-lettering:
|
|
37
|
+
*
|
|
38
|
+
* <queue> ──fail──▶ <queue>.retry.<delay> (TTL, dead-letters back into <queue>)
|
|
39
|
+
* ──fail after last retry / non-retryable──▶ <queue>.dlq
|
|
40
|
+
*
|
|
41
|
+
* One fixed TTL per retry queue avoids head-of-line blocking of per-message TTLs.
|
|
42
|
+
*/
|
|
43
|
+
export declare class MessageConsumer {
|
|
44
|
+
private readonly connection;
|
|
45
|
+
private readonly topology;
|
|
46
|
+
private readonly handles;
|
|
47
|
+
private readonly logger;
|
|
48
|
+
constructor(connection: AmqpConnection, topology: Topology, logger?: Logger);
|
|
49
|
+
/** Physical DLQ names for every subscription (for monitoring). */
|
|
50
|
+
readonly deadLetterQueues: string[];
|
|
51
|
+
subscribe<T = unknown>(options: SubscriptionOptions<T>): Promise<void>;
|
|
52
|
+
private dispatch;
|
|
53
|
+
private retryOrDeadLetter;
|
|
54
|
+
private deadLetter;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=consumer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consumer.d.ts","sourceRoot":"","sources":["../../../src/messaging/consumer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,cAAc,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC;AAGtC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,iBAAiB,CAAC;AAErE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAuC,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEnF,MAAM,WAAW,OAAO;IACtB,4FAA4F;IAC5F,QAAQ,EAAE,QAAQ,GAAG,UAAU,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,OAAO;IACzC,qEAAqE;IACrE,IAAI,EAAE,CAAC,CAAC;IACR,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,cAAc,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB,CAAC,CAAC,GAAG,OAAO;IAC9C,qGAAqG;IACrG,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,kGAAkG;IAClG,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,kEAAkE;IAClE,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAKD;;;;;;;GAOG;AACH,qBAAa,eAAe;IAId,OAAO,CAAC,QAAQ,CAAC,UAAU;IAAkB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAHlF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEH,UAAU,EAAE,cAAc,EAAmB,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,MAAM;IAI7G,kEAAkE;IAClE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAM;IAEnC,SAAS,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;YAiC9D,QAAQ;IAgDtB,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,UAAU;CASnB"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { EventEnvelope } from '../types/index.js';
|
|
2
|
+
export interface EnvelopeMeta {
|
|
3
|
+
actorId?: string | null;
|
|
4
|
+
correlationId?: string;
|
|
5
|
+
aggregate?: {
|
|
6
|
+
type: string;
|
|
7
|
+
id: string;
|
|
8
|
+
version?: number;
|
|
9
|
+
};
|
|
10
|
+
version?: number;
|
|
11
|
+
/** Deterministic ID for idempotent re-publishing (defaults to a new ULID). */
|
|
12
|
+
eventId?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function buildEnvelope<T>(type: string, data: T, source: string, meta?: EnvelopeMeta): EventEnvelope<T>;
|
|
15
|
+
export declare function isEnvelope(value: unknown): value is EventEnvelope;
|
|
16
|
+
//# sourceMappingURL=envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../../src/messaging/envelope.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,YAAiB,GAAG,aAAa,CAAC,CAAC,CAAC,CAYjH;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAGjE"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Redis } from 'ioredis';
|
|
2
|
+
export type ClaimResult = 'claimed' | 'done' | 'in-flight';
|
|
3
|
+
/**
|
|
4
|
+
* At-least-once delivery means duplicates. Consumers claim `consumer:eventId` before handling:
|
|
5
|
+
* - `claimed` → process it, then `complete()` (or `abandon()` on failure so a retry can run)
|
|
6
|
+
* - `done` → already processed; ack and skip
|
|
7
|
+
* - `in-flight` → another worker holds the lease; retry later
|
|
8
|
+
*/
|
|
9
|
+
export interface IdempotencyStore {
|
|
10
|
+
begin(key: string, leaseSec: number): Promise<ClaimResult>;
|
|
11
|
+
complete(key: string, ttlSec: number): Promise<void>;
|
|
12
|
+
abandon(key: string): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export declare class RedisIdempotencyStore implements IdempotencyStore {
|
|
15
|
+
private readonly redis;
|
|
16
|
+
private readonly prefix;
|
|
17
|
+
constructor(redis: Redis, prefix?: string);
|
|
18
|
+
begin(key: string, leaseSec: number): Promise<ClaimResult>;
|
|
19
|
+
complete(key: string, ttlSec: number): Promise<void>;
|
|
20
|
+
abandon(key: string): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
export declare class MemoryIdempotencyStore implements IdempotencyStore {
|
|
23
|
+
private readonly entries;
|
|
24
|
+
private read;
|
|
25
|
+
begin(key: string, leaseSec: number): Promise<ClaimResult>;
|
|
26
|
+
complete(key: string, ttlSec: number): Promise<void>;
|
|
27
|
+
abandon(key: string): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=idempotency.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../../../src/messaging/idempotency.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,CAAC;AAE3D;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC3D,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAKD,qBAAa,qBAAsB,YAAW,gBAAgB;IAChD,OAAO,CAAC,QAAQ,CAAC,KAAK;IAAS,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAArC,KAAK,EAAE,KAAK,EAAmB,MAAM,SAAU;IAEtE,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAO1D,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpD,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAK1C;AAED,qBAAa,sBAAuB,YAAW,gBAAgB;IAC7D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2D;IAEnF,OAAO,CAAC,IAAI;IASN,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAS1D,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpD,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG1C"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Logger } from '../types/index.js';
|
|
2
|
+
import { AmqpConnection } from './connection.js';
|
|
3
|
+
import { MessageConsumer } from './consumer.js';
|
|
4
|
+
import { QueueMonitor } from './monitor.js';
|
|
5
|
+
import { MessagePublisher } from './publisher.js';
|
|
6
|
+
import { RpcClient, RpcServer } from './rpc.js';
|
|
7
|
+
import { type Topology } from './topology.js';
|
|
8
|
+
export * from './connection.js';
|
|
9
|
+
export * from './topology.js';
|
|
10
|
+
export * from './envelope.js';
|
|
11
|
+
export * from './publisher.js';
|
|
12
|
+
export * from './consumer.js';
|
|
13
|
+
export * from './idempotency.js';
|
|
14
|
+
export * from './rpc.js';
|
|
15
|
+
export * from './outbox.js';
|
|
16
|
+
export * from './monitor.js';
|
|
17
|
+
export * from './staff-notifier.js';
|
|
18
|
+
export interface MessagingOptions {
|
|
19
|
+
url: string;
|
|
20
|
+
app: string;
|
|
21
|
+
prefix: string;
|
|
22
|
+
service: string;
|
|
23
|
+
logger?: Logger;
|
|
24
|
+
}
|
|
25
|
+
export interface Messaging {
|
|
26
|
+
topology: Topology;
|
|
27
|
+
connection: AmqpConnection;
|
|
28
|
+
publisher: MessagePublisher;
|
|
29
|
+
consumer: MessageConsumer;
|
|
30
|
+
rpcServer: RpcServer;
|
|
31
|
+
rpcClient: RpcClient;
|
|
32
|
+
monitor: QueueMonitor;
|
|
33
|
+
/** Registers the publisher, RPC client and monitor, then connects (retrying until up). */
|
|
34
|
+
start(): Promise<void>;
|
|
35
|
+
close(): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
/** Wires one connection with a publisher, consumer, RPC server/client and DLQ monitor. */
|
|
38
|
+
export declare function createMessaging(options: MessagingOptions): Messaging;
|
|
39
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/messaging/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9D,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AAEpC,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,cAAc,CAAC;IAC3B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,QAAQ,EAAE,eAAe,CAAC;IAC1B,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,YAAY,CAAC;IACtB,0FAA0F;IAC1F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,0FAA0F;AAC1F,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CA0BpE"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Logger } from '../types/index.js';
|
|
2
|
+
import type { AmqpConnection } from './connection.js';
|
|
3
|
+
export interface QueueStat {
|
|
4
|
+
queue: string;
|
|
5
|
+
messages: number;
|
|
6
|
+
consumers: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Reports queue depth (typically DLQs) for `/health` details and alerting. Uses its own
|
|
10
|
+
* channel because `checkQueue` on a missing queue closes the channel it runs on.
|
|
11
|
+
*/
|
|
12
|
+
export declare class QueueMonitor {
|
|
13
|
+
private readonly connection;
|
|
14
|
+
private readonly queues;
|
|
15
|
+
private handle;
|
|
16
|
+
private readonly logger;
|
|
17
|
+
private last;
|
|
18
|
+
constructor(connection: AmqpConnection, queues: () => string[], logger?: Logger);
|
|
19
|
+
init(): Promise<void>;
|
|
20
|
+
check(): Promise<QueueStat[]>;
|
|
21
|
+
get lastStats(): QueueStat[];
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=monitor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monitor.d.ts","sourceRoot":"","sources":["../../../src/messaging/monitor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,iBAAiB,CAAC;AAErE,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,qBAAa,YAAY;IAKX,OAAO,CAAC,QAAQ,CAAC,UAAU;IAAkB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJhF,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,IAAI,CAAmB;gBAEF,UAAU,EAAE,cAAc,EAAmB,MAAM,EAAE,MAAM,MAAM,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM;IAI3G,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,KAAK,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAkBnC,IAAI,SAAS,IAAI,SAAS,EAAE,CAE3B;CACF"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
import type { EventEnvelope, Logger } from '../types/index.js';
|
|
3
|
+
import { type CommandBus, type EventBus } from './publisher.js';
|
|
4
|
+
export declare const DEFAULT_OUTBOX_TABLE = "event_outbox";
|
|
5
|
+
export type OutboxKind = 'event' | 'command';
|
|
6
|
+
/** Outbox `routing_key` column value: the event type, or `command:<name>` for commands. */
|
|
7
|
+
export declare function outboxRoutingKey(envelope: Pick<EventEnvelope, 'type'>, kind?: OutboxKind): string;
|
|
8
|
+
/**
|
|
9
|
+
* Transactional outbox: write the domain change and the event in one DB transaction, then a
|
|
10
|
+
* relay publishes pending rows with broker confirms. Closes the "row committed, event lost"
|
|
11
|
+
* gap of publish-after-commit. Consumers must be idempotent (they are: see IdempotencyStore).
|
|
12
|
+
*
|
|
13
|
+
* Table (see `createOutboxTable` in `@heavybit/shared-lib/knex`):
|
|
14
|
+
* id CHAR(26) PK = eventId, routing_key, payload JSON, attempts, last_error, created_at, published_at
|
|
15
|
+
*/
|
|
16
|
+
export declare function enqueueOutbox(db: Knex | Knex.Transaction, envelope: EventEnvelope, options?: {
|
|
17
|
+
kind?: OutboxKind;
|
|
18
|
+
table?: string;
|
|
19
|
+
}): Promise<void>;
|
|
20
|
+
export interface OutboxRelayOptions {
|
|
21
|
+
knex: Knex;
|
|
22
|
+
bus: EventBus & CommandBus;
|
|
23
|
+
table?: string;
|
|
24
|
+
batchSize?: number;
|
|
25
|
+
/** Safety-net poll interval; `trigger()` after commits gives low latency. */
|
|
26
|
+
intervalMs?: number;
|
|
27
|
+
logger?: Logger;
|
|
28
|
+
}
|
|
29
|
+
export declare class OutboxRelay {
|
|
30
|
+
private readonly options;
|
|
31
|
+
private timer;
|
|
32
|
+
private running;
|
|
33
|
+
private rerun;
|
|
34
|
+
private readonly logger;
|
|
35
|
+
private readonly table;
|
|
36
|
+
constructor(options: OutboxRelayOptions);
|
|
37
|
+
start(): void;
|
|
38
|
+
stop(): void;
|
|
39
|
+
/** Publishes pending rows now. Concurrent triggers coalesce into one follow-up run. */
|
|
40
|
+
trigger(): Promise<void>;
|
|
41
|
+
/** One batch. Returns how many rows were published. */
|
|
42
|
+
flush(): Promise<number>;
|
|
43
|
+
/**
|
|
44
|
+
* Logged on the 1st, 2nd, 4th, 8th… failure of a row (the attempt count before this one is in `attempts`),
|
|
45
|
+
* so a queue that stays missing for hours is still visible without a line every tick.
|
|
46
|
+
*/
|
|
47
|
+
private reportUnroutable;
|
|
48
|
+
/** Deletes published rows older than `olderThanDays`. */
|
|
49
|
+
prune(olderThanDays?: number): Promise<number>;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=outbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../../src/messaging/outbox.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAEjC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAqB,KAAK,UAAU,EAAE,KAAK,QAAQ,EAA+B,MAAM,gBAAgB,CAAC;AAEhH,eAAO,MAAM,oBAAoB,iBAAiB,CAAC;AAEnD,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;AAI7C,2FAA2F;AAC3F,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,EAAE,IAAI,GAAE,UAAoB,GAAG,MAAM,CAE1G;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,UAAU,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ5J;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,IAAI,CAAC;IACX,GAAG,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,WAAW;IAOV,OAAO,CAAC,QAAQ,CAAC,OAAO;IANpC,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEF,OAAO,EAAE,kBAAkB;IAKxD,KAAK,IAAI,IAAI;IAMb,IAAI,IAAI,IAAI;IAKZ,uFAAuF;IACjF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB9B,uDAAuD;IACjD,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAwC9B;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAQxB,yDAAyD;IACnD,KAAK,CAAC,aAAa,SAAI,GAAG,OAAO,CAAC,MAAM,CAAC;CAIhD"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ServiceUnavailableError } from '../common/errors.js';
|
|
2
|
+
import type { EventEnvelope, Logger } from '../types/index.js';
|
|
3
|
+
import type { AmqpConnection } from './connection.js';
|
|
4
|
+
import { type EnvelopeMeta } from './envelope.js';
|
|
5
|
+
import type { Topology } from './topology.js';
|
|
6
|
+
/**
|
|
7
|
+
* The broker accepted a `mandatory` message but no queue was bound to receive it (the consumer has never
|
|
8
|
+
* started, or its binding is wrong). Retryable: the message is not lost — the caller (or the outbox relay)
|
|
9
|
+
* keeps it and tries again, and it goes through once the owner declares its queue.
|
|
10
|
+
*/
|
|
11
|
+
export declare class UnroutableMessageError extends ServiceUnavailableError {
|
|
12
|
+
readonly exchange: string;
|
|
13
|
+
readonly routingKey: string;
|
|
14
|
+
constructor(exchange: string, routingKey: string);
|
|
15
|
+
}
|
|
16
|
+
export declare function isUnroutableError(error: unknown): error is UnroutableMessageError;
|
|
17
|
+
/** Publishes domain events (fan-out to any interested consumer). */
|
|
18
|
+
export interface EventBus {
|
|
19
|
+
publish<T>(type: string, data: T, meta?: EnvelopeMeta): Promise<EventEnvelope<T>>;
|
|
20
|
+
publishEnvelope(envelope: EventEnvelope): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
/** Sends a command to the single service that owns it (e.g. `email:send`). */
|
|
23
|
+
export interface CommandBus {
|
|
24
|
+
send<T>(command: string, data: T, meta?: EnvelopeMeta): Promise<EventEnvelope<T>>;
|
|
25
|
+
/** Sends a pre-built command envelope (outbox relay). `envelope.type` is the command name. */
|
|
26
|
+
sendEnvelope(envelope: EventEnvelope): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
export interface QueueSendOptions {
|
|
29
|
+
/** Defaults to a new ULID. */
|
|
30
|
+
messageId?: string;
|
|
31
|
+
correlationId?: string;
|
|
32
|
+
}
|
|
33
|
+
/** Sends a plain JSON payload straight to a named queue, for a consumer outside the shared topology. */
|
|
34
|
+
export interface QueueSender {
|
|
35
|
+
sendToQueue(queue: string, payload: unknown, options?: QueueSendOptions): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
export interface PublisherOptions {
|
|
38
|
+
connection: AmqpConnection;
|
|
39
|
+
topology: Topology;
|
|
40
|
+
/** Service name stamped into every envelope's `source`. */
|
|
41
|
+
source: string;
|
|
42
|
+
logger?: Logger;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Event and command publisher on a confirm channel: `publish()` resolves only once the broker
|
|
46
|
+
* has persisted the message, and rejects with 503 when the broker is unreachable so callers
|
|
47
|
+
* (or the outbox relay) can retry.
|
|
48
|
+
*
|
|
49
|
+
* Commands and direct queue sends are `mandatory`: a command has exactly one owner, so one that no
|
|
50
|
+
* queue receives is a fault, not a no-op. The broker returns it (`basic.return`, always before its
|
|
51
|
+
* confirm), and the send rejects with `UnroutableMessageError` instead of resolving as if delivered.
|
|
52
|
+
* Events stay non-mandatory: a topic event with no subscriber yet is normal fan-out.
|
|
53
|
+
*/
|
|
54
|
+
export declare class MessagePublisher implements EventBus, CommandBus, QueueSender {
|
|
55
|
+
private readonly options;
|
|
56
|
+
private handle;
|
|
57
|
+
private readonly logger;
|
|
58
|
+
/** Message ids the broker returned as unroutable, awaiting their confirm. */
|
|
59
|
+
private readonly returned;
|
|
60
|
+
constructor(options: PublisherOptions);
|
|
61
|
+
init(): Promise<void>;
|
|
62
|
+
publish<T>(type: string, data: T, meta?: EnvelopeMeta): Promise<EventEnvelope<T>>;
|
|
63
|
+
publishEnvelope(envelope: EventEnvelope): Promise<void>;
|
|
64
|
+
send<T>(command: string, data: T, meta?: EnvelopeMeta): Promise<EventEnvelope<T>>;
|
|
65
|
+
sendEnvelope(envelope: EventEnvelope): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Sends `payload` as-is (no envelope) to `queue` through the default exchange. The queue is the
|
|
68
|
+
* consumer's to declare, so it is not asserted here; a missing queue rejects as unroutable.
|
|
69
|
+
*/
|
|
70
|
+
sendToQueue(queue: string, payload: unknown, options?: QueueSendOptions): Promise<void>;
|
|
71
|
+
private write;
|
|
72
|
+
}
|
|
73
|
+
/** Stand-in when messaging is disabled (local tooling, tests): logs and drops. */
|
|
74
|
+
export declare class NoopMessageBus implements EventBus, CommandBus, QueueSender {
|
|
75
|
+
private readonly source;
|
|
76
|
+
private readonly logger;
|
|
77
|
+
constructor(source: string, logger?: Logger);
|
|
78
|
+
publish<T>(type: string, data: T, meta?: EnvelopeMeta): Promise<EventEnvelope<T>>;
|
|
79
|
+
publishEnvelope(envelope: EventEnvelope): Promise<void>;
|
|
80
|
+
send<T>(command: string, data: T, meta?: EnvelopeMeta): Promise<EventEnvelope<T>>;
|
|
81
|
+
sendEnvelope(envelope: EventEnvelope): Promise<void>;
|
|
82
|
+
sendToQueue(queue: string): Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=publisher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publisher.d.ts","sourceRoot":"","sources":["../../../src/messaging/publisher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Logger } from '../types/index.js';
|
|
2
|
+
import type { AmqpConnection } from './connection.js';
|
|
3
|
+
import type { Topology } from './topology.js';
|
|
4
|
+
export interface RpcContext {
|
|
5
|
+
correlationId?: string;
|
|
6
|
+
callerService?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Request/response over RabbitMQ for the few synchronous cross-service calls that must fail
|
|
10
|
+
* cleanly (e.g. "create the profile before committing the account"). Uses direct reply-to, so
|
|
11
|
+
* no reply queue is declared per call, and sets the request TTL to the caller's timeout so a
|
|
12
|
+
* request the caller has given up on is never executed late.
|
|
13
|
+
*/
|
|
14
|
+
export declare class RpcServer {
|
|
15
|
+
private readonly connection;
|
|
16
|
+
private readonly topology;
|
|
17
|
+
private readonly handles;
|
|
18
|
+
private readonly logger;
|
|
19
|
+
constructor(connection: AmqpConnection, topology: Topology, logger?: Logger);
|
|
20
|
+
handle<P, R>(method: string, handler: (payload: P, context: RpcContext) => Promise<R>, options?: {
|
|
21
|
+
prefetch?: number;
|
|
22
|
+
}): Promise<void>;
|
|
23
|
+
private respond;
|
|
24
|
+
}
|
|
25
|
+
export interface RpcCaller {
|
|
26
|
+
call<P, R>(method: string, payload: P, options?: {
|
|
27
|
+
timeoutMs?: number;
|
|
28
|
+
}): Promise<R>;
|
|
29
|
+
}
|
|
30
|
+
export declare class RpcClient implements RpcCaller {
|
|
31
|
+
private readonly connection;
|
|
32
|
+
private readonly topology;
|
|
33
|
+
private readonly callerService;
|
|
34
|
+
private handle;
|
|
35
|
+
private readonly pending;
|
|
36
|
+
private readonly logger;
|
|
37
|
+
constructor(connection: AmqpConnection, topology: Topology, callerService: string, logger?: Logger);
|
|
38
|
+
init(): Promise<void>;
|
|
39
|
+
call<P, R>(method: string, payload: P, options?: {
|
|
40
|
+
timeoutMs?: number;
|
|
41
|
+
correlationId?: string;
|
|
42
|
+
}): Promise<R>;
|
|
43
|
+
private onReply;
|
|
44
|
+
private failAll;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=rpc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc.d.ts","sourceRoot":"","sources":["../../../src/messaging/rpc.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,iBAAiB,CAAC;AACrE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAM9C,MAAM,WAAW,UAAU;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,qBAAa,SAAS;IAIR,OAAO,CAAC,QAAQ,CAAC,UAAU;IAAkB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAHlF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEH,UAAU,EAAE,cAAc,EAAmB,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,MAAM;IAIvG,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;YAUlI,OAAO;CAiBtB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACtF;AAED,qBAAa,SAAU,YAAW,SAAS;IAK7B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAAkB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAAY,OAAO,CAAC,QAAQ,CAAC,aAAa;IAJ5H,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0H;IAClJ,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEH,UAAU,EAAE,cAAc,EAAmB,QAAQ,EAAE,QAAQ,EAAmB,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;IAI/I,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ3B,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAuBhH,OAAO,CAAC,OAAO;IAcf,OAAO,CAAC,OAAO;CAOhB"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type DispatchChannel, type DispatchRecipient, type DispatchValue } from '../contracts/notification-dispatch.js';
|
|
3
|
+
import type { Logger } from '../types/index.js';
|
|
4
|
+
import type { EnvelopeMeta } from './envelope.js';
|
|
5
|
+
/** The slice of a service's outbox the notifier needs; `E` is that service's transaction type. */
|
|
6
|
+
export interface CommandOutbox<E> {
|
|
7
|
+
command<T>(executor: E, command: string, data: T, meta?: EnvelopeMeta): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
/** Who holds a permission, as staff alert recipients. Configured lists today; an auth-admin lookup later. */
|
|
10
|
+
export interface StaffRecipientResolver {
|
|
11
|
+
resolve(permission: string): Promise<DispatchRecipient[]>;
|
|
12
|
+
}
|
|
13
|
+
/** Recipients from configuration (env), keyed by the permission an alert targets. */
|
|
14
|
+
export declare class ConfiguredStaffRecipients implements StaffRecipientResolver {
|
|
15
|
+
private readonly byPermission;
|
|
16
|
+
constructor(byPermission: Readonly<Record<string, readonly DispatchRecipient[]>>);
|
|
17
|
+
resolve(permission: string): Promise<DispatchRecipient[]>;
|
|
18
|
+
}
|
|
19
|
+
export interface StaffAlert {
|
|
20
|
+
/** Seeded notification template slug. */
|
|
21
|
+
template: string;
|
|
22
|
+
/** The permission its recipients hold (e.g. `newsdesk.policy.manage`). */
|
|
23
|
+
permission: string;
|
|
24
|
+
/** Template variables. */
|
|
25
|
+
data: Record<string, DispatchValue>;
|
|
26
|
+
/** Defaults to email: staff addresses have no account, so the in-app feed cannot reach them. */
|
|
27
|
+
channels?: DispatchChannel[];
|
|
28
|
+
}
|
|
29
|
+
/** Tracing for the staged commands; the actor is always the system. */
|
|
30
|
+
export type StaffAlertMeta = Pick<EnvelopeMeta, 'correlationId' | 'aggregate'>;
|
|
31
|
+
/**
|
|
32
|
+
* Stages one `notification:dispatch` per recipient through the outbox, in the caller's transaction, so an
|
|
33
|
+
* alert is sent if and only if the state change that raised it commits. Never sends email itself.
|
|
34
|
+
* An alert that resolves to nobody is logged as a warning (an unconfigured list must not fail silently).
|
|
35
|
+
*/
|
|
36
|
+
export declare class StaffNotifier<E> {
|
|
37
|
+
private readonly outbox;
|
|
38
|
+
private readonly recipients;
|
|
39
|
+
private readonly logger;
|
|
40
|
+
constructor(outbox: CommandOutbox<E>, recipients: StaffRecipientResolver, logger?: Logger);
|
|
41
|
+
/** Returns how many commands were staged. */
|
|
42
|
+
alert(executor: E, alert: StaffAlert, meta?: StaffAlertMeta): Promise<number>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* A comma-separated staff address list from env (`"a@x.io, b@y.io"`): trimmed, blanks dropped,
|
|
46
|
+
* de-duplicated case-insensitively. An invalid address fails validation, so a typo is caught at boot
|
|
47
|
+
* instead of dead-lettering in notification-service when the alert finally fires.
|
|
48
|
+
*/
|
|
49
|
+
export declare const staffEmailListSchema: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodOptional<z.ZodString>, string[], string | undefined>, string[], string | undefined>, {
|
|
50
|
+
email: string;
|
|
51
|
+
name?: string | undefined;
|
|
52
|
+
userId?: string | null | undefined;
|
|
53
|
+
locale?: string | null | undefined;
|
|
54
|
+
timezone?: string | null | undefined;
|
|
55
|
+
}[], string | undefined>;
|
|
56
|
+
//# sourceMappingURL=staff-notifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staff-notifier.d.ts","sourceRoot":"","sources":["../../../src/messaging/staff-notifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAEnB,MAAM,uCAAuC,CAAC;AAE/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,kGAAkG;AAClG,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvF;AAED,6GAA6G;AAC7G,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;CAC3D;AAED,qFAAqF;AACrF,qBAAa,yBAA0B,YAAW,sBAAsB;IAC1D,OAAO,CAAC,QAAQ,CAAC,YAAY;gBAAZ,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,iBAAiB,EAAE,CAAC,CAAC;IAE3F,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;CAGhE;AAED,MAAM,WAAW,UAAU;IACzB,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACpC,gGAAgG;IAChG,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAC;CAC9B;AAED,uEAAuE;AACvE,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,YAAY,EAAE,eAAe,GAAG,WAAW,CAAC,CAAC;AAE/E;;;;GAIG;AACH,qBAAa,aAAa,CAAC,CAAC;IAExB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAFN,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EACxB,UAAU,EAAE,sBAAsB,EAClC,MAAM,GAAE,MAAqB;IAGhD,6CAA6C;IACvC,KAAK,CAAC,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,MAAM,CAAC;CAoBxF;AAID;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;;;;;wBAegD,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Naming for exchanges and queues. Every name carries the environment prefix so two
|
|
3
|
+
* environments that accidentally share a vhost still cannot see each other's traffic.
|
|
4
|
+
*
|
|
5
|
+
* events exchange <app>.<prefix>.events (topic) domain events: `auth.user.registered`
|
|
6
|
+
* commands exchange <app>.<prefix>.commands (direct) work for one owner: `email:send`
|
|
7
|
+
* queues <prefix>.<name> e.g. `prod.notification-service:auth.events`
|
|
8
|
+
*/
|
|
9
|
+
export interface Topology {
|
|
10
|
+
readonly app: string;
|
|
11
|
+
readonly prefix: string;
|
|
12
|
+
readonly eventsExchange: string;
|
|
13
|
+
readonly commandsExchange: string;
|
|
14
|
+
/** Physical queue name for a logical name. */
|
|
15
|
+
queue(name: string): string;
|
|
16
|
+
}
|
|
17
|
+
export declare function createTopology(options: {
|
|
18
|
+
app: string;
|
|
19
|
+
prefix: string;
|
|
20
|
+
}): Topology;
|
|
21
|
+
export declare const retryQueueName: (queue: string, delayMs: number) => string;
|
|
22
|
+
export declare const deadLetterQueueName: (queue: string) => string;
|
|
23
|
+
//# sourceMappingURL=topology.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"topology.d.ts","sourceRoot":"","sources":["../../../src/messaging/topology.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,8CAA8C;IAC9C,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7B;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,QAAQ,CAWjF;AAED,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,EAAE,SAAS,MAAM,WAAgC,CAAC;AAC9F,eAAO,MAAM,mBAAmB,GAAI,OAAO,MAAM,WAAmB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prometheus metrics. Re-exports prom-client so every service runs one version of it;
|
|
3
|
+
* services keep their own metric definitions behind a narrow interface.
|
|
4
|
+
*/
|
|
5
|
+
export { Counter, Gauge, Histogram, Registry, Summary, collectDefaultMetrics } from 'prom-client';
|
|
6
|
+
export type { CounterConfiguration, GaugeConfiguration, HistogramConfiguration, SummaryConfiguration } from 'prom-client';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/metrics/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAClG,YAAY,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
2
|
+
import type { Redis } from 'ioredis';
|
|
3
|
+
import { type RateLimiterAbstract } from 'rate-limiter-flexible';
|
|
4
|
+
import type { Logger } from '../types/index.js';
|
|
5
|
+
export interface RateLimitOptions {
|
|
6
|
+
points: number;
|
|
7
|
+
durationSec: number;
|
|
8
|
+
/** Unique per limiter, e.g. `rl:auth:login-ip`. */
|
|
9
|
+
keyPrefix: string;
|
|
10
|
+
redis?: Redis | null;
|
|
11
|
+
/** Lock the key for this long once exhausted (default: none). */
|
|
12
|
+
blockDurationSec?: number;
|
|
13
|
+
logger?: Logger;
|
|
14
|
+
}
|
|
15
|
+
/** Redis limiter with an in-memory insurance limiter so a Redis outage never blocks traffic. */
|
|
16
|
+
export declare function createLimiter(options: RateLimitOptions): RateLimiterAbstract;
|
|
17
|
+
/**
|
|
18
|
+
* Consumes one point for `key`; throws TooManyRequestsError (429) when exhausted.
|
|
19
|
+
* For in-service checks such as per-identifier login throttling.
|
|
20
|
+
*/
|
|
21
|
+
export declare function consumeOrThrow(limiter: RateLimiterAbstract, key: string, message?: string): Promise<void>;
|
|
22
|
+
export interface RateLimitMiddlewareOptions extends RateLimitOptions {
|
|
23
|
+
/** Default: authenticated user ID, else client IP. */
|
|
24
|
+
keyOf?: (req: Request) => string;
|
|
25
|
+
}
|
|
26
|
+
export declare function rateLimitMiddleware(options: RateLimitMiddlewareOptions): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
27
|
+
export type { RateLimiterAbstract };
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/rate-limit/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAuC,KAAK,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAItG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;IACrB,iEAAiE;IACjE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,gGAAgG;AAChG,wBAAgB,aAAa,CAAC,OAAO,EAAE,gBAAgB,GAAG,mBAAmB,CAW5E;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,mBAAmB,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO/G;AAED,MAAM,WAAW,0BAA2B,SAAQ,gBAAgB;IAClE,sDAAsD;IACtD,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;CAClC;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,IAKvD,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC,CAiB9E;AAED,YAAY,EAAE,mBAAmB,EAAE,CAAC"}
|