@discover-cloud/shared 1.0.1 → 1.0.3
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/dist/dtos/auth-service.dto.d.ts +44 -0
- package/dist/dtos/auth-service.dto.js +2 -0
- package/dist/dtos/index.d.ts +3 -0
- package/dist/dtos/index.js +19 -0
- package/dist/dtos/response.dto.d.ts +55 -0
- package/dist/dtos/response.dto.js +6 -0
- package/dist/dtos/user-service.dto.d.ts +50 -0
- package/dist/dtos/user-service.dto.js +2 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/middleware/error-handler.middleware.d.ts +3 -10
- package/dist/middleware/error-handler.middleware.js +13 -23
- package/dist/types/express.types.d.ts +0 -1
- package/dist/types/express.types.js +0 -1
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/index.js +2 -1
- package/dist/utils/logger.utils.d.ts +51 -0
- package/dist/utils/logger.utils.js +62 -0
- package/dist/utils/response.utils.d.ts +12 -0
- package/dist/utils/response.utils.js +42 -0
- package/package.json +1 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { AccountStatus } from "../enums";
|
|
2
|
+
export interface AccountDto {
|
|
3
|
+
id: string;
|
|
4
|
+
email: string;
|
|
5
|
+
isVerified: boolean;
|
|
6
|
+
status: AccountStatus;
|
|
7
|
+
createdAt: Date;
|
|
8
|
+
updatedAt: Date;
|
|
9
|
+
}
|
|
10
|
+
export interface EmailVerificationDto {
|
|
11
|
+
id: string;
|
|
12
|
+
accountId: string;
|
|
13
|
+
token: string;
|
|
14
|
+
expiresAt: Date;
|
|
15
|
+
isUsed: boolean;
|
|
16
|
+
createdAt: Date;
|
|
17
|
+
}
|
|
18
|
+
export interface OAuthIdentityDto {
|
|
19
|
+
id: string;
|
|
20
|
+
accountId: string;
|
|
21
|
+
provider: string;
|
|
22
|
+
providerId: string;
|
|
23
|
+
createdAt: Date;
|
|
24
|
+
updatedAt: Date;
|
|
25
|
+
}
|
|
26
|
+
export interface PasswordResetDto {
|
|
27
|
+
id: string;
|
|
28
|
+
accountId: string;
|
|
29
|
+
token: string;
|
|
30
|
+
expiresAt: Date;
|
|
31
|
+
isUsed: boolean;
|
|
32
|
+
createdAt: Date;
|
|
33
|
+
}
|
|
34
|
+
export interface SessionDto {
|
|
35
|
+
id: string;
|
|
36
|
+
accountId: string;
|
|
37
|
+
expiresAt: Date;
|
|
38
|
+
revokedAt: Date | null;
|
|
39
|
+
ipAddress: string | null;
|
|
40
|
+
userAgent: string | null;
|
|
41
|
+
device: string | null;
|
|
42
|
+
createdAt: Date;
|
|
43
|
+
updatedAt: Date;
|
|
44
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./user-service.dto"), exports);
|
|
18
|
+
__exportStar(require("./auth-service.dto"), exports);
|
|
19
|
+
__exportStar(require("./response.dto"), exports);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export interface ApiMeta {
|
|
2
|
+
requestId: string;
|
|
3
|
+
timestamp: string;
|
|
4
|
+
}
|
|
5
|
+
export interface ApiSuccessResponse<T> {
|
|
6
|
+
success: true;
|
|
7
|
+
data: T;
|
|
8
|
+
meta: ApiMeta;
|
|
9
|
+
}
|
|
10
|
+
export interface ApiErrorResponse {
|
|
11
|
+
success: false;
|
|
12
|
+
error: {
|
|
13
|
+
code: string;
|
|
14
|
+
message: string;
|
|
15
|
+
details?: unknown;
|
|
16
|
+
};
|
|
17
|
+
meta: ApiMeta;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Returned on login / token refresh.
|
|
21
|
+
* accessToken goes in the response body.
|
|
22
|
+
* refreshToken goes in an HttpOnly cookie — not in the body.
|
|
23
|
+
* Only include refreshToken here if your client explicitly needs it
|
|
24
|
+
* (e.g. mobile clients that can't use cookies).
|
|
25
|
+
*/
|
|
26
|
+
export interface TokensResponseDto {
|
|
27
|
+
accessToken: string;
|
|
28
|
+
}
|
|
29
|
+
/** Generic message — use for simple confirmations */
|
|
30
|
+
export interface MessageResponseDto {
|
|
31
|
+
message: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Generic action confirmation — use when the client needs to know
|
|
35
|
+
* which action was performed (e.g. in a polling or event-driven context).
|
|
36
|
+
*
|
|
37
|
+
* action: machine-readable string, e.g. "account.suspended", "session.revoked"
|
|
38
|
+
* message: human-readable description
|
|
39
|
+
*/
|
|
40
|
+
export interface ActionResponseDto {
|
|
41
|
+
action: string;
|
|
42
|
+
message: string;
|
|
43
|
+
}
|
|
44
|
+
export interface PaginationMeta {
|
|
45
|
+
page: number;
|
|
46
|
+
pageSize: number;
|
|
47
|
+
totalItems: number;
|
|
48
|
+
totalPages: number;
|
|
49
|
+
hasNext: boolean;
|
|
50
|
+
hasPrev: boolean;
|
|
51
|
+
}
|
|
52
|
+
export interface PaginatedResponseDto<T> {
|
|
53
|
+
items: T[];
|
|
54
|
+
pagination: PaginationMeta;
|
|
55
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* ====================================================================
|
|
3
|
+
RESPONSE DTOs
|
|
4
|
+
Shared HTTP response shapes used across all services.
|
|
5
|
+
==================================================================== */
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { AccountStatus, OrganizationRole, MembershipStatus, OrganizationStatus, Theme, Currency } from "../enums";
|
|
2
|
+
export interface UserDto {
|
|
3
|
+
id: string;
|
|
4
|
+
email: string;
|
|
5
|
+
status: AccountStatus;
|
|
6
|
+
createdAt: Date;
|
|
7
|
+
updatedAt: Date;
|
|
8
|
+
}
|
|
9
|
+
export interface UserProfileDto {
|
|
10
|
+
id: string;
|
|
11
|
+
userId: string;
|
|
12
|
+
displayName: string | null;
|
|
13
|
+
avatarUrl: string | null;
|
|
14
|
+
jobTitle: string | null;
|
|
15
|
+
bio: string | null;
|
|
16
|
+
timezone: string | null;
|
|
17
|
+
locale: string;
|
|
18
|
+
country: string | null;
|
|
19
|
+
pronouns: string | null;
|
|
20
|
+
createdAt: Date;
|
|
21
|
+
updatedAt: Date;
|
|
22
|
+
}
|
|
23
|
+
export interface UserPreferencesDto {
|
|
24
|
+
id: string;
|
|
25
|
+
userId: string;
|
|
26
|
+
theme: Theme;
|
|
27
|
+
language: string;
|
|
28
|
+
currency: Currency;
|
|
29
|
+
emailAlerts: boolean;
|
|
30
|
+
createdAt: Date;
|
|
31
|
+
updatedAt: Date;
|
|
32
|
+
}
|
|
33
|
+
export interface OrganizationDto {
|
|
34
|
+
id: string;
|
|
35
|
+
name: string;
|
|
36
|
+
slug: string;
|
|
37
|
+
status: OrganizationStatus;
|
|
38
|
+
createdAt: Date;
|
|
39
|
+
updatedAt: Date;
|
|
40
|
+
deletedAt: Date | null;
|
|
41
|
+
}
|
|
42
|
+
export interface OrganizationMemberDto {
|
|
43
|
+
id: string;
|
|
44
|
+
userId: string;
|
|
45
|
+
organizationId: string;
|
|
46
|
+
role: OrganizationRole;
|
|
47
|
+
status: MembershipStatus;
|
|
48
|
+
createdAt: Date;
|
|
49
|
+
updatedAt: Date;
|
|
50
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./enums"), exports);
|
|
18
18
|
__exportStar(require("./errors"), exports);
|
|
19
|
-
__exportStar(require("./
|
|
19
|
+
__exportStar(require("./dtos"), exports);
|
|
20
20
|
__exportStar(require("./authorization"), exports);
|
|
21
21
|
__exportStar(require("./utils"), exports);
|
|
22
22
|
__exportStar(require("./middleware"), exports);
|
|
@@ -5,18 +5,11 @@ import { Request, Response, NextFunction } from "express";
|
|
|
5
5
|
* Centralized error handler for all Express services.
|
|
6
6
|
*
|
|
7
7
|
* Handles in order:
|
|
8
|
-
* 1. ZodError
|
|
9
|
-
* 2. AppError
|
|
10
|
-
* 3. Unknown
|
|
8
|
+
* 1. ZodError → 400 with flattened validation details
|
|
9
|
+
* 2. AppError → mapped statusCode + code
|
|
10
|
+
* 3. Unknown → 500 Internal Server Error (internals never leaked)
|
|
11
11
|
*
|
|
12
12
|
* Every response includes requestId for traceability.
|
|
13
|
-
*
|
|
14
|
-
* Fix vs original:
|
|
15
|
-
* - Replaced unsafe duck-type cast `(err as { statusCode? })` with
|
|
16
|
-
* proper `instanceof AppError` check. Duck-typing any error with a
|
|
17
|
-
* statusCode property as an AppError is a bug vector.
|
|
18
|
-
* - req.id included in every error response
|
|
19
|
-
* - Stack trace logged only in non-production environments
|
|
20
13
|
*/
|
|
21
14
|
export declare class GlobalErrorHandler {
|
|
22
15
|
static handle(err: unknown, req: Request, res: Response, _next: NextFunction): void;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.GlobalErrorHandler = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
-
const
|
|
5
|
+
const errors_1 = require("../errors");
|
|
6
6
|
const utils_1 = require("../utils");
|
|
7
7
|
/**
|
|
8
8
|
* GLOBAL ERROR HANDLER
|
|
@@ -10,43 +10,33 @@ const utils_1 = require("../utils");
|
|
|
10
10
|
* Centralized error handler for all Express services.
|
|
11
11
|
*
|
|
12
12
|
* Handles in order:
|
|
13
|
-
* 1. ZodError
|
|
14
|
-
* 2. AppError
|
|
15
|
-
* 3. Unknown
|
|
13
|
+
* 1. ZodError → 400 with flattened validation details
|
|
14
|
+
* 2. AppError → mapped statusCode + code
|
|
15
|
+
* 3. Unknown → 500 Internal Server Error (internals never leaked)
|
|
16
16
|
*
|
|
17
17
|
* Every response includes requestId for traceability.
|
|
18
|
-
*
|
|
19
|
-
* Fix vs original:
|
|
20
|
-
* - Replaced unsafe duck-type cast `(err as { statusCode? })` with
|
|
21
|
-
* proper `instanceof AppError` check. Duck-typing any error with a
|
|
22
|
-
* statusCode property as an AppError is a bug vector.
|
|
23
|
-
* - req.id included in every error response
|
|
24
|
-
* - Stack trace logged only in non-production environments
|
|
25
18
|
*/
|
|
26
19
|
class GlobalErrorHandler {
|
|
27
20
|
static handle(err, req, res, _next) {
|
|
28
|
-
const requestId = req.id;
|
|
29
|
-
// Always log — replace with your pino/winston logger in production
|
|
30
21
|
if (process.env.NODE_ENV !== "production") {
|
|
31
|
-
console.error(`[req ${
|
|
22
|
+
console.error(`[req ${req.id}]`, err);
|
|
32
23
|
}
|
|
33
24
|
else {
|
|
34
|
-
|
|
35
|
-
console.error(`[req ${requestId}]`, err instanceof Error ? err.message : err);
|
|
25
|
+
console.error(`[req ${req.id}]`, err instanceof Error ? err.message : err);
|
|
36
26
|
}
|
|
37
27
|
// 1. Zod validation errors
|
|
38
28
|
if (err instanceof zod_1.ZodError) {
|
|
39
|
-
(0, utils_1.failure)(res, "Validation failed", "VALIDATION_ERROR", 400, err.flatten(
|
|
29
|
+
(0, utils_1.failure)(res, req, "Validation failed", "VALIDATION_ERROR", 400, err.flatten() // default flatten — preserves fieldErrors + formErrors + codes
|
|
30
|
+
);
|
|
40
31
|
return;
|
|
41
32
|
}
|
|
42
|
-
// 2. Known AppError subclasses
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
(0, utils_1.failure)(res, err.message, err.code, err.statusCode, err.details);
|
|
33
|
+
// 2. Known AppError subclasses — instanceof, never duck-typing
|
|
34
|
+
if (err instanceof errors_1.AppError) {
|
|
35
|
+
(0, utils_1.failure)(res, req, err.message, err.code, err.statusCode, err.details);
|
|
46
36
|
return;
|
|
47
37
|
}
|
|
48
|
-
// 3.
|
|
49
|
-
(0, utils_1.failure)(res, "Internal Server Error", "INTERNAL_SERVER_ERROR", 500);
|
|
38
|
+
// 3. Unexpected errors — never leak internals to the client
|
|
39
|
+
(0, utils_1.failure)(res, req, "Internal Server Error", "INTERNAL_SERVER_ERROR", 500);
|
|
50
40
|
}
|
|
51
41
|
}
|
|
52
42
|
exports.GlobalErrorHandler = GlobalErrorHandler;
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
* perms never touch the JWT — they live in the permission cache and are
|
|
16
16
|
* resolved into req.accessContext by RequireAuthMiddleware on every request.
|
|
17
17
|
*/
|
|
18
|
-
import "express-serve-static-core";
|
|
19
18
|
import { JWTPayload } from "jose";
|
|
20
19
|
import { AccountRole } from "../enums";
|
|
21
20
|
import { GlobalPermission } from "../enums";
|
|
@@ -21,7 +21,6 @@ exports.isHumanPayload = isHumanPayload;
|
|
|
21
21
|
exports.isMachinePayload = isMachinePayload;
|
|
22
22
|
exports.isHumanContext = isHumanContext;
|
|
23
23
|
exports.isMachineContext = isMachineContext;
|
|
24
|
-
require("express-serve-static-core");
|
|
25
24
|
/* ====================================================================
|
|
26
25
|
4. TYPE GUARDS
|
|
27
26
|
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export * from "./response";
|
|
1
|
+
export * from "./response.utils";
|
|
2
|
+
export * from "./logger.utils";
|
package/dist/utils/index.js
CHANGED
|
@@ -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
|
+
};
|
|
@@ -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;
|