http-response-kit 1.0.0 → 1.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 CHANGED
@@ -37,17 +37,22 @@ throw HttpError.unauthorized('Token expired');
37
37
  throw new HttpError(404, { message: 'User not found' });
38
38
  throw new HttpError(429, { message: 'Slow down!', retryAfter: 60 });
39
39
 
40
- // Format success responses
41
- const response = HttpResponse.success({
40
+ // Format success responses using Generics for typesafety
41
+ interface User {
42
+ id: number;
43
+ name: string;
44
+ }
45
+
46
+ const response = HttpResponse.success<User>({
42
47
  data: { id: 1, name: 'John' },
43
48
  message: 'User retrieved successfully'
44
49
  });
45
- // { success: true, status_code: 200, data: {...}, message: '...' }
50
+ // { success: true, status_code: 200, data: User, message: '...' }
46
51
 
47
52
  // Format error responses
48
53
  const error = HttpError.notFound('User not found');
49
54
  const errorResponse = HttpResponse.error(error);
50
- // { success: false, status_code: 404, error: { type: 'not_found', ... } }
55
+ // { success: false, status_code: 404, error: { type: 'not_found', message: '...', details: '...', ... } }
51
56
  ```
52
57
 
53
58
  ## Usage with Express
@@ -87,7 +92,7 @@ app.use((err, req, res, next) => {
87
92
 
88
93
  ## API Reference
89
94
 
90
- ### HttpSuccessCode (1xx Informational)
95
+ ### HttpInfoCode (1xx Informational)
91
96
 
92
97
  | Enum | Code | Description |
93
98
  |------|------|-------------|
@@ -180,6 +185,10 @@ app.use((err, req, res, next) => {
180
185
  #### Utility Methods
181
186
 
182
187
  ```typescript
188
+ // Create error from status code (semantic alias for new HttpError(code))
189
+ const error = HttpError.fromStatus(503);
190
+ const errorWithMsg = HttpError.fromStatus(404, { message: 'Not here' });
191
+
183
192
  // Convert any error to HttpError
184
193
  const httpError = HttpError.fromError(unknownError);
185
194
 
@@ -199,16 +208,16 @@ throw HttpError.badRequest('Validation failed', {
199
208
  ### HttpResponse
200
209
 
201
210
  ```typescript
202
- // Success responses
203
- HttpResponse.success({ data, message, statusCode, metadata });
204
- HttpResponse.ok(data, message);
205
- HttpResponse.created(data, message);
206
- HttpResponse.accepted(data, message);
211
+ // Success responses using Generics for Type Safety
212
+ HttpResponse.success<User>({ data, message, statusCode, metadata });
213
+ HttpResponse.ok<User>(data, message);
214
+ HttpResponse.created<User>(data, message);
215
+ HttpResponse.accepted<User>(data, message);
207
216
  HttpResponse.noContent();
208
217
 
209
218
  // Error responses
210
- HttpResponse.error(httpError);
211
- HttpResponse.fromError(anyError);
219
+ HttpResponse.error(httpError, { includeStack: true });
220
+ HttpResponse.fromError(anyError, { fallbackCode: 503 });
212
221
 
213
222
  // Paginated responses
214
223
  HttpResponse.paginated(items, {
@@ -224,7 +233,7 @@ HttpResponse.paginated(items, {
224
233
  import { configure } from 'http-response-kit';
225
234
 
226
235
  configure({
227
- // Enable stack traces in error responses
236
+ // Enable stack traces in error responses (auto-detects from NODE_ENV if omitted)
228
237
  isDevelopment: true,
229
238
 
230
239
  // Include timestamps in all responses
@@ -234,7 +243,7 @@ configure({
234
243
  customMessages: {
235
244
  404: 'Oops! This page went on vacation 🏝️',
236
245
  500: 'Well, this is embarrassing... 🤖'
237
- },
246
+ }, // Note: multiple configure() calls are incrementally merged
238
247
 
239
248
  // Custom response transformer
240
249
  responseTransformer: (response) => ({
package/dist/index.d.mts CHANGED
@@ -44,9 +44,9 @@ interface HttpSuccessInfo {
44
44
  /**
45
45
  * Configuration for success responses
46
46
  */
47
- interface SuccessResponseConfig {
47
+ interface SuccessResponseConfig<T = unknown> {
48
48
  /** Response data payload */
49
- data?: unknown;
49
+ data?: T;
50
50
  /** Custom success message */
51
51
  message?: string;
52
52
  /** HTTP status code (default: 200) */
@@ -62,6 +62,8 @@ interface ErrorResponseConfig {
62
62
  includeStack?: boolean;
63
63
  /** Additional fields to include */
64
64
  additionalFields?: Record<string, unknown>;
65
+ /** Fallback status code for unknown errors */
66
+ fallbackCode?: number;
65
67
  }
66
68
  /**
67
69
  * Global library configuration
@@ -77,33 +79,63 @@ interface LibraryConfig {
77
79
  responseTransformer?: (response: Record<string, unknown>) => Record<string, unknown>;
78
80
  }
79
81
  /**
80
- * Standard success response structure
82
+ * Standard success response structure.
83
+ * The index signature `[key: string]: unknown` allows additional fields via
84
+ * `additionalFields`, but extra properties will be typed as `unknown` at compile time.
81
85
  */
82
- interface SuccessResponse {
86
+ interface SuccessResponse<T = unknown> {
83
87
  success: true;
84
88
  status_code: number;
85
89
  timestamp?: string;
86
- data?: unknown;
90
+ data?: T;
87
91
  message?: string;
88
92
  metadata?: Record<string, unknown>;
89
93
  [key: string]: unknown;
90
94
  }
91
95
  /**
92
- * Standard error response structure
96
+ * Standard error response structure.
97
+ * The index signature `[key: string]: unknown` allows additional fields via
98
+ * `additionalFields`, but extra properties will be typed as `unknown` at compile time.
93
99
  */
94
100
  interface ErrorResponse {
95
101
  success: false;
96
102
  status_code: number;
97
103
  timestamp?: string;
104
+ retry_after?: number;
98
105
  error: {
99
106
  type: string;
100
107
  title: string;
101
108
  message: string;
102
109
  details?: string;
110
+ stack?: string;
103
111
  };
104
112
  metadata?: Record<string, unknown>;
105
113
  [key: string]: unknown;
106
114
  }
115
+ /**
116
+ * Input parameters for paginated responses
117
+ */
118
+ interface PaginationInput {
119
+ /** Current page number */
120
+ page: number;
121
+ /** Items per page */
122
+ limit: number;
123
+ /** Total number of items */
124
+ total: number;
125
+ /** Optional pre-computed total pages (overrides auto-calculation) */
126
+ totalPages?: number;
127
+ }
128
+ /**
129
+ * Pagination metadata included in paginated responses
130
+ */
131
+ interface PaginationMeta {
132
+ page: number;
133
+ limit: number;
134
+ total: number;
135
+ total_pages: number;
136
+ has_next: boolean;
137
+ has_prev: boolean;
138
+ }
107
139
 
108
140
  /**
109
141
  * HTTP Response Kit - Status Code Enums
@@ -318,6 +350,8 @@ declare class HttpError extends Error {
318
350
  readonly type: string;
319
351
  /** Human-readable error title */
320
352
  readonly title: string;
353
+ /** Default error description from definition */
354
+ readonly details: string;
321
355
  /** Additional error metadata */
322
356
  readonly metadata?: Record<string, unknown>;
323
357
  /** Original error cause */
@@ -330,7 +364,7 @@ declare class HttpError extends Error {
330
364
  * @param code - HTTP status code (e.g., 404, 500)
331
365
  * @param options - Optional configuration
332
366
  */
333
- constructor(code: number, options?: HttpErrorOptions);
367
+ constructor(code: HttpClientErrorCode | HttpServerErrorCode | number, options?: HttpErrorOptions);
334
368
  /**
335
369
  * Convert error to plain object for JSON serialization
336
370
  */
@@ -417,6 +451,11 @@ declare class HttpError extends Error {
417
451
  static notExtended(message?: string, metadata?: Record<string, unknown>): HttpError;
418
452
  /** 511 Network Authentication Required */
419
453
  static networkAuthenticationRequired(message?: string, metadata?: Record<string, unknown>): HttpError;
454
+ /**
455
+ * Create an HttpError from a status code with default definition values.
456
+ * Semantic alias for `new HttpError(code)`.
457
+ */
458
+ static fromStatus(code: HttpClientErrorCode | HttpServerErrorCode | number, options?: HttpErrorOptions): HttpError;
420
459
  /**
421
460
  * Create an HttpError from an unknown error
422
461
  */
@@ -477,7 +516,7 @@ declare class HttpResponse {
477
516
  * @param config - Success response configuration
478
517
  * @returns Formatted success response object
479
518
  */
480
- static success(config?: SuccessResponseConfig): SuccessResponse;
519
+ static success<T = unknown>(config?: SuccessResponseConfig<T>): SuccessResponse<T>;
481
520
  /**
482
521
  * Format an error response
483
522
  *
@@ -495,17 +534,17 @@ declare class HttpResponse {
495
534
  */
496
535
  static fromError(error: unknown, config?: ErrorResponseConfig): ErrorResponse;
497
536
  /** 200 OK */
498
- static ok(data?: unknown, message?: string): SuccessResponse;
537
+ static ok<T = unknown>(data?: T, message?: string): SuccessResponse<T>;
499
538
  /** 201 Created */
500
- static created(data?: unknown, message?: string): SuccessResponse;
539
+ static created<T = unknown>(data?: T, message?: string): SuccessResponse<T>;
501
540
  /** 202 Accepted */
502
- static accepted(data?: unknown, message?: string): SuccessResponse;
541
+ static accepted<T = unknown>(data?: T, message?: string): SuccessResponse<T>;
503
542
  /** 204 No Content */
504
- static noContent(): SuccessResponse;
543
+ static noContent(): SuccessResponse<never>;
505
544
  /** 206 Partial Content */
506
- static partialContent(data?: unknown, message?: string): SuccessResponse;
507
- /** 304 Not Modified */
508
- static notModified(): SuccessResponse;
545
+ static partialContent<T = unknown>(data?: T, message?: string): SuccessResponse<T>;
546
+ /** 304 Not Modified — Note: 304 is a 3xx redirect code, included here as a convenience method */
547
+ static notModified(): SuccessResponse<never>;
509
548
  /**
510
549
  * Check if a response is a success response
511
550
  */
@@ -517,12 +556,7 @@ declare class HttpResponse {
517
556
  /**
518
557
  * Create a paginated success response
519
558
  */
520
- static paginated(data: unknown[], pagination: {
521
- page: number;
522
- limit: number;
523
- total: number;
524
- totalPages?: number;
525
- }, message?: string): SuccessResponse;
559
+ static paginated<T = unknown>(data: T[], pagination: PaginationInput, message?: string): SuccessResponse<T[]>;
526
560
  }
527
561
 
528
562
  /**
@@ -551,7 +585,7 @@ declare function configure(config: Partial<LibraryConfig>): void;
551
585
  /**
552
586
  * Get the current library configuration
553
587
  */
554
- declare function getConfig(): LibraryConfig;
588
+ declare function getConfig(): Readonly<LibraryConfig>;
555
589
  /**
556
590
  * Reset configuration to defaults
557
591
  */
@@ -561,4 +595,4 @@ declare function resetConfig(): void;
561
595
  */
562
596
  declare function isDevelopment(): boolean;
563
597
 
564
- export { type ErrorResponse, type ErrorResponseConfig, HttpClientErrorCode, HttpError, HttpErrorCode, HttpErrorDefinitions, type HttpErrorInfo, type HttpErrorOptions, HttpInfoCode, HttpInfoDefinitions, HttpRedirectCode, HttpRedirectDefinitions, HttpResponse, HttpServerErrorCode, HttpSuccessCode, HttpSuccessDefinitions, type HttpSuccessInfo, type LibraryConfig, type SuccessResponse, type SuccessResponseConfig, configure, getConfig, getErrorDefinition, getSuccessDefinition, isDevelopment, resetConfig };
598
+ export { type ErrorResponse, type ErrorResponseConfig, HttpClientErrorCode, HttpError, HttpErrorCode, HttpErrorDefinitions, type HttpErrorInfo, type HttpErrorOptions, HttpInfoCode, HttpInfoDefinitions, HttpRedirectCode, HttpRedirectDefinitions, HttpResponse, HttpServerErrorCode, HttpSuccessCode, HttpSuccessDefinitions, type HttpSuccessInfo, type LibraryConfig, type PaginationInput, type PaginationMeta, type SuccessResponse, type SuccessResponseConfig, configure, getConfig, getErrorDefinition, getSuccessDefinition, isDevelopment, resetConfig };
package/dist/index.d.ts CHANGED
@@ -44,9 +44,9 @@ interface HttpSuccessInfo {
44
44
  /**
45
45
  * Configuration for success responses
46
46
  */
47
- interface SuccessResponseConfig {
47
+ interface SuccessResponseConfig<T = unknown> {
48
48
  /** Response data payload */
49
- data?: unknown;
49
+ data?: T;
50
50
  /** Custom success message */
51
51
  message?: string;
52
52
  /** HTTP status code (default: 200) */
@@ -62,6 +62,8 @@ interface ErrorResponseConfig {
62
62
  includeStack?: boolean;
63
63
  /** Additional fields to include */
64
64
  additionalFields?: Record<string, unknown>;
65
+ /** Fallback status code for unknown errors */
66
+ fallbackCode?: number;
65
67
  }
66
68
  /**
67
69
  * Global library configuration
@@ -77,33 +79,63 @@ interface LibraryConfig {
77
79
  responseTransformer?: (response: Record<string, unknown>) => Record<string, unknown>;
78
80
  }
79
81
  /**
80
- * Standard success response structure
82
+ * Standard success response structure.
83
+ * The index signature `[key: string]: unknown` allows additional fields via
84
+ * `additionalFields`, but extra properties will be typed as `unknown` at compile time.
81
85
  */
82
- interface SuccessResponse {
86
+ interface SuccessResponse<T = unknown> {
83
87
  success: true;
84
88
  status_code: number;
85
89
  timestamp?: string;
86
- data?: unknown;
90
+ data?: T;
87
91
  message?: string;
88
92
  metadata?: Record<string, unknown>;
89
93
  [key: string]: unknown;
90
94
  }
91
95
  /**
92
- * Standard error response structure
96
+ * Standard error response structure.
97
+ * The index signature `[key: string]: unknown` allows additional fields via
98
+ * `additionalFields`, but extra properties will be typed as `unknown` at compile time.
93
99
  */
94
100
  interface ErrorResponse {
95
101
  success: false;
96
102
  status_code: number;
97
103
  timestamp?: string;
104
+ retry_after?: number;
98
105
  error: {
99
106
  type: string;
100
107
  title: string;
101
108
  message: string;
102
109
  details?: string;
110
+ stack?: string;
103
111
  };
104
112
  metadata?: Record<string, unknown>;
105
113
  [key: string]: unknown;
106
114
  }
115
+ /**
116
+ * Input parameters for paginated responses
117
+ */
118
+ interface PaginationInput {
119
+ /** Current page number */
120
+ page: number;
121
+ /** Items per page */
122
+ limit: number;
123
+ /** Total number of items */
124
+ total: number;
125
+ /** Optional pre-computed total pages (overrides auto-calculation) */
126
+ totalPages?: number;
127
+ }
128
+ /**
129
+ * Pagination metadata included in paginated responses
130
+ */
131
+ interface PaginationMeta {
132
+ page: number;
133
+ limit: number;
134
+ total: number;
135
+ total_pages: number;
136
+ has_next: boolean;
137
+ has_prev: boolean;
138
+ }
107
139
 
108
140
  /**
109
141
  * HTTP Response Kit - Status Code Enums
@@ -318,6 +350,8 @@ declare class HttpError extends Error {
318
350
  readonly type: string;
319
351
  /** Human-readable error title */
320
352
  readonly title: string;
353
+ /** Default error description from definition */
354
+ readonly details: string;
321
355
  /** Additional error metadata */
322
356
  readonly metadata?: Record<string, unknown>;
323
357
  /** Original error cause */
@@ -330,7 +364,7 @@ declare class HttpError extends Error {
330
364
  * @param code - HTTP status code (e.g., 404, 500)
331
365
  * @param options - Optional configuration
332
366
  */
333
- constructor(code: number, options?: HttpErrorOptions);
367
+ constructor(code: HttpClientErrorCode | HttpServerErrorCode | number, options?: HttpErrorOptions);
334
368
  /**
335
369
  * Convert error to plain object for JSON serialization
336
370
  */
@@ -417,6 +451,11 @@ declare class HttpError extends Error {
417
451
  static notExtended(message?: string, metadata?: Record<string, unknown>): HttpError;
418
452
  /** 511 Network Authentication Required */
419
453
  static networkAuthenticationRequired(message?: string, metadata?: Record<string, unknown>): HttpError;
454
+ /**
455
+ * Create an HttpError from a status code with default definition values.
456
+ * Semantic alias for `new HttpError(code)`.
457
+ */
458
+ static fromStatus(code: HttpClientErrorCode | HttpServerErrorCode | number, options?: HttpErrorOptions): HttpError;
420
459
  /**
421
460
  * Create an HttpError from an unknown error
422
461
  */
@@ -477,7 +516,7 @@ declare class HttpResponse {
477
516
  * @param config - Success response configuration
478
517
  * @returns Formatted success response object
479
518
  */
480
- static success(config?: SuccessResponseConfig): SuccessResponse;
519
+ static success<T = unknown>(config?: SuccessResponseConfig<T>): SuccessResponse<T>;
481
520
  /**
482
521
  * Format an error response
483
522
  *
@@ -495,17 +534,17 @@ declare class HttpResponse {
495
534
  */
496
535
  static fromError(error: unknown, config?: ErrorResponseConfig): ErrorResponse;
497
536
  /** 200 OK */
498
- static ok(data?: unknown, message?: string): SuccessResponse;
537
+ static ok<T = unknown>(data?: T, message?: string): SuccessResponse<T>;
499
538
  /** 201 Created */
500
- static created(data?: unknown, message?: string): SuccessResponse;
539
+ static created<T = unknown>(data?: T, message?: string): SuccessResponse<T>;
501
540
  /** 202 Accepted */
502
- static accepted(data?: unknown, message?: string): SuccessResponse;
541
+ static accepted<T = unknown>(data?: T, message?: string): SuccessResponse<T>;
503
542
  /** 204 No Content */
504
- static noContent(): SuccessResponse;
543
+ static noContent(): SuccessResponse<never>;
505
544
  /** 206 Partial Content */
506
- static partialContent(data?: unknown, message?: string): SuccessResponse;
507
- /** 304 Not Modified */
508
- static notModified(): SuccessResponse;
545
+ static partialContent<T = unknown>(data?: T, message?: string): SuccessResponse<T>;
546
+ /** 304 Not Modified — Note: 304 is a 3xx redirect code, included here as a convenience method */
547
+ static notModified(): SuccessResponse<never>;
509
548
  /**
510
549
  * Check if a response is a success response
511
550
  */
@@ -517,12 +556,7 @@ declare class HttpResponse {
517
556
  /**
518
557
  * Create a paginated success response
519
558
  */
520
- static paginated(data: unknown[], pagination: {
521
- page: number;
522
- limit: number;
523
- total: number;
524
- totalPages?: number;
525
- }, message?: string): SuccessResponse;
559
+ static paginated<T = unknown>(data: T[], pagination: PaginationInput, message?: string): SuccessResponse<T[]>;
526
560
  }
527
561
 
528
562
  /**
@@ -551,7 +585,7 @@ declare function configure(config: Partial<LibraryConfig>): void;
551
585
  /**
552
586
  * Get the current library configuration
553
587
  */
554
- declare function getConfig(): LibraryConfig;
588
+ declare function getConfig(): Readonly<LibraryConfig>;
555
589
  /**
556
590
  * Reset configuration to defaults
557
591
  */
@@ -561,4 +595,4 @@ declare function resetConfig(): void;
561
595
  */
562
596
  declare function isDevelopment(): boolean;
563
597
 
564
- export { type ErrorResponse, type ErrorResponseConfig, HttpClientErrorCode, HttpError, HttpErrorCode, HttpErrorDefinitions, type HttpErrorInfo, type HttpErrorOptions, HttpInfoCode, HttpInfoDefinitions, HttpRedirectCode, HttpRedirectDefinitions, HttpResponse, HttpServerErrorCode, HttpSuccessCode, HttpSuccessDefinitions, type HttpSuccessInfo, type LibraryConfig, type SuccessResponse, type SuccessResponseConfig, configure, getConfig, getErrorDefinition, getSuccessDefinition, isDevelopment, resetConfig };
598
+ export { type ErrorResponse, type ErrorResponseConfig, HttpClientErrorCode, HttpError, HttpErrorCode, HttpErrorDefinitions, type HttpErrorInfo, type HttpErrorOptions, HttpInfoCode, HttpInfoDefinitions, HttpRedirectCode, HttpRedirectDefinitions, HttpResponse, HttpServerErrorCode, HttpSuccessCode, HttpSuccessDefinitions, type HttpSuccessInfo, type LibraryConfig, type PaginationInput, type PaginationMeta, type SuccessResponse, type SuccessResponseConfig, configure, getConfig, getErrorDefinition, getSuccessDefinition, isDevelopment, resetConfig };
package/dist/index.js CHANGED
@@ -383,8 +383,23 @@ var HttpErrorDefinitions = {
383
383
  details: "The client needs to authenticate to gain network access."
384
384
  }
385
385
  };
386
+ Object.freeze(HttpErrorDefinitions);
386
387
  function getErrorDefinition(code) {
387
- return HttpErrorDefinitions[code] || HttpErrorDefinitions[500];
388
+ if (code < 400 || code > 599) {
389
+ throw new RangeError(
390
+ `Invalid HTTP error code: ${code}. Must be between 400 and 599.`
391
+ );
392
+ }
393
+ if (HttpErrorDefinitions[code]) {
394
+ return HttpErrorDefinitions[code];
395
+ }
396
+ const isClientError = code >= 400 && code < 500;
397
+ return {
398
+ type: isClientError ? "unknown_client_error" : "unknown_server_error",
399
+ title: isClientError ? "Unknown Client Error" : "Unknown Server Error",
400
+ code,
401
+ details: isClientError ? "An unknown client error occurred." : "An unknown server error occurred."
402
+ };
388
403
  }
389
404
 
390
405
  // src/constants/success-definitions.ts
@@ -430,6 +445,7 @@ var HttpSuccessDefinitions = {
430
445
  description: "The server has fulfilled a GET request and the response is a representation of the result of one or more instance-manipulations."
431
446
  }
432
447
  };
448
+ Object.freeze(HttpSuccessDefinitions);
433
449
  var HttpRedirectDefinitions = {
434
450
  [300 /* MULTIPLE_CHOICES */]: {
435
451
  code: 300,
@@ -464,6 +480,7 @@ var HttpRedirectDefinitions = {
464
480
  description: "The request and all future requests should be repeated using another URI."
465
481
  }
466
482
  };
483
+ Object.freeze(HttpRedirectDefinitions);
467
484
  var HttpInfoDefinitions = {
468
485
  [100 /* CONTINUE */]: {
469
486
  code: 100,
@@ -482,29 +499,41 @@ var HttpInfoDefinitions = {
482
499
  description: "The server is sending some response headers before the final response."
483
500
  }
484
501
  };
502
+ Object.freeze(HttpInfoDefinitions);
485
503
  function getSuccessDefinition(code) {
486
- return HttpSuccessDefinitions[code] || HttpRedirectDefinitions[code] || HttpInfoDefinitions[code] || HttpSuccessDefinitions[200];
504
+ const definedInfo = HttpSuccessDefinitions[code] || HttpRedirectDefinitions[code] || HttpInfoDefinitions[code];
505
+ if (definedInfo) {
506
+ return definedInfo;
507
+ }
508
+ return {
509
+ code,
510
+ description: "The request was processed with a non-standard status code."
511
+ };
487
512
  }
488
513
 
489
514
  // src/config/index.ts
490
515
  var defaultConfig = {
491
- isDevelopment: process.env.NODE_ENV === "development",
516
+ isDevelopment: void 0,
492
517
  includeTimestamp: true,
493
518
  customMessages: {},
494
519
  responseTransformer: void 0
495
520
  };
496
521
  var currentConfig = { ...defaultConfig };
497
522
  function configure(config) {
498
- currentConfig = { ...currentConfig, ...config };
523
+ currentConfig = {
524
+ ...currentConfig,
525
+ ...config,
526
+ customMessages: { ...currentConfig.customMessages, ...config.customMessages }
527
+ };
499
528
  }
500
529
  function getConfig() {
501
530
  return { ...currentConfig };
502
531
  }
503
532
  function resetConfig() {
504
- currentConfig = { ...defaultConfig };
533
+ currentConfig = { ...defaultConfig, customMessages: {} };
505
534
  }
506
535
  function isDevelopment() {
507
- return currentConfig.isDevelopment ?? false;
536
+ return currentConfig.isDevelopment ?? process.env.NODE_ENV === "development";
508
537
  }
509
538
  function shouldIncludeTimestamp() {
510
539
  return currentConfig.includeTimestamp ?? true;
@@ -533,6 +562,7 @@ var HttpError = class _HttpError extends Error {
533
562
  this.code = errorInfo.code;
534
563
  this.type = errorInfo.type;
535
564
  this.title = errorInfo.title;
565
+ this.details = errorInfo.details;
536
566
  this.metadata = options.metadata;
537
567
  this.cause = options.cause;
538
568
  this.retryAfter = options.retryAfter ?? errorInfo.retryAfter;
@@ -550,6 +580,7 @@ var HttpError = class _HttpError extends Error {
550
580
  type: this.type,
551
581
  title: this.title,
552
582
  message: this.message,
583
+ details: this.details,
553
584
  metadata: this.metadata,
554
585
  retryAfter: this.retryAfter
555
586
  };
@@ -727,6 +758,13 @@ var HttpError = class _HttpError extends Error {
727
758
  // ========================================================================
728
759
  // Utility Methods
729
760
  // ========================================================================
761
+ /**
762
+ * Create an HttpError from a status code with default definition values.
763
+ * Semantic alias for `new HttpError(code)`.
764
+ */
765
+ static fromStatus(code, options) {
766
+ return new _HttpError(code, options);
767
+ }
730
768
  /**
731
769
  * Create an HttpError from an unknown error
732
770
  */
@@ -734,6 +772,11 @@ var HttpError = class _HttpError extends Error {
734
772
  if (error instanceof _HttpError) {
735
773
  return error;
736
774
  }
775
+ if (fallbackCode < 400 || fallbackCode > 599) {
776
+ throw new RangeError(
777
+ `fallbackCode must be between 400 and 599, got ${fallbackCode}`
778
+ );
779
+ }
737
780
  if (error instanceof Error) {
738
781
  return new _HttpError(fallbackCode, {
739
782
  message: error.message,
@@ -774,7 +817,7 @@ var HttpResponse = class _HttpResponse {
774
817
  */
775
818
  static success(config = {}) {
776
819
  const {
777
- data = null,
820
+ data,
778
821
  message,
779
822
  statusCode = 200 /* OK */,
780
823
  metadata = {}
@@ -787,7 +830,7 @@ var HttpResponse = class _HttpResponse {
787
830
  if (shouldIncludeTimestamp()) {
788
831
  response.timestamp = (/* @__PURE__ */ new Date()).toISOString();
789
832
  }
790
- if (statusCode !== 204 /* NO_CONTENT */ && statusCode !== 205 /* RESET_CONTENT */) {
833
+ if (statusCode !== 204 /* NO_CONTENT */ && statusCode !== 205 /* RESET_CONTENT */ && statusCode !== 304 /* NOT_MODIFIED */) {
791
834
  if (data !== null && data !== void 0) {
792
835
  response.data = data;
793
836
  }
@@ -825,8 +868,11 @@ var HttpResponse = class _HttpResponse {
825
868
  if (shouldIncludeTimestamp()) {
826
869
  response.timestamp = (/* @__PURE__ */ new Date()).toISOString();
827
870
  }
871
+ if (error.details) {
872
+ response.error.details = error.details;
873
+ }
828
874
  if (includeStack ?? isDevelopment()) {
829
- response.error.details = error.stack;
875
+ response.error.stack = error.stack;
830
876
  }
831
877
  if (error.retryAfter) {
832
878
  response.retry_after = error.retryAfter;
@@ -835,7 +881,8 @@ var HttpResponse = class _HttpResponse {
835
881
  response.metadata = error.metadata;
836
882
  }
837
883
  if (additionalFields) {
838
- Object.assign(response, additionalFields);
884
+ const { success, status_code, error: _error, timestamp, metadata, retry_after, ...safeFields } = additionalFields;
885
+ Object.assign(response, safeFields);
839
886
  }
840
887
  const transformer = getResponseTransformer();
841
888
  if (transformer) {
@@ -851,7 +898,7 @@ var HttpResponse = class _HttpResponse {
851
898
  * @returns Formatted error response object
852
899
  */
853
900
  static fromError(error, config = {}) {
854
- const httpError = HttpError.fromError(error);
901
+ const httpError = HttpError.fromError(error, config.fallbackCode);
855
902
  return _HttpResponse.error(httpError, config);
856
903
  }
857
904
  // ========================================================================
@@ -877,7 +924,7 @@ var HttpResponse = class _HttpResponse {
877
924
  static partialContent(data, message) {
878
925
  return _HttpResponse.success({ data, message, statusCode: 206 /* PARTIAL_CONTENT */ });
879
926
  }
880
- /** 304 Not Modified */
927
+ /** 304 Not Modified — Note: 304 is a 3xx redirect code, included here as a convenience method */
881
928
  static notModified() {
882
929
  return _HttpResponse.success({ statusCode: 304 /* NOT_MODIFIED */ });
883
930
  }
@@ -900,16 +947,17 @@ var HttpResponse = class _HttpResponse {
900
947
  * Create a paginated success response
901
948
  */
902
949
  static paginated(data, pagination, message) {
950
+ const effectiveLimit = pagination.limit > 0 ? pagination.limit : 1;
903
951
  return _HttpResponse.success({
904
952
  data,
905
953
  message,
906
954
  metadata: {
907
955
  pagination: {
908
956
  page: pagination.page,
909
- limit: pagination.limit,
957
+ limit: effectiveLimit,
910
958
  total: pagination.total,
911
- total_pages: pagination.totalPages ?? Math.ceil(pagination.total / pagination.limit),
912
- has_next: pagination.page < (pagination.totalPages ?? Math.ceil(pagination.total / pagination.limit)),
959
+ total_pages: pagination.totalPages ?? Math.ceil(pagination.total / effectiveLimit),
960
+ has_next: pagination.page < (pagination.totalPages ?? Math.ceil(pagination.total / effectiveLimit)),
913
961
  has_prev: pagination.page > 1
914
962
  }
915
963
  }
package/dist/index.mjs CHANGED
@@ -340,8 +340,23 @@ var HttpErrorDefinitions = {
340
340
  details: "The client needs to authenticate to gain network access."
341
341
  }
342
342
  };
343
+ Object.freeze(HttpErrorDefinitions);
343
344
  function getErrorDefinition(code) {
344
- return HttpErrorDefinitions[code] || HttpErrorDefinitions[500];
345
+ if (code < 400 || code > 599) {
346
+ throw new RangeError(
347
+ `Invalid HTTP error code: ${code}. Must be between 400 and 599.`
348
+ );
349
+ }
350
+ if (HttpErrorDefinitions[code]) {
351
+ return HttpErrorDefinitions[code];
352
+ }
353
+ const isClientError = code >= 400 && code < 500;
354
+ return {
355
+ type: isClientError ? "unknown_client_error" : "unknown_server_error",
356
+ title: isClientError ? "Unknown Client Error" : "Unknown Server Error",
357
+ code,
358
+ details: isClientError ? "An unknown client error occurred." : "An unknown server error occurred."
359
+ };
345
360
  }
346
361
 
347
362
  // src/constants/success-definitions.ts
@@ -387,6 +402,7 @@ var HttpSuccessDefinitions = {
387
402
  description: "The server has fulfilled a GET request and the response is a representation of the result of one or more instance-manipulations."
388
403
  }
389
404
  };
405
+ Object.freeze(HttpSuccessDefinitions);
390
406
  var HttpRedirectDefinitions = {
391
407
  [300 /* MULTIPLE_CHOICES */]: {
392
408
  code: 300,
@@ -421,6 +437,7 @@ var HttpRedirectDefinitions = {
421
437
  description: "The request and all future requests should be repeated using another URI."
422
438
  }
423
439
  };
440
+ Object.freeze(HttpRedirectDefinitions);
424
441
  var HttpInfoDefinitions = {
425
442
  [100 /* CONTINUE */]: {
426
443
  code: 100,
@@ -439,29 +456,41 @@ var HttpInfoDefinitions = {
439
456
  description: "The server is sending some response headers before the final response."
440
457
  }
441
458
  };
459
+ Object.freeze(HttpInfoDefinitions);
442
460
  function getSuccessDefinition(code) {
443
- return HttpSuccessDefinitions[code] || HttpRedirectDefinitions[code] || HttpInfoDefinitions[code] || HttpSuccessDefinitions[200];
461
+ const definedInfo = HttpSuccessDefinitions[code] || HttpRedirectDefinitions[code] || HttpInfoDefinitions[code];
462
+ if (definedInfo) {
463
+ return definedInfo;
464
+ }
465
+ return {
466
+ code,
467
+ description: "The request was processed with a non-standard status code."
468
+ };
444
469
  }
445
470
 
446
471
  // src/config/index.ts
447
472
  var defaultConfig = {
448
- isDevelopment: process.env.NODE_ENV === "development",
473
+ isDevelopment: void 0,
449
474
  includeTimestamp: true,
450
475
  customMessages: {},
451
476
  responseTransformer: void 0
452
477
  };
453
478
  var currentConfig = { ...defaultConfig };
454
479
  function configure(config) {
455
- currentConfig = { ...currentConfig, ...config };
480
+ currentConfig = {
481
+ ...currentConfig,
482
+ ...config,
483
+ customMessages: { ...currentConfig.customMessages, ...config.customMessages }
484
+ };
456
485
  }
457
486
  function getConfig() {
458
487
  return { ...currentConfig };
459
488
  }
460
489
  function resetConfig() {
461
- currentConfig = { ...defaultConfig };
490
+ currentConfig = { ...defaultConfig, customMessages: {} };
462
491
  }
463
492
  function isDevelopment() {
464
- return currentConfig.isDevelopment ?? false;
493
+ return currentConfig.isDevelopment ?? process.env.NODE_ENV === "development";
465
494
  }
466
495
  function shouldIncludeTimestamp() {
467
496
  return currentConfig.includeTimestamp ?? true;
@@ -490,6 +519,7 @@ var HttpError = class _HttpError extends Error {
490
519
  this.code = errorInfo.code;
491
520
  this.type = errorInfo.type;
492
521
  this.title = errorInfo.title;
522
+ this.details = errorInfo.details;
493
523
  this.metadata = options.metadata;
494
524
  this.cause = options.cause;
495
525
  this.retryAfter = options.retryAfter ?? errorInfo.retryAfter;
@@ -507,6 +537,7 @@ var HttpError = class _HttpError extends Error {
507
537
  type: this.type,
508
538
  title: this.title,
509
539
  message: this.message,
540
+ details: this.details,
510
541
  metadata: this.metadata,
511
542
  retryAfter: this.retryAfter
512
543
  };
@@ -684,6 +715,13 @@ var HttpError = class _HttpError extends Error {
684
715
  // ========================================================================
685
716
  // Utility Methods
686
717
  // ========================================================================
718
+ /**
719
+ * Create an HttpError from a status code with default definition values.
720
+ * Semantic alias for `new HttpError(code)`.
721
+ */
722
+ static fromStatus(code, options) {
723
+ return new _HttpError(code, options);
724
+ }
687
725
  /**
688
726
  * Create an HttpError from an unknown error
689
727
  */
@@ -691,6 +729,11 @@ var HttpError = class _HttpError extends Error {
691
729
  if (error instanceof _HttpError) {
692
730
  return error;
693
731
  }
732
+ if (fallbackCode < 400 || fallbackCode > 599) {
733
+ throw new RangeError(
734
+ `fallbackCode must be between 400 and 599, got ${fallbackCode}`
735
+ );
736
+ }
694
737
  if (error instanceof Error) {
695
738
  return new _HttpError(fallbackCode, {
696
739
  message: error.message,
@@ -731,7 +774,7 @@ var HttpResponse = class _HttpResponse {
731
774
  */
732
775
  static success(config = {}) {
733
776
  const {
734
- data = null,
777
+ data,
735
778
  message,
736
779
  statusCode = 200 /* OK */,
737
780
  metadata = {}
@@ -744,7 +787,7 @@ var HttpResponse = class _HttpResponse {
744
787
  if (shouldIncludeTimestamp()) {
745
788
  response.timestamp = (/* @__PURE__ */ new Date()).toISOString();
746
789
  }
747
- if (statusCode !== 204 /* NO_CONTENT */ && statusCode !== 205 /* RESET_CONTENT */) {
790
+ if (statusCode !== 204 /* NO_CONTENT */ && statusCode !== 205 /* RESET_CONTENT */ && statusCode !== 304 /* NOT_MODIFIED */) {
748
791
  if (data !== null && data !== void 0) {
749
792
  response.data = data;
750
793
  }
@@ -782,8 +825,11 @@ var HttpResponse = class _HttpResponse {
782
825
  if (shouldIncludeTimestamp()) {
783
826
  response.timestamp = (/* @__PURE__ */ new Date()).toISOString();
784
827
  }
828
+ if (error.details) {
829
+ response.error.details = error.details;
830
+ }
785
831
  if (includeStack ?? isDevelopment()) {
786
- response.error.details = error.stack;
832
+ response.error.stack = error.stack;
787
833
  }
788
834
  if (error.retryAfter) {
789
835
  response.retry_after = error.retryAfter;
@@ -792,7 +838,8 @@ var HttpResponse = class _HttpResponse {
792
838
  response.metadata = error.metadata;
793
839
  }
794
840
  if (additionalFields) {
795
- Object.assign(response, additionalFields);
841
+ const { success, status_code, error: _error, timestamp, metadata, retry_after, ...safeFields } = additionalFields;
842
+ Object.assign(response, safeFields);
796
843
  }
797
844
  const transformer = getResponseTransformer();
798
845
  if (transformer) {
@@ -808,7 +855,7 @@ var HttpResponse = class _HttpResponse {
808
855
  * @returns Formatted error response object
809
856
  */
810
857
  static fromError(error, config = {}) {
811
- const httpError = HttpError.fromError(error);
858
+ const httpError = HttpError.fromError(error, config.fallbackCode);
812
859
  return _HttpResponse.error(httpError, config);
813
860
  }
814
861
  // ========================================================================
@@ -834,7 +881,7 @@ var HttpResponse = class _HttpResponse {
834
881
  static partialContent(data, message) {
835
882
  return _HttpResponse.success({ data, message, statusCode: 206 /* PARTIAL_CONTENT */ });
836
883
  }
837
- /** 304 Not Modified */
884
+ /** 304 Not Modified — Note: 304 is a 3xx redirect code, included here as a convenience method */
838
885
  static notModified() {
839
886
  return _HttpResponse.success({ statusCode: 304 /* NOT_MODIFIED */ });
840
887
  }
@@ -857,16 +904,17 @@ var HttpResponse = class _HttpResponse {
857
904
  * Create a paginated success response
858
905
  */
859
906
  static paginated(data, pagination, message) {
907
+ const effectiveLimit = pagination.limit > 0 ? pagination.limit : 1;
860
908
  return _HttpResponse.success({
861
909
  data,
862
910
  message,
863
911
  metadata: {
864
912
  pagination: {
865
913
  page: pagination.page,
866
- limit: pagination.limit,
914
+ limit: effectiveLimit,
867
915
  total: pagination.total,
868
- total_pages: pagination.totalPages ?? Math.ceil(pagination.total / pagination.limit),
869
- has_next: pagination.page < (pagination.totalPages ?? Math.ceil(pagination.total / pagination.limit)),
916
+ total_pages: pagination.totalPages ?? Math.ceil(pagination.total / effectiveLimit),
917
+ has_next: pagination.page < (pagination.totalPages ?? Math.ceil(pagination.total / effectiveLimit)),
870
918
  has_prev: pagination.page > 1
871
919
  }
872
920
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "http-response-kit",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "A professional HTTP error and response formatting library for Node.js",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -19,6 +19,8 @@
19
19
  "build": "tsup src/index.ts --format cjs,esm --dts --clean",
20
20
  "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
21
21
  "lint": "tsc --noEmit",
22
+ "test": "vitest run",
23
+ "test:watch": "vitest",
22
24
  "prepublishOnly": "npm run build"
23
25
  },
24
26
  "keywords": [
@@ -35,9 +37,11 @@
35
37
  "author": "Matteo Teodori",
36
38
  "license": "MIT",
37
39
  "devDependencies": {
38
- "@types/node": "^25.2.1",
39
- "tsup": "^8.0.0",
40
- "typescript": "^5.0.0"
40
+ "@types/node": "^25.3.2",
41
+ "tsup": "^8.5.1",
42
+ "tsx": "^4.21.0",
43
+ "typescript": "^5.9.3",
44
+ "vitest": "^4.0.18"
41
45
  },
42
46
  "engines": {
43
47
  "node": ">=16.0.0"
@@ -46,4 +50,4 @@
46
50
  "type": "git",
47
51
  "url": "https://github.com/matteo-teodori/http-response-kit"
48
52
  }
49
- }
53
+ }