@heavybit/shared-lib 2.1.0 → 2.7.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 +163 -2
- package/dist/cjs/access/index.js +9 -1
- package/dist/cjs/access/index.js.map +1 -1
- package/dist/cjs/approvals/index.js +137 -0
- package/dist/cjs/approvals/index.js.map +1 -0
- package/dist/cjs/audit/index.js +74 -0
- package/dist/cjs/audit/index.js.map +1 -0
- package/dist/cjs/cache/index.js +90 -16
- package/dist/cjs/cache/index.js.map +1 -1
- package/dist/cjs/cache/keyspace.js +101 -0
- package/dist/cjs/cache/keyspace.js.map +1 -0
- package/dist/cjs/common/circuit-breaker.js +74 -0
- package/dist/cjs/common/circuit-breaker.js.map +1 -0
- package/dist/cjs/common/index.js +4 -0
- package/dist/cjs/common/index.js.map +1 -1
- package/dist/cjs/common/internal-client.js +177 -0
- package/dist/cjs/common/internal-client.js.map +1 -0
- package/dist/cjs/common/internal-constants.js +12 -0
- package/dist/cjs/common/internal-constants.js.map +1 -0
- package/dist/cjs/common/internal-service-auth.js +160 -0
- package/dist/cjs/common/internal-service-auth.js.map +1 -0
- package/dist/cjs/common/money.js +215 -0
- package/dist/cjs/common/money.js.map +1 -0
- package/dist/cjs/common/phone.js +41 -22
- package/dist/cjs/common/phone.js.map +1 -1
- package/dist/cjs/common/time.js +28 -2
- package/dist/cjs/common/time.js.map +1 -1
- package/dist/cjs/config/index.js +139 -19
- package/dist/cjs/config/index.js.map +1 -1
- package/dist/cjs/contracts/notification-dispatch.js +1 -1
- package/dist/cjs/database/db.js +37 -0
- package/dist/cjs/database/db.js.map +1 -0
- package/dist/cjs/database/index.js +22 -51
- package/dist/cjs/database/index.js.map +1 -1
- package/dist/cjs/database/pagination.js +88 -0
- package/dist/cjs/database/pagination.js.map +1 -0
- package/dist/cjs/database/pool.js +60 -0
- package/dist/cjs/database/pool.js.map +1 -0
- package/dist/cjs/database/scoped-repository.js +132 -0
- package/dist/cjs/database/scoped-repository.js.map +1 -0
- package/dist/cjs/database/scopes.js +166 -0
- package/dist/cjs/database/scopes.js.map +1 -0
- package/dist/cjs/database/table-names.js +14 -0
- package/dist/cjs/database/table-names.js.map +1 -0
- package/dist/cjs/database/tables.js +71 -0
- package/dist/cjs/database/tables.js.map +1 -0
- package/dist/cjs/express/auth.js +64 -50
- package/dist/cjs/express/auth.js.map +1 -1
- package/dist/cjs/express/index.js +2 -0
- package/dist/cjs/express/index.js.map +1 -1
- package/dist/cjs/express/internal-caller.js +52 -0
- package/dist/cjs/express/internal-caller.js.map +1 -0
- package/dist/cjs/express/step-up.js +41 -0
- package/dist/cjs/express/step-up.js.map +1 -0
- package/dist/cjs/http/index.js +3 -71
- package/dist/cjs/http/index.js.map +1 -1
- package/dist/cjs/identity/index.js +269 -67
- package/dist/cjs/identity/index.js.map +1 -1
- package/dist/cjs/identity/step-up.js +52 -0
- package/dist/cjs/identity/step-up.js.map +1 -0
- package/dist/cjs/index.js +6 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/knex/columns.js +36 -0
- package/dist/cjs/knex/columns.js.map +1 -0
- package/dist/cjs/knex/index.js +26 -43
- package/dist/cjs/knex/index.js.map +1 -1
- package/dist/cjs/knex/migration-helpers.js +57 -0
- package/dist/cjs/knex/migration-helpers.js.map +1 -0
- package/dist/cjs/logging/redact.js +6 -11
- package/dist/cjs/logging/redact.js.map +1 -1
- package/dist/cjs/messaging/base-consumer.js +36 -0
- package/dist/cjs/messaging/base-consumer.js.map +1 -0
- package/dist/cjs/messaging/consumer.js +142 -73
- package/dist/cjs/messaging/consumer.js.map +1 -1
- package/dist/cjs/messaging/envelope.js +53 -10
- package/dist/cjs/messaging/envelope.js.map +1 -1
- package/dist/cjs/messaging/idempotency.js +95 -8
- package/dist/cjs/messaging/idempotency.js.map +1 -1
- package/dist/cjs/messaging/index.js +2 -1
- package/dist/cjs/messaging/index.js.map +1 -1
- package/dist/cjs/messaging/outbox.js +97 -55
- package/dist/cjs/messaging/outbox.js.map +1 -1
- package/dist/cjs/messaging/publisher.js +86 -38
- package/dist/cjs/messaging/publisher.js.map +1 -1
- package/dist/cjs/messaging/staff-notifier.js +4 -4
- package/dist/cjs/messaging/staff-notifier.js.map +1 -1
- package/dist/cjs/messaging/topology.js +41 -5
- package/dist/cjs/messaging/topology.js.map +1 -1
- package/dist/cjs/rate-limit/index.js +57 -6
- package/dist/cjs/rate-limit/index.js.map +1 -1
- package/dist/cjs/rate-limit/keys.js +47 -0
- package/dist/cjs/rate-limit/keys.js.map +1 -0
- package/dist/cjs/socket/index.js +127 -16
- package/dist/cjs/socket/index.js.map +1 -1
- package/dist/cjs/socket/ticket.js +124 -0
- package/dist/cjs/socket/ticket.js.map +1 -0
- package/dist/cjs/testing/index.js +23 -14
- package/dist/cjs/testing/index.js.map +1 -1
- package/dist/esm/access/index.js +9 -1
- package/dist/esm/access/index.js.map +1 -1
- package/dist/esm/approvals/index.js +127 -0
- package/dist/esm/approvals/index.js.map +1 -0
- package/dist/esm/audit/index.js +70 -0
- package/dist/esm/audit/index.js.map +1 -0
- package/dist/esm/cache/index.js +70 -13
- package/dist/esm/cache/index.js.map +1 -1
- package/dist/esm/cache/keyspace.js +95 -0
- package/dist/esm/cache/keyspace.js.map +1 -0
- package/dist/esm/common/circuit-breaker.js +70 -0
- package/dist/esm/common/circuit-breaker.js.map +1 -0
- package/dist/esm/common/index.js +4 -0
- package/dist/esm/common/index.js.map +1 -1
- package/dist/esm/common/internal-client.js +173 -0
- package/dist/esm/common/internal-client.js.map +1 -0
- package/dist/esm/common/internal-constants.js +9 -0
- package/dist/esm/common/internal-constants.js.map +1 -0
- package/dist/esm/common/internal-service-auth.js +150 -0
- package/dist/esm/common/internal-service-auth.js.map +1 -0
- package/dist/esm/common/money.js +207 -0
- package/dist/esm/common/money.js.map +1 -0
- package/dist/esm/common/phone.js +41 -22
- package/dist/esm/common/phone.js.map +1 -1
- package/dist/esm/common/time.js +27 -2
- package/dist/esm/common/time.js.map +1 -1
- package/dist/esm/config/index.js +135 -18
- package/dist/esm/config/index.js.map +1 -1
- package/dist/esm/contracts/notification-dispatch.js +1 -1
- package/dist/esm/database/db.js +32 -0
- package/dist/esm/database/db.js.map +1 -0
- package/dist/esm/database/index.js +9 -45
- package/dist/esm/database/index.js.map +1 -1
- package/dist/esm/database/pagination.js +81 -0
- package/dist/esm/database/pagination.js.map +1 -0
- package/dist/esm/database/pool.js +51 -0
- package/dist/esm/database/pool.js.map +1 -0
- package/dist/esm/database/scoped-repository.js +128 -0
- package/dist/esm/database/scoped-repository.js.map +1 -0
- package/dist/esm/database/scopes.js +157 -0
- package/dist/esm/database/scopes.js.map +1 -0
- package/dist/esm/database/table-names.js +11 -0
- package/dist/esm/database/table-names.js.map +1 -0
- package/dist/esm/database/tables.js +61 -0
- package/dist/esm/database/tables.js.map +1 -0
- package/dist/esm/express/auth.js +62 -48
- package/dist/esm/express/auth.js.map +1 -1
- package/dist/esm/express/index.js +2 -0
- package/dist/esm/express/index.js.map +1 -1
- package/dist/esm/express/internal-caller.js +49 -0
- package/dist/esm/express/internal-caller.js.map +1 -0
- package/dist/esm/express/step-up.js +38 -0
- package/dist/esm/express/step-up.js.map +1 -0
- package/dist/esm/http/index.js +3 -70
- package/dist/esm/http/index.js.map +1 -1
- package/dist/esm/identity/index.js +246 -64
- package/dist/esm/identity/index.js.map +1 -1
- package/dist/esm/identity/step-up.js +47 -0
- package/dist/esm/identity/step-up.js.map +1 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/knex/columns.js +28 -0
- package/dist/esm/knex/columns.js.map +1 -0
- package/dist/esm/knex/index.js +8 -34
- package/dist/esm/knex/index.js.map +1 -1
- package/dist/esm/knex/migration-helpers.js +51 -0
- package/dist/esm/knex/migration-helpers.js.map +1 -0
- package/dist/esm/logging/redact.js +3 -7
- package/dist/esm/logging/redact.js.map +1 -1
- package/dist/esm/messaging/base-consumer.js +32 -0
- package/dist/esm/messaging/base-consumer.js.map +1 -0
- package/dist/esm/messaging/consumer.js +139 -71
- package/dist/esm/messaging/consumer.js.map +1 -1
- package/dist/esm/messaging/envelope.js +50 -10
- package/dist/esm/messaging/envelope.js.map +1 -1
- package/dist/esm/messaging/idempotency.js +91 -7
- package/dist/esm/messaging/idempotency.js.map +1 -1
- package/dist/esm/messaging/index.js +2 -1
- package/dist/esm/messaging/index.js.map +1 -1
- package/dist/esm/messaging/outbox.js +97 -54
- package/dist/esm/messaging/outbox.js.map +1 -1
- package/dist/esm/messaging/publisher.js +86 -38
- package/dist/esm/messaging/publisher.js.map +1 -1
- package/dist/esm/messaging/staff-notifier.js +4 -4
- package/dist/esm/messaging/staff-notifier.js.map +1 -1
- package/dist/esm/messaging/topology.js +39 -4
- package/dist/esm/messaging/topology.js.map +1 -1
- package/dist/esm/rate-limit/index.js +41 -6
- package/dist/esm/rate-limit/index.js.map +1 -1
- package/dist/esm/rate-limit/keys.js +43 -0
- package/dist/esm/rate-limit/keys.js.map +1 -0
- package/dist/esm/socket/index.js +112 -17
- package/dist/esm/socket/index.js.map +1 -1
- package/dist/esm/socket/ticket.js +117 -0
- package/dist/esm/socket/ticket.js.map +1 -0
- package/dist/esm/testing/index.js +24 -15
- package/dist/esm/testing/index.js.map +1 -1
- package/dist/types/access/index.d.ts +9 -3
- package/dist/types/access/index.d.ts.map +1 -1
- package/dist/types/approvals/index.d.ts +99 -0
- package/dist/types/approvals/index.d.ts.map +1 -0
- package/dist/types/audit/index.d.ts +82 -0
- package/dist/types/audit/index.d.ts.map +1 -0
- package/dist/types/cache/index.d.ts +64 -14
- package/dist/types/cache/index.d.ts.map +1 -1
- package/dist/types/cache/keyspace.d.ts +84 -0
- package/dist/types/cache/keyspace.d.ts.map +1 -0
- package/dist/types/common/circuit-breaker.d.ts +51 -0
- package/dist/types/common/circuit-breaker.d.ts.map +1 -0
- package/dist/types/common/index.d.ts +4 -0
- package/dist/types/common/index.d.ts.map +1 -1
- package/dist/types/common/internal-client.d.ts +116 -0
- package/dist/types/common/internal-client.d.ts.map +1 -0
- package/dist/types/common/internal-constants.d.ts +9 -0
- package/dist/types/common/internal-constants.d.ts.map +1 -0
- package/dist/types/common/internal-service-auth.d.ts +102 -0
- package/dist/types/common/internal-service-auth.d.ts.map +1 -0
- package/dist/types/common/money.d.ts +104 -0
- package/dist/types/common/money.d.ts.map +1 -0
- package/dist/types/common/phone.d.ts +25 -15
- package/dist/types/common/phone.d.ts.map +1 -1
- package/dist/types/common/time.d.ts +20 -2
- package/dist/types/common/time.d.ts.map +1 -1
- package/dist/types/config/index.d.ts +251 -27
- package/dist/types/config/index.d.ts.map +1 -1
- package/dist/types/contracts/notification-dispatch.d.ts +13 -13
- package/dist/types/database/db.d.ts +46 -0
- package/dist/types/database/db.d.ts.map +1 -0
- package/dist/types/database/index.d.ts +9 -22
- package/dist/types/database/index.d.ts.map +1 -1
- package/dist/types/database/pagination.d.ts +68 -0
- package/dist/types/database/pagination.d.ts.map +1 -0
- package/dist/types/database/pool.d.ts +39 -0
- package/dist/types/database/pool.d.ts.map +1 -0
- package/dist/types/database/scoped-repository.d.ts +92 -0
- package/dist/types/database/scoped-repository.d.ts.map +1 -0
- package/dist/types/database/scopes.d.ts +159 -0
- package/dist/types/database/scopes.d.ts.map +1 -0
- package/dist/types/database/table-names.d.ts +13 -0
- package/dist/types/database/table-names.d.ts.map +1 -0
- package/dist/types/database/tables.d.ts +748 -0
- package/dist/types/database/tables.d.ts.map +1 -0
- package/dist/types/express/auth.d.ts +40 -24
- package/dist/types/express/auth.d.ts.map +1 -1
- package/dist/types/express/index.d.ts +2 -0
- package/dist/types/express/index.d.ts.map +1 -1
- package/dist/types/express/internal-caller.d.ts +53 -0
- package/dist/types/express/internal-caller.d.ts.map +1 -0
- package/dist/types/express/step-up.d.ts +38 -0
- package/dist/types/express/step-up.d.ts.map +1 -0
- package/dist/types/http/index.d.ts +3 -31
- package/dist/types/http/index.d.ts.map +1 -1
- package/dist/types/identity/index.d.ts +159 -21
- package/dist/types/identity/index.d.ts.map +1 -1
- package/dist/types/identity/step-up.d.ts +41 -0
- package/dist/types/identity/step-up.d.ts.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/knex/columns.d.ts +15 -0
- package/dist/types/knex/columns.d.ts.map +1 -0
- package/dist/types/knex/index.d.ts +3 -11
- package/dist/types/knex/index.d.ts.map +1 -1
- package/dist/types/knex/migration-helpers.d.ts +28 -0
- package/dist/types/knex/migration-helpers.d.ts.map +1 -0
- package/dist/types/logging/redact.d.ts +2 -2
- package/dist/types/logging/redact.d.ts.map +1 -1
- package/dist/types/messaging/base-consumer.d.ts +33 -0
- package/dist/types/messaging/base-consumer.d.ts.map +1 -0
- package/dist/types/messaging/consumer.d.ts +59 -21
- package/dist/types/messaging/consumer.d.ts.map +1 -1
- package/dist/types/messaging/envelope.d.ts +50 -11
- package/dist/types/messaging/envelope.d.ts.map +1 -1
- package/dist/types/messaging/idempotency.d.ts +60 -3
- package/dist/types/messaging/idempotency.d.ts.map +1 -1
- package/dist/types/messaging/index.d.ts +7 -0
- package/dist/types/messaging/index.d.ts.map +1 -1
- package/dist/types/messaging/outbox.d.ts +62 -22
- package/dist/types/messaging/outbox.d.ts.map +1 -1
- package/dist/types/messaging/publisher.d.ts +54 -21
- package/dist/types/messaging/publisher.d.ts.map +1 -1
- package/dist/types/messaging/staff-notifier.d.ts +9 -9
- package/dist/types/messaging/staff-notifier.d.ts.map +1 -1
- package/dist/types/messaging/staff-recipients.d.ts +1 -1
- package/dist/types/messaging/topology.d.ts +40 -8
- package/dist/types/messaging/topology.d.ts.map +1 -1
- package/dist/types/rate-limit/index.d.ts +40 -4
- package/dist/types/rate-limit/index.d.ts.map +1 -1
- package/dist/types/rate-limit/keys.d.ts +52 -0
- package/dist/types/rate-limit/keys.d.ts.map +1 -0
- package/dist/types/socket/index.d.ts +86 -2
- package/dist/types/socket/index.d.ts.map +1 -1
- package/dist/types/socket/ticket.d.ts +90 -0
- package/dist/types/socket/ticket.d.ts.map +1 -0
- package/dist/types/testing/index.d.ts +31 -10
- package/dist/types/testing/index.d.ts.map +1 -1
- package/dist/types/types/index.d.ts +26 -14
- package/dist/types/types/index.d.ts.map +1 -1
- package/package.json +3 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/knex/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/knex/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAKjC,cAAc,cAAc,CAAC;AAC7B,cAAc,wBAAwB,CAAC;AAEvC;;;GAGG;AAEH;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,kBAAkB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAYxI;AAED,6FAA6F;AAC7F,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,SAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAG9F;AAED,0EAA0E;AAC1E,wBAAgB,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAI5D"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
/**
|
|
3
|
+
* Column sets for infrastructure tables, for `.cjs` migrations. Each matches the Drizzle table of
|
|
4
|
+
* the same purpose in `@heavybit/shared-lib/database` (`outboxTable`, `processedEventsTable`,
|
|
5
|
+
* `schedulerRunsTable`); a test keeps the two in step.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* await knex.schema.createTable('processed_events', (t) => processedEventsColumns(t, knex));
|
|
9
|
+
*/
|
|
10
|
+
type Builder = Knex.CreateTableBuilder;
|
|
11
|
+
/**
|
|
12
|
+
* Outbox columns: id, routing_key, exchange, payload, correlation_id, attempts, last_error, created_at,
|
|
13
|
+
* published_at (+ relay index).
|
|
14
|
+
*/
|
|
15
|
+
export declare function outboxColumns(t: Builder, knex: Knex): void;
|
|
16
|
+
/** Consumer idempotency columns: id, consumer, event_id (unique together), processed_at. */
|
|
17
|
+
export declare function processedEventsColumns(t: Builder, knex: Knex): void;
|
|
18
|
+
/** Scheduler run columns: id, job, run_key (unique together), status, error, started_at, finished_at. */
|
|
19
|
+
export declare function schedulerRunsColumns(t: Builder, knex: Knex): void;
|
|
20
|
+
/**
|
|
21
|
+
* Index that always leads with the scope column (TEN-4); `columns` follow in order, duplicates of
|
|
22
|
+
* the scope column are dropped.
|
|
23
|
+
*
|
|
24
|
+
* @example scopedIndex(t, 'workspace_id', ['status', 'created_at'])
|
|
25
|
+
*/
|
|
26
|
+
export declare function scopedIndex(t: Builder | Knex.AlterTableBuilder, scopeColumn: string, columns?: string[], indexName?: string): void;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=migration-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-helpers.d.ts","sourceRoot":"","sources":["../../../src/knex/migration-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAIjC;;;;;;;GAOG;AAEH,KAAK,OAAO,GAAG,IAAI,CAAC,kBAAkB,CAAC;AAGvC;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAW1D;AAED,4FAA4F;AAC5F,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAOnE;AAED,yGAAyG;AACzG,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAUjE;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,iBAAiB,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,EAAO,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAGtI"}
|
|
@@ -11,13 +11,13 @@ export declare const DEFAULT_ALLOW_KEYS: readonly string[];
|
|
|
11
11
|
/** The marker that replaces a secret value. */
|
|
12
12
|
export declare const REDACTED = "[REDACTED]";
|
|
13
13
|
/**
|
|
14
|
-
* Masks every digit except the last four
|
|
14
|
+
* Masks every digit except the last four; the same rule as `maskPhone` in common (one rule for logs and UI).
|
|
15
15
|
*
|
|
16
16
|
* @param value A phone number in any format.
|
|
17
17
|
* @returns e.g. `********5678`; values with fewer than 5 digits are fully masked.
|
|
18
18
|
* @example maskPhoneForLog('15551234567') // '*******4567'
|
|
19
19
|
*/
|
|
20
|
-
export declare
|
|
20
|
+
export declare const maskPhoneForLog: (value: string) => string;
|
|
21
21
|
/**
|
|
22
22
|
* Masks phone-number-looking runs inside free text.
|
|
23
23
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../../src/logging/redact.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../../src/logging/redact.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,qGAAqG;AACrG,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAGhD,CAAC;AAEF,4GAA4G;AAC5G,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAAwF,CAAC;AAQzI,+CAA+C;AAC/C,eAAO,MAAM,QAAQ,eAAe,CAAC;AAGrC;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAkB,CAAC;AAEpE;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD;AAWD,gFAAgF;AAChF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC/B;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,GAAE,SAAS,MAAM,EAAO,EAAE,SAAS,GAAE,SAAS,MAAM,EAAuB,GAAG,aAAa,CAgBvI;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,KAAK,SAAI,GAAG,OAAO,CAYzF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { InboundMessage, MessageConsumer, SubscriptionOptions } from './consumer.js';
|
|
2
|
+
/** Everything a subscription needs except the handler, which a {@link BaseConsumer} subclass supplies. */
|
|
3
|
+
export type ConsumerDefinition<T, TTx> = Omit<SubscriptionOptions<T, TTx>, 'handler'>;
|
|
4
|
+
/**
|
|
5
|
+
* Base for a service's queue consumers (one class per queue in `src/consumers/`). The subclass declares
|
|
6
|
+
* its queue, bindings, payload schema, retry policy, dedupe and scope in the constructor and implements
|
|
7
|
+
* {@link BaseConsumer.handle}; `worker.ts` registers it. Validation, dedupe, retries, dead-lettering and
|
|
8
|
+
* context restore are done by {@link MessageConsumer}.
|
|
9
|
+
*
|
|
10
|
+
* @typeParam T Validated payload type.
|
|
11
|
+
* @typeParam TTx Dedupe transaction type (`DbExecutor` with `ProcessedEventsDeduper`).
|
|
12
|
+
* @example
|
|
13
|
+
* class ThingCreatedConsumer extends BaseConsumer<ThingCreated, DbExecutor> {
|
|
14
|
+
* constructor(private readonly things: ThingService, dedupe: ProcessedEventsDeduper) {
|
|
15
|
+
* super({ queue: 'thing-created', bindings: [{ exchange: 'events', routingKey: 'thing.created' }],
|
|
16
|
+
* schema: thingCreatedSchema, dedupe, scopeFrom: (d) => ({ tenantId: d.tenantId }) });
|
|
17
|
+
* }
|
|
18
|
+
* protected handle({ data, tx, scope }: InboundMessage<ThingCreated, DbExecutor>) { return this.things.onCreated(scope!, data, tx!); }
|
|
19
|
+
* }
|
|
20
|
+
*/
|
|
21
|
+
export declare abstract class BaseConsumer<T, TTx = undefined> {
|
|
22
|
+
protected readonly definition: ConsumerDefinition<T, TTx>;
|
|
23
|
+
protected constructor(definition: ConsumerDefinition<T, TTx>);
|
|
24
|
+
/** Handles one validated, de-duplicated message. Throw to retry (or a non-retryable error to dead-letter). */
|
|
25
|
+
protected abstract handle(message: InboundMessage<T, TTx>): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Subscribes this consumer.
|
|
28
|
+
*
|
|
29
|
+
* @param consumer The service's {@link MessageConsumer}.
|
|
30
|
+
*/
|
|
31
|
+
register(consumer: MessageConsumer): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=base-consumer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-consumer.d.ts","sourceRoot":"","sources":["../../../src/messaging/base-consumer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAE1F,0GAA0G;AAC1G,MAAM,MAAM,kBAAkB,CAAC,CAAC,EAAE,GAAG,IAAI,IAAI,CAAC,mBAAmB,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,SAAS,CAAC,CAAC;AAEtF;;;;;;;;;;;;;;;;GAgBG;AACH,8BAAsB,YAAY,CAAC,CAAC,EAAE,GAAG,GAAG,SAAS;IAC7B,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAC,EAAE,GAAG,CAAC;IAA/E,SAAS,aAAgC,UAAU,EAAE,kBAAkB,CAAC,CAAC,EAAE,GAAG,CAAC;IAE/E,8GAA8G;IAC9G,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAEzE;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;CAGnD"}
|
|
@@ -1,55 +1,93 @@
|
|
|
1
1
|
import type { ConsumeMessage } from 'amqplib';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ZodType, ZodTypeDef } from 'zod';
|
|
3
3
|
import type { EventEnvelope, Logger } from '../types/index.js';
|
|
4
4
|
import type { AmqpConnection } from './connection.js';
|
|
5
|
-
import type {
|
|
5
|
+
import type { EventDeduper } from './idempotency.js';
|
|
6
6
|
import { type Topology } from './topology.js';
|
|
7
|
+
/** One exchange binding of a subscription queue. */
|
|
7
8
|
export interface Binding {
|
|
8
|
-
/** `events` (topic, pattern keys like `
|
|
9
|
+
/** `events` (topic, pattern keys like `thing.*`) or `commands` (exact command name). */
|
|
9
10
|
exchange: 'events' | 'commands';
|
|
10
11
|
routingKey: string;
|
|
11
12
|
}
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
/** Scope values restored from a message, e.g. `{ tenantId: '01J…' }`; the app names the keys. */
|
|
14
|
+
export type MessageScope = Readonly<Record<string, string>>;
|
|
15
|
+
/** What a consumer handler receives for one delivery. */
|
|
16
|
+
export interface InboundMessage<T = unknown, TTx = undefined> {
|
|
17
|
+
/** Payload (envelope `data` without the reserved fields), validated against `schema` when given. */
|
|
14
18
|
data: T;
|
|
15
19
|
envelope: EventEnvelope | null;
|
|
20
|
+
/** Envelope event id, else the AMQP `messageId`. */
|
|
21
|
+
eventId: string | undefined;
|
|
16
22
|
routingKey: string;
|
|
17
|
-
/**
|
|
23
|
+
/** Delivery attempt, 1-based. */
|
|
18
24
|
attempt: number;
|
|
25
|
+
/** The dedupe ledger's open transaction (write through it so effects and ledger commit together). */
|
|
26
|
+
tx: TTx | undefined;
|
|
27
|
+
/** Scope restored by `scopeFrom`; also set as the request context's `scopes`. */
|
|
28
|
+
scope: MessageScope | undefined;
|
|
19
29
|
raw: ConsumeMessage;
|
|
20
30
|
}
|
|
21
|
-
|
|
22
|
-
|
|
31
|
+
/** One durable subscription: queue purpose, bindings, handler and delivery policy. */
|
|
32
|
+
export interface SubscriptionOptions<T = unknown, TTx = undefined> {
|
|
33
|
+
/** Queue purpose; the physical queue is `{prefix}.{service}.{purpose}`. */
|
|
23
34
|
queue: string;
|
|
24
35
|
bindings: Binding[];
|
|
25
|
-
handler: (message: InboundMessage<T>) => Promise<void>;
|
|
26
|
-
/** Validates
|
|
27
|
-
schema?:
|
|
36
|
+
handler: (message: InboundMessage<T, TTx>) => Promise<void>;
|
|
37
|
+
/** Validates the payload; failures dead-letter immediately (a bad payload never succeeds on retry). */
|
|
38
|
+
schema?: ZodType<T, ZodTypeDef, unknown>;
|
|
28
39
|
prefetch?: number;
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
|
|
33
|
-
|
|
40
|
+
/** Total deliveries before dead-lettering (default 5). Retry n waits `retryBaseDelayMs × 2^n`. */
|
|
41
|
+
maxAttempts?: number;
|
|
42
|
+
/** Base of the exponential retry delay (default 1000 ms → 2 s, 4 s, 8 s, 16 s). */
|
|
43
|
+
retryBaseDelayMs?: number;
|
|
44
|
+
/** Duplicate suppression keyed on `({service}.{purpose}, eventId)`; a message without an id dead-letters. */
|
|
45
|
+
dedupe?: EventDeduper<TTx>;
|
|
46
|
+
/**
|
|
47
|
+
* Re-establishes the scope (tenant) the message belongs to from its payload (TEN-7). Every returned
|
|
48
|
+
* value must be a non-empty string, or the message dead-letters.
|
|
49
|
+
*
|
|
50
|
+
* @example scopeFrom: (d) => ({ tenantId: d.tenantId })
|
|
51
|
+
*/
|
|
52
|
+
scopeFrom?: (data: T) => Record<string, string | null | undefined>;
|
|
34
53
|
}
|
|
54
|
+
/** Total deliveries before a failing message is dead-lettered. */
|
|
55
|
+
export declare const DEFAULT_MAX_ATTEMPTS = 5;
|
|
56
|
+
/** Base of the exponential retry delay (retry n waits base × 2^n). */
|
|
57
|
+
export declare const DEFAULT_RETRY_BASE_DELAY_MS = 1000;
|
|
35
58
|
/**
|
|
36
|
-
*
|
|
59
|
+
* Retry delays for `maxAttempts` deliveries: `base × 2^n` for n = 1 … maxAttempts − 1.
|
|
60
|
+
*
|
|
61
|
+
* @example retryDelays(5, 1000) // [2000, 4000, 8000, 16000]
|
|
62
|
+
*/
|
|
63
|
+
export declare function retryDelays(maxAttempts?: number, baseMs?: number): number[];
|
|
64
|
+
/**
|
|
65
|
+
* Durable subscriptions with manual ack, bounded exponential retries and dead-lettering:
|
|
37
66
|
*
|
|
38
67
|
* <queue> ──fail──▶ <queue>.retry.<delay> (TTL, dead-letters back into <queue>)
|
|
39
|
-
* ──
|
|
68
|
+
* ──last attempt / non-retryable / invalid──▶ service DLX ──▶ <queue>.dlq
|
|
40
69
|
*
|
|
41
|
-
* One fixed TTL per retry queue avoids head-of-line blocking of per-message TTLs.
|
|
70
|
+
* One fixed TTL per retry queue avoids head-of-line blocking of per-message TTLs. The main queue also
|
|
71
|
+
* names the service DLX, so broker-side rejects land in the same DLQ. Each message runs inside a request
|
|
72
|
+
* context carrying its correlation id, actor and restored scope.
|
|
42
73
|
*/
|
|
43
74
|
export declare class MessageConsumer {
|
|
44
75
|
private readonly connection;
|
|
45
76
|
private readonly topology;
|
|
77
|
+
private readonly service;
|
|
46
78
|
private readonly handles;
|
|
47
79
|
private readonly logger;
|
|
48
|
-
constructor(connection: AmqpConnection, topology: Topology, logger?: Logger);
|
|
49
80
|
/** Physical DLQ names for every subscription (for monitoring). */
|
|
50
81
|
readonly deadLetterQueues: string[];
|
|
51
|
-
|
|
82
|
+
constructor(connection: AmqpConnection, topology: Topology, service: string, logger?: Logger);
|
|
83
|
+
subscribe<T = unknown, TTx = undefined>(options: SubscriptionOptions<T, TTx>): Promise<void>;
|
|
84
|
+
private declare;
|
|
52
85
|
private dispatch;
|
|
86
|
+
/** Parses, validates and extracts scope; throws `Rejected` for messages that can never succeed. */
|
|
87
|
+
private prepare;
|
|
88
|
+
private scopeOf;
|
|
89
|
+
private correlationIdOf;
|
|
90
|
+
private handle;
|
|
53
91
|
private retryOrDeadLetter;
|
|
54
92
|
private deadLetter;
|
|
55
93
|
}
|
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"consumer.d.ts","sourceRoot":"","sources":["../../../src/messaging/consumer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,16 +1,55 @@
|
|
|
1
|
-
import type { EventEnvelope } from '../types/index.js';
|
|
1
|
+
import type { EnvelopeData, EventEnvelope } from '../types/index.js';
|
|
2
|
+
/** Names reserved in an envelope's `data`; a payload field with one of these names is overwritten. */
|
|
3
|
+
export declare const RESERVED_ENVELOPE_FIELDS: readonly ["eventId", "version", "actorUserId"];
|
|
4
|
+
/** AMQP header that carries the correlation id between services. */
|
|
5
|
+
export declare const CORRELATION_HEADER: "x-correlation-id";
|
|
6
|
+
/**
|
|
7
|
+
* Optional envelope values and transport metadata.
|
|
8
|
+
*
|
|
9
|
+
* @example { actorUserId: user.id, version: 2 }
|
|
10
|
+
*/
|
|
2
11
|
export interface EnvelopeMeta {
|
|
3
|
-
|
|
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
|
+
/** Deterministic id for idempotent re-publishing; defaults to a new ULID. */
|
|
12
13
|
eventId?: string;
|
|
14
|
+
/** Payload schema version; defaults to 1. */
|
|
15
|
+
version?: number;
|
|
16
|
+
/** Defaults to the request context's `userId`; pass `null` for the system. */
|
|
17
|
+
actorUserId?: string | null;
|
|
18
|
+
/** Sent as the `x-correlation-id` header (not part of the body); defaults to the request context's. */
|
|
19
|
+
correlationId?: string;
|
|
13
20
|
}
|
|
14
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Builds `{ routingKey, data: { ...payload, eventId, version, actorUserId? }, timestamp }`.
|
|
23
|
+
*
|
|
24
|
+
* @param routingKey Event or command name.
|
|
25
|
+
* @param data Payload fields (an object); {@link RESERVED_ENVELOPE_FIELDS} are set by the envelope.
|
|
26
|
+
* @param meta Optional id, version and actor.
|
|
27
|
+
* @returns The envelope.
|
|
28
|
+
* @example const envelope = buildEnvelope('thing.created', { thingId }, { actorUserId: user.id });
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildEnvelope<T extends object>(routingKey: string, data: T, meta?: EnvelopeMeta): EventEnvelope<T>;
|
|
31
|
+
/**
|
|
32
|
+
* True when `value` has the envelope shape (routing key, timestamp and `data.eventId`).
|
|
33
|
+
*
|
|
34
|
+
* @param value Parsed message body.
|
|
35
|
+
* @returns Whether it is an {@link EventEnvelope}.
|
|
36
|
+
*/
|
|
15
37
|
export declare function isEnvelope(value: unknown): value is EventEnvelope;
|
|
38
|
+
/**
|
|
39
|
+
* Splits an envelope's `data` into the payload and the reserved fields.
|
|
40
|
+
*
|
|
41
|
+
* @param envelope A received envelope.
|
|
42
|
+
* @returns `payload` (without reserved fields) and `meta` ({@link EnvelopeData}).
|
|
43
|
+
*/
|
|
44
|
+
export declare function splitEnvelopeData(envelope: EventEnvelope): {
|
|
45
|
+
payload: Record<string, unknown>;
|
|
46
|
+
meta: EnvelopeData;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* The correlation id to send: the explicit one, else the active request context's.
|
|
50
|
+
*
|
|
51
|
+
* @param meta Optional explicit value.
|
|
52
|
+
* @returns The id, or undefined outside a request/consumer context.
|
|
53
|
+
*/
|
|
54
|
+
export declare function outgoingCorrelationId(meta?: Pick<EnvelopeMeta, 'correlationId'>): string | undefined;
|
|
16
55
|
//# sourceMappingURL=envelope.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../../src/messaging/envelope.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../../src/messaging/envelope.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAErE,sGAAsG;AACtG,eAAO,MAAM,wBAAwB,gDAAiD,CAAC;AAEvF,oEAAoE;AACpE,eAAO,MAAM,kBAAkB,oBAAiC,CAAC;AAEjE;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,uGAAuG;IACvG,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,GAAE,YAAiB,GAAG,aAAa,CAAC,CAAC,CAAC,CAQtH;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAOjE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,aAAa,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,IAAI,EAAE,YAAY,CAAA;CAAE,CAGnH;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,MAAM,GAAG,SAAS,CAEpG"}
|
|
@@ -1,7 +1,44 @@
|
|
|
1
1
|
import type { Redis } from 'ioredis';
|
|
2
|
+
import type { RedisKeyspace } from '../cache/keyspace.js';
|
|
3
|
+
import type { DbExecutor } from '../database/scoped-repository.js';
|
|
4
|
+
import { type ProcessedEventsTable } from '../database/tables.js';
|
|
5
|
+
/** Result of {@link EventDeduper.run}. */
|
|
6
|
+
export type DedupeOutcome = 'processed' | 'duplicate';
|
|
7
|
+
/**
|
|
8
|
+
* Runs a consumer's work at most once per `(consumer, eventId)`. At-least-once delivery means duplicates;
|
|
9
|
+
* a deduper turns them into no-ops.
|
|
10
|
+
*
|
|
11
|
+
* @typeParam TTx What `work` receives: the open transaction for a database ledger, `undefined` otherwise.
|
|
12
|
+
*/
|
|
13
|
+
export interface EventDeduper<TTx = unknown> {
|
|
14
|
+
/**
|
|
15
|
+
* @param consumer Stable consumer name (e.g. `{service}.{purpose}`).
|
|
16
|
+
* @param eventId The message's event id.
|
|
17
|
+
* @param work The handler; it throws to fail (nothing is recorded, so a retry runs it again).
|
|
18
|
+
* @returns `processed`, or `duplicate` when the event was already handled (work not called).
|
|
19
|
+
*/
|
|
20
|
+
run(consumer: string, eventId: string, work: (tx: TTx) => Promise<void>): Promise<DedupeOutcome>;
|
|
21
|
+
}
|
|
22
|
+
/** True for a MySQL duplicate-key error, also when Drizzle wraps it in `cause`. */
|
|
23
|
+
export declare function isDuplicateKeyError(error: unknown): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Database ledger (`processed_events`, unique `(consumer, event_id)`): the ledger row and the handler's
|
|
26
|
+
* writes commit in one transaction, so an event is recorded as processed if and only if its effects
|
|
27
|
+
* commit. A concurrent duplicate blocks on the unique key until the first commits, then is skipped.
|
|
28
|
+
*
|
|
29
|
+
* @example const dedupe = new ProcessedEventsDeduper(db); // handler gets `tx` and writes through it
|
|
30
|
+
*/
|
|
31
|
+
export declare class ProcessedEventsDeduper implements EventDeduper<DbExecutor> {
|
|
32
|
+
private readonly db;
|
|
33
|
+
private readonly table;
|
|
34
|
+
constructor(db: DbExecutor, table?: ProcessedEventsTable);
|
|
35
|
+
run(consumer: string, eventId: string, work: (tx: DbExecutor) => Promise<void>): Promise<DedupeOutcome>;
|
|
36
|
+
}
|
|
37
|
+
/** Outcome of {@link IdempotencyStore.begin}. */
|
|
2
38
|
export type ClaimResult = 'claimed' | 'done' | 'in-flight';
|
|
3
39
|
/**
|
|
4
|
-
*
|
|
40
|
+
* Lease-based claim store for services without a database (Redis). Not transactional with the handler's
|
|
41
|
+
* effects: prefer {@link ProcessedEventsDeduper} wherever the handler writes to a database.
|
|
5
42
|
* - `claimed` → process it, then `complete()` (or `abandon()` on failure so a retry can run)
|
|
6
43
|
* - `done` → already processed; ack and skip
|
|
7
44
|
* - `in-flight` → another worker holds the lease; retry later
|
|
@@ -11,14 +48,17 @@ export interface IdempotencyStore {
|
|
|
11
48
|
complete(key: string, ttlSec: number): Promise<void>;
|
|
12
49
|
abandon(key: string): Promise<void>;
|
|
13
50
|
}
|
|
51
|
+
/** Redis claim store; every key comes from the service keyspace (`{prefix}idem:{key}`). */
|
|
14
52
|
export declare class RedisIdempotencyStore implements IdempotencyStore {
|
|
15
53
|
private readonly redis;
|
|
16
|
-
private readonly
|
|
17
|
-
constructor(redis: Redis,
|
|
54
|
+
private readonly keyspace;
|
|
55
|
+
constructor(redis: Redis, keyspace: RedisKeyspace);
|
|
56
|
+
private key;
|
|
18
57
|
begin(key: string, leaseSec: number): Promise<ClaimResult>;
|
|
19
58
|
complete(key: string, ttlSec: number): Promise<void>;
|
|
20
59
|
abandon(key: string): Promise<void>;
|
|
21
60
|
}
|
|
61
|
+
/** In-process claim store for tests and single-process tools. */
|
|
22
62
|
export declare class MemoryIdempotencyStore implements IdempotencyStore {
|
|
23
63
|
private readonly entries;
|
|
24
64
|
private read;
|
|
@@ -26,4 +66,21 @@ export declare class MemoryIdempotencyStore implements IdempotencyStore {
|
|
|
26
66
|
complete(key: string, ttlSec: number): Promise<void>;
|
|
27
67
|
abandon(key: string): Promise<void>;
|
|
28
68
|
}
|
|
69
|
+
/** Lease and retention for {@link StoreDeduper}. */
|
|
70
|
+
export interface StoreDeduperOptions {
|
|
71
|
+
/** How long a claim blocks other workers (default 300 s). */
|
|
72
|
+
leaseSec?: number;
|
|
73
|
+
/** How long a completed marker is kept (default 7 days). */
|
|
74
|
+
ttlSec?: number;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* {@link EventDeduper} over an {@link IdempotencyStore}. A duplicate still in flight elsewhere throws a
|
|
78
|
+
* retryable 503, so the consumer retries it later instead of acking it unprocessed.
|
|
79
|
+
*/
|
|
80
|
+
export declare class StoreDeduper implements EventDeduper<undefined> {
|
|
81
|
+
private readonly store;
|
|
82
|
+
private readonly options;
|
|
83
|
+
constructor(store: IdempotencyStore, options?: StoreDeduperOptions);
|
|
84
|
+
run(consumer: string, eventId: string, work: (tx: undefined) => Promise<void>): Promise<DedupeOutcome>;
|
|
85
|
+
}
|
|
29
86
|
//# sourceMappingURL=idempotency.d.ts.map
|
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../../../src/messaging/idempotency.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,19 +10,26 @@ export * from './topology.js';
|
|
|
10
10
|
export * from './envelope.js';
|
|
11
11
|
export * from './publisher.js';
|
|
12
12
|
export * from './consumer.js';
|
|
13
|
+
export * from './base-consumer.js';
|
|
13
14
|
export * from './idempotency.js';
|
|
14
15
|
export * from './rpc.js';
|
|
15
16
|
export * from './outbox.js';
|
|
16
17
|
export * from './monitor.js';
|
|
17
18
|
export * from './staff-notifier.js';
|
|
18
19
|
export * from './staff-recipients.js';
|
|
20
|
+
/** Inputs for {@link createMessaging}; all values come from the service config. */
|
|
19
21
|
export interface MessagingOptions {
|
|
22
|
+
/** AMQP URL (`RABBITMQ_URL`, or built by `amqpUrlFromEnv(env)`). */
|
|
20
23
|
url: string;
|
|
24
|
+
/** App slug: names the exchanges. */
|
|
21
25
|
app: string;
|
|
26
|
+
/** Environment prefix (`QUEUE_PREFIX`). */
|
|
22
27
|
prefix: string;
|
|
28
|
+
/** This service's name: names its queues and dead-letter exchange. */
|
|
23
29
|
service: string;
|
|
24
30
|
logger?: Logger;
|
|
25
31
|
}
|
|
32
|
+
/** One service's wired messaging components on a shared connection. */
|
|
26
33
|
export interface Messaging {
|
|
27
34
|
topology: Topology;
|
|
28
35
|
connection: AmqpConnection;
|
|
@@ -1 +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;AACpC,cAAc,uBAAuB,CAAC;AAEtC,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"}
|
|
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,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AAEtC,mFAAmF;AACnF,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,GAAG,EAAE,MAAM,CAAC;IACZ,qCAAqC;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,uEAAuE;AACvE,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"}
|
|
@@ -1,51 +1,91 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { DEFAULT_OUTBOX_TABLE } from '../database/table-names.js';
|
|
2
|
+
import { type OutboxExchange, type OutboxTable } from '../database/tables.js';
|
|
3
|
+
import type { DbExecutor } from '../database/scoped-repository.js';
|
|
2
4
|
import type { EventEnvelope, Logger } from '../types/index.js';
|
|
5
|
+
import { type EnvelopeMeta } from './envelope.js';
|
|
3
6
|
import { type CommandBus, type EventBus } from './publisher.js';
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
export { DEFAULT_OUTBOX_TABLE };
|
|
8
|
+
/** Options for {@link enqueueOutbox}. */
|
|
9
|
+
export interface EnqueueOptions extends EnvelopeMeta {
|
|
10
|
+
/** `events` (default, topic fan-out) or `commands` (direct, one owner). */
|
|
11
|
+
exchange?: OutboxExchange;
|
|
12
|
+
/** A service-specific outbox table; defaults to `event_outbox`. */
|
|
13
|
+
table?: OutboxTable;
|
|
14
|
+
}
|
|
8
15
|
/**
|
|
9
|
-
* Transactional outbox:
|
|
10
|
-
*
|
|
11
|
-
*
|
|
16
|
+
* Transactional outbox: stages a message in the caller's transaction, so it is published if and only if
|
|
17
|
+
* the domain change commits. The {@link OutboxRelay} publishes it later with broker confirms. The
|
|
18
|
+
* correlation id (explicit, else the request context's) is stored with the row and sent as a header.
|
|
12
19
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
20
|
+
* @param tx The caller's open Drizzle transaction (or db handle for a single-statement change).
|
|
21
|
+
* @param routingKey Event or command name.
|
|
22
|
+
* @param data Payload fields.
|
|
23
|
+
* @param options Exchange, envelope meta and table.
|
|
24
|
+
* @returns The staged envelope.
|
|
25
|
+
* @example await db.transaction(async (tx) => { await repo.insert(ctx, row, { tx }); await enqueueOutbox(tx, 'thing.created', { thingId: row.id }); });
|
|
15
26
|
*/
|
|
16
|
-
export declare function enqueueOutbox(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
27
|
+
export declare function enqueueOutbox<T extends object>(tx: DbExecutor, routingKey: string, data: T, options?: EnqueueOptions): Promise<EventEnvelope<T>>;
|
|
28
|
+
/** What one relay batch did; passed to {@link OutboxRelayOptions.onMetrics}. */
|
|
29
|
+
export interface OutboxBatchMetrics {
|
|
30
|
+
/** Rows published in this batch. */
|
|
31
|
+
published: number;
|
|
32
|
+
/** Rows that failed (broker down or unroutable) and stay pending. */
|
|
33
|
+
failed: number;
|
|
34
|
+
/** Age in ms of the oldest pending row when the batch started (0 when nothing was pending). */
|
|
35
|
+
lagMs: number;
|
|
36
|
+
}
|
|
37
|
+
/** Dependencies and tuning for {@link OutboxRelay}. */
|
|
20
38
|
export interface OutboxRelayOptions {
|
|
21
|
-
|
|
39
|
+
db: DbExecutor;
|
|
22
40
|
bus: EventBus & CommandBus;
|
|
23
|
-
table?:
|
|
41
|
+
table?: OutboxTable;
|
|
42
|
+
/** Rows per batch (default 100). */
|
|
24
43
|
batchSize?: number;
|
|
25
|
-
/**
|
|
44
|
+
/** Poll interval when healthy (default 2000 ms); `trigger()` after commits gives low latency. */
|
|
26
45
|
intervalMs?: number;
|
|
46
|
+
/** Ceiling for the exponential back-off after failed batches (default 60 000 ms). */
|
|
47
|
+
maxBackoffMs?: number;
|
|
48
|
+
/** Called after every batch, e.g. to export relay lag as a gauge. */
|
|
49
|
+
onMetrics?: (metrics: OutboxBatchMetrics) => void;
|
|
27
50
|
logger?: Logger;
|
|
28
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Publishes pending outbox rows in id (= time) order. Each batch locks its rows with
|
|
54
|
+
* `FOR UPDATE SKIP LOCKED`, so several replicas can relay at once without double-publishing, and marks a
|
|
55
|
+
* row published only after the broker confirms it. Consumers still dedupe: a crash between confirm and
|
|
56
|
+
* commit re-sends the row.
|
|
57
|
+
*
|
|
58
|
+
* Failures: an unroutable command stays pending and the batch continues; a broker error stops the batch
|
|
59
|
+
* (keeping order) and the poll interval backs off exponentially until a batch succeeds.
|
|
60
|
+
*/
|
|
29
61
|
export declare class OutboxRelay {
|
|
30
62
|
private readonly options;
|
|
31
63
|
private timer;
|
|
32
64
|
private running;
|
|
33
65
|
private rerun;
|
|
66
|
+
private failures;
|
|
34
67
|
private readonly logger;
|
|
35
68
|
private readonly table;
|
|
36
69
|
constructor(options: OutboxRelayOptions);
|
|
70
|
+
/** Starts polling. */
|
|
37
71
|
start(): void;
|
|
72
|
+
/** Stops polling (an in-flight batch finishes). */
|
|
38
73
|
stop(): void;
|
|
74
|
+
/** Delay before the next poll: the interval, doubled per consecutive failed batch, capped. */
|
|
75
|
+
nextDelayMs(): number;
|
|
39
76
|
/** Publishes pending rows now. Concurrent triggers coalesce into one follow-up run. */
|
|
40
77
|
trigger(): Promise<void>;
|
|
41
|
-
/** One batch.
|
|
42
|
-
flush(): Promise<
|
|
78
|
+
/** One batch. */
|
|
79
|
+
flush(): Promise<OutboxBatchMetrics>;
|
|
80
|
+
/** Deletes published rows older than `olderThanDays`; returns how many were removed. */
|
|
81
|
+
prune(olderThanDays?: number): Promise<number>;
|
|
82
|
+
private lockPending;
|
|
83
|
+
private publish;
|
|
84
|
+
private schedule;
|
|
43
85
|
/**
|
|
44
86
|
* 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
|
|
87
|
+
* so a queue that stays missing for hours is still visible without a line every poll.
|
|
46
88
|
*/
|
|
47
89
|
private reportUnroutable;
|
|
48
|
-
/** Deletes published rows older than `olderThanDays`. */
|
|
49
|
-
prune(olderThanDays?: number): Promise<number>;
|
|
50
90
|
}
|
|
51
91
|
//# sourceMappingURL=outbox.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../../src/messaging/outbox.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../../src/messaging/outbox.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAA2B,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACvG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAEnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAwC,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AACxF,OAAO,EAAqB,KAAK,UAAU,EAAE,KAAK,QAAQ,EAA+B,MAAM,gBAAgB,CAAC;AAEhH,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAEhC,yCAAyC;AACzC,MAAM,WAAW,cAAe,SAAQ,YAAY;IAClD,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,mEAAmE;IACnE,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAY1J;AAED,gFAAgF;AAChF,MAAM,WAAW,kBAAkB;IACjC,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,+FAA+F;IAC/F,KAAK,EAAE,MAAM,CAAC;CACf;AAED,uDAAuD;AACvD,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,UAAU,CAAC;IACf,GAAG,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC3B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iGAAiG;IACjG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qFAAqF;IACrF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID;;;;;;;;GAQG;AACH,qBAAa,WAAW;IAQV,OAAO,CAAC,QAAQ,CAAC,OAAO;IAPpC,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,OAAO,CAA4C;IAC3D,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;gBAEP,OAAO,EAAE,kBAAkB;IAKxD,sBAAsB;IACtB,KAAK,IAAI,IAAI;IAKb,mDAAmD;IACnD,IAAI,IAAI,IAAI;IAKZ,8FAA8F;IAC9F,WAAW,IAAI,MAAM;IAKrB,uFAAuF;IACjF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB9B,iBAAiB;IACX,KAAK,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAuC1C,wFAAwF;IAClF,KAAK,CAAC,aAAa,SAAI,GAAG,OAAO,CAAC,MAAM,CAAC;IAO/C,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,QAAQ;IAMhB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;CAOzB"}
|