@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,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.makeUnavailableError = makeUnavailableError;
|
|
4
|
+
exports.createInternalClient = createInternalClient;
|
|
5
|
+
const async_js_1 = require("./async.js");
|
|
6
|
+
const circuit_breaker_js_1 = require("./circuit-breaker.js");
|
|
7
|
+
const errors_js_1 = require("./errors.js");
|
|
8
|
+
const internal_service_auth_js_1 = require("./internal-service-auth.js");
|
|
9
|
+
const index_js_1 = require("../config/index.js");
|
|
10
|
+
const index_js_2 = require("../context/index.js");
|
|
11
|
+
const index_js_3 = require("../identity/index.js");
|
|
12
|
+
const unavailableClasses = new Map();
|
|
13
|
+
const pascalCase = (value) => value.split(/[^A-Za-z0-9]+/).filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join('');
|
|
14
|
+
/**
|
|
15
|
+
* Returns the `{Target}UnavailableError` class for a target service (the same class on every call, so
|
|
16
|
+
* `instanceof` works anywhere in the process).
|
|
17
|
+
*
|
|
18
|
+
* @param target The target service name, e.g. `records-service`.
|
|
19
|
+
* @returns A class named e.g. `RecordsServiceUnavailableError`, extending `ServiceUnavailableError` (503).
|
|
20
|
+
* @example
|
|
21
|
+
* const RecordsUnavailable = makeUnavailableError('records-service');
|
|
22
|
+
* try { await records.get('/records/1'); } catch (e) { if (e instanceof RecordsUnavailable) useCachedCopy(); }
|
|
23
|
+
*/
|
|
24
|
+
function makeUnavailableError(target) {
|
|
25
|
+
const existing = unavailableClasses.get(target);
|
|
26
|
+
if (existing)
|
|
27
|
+
return existing;
|
|
28
|
+
const TargetUnavailableError = class extends errors_js_1.ServiceUnavailableError {
|
|
29
|
+
target = target;
|
|
30
|
+
constructor(message = `${target} is unavailable`, options = {}) {
|
|
31
|
+
super(message, { cause: options.cause });
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
// AppError takes `name` from the constructor, so naming the class names every instance.
|
|
35
|
+
Object.defineProperty(TargetUnavailableError, 'name', { value: `${pascalCase(target)}UnavailableError` });
|
|
36
|
+
unavailableClasses.set(target, TargetUnavailableError);
|
|
37
|
+
return TargetUnavailableError;
|
|
38
|
+
}
|
|
39
|
+
const RETRYABLE_METHODS = new Set(['GET']);
|
|
40
|
+
const RETRYABLE_STATUS = new Set([502, 503, 504]);
|
|
41
|
+
/** Why an attempt counted as unavailable; logged, never sent to the end user. */
|
|
42
|
+
class AttemptFailure extends Error {
|
|
43
|
+
reason;
|
|
44
|
+
retryable;
|
|
45
|
+
constructor(reason, retryable, options) {
|
|
46
|
+
super(reason, options);
|
|
47
|
+
this.reason = reason;
|
|
48
|
+
this.retryable = retryable;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Creates a client for one target service.
|
|
53
|
+
*
|
|
54
|
+
* @param options Target, base URL, caller credential, timeout, retry, breaker and logger settings.
|
|
55
|
+
* @returns An {@link InternalClient}.
|
|
56
|
+
* @throws ConfigError when the caller name or credential is missing.
|
|
57
|
+
* @example
|
|
58
|
+
* // src/clients/records.client.ts
|
|
59
|
+
* const http = createInternalClient({ target: 'records-service', baseUrl: config.RECORDS_INTERNAL_URL, logger });
|
|
60
|
+
* export const getRecord = (id: string) => http.get(`/records/${id}`, { schema: recordSchema });
|
|
61
|
+
*/
|
|
62
|
+
function createInternalClient(options) {
|
|
63
|
+
const caller = options.caller ?? (() => {
|
|
64
|
+
const env = (0, internal_service_auth_js_1.internalServiceAuthConfigFromEnv)();
|
|
65
|
+
return { name: env.name, secret: env.credential };
|
|
66
|
+
})();
|
|
67
|
+
if (!caller.name || !caller.secret) {
|
|
68
|
+
throw new index_js_1.ConfigError(`createInternalClient(${options.target}): INTERNAL_SERVICE_NAME and INTERNAL_SERVICE_CREDENTIAL are required`);
|
|
69
|
+
}
|
|
70
|
+
const UnavailableError = makeUnavailableError(options.target);
|
|
71
|
+
const breaker = new circuit_breaker_js_1.CircuitBreaker(options.breaker);
|
|
72
|
+
const doFetch = options.fetch ?? fetch;
|
|
73
|
+
const base = options.baseUrl.replace(/\/+$/, '');
|
|
74
|
+
const backoff = options.backoffMs?.length ? options.backoffMs : [100, 400];
|
|
75
|
+
const buildUrl = (path, query) => {
|
|
76
|
+
const url = new URL(base + (path.startsWith('/') ? path : `/${path}`));
|
|
77
|
+
for (const [key, value] of Object.entries(query ?? {}))
|
|
78
|
+
if (value !== undefined)
|
|
79
|
+
url.searchParams.set(key, String(value));
|
|
80
|
+
return url;
|
|
81
|
+
};
|
|
82
|
+
const buildHeaders = (opts) => {
|
|
83
|
+
const correlationId = opts.correlationId ?? (0, index_js_2.getRequestContext)()?.correlationId;
|
|
84
|
+
return {
|
|
85
|
+
accept: 'application/json',
|
|
86
|
+
...(opts.body !== undefined ? { 'content-type': 'application/json' } : {}),
|
|
87
|
+
...opts.identityHeaders,
|
|
88
|
+
...(correlationId ? { [index_js_3.CONTEXT_HEADERS.CORRELATION_ID]: correlationId } : {}),
|
|
89
|
+
...(opts.idempotencyKey ? { [index_js_3.CONTEXT_HEADERS.IDEMPOTENCY_KEY]: opts.idempotencyKey } : {}),
|
|
90
|
+
// Last, so nothing passed in can replace the caller's own credential.
|
|
91
|
+
...(0, internal_service_auth_js_1.internalServiceHeaders)(caller),
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
/** One HTTP attempt: resolves with status + body text for any answer below 500, throws AttemptFailure otherwise. */
|
|
95
|
+
const attempt = async (method, url, init, timeoutMs) => {
|
|
96
|
+
const controller = new AbortController();
|
|
97
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
98
|
+
try {
|
|
99
|
+
const response = await doFetch(url, { ...init, method, signal: controller.signal });
|
|
100
|
+
const text = await response.text();
|
|
101
|
+
if (response.status >= 500)
|
|
102
|
+
throw new AttemptFailure(`status ${response.status}`, RETRYABLE_STATUS.has(response.status));
|
|
103
|
+
return { status: response.status, text };
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
if (error instanceof AttemptFailure)
|
|
107
|
+
throw error;
|
|
108
|
+
throw new AttemptFailure(controller.signal.aborted ? 'timeout' : 'network error', true, { cause: error });
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
clearTimeout(timer);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
/** Runs attempts with GET-only retries; throws AttemptFailure when the target never answered below 500. */
|
|
115
|
+
const send = async (method, url, init, timeoutMs) => {
|
|
116
|
+
const retries = RETRYABLE_METHODS.has(method) ? options.retries ?? 2 : 0;
|
|
117
|
+
for (let n = 0;; n += 1) {
|
|
118
|
+
try {
|
|
119
|
+
return await attempt(method, url, init, timeoutMs);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
const failure = error;
|
|
123
|
+
if (!failure.retryable || n >= retries)
|
|
124
|
+
throw failure;
|
|
125
|
+
await (0, async_js_1.sleep)(backoff[Math.min(n, backoff.length - 1)] ?? 0);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
const unavailable = (method, url, reason, cause) => {
|
|
130
|
+
options.logger?.warn('Internal call unavailable', { target: options.target, method, path: url.pathname, reason });
|
|
131
|
+
return new UnavailableError(`${options.target} unavailable: ${reason}`, { cause });
|
|
132
|
+
};
|
|
133
|
+
async function request(method, path, opts = {}) {
|
|
134
|
+
const url = buildUrl(path, opts.query);
|
|
135
|
+
if (!breaker.tryAcquire())
|
|
136
|
+
throw unavailable(method, url, 'circuit open');
|
|
137
|
+
const init = { headers: buildHeaders(opts), body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined };
|
|
138
|
+
let answer;
|
|
139
|
+
try {
|
|
140
|
+
answer = await send(method, url, init, opts.timeoutMs ?? options.timeoutMs ?? 3000);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
breaker.onFailure();
|
|
144
|
+
throw unavailable(method, url, error.reason, error);
|
|
145
|
+
}
|
|
146
|
+
breaker.onSuccess();
|
|
147
|
+
let payload = {};
|
|
148
|
+
try {
|
|
149
|
+
payload = answer.text ? JSON.parse(answer.text) : {};
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
// A 4xx without a JSON body still maps to a plain AppError below; a 2xx must be readable.
|
|
153
|
+
if (answer.status < 400)
|
|
154
|
+
throw unavailable(method, url, 'unreadable response body', error);
|
|
155
|
+
}
|
|
156
|
+
if (answer.status >= 400) {
|
|
157
|
+
throw new errors_js_1.AppError(answer.status, payload.message || `${options.target} rejected the request`, { code: payload.code, errors: payload.errors });
|
|
158
|
+
}
|
|
159
|
+
if (!opts.schema)
|
|
160
|
+
return payload.data;
|
|
161
|
+
const parsed = opts.schema.safeParse(payload.data);
|
|
162
|
+
if (!parsed.success)
|
|
163
|
+
throw unavailable(method, url, 'response did not match the expected schema', parsed.error);
|
|
164
|
+
return parsed.data;
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
target: options.target,
|
|
168
|
+
UnavailableError,
|
|
169
|
+
get circuitState() { return breaker.state; },
|
|
170
|
+
get: (path, opts) => request('GET', path, opts),
|
|
171
|
+
post: (path, opts) => request('POST', path, opts),
|
|
172
|
+
put: (path, opts) => request('PUT', path, opts),
|
|
173
|
+
patch: (path, opts) => request('PATCH', path, opts),
|
|
174
|
+
delete: (path, opts) => request('DELETE', path, opts),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
//# 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,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Leaf constants for internal-service auth, shared by `common/internal-service-auth` and the `config` env blocks
|
|
4
|
+
* (kept dependency-free so neither module imports the other).
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.SERVICE_NAME_PATTERN = exports.MIN_INTERNAL_CREDENTIAL_LENGTH = void 0;
|
|
8
|
+
/** Shortest internal credential accepted in staging and production. */
|
|
9
|
+
exports.MIN_INTERNAL_CREDENTIAL_LENGTH = 32;
|
|
10
|
+
/** Service names are compose-style: lower-case letters, digits and hyphens, starting with a letter. */
|
|
11
|
+
exports.SERVICE_NAME_PATTERN = /^[a-z][a-z0-9-]{0,62}$/;
|
|
12
|
+
//# 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;AAC1D,QAAA,8BAA8B,GAAG,EAAE,CAAC;AACjD,uGAAuG;AAC1F,QAAA,oBAAoB,GAAG,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SERVICE_NAME_PATTERN = exports.MIN_INTERNAL_CREDENTIAL_LENGTH = exports.INTERNAL_SERVICE_SECRET_HEADER = exports.INTERNAL_SERVICE_NAME_HEADER = void 0;
|
|
4
|
+
exports.parseInternalServiceCredentials = parseInternalServiceCredentials;
|
|
5
|
+
exports.internalServiceAuthConfigFromEnv = internalServiceAuthConfigFromEnv;
|
|
6
|
+
exports.internalServiceHeaders = internalServiceHeaders;
|
|
7
|
+
exports.assertInternalServiceAuthConfigured = assertInternalServiceAuthConfigured;
|
|
8
|
+
exports.verifyInternalCaller = verifyInternalCaller;
|
|
9
|
+
/**
|
|
10
|
+
* Per-caller service-to-service credentials.
|
|
11
|
+
*
|
|
12
|
+
* Layer: plumbing (no HTTP framework). Key rules:
|
|
13
|
+
* - Every calling service has its **own** secret (`INTERNAL_SERVICE_CREDENTIAL`) and sends it with its name
|
|
14
|
+
* (`INTERNAL_SERVICE_NAME`). A receiving service holds `INTERNAL_SERVICE_CREDENTIALS`, a JSON map of
|
|
15
|
+
* caller name → the secret it accepts from that caller, and each internal route lists the callers it allows.
|
|
16
|
+
* One leaked credential therefore reaches only the routes meant for that one caller.
|
|
17
|
+
* - Secrets are compared in constant time (SHA-256 digests + `timingSafeEqual`, so lengths don't leak) and
|
|
18
|
+
* are never logged or put in error messages.
|
|
19
|
+
* - Staging and production refuse to start without the credentials a service needs
|
|
20
|
+
* ({@link assertInternalServiceAuthConfigured}); development and test may run without them.
|
|
21
|
+
*
|
|
22
|
+
* App-independent: caller names are whatever the app's compose service names are.
|
|
23
|
+
*/
|
|
24
|
+
const node_crypto_1 = require("node:crypto");
|
|
25
|
+
const index_js_1 = require("../config/index.js");
|
|
26
|
+
const index_js_2 = require("../identity/index.js");
|
|
27
|
+
const internal_constants_js_1 = require("./internal-constants.js");
|
|
28
|
+
/** Header carrying the calling service's name. */
|
|
29
|
+
exports.INTERNAL_SERVICE_NAME_HEADER = index_js_2.CONTEXT_HEADERS.INTERNAL_SERVICE_NAME;
|
|
30
|
+
/** Header carrying the calling service's own secret. */
|
|
31
|
+
exports.INTERNAL_SERVICE_SECRET_HEADER = index_js_2.CONTEXT_HEADERS.INTERNAL_SECRET;
|
|
32
|
+
var internal_constants_js_2 = require("./internal-constants.js");
|
|
33
|
+
Object.defineProperty(exports, "MIN_INTERNAL_CREDENTIAL_LENGTH", { enumerable: true, get: function () { return internal_constants_js_2.MIN_INTERNAL_CREDENTIAL_LENGTH; } });
|
|
34
|
+
Object.defineProperty(exports, "SERVICE_NAME_PATTERN", { enumerable: true, get: function () { return internal_constants_js_2.SERVICE_NAME_PATTERN; } });
|
|
35
|
+
/** Environments where missing or weak internal credentials stop the service from starting. */
|
|
36
|
+
const STRICT_ENVIRONMENTS = new Set(['staging', 'production']);
|
|
37
|
+
const read = (env, key) => {
|
|
38
|
+
const value = env[key];
|
|
39
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Parses `INTERNAL_SERVICE_CREDENTIALS` (a JSON object of caller name → secret).
|
|
43
|
+
*
|
|
44
|
+
* @param raw The env value; empty or undefined gives `{}`.
|
|
45
|
+
* @returns A frozen map of caller name → secret.
|
|
46
|
+
* @throws ConfigError when the JSON is malformed, not an object, a name is not a valid service name, or a
|
|
47
|
+
* secret is not a non-empty string. The message names the bad key, never a secret.
|
|
48
|
+
* @example parseInternalServiceCredentials('{"alpha-service":"<secret>"}') // { 'alpha-service': '<secret>' }
|
|
49
|
+
*/
|
|
50
|
+
function parseInternalServiceCredentials(raw) {
|
|
51
|
+
if (!raw?.trim())
|
|
52
|
+
return Object.freeze({});
|
|
53
|
+
let parsed;
|
|
54
|
+
try {
|
|
55
|
+
parsed = JSON.parse(raw);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
throw new index_js_1.ConfigError('INTERNAL_SERVICE_CREDENTIALS must be a JSON object of service name to secret');
|
|
59
|
+
}
|
|
60
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
61
|
+
throw new index_js_1.ConfigError('INTERNAL_SERVICE_CREDENTIALS must be a JSON object of service name to secret');
|
|
62
|
+
}
|
|
63
|
+
const credentials = {};
|
|
64
|
+
for (const [name, secret] of Object.entries(parsed)) {
|
|
65
|
+
if (!internal_constants_js_1.SERVICE_NAME_PATTERN.test(name))
|
|
66
|
+
throw new index_js_1.ConfigError(`INTERNAL_SERVICE_CREDENTIALS has an invalid service name: "${name}"`);
|
|
67
|
+
if (typeof secret !== 'string' || !secret)
|
|
68
|
+
throw new index_js_1.ConfigError(`INTERNAL_SERVICE_CREDENTIALS has an empty or non-string secret for "${name}"`);
|
|
69
|
+
credentials[name] = secret;
|
|
70
|
+
}
|
|
71
|
+
return Object.freeze(credentials);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Reads `INTERNAL_SERVICE_NAME`, `INTERNAL_SERVICE_CREDENTIAL` and `INTERNAL_SERVICE_CREDENTIALS`.
|
|
75
|
+
*
|
|
76
|
+
* @param env process.env (default) or a parsed config object.
|
|
77
|
+
* @returns The settings; missing values are empty.
|
|
78
|
+
* @throws ConfigError when `INTERNAL_SERVICE_CREDENTIALS` is malformed.
|
|
79
|
+
* @example const internalAuth = internalServiceAuthConfigFromEnv();
|
|
80
|
+
*/
|
|
81
|
+
function internalServiceAuthConfigFromEnv(env = process.env) {
|
|
82
|
+
return {
|
|
83
|
+
name: read(env, 'INTERNAL_SERVICE_NAME'),
|
|
84
|
+
credential: read(env, 'INTERNAL_SERVICE_CREDENTIAL'),
|
|
85
|
+
credentials: parseInternalServiceCredentials(read(env, 'INTERNAL_SERVICE_CREDENTIALS')),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The headers a service adds to every internal call it makes.
|
|
90
|
+
*
|
|
91
|
+
* @param caller This service's name and secret.
|
|
92
|
+
* @returns `{ 'x-internal-service-name', 'x-internal-service-secret' }`.
|
|
93
|
+
* @example fetch(url, { headers: { ...internalServiceHeaders({ name, secret }) } });
|
|
94
|
+
*/
|
|
95
|
+
function internalServiceHeaders(caller) {
|
|
96
|
+
return { [exports.INTERNAL_SERVICE_NAME_HEADER]: caller.name, [exports.INTERNAL_SERVICE_SECRET_HEADER]: caller.secret };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Startup guard. In staging and production it refuses to start when a calling service lacks a valid name or a
|
|
100
|
+
* credential of at least 32 characters, or when a serving service has no accepted callers or any accepted
|
|
101
|
+
* secret shorter than 32 characters. In other environments it only checks that what is set parses.
|
|
102
|
+
*
|
|
103
|
+
* @param options Whether the service calls and/or serves internal routes.
|
|
104
|
+
* @throws ConfigError listing every problem (names only, never secret values).
|
|
105
|
+
* @example assertInternalServiceAuthConfigured({ calls: true, serves: true });
|
|
106
|
+
*/
|
|
107
|
+
function assertInternalServiceAuthConfigured(options) {
|
|
108
|
+
const env = options.env ?? process.env;
|
|
109
|
+
const config = internalServiceAuthConfigFromEnv(env);
|
|
110
|
+
if (!STRICT_ENVIRONMENTS.has(options.nodeEnv ?? read(env, 'NODE_ENV')))
|
|
111
|
+
return;
|
|
112
|
+
const problems = [];
|
|
113
|
+
if (options.calls) {
|
|
114
|
+
if (!internal_constants_js_1.SERVICE_NAME_PATTERN.test(config.name))
|
|
115
|
+
problems.push('INTERNAL_SERVICE_NAME is missing or not a valid service name');
|
|
116
|
+
if (config.credential.length < internal_constants_js_1.MIN_INTERNAL_CREDENTIAL_LENGTH) {
|
|
117
|
+
problems.push(`INTERNAL_SERVICE_CREDENTIAL must be at least ${internal_constants_js_1.MIN_INTERNAL_CREDENTIAL_LENGTH} characters`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (options.serves) {
|
|
121
|
+
const callers = Object.keys(config.credentials);
|
|
122
|
+
if (!callers.length)
|
|
123
|
+
problems.push('INTERNAL_SERVICE_CREDENTIALS is empty: no internal caller can authenticate');
|
|
124
|
+
const weak = callers.filter((name) => (config.credentials[name] ?? '').length < internal_constants_js_1.MIN_INTERNAL_CREDENTIAL_LENGTH);
|
|
125
|
+
if (weak.length)
|
|
126
|
+
problems.push(`INTERNAL_SERVICE_CREDENTIALS has secrets shorter than ${internal_constants_js_1.MIN_INTERNAL_CREDENTIAL_LENGTH} characters for: ${weak.join(', ')}`);
|
|
127
|
+
}
|
|
128
|
+
if (problems.length)
|
|
129
|
+
throw new index_js_1.ConfigError(`Refusing to start: ${problems.join('; ')}`);
|
|
130
|
+
}
|
|
131
|
+
const digest = (value) => (0, node_crypto_1.createHash)('sha256').update(value).digest();
|
|
132
|
+
/** A fixed value to compare against when the caller is unknown, so unknown and known callers take the same time. */
|
|
133
|
+
const UNKNOWN_CALLER_DIGEST = digest('unknown-caller');
|
|
134
|
+
const header = (headers, name) => {
|
|
135
|
+
const value = headers[name];
|
|
136
|
+
return typeof value === 'string' ? value : '';
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* Checks the internal headers against the accepted credentials and the route's caller list.
|
|
140
|
+
* 401 when the name or secret is missing, the name is unknown or the secret is wrong; 403 when a correctly
|
|
141
|
+
* authenticated caller is not in `allowed`.
|
|
142
|
+
*
|
|
143
|
+
* @param input Request headers, accepted credentials and the route's allowed callers.
|
|
144
|
+
* @returns A verdict; the caller name is included when one was sent (safe to log), the secret never is.
|
|
145
|
+
* @example verifyInternalCaller({ headers: req.headers, credentials, allowed: ['alpha-service'] });
|
|
146
|
+
*/
|
|
147
|
+
function verifyInternalCaller(input) {
|
|
148
|
+
const caller = header(input.headers, exports.INTERNAL_SERVICE_NAME_HEADER);
|
|
149
|
+
const secret = header(input.headers, exports.INTERNAL_SERVICE_SECRET_HEADER);
|
|
150
|
+
if (!caller || !secret)
|
|
151
|
+
return { ok: false, status: 401, reason: 'missing-credentials', caller: caller || undefined };
|
|
152
|
+
const expected = Object.prototype.hasOwnProperty.call(input.credentials, caller) ? input.credentials[caller] : undefined;
|
|
153
|
+
const matches = (0, node_crypto_1.timingSafeEqual)(digest(secret), expected ? digest(expected) : UNKNOWN_CALLER_DIGEST) && expected !== undefined;
|
|
154
|
+
if (!matches)
|
|
155
|
+
return { ok: false, status: 401, reason: 'invalid-credentials', caller };
|
|
156
|
+
if (!input.allowed.includes(caller))
|
|
157
|
+
return { ok: false, status: 403, reason: 'caller-not-allowed', caller };
|
|
158
|
+
return { ok: true, caller };
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=internal-service-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal-service-auth.js","sourceRoot":"","sources":["../../../src/common/internal-service-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Money = void 0;
|
|
7
|
+
exports.resolveCurrency = resolveCurrency;
|
|
8
|
+
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
9
|
+
/**
|
|
10
|
+
* Exact money arithmetic. Amounts travel and persist as decimal strings (`"1250.00"`) and are only ever
|
|
11
|
+
* computed with a decimal type, never JS numbers. The currency is always passed in: shared-lib has no
|
|
12
|
+
* default currency.
|
|
13
|
+
*
|
|
14
|
+
* Rounding is half-up (half away from zero) to the currency's minor unit, after every operation that can
|
|
15
|
+
* produce extra digits (`mul`), so a `Money` value always holds a whole number of minor units.
|
|
16
|
+
*/
|
|
17
|
+
/** A decimal.js constructor with enough precision for any realistic amount and half-up rounding. */
|
|
18
|
+
const Dec = decimal_js_1.default.clone({ precision: 40, rounding: decimal_js_1.default.ROUND_HALF_UP, toExpNeg: -40, toExpPos: 40 });
|
|
19
|
+
const CODE_PATTERN = /^[A-Z]{3}$/;
|
|
20
|
+
const AMOUNT_PATTERN = /^-?\d+(\.\d+)?$/;
|
|
21
|
+
/**
|
|
22
|
+
* Resolves a currency code or spec, validating both.
|
|
23
|
+
*
|
|
24
|
+
* @param currency ISO 4217 code (minor units from `Intl`) or `{ code, minorUnits }`.
|
|
25
|
+
* @returns The spec.
|
|
26
|
+
* @throws RangeError for a malformed code or minor-unit count.
|
|
27
|
+
* @example resolveCurrency('USD') // { code: 'USD', minorUnits: 2 }
|
|
28
|
+
*/
|
|
29
|
+
function resolveCurrency(currency) {
|
|
30
|
+
if (typeof currency !== 'string') {
|
|
31
|
+
if (!CODE_PATTERN.test(currency.code))
|
|
32
|
+
throw new RangeError(`Invalid currency code "${currency.code}"`);
|
|
33
|
+
if (!Number.isInteger(currency.minorUnits) || currency.minorUnits < 0 || currency.minorUnits > 6) {
|
|
34
|
+
throw new RangeError(`Invalid minor units ${currency.minorUnits} for ${currency.code}`);
|
|
35
|
+
}
|
|
36
|
+
return { code: currency.code, minorUnits: currency.minorUnits };
|
|
37
|
+
}
|
|
38
|
+
if (!CODE_PATTERN.test(currency))
|
|
39
|
+
throw new RangeError(`Invalid currency code "${currency}"`);
|
|
40
|
+
const minorUnits = new Intl.NumberFormat('en', { style: 'currency', currency }).resolvedOptions().maximumFractionDigits ?? 2;
|
|
41
|
+
return { code: currency, minorUnits };
|
|
42
|
+
}
|
|
43
|
+
function toDecimal(value, what) {
|
|
44
|
+
if (typeof value === 'number' && !Number.isFinite(value))
|
|
45
|
+
throw new RangeError(`${what} must be finite`);
|
|
46
|
+
const text = String(value).trim();
|
|
47
|
+
if (!AMOUNT_PATTERN.test(text))
|
|
48
|
+
throw new RangeError(`${what} must be a decimal number, got "${value}"`);
|
|
49
|
+
return new Dec(text);
|
|
50
|
+
}
|
|
51
|
+
/** An immutable amount of one currency, always a whole number of minor units. */
|
|
52
|
+
class Money {
|
|
53
|
+
value;
|
|
54
|
+
currency;
|
|
55
|
+
constructor(value,
|
|
56
|
+
/** The amount's currency. */
|
|
57
|
+
currency) {
|
|
58
|
+
this.value = value;
|
|
59
|
+
this.currency = currency;
|
|
60
|
+
Object.freeze(this);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Parses a decimal string. Strict: no thousands separators, symbols or more decimals than the
|
|
64
|
+
* currency allows (D5), so a stored value is never silently rounded on the way in.
|
|
65
|
+
*
|
|
66
|
+
* @param amount e.g. `"1250.5"` or `"-3.25"`.
|
|
67
|
+
* @param currency Code or spec.
|
|
68
|
+
* @returns The amount.
|
|
69
|
+
* @throws RangeError for anything else.
|
|
70
|
+
* @example Money.fromDecimalString('19.99', 'USD')
|
|
71
|
+
*/
|
|
72
|
+
static fromDecimalString(amount, currency) {
|
|
73
|
+
const spec = resolveCurrency(currency);
|
|
74
|
+
if (typeof amount !== 'string')
|
|
75
|
+
throw new RangeError('Amount must be a decimal string');
|
|
76
|
+
const value = toDecimal(amount, 'Amount');
|
|
77
|
+
if (value.decimalPlaces() > spec.minorUnits) {
|
|
78
|
+
throw new RangeError(`Amount "${amount}" has more than ${spec.minorUnits} decimal places for ${spec.code}`);
|
|
79
|
+
}
|
|
80
|
+
return new Money(value, spec);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Builds an amount from an integer count of minor units (e.g. cents).
|
|
84
|
+
*
|
|
85
|
+
* @param units Integer (number within the safe range, or bigint).
|
|
86
|
+
* @param currency Code or spec.
|
|
87
|
+
* @returns The amount.
|
|
88
|
+
* @example Money.fromMinorUnits(1999, 'USD').toDecimalString() // '19.99'
|
|
89
|
+
*/
|
|
90
|
+
static fromMinorUnits(units, currency) {
|
|
91
|
+
const spec = resolveCurrency(currency);
|
|
92
|
+
if (typeof units === 'number' && !Number.isSafeInteger(units))
|
|
93
|
+
throw new RangeError('Minor units must be a safe integer');
|
|
94
|
+
return new Money(new Dec(units.toString()).div(new Dec(10).pow(spec.minorUnits)), spec);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Zero in a currency.
|
|
98
|
+
*
|
|
99
|
+
* @param currency Code or spec.
|
|
100
|
+
* @returns `0` of that currency.
|
|
101
|
+
*/
|
|
102
|
+
static zero(currency) {
|
|
103
|
+
return new Money(new Dec(0), resolveCurrency(currency));
|
|
104
|
+
}
|
|
105
|
+
/** The amount with exactly the currency's decimal places, e.g. `"1250.00"` — the wire and storage form. */
|
|
106
|
+
toDecimalString() {
|
|
107
|
+
return this.value.toFixed(this.currency.minorUnits);
|
|
108
|
+
}
|
|
109
|
+
/** The amount as an integer count of minor units. */
|
|
110
|
+
toMinorUnits() {
|
|
111
|
+
return BigInt(this.value.times(new Dec(10).pow(this.currency.minorUnits)).toFixed(0));
|
|
112
|
+
}
|
|
113
|
+
/** Serialises as the decimal string, so an amount never becomes a JSON number by accident. */
|
|
114
|
+
toJSON() {
|
|
115
|
+
return this.toDecimalString();
|
|
116
|
+
}
|
|
117
|
+
/** Same as {@link toDecimalString}. */
|
|
118
|
+
toString() {
|
|
119
|
+
return this.toDecimalString();
|
|
120
|
+
}
|
|
121
|
+
/** Sum of two amounts of the same currency. */
|
|
122
|
+
add(other) {
|
|
123
|
+
return new Money(this.value.plus(this.same(other).value), this.currency);
|
|
124
|
+
}
|
|
125
|
+
/** Difference of two amounts of the same currency. */
|
|
126
|
+
sub(other) {
|
|
127
|
+
return new Money(this.value.minus(this.same(other).value), this.currency);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Multiplies by a rate (tax, fee, quantity), rounding half-up to the minor unit.
|
|
131
|
+
*
|
|
132
|
+
* @param rate e.g. `"0.16"` or `3`.
|
|
133
|
+
* @returns The rounded product.
|
|
134
|
+
* @example Money.fromDecimalString('10.05', 'USD').mul('0.5').toDecimalString() // '5.03'
|
|
135
|
+
*/
|
|
136
|
+
mul(rate) {
|
|
137
|
+
return new Money(this.round(this.value.times(toDecimal(rate, 'Rate'))), this.currency);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Splits the amount in proportion to `ratios`; the parts always sum to the amount exactly. Each part is
|
|
141
|
+
* rounded down to the minor unit and the left-over units go, one each, to the parts with the largest
|
|
142
|
+
* remainders (earlier parts win ties). A negative amount is split like its absolute value, then negated.
|
|
143
|
+
*
|
|
144
|
+
* @param ratios Non-negative weights with a positive sum, e.g. `['1', '1', '1']` or `[70, 30]`.
|
|
145
|
+
* @returns One amount per ratio, in order.
|
|
146
|
+
* @throws RangeError for an empty list, a negative ratio or a zero sum.
|
|
147
|
+
* @example Money.fromDecimalString('100.00', 'USD').allocate([1, 1, 1]).map(String) // ['33.34', '33.33', '33.33']
|
|
148
|
+
*/
|
|
149
|
+
allocate(ratios) {
|
|
150
|
+
if (ratios.length === 0)
|
|
151
|
+
throw new RangeError('allocate needs at least one ratio');
|
|
152
|
+
const weights = ratios.map((r) => toDecimal(r, 'Ratio'));
|
|
153
|
+
if (weights.some((w) => w.isNegative()))
|
|
154
|
+
throw new RangeError('Ratios must not be negative');
|
|
155
|
+
const sum = weights.reduce((acc, w) => acc.plus(w), new Dec(0));
|
|
156
|
+
if (sum.isZero())
|
|
157
|
+
throw new RangeError('Ratios must not all be zero');
|
|
158
|
+
const scale = new Dec(10).pow(this.currency.minorUnits);
|
|
159
|
+
const total = this.value.abs().times(scale);
|
|
160
|
+
const exact = weights.map((w) => total.times(w).div(sum));
|
|
161
|
+
const floors = exact.map((e) => e.floor());
|
|
162
|
+
let leftover = total.minus(floors.reduce((acc, f) => acc.plus(f), new Dec(0))).toNumber();
|
|
163
|
+
const order = exact.map((e, i) => ({ i, rem: e.minus(floors[i]) })).sort((a, b) => b.rem.comparedTo(a.rem) || a.i - b.i);
|
|
164
|
+
for (const { i } of order) {
|
|
165
|
+
if (leftover <= 0)
|
|
166
|
+
break;
|
|
167
|
+
floors[i] = floors[i].plus(1);
|
|
168
|
+
leftover -= 1;
|
|
169
|
+
}
|
|
170
|
+
const sign = this.value.isNegative() ? -1 : 1;
|
|
171
|
+
return floors.map((units) => new Money(units.div(scale).times(sign), this.currency));
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Rounds up (toward +∞) to a multiple of `unit`, e.g. whole units for a payment rail that rejects cents.
|
|
175
|
+
*
|
|
176
|
+
* @param unit Positive step in major units, e.g. `"1"` or `"0.05"`; defaults to `"1"`.
|
|
177
|
+
* @returns The rounded-up amount.
|
|
178
|
+
* @example Money.fromDecimalString('99.01', 'USD').ceilToUnit().toDecimalString() // '100.00'
|
|
179
|
+
*/
|
|
180
|
+
ceilToUnit(unit = '1') {
|
|
181
|
+
const step = toDecimal(unit, 'Unit');
|
|
182
|
+
if (step.lte(0))
|
|
183
|
+
throw new RangeError('Unit must be positive');
|
|
184
|
+
if (step.decimalPlaces() > this.currency.minorUnits)
|
|
185
|
+
throw new RangeError(`Unit is finer than the ${this.currency.code} minor unit`);
|
|
186
|
+
return new Money(this.value.div(step).ceil().times(step), this.currency);
|
|
187
|
+
}
|
|
188
|
+
/** -1, 0 or 1 comparing this amount with `other` (same currency). */
|
|
189
|
+
compare(other) {
|
|
190
|
+
return this.value.comparedTo(this.same(other).value);
|
|
191
|
+
}
|
|
192
|
+
/** True when both amounts have the same currency and value. */
|
|
193
|
+
equals(other) {
|
|
194
|
+
return this.currency.code === other.currency.code && this.value.equals(other.value);
|
|
195
|
+
}
|
|
196
|
+
/** True for zero. */
|
|
197
|
+
isZero() {
|
|
198
|
+
return this.value.isZero();
|
|
199
|
+
}
|
|
200
|
+
/** True below zero. */
|
|
201
|
+
isNegative() {
|
|
202
|
+
return this.value.isNegative() && !this.value.isZero();
|
|
203
|
+
}
|
|
204
|
+
same(other) {
|
|
205
|
+
if (other.currency.code !== this.currency.code) {
|
|
206
|
+
throw new RangeError(`Currency mismatch: ${this.currency.code} vs ${other.currency.code}`);
|
|
207
|
+
}
|
|
208
|
+
return other;
|
|
209
|
+
}
|
|
210
|
+
round(value) {
|
|
211
|
+
return value.toDecimalPlaces(this.currency.minorUnits, decimal_js_1.default.ROUND_HALF_UP);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
exports.Money = Money;
|
|
215
|
+
//# sourceMappingURL=money.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money.js","sourceRoot":"","sources":["../../../src/common/money.ts"],"names":[],"mappings":"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"}
|