@discover-cloud/shared 1.0.0 → 1.0.2

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 (61) hide show
  1. package/dist/authorization/index.d.ts +2 -0
  2. package/dist/authorization/index.js +18 -0
  3. package/dist/authorization/permission-cache.service.d.ts +12 -0
  4. package/dist/authorization/permission-cache.service.js +132 -0
  5. package/dist/authorization/permissions.d.ts +74 -0
  6. package/dist/authorization/permissions.js +171 -0
  7. package/dist/dto/auth-service.dtos.d.ts +5 -12
  8. package/dist/dto/response.dtos.d.ts +34 -27
  9. package/dist/dto/response.dtos.js +4 -0
  10. package/dist/dto/user-service.dtos.d.ts +9 -13
  11. package/dist/dtos/auth-service.dto.d.ts +44 -0
  12. package/dist/dtos/auth-service.dto.js +2 -0
  13. package/dist/dtos/index.d.ts +3 -0
  14. package/dist/dtos/index.js +19 -0
  15. package/dist/dtos/response.dto.d.ts +55 -0
  16. package/dist/dtos/response.dto.js +6 -0
  17. package/dist/dtos/user-service.dto.d.ts +50 -0
  18. package/dist/dtos/user-service.dto.js +2 -0
  19. package/dist/enums/domain.enums.d.ts +42 -0
  20. package/dist/enums/domain.enums.js +79 -0
  21. package/dist/enums/index.d.ts +2 -3
  22. package/dist/enums/index.js +2 -3
  23. package/dist/enums/permissions.enums.d.ts +124 -0
  24. package/dist/enums/permissions.enums.js +141 -0
  25. package/dist/errors/app-error.d.ts +19 -2
  26. package/dist/errors/app-error.js +17 -2
  27. package/dist/errors/http-errors.d.ts +18 -0
  28. package/dist/errors/http-errors.js +25 -1
  29. package/dist/http/index.d.ts +1 -0
  30. package/dist/http/index.js +17 -0
  31. package/dist/http/service-client.d.ts +23 -7
  32. package/dist/http/service-client.js +54 -22
  33. package/dist/index.d.ts +3 -2
  34. package/dist/index.js +3 -2
  35. package/dist/jwt/index.d.ts +1 -2
  36. package/dist/jwt/index.js +1 -2
  37. package/dist/jwt/internal-jwt-verifier.d.ts +35 -0
  38. package/dist/jwt/internal-jwt-verifier.js +162 -0
  39. package/dist/middleware/authorize.middleware.d.ts +22 -0
  40. package/dist/middleware/authorize.middleware.js +77 -0
  41. package/dist/middleware/error-handler.middleware.d.ts +16 -0
  42. package/dist/middleware/error-handler.middleware.js +42 -0
  43. package/dist/middleware/index.d.ts +4 -5
  44. package/dist/middleware/index.js +4 -5
  45. package/dist/middleware/request-id.middleware.d.ts +20 -0
  46. package/dist/middleware/request-id.middleware.js +34 -0
  47. package/dist/middleware/validate.middleware.d.ts +26 -0
  48. package/dist/middleware/validate.middleware.js +41 -0
  49. package/dist/types/express.types.d.ts +148 -0
  50. package/dist/types/express.types.js +42 -0
  51. package/dist/types/index.d.ts +1 -1
  52. package/dist/types/index.js +1 -1
  53. package/dist/utils/index.d.ts +2 -1
  54. package/dist/utils/index.js +2 -1
  55. package/dist/utils/logger.utils.d.ts +51 -0
  56. package/dist/utils/logger.utils.js +62 -0
  57. package/dist/utils/response.d.ts +2 -1
  58. package/dist/utils/response.js +6 -3
  59. package/dist/utils/response.utils.d.ts +12 -0
  60. package/dist/utils/response.utils.js +42 -0
  61. package/package.json +3 -2
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requestId = void 0;
4
+ const crypto_1 = require("crypto");
5
+ /**
6
+ * REQUEST ID MIDDLEWARE
7
+ * ──────────────────────
8
+ * Attaches a unique request ID to req.id for distributed tracing.
9
+ *
10
+ * Priority:
11
+ * 1. x-request-id header forwarded by the API Gateway (or load balancer)
12
+ * 2. Generate a new UUID if no upstream header is present
13
+ *
14
+ * Fix vs original:
15
+ * The original always generated a new UUID, breaking distributed tracing.
16
+ * If the gateway mints a requestId and forwards it via x-request-id,
17
+ * the downstream service must carry that same ID — otherwise logs across
18
+ * services cannot be correlated.
19
+ *
20
+ * Also sets x-request-id on the response so clients and proxies can
21
+ * correlate their logs with the service's logs.
22
+ */
23
+ const requestId = (req, res, next) => {
24
+ const upstream = req.headers["x-request-id"];
25
+ // Header can technically be a string array (multi-value) — take the first
26
+ const id = Array.isArray(upstream)
27
+ ? upstream[0]
28
+ : upstream ?? (0, crypto_1.randomUUID)();
29
+ req.id = id;
30
+ // Echo back on the response for client-side correlation
31
+ res.setHeader("x-request-id", id);
32
+ next();
33
+ };
34
+ exports.requestId = requestId;
@@ -0,0 +1,26 @@
1
+ import { Request, Response, NextFunction } from "express";
2
+ import { ZodType } from "zod";
3
+ /**
4
+ * VALIDATOR MIDDLEWARE
5
+ * ─────────────────────
6
+ * Validates and parses request input using a Zod schema.
7
+ * On success, attaches the parsed data to req.validated.
8
+ * On failure, passes a ZodError to GlobalErrorHandler → 400 response.
9
+ *
10
+ * Changes vs original:
11
+ * - Added "headers" as a valid source (useful for API key / custom header validation)
12
+ * - req.validated typed as unknown — consumers must narrow via z.infer at route level
13
+ *
14
+ * Usage:
15
+ * router.post("/orders",
16
+ * requireAuth,
17
+ * Validator.validate(CreateOrderSchema, "body"),
18
+ * controller.create
19
+ * );
20
+ *
21
+ * // In controller:
22
+ * const body = req.validated as z.infer<typeof CreateOrderSchema>;
23
+ */
24
+ export declare class Validator {
25
+ static validate<T>(schema: ZodType<T>, source?: "body" | "params" | "query" | "headers"): (req: Request, _res: Response, next: NextFunction) => void;
26
+ }
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Validator = void 0;
4
+ /**
5
+ * VALIDATOR MIDDLEWARE
6
+ * ─────────────────────
7
+ * Validates and parses request input using a Zod schema.
8
+ * On success, attaches the parsed data to req.validated.
9
+ * On failure, passes a ZodError to GlobalErrorHandler → 400 response.
10
+ *
11
+ * Changes vs original:
12
+ * - Added "headers" as a valid source (useful for API key / custom header validation)
13
+ * - req.validated typed as unknown — consumers must narrow via z.infer at route level
14
+ *
15
+ * Usage:
16
+ * router.post("/orders",
17
+ * requireAuth,
18
+ * Validator.validate(CreateOrderSchema, "body"),
19
+ * controller.create
20
+ * );
21
+ *
22
+ * // In controller:
23
+ * const body = req.validated as z.infer<typeof CreateOrderSchema>;
24
+ */
25
+ class Validator {
26
+ static validate(schema, source = "body") {
27
+ return (req, _res, next) => {
28
+ const result = schema.safeParse(req[source]);
29
+ if (!result.success) {
30
+ // ZodError propagates to GlobalErrorHandler → 400 with flatten()
31
+ next(result.error);
32
+ return;
33
+ }
34
+ // req.validated typed as unknown in express.d.ts
35
+ // Narrow to your schema type at the route/controller level
36
+ req.validated = result.data;
37
+ next();
38
+ };
39
+ }
40
+ }
41
+ exports.Validator = Validator;
@@ -0,0 +1,148 @@
1
+ /**
2
+ * EXPRESS TYPE AUGMENTATION + JWT PAYLOAD TYPES
3
+ * ───────────────────────────────────────────────
4
+ * Single source of truth for everything attached to Express's Request object
5
+ * and all internal JWT payload shapes.
6
+ *
7
+ * Belongs in @discover-cloud/shared — import in each service's entrypoint:
8
+ * import "@discover-cloud/shared/types/express";
9
+ *
10
+ * ─── Permission flow ────────────────────────────────────────────────
11
+ * JWT carries: accountId + accountRole (lean, no perms)
12
+ * Cache derives: GlobalPermission[] (Redis → role map on miss)
13
+ * AccessContext has: perms (populated by auth middleware)
14
+ *
15
+ * perms never touch the JWT — they live in the permission cache and are
16
+ * resolved into req.accessContext by RequireAuthMiddleware on every request.
17
+ */
18
+ import "express-serve-static-core";
19
+ import { JWTPayload } from "jose";
20
+ import { AccountRole } from "../enums";
21
+ import { GlobalPermission } from "../enums";
22
+ interface BaseInternalJwtPayload extends JWTPayload {
23
+ /**
24
+ * JWT ID — REQUIRED.
25
+ * Used for the Redis kill-switch (session revocation / account suspension).
26
+ * Overrides JWTPayload's optional jti with required.
27
+ */
28
+ jti: string;
29
+ /** typ discriminator — guards against token type confusion attacks */
30
+ typ: "internal";
31
+ /**
32
+ * The entity that last minted or forwarded this token.
33
+ * "api-gateway" → direct user-initiated request
34
+ * "order-service" → service-to-service call on behalf of a user
35
+ * Used for audit logging and conditional authorization.
36
+ */
37
+ caller: string;
38
+ /**
39
+ * Propagated request ID for distributed tracing.
40
+ * Ties together logs across the entire service call chain.
41
+ */
42
+ requestId?: string;
43
+ iss: string;
44
+ aud: string | string[];
45
+ iat: number;
46
+ exp: number;
47
+ nbf: number;
48
+ }
49
+ /**
50
+ * Human JWT payload — issued on behalf of an authenticated user.
51
+ * Minted by the gateway after verifying an external JWT.
52
+ *
53
+ * accountRole is carried so the permission cache can derive perms
54
+ * from globalRolePermissions on a cold cache miss — without a DB call.
55
+ */
56
+ export interface HumanInternalJwtPayload extends BaseInternalJwtPayload {
57
+ isMachine: false;
58
+ accountId: string;
59
+ accountRole: AccountRole;
60
+ }
61
+ /**
62
+ * Machine JWT payload — service-to-service calls with no user context.
63
+ * Intentionally carries NO user fields — absence is enforced by the type.
64
+ */
65
+ export interface MachineInternalJwtPayload extends BaseInternalJwtPayload {
66
+ isMachine: true;
67
+ serviceId: string;
68
+ }
69
+ /** The full internal JWT payload union — use this in verifiers and auth middleware */
70
+ export type InternalJwtPayload = HumanInternalJwtPayload | MachineInternalJwtPayload;
71
+ export interface HumanAccessContext {
72
+ kind: "human";
73
+ accountId: string;
74
+ accountRole: AccountRole;
75
+ /**
76
+ * Resolved from Redis permission cache — NOT from the JWT.
77
+ * Always present by the time req.accessContext is set.
78
+ * Invalidated on role change, suspension, and account deletion.
79
+ */
80
+ perms: GlobalPermission[];
81
+ }
82
+ export interface MachineAccessContext {
83
+ kind: "machine";
84
+ serviceId: string;
85
+ }
86
+ export type AccessContext = HumanAccessContext | MachineAccessContext;
87
+ export declare function isHumanPayload(payload: InternalJwtPayload): payload is HumanInternalJwtPayload;
88
+ export declare function isMachinePayload(payload: InternalJwtPayload): payload is MachineInternalJwtPayload;
89
+ export declare function isHumanContext(ctx: AccessContext): ctx is HumanAccessContext;
90
+ export declare function isMachineContext(ctx: AccessContext): ctx is MachineAccessContext;
91
+ export interface VerifiedAccessPayload {
92
+ accountId: string;
93
+ accountRole: AccountRole;
94
+ typ: "access";
95
+ iss: string;
96
+ aud: string | string[];
97
+ iat: number;
98
+ exp: number;
99
+ nbf: number;
100
+ jti: string;
101
+ }
102
+ export interface VerifiedRefreshPayload {
103
+ accountId: string;
104
+ typ: "refresh";
105
+ iss: string;
106
+ aud: string | string[];
107
+ iat: number;
108
+ exp: number;
109
+ nbf: number;
110
+ jti: string;
111
+ }
112
+ declare module "express-serve-static-core" {
113
+ interface Request {
114
+ /**
115
+ * Unique request ID — set by requestId middleware.
116
+ * Respects upstream x-request-id from the gateway / load balancer.
117
+ * Always present after requestId middleware runs.
118
+ */
119
+ id: string;
120
+ /**
121
+ * Validated + parsed request body/query/params/headers.
122
+ * Typed as unknown — narrow at the route level:
123
+ *
124
+ * const body = req.validated as z.infer<typeof CreateOrderSchema>;
125
+ *
126
+ * Never cast to any.
127
+ */
128
+ validated?: unknown;
129
+ /**
130
+ * Raw verified internal JWT payload.
131
+ * Set by RequireAuthMiddleware after InternalJwtVerifier.verifyInternal().
132
+ *
133
+ * Prefer req.accessContext in route handlers.
134
+ * Access this only when you need raw JWT claims (e.g. jti for blacklisting).
135
+ */
136
+ internalAuth?: InternalJwtPayload;
137
+ /**
138
+ * Clean access context — built from internalAuth + permission cache.
139
+ * This is what route handlers, controllers, and domain services use.
140
+ *
141
+ * if (isHumanContext(req.accessContext)) {
142
+ * const { accountId, accountRole, perms } = req.accessContext;
143
+ * }
144
+ */
145
+ accessContext?: AccessContext;
146
+ }
147
+ }
148
+ export {};
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ /**
3
+ * EXPRESS TYPE AUGMENTATION + JWT PAYLOAD TYPES
4
+ * ───────────────────────────────────────────────
5
+ * Single source of truth for everything attached to Express's Request object
6
+ * and all internal JWT payload shapes.
7
+ *
8
+ * Belongs in @discover-cloud/shared — import in each service's entrypoint:
9
+ * import "@discover-cloud/shared/types/express";
10
+ *
11
+ * ─── Permission flow ────────────────────────────────────────────────
12
+ * JWT carries: accountId + accountRole (lean, no perms)
13
+ * Cache derives: GlobalPermission[] (Redis → role map on miss)
14
+ * AccessContext has: perms (populated by auth middleware)
15
+ *
16
+ * perms never touch the JWT — they live in the permission cache and are
17
+ * resolved into req.accessContext by RequireAuthMiddleware on every request.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.isHumanPayload = isHumanPayload;
21
+ exports.isMachinePayload = isMachinePayload;
22
+ exports.isHumanContext = isHumanContext;
23
+ exports.isMachineContext = isMachineContext;
24
+ require("express-serve-static-core");
25
+ /* ====================================================================
26
+ 4. TYPE GUARDS
27
+
28
+ Use these over raw property checks — TypeScript narrows correctly
29
+ through them on both InternalJwtPayload and AccessContext.
30
+ ==================================================================== */
31
+ function isHumanPayload(payload) {
32
+ return payload.isMachine === false;
33
+ }
34
+ function isMachinePayload(payload) {
35
+ return payload.isMachine === true;
36
+ }
37
+ function isHumanContext(ctx) {
38
+ return ctx.kind === "human";
39
+ }
40
+ function isMachineContext(ctx) {
41
+ return ctx.kind === "machine";
42
+ }
@@ -1 +1 @@
1
- export * from "./express";
1
+ export * from "./express.types";
@@ -14,4 +14,4 @@ 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("./express"), exports);
17
+ __exportStar(require("./express.types"), exports);
@@ -1 +1,2 @@
1
- export * from "./response";
1
+ export * from "./response.utils";
2
+ export * from "./logger.utils";
@@ -14,4 +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("./response"), exports);
17
+ __exportStar(require("./response.utils"), exports);
18
+ __exportStar(require("./logger.utils"), exports);
@@ -0,0 +1,51 @@
1
+ /**
2
+ * LOGGER INTERFACE (@discover-cloud/shared)
3
+ * ────────────────────────────────────────────
4
+ * A minimal logger contract shared code can depend on.
5
+ * Each service injects its own implementation (pino, winston, console).
6
+ *
7
+ * Shared code (RequireAuthMiddleware, PermissionCacheService, etc.)
8
+ * accepts this interface — it never imports a concrete logger directly.
9
+ *
10
+ * ─── Usage in shared classes ────────────────────────────────────────
11
+ * class RequireAuthMiddleware {
12
+ * constructor(
13
+ * private readonly verifier: InternalJwtVerifier,
14
+ * private readonly logger: ILogger = noopLogger,
15
+ * ) {}
16
+ * }
17
+ *
18
+ * ─── Wiring in each service ─────────────────────────────────────────
19
+ * // auth-service / composition root:
20
+ * import pino from "pino";
21
+ * const pinoLogger = pino({ level: "info" });
22
+ *
23
+ * const requireAuth = new RequireAuthMiddleware(jwtVerifier, pinoLogger);
24
+ *
25
+ * ─── noopLogger ─────────────────────────────────────────────────────
26
+ * The default when no logger is injected — silently drops all logs.
27
+ * Safe for tests and library consumers that don't want log noise.
28
+ */
29
+ export interface ILogger {
30
+ debug(obj: object, msg?: string): void;
31
+ debug(msg: string): void;
32
+ info(obj: object, msg?: string): void;
33
+ info(msg: string): void;
34
+ warn(obj: object, msg?: string): void;
35
+ warn(msg: string): void;
36
+ error(obj: object, msg?: string): void;
37
+ error(msg: string): void;
38
+ }
39
+ /**
40
+ * noopLogger
41
+ * Silent default — used when no logger is injected.
42
+ * Satisfies ILogger without importing any logging library.
43
+ */
44
+ export declare const noopLogger: ILogger;
45
+ /**
46
+ * consoleLogger
47
+ * Thin console wrapper — useful for local dev and simple services
48
+ * that don't need structured logging.
49
+ * Pass this when you want logs without wiring up pino/winston.
50
+ */
51
+ export declare const consoleLogger: ILogger;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ /**
3
+ * LOGGER INTERFACE (@discover-cloud/shared)
4
+ * ────────────────────────────────────────────
5
+ * A minimal logger contract shared code can depend on.
6
+ * Each service injects its own implementation (pino, winston, console).
7
+ *
8
+ * Shared code (RequireAuthMiddleware, PermissionCacheService, etc.)
9
+ * accepts this interface — it never imports a concrete logger directly.
10
+ *
11
+ * ─── Usage in shared classes ────────────────────────────────────────
12
+ * class RequireAuthMiddleware {
13
+ * constructor(
14
+ * private readonly verifier: InternalJwtVerifier,
15
+ * private readonly logger: ILogger = noopLogger,
16
+ * ) {}
17
+ * }
18
+ *
19
+ * ─── Wiring in each service ─────────────────────────────────────────
20
+ * // auth-service / composition root:
21
+ * import pino from "pino";
22
+ * const pinoLogger = pino({ level: "info" });
23
+ *
24
+ * const requireAuth = new RequireAuthMiddleware(jwtVerifier, pinoLogger);
25
+ *
26
+ * ─── noopLogger ─────────────────────────────────────────────────────
27
+ * The default when no logger is injected — silently drops all logs.
28
+ * Safe for tests and library consumers that don't want log noise.
29
+ */
30
+ Object.defineProperty(exports, "__esModule", { value: true });
31
+ exports.consoleLogger = exports.noopLogger = void 0;
32
+ /**
33
+ * noopLogger
34
+ * Silent default — used when no logger is injected.
35
+ * Satisfies ILogger without importing any logging library.
36
+ */
37
+ exports.noopLogger = {
38
+ debug: () => { },
39
+ info: () => { },
40
+ warn: () => { },
41
+ error: () => { },
42
+ };
43
+ /**
44
+ * consoleLogger
45
+ * Thin console wrapper — useful for local dev and simple services
46
+ * that don't need structured logging.
47
+ * Pass this when you want logs without wiring up pino/winston.
48
+ */
49
+ exports.consoleLogger = {
50
+ debug: (objOrMsg, msg) => {
51
+ console.debug(msg ?? objOrMsg, typeof objOrMsg === "object" ? objOrMsg : "");
52
+ },
53
+ info: (objOrMsg, msg) => {
54
+ console.info(msg ?? objOrMsg, typeof objOrMsg === "object" ? objOrMsg : "");
55
+ },
56
+ warn: (objOrMsg, msg) => {
57
+ console.warn(msg ?? objOrMsg, typeof objOrMsg === "object" ? objOrMsg : "");
58
+ },
59
+ error: (objOrMsg, msg) => {
60
+ console.error(msg ?? objOrMsg, typeof objOrMsg === "object" ? objOrMsg : "");
61
+ },
62
+ };
@@ -1,3 +1,4 @@
1
1
  import { Response } from "express";
2
2
  export declare const success: <T>(res: Response, data: T, statusCode?: number) => Response<any, Record<string, any>>;
3
- export declare const failure: (res: Response, message: string, statusCode?: number, details?: unknown) => Response<any, Record<string, any>>;
3
+ export declare const failure: (res: Response, message: string, code: string, // <-- Added to match the DTO requirement
4
+ statusCode?: number, details?: unknown) => Response<any, Record<string, any>>;
@@ -2,28 +2,31 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.failure = exports.success = void 0;
4
4
  const success = (res, data, statusCode = 200) => {
5
+ // Cast to Request to access properties safely, assuming module augmentation
5
6
  const req = res.req;
6
7
  const response = {
7
8
  success: true,
8
9
  data,
9
10
  meta: {
10
- requestId: req?.id ?? null,
11
+ requestId: req.id ?? "unknown", // Fallback if middleware failed
11
12
  timestamp: new Date().toISOString()
12
13
  }
13
14
  };
14
15
  return res.status(statusCode).json(response);
15
16
  };
16
17
  exports.success = success;
17
- const failure = (res, message, statusCode = 400, details) => {
18
+ const failure = (res, message, code, // <-- Added to match the DTO requirement
19
+ statusCode = 400, details) => {
18
20
  const req = res.req;
19
21
  const response = {
20
22
  success: false,
21
23
  error: {
24
+ code, // <-- Added here
22
25
  message,
23
26
  details: details ?? null
24
27
  },
25
28
  meta: {
26
- requestId: req?.id ?? null,
29
+ requestId: req.id ?? "unknown",
27
30
  timestamp: new Date().toISOString()
28
31
  }
29
32
  };
@@ -0,0 +1,12 @@
1
+ import { Response, Request } from "express";
2
+ /**
3
+ * RESPONSE HELPERS
4
+ * ─────────────────
5
+ * Typed wrappers around res.json() that enforce the ApiSuccessResponse
6
+ * and ApiErrorResponse envelope shapes.
7
+ *
8
+ * Both require req explicitly — avoids the res.req cast anti-pattern
9
+ * and makes the dependency clear at the call site.
10
+ */
11
+ export declare const success: <T>(res: Response, req: Request, data: T, statusCode?: number) => void;
12
+ export declare const failure: (res: Response, req: Request, message: string, code: string, statusCode?: number, details?: unknown) => void;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.failure = exports.success = void 0;
4
+ const crypto_1 = require("crypto");
5
+ /**
6
+ * RESPONSE HELPERS
7
+ * ─────────────────
8
+ * Typed wrappers around res.json() that enforce the ApiSuccessResponse
9
+ * and ApiErrorResponse envelope shapes.
10
+ *
11
+ * Both require req explicitly — avoids the res.req cast anti-pattern
12
+ * and makes the dependency clear at the call site.
13
+ */
14
+ const success = (res, req, data, statusCode = 200) => {
15
+ const response = {
16
+ success: true,
17
+ data,
18
+ meta: {
19
+ requestId: req.id ?? (0, crypto_1.randomUUID)(), // randomUUID fallback — "unknown" breaks log search
20
+ timestamp: new Date().toISOString(),
21
+ },
22
+ };
23
+ res.status(statusCode).json(response);
24
+ };
25
+ exports.success = success;
26
+ const failure = (res, req, message, code, statusCode = 400, details) => {
27
+ const response = {
28
+ success: false,
29
+ error: {
30
+ code,
31
+ message,
32
+ // Omit details entirely when not provided — avoids "details": null noise
33
+ ...(details !== undefined ? { details } : {}),
34
+ },
35
+ meta: {
36
+ requestId: req.id ?? (0, crypto_1.randomUUID)(),
37
+ timestamp: new Date().toISOString(),
38
+ },
39
+ };
40
+ res.status(statusCode).json(response);
41
+ };
42
+ exports.failure = failure;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@discover-cloud/shared",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "private": false,
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",
@@ -17,7 +17,8 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "axios-retry": "^4.5.0",
20
- "jose": "^6.1.3"
20
+ "jose": "^6.1.3",
21
+ "redis": "^5.11.0"
21
22
  },
22
23
  "peerDependencies": {
23
24
  "axios": "^1.13.5",