@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.
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,3 @@
1
+ export * from "./user-service.dto";
2
+ export * from "./auth-service.dto";
3
+ export * from "./response.dto";
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export * from "./enums";
2
2
  export * from "./errors";
3
- export * from "./dto";
3
+ export * from "./dtos";
4
4
  export * from "./authorization";
5
5
  export * from "./utils";
6
6
  export * from "./middleware";
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("./dto"), exports);
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 → 400 with flattened validation details
9
- * 2. AppError → mapped statusCode + code (BadRequestError, NotFoundError, etc.)
10
- * 3. Unknown → 500 Internal Server Error
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 app_error_1 = require("../errors/app-error");
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 → 400 with flattened validation details
14
- * 2. AppError → mapped statusCode + code (BadRequestError, NotFoundError, etc.)
15
- * 3. Unknown → 500 Internal Server Error
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 ${requestId}]`, err);
22
+ console.error(`[req ${req.id}]`, err);
32
23
  }
33
24
  else {
34
- // In production, log without the stack trace in the response
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((issue) => issue.message));
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 (BadRequestError, NotFoundError, etc.)
43
- // instanceof check safe, no duck-typing
44
- if (err instanceof app_error_1.AppError) {
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. Unhandled / unexpected errors — never leak internals
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
 
@@ -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
+ };
@@ -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.1",
3
+ "version": "1.0.3",
4
4
  "private": false,
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",