@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
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { createHash, createHmac } from 'node:crypto';
|
|
2
|
+
import { ConfigError, isDeployedNodeEnv } from '../config/index.js';
|
|
3
|
+
/** Environment segment used outside staging and production when none is given. */
|
|
4
|
+
export const DEFAULT_KEY_ENV = 'dev';
|
|
5
|
+
/** Parts longer than this are hashed. */
|
|
6
|
+
export const MAX_PLAIN_KEY_PART_LENGTH = 64;
|
|
7
|
+
/** Marker that starts every hashed part. */
|
|
8
|
+
export const HASHED_PART_PREFIX = 'h.';
|
|
9
|
+
const SEGMENT_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
10
|
+
const EMAIL_LIKE = /@/;
|
|
11
|
+
const PHONE_LIKE = /^\+?\d{9,15}$/;
|
|
12
|
+
function assertSegment(kind, value) {
|
|
13
|
+
if (!SEGMENT_PATTERN.test(value))
|
|
14
|
+
throw new ConfigError(`Redis keyspace: invalid ${kind} "${value}" (lower-case kebab-case [a-z0-9-] only)`);
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Marks a key part as personal data or a secret so the keyspace always hashes it.
|
|
19
|
+
*
|
|
20
|
+
* @param value Raw value (e.g. an email address or a token).
|
|
21
|
+
* @returns A part the keyspace hashes.
|
|
22
|
+
* @example ks.key('otp', sensitivePart(email))
|
|
23
|
+
*/
|
|
24
|
+
export const sensitivePart = (value) => ({ sensitive: value });
|
|
25
|
+
/**
|
|
26
|
+
* Percent-encodes a plain key part, including the glob characters SCAN interprets.
|
|
27
|
+
*
|
|
28
|
+
* @param value Part text (non-empty).
|
|
29
|
+
* @returns The encoded part.
|
|
30
|
+
* @throws ConfigError when the part is empty.
|
|
31
|
+
* @example encodeKeyPart('a:b*') // 'a%3Ab%2A'
|
|
32
|
+
*/
|
|
33
|
+
export function encodeKeyPart(value) {
|
|
34
|
+
if (!value)
|
|
35
|
+
throw new ConfigError('Redis keyspace: key parts must not be empty');
|
|
36
|
+
return encodeURIComponent(value).replace(/[*?[\]!'()]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
37
|
+
}
|
|
38
|
+
function hashValue(value, secret) {
|
|
39
|
+
const digest = secret ? createHmac('sha256', secret).update(value).digest('hex') : createHash('sha256').update(value).digest('hex');
|
|
40
|
+
return `${HASHED_PART_PREFIX}${digest}`;
|
|
41
|
+
}
|
|
42
|
+
function shouldHash(value) {
|
|
43
|
+
return value.length > MAX_PLAIN_KEY_PART_LENGTH || EMAIL_LIKE.test(value) || PHONE_LIKE.test(value) || value.startsWith(HASHED_PART_PREFIX);
|
|
44
|
+
}
|
|
45
|
+
function renderPart(part, secret) {
|
|
46
|
+
if (typeof part === 'object') {
|
|
47
|
+
if (!part.sensitive)
|
|
48
|
+
throw new ConfigError('Redis keyspace: key parts must not be empty');
|
|
49
|
+
return hashValue(part.sensitive, secret);
|
|
50
|
+
}
|
|
51
|
+
const value = String(part);
|
|
52
|
+
return shouldHash(value) ? hashValue(value, secret) : encodeKeyPart(value);
|
|
53
|
+
}
|
|
54
|
+
function resolveEnv(env, nodeEnv) {
|
|
55
|
+
const explicit = env?.trim();
|
|
56
|
+
if (explicit)
|
|
57
|
+
return assertSegment('env', explicit);
|
|
58
|
+
if (isDeployedNodeEnv(nodeEnv))
|
|
59
|
+
throw new ConfigError(`Redis keyspace: CACHE_KEY_PREFIX must be set when NODE_ENV=${(nodeEnv ?? '').trim()}`);
|
|
60
|
+
return DEFAULT_KEY_ENV;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Creates the keyspace for one service.
|
|
64
|
+
*
|
|
65
|
+
* @param options App slug, service, env segment (`CACHE_KEY_PREFIX`), optional hash secret.
|
|
66
|
+
* @returns A {@link RedisKeyspace}.
|
|
67
|
+
* @throws ConfigError on an invalid segment, or when `env` is missing in staging/production.
|
|
68
|
+
* @example
|
|
69
|
+
* const ks = createRedisKeyspace({ app: 'myapp', service: 'widget-service', env: config.CACHE_KEY_PREFIX });
|
|
70
|
+
* ks.key('widget', widgetId) // 'myapp:prod:widget-service:widget:01J…'
|
|
71
|
+
*/
|
|
72
|
+
export function createRedisKeyspace(options) {
|
|
73
|
+
const app = assertSegment('app', options.app);
|
|
74
|
+
const service = assertSegment('service', options.service);
|
|
75
|
+
const env = resolveEnv(options.env, options.nodeEnv ?? process.env.NODE_ENV);
|
|
76
|
+
const prefix = `${app}:${env}:${service}:`;
|
|
77
|
+
const secret = options.hashSecret;
|
|
78
|
+
return Object.freeze({
|
|
79
|
+
app,
|
|
80
|
+
env,
|
|
81
|
+
service,
|
|
82
|
+
prefix,
|
|
83
|
+
key(purpose, ...parts) {
|
|
84
|
+
const head = `${prefix}${assertSegment('purpose', purpose)}`;
|
|
85
|
+
return (parts.length ? `${head}:${parts.map((p) => renderPart(p, secret)).join(':')}` : head);
|
|
86
|
+
},
|
|
87
|
+
pattern(purpose, suffix = '*') {
|
|
88
|
+
return `${prefix}${assertSegment('purpose', purpose)}:${suffix}`;
|
|
89
|
+
},
|
|
90
|
+
owns(key) {
|
|
91
|
+
return key.startsWith(prefix) && key.length > prefix.length;
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=keyspace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyspace.js","sourceRoot":"","sources":["../../../src/cache/keyspace.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A minimal in-process circuit breaker for outbound calls.
|
|
3
|
+
*
|
|
4
|
+
* Layer: plumbing. States: `closed` (calls flow) → `open` after `failureThreshold` consecutive failures
|
|
5
|
+
* (calls are refused without touching the network) → `half-open` once `resetAfterMs` has passed, when exactly
|
|
6
|
+
* one trial call is let through: success closes the circuit, failure re-opens it for another `resetAfterMs`.
|
|
7
|
+
* State is per instance (per process), so each client owns its breaker; nothing is shared between targets.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Counts consecutive failures and refuses calls while open.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const breaker = new CircuitBreaker({ failureThreshold: 5, resetAfterMs: 30_000 });
|
|
14
|
+
* if (!breaker.tryAcquire()) throw new TargetUnavailableError();
|
|
15
|
+
* try { const r = await call(); breaker.onSuccess(); return r; } catch (e) { breaker.onFailure(); throw e; }
|
|
16
|
+
*/
|
|
17
|
+
export class CircuitBreaker {
|
|
18
|
+
failureThreshold;
|
|
19
|
+
resetAfterMs;
|
|
20
|
+
now;
|
|
21
|
+
failures = 0;
|
|
22
|
+
openedAt = 0;
|
|
23
|
+
current = 'closed';
|
|
24
|
+
trialInFlight = false;
|
|
25
|
+
/** @param options Threshold, reset time and clock. @throws Error when a setting is not a positive number. */
|
|
26
|
+
constructor(options = {}) {
|
|
27
|
+
this.failureThreshold = options.failureThreshold ?? 5;
|
|
28
|
+
this.resetAfterMs = options.resetAfterMs ?? 30_000;
|
|
29
|
+
this.now = options.now ?? Date.now;
|
|
30
|
+
if (!(this.failureThreshold >= 1) || !(this.resetAfterMs > 0))
|
|
31
|
+
throw new Error('CircuitBreaker: failureThreshold must be ≥ 1 and resetAfterMs > 0');
|
|
32
|
+
}
|
|
33
|
+
/** The current state (an open circuit whose reset time has passed reports `half-open`). */
|
|
34
|
+
get state() {
|
|
35
|
+
if (this.current === 'open' && this.now() - this.openedAt >= this.resetAfterMs)
|
|
36
|
+
return 'half-open';
|
|
37
|
+
return this.current;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Asks to make a call. While half-open only the first caller gets the trial; the rest are refused until it settles.
|
|
41
|
+
*
|
|
42
|
+
* @returns True when the call may go ahead; the caller must then report {@link onSuccess} or {@link onFailure}.
|
|
43
|
+
*/
|
|
44
|
+
tryAcquire() {
|
|
45
|
+
const state = this.state;
|
|
46
|
+
if (state === 'closed')
|
|
47
|
+
return true;
|
|
48
|
+
if (state === 'open' || this.trialInFlight)
|
|
49
|
+
return false;
|
|
50
|
+
this.current = 'half-open';
|
|
51
|
+
this.trialInFlight = true;
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
/** Records a successful call: closes the circuit and clears the failure count. */
|
|
55
|
+
onSuccess() {
|
|
56
|
+
this.failures = 0;
|
|
57
|
+
this.trialInFlight = false;
|
|
58
|
+
this.current = 'closed';
|
|
59
|
+
}
|
|
60
|
+
/** Records a failed call: a failed trial re-opens at once; otherwise opens after `failureThreshold` in a row. */
|
|
61
|
+
onFailure() {
|
|
62
|
+
this.failures += 1;
|
|
63
|
+
if (this.current === 'half-open' || this.failures >= this.failureThreshold) {
|
|
64
|
+
this.current = 'open';
|
|
65
|
+
this.openedAt = this.now();
|
|
66
|
+
}
|
|
67
|
+
this.trialInFlight = false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=circuit-breaker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../../src/common/circuit-breaker.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAeH;;;;;;;GAOG;AACH,MAAM,OAAO,cAAc;IACR,gBAAgB,CAAS;IACzB,YAAY,CAAS;IACrB,GAAG,CAAe;IAC3B,QAAQ,GAAG,CAAC,CAAC;IACb,QAAQ,GAAG,CAAC,CAAC;IACb,OAAO,GAAiB,QAAQ,CAAC;IACjC,aAAa,GAAG,KAAK,CAAC;IAE9B,6GAA6G;IAC7G,YAAY,UAAiC,EAAE;QAC7C,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,CAAC,CAAC;QACtD,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,MAAM,CAAC;QACnD,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;QACnC,IAAI,CAAC,CAAC,IAAI,CAAC,gBAAgB,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;IACtJ,CAAC;IAED,2FAA2F;IAC3F,IAAI,KAAK;QACP,IAAI,IAAI,CAAC,OAAO,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO,WAAW,CAAC;QACnG,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED;;;;OAIG;IACH,UAAU;QACR,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,IAAI,KAAK,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACpC,IAAI,KAAK,KAAK,MAAM,IAAI,IAAI,CAAC,aAAa;YAAE,OAAO,KAAK,CAAC;QACzD,IAAI,CAAC,OAAO,GAAG,WAAW,CAAC;QAC3B,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kFAAkF;IAClF,SAAS;QACP,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;QAClB,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,QAAQ,CAAC;IAC1B,CAAC;IAED,iHAAiH;IACjH,SAAS;QACP,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;QACnB,IAAI,IAAI,CAAC,OAAO,KAAK,WAAW,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC3E,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;YACtB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,CAAC;QACD,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;IAC7B,CAAC;CACF"}
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
* Base class for errors that map to an HTTP status. Throw these from services; the shared
|
|
3
3
|
* error middleware turns them into the standard envelope. Anything that is not an AppError is
|
|
4
4
|
* reported as a 500 with a generic message so internals never leak.
|
|
5
|
+
*
|
|
6
|
+
* @param statusCode HTTP status. 4xx messages reach the caller; 5xx messages never do.
|
|
7
|
+
* @param message Caller-facing message (4xx) or log message (5xx).
|
|
8
|
+
* @param options `code` (defaults from the status), field `errors`, `retryable` (consumers),
|
|
9
|
+
* `retryAfterSeconds` (sent as `Retry-After`) and `cause`.
|
|
10
|
+
* @example throw new AppError(402, 'Payment is required first', { code: 'PAYMENT_REQUIRED' });
|
|
5
11
|
*/
|
|
6
12
|
export class AppError extends Error {
|
|
7
13
|
statusCode;
|
|
@@ -9,6 +15,8 @@ export class AppError extends Error {
|
|
|
9
15
|
errors;
|
|
10
16
|
/** Consumers dead-letter immediately instead of retrying when this is false. */
|
|
11
17
|
retryable;
|
|
18
|
+
/** Seconds the caller should wait before retrying; the error middleware sends it as `Retry-After`. */
|
|
19
|
+
retryAfterSeconds;
|
|
12
20
|
constructor(statusCode, message, options = {}) {
|
|
13
21
|
super(message, options.cause ? { cause: options.cause } : undefined);
|
|
14
22
|
this.name = new.target.name;
|
|
@@ -16,8 +24,22 @@ export class AppError extends Error {
|
|
|
16
24
|
this.code = options.code ?? defaultCode(statusCode);
|
|
17
25
|
this.errors = options.errors;
|
|
18
26
|
this.retryable = options.retryable ?? statusCode >= 500;
|
|
27
|
+
this.retryAfterSeconds = options.retryAfterSeconds;
|
|
19
28
|
}
|
|
20
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Shorthand for an error with an explicit status and code.
|
|
32
|
+
*
|
|
33
|
+
* @param statusCode HTTP status (4xx messages reach the caller; 5xx are replaced with a generic message).
|
|
34
|
+
* @param code UPPER_SNAKE_CASE machine-readable code, e.g. `SLUG_TAKEN`.
|
|
35
|
+
* @param message Human-readable message for 4xx responses.
|
|
36
|
+
* @param errors Optional field errors (`{ 'items.0.qty': ['Must be positive'] }`).
|
|
37
|
+
* @returns An {@link AppError} to `throw`.
|
|
38
|
+
* @example throw httpError(409, 'SLUG_TAKEN', 'That address is already in use');
|
|
39
|
+
*/
|
|
40
|
+
export function httpError(statusCode, code, message, errors) {
|
|
41
|
+
return new AppError(statusCode, message, { code, errors });
|
|
42
|
+
}
|
|
21
43
|
function defaultCode(status) {
|
|
22
44
|
switch (status) {
|
|
23
45
|
case 400: return 'BAD_REQUEST';
|
|
@@ -31,27 +53,35 @@ function defaultCode(status) {
|
|
|
31
53
|
default: return status >= 500 ? 'INTERNAL_ERROR' : 'ERROR';
|
|
32
54
|
}
|
|
33
55
|
}
|
|
56
|
+
/** 400 BAD_REQUEST: the request is malformed or breaks a business rule. */
|
|
34
57
|
export class BadRequestError extends AppError {
|
|
35
58
|
constructor(message = 'Bad request', options = {}) { super(400, message, options); }
|
|
36
59
|
}
|
|
60
|
+
/** 401 UNAUTHENTICATED: no valid identity. */
|
|
37
61
|
export class UnauthorizedError extends AppError {
|
|
38
62
|
constructor(message = 'Authentication required', options = {}) { super(401, message, options); }
|
|
39
63
|
}
|
|
64
|
+
/** 403 FORBIDDEN: authenticated, but not allowed. */
|
|
40
65
|
export class ForbiddenError extends AppError {
|
|
41
66
|
constructor(message = 'You do not have permission to perform this action', options = {}) { super(403, message, options); }
|
|
42
67
|
}
|
|
68
|
+
/** 404 NOT_FOUND. Also use it for out-of-scope resources so their existence is not revealed. */
|
|
43
69
|
export class NotFoundError extends AppError {
|
|
44
70
|
constructor(message = 'Resource not found', options = {}) { super(404, message, options); }
|
|
45
71
|
}
|
|
72
|
+
/** 409 CONFLICT: duplicate or state conflict. */
|
|
46
73
|
export class ConflictError extends AppError {
|
|
47
74
|
constructor(message = 'Resource already exists', options = {}) { super(409, message, options); }
|
|
48
75
|
}
|
|
76
|
+
/** 422 VALIDATION_FAILED with field errors (`{ 'items.0.qty': ['…'] }`). */
|
|
49
77
|
export class ValidationError extends AppError {
|
|
50
78
|
constructor(errors, message = 'Validation failed') { super(422, message, { errors }); }
|
|
51
79
|
}
|
|
80
|
+
/** 429. Pass `retryAfterSeconds` so the error middleware sets the `Retry-After` header. */
|
|
52
81
|
export class TooManyRequestsError extends AppError {
|
|
53
82
|
constructor(message = 'Too many requests. Please try again later.', options = {}) { super(429, message, options); }
|
|
54
83
|
}
|
|
84
|
+
/** 503 SERVICE_UNAVAILABLE: a dependency is down. The message is logged, never sent (callers see the generic one). */
|
|
55
85
|
export class ServiceUnavailableError extends AppError {
|
|
56
86
|
constructor(message = 'Service temporarily unavailable', options = {}) { super(503, message, options); }
|
|
57
87
|
}
|
|
@@ -61,15 +91,23 @@ export class NonRetryableError extends AppError {
|
|
|
61
91
|
super(422, message, { ...options, retryable: false });
|
|
62
92
|
}
|
|
63
93
|
}
|
|
94
|
+
/** Type guard for {@link AppError} (and its subclasses). */
|
|
64
95
|
export function isAppError(error) {
|
|
65
96
|
return error instanceof AppError;
|
|
66
97
|
}
|
|
67
|
-
/**
|
|
98
|
+
/** The message every 5xx response carries. */
|
|
99
|
+
export const GENERIC_ERROR_MESSAGE = 'Internal server error';
|
|
100
|
+
/**
|
|
101
|
+
* Message safe to show a caller. Only 4xx AppErrors keep their own message; every 5xx (including
|
|
102
|
+
* 503s, whose messages often name upstreams or RPC methods) and every non-AppError get the generic one.
|
|
103
|
+
*
|
|
104
|
+
* @param error Anything thrown.
|
|
105
|
+
* @returns The message to put in the response body.
|
|
106
|
+
* @example publicMessage(new NotFoundError('Order not found')) // 'Order not found'
|
|
107
|
+
*/
|
|
68
108
|
export function publicMessage(error) {
|
|
69
109
|
if (isAppError(error) && error.statusCode < 500)
|
|
70
110
|
return error.message;
|
|
71
|
-
|
|
72
|
-
return error.message;
|
|
73
|
-
return 'Internal server error';
|
|
111
|
+
return GENERIC_ERROR_MESSAGE;
|
|
74
112
|
}
|
|
75
113
|
//# sourceMappingURL=errors.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/common/errors.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/common/errors.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/common/index.js
CHANGED
|
@@ -7,6 +7,10 @@ export * from './jwt.js';
|
|
|
7
7
|
export * from './text.js';
|
|
8
8
|
export * from './async.js';
|
|
9
9
|
export * from './phone.js';
|
|
10
|
+
export * from './money.js';
|
|
10
11
|
export * from './time.js';
|
|
11
12
|
export * from './audit.js';
|
|
13
|
+
export * from './circuit-breaker.js';
|
|
14
|
+
export * from './internal-service-auth.js';
|
|
15
|
+
export * from './internal-client.js';
|
|
12
16
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/common/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/common/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { sleep } from './async.js';
|
|
2
|
+
import { CircuitBreaker } from './circuit-breaker.js';
|
|
3
|
+
import { AppError, ServiceUnavailableError } from './errors.js';
|
|
4
|
+
import { internalServiceAuthConfigFromEnv, internalServiceHeaders } from './internal-service-auth.js';
|
|
5
|
+
import { ConfigError } from '../config/index.js';
|
|
6
|
+
import { getRequestContext } from '../context/index.js';
|
|
7
|
+
import { CONTEXT_HEADERS } from '../identity/index.js';
|
|
8
|
+
const unavailableClasses = new Map();
|
|
9
|
+
const pascalCase = (value) => value.split(/[^A-Za-z0-9]+/).filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join('');
|
|
10
|
+
/**
|
|
11
|
+
* Returns the `{Target}UnavailableError` class for a target service (the same class on every call, so
|
|
12
|
+
* `instanceof` works anywhere in the process).
|
|
13
|
+
*
|
|
14
|
+
* @param target The target service name, e.g. `records-service`.
|
|
15
|
+
* @returns A class named e.g. `RecordsServiceUnavailableError`, extending `ServiceUnavailableError` (503).
|
|
16
|
+
* @example
|
|
17
|
+
* const RecordsUnavailable = makeUnavailableError('records-service');
|
|
18
|
+
* try { await records.get('/records/1'); } catch (e) { if (e instanceof RecordsUnavailable) useCachedCopy(); }
|
|
19
|
+
*/
|
|
20
|
+
export function makeUnavailableError(target) {
|
|
21
|
+
const existing = unavailableClasses.get(target);
|
|
22
|
+
if (existing)
|
|
23
|
+
return existing;
|
|
24
|
+
const TargetUnavailableError = class extends ServiceUnavailableError {
|
|
25
|
+
target = target;
|
|
26
|
+
constructor(message = `${target} is unavailable`, options = {}) {
|
|
27
|
+
super(message, { cause: options.cause });
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
// AppError takes `name` from the constructor, so naming the class names every instance.
|
|
31
|
+
Object.defineProperty(TargetUnavailableError, 'name', { value: `${pascalCase(target)}UnavailableError` });
|
|
32
|
+
unavailableClasses.set(target, TargetUnavailableError);
|
|
33
|
+
return TargetUnavailableError;
|
|
34
|
+
}
|
|
35
|
+
const RETRYABLE_METHODS = new Set(['GET']);
|
|
36
|
+
const RETRYABLE_STATUS = new Set([502, 503, 504]);
|
|
37
|
+
/** Why an attempt counted as unavailable; logged, never sent to the end user. */
|
|
38
|
+
class AttemptFailure extends Error {
|
|
39
|
+
reason;
|
|
40
|
+
retryable;
|
|
41
|
+
constructor(reason, retryable, options) {
|
|
42
|
+
super(reason, options);
|
|
43
|
+
this.reason = reason;
|
|
44
|
+
this.retryable = retryable;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Creates a client for one target service.
|
|
49
|
+
*
|
|
50
|
+
* @param options Target, base URL, caller credential, timeout, retry, breaker and logger settings.
|
|
51
|
+
* @returns An {@link InternalClient}.
|
|
52
|
+
* @throws ConfigError when the caller name or credential is missing.
|
|
53
|
+
* @example
|
|
54
|
+
* // src/clients/records.client.ts
|
|
55
|
+
* const http = createInternalClient({ target: 'records-service', baseUrl: config.RECORDS_INTERNAL_URL, logger });
|
|
56
|
+
* export const getRecord = (id: string) => http.get(`/records/${id}`, { schema: recordSchema });
|
|
57
|
+
*/
|
|
58
|
+
export function createInternalClient(options) {
|
|
59
|
+
const caller = options.caller ?? (() => {
|
|
60
|
+
const env = internalServiceAuthConfigFromEnv();
|
|
61
|
+
return { name: env.name, secret: env.credential };
|
|
62
|
+
})();
|
|
63
|
+
if (!caller.name || !caller.secret) {
|
|
64
|
+
throw new ConfigError(`createInternalClient(${options.target}): INTERNAL_SERVICE_NAME and INTERNAL_SERVICE_CREDENTIAL are required`);
|
|
65
|
+
}
|
|
66
|
+
const UnavailableError = makeUnavailableError(options.target);
|
|
67
|
+
const breaker = new CircuitBreaker(options.breaker);
|
|
68
|
+
const doFetch = options.fetch ?? fetch;
|
|
69
|
+
const base = options.baseUrl.replace(/\/+$/, '');
|
|
70
|
+
const backoff = options.backoffMs?.length ? options.backoffMs : [100, 400];
|
|
71
|
+
const buildUrl = (path, query) => {
|
|
72
|
+
const url = new URL(base + (path.startsWith('/') ? path : `/${path}`));
|
|
73
|
+
for (const [key, value] of Object.entries(query ?? {}))
|
|
74
|
+
if (value !== undefined)
|
|
75
|
+
url.searchParams.set(key, String(value));
|
|
76
|
+
return url;
|
|
77
|
+
};
|
|
78
|
+
const buildHeaders = (opts) => {
|
|
79
|
+
const correlationId = opts.correlationId ?? getRequestContext()?.correlationId;
|
|
80
|
+
return {
|
|
81
|
+
accept: 'application/json',
|
|
82
|
+
...(opts.body !== undefined ? { 'content-type': 'application/json' } : {}),
|
|
83
|
+
...opts.identityHeaders,
|
|
84
|
+
...(correlationId ? { [CONTEXT_HEADERS.CORRELATION_ID]: correlationId } : {}),
|
|
85
|
+
...(opts.idempotencyKey ? { [CONTEXT_HEADERS.IDEMPOTENCY_KEY]: opts.idempotencyKey } : {}),
|
|
86
|
+
// Last, so nothing passed in can replace the caller's own credential.
|
|
87
|
+
...internalServiceHeaders(caller),
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
/** One HTTP attempt: resolves with status + body text for any answer below 500, throws AttemptFailure otherwise. */
|
|
91
|
+
const attempt = async (method, url, init, timeoutMs) => {
|
|
92
|
+
const controller = new AbortController();
|
|
93
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
94
|
+
try {
|
|
95
|
+
const response = await doFetch(url, { ...init, method, signal: controller.signal });
|
|
96
|
+
const text = await response.text();
|
|
97
|
+
if (response.status >= 500)
|
|
98
|
+
throw new AttemptFailure(`status ${response.status}`, RETRYABLE_STATUS.has(response.status));
|
|
99
|
+
return { status: response.status, text };
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
if (error instanceof AttemptFailure)
|
|
103
|
+
throw error;
|
|
104
|
+
throw new AttemptFailure(controller.signal.aborted ? 'timeout' : 'network error', true, { cause: error });
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
clearTimeout(timer);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
/** Runs attempts with GET-only retries; throws AttemptFailure when the target never answered below 500. */
|
|
111
|
+
const send = async (method, url, init, timeoutMs) => {
|
|
112
|
+
const retries = RETRYABLE_METHODS.has(method) ? options.retries ?? 2 : 0;
|
|
113
|
+
for (let n = 0;; n += 1) {
|
|
114
|
+
try {
|
|
115
|
+
return await attempt(method, url, init, timeoutMs);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
const failure = error;
|
|
119
|
+
if (!failure.retryable || n >= retries)
|
|
120
|
+
throw failure;
|
|
121
|
+
await sleep(backoff[Math.min(n, backoff.length - 1)] ?? 0);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const unavailable = (method, url, reason, cause) => {
|
|
126
|
+
options.logger?.warn('Internal call unavailable', { target: options.target, method, path: url.pathname, reason });
|
|
127
|
+
return new UnavailableError(`${options.target} unavailable: ${reason}`, { cause });
|
|
128
|
+
};
|
|
129
|
+
async function request(method, path, opts = {}) {
|
|
130
|
+
const url = buildUrl(path, opts.query);
|
|
131
|
+
if (!breaker.tryAcquire())
|
|
132
|
+
throw unavailable(method, url, 'circuit open');
|
|
133
|
+
const init = { headers: buildHeaders(opts), body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined };
|
|
134
|
+
let answer;
|
|
135
|
+
try {
|
|
136
|
+
answer = await send(method, url, init, opts.timeoutMs ?? options.timeoutMs ?? 3000);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
breaker.onFailure();
|
|
140
|
+
throw unavailable(method, url, error.reason, error);
|
|
141
|
+
}
|
|
142
|
+
breaker.onSuccess();
|
|
143
|
+
let payload = {};
|
|
144
|
+
try {
|
|
145
|
+
payload = answer.text ? JSON.parse(answer.text) : {};
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
// A 4xx without a JSON body still maps to a plain AppError below; a 2xx must be readable.
|
|
149
|
+
if (answer.status < 400)
|
|
150
|
+
throw unavailable(method, url, 'unreadable response body', error);
|
|
151
|
+
}
|
|
152
|
+
if (answer.status >= 400) {
|
|
153
|
+
throw new AppError(answer.status, payload.message || `${options.target} rejected the request`, { code: payload.code, errors: payload.errors });
|
|
154
|
+
}
|
|
155
|
+
if (!opts.schema)
|
|
156
|
+
return payload.data;
|
|
157
|
+
const parsed = opts.schema.safeParse(payload.data);
|
|
158
|
+
if (!parsed.success)
|
|
159
|
+
throw unavailable(method, url, 'response did not match the expected schema', parsed.error);
|
|
160
|
+
return parsed.data;
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
target: options.target,
|
|
164
|
+
UnavailableError,
|
|
165
|
+
get circuitState() { return breaker.state; },
|
|
166
|
+
get: (path, opts) => request('GET', path, opts),
|
|
167
|
+
post: (path, opts) => request('POST', path, opts),
|
|
168
|
+
put: (path, opts) => request('PUT', path, opts),
|
|
169
|
+
patch: (path, opts) => request('PATCH', path, opts),
|
|
170
|
+
delete: (path, opts) => request('DELETE', path, opts),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=internal-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal-client.js","sourceRoot":"","sources":["../../../src/common/internal-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Leaf constants for internal-service auth, shared by `common/internal-service-auth` and the `config` env blocks
|
|
3
|
+
* (kept dependency-free so neither module imports the other).
|
|
4
|
+
*/
|
|
5
|
+
/** Shortest internal credential accepted in staging and production. */
|
|
6
|
+
export const MIN_INTERNAL_CREDENTIAL_LENGTH = 32;
|
|
7
|
+
/** Service names are compose-style: lower-case letters, digits and hyphens, starting with a letter. */
|
|
8
|
+
export const SERVICE_NAME_PATTERN = /^[a-z][a-z0-9-]{0,62}$/;
|
|
9
|
+
//# sourceMappingURL=internal-constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal-constants.js","sourceRoot":"","sources":["../../../src/common/internal-constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,uEAAuE;AACvE,MAAM,CAAC,MAAM,8BAA8B,GAAG,EAAE,CAAC;AACjD,uGAAuG;AACvG,MAAM,CAAC,MAAM,oBAAoB,GAAG,wBAAwB,CAAC"}
|