@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,51 @@
|
|
|
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
|
+
/** Breaker state. */
|
|
10
|
+
export type CircuitState = 'closed' | 'open' | 'half-open';
|
|
11
|
+
/** Breaker settings. */
|
|
12
|
+
export interface CircuitBreakerOptions {
|
|
13
|
+
/** Consecutive failures that open the circuit (default 5). */
|
|
14
|
+
failureThreshold?: number;
|
|
15
|
+
/** How long the circuit stays open before one trial call is allowed (default 30 000 ms). */
|
|
16
|
+
resetAfterMs?: number;
|
|
17
|
+
/** Clock, for tests (default `Date.now`). */
|
|
18
|
+
now?: () => number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Counts consecutive failures and refuses calls while open.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* const breaker = new CircuitBreaker({ failureThreshold: 5, resetAfterMs: 30_000 });
|
|
25
|
+
* if (!breaker.tryAcquire()) throw new TargetUnavailableError();
|
|
26
|
+
* try { const r = await call(); breaker.onSuccess(); return r; } catch (e) { breaker.onFailure(); throw e; }
|
|
27
|
+
*/
|
|
28
|
+
export declare class CircuitBreaker {
|
|
29
|
+
private readonly failureThreshold;
|
|
30
|
+
private readonly resetAfterMs;
|
|
31
|
+
private readonly now;
|
|
32
|
+
private failures;
|
|
33
|
+
private openedAt;
|
|
34
|
+
private current;
|
|
35
|
+
private trialInFlight;
|
|
36
|
+
/** @param options Threshold, reset time and clock. @throws Error when a setting is not a positive number. */
|
|
37
|
+
constructor(options?: CircuitBreakerOptions);
|
|
38
|
+
/** The current state (an open circuit whose reset time has passed reports `half-open`). */
|
|
39
|
+
get state(): CircuitState;
|
|
40
|
+
/**
|
|
41
|
+
* Asks to make a call. While half-open only the first caller gets the trial; the rest are refused until it settles.
|
|
42
|
+
*
|
|
43
|
+
* @returns True when the call may go ahead; the caller must then report {@link onSuccess} or {@link onFailure}.
|
|
44
|
+
*/
|
|
45
|
+
tryAcquire(): boolean;
|
|
46
|
+
/** Records a successful call: closes the circuit and clears the failure count. */
|
|
47
|
+
onSuccess(): void;
|
|
48
|
+
/** Records a failed call: a failed trial re-opens at once; otherwise opens after `failureThreshold` in a row. */
|
|
49
|
+
onFailure(): void;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=circuit-breaker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../../../src/common/circuit-breaker.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,qBAAqB;AACrB,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAE3D,wBAAwB;AACxB,MAAM,WAAW,qBAAqB;IACpC,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4FAA4F;IAC5F,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,aAAa,CAAS;IAE9B,6GAA6G;gBACjG,OAAO,GAAE,qBAA0B;IAO/C,2FAA2F;IAC3F,IAAI,KAAK,IAAI,YAAY,CAGxB;IAED;;;;OAIG;IACH,UAAU,IAAI,OAAO;IASrB,kFAAkF;IAClF,SAAS,IAAI,IAAI;IAMjB,iHAAiH;IACjH,SAAS,IAAI,IAAI;CAQlB"}
|
|
@@ -3,6 +3,12 @@ export type ErrorDetails = Record<string, string[]>;
|
|
|
3
3
|
* Base class for errors that map to an HTTP status. Throw these from services; the shared
|
|
4
4
|
* error middleware turns them into the standard envelope. Anything that is not an AppError is
|
|
5
5
|
* reported as a 500 with a generic message so internals never leak.
|
|
6
|
+
*
|
|
7
|
+
* @param statusCode HTTP status. 4xx messages reach the caller; 5xx messages never do.
|
|
8
|
+
* @param message Caller-facing message (4xx) or log message (5xx).
|
|
9
|
+
* @param options `code` (defaults from the status), field `errors`, `retryable` (consumers),
|
|
10
|
+
* `retryAfterSeconds` (sent as `Retry-After`) and `cause`.
|
|
11
|
+
* @example throw new AppError(402, 'Payment is required first', { code: 'PAYMENT_REQUIRED' });
|
|
6
12
|
*/
|
|
7
13
|
export declare class AppError extends Error {
|
|
8
14
|
readonly statusCode: number;
|
|
@@ -10,48 +16,73 @@ export declare class AppError extends Error {
|
|
|
10
16
|
readonly errors?: ErrorDetails;
|
|
11
17
|
/** Consumers dead-letter immediately instead of retrying when this is false. */
|
|
12
18
|
readonly retryable: boolean;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
/** Seconds the caller should wait before retrying; the error middleware sends it as `Retry-After`. */
|
|
20
|
+
readonly retryAfterSeconds?: number;
|
|
21
|
+
constructor(statusCode: number, message: string, options?: AppErrorOptions);
|
|
22
|
+
}
|
|
23
|
+
/** Options for {@link AppError}. `code` should be UPPER_SNAKE_CASE; it defaults from the status. */
|
|
24
|
+
export interface AppErrorOptions {
|
|
25
|
+
code?: string;
|
|
26
|
+
errors?: ErrorDetails;
|
|
27
|
+
retryable?: boolean;
|
|
28
|
+
retryAfterSeconds?: number;
|
|
29
|
+
cause?: unknown;
|
|
19
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Shorthand for an error with an explicit status and code.
|
|
33
|
+
*
|
|
34
|
+
* @param statusCode HTTP status (4xx messages reach the caller; 5xx are replaced with a generic message).
|
|
35
|
+
* @param code UPPER_SNAKE_CASE machine-readable code, e.g. `SLUG_TAKEN`.
|
|
36
|
+
* @param message Human-readable message for 4xx responses.
|
|
37
|
+
* @param errors Optional field errors (`{ 'items.0.qty': ['Must be positive'] }`).
|
|
38
|
+
* @returns An {@link AppError} to `throw`.
|
|
39
|
+
* @example throw httpError(409, 'SLUG_TAKEN', 'That address is already in use');
|
|
40
|
+
*/
|
|
41
|
+
export declare function httpError(statusCode: number, code: string, message: string, errors?: ErrorDetails): AppError;
|
|
42
|
+
/** 400 BAD_REQUEST: the request is malformed or breaks a business rule. */
|
|
20
43
|
export declare class BadRequestError extends AppError {
|
|
21
44
|
constructor(message?: string, options?: {
|
|
22
45
|
code?: string;
|
|
23
46
|
errors?: ErrorDetails;
|
|
24
47
|
});
|
|
25
48
|
}
|
|
49
|
+
/** 401 UNAUTHENTICATED: no valid identity. */
|
|
26
50
|
export declare class UnauthorizedError extends AppError {
|
|
27
51
|
constructor(message?: string, options?: {
|
|
28
52
|
code?: string;
|
|
29
53
|
});
|
|
30
54
|
}
|
|
55
|
+
/** 403 FORBIDDEN: authenticated, but not allowed. */
|
|
31
56
|
export declare class ForbiddenError extends AppError {
|
|
32
57
|
constructor(message?: string, options?: {
|
|
33
58
|
code?: string;
|
|
34
59
|
});
|
|
35
60
|
}
|
|
61
|
+
/** 404 NOT_FOUND. Also use it for out-of-scope resources so their existence is not revealed. */
|
|
36
62
|
export declare class NotFoundError extends AppError {
|
|
37
63
|
constructor(message?: string, options?: {
|
|
38
64
|
code?: string;
|
|
39
65
|
});
|
|
40
66
|
}
|
|
67
|
+
/** 409 CONFLICT: duplicate or state conflict. */
|
|
41
68
|
export declare class ConflictError extends AppError {
|
|
42
69
|
constructor(message?: string, options?: {
|
|
43
70
|
code?: string;
|
|
44
71
|
errors?: ErrorDetails;
|
|
45
72
|
});
|
|
46
73
|
}
|
|
74
|
+
/** 422 VALIDATION_FAILED with field errors (`{ 'items.0.qty': ['…'] }`). */
|
|
47
75
|
export declare class ValidationError extends AppError {
|
|
48
76
|
constructor(errors: ErrorDetails, message?: string);
|
|
49
77
|
}
|
|
78
|
+
/** 429. Pass `retryAfterSeconds` so the error middleware sets the `Retry-After` header. */
|
|
50
79
|
export declare class TooManyRequestsError extends AppError {
|
|
51
80
|
constructor(message?: string, options?: {
|
|
52
81
|
code?: string;
|
|
82
|
+
retryAfterSeconds?: number;
|
|
53
83
|
});
|
|
54
84
|
}
|
|
85
|
+
/** 503 SERVICE_UNAVAILABLE: a dependency is down. The message is logged, never sent (callers see the generic one). */
|
|
55
86
|
export declare class ServiceUnavailableError extends AppError {
|
|
56
87
|
constructor(message?: string, options?: {
|
|
57
88
|
code?: string;
|
|
@@ -65,7 +96,17 @@ export declare class NonRetryableError extends AppError {
|
|
|
65
96
|
cause?: unknown;
|
|
66
97
|
});
|
|
67
98
|
}
|
|
99
|
+
/** Type guard for {@link AppError} (and its subclasses). */
|
|
68
100
|
export declare function isAppError(error: unknown): error is AppError;
|
|
69
|
-
/**
|
|
101
|
+
/** The message every 5xx response carries. */
|
|
102
|
+
export declare const GENERIC_ERROR_MESSAGE = "Internal server error";
|
|
103
|
+
/**
|
|
104
|
+
* Message safe to show a caller. Only 4xx AppErrors keep their own message; every 5xx (including
|
|
105
|
+
* 503s, whose messages often name upstreams or RPC methods) and every non-AppError get the generic one.
|
|
106
|
+
*
|
|
107
|
+
* @param error Anything thrown.
|
|
108
|
+
* @returns The message to put in the response body.
|
|
109
|
+
* @example publicMessage(new NotFoundError('Order not found')) // 'Order not found'
|
|
110
|
+
*/
|
|
70
111
|
export declare function publicMessage(error: unknown): string;
|
|
71
112
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/common/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;AAEpD
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/common/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -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.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","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.d.ts","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,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed HTTP client base for service-to-service (`internal`) calls.
|
|
3
|
+
*
|
|
4
|
+
* Layer: plumbing for a service's `src/clients/{target}.client.ts`. Key rules:
|
|
5
|
+
* - Sends the caller's own credential (`x-internal-service-name` + `x-internal-service-secret`) on every call,
|
|
6
|
+
* the current correlation id (INT-12) and, when given, the end user's signed identity headers unchanged.
|
|
7
|
+
* - Callers can tell "service down" from "service said no": network errors, timeouts, 5xx, unreadable bodies
|
|
8
|
+
* and an open circuit all throw the target's `{Target}UnavailableError` (503); a 4xx throws an `AppError`
|
|
9
|
+
* carrying the remote status, code, message and field errors.
|
|
10
|
+
* - Only GET is retried (default 2 retries, 100 ms then 400 ms back-off), and only for network errors,
|
|
11
|
+
* timeouts and 502/503/504. Mutating calls are never retried here; they accept an idempotency key instead.
|
|
12
|
+
* - Each client owns one {@link CircuitBreaker}; a call that ends unavailable counts as a failure, any HTTP
|
|
13
|
+
* answer below 500 counts as a success.
|
|
14
|
+
* - Secrets and header values are never logged; failure logs carry target, method, path and reason only.
|
|
15
|
+
*/
|
|
16
|
+
import type { ZodType } from 'zod';
|
|
17
|
+
import { type CircuitBreakerOptions, type CircuitState } from './circuit-breaker.js';
|
|
18
|
+
import { ServiceUnavailableError } from './errors.js';
|
|
19
|
+
import { type InternalCallerCredential } from './internal-service-auth.js';
|
|
20
|
+
import type { Logger } from '../types/index.js';
|
|
21
|
+
/** An unavailable-error class bound to one target service. Instances are 503 `ServiceUnavailableError`s. */
|
|
22
|
+
export type UnavailableErrorClass = new (message?: string, options?: {
|
|
23
|
+
cause?: unknown;
|
|
24
|
+
}) => ServiceUnavailableError & {
|
|
25
|
+
readonly target: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Returns the `{Target}UnavailableError` class for a target service (the same class on every call, so
|
|
29
|
+
* `instanceof` works anywhere in the process).
|
|
30
|
+
*
|
|
31
|
+
* @param target The target service name, e.g. `records-service`.
|
|
32
|
+
* @returns A class named e.g. `RecordsServiceUnavailableError`, extending `ServiceUnavailableError` (503).
|
|
33
|
+
* @example
|
|
34
|
+
* const RecordsUnavailable = makeUnavailableError('records-service');
|
|
35
|
+
* try { await records.get('/records/1'); } catch (e) { if (e instanceof RecordsUnavailable) useCachedCopy(); }
|
|
36
|
+
*/
|
|
37
|
+
export declare function makeUnavailableError(target: string): UnavailableErrorClass;
|
|
38
|
+
/** Settings for {@link createInternalClient}. */
|
|
39
|
+
export interface InternalClientOptions {
|
|
40
|
+
/** Target service name (compose name), e.g. `records-service`; names the unavailable error. */
|
|
41
|
+
target: string;
|
|
42
|
+
/** Target base URL on the private network, e.g. `http://records-service:4000/internal`. */
|
|
43
|
+
baseUrl: string;
|
|
44
|
+
/** This service's name and secret; defaults to env `INTERNAL_SERVICE_NAME` / `INTERNAL_SERVICE_CREDENTIAL`. */
|
|
45
|
+
caller?: InternalCallerCredential;
|
|
46
|
+
/** Per-attempt timeout (default 3000 ms). */
|
|
47
|
+
timeoutMs?: number;
|
|
48
|
+
/** Extra attempts for GET (default 2). */
|
|
49
|
+
retries?: number;
|
|
50
|
+
/** Delay before retry n (1-based) is `backoffMs[n-1]`, the last value repeating (default `[100, 400]`). */
|
|
51
|
+
backoffMs?: readonly number[];
|
|
52
|
+
/** Breaker settings (default `{ failureThreshold: 5, resetAfterMs: 30000 }`). */
|
|
53
|
+
breaker?: CircuitBreakerOptions;
|
|
54
|
+
/** Receives a warning per unavailable call (target, method, path, reason). */
|
|
55
|
+
logger?: Logger;
|
|
56
|
+
/** Fetch implementation, for tests (default global `fetch`). */
|
|
57
|
+
fetch?: typeof fetch;
|
|
58
|
+
}
|
|
59
|
+
/** Options for one call. */
|
|
60
|
+
export interface InternalRequestOptions<T = unknown> {
|
|
61
|
+
query?: Record<string, string | number | boolean | undefined>;
|
|
62
|
+
body?: unknown;
|
|
63
|
+
/** Overrides the correlation id taken from the request context. */
|
|
64
|
+
correlationId?: string;
|
|
65
|
+
/**
|
|
66
|
+
* The end user's gateway identity headers, forwarded unchanged. Gateway signatures are bound to method and
|
|
67
|
+
* path, so a service acting for a user re-signs them for this exact call with `signedIdentityHeaders`.
|
|
68
|
+
*/
|
|
69
|
+
identityHeaders?: Record<string, string>;
|
|
70
|
+
/** Sent as `idempotency-key` so the target can dedupe a mutating call the caller repeats. */
|
|
71
|
+
idempotencyKey?: string;
|
|
72
|
+
/** Overrides the client's per-attempt timeout. */
|
|
73
|
+
timeoutMs?: number;
|
|
74
|
+
/** Validates the unwrapped `data`; a mismatch throws the unavailable error (the contract is broken). */
|
|
75
|
+
schema?: ZodType<T>;
|
|
76
|
+
}
|
|
77
|
+
/** A client bound to one target service. */
|
|
78
|
+
export interface InternalClient {
|
|
79
|
+
readonly target: string;
|
|
80
|
+
/** This target's unavailable error class, for `instanceof` checks. */
|
|
81
|
+
readonly UnavailableError: UnavailableErrorClass;
|
|
82
|
+
/** Current breaker state, for health and metrics. */
|
|
83
|
+
readonly circuitState: CircuitState;
|
|
84
|
+
/**
|
|
85
|
+
* Sends one call to the target. `get` is retried on transient failures; `post`/`put`/`patch`/`delete` never are.
|
|
86
|
+
*
|
|
87
|
+
* @param path Path under `baseUrl`, e.g. `/records/01J…`.
|
|
88
|
+
* @param options Query, body, correlation/identity headers, idempotency key, timeout and response schema.
|
|
89
|
+
* @returns The unwrapped `data` of the target's success envelope (validated when `schema` is given).
|
|
90
|
+
* @throws `{Target}UnavailableError` (503) when the target is down, slow, answers 5xx, sends an unreadable or
|
|
91
|
+
* schema-mismatched body, or the circuit is open; `AppError` with the remote status/code for a 4xx.
|
|
92
|
+
* @example const record = await records.get('/records/01J…', { schema: recordSchema });
|
|
93
|
+
*/
|
|
94
|
+
get<T>(path: string, options?: InternalRequestOptions<T>): Promise<T>;
|
|
95
|
+
/** Same as {@link InternalClient.get}, never retried; pass `idempotencyKey` so the target can dedupe a repeat. */
|
|
96
|
+
post<T>(path: string, options?: InternalRequestOptions<T>): Promise<T>;
|
|
97
|
+
/** Same as {@link InternalClient.post}. */
|
|
98
|
+
put<T>(path: string, options?: InternalRequestOptions<T>): Promise<T>;
|
|
99
|
+
/** Same as {@link InternalClient.post}. */
|
|
100
|
+
patch<T>(path: string, options?: InternalRequestOptions<T>): Promise<T>;
|
|
101
|
+
/** Same as {@link InternalClient.post}. */
|
|
102
|
+
delete<T>(path: string, options?: InternalRequestOptions<T>): Promise<T>;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Creates a client for one target service.
|
|
106
|
+
*
|
|
107
|
+
* @param options Target, base URL, caller credential, timeout, retry, breaker and logger settings.
|
|
108
|
+
* @returns An {@link InternalClient}.
|
|
109
|
+
* @throws ConfigError when the caller name or credential is missing.
|
|
110
|
+
* @example
|
|
111
|
+
* // src/clients/records.client.ts
|
|
112
|
+
* const http = createInternalClient({ target: 'records-service', baseUrl: config.RECORDS_INTERNAL_URL, logger });
|
|
113
|
+
* export const getRecord = (id: string) => http.get(`/records/${id}`, { schema: recordSchema });
|
|
114
|
+
*/
|
|
115
|
+
export declare function createInternalClient(options: InternalClientOptions): InternalClient;
|
|
116
|
+
//# sourceMappingURL=internal-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal-client.d.ts","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 declare const MIN_INTERNAL_CREDENTIAL_LENGTH = 32;
|
|
7
|
+
/** Service names are compose-style: lower-case letters, digits and hyphens, starting with a letter. */
|
|
8
|
+
export declare const SERVICE_NAME_PATTERN: RegExp;
|
|
9
|
+
//# sourceMappingURL=internal-constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal-constants.d.ts","sourceRoot":"","sources":["../../../src/common/internal-constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,uEAAuE;AACvE,eAAO,MAAM,8BAA8B,KAAK,CAAC;AACjD,uGAAuG;AACvG,eAAO,MAAM,oBAAoB,QAA2B,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/** Header carrying the calling service's name. */
|
|
2
|
+
export declare const INTERNAL_SERVICE_NAME_HEADER: "x-internal-service-name";
|
|
3
|
+
/** Header carrying the calling service's own secret. */
|
|
4
|
+
export declare const INTERNAL_SERVICE_SECRET_HEADER: "x-internal-service-secret";
|
|
5
|
+
export { MIN_INTERNAL_CREDENTIAL_LENGTH, SERVICE_NAME_PATTERN } from './internal-constants.js';
|
|
6
|
+
/** The credential a service sends when it calls another service. */
|
|
7
|
+
export interface InternalCallerCredential {
|
|
8
|
+
/** This service's own name (`INTERNAL_SERVICE_NAME`). */
|
|
9
|
+
name: string;
|
|
10
|
+
/** This service's own secret (`INTERNAL_SERVICE_CREDENTIAL`). */
|
|
11
|
+
secret: string;
|
|
12
|
+
}
|
|
13
|
+
/** Internal-auth settings read from the environment. */
|
|
14
|
+
export interface InternalServiceAuthConfig {
|
|
15
|
+
/** Own name, or `''` when unset. */
|
|
16
|
+
name: string;
|
|
17
|
+
/** Own secret, or `''` when unset. */
|
|
18
|
+
credential: string;
|
|
19
|
+
/** Caller name → secret accepted from it (empty when unset). */
|
|
20
|
+
credentials: Readonly<Record<string, string>>;
|
|
21
|
+
}
|
|
22
|
+
/** process.env or a parsed config object; only string values are read. */
|
|
23
|
+
type Env = Readonly<Record<string, unknown>>;
|
|
24
|
+
/**
|
|
25
|
+
* Parses `INTERNAL_SERVICE_CREDENTIALS` (a JSON object of caller name → secret).
|
|
26
|
+
*
|
|
27
|
+
* @param raw The env value; empty or undefined gives `{}`.
|
|
28
|
+
* @returns A frozen map of caller name → secret.
|
|
29
|
+
* @throws ConfigError when the JSON is malformed, not an object, a name is not a valid service name, or a
|
|
30
|
+
* secret is not a non-empty string. The message names the bad key, never a secret.
|
|
31
|
+
* @example parseInternalServiceCredentials('{"alpha-service":"<secret>"}') // { 'alpha-service': '<secret>' }
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseInternalServiceCredentials(raw: string | undefined): Readonly<Record<string, string>>;
|
|
34
|
+
/**
|
|
35
|
+
* Reads `INTERNAL_SERVICE_NAME`, `INTERNAL_SERVICE_CREDENTIAL` and `INTERNAL_SERVICE_CREDENTIALS`.
|
|
36
|
+
*
|
|
37
|
+
* @param env process.env (default) or a parsed config object.
|
|
38
|
+
* @returns The settings; missing values are empty.
|
|
39
|
+
* @throws ConfigError when `INTERNAL_SERVICE_CREDENTIALS` is malformed.
|
|
40
|
+
* @example const internalAuth = internalServiceAuthConfigFromEnv();
|
|
41
|
+
*/
|
|
42
|
+
export declare function internalServiceAuthConfigFromEnv(env?: Env): InternalServiceAuthConfig;
|
|
43
|
+
/**
|
|
44
|
+
* The headers a service adds to every internal call it makes.
|
|
45
|
+
*
|
|
46
|
+
* @param caller This service's name and secret.
|
|
47
|
+
* @returns `{ 'x-internal-service-name', 'x-internal-service-secret' }`.
|
|
48
|
+
* @example fetch(url, { headers: { ...internalServiceHeaders({ name, secret }) } });
|
|
49
|
+
*/
|
|
50
|
+
export declare function internalServiceHeaders(caller: InternalCallerCredential): Record<string, string>;
|
|
51
|
+
/** What a service does with internal routes, for {@link assertInternalServiceAuthConfigured}. */
|
|
52
|
+
export interface InternalServiceAuthReadiness {
|
|
53
|
+
/** The service calls other services' internal routes (needs its own name and credential). */
|
|
54
|
+
calls?: boolean;
|
|
55
|
+
/** The service exposes internal routes (needs the map of accepted callers). */
|
|
56
|
+
serves?: boolean;
|
|
57
|
+
/** Defaults to env `NODE_ENV`. */
|
|
58
|
+
nodeEnv?: string;
|
|
59
|
+
/** Defaults to process.env. */
|
|
60
|
+
env?: Env;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Startup guard. In staging and production it refuses to start when a calling service lacks a valid name or a
|
|
64
|
+
* credential of at least 32 characters, or when a serving service has no accepted callers or any accepted
|
|
65
|
+
* secret shorter than 32 characters. In other environments it only checks that what is set parses.
|
|
66
|
+
*
|
|
67
|
+
* @param options Whether the service calls and/or serves internal routes.
|
|
68
|
+
* @throws ConfigError listing every problem (names only, never secret values).
|
|
69
|
+
* @example assertInternalServiceAuthConfigured({ calls: true, serves: true });
|
|
70
|
+
*/
|
|
71
|
+
export declare function assertInternalServiceAuthConfigured(options: InternalServiceAuthReadiness): void;
|
|
72
|
+
/** Outcome of {@link verifyInternalCaller}. */
|
|
73
|
+
export type InternalCallerVerdict = {
|
|
74
|
+
ok: true;
|
|
75
|
+
caller: string;
|
|
76
|
+
} | {
|
|
77
|
+
ok: false;
|
|
78
|
+
status: 401;
|
|
79
|
+
reason: 'missing-credentials' | 'invalid-credentials';
|
|
80
|
+
caller?: string;
|
|
81
|
+
} | {
|
|
82
|
+
ok: false;
|
|
83
|
+
status: 403;
|
|
84
|
+
reason: 'caller-not-allowed';
|
|
85
|
+
caller: string;
|
|
86
|
+
};
|
|
87
|
+
type HeaderBag = Readonly<Record<string, string | string[] | undefined>>;
|
|
88
|
+
/**
|
|
89
|
+
* Checks the internal headers against the accepted credentials and the route's caller list.
|
|
90
|
+
* 401 when the name or secret is missing, the name is unknown or the secret is wrong; 403 when a correctly
|
|
91
|
+
* authenticated caller is not in `allowed`.
|
|
92
|
+
*
|
|
93
|
+
* @param input Request headers, accepted credentials and the route's allowed callers.
|
|
94
|
+
* @returns A verdict; the caller name is included when one was sent (safe to log), the secret never is.
|
|
95
|
+
* @example verifyInternalCaller({ headers: req.headers, credentials, allowed: ['alpha-service'] });
|
|
96
|
+
*/
|
|
97
|
+
export declare function verifyInternalCaller(input: {
|
|
98
|
+
headers: HeaderBag;
|
|
99
|
+
credentials: Readonly<Record<string, string>>;
|
|
100
|
+
allowed: readonly string[];
|
|
101
|
+
}): InternalCallerVerdict;
|
|
102
|
+
//# sourceMappingURL=internal-service-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal-service-auth.d.ts","sourceRoot":"","sources":["../../../src/common/internal-service-auth.ts"],"names":[],"mappings":"AAoBA,kDAAkD;AAClD,eAAO,MAAM,4BAA4B,2BAAwC,CAAC;AAClF,wDAAwD;AACxD,eAAO,MAAM,8BAA8B,6BAAkC,CAAC;AAC9E,OAAO,EAAE,8BAA8B,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAK/F,oEAAoE;AACpE,MAAM,WAAW,wBAAwB;IACvC,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wDAAwD;AACxD,MAAM,WAAW,yBAAyB;IACxC,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC/C;AAED,0EAA0E;AAC1E,KAAK,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAO7C;;;;;;;;GAQG;AACH,wBAAgB,+BAA+B,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAkBzG;AAED;;;;;;;GAOG;AACH,wBAAgB,gCAAgC,CAAC,GAAG,GAAE,GAAiB,GAAG,yBAAyB,CAMlG;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,wBAAwB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAE/F;AAED,iGAAiG;AACjG,MAAM,WAAW,4BAA4B;IAC3C,6FAA6F;IAC7F,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,kCAAkC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,GAAG,CAAC,EAAE,GAAG,CAAC;CACX;AAED;;;;;;;;GAQG;AACH,wBAAgB,mCAAmC,CAAC,OAAO,EAAE,4BAA4B,GAAG,IAAI,CAmB/F;AAMD,+CAA+C;AAC/C,MAAM,MAAM,qBAAqB,GAC7B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC5B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,qBAAqB,GAAG,qBAAqB,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAClG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,oBAAoB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7E,KAAK,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC;AAOzE;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,OAAO,EAAE,SAAS,CAAC;IACnB,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B,GAAG,qBAAqB,CASxB"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/** A currency: its ISO 4217 code and how many decimal places its minor unit has. */
|
|
2
|
+
export interface CurrencySpec {
|
|
3
|
+
/** ISO 4217 code, e.g. `USD`. */
|
|
4
|
+
readonly code: string;
|
|
5
|
+
/** Decimal places of the minor unit (2 for cents, 3 for fils, 0 for yen). */
|
|
6
|
+
readonly minorUnits: number;
|
|
7
|
+
}
|
|
8
|
+
/** A currency code (minor units read from the runtime's ISO 4217 data) or an explicit spec. */
|
|
9
|
+
export type CurrencyInput = string | CurrencySpec;
|
|
10
|
+
/** A rate or ratio: a decimal string (`"0.16"`) or a finite number (converted through its shortest decimal form). */
|
|
11
|
+
export type DecimalInput = string | number;
|
|
12
|
+
/**
|
|
13
|
+
* Resolves a currency code or spec, validating both.
|
|
14
|
+
*
|
|
15
|
+
* @param currency ISO 4217 code (minor units from `Intl`) or `{ code, minorUnits }`.
|
|
16
|
+
* @returns The spec.
|
|
17
|
+
* @throws RangeError for a malformed code or minor-unit count.
|
|
18
|
+
* @example resolveCurrency('USD') // { code: 'USD', minorUnits: 2 }
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveCurrency(currency: CurrencyInput): CurrencySpec;
|
|
21
|
+
/** An immutable amount of one currency, always a whole number of minor units. */
|
|
22
|
+
export declare class Money {
|
|
23
|
+
private readonly value;
|
|
24
|
+
/** The amount's currency. */
|
|
25
|
+
readonly currency: CurrencySpec;
|
|
26
|
+
private constructor();
|
|
27
|
+
/**
|
|
28
|
+
* Parses a decimal string. Strict: no thousands separators, symbols or more decimals than the
|
|
29
|
+
* currency allows (D5), so a stored value is never silently rounded on the way in.
|
|
30
|
+
*
|
|
31
|
+
* @param amount e.g. `"1250.5"` or `"-3.25"`.
|
|
32
|
+
* @param currency Code or spec.
|
|
33
|
+
* @returns The amount.
|
|
34
|
+
* @throws RangeError for anything else.
|
|
35
|
+
* @example Money.fromDecimalString('19.99', 'USD')
|
|
36
|
+
*/
|
|
37
|
+
static fromDecimalString(amount: string, currency: CurrencyInput): Money;
|
|
38
|
+
/**
|
|
39
|
+
* Builds an amount from an integer count of minor units (e.g. cents).
|
|
40
|
+
*
|
|
41
|
+
* @param units Integer (number within the safe range, or bigint).
|
|
42
|
+
* @param currency Code or spec.
|
|
43
|
+
* @returns The amount.
|
|
44
|
+
* @example Money.fromMinorUnits(1999, 'USD').toDecimalString() // '19.99'
|
|
45
|
+
*/
|
|
46
|
+
static fromMinorUnits(units: number | bigint, currency: CurrencyInput): Money;
|
|
47
|
+
/**
|
|
48
|
+
* Zero in a currency.
|
|
49
|
+
*
|
|
50
|
+
* @param currency Code or spec.
|
|
51
|
+
* @returns `0` of that currency.
|
|
52
|
+
*/
|
|
53
|
+
static zero(currency: CurrencyInput): Money;
|
|
54
|
+
/** The amount with exactly the currency's decimal places, e.g. `"1250.00"` — the wire and storage form. */
|
|
55
|
+
toDecimalString(): string;
|
|
56
|
+
/** The amount as an integer count of minor units. */
|
|
57
|
+
toMinorUnits(): bigint;
|
|
58
|
+
/** Serialises as the decimal string, so an amount never becomes a JSON number by accident. */
|
|
59
|
+
toJSON(): string;
|
|
60
|
+
/** Same as {@link toDecimalString}. */
|
|
61
|
+
toString(): string;
|
|
62
|
+
/** Sum of two amounts of the same currency. */
|
|
63
|
+
add(other: Money): Money;
|
|
64
|
+
/** Difference of two amounts of the same currency. */
|
|
65
|
+
sub(other: Money): Money;
|
|
66
|
+
/**
|
|
67
|
+
* Multiplies by a rate (tax, fee, quantity), rounding half-up to the minor unit.
|
|
68
|
+
*
|
|
69
|
+
* @param rate e.g. `"0.16"` or `3`.
|
|
70
|
+
* @returns The rounded product.
|
|
71
|
+
* @example Money.fromDecimalString('10.05', 'USD').mul('0.5').toDecimalString() // '5.03'
|
|
72
|
+
*/
|
|
73
|
+
mul(rate: DecimalInput): Money;
|
|
74
|
+
/**
|
|
75
|
+
* Splits the amount in proportion to `ratios`; the parts always sum to the amount exactly. Each part is
|
|
76
|
+
* rounded down to the minor unit and the left-over units go, one each, to the parts with the largest
|
|
77
|
+
* remainders (earlier parts win ties). A negative amount is split like its absolute value, then negated.
|
|
78
|
+
*
|
|
79
|
+
* @param ratios Non-negative weights with a positive sum, e.g. `['1', '1', '1']` or `[70, 30]`.
|
|
80
|
+
* @returns One amount per ratio, in order.
|
|
81
|
+
* @throws RangeError for an empty list, a negative ratio or a zero sum.
|
|
82
|
+
* @example Money.fromDecimalString('100.00', 'USD').allocate([1, 1, 1]).map(String) // ['33.34', '33.33', '33.33']
|
|
83
|
+
*/
|
|
84
|
+
allocate(ratios: readonly DecimalInput[]): Money[];
|
|
85
|
+
/**
|
|
86
|
+
* Rounds up (toward +∞) to a multiple of `unit`, e.g. whole units for a payment rail that rejects cents.
|
|
87
|
+
*
|
|
88
|
+
* @param unit Positive step in major units, e.g. `"1"` or `"0.05"`; defaults to `"1"`.
|
|
89
|
+
* @returns The rounded-up amount.
|
|
90
|
+
* @example Money.fromDecimalString('99.01', 'USD').ceilToUnit().toDecimalString() // '100.00'
|
|
91
|
+
*/
|
|
92
|
+
ceilToUnit(unit?: DecimalInput): Money;
|
|
93
|
+
/** -1, 0 or 1 comparing this amount with `other` (same currency). */
|
|
94
|
+
compare(other: Money): -1 | 0 | 1;
|
|
95
|
+
/** True when both amounts have the same currency and value. */
|
|
96
|
+
equals(other: Money): boolean;
|
|
97
|
+
/** True for zero. */
|
|
98
|
+
isZero(): boolean;
|
|
99
|
+
/** True below zero. */
|
|
100
|
+
isNegative(): boolean;
|
|
101
|
+
private same;
|
|
102
|
+
private round;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=money.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../../../src/common/money.ts"],"names":[],"mappings":"AAeA,oFAAoF;AACpF,MAAM,WAAW,YAAY;IAC3B,iCAAiC;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,+FAA+F;AAC/F,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,YAAY,CAAC;AAElD,qHAAqH;AACrH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;AAK3C;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,aAAa,GAAG,YAAY,CAWrE;AASD,iFAAiF;AACjF,qBAAa,KAAK;IAEd,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,6BAA6B;IAC7B,QAAQ,CAAC,QAAQ,EAAE,YAAY;IAHjC,OAAO;IAQP;;;;;;;;;OASG;IACH,MAAM,CAAC,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,GAAG,KAAK;IAUxE;;;;;;;OAOG;IACH,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,aAAa,GAAG,KAAK;IAM7E;;;;;OAKG;IACH,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,aAAa,GAAG,KAAK;IAI3C,2GAA2G;IAC3G,eAAe,IAAI,MAAM;IAIzB,qDAAqD;IACrD,YAAY,IAAI,MAAM;IAItB,8FAA8F;IAC9F,MAAM,IAAI,MAAM;IAIhB,uCAAuC;IACvC,QAAQ,IAAI,MAAM;IAIlB,+CAA+C;IAC/C,GAAG,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK;IAIxB,sDAAsD;IACtD,GAAG,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK;IAIxB;;;;;;OAMG;IACH,GAAG,CAAC,IAAI,EAAE,YAAY,GAAG,KAAK;IAI9B;;;;;;;;;OASG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,GAAG,KAAK,EAAE;IAsBlD;;;;;;OAMG;IACH,UAAU,CAAC,IAAI,GAAE,YAAkB,GAAG,KAAK;IAO3C,qEAAqE;IACrE,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;IAIjC,+DAA+D;IAC/D,MAAM,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO;IAI7B,qBAAqB;IACrB,MAAM,IAAI,OAAO;IAIjB,uBAAuB;IACvB,UAAU,IAAI,OAAO;IAIrB,OAAO,CAAC,IAAI;IAOZ,OAAO,CAAC,KAAK;CAGd"}
|
|
@@ -1,10 +1,49 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Normalises a phone number to E.164
|
|
3
|
-
*
|
|
2
|
+
* Normalises a phone number to E.164. The region is required (no app default): local formats such as
|
|
3
|
+
* `0712345678` only make sense against a country, and that country is app configuration.
|
|
4
|
+
*
|
|
5
|
+
* @param phone Number as typed (local or international format).
|
|
6
|
+
* @param region ISO 3166-1 alpha-2 region used for local formats (e.g. `GB`, `US`).
|
|
7
|
+
* @returns E.164 (`+447400123456`), or null when not a valid number for that region.
|
|
8
|
+
* @example toE164('07400 123456', 'GB') // '+447400123456'
|
|
9
|
+
*/
|
|
10
|
+
export declare function toE164(phone: string, region: string): string | null;
|
|
11
|
+
/** Options for {@link toMsisdn}. */
|
|
12
|
+
export interface MsisdnOptions {
|
|
13
|
+
/** Reject numbers that are not mobile (or mobile-or-fixed) lines, e.g. for SMS or mobile-money payers. */
|
|
14
|
+
mobileOnly?: boolean;
|
|
15
|
+
/** Reject numbers that belong to another country than `region` (a foreign `+…` number). */
|
|
16
|
+
regionOnly?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* E.164 without the plus sign (an MSISDN), as SMS gateways and mobile-money rails expect. Throws instead of
|
|
20
|
+
* returning null so a bad number can never be stored or sent by mistake; use {@link toE164} to test input.
|
|
21
|
+
*
|
|
22
|
+
* @param phone Number as typed (local or international format).
|
|
23
|
+
* @param region ISO 3166-1 alpha-2 region used for local formats (required; no app default).
|
|
24
|
+
* @param options Mobile-only and same-region checks.
|
|
25
|
+
* @returns Digits only, country code first.
|
|
26
|
+
* @throws BadRequestError (`INVALID_PHONE`) when the number is invalid or fails an option.
|
|
27
|
+
* @example toMsisdn('07400 123456', 'GB') // '447400123456'
|
|
28
|
+
*/
|
|
29
|
+
export declare function toMsisdn(phone: string, region: string, options?: MsisdnOptions): string;
|
|
30
|
+
/**
|
|
31
|
+
* The region a number belongs to.
|
|
32
|
+
*
|
|
33
|
+
* @param phone Number as typed.
|
|
34
|
+
* @param region ISO region used to interpret local formats.
|
|
35
|
+
* @returns ISO region code, or null when it cannot be determined.
|
|
36
|
+
* @example phoneRegion('+447400123456', 'US') // 'GB'
|
|
37
|
+
*/
|
|
38
|
+
export declare function phoneRegion(phone: string, region: string): string | null;
|
|
39
|
+
/**
|
|
40
|
+
* Masks a phone number for logs, audit metadata and UI: every digit except the last 4 becomes `*`, so a
|
|
41
|
+
* full number never lands in a log line or on a screen. Fewer than 5 digits are fully masked
|
|
42
|
+
* (the same rule the logger applies to phone-like values).
|
|
43
|
+
*
|
|
44
|
+
* @param phone Any phone string (normalised or not).
|
|
45
|
+
* @returns The masked digits.
|
|
46
|
+
* @example maskPhone('+44 7400 123456') // '********3456'
|
|
4
47
|
*/
|
|
5
|
-
export declare function toE164(phone: string, defaultRegion?: string): string | null;
|
|
6
|
-
/** E.164 without the plus sign, as most SMS gateways expect (`254712345678`). */
|
|
7
|
-
export declare function toMsisdn(phone: string, defaultRegion?: string): string | null;
|
|
8
|
-
export declare function phoneRegion(phone: string, defaultRegion?: string): string | null;
|
|
9
48
|
export declare function maskPhone(phone: string): string;
|
|
10
49
|
//# sourceMappingURL=phone.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"phone.d.ts","sourceRoot":"","sources":["../../../src/common/phone.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"phone.d.ts","sourceRoot":"","sources":["../../../src/common/phone.ts"],"names":[],"mappings":"AAMA;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOnE;AAED,oCAAoC;AACpC,MAAM,WAAW,aAAa;IAC5B,0GAA0G;IAC1G,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,2FAA2F;IAC3F,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAa3F;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMxE;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI/C"}
|