@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"respond.js","sourceRoot":"","sources":["../../../src/express/respond.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGjF;;;;;;;;;GASG;AACH,MAAM,UAAU,EAAE,CAAI,GAAa,EAAE,IAAO,EAAE,OAAgB,EAAE,IAAsC;IACpG,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC5C,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,OAAO,CAAI,GAAa,EAAE,IAAO,EAAE,OAAO,GAAG,SAAS;IACpE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,MAAM,CAAI,GAAa,EAAE,IAAmB,EAAE,OAAgB;IAC5E,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;AACtE,CAAC"}
package/dist/esm/index.js CHANGED
@@ -3,6 +3,7 @@
3
3
  // live behind subpaths: `@heavybit/shared-lib/express`, `/cache`, `/messaging`, …
4
4
  export * from './types/index.js';
5
5
  export * from './common/index.js';
6
+ export * from './context/index.js';
6
7
  export * from './identity/index.js';
7
8
  export * from './access/index.js';
8
9
  export * from './validation/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,8FAA8F;AAC9F,kFAAkF;AAClF,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,8FAA8F;AAC9F,kFAAkF;AAClF,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC"}
@@ -1,54 +1,78 @@
1
1
  import winston from 'winston';
2
- const { combine, timestamp, json, errors, colorize, printf, splat } = winston.format;
3
- /** Keys whose values are replaced with `[REDACTED]` anywhere in log metadata. */
4
- export const DEFAULT_REDACT_KEYS = [
5
- 'password', 'newPassword', 'currentPassword', 'passwordHash', 'token', 'accessToken', 'refreshToken',
6
- 'secret', 'clientSecret', 'apiKey', 'authorization', 'cookie', 'otp', 'pass', 'smtpPassword',
7
- ];
8
- function redactValue(value, keys, depth = 0) {
9
- if (depth > 6 || value === null || typeof value !== 'object')
10
- return value;
11
- if (Array.isArray(value))
12
- return value.map((v) => redactValue(v, keys, depth + 1));
13
- if (value instanceof Error)
14
- return value;
15
- const out = {};
16
- for (const [k, v] of Object.entries(value)) {
17
- out[k] = keys.has(k) || k.endsWith('Enc') || k.endsWith('_enc') ? '[REDACTED]' : redactValue(v, keys, depth + 1);
18
- }
19
- return out;
20
- }
21
- const redact = (keys) => winston.format((info) => {
22
- for (const k of Object.keys(info)) {
23
- if (keys.has(k))
24
- info[k] = '[REDACTED]';
25
- else if (k !== 'message' && k !== 'level')
26
- info[k] = redactValue(info[k], keys);
27
- }
2
+ import { getRequestContext } from '../context/index.js';
3
+ import { createKeyClassifier, maskPhonesInText, redactValue } from './redact.js';
4
+ export { DEFAULT_ALLOW_KEYS, DEFAULT_REDACT_KEYS, REDACTED, createKeyClassifier, maskPhoneForLog, maskPhonesInText, redactValue } from './redact.js';
5
+ const { combine, timestamp, errors, colorize, printf, splat } = winston.format;
6
+ /** Winston bookkeeping keys that never appear as metadata. */
7
+ const RESERVED_KEYS = new Set(['level', 'message', 'timestamp', 'splat']);
8
+ /** Ids the library itself puts on a line; never redacted or phone-masked. */
9
+ const CONTEXT_KEYS = new Set(['service', 'correlationId', 'userId', 'scopes']);
10
+ /** Adds the request context (correlation id, user id, scopes, service) unless the line already has it. */
11
+ const withContext = winston.format((info) => {
12
+ const ctx = getRequestContext();
13
+ if (!ctx)
14
+ return info;
15
+ const line = info;
16
+ line.correlationId ??= ctx.correlationId;
17
+ if (ctx.userId !== undefined)
18
+ line.userId ??= ctx.userId;
19
+ if (ctx.scopes && Object.keys(ctx.scopes).length > 0)
20
+ line.scopes ??= ctx.scopes;
21
+ if (ctx.service !== undefined)
22
+ line.service ??= ctx.service;
23
+ return info;
24
+ });
25
+ /** Redacts secrets and masks phones in the message and every metadata value. */
26
+ const redact = (classifier) => winston.format((info) => {
27
+ const line = info;
28
+ if (typeof line.message === 'string')
29
+ line.message = maskPhonesInText(line.message);
30
+ const meta = Object.fromEntries(Object.entries(line).filter(([key]) => !RESERVED_KEYS.has(key) && !CONTEXT_KEYS.has(key)));
31
+ Object.assign(line, redactValue(meta, classifier));
28
32
  return info;
29
- })();
30
- const devFormat = printf((info) => {
33
+ });
34
+ /** One JSON object per line: `{ level, time, msg, service, correlationId, userId, scopes, ...meta }`. */
35
+ const jsonLine = printf((info) => {
36
+ const { level, message, timestamp: time, service, correlationId, userId, scopes, ...meta } = info;
37
+ return JSON.stringify({ level, time, msg: message, service, correlationId, userId, scopes, ...meta });
38
+ });
39
+ const prettyLine = printf((info) => {
31
40
  const { level, message, timestamp: ts, service, stack, ...rest } = info;
32
- const meta = Object.fromEntries(Object.entries(rest).filter(([k]) => typeof k === 'string' && k !== 'splat'));
33
- const metaStr = Object.keys(meta).length ? ` ${JSON.stringify(meta)}` : '';
41
+ const metaStr = Object.keys(rest).length ? ` ${JSON.stringify(rest)}` : '';
34
42
  return `${String(ts ?? '')} ${String(level)}: ${service ? `[${String(service)}] ` : ''}${String(message)}${metaStr}${stack ? `\n${String(stack)}` : ''}`;
35
43
  });
36
- /** Structured logger with secret redaction. JSON to stdout in production, pretty otherwise. */
44
+ /**
45
+ * Structured logger. Every line carries `service` and, inside a request or {@link runWithContext}, the
46
+ * context's `correlationId`, `userId` and `scopes`. Secrets are redacted by key word (see
47
+ * {@link DEFAULT_REDACT_KEYS}) and phone numbers are masked to their last 4 digits, in the message and
48
+ * at any depth of the metadata. Writes to stdout (JSON in production, pretty otherwise; silent under
49
+ * `NODE_ENV=test` unless `LOG_IN_TESTS` is set) plus an optional file and stream.
50
+ *
51
+ * @param service Service name on every line.
52
+ * @param options Level, format, extra redact/allow keys, file path, stream.
53
+ * @returns A winston logger that satisfies {@link Logger}.
54
+ * @example const logger = createLogger('api', { filePath: 'logs/api.log' });
55
+ */
37
56
  export function createLogger(service, options = {}) {
38
57
  const isProduction = process.env.NODE_ENV === 'production';
39
58
  const level = options.level ?? process.env.LOG_LEVEL ?? (isProduction ? 'info' : 'debug');
40
59
  const format = options.format ?? process.env.LOG_FORMAT ?? (isProduction ? 'json' : 'pretty');
41
- const keys = new Set([...DEFAULT_REDACT_KEYS, ...(options.redactKeys ?? [])]);
60
+ const classifier = createKeyClassifier(options.redactKeys, options.allowKeys);
61
+ const transports = [
62
+ new winston.transports.Console({
63
+ silent: process.env.NODE_ENV === 'test' && !process.env.LOG_IN_TESTS,
64
+ format: format === 'json' ? jsonLine : combine(colorize({ level: true }), prettyLine),
65
+ }),
66
+ ];
67
+ if (options.filePath)
68
+ transports.push(new winston.transports.File({ filename: options.filePath, format: jsonLine }));
69
+ if (options.stream)
70
+ transports.push(new winston.transports.Stream({ stream: options.stream, format: jsonLine }));
42
71
  return winston.createLogger({
43
72
  level,
44
73
  defaultMeta: { service },
45
- format: combine(errors({ stack: true }), splat(), redact(keys), timestamp()),
46
- transports: [
47
- new winston.transports.Console({
48
- silent: process.env.NODE_ENV === 'test' && !process.env.LOG_IN_TESTS,
49
- format: format === 'json' ? json() : combine(colorize({ level: true }), devFormat),
50
- }),
51
- ],
74
+ format: combine(errors({ stack: true }), splat(), withContext(), redact(classifier)(), timestamp()),
75
+ transports,
52
76
  });
53
77
  }
54
78
  /** Logger that discards everything — handy default for libraries and tests. */
@@ -58,6 +82,11 @@ export const silentLogger = { debug: () => { }, info: () => { }, warn: () => { }
58
82
  * meta wins on a key clash. `undefined` context values are dropped so lines never carry empty keys.
59
83
  * Works over any {@link Logger} (winston, a test spy, or another child), so handlers can create one
60
84
  * per message and pass it down.
85
+ *
86
+ * @param logger The logger to write through.
87
+ * @param context Values added to every line.
88
+ * @returns A {@link Logger}.
89
+ * @example const log = childLogger(logger, { jobId });
61
90
  */
62
91
  export function childLogger(logger, context) {
63
92
  const base = Object.fromEntries(Object.entries(context).filter(([, v]) => v !== undefined));
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/logging/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/logging/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Secret redaction and phone masking for log lines. Matching is by key *word*, not substring: a key is
3
+ * split on camelCase and separators (`refresh_token` → `refresh`, `token`; `OTPCode` → `otp`, `code`),
4
+ * and it is sensitive when a word (or two adjacent words joined, e.g. `api`+`key`) equals a listed term
5
+ * or its plural. So `accessToken` and `Set-Cookie` are redacted while `pinned` or `spinner` are not.
6
+ */
7
+ /** Words whose values are always replaced with `[REDACTED]`. Apps add their own via `redactKeys`. */
8
+ export const DEFAULT_REDACT_KEYS = [
9
+ 'password', 'pass', 'passphrase', 'token', 'secret', 'code', 'otp', 'authorization', 'cookie', 'credential',
10
+ 'pin', 'apikey', 'jwt', 'signature',
11
+ ];
12
+ /** Keys that are never redacted although they contain a listed word (error, status and reference codes). */
13
+ export const DEFAULT_ALLOW_KEYS = ['errorCode', 'statusCode', 'countryCode', 'currencyCode', 'postalCode', 'zipCode'];
14
+ /** Words that mark a value as a phone number, masked to its last 4 digits. */
15
+ const PHONE_KEY_WORDS = new Set(['phone', 'msisdn', 'mobile', 'tel']);
16
+ /** 9–15 digits, optionally with a leading `+`, not part of a longer word or number. */
17
+ const PHONE_IN_TEXT = /(?<![\w+]|\d\.)\+?\d{9,15}(?!\w|\.\d)/g;
18
+ /** The marker that replaces a secret value. */
19
+ export const REDACTED = '[REDACTED]';
20
+ const MAX_DEPTH = 8;
21
+ /**
22
+ * Masks every digit except the last four (log rule; the UI uses `maskPhone` from common).
23
+ *
24
+ * @param value A phone number in any format.
25
+ * @returns e.g. `********5678`; values with fewer than 5 digits are fully masked.
26
+ * @example maskPhoneForLog('15551234567') // '*******4567'
27
+ */
28
+ export function maskPhoneForLog(value) {
29
+ const digits = value.replace(/\D/g, '');
30
+ if (digits.length < 5)
31
+ return '*'.repeat(digits.length || 4);
32
+ return `${'*'.repeat(digits.length - 4)}${digits.slice(-4)}`;
33
+ }
34
+ /**
35
+ * Masks phone-number-looking runs inside free text.
36
+ *
37
+ * @param text Any string (a log message or value).
38
+ * @returns The text with each 9–15 digit run masked to its last 4 digits.
39
+ * @example maskPhonesInText('OTP sent to 15551234567') // 'OTP sent to *******4567'
40
+ */
41
+ export function maskPhonesInText(text) {
42
+ return text.replace(PHONE_IN_TEXT, maskPhoneForLog);
43
+ }
44
+ function keyWords(key) {
45
+ return key
46
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
47
+ .replace(/([A-Z])([A-Z][a-z])/g, '$1 $2')
48
+ .toLowerCase()
49
+ .split(/[^a-z0-9]+/)
50
+ .filter(Boolean);
51
+ }
52
+ /**
53
+ * Builds a key classifier from the default terms plus app-specific ones.
54
+ *
55
+ * @param extraKeys Extra words (matched like the defaults, case-insensitive).
56
+ * @param allowKeys Exact keys never treated as secret (defaults to {@link DEFAULT_ALLOW_KEYS}).
57
+ * @returns The classifier.
58
+ * @example createKeyClassifier(['nationalId']).isSecret('nationalId') // true
59
+ */
60
+ export function createKeyClassifier(extraKeys = [], allowKeys = DEFAULT_ALLOW_KEYS) {
61
+ const terms = new Set([...DEFAULT_REDACT_KEYS, ...extraKeys].map((k) => keyWords(k).join('')));
62
+ const allow = new Set(allowKeys);
63
+ const matches = (word) => terms.has(word) || (word.endsWith('s') && terms.has(word.slice(0, -1)));
64
+ return {
65
+ isSecret(key) {
66
+ if (allow.has(key))
67
+ return false;
68
+ if (key.endsWith('Enc') || key.endsWith('_enc'))
69
+ return true;
70
+ const words = keyWords(key);
71
+ if (matches(words.join('')))
72
+ return true;
73
+ return words.some((word, i) => matches(word) || (i > 0 && matches(`${words[i - 1]}${word}`)));
74
+ },
75
+ isPhone(key) {
76
+ return keyWords(key).some((word) => PHONE_KEY_WORDS.has(word));
77
+ },
78
+ };
79
+ }
80
+ /**
81
+ * Returns a copy of `value` with secrets replaced by `[REDACTED]` and phone numbers masked, at any depth
82
+ * (up to 8 levels; deeper values are dropped as `[TRUNCATED]`). Errors are kept as-is.
83
+ *
84
+ * @param value Log metadata (object, array or scalar).
85
+ * @param classifier From {@link createKeyClassifier}.
86
+ * @param depth Internal recursion depth; callers omit it.
87
+ * @returns The redacted copy; the input is not modified.
88
+ * @example redactValue({ user: { otp: '1234', phone: '15551234567' } }, createKeyClassifier())
89
+ */
90
+ export function redactValue(value, classifier, depth = 0) {
91
+ if (typeof value === 'string')
92
+ return maskPhonesInText(value);
93
+ if (value === null || typeof value !== 'object' || value instanceof Error || value instanceof Date)
94
+ return value;
95
+ if (depth >= MAX_DEPTH)
96
+ return '[TRUNCATED]';
97
+ if (Array.isArray(value))
98
+ return value.map((v) => redactValue(v, classifier, depth + 1));
99
+ const out = {};
100
+ for (const [key, v] of Object.entries(value)) {
101
+ if (classifier.isSecret(key))
102
+ out[key] = REDACTED;
103
+ else if (classifier.isPhone(key) && (typeof v === 'string' || typeof v === 'number'))
104
+ out[key] = maskPhoneForLog(String(v));
105
+ else
106
+ out[key] = redactValue(v, classifier, depth + 1);
107
+ }
108
+ return out;
109
+ }
110
+ //# sourceMappingURL=redact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.js","sourceRoot":"","sources":["../../../src/logging/redact.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,7 @@ export * from './rpc.js';
15
15
  export * from './outbox.js';
16
16
  export * from './monitor.js';
17
17
  export * from './staff-notifier.js';
18
+ export * from './staff-recipients.js';
18
19
  /** Wires one connection with a publisher, consumer, RPC server/client and DLQ monitor. */
19
20
  export function createMessaging(options) {
20
21
  const logger = options.logger ?? silentLogger;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/messaging/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,cAAc,EAAiB,MAAM,eAAe,CAAC;AAE9D,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AAuBpC,0FAA0F;AAC1F,MAAM,UAAU,eAAe,CAAC,OAAyB;IACvD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,YAAY,CAAC;IAC9C,MAAM,QAAQ,GAAG,cAAc,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9E,MAAM,UAAU,GAAG,IAAI,cAAc,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAC5H,MAAM,SAAS,GAAG,IAAI,gBAAgB,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAClG,MAAM,QAAQ,GAAG,IAAI,eAAe,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IACnE,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC9D,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC/E,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;IAEtF,OAAO;QACL,QAAQ;QACR,UAAU;QACV,SAAS;QACT,QAAQ;QACR,SAAS;QACT,SAAS;QACT,OAAO;QACP,KAAK,CAAC,KAAK;YACT,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC;YACvB,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC;YACvB,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;YACrB,MAAM,UAAU,CAAC,OAAO,EAAE,CAAC;QAC7B,CAAC;QACD,KAAK,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE;KAChC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/messaging/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,cAAc,EAAiB,MAAM,eAAe,CAAC;AAE9D,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AAuBtC,0FAA0F;AAC1F,MAAM,UAAU,eAAe,CAAC,OAAyB;IACvD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,YAAY,CAAC;IAC9C,MAAM,QAAQ,GAAG,cAAc,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9E,MAAM,UAAU,GAAG,IAAI,cAAc,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAC5H,MAAM,SAAS,GAAG,IAAI,gBAAgB,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAClG,MAAM,QAAQ,GAAG,IAAI,eAAe,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IACnE,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC9D,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC/E,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;IAEtF,OAAO;QACL,QAAQ;QACR,UAAU;QACV,SAAS;QACT,QAAQ;QACR,SAAS;QACT,SAAS;QACT,OAAO;QACP,KAAK,CAAC,KAAK;YACT,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC;YACvB,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC;YACvB,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;YACrB,MAAM,UAAU,CAAC,OAAO,EAAE,CAAC;QAC7B,CAAC;QACD,KAAK,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE;KAChC,CAAC;AACJ,CAAC"}
@@ -1,31 +1,47 @@
1
- import { z } from 'zod';
2
- import { DISPATCH_LIMITS, NOTIFICATION_DISPATCH, } from '../contracts/notification-dispatch.js';
1
+ /**
2
+ * Staff alerts (messaging layer helper): stage one notification dispatch per recipient of a
3
+ * permission through the caller's outbox. App vocabulary (category, channels, command name) is
4
+ * passed in as options; nothing app-specific is assumed.
5
+ */
6
+ import { NOTIFICATION_DISPATCH, } from '../contracts/notification-dispatch.js';
3
7
  import { silentLogger } from '../logging/index.js';
4
- /** Recipients from configuration (env), keyed by the permission an alert targets. */
5
- export class ConfiguredStaffRecipients {
6
- byPermission;
7
- constructor(byPermission) {
8
- this.byPermission = byPermission;
9
- }
10
- async resolve(permission) {
11
- return [...(this.byPermission[permission] ?? [])];
12
- }
13
- }
14
8
  /**
15
- * Stages one `notification:dispatch` per recipient through the outbox, in the caller's transaction, so an
16
- * alert is sent if and only if the state change that raised it commits. Never sends email itself.
9
+ * Stages one dispatch command per recipient through the outbox, in the caller's transaction, so an
10
+ * alert is sent if and only if the state change that raised it commits. Never sends anything itself.
17
11
  * An alert that resolves to nobody is logged as a warning (an unconfigured list must not fail silently).
12
+ * Staff alerts always bypass recipient preferences and record the target permission.
13
+ *
14
+ * @typeParam E The service's transaction/executor type.
15
+ * @typeParam C The app's dispatch categories.
16
+ * @typeParam Ch The app's dispatch channels.
17
+ * @example
18
+ * const notifier = new StaffNotifier(outbox, new ConfiguredStaffRecipients({ 'payouts.manage': config.financeEmails }),
19
+ * { category: 'staff', defaultChannels: ['email'], logger });
20
+ * await notifier.alert(tx, { template: 'payout-failed', permission: 'payouts.manage', data: { reference } }, { correlationId });
18
21
  */
19
22
  export class StaffNotifier {
20
23
  outbox;
21
24
  recipients;
25
+ category;
26
+ defaultChannels;
27
+ command;
22
28
  logger;
23
- constructor(outbox, recipients, logger = silentLogger) {
29
+ constructor(outbox, recipients, options) {
24
30
  this.outbox = outbox;
25
31
  this.recipients = recipients;
26
- this.logger = logger;
32
+ this.category = options.category;
33
+ this.defaultChannels = options.defaultChannels;
34
+ this.command = options.command ?? NOTIFICATION_DISPATCH;
35
+ this.logger = options.logger ?? silentLogger;
27
36
  }
28
- /** Returns how many commands were staged. */
37
+ /**
38
+ * Stage the alert for every recipient of its permission.
39
+ *
40
+ * @param executor The caller's open transaction; the commands commit or roll back with it.
41
+ * @param alert Template, permission, variables and optional channels.
42
+ * @param meta Correlation id and aggregate for tracing; the actor is always the system.
43
+ * @returns How many commands were staged (0 when nobody is configured).
44
+ */
29
45
  async alert(executor, alert, meta = {}) {
30
46
  const targets = await this.recipients.resolve(alert.permission);
31
47
  if (targets.length === 0) {
@@ -35,39 +51,16 @@ export class StaffNotifier {
35
51
  for (const recipient of targets) {
36
52
  const dispatch = {
37
53
  template: alert.template,
38
- category: 'staff',
54
+ category: this.category,
39
55
  recipient,
40
- channels: alert.channels ?? ['email'],
56
+ channels: [...(alert.channels ?? this.defaultChannels)],
41
57
  data: alert.data,
42
58
  bypassPreferences: true,
43
59
  targetPermission: alert.permission,
44
60
  };
45
- await this.outbox.command(executor, NOTIFICATION_DISPATCH, dispatch, { ...meta, actorId: null });
61
+ await this.outbox.command(executor, this.command, dispatch, { ...meta, actorId: null });
46
62
  }
47
63
  return targets.length;
48
64
  }
49
65
  }
50
- const emailSchema = z.string().email().max(DISPATCH_LIMITS.email);
51
- /**
52
- * A comma-separated staff address list from env (`"a@x.io, b@y.io"`): trimmed, blanks dropped,
53
- * de-duplicated case-insensitively. An invalid address fails validation, so a typo is caught at boot
54
- * instead of dead-lettering in notification-service when the alert finally fires.
55
- */
56
- export const staffEmailListSchema = z
57
- .string()
58
- .optional()
59
- .transform((csv) => {
60
- const seen = new Set();
61
- return (csv ?? '')
62
- .split(',')
63
- .map((entry) => entry.trim())
64
- .filter((email) => email && !seen.has(email.toLowerCase()) && seen.add(email.toLowerCase()));
65
- })
66
- .superRefine((emails, ctx) => {
67
- for (const email of emails) {
68
- if (!emailSchema.safeParse(email).success)
69
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: `Not a valid email address: ${email}` });
70
- }
71
- })
72
- .transform((emails) => emails.map((email) => ({ email })));
73
66
  //# sourceMappingURL=staff-notifier.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"staff-notifier.js","sourceRoot":"","sources":["../../../src/messaging/staff-notifier.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"staff-notifier.js","sourceRoot":"","sources":["../../../src/messaging/staff-notifier.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,qBAAqB,GAGtB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAsDnD;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,aAAa;IAOL;IACA;IAPF,QAAQ,CAAI;IACZ,eAAe,CAAyB;IACxC,OAAO,CAAS;IAChB,MAAM,CAAS;IAEhC,YACmB,MAAwB,EACxB,UAAkC,EACnD,OAAoC;QAFnB,WAAM,GAAN,MAAM,CAAkB;QACxB,eAAU,GAAV,UAAU,CAAwB;QAGnD,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;QAC/C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,qBAAqB,CAAC;QACxD,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,YAAY,CAAC;IAC/C,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,KAAK,CAAC,QAAW,EAAE,KAAqB,EAAE,OAAuB,EAAE;QACvE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAChE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,oDAAoD,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;YACnI,OAAO,CAAC,CAAC;QACX,CAAC;QACD,KAAK,MAAM,SAAS,IAAI,OAAO,EAAE,CAAC;YAChC,MAAM,QAAQ,GAAuC;gBACnD,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,SAAS;gBACT,QAAQ,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,eAAe,CAAC,CAAC;gBACvD,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,iBAAiB,EAAE,IAAI;gBACvB,gBAAgB,EAAE,KAAK,CAAC,UAAU;aACnC,CAAC;YACF,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1F,CAAC;QACD,OAAO,OAAO,CAAC,MAAM,CAAC;IACxB,CAAC;CACF"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Staff alert recipients (messaging layer helper): who receives an alert raised for a permission.
3
+ * Today the lists come from configuration; an app can plug in a lookup (e.g. its auth service)
4
+ * by implementing {@link StaffRecipientResolver}.
5
+ */
6
+ import { z } from 'zod';
7
+ import { DISPATCH_LIMITS } from '../contracts/notification-dispatch.js';
8
+ /**
9
+ * Recipients from configuration (env), keyed by the permission an alert targets.
10
+ *
11
+ * @param byPermission Map of permission → recipients, typically built with {@link staffEmailListSchema}.
12
+ * @example new ConfiguredStaffRecipients({ 'payouts.manage': env.FINANCE_ALERT_EMAILS })
13
+ */
14
+ export class ConfiguredStaffRecipients {
15
+ byPermission;
16
+ constructor(byPermission) {
17
+ this.byPermission = byPermission;
18
+ }
19
+ /**
20
+ * @param permission The permission an alert targets.
21
+ * @returns A copy of the configured recipients, or `[]` when none are configured.
22
+ */
23
+ async resolve(permission) {
24
+ return [...(this.byPermission[permission] ?? [])];
25
+ }
26
+ }
27
+ const emailSchema = z.string().email().max(DISPATCH_LIMITS.email);
28
+ /**
29
+ * A comma-separated staff address list from env (`"a@x.io, b@y.io"`): trimmed, blanks dropped,
30
+ * de-duplicated case-insensitively. An invalid address fails validation, so a typo is caught at boot
31
+ * instead of dead-lettering in the notification-service when the alert finally fires.
32
+ *
33
+ * @returns (on parse) `DispatchRecipient[]` with `email` only.
34
+ * @example const config = z.object({ FINANCE_ALERT_EMAILS: staffEmailListSchema }).parse(process.env);
35
+ */
36
+ export const staffEmailListSchema = z
37
+ .string()
38
+ .optional()
39
+ .transform((csv) => {
40
+ const seen = new Set();
41
+ return (csv ?? '')
42
+ .split(',')
43
+ .map((entry) => entry.trim())
44
+ .filter((email) => email && !seen.has(email.toLowerCase()) && seen.add(email.toLowerCase()));
45
+ })
46
+ .superRefine((emails, ctx) => {
47
+ for (const email of emails) {
48
+ if (!emailSchema.safeParse(email).success)
49
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: `Not a valid email address: ${email}` });
50
+ }
51
+ })
52
+ .transform((emails) => emails.map((email) => ({ email })));
53
+ //# sourceMappingURL=staff-recipients.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staff-recipients.js","sourceRoot":"","sources":["../../../src/messaging/staff-recipients.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,EAA0B,MAAM,uCAAuC,CAAC;AAehG;;;;;GAKG;AACH,MAAM,OAAO,yBAAyB;IACP;IAA7B,YAA6B,YAAoE;QAApE,iBAAY,GAAZ,YAAY,CAAwD;IAAG,CAAC;IAErG;;;OAGG;IACH,KAAK,CAAC,OAAO,CAAC,UAAkB;QAC9B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACpD,CAAC;CACF;AAED,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;AAElE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC;KAClC,MAAM,EAAE;KACR,QAAQ,EAAE;KACV,SAAS,CAAC,CAAC,GAAG,EAAE,EAAE;IACjB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC;SACf,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;SAC5B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;AACjG,CAAC,CAAC;KACD,WAAW,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;IAC3B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO;YAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,8BAA8B,KAAK,EAAE,EAAE,CAAC,CAAC;IAC3I,CAAC;AACH,CAAC,CAAC;KACD,SAAS,CAAC,CAAC,MAAM,EAAuB,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC"}
@@ -1,14 +1,25 @@
1
1
  import { z } from 'zod';
2
2
  import { ID_PATTERN } from '../common/id.js';
3
3
  import { ValidationError } from '../common/errors.js';
4
+ /** A ULID in canonical form (26 upper-case Crockford base32 chars), as `newId()` makes and `CHAR(26)` stores. */
4
5
  export const idSchema = z.string().regex(ID_PATTERN, 'Invalid id');
6
+ /** Route params `{ id }` where `id` is a canonical ULID. Use with `validate({ params: idParamSchema })`. */
5
7
  export const idParamSchema = z.object({ id: idSchema });
8
+ /**
9
+ * Alias of {@link idParamSchema} for `validate({ params: idParam })`. Stricter than `z.string().ulid()`:
10
+ * lower-case ULIDs are rejected, so a lookup never misses a stored upper-case id.
11
+ */
12
+ export const idParam = idParamSchema;
13
+ /** Offset pagination query: `page` ≥ 1 (default 1), `limit` 1–100 (default 20). Prefer cursor pagination (D6). */
6
14
  export const paginationSchema = z.object({
7
15
  page: z.coerce.number().int().min(1).default(1),
8
16
  limit: z.coerce.number().int().min(1).max(100).default(20),
9
17
  });
18
+ /** `asc` or `desc`, default `desc`. */
10
19
  export const sortOrderSchema = z.enum(['asc', 'desc']).default('desc');
20
+ /** Trimmed, lower-cased email address, max 191 chars. */
11
21
  export const emailSchema = z.string().trim().toLowerCase().email('Invalid email address').max(191);
22
+ /** Lower-case kebab-case slug, 1–120 chars. */
12
23
  export const slugSchema = z.string().trim().min(1).max(120).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, 'Invalid slug');
13
24
  /** Length-first password rule (NIST 800-63B): 8–128 chars, no composition rules. Tighten per app if needed. */
14
25
  export const passwordSchema = z.string().min(8, 'Password must be at least 8 characters').max(128, 'Password is too long');
@@ -16,7 +27,13 @@ export const passwordSchema = z.string().min(8, 'Password must be at least 8 cha
16
27
  export const booleanish = z.union([z.boolean(), z.enum(['true', 'false', '1', '0', 'yes', 'no'])]).transform((v) => v === true || v === 'true' || v === '1' || v === 'yes');
17
28
  /** Comma list or repeated query param → string[]. */
18
29
  export const csvList = z.union([z.string(), z.array(z.string())]).transform((v) => (Array.isArray(v) ? v : v.split(',')).map((s) => s.trim()).filter(Boolean));
19
- /** Converts Zod issues into the `{ field: [messages] }` shape used by the error envelope. */
30
+ /**
31
+ * Converts Zod issues into the `{ field: [messages] }` shape used by the error envelope.
32
+ *
33
+ * @param error A Zod error.
34
+ * @returns Messages keyed by dotted field path (`_` for the root).
35
+ * @example zodIssuesToErrors(result.error) // { 'items.0': ['Expected string'] }
36
+ */
20
37
  export function zodIssuesToErrors(error) {
21
38
  const errors = {};
22
39
  for (const issue of error.issues) {
@@ -25,12 +42,20 @@ export function zodIssuesToErrors(error) {
25
42
  }
26
43
  return errors;
27
44
  }
28
- /** Parses `input` or throws a ValidationError (422). */
45
+ /**
46
+ * Parses `input` or throws a ValidationError (422).
47
+ *
48
+ * @param schema Zod schema.
49
+ * @param input Untrusted value.
50
+ * @returns The parsed value.
51
+ * @example const dto = parseOrThrow(createSchema, payload);
52
+ */
29
53
  export function parseOrThrow(schema, input) {
30
54
  const result = schema.safeParse(input);
31
55
  if (!result.success)
32
56
  throw new ValidationError(zodIssuesToErrors(result.error));
33
57
  return result.data;
34
58
  }
59
+ /** Zod re-exported so services build schemas on the same zod instance as the library. */
35
60
  export { z };
36
61
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/validation/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/validation/index.ts"],"names":[],"mappings":"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"}
@@ -17,7 +17,7 @@ export interface SlackAlertPayload {
17
17
  export interface SlackAlerterOptions {
18
18
  /** The notification app's URL. Empty = not configured: alerts are logged and skipped. */
19
19
  url: string;
20
- /** Product name shown on the alert, e.g. `Showbiz`. */
20
+ /** Product name shown on the alert, e.g. `Acme Shop`. */
21
21
  app: string;
22
22
  /** `staging`, `production`… */
23
23
  environment: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/alerting/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,4GAA4G;AAC5G,MAAM,WAAW,WAAW;IAC1B,yGAAyG;IACzG,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9E;AAED,uGAAuG;AACvG,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,yFAAyF;IACzF,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,GAAG,EAAE,MAAM,CAAC;IACZ,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED;;;GAGG;AACH,qBAAa,YAAa,YAAW,WAAW;IAIlC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEH,OAAO,EAAE,mBAAmB;IAIzD,IAAI,UAAU,IAAI,OAAO,CAExB;IAEK,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,OAAO,CAAC;CAgCvF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/alerting/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,4GAA4G;AAC5G,MAAM,WAAW,WAAW;IAC1B,yGAAyG;IACzG,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9E;AAED,uGAAuG;AACvG,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,yFAAyF;IACzF,GAAG,EAAE,MAAM,CAAC;IACZ,yDAAyD;IACzD,GAAG,EAAE,MAAM,CAAC;IACZ,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED;;;GAGG;AACH,qBAAa,YAAa,YAAW,WAAW;IAIlC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEH,OAAO,EAAE,mBAAmB;IAIzD,IAAI,UAAU,IAAI,OAAO,CAExB;IAEK,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,OAAO,CAAC;CAgCvF"}
@@ -34,7 +34,7 @@ export declare function createCache(options: CacheOptions): Cache;
34
34
  export interface RedisClientOptions {
35
35
  url?: string;
36
36
  db?: number;
37
- /** Prepended to every key by ioredis, e.g. `showbiz:prod:`. */
37
+ /** Prepended to every key by ioredis, e.g. `myapp:prod:`. */
38
38
  keyPrefix?: string;
39
39
  logger?: Logger;
40
40
  options?: RedisOptions;