@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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publisher.d.ts","sourceRoot":"","sources":["../../../src/messaging/publisher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAG9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,iBAAiB,CAAC;AACrE,OAAO,
|
|
1
|
+
{"version":3,"file":"publisher.d.ts","sourceRoot":"","sources":["../../../src/messaging/publisher.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,56 +1,87 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Staff alerts (messaging layer helper): stage one notification dispatch per recipient of a
|
|
3
|
+
* permission through the caller's outbox. App vocabulary (category, channels, command name) is
|
|
4
|
+
* passed in as options; nothing app-specific is assumed.
|
|
5
|
+
*/
|
|
6
|
+
import { type DispatchValue } from '../contracts/notification-dispatch.js';
|
|
3
7
|
import type { Logger } from '../types/index.js';
|
|
4
8
|
import type { EnvelopeMeta } from './envelope.js';
|
|
5
|
-
|
|
9
|
+
import type { StaffRecipientResolver } from './staff-recipients.js';
|
|
10
|
+
/**
|
|
11
|
+
* The slice of a service's outbox the notifier needs.
|
|
12
|
+
*
|
|
13
|
+
* @typeParam E The service's transaction/executor type.
|
|
14
|
+
* @example const outbox: CommandOutbox<DbExecutor> = { command: (tx, name, data, meta) => enqueueOutbox(tx, name, data, { ...meta, exchange: 'commands' }).then(() => undefined) };
|
|
15
|
+
*/
|
|
6
16
|
export interface CommandOutbox<E> {
|
|
7
|
-
command<T>(executor: E, command: string, data: T, meta?: EnvelopeMeta): Promise<void>;
|
|
17
|
+
command<T extends object>(executor: E, command: string, data: T, meta?: EnvelopeMeta): Promise<void>;
|
|
8
18
|
}
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export
|
|
15
|
-
private readonly byPermission;
|
|
16
|
-
constructor(byPermission: Readonly<Record<string, readonly DispatchRecipient[]>>);
|
|
17
|
-
resolve(permission: string): Promise<DispatchRecipient[]>;
|
|
18
|
-
}
|
|
19
|
-
export interface StaffAlert {
|
|
19
|
+
/**
|
|
20
|
+
* One operational alert to the staff who hold a permission.
|
|
21
|
+
*
|
|
22
|
+
* @example { template: 'job-failed', permission: 'jobs.manage', data: { reference: 'J-1' } }
|
|
23
|
+
*/
|
|
24
|
+
export interface StaffAlert<Ch extends string = string> {
|
|
20
25
|
/** Seeded notification template slug. */
|
|
21
26
|
template: string;
|
|
22
|
-
/** The permission its recipients hold (e.g. `
|
|
27
|
+
/** The permission its recipients hold (e.g. `reports.manage`). */
|
|
23
28
|
permission: string;
|
|
24
29
|
/** Template variables. */
|
|
25
30
|
data: Record<string, DispatchValue>;
|
|
26
|
-
/**
|
|
27
|
-
channels?:
|
|
31
|
+
/** Channels for this alert (at least one); defaults to the notifier's `defaultChannels`. */
|
|
32
|
+
channels?: [Ch, ...Ch[]];
|
|
28
33
|
}
|
|
29
|
-
/** Tracing for the staged commands; the actor is always the system. */
|
|
30
|
-
export type StaffAlertMeta = Pick<EnvelopeMeta, 'correlationId' | 'aggregate'>;
|
|
31
34
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
35
|
+
* Tracing for the staged commands; the actor is always the system.
|
|
36
|
+
*
|
|
37
|
+
* @example { correlationId: correlationId(req) }
|
|
38
|
+
*/
|
|
39
|
+
export type StaffAlertMeta = Pick<EnvelopeMeta, 'correlationId'>;
|
|
40
|
+
/**
|
|
41
|
+
* How the notifier stages commands. All app vocabulary is passed in; nothing is assumed.
|
|
42
|
+
*
|
|
43
|
+
* @example { category: 'staff', defaultChannels: ['email'], logger }
|
|
44
|
+
*/
|
|
45
|
+
export interface StaffNotifierOptions<C extends string, Ch extends string> {
|
|
46
|
+
/** The dispatch category for staff alerts in this app (e.g. `staff`). */
|
|
47
|
+
category: C;
|
|
48
|
+
/** Channels used when an alert names none. Staff addresses usually have no account, so typically `['email']`. */
|
|
49
|
+
defaultChannels: [Ch, ...Ch[]];
|
|
50
|
+
/** Command name; defaults to {@link NOTIFICATION_DISPATCH}. */
|
|
51
|
+
command?: string;
|
|
52
|
+
/** Warns when an alert resolves to no recipients; silent when omitted. */
|
|
53
|
+
logger?: Logger;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Stages one dispatch command per recipient through the outbox, in the caller's transaction, so an
|
|
57
|
+
* alert is sent if and only if the state change that raised it commits. Never sends anything itself.
|
|
34
58
|
* An alert that resolves to nobody is logged as a warning (an unconfigured list must not fail silently).
|
|
59
|
+
* Staff alerts always bypass recipient preferences and record the target permission.
|
|
60
|
+
*
|
|
61
|
+
* @typeParam E The service's transaction/executor type.
|
|
62
|
+
* @typeParam C The app's dispatch categories.
|
|
63
|
+
* @typeParam Ch The app's dispatch channels.
|
|
64
|
+
* @example
|
|
65
|
+
* const notifier = new StaffNotifier(outbox, new ConfiguredStaffRecipients({ 'jobs.manage': config.opsEmails }),
|
|
66
|
+
* { category: 'staff', defaultChannels: ['email'], logger });
|
|
67
|
+
* await notifier.alert(tx, { template: 'job-failed', permission: 'jobs.manage', data: { reference } }, { correlationId });
|
|
35
68
|
*/
|
|
36
|
-
export declare class StaffNotifier<E> {
|
|
69
|
+
export declare class StaffNotifier<E, C extends string = string, Ch extends string = string> {
|
|
37
70
|
private readonly outbox;
|
|
38
71
|
private readonly recipients;
|
|
72
|
+
private readonly category;
|
|
73
|
+
private readonly defaultChannels;
|
|
74
|
+
private readonly command;
|
|
39
75
|
private readonly logger;
|
|
40
|
-
constructor(outbox: CommandOutbox<E>, recipients: StaffRecipientResolver,
|
|
41
|
-
/**
|
|
42
|
-
|
|
76
|
+
constructor(outbox: CommandOutbox<E>, recipients: StaffRecipientResolver, options: StaffNotifierOptions<C, Ch>);
|
|
77
|
+
/**
|
|
78
|
+
* Stage the alert for every recipient of its permission.
|
|
79
|
+
*
|
|
80
|
+
* @param executor The caller's open transaction; the commands commit or roll back with it.
|
|
81
|
+
* @param alert Template, permission, variables and optional channels.
|
|
82
|
+
* @param meta Correlation id for tracing; the actor is always the system.
|
|
83
|
+
* @returns How many commands were staged (0 when nobody is configured).
|
|
84
|
+
*/
|
|
85
|
+
alert(executor: E, alert: StaffAlert<Ch>, meta?: StaffAlertMeta): Promise<number>;
|
|
43
86
|
}
|
|
44
|
-
/**
|
|
45
|
-
* A comma-separated staff address list from env (`"a@x.io, b@y.io"`): trimmed, blanks dropped,
|
|
46
|
-
* de-duplicated case-insensitively. An invalid address fails validation, so a typo is caught at boot
|
|
47
|
-
* instead of dead-lettering in notification-service when the alert finally fires.
|
|
48
|
-
*/
|
|
49
|
-
export declare const staffEmailListSchema: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodOptional<z.ZodString>, string[], string | undefined>, string[], string | undefined>, {
|
|
50
|
-
email: string;
|
|
51
|
-
name?: string | undefined;
|
|
52
|
-
userId?: string | null | undefined;
|
|
53
|
-
locale?: string | null | undefined;
|
|
54
|
-
timezone?: string | null | undefined;
|
|
55
|
-
}[], string | undefined>;
|
|
56
87
|
//# sourceMappingURL=staff-notifier.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"staff-notifier.d.ts","sourceRoot":"","sources":["../../../src/messaging/staff-notifier.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"staff-notifier.d.ts","sourceRoot":"","sources":["../../../src/messaging/staff-notifier.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAEL,KAAK,aAAa,EAEnB,MAAM,uCAAuC,CAAC;AAE/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtG;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM;IACpD,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,UAAU,EAAE,MAAM,CAAC;IACnB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACpC,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;AAEjE;;;;GAIG;AACH,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,MAAM,EAAE,EAAE,SAAS,MAAM;IACvE,yEAAyE;IACzE,QAAQ,EAAE,CAAC,CAAC;IACZ,iHAAiH;IACjH,eAAe,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IAC/B,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,aAAa,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,EAAE,SAAS,MAAM,GAAG,MAAM;IAO/E,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAP7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAI;IAC7B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAyB;IACzD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAGb,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EACxB,UAAU,EAAE,sBAAsB,EACnD,OAAO,EAAE,oBAAoB,CAAC,CAAC,EAAE,EAAE,CAAC;IAQtC;;;;;;;OAOG;IACG,KAAK,CAAC,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,EAAE,CAAC,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,MAAM,CAAC;CAoB5F"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Staff alert recipients (messaging layer helper): who receives an alert raised for a permission.
|
|
3
|
+
* Today the lists come from configuration; an app can plug in a lookup (e.g. its auth service)
|
|
4
|
+
* by implementing {@link StaffRecipientResolver}.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { type DispatchRecipient } from '../contracts/notification-dispatch.js';
|
|
8
|
+
/**
|
|
9
|
+
* Resolves the recipients who hold a permission.
|
|
10
|
+
*
|
|
11
|
+
* @example const resolver: StaffRecipientResolver = { resolve: async (p) => authClient.usersWithPermission(p) };
|
|
12
|
+
*/
|
|
13
|
+
export interface StaffRecipientResolver {
|
|
14
|
+
/**
|
|
15
|
+
* @param permission The permission an alert targets.
|
|
16
|
+
* @returns Recipients (possibly empty).
|
|
17
|
+
*/
|
|
18
|
+
resolve(permission: string): Promise<DispatchRecipient[]>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Recipients from configuration (env), keyed by the permission an alert targets.
|
|
22
|
+
*
|
|
23
|
+
* @param byPermission Map of permission → recipients, typically built with {@link staffEmailListSchema}.
|
|
24
|
+
* @example new ConfiguredStaffRecipients({ 'payouts.manage': env.FINANCE_ALERT_EMAILS })
|
|
25
|
+
*/
|
|
26
|
+
export declare class ConfiguredStaffRecipients implements StaffRecipientResolver {
|
|
27
|
+
private readonly byPermission;
|
|
28
|
+
constructor(byPermission: Readonly<Record<string, readonly DispatchRecipient[]>>);
|
|
29
|
+
/**
|
|
30
|
+
* @param permission The permission an alert targets.
|
|
31
|
+
* @returns A copy of the configured recipients, or `[]` when none are configured.
|
|
32
|
+
*/
|
|
33
|
+
resolve(permission: string): Promise<DispatchRecipient[]>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A comma-separated staff address list from env (`"a@x.io, b@y.io"`): trimmed, blanks dropped,
|
|
37
|
+
* de-duplicated case-insensitively. An invalid address fails validation, so a typo is caught at boot
|
|
38
|
+
* instead of dead-lettering in the notification-service when the alert finally fires.
|
|
39
|
+
*
|
|
40
|
+
* @returns (on parse) `DispatchRecipient[]` with `email` only.
|
|
41
|
+
* @example const config = z.object({ FINANCE_ALERT_EMAILS: staffEmailListSchema }).parse(process.env);
|
|
42
|
+
*/
|
|
43
|
+
export declare const staffEmailListSchema: z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodOptional<z.ZodString>, string[], string | undefined>, string[], string | undefined>, {
|
|
44
|
+
email: string;
|
|
45
|
+
name?: string | undefined;
|
|
46
|
+
userId?: string | null | undefined;
|
|
47
|
+
locale?: string | null | undefined;
|
|
48
|
+
timezone?: string | null | undefined;
|
|
49
|
+
}[], string | undefined>;
|
|
50
|
+
//# sourceMappingURL=staff-recipients.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staff-recipients.d.ts","sourceRoot":"","sources":["../../../src/messaging/staff-recipients.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAmB,KAAK,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAEhG;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;CAC3D;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,YAAW,sBAAsB;IAC1D,OAAO,CAAC,QAAQ,CAAC,YAAY;gBAAZ,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,iBAAiB,EAAE,CAAC,CAAC;IAEjG;;;OAGG;IACG,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;CAGhE;AAID;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB;;;;;;wBAegD,CAAC"}
|
|
@@ -1,23 +1,55 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Naming for exchanges and queues
|
|
3
|
-
* environments that
|
|
2
|
+
* Naming for exchanges and queues, all derived from the app slug and the environment prefix
|
|
3
|
+
* (`QUEUE_PREFIX`), so two environments that share a broker or vhost never see each other's traffic.
|
|
4
4
|
*
|
|
5
|
-
* events exchange
|
|
6
|
-
* commands exchange
|
|
7
|
-
*
|
|
5
|
+
* events exchange {app}.{prefix}.events (topic) domain events, e.g. `thing.created`
|
|
6
|
+
* commands exchange {app}.{prefix}.commands (direct) work for one owner, e.g. `mail:send`
|
|
7
|
+
* dead-letter exch. {app}.{prefix}.{service}.dlx (direct) one per service
|
|
8
|
+
* service queues {prefix}.{service}.{purpose} e.g. `dev.billing.thing-events`
|
|
9
|
+
* dead-letter queue {queue}.dlq retry queues {queue}.retry.{ms} delay queues {queue}.delay.{ms}
|
|
8
10
|
*/
|
|
9
11
|
export interface Topology {
|
|
10
12
|
readonly app: string;
|
|
11
13
|
readonly prefix: string;
|
|
12
14
|
readonly eventsExchange: string;
|
|
13
15
|
readonly commandsExchange: string;
|
|
14
|
-
/** Physical queue name for a logical name. */
|
|
16
|
+
/** Physical queue name for a logical name (adds the environment prefix once). */
|
|
15
17
|
queue(name: string): string;
|
|
18
|
+
/** `{prefix}.{service}.{purpose}` — the name of a service's subscription queue. */
|
|
19
|
+
queueName(service: string, purpose: string): string;
|
|
20
|
+
/** `{app}.{prefix}.{service}.dlx` — the service's dead-letter exchange. */
|
|
21
|
+
deadLetterExchange(service: string): string;
|
|
16
22
|
}
|
|
17
|
-
|
|
23
|
+
/** Options for {@link createTopology}; values come from config, never literals in shared code. */
|
|
24
|
+
export interface TopologyOptions {
|
|
25
|
+
/** App slug (lower-case letters, digits, `-`, `_`). */
|
|
18
26
|
app: string;
|
|
27
|
+
/** Environment prefix, i.e. `QUEUE_PREFIX` (lower-case letters, digits, `-`, `_`). */
|
|
19
28
|
prefix: string;
|
|
20
|
-
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Builds the naming scheme for one app and environment.
|
|
32
|
+
*
|
|
33
|
+
* @param options App slug and environment prefix.
|
|
34
|
+
* @returns The {@link Topology}.
|
|
35
|
+
* @throws Error when a segment is empty or has other characters.
|
|
36
|
+
* @example const topology = createTopology({ app: config.APP_SLUG, prefix: config.QUEUE_PREFIX });
|
|
37
|
+
*/
|
|
38
|
+
export declare function createTopology(options: TopologyOptions): Topology;
|
|
39
|
+
/** Retry queue for one delay: a fixed TTL, dead-lettering back into `queue`. */
|
|
21
40
|
export declare const retryQueueName: (queue: string, delayMs: number) => string;
|
|
41
|
+
/** Dead-letter queue of `queue`. */
|
|
22
42
|
export declare const deadLetterQueueName: (queue: string) => string;
|
|
43
|
+
/** Smallest delay bucket for {@link delayQueueName}. */
|
|
44
|
+
export declare const MIN_DELAY_BUCKET_MS = 1000;
|
|
45
|
+
/**
|
|
46
|
+
* Delay queue for `scheduleDelayed`: one per power-of-two bucket (1s, 2s, 4s …) at or above `delayMs`.
|
|
47
|
+
* Per-message TTLs only expire at the head of a queue, so bucketing bounds how long a short delay can
|
|
48
|
+
* wait behind a longer one (at most its own bucket size).
|
|
49
|
+
*
|
|
50
|
+
* @param queue Target queue (physical name).
|
|
51
|
+
* @param delayMs Requested delay in milliseconds.
|
|
52
|
+
* @returns `{queue}.delay.{bucketMs}`.
|
|
53
|
+
*/
|
|
54
|
+
export declare function delayQueueName(queue: string, delayMs: number): string;
|
|
23
55
|
//# sourceMappingURL=topology.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"topology.d.ts","sourceRoot":"","sources":["../../../src/messaging/topology.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"topology.d.ts","sourceRoot":"","sources":["../../../src/messaging/topology.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,iFAAiF;IACjF,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,mFAAmF;IACnF,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACpD,2EAA2E;IAC3E,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7C;AAED,kGAAkG;AAClG,MAAM,WAAW,eAAe;IAC9B,uDAAuD;IACvD,GAAG,EAAE,MAAM,CAAC;IACZ,sFAAsF;IACtF,MAAM,EAAE,MAAM,CAAC;CAChB;AAWD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,QAAQ,CAYjE;AAED,gFAAgF;AAChF,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,EAAE,SAAS,MAAM,WAAgC,CAAC;AAC9F,oCAAoC;AACpC,eAAO,MAAM,mBAAmB,GAAI,OAAO,MAAM,WAAmB,CAAC;AAErE,wDAAwD;AACxD,eAAO,MAAM,mBAAmB,OAAO,CAAC;AAExC;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAIrE"}
|
|
@@ -2,27 +2,63 @@ import type { NextFunction, Request, Response } from 'express';
|
|
|
2
2
|
import type { Redis } from 'ioredis';
|
|
3
3
|
import { type RateLimiterAbstract } from 'rate-limiter-flexible';
|
|
4
4
|
import type { Logger } from '../types/index.js';
|
|
5
|
+
import { type RedisKeyspace } from '../cache/keyspace.js';
|
|
6
|
+
export * from './keys.js';
|
|
7
|
+
/** Inputs to {@link createLimiter}. */
|
|
5
8
|
export interface RateLimitOptions {
|
|
6
9
|
points: number;
|
|
7
10
|
durationSec: number;
|
|
8
|
-
/**
|
|
9
|
-
|
|
11
|
+
/** The service keyspace; limiter keys read `{prefix}rl:{scope}:{key}`. */
|
|
12
|
+
keyspace: RedisKeyspace;
|
|
13
|
+
/** Limit name, unique per limiter (lower-case kebab-case), e.g. `sign-in-ip`. */
|
|
14
|
+
scope: string;
|
|
10
15
|
redis?: Redis | null;
|
|
11
16
|
/** Lock the key for this long once exhausted (default: none). */
|
|
12
17
|
blockDurationSec?: number;
|
|
13
18
|
logger?: Logger;
|
|
14
19
|
}
|
|
15
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Redis limiter with an in-memory insurance limiter so a Redis outage never blocks traffic. Its
|
|
22
|
+
* keys come from the keyspace; pass already-encoded keys (see {@link limiterKey}) to `consume`.
|
|
23
|
+
*
|
|
24
|
+
* @param options Points, window, keyspace and scope.
|
|
25
|
+
* @returns The limiter.
|
|
26
|
+
* @example const limiter = createLimiter({ keyspace: ks, scope: 'sign-in-ip', points: 10, durationSec: 60, redis });
|
|
27
|
+
*/
|
|
16
28
|
export declare function createLimiter(options: RateLimitOptions): RateLimiterAbstract;
|
|
29
|
+
/**
|
|
30
|
+
* Encodes a raw limiter key (user id, IP …) so it cannot add segments to the limiter's Redis key.
|
|
31
|
+
*
|
|
32
|
+
* @param raw Raw identifier.
|
|
33
|
+
* @returns The encoded key part.
|
|
34
|
+
* @example limiter.consume(limiterKey(req.ip))
|
|
35
|
+
*/
|
|
36
|
+
export declare const limiterKey: (raw: string) => string;
|
|
17
37
|
/**
|
|
18
38
|
* Consumes one point for `key`; throws TooManyRequestsError (429) when exhausted.
|
|
19
|
-
* For in-service checks such as per-identifier
|
|
39
|
+
* For in-service checks such as per-identifier sign-in throttling.
|
|
40
|
+
*
|
|
41
|
+
* @param limiter Limiter from {@link createLimiter}.
|
|
42
|
+
* @param key Encoded key (see {@link limiterKey}).
|
|
43
|
+
* @param message Optional public message for the 429.
|
|
44
|
+
* @throws TooManyRequestsError when the limit is exhausted; rethrows limiter failures.
|
|
45
|
+
* @example await consumeOrThrow(limiter, limiterKey(userId));
|
|
20
46
|
*/
|
|
21
47
|
export declare function consumeOrThrow(limiter: RateLimiterAbstract, key: string, message?: string): Promise<void>;
|
|
48
|
+
/** Inputs to {@link rateLimitMiddleware}: limiter options plus the request → key function. */
|
|
22
49
|
export interface RateLimitMiddlewareOptions extends RateLimitOptions {
|
|
23
50
|
/** Default: authenticated user ID, else client IP. */
|
|
24
51
|
keyOf?: (req: Request) => string;
|
|
25
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Express middleware that consumes one point per request. Sets `X-RateLimit-Limit` and
|
|
55
|
+
* `X-RateLimit-Remaining`; when exhausted answers 429 `RATE_LIMITED` with `Retry-After` (seconds).
|
|
56
|
+
* Fails open: a limiter error (not a rejection) is logged and the request continues.
|
|
57
|
+
*
|
|
58
|
+
* @param options Limiter options and `keyOf` (default user id, else client IP).
|
|
59
|
+
* @returns The middleware.
|
|
60
|
+
* @example router.post('/sessions', rateLimitMiddleware({ keyspace: ks, scope: 'sign-in-ip', points: 10, durationSec: 60, redis }), handler);
|
|
61
|
+
*/
|
|
26
62
|
export declare function rateLimitMiddleware(options: RateLimitMiddlewareOptions): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
27
63
|
export type { RateLimiterAbstract };
|
|
28
64
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/rate-limit/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAuC,KAAK,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAItG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/rate-limit/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAuC,KAAK,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAItG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGzE,cAAc,WAAW,CAAC;AAE1B,uCAAuC;AACvC,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,QAAQ,EAAE,aAAa,CAAC;IACxB,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;IACrB,iEAAiE;IACjE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,gBAAgB,GAAG,mBAAmB,CAY5E;AAED;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM,KAAG,MAA4B,CAAC;AAEtE;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,mBAAmB,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAO/G;AAED,8FAA8F;AAC9F,MAAM,WAAW,0BAA2B,SAAQ,gBAAgB;IAClE,sDAAsD;IACtD,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;CAClC;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,IAMvD,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC,CAiB9E;AAED,YAAY,EAAE,mBAAmB,EAAE,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type RedisKey, type RedisKeyspace } from '../cache/keyspace.js';
|
|
2
|
+
/**
|
|
3
|
+
* Rate-limit key builder and the sliding-window limiter contract. Dependency-free (exported from the
|
|
4
|
+
* package root as well as `/rate-limit`); the consuming service implements the sliding window.
|
|
5
|
+
*/
|
|
6
|
+
/** Purpose segment for every rate-limit key: `{prefix}rl:{scope}:…`. */
|
|
7
|
+
export declare const RATE_LIMIT_PURPOSE = "rl";
|
|
8
|
+
/** What a limit counts against. At least one dimension is required; they combine in a fixed order. */
|
|
9
|
+
export interface RateLimitKeyInput {
|
|
10
|
+
/** Limit name, e.g. `sign-in` (lower-case kebab-case). */
|
|
11
|
+
scope: string;
|
|
12
|
+
ip?: string;
|
|
13
|
+
userId?: string;
|
|
14
|
+
/** Normalised (trimmed, lower-cased) and always hashed — never stored in clear. */
|
|
15
|
+
email?: string;
|
|
16
|
+
/** Request host, lower-cased. */
|
|
17
|
+
host?: string;
|
|
18
|
+
/** Route template, e.g. `/sessions` (encoded, so slashes cannot add segments). */
|
|
19
|
+
route?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Builds `{prefix}rl:{scope}[:ip:{ip}][:user:{id}][:email:{hash}][:host:{host}][:route:{route}]`.
|
|
23
|
+
*
|
|
24
|
+
* @param keyspace The service keyspace.
|
|
25
|
+
* @param input Scope plus at least one dimension.
|
|
26
|
+
* @returns The rate-limit key.
|
|
27
|
+
* @throws ConfigError when no dimension is given or the scope is invalid.
|
|
28
|
+
* @example rateLimitKey(ks, { scope: 'sign-in', ip: req.ip, email: body.email })
|
|
29
|
+
*/
|
|
30
|
+
export declare function rateLimitKey(keyspace: RedisKeyspace, input: RateLimitKeyInput): RedisKey;
|
|
31
|
+
/** Input to one sliding-window check. */
|
|
32
|
+
export interface SlidingWindowCheck {
|
|
33
|
+
/** Key from {@link rateLimitKey}. */
|
|
34
|
+
key: RedisKey;
|
|
35
|
+
/** Requests allowed per window. */
|
|
36
|
+
limit: number;
|
|
37
|
+
/** Window length in milliseconds. */
|
|
38
|
+
windowMs: number;
|
|
39
|
+
/** Clock override for tests (epoch ms). */
|
|
40
|
+
now?: number;
|
|
41
|
+
}
|
|
42
|
+
/** Outcome of one sliding-window check. */
|
|
43
|
+
export interface SlidingWindowResult {
|
|
44
|
+
allowed: boolean;
|
|
45
|
+
/** Requests left in the current window (0 when refused). */
|
|
46
|
+
remaining: number;
|
|
47
|
+
/** Milliseconds until the next request would be allowed (0 when allowed). */
|
|
48
|
+
retryAfterMs: number;
|
|
49
|
+
}
|
|
50
|
+
/** Sliding-window limiter contract; implemented by the consuming service (e.g. over Redis sorted sets). */
|
|
51
|
+
export type SlidingWindowLimiter = (check: SlidingWindowCheck) => Promise<SlidingWindowResult>;
|
|
52
|
+
//# sourceMappingURL=keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../../src/rate-limit/keys.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,QAAQ,EAAqB,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE3G;;;GAGG;AAEH,wEAAwE;AACxE,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC,sGAAsG;AACtG,MAAM,WAAW,iBAAiB;IAChC,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAeD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,iBAAiB,GAAG,QAAQ,CAUxF;AAED,yCAAyC;AACzC,MAAM,WAAW,kBAAkB;IACjC,qCAAqC;IACrC,GAAG,EAAE,QAAQ,CAAC;IACd,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,2CAA2C;AAC3C,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,2GAA2G;AAC3G,MAAM,MAAM,oBAAoB,GAAG,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC"}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { Server as HttpServer } from 'node:http';
|
|
2
|
+
import type { Redis } from 'ioredis';
|
|
2
3
|
import { Server, type ServerOptions, type Socket } from 'socket.io';
|
|
4
|
+
import type { RedisKeyspace } from '../cache/keyspace.js';
|
|
5
|
+
import { type IdentityProtocol } from '../identity/index.js';
|
|
3
6
|
import type { AuthUser, Logger } from '../types/index.js';
|
|
7
|
+
import { type SocketTicketClaims, type SocketTicketKeyOptions, type SocketTicketRejection } from './ticket.js';
|
|
8
|
+
export * from './ticket.js';
|
|
4
9
|
export interface SocketServerOptions {
|
|
5
10
|
/** Socket.IO path, e.g. `/ws/notifications` (the gateway proxies the same path). */
|
|
6
11
|
path: string;
|
|
@@ -23,8 +28,87 @@ export interface SocketEmitter {
|
|
|
23
28
|
emitToRoom(room: string, event: string, payload: unknown): void;
|
|
24
29
|
close(): Promise<void>;
|
|
25
30
|
}
|
|
31
|
+
/** Name of the private room every connection of `userId` joins. */
|
|
26
32
|
export declare const userRoom: (userId: string) => string;
|
|
33
|
+
/**
|
|
34
|
+
* Creates a Socket.IO server that authenticates every connection and joins it only to server-derived rooms.
|
|
35
|
+
* The verified user is in `socket.data.identity`.
|
|
36
|
+
*
|
|
37
|
+
* @example const sockets = createSocketServer(httpServer, { path: '/ws', authenticate: gatewaySocketAuth({ secret, protocol }) });
|
|
38
|
+
*/
|
|
27
39
|
export declare function createSocketServer(httpServer: HttpServer, options: SocketServerOptions): SocketEmitter;
|
|
28
|
-
/**
|
|
29
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Trusts the gateway-signed (v2) identity headers forwarded on the WebSocket upgrade request. The gateway
|
|
42
|
+
* signs the upgrade as `GET <handshake path>` (query ignored).
|
|
43
|
+
*
|
|
44
|
+
* @param options The shared secret (at least 32 characters) and the app's identity protocol.
|
|
45
|
+
* @returns An `authenticate` function for {@link createSocketServer}.
|
|
46
|
+
* @example createSocketServer(server, { path: '/ws', authenticate: gatewaySocketAuth({ secret, protocol }) });
|
|
47
|
+
*/
|
|
48
|
+
export declare function gatewaySocketAuth(options: {
|
|
49
|
+
secret: string;
|
|
50
|
+
protocol: IdentityProtocol;
|
|
51
|
+
}): (socket: Socket) => AuthUser | null;
|
|
52
|
+
/** Why a ticket handshake was refused: a ticket problem, an origin problem or a replayed ticket. */
|
|
53
|
+
export type SocketRejectionReason = SocketTicketRejection | 'origin_not_allowed' | 'replayed';
|
|
54
|
+
/** Passed to `onRejected` so the service can log it and publish its security event. */
|
|
55
|
+
export interface SocketRejection {
|
|
56
|
+
reason: SocketRejectionReason;
|
|
57
|
+
/** Known once the ticket verified (origin and replay rejections). */
|
|
58
|
+
userId: string | null;
|
|
59
|
+
/** The Socket.IO path that was refused. */
|
|
60
|
+
path: string;
|
|
61
|
+
origin: string | null;
|
|
62
|
+
}
|
|
63
|
+
/** Settings for {@link createTicketHandshake}. */
|
|
64
|
+
export interface TicketHandshakeOptions extends SocketTicketKeyOptions {
|
|
65
|
+
/** Ticket audience(s) this server accepts. */
|
|
66
|
+
audience: string | readonly string[];
|
|
67
|
+
/** Browser origins allowed for this ticket holder (e.g. the app's origins, or a tenant's own domain). */
|
|
68
|
+
allowedOrigins: (claims: SocketTicketClaims) => readonly string[] | Promise<readonly string[]>;
|
|
69
|
+
/** Accept handshakes without an `Origin` header (non-browser clients). Default false. */
|
|
70
|
+
allowMissingOrigin?: boolean;
|
|
71
|
+
/** Returns false when the ticket id was already used (makes tickets single-use). */
|
|
72
|
+
isFirstUse?: (claims: SocketTicketClaims) => Promise<boolean>;
|
|
73
|
+
/** Called for every refused handshake. Must not throw. */
|
|
74
|
+
onRejected?: (rejection: SocketRejection) => void;
|
|
75
|
+
/** Socket.IO path, reported in rejections. */
|
|
76
|
+
path: string;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The handshake check on its own (testable without a server): verifies the ticket from
|
|
80
|
+
* `socket.handshake.auth.ticket`, then the browser origin against `allowedOrigins(claims)`, then
|
|
81
|
+
* single use.
|
|
82
|
+
*
|
|
83
|
+
* @param options Ticket keys, audience, origin rule, replay check and rejection hook.
|
|
84
|
+
* @returns `authenticate(socket)` resolving to the claims, or null when refused.
|
|
85
|
+
*/
|
|
86
|
+
export declare function createTicketHandshake(options: TicketHandshakeOptions): (socket: Pick<Socket, 'handshake'>) => Promise<SocketTicketClaims | null>;
|
|
87
|
+
/** Settings for {@link createTicketSocketServer}. */
|
|
88
|
+
export interface TicketSocketServerOptions extends Omit<TicketHandshakeOptions, 'isFirstUse'> {
|
|
89
|
+
httpServer: HttpServer;
|
|
90
|
+
/**
|
|
91
|
+
* Shared Redis: enables the `@socket.io/redis-adapter` fan-out across replicas (channel prefix from
|
|
92
|
+
* `keyspace`) and makes tickets single-use (`{keyspace}socket-ticket:{jti}`, TTL = ticket lifetime).
|
|
93
|
+
* Omit only for a single in-process instance (tests, local tools).
|
|
94
|
+
*/
|
|
95
|
+
redis?: Redis;
|
|
96
|
+
/** The service's keyspace; required with `redis`. */
|
|
97
|
+
keyspace?: RedisKeyspace;
|
|
98
|
+
/** Extra rooms to join on connect, derived from the verified claims. `user:{userId}` is always joined. */
|
|
99
|
+
rooms?: (claims: SocketTicketClaims) => string[];
|
|
100
|
+
serverOptions?: Partial<ServerOptions>;
|
|
101
|
+
logger?: Logger;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Creates a Socket.IO server whose clients authenticate with a short-lived ticket
|
|
105
|
+
* (`io(url, { path, auth: (cb) => getTicket().then((ticket) => cb({ ticket })) })`). Each connection
|
|
106
|
+
* auto-joins `user:{userId}`; the verified claims are in `socket.data.identity` for later room checks
|
|
107
|
+
* with `canJoinScopeRoom`.
|
|
108
|
+
*
|
|
109
|
+
* @param options HTTP server, path, ticket keys, origin rule, Redis + keyspace, rejection hook.
|
|
110
|
+
* @returns The emitter.
|
|
111
|
+
* @example createTicketSocketServer({ httpServer, path: '/ws', secret, domain, audience: 'admin-app', redis, keyspace, allowedOrigins: () => origins, onRejected })
|
|
112
|
+
*/
|
|
113
|
+
export declare function createTicketSocketServer(options: TicketSocketServerOptions): SocketEmitter;
|
|
30
114
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/socket/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/socket/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Short-lived socket handshake tickets. The browser cannot send its in-memory access token on a
|
|
3
|
+
* WebSocket upgrade header, so it asks the API for a ticket (≤ 60 s) and passes it in the Socket.IO
|
|
4
|
+
* `auth` payload. A ticket is `v1.<base64url claims>.<base64url HMAC-SHA256>`, signed over a domain
|
|
5
|
+
* separator so a ticket minted for another app or purpose never verifies here.
|
|
6
|
+
*
|
|
7
|
+
* Dependency-free (no socket.io), so the issuer can sign tickets without the socket stack.
|
|
8
|
+
*/
|
|
9
|
+
/** Longest allowed ticket lifetime (and the default), in seconds. */
|
|
10
|
+
export declare const SOCKET_TICKET_MAX_TTL_SECONDS = 60;
|
|
11
|
+
/** Shortest accepted signing secret. */
|
|
12
|
+
export declare const MIN_SOCKET_TICKET_SECRET_LENGTH = 32;
|
|
13
|
+
/** What the issuer puts in a ticket. */
|
|
14
|
+
export interface SocketTicketInput {
|
|
15
|
+
userId: string;
|
|
16
|
+
/** Who the ticket is for (e.g. the front-end app); the server checks it. */
|
|
17
|
+
aud: string;
|
|
18
|
+
/** Resource ids the user may act on, keyed by scope name (as in `AuthUser.scopes`). */
|
|
19
|
+
scopes?: Record<string, string[]>;
|
|
20
|
+
permissions?: string[];
|
|
21
|
+
isSuperAdmin?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/** Verified ticket claims. */
|
|
24
|
+
export interface SocketTicketClaims {
|
|
25
|
+
userId: string;
|
|
26
|
+
aud: string;
|
|
27
|
+
scopes: Record<string, string[]>;
|
|
28
|
+
permissions: string[];
|
|
29
|
+
isSuperAdmin: boolean;
|
|
30
|
+
/** Unique id, so a server can make tickets single-use. */
|
|
31
|
+
jti: string;
|
|
32
|
+
/** Issued-at and expiry, epoch seconds. */
|
|
33
|
+
iat: number;
|
|
34
|
+
exp: number;
|
|
35
|
+
}
|
|
36
|
+
/** Signing and verification settings. */
|
|
37
|
+
export interface SocketTicketKeyOptions {
|
|
38
|
+
/** Shared HMAC secret (at least 32 characters). */
|
|
39
|
+
secret: string;
|
|
40
|
+
/** Domain separator, e.g. `myapp-socket-ticket:v1`. */
|
|
41
|
+
domain: string;
|
|
42
|
+
/** Clock (tests). */
|
|
43
|
+
now?: () => Date;
|
|
44
|
+
}
|
|
45
|
+
/** Why a ticket was refused. */
|
|
46
|
+
export type SocketTicketRejection = 'missing' | 'malformed' | 'bad_signature' | 'expired' | 'wrong_audience';
|
|
47
|
+
/** Result of {@link verifySocketTicket}. */
|
|
48
|
+
export type SocketTicketResult = {
|
|
49
|
+
ok: true;
|
|
50
|
+
claims: SocketTicketClaims;
|
|
51
|
+
} | {
|
|
52
|
+
ok: false;
|
|
53
|
+
reason: SocketTicketRejection;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Signs a ticket.
|
|
57
|
+
*
|
|
58
|
+
* @param input User, audience, scopes and permissions.
|
|
59
|
+
* @param options Secret, domain separator and `ttlSeconds` (1–60, default 60).
|
|
60
|
+
* @returns The ticket string.
|
|
61
|
+
* @example signSocketTicket({ userId, aud: 'admin-app', scopes: { tenant: [tenantId] }, permissions }, { secret, domain: 'myapp-socket-ticket:v1' })
|
|
62
|
+
*/
|
|
63
|
+
export declare function signSocketTicket(input: SocketTicketInput, options: SocketTicketKeyOptions & {
|
|
64
|
+
ttlSeconds?: number;
|
|
65
|
+
}): string;
|
|
66
|
+
/**
|
|
67
|
+
* Verifies a ticket's signature, domain, lifetime and audience. Never throws for a bad ticket.
|
|
68
|
+
*
|
|
69
|
+
* @param ticket The string from the handshake.
|
|
70
|
+
* @param options Secret, domain separator and the accepted audience(s).
|
|
71
|
+
* @returns The claims, or the rejection reason.
|
|
72
|
+
* @example const result = verifySocketTicket(socket.handshake.auth.ticket, { secret, domain, audience: 'admin-app' });
|
|
73
|
+
*/
|
|
74
|
+
export declare function verifySocketTicket(ticket: unknown, options: SocketTicketKeyOptions & {
|
|
75
|
+
audience: string | readonly string[];
|
|
76
|
+
}): SocketTicketResult;
|
|
77
|
+
/** Name of the room for one scope id, e.g. `tenant:01J…`. */
|
|
78
|
+
export declare const scopeRoom: (scope: string, scopeId: string) => string;
|
|
79
|
+
/**
|
|
80
|
+
* True when the ticket holder may join the room of `scopeId` in `scope`: the id is in the ticket's scope
|
|
81
|
+
* ids (or the holder is a super admin) and, when given, the holder has `permission`.
|
|
82
|
+
*
|
|
83
|
+
* @param claims Verified ticket claims.
|
|
84
|
+
* @param scope Scope name, e.g. `tenant`.
|
|
85
|
+
* @param scopeId The id whose room is requested.
|
|
86
|
+
* @param permission Permission the room needs (optional).
|
|
87
|
+
* @example canJoinScopeRoom(claims, 'tenant', tenantId, 'widgets.view')
|
|
88
|
+
*/
|
|
89
|
+
export declare function canJoinScopeRoom(claims: SocketTicketClaims, scope: string, scopeId: string, permission?: string): boolean;
|
|
90
|
+
//# sourceMappingURL=ticket.d.ts.map
|