@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
|
@@ -1,20 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.z = exports.csvList = exports.booleanish = exports.passwordSchema = exports.slugSchema = exports.emailSchema = exports.sortOrderSchema = exports.paginationSchema = exports.idParamSchema = exports.idSchema = void 0;
|
|
3
|
+
exports.z = exports.csvList = exports.booleanish = exports.passwordSchema = exports.slugSchema = exports.emailSchema = exports.sortOrderSchema = exports.paginationSchema = exports.idParam = exports.idParamSchema = exports.idSchema = void 0;
|
|
4
4
|
exports.zodIssuesToErrors = zodIssuesToErrors;
|
|
5
5
|
exports.parseOrThrow = parseOrThrow;
|
|
6
6
|
const zod_1 = require("zod");
|
|
7
7
|
Object.defineProperty(exports, "z", { enumerable: true, get: function () { return zod_1.z; } });
|
|
8
8
|
const id_js_1 = require("../common/id.js");
|
|
9
9
|
const errors_js_1 = require("../common/errors.js");
|
|
10
|
+
/** A ULID in canonical form (26 upper-case Crockford base32 chars), as `newId()` makes and `CHAR(26)` stores. */
|
|
10
11
|
exports.idSchema = zod_1.z.string().regex(id_js_1.ID_PATTERN, 'Invalid id');
|
|
12
|
+
/** Route params `{ id }` where `id` is a canonical ULID. Use with `validate({ params: idParamSchema })`. */
|
|
11
13
|
exports.idParamSchema = zod_1.z.object({ id: exports.idSchema });
|
|
14
|
+
/**
|
|
15
|
+
* Alias of {@link idParamSchema} for `validate({ params: idParam })`. Stricter than `z.string().ulid()`:
|
|
16
|
+
* lower-case ULIDs are rejected, so a lookup never misses a stored upper-case id.
|
|
17
|
+
*/
|
|
18
|
+
exports.idParam = exports.idParamSchema;
|
|
19
|
+
/** Offset pagination query: `page` ≥ 1 (default 1), `limit` 1–100 (default 20). Prefer cursor pagination (D6). */
|
|
12
20
|
exports.paginationSchema = zod_1.z.object({
|
|
13
21
|
page: zod_1.z.coerce.number().int().min(1).default(1),
|
|
14
22
|
limit: zod_1.z.coerce.number().int().min(1).max(100).default(20),
|
|
15
23
|
});
|
|
24
|
+
/** `asc` or `desc`, default `desc`. */
|
|
16
25
|
exports.sortOrderSchema = zod_1.z.enum(['asc', 'desc']).default('desc');
|
|
26
|
+
/** Trimmed, lower-cased email address, max 191 chars. */
|
|
17
27
|
exports.emailSchema = zod_1.z.string().trim().toLowerCase().email('Invalid email address').max(191);
|
|
28
|
+
/** Lower-case kebab-case slug, 1–120 chars. */
|
|
18
29
|
exports.slugSchema = zod_1.z.string().trim().min(1).max(120).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, 'Invalid slug');
|
|
19
30
|
/** Length-first password rule (NIST 800-63B): 8–128 chars, no composition rules. Tighten per app if needed. */
|
|
20
31
|
exports.passwordSchema = zod_1.z.string().min(8, 'Password must be at least 8 characters').max(128, 'Password is too long');
|
|
@@ -22,7 +33,13 @@ exports.passwordSchema = zod_1.z.string().min(8, 'Password must be at least 8 ch
|
|
|
22
33
|
exports.booleanish = zod_1.z.union([zod_1.z.boolean(), zod_1.z.enum(['true', 'false', '1', '0', 'yes', 'no'])]).transform((v) => v === true || v === 'true' || v === '1' || v === 'yes');
|
|
23
34
|
/** Comma list or repeated query param → string[]. */
|
|
24
35
|
exports.csvList = zod_1.z.union([zod_1.z.string(), zod_1.z.array(zod_1.z.string())]).transform((v) => (Array.isArray(v) ? v : v.split(',')).map((s) => s.trim()).filter(Boolean));
|
|
25
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Converts Zod issues into the `{ field: [messages] }` shape used by the error envelope.
|
|
38
|
+
*
|
|
39
|
+
* @param error A Zod error.
|
|
40
|
+
* @returns Messages keyed by dotted field path (`_` for the root).
|
|
41
|
+
* @example zodIssuesToErrors(result.error) // { 'items.0': ['Expected string'] }
|
|
42
|
+
*/
|
|
26
43
|
function zodIssuesToErrors(error) {
|
|
27
44
|
const errors = {};
|
|
28
45
|
for (const issue of error.issues) {
|
|
@@ -31,7 +48,14 @@ function zodIssuesToErrors(error) {
|
|
|
31
48
|
}
|
|
32
49
|
return errors;
|
|
33
50
|
}
|
|
34
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* Parses `input` or throws a ValidationError (422).
|
|
53
|
+
*
|
|
54
|
+
* @param schema Zod schema.
|
|
55
|
+
* @param input Untrusted value.
|
|
56
|
+
* @returns The parsed value.
|
|
57
|
+
* @example const dto = parseOrThrow(createSchema, payload);
|
|
58
|
+
*/
|
|
35
59
|
function parseOrThrow(schema, input) {
|
|
36
60
|
const result = schema.safeParse(input);
|
|
37
61
|
if (!result.success)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/validation/index.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/validation/index.ts"],"names":[],"mappings":";;;AA+CA,8CAOC;AAUD,oCAIC;AApED,6BAAwB;AAuEf,kFAvEA,OAAC,OAuEA;AAtEV,2CAA6C;AAC7C,mDAAsD;AAEtD,iHAAiH;AACpG,QAAA,QAAQ,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,kBAAU,EAAE,YAAY,CAAC,CAAC;AAEnE,4GAA4G;AAC/F,QAAA,aAAa,GAAG,OAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,gBAAQ,EAAE,CAAC,CAAC;AAExD;;;GAGG;AACU,QAAA,OAAO,GAAG,qBAAa,CAAC;AAErC,kHAAkH;AACrG,QAAA,gBAAgB,GAAG,OAAC,CAAC,MAAM,CAAC;IACvC,IAAI,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/C,KAAK,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CAC3D,CAAC,CAAC;AAEH,uCAAuC;AAC1B,QAAA,eAAe,GAAG,OAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AAEvE,yDAAyD;AAC5C,QAAA,WAAW,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAEnG,+CAA+C;AAClC,QAAA,UAAU,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,4BAA4B,EAAE,cAAc,CAAC,CAAC;AAEhH,+GAA+G;AAClG,QAAA,cAAc,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,wCAAwC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,sBAAsB,CAAC,CAAC;AAE3H,0DAA0D;AAC7C,QAAA,UAAU,GAAG,OAAC,CAAC,KAAK,CAAC,CAAC,OAAC,CAAC,OAAO,EAAE,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC;AAE5K,qDAAqD;AACxC,QAAA,OAAO,GAAG,OAAC,CAAC,KAAK,CAAC,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;AAE/J;;;;;;GAMG;AACH,SAAgB,iBAAiB,CAAC,KAAiB;IACjD,MAAM,MAAM,GAA6B,EAAE,CAAC;IAC5C,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC;QACxC,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,YAAY,CAAyB,MAAS,EAAE,KAAc;IAC5E,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,MAAM,IAAI,2BAAe,CAAC,iBAAiB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAChF,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC"}
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
* Base class for errors that map to an HTTP status. Throw these from services; the shared
|
|
3
3
|
* error middleware turns them into the standard envelope. Anything that is not an AppError is
|
|
4
4
|
* reported as a 500 with a generic message so internals never leak.
|
|
5
|
+
*
|
|
6
|
+
* @param statusCode HTTP status. 4xx messages reach the caller; 5xx messages never do.
|
|
7
|
+
* @param message Caller-facing message (4xx) or log message (5xx).
|
|
8
|
+
* @param options `code` (defaults from the status), field `errors`, `retryable` (consumers),
|
|
9
|
+
* `retryAfterSeconds` (sent as `Retry-After`) and `cause`.
|
|
10
|
+
* @example throw new AppError(402, 'Payment is required first', { code: 'PAYMENT_REQUIRED' });
|
|
5
11
|
*/
|
|
6
12
|
export class AppError extends Error {
|
|
7
13
|
statusCode;
|
|
@@ -9,6 +15,8 @@ export class AppError extends Error {
|
|
|
9
15
|
errors;
|
|
10
16
|
/** Consumers dead-letter immediately instead of retrying when this is false. */
|
|
11
17
|
retryable;
|
|
18
|
+
/** Seconds the caller should wait before retrying; the error middleware sends it as `Retry-After`. */
|
|
19
|
+
retryAfterSeconds;
|
|
12
20
|
constructor(statusCode, message, options = {}) {
|
|
13
21
|
super(message, options.cause ? { cause: options.cause } : undefined);
|
|
14
22
|
this.name = new.target.name;
|
|
@@ -16,8 +24,22 @@ export class AppError extends Error {
|
|
|
16
24
|
this.code = options.code ?? defaultCode(statusCode);
|
|
17
25
|
this.errors = options.errors;
|
|
18
26
|
this.retryable = options.retryable ?? statusCode >= 500;
|
|
27
|
+
this.retryAfterSeconds = options.retryAfterSeconds;
|
|
19
28
|
}
|
|
20
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Shorthand for an error with an explicit status and code.
|
|
32
|
+
*
|
|
33
|
+
* @param statusCode HTTP status (4xx messages reach the caller; 5xx are replaced with a generic message).
|
|
34
|
+
* @param code UPPER_SNAKE_CASE machine-readable code, e.g. `SLUG_TAKEN`.
|
|
35
|
+
* @param message Human-readable message for 4xx responses.
|
|
36
|
+
* @param errors Optional field errors (`{ 'items.0.qty': ['Must be positive'] }`).
|
|
37
|
+
* @returns An {@link AppError} to `throw`.
|
|
38
|
+
* @example throw httpError(409, 'SLUG_TAKEN', 'That address is already in use');
|
|
39
|
+
*/
|
|
40
|
+
export function httpError(statusCode, code, message, errors) {
|
|
41
|
+
return new AppError(statusCode, message, { code, errors });
|
|
42
|
+
}
|
|
21
43
|
function defaultCode(status) {
|
|
22
44
|
switch (status) {
|
|
23
45
|
case 400: return 'BAD_REQUEST';
|
|
@@ -31,27 +53,35 @@ function defaultCode(status) {
|
|
|
31
53
|
default: return status >= 500 ? 'INTERNAL_ERROR' : 'ERROR';
|
|
32
54
|
}
|
|
33
55
|
}
|
|
56
|
+
/** 400 BAD_REQUEST: the request is malformed or breaks a business rule. */
|
|
34
57
|
export class BadRequestError extends AppError {
|
|
35
58
|
constructor(message = 'Bad request', options = {}) { super(400, message, options); }
|
|
36
59
|
}
|
|
60
|
+
/** 401 UNAUTHENTICATED: no valid identity. */
|
|
37
61
|
export class UnauthorizedError extends AppError {
|
|
38
62
|
constructor(message = 'Authentication required', options = {}) { super(401, message, options); }
|
|
39
63
|
}
|
|
64
|
+
/** 403 FORBIDDEN: authenticated, but not allowed. */
|
|
40
65
|
export class ForbiddenError extends AppError {
|
|
41
66
|
constructor(message = 'You do not have permission to perform this action', options = {}) { super(403, message, options); }
|
|
42
67
|
}
|
|
68
|
+
/** 404 NOT_FOUND. Also use it for out-of-scope resources so their existence is not revealed. */
|
|
43
69
|
export class NotFoundError extends AppError {
|
|
44
70
|
constructor(message = 'Resource not found', options = {}) { super(404, message, options); }
|
|
45
71
|
}
|
|
72
|
+
/** 409 CONFLICT: duplicate or state conflict. */
|
|
46
73
|
export class ConflictError extends AppError {
|
|
47
74
|
constructor(message = 'Resource already exists', options = {}) { super(409, message, options); }
|
|
48
75
|
}
|
|
76
|
+
/** 422 VALIDATION_FAILED with field errors (`{ 'items.0.qty': ['…'] }`). */
|
|
49
77
|
export class ValidationError extends AppError {
|
|
50
78
|
constructor(errors, message = 'Validation failed') { super(422, message, { errors }); }
|
|
51
79
|
}
|
|
80
|
+
/** 429. Pass `retryAfterSeconds` so the error middleware sets the `Retry-After` header. */
|
|
52
81
|
export class TooManyRequestsError extends AppError {
|
|
53
82
|
constructor(message = 'Too many requests. Please try again later.', options = {}) { super(429, message, options); }
|
|
54
83
|
}
|
|
84
|
+
/** 503 SERVICE_UNAVAILABLE: a dependency is down. The message is logged, never sent (callers see the generic one). */
|
|
55
85
|
export class ServiceUnavailableError extends AppError {
|
|
56
86
|
constructor(message = 'Service temporarily unavailable', options = {}) { super(503, message, options); }
|
|
57
87
|
}
|
|
@@ -61,15 +91,23 @@ export class NonRetryableError extends AppError {
|
|
|
61
91
|
super(422, message, { ...options, retryable: false });
|
|
62
92
|
}
|
|
63
93
|
}
|
|
94
|
+
/** Type guard for {@link AppError} (and its subclasses). */
|
|
64
95
|
export function isAppError(error) {
|
|
65
96
|
return error instanceof AppError;
|
|
66
97
|
}
|
|
67
|
-
/**
|
|
98
|
+
/** The message every 5xx response carries. */
|
|
99
|
+
export const GENERIC_ERROR_MESSAGE = 'Internal server error';
|
|
100
|
+
/**
|
|
101
|
+
* Message safe to show a caller. Only 4xx AppErrors keep their own message; every 5xx (including
|
|
102
|
+
* 503s, whose messages often name upstreams or RPC methods) and every non-AppError get the generic one.
|
|
103
|
+
*
|
|
104
|
+
* @param error Anything thrown.
|
|
105
|
+
* @returns The message to put in the response body.
|
|
106
|
+
* @example publicMessage(new NotFoundError('Order not found')) // 'Order not found'
|
|
107
|
+
*/
|
|
68
108
|
export function publicMessage(error) {
|
|
69
109
|
if (isAppError(error) && error.statusCode < 500)
|
|
70
110
|
return error.message;
|
|
71
|
-
|
|
72
|
-
return error.message;
|
|
73
|
-
return 'Internal server error';
|
|
111
|
+
return GENERIC_ERROR_MESSAGE;
|
|
74
112
|
}
|
|
75
113
|
//# sourceMappingURL=errors.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/common/errors.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/common/errors.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/common/phone.js
CHANGED
|
@@ -2,32 +2,65 @@ import libphonenumber from 'google-libphonenumber';
|
|
|
2
2
|
const util = libphonenumber.PhoneNumberUtil.getInstance();
|
|
3
3
|
const { PhoneNumberFormat } = libphonenumber;
|
|
4
4
|
/**
|
|
5
|
-
* Normalises a phone number to E.164
|
|
6
|
-
*
|
|
5
|
+
* Normalises a phone number to E.164. The region is required (no app default): local formats such as
|
|
6
|
+
* `0712345678` only make sense against a country, and that country is app configuration.
|
|
7
|
+
*
|
|
8
|
+
* @param phone Number as typed (local or international format).
|
|
9
|
+
* @param region ISO 3166-1 alpha-2 region used for local formats (e.g. `KE`, `GB`).
|
|
10
|
+
* @returns E.164 (`+254712345678`), or null when not a valid number for that region.
|
|
11
|
+
* @example toE164('0712345678', 'KE') // '+254712345678'
|
|
7
12
|
*/
|
|
8
|
-
export function toE164(phone,
|
|
13
|
+
export function toE164(phone, region) {
|
|
9
14
|
try {
|
|
10
|
-
const parsed = util.parseAndKeepRawInput(phone.trim(),
|
|
15
|
+
const parsed = util.parseAndKeepRawInput(phone.trim(), region);
|
|
11
16
|
return util.isValidNumber(parsed) ? util.format(parsed, PhoneNumberFormat.E164) : null;
|
|
12
17
|
}
|
|
13
18
|
catch {
|
|
14
19
|
return null;
|
|
15
20
|
}
|
|
16
21
|
}
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
/**
|
|
23
|
+
* E.164 without the plus sign, as most SMS gateways expect.
|
|
24
|
+
*
|
|
25
|
+
* @param phone Number as typed.
|
|
26
|
+
* @param region ISO region for local formats.
|
|
27
|
+
* @returns Digits only, or null when invalid.
|
|
28
|
+
* @example toMsisdn('0712345678', 'KE') // '254712345678'
|
|
29
|
+
*/
|
|
30
|
+
export function toMsisdn(phone, region) {
|
|
31
|
+
const e164 = toE164(phone, region);
|
|
20
32
|
return e164 ? e164.slice(1) : null;
|
|
21
33
|
}
|
|
22
|
-
|
|
34
|
+
/**
|
|
35
|
+
* The region a number belongs to.
|
|
36
|
+
*
|
|
37
|
+
* @param phone Number as typed.
|
|
38
|
+
* @param region ISO region used to interpret local formats.
|
|
39
|
+
* @returns ISO region code, or null when it cannot be determined.
|
|
40
|
+
* @example phoneRegion('+447400123456', 'KE') // 'GB'
|
|
41
|
+
*/
|
|
42
|
+
export function phoneRegion(phone, region) {
|
|
23
43
|
try {
|
|
24
|
-
return util.getRegionCodeForNumber(util.parse(phone,
|
|
44
|
+
return util.getRegionCodeForNumber(util.parse(phone, region)) ?? null;
|
|
25
45
|
}
|
|
26
46
|
catch {
|
|
27
47
|
return null;
|
|
28
48
|
}
|
|
29
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Masks a phone number for logs and UI, so a full number never lands in a log line or screen.
|
|
52
|
+
* Never reveals more than about a third of the input: numbers of 10+ characters keep the first 4 and
|
|
53
|
+
* last 2; shorter inputs keep only the last 2; 4 characters or fewer are fully masked.
|
|
54
|
+
*
|
|
55
|
+
* @param phone Any phone string (normalised or not).
|
|
56
|
+
* @returns The masked value.
|
|
57
|
+
* @example maskPhone('254712345678') // '2547****78'
|
|
58
|
+
*/
|
|
30
59
|
export function maskPhone(phone) {
|
|
31
|
-
|
|
60
|
+
if (phone.length <= 4)
|
|
61
|
+
return '****';
|
|
62
|
+
if (phone.length < 10)
|
|
63
|
+
return `****${phone.slice(-2)}`;
|
|
64
|
+
return `${phone.slice(0, 4)}****${phone.slice(-2)}`;
|
|
32
65
|
}
|
|
33
66
|
//# sourceMappingURL=phone.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"phone.js","sourceRoot":"","sources":["../../../src/common/phone.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,uBAAuB,CAAC;AAEnD,MAAM,IAAI,GAAG,cAAc,CAAC,eAAe,CAAC,WAAW,EAAE,CAAC;AAC1D,MAAM,EAAE,iBAAiB,EAAE,GAAG,cAAc,CAAC;AAE7C
|
|
1
|
+
{"version":3,"file":"phone.js","sourceRoot":"","sources":["../../../src/common/phone.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,uBAAuB,CAAC;AAEnD,MAAM,IAAI,GAAG,cAAc,CAAC,eAAe,CAAC,WAAW,EAAE,CAAC;AAC1D,MAAM,EAAE,iBAAiB,EAAE,GAAG,cAAc,CAAC;AAE7C;;;;;;;;GAQG;AACH,MAAM,UAAU,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,MAAM,UAAU,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,MAAM,UAAU,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,MAAM,UAAU,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"}
|
|
@@ -1,19 +1,75 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Standard success envelope: `{ success, message, data }`.
|
|
3
|
+
*
|
|
4
|
+
* @param data Payload placed under `data`.
|
|
5
|
+
* @param message Human-readable message (default `Success`).
|
|
6
|
+
* @returns The envelope, ready for `res.json`.
|
|
7
|
+
* @example res.json(successResponse(order, 'Order placed'));
|
|
8
|
+
*/
|
|
2
9
|
export function successResponse(data, message = 'Success') {
|
|
3
10
|
return { success: true, message, data };
|
|
4
11
|
}
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Standard error envelope: `{ success: false, message, code?, errors?, requestId? }`.
|
|
14
|
+
* Prefer throwing an AppError and letting the error middleware answer; use this only where a
|
|
15
|
+
* middleware must answer directly. For a body with a guaranteed `code`, use {@link errorBody}.
|
|
16
|
+
*
|
|
17
|
+
* @param message Message for the caller (never an internal error message).
|
|
18
|
+
* @param errors Optional field errors.
|
|
19
|
+
* @param code Optional UPPER_SNAKE_CASE code.
|
|
20
|
+
* @param requestId Optional correlation id of the request.
|
|
21
|
+
* @returns An envelope with `success: false`.
|
|
22
|
+
*/
|
|
23
|
+
export function errorResponse(message, errors, code, requestId) {
|
|
24
|
+
return { success: false, message, ...(code ? { code } : {}), ...(errors ? { errors } : {}), ...(requestId ? { requestId } : {}) };
|
|
8
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* The error body with a required `code`, as the error middleware sends it.
|
|
28
|
+
*
|
|
29
|
+
* @param code UPPER_SNAKE_CASE machine-readable code.
|
|
30
|
+
* @param message Message for the caller (generic for 5xx).
|
|
31
|
+
* @param options `errors` (field errors) and `requestId` (correlation id), both omitted when absent.
|
|
32
|
+
* @returns `{ success: false, code, message, errors?, requestId? }`.
|
|
33
|
+
*
|
|
34
|
+
* @example res.status(404).json(errorBody('NOT_FOUND', 'Resource not found', { requestId }));
|
|
35
|
+
*/
|
|
36
|
+
export function errorBody(code, message, options = {}) {
|
|
37
|
+
return {
|
|
38
|
+
success: false,
|
|
39
|
+
code,
|
|
40
|
+
message,
|
|
41
|
+
...(options.errors ? { errors: options.errors } : {}),
|
|
42
|
+
...(options.requestId ? { requestId: options.requestId } : {}),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Offset pagination meta. `totalPages` is at least 1 so an empty list still reads as "page 1 of 1".
|
|
47
|
+
*
|
|
48
|
+
* @param total Total matching rows.
|
|
49
|
+
* @param page 1-based page that was returned.
|
|
50
|
+
* @param limit Page size.
|
|
51
|
+
* @returns `{ page, limit, total, totalPages, hasNext, hasPrev }`.
|
|
52
|
+
* @example buildPaginationMeta(45, 2, 20) // { page: 2, totalPages: 3, hasNext: true, hasPrev: true, … }
|
|
53
|
+
*/
|
|
9
54
|
export function buildPaginationMeta(total, page, limit) {
|
|
10
55
|
const totalPages = limit > 0 ? Math.max(1, Math.ceil(total / limit)) : 1;
|
|
11
56
|
return { page, limit, total, totalPages, hasNext: page < totalPages, hasPrev: page > 1 };
|
|
12
57
|
}
|
|
13
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Offset-paginated envelope: `{ success, message, data: T[], meta }`.
|
|
60
|
+
*
|
|
61
|
+
* @returns The envelope with meta from {@link buildPaginationMeta}.
|
|
62
|
+
* @example res.json(paginatedResponse(rows, total, page, limit));
|
|
63
|
+
*/
|
|
14
64
|
export function paginatedResponse(data, total, page, limit, message = 'Success') {
|
|
15
65
|
return { success: true, message, data, meta: buildPaginationMeta(total, page, limit) };
|
|
16
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Offset page as a service returns it (data + meta, no envelope).
|
|
69
|
+
*
|
|
70
|
+
* @returns `{ data, meta }`.
|
|
71
|
+
* @example return buildPaginatedResult(rows, total, page, limit);
|
|
72
|
+
*/
|
|
17
73
|
export function buildPaginatedResult(data, total, page, limit) {
|
|
18
74
|
return { data, meta: buildPaginationMeta(total, page, limit) };
|
|
19
75
|
}
|
|
@@ -21,4 +77,18 @@ export function buildPaginatedResult(data, total, page, limit) {
|
|
|
21
77
|
export function pageOffset(page, limit) {
|
|
22
78
|
return Math.max(0, (Math.max(1, page) - 1) * limit);
|
|
23
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Cursor pagination meta. `hasMore` is derived from `nextCursor`, so the two can never disagree.
|
|
82
|
+
*
|
|
83
|
+
* @param limit The page size that was applied.
|
|
84
|
+
* @param nextCursor Opaque cursor for the next page, or null on the last page.
|
|
85
|
+
* @example buildCursorPageMeta(20, rows.length > 20 ? encodeCursor(rows[19]) : null)
|
|
86
|
+
*/
|
|
87
|
+
export function buildCursorPageMeta(limit, nextCursor) {
|
|
88
|
+
return { limit, nextCursor, hasMore: nextCursor !== null };
|
|
89
|
+
}
|
|
90
|
+
/** Cursor-paginated envelope: `{ success, message, data: T[], meta: { limit, nextCursor, hasMore } }`. */
|
|
91
|
+
export function cursorPaginatedResponse(page, message = 'Success') {
|
|
92
|
+
return { success: true, message, data: page.data, meta: page.meta };
|
|
93
|
+
}
|
|
24
94
|
//# sourceMappingURL=response.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response.js","sourceRoot":"","sources":["../../../src/common/response.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"response.js","sourceRoot":"","sources":["../../../src/common/response.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/config/index.js
CHANGED
|
@@ -10,7 +10,7 @@ export const serviceEnv = z.object({
|
|
|
10
10
|
NODE_ENV: z.enum(['development', 'test', 'staging', 'production']).default('development'),
|
|
11
11
|
PORT: z.coerce.number().int().positive(),
|
|
12
12
|
SERVICE_NAME: z.string().min(1),
|
|
13
|
-
/** Short app identifier used in exchange names and key prefixes (e.g. `
|
|
13
|
+
/** Short app identifier used in exchange names and key prefixes (e.g. `myapp`). */
|
|
14
14
|
APP_NAME: z.string().min(1).default('app'),
|
|
15
15
|
LOG_LEVEL: z.enum(['error', 'warn', 'info', 'http', 'debug']).optional(),
|
|
16
16
|
CORS_ORIGINS: z.string().default(''),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC;IACzF,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACxC,YAAY,EAAE,CAAC,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,44 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
const storage = new AsyncLocalStorage();
|
|
3
|
+
/**
|
|
4
|
+
* Runs `fn` (and all async work it starts) inside `context`. Use it at every entry point that is not an
|
|
5
|
+
* HTTP request: queue consumers, jobs and scripts.
|
|
6
|
+
*
|
|
7
|
+
* @param context Values for the duration of `fn`; the object is copied, so later edits don't leak back.
|
|
8
|
+
* @param fn Work to run inside the context.
|
|
9
|
+
* @returns Whatever `fn` returns.
|
|
10
|
+
* @example await runWithContext({ correlationId: msg.correlationId, service: 'worker' }, () => handle(msg));
|
|
11
|
+
*/
|
|
12
|
+
export function runWithContext(context, fn) {
|
|
13
|
+
return storage.run({ ...context, scopes: context.scopes ? { ...context.scopes } : undefined }, fn);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The current request context.
|
|
17
|
+
*
|
|
18
|
+
* @returns The context, or undefined outside {@link runWithContext} / `correlationMiddleware`.
|
|
19
|
+
* @example const id = getRequestContext()?.correlationId;
|
|
20
|
+
*/
|
|
21
|
+
export function getRequestContext() {
|
|
22
|
+
return storage.getStore();
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Adds values to the current context, for example the user id and scopes once auth has run. A no-op
|
|
26
|
+
* outside a context. The correlation id can't be changed once set.
|
|
27
|
+
*
|
|
28
|
+
* @param patch Values to add or replace; `scopes` are merged key by key.
|
|
29
|
+
* @returns True when a context was active and updated.
|
|
30
|
+
* @example updateRequestContext({ userId: user.id, scopes: user.scopes });
|
|
31
|
+
*/
|
|
32
|
+
export function updateRequestContext(patch) {
|
|
33
|
+
const current = storage.getStore();
|
|
34
|
+
if (!current)
|
|
35
|
+
return false;
|
|
36
|
+
if (patch.service !== undefined)
|
|
37
|
+
current.service = patch.service;
|
|
38
|
+
if (patch.userId !== undefined)
|
|
39
|
+
current.userId = patch.userId;
|
|
40
|
+
if (patch.scopes)
|
|
41
|
+
current.scopes = { ...current.scopes, ...patch.scopes };
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/context/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAerD,MAAM,OAAO,GAAG,IAAI,iBAAiB,EAAkB,CAAC;AAExD;;;;;;;;GAQG;AACH,MAAM,UAAU,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,MAAM,UAAU,iBAAiB;IAC/B,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC;AAC5B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/contracts/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/contracts/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC"}
|