@heavybit/shared-lib 1.3.2 → 2.1.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 +100 -0
- package/dist/cjs/common/errors.js +44 -5
- package/dist/cjs/common/errors.js.map +1 -1
- package/dist/cjs/common/phone.js +43 -10
- 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/config/index.js +1 -1
- 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/express/auth.js +44 -11
- package/dist/cjs/express/auth.js.map +1 -1
- package/dist/cjs/express/index.js +1 -0
- package/dist/cjs/express/index.js.map +1 -1
- 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/index.js +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/logging/index.js +76 -40
- package/dist/cjs/logging/index.js.map +1 -1
- package/dist/cjs/logging/redact.js +117 -0
- package/dist/cjs/logging/redact.js.map +1 -0
- package/dist/cjs/messaging/index.js +1 -0
- package/dist/cjs/messaging/index.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/validation/index.js +27 -3
- package/dist/cjs/validation/index.js.map +1 -1
- package/dist/esm/common/errors.js +42 -4
- package/dist/esm/common/errors.js.map +1 -1
- package/dist/esm/common/phone.js +43 -10
- 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/config/index.js +1 -1
- 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/express/auth.js +45 -12
- package/dist/esm/express/auth.js.map +1 -1
- package/dist/esm/express/index.js +1 -0
- package/dist/esm/express/index.js.map +1 -1
- 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/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/logging/index.js +68 -39
- package/dist/esm/logging/index.js.map +1 -1
- package/dist/esm/logging/redact.js +110 -0
- package/dist/esm/logging/redact.js.map +1 -0
- package/dist/esm/messaging/index.js +1 -0
- package/dist/esm/messaging/index.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/validation/index.js +27 -2
- package/dist/esm/validation/index.js.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/cache/index.d.ts +1 -1
- package/dist/types/cache/index.d.ts.map +1 -1
- package/dist/types/common/errors.d.ts +48 -7
- package/dist/types/common/errors.d.ts.map +1 -1
- package/dist/types/common/phone.d.ts +35 -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/config/index.d.ts +1 -1
- 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 +208 -90
- package/dist/types/contracts/notification-dispatch.d.ts.map +1 -1
- package/dist/types/express/auth.d.ts +42 -6
- package/dist/types/express/auth.d.ts.map +1 -1
- package/dist/types/express/index.d.ts +1 -0
- package/dist/types/express/index.d.ts.map +1 -1
- 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/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- 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/index.d.ts +1 -0
- package/dist/types/messaging/index.d.ts.map +1 -1
- package/dist/types/messaging/staff-notifier.d.ts +67 -36
- 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/types/index.d.ts +36 -1
- 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 +1 -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
package/README.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# @heavybit/shared-lib
|
|
2
|
+
|
|
3
|
+
Building blocks for Express/TypeScript microservices that any app can use: response envelope and
|
|
4
|
+
errors, gateway identity, access scoping, validation, logging, config, RabbitMQ
|
|
5
|
+
(events/commands/RPC, outbox, idempotent consumers, DLQs), cache-aside, rate limiting, sockets,
|
|
6
|
+
signing, metrics and scheduling.
|
|
7
|
+
|
|
8
|
+
## Rule: shared concerns only
|
|
9
|
+
|
|
10
|
+
This package must drop into **any** service of **any** app unchanged.
|
|
11
|
+
|
|
12
|
+
- **In:** plumbing and contracts that every service needs in the same form, such as the error
|
|
13
|
+
envelope, signed identity headers, the messaging envelope and key builders.
|
|
14
|
+
- **Out:** anything one app or one service owns, such as domain events, payload schemas, permission
|
|
15
|
+
names, money or tax rules, and app names. Those live in the app's services or in a thin app-level
|
|
16
|
+
package built on top of this one.
|
|
17
|
+
- App-specific values come in as **parameters or config** (app slug, key prefixes, queue prefixes,
|
|
18
|
+
default phone region), never as hardcoded constants.
|
|
19
|
+
- A change that only one app needs is not made here.
|
|
20
|
+
|
|
21
|
+
## Install and build
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm ci
|
|
25
|
+
npm run build # ESM + CJS + .d.ts into dist/
|
|
26
|
+
npm test # jest --runInBand
|
|
27
|
+
npx tsc --noEmit
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Export map
|
|
31
|
+
|
|
32
|
+
| Import path | What it holds |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `@heavybit/shared-lib` | Root: `types`, `common`, `identity`, `access`, `validation`, `logging`, `config`, `contracts`, `alerting` (no optional peers) |
|
|
35
|
+
| `@heavybit/shared-lib/express` | `correlationMiddleware`, `correlationId`, `validate`, `asyncHandler`, `errorHandler`, `notFoundHandler`, `ok`, `created`, `okPage`, gateway auth guards, health and security middleware |
|
|
36
|
+
| `@heavybit/shared-lib/http` | SSRF-guarded `safeFetch` |
|
|
37
|
+
| `@heavybit/shared-lib/messaging` | RabbitMQ connection, publisher, consumer, RPC, outbox, idempotency, topology |
|
|
38
|
+
| `@heavybit/shared-lib/cache` · `/database` · `/knex` · `/rate-limit` · `/socket` · `/security` · `/metrics` · `/scheduling` · `/testing` | As named |
|
|
39
|
+
|
|
40
|
+
### Response envelope and errors (`common`, `express`)
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { createLogger, httpError, NotFoundError, TooManyRequestsError } from '@heavybit/shared-lib';
|
|
44
|
+
import { correlationMiddleware, errorHandler, notFoundHandler, ok, created, okPage } from '@heavybit/shared-lib/express';
|
|
45
|
+
|
|
46
|
+
const logger = createLogger('orders');
|
|
47
|
+
app.use(correlationMiddleware('orders')); // 1. first: correlation id + request context
|
|
48
|
+
app.use(express.json()); // 2. body parser
|
|
49
|
+
// 3. …routers…
|
|
50
|
+
app.use(notFoundHandler()); // 4.
|
|
51
|
+
app.use(errorHandler(logger)); // 5. last
|
|
52
|
+
|
|
53
|
+
ok(res, data); // 200 { success: true, message, data }
|
|
54
|
+
created(res, { id }); // 201
|
|
55
|
+
okPage(res, { data, meta }); // 200 with meta { limit, nextCursor, hasMore }
|
|
56
|
+
throw httpError(409, 'SLUG_TAKEN', 'That address is already in use');
|
|
57
|
+
throw new TooManyRequestsError(undefined, { retryAfterSeconds: 30 }); // sets Retry-After
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Error body: `{ success: false, code, message, errors?, requestId }`.
|
|
61
|
+
|
|
62
|
+
- 4xx responses keep their message.
|
|
63
|
+
- Every 5xx response, and anything that is not an `AppError`, gets a generic message and is logged in full.
|
|
64
|
+
- A thrown `ZodError` becomes 422 `VALIDATION_FAILED` with `errors` keyed by field path.
|
|
65
|
+
|
|
66
|
+
| Status | Default code | Class |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| 400 | `BAD_REQUEST` | `BadRequestError` |
|
|
69
|
+
| 401 | `UNAUTHENTICATED` | `UnauthorizedError` |
|
|
70
|
+
| 403 | `FORBIDDEN` | `ForbiddenError` |
|
|
71
|
+
| 404 | `NOT_FOUND` | `NotFoundError` |
|
|
72
|
+
| 409 | `CONFLICT` | `ConflictError` |
|
|
73
|
+
| 422 | `VALIDATION_FAILED` | `ValidationError`, `NonRetryableError` |
|
|
74
|
+
| 429 | `RATE_LIMITED` | `TooManyRequestsError` |
|
|
75
|
+
| 503 | `SERVICE_UNAVAILABLE` | `ServiceUnavailableError` |
|
|
76
|
+
| 5xx | `INTERNAL_ERROR` | anything else |
|
|
77
|
+
|
|
78
|
+
See [CHANGELOG.md](CHANGELOG.md).
|
|
79
|
+
|
|
80
|
+
### Request context, validation and logging (`context`, `express`, `logging`)
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import { createLogger, getRequestContext, runWithContext, updateRequestContext, idParam, z } from '@heavybit/shared-lib';
|
|
84
|
+
import { validate } from '@heavybit/shared-lib/express';
|
|
85
|
+
|
|
86
|
+
router.patch('/:id', validate({ params: idParam, query: listQuery, body: updateBody }), controller.update);
|
|
87
|
+
updateRequestContext({ userId: user.id, scopes: { tenant: tenantId } }); // auth middleware
|
|
88
|
+
logger.info('Order placed', { orderId }); // line carries correlationId, userId, scopes automatically
|
|
89
|
+
await runWithContext({ correlationId: msg.correlationId, service: 'worker' }, () => handle(msg)); // consumers/jobs
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- `correlationMiddleware` keeps an incoming `x-correlation-id` only if it is 8–64 of `[A-Za-z0-9-]`, else makes a ULID.
|
|
93
|
+
- `validate` replaces `req.params/query/body` with parsed values (unknown keys stripped; `.strict()` rejects them) and
|
|
94
|
+
throws one 422 with every part's field errors (`params.id`, `query.limit`, body fields unprefixed).
|
|
95
|
+
- `createLogger(service, { redactKeys?, allowKeys?, filePath?, stream? })` writes JSON lines
|
|
96
|
+
`{ level, time, msg, service, correlationId, userId, scopes, ...meta }`. Keys containing the words password,
|
|
97
|
+
pass, token, secret, code, otp, authorization, cookie, credential, pin, apikey, jwt or signature are
|
|
98
|
+
`[REDACTED]` at any depth (except `errorCode`, `statusCode`, `countryCode`, `currencyCode`, `postalCode`,
|
|
99
|
+
`zipCode`); phone numbers are masked to their last 4 digits.
|
|
100
|
+
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.NonRetryableError = exports.ServiceUnavailableError = exports.TooManyRequestsError = exports.ValidationError = exports.ConflictError = exports.NotFoundError = exports.ForbiddenError = exports.UnauthorizedError = exports.BadRequestError = exports.AppError = void 0;
|
|
3
|
+
exports.GENERIC_ERROR_MESSAGE = exports.NonRetryableError = exports.ServiceUnavailableError = exports.TooManyRequestsError = exports.ValidationError = exports.ConflictError = exports.NotFoundError = exports.ForbiddenError = exports.UnauthorizedError = exports.BadRequestError = exports.AppError = void 0;
|
|
4
|
+
exports.httpError = httpError;
|
|
4
5
|
exports.isAppError = isAppError;
|
|
5
6
|
exports.publicMessage = publicMessage;
|
|
6
7
|
/**
|
|
7
8
|
* Base class for errors that map to an HTTP status. Throw these from services; the shared
|
|
8
9
|
* error middleware turns them into the standard envelope. Anything that is not an AppError is
|
|
9
10
|
* reported as a 500 with a generic message so internals never leak.
|
|
11
|
+
*
|
|
12
|
+
* @param statusCode HTTP status. 4xx messages reach the caller; 5xx messages never do.
|
|
13
|
+
* @param message Caller-facing message (4xx) or log message (5xx).
|
|
14
|
+
* @param options `code` (defaults from the status), field `errors`, `retryable` (consumers),
|
|
15
|
+
* `retryAfterSeconds` (sent as `Retry-After`) and `cause`.
|
|
16
|
+
* @example throw new AppError(402, 'Payment is required first', { code: 'PAYMENT_REQUIRED' });
|
|
10
17
|
*/
|
|
11
18
|
class AppError extends Error {
|
|
12
19
|
statusCode;
|
|
@@ -14,6 +21,8 @@ class AppError extends Error {
|
|
|
14
21
|
errors;
|
|
15
22
|
/** Consumers dead-letter immediately instead of retrying when this is false. */
|
|
16
23
|
retryable;
|
|
24
|
+
/** Seconds the caller should wait before retrying; the error middleware sends it as `Retry-After`. */
|
|
25
|
+
retryAfterSeconds;
|
|
17
26
|
constructor(statusCode, message, options = {}) {
|
|
18
27
|
super(message, options.cause ? { cause: options.cause } : undefined);
|
|
19
28
|
this.name = new.target.name;
|
|
@@ -21,9 +30,23 @@ class AppError extends Error {
|
|
|
21
30
|
this.code = options.code ?? defaultCode(statusCode);
|
|
22
31
|
this.errors = options.errors;
|
|
23
32
|
this.retryable = options.retryable ?? statusCode >= 500;
|
|
33
|
+
this.retryAfterSeconds = options.retryAfterSeconds;
|
|
24
34
|
}
|
|
25
35
|
}
|
|
26
36
|
exports.AppError = AppError;
|
|
37
|
+
/**
|
|
38
|
+
* Shorthand for an error with an explicit status and code.
|
|
39
|
+
*
|
|
40
|
+
* @param statusCode HTTP status (4xx messages reach the caller; 5xx are replaced with a generic message).
|
|
41
|
+
* @param code UPPER_SNAKE_CASE machine-readable code, e.g. `SLUG_TAKEN`.
|
|
42
|
+
* @param message Human-readable message for 4xx responses.
|
|
43
|
+
* @param errors Optional field errors (`{ 'items.0.qty': ['Must be positive'] }`).
|
|
44
|
+
* @returns An {@link AppError} to `throw`.
|
|
45
|
+
* @example throw httpError(409, 'SLUG_TAKEN', 'That address is already in use');
|
|
46
|
+
*/
|
|
47
|
+
function httpError(statusCode, code, message, errors) {
|
|
48
|
+
return new AppError(statusCode, message, { code, errors });
|
|
49
|
+
}
|
|
27
50
|
function defaultCode(status) {
|
|
28
51
|
switch (status) {
|
|
29
52
|
case 400: return 'BAD_REQUEST';
|
|
@@ -37,34 +60,42 @@ function defaultCode(status) {
|
|
|
37
60
|
default: return status >= 500 ? 'INTERNAL_ERROR' : 'ERROR';
|
|
38
61
|
}
|
|
39
62
|
}
|
|
63
|
+
/** 400 BAD_REQUEST: the request is malformed or breaks a business rule. */
|
|
40
64
|
class BadRequestError extends AppError {
|
|
41
65
|
constructor(message = 'Bad request', options = {}) { super(400, message, options); }
|
|
42
66
|
}
|
|
43
67
|
exports.BadRequestError = BadRequestError;
|
|
68
|
+
/** 401 UNAUTHENTICATED: no valid identity. */
|
|
44
69
|
class UnauthorizedError extends AppError {
|
|
45
70
|
constructor(message = 'Authentication required', options = {}) { super(401, message, options); }
|
|
46
71
|
}
|
|
47
72
|
exports.UnauthorizedError = UnauthorizedError;
|
|
73
|
+
/** 403 FORBIDDEN: authenticated, but not allowed. */
|
|
48
74
|
class ForbiddenError extends AppError {
|
|
49
75
|
constructor(message = 'You do not have permission to perform this action', options = {}) { super(403, message, options); }
|
|
50
76
|
}
|
|
51
77
|
exports.ForbiddenError = ForbiddenError;
|
|
78
|
+
/** 404 NOT_FOUND. Also use it for out-of-scope resources so their existence is not revealed. */
|
|
52
79
|
class NotFoundError extends AppError {
|
|
53
80
|
constructor(message = 'Resource not found', options = {}) { super(404, message, options); }
|
|
54
81
|
}
|
|
55
82
|
exports.NotFoundError = NotFoundError;
|
|
83
|
+
/** 409 CONFLICT: duplicate or state conflict. */
|
|
56
84
|
class ConflictError extends AppError {
|
|
57
85
|
constructor(message = 'Resource already exists', options = {}) { super(409, message, options); }
|
|
58
86
|
}
|
|
59
87
|
exports.ConflictError = ConflictError;
|
|
88
|
+
/** 422 VALIDATION_FAILED with field errors (`{ 'items.0.qty': ['…'] }`). */
|
|
60
89
|
class ValidationError extends AppError {
|
|
61
90
|
constructor(errors, message = 'Validation failed') { super(422, message, { errors }); }
|
|
62
91
|
}
|
|
63
92
|
exports.ValidationError = ValidationError;
|
|
93
|
+
/** 429. Pass `retryAfterSeconds` so the error middleware sets the `Retry-After` header. */
|
|
64
94
|
class TooManyRequestsError extends AppError {
|
|
65
95
|
constructor(message = 'Too many requests. Please try again later.', options = {}) { super(429, message, options); }
|
|
66
96
|
}
|
|
67
97
|
exports.TooManyRequestsError = TooManyRequestsError;
|
|
98
|
+
/** 503 SERVICE_UNAVAILABLE: a dependency is down. The message is logged, never sent (callers see the generic one). */
|
|
68
99
|
class ServiceUnavailableError extends AppError {
|
|
69
100
|
constructor(message = 'Service temporarily unavailable', options = {}) { super(503, message, options); }
|
|
70
101
|
}
|
|
@@ -76,15 +107,23 @@ class NonRetryableError extends AppError {
|
|
|
76
107
|
}
|
|
77
108
|
}
|
|
78
109
|
exports.NonRetryableError = NonRetryableError;
|
|
110
|
+
/** Type guard for {@link AppError} (and its subclasses). */
|
|
79
111
|
function isAppError(error) {
|
|
80
112
|
return error instanceof AppError;
|
|
81
113
|
}
|
|
82
|
-
/**
|
|
114
|
+
/** The message every 5xx response carries. */
|
|
115
|
+
exports.GENERIC_ERROR_MESSAGE = 'Internal server error';
|
|
116
|
+
/**
|
|
117
|
+
* Message safe to show a caller. Only 4xx AppErrors keep their own message; every 5xx (including
|
|
118
|
+
* 503s, whose messages often name upstreams or RPC methods) and every non-AppError get the generic one.
|
|
119
|
+
*
|
|
120
|
+
* @param error Anything thrown.
|
|
121
|
+
* @returns The message to put in the response body.
|
|
122
|
+
* @example publicMessage(new NotFoundError('Order not found')) // 'Order not found'
|
|
123
|
+
*/
|
|
83
124
|
function publicMessage(error) {
|
|
84
125
|
if (isAppError(error) && error.statusCode < 500)
|
|
85
126
|
return error.message;
|
|
86
|
-
|
|
87
|
-
return error.message;
|
|
88
|
-
return 'Internal server error';
|
|
127
|
+
return exports.GENERIC_ERROR_MESSAGE;
|
|
89
128
|
}
|
|
90
129
|
//# sourceMappingURL=errors.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/common/errors.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/common/errors.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/common/phone.js
CHANGED
|
@@ -11,32 +11,65 @@ const google_libphonenumber_1 = __importDefault(require("google-libphonenumber")
|
|
|
11
11
|
const util = google_libphonenumber_1.default.PhoneNumberUtil.getInstance();
|
|
12
12
|
const { PhoneNumberFormat } = google_libphonenumber_1.default;
|
|
13
13
|
/**
|
|
14
|
-
* Normalises a phone number to E.164
|
|
15
|
-
*
|
|
14
|
+
* Normalises a phone number to E.164. The region is required (no app default): local formats such as
|
|
15
|
+
* `0712345678` only make sense against a country, and that country is app configuration.
|
|
16
|
+
*
|
|
17
|
+
* @param phone Number as typed (local or international format).
|
|
18
|
+
* @param region ISO 3166-1 alpha-2 region used for local formats (e.g. `KE`, `GB`).
|
|
19
|
+
* @returns E.164 (`+254712345678`), or null when not a valid number for that region.
|
|
20
|
+
* @example toE164('0712345678', 'KE') // '+254712345678'
|
|
16
21
|
*/
|
|
17
|
-
function toE164(phone,
|
|
22
|
+
function toE164(phone, region) {
|
|
18
23
|
try {
|
|
19
|
-
const parsed = util.parseAndKeepRawInput(phone.trim(),
|
|
24
|
+
const parsed = util.parseAndKeepRawInput(phone.trim(), region);
|
|
20
25
|
return util.isValidNumber(parsed) ? util.format(parsed, PhoneNumberFormat.E164) : null;
|
|
21
26
|
}
|
|
22
27
|
catch {
|
|
23
28
|
return null;
|
|
24
29
|
}
|
|
25
30
|
}
|
|
26
|
-
/**
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
/**
|
|
32
|
+
* E.164 without the plus sign, as most SMS gateways expect.
|
|
33
|
+
*
|
|
34
|
+
* @param phone Number as typed.
|
|
35
|
+
* @param region ISO region for local formats.
|
|
36
|
+
* @returns Digits only, or null when invalid.
|
|
37
|
+
* @example toMsisdn('0712345678', 'KE') // '254712345678'
|
|
38
|
+
*/
|
|
39
|
+
function toMsisdn(phone, region) {
|
|
40
|
+
const e164 = toE164(phone, region);
|
|
29
41
|
return e164 ? e164.slice(1) : null;
|
|
30
42
|
}
|
|
31
|
-
|
|
43
|
+
/**
|
|
44
|
+
* The region a number belongs to.
|
|
45
|
+
*
|
|
46
|
+
* @param phone Number as typed.
|
|
47
|
+
* @param region ISO region used to interpret local formats.
|
|
48
|
+
* @returns ISO region code, or null when it cannot be determined.
|
|
49
|
+
* @example phoneRegion('+447400123456', 'KE') // 'GB'
|
|
50
|
+
*/
|
|
51
|
+
function phoneRegion(phone, region) {
|
|
32
52
|
try {
|
|
33
|
-
return util.getRegionCodeForNumber(util.parse(phone,
|
|
53
|
+
return util.getRegionCodeForNumber(util.parse(phone, region)) ?? null;
|
|
34
54
|
}
|
|
35
55
|
catch {
|
|
36
56
|
return null;
|
|
37
57
|
}
|
|
38
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Masks a phone number for logs and UI, so a full number never lands in a log line or screen.
|
|
61
|
+
* Never reveals more than about a third of the input: numbers of 10+ characters keep the first 4 and
|
|
62
|
+
* last 2; shorter inputs keep only the last 2; 4 characters or fewer are fully masked.
|
|
63
|
+
*
|
|
64
|
+
* @param phone Any phone string (normalised or not).
|
|
65
|
+
* @returns The masked value.
|
|
66
|
+
* @example maskPhone('254712345678') // '2547****78'
|
|
67
|
+
*/
|
|
39
68
|
function maskPhone(phone) {
|
|
40
|
-
|
|
69
|
+
if (phone.length <= 4)
|
|
70
|
+
return '****';
|
|
71
|
+
if (phone.length < 10)
|
|
72
|
+
return `****${phone.slice(-2)}`;
|
|
73
|
+
return `${phone.slice(0, 4)}****${phone.slice(-2)}`;
|
|
41
74
|
}
|
|
42
75
|
//# sourceMappingURL=phone.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"phone.js","sourceRoot":"","sources":["../../../src/common/phone.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"phone.js","sourceRoot":"","sources":["../../../src/common/phone.ts"],"names":[],"mappings":";;;;;AAcA,wBAOC;AAUD,4BAGC;AAUD,kCAMC;AAWD,8BAIC;AAjED,kFAAmD;AAEnD,MAAM,IAAI,GAAG,+BAAc,CAAC,eAAe,CAAC,WAAW,EAAE,CAAC;AAC1D,MAAM,EAAE,iBAAiB,EAAE,GAAG,+BAAc,CAAC;AAE7C;;;;;;;;GAQG;AACH,SAAgB,MAAM,CAAC,KAAa,EAAE,MAAc;IAClD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC;QAC/D,OAAO,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACzF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,QAAQ,CAAC,KAAa,EAAE,MAAc;IACpD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACnC,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACrC,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,WAAW,CAAC,KAAa,EAAE,MAAc;IACvD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,IAAI,IAAI,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,SAAS,CAAC,KAAa;IACrC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC;IACrC,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACvD,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACtD,CAAC"}
|
|
@@ -2,26 +2,85 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.successResponse = successResponse;
|
|
4
4
|
exports.errorResponse = errorResponse;
|
|
5
|
+
exports.errorBody = errorBody;
|
|
5
6
|
exports.buildPaginationMeta = buildPaginationMeta;
|
|
6
7
|
exports.paginatedResponse = paginatedResponse;
|
|
7
8
|
exports.buildPaginatedResult = buildPaginatedResult;
|
|
8
9
|
exports.pageOffset = pageOffset;
|
|
9
|
-
|
|
10
|
+
exports.buildCursorPageMeta = buildCursorPageMeta;
|
|
11
|
+
exports.cursorPaginatedResponse = cursorPaginatedResponse;
|
|
12
|
+
/**
|
|
13
|
+
* Standard success envelope: `{ success, message, data }`.
|
|
14
|
+
*
|
|
15
|
+
* @param data Payload placed under `data`.
|
|
16
|
+
* @param message Human-readable message (default `Success`).
|
|
17
|
+
* @returns The envelope, ready for `res.json`.
|
|
18
|
+
* @example res.json(successResponse(order, 'Order placed'));
|
|
19
|
+
*/
|
|
10
20
|
function successResponse(data, message = 'Success') {
|
|
11
21
|
return { success: true, message, data };
|
|
12
22
|
}
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Standard error envelope: `{ success: false, message, code?, errors?, requestId? }`.
|
|
25
|
+
* Prefer throwing an AppError and letting the error middleware answer; use this only where a
|
|
26
|
+
* middleware must answer directly. For a body with a guaranteed `code`, use {@link errorBody}.
|
|
27
|
+
*
|
|
28
|
+
* @param message Message for the caller (never an internal error message).
|
|
29
|
+
* @param errors Optional field errors.
|
|
30
|
+
* @param code Optional UPPER_SNAKE_CASE code.
|
|
31
|
+
* @param requestId Optional correlation id of the request.
|
|
32
|
+
* @returns An envelope with `success: false`.
|
|
33
|
+
*/
|
|
34
|
+
function errorResponse(message, errors, code, requestId) {
|
|
35
|
+
return { success: false, message, ...(code ? { code } : {}), ...(errors ? { errors } : {}), ...(requestId ? { requestId } : {}) };
|
|
16
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* The error body with a required `code`, as the error middleware sends it.
|
|
39
|
+
*
|
|
40
|
+
* @param code UPPER_SNAKE_CASE machine-readable code.
|
|
41
|
+
* @param message Message for the caller (generic for 5xx).
|
|
42
|
+
* @param options `errors` (field errors) and `requestId` (correlation id), both omitted when absent.
|
|
43
|
+
* @returns `{ success: false, code, message, errors?, requestId? }`.
|
|
44
|
+
*
|
|
45
|
+
* @example res.status(404).json(errorBody('NOT_FOUND', 'Resource not found', { requestId }));
|
|
46
|
+
*/
|
|
47
|
+
function errorBody(code, message, options = {}) {
|
|
48
|
+
return {
|
|
49
|
+
success: false,
|
|
50
|
+
code,
|
|
51
|
+
message,
|
|
52
|
+
...(options.errors ? { errors: options.errors } : {}),
|
|
53
|
+
...(options.requestId ? { requestId: options.requestId } : {}),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Offset pagination meta. `totalPages` is at least 1 so an empty list still reads as "page 1 of 1".
|
|
58
|
+
*
|
|
59
|
+
* @param total Total matching rows.
|
|
60
|
+
* @param page 1-based page that was returned.
|
|
61
|
+
* @param limit Page size.
|
|
62
|
+
* @returns `{ page, limit, total, totalPages, hasNext, hasPrev }`.
|
|
63
|
+
* @example buildPaginationMeta(45, 2, 20) // { page: 2, totalPages: 3, hasNext: true, hasPrev: true, … }
|
|
64
|
+
*/
|
|
17
65
|
function buildPaginationMeta(total, page, limit) {
|
|
18
66
|
const totalPages = limit > 0 ? Math.max(1, Math.ceil(total / limit)) : 1;
|
|
19
67
|
return { page, limit, total, totalPages, hasNext: page < totalPages, hasPrev: page > 1 };
|
|
20
68
|
}
|
|
21
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* Offset-paginated envelope: `{ success, message, data: T[], meta }`.
|
|
71
|
+
*
|
|
72
|
+
* @returns The envelope with meta from {@link buildPaginationMeta}.
|
|
73
|
+
* @example res.json(paginatedResponse(rows, total, page, limit));
|
|
74
|
+
*/
|
|
22
75
|
function paginatedResponse(data, total, page, limit, message = 'Success') {
|
|
23
76
|
return { success: true, message, data, meta: buildPaginationMeta(total, page, limit) };
|
|
24
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Offset page as a service returns it (data + meta, no envelope).
|
|
80
|
+
*
|
|
81
|
+
* @returns `{ data, meta }`.
|
|
82
|
+
* @example return buildPaginatedResult(rows, total, page, limit);
|
|
83
|
+
*/
|
|
25
84
|
function buildPaginatedResult(data, total, page, limit) {
|
|
26
85
|
return { data, meta: buildPaginationMeta(total, page, limit) };
|
|
27
86
|
}
|
|
@@ -29,4 +88,18 @@ function buildPaginatedResult(data, total, page, limit) {
|
|
|
29
88
|
function pageOffset(page, limit) {
|
|
30
89
|
return Math.max(0, (Math.max(1, page) - 1) * limit);
|
|
31
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Cursor pagination meta. `hasMore` is derived from `nextCursor`, so the two can never disagree.
|
|
93
|
+
*
|
|
94
|
+
* @param limit The page size that was applied.
|
|
95
|
+
* @param nextCursor Opaque cursor for the next page, or null on the last page.
|
|
96
|
+
* @example buildCursorPageMeta(20, rows.length > 20 ? encodeCursor(rows[19]) : null)
|
|
97
|
+
*/
|
|
98
|
+
function buildCursorPageMeta(limit, nextCursor) {
|
|
99
|
+
return { limit, nextCursor, hasMore: nextCursor !== null };
|
|
100
|
+
}
|
|
101
|
+
/** Cursor-paginated envelope: `{ success, message, data: T[], meta: { limit, nextCursor, hasMore } }`. */
|
|
102
|
+
function cursorPaginatedResponse(page, message = 'Success') {
|
|
103
|
+
return { success: true, message, data: page.data, meta: page.meta };
|
|
104
|
+
}
|
|
32
105
|
//# sourceMappingURL=response.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response.js","sourceRoot":"","sources":["../../../src/common/response.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"response.js","sourceRoot":"","sources":["../../../src/common/response.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/config/index.js
CHANGED
|
@@ -16,7 +16,7 @@ exports.serviceEnv = zod_1.z.object({
|
|
|
16
16
|
NODE_ENV: zod_1.z.enum(['development', 'test', 'staging', 'production']).default('development'),
|
|
17
17
|
PORT: zod_1.z.coerce.number().int().positive(),
|
|
18
18
|
SERVICE_NAME: zod_1.z.string().min(1),
|
|
19
|
-
/** Short app identifier used in exchange names and key prefixes (e.g. `
|
|
19
|
+
/** Short app identifier used in exchange names and key prefixes (e.g. `myapp`). */
|
|
20
20
|
APP_NAME: zod_1.z.string().min(1).default('app'),
|
|
21
21
|
LOG_LEVEL: zod_1.z.enum(['error', 'warn', 'info', 'http', 'debug']).optional(),
|
|
22
22
|
CORS_ORIGINS: zod_1.z.string().default(''),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/config/index.ts"],"names":[],"mappings":";;;AAoDA,wCAIC;AAGD,gCAYC;AAKD,0BAQC;AApFD,qCAAmD;AACnD,yCAAoC;AACpC,6BAAwB;AAExB;;;;GAIG;AACU,QAAA,UAAU,GAAG,OAAC,CAAC,MAAM,CAAC;IACjC,QAAQ,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC;IACzF,IAAI,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACxC,YAAY,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/B,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/config/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runWithContext = runWithContext;
|
|
4
|
+
exports.getRequestContext = getRequestContext;
|
|
5
|
+
exports.updateRequestContext = updateRequestContext;
|
|
6
|
+
const node_async_hooks_1 = require("node:async_hooks");
|
|
7
|
+
const storage = new node_async_hooks_1.AsyncLocalStorage();
|
|
8
|
+
/**
|
|
9
|
+
* Runs `fn` (and all async work it starts) inside `context`. Use it at every entry point that is not an
|
|
10
|
+
* HTTP request: queue consumers, jobs and scripts.
|
|
11
|
+
*
|
|
12
|
+
* @param context Values for the duration of `fn`; the object is copied, so later edits don't leak back.
|
|
13
|
+
* @param fn Work to run inside the context.
|
|
14
|
+
* @returns Whatever `fn` returns.
|
|
15
|
+
* @example await runWithContext({ correlationId: msg.correlationId, service: 'worker' }, () => handle(msg));
|
|
16
|
+
*/
|
|
17
|
+
function runWithContext(context, fn) {
|
|
18
|
+
return storage.run({ ...context, scopes: context.scopes ? { ...context.scopes } : undefined }, fn);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The current request context.
|
|
22
|
+
*
|
|
23
|
+
* @returns The context, or undefined outside {@link runWithContext} / `correlationMiddleware`.
|
|
24
|
+
* @example const id = getRequestContext()?.correlationId;
|
|
25
|
+
*/
|
|
26
|
+
function getRequestContext() {
|
|
27
|
+
return storage.getStore();
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Adds values to the current context, for example the user id and scopes once auth has run. A no-op
|
|
31
|
+
* outside a context. The correlation id can't be changed once set.
|
|
32
|
+
*
|
|
33
|
+
* @param patch Values to add or replace; `scopes` are merged key by key.
|
|
34
|
+
* @returns True when a context was active and updated.
|
|
35
|
+
* @example updateRequestContext({ userId: user.id, scopes: user.scopes });
|
|
36
|
+
*/
|
|
37
|
+
function updateRequestContext(patch) {
|
|
38
|
+
const current = storage.getStore();
|
|
39
|
+
if (!current)
|
|
40
|
+
return false;
|
|
41
|
+
if (patch.service !== undefined)
|
|
42
|
+
current.service = patch.service;
|
|
43
|
+
if (patch.userId !== undefined)
|
|
44
|
+
current.userId = patch.userId;
|
|
45
|
+
if (patch.scopes)
|
|
46
|
+
current.scopes = { ...current.scopes, ...patch.scopes };
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/context/index.ts"],"names":[],"mappings":";;AA0BA,wCAEC;AAQD,8CAEC;AAUD,oDAOC;AAvDD,uDAAqD;AAerD,MAAM,OAAO,GAAG,IAAI,oCAAiB,EAAkB,CAAC;AAExD;;;;;;;;GAQG;AACH,SAAgB,cAAc,CAAI,OAAuB,EAAE,EAAW;IACpE,OAAO,OAAO,CAAC,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;AACrG,CAAC;AAED;;;;;GAKG;AACH,SAAgB,iBAAiB;IAC/B,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC;AAC5B,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,oBAAoB,CAAC,KAAqD;IACxF,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IACnC,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAC3B,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IACjE,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC9D,IAAI,KAAK,CAAC,MAAM;QAAE,OAAO,CAAC,MAAM,GAAG,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;IAC1E,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -14,7 +14,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./editorial-draft.js"), exports);
|
|
18
17
|
__exportStar(require("./notification-dispatch.js"), exports);
|
|
19
|
-
__exportStar(require("./upload-import.js"), exports);
|
|
20
18
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/contracts/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/contracts/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6DAA2C"}
|