@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.
Files changed (128) hide show
  1. package/README.md +100 -0
  2. package/dist/cjs/common/errors.js +44 -5
  3. package/dist/cjs/common/errors.js.map +1 -1
  4. package/dist/cjs/common/phone.js +43 -10
  5. package/dist/cjs/common/phone.js.map +1 -1
  6. package/dist/cjs/common/response.js +78 -5
  7. package/dist/cjs/common/response.js.map +1 -1
  8. package/dist/cjs/config/index.js +1 -1
  9. package/dist/cjs/config/index.js.map +1 -1
  10. package/dist/cjs/context/index.js +49 -0
  11. package/dist/cjs/context/index.js.map +1 -0
  12. package/dist/cjs/contracts/index.js +0 -2
  13. package/dist/cjs/contracts/index.js.map +1 -1
  14. package/dist/cjs/contracts/notification-dispatch.js +87 -63
  15. package/dist/cjs/contracts/notification-dispatch.js.map +1 -1
  16. package/dist/cjs/express/auth.js +44 -11
  17. package/dist/cjs/express/auth.js.map +1 -1
  18. package/dist/cjs/express/index.js +1 -0
  19. package/dist/cjs/express/index.js.map +1 -1
  20. package/dist/cjs/express/middleware.js +154 -34
  21. package/dist/cjs/express/middleware.js.map +1 -1
  22. package/dist/cjs/express/respond.js +47 -0
  23. package/dist/cjs/express/respond.js.map +1 -0
  24. package/dist/cjs/index.js +1 -0
  25. package/dist/cjs/index.js.map +1 -1
  26. package/dist/cjs/logging/index.js +76 -40
  27. package/dist/cjs/logging/index.js.map +1 -1
  28. package/dist/cjs/logging/redact.js +117 -0
  29. package/dist/cjs/logging/redact.js.map +1 -0
  30. package/dist/cjs/messaging/index.js +1 -0
  31. package/dist/cjs/messaging/index.js.map +1 -1
  32. package/dist/cjs/messaging/staff-notifier.js +36 -44
  33. package/dist/cjs/messaging/staff-notifier.js.map +1 -1
  34. package/dist/cjs/messaging/staff-recipients.js +57 -0
  35. package/dist/cjs/messaging/staff-recipients.js.map +1 -0
  36. package/dist/cjs/validation/index.js +27 -3
  37. package/dist/cjs/validation/index.js.map +1 -1
  38. package/dist/esm/common/errors.js +42 -4
  39. package/dist/esm/common/errors.js.map +1 -1
  40. package/dist/esm/common/phone.js +43 -10
  41. package/dist/esm/common/phone.js.map +1 -1
  42. package/dist/esm/common/response.js +75 -5
  43. package/dist/esm/common/response.js.map +1 -1
  44. package/dist/esm/config/index.js +1 -1
  45. package/dist/esm/config/index.js.map +1 -1
  46. package/dist/esm/context/index.js +44 -0
  47. package/dist/esm/context/index.js.map +1 -0
  48. package/dist/esm/contracts/index.js +0 -2
  49. package/dist/esm/contracts/index.js.map +1 -1
  50. package/dist/esm/contracts/notification-dispatch.js +85 -62
  51. package/dist/esm/contracts/notification-dispatch.js.map +1 -1
  52. package/dist/esm/express/auth.js +45 -12
  53. package/dist/esm/express/auth.js.map +1 -1
  54. package/dist/esm/express/index.js +1 -0
  55. package/dist/esm/express/index.js.map +1 -1
  56. package/dist/esm/express/middleware.js +146 -27
  57. package/dist/esm/express/middleware.js.map +1 -1
  58. package/dist/esm/express/respond.js +42 -0
  59. package/dist/esm/express/respond.js.map +1 -0
  60. package/dist/esm/index.js +1 -0
  61. package/dist/esm/index.js.map +1 -1
  62. package/dist/esm/logging/index.js +68 -39
  63. package/dist/esm/logging/index.js.map +1 -1
  64. package/dist/esm/logging/redact.js +110 -0
  65. package/dist/esm/logging/redact.js.map +1 -0
  66. package/dist/esm/messaging/index.js +1 -0
  67. package/dist/esm/messaging/index.js.map +1 -1
  68. package/dist/esm/messaging/staff-notifier.js +36 -43
  69. package/dist/esm/messaging/staff-notifier.js.map +1 -1
  70. package/dist/esm/messaging/staff-recipients.js +53 -0
  71. package/dist/esm/messaging/staff-recipients.js.map +1 -0
  72. package/dist/esm/validation/index.js +27 -2
  73. package/dist/esm/validation/index.js.map +1 -1
  74. package/dist/types/alerting/index.d.ts +1 -1
  75. package/dist/types/alerting/index.d.ts.map +1 -1
  76. package/dist/types/cache/index.d.ts +1 -1
  77. package/dist/types/cache/index.d.ts.map +1 -1
  78. package/dist/types/common/errors.d.ts +48 -7
  79. package/dist/types/common/errors.d.ts.map +1 -1
  80. package/dist/types/common/phone.d.ts +35 -6
  81. package/dist/types/common/phone.d.ts.map +1 -1
  82. package/dist/types/common/response.d.ts +66 -5
  83. package/dist/types/common/response.d.ts.map +1 -1
  84. package/dist/types/config/index.d.ts +1 -1
  85. package/dist/types/config/index.d.ts.map +1 -1
  86. package/dist/types/context/index.d.ts +39 -0
  87. package/dist/types/context/index.d.ts.map +1 -0
  88. package/dist/types/contracts/index.d.ts +0 -2
  89. package/dist/types/contracts/index.d.ts.map +1 -1
  90. package/dist/types/contracts/notification-dispatch.d.ts +208 -90
  91. package/dist/types/contracts/notification-dispatch.d.ts.map +1 -1
  92. package/dist/types/express/auth.d.ts +42 -6
  93. package/dist/types/express/auth.d.ts.map +1 -1
  94. package/dist/types/express/index.d.ts +1 -0
  95. package/dist/types/express/index.d.ts.map +1 -1
  96. package/dist/types/express/middleware.d.ts +86 -7
  97. package/dist/types/express/middleware.d.ts.map +1 -1
  98. package/dist/types/express/respond.d.ts +41 -0
  99. package/dist/types/express/respond.d.ts.map +1 -0
  100. package/dist/types/index.d.ts +1 -0
  101. package/dist/types/index.d.ts.map +1 -1
  102. package/dist/types/logging/index.d.ts +30 -4
  103. package/dist/types/logging/index.d.ts.map +1 -1
  104. package/dist/types/logging/redact.d.ts +54 -0
  105. package/dist/types/logging/redact.d.ts.map +1 -0
  106. package/dist/types/messaging/index.d.ts +1 -0
  107. package/dist/types/messaging/index.d.ts.map +1 -1
  108. package/dist/types/messaging/staff-notifier.d.ts +67 -36
  109. package/dist/types/messaging/staff-notifier.d.ts.map +1 -1
  110. package/dist/types/messaging/staff-recipients.d.ts +50 -0
  111. package/dist/types/messaging/staff-recipients.d.ts.map +1 -0
  112. package/dist/types/types/index.d.ts +36 -1
  113. package/dist/types/types/index.d.ts.map +1 -1
  114. package/dist/types/validation/index.d.ts +33 -2
  115. package/dist/types/validation/index.d.ts.map +1 -1
  116. package/package.json +1 -1
  117. package/dist/cjs/contracts/editorial-draft.js +0 -120
  118. package/dist/cjs/contracts/editorial-draft.js.map +0 -1
  119. package/dist/cjs/contracts/upload-import.js +0 -54
  120. package/dist/cjs/contracts/upload-import.js.map +0 -1
  121. package/dist/esm/contracts/editorial-draft.js +0 -117
  122. package/dist/esm/contracts/editorial-draft.js.map +0 -1
  123. package/dist/esm/contracts/upload-import.js +0 -51
  124. package/dist/esm/contracts/upload-import.js.map +0 -1
  125. package/dist/types/contracts/editorial-draft.d.ts +0 -610
  126. package/dist/types/contracts/editorial-draft.d.ts.map +0 -1
  127. package/dist/types/contracts/upload-import.d.ts +0 -96
  128. 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
- /** Message safe to show a caller; hides internals for unexpected errors. */
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
- if (isAppError(error) && error.statusCode === 503)
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":"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"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/common/errors.ts"],"names":[],"mappings":"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"}
@@ -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 (`+254712345678`). Returns null for anything that is not a
15
- * valid number for the given default region.
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, defaultRegion = 'KE') {
22
+ function toE164(phone, region) {
18
23
  try {
19
- const parsed = util.parseAndKeepRawInput(phone.trim(), defaultRegion);
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
- /** E.164 without the plus sign, as most SMS gateways expect (`254712345678`). */
27
- function toMsisdn(phone, defaultRegion = 'KE') {
28
- const e164 = toE164(phone, defaultRegion);
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
- function phoneRegion(phone, defaultRegion = 'KE') {
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, defaultRegion)) ?? null;
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
- return phone.length <= 4 ? '****' : `${phone.slice(0, 4)}****${phone.slice(-2)}`;
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":";;;;;AASA,wBAOC;AAGD,4BAGC;AAED,kCAMC;AAED,8BAEC;AAlCD,kFAAmD;AAEnD,MAAM,IAAI,GAAG,+BAAc,CAAC,eAAe,CAAC,WAAW,EAAE,CAAC;AAC1D,MAAM,EAAE,iBAAiB,EAAE,GAAG,+BAAc,CAAC;AAE7C;;;GAGG;AACH,SAAgB,MAAM,CAAC,KAAa,EAAE,aAAa,GAAG,IAAI;IACxD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,aAAa,CAAC,CAAC;QACtE,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,iFAAiF;AACjF,SAAgB,QAAQ,CAAC,KAAa,EAAE,aAAa,GAAG,IAAI;IAC1D,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IAC1C,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACrC,CAAC;AAED,SAAgB,WAAW,CAAC,KAAa,EAAE,aAAa,GAAG,IAAI;IAC7D,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,IAAI,IAAI,CAAC;IAC/E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAgB,SAAS,CAAC,KAAa;IACrC,OAAO,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACnF,CAAC"}
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
- /** Standard success envelope: `{ success, message, data }`. */
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
- /** Standard error envelope: `{ success: false, message, code?, errors? }`. */
14
- function errorResponse(message, errors, code) {
15
- return { success: false, message, ...(code ? { code } : {}), ...(errors ? { errors } : {}) };
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
- /** Paginated envelope: `{ success, message, data: T[], meta }`. */
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":";;AAGA,0CAEC;AAGD,sCAEC;AAED,kDAGC;AAGD,8CAEC;AAED,oDAEC;AAGD,gCAEC;AA3BD,+DAA+D;AAC/D,SAAgB,eAAe,CAAI,IAAO,EAAE,OAAO,GAAG,SAAS;IAC7D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC1C,CAAC;AAED,8EAA8E;AAC9E,SAAgB,aAAa,CAAC,OAAe,EAAE,MAAiC,EAAE,IAAa;IAC7F,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC/F,CAAC;AAED,SAAgB,mBAAmB,CAAC,KAAa,EAAE,IAAY,EAAE,KAAa;IAC5E,MAAM,UAAU,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACzE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,GAAG,UAAU,EAAE,OAAO,EAAE,IAAI,GAAG,CAAC,EAAE,CAAC;AAC3F,CAAC;AAED,mEAAmE;AACnE,SAAgB,iBAAiB,CAAI,IAAS,EAAE,KAAa,EAAE,IAAY,EAAE,KAAa,EAAE,OAAO,GAAG,SAAS;IAC7G,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,mBAAmB,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;AACzF,CAAC;AAED,SAAgB,oBAAoB,CAAI,IAAS,EAAE,KAAa,EAAE,IAAY,EAAE,KAAa;IAC3F,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,mBAAmB,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;AACjE,CAAC;AAED,oCAAoC;AACpC,SAAgB,UAAU,CAAC,IAAY,EAAE,KAAa;IACpD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;AACtD,CAAC"}
1
+ {"version":3,"file":"response.js","sourceRoot":"","sources":["../../../src/common/response.ts"],"names":[],"mappings":"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"}
@@ -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. `showbiz`). */
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":"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"}
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,uDAAqC;AACrC,6DAA2C;AAC3C,qDAAmC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/contracts/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6DAA2C"}