@heavybit/shared-lib 1.3.2 → 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 +261 -0
- 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/errors.js +44 -5
- package/dist/cjs/common/errors.js.map +1 -1
- 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 +63 -11
- package/dist/cjs/common/phone.js.map +1 -1
- package/dist/cjs/common/response.js +78 -5
- package/dist/cjs/common/response.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 +140 -20
- package/dist/cjs/config/index.js.map +1 -1
- package/dist/cjs/context/index.js +49 -0
- package/dist/cjs/context/index.js.map +1 -0
- package/dist/cjs/contracts/index.js +0 -2
- package/dist/cjs/contracts/index.js.map +1 -1
- package/dist/cjs/contracts/notification-dispatch.js +87 -63
- package/dist/cjs/contracts/notification-dispatch.js.map +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 +88 -41
- package/dist/cjs/express/auth.js.map +1 -1
- package/dist/cjs/express/index.js +3 -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/middleware.js +154 -34
- package/dist/cjs/express/middleware.js.map +1 -1
- package/dist/cjs/express/respond.js +47 -0
- package/dist/cjs/express/respond.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 +7 -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/index.js +76 -40
- package/dist/cjs/logging/index.js.map +1 -1
- package/dist/cjs/logging/redact.js +112 -0
- package/dist/cjs/logging/redact.js.map +1 -0
- 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 +3 -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 +36 -44
- package/dist/cjs/messaging/staff-notifier.js.map +1 -1
- package/dist/cjs/messaging/staff-recipients.js +57 -0
- package/dist/cjs/messaging/staff-recipients.js.map +1 -0
- 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/cjs/validation/index.js +27 -3
- package/dist/cjs/validation/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/errors.js +42 -4
- package/dist/esm/common/errors.js.map +1 -1
- 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 +63 -11
- package/dist/esm/common/phone.js.map +1 -1
- package/dist/esm/common/response.js +75 -5
- package/dist/esm/common/response.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 +136 -19
- package/dist/esm/config/index.js.map +1 -1
- package/dist/esm/context/index.js +44 -0
- package/dist/esm/context/index.js.map +1 -0
- package/dist/esm/contracts/index.js +0 -2
- package/dist/esm/contracts/index.js.map +1 -1
- package/dist/esm/contracts/notification-dispatch.js +85 -62
- package/dist/esm/contracts/notification-dispatch.js.map +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 +87 -40
- package/dist/esm/express/auth.js.map +1 -1
- package/dist/esm/express/index.js +3 -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/middleware.js +146 -27
- package/dist/esm/express/middleware.js.map +1 -1
- package/dist/esm/express/respond.js +42 -0
- package/dist/esm/express/respond.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 +7 -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/index.js +68 -39
- package/dist/esm/logging/index.js.map +1 -1
- package/dist/esm/logging/redact.js +106 -0
- package/dist/esm/logging/redact.js.map +1 -0
- 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 +3 -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 +36 -43
- package/dist/esm/messaging/staff-notifier.js.map +1 -1
- package/dist/esm/messaging/staff-recipients.js +53 -0
- package/dist/esm/messaging/staff-recipients.js.map +1 -0
- 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/esm/validation/index.js +27 -2
- package/dist/esm/validation/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/alerting/index.d.ts +1 -1
- package/dist/types/alerting/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/errors.d.ts +48 -7
- package/dist/types/common/errors.d.ts.map +1 -1
- 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 +45 -6
- package/dist/types/common/phone.d.ts.map +1 -1
- package/dist/types/common/response.d.ts +66 -5
- package/dist/types/common/response.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 +252 -28
- package/dist/types/config/index.d.ts.map +1 -1
- package/dist/types/context/index.d.ts +39 -0
- package/dist/types/context/index.d.ts.map +1 -0
- package/dist/types/contracts/index.d.ts +0 -2
- package/dist/types/contracts/index.d.ts.map +1 -1
- package/dist/types/contracts/notification-dispatch.d.ts +204 -86
- package/dist/types/contracts/notification-dispatch.d.ts.map +1 -1
- 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 +63 -11
- package/dist/types/express/auth.d.ts.map +1 -1
- package/dist/types/express/index.d.ts +3 -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/middleware.d.ts +86 -7
- package/dist/types/express/middleware.d.ts.map +1 -1
- package/dist/types/express/respond.d.ts +41 -0
- package/dist/types/express/respond.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 +7 -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/index.d.ts +30 -4
- package/dist/types/logging/index.d.ts.map +1 -1
- package/dist/types/logging/redact.d.ts +54 -0
- package/dist/types/logging/redact.d.ts.map +1 -0
- 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 +8 -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 +69 -38
- package/dist/types/messaging/staff-notifier.d.ts.map +1 -1
- package/dist/types/messaging/staff-recipients.d.ts +50 -0
- package/dist/types/messaging/staff-recipients.d.ts.map +1 -0
- 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 +62 -15
- package/dist/types/types/index.d.ts.map +1 -1
- package/dist/types/validation/index.d.ts +33 -2
- package/dist/types/validation/index.d.ts.map +1 -1
- package/package.json +3 -1
- package/dist/cjs/contracts/editorial-draft.js +0 -120
- package/dist/cjs/contracts/editorial-draft.js.map +0 -1
- package/dist/cjs/contracts/upload-import.js +0 -54
- package/dist/cjs/contracts/upload-import.js.map +0 -1
- package/dist/esm/contracts/editorial-draft.js +0 -117
- package/dist/esm/contracts/editorial-draft.js.map +0 -1
- package/dist/esm/contracts/upload-import.js +0 -51
- package/dist/esm/contracts/upload-import.js.map +0 -1
- package/dist/types/contracts/editorial-draft.d.ts +0 -610
- package/dist/types/contracts/editorial-draft.d.ts.map +0 -1
- package/dist/types/contracts/upload-import.d.ts +0 -96
- package/dist/types/contracts/upload-import.d.ts.map +0 -1
|
@@ -1,56 +1,78 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OutboxRelay = exports.DEFAULT_OUTBOX_TABLE = void 0;
|
|
4
|
-
exports.outboxRoutingKey = outboxRoutingKey;
|
|
5
4
|
exports.enqueueOutbox = enqueueOutbox;
|
|
5
|
+
const drizzle_orm_1 = require("drizzle-orm");
|
|
6
|
+
const table_names_js_1 = require("../database/table-names.js");
|
|
7
|
+
Object.defineProperty(exports, "DEFAULT_OUTBOX_TABLE", { enumerable: true, get: function () { return table_names_js_1.DEFAULT_OUTBOX_TABLE; } });
|
|
8
|
+
const tables_js_1 = require("../database/tables.js");
|
|
6
9
|
const index_js_1 = require("../logging/index.js");
|
|
10
|
+
const envelope_js_1 = require("./envelope.js");
|
|
7
11
|
const publisher_js_1 = require("./publisher.js");
|
|
8
|
-
exports.DEFAULT_OUTBOX_TABLE = 'event_outbox';
|
|
9
|
-
const COMMAND_PREFIX = 'command:';
|
|
10
|
-
/** Outbox `routing_key` column value: the event type, or `command:<name>` for commands. */
|
|
11
|
-
function outboxRoutingKey(envelope, kind = 'event') {
|
|
12
|
-
return kind === 'command' ? `${COMMAND_PREFIX}${envelope.type}` : envelope.type;
|
|
13
|
-
}
|
|
14
12
|
/**
|
|
15
|
-
* Transactional outbox:
|
|
16
|
-
*
|
|
17
|
-
*
|
|
13
|
+
* Transactional outbox: stages a message in the caller's transaction, so it is published if and only if
|
|
14
|
+
* the domain change commits. The {@link OutboxRelay} publishes it later with broker confirms. The
|
|
15
|
+
* correlation id (explicit, else the request context's) is stored with the row and sent as a header.
|
|
18
16
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
17
|
+
* @param tx The caller's open Drizzle transaction (or db handle for a single-statement change).
|
|
18
|
+
* @param routingKey Event or command name.
|
|
19
|
+
* @param data Payload fields.
|
|
20
|
+
* @param options Exchange, envelope meta and table.
|
|
21
|
+
* @returns The staged envelope.
|
|
22
|
+
* @example await db.transaction(async (tx) => { await repo.insert(ctx, row, { tx }); await enqueueOutbox(tx, 'thing.created', { thingId: row.id }); });
|
|
21
23
|
*/
|
|
22
|
-
async function enqueueOutbox(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
async function enqueueOutbox(tx, routingKey, data, options = {}) {
|
|
25
|
+
const table = options.table ?? tables_js_1.outbox;
|
|
26
|
+
const envelope = (0, envelope_js_1.buildEnvelope)(routingKey, data, options);
|
|
27
|
+
await tx.insert(table).values({
|
|
28
|
+
id: envelope.data.eventId,
|
|
29
|
+
routingKey,
|
|
30
|
+
exchange: options.exchange ?? 'events',
|
|
31
|
+
payload: envelope,
|
|
32
|
+
correlationId: (0, envelope_js_1.outgoingCorrelationId)(options) ?? null,
|
|
27
33
|
attempts: 0,
|
|
28
|
-
created_at: new Date(),
|
|
29
34
|
});
|
|
35
|
+
return envelope;
|
|
30
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Publishes pending outbox rows in id (= time) order. Each batch locks its rows with
|
|
39
|
+
* `FOR UPDATE SKIP LOCKED`, so several replicas can relay at once without double-publishing, and marks a
|
|
40
|
+
* row published only after the broker confirms it. Consumers still dedupe: a crash between confirm and
|
|
41
|
+
* commit re-sends the row.
|
|
42
|
+
*
|
|
43
|
+
* Failures: an unroutable command stays pending and the batch continues; a broker error stops the batch
|
|
44
|
+
* (keeping order) and the poll interval backs off exponentially until a batch succeeds.
|
|
45
|
+
*/
|
|
31
46
|
class OutboxRelay {
|
|
32
47
|
options;
|
|
33
48
|
timer = null;
|
|
34
49
|
running = null;
|
|
35
50
|
rerun = false;
|
|
51
|
+
failures = 0;
|
|
36
52
|
logger;
|
|
37
53
|
table;
|
|
38
54
|
constructor(options) {
|
|
39
55
|
this.options = options;
|
|
40
56
|
this.logger = options.logger ?? index_js_1.silentLogger;
|
|
41
|
-
this.table = options.table ??
|
|
57
|
+
this.table = options.table ?? tables_js_1.outbox;
|
|
42
58
|
}
|
|
59
|
+
/** Starts polling. */
|
|
43
60
|
start() {
|
|
44
61
|
if (this.timer)
|
|
45
62
|
return;
|
|
46
|
-
this.
|
|
47
|
-
void this.trigger();
|
|
63
|
+
this.schedule(0);
|
|
48
64
|
}
|
|
65
|
+
/** Stops polling (an in-flight batch finishes). */
|
|
49
66
|
stop() {
|
|
50
67
|
if (this.timer)
|
|
51
|
-
|
|
68
|
+
clearTimeout(this.timer);
|
|
52
69
|
this.timer = null;
|
|
53
70
|
}
|
|
71
|
+
/** Delay before the next poll: the interval, doubled per consecutive failed batch, capped. */
|
|
72
|
+
nextDelayMs() {
|
|
73
|
+
const interval = this.options.intervalMs ?? 2000;
|
|
74
|
+
return Math.min(this.options.maxBackoffMs ?? 60_000, interval * 2 ** this.failures);
|
|
75
|
+
}
|
|
54
76
|
/** Publishes pending rows now. Concurrent triggers coalesce into one follow-up run. */
|
|
55
77
|
async trigger() {
|
|
56
78
|
if (this.running) {
|
|
@@ -68,68 +90,88 @@ class OutboxRelay {
|
|
|
68
90
|
}
|
|
69
91
|
} while (this.rerun);
|
|
70
92
|
}
|
|
71
|
-
/** One batch.
|
|
93
|
+
/** One batch. */
|
|
72
94
|
async flush() {
|
|
73
|
-
const {
|
|
74
|
-
let
|
|
95
|
+
const metrics = { published: 0, failed: 0, lagMs: 0 };
|
|
96
|
+
let brokerDown = false;
|
|
75
97
|
try {
|
|
76
|
-
await
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
.
|
|
80
|
-
.whereNull('published_at')
|
|
81
|
-
.orderBy('id', 'asc')
|
|
82
|
-
.limit(this.options.batchSize ?? 100)
|
|
83
|
-
.forUpdate()
|
|
84
|
-
.skipLocked();
|
|
98
|
+
await this.options.db.transaction(async (tx) => {
|
|
99
|
+
const rows = await this.lockPending(tx);
|
|
100
|
+
if (rows[0])
|
|
101
|
+
metrics.lagMs = Math.max(0, Date.now() - new Date(rows[0].createdAt).getTime());
|
|
85
102
|
for (const row of rows) {
|
|
86
|
-
const envelope = (typeof row.payload === 'string' ? JSON.parse(row.payload) : row.payload);
|
|
87
103
|
try {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
await bus.publishEnvelope(envelope);
|
|
92
|
-
await trx(this.table).where({ id: row.id }).update({ published_at: new Date(), last_error: null });
|
|
93
|
-
published += 1;
|
|
104
|
+
await this.publish(row);
|
|
105
|
+
await tx.update(this.table).set({ publishedAt: new Date(), lastError: null }).where((0, drizzle_orm_1.eq)(this.table.id, row.id));
|
|
106
|
+
metrics.published += 1;
|
|
94
107
|
}
|
|
95
108
|
catch (error) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
109
|
+
metrics.failed += 1;
|
|
110
|
+
await tx.update(this.table)
|
|
111
|
+
.set({ attempts: (0, drizzle_orm_1.sql) `${this.table.attempts} + 1`, lastError: String(error?.message ?? error).slice(0, 500) })
|
|
112
|
+
.where((0, drizzle_orm_1.eq)(this.table.id, row.id));
|
|
113
|
+
// Unroutable: only this command's owner is missing. Keep the row and carry on, so one
|
|
114
|
+
// undeclared queue never holds back every other message behind it.
|
|
99
115
|
if ((0, publisher_js_1.isUnroutableError)(error)) {
|
|
100
116
|
this.reportUnroutable(row, error);
|
|
101
117
|
continue;
|
|
102
118
|
}
|
|
103
|
-
// Broker down: stop this batch; the next
|
|
119
|
+
// Broker down: stop this batch; the next poll retries in order.
|
|
120
|
+
brokerDown = true;
|
|
104
121
|
break;
|
|
105
122
|
}
|
|
106
123
|
}
|
|
107
124
|
});
|
|
108
125
|
}
|
|
109
126
|
catch (error) {
|
|
127
|
+
brokerDown = true;
|
|
110
128
|
this.logger.error('Outbox relay batch failed', { error: error.message });
|
|
111
129
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
130
|
+
this.failures = brokerDown ? Math.min(this.failures + 1, 16) : 0;
|
|
131
|
+
if (metrics.published)
|
|
132
|
+
this.logger.debug('Outbox relayed', { published: metrics.published });
|
|
133
|
+
this.options.onMetrics?.(metrics);
|
|
134
|
+
return metrics;
|
|
135
|
+
}
|
|
136
|
+
/** Deletes published rows older than `olderThanDays`; returns how many were removed. */
|
|
137
|
+
async prune(olderThanDays = 7) {
|
|
138
|
+
const cutoff = new Date(Date.now() - olderThanDays * 86_400_000);
|
|
139
|
+
const [result] = await this.options.db.delete(this.table)
|
|
140
|
+
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.isNotNull)(this.table.publishedAt), (0, drizzle_orm_1.lt)(this.table.publishedAt, cutoff)));
|
|
141
|
+
return result?.affectedRows ?? 0;
|
|
142
|
+
}
|
|
143
|
+
lockPending(tx) {
|
|
144
|
+
return tx.select().from(this.table)
|
|
145
|
+
.where((0, drizzle_orm_1.isNull)(this.table.publishedAt))
|
|
146
|
+
.orderBy((0, drizzle_orm_1.asc)(this.table.id))
|
|
147
|
+
.limit(this.options.batchSize ?? 100)
|
|
148
|
+
.for('update', { skipLocked: true });
|
|
149
|
+
}
|
|
150
|
+
publish(row) {
|
|
151
|
+
const envelope = (typeof row.payload === 'string' ? JSON.parse(row.payload) : row.payload);
|
|
152
|
+
const options = row.correlationId ? { correlationId: row.correlationId } : {};
|
|
153
|
+
return row.exchange === 'commands'
|
|
154
|
+
? this.options.bus.sendEnvelope(envelope, options)
|
|
155
|
+
: this.options.bus.publishEnvelope(envelope, options);
|
|
156
|
+
}
|
|
157
|
+
schedule(delayMs) {
|
|
158
|
+
this.timer = setTimeout(() => {
|
|
159
|
+
void this.trigger().finally(() => { if (this.timer)
|
|
160
|
+
this.schedule(this.nextDelayMs()); });
|
|
161
|
+
}, delayMs);
|
|
115
162
|
}
|
|
116
163
|
/**
|
|
117
164
|
* Logged on the 1st, 2nd, 4th, 8th… failure of a row (the attempt count before this one is in `attempts`),
|
|
118
|
-
* so a queue that stays missing for hours is still visible without a line every
|
|
165
|
+
* so a queue that stays missing for hours is still visible without a line every poll.
|
|
119
166
|
*/
|
|
120
167
|
reportUnroutable(row, error) {
|
|
121
168
|
const attempt = Number(row.attempts ?? 0) + 1;
|
|
122
169
|
if ((attempt & (attempt - 1)) !== 0)
|
|
123
170
|
return;
|
|
124
171
|
this.logger.error('Outbox command is unroutable: its consumer queue is not declared; will retry', {
|
|
125
|
-
eventId: row.id, routingKey: row.
|
|
172
|
+
eventId: row.id, routingKey: row.routingKey, target: error.routingKey, attempt,
|
|
126
173
|
});
|
|
127
174
|
}
|
|
128
|
-
/** Deletes published rows older than `olderThanDays`. */
|
|
129
|
-
async prune(olderThanDays = 7) {
|
|
130
|
-
const cutoff = new Date(Date.now() - olderThanDays * 86_400_000);
|
|
131
|
-
return this.options.knex(this.table).whereNotNull('published_at').andWhere('published_at', '<', cutoff).delete();
|
|
132
|
-
}
|
|
133
175
|
}
|
|
134
176
|
exports.OutboxRelay = OutboxRelay;
|
|
135
177
|
//# sourceMappingURL=outbox.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outbox.js","sourceRoot":"","sources":["../../../src/messaging/outbox.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"outbox.js","sourceRoot":"","sources":["../../../src/messaging/outbox.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.NoopMessageBus = exports.MessagePublisher = exports.UnroutableMessageError = void 0;
|
|
3
|
+
exports.NoopMessageBus = exports.MessagePublisher = exports.MAX_DELAY_MS = exports.UnroutableMessageError = void 0;
|
|
4
4
|
exports.isUnroutableError = isUnroutableError;
|
|
5
5
|
const errors_js_1 = require("../common/errors.js");
|
|
6
6
|
const id_js_1 = require("../common/id.js");
|
|
7
7
|
const index_js_1 = require("../logging/index.js");
|
|
8
8
|
const envelope_js_1 = require("./envelope.js");
|
|
9
|
+
const topology_js_1 = require("./topology.js");
|
|
9
10
|
/**
|
|
10
11
|
* The broker accepted a `mandatory` message but no queue was bound to receive it (the consumer has never
|
|
11
12
|
* started, or its binding is wrong). Retryable: the message is not lost — the caller (or the outbox relay)
|
|
@@ -21,16 +22,20 @@ class UnroutableMessageError extends errors_js_1.ServiceUnavailableError {
|
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
exports.UnroutableMessageError = UnroutableMessageError;
|
|
25
|
+
/** True when `error` is an {@link UnroutableMessageError} (retryable; the target queue is missing). */
|
|
24
26
|
function isUnroutableError(error) {
|
|
25
27
|
return error instanceof UnroutableMessageError;
|
|
26
28
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
/** Largest delay `scheduleDelayed` accepts (AMQP per-message TTL is a 32-bit value). */
|
|
30
|
+
exports.MAX_DELAY_MS = 2 ** 31 - 1;
|
|
31
|
+
const envelopeProps = (envelope, mandatory, options) => ({
|
|
32
|
+
mandatory, messageId: envelope.data.eventId, correlationId: (0, envelope_js_1.outgoingCorrelationId)(options), type: envelope.routingKey,
|
|
29
33
|
});
|
|
30
34
|
/**
|
|
31
35
|
* Event and command publisher on a confirm channel: `publish()` resolves only once the broker
|
|
32
36
|
* has persisted the message, and rejects with 503 when the broker is unreachable so callers
|
|
33
|
-
* (or the outbox relay) can retry.
|
|
37
|
+
* (or the outbox relay) can retry. Every message is persistent JSON with `messageId` = event id and the
|
|
38
|
+
* correlation id in the `x-correlation-id` header.
|
|
34
39
|
*
|
|
35
40
|
* Commands and direct queue sends are `mandatory`: a command has exactly one owner, so one that no
|
|
36
41
|
* queue receives is a fault, not a no-op. The broker returns it (`basic.return`, always before its
|
|
@@ -43,6 +48,8 @@ class MessagePublisher {
|
|
|
43
48
|
logger;
|
|
44
49
|
/** Message ids the broker returned as unroutable, awaiting their confirm. */
|
|
45
50
|
returned = new Set();
|
|
51
|
+
/** Delay queues already declared, per live channel (a reconnect gets a new channel). */
|
|
52
|
+
declaredDelayQueues = new WeakMap();
|
|
46
53
|
constructor(options) {
|
|
47
54
|
this.options = options;
|
|
48
55
|
this.logger = options.logger ?? index_js_1.silentLogger;
|
|
@@ -59,46 +66,86 @@ class MessagePublisher {
|
|
|
59
66
|
});
|
|
60
67
|
}, { confirm: true });
|
|
61
68
|
}
|
|
62
|
-
async publish(
|
|
63
|
-
const envelope = (0, envelope_js_1.buildEnvelope)(
|
|
64
|
-
await this.publishEnvelope(envelope);
|
|
69
|
+
async publish(routingKey, data, meta = {}) {
|
|
70
|
+
const envelope = (0, envelope_js_1.buildEnvelope)(routingKey, data, meta);
|
|
71
|
+
await this.publishEnvelope(envelope, meta);
|
|
65
72
|
return envelope;
|
|
66
73
|
}
|
|
67
|
-
async publishEnvelope(envelope) {
|
|
68
|
-
await this.write(this.options.topology.eventsExchange, envelope.
|
|
74
|
+
async publishEnvelope(envelope, options) {
|
|
75
|
+
await this.write(this.options.topology.eventsExchange, envelope.routingKey, envelope, envelopeProps(envelope, false, options));
|
|
69
76
|
}
|
|
70
77
|
async send(command, data, meta = {}) {
|
|
71
|
-
const envelope = (0, envelope_js_1.buildEnvelope)(command, data,
|
|
72
|
-
await this.sendEnvelope(envelope);
|
|
78
|
+
const envelope = (0, envelope_js_1.buildEnvelope)(command, data, meta);
|
|
79
|
+
await this.sendEnvelope(envelope, meta);
|
|
73
80
|
return envelope;
|
|
74
81
|
}
|
|
75
|
-
async sendEnvelope(envelope) {
|
|
76
|
-
|
|
82
|
+
async sendEnvelope(envelope, options) {
|
|
83
|
+
const { topology } = this.options;
|
|
84
|
+
await this.write(topology.commandsExchange, topology.queue(envelope.routingKey), envelope, envelopeProps(envelope, true, options));
|
|
77
85
|
}
|
|
78
86
|
/**
|
|
79
87
|
* Sends `payload` as-is (no envelope) to `queue` through the default exchange. The queue is the
|
|
80
88
|
* consumer's to declare, so it is not asserted here; a missing queue rejects as unroutable.
|
|
81
89
|
*/
|
|
82
90
|
async sendToQueue(queue, payload, options = {}) {
|
|
83
|
-
await this.write('', queue, payload, { mandatory: true, messageId: options.messageId ?? (0, id_js_1.newId)(), correlationId: options
|
|
91
|
+
await this.write('', queue, payload, { mandatory: true, messageId: options.messageId ?? (0, id_js_1.newId)(), correlationId: (0, envelope_js_1.outgoingCorrelationId)(options) });
|
|
84
92
|
}
|
|
85
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Parks `payload` in a delay queue with a per-message TTL; when it expires the broker dead-letters it
|
|
95
|
+
* into `queue` (MSG-4). Delay queues are bucketed by power of two (see `delayQueueName`). The target
|
|
96
|
+
* queue must exist when the TTL fires, or the broker drops the message.
|
|
97
|
+
*
|
|
98
|
+
* @throws RangeError when `delayMs` is not an integer in 1 … {@link MAX_DELAY_MS}.
|
|
99
|
+
*/
|
|
100
|
+
async scheduleDelayed(queue, payload, delayMs, options = {}) {
|
|
101
|
+
if (!Number.isInteger(delayMs) || delayMs < 1 || delayMs > exports.MAX_DELAY_MS) {
|
|
102
|
+
throw new RangeError('delayMs must be an integer between 1 and 2^31-1');
|
|
103
|
+
}
|
|
104
|
+
const delayQueue = await this.declareDelayQueue(queue, delayMs);
|
|
105
|
+
await this.write('', delayQueue, payload, {
|
|
106
|
+
mandatory: true, messageId: options.messageId ?? (0, id_js_1.newId)(), correlationId: (0, envelope_js_1.outgoingCorrelationId)(options), expiration: String(delayMs),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
async declareDelayQueue(queue, delayMs) {
|
|
110
|
+
const channel = this.channel();
|
|
111
|
+
const name = (0, topology_js_1.delayQueueName)(queue, delayMs);
|
|
112
|
+
const declared = this.declaredDelayQueues.get(channel) ?? new Set();
|
|
113
|
+
if (!declared.has(name)) {
|
|
114
|
+
await channel.assertQueue(name, { durable: true, arguments: { 'x-dead-letter-exchange': '', 'x-dead-letter-routing-key': queue } });
|
|
115
|
+
declared.add(name);
|
|
116
|
+
this.declaredDelayQueues.set(channel, declared);
|
|
117
|
+
}
|
|
118
|
+
return name;
|
|
119
|
+
}
|
|
120
|
+
channel() {
|
|
86
121
|
const channel = this.handle?.current();
|
|
87
|
-
if (!channel)
|
|
88
|
-
|
|
122
|
+
if (!channel)
|
|
123
|
+
throw new errors_js_1.ServiceUnavailableError('Message broker unavailable');
|
|
124
|
+
return channel;
|
|
125
|
+
}
|
|
126
|
+
write(exchange, routingKey, body, props) {
|
|
127
|
+
let channel;
|
|
128
|
+
try {
|
|
129
|
+
channel = this.channel();
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
return Promise.reject(error);
|
|
89
133
|
}
|
|
90
|
-
const { messageId } = props;
|
|
134
|
+
const { messageId, correlationId } = props;
|
|
135
|
+
const publishOptions = {
|
|
136
|
+
persistent: true,
|
|
137
|
+
mandatory: props.mandatory,
|
|
138
|
+
contentType: 'application/json',
|
|
139
|
+
messageId,
|
|
140
|
+
correlationId,
|
|
141
|
+
type: props.type,
|
|
142
|
+
expiration: props.expiration,
|
|
143
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
144
|
+
appId: this.options.source,
|
|
145
|
+
headers: correlationId ? { [envelope_js_1.CORRELATION_HEADER]: correlationId } : {},
|
|
146
|
+
};
|
|
91
147
|
return new Promise((resolve, reject) => {
|
|
92
|
-
channel.publish(exchange, routingKey, Buffer.from(JSON.stringify(body)), {
|
|
93
|
-
persistent: true,
|
|
94
|
-
mandatory: props.mandatory,
|
|
95
|
-
contentType: 'application/json',
|
|
96
|
-
messageId,
|
|
97
|
-
correlationId: props.correlationId,
|
|
98
|
-
type: props.type,
|
|
99
|
-
timestamp: Math.floor(Date.now() / 1000),
|
|
100
|
-
appId: this.options.source,
|
|
101
|
-
}, (error) => {
|
|
148
|
+
channel.publish(exchange, routingKey, Buffer.from(JSON.stringify(body)), publishOptions, (error) => {
|
|
102
149
|
// The broker sends `basic.return` before the confirm, so a returned message is already recorded here.
|
|
103
150
|
const unroutable = this.returned.delete(messageId);
|
|
104
151
|
if (error) {
|
|
@@ -119,31 +166,32 @@ class MessagePublisher {
|
|
|
119
166
|
exports.MessagePublisher = MessagePublisher;
|
|
120
167
|
/** Stand-in when messaging is disabled (local tooling, tests): logs and drops. */
|
|
121
168
|
class NoopMessageBus {
|
|
122
|
-
source;
|
|
123
169
|
logger;
|
|
124
|
-
constructor(
|
|
125
|
-
this.source = source;
|
|
170
|
+
constructor(logger = index_js_1.silentLogger) {
|
|
126
171
|
this.logger = logger;
|
|
127
172
|
}
|
|
128
|
-
async publish(
|
|
129
|
-
const envelope = (0, envelope_js_1.buildEnvelope)(
|
|
130
|
-
this.logger.debug('Messaging disabled; event dropped', {
|
|
173
|
+
async publish(routingKey, data, meta = {}) {
|
|
174
|
+
const envelope = (0, envelope_js_1.buildEnvelope)(routingKey, data, meta);
|
|
175
|
+
this.logger.debug('Messaging disabled; event dropped', { routingKey, eventId: envelope.data.eventId });
|
|
131
176
|
return envelope;
|
|
132
177
|
}
|
|
133
178
|
async publishEnvelope(envelope) {
|
|
134
|
-
this.logger.debug('Messaging disabled; event dropped', {
|
|
179
|
+
this.logger.debug('Messaging disabled; event dropped', { routingKey: envelope.routingKey, eventId: envelope.data.eventId });
|
|
135
180
|
}
|
|
136
181
|
async send(command, data, meta = {}) {
|
|
137
|
-
const envelope = (0, envelope_js_1.buildEnvelope)(command, data,
|
|
138
|
-
this.logger.debug('Messaging disabled; command dropped', { command, eventId: envelope.eventId });
|
|
182
|
+
const envelope = (0, envelope_js_1.buildEnvelope)(command, data, meta);
|
|
183
|
+
this.logger.debug('Messaging disabled; command dropped', { command, eventId: envelope.data.eventId });
|
|
139
184
|
return envelope;
|
|
140
185
|
}
|
|
141
186
|
async sendEnvelope(envelope) {
|
|
142
|
-
this.logger.debug('Messaging disabled; command dropped', { command: envelope.
|
|
187
|
+
this.logger.debug('Messaging disabled; command dropped', { command: envelope.routingKey, eventId: envelope.data.eventId });
|
|
143
188
|
}
|
|
144
189
|
async sendToQueue(queue) {
|
|
145
190
|
this.logger.debug('Messaging disabled; queue message dropped', { queue });
|
|
146
191
|
}
|
|
192
|
+
async scheduleDelayed(queue, _payload, delayMs) {
|
|
193
|
+
this.logger.debug('Messaging disabled; delayed message dropped', { queue, delayMs });
|
|
194
|
+
}
|
|
147
195
|
}
|
|
148
196
|
exports.NoopMessageBus = NoopMessageBus;
|
|
149
197
|
//# sourceMappingURL=publisher.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publisher.js","sourceRoot":"","sources":["../../../src/messaging/publisher.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"publisher.js","sourceRoot":"","sources":["../../../src/messaging/publisher.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,35 +1,50 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
3
|
+
exports.StaffNotifier = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Staff alerts (messaging layer helper): stage one notification dispatch per recipient of a
|
|
6
|
+
* permission through the caller's outbox. App vocabulary (category, channels, command name) is
|
|
7
|
+
* passed in as options; nothing app-specific is assumed.
|
|
8
|
+
*/
|
|
5
9
|
const notification_dispatch_js_1 = require("../contracts/notification-dispatch.js");
|
|
6
10
|
const index_js_1 = require("../logging/index.js");
|
|
7
|
-
/** Recipients from configuration (env), keyed by the permission an alert targets. */
|
|
8
|
-
class ConfiguredStaffRecipients {
|
|
9
|
-
byPermission;
|
|
10
|
-
constructor(byPermission) {
|
|
11
|
-
this.byPermission = byPermission;
|
|
12
|
-
}
|
|
13
|
-
async resolve(permission) {
|
|
14
|
-
return [...(this.byPermission[permission] ?? [])];
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
exports.ConfiguredStaffRecipients = ConfiguredStaffRecipients;
|
|
18
11
|
/**
|
|
19
|
-
* Stages one
|
|
20
|
-
* alert is sent if and only if the state change that raised it commits. Never sends
|
|
12
|
+
* Stages one dispatch command per recipient through the outbox, in the caller's transaction, so an
|
|
13
|
+
* alert is sent if and only if the state change that raised it commits. Never sends anything itself.
|
|
21
14
|
* An alert that resolves to nobody is logged as a warning (an unconfigured list must not fail silently).
|
|
15
|
+
* Staff alerts always bypass recipient preferences and record the target permission.
|
|
16
|
+
*
|
|
17
|
+
* @typeParam E The service's transaction/executor type.
|
|
18
|
+
* @typeParam C The app's dispatch categories.
|
|
19
|
+
* @typeParam Ch The app's dispatch channels.
|
|
20
|
+
* @example
|
|
21
|
+
* const notifier = new StaffNotifier(outbox, new ConfiguredStaffRecipients({ 'jobs.manage': config.opsEmails }),
|
|
22
|
+
* { category: 'staff', defaultChannels: ['email'], logger });
|
|
23
|
+
* await notifier.alert(tx, { template: 'job-failed', permission: 'jobs.manage', data: { reference } }, { correlationId });
|
|
22
24
|
*/
|
|
23
25
|
class StaffNotifier {
|
|
24
26
|
outbox;
|
|
25
27
|
recipients;
|
|
28
|
+
category;
|
|
29
|
+
defaultChannels;
|
|
30
|
+
command;
|
|
26
31
|
logger;
|
|
27
|
-
constructor(outbox, recipients,
|
|
32
|
+
constructor(outbox, recipients, options) {
|
|
28
33
|
this.outbox = outbox;
|
|
29
34
|
this.recipients = recipients;
|
|
30
|
-
this.
|
|
35
|
+
this.category = options.category;
|
|
36
|
+
this.defaultChannels = options.defaultChannels;
|
|
37
|
+
this.command = options.command ?? notification_dispatch_js_1.NOTIFICATION_DISPATCH;
|
|
38
|
+
this.logger = options.logger ?? index_js_1.silentLogger;
|
|
31
39
|
}
|
|
32
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* Stage the alert for every recipient of its permission.
|
|
42
|
+
*
|
|
43
|
+
* @param executor The caller's open transaction; the commands commit or roll back with it.
|
|
44
|
+
* @param alert Template, permission, variables and optional channels.
|
|
45
|
+
* @param meta Correlation id for tracing; the actor is always the system.
|
|
46
|
+
* @returns How many commands were staged (0 when nobody is configured).
|
|
47
|
+
*/
|
|
33
48
|
async alert(executor, alert, meta = {}) {
|
|
34
49
|
const targets = await this.recipients.resolve(alert.permission);
|
|
35
50
|
if (targets.length === 0) {
|
|
@@ -39,40 +54,17 @@ class StaffNotifier {
|
|
|
39
54
|
for (const recipient of targets) {
|
|
40
55
|
const dispatch = {
|
|
41
56
|
template: alert.template,
|
|
42
|
-
category:
|
|
57
|
+
category: this.category,
|
|
43
58
|
recipient,
|
|
44
|
-
channels: alert.channels ??
|
|
59
|
+
channels: [...(alert.channels ?? this.defaultChannels)],
|
|
45
60
|
data: alert.data,
|
|
46
61
|
bypassPreferences: true,
|
|
47
62
|
targetPermission: alert.permission,
|
|
48
63
|
};
|
|
49
|
-
await this.outbox.command(executor,
|
|
64
|
+
await this.outbox.command(executor, this.command, dispatch, { ...meta, actorUserId: null });
|
|
50
65
|
}
|
|
51
66
|
return targets.length;
|
|
52
67
|
}
|
|
53
68
|
}
|
|
54
69
|
exports.StaffNotifier = StaffNotifier;
|
|
55
|
-
const emailSchema = zod_1.z.string().email().max(notification_dispatch_js_1.DISPATCH_LIMITS.email);
|
|
56
|
-
/**
|
|
57
|
-
* A comma-separated staff address list from env (`"a@x.io, b@y.io"`): trimmed, blanks dropped,
|
|
58
|
-
* de-duplicated case-insensitively. An invalid address fails validation, so a typo is caught at boot
|
|
59
|
-
* instead of dead-lettering in notification-service when the alert finally fires.
|
|
60
|
-
*/
|
|
61
|
-
exports.staffEmailListSchema = zod_1.z
|
|
62
|
-
.string()
|
|
63
|
-
.optional()
|
|
64
|
-
.transform((csv) => {
|
|
65
|
-
const seen = new Set();
|
|
66
|
-
return (csv ?? '')
|
|
67
|
-
.split(',')
|
|
68
|
-
.map((entry) => entry.trim())
|
|
69
|
-
.filter((email) => email && !seen.has(email.toLowerCase()) && seen.add(email.toLowerCase()));
|
|
70
|
-
})
|
|
71
|
-
.superRefine((emails, ctx) => {
|
|
72
|
-
for (const email of emails) {
|
|
73
|
-
if (!emailSchema.safeParse(email).success)
|
|
74
|
-
ctx.addIssue({ code: zod_1.z.ZodIssueCode.custom, message: `Not a valid email address: ${email}` });
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
.transform((emails) => emails.map((email) => ({ email })));
|
|
78
70
|
//# sourceMappingURL=staff-notifier.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"staff-notifier.js","sourceRoot":"","sources":["../../../src/messaging/staff-notifier.ts"],"names":[],"mappings":";;;AAAA
|
|
1
|
+
{"version":3,"file":"staff-notifier.js","sourceRoot":"","sources":["../../../src/messaging/staff-notifier.ts"],"names":[],"mappings":";;;AAAA;;;;GAIG;AACH,oFAI+C;AAC/C,kDAAmD;AAsDnD;;;;;;;;;;;;;GAaG;AACH,MAAa,aAAa;IAOL;IACA;IAPF,QAAQ,CAAI;IACZ,eAAe,CAAyB;IACxC,OAAO,CAAS;IAChB,MAAM,CAAS;IAEhC,YACmB,MAAwB,EACxB,UAAkC,EACnD,OAAoC;QAFnB,WAAM,GAAN,MAAM,CAAkB;QACxB,eAAU,GAAV,UAAU,CAAwB;QAGnD,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;QAC/C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,gDAAqB,CAAC;QACxD,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,uBAAY,CAAC;IAC/C,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,KAAK,CAAC,QAAW,EAAE,KAAqB,EAAE,OAAuB,EAAE;QACvE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAChE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,oDAAoD,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;YACnI,OAAO,CAAC,CAAC;QACX,CAAC;QACD,KAAK,MAAM,SAAS,IAAI,OAAO,EAAE,CAAC;YAChC,MAAM,QAAQ,GAAuC;gBACnD,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,SAAS;gBACT,QAAQ,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,eAAe,CAAC,CAAC;gBACvD,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,iBAAiB,EAAE,IAAI;gBACvB,gBAAgB,EAAE,KAAK,CAAC,UAAU;aACnC,CAAC;YACF,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,GAAG,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9F,CAAC;QACD,OAAO,OAAO,CAAC,MAAM,CAAC;IACxB,CAAC;CACF;AA7CD,sCA6CC"}
|